Merge remote-tracking branch 'origin/master' into 1.7
This commit is contained in:
@@ -632,7 +632,8 @@ namespace FlaxEditor.Modules
|
|||||||
/// Deletes the specified item.
|
/// Deletes the specified item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
public void Delete(ContentItem item)
|
/// <param name="deletedByUser">If the file was deleted by the user and not outside the editor.</param>
|
||||||
|
public void Delete(ContentItem item, bool deletedByUser = false)
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
throw new ArgumentNullException();
|
throw new ArgumentNullException();
|
||||||
@@ -656,12 +657,12 @@ namespace FlaxEditor.Modules
|
|||||||
var children = folder.Children.ToArray();
|
var children = folder.Children.ToArray();
|
||||||
for (int i = 0; i < children.Length; i++)
|
for (int i = 0; i < children.Length; i++)
|
||||||
{
|
{
|
||||||
Delete(children[i]);
|
Delete(children[i], deletedByUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove directory
|
// Remove directory
|
||||||
if (Directory.Exists(path))
|
if (deletedByUser && Directory.Exists(path))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -689,7 +690,7 @@ namespace FlaxEditor.Modules
|
|||||||
// Delete asset by using content pool
|
// Delete asset by using content pool
|
||||||
FlaxEngine.Content.DeleteAsset(path);
|
FlaxEngine.Content.DeleteAsset(path);
|
||||||
}
|
}
|
||||||
else
|
else if (deletedByUser)
|
||||||
{
|
{
|
||||||
// Delete file
|
// Delete file
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
@@ -835,7 +836,7 @@ namespace FlaxEditor.Modules
|
|||||||
Editor.Log(string.Format($"Content item \'{child.Path}\' has been removed"));
|
Editor.Log(string.Format($"Content item \'{child.Path}\' has been removed"));
|
||||||
|
|
||||||
// Destroy it
|
// Destroy it
|
||||||
Delete(child);
|
Delete(child, false);
|
||||||
|
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ namespace FlaxEditor.Modules
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.IsFolder && shortName.EndsWith("."))
|
||||||
|
{
|
||||||
|
hint = "Name cannot end with '.'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Find invalid characters
|
// Find invalid characters
|
||||||
if (Utilities.Utils.HasInvalidPathChar(shortName))
|
if (Utilities.Utils.HasInvalidPathChar(shortName))
|
||||||
{
|
{
|
||||||
@@ -134,7 +140,7 @@ namespace FlaxEditor.Modules
|
|||||||
// Cache data
|
// Cache data
|
||||||
string sourcePath = item.Path;
|
string sourcePath = item.Path;
|
||||||
string sourceFolder = System.IO.Path.GetDirectoryName(sourcePath);
|
string sourceFolder = System.IO.Path.GetDirectoryName(sourcePath);
|
||||||
string extension = System.IO.Path.GetExtension(sourcePath);
|
string extension = item.IsFolder ? "" : System.IO.Path.GetExtension(sourcePath);
|
||||||
string destinationPath = StringUtils.CombinePaths(sourceFolder, shortName + extension);
|
string destinationPath = StringUtils.CombinePaths(sourceFolder, shortName + extension);
|
||||||
|
|
||||||
if (item.IsFolder)
|
if (item.IsFolder)
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ namespace
|
|||||||
String version;
|
String version;
|
||||||
|
|
||||||
RiderInstallation(const String& path_, const String& version_)
|
RiderInstallation(const String& path_, const String& version_)
|
||||||
: path(path_), version(version_)
|
: path(path_)
|
||||||
|
, version(version_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -146,13 +147,13 @@ bool sortInstallations(RiderInstallation* const& i1, RiderInstallation* const& i
|
|||||||
StringUtils::Parse(values1[0].Get(), &version1[0]);
|
StringUtils::Parse(values1[0].Get(), &version1[0]);
|
||||||
StringUtils::Parse(values1[1].Get(), &version1[1]);
|
StringUtils::Parse(values1[1].Get(), &version1[1]);
|
||||||
|
|
||||||
if(values1.Count() > 2)
|
if (values1.Count() > 2)
|
||||||
StringUtils::Parse(values1[2].Get(), &version1[2]);
|
StringUtils::Parse(values1[2].Get(), &version1[2]);
|
||||||
|
|
||||||
StringUtils::Parse(values2[0].Get(), &version2[0]);
|
StringUtils::Parse(values2[0].Get(), &version2[0]);
|
||||||
StringUtils::Parse(values2[1].Get(), &version2[1]);
|
StringUtils::Parse(values2[1].Get(), &version2[1]);
|
||||||
|
|
||||||
if(values2.Count() > 2)
|
if (values2.Count() > 2)
|
||||||
StringUtils::Parse(values2[2].Get(), &version2[2]);
|
StringUtils::Parse(values2[2].Get(), &version2[2]);
|
||||||
|
|
||||||
// Compare by MAJOR.MINOR.BUILD
|
// Compare by MAJOR.MINOR.BUILD
|
||||||
@@ -216,7 +217,24 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
|||||||
TEXT("flatpak run com.jetbrains.Rider"));
|
TEXT("flatpak run com.jetbrains.Rider"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (auto directory : subDirectories)
|
#if PLATFORM_MAC
|
||||||
|
String applicationSupportFolder;
|
||||||
|
FileSystem::GetSpecialFolderPath(SpecialFolder::ProgramData, applicationSupportFolder);
|
||||||
|
|
||||||
|
Array<String> subMacDirectories;
|
||||||
|
FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-0/"));
|
||||||
|
FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-1/"));
|
||||||
|
for (const String& directory : subMacDirectories)
|
||||||
|
{
|
||||||
|
String riderAppDirectory = directory / TEXT("Rider.app/Contents/Resources");
|
||||||
|
SearchDirectory(&installations, riderAppDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the local installer version
|
||||||
|
SearchDirectory(&installations, TEXT("/Applications/Rider.app/Contents/Resources"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (const String& directory : subDirectories)
|
||||||
SearchDirectory(&installations, directory);
|
SearchDirectory(&installations, directory);
|
||||||
|
|
||||||
// Sort found installations by version number
|
// Sort found installations by version number
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Title = "Clamp",
|
Title = "Clamp",
|
||||||
Description = "Clamps value to the specified range",
|
Description = "Clamps value to the specified range",
|
||||||
Flags = NodeFlags.AllGraphs,
|
Flags = NodeFlags.AllGraphs,
|
||||||
Size = new Float2(110, 60),
|
Size = new Float2(140, 60),
|
||||||
ConnectionsHints = ConnectionsHint.Numeric,
|
ConnectionsHints = ConnectionsHint.Numeric,
|
||||||
IndependentBoxes = new[] { 0 },
|
IndependentBoxes = new[] { 0 },
|
||||||
DependentBoxes = new[] { 1, 2, 3 },
|
DependentBoxes = new[] { 1, 2, 3 },
|
||||||
|
|||||||
@@ -519,7 +519,7 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cache data
|
// Cache data
|
||||||
string extension = Path.GetExtension(item.Path);
|
string extension = item.IsFolder ? "" : Path.GetExtension(item.Path);
|
||||||
var newPath = StringUtils.CombinePaths(item.ParentFolder.Path, newShortName + extension);
|
var newPath = StringUtils.CombinePaths(item.ParentFolder.Path, newShortName + extension);
|
||||||
|
|
||||||
// Check if was renaming mock element
|
// Check if was renaming mock element
|
||||||
@@ -625,7 +625,7 @@ namespace FlaxEditor.Windows
|
|||||||
|
|
||||||
// Delete items
|
// Delete items
|
||||||
for (int i = 0; i < toDelete.Count; i++)
|
for (int i = 0; i < toDelete.Count; i++)
|
||||||
Editor.ContentDatabase.Delete(toDelete[i]);
|
Editor.ContentDatabase.Delete(toDelete[i], true);
|
||||||
|
|
||||||
RefreshView();
|
RefreshView();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,8 +205,6 @@ void MaterialInstance::Bind(BindParameters& params)
|
|||||||
|
|
||||||
Asset::LoadResult MaterialInstance::load()
|
Asset::LoadResult MaterialInstance::load()
|
||||||
{
|
{
|
||||||
ASSERT(_baseMaterial == nullptr);
|
|
||||||
|
|
||||||
// Get main chunk
|
// Get main chunk
|
||||||
auto chunk0 = GetChunk(0);
|
auto chunk0 = GetChunk(0);
|
||||||
if (chunk0 == nullptr || chunk0->IsMissing())
|
if (chunk0 == nullptr || chunk0->IsMissing())
|
||||||
@@ -229,6 +227,7 @@ Asset::LoadResult MaterialInstance::load()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Clear parameters if has no material loaded
|
// Clear parameters if has no material loaded
|
||||||
|
_baseMaterial = nullptr;
|
||||||
Params.Dispose();
|
Params.Dispose();
|
||||||
ParamsChanged();
|
ParamsChanged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ void String::Set(const char* chars, int32 length)
|
|||||||
}
|
}
|
||||||
_length = length;
|
_length = length;
|
||||||
}
|
}
|
||||||
if (chars)
|
if (chars && length)
|
||||||
StringUtils::ConvertANSI2UTF16(chars, _data, length, _length);
|
StringUtils::ConvertANSI2UTF16(chars, _data, length, _length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,8 +298,10 @@ String String::TrimTrailing() const
|
|||||||
end--;
|
end--;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_LOW_LAYER(end >= start);
|
const int32 count = end - start + 1;
|
||||||
return Substring(start, end - start + 1);
|
if (start >= 0 && start + count <= Length() && count >= 0)
|
||||||
|
return String(_data + start, count);
|
||||||
|
return Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator/=(const Char* str)
|
String& String::operator/=(const Char* str)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ namespace FlaxEngine.Interop
|
|||||||
public static class NativeToManaged
|
public static class NativeToManaged
|
||||||
{
|
{
|
||||||
public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target;
|
public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target;
|
||||||
|
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
|
||||||
|
|
||||||
public static void Free(IntPtr unmanaged)
|
public static void Free(IntPtr unmanaged)
|
||||||
{
|
{
|
||||||
@@ -44,6 +45,7 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
public static class ManagedToNative
|
public static class ManagedToNative
|
||||||
{
|
{
|
||||||
|
public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target;
|
||||||
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
|
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
|
||||||
|
|
||||||
public static void Free(IntPtr unmanaged)
|
public static void Free(IntPtr unmanaged)
|
||||||
@@ -147,29 +149,16 @@ namespace FlaxEngine.Interop
|
|||||||
#if FLAX_EDITOR
|
#if FLAX_EDITOR
|
||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
#endif
|
#endif
|
||||||
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller.ManagedToNative))]
|
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller))]
|
||||||
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller.ManagedToNative))]
|
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller))]
|
||||||
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller.ManagedToNative))]
|
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller))]
|
||||||
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedOut, typeof(ObjectMarshaller.NativeToManaged))]
|
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedOut, typeof(ObjectMarshaller))]
|
||||||
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedIn, typeof(ObjectMarshaller.NativeToManaged))]
|
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedIn, typeof(ObjectMarshaller))]
|
||||||
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller.NativeToManaged))]
|
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller))]
|
||||||
public static class ObjectMarshaller
|
public static class ObjectMarshaller
|
||||||
{
|
{
|
||||||
#if FLAX_EDITOR
|
public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged).Target) : null;
|
||||||
[HideInEditor]
|
public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => Unsafe.As<object>(managed) != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero;
|
||||||
#endif
|
|
||||||
public static class NativeToManaged
|
|
||||||
{
|
|
||||||
public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged).Target) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FLAX_EDITOR
|
|
||||||
[HideInEditor]
|
|
||||||
#endif
|
|
||||||
public static class ManagedToNative
|
|
||||||
{
|
|
||||||
public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => Unsafe.As<object>(managed) != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FLAX_EDITOR
|
#if FLAX_EDITOR
|
||||||
@@ -342,6 +331,7 @@ namespace FlaxEngine.Interop
|
|||||||
public static class NativeToManaged
|
public static class NativeToManaged
|
||||||
{
|
{
|
||||||
public static Dictionary<T, U> ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller<T, U>.ToManaged(unmanaged);
|
public static Dictionary<T, U> ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller<T, U>.ToManaged(unmanaged);
|
||||||
|
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed, GCHandleType.Weak);
|
||||||
public static void Free(IntPtr unmanaged) => DictionaryMarshaller<T, U>.Free(unmanaged);
|
public static void Free(IntPtr unmanaged) => DictionaryMarshaller<T, U>.Free(unmanaged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,8 +340,8 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
public static class ManagedToNative
|
public static class ManagedToNative
|
||||||
{
|
{
|
||||||
|
public static Dictionary<T, U> ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller<T, U>.ToManaged(unmanaged);
|
||||||
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed, GCHandleType.Weak);
|
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed, GCHandleType.Weak);
|
||||||
|
|
||||||
public static void Free(IntPtr unmanaged)
|
public static void Free(IntPtr unmanaged)
|
||||||
{
|
{
|
||||||
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
|
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
|
||||||
@@ -425,6 +415,28 @@ namespace FlaxEngine.Interop
|
|||||||
return new T[numElements];
|
return new T[numElements];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||||
|
{
|
||||||
|
if (managed is null)
|
||||||
|
{
|
||||||
|
numElements = 0;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
numElements = managed.Length;
|
||||||
|
(ManagedHandle managedArrayHandle, _) = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||||
|
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArrayHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
|
||||||
|
|
||||||
|
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged)
|
||||||
|
{
|
||||||
|
if (unmanaged == null)
|
||||||
|
return Span<TUnmanagedElement>.Empty;
|
||||||
|
ManagedArray managedArray = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
|
||||||
|
return managedArray.ToSpan<TUnmanagedElement>();
|
||||||
|
}
|
||||||
|
|
||||||
public static Span<T> GetManagedValuesDestination(T[] managed) => managed;
|
public static Span<T> GetManagedValuesDestination(T[] managed) => managed;
|
||||||
|
|
||||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
||||||
@@ -591,6 +603,7 @@ namespace FlaxEngine.Interop
|
|||||||
public static class NativeToManaged
|
public static class NativeToManaged
|
||||||
{
|
{
|
||||||
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
|
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
|
||||||
|
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||||
public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
|
public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,11 +612,8 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
public static class ManagedToNative
|
public static class ManagedToNative
|
||||||
{
|
{
|
||||||
public static unsafe IntPtr ConvertToUnmanaged(string managed)
|
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
|
||||||
{
|
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||||
return managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Free(IntPtr unmanaged)
|
public static void Free(IntPtr unmanaged)
|
||||||
{
|
{
|
||||||
//ManagedString.Free(unmanaged); // No need to free weak handles
|
//ManagedString.Free(unmanaged); // No need to free weak handles
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ namespace FlaxEngine.Interop
|
|||||||
{
|
{
|
||||||
string moduleName = Marshal.PtrToStringAnsi(moduleNamePtr);
|
string moduleName = Marshal.PtrToStringAnsi(moduleNamePtr);
|
||||||
string modulePath = Marshal.PtrToStringAnsi(modulePathPtr);
|
string modulePath = Marshal.PtrToStringAnsi(modulePathPtr);
|
||||||
nativeLibraryPaths[moduleName] = modulePath;
|
libraryPaths[moduleName] = modulePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
@@ -297,7 +297,7 @@ namespace FlaxEngine.Interop
|
|||||||
internal static void GetClassFields(ManagedHandle typeHandle, NativeFieldDefinitions** classFields, int* classFieldsCount)
|
internal static void GetClassFields(ManagedHandle typeHandle, NativeFieldDefinitions** classFields, int* classFieldsCount)
|
||||||
{
|
{
|
||||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
var fields = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
NativeFieldDefinitions* arr = (NativeFieldDefinitions*)NativeAlloc(fields.Length, Unsafe.SizeOf<NativeFieldDefinitions>());
|
NativeFieldDefinitions* arr = (NativeFieldDefinitions*)NativeAlloc(fields.Length, Unsafe.SizeOf<NativeFieldDefinitions>());
|
||||||
for (int i = 0; i < fields.Length; i++)
|
for (int i = 0; i < fields.Length; i++)
|
||||||
@@ -331,7 +331,7 @@ namespace FlaxEngine.Interop
|
|||||||
internal static void GetClassProperties(ManagedHandle typeHandle, NativePropertyDefinitions** classProperties, int* classPropertiesCount)
|
internal static void GetClassProperties(ManagedHandle typeHandle, NativePropertyDefinitions** classProperties, int* classPropertiesCount)
|
||||||
{
|
{
|
||||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||||
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
var properties = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
var arr = (NativePropertyDefinitions*)NativeAlloc(properties.Length, Unsafe.SizeOf<NativePropertyDefinitions>());
|
var arr = (NativePropertyDefinitions*)NativeAlloc(properties.Length, Unsafe.SizeOf<NativePropertyDefinitions>());
|
||||||
for (int i = 0; i < properties.Length; i++)
|
for (int i = 0; i < properties.Length; i++)
|
||||||
@@ -804,8 +804,8 @@ namespace FlaxEngine.Interop
|
|||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
internal static IntPtr FieldGetValueBoxed(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle)
|
internal static IntPtr FieldGetValueBoxed(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle)
|
||||||
{
|
{
|
||||||
object fieldOwner = fieldOwnerHandle.Target;
|
|
||||||
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
||||||
|
object fieldOwner = field.field.IsStatic ? null : fieldOwnerHandle.Target;
|
||||||
object fieldValue = field.field.GetValue(fieldOwner);
|
object fieldValue = field.field.GetValue(fieldOwner);
|
||||||
return Invoker.MarshalReturnValueGeneric(field.field.FieldType, fieldValue);
|
return Invoker.MarshalReturnValueGeneric(field.field.FieldType, fieldValue);
|
||||||
}
|
}
|
||||||
@@ -909,7 +909,7 @@ namespace FlaxEngine.Interop
|
|||||||
loadedNativeLibraries.Remove(nativeLibraryName);
|
loadedNativeLibraries.Remove(nativeLibraryName);
|
||||||
}
|
}
|
||||||
if (nativeLibraryName != null)
|
if (nativeLibraryName != null)
|
||||||
nativeLibraryPaths.Remove(nativeLibraryName);
|
libraryPaths.Remove(nativeLibraryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace FlaxEngine.Interop
|
|||||||
private static Dictionary<Type, int> _typeSizeCache = new();
|
private static Dictionary<Type, int> _typeSizeCache = new();
|
||||||
|
|
||||||
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
||||||
internal static Dictionary<string, string> nativeLibraryPaths = new();
|
internal static Dictionary<string, string> libraryPaths = new();
|
||||||
private static Dictionary<Assembly, string> assemblyOwnedNativeLibraries = new();
|
private static Dictionary<Assembly, string> assemblyOwnedNativeLibraries = new();
|
||||||
internal static AssemblyLoadContext scriptingAssemblyLoadContext;
|
internal static AssemblyLoadContext scriptingAssemblyLoadContext;
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ namespace FlaxEngine.Interop
|
|||||||
{
|
{
|
||||||
if (!loadedNativeLibraries.TryGetValue(libraryName, out IntPtr nativeLibrary))
|
if (!loadedNativeLibraries.TryGetValue(libraryName, out IntPtr nativeLibrary))
|
||||||
{
|
{
|
||||||
if (!nativeLibraryPaths.TryGetValue(libraryName, out var nativeLibraryPath))
|
if (!libraryPaths.TryGetValue(libraryName, out var nativeLibraryPath))
|
||||||
nativeLibraryPath = libraryName;
|
nativeLibraryPath = libraryName;
|
||||||
|
|
||||||
nativeLibrary = NativeLibrary.Load(nativeLibraryPath, assembly, dllImportSearchPath);
|
nativeLibrary = NativeLibrary.Load(nativeLibraryPath, assembly, dllImportSearchPath);
|
||||||
@@ -101,9 +101,9 @@ namespace FlaxEngine.Interop
|
|||||||
private static Assembly OnScriptingAssemblyLoadContextResolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
|
private static Assembly OnScriptingAssemblyLoadContextResolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
|
||||||
{
|
{
|
||||||
// FIXME: There should be a better way to resolve the path to EditorTargetPath where the dependencies are stored
|
// FIXME: There should be a better way to resolve the path to EditorTargetPath where the dependencies are stored
|
||||||
foreach (string nativeLibraryPath in nativeLibraryPaths.Values)
|
foreach (string libraryPath in libraryPaths.Values)
|
||||||
{
|
{
|
||||||
string editorTargetPath = Path.GetDirectoryName(nativeLibraryPath);
|
string editorTargetPath = Path.GetDirectoryName(libraryPath);
|
||||||
|
|
||||||
var assemblyPath = Path.Combine(editorTargetPath, assemblyName.Name + ".dll");
|
var assemblyPath = Path.Combine(editorTargetPath, assemblyName.Name + ".dll");
|
||||||
if (File.Exists(assemblyPath))
|
if (File.Exists(assemblyPath))
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace
|
|||||||
FORCE_INLINE void InitFilter(dtQueryFilter& filter)
|
FORCE_INLINE void InitFilter(dtQueryFilter& filter)
|
||||||
{
|
{
|
||||||
Platform::MemoryCopy(filter.m_areaCost, NavMeshRuntime::NavAreasCosts, sizeof(NavMeshRuntime::NavAreasCosts));
|
Platform::MemoryCopy(filter.m_areaCost, NavMeshRuntime::NavAreasCosts, sizeof(NavMeshRuntime::NavAreasCosts));
|
||||||
|
static_assert(sizeof(dtQueryFilter::m_areaCost) == sizeof(NavMeshRuntime::NavAreasCosts), "Invalid navmesh area cost list.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -462,6 +462,9 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sanatize the string if the exePath has spaces with properly espcaped spaces for popen
|
||||||
|
exePath.Replace(TEXT(" "), TEXT("\\ "));
|
||||||
|
|
||||||
const String cmdLine = exePath + TEXT(" ") + settings.Arguments;
|
const String cmdLine = exePath + TEXT(" ") + settings.Arguments;
|
||||||
const StringAsANSI<> cmdLineAnsi(*cmdLine, cmdLine.Length());
|
const StringAsANSI<> cmdLineAnsi(*cmdLine, cmdLine.Length());
|
||||||
FILE* pipe = popen(cmdLineAnsi.Get(), "r");
|
FILE* pipe = popen(cmdLineAnsi.Get(), "r");
|
||||||
|
|||||||
@@ -1010,12 +1010,36 @@ void ManagedBinaryModule::InitType(MClass* mclass)
|
|||||||
}
|
}
|
||||||
if (baseType.Module == this)
|
if (baseType.Module == this)
|
||||||
InitType(baseClass); // Ensure base is initialized before
|
InitType(baseClass); // Ensure base is initialized before
|
||||||
|
|
||||||
baseType.Module->TypeNameToTypeIndex.TryGet(baseClass->GetFullName(), *(int32*)&baseType.TypeIndex);
|
baseType.Module->TypeNameToTypeIndex.TryGet(baseClass->GetFullName(), *(int32*)&baseType.TypeIndex);
|
||||||
|
|
||||||
|
// So we must special case this flow of a generic class of which its possible the generic base class is not in the same module
|
||||||
|
if (baseType.TypeIndex == -1 && baseClass->IsGeneric())
|
||||||
|
{
|
||||||
|
auto genericNameIndex = baseClass->GetFullName().FindLast('`');
|
||||||
|
// we add 2 because of the way generic names work its `N
|
||||||
|
auto genericClassName = baseClass->GetFullName().Substring(0, genericNameIndex + 2);
|
||||||
|
|
||||||
|
// We check for the generic class name instead of the baseclass fullname
|
||||||
|
baseType.Module->TypeNameToTypeIndex.TryGet(genericClassName, *(int32*)&baseType.TypeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
if (!baseType)
|
if (!baseType)
|
||||||
{
|
{
|
||||||
LOG(Error, "Missing base class for managed class {0} from assembly {1}.", String(typeName), Assembly->ToString());
|
LOG(Error, "Missing base class for managed class {0} from assembly {1}.", String(typeName), Assembly->ToString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (baseType.TypeIndex == -1)
|
||||||
|
{
|
||||||
|
if (baseType.Module)
|
||||||
|
LOG(Error, "Missing base class for managed class {0} from assembly {1}.", String(baseClass->GetFullName()), baseType.Module->GetName().ToString());
|
||||||
|
else
|
||||||
|
// Not sure this can happen but never hurts to account for it
|
||||||
|
LOG(Error, "Missing base class for managed class {0} from unknown assembly.", String(baseClass->GetFullName()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ScriptingTypeHandle nativeType = baseType;
|
ScriptingTypeHandle nativeType = baseType;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -719,6 +719,13 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa
|
|||||||
StringAnsi nativeName = _name.EndsWith(".CSharp") ? StringAnsi(_name.Get(), _name.Length() - 7) : StringAnsi(_name);
|
StringAnsi nativeName = _name.EndsWith(".CSharp") ? StringAnsi(_name.Get(), _name.Length() - 7) : StringAnsi(_name);
|
||||||
RegisterNativeLibrary(nativeName.Get(), StringAnsi(nativePath).Get());
|
RegisterNativeLibrary(nativeName.Get(), StringAnsi(nativePath).Get());
|
||||||
}
|
}
|
||||||
|
#if USE_EDITOR
|
||||||
|
// Register the editor module location for Assembly resolver
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RegisterNativeLibrary(_name.Get(), StringAnsi(assemblyPath).Get());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
_hasCachedClasses = false;
|
_hasCachedClasses = false;
|
||||||
_assemblyPath = assemblyPath;
|
_assemblyPath = assemblyPath;
|
||||||
@@ -898,7 +905,6 @@ const Array<MMethod*>& MClass::GetMethods() const
|
|||||||
NativeMethodDefinitions& definition = methods[i];
|
NativeMethodDefinitions& definition = methods[i];
|
||||||
MMethod* method = New<MMethod>(const_cast<MClass*>(this), StringAnsi(definition.name), definition.handle, definition.numParameters, definition.methodAttributes);
|
MMethod* method = New<MMethod>(const_cast<MClass*>(this), StringAnsi(definition.name), definition.handle, definition.numParameters, definition.methodAttributes);
|
||||||
_methods.Add(method);
|
_methods.Add(method);
|
||||||
|
|
||||||
MCore::GC::FreeMemory((void*)definition.name);
|
MCore::GC::FreeMemory((void*)definition.name);
|
||||||
}
|
}
|
||||||
MCore::GC::FreeMemory(methods);
|
MCore::GC::FreeMemory(methods);
|
||||||
@@ -932,7 +938,6 @@ const Array<MField*>& MClass::GetFields() const
|
|||||||
NativeFieldDefinitions& definition = fields[i];
|
NativeFieldDefinitions& definition = fields[i];
|
||||||
MField* field = New<MField>(const_cast<MClass*>(this), definition.fieldHandle, definition.name, definition.fieldType, definition.fieldAttributes);
|
MField* field = New<MField>(const_cast<MClass*>(this), definition.fieldHandle, definition.name, definition.fieldType, definition.fieldAttributes);
|
||||||
_fields.Add(field);
|
_fields.Add(field);
|
||||||
|
|
||||||
MCore::GC::FreeMemory((void*)definition.name);
|
MCore::GC::FreeMemory((void*)definition.name);
|
||||||
}
|
}
|
||||||
MCore::GC::FreeMemory(fields);
|
MCore::GC::FreeMemory(fields);
|
||||||
@@ -977,7 +982,6 @@ const Array<MProperty*>& MClass::GetProperties() const
|
|||||||
const NativePropertyDefinitions& definition = foundProperties[i];
|
const NativePropertyDefinitions& definition = foundProperties[i];
|
||||||
MProperty* property = New<MProperty>(const_cast<MClass*>(this), definition.name, definition.getterHandle, definition.setterHandle, definition.getterAttributes, definition.setterAttributes);
|
MProperty* property = New<MProperty>(const_cast<MClass*>(this), definition.name, definition.getterHandle, definition.setterHandle, definition.getterAttributes, definition.setterAttributes);
|
||||||
_properties.Add(property);
|
_properties.Add(property);
|
||||||
|
|
||||||
MCore::GC::FreeMemory((void*)definition.name);
|
MCore::GC::FreeMemory((void*)definition.name);
|
||||||
}
|
}
|
||||||
MCore::GC::FreeMemory(foundProperties);
|
MCore::GC::FreeMemory(foundProperties);
|
||||||
@@ -1541,7 +1545,16 @@ bool InitHostfxr()
|
|||||||
get_hostfxr_params.size = sizeof(hostfxr_initialize_parameters);
|
get_hostfxr_params.size = sizeof(hostfxr_initialize_parameters);
|
||||||
get_hostfxr_params.assembly_path = libraryPath.Get();
|
get_hostfxr_params.assembly_path = libraryPath.Get();
|
||||||
#if PLATFORM_MAC
|
#if PLATFORM_MAC
|
||||||
get_hostfxr_params.dotnet_root = "/usr/local/share/dotnet";
|
::String macOSDotnetRoot = TEXT("/usr/local/share/dotnet");
|
||||||
|
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
|
||||||
|
// When emulating x64 on arm
|
||||||
|
const ::String dotnetRootEmulated = macOSDotnetRoot / TEXT("x64");
|
||||||
|
if (FileSystem::FileExists(dotnetRootEmulated / TEXT("dotnet"))) {
|
||||||
|
macOSDotnetRoot = dotnetRootEmulated;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
const FLAX_CORECLR_STRING& finalDotnetRootPath = FLAX_CORECLR_STRING(macOSDotnetRoot);
|
||||||
|
get_hostfxr_params.dotnet_root = finalDotnetRootPath.Get();
|
||||||
#else
|
#else
|
||||||
get_hostfxr_params.dotnet_root = nullptr;
|
get_hostfxr_params.dotnet_root = nullptr;
|
||||||
#endif
|
#endif
|
||||||
@@ -1588,7 +1601,10 @@ bool InitHostfxr()
|
|||||||
void* hostfxr = Platform::LoadLibrary(path.Get());
|
void* hostfxr = Platform::LoadLibrary(path.Get());
|
||||||
if (hostfxr == nullptr)
|
if (hostfxr == nullptr)
|
||||||
{
|
{
|
||||||
LOG(Fatal, "Failed to load hostfxr library ({0})", path);
|
if (FileSystem::FileExists(path))
|
||||||
|
LOG(Fatal, "Failed to load hostfxr library, possible platform/architecture mismatch with the library. See log for more information. ({0})", path);
|
||||||
|
else
|
||||||
|
LOG(Fatal, "Failed to load hostfxr library ({0})", path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_runtime_config");
|
hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_runtime_config");
|
||||||
@@ -1627,7 +1643,28 @@ bool InitHostfxr()
|
|||||||
if (rc != 0 || handle == nullptr)
|
if (rc != 0 || handle == nullptr)
|
||||||
{
|
{
|
||||||
hostfxr_close(handle);
|
hostfxr_close(handle);
|
||||||
LOG(Fatal, "Failed to initialize hostfxr: {0:x} ({1})", (unsigned int)rc, String(init_params.dotnet_root));
|
if (rc == 0x80008096) // FrameworkMissingFailure
|
||||||
|
{
|
||||||
|
String platformStr;
|
||||||
|
switch (PLATFORM_TYPE)
|
||||||
|
{
|
||||||
|
case PlatformType::Windows:
|
||||||
|
case PlatformType::UWP:
|
||||||
|
platformStr = PLATFORM_64BITS ? "Windows x64" : "Windows x86";
|
||||||
|
break;
|
||||||
|
case PlatformType::Linux:
|
||||||
|
platformStr = PLATFORM_ARCH_ARM64 ? "Linux Arm64" : PLATFORM_ARCH_ARM ? "Linux Arm32" : PLATFORM_64BITS ? "Linux x64" : "Linux x86";
|
||||||
|
break;
|
||||||
|
case PlatformType::Mac:
|
||||||
|
platformStr = PLATFORM_ARCH_ARM || PLATFORM_ARCH_ARM64 ? "macOS Arm64" : PLATFORM_64BITS ? "macOS x64" : "macOS x86";
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
platformStr = "";
|
||||||
|
}
|
||||||
|
LOG(Fatal, "Failed to resolve compatible .NET runtime version in '{0}'. Make sure the correct platform version for runtime is installed ({1})", platformStr, String(init_params.dotnet_root));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LOG(Fatal, "Failed to initialize hostfxr: {0:x} ({1})", (unsigned int)rc, String(init_params.dotnet_root));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace FlaxEngine.Tests
|
|||||||
{
|
{
|
||||||
var result = 0;
|
var result = 0;
|
||||||
var libraryName = "FlaxEngine";
|
var libraryName = "FlaxEngine";
|
||||||
var library = NativeLibrary.Load(Interop.NativeInterop.nativeLibraryPaths[libraryName]);
|
var library = NativeLibrary.Load(Interop.NativeInterop.libraryPaths[libraryName]);
|
||||||
if (library == IntPtr.Zero)
|
if (library == IntPtr.Zero)
|
||||||
return -1;
|
return -1;
|
||||||
var types = typeof(FlaxEngine.Object).Assembly.GetTypes();
|
var types = typeof(FlaxEngine.Object).Assembly.GetTypes();
|
||||||
|
|||||||
@@ -43,7 +43,16 @@ public:
|
|||||||
void write(const char* message) override
|
void write(const char* message) override
|
||||||
{
|
{
|
||||||
String s(message);
|
String s(message);
|
||||||
s.Replace('\n', ' ');
|
if (s.Length() <= 0)
|
||||||
|
return;
|
||||||
|
for (int32 i = 0; i < s.Length(); i++)
|
||||||
|
{
|
||||||
|
Char& c = s[i];
|
||||||
|
if (c == '\n')
|
||||||
|
c = ' ';
|
||||||
|
else if (c >= 255)
|
||||||
|
c = '?';
|
||||||
|
}
|
||||||
LOG(Info, "[Assimp]: {0}", s);
|
LOG(Info, "[Assimp]: {0}", s);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ public class Slider : ContainerControl
|
|||||||
private float _thumbCenter;
|
private float _thumbCenter;
|
||||||
private Float2 _thumbSize = new Float2(16, 16);
|
private Float2 _thumbSize = new Float2(16, 16);
|
||||||
private bool _isSliding;
|
private bool _isSliding;
|
||||||
|
private bool _mouseOverThumb;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the value (normalized to range 0-100).
|
/// Gets or sets the value (normalized to range 0-100).
|
||||||
@@ -163,21 +164,27 @@ public class Slider : ContainerControl
|
|||||||
public IBrush FillTrackBrush { get; set; }
|
public IBrush FillTrackBrush { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The color of the slider thumb when it's not selected
|
/// The color of the slider thumb when it's not selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EditorDisplay("Thumb Style"), EditorOrder(2030), Tooltip("The color of the slider thumb when it's not selected."), ExpandGroups]
|
[EditorDisplay("Thumb Style"), EditorOrder(2030), Tooltip("The color of the slider thumb when it's not selected."), ExpandGroups]
|
||||||
public Color ThumbColor { get; set; }
|
public Color ThumbColor { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The color of the slider thumb when it's selected
|
/// The color of the slider thumb when it's highlighted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EditorDisplay("Thumb Style"), EditorOrder(2031), Tooltip("The color of the slider thumb when it's selected.")]
|
[EditorDisplay("Thumb Style"), EditorOrder(2031), Tooltip("The color of the slider thumb when it's highlighted.")]
|
||||||
|
public Color ThumbColorHighlighted { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the slider thumb when it's selected.
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Thumb Style"), EditorOrder(2032), Tooltip("The color of the slider thumb when it's selected.")]
|
||||||
public Color ThumbColorSelected { get; set; }
|
public Color ThumbColorSelected { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the brush used for slider thumb drawing.
|
/// Gets or sets the brush used for slider thumb drawing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EditorDisplay("Thumb Style"), EditorOrder(2032), Tooltip("The brush of the slider thumb.")]
|
[EditorDisplay("Thumb Style"), EditorOrder(2033), Tooltip("The brush of the slider thumb.")]
|
||||||
public IBrush ThumbBrush { get; set; }
|
public IBrush ThumbBrush { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -222,6 +229,7 @@ public class Slider : ContainerControl
|
|||||||
TrackFillLineColor = style.LightBackground;
|
TrackFillLineColor = style.LightBackground;
|
||||||
ThumbColor = style.BackgroundNormal;
|
ThumbColor = style.BackgroundNormal;
|
||||||
ThumbColorSelected = style.BackgroundSelected;
|
ThumbColorSelected = style.BackgroundSelected;
|
||||||
|
ThumbColorHighlighted = style.BackgroundHighlighted;
|
||||||
UpdateThumb();
|
UpdateThumb();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +278,7 @@ public class Slider : ContainerControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw thumb
|
// Draw thumb
|
||||||
var thumbColor = _isSliding ? ThumbColorSelected : ThumbColor;
|
var thumbColor = _isSliding ? ThumbColorSelected : (_mouseOverThumb ? ThumbColorHighlighted : ThumbColor);
|
||||||
if (ThumbBrush != null)
|
if (ThumbBrush != null)
|
||||||
ThumbBrush.Draw(_thumbRect, thumbColor);
|
ThumbBrush.Draw(_thumbRect, thumbColor);
|
||||||
else
|
else
|
||||||
@@ -317,6 +325,7 @@ public class Slider : ContainerControl
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnMouseMove(Float2 location)
|
public override void OnMouseMove(Float2 location)
|
||||||
{
|
{
|
||||||
|
_mouseOverThumb = _thumbRect.Contains(location);
|
||||||
if (_isSliding)
|
if (_isSliding)
|
||||||
{
|
{
|
||||||
// Update sliding
|
// Update sliding
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
|
|
||||||
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
// From https://web.archive.org/web/20210117002833/http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
||||||
# define dtAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
|
# define dtAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
|
|||||||
float& tmin, float& tmax,
|
float& tmin, float& tmax,
|
||||||
int& segMin, int& segMax)
|
int& segMin, int& segMax)
|
||||||
{
|
{
|
||||||
static const float EPS = 0.00000001f;
|
static const float EPS = 0.000001f;
|
||||||
|
|
||||||
tmin = 0;
|
tmin = 0;
|
||||||
tmax = 1;
|
tmax = 1;
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ feature to find minor members.
|
|||||||
|
|
||||||
/// Used to ignore a function parameter. VS complains about unused parameters
|
/// Used to ignore a function parameter. VS complains about unused parameters
|
||||||
/// and this silences the warning.
|
/// and this silences the warning.
|
||||||
/// @param [in] _ Unused parameter
|
|
||||||
template<class T> void dtIgnoreUnused(const T&) { }
|
template<class T> void dtIgnoreUnused(const T&) { }
|
||||||
|
|
||||||
/// Swaps the values of the two parameters.
|
/// Swaps the values of the two parameters.
|
||||||
@@ -319,7 +318,7 @@ inline float dtVdot2D(const float* u, const float* v)
|
|||||||
/// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz)
|
/// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz)
|
||||||
/// @param[in] u The LHV vector [(x, y, z)]
|
/// @param[in] u The LHV vector [(x, y, z)]
|
||||||
/// @param[in] v The RHV vector [(x, y, z)]
|
/// @param[in] v The RHV vector [(x, y, z)]
|
||||||
/// @return The dot product on the xz-plane.
|
/// @return The perp dot product on the xz-plane.
|
||||||
///
|
///
|
||||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
||||||
inline float dtVperp2D(const float* u, const float* v)
|
inline float dtVperp2D(const float* u, const float* v)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
// 3. This notice may not be removed or altered from any source distribution.
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
//
|
//
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ enum CrowdAgentState
|
|||||||
{
|
{
|
||||||
DT_CROWDAGENT_STATE_INVALID, ///< The agent is not in a valid state.
|
DT_CROWDAGENT_STATE_INVALID, ///< The agent is not in a valid state.
|
||||||
DT_CROWDAGENT_STATE_WALKING, ///< The agent is traversing a normal navigation mesh polygon.
|
DT_CROWDAGENT_STATE_WALKING, ///< The agent is traversing a normal navigation mesh polygon.
|
||||||
DT_CROWDAGENT_STATE_OFFMESH, ///< The agent is traversing an off-mesh connection.
|
DT_CROWDAGENT_STATE_OFFMESH ///< The agent is traversing an off-mesh connection.
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Configuration parameters for a crowd agent.
|
/// Configuration parameters for a crowd agent.
|
||||||
@@ -108,7 +108,7 @@ enum MoveRequestState
|
|||||||
DT_CROWDAGENT_TARGET_REQUESTING,
|
DT_CROWDAGENT_TARGET_REQUESTING,
|
||||||
DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE,
|
DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE,
|
||||||
DT_CROWDAGENT_TARGET_WAITING_FOR_PATH,
|
DT_CROWDAGENT_TARGET_WAITING_FOR_PATH,
|
||||||
DT_CROWDAGENT_TARGET_VELOCITY,
|
DT_CROWDAGENT_TARGET_VELOCITY
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents an agent managed by a #dtCrowd object.
|
/// Represents an agent managed by a #dtCrowd object.
|
||||||
@@ -188,7 +188,7 @@ enum UpdateFlags
|
|||||||
DT_CROWD_OBSTACLE_AVOIDANCE = 2,
|
DT_CROWD_OBSTACLE_AVOIDANCE = 2,
|
||||||
DT_CROWD_SEPARATION = 4,
|
DT_CROWD_SEPARATION = 4,
|
||||||
DT_CROWD_OPTIMIZE_VIS = 8, ///< Use #dtPathCorridor::optimizePathVisibility() to optimize the agent path.
|
DT_CROWD_OPTIMIZE_VIS = 8, ///< Use #dtPathCorridor::optimizePathVisibility() to optimize the agent path.
|
||||||
DT_CROWD_OPTIMIZE_TOPO = 16, ///< Use dtPathCorridor::optimizePathTopology() to optimize the agent path.
|
DT_CROWD_OPTIMIZE_TOPO = 16 ///< Use dtPathCorridor::optimizePathTopology() to optimize the agent path.
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dtCrowdAgentDebugInfo
|
struct dtCrowdAgentDebugInfo
|
||||||
|
|||||||
@@ -433,8 +433,8 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
|
|||||||
float tmax = (neia[k*2+1]-va[2]) / (vb[2]-va[2]);
|
float tmax = (neia[k*2+1]-va[2]) / (vb[2]-va[2]);
|
||||||
if (tmin > tmax)
|
if (tmin > tmax)
|
||||||
dtSwap(tmin,tmax);
|
dtSwap(tmin,tmax);
|
||||||
link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
|
link->bmin = (unsigned char)roundf(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
|
||||||
link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
|
link->bmax = (unsigned char)roundf(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
|
||||||
}
|
}
|
||||||
else if (dir == 2 || dir == 6)
|
else if (dir == 2 || dir == 6)
|
||||||
{
|
{
|
||||||
@@ -442,8 +442,8 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
|
|||||||
float tmax = (neia[k*2+1]-va[0]) / (vb[0]-va[0]);
|
float tmax = (neia[k*2+1]-va[0]) / (vb[0]-va[0]);
|
||||||
if (tmin > tmax)
|
if (tmin > tmax)
|
||||||
dtSwap(tmin,tmax);
|
dtSwap(tmin,tmax);
|
||||||
link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
|
link->bmin = (unsigned char)roundf(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
|
||||||
link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
|
link->bmax = (unsigned char)roundf(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ static const int DT_MAX_AREAS = 64;
|
|||||||
enum dtTileFlags
|
enum dtTileFlags
|
||||||
{
|
{
|
||||||
/// The navigation mesh owns the tile memory and is responsible for freeing it.
|
/// The navigation mesh owns the tile memory and is responsible for freeing it.
|
||||||
DT_TILE_FREE_DATA = 0x01,
|
DT_TILE_FREE_DATA = 0x01
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Vertex flags returned by dtNavMeshQuery::findStraightPath.
|
/// Vertex flags returned by dtNavMeshQuery::findStraightPath.
|
||||||
@@ -107,32 +107,32 @@ enum dtStraightPathFlags
|
|||||||
{
|
{
|
||||||
DT_STRAIGHTPATH_START = 0x01, ///< The vertex is the start position in the path.
|
DT_STRAIGHTPATH_START = 0x01, ///< The vertex is the start position in the path.
|
||||||
DT_STRAIGHTPATH_END = 0x02, ///< The vertex is the end position in the path.
|
DT_STRAIGHTPATH_END = 0x02, ///< The vertex is the end position in the path.
|
||||||
DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04, ///< The vertex is the start of an off-mesh connection.
|
DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04 ///< The vertex is the start of an off-mesh connection.
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Options for dtNavMeshQuery::findStraightPath.
|
/// Options for dtNavMeshQuery::findStraightPath.
|
||||||
enum dtStraightPathOptions
|
enum dtStraightPathOptions
|
||||||
{
|
{
|
||||||
DT_STRAIGHTPATH_AREA_CROSSINGS = 0x01, ///< Add a vertex at every polygon edge crossing where area changes.
|
DT_STRAIGHTPATH_AREA_CROSSINGS = 0x01, ///< Add a vertex at every polygon edge crossing where area changes.
|
||||||
DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02, ///< Add a vertex at every polygon edge crossing.
|
DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02 ///< Add a vertex at every polygon edge crossing.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath
|
/// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath
|
||||||
enum dtFindPathOptions
|
enum dtFindPathOptions
|
||||||
{
|
{
|
||||||
DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
|
DT_FINDPATH_ANY_ANGLE = 0x02 ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Options for dtNavMeshQuery::raycast
|
/// Options for dtNavMeshQuery::raycast
|
||||||
enum dtRaycastOptions
|
enum dtRaycastOptions
|
||||||
{
|
{
|
||||||
DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
|
DT_RAYCAST_USE_COSTS = 0x01 ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
|
||||||
};
|
};
|
||||||
|
|
||||||
enum dtDetailTriEdgeFlags
|
enum dtDetailTriEdgeFlags
|
||||||
{
|
{
|
||||||
DT_DETAIL_EDGE_BOUNDARY = 0x01, ///< Detail triangle edge is part of the poly boundary
|
DT_DETAIL_EDGE_BOUNDARY = 0x01 ///< Detail triangle edge is part of the poly boundary
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ enum dtPolyTypes
|
|||||||
/// The polygon is a standard convex polygon that is part of the surface of the mesh.
|
/// The polygon is a standard convex polygon that is part of the surface of the mesh.
|
||||||
DT_POLYTYPE_GROUND = 0,
|
DT_POLYTYPE_GROUND = 0,
|
||||||
/// The polygon is an off-mesh connection consisting of two vertices.
|
/// The polygon is an off-mesh connection consisting of two vertices.
|
||||||
DT_POLYTYPE_OFFMESH_CONNECTION = 1,
|
DT_POLYTYPE_OFFMESH_CONNECTION = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ struct dtMeshTile
|
|||||||
unsigned int linksFreeList; ///< Index to the next free link.
|
unsigned int linksFreeList; ///< Index to the next free link.
|
||||||
dtMeshHeader* header; ///< The tile header.
|
dtMeshHeader* header; ///< The tile header.
|
||||||
dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount]
|
dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount]
|
||||||
float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount]
|
float* verts; ///< The tile vertices. [(x, y, z) * dtMeshHeader::vertCount]
|
||||||
dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount]
|
dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount]
|
||||||
dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount]
|
dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount]
|
||||||
|
|
||||||
@@ -312,8 +312,8 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Get flags for edge in detail triangle.
|
/// Get flags for edge in detail triangle.
|
||||||
/// @param triFlags[in] The flags for the triangle (last component of detail vertices above).
|
/// @param[in] triFlags The flags for the triangle (last component of detail vertices above).
|
||||||
/// @param edgeIndex[in] The index of the first vertex of the edge. For instance, if 0,
|
/// @param[in] edgeIndex The index of the first vertex of the edge. For instance, if 0,
|
||||||
/// returns flags for edge AB.
|
/// returns flags for edge AB.
|
||||||
inline int dtGetDetailTriEdgeFlags(unsigned char triFlags, int edgeIndex)
|
inline int dtGetDetailTriEdgeFlags(unsigned char triFlags, int edgeIndex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -117,6 +117,11 @@ void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh)
|
|||||||
dtFree(navmesh);
|
dtFree(navmesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dtPolyQuery::~dtPolyQuery()
|
||||||
|
{
|
||||||
|
// Defined out of line to fix the weak v-tables warning
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// @class dtNavMeshQuery
|
/// @class dtNavMeshQuery
|
||||||
@@ -301,11 +306,7 @@ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*fr
|
|||||||
float pt[3];
|
float pt[3];
|
||||||
dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt);
|
dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt);
|
||||||
|
|
||||||
float h = 0.0f;
|
closestPointOnPoly(polyRef, pt, pt, NULL);
|
||||||
dtStatus status = getPolyHeight(polyRef, pt, &h);
|
|
||||||
if (dtStatusFailed(status))
|
|
||||||
return status;
|
|
||||||
pt[1] = h;
|
|
||||||
|
|
||||||
dtVcopy(randomPt, pt);
|
dtVcopy(randomPt, pt);
|
||||||
*randomRef = polyRef;
|
*randomRef = polyRef;
|
||||||
@@ -483,24 +484,23 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
|
|||||||
}
|
}
|
||||||
|
|
||||||
float pt[3];
|
float pt[3];
|
||||||
|
int checksLimit = 100;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
const float s = frand();
|
const float s = frand();
|
||||||
const float t = frand();
|
const float t = frand();
|
||||||
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
|
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
|
||||||
}
|
}
|
||||||
while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr);
|
while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr && checksLimit-- > 0);
|
||||||
|
if (checksLimit <= 0)
|
||||||
|
return DT_FAILURE;
|
||||||
|
|
||||||
float h = 0.0f;
|
closestPointOnPoly(randomPolyRef, pt, pt, NULL);
|
||||||
dtStatus stat = getPolyHeight(randomPolyRef, pt, &h);
|
|
||||||
if (dtStatusFailed(status))
|
|
||||||
return stat;
|
|
||||||
pt[1] = h;
|
|
||||||
|
|
||||||
dtVcopy(randomPt, pt);
|
dtVcopy(randomPt, pt);
|
||||||
*randomRef = randomPolyRef;
|
*randomRef = randomPolyRef;
|
||||||
|
|
||||||
return DT_SUCCESS;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -641,6 +641,8 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ~dtFindNearestPolyQuery();
|
||||||
|
|
||||||
dtPolyRef nearestRef() const { return m_nearestRef; }
|
dtPolyRef nearestRef() const { return m_nearestRef; }
|
||||||
const float* nearestPoint() const { return m_nearestPoint; }
|
const float* nearestPoint() const { return m_nearestPoint; }
|
||||||
bool isOverPoly() const { return m_overPoly; }
|
bool isOverPoly() const { return m_overPoly; }
|
||||||
@@ -683,6 +685,11 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dtFindNearestPolyQuery::~dtFindNearestPolyQuery()
|
||||||
|
{
|
||||||
|
// Defined out of line to fix the weak v-tables warning
|
||||||
|
}
|
||||||
|
|
||||||
/// @par
|
/// @par
|
||||||
///
|
///
|
||||||
/// @note If the search box does not intersect any polygons the search will
|
/// @note If the search box does not intersect any polygons the search will
|
||||||
@@ -858,6 +865,8 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ~dtCollectPolysQuery();
|
||||||
|
|
||||||
int numCollected() const { return m_numCollected; }
|
int numCollected() const { return m_numCollected; }
|
||||||
bool overflowed() const { return m_overflow; }
|
bool overflowed() const { return m_overflow; }
|
||||||
|
|
||||||
@@ -879,6 +888,11 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dtCollectPolysQuery::~dtCollectPolysQuery()
|
||||||
|
{
|
||||||
|
// Defined out of line to fix the weak v-tables warning
|
||||||
|
}
|
||||||
|
|
||||||
/// @par
|
/// @par
|
||||||
///
|
///
|
||||||
/// If no polygons are found, the function will return #DT_SUCCESS with a
|
/// If no polygons are found, the function will return #DT_SUCCESS with a
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ struct dtRaycastHit
|
|||||||
class dtPolyQuery
|
class dtPolyQuery
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~dtPolyQuery() { }
|
virtual ~dtPolyQuery();
|
||||||
|
|
||||||
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
|
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
|
||||||
/// This can be called multiple times for a single query.
|
/// This can be called multiple times for a single query.
|
||||||
@@ -176,7 +176,7 @@ public:
|
|||||||
dtStatus init(const dtNavMesh* nav, const int maxNodes);
|
dtStatus init(const dtNavMesh* nav, const int maxNodes);
|
||||||
|
|
||||||
/// @name Standard Pathfinding Functions
|
/// @name Standard Pathfinding Functions
|
||||||
// /@{
|
/// @{
|
||||||
|
|
||||||
/// Finds a path from the start polygon to the end polygon.
|
/// Finds a path from the start polygon to the end polygon.
|
||||||
/// @param[in] startRef The refrence id of the start polygon.
|
/// @param[in] startRef The refrence id of the start polygon.
|
||||||
@@ -397,9 +397,9 @@ public:
|
|||||||
/// @param[in] startPos A position within the start polygon representing
|
/// @param[in] startPos A position within the start polygon representing
|
||||||
/// the start of the ray. [(x, y, z)]
|
/// the start of the ray. [(x, y, z)]
|
||||||
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
|
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
|
||||||
|
/// @param[in] filter The polygon filter to apply to the query.
|
||||||
/// @param[out] t The hit parameter. (FLT_MAX if no wall hit.)
|
/// @param[out] t The hit parameter. (FLT_MAX if no wall hit.)
|
||||||
/// @param[out] hitNormal The normal of the nearest wall hit. [(x, y, z)]
|
/// @param[out] hitNormal The normal of the nearest wall hit. [(x, y, z)]
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] path The reference ids of the visited polygons. [opt]
|
/// @param[out] path The reference ids of the visited polygons. [opt]
|
||||||
/// @param[out] pathCount The number of visited polygons. [opt]
|
/// @param[out] pathCount The number of visited polygons. [opt]
|
||||||
/// @param[in] maxPath The maximum number of polygons the @p path array can hold.
|
/// @param[in] maxPath The maximum number of polygons the @p path array can hold.
|
||||||
@@ -415,7 +415,7 @@ public:
|
|||||||
/// the start of the ray. [(x, y, z)]
|
/// the start of the ray. [(x, y, z)]
|
||||||
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
|
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
/// @param[in] filter The polygon filter to apply to the query.
|
||||||
/// @param[in] flags govern how the raycast behaves. See dtRaycastOptions
|
/// @param[in] options govern how the raycast behaves. See dtRaycastOptions
|
||||||
/// @param[out] hit Pointer to a raycast hit structure which will be filled by the results.
|
/// @param[out] hit Pointer to a raycast hit structure which will be filled by the results.
|
||||||
/// @param[in] prevRef parent of start ref. Used during for cost calculation [opt]
|
/// @param[in] prevRef parent of start ref. Used during for cost calculation [opt]
|
||||||
/// @returns The status flags for the query.
|
/// @returns The status flags for the query.
|
||||||
@@ -466,6 +466,7 @@ public:
|
|||||||
/// The location is not exactly constrained by the circle, but it limits the visited polygons.
|
/// The location is not exactly constrained by the circle, but it limits the visited polygons.
|
||||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
/// @param[in] startRef The reference id of the polygon where the search starts.
|
||||||
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
|
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
|
||||||
|
/// @param[in] maxRadius The radius of the search circle. [Units: wu]
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
/// @param[in] filter The polygon filter to apply to the query.
|
||||||
/// @param[in] frand Function returning a random number [0..1).
|
/// @param[in] frand Function returning a random number [0..1).
|
||||||
/// @param[out] randomRef The reference id of the random location.
|
/// @param[out] randomRef The reference id of the random location.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ enum dtNodeFlags
|
|||||||
{
|
{
|
||||||
DT_NODE_OPEN = 0x01,
|
DT_NODE_OPEN = 0x01,
|
||||||
DT_NODE_CLOSED = 0x02,
|
DT_NODE_CLOSED = 0x02,
|
||||||
DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast.
|
DT_NODE_PARENT_DETACHED = 0x04 // parent of the node is not adjacent. Found using raycast.
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef unsigned short dtNodeIndex;
|
typedef unsigned short dtNodeIndex;
|
||||||
|
|||||||
@@ -239,6 +239,11 @@ const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref)
|
|||||||
return ob;
|
return ob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dtTileCacheMeshProcess::~dtTileCacheMeshProcess()
|
||||||
|
{
|
||||||
|
// Defined out of line to fix the weak v-tables warning
|
||||||
|
}
|
||||||
|
|
||||||
dtStatus dtTileCache::addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result)
|
dtStatus dtTileCache::addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result)
|
||||||
{
|
{
|
||||||
// Make sure the data is in right format.
|
// Make sure the data is in right format.
|
||||||
|
|||||||
@@ -3,16 +3,13 @@
|
|||||||
|
|
||||||
#include "DetourStatus.h"
|
#include "DetourStatus.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef unsigned int dtObstacleRef;
|
typedef unsigned int dtObstacleRef;
|
||||||
|
|
||||||
typedef unsigned int dtCompressedTileRef;
|
typedef unsigned int dtCompressedTileRef;
|
||||||
|
|
||||||
/// Flags for addTile
|
/// Flags for addTile
|
||||||
enum dtCompressedTileFlags
|
enum dtCompressedTileFlags
|
||||||
{
|
{
|
||||||
DT_COMPRESSEDTILE_FREE_DATA = 0x01, ///< Navmesh owns the tile memory and should free it.
|
DT_COMPRESSEDTILE_FREE_DATA = 0x01 ///< Navmesh owns the tile memory and should free it.
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dtCompressedTile
|
struct dtCompressedTile
|
||||||
@@ -32,14 +29,14 @@ enum ObstacleState
|
|||||||
DT_OBSTACLE_EMPTY,
|
DT_OBSTACLE_EMPTY,
|
||||||
DT_OBSTACLE_PROCESSING,
|
DT_OBSTACLE_PROCESSING,
|
||||||
DT_OBSTACLE_PROCESSED,
|
DT_OBSTACLE_PROCESSED,
|
||||||
DT_OBSTACLE_REMOVING,
|
DT_OBSTACLE_REMOVING
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ObstacleType
|
enum ObstacleType
|
||||||
{
|
{
|
||||||
DT_OBSTACLE_CYLINDER,
|
DT_OBSTACLE_CYLINDER,
|
||||||
DT_OBSTACLE_BOX, // AABB
|
DT_OBSTACLE_BOX, // AABB
|
||||||
DT_OBSTACLE_ORIENTED_BOX, // OBB
|
DT_OBSTACLE_ORIENTED_BOX // OBB
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dtObstacleCylinder
|
struct dtObstacleCylinder
|
||||||
@@ -97,13 +94,10 @@ struct dtTileCacheParams
|
|||||||
|
|
||||||
struct dtTileCacheMeshProcess
|
struct dtTileCacheMeshProcess
|
||||||
{
|
{
|
||||||
virtual ~dtTileCacheMeshProcess() { }
|
virtual ~dtTileCacheMeshProcess();
|
||||||
|
virtual void process(struct dtNavMeshCreateParams* params, unsigned char* polyAreas, unsigned short* polyFlags) = 0;
|
||||||
virtual void process(struct dtNavMeshCreateParams* params,
|
|
||||||
unsigned char* polyAreas, unsigned short* polyFlags) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class dtTileCache
|
class dtTileCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -219,7 +213,7 @@ private:
|
|||||||
enum ObstacleRequestAction
|
enum ObstacleRequestAction
|
||||||
{
|
{
|
||||||
REQUEST_ADD,
|
REQUEST_ADD,
|
||||||
REQUEST_REMOVE,
|
REQUEST_REMOVE
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ObstacleRequest
|
struct ObstacleRequest
|
||||||
|
|||||||
@@ -23,6 +23,15 @@
|
|||||||
#include "DetourTileCacheBuilder.h"
|
#include "DetourTileCacheBuilder.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
dtTileCacheAlloc::~dtTileCacheAlloc()
|
||||||
|
{
|
||||||
|
// Defined out of line to fix the weak v-tables warning
|
||||||
|
}
|
||||||
|
|
||||||
|
dtTileCacheCompressor::~dtTileCacheCompressor()
|
||||||
|
{
|
||||||
|
// Defined out of line to fix the weak v-tables warning
|
||||||
|
}
|
||||||
|
|
||||||
template<class T> class dtFixedArray
|
template<class T> class dtFixedArray
|
||||||
{
|
{
|
||||||
@@ -881,7 +890,7 @@ static bool buildMeshAdjacency(dtTileCacheAlloc* alloc,
|
|||||||
const dtTileCacheContourSet& lcset)
|
const dtTileCacheContourSet& lcset)
|
||||||
{
|
{
|
||||||
// Based on code by Eric Lengyel from:
|
// Based on code by Eric Lengyel from:
|
||||||
// http://www.terathon.com/code/edges.php
|
// https://web.archive.org/web/20080704083314/http://www.terathon.com/code/edges.php
|
||||||
|
|
||||||
const int maxEdgeCount = npolys*MAX_VERTS_PER_POLY;
|
const int maxEdgeCount = npolys*MAX_VERTS_PER_POLY;
|
||||||
dtFixedArray<unsigned short> firstEdge(alloc, nverts + maxEdgeCount);
|
dtFixedArray<unsigned short> firstEdge(alloc, nverts + maxEdgeCount);
|
||||||
@@ -1399,7 +1408,6 @@ static void pushBack(unsigned short v, unsigned short* arr, int& an)
|
|||||||
static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem)
|
static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem)
|
||||||
{
|
{
|
||||||
// Count number of polygons to remove.
|
// Count number of polygons to remove.
|
||||||
int numRemovedVerts = 0;
|
|
||||||
int numTouchedVerts = 0;
|
int numTouchedVerts = 0;
|
||||||
int numRemainingEdges = 0;
|
int numRemainingEdges = 0;
|
||||||
for (int i = 0; i < mesh.npolys; ++i)
|
for (int i = 0; i < mesh.npolys; ++i)
|
||||||
@@ -1419,7 +1427,6 @@ static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem)
|
|||||||
}
|
}
|
||||||
if (numRemoved)
|
if (numRemoved)
|
||||||
{
|
{
|
||||||
numRemovedVerts += numRemoved;
|
|
||||||
numRemainingEdges += numVerts-(numRemoved+1);
|
numRemainingEdges += numVerts-(numRemoved+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1551,7 +1558,7 @@ static dtStatus removeVertex(dtTileCachePolyMesh& mesh, const unsigned short rem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove vertex.
|
// Remove vertex.
|
||||||
for (int i = (int)rem; i < mesh.nverts; ++i)
|
for (int i = (int)rem; i < mesh.nverts - 1; ++i)
|
||||||
{
|
{
|
||||||
mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
|
mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
|
||||||
mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
|
mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ struct dtTileCachePolyMesh
|
|||||||
|
|
||||||
struct dtTileCacheAlloc
|
struct dtTileCacheAlloc
|
||||||
{
|
{
|
||||||
virtual ~dtTileCacheAlloc() {}
|
virtual ~dtTileCacheAlloc();
|
||||||
|
|
||||||
virtual void reset() {}
|
virtual void reset() {}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ struct dtTileCacheAlloc
|
|||||||
|
|
||||||
struct dtTileCacheCompressor
|
struct dtTileCacheCompressor
|
||||||
{
|
{
|
||||||
virtual ~dtTileCacheCompressor() { }
|
virtual ~dtTileCacheCompressor();
|
||||||
|
|
||||||
virtual int maxCompressedSize(const int bufferSize) = 0;
|
virtual int maxCompressedSize(const int bufferSize) = 0;
|
||||||
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
|
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
|
||||||
|
|||||||
613
Source/ThirdParty/recastnavigation/Recast.cpp
vendored
613
Source/ThirdParty/recastnavigation/Recast.cpp
vendored
@@ -16,98 +16,93 @@
|
|||||||
// 3. This notice may not be removed or altered from any source distribution.
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <float.h>
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include "Recast.h"
|
#include "Recast.h"
|
||||||
#include "RecastAlloc.h"
|
#include "RecastAlloc.h"
|
||||||
#include "RecastAssert.h"
|
#include "RecastAssert.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
/// Allocates and constructs an object of the given type, returning a pointer.
|
/// Allocates and constructs an object of the given type, returning a pointer.
|
||||||
/// TODO: Support constructor args.
|
/// @param[in] allocLifetime Allocation lifetime hint
|
||||||
/// @param[in] hint Hint to the allocator.
|
template<typename T>
|
||||||
template <typename T>
|
T* rcNew(const rcAllocHint allocLifetime)
|
||||||
T* rcNew(rcAllocHint hint) {
|
{
|
||||||
T* ptr = (T*)rcAlloc(sizeof(T), hint);
|
T* ptr = (T*)rcAlloc(sizeof(T), allocLifetime);
|
||||||
::new(rcNewTag(), (void*)ptr) T();
|
::new(rcNewTag(), (void*)ptr) T();
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroys and frees an object allocated with rcNew.
|
/// Destroys and frees an object allocated with rcNew.
|
||||||
/// @param[in] ptr The object pointer to delete.
|
/// @param[in] ptr The object pointer to delete.
|
||||||
template <typename T>
|
template<typename T>
|
||||||
void rcDelete(T* ptr) {
|
void rcDelete(T* ptr)
|
||||||
if (ptr) {
|
{
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
ptr->~T();
|
ptr->~T();
|
||||||
rcFree((void*)ptr);
|
rcFree((void*)ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
float rcSqrt(float x)
|
float rcSqrt(float x)
|
||||||
{
|
{
|
||||||
return sqrtf(x);
|
return sqrtf(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @class rcContext
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// This class does not provide logging or timer functionality on its
|
|
||||||
/// own. Both must be provided by a concrete implementation
|
|
||||||
/// by overriding the protected member functions. Also, this class does not
|
|
||||||
/// provide an interface for extracting log messages. (Only adding them.)
|
|
||||||
/// So concrete implementations must provide one.
|
|
||||||
///
|
|
||||||
/// If no logging or timers are required, just pass an instance of this
|
|
||||||
/// class through the Recast build process.
|
|
||||||
///
|
|
||||||
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// @code
|
|
||||||
/// // Where ctx is an instance of rcContext and filepath is a char array.
|
|
||||||
/// ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath);
|
|
||||||
/// @endcode
|
|
||||||
void rcContext::log(const rcLogCategory category, const char* format, ...)
|
void rcContext::log(const rcLogCategory category, const char* format, ...)
|
||||||
{
|
{
|
||||||
if (!m_logEnabled)
|
if (!m_logEnabled)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
static const int MSG_SIZE = 512;
|
static const int MSG_SIZE = 512;
|
||||||
char msg[MSG_SIZE];
|
char msg[MSG_SIZE];
|
||||||
va_list ap;
|
va_list argList;
|
||||||
va_start(ap, format);
|
va_start(argList, format);
|
||||||
int len = vsnprintf(msg, MSG_SIZE, format, ap);
|
int len = vsnprintf(msg, MSG_SIZE, format, argList);
|
||||||
if (len >= MSG_SIZE)
|
if (len >= MSG_SIZE)
|
||||||
{
|
{
|
||||||
len = MSG_SIZE-1;
|
len = MSG_SIZE - 1;
|
||||||
msg[MSG_SIZE-1] = '\0';
|
msg[MSG_SIZE - 1] = '\0';
|
||||||
|
|
||||||
|
const char* errorMessage = "Log message was truncated";
|
||||||
|
doLog(RC_LOG_ERROR, errorMessage, (int)strlen(errorMessage));
|
||||||
}
|
}
|
||||||
va_end(ap);
|
va_end(argList);
|
||||||
doLog(category, msg, len);
|
doLog(category, msg, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rcContext::doResetLog()
|
||||||
|
{
|
||||||
|
// Defined out of line to fix the weak v-tables warning
|
||||||
|
}
|
||||||
|
|
||||||
rcHeightfield* rcAllocHeightfield()
|
rcHeightfield* rcAllocHeightfield()
|
||||||
{
|
{
|
||||||
return rcNew<rcHeightfield>(RC_ALLOC_PERM);
|
return rcNew<rcHeightfield>(RC_ALLOC_PERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rcFreeHeightField(rcHeightfield* heightfield)
|
||||||
|
{
|
||||||
|
rcDelete(heightfield);
|
||||||
|
}
|
||||||
|
|
||||||
rcHeightfield::rcHeightfield()
|
rcHeightfield::rcHeightfield()
|
||||||
: width()
|
: width()
|
||||||
, height()
|
, height()
|
||||||
, bmin()
|
, bmin()
|
||||||
, bmax()
|
, bmax()
|
||||||
, cs()
|
, cs()
|
||||||
, ch()
|
, ch()
|
||||||
, spans()
|
, spans()
|
||||||
, pools()
|
, pools()
|
||||||
, freelist()
|
, freelist()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,40 +119,36 @@ rcHeightfield::~rcHeightfield()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rcFreeHeightField(rcHeightfield* hf)
|
|
||||||
{
|
|
||||||
rcDelete(hf);
|
|
||||||
}
|
|
||||||
|
|
||||||
rcCompactHeightfield* rcAllocCompactHeightfield()
|
rcCompactHeightfield* rcAllocCompactHeightfield()
|
||||||
{
|
{
|
||||||
return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);
|
return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
|
void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield)
|
||||||
{
|
{
|
||||||
rcDelete(chf);
|
rcDelete(compactHeightfield);
|
||||||
}
|
}
|
||||||
|
|
||||||
rcCompactHeightfield::rcCompactHeightfield()
|
rcCompactHeightfield::rcCompactHeightfield()
|
||||||
: width(),
|
: width()
|
||||||
height(),
|
, height()
|
||||||
spanCount(),
|
, spanCount()
|
||||||
walkableHeight(),
|
, walkableHeight()
|
||||||
walkableClimb(),
|
, walkableClimb()
|
||||||
borderSize(),
|
, borderSize()
|
||||||
maxDistance(),
|
, maxDistance()
|
||||||
maxRegions(),
|
, maxRegions()
|
||||||
bmin(),
|
, bmin()
|
||||||
bmax(),
|
, bmax()
|
||||||
cs(),
|
, cs()
|
||||||
ch(),
|
, ch()
|
||||||
cells(),
|
, cells()
|
||||||
spans(),
|
, spans()
|
||||||
dist(),
|
, dist()
|
||||||
areas()
|
, areas()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
rcCompactHeightfield::~rcCompactHeightfield()
|
rcCompactHeightfield::~rcCompactHeightfield()
|
||||||
{
|
{
|
||||||
rcFree(cells);
|
rcFree(cells);
|
||||||
@@ -170,13 +161,18 @@ rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
|
|||||||
{
|
{
|
||||||
return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM);
|
return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM);
|
||||||
}
|
}
|
||||||
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset)
|
|
||||||
|
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* layerSet)
|
||||||
{
|
{
|
||||||
rcDelete(lset);
|
rcDelete(layerSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
rcHeightfieldLayerSet::rcHeightfieldLayerSet()
|
rcHeightfieldLayerSet::rcHeightfieldLayerSet()
|
||||||
: layers(), nlayers() {}
|
: layers()
|
||||||
|
, nlayers()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
rcHeightfieldLayerSet::~rcHeightfieldLayerSet()
|
rcHeightfieldLayerSet::~rcHeightfieldLayerSet()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < nlayers; ++i)
|
for (int i = 0; i < nlayers; ++i)
|
||||||
@@ -193,22 +189,26 @@ rcContourSet* rcAllocContourSet()
|
|||||||
{
|
{
|
||||||
return rcNew<rcContourSet>(RC_ALLOC_PERM);
|
return rcNew<rcContourSet>(RC_ALLOC_PERM);
|
||||||
}
|
}
|
||||||
void rcFreeContourSet(rcContourSet* cset)
|
|
||||||
|
void rcFreeContourSet(rcContourSet* contourSet)
|
||||||
{
|
{
|
||||||
rcDelete(cset);
|
rcDelete(contourSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
rcContourSet::rcContourSet()
|
rcContourSet::rcContourSet()
|
||||||
: conts(),
|
: conts()
|
||||||
nconts(),
|
, nconts()
|
||||||
bmin(),
|
, bmin()
|
||||||
bmax(),
|
, bmax()
|
||||||
cs(),
|
, cs()
|
||||||
ch(),
|
, ch()
|
||||||
width(),
|
, width()
|
||||||
height(),
|
, height()
|
||||||
borderSize(),
|
, borderSize()
|
||||||
maxError() {}
|
, maxError()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
rcContourSet::~rcContourSet()
|
rcContourSet::~rcContourSet()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < nconts; ++i)
|
for (int i = 0; i < nconts; ++i)
|
||||||
@@ -219,32 +219,34 @@ rcContourSet::~rcContourSet()
|
|||||||
rcFree(conts);
|
rcFree(conts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
rcPolyMesh* rcAllocPolyMesh()
|
rcPolyMesh* rcAllocPolyMesh()
|
||||||
{
|
{
|
||||||
return rcNew<rcPolyMesh>(RC_ALLOC_PERM);
|
return rcNew<rcPolyMesh>(RC_ALLOC_PERM);
|
||||||
}
|
}
|
||||||
void rcFreePolyMesh(rcPolyMesh* pmesh)
|
|
||||||
|
void rcFreePolyMesh(rcPolyMesh* polyMesh)
|
||||||
{
|
{
|
||||||
rcDelete(pmesh);
|
rcDelete(polyMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
rcPolyMesh::rcPolyMesh()
|
rcPolyMesh::rcPolyMesh()
|
||||||
: verts(),
|
: verts()
|
||||||
polys(),
|
, polys()
|
||||||
regs(),
|
, regs()
|
||||||
flags(),
|
, flags()
|
||||||
areas(),
|
, areas()
|
||||||
nverts(),
|
, nverts()
|
||||||
npolys(),
|
, npolys()
|
||||||
maxpolys(),
|
, maxpolys()
|
||||||
nvp(),
|
, nvp()
|
||||||
bmin(),
|
, bmin()
|
||||||
bmax(),
|
, bmax()
|
||||||
cs(),
|
, cs()
|
||||||
ch(),
|
, ch()
|
||||||
borderSize(),
|
, borderSize()
|
||||||
maxEdgeError() {}
|
, maxEdgeError()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
rcPolyMesh::~rcPolyMesh()
|
rcPolyMesh::~rcPolyMesh()
|
||||||
{
|
{
|
||||||
@@ -257,319 +259,284 @@ rcPolyMesh::~rcPolyMesh()
|
|||||||
|
|
||||||
rcPolyMeshDetail* rcAllocPolyMeshDetail()
|
rcPolyMeshDetail* rcAllocPolyMeshDetail()
|
||||||
{
|
{
|
||||||
rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM);
|
return rcNew<rcPolyMeshDetail>(RC_ALLOC_PERM);
|
||||||
memset(dmesh, 0, sizeof(rcPolyMeshDetail));
|
|
||||||
return dmesh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
|
void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh)
|
||||||
{
|
{
|
||||||
if (!dmesh) return;
|
if (detailMesh == NULL)
|
||||||
rcFree(dmesh->meshes);
|
{
|
||||||
rcFree(dmesh->verts);
|
return;
|
||||||
rcFree(dmesh->tris);
|
}
|
||||||
rcFree(dmesh);
|
rcFree(detailMesh->meshes);
|
||||||
|
rcFree(detailMesh->verts);
|
||||||
|
rcFree(detailMesh->tris);
|
||||||
|
rcFree(detailMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
|
rcPolyMeshDetail::rcPolyMeshDetail()
|
||||||
|
: meshes()
|
||||||
|
, verts()
|
||||||
|
, tris()
|
||||||
|
, nmeshes()
|
||||||
|
, nverts()
|
||||||
|
, ntris()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void rcCalcBounds(const float* verts, int numVerts, float* minBounds, float* maxBounds)
|
||||||
{
|
{
|
||||||
// Calculate bounding box.
|
// Calculate bounding box.
|
||||||
rcVcopy(bmin, verts);
|
rcVcopy(minBounds, verts);
|
||||||
rcVcopy(bmax, verts);
|
rcVcopy(maxBounds, verts);
|
||||||
for (int i = 1; i < nv; ++i)
|
for (int i = 1; i < numVerts; ++i)
|
||||||
{
|
{
|
||||||
const float* v = &verts[i*3];
|
const float* v = &verts[i * 3];
|
||||||
rcVmin(bmin, v);
|
rcVmin(minBounds, v);
|
||||||
rcVmax(bmax, v);
|
rcVmax(maxBounds, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h)
|
void rcCalcGridSize(const float* minBounds, const float* maxBounds, const float cellSize, int* sizeX, int* sizeZ)
|
||||||
{
|
{
|
||||||
*w = (int)((bmax[0] - bmin[0])/cs+0.5f);
|
*sizeX = (int)((maxBounds[0] - minBounds[0]) / cellSize + 0.5f);
|
||||||
*h = (int)((bmax[2] - bmin[2])/cs+0.5f);
|
*sizeZ = (int)((maxBounds[2] - minBounds[2]) / cellSize + 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ,
|
||||||
///
|
const float* minBounds, const float* maxBounds,
|
||||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
float cellSize, float cellHeight)
|
||||||
///
|
|
||||||
/// @see rcAllocHeightfield, rcHeightfield
|
|
||||||
bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
|
|
||||||
const float* bmin, const float* bmax,
|
|
||||||
float cs, float ch)
|
|
||||||
{
|
{
|
||||||
rcIgnoreUnused(ctx);
|
rcIgnoreUnused(context);
|
||||||
|
|
||||||
hf.width = width;
|
heightfield.width = sizeX;
|
||||||
hf.height = height;
|
heightfield.height = sizeZ;
|
||||||
rcVcopy(hf.bmin, bmin);
|
rcVcopy(heightfield.bmin, minBounds);
|
||||||
rcVcopy(hf.bmax, bmax);
|
rcVcopy(heightfield.bmax, maxBounds);
|
||||||
hf.cs = cs;
|
heightfield.cs = cellSize;
|
||||||
hf.ch = ch;
|
heightfield.ch = cellHeight;
|
||||||
hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM);
|
heightfield.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*) * heightfield.width * heightfield.height, RC_ALLOC_PERM);
|
||||||
if (!hf.spans)
|
if (!heightfield.spans)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
|
}
|
||||||
|
memset(heightfield.spans, 0, sizeof(rcSpan*) * heightfield.width * heightfield.height);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
|
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* faceNormal)
|
||||||
{
|
{
|
||||||
float e0[3], e1[3];
|
float e0[3], e1[3];
|
||||||
rcVsub(e0, v1, v0);
|
rcVsub(e0, v1, v0);
|
||||||
rcVsub(e1, v2, v0);
|
rcVsub(e1, v2, v0);
|
||||||
rcVcross(norm, e0, e1);
|
rcVcross(faceNormal, e0, e1);
|
||||||
rcVnormalize(norm);
|
rcVnormalize(faceNormal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
void rcMarkWalkableTriangles(rcContext* context, const float walkableSlopeAngle,
|
||||||
///
|
const float* verts, const int numVerts,
|
||||||
/// Only sets the area id's for the walkable triangles. Does not alter the
|
const int* tris, const int numTris,
|
||||||
/// area id's for unwalkable triangles.
|
unsigned char* triAreaIDs)
|
||||||
///
|
|
||||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
|
||||||
///
|
|
||||||
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
|
|
||||||
void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
|
|
||||||
const float* verts, int nv,
|
|
||||||
const int* tris, int nt,
|
|
||||||
unsigned char* areas)
|
|
||||||
{
|
{
|
||||||
rcIgnoreUnused(ctx);
|
rcIgnoreUnused(context);
|
||||||
rcIgnoreUnused(nv);
|
rcIgnoreUnused(numVerts);
|
||||||
|
|
||||||
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
|
const float walkableThr = cosf(walkableSlopeAngle / 180.0f * RC_PI);
|
||||||
|
|
||||||
float norm[3];
|
float norm[3];
|
||||||
|
|
||||||
for (int i = 0; i < nt; ++i)
|
for (int i = 0; i < numTris; ++i)
|
||||||
{
|
{
|
||||||
const int* tri = &tris[i*3];
|
const int* tri = &tris[i * 3];
|
||||||
calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
|
calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], norm);
|
||||||
// Check if the face is walkable.
|
// Check if the face is walkable.
|
||||||
if (norm[1] > walkableThr)
|
if (norm[1] > walkableThr)
|
||||||
areas[i] = RC_WALKABLE_AREA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// Only sets the area id's for the unwalkable triangles. Does not alter the
|
|
||||||
/// area id's for walkable triangles.
|
|
||||||
///
|
|
||||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
|
||||||
///
|
|
||||||
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
|
|
||||||
void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
|
|
||||||
const float* verts, int /*nv*/,
|
|
||||||
const int* tris, int nt,
|
|
||||||
unsigned char* areas)
|
|
||||||
{
|
|
||||||
rcIgnoreUnused(ctx);
|
|
||||||
|
|
||||||
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
|
|
||||||
|
|
||||||
float norm[3];
|
|
||||||
|
|
||||||
for (int i = 0; i < nt; ++i)
|
|
||||||
{
|
|
||||||
const int* tri = &tris[i*3];
|
|
||||||
calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
|
|
||||||
// Check if the face is walkable.
|
|
||||||
if (norm[1] <= walkableThr)
|
|
||||||
areas[i] = RC_NULL_AREA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf)
|
|
||||||
{
|
|
||||||
rcIgnoreUnused(ctx);
|
|
||||||
|
|
||||||
const int w = hf.width;
|
|
||||||
const int h = hf.height;
|
|
||||||
int spanCount = 0;
|
|
||||||
for (int y = 0; y < h; ++y)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < w; ++x)
|
|
||||||
{
|
{
|
||||||
for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next)
|
triAreaIDs[i] = RC_WALKABLE_AREA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rcClearUnwalkableTriangles(rcContext* context, const float walkableSlopeAngle,
|
||||||
|
const float* verts, int numVerts,
|
||||||
|
const int* tris, int numTris,
|
||||||
|
unsigned char* triAreaIDs)
|
||||||
|
{
|
||||||
|
rcIgnoreUnused(context);
|
||||||
|
rcIgnoreUnused(numVerts);
|
||||||
|
|
||||||
|
// The minimum Y value for a face normal of a triangle with a walkable slope.
|
||||||
|
const float walkableLimitY = cosf(walkableSlopeAngle / 180.0f * RC_PI);
|
||||||
|
|
||||||
|
float faceNormal[3];
|
||||||
|
for (int i = 0; i < numTris; ++i)
|
||||||
|
{
|
||||||
|
const int* tri = &tris[i * 3];
|
||||||
|
calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], faceNormal);
|
||||||
|
// Check if the face is walkable.
|
||||||
|
if (faceNormal[1] <= walkableLimitY)
|
||||||
|
{
|
||||||
|
triAreaIDs[i] = RC_NULL_AREA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rcGetHeightFieldSpanCount(rcContext* context, const rcHeightfield& heightfield)
|
||||||
|
{
|
||||||
|
rcIgnoreUnused(context);
|
||||||
|
|
||||||
|
const int numCols = heightfield.width * heightfield.height;
|
||||||
|
int spanCount = 0;
|
||||||
|
for (int columnIndex = 0; columnIndex < numCols; ++columnIndex)
|
||||||
|
{
|
||||||
|
for (rcSpan* span = heightfield.spans[columnIndex]; span != NULL; span = span->next)
|
||||||
|
{
|
||||||
|
if (span->area != RC_NULL_AREA)
|
||||||
{
|
{
|
||||||
if (s->area != RC_NULL_AREA)
|
spanCount++;
|
||||||
spanCount++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return spanCount;
|
return spanCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
bool rcBuildCompactHeightfield(rcContext* context, const int walkableHeight, const int walkableClimb,
|
||||||
///
|
const rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield)
|
||||||
/// This is just the beginning of the process of fully building a compact heightfield.
|
|
||||||
/// Various filters may be applied, then the distance field and regions built.
|
|
||||||
/// E.g: #rcBuildDistanceField and #rcBuildRegions
|
|
||||||
///
|
|
||||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
|
||||||
///
|
|
||||||
/// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig
|
|
||||||
bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
|
||||||
rcHeightfield& hf, rcCompactHeightfield& chf)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context);
|
||||||
|
|
||||||
rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
rcScopedTimer timer(context, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||||
|
|
||||||
const int w = hf.width;
|
const int xSize = heightfield.width;
|
||||||
const int h = hf.height;
|
const int zSize = heightfield.height;
|
||||||
const int spanCount = rcGetHeightFieldSpanCount(ctx, hf);
|
const int spanCount = rcGetHeightFieldSpanCount(context, heightfield);
|
||||||
|
|
||||||
// Fill in header.
|
// Fill in header.
|
||||||
chf.width = w;
|
compactHeightfield.width = xSize;
|
||||||
chf.height = h;
|
compactHeightfield.height = zSize;
|
||||||
chf.spanCount = spanCount;
|
compactHeightfield.spanCount = spanCount;
|
||||||
chf.walkableHeight = walkableHeight;
|
compactHeightfield.walkableHeight = walkableHeight;
|
||||||
chf.walkableClimb = walkableClimb;
|
compactHeightfield.walkableClimb = walkableClimb;
|
||||||
chf.maxRegions = 0;
|
compactHeightfield.maxRegions = 0;
|
||||||
rcVcopy(chf.bmin, hf.bmin);
|
rcVcopy(compactHeightfield.bmin, heightfield.bmin);
|
||||||
rcVcopy(chf.bmax, hf.bmax);
|
rcVcopy(compactHeightfield.bmax, heightfield.bmax);
|
||||||
chf.bmax[1] += walkableHeight*hf.ch;
|
compactHeightfield.bmax[1] += walkableHeight * heightfield.ch;
|
||||||
chf.cs = hf.cs;
|
compactHeightfield.cs = heightfield.cs;
|
||||||
chf.ch = hf.ch;
|
compactHeightfield.ch = heightfield.ch;
|
||||||
chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM);
|
compactHeightfield.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell) * xSize * zSize, RC_ALLOC_PERM);
|
||||||
if (!chf.cells)
|
if (!compactHeightfield.cells)
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
|
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", xSize * zSize);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
|
memset(compactHeightfield.cells, 0, sizeof(rcCompactCell) * xSize * zSize);
|
||||||
chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM);
|
compactHeightfield.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan) * spanCount, RC_ALLOC_PERM);
|
||||||
if (!chf.spans)
|
if (!compactHeightfield.spans)
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
|
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount);
|
memset(compactHeightfield.spans, 0, sizeof(rcCompactSpan) * spanCount);
|
||||||
chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM);
|
compactHeightfield.areas = (unsigned char*)rcAlloc(sizeof(unsigned char) * spanCount, RC_ALLOC_PERM);
|
||||||
if (!chf.areas)
|
if (!compactHeightfield.areas)
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
|
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount);
|
memset(compactHeightfield.areas, RC_NULL_AREA, sizeof(unsigned char) * spanCount);
|
||||||
|
|
||||||
const int MAX_HEIGHT = 0xffff;
|
const int MAX_HEIGHT = 0xffff;
|
||||||
|
|
||||||
// Fill in cells and spans.
|
// Fill in cells and spans.
|
||||||
int idx = 0;
|
int currentCellIndex = 0;
|
||||||
for (int y = 0; y < h; ++y)
|
const int numColumns = xSize * zSize;
|
||||||
|
for (int columnIndex = 0; columnIndex < numColumns; ++columnIndex)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
const rcSpan* span = heightfield.spans[columnIndex];
|
||||||
|
|
||||||
|
// If there are no spans at this cell, just leave the data to index=0, count=0.
|
||||||
|
if (span == NULL)
|
||||||
{
|
{
|
||||||
const rcSpan* s = hf.spans[x + y*w];
|
continue;
|
||||||
// If there are no spans at this cell, just leave the data to index=0, count=0.
|
}
|
||||||
if (!s) continue;
|
|
||||||
rcCompactCell& c = chf.cells[x+y*w];
|
rcCompactCell& cell = compactHeightfield.cells[columnIndex];
|
||||||
c.index = idx;
|
cell.index = currentCellIndex;
|
||||||
c.count = 0;
|
cell.count = 0;
|
||||||
while (s)
|
|
||||||
|
for (; span != NULL; span = span->next)
|
||||||
|
{
|
||||||
|
if (span->area != RC_NULL_AREA)
|
||||||
{
|
{
|
||||||
if (s->area != RC_NULL_AREA)
|
const int bot = (int)span->smax;
|
||||||
{
|
const int top = span->next ? (int)span->next->smin : MAX_HEIGHT;
|
||||||
const int bot = (int)s->smax;
|
compactHeightfield.spans[currentCellIndex].y = (unsigned short)rcClamp(bot, 0, 0xffff);
|
||||||
const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
|
compactHeightfield.spans[currentCellIndex].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
|
||||||
chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff);
|
compactHeightfield.areas[currentCellIndex] = span->area;
|
||||||
chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
|
currentCellIndex++;
|
||||||
chf.areas[idx] = s->area;
|
cell.count++;
|
||||||
idx++;
|
|
||||||
c.count++;
|
|
||||||
}
|
|
||||||
s = s->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find neighbour connections.
|
// Find neighbour connections.
|
||||||
const int MAX_LAYERS = RC_NOT_CONNECTED-1;
|
const int MAX_LAYERS = RC_NOT_CONNECTED - 1;
|
||||||
int tooHighNeighbour = 0;
|
int maxLayerIndex = 0;
|
||||||
for (int y = 0; y < h; ++y)
|
const int zStride = xSize; // for readability
|
||||||
|
for (int z = 0; z < zSize; ++z)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
for (int x = 0; x < xSize; ++x)
|
||||||
{
|
{
|
||||||
const rcCompactCell& c = chf.cells[x+y*w];
|
const rcCompactCell& cell = compactHeightfield.cells[x + z * zStride];
|
||||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
for (int i = (int)cell.index, ni = (int)(cell.index + cell.count); i < ni; ++i)
|
||||||
{
|
{
|
||||||
rcCompactSpan& s = chf.spans[i];
|
rcCompactSpan& span = compactHeightfield.spans[i];
|
||||||
|
|
||||||
for (int dir = 0; dir < 4; ++dir)
|
for (int dir = 0; dir < 4; ++dir)
|
||||||
{
|
{
|
||||||
rcSetCon(s, dir, RC_NOT_CONNECTED);
|
rcSetCon(span, dir, RC_NOT_CONNECTED);
|
||||||
const int nx = x + rcGetDirOffsetX(dir);
|
const int neighborX = x + rcGetDirOffsetX(dir);
|
||||||
const int ny = y + rcGetDirOffsetY(dir);
|
const int neighborZ = z + rcGetDirOffsetY(dir);
|
||||||
// First check that the neighbour cell is in bounds.
|
// First check that the neighbour cell is in bounds.
|
||||||
if (nx < 0 || ny < 0 || nx >= w || ny >= h)
|
if (neighborX < 0 || neighborZ < 0 || neighborX >= xSize || neighborZ >= zSize)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate over all neighbour spans and check if any of the is
|
// Iterate over all neighbour spans and check if any of the is
|
||||||
// accessible from current cell.
|
// accessible from current cell.
|
||||||
const rcCompactCell& nc = chf.cells[nx+ny*w];
|
const rcCompactCell& neighborCell = compactHeightfield.cells[neighborX + neighborZ * zStride];
|
||||||
for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k)
|
for (int k = (int)neighborCell.index, nk = (int)(neighborCell.index + neighborCell.count); k < nk; ++k)
|
||||||
{
|
{
|
||||||
const rcCompactSpan& ns = chf.spans[k];
|
const rcCompactSpan& neighborSpan = compactHeightfield.spans[k];
|
||||||
const int bot = rcMax(s.y, ns.y);
|
const int bot = rcMax(span.y, neighborSpan.y);
|
||||||
const int top = rcMin(s.y+s.h, ns.y+ns.h);
|
const int top = rcMin(span.y + span.h, neighborSpan.y + neighborSpan.h);
|
||||||
|
|
||||||
// Check that the gap between the spans is walkable,
|
// Check that the gap between the spans is walkable,
|
||||||
// and that the climb height between the gaps is not too high.
|
// and that the climb height between the gaps is not too high.
|
||||||
if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
|
if ((top - bot) >= walkableHeight && rcAbs((int)neighborSpan.y - (int)span.y) <= walkableClimb)
|
||||||
{
|
{
|
||||||
// Mark direction as walkable.
|
// Mark direction as walkable.
|
||||||
const int lidx = k - (int)nc.index;
|
const int layerIndex = k - (int)neighborCell.index;
|
||||||
if (lidx < 0 || lidx > MAX_LAYERS)
|
if (layerIndex < 0 || layerIndex > MAX_LAYERS)
|
||||||
{
|
{
|
||||||
tooHighNeighbour = rcMax(tooHighNeighbour, lidx);
|
maxLayerIndex = rcMax(maxLayerIndex, layerIndex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rcSetCon(s, dir, lidx);
|
rcSetCon(span, dir, layerIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tooHighNeighbour > MAX_LAYERS)
|
if (maxLayerIndex > MAX_LAYERS)
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
|
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
|
||||||
tooHighNeighbour, MAX_LAYERS);
|
maxLayerIndex, MAX_LAYERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
static int getHeightfieldMemoryUsage(const rcHeightfield& hf)
|
|
||||||
{
|
|
||||||
int size = 0;
|
|
||||||
size += sizeof(hf);
|
|
||||||
size += hf.width * hf.height * sizeof(rcSpan*);
|
|
||||||
|
|
||||||
rcSpanPool* pool = hf.pools;
|
|
||||||
while (pool)
|
|
||||||
{
|
|
||||||
size += (sizeof(rcSpanPool) - sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL;
|
|
||||||
pool = pool->next;
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
|
|
||||||
{
|
|
||||||
int size = 0;
|
|
||||||
size += sizeof(rcCompactHeightfield);
|
|
||||||
size += sizeof(rcCompactSpan) * chf.spanCount;
|
|
||||||
size += sizeof(rcCompactCell) * chf.width * chf.height;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
972
Source/ThirdParty/recastnavigation/Recast.h
vendored
972
Source/ThirdParty/recastnavigation/Recast.h
vendored
File diff suppressed because it is too large
Load Diff
@@ -16,12 +16,9 @@
|
|||||||
// 3. This notice may not be removed or altered from any source distribution.
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "RecastAlloc.h"
|
#include "RecastAlloc.h"
|
||||||
#include "RecastAssert.h"
|
|
||||||
|
|
||||||
static void *rcAllocDefault(size_t size, rcAllocHint)
|
static void* rcAllocDefault(size_t size, rcAllocHint)
|
||||||
{
|
{
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
}
|
}
|
||||||
@@ -34,27 +31,21 @@ static void rcFreeDefault(void *ptr)
|
|||||||
static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
|
static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
|
||||||
static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
|
static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
|
||||||
|
|
||||||
/// @see rcAlloc, rcFree
|
void rcAllocSetCustom(rcAllocFunc* allocFunc, rcFreeFunc* freeFunc)
|
||||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
|
|
||||||
{
|
{
|
||||||
sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
|
sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
|
||||||
sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
|
sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @see rcAllocSetCustom
|
|
||||||
void* rcAlloc(size_t size, rcAllocHint hint)
|
void* rcAlloc(size_t size, rcAllocHint hint)
|
||||||
{
|
{
|
||||||
return sRecastAllocFunc(size, hint);
|
return sRecastAllocFunc(size, hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// @warning This function leaves the value of @p ptr unchanged. So it still
|
|
||||||
/// points to the same (now invalid) location, and not to null.
|
|
||||||
///
|
|
||||||
/// @see rcAllocSetCustom
|
|
||||||
void rcFree(void* ptr)
|
void rcFree(void* ptr)
|
||||||
{
|
{
|
||||||
if (ptr)
|
if (ptr != NULL)
|
||||||
|
{
|
||||||
sRecastFreeFunc(ptr);
|
sRecastFreeFunc(ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
Source/ThirdParty/recastnavigation/RecastAlloc.h
vendored
35
Source/ThirdParty/recastnavigation/RecastAlloc.h
vendored
@@ -19,11 +19,11 @@
|
|||||||
#ifndef RECASTALLOC_H
|
#ifndef RECASTALLOC_H
|
||||||
#define RECASTALLOC_H
|
#define RECASTALLOC_H
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "RecastAssert.h"
|
#include "RecastAssert.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/// Provides hint values to the memory allocator on how long the
|
/// Provides hint values to the memory allocator on how long the
|
||||||
/// memory is expected to be used.
|
/// memory is expected to be used.
|
||||||
enum rcAllocHint
|
enum rcAllocHint
|
||||||
@@ -47,18 +47,27 @@ typedef void (rcFreeFunc)(void* ptr);
|
|||||||
/// Sets the base custom allocation functions to be used by Recast.
|
/// Sets the base custom allocation functions to be used by Recast.
|
||||||
/// @param[in] allocFunc The memory allocation function to be used by #rcAlloc
|
/// @param[in] allocFunc The memory allocation function to be used by #rcAlloc
|
||||||
/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree
|
/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree
|
||||||
|
///
|
||||||
|
/// @see rcAlloc, rcFree
|
||||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
|
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
|
||||||
|
|
||||||
/// Allocates a memory block.
|
/// Allocates a memory block.
|
||||||
/// @param[in] size The size, in bytes of memory, to allocate.
|
///
|
||||||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
/// @param[in] size The size, in bytes of memory, to allocate.
|
||||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
||||||
/// @see rcFree
|
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||||
|
///
|
||||||
|
/// @see rcFree, rcAllocSetCustom
|
||||||
void* rcAlloc(size_t size, rcAllocHint hint);
|
void* rcAlloc(size_t size, rcAllocHint hint);
|
||||||
|
|
||||||
/// Deallocates a memory block.
|
/// Deallocates a memory block. If @p ptr is NULL, this does nothing.
|
||||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
|
///
|
||||||
/// @see rcAlloc
|
/// @warning This function leaves the value of @p ptr unchanged. So it still
|
||||||
|
/// points to the same (now invalid) location, and not to null.
|
||||||
|
///
|
||||||
|
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
|
||||||
|
///
|
||||||
|
/// @see rcAlloc, rcAllocSetCustom
|
||||||
void rcFree(void* ptr);
|
void rcFree(void* ptr);
|
||||||
|
|
||||||
/// An implementation of operator new usable for placement new. The default one is part of STL (which we don't use).
|
/// An implementation of operator new usable for placement new. The default one is part of STL (which we don't use).
|
||||||
@@ -112,7 +121,7 @@ class rcVectorBase {
|
|||||||
typedef rcSizeType size_type;
|
typedef rcSizeType size_type;
|
||||||
typedef T value_type;
|
typedef T value_type;
|
||||||
|
|
||||||
rcVectorBase() : m_size(0), m_cap(0), m_data(0) {};
|
rcVectorBase() : m_size(0), m_cap(0), m_data(0) {}
|
||||||
rcVectorBase(const rcVectorBase<T, H>& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); }
|
rcVectorBase(const rcVectorBase<T, H>& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); }
|
||||||
explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); }
|
explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); }
|
||||||
rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); }
|
rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); }
|
||||||
@@ -142,8 +151,8 @@ class rcVectorBase {
|
|||||||
|
|
||||||
const T& front() const { rcAssert(m_size); return m_data[0]; }
|
const T& front() const { rcAssert(m_size); return m_data[0]; }
|
||||||
T& front() { rcAssert(m_size); return m_data[0]; }
|
T& front() { rcAssert(m_size); return m_data[0]; }
|
||||||
const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; };
|
const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; }
|
||||||
T& back() { rcAssert(m_size); return m_data[m_size - 1]; };
|
T& back() { rcAssert(m_size); return m_data[m_size - 1]; }
|
||||||
const T* data() const { return m_data; }
|
const T* data() const { return m_data; }
|
||||||
T* data() { return m_data; }
|
T* data() { return m_data; }
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
static rcAssertFailFunc* sRecastAssertFailFunc = 0;
|
static rcAssertFailFunc* sRecastAssertFailFunc = 0;
|
||||||
|
|
||||||
void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc)
|
void rcAssertFailSetCustom(rcAssertFailFunc* assertFailFunc)
|
||||||
{
|
{
|
||||||
sRecastAssertFailFunc = assertFailFunc;
|
sRecastAssertFailFunc = assertFailFunc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,10 @@
|
|||||||
#ifndef RECASTASSERT_H
|
#ifndef RECASTASSERT_H
|
||||||
#define RECASTASSERT_H
|
#define RECASTASSERT_H
|
||||||
|
|
||||||
// Note: This header file's only purpose is to include define assert.
|
|
||||||
// Feel free to change the file and include your own implementation instead.
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
|
|
||||||
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
// From https://web.archive.org/web/20210117002833/http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
||||||
# define rcAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
|
# define rcAssert(x) do { (void)sizeof(x); } while ((void)(__LINE__==-1), false)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
@@ -38,7 +35,7 @@ typedef void (rcAssertFailFunc)(const char* expression, const char* file, int li
|
|||||||
|
|
||||||
/// Sets the base custom assertion failure function to be used by Recast.
|
/// Sets the base custom assertion failure function to be used by Recast.
|
||||||
/// @param[in] assertFailFunc The function to be used in case of failure of #dtAssert
|
/// @param[in] assertFailFunc The function to be used in case of failure of #dtAssert
|
||||||
void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc);
|
void rcAssertFailSetCustom(rcAssertFailFunc* assertFailFunc);
|
||||||
|
|
||||||
/// Gets the base custom assertion failure function to be used by Recast.
|
/// Gets the base custom assertion failure function to be used by Recast.
|
||||||
rcAssertFailFunc* rcAssertFailGetCustom();
|
rcAssertFailFunc* rcAssertFailGetCustom();
|
||||||
@@ -47,8 +44,8 @@ rcAssertFailFunc* rcAssertFailGetCustom();
|
|||||||
# define rcAssert(expression) \
|
# define rcAssert(expression) \
|
||||||
{ \
|
{ \
|
||||||
rcAssertFailFunc* failFunc = rcAssertFailGetCustom(); \
|
rcAssertFailFunc* failFunc = rcAssertFailGetCustom(); \
|
||||||
if(failFunc == NULL) { assert(expression); } \
|
if (failFunc == NULL) { assert(expression); } \
|
||||||
else if(!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
|
else if (!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
// 3. This notice may not be removed or altered from any source distribution.
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
//
|
//
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -102,7 +101,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void walkContour(int x, int y, int i,
|
static void walkContour(int x, int y, int i,
|
||||||
rcCompactHeightfield& chf,
|
const rcCompactHeightfield& chf,
|
||||||
unsigned char* flags, rcIntArray& points)
|
unsigned char* flags, rcIntArray& points)
|
||||||
{
|
{
|
||||||
// Choose the first non-connected edge
|
// Choose the first non-connected edge
|
||||||
@@ -542,7 +541,7 @@ static bool vequal(const int* a, const int* b)
|
|||||||
return a[0] == b[0] && a[2] == b[2];
|
return a[0] == b[0] && a[2] == b[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool intersectSegCountour(const int* d0, const int* d1, int i, int n, const int* verts)
|
static bool intersectSegContour(const int* d0, const int* d1, int i, int n, const int* verts)
|
||||||
{
|
{
|
||||||
// For each edge (k,k+1) of P
|
// For each edge (k,k+1) of P
|
||||||
for (int k = 0; k < n; k++)
|
for (int k = 0; k < n; k++)
|
||||||
@@ -778,9 +777,9 @@ static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
|
|||||||
for (int j = 0; j < ndiags; j++)
|
for (int j = 0; j < ndiags; j++)
|
||||||
{
|
{
|
||||||
const int* pt = &outline->verts[diags[j].vert*4];
|
const int* pt = &outline->verts[diags[j].vert*4];
|
||||||
bool intersect = intersectSegCountour(pt, corner, diags[i].vert, outline->nverts, outline->verts);
|
bool intersect = intersectSegContour(pt, corner, diags[i].vert, outline->nverts, outline->verts);
|
||||||
for (int k = i; k < region.nholes && !intersect; k++)
|
for (int k = i; k < region.nholes && !intersect; k++)
|
||||||
intersect |= intersectSegCountour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts);
|
intersect |= intersectSegContour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts);
|
||||||
if (!intersect)
|
if (!intersect)
|
||||||
{
|
{
|
||||||
index = diags[j].vert;
|
index = diags[j].vert;
|
||||||
@@ -821,7 +820,7 @@ static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
|
|||||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||||
///
|
///
|
||||||
/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
|
/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
|
||||||
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
bool rcBuildContours(rcContext* ctx, const rcCompactHeightfield& chf,
|
||||||
const float maxError, const int maxEdgeLen,
|
const float maxError, const int maxEdgeLen,
|
||||||
rcContourSet& cset, const int buildFlags)
|
rcContourSet& cset, const int buildFlags)
|
||||||
{
|
{
|
||||||
|
|||||||
178
Source/ThirdParty/recastnavigation/RecastFilter.cpp
vendored
178
Source/ThirdParty/recastnavigation/RecastFilter.cpp
vendored
@@ -16,135 +16,121 @@
|
|||||||
// 3. This notice may not be removed or altered from any source distribution.
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
//
|
//
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "Recast.h"
|
#include "Recast.h"
|
||||||
#include "RecastAssert.h"
|
#include "RecastAssert.h"
|
||||||
|
|
||||||
/// @par
|
#include <stdlib.h>
|
||||||
///
|
|
||||||
/// Allows the formation of walkable regions that will flow over low lying
|
void rcFilterLowHangingWalkableObstacles(rcContext* context, const int walkableClimb, rcHeightfield& heightfield)
|
||||||
/// objects such as curbs, and up structures such as stairways.
|
|
||||||
///
|
|
||||||
/// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
|
|
||||||
///
|
|
||||||
/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call
|
|
||||||
/// #rcFilterLedgeSpans after calling this filter.
|
|
||||||
///
|
|
||||||
/// @see rcHeightfield, rcConfig
|
|
||||||
void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context);
|
||||||
|
|
||||||
rcScopedTimer timer(ctx, RC_TIMER_FILTER_LOW_OBSTACLES);
|
rcScopedTimer timer(context, RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||||
|
|
||||||
const int w = solid.width;
|
const int xSize = heightfield.width;
|
||||||
const int h = solid.height;
|
const int zSize = heightfield.height;
|
||||||
|
|
||||||
for (int y = 0; y < h; ++y)
|
for (int z = 0; z < zSize; ++z)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
for (int x = 0; x < xSize; ++x)
|
||||||
{
|
{
|
||||||
rcSpan* ps = 0;
|
rcSpan* previousSpan = NULL;
|
||||||
bool previousWalkable = false;
|
bool previousWasWalkable = false;
|
||||||
unsigned char previousArea = RC_NULL_AREA;
|
unsigned char previousArea = RC_NULL_AREA;
|
||||||
|
|
||||||
for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next)
|
for (rcSpan* span = heightfield.spans[x + z * xSize]; span != NULL; previousSpan = span, span = span->next)
|
||||||
{
|
{
|
||||||
const bool walkable = s->area != RC_NULL_AREA;
|
const bool walkable = span->area != RC_NULL_AREA;
|
||||||
// If current span is not walkable, but there is walkable
|
// If current span is not walkable, but there is walkable
|
||||||
// span just below it, mark the span above it walkable too.
|
// span just below it, mark the span above it walkable too.
|
||||||
if (!walkable && previousWalkable)
|
if (!walkable && previousWasWalkable)
|
||||||
{
|
{
|
||||||
if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb)
|
if (rcAbs((int)span->smax - (int)previousSpan->smax) <= walkableClimb)
|
||||||
s->area = previousArea;
|
{
|
||||||
|
span->area = previousArea;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Copy walkable flag so that it cannot propagate
|
// Copy walkable flag so that it cannot propagate
|
||||||
// past multiple non-walkable objects.
|
// past multiple non-walkable objects.
|
||||||
previousWalkable = walkable;
|
previousWasWalkable = walkable;
|
||||||
previousArea = s->area;
|
previousArea = span->area;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
void rcFilterLedgeSpans(rcContext* context, const int walkableHeight, const int walkableClimb,
|
||||||
///
|
rcHeightfield& heightfield)
|
||||||
/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
|
|
||||||
/// from the current span's maximum.
|
|
||||||
/// This method removes the impact of the overestimation of conservative voxelization
|
|
||||||
/// so the resulting mesh will not have regions hanging in the air over ledges.
|
|
||||||
///
|
|
||||||
/// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
|
|
||||||
///
|
|
||||||
/// @see rcHeightfield, rcConfig
|
|
||||||
void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
|
||||||
rcHeightfield& solid)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context);
|
||||||
|
|
||||||
rcScopedTimer timer(ctx, RC_TIMER_FILTER_BORDER);
|
rcScopedTimer timer(context, RC_TIMER_FILTER_BORDER);
|
||||||
|
|
||||||
const int w = solid.width;
|
const int xSize = heightfield.width;
|
||||||
const int h = solid.height;
|
const int zSize = heightfield.height;
|
||||||
const int MAX_HEIGHT = 0xffff;
|
const int MAX_HEIGHT = 0xffff; // TODO (graham): Move this to a more visible constant and update usages.
|
||||||
|
|
||||||
// Mark border spans.
|
// Mark border spans.
|
||||||
for (int y = 0; y < h; ++y)
|
for (int z = 0; z < zSize; ++z)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
for (int x = 0; x < xSize; ++x)
|
||||||
{
|
{
|
||||||
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
|
for (rcSpan* span = heightfield.spans[x + z * xSize]; span; span = span->next)
|
||||||
{
|
{
|
||||||
// Skip non walkable spans.
|
// Skip non walkable spans.
|
||||||
if (s->area == RC_NULL_AREA)
|
if (span->area == RC_NULL_AREA)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const int bot = (int)(s->smax);
|
const int bot = (int)(span->smax);
|
||||||
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
|
const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT;
|
||||||
|
|
||||||
// Find neighbours minimum height.
|
// Find neighbours minimum height.
|
||||||
int minh = MAX_HEIGHT;
|
int minNeighborHeight = MAX_HEIGHT;
|
||||||
|
|
||||||
// Min and max height of accessible neighbours.
|
// Min and max height of accessible neighbours.
|
||||||
int asmin = s->smax;
|
int accessibleNeighborMinHeight = span->smax;
|
||||||
int asmax = s->smax;
|
int accessibleNeighborMaxHeight = span->smax;
|
||||||
|
|
||||||
for (int dir = 0; dir < 4; ++dir)
|
for (int direction = 0; direction < 4; ++direction)
|
||||||
{
|
{
|
||||||
int dx = x + rcGetDirOffsetX(dir);
|
int dx = x + rcGetDirOffsetX(direction);
|
||||||
int dy = y + rcGetDirOffsetY(dir);
|
int dy = z + rcGetDirOffsetY(direction);
|
||||||
// Skip neighbours which are out of bounds.
|
// Skip neighbours which are out of bounds.
|
||||||
if (dx < 0 || dy < 0 || dx >= w || dy >= h)
|
if (dx < 0 || dy < 0 || dx >= xSize || dy >= zSize)
|
||||||
{
|
{
|
||||||
minh = rcMin(minh, -walkableClimb - bot);
|
minNeighborHeight = rcMin(minNeighborHeight, -walkableClimb - bot);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// From minus infinity to the first span.
|
// From minus infinity to the first span.
|
||||||
rcSpan* ns = solid.spans[dx + dy*w];
|
const rcSpan* neighborSpan = heightfield.spans[dx + dy * xSize];
|
||||||
int nbot = -walkableClimb;
|
int neighborBot = -walkableClimb;
|
||||||
int ntop = ns ? (int)ns->smin : MAX_HEIGHT;
|
int neighborTop = neighborSpan ? (int)neighborSpan->smin : MAX_HEIGHT;
|
||||||
// Skip neightbour if the gap between the spans is too small.
|
|
||||||
if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
|
// Skip neighbour if the gap between the spans is too small.
|
||||||
minh = rcMin(minh, nbot - bot);
|
if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight)
|
||||||
|
{
|
||||||
|
minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot);
|
||||||
|
}
|
||||||
|
|
||||||
// Rest of the spans.
|
// Rest of the spans.
|
||||||
for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
|
for (neighborSpan = heightfield.spans[dx + dy * xSize]; neighborSpan; neighborSpan = neighborSpan->next)
|
||||||
{
|
{
|
||||||
nbot = (int)ns->smax;
|
neighborBot = (int)neighborSpan->smax;
|
||||||
ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT;
|
neighborTop = neighborSpan->next ? (int)neighborSpan->next->smin : MAX_HEIGHT;
|
||||||
// Skip neightbour if the gap between the spans is too small.
|
|
||||||
if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
|
// Skip neighbour if the gap between the spans is too small.
|
||||||
|
if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight)
|
||||||
{
|
{
|
||||||
minh = rcMin(minh, nbot - bot);
|
minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot);
|
||||||
|
|
||||||
// Find min/max accessible neighbour height.
|
// Find min/max accessible neighbour height.
|
||||||
if (rcAbs(nbot - bot) <= walkableClimb)
|
if (rcAbs(neighborBot - bot) <= walkableClimb)
|
||||||
{
|
{
|
||||||
if (nbot < asmin) asmin = nbot;
|
if (neighborBot < accessibleNeighborMinHeight) accessibleNeighborMinHeight = neighborBot;
|
||||||
if (nbot > asmax) asmax = nbot;
|
if (neighborBot > accessibleNeighborMaxHeight) accessibleNeighborMaxHeight = neighborBot;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -153,49 +139,45 @@ void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walk
|
|||||||
|
|
||||||
// The current span is close to a ledge if the drop to any
|
// The current span is close to a ledge if the drop to any
|
||||||
// neighbour span is less than the walkableClimb.
|
// neighbour span is less than the walkableClimb.
|
||||||
if (minh < -walkableClimb)
|
if (minNeighborHeight < -walkableClimb)
|
||||||
{
|
{
|
||||||
s->area = RC_NULL_AREA;
|
span->area = RC_NULL_AREA;
|
||||||
}
|
}
|
||||||
// If the difference between all neighbours is too large,
|
// If the difference between all neighbours is too large,
|
||||||
// we are at steep slope, mark the span as ledge.
|
// we are at steep slope, mark the span as ledge.
|
||||||
else if ((asmax - asmin) > walkableClimb)
|
else if ((accessibleNeighborMaxHeight - accessibleNeighborMinHeight) > walkableClimb)
|
||||||
{
|
{
|
||||||
s->area = RC_NULL_AREA;
|
span->area = RC_NULL_AREA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
void rcFilterWalkableLowHeightSpans(rcContext* context, const int walkableHeight, rcHeightfield& heightfield)
|
||||||
///
|
|
||||||
/// For this filter, the clearance above the span is the distance from the span's
|
|
||||||
/// maximum to the next higher span's minimum. (Same grid column.)
|
|
||||||
///
|
|
||||||
/// @see rcHeightfield, rcConfig
|
|
||||||
void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context);
|
||||||
|
|
||||||
rcScopedTimer timer(ctx, RC_TIMER_FILTER_WALKABLE);
|
rcScopedTimer timer(context, RC_TIMER_FILTER_WALKABLE);
|
||||||
|
|
||||||
const int w = solid.width;
|
const int xSize = heightfield.width;
|
||||||
const int h = solid.height;
|
const int zSize = heightfield.height;
|
||||||
const int MAX_HEIGHT = 0xffff;
|
const int MAX_HEIGHT = 0xffff;
|
||||||
|
|
||||||
// Remove walkable flag from spans which do not have enough
|
// Remove walkable flag from spans which do not have enough
|
||||||
// space above them for the agent to stand there.
|
// space above them for the agent to stand there.
|
||||||
for (int y = 0; y < h; ++y)
|
for (int z = 0; z < zSize; ++z)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
for (int x = 0; x < xSize; ++x)
|
||||||
{
|
{
|
||||||
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
|
for (rcSpan* span = heightfield.spans[x + z*xSize]; span; span = span->next)
|
||||||
{
|
{
|
||||||
const int bot = (int)(s->smax);
|
const int bot = (int)(span->smax);
|
||||||
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
|
const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT;
|
||||||
if ((top - bot) <= walkableHeight)
|
if ((top - bot) < walkableHeight)
|
||||||
s->area = RC_NULL_AREA;
|
{
|
||||||
|
span->area = RC_NULL_AREA;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -29,8 +28,21 @@
|
|||||||
|
|
||||||
// Must be 255 or smaller (not 256) because layer IDs are stored as
|
// Must be 255 or smaller (not 256) because layer IDs are stored as
|
||||||
// a byte where 255 is a special value.
|
// a byte where 255 is a special value.
|
||||||
static const int RC_MAX_LAYERS = 63;
|
#ifndef RC_MAX_LAYERS_DEF
|
||||||
static const int RC_MAX_NEIS = 16;
|
#define RC_MAX_LAYERS_DEF 63
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if RC_MAX_LAYERS_DEF > 255
|
||||||
|
#error RC_MAX_LAYERS_DEF must be 255 or smaller
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RC_MAX_NEIS_DEF
|
||||||
|
#define RC_MAX_NEIS_DEF 16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Keep type checking.
|
||||||
|
static const int RC_MAX_LAYERS = RC_MAX_LAYERS_DEF;
|
||||||
|
static const int RC_MAX_NEIS = RC_MAX_NEIS_DEF;
|
||||||
|
|
||||||
struct rcLayerRegion
|
struct rcLayerRegion
|
||||||
{
|
{
|
||||||
@@ -89,7 +101,7 @@ struct rcLayerSweepSpan
|
|||||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||||
///
|
///
|
||||||
/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
|
/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
|
||||||
bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
|
bool rcBuildHeightfieldLayers(rcContext* ctx, const rcCompactHeightfield& chf,
|
||||||
const int borderSize, const int walkableHeight,
|
const int borderSize, const int walkableHeight,
|
||||||
rcHeightfieldLayerSet& lset)
|
rcHeightfieldLayerSet& lset)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
// 3. This notice may not be removed or altered from any source distribution.
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
//
|
//
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -35,7 +34,7 @@ static bool buildMeshAdjacency(unsigned short* polys, const int npolys,
|
|||||||
const int nverts, const int vertsPerPoly)
|
const int nverts, const int vertsPerPoly)
|
||||||
{
|
{
|
||||||
// Based on code by Eric Lengyel from:
|
// Based on code by Eric Lengyel from:
|
||||||
// http://www.terathon.com/code/edges.php
|
// https://web.archive.org/web/20080704083314/http://www.terathon.com/code/edges.php
|
||||||
|
|
||||||
int maxEdgeCount = npolys*vertsPerPoly;
|
int maxEdgeCount = npolys*vertsPerPoly;
|
||||||
unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP);
|
unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP);
|
||||||
@@ -566,7 +565,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
|
|||||||
const int nvp = mesh.nvp;
|
const int nvp = mesh.nvp;
|
||||||
|
|
||||||
// Count number of polygons to remove.
|
// Count number of polygons to remove.
|
||||||
int numRemovedVerts = 0;
|
|
||||||
int numTouchedVerts = 0;
|
int numTouchedVerts = 0;
|
||||||
int numRemainingEdges = 0;
|
int numRemainingEdges = 0;
|
||||||
for (int i = 0; i < mesh.npolys; ++i)
|
for (int i = 0; i < mesh.npolys; ++i)
|
||||||
@@ -586,7 +584,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
|
|||||||
}
|
}
|
||||||
if (numRemoved)
|
if (numRemoved)
|
||||||
{
|
{
|
||||||
numRemovedVerts += numRemoved;
|
|
||||||
numRemainingEdges += numVerts-(numRemoved+1);
|
numRemainingEdges += numVerts-(numRemoved+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -989,7 +986,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||||||
/// limit must be retricted to <= #DT_VERTS_PER_POLYGON.
|
/// limit must be retricted to <= #DT_VERTS_PER_POLYGON.
|
||||||
///
|
///
|
||||||
/// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig
|
/// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig
|
||||||
bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh)
|
bool rcBuildPolyMesh(rcContext* ctx, const rcContourSet& cset, const int nvp, rcPolyMesh& mesh)
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -284,7 +283,7 @@ static unsigned short getHeight(const float fx, const float fy, const float fz,
|
|||||||
enum EdgeValues
|
enum EdgeValues
|
||||||
{
|
{
|
||||||
EV_UNDEF = -1,
|
EV_UNDEF = -1,
|
||||||
EV_HULL = -2,
|
EV_HULL = -2
|
||||||
};
|
};
|
||||||
|
|
||||||
static int findEdge(const int* edges, int nedges, int s, int t)
|
static int findEdge(const int* edges, int nedges, int s, int t)
|
||||||
|
|||||||
@@ -16,373 +16,485 @@
|
|||||||
// 3. This notice may not be removed or altered from any source distribution.
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
//
|
//
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "Recast.h"
|
#include "Recast.h"
|
||||||
#include "RecastAlloc.h"
|
#include "RecastAlloc.h"
|
||||||
#include "RecastAssert.h"
|
#include "RecastAssert.h"
|
||||||
|
|
||||||
inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax)
|
/// Check whether two bounding boxes overlap
|
||||||
|
///
|
||||||
|
/// @param[in] aMin Min axis extents of bounding box A
|
||||||
|
/// @param[in] aMax Max axis extents of bounding box A
|
||||||
|
/// @param[in] bMin Min axis extents of bounding box B
|
||||||
|
/// @param[in] bMax Max axis extents of bounding box B
|
||||||
|
/// @returns true if the two bounding boxes overlap. False otherwise.
|
||||||
|
static bool overlapBounds(const float* aMin, const float* aMax, const float* bMin, const float* bMax)
|
||||||
{
|
{
|
||||||
bool overlap = true;
|
return
|
||||||
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
|
aMin[0] <= bMax[0] && aMax[0] >= bMin[0] &&
|
||||||
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
|
aMin[1] <= bMax[1] && aMax[1] >= bMin[1] &&
|
||||||
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
|
aMin[2] <= bMax[2] && aMax[2] >= bMin[2];
|
||||||
return overlap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool overlapInterval(unsigned short amin, unsigned short amax,
|
/// Allocates a new span in the heightfield.
|
||||||
unsigned short bmin, unsigned short bmax)
|
/// Use a memory pool and free list to minimize actual allocations.
|
||||||
{
|
///
|
||||||
if (amax < bmin) return false;
|
/// @param[in] hf The heightfield
|
||||||
if (amin > bmax) return false;
|
/// @returns A pointer to the allocated or re-used span memory.
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static rcSpan* allocSpan(rcHeightfield& hf)
|
static rcSpan* allocSpan(rcHeightfield& hf)
|
||||||
{
|
{
|
||||||
// If running out of memory, allocate new page and update the freelist.
|
// If necessary, allocate new page and update the freelist.
|
||||||
if (!hf.freelist || !hf.freelist->next)
|
if (hf.freelist == NULL || hf.freelist->next == NULL)
|
||||||
{
|
{
|
||||||
// Create new page.
|
// Create new page.
|
||||||
// Allocate memory for the new pool.
|
// Allocate memory for the new pool.
|
||||||
rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
|
rcSpanPool* spanPool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
|
||||||
if (!pool) return 0;
|
if (spanPool == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Add the pool into the list of pools.
|
// Add the pool into the list of pools.
|
||||||
pool->next = hf.pools;
|
spanPool->next = hf.pools;
|
||||||
hf.pools = pool;
|
hf.pools = spanPool;
|
||||||
// Add new items to the free list.
|
|
||||||
rcSpan* freelist = hf.freelist;
|
// Add new spans to the free list.
|
||||||
rcSpan* head = &pool->items[0];
|
rcSpan* freeList = hf.freelist;
|
||||||
rcSpan* it = &pool->items[RC_SPANS_PER_POOL];
|
rcSpan* head = &spanPool->items[0];
|
||||||
|
rcSpan* it = &spanPool->items[RC_SPANS_PER_POOL];
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
--it;
|
--it;
|
||||||
it->next = freelist;
|
it->next = freeList;
|
||||||
freelist = it;
|
freeList = it;
|
||||||
}
|
}
|
||||||
while (it != head);
|
while (it != head);
|
||||||
hf.freelist = it;
|
hf.freelist = it;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop item from in front of the free list.
|
// Pop item from the front of the free list.
|
||||||
rcSpan* it = hf.freelist;
|
rcSpan* newSpan = hf.freelist;
|
||||||
hf.freelist = hf.freelist->next;
|
hf.freelist = hf.freelist->next;
|
||||||
return it;
|
return newSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
|
/// Releases the memory used by the span back to the heightfield, so it can be re-used for new spans.
|
||||||
|
/// @param[in] hf The heightfield.
|
||||||
|
/// @param[in] span A pointer to the span to free
|
||||||
|
static void freeSpan(rcHeightfield& hf, rcSpan* span)
|
||||||
{
|
{
|
||||||
if (!ptr) return;
|
if (span == NULL)
|
||||||
// Add the node in front of the free list.
|
|
||||||
ptr->next = hf.freelist;
|
|
||||||
hf.freelist = ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool addSpan(rcHeightfield& hf, const int x, const int y,
|
|
||||||
const unsigned short smin, const unsigned short smax,
|
|
||||||
const unsigned char area, const int flagMergeThr)
|
|
||||||
{
|
|
||||||
|
|
||||||
int idx = x + y*hf.width;
|
|
||||||
|
|
||||||
rcSpan* s = allocSpan(hf);
|
|
||||||
if (!s)
|
|
||||||
return false;
|
|
||||||
s->smin = smin;
|
|
||||||
s->smax = smax;
|
|
||||||
s->area = area;
|
|
||||||
s->next = 0;
|
|
||||||
|
|
||||||
// Empty cell, add the first span.
|
|
||||||
if (!hf.spans[idx])
|
|
||||||
{
|
{
|
||||||
hf.spans[idx] = s;
|
return;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
rcSpan* prev = 0;
|
// Add the span to the front of the free list.
|
||||||
rcSpan* cur = hf.spans[idx];
|
span->next = hf.freelist;
|
||||||
|
hf.freelist = span;
|
||||||
|
}
|
||||||
|
|
||||||
// Insert and merge spans.
|
/// Adds a span to the heightfield. If the new span overlaps existing spans,
|
||||||
while (cur)
|
/// it will merge the new span with the existing ones.
|
||||||
|
///
|
||||||
|
/// @param[in] hf Heightfield to add spans to
|
||||||
|
/// @param[in] x The new span's column cell x index
|
||||||
|
/// @param[in] z The new span's column cell z index
|
||||||
|
/// @param[in] min The new span's minimum cell index
|
||||||
|
/// @param[in] max The new span's maximum cell index
|
||||||
|
/// @param[in] areaID The new span's area type ID
|
||||||
|
/// @param[in] flagMergeThreshold How close two spans maximum extents need to be to merge area type IDs
|
||||||
|
static bool addSpan(rcHeightfield& hf,
|
||||||
|
const int x, const int z,
|
||||||
|
const unsigned short min, const unsigned short max,
|
||||||
|
const unsigned char areaID, const int flagMergeThreshold)
|
||||||
|
{
|
||||||
|
// Create the new span.
|
||||||
|
rcSpan* newSpan = allocSpan(hf);
|
||||||
|
if (newSpan == NULL)
|
||||||
{
|
{
|
||||||
if (cur->smin > s->smax)
|
return false;
|
||||||
|
}
|
||||||
|
newSpan->smin = min;
|
||||||
|
newSpan->smax = max;
|
||||||
|
newSpan->area = areaID;
|
||||||
|
newSpan->next = NULL;
|
||||||
|
|
||||||
|
const int columnIndex = x + z * hf.width;
|
||||||
|
rcSpan* previousSpan = NULL;
|
||||||
|
rcSpan* currentSpan = hf.spans[columnIndex];
|
||||||
|
|
||||||
|
// Insert the new span, possibly merging it with existing spans.
|
||||||
|
while (currentSpan != NULL)
|
||||||
|
{
|
||||||
|
if (currentSpan->smin > newSpan->smax)
|
||||||
{
|
{
|
||||||
// Current span is further than the new span, break.
|
// Current span is completely after the new span, break.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (cur->smax < s->smin)
|
|
||||||
|
if (currentSpan->smax < newSpan->smin)
|
||||||
{
|
{
|
||||||
// Current span is before the new span advance.
|
// Current span is completely before the new span. Keep going.
|
||||||
prev = cur;
|
previousSpan = currentSpan;
|
||||||
cur = cur->next;
|
currentSpan = currentSpan->next;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Merge spans.
|
// The new span overlaps with an existing span. Merge them.
|
||||||
if (cur->smin < s->smin)
|
if (currentSpan->smin < newSpan->smin)
|
||||||
s->smin = cur->smin;
|
{
|
||||||
if (cur->smax > s->smax)
|
newSpan->smin = currentSpan->smin;
|
||||||
s->smax = cur->smax;
|
}
|
||||||
|
if (currentSpan->smax > newSpan->smax)
|
||||||
|
{
|
||||||
|
newSpan->smax = currentSpan->smax;
|
||||||
|
}
|
||||||
|
|
||||||
// Merge flags.
|
// Merge flags.
|
||||||
if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr)
|
if (rcAbs((int)newSpan->smax - (int)currentSpan->smax) <= flagMergeThreshold)
|
||||||
s->area = rcMax(s->area, cur->area);
|
{
|
||||||
|
// Higher area ID numbers indicate higher resolution priority.
|
||||||
|
newSpan->area = rcMax(newSpan->area, currentSpan->area);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove current span.
|
// Remove the current span since it's now merged with newSpan.
|
||||||
rcSpan* next = cur->next;
|
// Keep going because there might be other overlapping spans that also need to be merged.
|
||||||
freeSpan(hf, cur);
|
rcSpan* next = currentSpan->next;
|
||||||
if (prev)
|
freeSpan(hf, currentSpan);
|
||||||
prev->next = next;
|
if (previousSpan)
|
||||||
|
{
|
||||||
|
previousSpan->next = next;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
hf.spans[idx] = next;
|
{
|
||||||
cur = next;
|
hf.spans[columnIndex] = next;
|
||||||
|
}
|
||||||
|
currentSpan = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert new span.
|
// Insert new span after prev
|
||||||
if (prev)
|
if (previousSpan != NULL)
|
||||||
{
|
{
|
||||||
s->next = prev->next;
|
newSpan->next = previousSpan->next;
|
||||||
prev->next = s;
|
previousSpan->next = newSpan;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s->next = hf.spans[idx];
|
// This span should go before the others in the list
|
||||||
hf.spans[idx] = s;
|
newSpan->next = hf.spans[columnIndex];
|
||||||
|
hf.spans[columnIndex] = newSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
bool rcAddSpan(rcContext* context, rcHeightfield& heightfield,
|
||||||
///
|
const int x, const int z,
|
||||||
/// The span addition can be set to favor flags. If the span is merged to
|
const unsigned short spanMin, const unsigned short spanMax,
|
||||||
/// another span and the new @p smax is within @p flagMergeThr units
|
const unsigned char areaID, const int flagMergeThreshold)
|
||||||
/// from the existing span, the span flags are merged.
|
|
||||||
///
|
|
||||||
/// @see rcHeightfield, rcSpan.
|
|
||||||
bool rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y,
|
|
||||||
const unsigned short smin, const unsigned short smax,
|
|
||||||
const unsigned char area, const int flagMergeThr)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context);
|
||||||
|
|
||||||
if (!addSpan(hf, x, y, smin, smax, area, flagMergeThr))
|
if (!addSpan(heightfield, x, z, spanMin, spanMax, areaID, flagMergeThreshold))
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");
|
context->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// divides a convex polygons into two convex polygons on both sides of a line
|
enum rcAxis
|
||||||
static void dividePoly(const float* in, int nin,
|
|
||||||
float* out1, int* nout1,
|
|
||||||
float* out2, int* nout2,
|
|
||||||
float x, int axis)
|
|
||||||
{
|
{
|
||||||
float d[12];
|
RC_AXIS_X = 0,
|
||||||
for (int i = 0; i < nin; ++i)
|
RC_AXIS_Y = 1,
|
||||||
d[i] = x - in[i*3+axis];
|
RC_AXIS_Z = 2
|
||||||
|
};
|
||||||
|
|
||||||
int m = 0, n = 0;
|
/// Divides a convex polygon of max 12 vertices into two convex polygons
|
||||||
for (int i = 0, j = nin-1; i < nin; j=i, ++i)
|
/// across a separating axis.
|
||||||
|
///
|
||||||
|
/// @param[in] inVerts The input polygon vertices
|
||||||
|
/// @param[in] inVertsCount The number of input polygon vertices
|
||||||
|
/// @param[out] outVerts1 Resulting polygon 1's vertices
|
||||||
|
/// @param[out] outVerts1Count The number of resulting polygon 1 vertices
|
||||||
|
/// @param[out] outVerts2 Resulting polygon 2's vertices
|
||||||
|
/// @param[out] outVerts2Count The number of resulting polygon 2 vertices
|
||||||
|
/// @param[in] axisOffset THe offset along the specified axis
|
||||||
|
/// @param[in] axis The separating axis
|
||||||
|
static void dividePoly(const float* inVerts, int inVertsCount,
|
||||||
|
float* outVerts1, int* outVerts1Count,
|
||||||
|
float* outVerts2, int* outVerts2Count,
|
||||||
|
float axisOffset, rcAxis axis)
|
||||||
|
{
|
||||||
|
rcAssert(inVertsCount <= 12);
|
||||||
|
|
||||||
|
// How far positive or negative away from the separating axis is each vertex.
|
||||||
|
float inVertAxisDelta[12];
|
||||||
|
for (int inVert = 0; inVert < inVertsCount; ++inVert)
|
||||||
{
|
{
|
||||||
bool ina = d[j] >= 0;
|
inVertAxisDelta[inVert] = axisOffset - inVerts[inVert * 3 + axis];
|
||||||
bool inb = d[i] >= 0;
|
}
|
||||||
if (ina != inb)
|
|
||||||
|
int poly1Vert = 0;
|
||||||
|
int poly2Vert = 0;
|
||||||
|
for (int inVertA = 0, inVertB = inVertsCount - 1; inVertA < inVertsCount; inVertB = inVertA, ++inVertA)
|
||||||
|
{
|
||||||
|
// If the two vertices are on the same side of the separating axis
|
||||||
|
bool sameSide = (inVertAxisDelta[inVertA] >= 0) == (inVertAxisDelta[inVertB] >= 0);
|
||||||
|
|
||||||
|
if (!sameSide)
|
||||||
{
|
{
|
||||||
float s = d[j] / (d[j] - d[i]);
|
float s = inVertAxisDelta[inVertB] / (inVertAxisDelta[inVertB] - inVertAxisDelta[inVertA]);
|
||||||
out1[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s;
|
outVerts1[poly1Vert * 3 + 0] = inVerts[inVertB * 3 + 0] + (inVerts[inVertA * 3 + 0] - inVerts[inVertB * 3 + 0]) * s;
|
||||||
out1[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s;
|
outVerts1[poly1Vert * 3 + 1] = inVerts[inVertB * 3 + 1] + (inVerts[inVertA * 3 + 1] - inVerts[inVertB * 3 + 1]) * s;
|
||||||
out1[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s;
|
outVerts1[poly1Vert * 3 + 2] = inVerts[inVertB * 3 + 2] + (inVerts[inVertA * 3 + 2] - inVerts[inVertB * 3 + 2]) * s;
|
||||||
rcVcopy(out2 + n*3, out1 + m*3);
|
rcVcopy(&outVerts2[poly2Vert * 3], &outVerts1[poly1Vert * 3]);
|
||||||
m++;
|
poly1Vert++;
|
||||||
n++;
|
poly2Vert++;
|
||||||
// add the i'th point to the right polygon. Do NOT add points that are on the dividing line
|
|
||||||
|
// add the inVertA point to the right polygon. Do NOT add points that are on the dividing line
|
||||||
// since these were already added above
|
// since these were already added above
|
||||||
if (d[i] > 0)
|
if (inVertAxisDelta[inVertA] > 0)
|
||||||
{
|
{
|
||||||
rcVcopy(out1 + m*3, in + i*3);
|
rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
|
||||||
m++;
|
poly1Vert++;
|
||||||
}
|
}
|
||||||
else if (d[i] < 0)
|
else if (inVertAxisDelta[inVertA] < 0)
|
||||||
{
|
{
|
||||||
rcVcopy(out2 + n*3, in + i*3);
|
rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
|
||||||
n++;
|
poly2Vert++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // same side
|
else
|
||||||
{
|
{
|
||||||
// add the i'th point to the right polygon. Addition is done even for points on the dividing line
|
// add the inVertA point to the right polygon. Addition is done even for points on the dividing line
|
||||||
if (d[i] >= 0)
|
if (inVertAxisDelta[inVertA] >= 0)
|
||||||
{
|
{
|
||||||
rcVcopy(out1 + m*3, in + i*3);
|
rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
|
||||||
m++;
|
poly1Vert++;
|
||||||
if (d[i] != 0)
|
if (inVertAxisDelta[inVertA] != 0)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rcVcopy(out2 + n*3, in + i*3);
|
rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
|
||||||
n++;
|
poly2Vert++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*nout1 = m;
|
*outVerts1Count = poly1Vert;
|
||||||
*nout2 = n;
|
*outVerts2Count = poly2Vert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rasterize a single triangle to the heightfield.
|
||||||
|
///
|
||||||
|
/// This code is extremely hot, so much care should be given to maintaining maximum perf here.
|
||||||
|
///
|
||||||
|
/// @param[in] v0 Triangle vertex 0
|
||||||
|
/// @param[in] v1 Triangle vertex 1
|
||||||
|
/// @param[in] v2 Triangle vertex 2
|
||||||
|
/// @param[in] areaID The area ID to assign to the rasterized spans
|
||||||
|
/// @param[in] hf Heightfield to rasterize into
|
||||||
|
/// @param[in] hfBBMin The min extents of the heightfield bounding box
|
||||||
|
/// @param[in] hfBBMax The max extents of the heightfield bounding box
|
||||||
|
/// @param[in] cellSize The x and z axis size of a voxel in the heightfield
|
||||||
|
/// @param[in] inverseCellSize 1 / cellSize
|
||||||
|
/// @param[in] inverseCellHeight 1 / cellHeight
|
||||||
|
/// @param[in] flagMergeThreshold The threshold in which area flags will be merged
|
||||||
|
/// @returns true if the operation completes successfully. false if there was an error adding spans to the heightfield.
|
||||||
static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
|
static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
|
||||||
const unsigned char area, rcHeightfield& hf,
|
const unsigned char areaID, rcHeightfield& hf,
|
||||||
const float* bmin, const float* bmax,
|
const float* hfBBMin, const float* hfBBMax,
|
||||||
const float cs, const float ics, const float ich,
|
const float cellSize, const float inverseCellSize, const float inverseCellHeight,
|
||||||
const int flagMergeThr)
|
const int flagMergeThreshold)
|
||||||
{
|
{
|
||||||
|
// Calculate the bounding box of the triangle.
|
||||||
|
float triBBMin[3];
|
||||||
|
rcVcopy(triBBMin, v0);
|
||||||
|
rcVmin(triBBMin, v1);
|
||||||
|
rcVmin(triBBMin, v2);
|
||||||
|
|
||||||
|
float triBBMax[3];
|
||||||
|
rcVcopy(triBBMax, v0);
|
||||||
|
rcVmax(triBBMax, v1);
|
||||||
|
rcVmax(triBBMax, v2);
|
||||||
|
|
||||||
|
// If the triangle does not touch the bounding box of the heightfield, skip the triangle.
|
||||||
|
if (!overlapBounds(triBBMin, triBBMax, hfBBMin, hfBBMax))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const int w = hf.width;
|
const int w = hf.width;
|
||||||
const int h = hf.height;
|
const int h = hf.height;
|
||||||
float tmin[3], tmax[3];
|
const float by = hfBBMax[1] - hfBBMin[1];
|
||||||
const float by = bmax[1] - bmin[1];
|
|
||||||
|
|
||||||
// Calculate the bounding box of the triangle.
|
// Calculate the footprint of the triangle on the grid's z-axis
|
||||||
rcVcopy(tmin, v0);
|
int z0 = (int)((triBBMin[2] - hfBBMin[2]) * inverseCellSize);
|
||||||
rcVcopy(tmax, v0);
|
int z1 = (int)((triBBMax[2] - hfBBMin[2]) * inverseCellSize);
|
||||||
rcVmin(tmin, v1);
|
|
||||||
rcVmin(tmin, v2);
|
|
||||||
rcVmax(tmax, v1);
|
|
||||||
rcVmax(tmax, v2);
|
|
||||||
|
|
||||||
// If the triangle does not touch the bbox of the heightfield, skip the triagle.
|
// use -1 rather than 0 to cut the polygon properly at the start of the tile
|
||||||
if (!overlapBounds(bmin, bmax, tmin, tmax))
|
z0 = rcClamp(z0, -1, h - 1);
|
||||||
return true;
|
z1 = rcClamp(z1, 0, h - 1);
|
||||||
|
|
||||||
// Calculate the footprint of the triangle on the grid's y-axis
|
|
||||||
int y0 = (int)((tmin[2] - bmin[2])*ics);
|
|
||||||
int y1 = (int)((tmax[2] - bmin[2])*ics);
|
|
||||||
y0 = rcClamp(y0, 0, h-1);
|
|
||||||
y1 = rcClamp(y1, 0, h-1);
|
|
||||||
|
|
||||||
// Clip the triangle into all grid cells it touches.
|
// Clip the triangle into all grid cells it touches.
|
||||||
float buf[7*3*4];
|
float buf[7 * 3 * 4];
|
||||||
float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3;
|
float* in = buf;
|
||||||
|
float* inRow = buf + 7 * 3;
|
||||||
|
float* p1 = inRow + 7 * 3;
|
||||||
|
float* p2 = p1 + 7 * 3;
|
||||||
|
|
||||||
rcVcopy(&in[0], v0);
|
rcVcopy(&in[0], v0);
|
||||||
rcVcopy(&in[1*3], v1);
|
rcVcopy(&in[1 * 3], v1);
|
||||||
rcVcopy(&in[2*3], v2);
|
rcVcopy(&in[2 * 3], v2);
|
||||||
int nvrow, nvIn = 3;
|
int nvRow;
|
||||||
|
int nvIn = 3;
|
||||||
|
|
||||||
for (int y = y0; y <= y1; ++y)
|
for (int z = z0; z <= z1; ++z)
|
||||||
{
|
{
|
||||||
// Clip polygon to row. Store the remaining polygon as well
|
// Clip polygon to row. Store the remaining polygon as well
|
||||||
const float cz = bmin[2] + y*cs;
|
const float cellZ = hfBBMin[2] + (float)z * cellSize;
|
||||||
dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2);
|
dividePoly(in, nvIn, inRow, &nvRow, p1, &nvIn, cellZ + cellSize, RC_AXIS_Z);
|
||||||
rcSwap(in, p1);
|
rcSwap(in, p1);
|
||||||
if (nvrow < 3) continue;
|
|
||||||
|
|
||||||
// find the horizontal bounds in the row
|
if (nvRow < 3)
|
||||||
float minX = inrow[0], maxX = inrow[0];
|
|
||||||
for (int i=1; i<nvrow; ++i)
|
|
||||||
{
|
{
|
||||||
if (minX > inrow[i*3]) minX = inrow[i*3];
|
continue;
|
||||||
if (maxX < inrow[i*3]) maxX = inrow[i*3];
|
}
|
||||||
|
if (z < 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
int x0 = (int)((minX - bmin[0])*ics);
|
|
||||||
int x1 = (int)((maxX - bmin[0])*ics);
|
|
||||||
x0 = rcClamp(x0, 0, w-1);
|
|
||||||
x1 = rcClamp(x1, 0, w-1);
|
|
||||||
|
|
||||||
int nv, nv2 = nvrow;
|
// find X-axis bounds of the row
|
||||||
|
float minX = inRow[0];
|
||||||
|
float maxX = inRow[0];
|
||||||
|
for (int vert = 1; vert < nvRow; ++vert)
|
||||||
|
{
|
||||||
|
if (minX > inRow[vert * 3])
|
||||||
|
{
|
||||||
|
minX = inRow[vert * 3];
|
||||||
|
}
|
||||||
|
if (maxX < inRow[vert * 3])
|
||||||
|
{
|
||||||
|
maxX = inRow[vert * 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int x0 = (int)((minX - hfBBMin[0]) * inverseCellSize);
|
||||||
|
int x1 = (int)((maxX - hfBBMin[0]) * inverseCellSize);
|
||||||
|
if (x1 < 0 || x0 >= w)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
x0 = rcClamp(x0, -1, w - 1);
|
||||||
|
x1 = rcClamp(x1, 0, w - 1);
|
||||||
|
|
||||||
|
int nv;
|
||||||
|
int nv2 = nvRow;
|
||||||
|
|
||||||
for (int x = x0; x <= x1; ++x)
|
for (int x = x0; x <= x1; ++x)
|
||||||
{
|
{
|
||||||
// Clip polygon to column. store the remaining polygon as well
|
// Clip polygon to column. store the remaining polygon as well
|
||||||
const float cx = bmin[0] + x*cs;
|
const float cx = hfBBMin[0] + (float)x * cellSize;
|
||||||
dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0);
|
dividePoly(inRow, nv2, p1, &nv, p2, &nv2, cx + cellSize, RC_AXIS_X);
|
||||||
rcSwap(inrow, p2);
|
rcSwap(inRow, p2);
|
||||||
if (nv < 3) continue;
|
|
||||||
|
if (nv < 3)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (x < 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate min and max of the span.
|
// Calculate min and max of the span.
|
||||||
float smin = p1[1], smax = p1[1];
|
float spanMin = p1[1];
|
||||||
for (int i = 1; i < nv; ++i)
|
float spanMax = p1[1];
|
||||||
|
for (int vert = 1; vert < nv; ++vert)
|
||||||
{
|
{
|
||||||
smin = rcMin(smin, p1[i*3+1]);
|
spanMin = rcMin(spanMin, p1[vert * 3 + 1]);
|
||||||
smax = rcMax(smax, p1[i*3+1]);
|
spanMax = rcMax(spanMax, p1[vert * 3 + 1]);
|
||||||
|
}
|
||||||
|
spanMin -= hfBBMin[1];
|
||||||
|
spanMax -= hfBBMin[1];
|
||||||
|
|
||||||
|
// Skip the span if it's completely outside the heightfield bounding box
|
||||||
|
if (spanMax < 0.0f)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (spanMin > by)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp the span to the heightfield bounding box.
|
||||||
|
if (spanMin < 0.0f)
|
||||||
|
{
|
||||||
|
spanMin = 0;
|
||||||
|
}
|
||||||
|
if (spanMax > by)
|
||||||
|
{
|
||||||
|
spanMax = by;
|
||||||
}
|
}
|
||||||
smin -= bmin[1];
|
|
||||||
smax -= bmin[1];
|
|
||||||
// Skip the span if it is outside the heightfield bbox
|
|
||||||
if (smax < 0.0f) continue;
|
|
||||||
if (smin > by) continue;
|
|
||||||
// Clamp the span to the heightfield bbox.
|
|
||||||
if (smin < 0.0f) smin = 0;
|
|
||||||
if (smax > by) smax = by;
|
|
||||||
|
|
||||||
// Snap the span to the heightfield height grid.
|
// Snap the span to the heightfield height grid.
|
||||||
unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
|
unsigned short spanMinCellIndex = (unsigned short)rcClamp((int)floorf(spanMin * inverseCellHeight), 0, RC_SPAN_MAX_HEIGHT);
|
||||||
unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
|
unsigned short spanMaxCellIndex = (unsigned short)rcClamp((int)ceilf(spanMax * inverseCellHeight), (int)spanMinCellIndex + 1, RC_SPAN_MAX_HEIGHT);
|
||||||
|
|
||||||
if (!addSpan(hf, x, y, ismin, ismax, area, flagMergeThr))
|
if (!addSpan(hf, x, z, spanMinCellIndex, spanMaxCellIndex, areaID, flagMergeThreshold))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
bool rcRasterizeTriangle(rcContext* context,
|
||||||
///
|
const float* v0, const float* v1, const float* v2,
|
||||||
/// No spans will be added if the triangle does not overlap the heightfield grid.
|
const unsigned char areaID, rcHeightfield& heightfield, const int flagMergeThreshold)
|
||||||
///
|
|
||||||
/// @see rcHeightfield
|
|
||||||
bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
|
|
||||||
const unsigned char area, rcHeightfield& solid,
|
|
||||||
const int flagMergeThr)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context != NULL);
|
||||||
|
|
||||||
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
|
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||||
|
|
||||||
const float ics = 1.0f/solid.cs;
|
// Rasterize the single triangle.
|
||||||
const float ich = 1.0f/solid.ch;
|
const float inverseCellSize = 1.0f / heightfield.cs;
|
||||||
if (!rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
|
const float inverseCellHeight = 1.0f / heightfield.ch;
|
||||||
|
if (!rasterizeTri(v0, v1, v2, areaID, heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");
|
context->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
bool rcRasterizeTriangles(rcContext* context,
|
||||||
///
|
const float* verts, const int /*nv*/,
|
||||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
const int* tris, const unsigned char* triAreaIDs, const int numTris,
|
||||||
///
|
rcHeightfield& heightfield, const int flagMergeThreshold)
|
||||||
/// @see rcHeightfield
|
|
||||||
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
|
||||||
const int* tris, const unsigned char* areas, const int nt,
|
|
||||||
rcHeightfield& solid, const int flagMergeThr)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context != NULL);
|
||||||
|
|
||||||
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
|
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||||
|
|
||||||
const float ics = 1.0f/solid.cs;
|
// Rasterize the triangles.
|
||||||
const float ich = 1.0f/solid.ch;
|
const float inverseCellSize = 1.0f / heightfield.cs;
|
||||||
// Rasterize triangles.
|
const float inverseCellHeight = 1.0f / heightfield.ch;
|
||||||
for (int i = 0; i < nt; ++i)
|
for (int triIndex = 0; triIndex < numTris; ++triIndex)
|
||||||
{
|
{
|
||||||
const float* v0 = &verts[tris[i*3+0]*3];
|
const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
|
||||||
const float* v1 = &verts[tris[i*3+1]*3];
|
const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
|
||||||
const float* v2 = &verts[tris[i*3+2]*3];
|
const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
|
||||||
// Rasterize.
|
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
|
||||||
if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
|
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,31 +502,26 @@ bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
bool rcRasterizeTriangles(rcContext* context,
|
||||||
///
|
const float* verts, const int /*nv*/,
|
||||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
const unsigned short* tris, const unsigned char* triAreaIDs, const int numTris,
|
||||||
///
|
rcHeightfield& heightfield, const int flagMergeThreshold)
|
||||||
/// @see rcHeightfield
|
|
||||||
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
|
||||||
const unsigned short* tris, const unsigned char* areas, const int nt,
|
|
||||||
rcHeightfield& solid, const int flagMergeThr)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context != NULL);
|
||||||
|
|
||||||
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
|
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||||
|
|
||||||
const float ics = 1.0f/solid.cs;
|
// Rasterize the triangles.
|
||||||
const float ich = 1.0f/solid.ch;
|
const float inverseCellSize = 1.0f / heightfield.cs;
|
||||||
// Rasterize triangles.
|
const float inverseCellHeight = 1.0f / heightfield.ch;
|
||||||
for (int i = 0; i < nt; ++i)
|
for (int triIndex = 0; triIndex < numTris; ++triIndex)
|
||||||
{
|
{
|
||||||
const float* v0 = &verts[tris[i*3+0]*3];
|
const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
|
||||||
const float* v1 = &verts[tris[i*3+1]*3];
|
const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
|
||||||
const float* v2 = &verts[tris[i*3+2]*3];
|
const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
|
||||||
// Rasterize.
|
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
|
||||||
if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
|
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,30 +529,25 @@ bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
bool rcRasterizeTriangles(rcContext* context,
|
||||||
///
|
const float* verts, const unsigned char* triAreaIDs, const int numTris,
|
||||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
rcHeightfield& heightfield, const int flagMergeThreshold)
|
||||||
///
|
|
||||||
/// @see rcHeightfield
|
|
||||||
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
|
|
||||||
rcHeightfield& solid, const int flagMergeThr)
|
|
||||||
{
|
{
|
||||||
rcAssert(ctx);
|
rcAssert(context != NULL);
|
||||||
|
|
||||||
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
|
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||||
|
|
||||||
const float ics = 1.0f/solid.cs;
|
// Rasterize the triangles.
|
||||||
const float ich = 1.0f/solid.ch;
|
const float inverseCellSize = 1.0f / heightfield.cs;
|
||||||
// Rasterize triangles.
|
const float inverseCellHeight = 1.0f / heightfield.ch;
|
||||||
for (int i = 0; i < nt; ++i)
|
for (int triIndex = 0; triIndex < numTris; ++triIndex)
|
||||||
{
|
{
|
||||||
const float* v0 = &verts[(i*3+0)*3];
|
const float* v0 = &verts[(triIndex * 3 + 0) * 3];
|
||||||
const float* v1 = &verts[(i*3+1)*3];
|
const float* v1 = &verts[(triIndex * 3 + 1) * 3];
|
||||||
const float* v2 = &verts[(i*3+2)*3];
|
const float* v2 = &verts[(triIndex * 3 + 2) * 3];
|
||||||
// Rasterize.
|
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
|
||||||
if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
|
|
||||||
{
|
{
|
||||||
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -1355,6 +1355,7 @@ namespace Flax.Build.Bindings
|
|||||||
public static class NativeToManaged
|
public static class NativeToManaged
|
||||||
{
|
{
|
||||||
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
|
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
|
||||||
|
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
|
||||||
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.NativeToManaged.Free(unmanaged);
|
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.NativeToManaged.Free(unmanaged);
|
||||||
}
|
}
|
||||||
#if FLAX_EDITOR
|
#if FLAX_EDITOR
|
||||||
@@ -1362,6 +1363,7 @@ namespace Flax.Build.Bindings
|
|||||||
#endif
|
#endif
|
||||||
public static class ManagedToNative
|
public static class ManagedToNative
|
||||||
{
|
{
|
||||||
|
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
|
||||||
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
|
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
|
||||||
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.ManagedToNative.Free(unmanaged);
|
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.ManagedToNative.Free(unmanaged);
|
||||||
}
|
}
|
||||||
@@ -1671,6 +1673,7 @@ namespace Flax.Build.Bindings
|
|||||||
contents.Append(indent).AppendLine("[HideInEditor]");
|
contents.Append(indent).AppendLine("[HideInEditor]");
|
||||||
contents.Append(indent).AppendLine("public static class NativeToManaged").Append(indent).AppendLine("{");
|
contents.Append(indent).AppendLine("public static class NativeToManaged").Append(indent).AppendLine("{");
|
||||||
contents.Append(indent2).AppendLine($"public static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => {marshallerName}.ToManaged(unmanaged);");
|
contents.Append(indent2).AppendLine($"public static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => {marshallerName}.ToManaged(unmanaged);");
|
||||||
|
contents.Append(indent2).AppendLine($"public static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);");
|
||||||
contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged)");
|
contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged)");
|
||||||
contents.Append(indent2).AppendLine("{").Append(indent3).AppendLine(freeContents2.Replace("\n", "\n" + indent3).ToString().TrimEnd()).Append(indent2).AppendLine("}");
|
contents.Append(indent2).AppendLine("{").Append(indent3).AppendLine(freeContents2.Replace("\n", "\n" + indent3).ToString().TrimEnd()).Append(indent2).AppendLine("}");
|
||||||
contents.Append(indent).AppendLine("}");
|
contents.Append(indent).AppendLine("}");
|
||||||
@@ -1679,6 +1682,7 @@ namespace Flax.Build.Bindings
|
|||||||
if (buildData.Target != null && buildData.Target.IsEditor)
|
if (buildData.Target != null && buildData.Target.IsEditor)
|
||||||
contents.Append(indent).AppendLine("[HideInEditor]");
|
contents.Append(indent).AppendLine("[HideInEditor]");
|
||||||
contents.Append(indent).AppendLine($"public static class ManagedToNative").Append(indent).AppendLine("{");
|
contents.Append(indent).AppendLine($"public static class ManagedToNative").Append(indent).AppendLine("{");
|
||||||
|
contents.Append(indent2).AppendLine($"public static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => {marshallerName}.ToManaged(unmanaged);");
|
||||||
contents.Append(indent2).AppendLine($"public static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);");
|
contents.Append(indent2).AppendLine($"public static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);");
|
||||||
contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged) => {marshallerName}.Free(unmanaged);");
|
contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged) => {marshallerName}.Free(unmanaged);");
|
||||||
contents.Append(indent).AppendLine("}");
|
contents.Append(indent).AppendLine("}");
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ namespace Flax.Build
|
|||||||
case TargetPlatform.Mac:
|
case TargetPlatform.Mac:
|
||||||
{
|
{
|
||||||
#if USE_NETCORE
|
#if USE_NETCORE
|
||||||
|
dotnetPath = Path.Combine(dotnetSdk.RootPath, "dotnet");
|
||||||
cscPath = Path.Combine(dotnetSdk.RootPath, $"sdk/{dotnetSdk.VersionName}/Roslyn/bincore/csc.dll");
|
cscPath = Path.Combine(dotnetSdk.RootPath, $"sdk/{dotnetSdk.VersionName}/Roslyn/bincore/csc.dll");
|
||||||
referenceAssemblies = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/ref/net{runtimeVersionShort}/");
|
referenceAssemblies = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/ref/net{runtimeVersionShort}/");
|
||||||
referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/");
|
referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/");
|
||||||
|
|||||||
@@ -212,6 +212,15 @@ namespace Flax.Build
|
|||||||
ridFallback = "";
|
ridFallback = "";
|
||||||
if (string.IsNullOrEmpty(dotnetPath))
|
if (string.IsNullOrEmpty(dotnetPath))
|
||||||
dotnetPath = "/usr/local/share/dotnet/";
|
dotnetPath = "/usr/local/share/dotnet/";
|
||||||
|
|
||||||
|
// Use x64 when cross-compiling from ARM64
|
||||||
|
if (architecture == TargetArchitecture.ARM64 && (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64))
|
||||||
|
{
|
||||||
|
rid = "osx-x64";
|
||||||
|
dotnetPath = Path.Combine(dotnetPath, "x64");
|
||||||
|
architecture = TargetArchitecture.x64;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: throw new InvalidPlatformException(platform);
|
default: throw new InvalidPlatformException(platform);
|
||||||
@@ -271,7 +280,7 @@ namespace Flax.Build
|
|||||||
|
|
||||||
// Found
|
// Found
|
||||||
IsValid = true;
|
IsValid = true;
|
||||||
Log.Verbose($"Found .NET SDK {VersionName} (runtime {RuntimeVersionName}) at {RootPath}");
|
Log.Info($"Using .NET SDK {VersionName}, runtime {RuntimeVersionName} ({RootPath})");
|
||||||
foreach (var e in _hostRuntimes)
|
foreach (var e in _hostRuntimes)
|
||||||
Log.Verbose($" - Host Runtime for {e.Key.Key} {e.Key.Value}");
|
Log.Verbose($" - Host Runtime for {e.Key.Key} {e.Key.Value}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Flax.Build.Platforms
|
namespace Flax.Build.Platforms
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -11,6 +13,9 @@ namespace Flax.Build.Platforms
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override TargetPlatform Target => TargetPlatform.iOS;
|
public override TargetPlatform Target => TargetPlatform.iOS;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool HasRequiredSDKsInstalled { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool HasDynamicCodeExecutionSupport => false;
|
public override bool HasDynamicCodeExecutionSupport => false;
|
||||||
|
|
||||||
@@ -21,11 +26,21 @@ namespace Flax.Build.Platforms
|
|||||||
{
|
{
|
||||||
if (Platform.BuildTargetPlatform != TargetPlatform.Mac)
|
if (Platform.BuildTargetPlatform != TargetPlatform.Mac)
|
||||||
return;
|
return;
|
||||||
if (!HasRequiredSDKsInstalled)
|
if (!XCode.Instance.IsValid)
|
||||||
{
|
{
|
||||||
Log.Warning("Missing XCode. Cannot build for iOS platform.");
|
Log.Warning("Missing XCode. Cannot build for iOS platform.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should check and see if the actual iphoneSDK is installed
|
||||||
|
string iphoneSDKPath = Utilities.ReadProcessOutput("/usr/bin/xcrun", "--sdk iphoneos --show-sdk-path");
|
||||||
|
if (string.IsNullOrEmpty(iphoneSDKPath) || !Directory.Exists(iphoneSDKPath))
|
||||||
|
{
|
||||||
|
Log.Warning("Missing iPhoneSDK. Cannot build for iOS platform.");
|
||||||
|
HasRequiredSDKsInstalled = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
HasRequiredSDKsInstalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
Reference in New Issue
Block a user