From 2d2bfd9cd1c066c758de96c5314a36964be34d0c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 16 Sep 2023 14:14:26 +0300 Subject: [PATCH 01/28] Include EditorModules in Assembly lookup paths --- Source/Engine/Engine/NativeInterop.Unmanaged.cs | 4 ++-- Source/Engine/Engine/NativeInterop.cs | 8 ++++---- Source/Engine/Scripting/Runtime/DotNet.cpp | 7 +++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 2c1bab744..e96839546 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -184,7 +184,7 @@ namespace FlaxEngine.Interop { string moduleName = Marshal.PtrToStringAnsi(moduleNamePtr); string modulePath = Marshal.PtrToStringAnsi(modulePathPtr); - nativeLibraryPaths[moduleName] = modulePath; + libraryPaths[moduleName] = modulePath; } [UnmanagedCallersOnly] @@ -909,7 +909,7 @@ namespace FlaxEngine.Interop loadedNativeLibraries.Remove(nativeLibraryName); } if (nativeLibraryName != null) - nativeLibraryPaths.Remove(nativeLibraryName); + libraryPaths.Remove(nativeLibraryName); } [UnmanagedCallersOnly] diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index dd6087b98..8a8d543f1 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -50,7 +50,7 @@ namespace FlaxEngine.Interop private static Dictionary _typeSizeCache = new(); private static Dictionary loadedNativeLibraries = new(); - internal static Dictionary nativeLibraryPaths = new(); + internal static Dictionary libraryPaths = new(); private static Dictionary assemblyOwnedNativeLibraries = new(); internal static AssemblyLoadContext scriptingAssemblyLoadContext; @@ -59,7 +59,7 @@ namespace FlaxEngine.Interop { if (!loadedNativeLibraries.TryGetValue(libraryName, out IntPtr nativeLibrary)) { - if (!nativeLibraryPaths.TryGetValue(libraryName, out var nativeLibraryPath)) + if (!libraryPaths.TryGetValue(libraryName, out var nativeLibraryPath)) nativeLibraryPath = libraryName; nativeLibrary = NativeLibrary.Load(nativeLibraryPath, assembly, dllImportSearchPath); @@ -101,9 +101,9 @@ namespace FlaxEngine.Interop 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 - 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"); if (File.Exists(assemblyPath)) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 1bcef50b8..63feccba6 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -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); 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; _assemblyPath = assemblyPath; From f114301e97a124b15a1ffd08b2eeb390c0fcb3e4 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 16 Sep 2023 15:02:31 +0300 Subject: [PATCH 02/28] Improve hostfxr error messages --- Source/Engine/Scripting/Runtime/DotNet.cpp | 28 ++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 1bcef50b8..dba30f1e5 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1588,7 +1588,10 @@ bool InitHostfxr() void* hostfxr = Platform::LoadLibrary(path.Get()); 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; } hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_runtime_config"); @@ -1627,7 +1630,28 @@ bool InitHostfxr() if (rc != 0 || handle == nullptr) { 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; } From 785943bef81b7e6c87ed207085127bc99d51c3a3 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 16 Sep 2023 15:58:02 +0300 Subject: [PATCH 03/28] Add missing marshaller methods for compatibility --- .../Engine/NativeInterop.Marshallers.cs | 64 +++++++++++-------- .../Bindings/BindingsGenerator.CSharp.cs | 4 ++ .../Flax.Build/Build/DotNet/DotNetSdk.cs | 2 +- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs index 74d6e6bad..a74299670 100644 --- a/Source/Engine/Engine/NativeInterop.Marshallers.cs +++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs @@ -32,6 +32,7 @@ namespace FlaxEngine.Interop public static class NativeToManaged { 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) { @@ -44,6 +45,7 @@ namespace FlaxEngine.Interop #endif 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 void Free(IntPtr unmanaged) @@ -147,29 +149,16 @@ namespace FlaxEngine.Interop #if FLAX_EDITOR [HideInEditor] #endif - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedOut, typeof(ObjectMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedIn, typeof(ObjectMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedOut, typeof(ObjectMarshaller))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedIn, typeof(ObjectMarshaller))] + [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller))] public static class ObjectMarshaller { -#if FLAX_EDITOR - [HideInEditor] -#endif - public static class NativeToManaged - { - public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(unmanaged).Target) : null; - } - -#if FLAX_EDITOR - [HideInEditor] -#endif - public static class ManagedToNative - { - public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => Unsafe.As(managed) != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; - } + public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(unmanaged).Target) : null; + public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => Unsafe.As(managed) != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; } #if FLAX_EDITOR @@ -342,6 +331,7 @@ namespace FlaxEngine.Interop public static class NativeToManaged { public static Dictionary ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller.ToManaged(unmanaged); + public static IntPtr ConvertToUnmanaged(Dictionary managed) => DictionaryMarshaller.ToNative(managed, GCHandleType.Weak); public static void Free(IntPtr unmanaged) => DictionaryMarshaller.Free(unmanaged); } @@ -350,8 +340,8 @@ namespace FlaxEngine.Interop #endif public static class ManagedToNative { + public static Dictionary ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller.ToManaged(unmanaged); public static IntPtr ConvertToUnmanaged(Dictionary managed) => DictionaryMarshaller.ToNative(managed, GCHandleType.Weak); - public static void Free(IntPtr unmanaged) { //DictionaryMarshaller.Free(unmanaged); // No need to free weak handles @@ -425,6 +415,28 @@ namespace FlaxEngine.Interop 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(managed.Length); + return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArrayHandle); + } + + public static ReadOnlySpan GetManagedValuesSource(T[] managed) => managed; + + public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged) + { + if (unmanaged == null) + return Span.Empty; + ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return managedArray.ToSpan(); + } + public static Span GetManagedValuesDestination(T[] managed) => managed; public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) @@ -591,6 +603,7 @@ namespace FlaxEngine.Interop public static class NativeToManaged { 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); } @@ -599,11 +612,8 @@ namespace FlaxEngine.Interop #endif public static class ManagedToNative { - public static unsafe IntPtr ConvertToUnmanaged(string managed) - { - return managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak); - } - + 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); // No need to free weak handles diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index b825f5ac1..6a9aa7669 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1340,6 +1340,7 @@ namespace Flax.Build.Bindings public static class NativeToManaged { 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); } #if FLAX_EDITOR @@ -1347,6 +1348,7 @@ namespace Flax.Build.Bindings #endif 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 void Free(IntPtr unmanaged) => ManagedHandleMarshaller.ManagedToNative.Free(unmanaged); } @@ -1668,6 +1670,7 @@ namespace Flax.Build.Bindings contents.Append(indent).AppendLine("[HideInEditor]"); 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}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);"); 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(indent).AppendLine("}"); @@ -1676,6 +1679,7 @@ namespace Flax.Build.Bindings if (buildData.Target != null && buildData.Target.IsEditor) contents.Append(indent).AppendLine("[HideInEditor]"); 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 void Free({structureInfo.Name}Internal unmanaged) => {marshallerName}.Free(unmanaged);"); contents.Append(indent).AppendLine("}"); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index d3a706a43..385bc3572 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -271,7 +271,7 @@ namespace Flax.Build // Found 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) Log.Verbose($" - Host Runtime for {e.Key.Key} {e.Key.Value}"); } From 5ab299fed9377280e9cec6a57634c342135520fd Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 16 Sep 2023 17:58:38 +0300 Subject: [PATCH 04/28] Fix assets getting deleted when modified by external applications --- Source/Editor/Modules/ContentDatabaseModule.cs | 11 ++++++----- Source/Editor/Windows/ContentWindow.cs | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index 29d6b2c00..143352421 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -643,7 +643,8 @@ namespace FlaxEditor.Modules /// Deletes the specified item. /// /// The item. - public void Delete(ContentItem item) + /// If the file was deleted by the user and not outside the editor. + public void Delete(ContentItem item, bool deletedByUser = false) { if (item == null) throw new ArgumentNullException(); @@ -667,12 +668,12 @@ namespace FlaxEditor.Modules var children = folder.Children.ToArray(); for (int i = 0; i < children.Length; i++) { - Delete(children[i]); + Delete(children[i], deletedByUser); } } // Remove directory - if (Directory.Exists(path)) + if (deletedByUser && Directory.Exists(path)) { try { @@ -701,7 +702,7 @@ namespace FlaxEditor.Modules // Delete asset by using content pool FlaxEngine.Content.DeleteAsset(path); } - else + else if (deletedByUser) { // Delete file if (File.Exists(path)) @@ -847,7 +848,7 @@ namespace FlaxEditor.Modules Editor.Log(string.Format($"Content item \'{child.Path}\' has been removed")); // Destroy it - Delete(child); + Delete(child, false); i--; } diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 0f3c12286..91e4ec1b5 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -626,7 +626,7 @@ namespace FlaxEditor.Windows // Delete items for (int i = 0; i < toDelete.Count; i++) - Editor.ContentDatabase.Delete(toDelete[i]); + Editor.ContentDatabase.Delete(toDelete[i], true); RefreshView(); } From a89856becef9b76bc5712a421e5f7b6e73748b35 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 16 Sep 2023 10:15:26 -0500 Subject: [PATCH 05/28] Increase size of clamp node to handle vector4. --- Source/Editor/Surface/Archetypes/Math.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Math.cs b/Source/Editor/Surface/Archetypes/Math.cs index 1b8f62e62..fe2f1e044 100644 --- a/Source/Editor/Surface/Archetypes/Math.cs +++ b/Source/Editor/Surface/Archetypes/Math.cs @@ -126,7 +126,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Clamp", Description = "Clamps value to the specified range", Flags = NodeFlags.AllGraphs, - Size = new Float2(110, 60), + Size = new Float2(140, 60), ConnectionsHints = ConnectionsHint.Numeric, IndependentBoxes = new[] { 0 }, DependentBoxes = new[] { 1, 2, 3 }, From 9a7fb827381b9b951dbe7c6ffd7b3ce0e5db5df8 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 16 Sep 2023 18:38:24 +0300 Subject: [PATCH 06/28] Fix tests --- Source/Engine/Tests/TestScripting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Tests/TestScripting.cs b/Source/Engine/Tests/TestScripting.cs index fd94b8017..93c6637fe 100644 --- a/Source/Engine/Tests/TestScripting.cs +++ b/Source/Engine/Tests/TestScripting.cs @@ -19,7 +19,7 @@ namespace FlaxEngine.Tests { var result = 0; var libraryName = "FlaxEngine"; - var library = NativeLibrary.Load(Interop.NativeInterop.nativeLibraryPaths[libraryName]); + var library = NativeLibrary.Load(Interop.NativeInterop.libraryPaths[libraryName]); if (library == IntPtr.Zero) return -1; var types = typeof(FlaxEngine.Object).Assembly.GetTypes(); From 9931a5c02660bca47eeaea50e0cae2d4906daa21 Mon Sep 17 00:00:00 2001 From: MineBill Date: Sat, 16 Sep 2023 19:07:23 +0300 Subject: [PATCH 07/28] Don't treat '.' in folder names as extensions. --- Source/Editor/Modules/ContentEditingModule.cs | 8 +++++++- Source/Editor/Windows/ContentWindow.cs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Modules/ContentEditingModule.cs b/Source/Editor/Modules/ContentEditingModule.cs index 3db27af40..a6d8132f0 100644 --- a/Source/Editor/Modules/ContentEditingModule.cs +++ b/Source/Editor/Modules/ContentEditingModule.cs @@ -90,6 +90,12 @@ namespace FlaxEditor.Modules hint = "Too long name."; return false; } + + if (item.IsFolder && shortName.EndsWith(".")) + { + hint = "Name cannot end with '.'"; + return false; + } // Find invalid characters if (Utilities.Utils.HasInvalidPathChar(shortName)) @@ -120,7 +126,7 @@ namespace FlaxEditor.Modules // Cache data string sourcePath = item.Path; 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); if (item.IsFolder) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 0f3c12286..d29775a8b 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -520,7 +520,7 @@ namespace FlaxEditor.Windows } // 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); // Check if was renaming mock element From dabbd7bc6a1f04fe57970ceed12f9c4dcfad388c Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 16 Sep 2023 11:46:36 -0500 Subject: [PATCH 08/28] Add highlighting when mouse is over slider thumb. --- Source/Engine/UI/GUI/Common/Slider.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs index d7324ae0e..7ea2accef 100644 --- a/Source/Engine/UI/GUI/Common/Slider.cs +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -60,6 +60,7 @@ public class Slider : ContainerControl private float _thumbCenter; private Float2 _thumbSize = new Float2(16, 16); private bool _isSliding; + private bool _mouseOverThumb; /// /// Gets or sets the value (normalized to range 0-100). @@ -163,21 +164,27 @@ public class Slider : ContainerControl public IBrush FillTrackBrush { get; set; } /// - /// The color of the slider thumb when it's not selected + /// The color of the slider thumb when it's not selected. /// [EditorDisplay("Thumb Style"), EditorOrder(2030), Tooltip("The color of the slider thumb when it's not selected."), ExpandGroups] public Color ThumbColor { get; set; } + + /// + /// The color of the slider thumb when it's highlighted. + /// + [EditorDisplay("Thumb Style"), EditorOrder(2031), Tooltip("The color of the slider thumb when it's highlighted.")] + public Color ThumbColorHighlighted { get; set; } /// - /// The color of the slider thumb when it's selected + /// 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 selected.")] + [EditorDisplay("Thumb Style"), EditorOrder(2032), Tooltip("The color of the slider thumb when it's selected.")] public Color ThumbColorSelected { get; set; } /// /// Gets or sets the brush used for slider thumb drawing. /// - [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; } /// @@ -222,6 +229,7 @@ public class Slider : ContainerControl TrackFillLineColor = style.LightBackground; ThumbColor = style.BackgroundNormal; ThumbColorSelected = style.BackgroundSelected; + ThumbColorHighlighted = style.BackgroundHighlighted; UpdateThumb(); } @@ -270,7 +278,7 @@ public class Slider : ContainerControl } // Draw thumb - var thumbColor = _isSliding ? ThumbColorSelected : ThumbColor; + var thumbColor = _isSliding ? ThumbColorSelected : (_mouseOverThumb ? ThumbColorHighlighted : ThumbColor); if (ThumbBrush != null) ThumbBrush.Draw(_thumbRect, thumbColor); else @@ -317,6 +325,7 @@ public class Slider : ContainerControl /// public override void OnMouseMove(Float2 location) { + _mouseOverThumb = _thumbRect.Contains(location); if (_isSliding) { // Update sliding From bdd182f3da273321cc3514624e1537eb5bc56c5a Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Sat, 16 Sep 2023 18:23:27 -0700 Subject: [PATCH 09/28] Fixing x64 editor/runtime on arm64 macs * This allows for a much more broader check for the dotnet sdk runtime to support running x64 binaries on arm64 --- Source/Engine/Scripting/Runtime/DotNet.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 1bcef50b8..2727593a0 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1541,7 +1541,16 @@ bool InitHostfxr() get_hostfxr_params.size = sizeof(hostfxr_initialize_parameters); get_hostfxr_params.assembly_path = libraryPath.Get(); #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 get_hostfxr_params.dotnet_root = nullptr; #endif From 76945b9144bf75ca10d913ea1cd29dd772313170 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Sun, 17 Sep 2023 14:56:22 -0700 Subject: [PATCH 10/28] MacOS Rider Support * Adding in code to properly look for rider on macOS * Also fixing an issue in the macOS CreateProcess where spaces where not properly escaped for popen(which one should likely use NSTask long term) --- .../Scripting/CodeEditors/RiderCodeEditor.cpp | 17 +++++++++++++++++ Source/Engine/Platform/Mac/MacPlatform.cpp | 3 +++ 2 files changed, 20 insertions(+) diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index e28d1e381..41b91a629 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -210,6 +210,23 @@ void RiderCodeEditor::FindEditors(Array* output) TEXT("flatpak run com.jetbrains.Rider")); #endif +#if PLATFORM_MAC + String applicationSupportFolder; + FileSystem::GetSpecialFolderPath(SpecialFolder::ProgramData, applicationSupportFolder); + + Array subMacDirectories; + + FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-0/")); + FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-1/")); + + for (auto 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 (auto directory : subDirectories) SearchDirectory(&installations, directory); diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index 46b978e1d..8cba5e1dc 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -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 StringAsANSI<> cmdLineAnsi(*cmdLine, cmdLine.Length()); FILE* pipe = popen(cmdLineAnsi.Get(), "r"); From 183ab7738fe55778ee693a4288a1d69a8f9f3bd7 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Sun, 17 Sep 2023 15:42:26 -0700 Subject: [PATCH 11/28] Fixing Flex Build issues on M1/2 macs * This resolves some issues where if you are building the actual C# dlls you also need them to be x64 based if you are targeting an x64 based target. This is a little complicated here because we set all this up ahead of time assuming that all the targets are compatible but in this case they are not so, just following what other places in the code are doing around this specifically dotnet AOT. --- Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs | 2 ++ Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index 43c54a92d..5cbb9b661 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -207,6 +207,8 @@ namespace Flax.Build case TargetPlatform.Mac: { #if USE_NETCORE + dotnetPath = Path.Combine(dotnetSdk.RootPath, "dotnet"); + 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}/"); referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/"); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index d3a706a43..c4894201b 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -212,6 +212,16 @@ namespace Flax.Build ridFallback = ""; if (string.IsNullOrEmpty(dotnetPath)) dotnetPath = "/usr/local/share/dotnet/"; + + // So there is not a real great way to do this but if the first thing in this list is x64 + // then lets assume we are building for it and thus should using that rid and that dotnet path + if (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64) + { + rid = $"osx-64"; + dotnetPath = Path.Combine(dotnetPath, "x64"); + architecture = TargetArchitecture.x64; + } + break; } default: throw new InvalidPlatformException(platform); From 69e54d7f88186ee7faf003d9bcd353b04e254aea Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Sun, 17 Sep 2023 15:53:48 -0700 Subject: [PATCH 12/28] Fixing an issue if running an x64 machine already --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index c4894201b..df7342420 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -215,7 +215,7 @@ namespace Flax.Build // So there is not a real great way to do this but if the first thing in this list is x64 // then lets assume we are building for it and thus should using that rid and that dotnet path - if (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64) + if (architecture == TargetArchitecture.ARM64 && (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64)) { rid = $"osx-64"; dotnetPath = Path.Combine(dotnetPath, "x64"); From 824ee9ec7f374a58547cc3a8e2fe4369a227420f Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Sun, 17 Sep 2023 18:48:54 -0700 Subject: [PATCH 13/28] Fixing a typo :/ --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index df7342420..fd2eb354d 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -217,7 +217,7 @@ namespace Flax.Build // then lets assume we are building for it and thus should using that rid and that dotnet path if (architecture == TargetArchitecture.ARM64 && (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64)) { - rid = $"osx-64"; + rid = $"osx-x64"; dotnetPath = Path.Combine(dotnetPath, "x64"); architecture = TargetArchitecture.x64; } From 3ac00b1e4e502dce2ee5d7f9b821bfe0131ff32e Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Sun, 17 Sep 2023 20:37:37 -0700 Subject: [PATCH 14/28] Fixing a crash when generic classes spanned across different assemblies This code attempts to resolve an issue where if you had a class in Assembly A and it was generic and you had a class that inherited from it in Assembly B it would not properly resolve these classes, also added an error check for if I was not able to find the TypeIndex then log it and return thus also preventing a crash when it goes to resolve the NativeType --- Source/Engine/Scripting/BinaryModule.cpp | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 6ffdb14a9..667f41d85 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -1009,12 +1009,37 @@ void ManagedBinaryModule::InitType(MClass* mclass) } if (baseType.Module == this) InitType(baseClass); // Ensure base is initialized before + 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) { LOG(Error, "Missing base class for managed class {0} from assembly {1}.", String(typeName), Assembly->ToString()); 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; while (true) { From 9f4429f87c8aa2f1c57c25e1eec3e51385c14e49 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Sun, 17 Sep 2023 22:26:50 -0700 Subject: [PATCH 15/28] When building the engine if you don't have iOS SDK Installed then don't build it Currently when you try and build macOS editor it assumes you also want to build iOS because of the way this check works which assumes if you have Xcode Installed you are ready to go. This really should not be the case, so instead lets check to see if you have the iophonesdk installed for your current Xcode if not then skip it. --- .../Flax.Build/Platforms/iOS/iOSPlatform.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs b/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs index 2d7a3372a..379616e5a 100644 --- a/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs @@ -1,5 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System; +using System.IO; + namespace Flax.Build.Platforms { /// @@ -11,6 +14,9 @@ namespace Flax.Build.Platforms /// public override TargetPlatform Target => TargetPlatform.iOS; + /// + public override bool HasRequiredSDKsInstalled { get; } + /// public override bool HasDynamicCodeExecutionSupport => false; @@ -21,11 +27,19 @@ namespace Flax.Build.Platforms { if (Platform.BuildTargetPlatform != TargetPlatform.Mac) return; - if (!HasRequiredSDKsInstalled) + + if (!XCode.Instance.IsValid) { Log.Warning("Missing XCode. Cannot build for iOS platform."); 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; + } } /// From 97f595922eef0199ae06f0157c3eb4ae9e0fd817 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Sun, 17 Sep 2023 23:11:23 -0700 Subject: [PATCH 16/28] Fixing broken build Have to actually set it to true --- Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs b/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs index 379616e5a..49c3ec406 100644 --- a/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs @@ -40,6 +40,8 @@ namespace Flax.Build.Platforms Log.Warning("Missing iPhoneSDK. Cannot build for iOS platform."); HasRequiredSDKsInstalled = false; } + else + HasRequiredSDKsInstalled = true; } /// From e38a8bda7a18c7d0e5dfc3408d892f4f8b3ed3b4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Sep 2023 11:26:56 +0200 Subject: [PATCH 17/28] Codestyle fix for #1425 --- Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs b/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs index 49c3ec406..d4eaa5006 100644 --- a/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/iOS/iOSPlatform.cs @@ -1,6 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System; using System.IO; namespace Flax.Build.Platforms @@ -27,7 +26,6 @@ namespace Flax.Build.Platforms { if (Platform.BuildTargetPlatform != TargetPlatform.Mac) return; - if (!XCode.Instance.IsValid) { Log.Warning("Missing XCode. Cannot build for iOS platform."); @@ -36,11 +34,12 @@ namespace Flax.Build.Platforms // 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)) { + if (string.IsNullOrEmpty(iphoneSDKPath) || !Directory.Exists(iphoneSDKPath)) + { Log.Warning("Missing iPhoneSDK. Cannot build for iOS platform."); HasRequiredSDKsInstalled = false; } - else + else HasRequiredSDKsInstalled = true; } From 79bf226fe1070fcea642a2f02b7cb049cdf43c96 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Sep 2023 12:11:28 +0200 Subject: [PATCH 18/28] Codestyle fix --- Source/Engine/Scripting/BinaryModule.cpp | 3 +-- Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs | 1 - Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 5 ++--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 667f41d85..8f1bb27d0 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -1012,8 +1012,7 @@ void ManagedBinaryModule::InitType(MClass* mclass) 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 + // 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('`'); diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index 5cbb9b661..c64ee38b3 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -208,7 +208,6 @@ namespace Flax.Build { #if USE_NETCORE dotnetPath = Path.Combine(dotnetSdk.RootPath, "dotnet"); - 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}/"); referenceAnalyzers = Path.Combine(dotnetSdk.RootPath, $"packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/"); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index fd2eb354d..dee92d384 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -213,11 +213,10 @@ namespace Flax.Build if (string.IsNullOrEmpty(dotnetPath)) dotnetPath = "/usr/local/share/dotnet/"; - // So there is not a real great way to do this but if the first thing in this list is x64 - // then lets assume we are building for it and thus should using that rid and that dotnet path + // Use x64 when cross-compiling from ARM64 if (architecture == TargetArchitecture.ARM64 && (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64)) { - rid = $"osx-x64"; + rid = "osx-x64"; dotnetPath = Path.Combine(dotnetPath, "x64"); architecture = TargetArchitecture.x64; } From 03b8b9f73f28d79485039d518d2a7713a1ee3c6b Mon Sep 17 00:00:00 2001 From: Walrusking Date: Sat, 16 Sep 2023 13:48:49 -0400 Subject: [PATCH 19/28] Fix rider search detecting any jetbrains product as rider --- Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 41b91a629..5ed73cedf 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -44,6 +44,10 @@ namespace if (document.HasParseError()) return; + // Check if this is actually rider and not another jetbrains product + if (document.FindMember("name")->value != "JetBrains Rider") + return; + // Find version auto versionMember = document.FindMember("version"); if (versionMember == document.MemberEnd()) From 4b0e70aa06ef8df3d7950d58f4c7450674673b31 Mon Sep 17 00:00:00 2001 From: Walrusking Date: Fri, 15 Sep 2023 01:31:33 -0400 Subject: [PATCH 20/28] Fixes Rider 2022 not detected when installed with toolbox --- Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 5ed73cedf..9764b06a6 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -191,6 +191,7 @@ void RiderCodeEditor::FindEditors(Array* output) SearchRegistry(&installations, HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); // Versions installed via JetBrains Toolbox + FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("Programs")); FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains\\Toolbox\\apps\\Rider\\ch-0\\")); FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains\\Toolbox\\apps\\Rider\\ch-1\\")); // Beta versions #endif From 4fe02a24e952ae90499019993ac7b2bbc960bd35 Mon Sep 17 00:00:00 2001 From: elestrago Date: Fri, 15 Sep 2023 02:35:20 +0200 Subject: [PATCH 21/28] Fix find Rider versions 2022 and later --- Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 9764b06a6..66065f0f3 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -206,6 +206,7 @@ void RiderCodeEditor::FindEditors(Array* output) FileSystem::GetChildDirectories(subDirectories, TEXT("/opt/")); // Versions installed via JetBrains Toolbox + SearchDirectory(&installations, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/rider/")); FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-0")); FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-1")); // Beta versions From 960b825b4cd7168cc12df041e92cb9b60c30fe50 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Sep 2023 12:21:12 +0200 Subject: [PATCH 22/28] Code style fix --- .../Scripting/CodeEditors/RiderCodeEditor.cpp | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 66065f0f3..05608d71e 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -24,7 +24,8 @@ namespace String version; RiderInstallation(const String& path_, const String& version_) - : path(path_), version(version_) + : path(path_) + , version(version_) { } }; @@ -145,14 +146,14 @@ bool sortInstallations(RiderInstallation* const& i1, RiderInstallation* const& i int32 version2[3] = { 0 }; StringUtils::Parse(values1[0].Get(), &version1[0]); StringUtils::Parse(values1[1].Get(), &version1[1]); - - if(values1.Count() > 2) + + if (values1.Count() > 2) StringUtils::Parse(values1[2].Get(), &version1[2]); - + StringUtils::Parse(values2[0].Get(), &version2[0]); StringUtils::Parse(values2[1].Get(), &version2[1]); - - if(values2.Count() > 2) + + if (values2.Count() > 2) StringUtils::Parse(values2[2].Get(), &version2[2]); // Compare by MAJOR.MINOR.BUILD @@ -178,7 +179,7 @@ void RiderCodeEditor::FindEditors(Array* output) String localAppDataPath; FileSystem::GetSpecialFolderPath(SpecialFolder::LocalAppData, localAppDataPath); - + #if PLATFORM_WINDOWS // Lookup from all known registry locations SearchRegistry(&installations, HKEY_CURRENT_USER, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\Rider for Unreal Engine")); @@ -219,21 +220,21 @@ void RiderCodeEditor::FindEditors(Array* output) #if PLATFORM_MAC String applicationSupportFolder; FileSystem::GetSpecialFolderPath(SpecialFolder::ProgramData, applicationSupportFolder); - - Array subMacDirectories; + Array subMacDirectories; FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-0/")); FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-1/")); - - for (auto directory : subMacDirectories) { + 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 (auto directory : subDirectories) + for (const String& directory : subDirectories) SearchDirectory(&installations, directory); // Sort found installations by version number From 10dc06be9bc9e085a83bd5c7fe6347eba6ee5c95 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Sep 2023 19:23:10 +0200 Subject: [PATCH 23/28] Fix crash if `OpenAL` internal device name is all whitespaces --- Source/Engine/Core/Types/String.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Core/Types/String.cpp b/Source/Engine/Core/Types/String.cpp index e53f04af7..98b2c08dd 100644 --- a/Source/Engine/Core/Types/String.cpp +++ b/Source/Engine/Core/Types/String.cpp @@ -298,8 +298,10 @@ String String::TrimTrailing() const end--; } - ASSERT_LOW_LAYER(end >= start); - return Substring(start, end - start + 1); + const int32 count = end - start + 1; + if (start >= 0 && start + count <= Length() && count >= 0) + return String(_data + start, count); + return Empty; } String& String::operator/=(const Char* str) From 0083ebd8871b45a3a5053f6f0258d6ed2396c7ba Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Sep 2023 09:32:37 +0200 Subject: [PATCH 24/28] Fix missing C# static fields in native scripting api --- Source/Engine/Engine/NativeInterop.Unmanaged.cs | 6 +++--- Source/Engine/Scripting/Runtime/DotNet.cpp | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index e96839546..670b7fff5 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -297,7 +297,7 @@ namespace FlaxEngine.Interop internal static void GetClassFields(ManagedHandle typeHandle, NativeFieldDefinitions** classFields, int* classFieldsCount) { Type type = Unsafe.As(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()); 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) { Type type = Unsafe.As(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()); for (int i = 0; i < properties.Length; i++) @@ -804,8 +804,8 @@ namespace FlaxEngine.Interop [UnmanagedCallersOnly] internal static IntPtr FieldGetValueBoxed(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle) { - object fieldOwner = fieldOwnerHandle.Target; FieldHolder field = Unsafe.As(fieldHandle.Target); + object fieldOwner = field.field.IsStatic ? null : fieldOwnerHandle.Target; object fieldValue = field.field.GetValue(fieldOwner); return Invoker.MarshalReturnValueGeneric(field.field.FieldType, fieldValue); } diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 8c87a5748..2157e8dff 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -905,7 +905,6 @@ const Array& MClass::GetMethods() const NativeMethodDefinitions& definition = methods[i]; MMethod* method = New(const_cast(this), StringAnsi(definition.name), definition.handle, definition.numParameters, definition.methodAttributes); _methods.Add(method); - MCore::GC::FreeMemory((void*)definition.name); } MCore::GC::FreeMemory(methods); @@ -939,7 +938,6 @@ const Array& MClass::GetFields() const NativeFieldDefinitions& definition = fields[i]; MField* field = New(const_cast(this), definition.fieldHandle, definition.name, definition.fieldType, definition.fieldAttributes); _fields.Add(field); - MCore::GC::FreeMemory((void*)definition.name); } MCore::GC::FreeMemory(fields); @@ -984,7 +982,6 @@ const Array& MClass::GetProperties() const const NativePropertyDefinitions& definition = foundProperties[i]; MProperty* property = New(const_cast(this), definition.name, definition.getterHandle, definition.setterHandle, definition.getterAttributes, definition.setterAttributes); _properties.Add(property); - MCore::GC::FreeMemory((void*)definition.name); } MCore::GC::FreeMemory(foundProperties); From bb5ff740bf7b53ebd6b324f10ba353f2f7146e46 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Sep 2023 13:34:32 +0200 Subject: [PATCH 25/28] Update `recastnavigation` lib to `1.6` --- Source/Engine/Navigation/NavMeshRuntime.cpp | 1 + .../recastnavigation/DetourAssert.h | 2 +- .../recastnavigation/DetourCommon.cpp | 9 +- .../recastnavigation/DetourCommon.h | 5 +- .../recastnavigation/DetourCrowd.cpp | 1 - .../ThirdParty/recastnavigation/DetourCrowd.h | 6 +- .../recastnavigation/DetourNavMesh.cpp | 8 +- .../recastnavigation/DetourNavMesh.h | 20 +- .../recastnavigation/DetourNavMeshQuery.cpp | 46 +- .../recastnavigation/DetourNavMeshQuery.h | 9 +- .../ThirdParty/recastnavigation/DetourNode.h | 2 +- .../recastnavigation/DetourTileCache.cpp | 5 + .../recastnavigation/DetourTileCache.h | 18 +- .../DetourTileCacheBuilder.cpp | 15 +- .../recastnavigation/DetourTileCacheBuilder.h | 4 +- Source/ThirdParty/recastnavigation/Recast.cpp | 637 ++++++------ Source/ThirdParty/recastnavigation/Recast.h | 974 ++++++++++-------- .../recastnavigation/RecastAlloc.cpp | 19 +- .../ThirdParty/recastnavigation/RecastAlloc.h | 35 +- .../recastnavigation/RecastArea.cpp | 1 - .../recastnavigation/RecastAssert.cpp | 2 +- .../recastnavigation/RecastAssert.h | 13 +- .../recastnavigation/RecastContour.cpp | 11 +- .../recastnavigation/RecastFilter.cpp | 198 ++-- .../recastnavigation/RecastLayers.cpp | 20 +- .../recastnavigation/RecastMesh.cpp | 7 +- .../recastnavigation/RecastMeshDetail.cpp | 3 +- .../recastnavigation/RecastRasterization.cpp | 664 +++++++----- .../recastnavigation/RecastRegion.cpp | 1 - 29 files changed, 1461 insertions(+), 1275 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index b09f8346a..acb73a8d5 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -21,6 +21,7 @@ namespace FORCE_INLINE void InitFilter(dtQueryFilter& filter) { Platform::MemoryCopy(filter.m_areaCost, NavMeshRuntime::NavAreasCosts, sizeof(NavMeshRuntime::NavAreasCosts)); + static_assert(sizeof(dtQueryFilter::m_areaCost) == sizeof(NavMeshRuntime::NavAreasCosts), "Invalid navmesh area cost list."); } } diff --git a/Source/ThirdParty/recastnavigation/DetourAssert.h b/Source/ThirdParty/recastnavigation/DetourAssert.h index e05fd66fa..038d538a5 100644 --- a/Source/ThirdParty/recastnavigation/DetourAssert.h +++ b/Source/ThirdParty/recastnavigation/DetourAssert.h @@ -24,7 +24,7 @@ #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) #else diff --git a/Source/ThirdParty/recastnavigation/DetourCommon.cpp b/Source/ThirdParty/recastnavigation/DetourCommon.cpp index 7abdff072..b8457828e 100644 --- a/Source/ThirdParty/recastnavigation/DetourCommon.cpp +++ b/Source/ThirdParty/recastnavigation/DetourCommon.cpp @@ -112,7 +112,7 @@ bool dtIntersectSegmentPoly2D(const float* p0, const float* p1, float& tmin, float& tmax, int& segMin, int& segMax) { - static const float EPS = 0.00000001f; + static const float EPS = 0.000001f; tmin = 0; tmax = 1; @@ -183,13 +183,6 @@ float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, floa return dx*dx + dz*dz; } -float dtDistancePtPtSqr2D(const float* pt, const float* p) -{ - float dx = pt[0] - p[0]; - float dz = pt[2] - p[2]; - return dx*dx + dz*dz; -} - void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts) { tc[0] = 0.0f; diff --git a/Source/ThirdParty/recastnavigation/DetourCommon.h b/Source/ThirdParty/recastnavigation/DetourCommon.h index a2597f3a2..d54bc7edc 100644 --- a/Source/ThirdParty/recastnavigation/DetourCommon.h +++ b/Source/ThirdParty/recastnavigation/DetourCommon.h @@ -37,7 +37,6 @@ feature to find minor members. /// Used to ignore a function parameter. VS complains about unused parameters /// and this silences the warning. -/// @param [in] _ Unused parameter template void dtIgnoreUnused(const T&) { } /// 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) /// @param[in] u The LHV 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. inline float dtVperp2D(const float* u, const float* v) @@ -417,8 +416,6 @@ bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nve float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t); -float dtDistancePtPtSqr2D(const float* pt, const float* p); - /// Derives the centroid of a convex polygon. /// @param[out] tc The centroid of the polgyon. [(x, y, z)] /// @param[in] idx The polygon indices. [(vertIndex) * @p nidx] diff --git a/Source/ThirdParty/recastnavigation/DetourCrowd.cpp b/Source/ThirdParty/recastnavigation/DetourCrowd.cpp index 3f0311f7f..d7cdae0b9 100644 --- a/Source/ThirdParty/recastnavigation/DetourCrowd.cpp +++ b/Source/ThirdParty/recastnavigation/DetourCrowd.cpp @@ -16,7 +16,6 @@ // 3. This notice may not be removed or altered from any source distribution. // -#define _USE_MATH_DEFINES #include #include #include diff --git a/Source/ThirdParty/recastnavigation/DetourCrowd.h b/Source/ThirdParty/recastnavigation/DetourCrowd.h index 952050878..854546fc1 100644 --- a/Source/ThirdParty/recastnavigation/DetourCrowd.h +++ b/Source/ThirdParty/recastnavigation/DetourCrowd.h @@ -66,7 +66,7 @@ enum CrowdAgentState { 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_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. @@ -108,7 +108,7 @@ enum MoveRequestState DT_CROWDAGENT_TARGET_REQUESTING, DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE, DT_CROWDAGENT_TARGET_WAITING_FOR_PATH, - DT_CROWDAGENT_TARGET_VELOCITY, + DT_CROWDAGENT_TARGET_VELOCITY }; /// Represents an agent managed by a #dtCrowd object. @@ -188,7 +188,7 @@ enum UpdateFlags DT_CROWD_OBSTACLE_AVOIDANCE = 2, DT_CROWD_SEPARATION = 4, 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 diff --git a/Source/ThirdParty/recastnavigation/DetourNavMesh.cpp b/Source/ThirdParty/recastnavigation/DetourNavMesh.cpp index b119cd541..2240526eb 100644 --- a/Source/ThirdParty/recastnavigation/DetourNavMesh.cpp +++ b/Source/ThirdParty/recastnavigation/DetourNavMesh.cpp @@ -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]); if (tmin > tmax) dtSwap(tmin,tmax); - link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f); - link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f); + link->bmin = (unsigned char)roundf(dtClamp(tmin, 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) { @@ -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]); if (tmin > tmax) dtSwap(tmin,tmax); - link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f); - link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f); + link->bmin = (unsigned char)roundf(dtClamp(tmin, 0.0f, 1.0f)*255.0f); + link->bmax = (unsigned char)roundf(dtClamp(tmax, 0.0f, 1.0f)*255.0f); } } } diff --git a/Source/ThirdParty/recastnavigation/DetourNavMesh.h b/Source/ThirdParty/recastnavigation/DetourNavMesh.h index 9ac1dc8d6..4c1277d78 100644 --- a/Source/ThirdParty/recastnavigation/DetourNavMesh.h +++ b/Source/ThirdParty/recastnavigation/DetourNavMesh.h @@ -99,7 +99,7 @@ static const int DT_MAX_AREAS = 64; enum dtTileFlags { /// 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. @@ -107,32 +107,32 @@ enum dtStraightPathFlags { 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_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. enum dtStraightPathOptions { 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 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 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 { - 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. DT_POLYTYPE_GROUND = 0, /// 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. dtMeshHeader* header; ///< The tile header. 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] dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount] @@ -312,8 +312,8 @@ private: }; /// Get flags for edge in detail triangle. -/// @param triFlags[in] 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] triFlags The flags for the triangle (last component of detail vertices above). +/// @param[in] edgeIndex The index of the first vertex of the edge. For instance, if 0, /// returns flags for edge AB. inline int dtGetDetailTriEdgeFlags(unsigned char triFlags, int edgeIndex) { diff --git a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp index 1e519d211..cebe26b5d 100644 --- a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp +++ b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp @@ -117,6 +117,11 @@ void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh) dtFree(navmesh); } +dtPolyQuery::~dtPolyQuery() +{ + // Defined out of line to fix the weak v-tables warning +} + ////////////////////////////////////////////////////////////////////////////////////////// /// @class dtNavMeshQuery @@ -301,11 +306,7 @@ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*fr float pt[3]; dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt); - float h = 0.0f; - dtStatus status = getPolyHeight(polyRef, pt, &h); - if (dtStatusFailed(status)) - return status; - pt[1] = h; + closestPointOnPoly(polyRef, pt, pt, NULL); dtVcopy(randomPt, pt); *randomRef = polyRef; @@ -482,25 +483,18 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f dtVcopy(&verts[j*3],v); } + const float s = frand(); + const float t = frand(); + float pt[3]; - do - { - const float s = frand(); - const float t = frand(); - dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt); - } - while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr); - - float h = 0.0f; - dtStatus stat = getPolyHeight(randomPolyRef, pt, &h); - if (dtStatusFailed(status)) - return stat; - pt[1] = h; + dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt); + + closestPointOnPoly(randomPolyRef, pt, pt, NULL); dtVcopy(randomPt, pt); *randomRef = randomPolyRef; - return DT_SUCCESS; + return status; } @@ -641,6 +635,8 @@ public: { } + virtual ~dtFindNearestPolyQuery(); + dtPolyRef nearestRef() const { return m_nearestRef; } const float* nearestPoint() const { return m_nearestPoint; } bool isOverPoly() const { return m_overPoly; } @@ -683,6 +679,11 @@ public: } }; +dtFindNearestPolyQuery::~dtFindNearestPolyQuery() +{ + // Defined out of line to fix the weak v-tables warning +} + /// @par /// /// @note If the search box does not intersect any polygons the search will @@ -858,6 +859,8 @@ public: { } + virtual ~dtCollectPolysQuery(); + int numCollected() const { return m_numCollected; } bool overflowed() const { return m_overflow; } @@ -879,6 +882,11 @@ public: } }; +dtCollectPolysQuery::~dtCollectPolysQuery() +{ + // Defined out of line to fix the weak v-tables warning +} + /// @par /// /// If no polygons are found, the function will return #DT_SUCCESS with a diff --git a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.h b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.h index 43c7268f2..244b20065 100644 --- a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.h +++ b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.h @@ -153,7 +153,7 @@ struct dtRaycastHit class dtPolyQuery { public: - virtual ~dtPolyQuery() { } + virtual ~dtPolyQuery(); /// 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. @@ -176,7 +176,7 @@ public: dtStatus init(const dtNavMesh* nav, const int maxNodes); /// @name Standard Pathfinding Functions - // /@{ + /// @{ /// Finds a path from the start polygon to the end 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 /// the start of the ray. [(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] 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] pathCount The number of visited polygons. [opt] /// @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)] /// @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] 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[in] prevRef parent of start ref. Used during for cost calculation [opt] /// @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. /// @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] maxRadius The radius of the search circle. [Units: wu] /// @param[in] filter The polygon filter to apply to the query. /// @param[in] frand Function returning a random number [0..1). /// @param[out] randomRef The reference id of the random location. diff --git a/Source/ThirdParty/recastnavigation/DetourNode.h b/Source/ThirdParty/recastnavigation/DetourNode.h index db0974708..8918d4686 100644 --- a/Source/ThirdParty/recastnavigation/DetourNode.h +++ b/Source/ThirdParty/recastnavigation/DetourNode.h @@ -25,7 +25,7 @@ enum dtNodeFlags { DT_NODE_OPEN = 0x01, 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; diff --git a/Source/ThirdParty/recastnavigation/DetourTileCache.cpp b/Source/ThirdParty/recastnavigation/DetourTileCache.cpp index a82cd1350..6f97ab5b7 100644 --- a/Source/ThirdParty/recastnavigation/DetourTileCache.cpp +++ b/Source/ThirdParty/recastnavigation/DetourTileCache.cpp @@ -239,6 +239,11 @@ const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref) 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) { // Make sure the data is in right format. diff --git a/Source/ThirdParty/recastnavigation/DetourTileCache.h b/Source/ThirdParty/recastnavigation/DetourTileCache.h index 75713366d..0d346f17c 100644 --- a/Source/ThirdParty/recastnavigation/DetourTileCache.h +++ b/Source/ThirdParty/recastnavigation/DetourTileCache.h @@ -3,16 +3,13 @@ #include "DetourStatus.h" - - typedef unsigned int dtObstacleRef; - typedef unsigned int dtCompressedTileRef; /// Flags for addTile 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 @@ -32,14 +29,14 @@ enum ObstacleState DT_OBSTACLE_EMPTY, DT_OBSTACLE_PROCESSING, DT_OBSTACLE_PROCESSED, - DT_OBSTACLE_REMOVING, + DT_OBSTACLE_REMOVING }; enum ObstacleType { DT_OBSTACLE_CYLINDER, DT_OBSTACLE_BOX, // AABB - DT_OBSTACLE_ORIENTED_BOX, // OBB + DT_OBSTACLE_ORIENTED_BOX // OBB }; struct dtObstacleCylinder @@ -97,13 +94,10 @@ struct dtTileCacheParams struct dtTileCacheMeshProcess { - virtual ~dtTileCacheMeshProcess() { } - - virtual void process(struct dtNavMeshCreateParams* params, - unsigned char* polyAreas, unsigned short* polyFlags) = 0; + virtual ~dtTileCacheMeshProcess(); + virtual void process(struct dtNavMeshCreateParams* params, unsigned char* polyAreas, unsigned short* polyFlags) = 0; }; - class dtTileCache { public: @@ -219,7 +213,7 @@ private: enum ObstacleRequestAction { REQUEST_ADD, - REQUEST_REMOVE, + REQUEST_REMOVE }; struct ObstacleRequest diff --git a/Source/ThirdParty/recastnavigation/DetourTileCacheBuilder.cpp b/Source/ThirdParty/recastnavigation/DetourTileCacheBuilder.cpp index 21a59f1a6..dbc09eb48 100644 --- a/Source/ThirdParty/recastnavigation/DetourTileCacheBuilder.cpp +++ b/Source/ThirdParty/recastnavigation/DetourTileCacheBuilder.cpp @@ -23,6 +23,15 @@ #include "DetourTileCacheBuilder.h" #include +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 dtFixedArray { @@ -881,7 +890,7 @@ static bool buildMeshAdjacency(dtTileCacheAlloc* alloc, const dtTileCacheContourSet& lcset) { // 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; dtFixedArray 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) { // Count number of polygons to remove. - int numRemovedVerts = 0; int numTouchedVerts = 0; int numRemainingEdges = 0; for (int i = 0; i < mesh.npolys; ++i) @@ -1419,7 +1427,6 @@ static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem) } if (numRemoved) { - numRemovedVerts += numRemoved; numRemainingEdges += numVerts-(numRemoved+1); } } @@ -1551,7 +1558,7 @@ static dtStatus removeVertex(dtTileCachePolyMesh& mesh, const unsigned short rem } // 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+1] = mesh.verts[(i+1)*3+1]; diff --git a/Source/ThirdParty/recastnavigation/DetourTileCacheBuilder.h b/Source/ThirdParty/recastnavigation/DetourTileCacheBuilder.h index ff6109193..9a6844c65 100644 --- a/Source/ThirdParty/recastnavigation/DetourTileCacheBuilder.h +++ b/Source/ThirdParty/recastnavigation/DetourTileCacheBuilder.h @@ -78,7 +78,7 @@ struct dtTileCachePolyMesh struct dtTileCacheAlloc { - virtual ~dtTileCacheAlloc() {} + virtual ~dtTileCacheAlloc(); virtual void reset() {} @@ -95,7 +95,7 @@ struct dtTileCacheAlloc struct dtTileCacheCompressor { - virtual ~dtTileCacheCompressor() { } + virtual ~dtTileCacheCompressor(); virtual int maxCompressedSize(const int bufferSize) = 0; virtual dtStatus compress(const unsigned char* buffer, const int bufferSize, diff --git a/Source/ThirdParty/recastnavigation/Recast.cpp b/Source/ThirdParty/recastnavigation/Recast.cpp index 1b71710cd..d75a9f59f 100644 --- a/Source/ThirdParty/recastnavigation/Recast.cpp +++ b/Source/ThirdParty/recastnavigation/Recast.cpp @@ -16,98 +16,93 @@ // 3. This notice may not be removed or altered from any source distribution. // -#include -#define _USE_MATH_DEFINES -#include -#include -#include -#include -#include #include "Recast.h" #include "RecastAlloc.h" #include "RecastAssert.h" +#include +#include +#include +#include + namespace { /// Allocates and constructs an object of the given type, returning a pointer. -/// TODO: Support constructor args. -/// @param[in] hint Hint to the allocator. -template -T* rcNew(rcAllocHint hint) { - T* ptr = (T*)rcAlloc(sizeof(T), hint); +/// @param[in] allocLifetime Allocation lifetime hint +template +T* rcNew(const rcAllocHint allocLifetime) +{ + T* ptr = (T*)rcAlloc(sizeof(T), allocLifetime); ::new(rcNewTag(), (void*)ptr) T(); return ptr; } /// Destroys and frees an object allocated with rcNew. /// @param[in] ptr The object pointer to delete. -template -void rcDelete(T* ptr) { - if (ptr) { +template +void rcDelete(T* ptr) +{ + if (ptr) + { ptr->~T(); rcFree((void*)ptr); } } -} // namespace - +} // anonymous namespace float rcSqrt(float 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, ...) { if (!m_logEnabled) + { return; + } static const int MSG_SIZE = 512; char msg[MSG_SIZE]; - va_list ap; - va_start(ap, format); - int len = vsnprintf(msg, MSG_SIZE, format, ap); + va_list argList; + va_start(argList, format); + int len = vsnprintf(msg, MSG_SIZE, format, argList); if (len >= MSG_SIZE) { - len = MSG_SIZE-1; - msg[MSG_SIZE-1] = '\0'; + len = MSG_SIZE - 1; + 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); } +void rcContext::doResetLog() +{ + // Defined out of line to fix the weak v-tables warning +} + rcHeightfield* rcAllocHeightfield() { return rcNew(RC_ALLOC_PERM); } + +void rcFreeHeightField(rcHeightfield* heightfield) +{ + rcDelete(heightfield); +} + rcHeightfield::rcHeightfield() - : width() - , height() - , bmin() - , bmax() - , cs() - , ch() - , spans() - , pools() - , freelist() +: width() +, height() +, bmin() +, bmax() +, cs() +, ch() +, spans() +, pools() +, freelist() { } @@ -124,40 +119,36 @@ rcHeightfield::~rcHeightfield() } } -void rcFreeHeightField(rcHeightfield* hf) -{ - rcDelete(hf); -} - rcCompactHeightfield* rcAllocCompactHeightfield() { return rcNew(RC_ALLOC_PERM); } -void rcFreeCompactHeightfield(rcCompactHeightfield* chf) +void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield) { - rcDelete(chf); + rcDelete(compactHeightfield); } rcCompactHeightfield::rcCompactHeightfield() - : width(), - height(), - spanCount(), - walkableHeight(), - walkableClimb(), - borderSize(), - maxDistance(), - maxRegions(), - bmin(), - bmax(), - cs(), - ch(), - cells(), - spans(), - dist(), - areas() +: width() +, height() +, spanCount() +, walkableHeight() +, walkableClimb() +, borderSize() +, maxDistance() +, maxRegions() +, bmin() +, bmax() +, cs() +, ch() +, cells() +, spans() +, dist() +, areas() { } + rcCompactHeightfield::~rcCompactHeightfield() { rcFree(cells); @@ -170,13 +161,18 @@ rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet() { return rcNew(RC_ALLOC_PERM); } -void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset) + +void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* layerSet) { - rcDelete(lset); + rcDelete(layerSet); } rcHeightfieldLayerSet::rcHeightfieldLayerSet() - : layers(), nlayers() {} +: layers() +, nlayers() +{ +} + rcHeightfieldLayerSet::~rcHeightfieldLayerSet() { for (int i = 0; i < nlayers; ++i) @@ -193,22 +189,26 @@ rcContourSet* rcAllocContourSet() { return rcNew(RC_ALLOC_PERM); } -void rcFreeContourSet(rcContourSet* cset) + +void rcFreeContourSet(rcContourSet* contourSet) { - rcDelete(cset); + rcDelete(contourSet); } rcContourSet::rcContourSet() - : conts(), - nconts(), - bmin(), - bmax(), - cs(), - ch(), - width(), - height(), - borderSize(), - maxError() {} +: conts() +, nconts() +, bmin() +, bmax() +, cs() +, ch() +, width() +, height() +, borderSize() +, maxError() +{ +} + rcContourSet::~rcContourSet() { for (int i = 0; i < nconts; ++i) @@ -219,32 +219,34 @@ rcContourSet::~rcContourSet() rcFree(conts); } - rcPolyMesh* rcAllocPolyMesh() { return rcNew(RC_ALLOC_PERM); } -void rcFreePolyMesh(rcPolyMesh* pmesh) + +void rcFreePolyMesh(rcPolyMesh* polyMesh) { - rcDelete(pmesh); + rcDelete(polyMesh); } rcPolyMesh::rcPolyMesh() - : verts(), - polys(), - regs(), - flags(), - areas(), - nverts(), - npolys(), - maxpolys(), - nvp(), - bmin(), - bmax(), - cs(), - ch(), - borderSize(), - maxEdgeError() {} +: verts() +, polys() +, regs() +, flags() +, areas() +, nverts() +, npolys() +, maxpolys() +, nvp() +, bmin() +, bmax() +, cs() +, ch() +, borderSize() +, maxEdgeError() +{ +} rcPolyMesh::~rcPolyMesh() { @@ -257,319 +259,284 @@ rcPolyMesh::~rcPolyMesh() rcPolyMeshDetail* rcAllocPolyMeshDetail() { - rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM); - memset(dmesh, 0, sizeof(rcPolyMeshDetail)); - return dmesh; + return rcNew(RC_ALLOC_PERM); } -void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh) +void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh) { - if (!dmesh) return; - rcFree(dmesh->meshes); - rcFree(dmesh->verts); - rcFree(dmesh->tris); - rcFree(dmesh); + if (detailMesh == NULL) + { + return; + } + 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. - rcVcopy(bmin, verts); - rcVcopy(bmax, verts); - for (int i = 1; i < nv; ++i) + rcVcopy(minBounds, verts); + rcVcopy(maxBounds, verts); + for (int i = 1; i < numVerts; ++i) { - const float* v = &verts[i*3]; - rcVmin(bmin, v); - rcVmax(bmax, v); + const float* v = &verts[i * 3]; + rcVmin(minBounds, 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); - *h = (int)((bmax[2] - bmin[2])/cs+0.5f); + *sizeX = (int)((maxBounds[0] - minBounds[0]) / cellSize + 0.5f); + *sizeZ = (int)((maxBounds[2] - minBounds[2]) / cellSize + 0.5f); } -/// @par -/// -/// See the #rcConfig documentation for more information on the configuration parameters. -/// -/// @see rcAllocHeightfield, rcHeightfield -bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, - const float* bmin, const float* bmax, - float cs, float ch) +bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ, + const float* minBounds, const float* maxBounds, + float cellSize, float cellHeight) { - rcIgnoreUnused(ctx); - - hf.width = width; - hf.height = height; - rcVcopy(hf.bmin, bmin); - rcVcopy(hf.bmax, bmax); - hf.cs = cs; - hf.ch = ch; - hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM); - if (!hf.spans) + rcIgnoreUnused(context); + + heightfield.width = sizeX; + heightfield.height = sizeZ; + rcVcopy(heightfield.bmin, minBounds); + rcVcopy(heightfield.bmax, maxBounds); + heightfield.cs = cellSize; + heightfield.ch = cellHeight; + heightfield.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*) * heightfield.width * heightfield.height, RC_ALLOC_PERM); + if (!heightfield.spans) + { return false; - memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height); + } + memset(heightfield.spans, 0, sizeof(rcSpan*) * heightfield.width * heightfield.height); 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]; rcVsub(e0, v1, v0); rcVsub(e1, v2, v0); - rcVcross(norm, e0, e1); - rcVnormalize(norm); + rcVcross(faceNormal, e0, e1); + rcVnormalize(faceNormal); } -/// @par -/// -/// Only sets the area id's for the walkable triangles. Does not alter the -/// area id's for unwalkable triangles. -/// -/// 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) +void rcMarkWalkableTriangles(rcContext* context, const float walkableSlopeAngle, + const float* verts, const int numVerts, + const int* tris, const int numTris, + unsigned char* triAreaIDs) { - rcIgnoreUnused(ctx); - rcIgnoreUnused(nv); - - const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); + rcIgnoreUnused(context); + rcIgnoreUnused(numVerts); + + const float walkableThr = cosf(walkableSlopeAngle / 180.0f * RC_PI); float norm[3]; - - for (int i = 0; i < nt; ++i) + + 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], norm); + 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_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; } -/// @par -/// -/// 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) +bool rcBuildCompactHeightfield(rcContext* context, const int walkableHeight, const int walkableClimb, + const rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield) { - rcAssert(ctx); - - rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD); - - const int w = hf.width; - const int h = hf.height; - const int spanCount = rcGetHeightFieldSpanCount(ctx, hf); + rcAssert(context); + + rcScopedTimer timer(context, RC_TIMER_BUILD_COMPACTHEIGHTFIELD); + + const int xSize = heightfield.width; + const int zSize = heightfield.height; + const int spanCount = rcGetHeightFieldSpanCount(context, heightfield); // Fill in header. - chf.width = w; - chf.height = h; - chf.spanCount = spanCount; - chf.walkableHeight = walkableHeight; - chf.walkableClimb = walkableClimb; - chf.maxRegions = 0; - rcVcopy(chf.bmin, hf.bmin); - rcVcopy(chf.bmax, hf.bmax); - chf.bmax[1] += walkableHeight*hf.ch; - chf.cs = hf.cs; - chf.ch = hf.ch; - chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM); - if (!chf.cells) + compactHeightfield.width = xSize; + compactHeightfield.height = zSize; + compactHeightfield.spanCount = spanCount; + compactHeightfield.walkableHeight = walkableHeight; + compactHeightfield.walkableClimb = walkableClimb; + compactHeightfield.maxRegions = 0; + rcVcopy(compactHeightfield.bmin, heightfield.bmin); + rcVcopy(compactHeightfield.bmax, heightfield.bmax); + compactHeightfield.bmax[1] += walkableHeight * heightfield.ch; + compactHeightfield.cs = heightfield.cs; + compactHeightfield.ch = heightfield.ch; + compactHeightfield.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell) * xSize * zSize, RC_ALLOC_PERM); + 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; } - memset(chf.cells, 0, sizeof(rcCompactCell)*w*h); - chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM); - if (!chf.spans) + memset(compactHeightfield.cells, 0, sizeof(rcCompactCell) * xSize * zSize); + compactHeightfield.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan) * spanCount, RC_ALLOC_PERM); + 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; } - memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount); - chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM); - if (!chf.areas) + memset(compactHeightfield.spans, 0, sizeof(rcCompactSpan) * spanCount); + compactHeightfield.areas = (unsigned char*)rcAlloc(sizeof(unsigned char) * spanCount, RC_ALLOC_PERM); + 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; } - 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; - + // Fill in cells and spans. - int idx = 0; - for (int y = 0; y < h; ++y) + int currentCellIndex = 0; + 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]; - // 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]; - c.index = idx; - c.count = 0; - while (s) + continue; + } + + rcCompactCell& cell = compactHeightfield.cells[columnIndex]; + cell.index = currentCellIndex; + cell.count = 0; + + for (; span != NULL; span = span->next) + { + if (span->area != RC_NULL_AREA) { - if (s->area != RC_NULL_AREA) - { - const int bot = (int)s->smax; - const int top = s->next ? (int)s->next->smin : MAX_HEIGHT; - chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff); - chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff); - chf.areas[idx] = s->area; - idx++; - c.count++; - } - s = s->next; + const int bot = (int)span->smax; + const int top = span->next ? (int)span->next->smin : MAX_HEIGHT; + compactHeightfield.spans[currentCellIndex].y = (unsigned short)rcClamp(bot, 0, 0xffff); + compactHeightfield.spans[currentCellIndex].h = (unsigned char)rcClamp(top - bot, 0, 0xff); + compactHeightfield.areas[currentCellIndex] = span->area; + currentCellIndex++; + cell.count++; } } } - + // Find neighbour connections. - const int MAX_LAYERS = RC_NOT_CONNECTED-1; - int tooHighNeighbour = 0; - for (int y = 0; y < h; ++y) + const int MAX_LAYERS = RC_NOT_CONNECTED - 1; + int maxLayerIndex = 0; + 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]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + const rcCompactCell& cell = compactHeightfield.cells[x + z * zStride]; + 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) { - rcSetCon(s, dir, RC_NOT_CONNECTED); - const int nx = x + rcGetDirOffsetX(dir); - const int ny = y + rcGetDirOffsetY(dir); + rcSetCon(span, dir, RC_NOT_CONNECTED); + const int neighborX = x + rcGetDirOffsetX(dir); + const int neighborZ = z + rcGetDirOffsetY(dir); // 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; - + } + // Iterate over all neighbour spans and check if any of the is // accessible from current cell. - const rcCompactCell& nc = chf.cells[nx+ny*w]; - for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k) + const rcCompactCell& neighborCell = compactHeightfield.cells[neighborX + neighborZ * zStride]; + for (int k = (int)neighborCell.index, nk = (int)(neighborCell.index + neighborCell.count); k < nk; ++k) { - const rcCompactSpan& ns = chf.spans[k]; - const int bot = rcMax(s.y, ns.y); - const int top = rcMin(s.y+s.h, ns.y+ns.h); + const rcCompactSpan& neighborSpan = compactHeightfield.spans[k]; + const int bot = rcMax(span.y, neighborSpan.y); + const int top = rcMin(span.y + span.h, neighborSpan.y + neighborSpan.h); // Check that the gap between the spans is walkable, // 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. - const int lidx = k - (int)nc.index; - if (lidx < 0 || lidx > MAX_LAYERS) + const int layerIndex = k - (int)neighborCell.index; + if (layerIndex < 0 || layerIndex > MAX_LAYERS) { - tooHighNeighbour = rcMax(tooHighNeighbour, lidx); + maxLayerIndex = rcMax(maxLayerIndex, layerIndex); continue; } - rcSetCon(s, dir, lidx); + rcSetCon(span, dir, layerIndex); break; } } - } } } } - - if (tooHighNeighbour > MAX_LAYERS) + + if (maxLayerIndex > MAX_LAYERS) { - ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)", - tooHighNeighbour, MAX_LAYERS); + context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)", + maxLayerIndex, MAX_LAYERS); } - + 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; -} -*/ diff --git a/Source/ThirdParty/recastnavigation/Recast.h b/Source/ThirdParty/recastnavigation/Recast.h index 4d557389b..9def8fd2a 100644 --- a/Source/ThirdParty/recastnavigation/Recast.h +++ b/Source/ThirdParty/recastnavigation/Recast.h @@ -22,13 +22,16 @@ /// The value of PI used by Recast. static const float RC_PI = 3.14159265f; +/// Used to ignore unused function parameters and silence any compiler warnings. +template void rcIgnoreUnused(const T&) { } + /// Recast log categories. /// @see rcContext enum rcLogCategory { RC_LOG_PROGRESS = 1, ///< A progress log entry. RC_LOG_WARNING, ///< A warning log entry. - RC_LOG_ERROR, ///< An error log entry. + RC_LOG_ERROR ///< An error log entry. }; /// Recast performance timer categories. @@ -97,12 +100,21 @@ enum rcTimerLabel /// Provides an interface for optional logging and performance tracking of the Recast /// build process. +/// +/// 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. +/// /// @ingroup recast class rcContext { public: - - /// Contructor. + /// Constructor. /// @param[in] state TRUE if the logging and performance timers should be enabled. [Default: true] inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {} virtual ~rcContext() {} @@ -115,56 +127,62 @@ public: inline void resetLog() { if (m_logEnabled) doResetLog(); } /// Logs a message. - /// @param[in] category The category of the message. - /// @param[in] format The message. + /// + /// 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 + /// + /// @param[in] category The category of the message. + /// @param[in] format The message. void log(const rcLogCategory category, const char* format, ...); /// Enables or disables the performance timers. /// @param[in] state TRUE if timers should be enabled. inline void enableTimer(bool state) { m_timerEnabled = state; } - /// Clears all peformance timers. (Resets all to unused.) + /// Clears all performance timers. (Resets all to unused.) inline void resetTimers() { if (m_timerEnabled) doResetTimers(); } /// Starts the specified performance timer. - /// @param label The category of the timer. + /// @param label The category of the timer. inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); } /// Stops the specified performance timer. - /// @param label The category of the timer. + /// @param label The category of the timer. inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); } /// Returns the total accumulated time of the specified performance timer. - /// @param label The category of the timer. - /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. + /// @param label The category of the timer. + /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; } protected: - /// Clears all log entries. - virtual void doResetLog() {} + virtual void doResetLog(); /// Logs a message. - /// @param[in] category The category of the message. - /// @param[in] msg The formatted message. - /// @param[in] len The length of the formatted message. - virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {} + /// @param[in] category The category of the message. + /// @param[in] msg The formatted message. + /// @param[in] len The length of the formatted message. + virtual void doLog(const rcLogCategory category, const char* msg, const int len) { rcIgnoreUnused(category); rcIgnoreUnused(msg); rcIgnoreUnused(len); } /// Clears all timers. (Resets all to unused.) virtual void doResetTimers() {} /// Starts the specified performance timer. - /// @param[in] label The category of timer. - virtual void doStartTimer(const rcTimerLabel /*label*/) {} + /// @param[in] label The category of timer. + virtual void doStartTimer(const rcTimerLabel label) { rcIgnoreUnused(label); } /// Stops the specified performance timer. - /// @param[in] label The category of the timer. - virtual void doStopTimer(const rcTimerLabel /*label*/) {} + /// @param[in] label The category of the timer. + virtual void doStopTimer(const rcTimerLabel label) { rcIgnoreUnused(label); } /// Returns the total accumulated time of the specified performance timer. - /// @param[in] label The category of the timer. - /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. - virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; } + /// @param[in] label The category of the timer. + /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. + virtual int doGetAccumulatedTime(const rcTimerLabel label) const { rcIgnoreUnused(label); return -1; } /// True if logging is enabled. bool m_logEnabled; @@ -238,7 +256,7 @@ struct rcConfig /// The maximum allowed length for contour edges along the border of the mesh. [Limit: >=0] [Units: vx] int maxEdgeLen; - /// The maximum distance a simplfied contour's border edges should deviate + /// The maximum distance a simplified contour's border edges should deviate /// the original raw contour. [Limit: >=0] [Units: vx] float maxSimplificationError; @@ -334,6 +352,7 @@ struct rcCompactHeightfield { rcCompactHeightfield(); ~rcCompactHeightfield(); + int width; ///< The width of the heightfield. (Along the x-axis in cell units.) int height; ///< The height of the heightfield. (Along the z-axis in cell units.) int spanCount; ///< The number of spans in the heightfield. @@ -350,6 +369,11 @@ struct rcCompactHeightfield rcCompactSpan* spans; ///< Array of spans. [Size: #spanCount] unsigned short* dist; ///< Array containing border distance data. [Size: #spanCount] unsigned char* areas; ///< Array containing area id data. [Size: #spanCount] + +private: + // Explicitly-disabled copy constructor and copy assignment operator. + rcCompactHeightfield(const rcCompactHeightfield&); + rcCompactHeightfield& operator=(const rcCompactHeightfield&); }; /// Represents a heightfield layer within a layer set. @@ -380,8 +404,14 @@ struct rcHeightfieldLayerSet { rcHeightfieldLayerSet(); ~rcHeightfieldLayerSet(); + rcHeightfieldLayer* layers; ///< The layers in the set. [Size: #nlayers] int nlayers; ///< The number of layers in the set. + +private: + // Explicitly-disabled copy constructor and copy assignment operator. + rcHeightfieldLayerSet(const rcHeightfieldLayerSet&); + rcHeightfieldLayerSet& operator=(const rcHeightfieldLayerSet&); }; /// Represents a simple, non-overlapping contour in field space. @@ -401,6 +431,7 @@ struct rcContourSet { rcContourSet(); ~rcContourSet(); + rcContour* conts; ///< An array of the contours in the set. [Size: #nconts] int nconts; ///< The number of contours in the set. float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)] @@ -411,6 +442,11 @@ struct rcContourSet int height; ///< The height of the set. (Along the z-axis in cell units.) int borderSize; ///< The AABB border size used to generate the source data from which the contours were derived. float maxError; ///< The max edge error that this contour set was simplified with. + +private: + // Explicitly-disabled copy constructor and copy assignment operator. + rcContourSet(const rcContourSet&); + rcContourSet& operator=(const rcContourSet&); }; /// Represents a polygon mesh suitable for use in building a navigation mesh. @@ -419,6 +455,7 @@ struct rcPolyMesh { rcPolyMesh(); ~rcPolyMesh(); + unsigned short* verts; ///< The mesh vertices. [Form: (x, y, z) * #nverts] unsigned short* polys; ///< Polygon and neighbor data. [Length: #maxpolys * 2 * #nvp] unsigned short* regs; ///< The region id assigned to each polygon. [Length: #maxpolys] @@ -434,6 +471,11 @@ struct rcPolyMesh float ch; ///< The height of each cell. (The minimum increment along the y-axis.) int borderSize; ///< The AABB border size used to generate the source data from which the mesh was derived. float maxEdgeError; ///< The max error of the polygon edges in the mesh. + +private: + // Explicitly-disabled copy constructor and copy assignment operator. + rcPolyMesh(const rcPolyMesh&); + rcPolyMesh& operator=(const rcPolyMesh&); }; /// Contains triangle meshes that represent detailed height data associated @@ -441,12 +483,19 @@ struct rcPolyMesh /// @ingroup recast struct rcPolyMeshDetail { + rcPolyMeshDetail(); + unsigned int* meshes; ///< The sub-mesh data. [Size: 4*#nmeshes] float* verts; ///< The mesh vertices. [Size: 3*#nverts] unsigned char* tris; ///< The mesh triangles. [Size: 4*#ntris] int nmeshes; ///< The number of sub-meshes defined by #meshes. int nverts; ///< The number of vertices in #verts. int ntris; ///< The number of triangles in #tris. + +private: + // Explicitly-disabled copy constructor and copy assignment operator. + rcPolyMeshDetail(const rcPolyMeshDetail&); + rcPolyMeshDetail& operator=(const rcPolyMeshDetail&); }; /// @name Allocation Functions @@ -455,82 +504,82 @@ struct rcPolyMeshDetail /// @{ /// Allocates a heightfield object using the Recast allocator. -/// @return A heightfield that is ready for initialization, or null on failure. -/// @ingroup recast -/// @see rcCreateHeightfield, rcFreeHeightField +/// @return A heightfield that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcCreateHeightfield, rcFreeHeightField rcHeightfield* rcAllocHeightfield(); /// Frees the specified heightfield object using the Recast allocator. -/// @param[in] hf A heightfield allocated using #rcAllocHeightfield -/// @ingroup recast -/// @see rcAllocHeightfield -void rcFreeHeightField(rcHeightfield* hf); +/// @param[in] heightfield A heightfield allocated using #rcAllocHeightfield +/// @ingroup recast +/// @see rcAllocHeightfield +void rcFreeHeightField(rcHeightfield* heightfield); /// Allocates a compact heightfield object using the Recast allocator. -/// @return A compact heightfield that is ready for initialization, or null on failure. -/// @ingroup recast -/// @see rcBuildCompactHeightfield, rcFreeCompactHeightfield +/// @return A compact heightfield that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildCompactHeightfield, rcFreeCompactHeightfield rcCompactHeightfield* rcAllocCompactHeightfield(); /// Frees the specified compact heightfield object using the Recast allocator. -/// @param[in] chf A compact heightfield allocated using #rcAllocCompactHeightfield -/// @ingroup recast -/// @see rcAllocCompactHeightfield -void rcFreeCompactHeightfield(rcCompactHeightfield* chf); +/// @param[in] compactHeightfield A compact heightfield allocated using #rcAllocCompactHeightfield +/// @ingroup recast +/// @see rcAllocCompactHeightfield +void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield); /// Allocates a heightfield layer set using the Recast allocator. -/// @return A heightfield layer set that is ready for initialization, or null on failure. -/// @ingroup recast -/// @see rcBuildHeightfieldLayers, rcFreeHeightfieldLayerSet +/// @return A heightfield layer set that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildHeightfieldLayers, rcFreeHeightfieldLayerSet rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet(); /// Frees the specified heightfield layer set using the Recast allocator. -/// @param[in] lset A heightfield layer set allocated using #rcAllocHeightfieldLayerSet -/// @ingroup recast -/// @see rcAllocHeightfieldLayerSet -void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset); +/// @param[in] layerSet A heightfield layer set allocated using #rcAllocHeightfieldLayerSet +/// @ingroup recast +/// @see rcAllocHeightfieldLayerSet +void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* layerSet); /// Allocates a contour set object using the Recast allocator. -/// @return A contour set that is ready for initialization, or null on failure. -/// @ingroup recast -/// @see rcBuildContours, rcFreeContourSet +/// @return A contour set that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildContours, rcFreeContourSet rcContourSet* rcAllocContourSet(); /// Frees the specified contour set using the Recast allocator. -/// @param[in] cset A contour set allocated using #rcAllocContourSet -/// @ingroup recast -/// @see rcAllocContourSet -void rcFreeContourSet(rcContourSet* cset); +/// @param[in] contourSet A contour set allocated using #rcAllocContourSet +/// @ingroup recast +/// @see rcAllocContourSet +void rcFreeContourSet(rcContourSet* contourSet); /// Allocates a polygon mesh object using the Recast allocator. -/// @return A polygon mesh that is ready for initialization, or null on failure. -/// @ingroup recast -/// @see rcBuildPolyMesh, rcFreePolyMesh +/// @return A polygon mesh that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildPolyMesh, rcFreePolyMesh rcPolyMesh* rcAllocPolyMesh(); /// Frees the specified polygon mesh using the Recast allocator. -/// @param[in] pmesh A polygon mesh allocated using #rcAllocPolyMesh -/// @ingroup recast -/// @see rcAllocPolyMesh -void rcFreePolyMesh(rcPolyMesh* pmesh); +/// @param[in] polyMesh A polygon mesh allocated using #rcAllocPolyMesh +/// @ingroup recast +/// @see rcAllocPolyMesh +void rcFreePolyMesh(rcPolyMesh* polyMesh); /// Allocates a detail mesh object using the Recast allocator. -/// @return A detail mesh that is ready for initialization, or null on failure. -/// @ingroup recast -/// @see rcBuildPolyMeshDetail, rcFreePolyMeshDetail +/// @return A detail mesh that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildPolyMeshDetail, rcFreePolyMeshDetail rcPolyMeshDetail* rcAllocPolyMeshDetail(); /// Frees the specified detail mesh using the Recast allocator. -/// @param[in] dmesh A detail mesh allocated using #rcAllocPolyMeshDetail -/// @ingroup recast -/// @see rcAllocPolyMeshDetail -void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh); +/// @param[in] detailMesh A detail mesh allocated using #rcAllocPolyMeshDetail +/// @ingroup recast +/// @see rcAllocPolyMeshDetail +void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh); /// @} -/// Heighfield border flag. +/// Heightfield border flag. /// If a heightfield region ID has this bit set, then the region is a border -/// region and its spans are considered unwalkable. +/// region and its spans are considered un-walkable. /// (Used during the region and contour build process.) /// @see rcCompactSpan::reg static const unsigned short RC_BORDER_REG = 0x8000; @@ -564,7 +613,7 @@ static const int RC_AREA_BORDER = 0x20000; enum rcBuildContoursFlags { RC_CONTOUR_TESS_WALL_EDGES = 0x01, ///< Tessellate solid (impassable) edges during contour simplification. - RC_CONTOUR_TESS_AREA_EDGES = 0x02, ///< Tessellate edges between areas during contour simplification. + RC_CONTOUR_TESS_AREA_EDGES = 0x02 ///< Tessellate edges between areas during contour simplification. }; /// Applied to the region id field of contour vertices in order to extract the region id. @@ -580,7 +629,7 @@ static const unsigned short RC_MESH_NULL_IDX = 0xffff; /// Represents the null area. /// When a data element is given this value it is considered to no longer be -/// assigned to a usable area. (E.g. It is unwalkable.) +/// assigned to a usable area. (E.g. It is un-walkable.) static const unsigned char RC_NULL_AREA = 0; /// The default area id used to indicate a walkable polygon. @@ -595,44 +644,42 @@ static const int RC_NOT_CONNECTED = 0x3f; /// @name General helper functions /// @{ -/// Used to ignore a function parameter. VS complains about unused parameters -/// and this silences the warning. -/// @param [in] _ Unused parameter -template void rcIgnoreUnused(const T&) { } - /// Swaps the values of the two parameters. -/// @param[in,out] a Value A -/// @param[in,out] b Value B +/// @param[in,out] a Value A +/// @param[in,out] b Value B template inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; } /// Returns the minimum of two values. -/// @param[in] a Value A -/// @param[in] b Value B -/// @return The minimum of the two values. +/// @param[in] a Value A +/// @param[in] b Value B +/// @return The minimum of the two values. template inline T rcMin(T a, T b) { return a < b ? a : b; } /// Returns the maximum of two values. -/// @param[in] a Value A -/// @param[in] b Value B -/// @return The maximum of the two values. +/// @param[in] a Value A +/// @param[in] b Value B +/// @return The maximum of the two values. template inline T rcMax(T a, T b) { return a > b ? a : b; } /// Returns the absolute value. -/// @param[in] a The value. -/// @return The absolute value of the specified value. +/// @param[in] a The value. +/// @return The absolute value of the specified value. template inline T rcAbs(T a) { return a < 0 ? -a : a; } /// Returns the square of the value. -/// @param[in] a The value. -/// @return The square of the value. +/// @param[in] a The value. +/// @return The square of the value. template inline T rcSqr(T a) { return a*a; } /// Clamps the value to the specified range. -/// @param[in] v The value to clamp. -/// @param[in] mn The minimum permitted return value. -/// @param[in] mx The maximum permitted return value. -/// @return The value, clamped to the specified range. -template inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); } +/// @param[in] value The value to clamp. +/// @param[in] minInclusive The minimum permitted return value. +/// @param[in] maxInclusive The maximum permitted return value. +/// @return The value, clamped to the specified range. +template inline T rcClamp(T value, T minInclusive, T maxInclusive) +{ + return value < minInclusive ? minInclusive: (value > maxInclusive ? maxInclusive : value); +} /// Returns the square root of the value. /// @param[in] x The value. @@ -644,9 +691,9 @@ float rcSqrt(float x); /// @{ /// Derives the cross product of two vectors. (@p v1 x @p v2) -/// @param[out] dest The cross product. [(x, y, z)] -/// @param[in] v1 A Vector [(x, y, z)] -/// @param[in] v2 A vector [(x, y, z)] +/// @param[out] dest The cross product. [(x, y, z)] +/// @param[in] v1 A Vector [(x, y, z)] +/// @param[in] v2 A vector [(x, y, z)] inline void rcVcross(float* dest, const float* v1, const float* v2) { dest[0] = v1[1]*v2[2] - v1[2]*v2[1]; @@ -655,8 +702,8 @@ inline void rcVcross(float* dest, const float* v1, const float* v2) } /// Derives the dot product of two vectors. (@p v1 . @p v2) -/// @param[in] v1 A Vector [(x, y, z)] -/// @param[in] v2 A vector [(x, y, z)] +/// @param[in] v1 A Vector [(x, y, z)] +/// @param[in] v2 A vector [(x, y, z)] /// @return The dot product. inline float rcVdot(const float* v1, const float* v2) { @@ -664,10 +711,10 @@ inline float rcVdot(const float* v1, const float* v2) } /// Performs a scaled vector addition. (@p v1 + (@p v2 * @p s)) -/// @param[out] dest The result vector. [(x, y, z)] -/// @param[in] v1 The base vector. [(x, y, z)] -/// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)] -/// @param[in] s The amount to scale @p v2 by before adding to @p v1. +/// @param[out] dest The result vector. [(x, y, z)] +/// @param[in] v1 The base vector. [(x, y, z)] +/// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)] +/// @param[in] s The amount to scale @p v2 by before adding to @p v1. inline void rcVmad(float* dest, const float* v1, const float* v2, const float s) { dest[0] = v1[0]+v2[0]*s; @@ -676,9 +723,9 @@ inline void rcVmad(float* dest, const float* v1, const float* v2, const float s) } /// Performs a vector addition. (@p v1 + @p v2) -/// @param[out] dest The result vector. [(x, y, z)] -/// @param[in] v1 The base vector. [(x, y, z)] -/// @param[in] v2 The vector to add to @p v1. [(x, y, z)] +/// @param[out] dest The result vector. [(x, y, z)] +/// @param[in] v1 The base vector. [(x, y, z)] +/// @param[in] v2 The vector to add to @p v1. [(x, y, z)] inline void rcVadd(float* dest, const float* v1, const float* v2) { dest[0] = v1[0]+v2[0]; @@ -687,9 +734,9 @@ inline void rcVadd(float* dest, const float* v1, const float* v2) } /// Performs a vector subtraction. (@p v1 - @p v2) -/// @param[out] dest The result vector. [(x, y, z)] -/// @param[in] v1 The base vector. [(x, y, z)] -/// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)] +/// @param[out] dest The result vector. [(x, y, z)] +/// @param[in] v1 The base vector. [(x, y, z)] +/// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)] inline void rcVsub(float* dest, const float* v1, const float* v2) { dest[0] = v1[0]-v2[0]; @@ -698,8 +745,8 @@ inline void rcVsub(float* dest, const float* v1, const float* v2) } /// Selects the minimum value of each element from the specified vectors. -/// @param[in,out] mn A vector. (Will be updated with the result.) [(x, y, z)] -/// @param[in] v A vector. [(x, y, z)] +/// @param[in,out] mn A vector. (Will be updated with the result.) [(x, y, z)] +/// @param[in] v A vector. [(x, y, z)] inline void rcVmin(float* mn, const float* v) { mn[0] = rcMin(mn[0], v[0]); @@ -708,8 +755,8 @@ inline void rcVmin(float* mn, const float* v) } /// Selects the maximum value of each element from the specified vectors. -/// @param[in,out] mx A vector. (Will be updated with the result.) [(x, y, z)] -/// @param[in] v A vector. [(x, y, z)] +/// @param[in,out] mx A vector. (Will be updated with the result.) [(x, y, z)] +/// @param[in] v A vector. [(x, y, z)] inline void rcVmax(float* mx, const float* v) { mx[0] = rcMax(mx[0], v[0]); @@ -718,8 +765,8 @@ inline void rcVmax(float* mx, const float* v) } /// Performs a vector copy. -/// @param[out] dest The result. [(x, y, z)] -/// @param[in] v The vector to copy. [(x, y, z)] +/// @param[out] dest The result. [(x, y, z)] +/// @param[in] v The vector to copy. [(x, y, z)] inline void rcVcopy(float* dest, const float* v) { dest[0] = v[0]; @@ -728,8 +775,8 @@ inline void rcVcopy(float* dest, const float* v) } /// Returns the distance between two points. -/// @param[in] v1 A point. [(x, y, z)] -/// @param[in] v2 A point. [(x, y, z)] +/// @param[in] v1 A point. [(x, y, z)] +/// @param[in] v2 A point. [(x, y, z)] /// @return The distance between the two points. inline float rcVdist(const float* v1, const float* v2) { @@ -740,8 +787,8 @@ inline float rcVdist(const float* v1, const float* v2) } /// Returns the square of the distance between two points. -/// @param[in] v1 A point. [(x, y, z)] -/// @param[in] v2 A point. [(x, y, z)] +/// @param[in] v1 A point. [(x, y, z)] +/// @param[in] v2 A point. [(x, y, z)] /// @return The square of the distance between the two points. inline float rcVdistSqr(const float* v1, const float* v2) { @@ -752,7 +799,7 @@ inline float rcVdistSqr(const float* v1, const float* v2) } /// Normalizes the vector. -/// @param[in,out] v The vector to normalize. [(x, y, z)] +/// @param[in,out] v The vector to normalize. [(x, y, z)] inline void rcVnormalize(float* v) { float d = 1.0f / rcSqrt(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2])); @@ -767,174 +814,249 @@ inline void rcVnormalize(float* v) /// @{ /// Calculates the bounding box of an array of vertices. -/// @ingroup recast -/// @param[in] verts An array of vertices. [(x, y, z) * @p nv] -/// @param[in] nv The number of vertices in the @p verts array. -/// @param[out] bmin The minimum bounds of the AABB. [(x, y, z)] [Units: wu] -/// @param[out] bmax The maximum bounds of the AABB. [(x, y, z)] [Units: wu] -void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax); +/// @ingroup recast +/// @param[in] verts An array of vertices. [(x, y, z) * @p nv] +/// @param[in] numVerts The number of vertices in the @p verts array. +/// @param[out] minBounds The minimum bounds of the AABB. [(x, y, z)] [Units: wu] +/// @param[out] maxBounds The maximum bounds of the AABB. [(x, y, z)] [Units: wu] +void rcCalcBounds(const float* verts, int numVerts, float* minBounds, float* maxBounds); /// Calculates the grid size based on the bounding box and grid cell size. -/// @ingroup recast -/// @param[in] bmin The minimum bounds of the AABB. [(x, y, z)] [Units: wu] -/// @param[in] bmax The maximum bounds of the AABB. [(x, y, z)] [Units: wu] -/// @param[in] cs The xz-plane cell size. [Limit: > 0] [Units: wu] -/// @param[out] w The width along the x-axis. [Limit: >= 0] [Units: vx] -/// @param[out] h The height along the z-axis. [Limit: >= 0] [Units: vx] -void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h); +/// @ingroup recast +/// @param[in] minBounds The minimum bounds of the AABB. [(x, y, z)] [Units: wu] +/// @param[in] maxBounds The maximum bounds of the AABB. [(x, y, z)] [Units: wu] +/// @param[in] cellSize The xz-plane cell size. [Limit: > 0] [Units: wu] +/// @param[out] sizeX The width along the x-axis. [Limit: >= 0] [Units: vx] +/// @param[out] sizeZ The height along the z-axis. [Limit: >= 0] [Units: vx] +void rcCalcGridSize(const float* minBounds, const float* maxBounds, float cellSize, int* sizeX, int* sizeZ); /// Initializes a new heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in,out] hf The allocated heightfield to initialize. -/// @param[in] width The width of the field along the x-axis. [Limit: >= 0] [Units: vx] -/// @param[in] height The height of the field along the z-axis. [Limit: >= 0] [Units: vx] -/// @param[in] bmin The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu] -/// @param[in] bmax The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu] -/// @param[in] cs The xz-plane cell size to use for the field. [Limit: > 0] [Units: wu] -/// @param[in] ch The y-axis cell size to use for field. [Limit: > 0] [Units: wu] -/// @returns True if the operation completed successfully. -bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, - const float* bmin, const float* bmax, - float cs, float ch); +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocHeightfield, rcHeightfield +/// @ingroup recast +/// +/// @param[in,out] context The build context to use during the operation. +/// @param[in,out] heightfield The allocated heightfield to initialize. +/// @param[in] sizeX The width of the field along the x-axis. [Limit: >= 0] [Units: vx] +/// @param[in] sizeZ The height of the field along the z-axis. [Limit: >= 0] [Units: vx] +/// @param[in] minBounds The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu] +/// @param[in] maxBounds The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu] +/// @param[in] cellSize The xz-plane cell size to use for the field. [Limit: > 0] [Units: wu] +/// @param[in] cellHeight The y-axis cell size to use for field. [Limit: > 0] [Units: wu] +/// @returns True if the operation completed successfully. +bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ, + const float* minBounds, const float* maxBounds, + float cellSize, float cellHeight); /// Sets the area id of all triangles with a slope below the specified value /// to #RC_WALKABLE_AREA. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. -/// [Limits: 0 <= value < 90] [Units: Degrees] -/// @param[in] verts The vertices. [(x, y, z) * @p nv] -/// @param[in] nv The number of vertices. -/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] -/// @param[in] nt The number of triangles. -/// @param[out] areas The triangle area ids. [Length: >= @p nt] -void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, - const int* tris, int nt, unsigned char* areas); +/// +/// Only sets the area id's for the walkable triangles. Does not alter the +/// area id's for un-walkable triangles. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles +/// +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. +/// [Limits: 0 <= value < 90] [Units: Degrees] +/// @param[in] verts The vertices. [(x, y, z) * @p nv] +/// @param[in] numVerts The number of vertices. +/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] +/// @param[in] numTris The number of triangles. +/// @param[out] triAreaIDs The triangle area ids. [Length: >= @p nt] +void rcMarkWalkableTriangles(rcContext* context, float walkableSlopeAngle, const float* verts, int numVerts, + const int* tris, int numTris, unsigned char* triAreaIDs); /// Sets the area id of all triangles with a slope greater than or equal to the specified value to #RC_NULL_AREA. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. -/// [Limits: 0 <= value < 90] [Units: Degrees] -/// @param[in] verts The vertices. [(x, y, z) * @p nv] -/// @param[in] nv The number of vertices. -/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] -/// @param[in] nt The number of triangles. -/// @param[out] areas The triangle area ids. [Length: >= @p nt] -void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, - const int* tris, int nt, unsigned char* areas); +/// +/// Only sets the area id's for the un-walkable 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 +/// +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. +/// [Limits: 0 <= value < 90] [Units: Degrees] +/// @param[in] verts The vertices. [(x, y, z) * @p nv] +/// @param[in] numVerts The number of vertices. +/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] +/// @param[in] numTris The number of triangles. +/// @param[out] triAreaIDs The triangle area ids. [Length: >= @p nt] +void rcClearUnwalkableTriangles(rcContext* context, float walkableSlopeAngle, const float* verts, int numVerts, + const int* tris, int numTris, unsigned char* triAreaIDs); /// Adds a span to the specified heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in,out] hf An initialized heightfield. -/// @param[in] x The width index where the span is to be added. -/// [Limits: 0 <= value < rcHeightfield::width] -/// @param[in] y The height index where the span is to be added. -/// [Limits: 0 <= value < rcHeightfield::height] -/// @param[in] smin The minimum height of the span. [Limit: < @p smax] [Units: vx] -/// @param[in] smax The maximum height of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] [Units: vx] -/// @param[in] area The area id of the span. [Limit: <= #RC_WALKABLE_AREA) -/// @param[in] flagMergeThr The merge theshold. [Limit: >= 0] [Units: vx] -/// @returns True if the operation completed successfully. -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); +/// +/// The span addition can be set to favor flags. If the span is merged to +/// another span and the new @p spanMax is within @p flagMergeThreshold units +/// from the existing span, the span flags are merged. +/// +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in,out] heightfield An initialized heightfield. +/// @param[in] x The column x index where the span is to be added. +/// [Limits: 0 <= value < rcHeightfield::width] +/// @param[in] z The column z index where the span is to be added. +/// [Limits: 0 <= value < rcHeightfield::height] +/// @param[in] spanMin The minimum height of the span. [Limit: < @p spanMax] [Units: vx] +/// @param[in] spanMax The maximum height of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] [Units: vx] +/// @param[in] areaID The area id of the span. [Limit: <= #RC_WALKABLE_AREA) +/// @param[in] flagMergeThreshold The merge threshold. [Limit: >= 0] [Units: vx] +/// @returns True if the operation completed successfully. +bool rcAddSpan(rcContext* context, rcHeightfield& heightfield, + int x, int z, + unsigned short spanMin, unsigned short spanMax, + unsigned char areaID, int flagMergeThreshold); -/// Rasterizes a triangle into the specified heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] v0 Triangle vertex 0 [(x, y, z)] -/// @param[in] v1 Triangle vertex 1 [(x, y, z)] -/// @param[in] v2 Triangle vertex 2 [(x, y, z)] -/// @param[in] area The area id of the triangle. [Limit: <= #RC_WALKABLE_AREA] -/// @param[in,out] solid An initialized heightfield. -/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. -/// [Limit: >= 0] [Units: vx] -/// @returns True if the operation completed successfully. -bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, - const unsigned char area, rcHeightfield& solid, - const int flagMergeThr = 1); +/// Rasterizes a single triangle into the specified heightfield. +/// +/// Calling this for each triangle in a mesh is less efficient than calling rcRasterizeTriangles +/// +/// No spans will be added if the triangle does not overlap the heightfield grid. +/// +/// @see rcHeightfield +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in] v0 Triangle vertex 0 [(x, y, z)] +/// @param[in] v1 Triangle vertex 1 [(x, y, z)] +/// @param[in] v2 Triangle vertex 2 [(x, y, z)] +/// @param[in] areaID The area id of the triangle. [Limit: <= #RC_WALKABLE_AREA] +/// @param[in,out] heightfield An initialized heightfield. +/// @param[in] flagMergeThreshold The distance where the walkable flag is favored over the non-walkable flag. +/// [Limit: >= 0] [Units: vx] +/// @returns True if the operation completed successfully. +bool rcRasterizeTriangle(rcContext* context, + const float* v0, const float* v1, const float* v2, + unsigned char areaID, rcHeightfield& heightfield, int flagMergeThreshold = 1); /// Rasterizes an indexed triangle mesh into the specified heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] verts The vertices. [(x, y, z) * @p nv] -/// @param[in] nv The number of vertices. -/// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] -/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] -/// @param[in] nt The number of triangles. -/// @param[in,out] solid An initialized heightfield. -/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. -/// [Limit: >= 0] [Units: vx] -/// @returns True if the operation completed successfully. -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 = 1); +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in] verts The vertices. [(x, y, z) * @p nv] +/// @param[in] numVerts The number of vertices. (unused) TODO (graham): Remove in next major release +/// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] +/// @param[in] triAreaIDs The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] +/// @param[in] numTris The number of triangles. +/// @param[in,out] heightfield An initialized heightfield. +/// @param[in] flagMergeThreshold The distance where the walkable flag is favored over the non-walkable flag. +/// [Limit: >= 0] [Units: vx] +/// @returns True if the operation completed successfully. +bool rcRasterizeTriangles(rcContext* context, + const float* verts, int numVerts, + const int* tris, const unsigned char* triAreaIDs, int numTris, + rcHeightfield& heightfield, int flagMergeThreshold = 1); /// Rasterizes an indexed triangle mesh into the specified heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] verts The vertices. [(x, y, z) * @p nv] -/// @param[in] nv The number of vertices. -/// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] -/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] -/// @param[in] nt The number of triangles. -/// @param[in,out] solid An initialized heightfield. -/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. -/// [Limit: >= 0] [Units: vx] -/// @returns True if the operation completed successfully. -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 = 1); +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in] verts The vertices. [(x, y, z) * @p nv] +/// @param[in] numVerts The number of vertices. (unused) TODO (graham): Remove in next major release +/// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] +/// @param[in] triAreaIDs The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] +/// @param[in] numTris The number of triangles. +/// @param[in,out] heightfield An initialized heightfield. +/// @param[in] flagMergeThreshold The distance where the walkable flag is favored over the non-walkable flag. +/// [Limit: >= 0] [Units: vx] +/// @returns True if the operation completed successfully. +bool rcRasterizeTriangles(rcContext* context, + const float* verts, int numVerts, + const unsigned short* tris, const unsigned char* triAreaIDs, int numTris, + rcHeightfield& heightfield, int flagMergeThreshold = 1); -/// Rasterizes triangles into the specified heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] verts The triangle vertices. [(ax, ay, az, bx, by, bz, cx, by, cx) * @p nt] -/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] -/// @param[in] nt The number of triangles. -/// @param[in,out] solid An initialized heightfield. -/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. -/// [Limit: >= 0] [Units: vx] -/// @returns True if the operation completed successfully. -bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, - rcHeightfield& solid, const int flagMergeThr = 1); +/// Rasterizes a triangle list into the specified heightfield. +/// +/// Expects each triangle to be specified as three sequential vertices of 3 floats. +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in] verts The triangle vertices. [(ax, ay, az, bx, by, bz, cx, by, cx) * @p nt] +/// @param[in] triAreaIDs The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] +/// @param[in] numTris The number of triangles. +/// @param[in,out] heightfield An initialized heightfield. +/// @param[in] flagMergeThreshold The distance where the walkable flag is favored over the non-walkable flag. +/// [Limit: >= 0] [Units: vx] +/// @returns True if the operation completed successfully. +bool rcRasterizeTriangles(rcContext* context, + const float* verts, const unsigned char* triAreaIDs, int numTris, + rcHeightfield& heightfield, int flagMergeThreshold = 1); -/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neighbor. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. -/// [Limit: >=0] [Units: vx] -/// @param[in,out] solid A fully built heightfield. (All spans have been added.) -void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid); +/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimb of a walkable neighbor. +/// +/// Allows the formation of walkable regions that will flow over low lying +/// objects such as curbs, and up structures such as stairways. +/// +/// Two neighboring spans are walkable if: rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb +/// +/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call +/// #rcFilterLedgeSpans after calling this filter. +/// +/// @see rcHeightfield, rcConfig +/// +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. +/// [Limit: >=0] [Units: vx] +/// @param[in,out] heightfield A fully built heightfield. (All spans have been added.) +void rcFilterLowHangingWalkableObstacles(rcContext* context, int walkableClimb, rcHeightfield& heightfield); -/// Marks spans that are ledges as not-walkable. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to -/// be considered walkable. [Limit: >= 3] [Units: vx] -/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. -/// [Limit: >=0] [Units: vx] -/// @param[in,out] solid A fully built heightfield. (All spans have been added.) -void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, - const int walkableClimb, rcHeightfield& solid); +/// Marks spans that are ledges as not-walkable. +/// +/// 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: rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb +/// +/// @see rcHeightfield, rcConfig +/// +/// @ingroup recast +/// @param[in,out] context The build context to use during the operation. +/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to +/// be considered walkable. [Limit: >= 3] [Units: vx] +/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. +/// [Limit: >=0] [Units: vx] +/// @param[in,out] heightfield A fully built heightfield. (All spans have been added.) +void rcFilterLedgeSpans(rcContext* context, int walkableHeight, int walkableClimb, rcHeightfield& heightfield); -/// Marks walkable spans as not walkable if the clearence above the span is less than the specified height. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to -/// be considered walkable. [Limit: >= 3] [Units: vx] -/// @param[in,out] solid A fully built heightfield. (All spans have been added.) -void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid); +/// Marks walkable spans as not walkable if the clearance above the span is less than the specified height. +/// +/// 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 +/// @ingroup recast +/// +/// @param[in,out] context The build context to use during the operation. +/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to +/// be considered walkable. [Limit: >= 3] [Units: vx] +/// @param[in,out] heightfield A fully built heightfield. (All spans have been added.) +void rcFilterWalkableLowHeightSpans(rcContext* context, int walkableHeight, rcHeightfield& heightfield); /// Returns the number of spans contained in the specified heightfield. /// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] hf An initialized heightfield. +/// @param[in,out] context The build context to use during the operation. +/// @param[in] heightfield An initialized heightfield. /// @returns The number of spans in the heightfield. -int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf); +int rcGetHeightFieldSpanCount(rcContext* context, const rcHeightfield& heightfield); /// @} /// @name Compact Heightfield Functions @@ -942,175 +1064,181 @@ int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf); /// @{ /// Builds a compact heightfield representing open space, from a heightfield representing solid space. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area -/// to be considered walkable. [Limit: >= 3] [Units: vx] -/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. -/// [Limit: >=0] [Units: vx] -/// @param[in] hf The heightfield to be compacted. -/// @param[out] chf The resulting compact heightfield. (Must be pre-allocated.) -/// @returns True if the operation completed successfully. -bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb, - rcHeightfield& hf, rcCompactHeightfield& chf); +/// +/// 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 +/// @ingroup recast +/// +/// @param[in,out] context The build context to use during the operation. +/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area +/// to be considered walkable. [Limit: >= 3] [Units: vx] +/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. +/// [Limit: >=0] [Units: vx] +/// @param[in] heightfield The heightfield to be compacted. +/// @param[out] compactHeightfield The resulting compact heightfield. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. +bool rcBuildCompactHeightfield(rcContext* context, int walkableHeight, int walkableClimb, + const rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield); /// Erodes the walkable area within the heightfield by the specified radius. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] radius The radius of erosion. [Limits: 0 < value < 255] [Units: vx] -/// @param[in,out] chf The populated compact heightfield to erode. -/// @returns True if the operation completed successfully. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] radius The radius of erosion. [Limits: 0 < value < 255] [Units: vx] +/// @param[in,out] chf The populated compact heightfield to erode. +/// @returns True if the operation completed successfully. bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf); /// Applies a median filter to walkable area types (based on area id), removing noise. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in,out] chf A populated compact heightfield. -/// @returns True if the operation completed successfully. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @returns True if the operation completed successfully. bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf); /// Applies an area id to all spans within the specified bounding box. (AABB) -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] bmin The minimum of the bounding box. [(x, y, z)] -/// @param[in] bmax The maximum of the bounding box. [(x, y, z)] -/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] -/// @param[in,out] chf A populated compact heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] bmin The minimum of the bounding box. [(x, y, z)] +/// @param[in] bmax The maximum of the bounding box. [(x, y, z)] +/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] +/// @param[in,out] chf A populated compact heightfield. void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId, rcCompactHeightfield& chf); /// Applies the area id to the all spans within the specified convex polygon. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] verts The vertices of the polygon [Fomr: (x, y, z) * @p nverts] -/// @param[in] nverts The number of vertices in the polygon. -/// @param[in] hmin The height of the base of the polygon. -/// @param[in] hmax The height of the top of the polygon. -/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] -/// @param[in,out] chf A populated compact heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] verts The vertices of the polygon [Fomr: (x, y, z) * @p nverts] +/// @param[in] nverts The number of vertices in the polygon. +/// @param[in] hmin The height of the base of the polygon. +/// @param[in] hmax The height of the top of the polygon. +/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] +/// @param[in,out] chf A populated compact heightfield. void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, const float hmin, const float hmax, unsigned char areaId, rcCompactHeightfield& chf); /// Helper function to offset voncex polygons for rcMarkConvexPolyArea. -/// @ingroup recast -/// @param[in] verts The vertices of the polygon [Form: (x, y, z) * @p nverts] -/// @param[in] nverts The number of vertices in the polygon. -/// @param[out] outVerts The offset vertices (should hold up to 2 * @p nverts) [Form: (x, y, z) * return value] -/// @param[in] maxOutVerts The max number of vertices that can be stored to @p outVerts. -/// @returns Number of vertices in the offset polygon or 0 if too few vertices in @p outVerts. +/// @ingroup recast +/// @param[in] verts The vertices of the polygon [Form: (x, y, z) * @p nverts] +/// @param[in] nverts The number of vertices in the polygon. +/// @param[in] offset How much to offset the polygon by. [Units: wu] +/// @param[out] outVerts The offset vertices (should hold up to 2 * @p nverts) [Form: (x, y, z) * return value] +/// @param[in] maxOutVerts The max number of vertices that can be stored to @p outVerts. +/// @returns Number of vertices in the offset polygon or 0 if too few vertices in @p outVerts. int rcOffsetPoly(const float* verts, const int nverts, const float offset, float* outVerts, const int maxOutVerts); /// Applies the area id to all spans within the specified cylinder. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] pos The center of the base of the cylinder. [Form: (x, y, z)] -/// @param[in] r The radius of the cylinder. -/// @param[in] h The height of the cylinder. -/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] -/// @param[in,out] chf A populated compact heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] pos The center of the base of the cylinder. [Form: (x, y, z)] +/// @param[in] r The radius of the cylinder. +/// @param[in] h The height of the cylinder. +/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] +/// @param[in,out] chf A populated compact heightfield. void rcMarkCylinderArea(rcContext* ctx, const float* pos, const float r, const float h, unsigned char areaId, rcCompactHeightfield& chf); /// Builds the distance field for the specified compact heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in,out] chf A populated compact heightfield. -/// @returns True if the operation completed successfully. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @returns True if the operation completed successfully. bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); /// Builds region data for the heightfield using watershed partitioning. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in,out] chf A populated compact heightfield. -/// @param[in] borderSize The size of the non-navigable border around the heightfield. -/// [Limit: >=0] [Units: vx] -/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. -/// [Limit: >=0] [Units: vx]. -/// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, -/// be merged with larger regions. [Limit: >=0] [Units: vx] -/// @returns True if the operation completed successfully. -bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int minRegionArea, const int mergeRegionArea); +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. +/// [Limit: >=0] [Units: vx] +/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. +/// [Limit: >=0] [Units: vx]. +/// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, +/// be merged with larger regions. [Limit: >=0] [Units: vx] +/// @returns True if the operation completed successfully. +bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, int borderSize, int minRegionArea, int mergeRegionArea); /// Builds region data for the heightfield by partitioning the heightfield in non-overlapping layers. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in,out] chf A populated compact heightfield. -/// @param[in] borderSize The size of the non-navigable border around the heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. /// [Limit: >=0] [Units: vx] -/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. +/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. /// [Limit: >=0] [Units: vx]. -/// @returns True if the operation completed successfully. -bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int minRegionArea); +/// @returns True if the operation completed successfully. +bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, int borderSize, int minRegionArea); /// Builds region data for the heightfield using simple monotone partitioning. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in,out] chf A populated compact heightfield. -/// @param[in] borderSize The size of the non-navigable border around the heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. /// [Limit: >=0] [Units: vx] -/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. +/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. /// [Limit: >=0] [Units: vx]. -/// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, +/// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, /// be merged with larger regions. [Limit: >=0] [Units: vx] -/// @returns True if the operation completed successfully. +/// @returns True if the operation completed successfully. bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int minRegionArea, const int mergeRegionArea); + int borderSize, int minRegionArea, int mergeRegionArea); /// Sets the neighbor connection data for the specified direction. -/// @param[in] s The span to update. -/// @param[in] dir The direction to set. [Limits: 0 <= value < 4] -/// @param[in] i The index of the neighbor span. -inline void rcSetCon(rcCompactSpan& s, int dir, int i) +/// @param[in] span The span to update. +/// @param[in] direction The direction to set. [Limits: 0 <= value < 4] +/// @param[in] neighborIndex The index of the neighbor span. +inline void rcSetCon(rcCompactSpan& span, int direction, int neighborIndex) { - const unsigned int shift = (unsigned int)dir*6; - unsigned int con = s.con; - s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift); + const unsigned int shift = (unsigned int)direction * 6; + const unsigned int con = span.con; + span.con = (con & ~(0x3f << shift)) | (((unsigned int)neighborIndex & 0x3f) << shift); } /// Gets neighbor connection data for the specified direction. -/// @param[in] s The span to check. -/// @param[in] dir The direction to check. [Limits: 0 <= value < 4] -/// @return The neighbor connection data for the specified direction, -/// or #RC_NOT_CONNECTED if there is no connection. -inline int rcGetCon(const rcCompactSpan& s, int dir) +/// @param[in] span The span to check. +/// @param[in] direction The direction to check. [Limits: 0 <= value < 4] +/// @return The neighbor connection data for the specified direction, or #RC_NOT_CONNECTED if there is no connection. +inline int rcGetCon(const rcCompactSpan& span, int direction) { - const unsigned int shift = (unsigned int)dir*6; - return (s.con >> shift) & 0x3f; + const unsigned int shift = (unsigned int)direction * 6; + return (span.con >> shift) & 0x3f; } /// Gets the standard width (x-axis) offset for the specified direction. -/// @param[in] dir The direction. [Limits: 0 <= value < 4] -/// @return The width offset to apply to the current cell position to move -/// in the direction. -inline int rcGetDirOffsetX(int dir) +/// @param[in] direction The direction. [Limits: 0 <= value < 4] +/// @return The width offset to apply to the current cell position to move in the direction. +inline int rcGetDirOffsetX(int direction) { static const int offset[4] = { -1, 0, 1, 0, }; - return offset[dir&0x03]; + return offset[direction & 0x03]; } +// TODO (graham): Rename this to rcGetDirOffsetZ /// Gets the standard height (z-axis) offset for the specified direction. -/// @param[in] dir The direction. [Limits: 0 <= value < 4] -/// @return The height offset to apply to the current cell position to move -/// in the direction. -inline int rcGetDirOffsetY(int dir) +/// @param[in] direction The direction. [Limits: 0 <= value < 4] +/// @return The height offset to apply to the current cell position to move in the direction. +inline int rcGetDirOffsetY(int direction) { static const int offset[4] = { 0, 1, 0, -1 }; - return offset[dir&0x03]; + return offset[direction & 0x03]; } /// Gets the direction for the specified offset. One of x and y should be 0. -/// @param[in] x The x offset. [Limits: -1 <= value <= 1] -/// @param[in] y The y offset. [Limits: -1 <= value <= 1] -/// @return The direction that represents the offset. -inline int rcGetDirForOffset(int x, int y) +/// @param[in] offsetX The x offset. [Limits: -1 <= value <= 1] +/// @param[in] offsetZ The z offset. [Limits: -1 <= value <= 1] +/// @return The direction that represents the offset. +inline int rcGetDirForOffset(int offsetX, int offsetZ) { static const int dirs[5] = { 3, 0, -1, 2, 1 }; - return dirs[((y+1)<<1)+x]; + return dirs[((offsetZ + 1) << 1) + offsetX]; } /// @} @@ -1119,43 +1247,43 @@ inline int rcGetDirForOffset(int x, int y) /// @{ /// Builds a layer set from the specified compact heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] chf A fully built compact heightfield. -/// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0] -/// [Units: vx] -/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area -/// to be considered walkable. [Limit: >= 3] [Units: vx] -/// @param[out] lset The resulting layer set. (Must be pre-allocated.) -/// @returns True if the operation completed successfully. -bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int walkableHeight, +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] chf A fully built compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0] +/// [Units: vx] +/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area +/// to be considered walkable. [Limit: >= 3] [Units: vx] +/// @param[out] lset The resulting layer set. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. +bool rcBuildHeightfieldLayers(rcContext* ctx, const rcCompactHeightfield& chf, + int borderSize, int walkableHeight, rcHeightfieldLayerSet& lset); /// Builds a contour set from the region outlines in the provided compact heightfield. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] chf A fully built compact heightfield. -/// @param[in] maxError The maximum distance a simplfied contour's border edges should deviate -/// the original raw contour. [Limit: >=0] [Units: wu] -/// @param[in] maxEdgeLen The maximum allowed length for contour edges along the border of the mesh. -/// [Limit: >=0] [Units: vx] -/// @param[out] cset The resulting contour set. (Must be pre-allocated.) -/// @param[in] buildFlags The build flags. (See: #rcBuildContoursFlags) -/// @returns True if the operation completed successfully. -bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, - const float maxError, const int maxEdgeLen, - rcContourSet& cset, const int buildFlags = RC_CONTOUR_TESS_WALL_EDGES); +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] chf A fully built compact heightfield. +/// @param[in] maxError The maximum distance a simplified contour's border edges should deviate +/// the original raw contour. [Limit: >=0] [Units: wu] +/// @param[in] maxEdgeLen The maximum allowed length for contour edges along the border of the mesh. +/// [Limit: >=0] [Units: vx] +/// @param[out] cset The resulting contour set. (Must be pre-allocated.) +/// @param[in] buildFlags The build flags. (See: #rcBuildContoursFlags) +/// @returns True if the operation completed successfully. +bool rcBuildContours(rcContext* ctx, const rcCompactHeightfield& chf, + float maxError, int maxEdgeLen, + rcContourSet& cset, int buildFlags = RC_CONTOUR_TESS_WALL_EDGES); /// Builds a polygon mesh from the provided contours. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] cset A fully built contour set. -/// @param[in] nvp The maximum number of vertices allowed for polygons generated during the -/// contour to polygon conversion process. [Limit: >= 3] -/// @param[out] mesh The resulting polygon mesh. (Must be re-allocated.) -/// @returns True if the operation completed successfully. -bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh); +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] cset A fully built contour set. +/// @param[in] nvp The maximum number of vertices allowed for polygons generated during the +/// contour to polygon conversion process. [Limit: >= 3] +/// @param[out] mesh The resulting polygon mesh. (Must be re-allocated.) +/// @returns True if the operation completed successfully. +bool rcBuildPolyMesh(rcContext* ctx, const rcContourSet& cset, const int nvp, rcPolyMesh& mesh); /// Merges multiple polygon meshes into a single mesh. /// @ingroup recast @@ -1167,34 +1295,34 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh); /// Builds a detail mesh from the provided polygon mesh. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] mesh A fully built polygon mesh. -/// @param[in] chf The compact heightfield used to build the polygon mesh. -/// @param[in] sampleDist Sets the distance to use when samping the heightfield. [Limit: >=0] [Units: wu] -/// @param[in] sampleMaxError The maximum distance the detail mesh surface should deviate from -/// heightfield data. [Limit: >=0] [Units: wu] -/// @param[out] dmesh The resulting detail mesh. (Must be pre-allocated.) -/// @returns True if the operation completed successfully. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] mesh A fully built polygon mesh. +/// @param[in] chf The compact heightfield used to build the polygon mesh. +/// @param[in] sampleDist Sets the distance to use when sampling the heightfield. [Limit: >=0] [Units: wu] +/// @param[in] sampleMaxError The maximum distance the detail mesh surface should deviate from +/// heightfield data. [Limit: >=0] [Units: wu] +/// @param[out] dmesh The resulting detail mesh. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf, - const float sampleDist, const float sampleMaxError, + float sampleDist, float sampleMaxError, rcPolyMeshDetail& dmesh); /// Copies the poly mesh data from src to dst. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] src The source mesh to copy from. -/// @param[out] dst The resulting detail mesh. (Must be pre-allocated, must be empty mesh.) -/// @returns True if the operation completed successfully. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] src The source mesh to copy from. +/// @param[out] dst The resulting detail mesh. (Must be pre-allocated, must be empty mesh.) +/// @returns True if the operation completed successfully. bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst); /// Merges multiple detail meshes into a single detail mesh. -/// @ingroup recast -/// @param[in,out] ctx The build context to use during the operation. -/// @param[in] meshes An array of detail meshes to merge. [Size: @p nmeshes] -/// @param[in] nmeshes The number of detail meshes in the meshes array. -/// @param[out] mesh The resulting detail mesh. (Must be pre-allocated.) -/// @returns True if the operation completed successfully. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] meshes An array of detail meshes to merge. [Size: @p nmeshes] +/// @param[in] nmeshes The number of detail meshes in the meshes array. +/// @param[out] mesh The resulting detail mesh. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh); /// @} diff --git a/Source/ThirdParty/recastnavigation/RecastAlloc.cpp b/Source/ThirdParty/recastnavigation/RecastAlloc.cpp index bdc366116..e919daf7b 100644 --- a/Source/ThirdParty/recastnavigation/RecastAlloc.cpp +++ b/Source/ThirdParty/recastnavigation/RecastAlloc.cpp @@ -16,12 +16,9 @@ // 3. This notice may not be removed or altered from any source distribution. // -#include -#include #include "RecastAlloc.h" -#include "RecastAssert.h" -static void *rcAllocDefault(size_t size, rcAllocHint) +static void* rcAllocDefault(size_t size, rcAllocHint) { return malloc(size); } @@ -34,27 +31,21 @@ static void rcFreeDefault(void *ptr) static rcAllocFunc* sRecastAllocFunc = rcAllocDefault; static rcFreeFunc* sRecastFreeFunc = rcFreeDefault; -/// @see rcAlloc, rcFree -void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc) +void rcAllocSetCustom(rcAllocFunc* allocFunc, rcFreeFunc* freeFunc) { sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault; sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault; } -/// @see rcAllocSetCustom void* rcAlloc(size_t size, rcAllocHint 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) { - if (ptr) + if (ptr != NULL) + { sRecastFreeFunc(ptr); + } } diff --git a/Source/ThirdParty/recastnavigation/RecastAlloc.h b/Source/ThirdParty/recastnavigation/RecastAlloc.h index 071278d65..1741de9f0 100644 --- a/Source/ThirdParty/recastnavigation/RecastAlloc.h +++ b/Source/ThirdParty/recastnavigation/RecastAlloc.h @@ -19,11 +19,11 @@ #ifndef RECASTALLOC_H #define RECASTALLOC_H -#include -#include - #include "RecastAssert.h" +#include +#include + /// Provides hint values to the memory allocator on how long the /// memory is expected to be used. enum rcAllocHint @@ -47,18 +47,27 @@ typedef void (rcFreeFunc)(void* ptr); /// 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] freeFunc The memory de-allocation function to be used by #rcFree +/// +/// @see rcAlloc, rcFree void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc); /// 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. -/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed. -/// @see rcFree +/// +/// @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. +/// @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); -/// Deallocates a memory block. -/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc. -/// @see rcAlloc +/// Deallocates a memory block. If @p ptr is NULL, this does nothing. +/// +/// @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); /// 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 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& 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); } 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]; } T& front() { rcAssert(m_size); return m_data[0]; } - const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; }; - T& back() { 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]; } const T* data() const { return m_data; } T* data() { return m_data; } diff --git a/Source/ThirdParty/recastnavigation/RecastArea.cpp b/Source/ThirdParty/recastnavigation/RecastArea.cpp index 97139cf99..07fb02189 100644 --- a/Source/ThirdParty/recastnavigation/RecastArea.cpp +++ b/Source/ThirdParty/recastnavigation/RecastArea.cpp @@ -17,7 +17,6 @@ // #include -#define _USE_MATH_DEFINES #include #include #include diff --git a/Source/ThirdParty/recastnavigation/RecastAssert.cpp b/Source/ThirdParty/recastnavigation/RecastAssert.cpp index 6297d4202..973b68112 100644 --- a/Source/ThirdParty/recastnavigation/RecastAssert.cpp +++ b/Source/ThirdParty/recastnavigation/RecastAssert.cpp @@ -22,7 +22,7 @@ static rcAssertFailFunc* sRecastAssertFailFunc = 0; -void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc) +void rcAssertFailSetCustom(rcAssertFailFunc* assertFailFunc) { sRecastAssertFailFunc = assertFailFunc; } diff --git a/Source/ThirdParty/recastnavigation/RecastAssert.h b/Source/ThirdParty/recastnavigation/RecastAssert.h index e7cc10e49..81705bbe0 100644 --- a/Source/ThirdParty/recastnavigation/RecastAssert.h +++ b/Source/ThirdParty/recastnavigation/RecastAssert.h @@ -19,13 +19,10 @@ #ifndef 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 -// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ -# define rcAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false) +// 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) #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. /// @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. rcAssertFailFunc* rcAssertFailGetCustom(); @@ -47,8 +44,8 @@ rcAssertFailFunc* rcAssertFailGetCustom(); # define rcAssert(expression) \ { \ rcAssertFailFunc* failFunc = rcAssertFailGetCustom(); \ - if(failFunc == NULL) { assert(expression); } \ - else if(!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \ + if (failFunc == NULL) { assert(expression); } \ + else if (!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \ } #endif diff --git a/Source/ThirdParty/recastnavigation/RecastContour.cpp b/Source/ThirdParty/recastnavigation/RecastContour.cpp index 1293d4fbd..5508a98a7 100644 --- a/Source/ThirdParty/recastnavigation/RecastContour.cpp +++ b/Source/ThirdParty/recastnavigation/RecastContour.cpp @@ -16,7 +16,6 @@ // 3. This notice may not be removed or altered from any source distribution. // -#define _USE_MATH_DEFINES #include #include #include @@ -102,7 +101,7 @@ static int getCornerHeight(int x, int y, int i, int dir, } static void walkContour(int x, int y, int i, - rcCompactHeightfield& chf, + const rcCompactHeightfield& chf, unsigned char* flags, rcIntArray& points) { // 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]; } -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 (int k = 0; k < n; k++) @@ -778,9 +777,9 @@ static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region) for (int j = 0; j < ndiags; j++) { 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++) - 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) { 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 rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig -bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, +bool rcBuildContours(rcContext* ctx, const rcCompactHeightfield& chf, const float maxError, const int maxEdgeLen, rcContourSet& cset, const int buildFlags) { diff --git a/Source/ThirdParty/recastnavigation/RecastFilter.cpp b/Source/ThirdParty/recastnavigation/RecastFilter.cpp index 9d3e63c48..b5adba43e 100644 --- a/Source/ThirdParty/recastnavigation/RecastFilter.cpp +++ b/Source/ThirdParty/recastnavigation/RecastFilter.cpp @@ -16,186 +16,168 @@ // 3. This notice may not be removed or altered from any source distribution. // -#define _USE_MATH_DEFINES -#include -#include #include "Recast.h" #include "RecastAssert.h" -/// @par -/// -/// Allows the formation of walkable regions that will flow over low lying -/// objects such as curbs, and up structures such as stairways. -/// -/// Two neighboring spans are walkable if: rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb -/// -/// @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); +#include - rcScopedTimer timer(ctx, RC_TIMER_FILTER_LOW_OBSTACLES); - - const int w = solid.width; - const int h = solid.height; - - for (int y = 0; y < h; ++y) +void rcFilterLowHangingWalkableObstacles(rcContext* context, const int walkableClimb, rcHeightfield& heightfield) +{ + rcAssert(context); + + rcScopedTimer timer(context, RC_TIMER_FILTER_LOW_OBSTACLES); + + const int xSize = heightfield.width; + const int zSize = heightfield.height; + + for (int z = 0; z < zSize; ++z) { - for (int x = 0; x < w; ++x) + for (int x = 0; x < xSize; ++x) { - rcSpan* ps = 0; - bool previousWalkable = false; + rcSpan* previousSpan = NULL; + bool previousWasWalkable = false; 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 // 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) - s->area = previousArea; + if (rcAbs((int)span->smax - (int)previousSpan->smax) <= walkableClimb) + { + span->area = previousArea; + } } // Copy walkable flag so that it cannot propagate // past multiple non-walkable objects. - previousWalkable = walkable; - previousArea = s->area; + previousWasWalkable = walkable; + previousArea = span->area; } } } } -/// @par -/// -/// 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: rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb -/// -/// @see rcHeightfield, rcConfig -void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb, - rcHeightfield& solid) +void rcFilterLedgeSpans(rcContext* context, const int walkableHeight, const int walkableClimb, + rcHeightfield& heightfield) { - 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 h = solid.height; - const int MAX_HEIGHT = 0xffff; + const int xSize = heightfield.width; + const int zSize = heightfield.height; + const int MAX_HEIGHT = 0xffff; // TODO (graham): Move this to a more visible constant and update usages. // 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. - if (s->area == RC_NULL_AREA) + if (span->area == RC_NULL_AREA) + { continue; - - const int bot = (int)(s->smax); - const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; - + } + + const int bot = (int)(span->smax); + const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT; + // Find neighbours minimum height. - int minh = MAX_HEIGHT; + int minNeighborHeight = MAX_HEIGHT; // Min and max height of accessible neighbours. - int asmin = s->smax; - int asmax = s->smax; + int accessibleNeighborMinHeight = span->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 dy = y + rcGetDirOffsetY(dir); + int dx = x + rcGetDirOffsetX(direction); + int dy = z + rcGetDirOffsetY(direction); // 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; } // From minus infinity to the first span. - rcSpan* ns = solid.spans[dx + dy*w]; - int nbot = -walkableClimb; - int ntop = ns ? (int)ns->smin : MAX_HEIGHT; - // Skip neightbour if the gap between the spans is too small. - if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) - minh = rcMin(minh, nbot - bot); + const rcSpan* neighborSpan = heightfield.spans[dx + dy * xSize]; + int neighborBot = -walkableClimb; + int neighborTop = neighborSpan ? (int)neighborSpan->smin : MAX_HEIGHT; - // Rest of the spans. - for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next) + // Skip neighbour if the gap between the spans is too small. + if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight) { - nbot = (int)ns->smax; - ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT; - // Skip neightbour if the gap between the spans is too small. - if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) - { - minh = rcMin(minh, nbot - bot); + minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot); + } + + // Rest of the spans. + for (neighborSpan = heightfield.spans[dx + dy * xSize]; neighborSpan; neighborSpan = neighborSpan->next) + { + neighborBot = (int)neighborSpan->smax; + neighborTop = neighborSpan->next ? (int)neighborSpan->next->smin : MAX_HEIGHT; + // Skip neighbour if the gap between the spans is too small. + if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight) + { + minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot); + // Find min/max accessible neighbour height. - if (rcAbs(nbot - bot) <= walkableClimb) + if (rcAbs(neighborBot - bot) <= walkableClimb) { - if (nbot < asmin) asmin = nbot; - if (nbot > asmax) asmax = nbot; + if (neighborBot < accessibleNeighborMinHeight) accessibleNeighborMinHeight = neighborBot; + if (neighborBot > accessibleNeighborMaxHeight) accessibleNeighborMaxHeight = neighborBot; } - + } } } - + // The current span is close to a ledge if the drop to any // 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, // 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 -/// -/// 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) +void rcFilterWalkableLowHeightSpans(rcContext* context, const int walkableHeight, rcHeightfield& heightfield) { - 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 h = solid.height; + const int xSize = heightfield.width; + const int zSize = heightfield.height; const int MAX_HEIGHT = 0xffff; // Remove walkable flag from spans which do not have enough // 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 top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; - if ((top - bot) <= walkableHeight) - s->area = RC_NULL_AREA; + const int bot = (int)(span->smax); + const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT; + if ((top - bot) < walkableHeight) + { + span->area = RC_NULL_AREA; + } } } } diff --git a/Source/ThirdParty/recastnavigation/RecastLayers.cpp b/Source/ThirdParty/recastnavigation/RecastLayers.cpp index acc97e44f..ca37ebba7 100644 --- a/Source/ThirdParty/recastnavigation/RecastLayers.cpp +++ b/Source/ThirdParty/recastnavigation/RecastLayers.cpp @@ -17,7 +17,6 @@ // #include -#define _USE_MATH_DEFINES #include #include #include @@ -29,8 +28,21 @@ // Must be 255 or smaller (not 256) because layer IDs are stored as // a byte where 255 is a special value. -static const int RC_MAX_LAYERS = 63; -static const int RC_MAX_NEIS = 16; +#ifndef RC_MAX_LAYERS_DEF +#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 { @@ -89,7 +101,7 @@ struct rcLayerSweepSpan /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig -bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, +bool rcBuildHeightfieldLayers(rcContext* ctx, const rcCompactHeightfield& chf, const int borderSize, const int walkableHeight, rcHeightfieldLayerSet& lset) { diff --git a/Source/ThirdParty/recastnavigation/RecastMesh.cpp b/Source/ThirdParty/recastnavigation/RecastMesh.cpp index e99eaebb7..c2c0d5174 100644 --- a/Source/ThirdParty/recastnavigation/RecastMesh.cpp +++ b/Source/ThirdParty/recastnavigation/RecastMesh.cpp @@ -16,7 +16,6 @@ // 3. This notice may not be removed or altered from any source distribution. // -#define _USE_MATH_DEFINES #include #include #include @@ -35,7 +34,7 @@ static bool buildMeshAdjacency(unsigned short* polys, const int npolys, const int nverts, const int vertsPerPoly) { // 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; 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; // Count number of polygons to remove. - int numRemovedVerts = 0; int numTouchedVerts = 0; int numRemainingEdges = 0; for (int i = 0; i < mesh.npolys; ++i) @@ -586,7 +584,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho } if (numRemoved) { - numRemovedVerts += numRemoved; 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. /// /// @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); diff --git a/Source/ThirdParty/recastnavigation/RecastMeshDetail.cpp b/Source/ThirdParty/recastnavigation/RecastMeshDetail.cpp index 1999200c1..40f5b8c60 100644 --- a/Source/ThirdParty/recastnavigation/RecastMeshDetail.cpp +++ b/Source/ThirdParty/recastnavigation/RecastMeshDetail.cpp @@ -17,7 +17,6 @@ // #include -#define _USE_MATH_DEFINES #include #include #include @@ -284,7 +283,7 @@ static unsigned short getHeight(const float fx, const float fy, const float fz, enum EdgeValues { EV_UNDEF = -1, - EV_HULL = -2, + EV_HULL = -2 }; static int findEdge(const int* edges, int nedges, int s, int t) diff --git a/Source/ThirdParty/recastnavigation/RecastRasterization.cpp b/Source/ThirdParty/recastnavigation/RecastRasterization.cpp index a4cef7490..2a4f619fb 100644 --- a/Source/ThirdParty/recastnavigation/RecastRasterization.cpp +++ b/Source/ThirdParty/recastnavigation/RecastRasterization.cpp @@ -16,373 +16,485 @@ // 3. This notice may not be removed or altered from any source distribution. // -#define _USE_MATH_DEFINES #include #include #include "Recast.h" #include "RecastAlloc.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; - overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; - overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; - overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; - return overlap; + return + aMin[0] <= bMax[0] && aMax[0] >= bMin[0] && + aMin[1] <= bMax[1] && aMax[1] >= bMin[1] && + aMin[2] <= bMax[2] && aMax[2] >= bMin[2]; } -inline bool overlapInterval(unsigned short amin, unsigned short amax, - unsigned short bmin, unsigned short bmax) -{ - if (amax < bmin) return false; - if (amin > bmax) return false; - return true; -} - - +/// Allocates a new span in the heightfield. +/// Use a memory pool and free list to minimize actual allocations. +/// +/// @param[in] hf The heightfield +/// @returns A pointer to the allocated or re-used span memory. static rcSpan* allocSpan(rcHeightfield& hf) { - // If running out of memory, allocate new page and update the freelist. - if (!hf.freelist || !hf.freelist->next) + // If necessary, allocate new page and update the freelist. + if (hf.freelist == NULL || hf.freelist->next == NULL) { // Create new page. // Allocate memory for the new pool. - rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); - if (!pool) return 0; + rcSpanPool* spanPool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); + if (spanPool == NULL) + { + return NULL; + } // Add the pool into the list of pools. - pool->next = hf.pools; - hf.pools = pool; - // Add new items to the free list. - rcSpan* freelist = hf.freelist; - rcSpan* head = &pool->items[0]; - rcSpan* it = &pool->items[RC_SPANS_PER_POOL]; + spanPool->next = hf.pools; + hf.pools = spanPool; + + // Add new spans to the free list. + rcSpan* freeList = hf.freelist; + rcSpan* head = &spanPool->items[0]; + rcSpan* it = &spanPool->items[RC_SPANS_PER_POOL]; do { --it; - it->next = freelist; - freelist = it; + it->next = freeList; + freeList = it; } while (it != head); hf.freelist = it; } - - // Pop item from in front of the free list. - rcSpan* it = hf.freelist; + + // Pop item from the front of the free list. + rcSpan* newSpan = hf.freelist; 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; - // 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]) + if (span == NULL) { - hf.spans[idx] = s; - return true; + return; } - rcSpan* prev = 0; - rcSpan* cur = hf.spans[idx]; - - // Insert and merge spans. - while (cur) + // Add the span to the front of the free list. + span->next = hf.freelist; + hf.freelist = span; +} + +/// Adds a span to the heightfield. If the new span overlaps existing spans, +/// 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; } - else if (cur->smax < s->smin) + + if (currentSpan->smax < newSpan->smin) { - // Current span is before the new span advance. - prev = cur; - cur = cur->next; + // Current span is completely before the new span. Keep going. + previousSpan = currentSpan; + currentSpan = currentSpan->next; } else { - // Merge spans. - if (cur->smin < s->smin) - s->smin = cur->smin; - if (cur->smax > s->smax) - s->smax = cur->smax; + // The new span overlaps with an existing span. Merge them. + if (currentSpan->smin < newSpan->smin) + { + newSpan->smin = currentSpan->smin; + } + if (currentSpan->smax > newSpan->smax) + { + newSpan->smax = currentSpan->smax; + } // Merge flags. - if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr) - s->area = rcMax(s->area, cur->area); + if (rcAbs((int)newSpan->smax - (int)currentSpan->smax) <= flagMergeThreshold) + { + // Higher area ID numbers indicate higher resolution priority. + newSpan->area = rcMax(newSpan->area, currentSpan->area); + } - // Remove current span. - rcSpan* next = cur->next; - freeSpan(hf, cur); - if (prev) - prev->next = next; + // Remove the current span since it's now merged with newSpan. + // Keep going because there might be other overlapping spans that also need to be merged. + rcSpan* next = currentSpan->next; + freeSpan(hf, currentSpan); + if (previousSpan) + { + previousSpan->next = next; + } else - hf.spans[idx] = next; - cur = next; + { + hf.spans[columnIndex] = next; + } + currentSpan = next; } } - // Insert new span. - if (prev) + // Insert new span after prev + if (previousSpan != NULL) { - s->next = prev->next; - prev->next = s; + newSpan->next = previousSpan->next; + previousSpan->next = newSpan; } else { - s->next = hf.spans[idx]; - hf.spans[idx] = s; + // This span should go before the others in the list + newSpan->next = hf.spans[columnIndex]; + hf.spans[columnIndex] = newSpan; } return true; } -/// @par -/// -/// The span addition can be set to favor flags. If the span is merged to -/// another span and the new @p smax is within @p flagMergeThr units -/// 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) +bool rcAddSpan(rcContext* context, rcHeightfield& heightfield, + const int x, const int z, + const unsigned short spanMin, const unsigned short spanMax, + const unsigned char areaID, const int flagMergeThreshold) { - 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 true; } -// divides a convex polygons into two convex polygons on both sides of a line -static void dividePoly(const float* in, int nin, - float* out1, int* nout1, - float* out2, int* nout2, - float x, int axis) +enum rcAxis { - float d[12]; - for (int i = 0; i < nin; ++i) - d[i] = x - in[i*3+axis]; + RC_AXIS_X = 0, + RC_AXIS_Y = 1, + RC_AXIS_Z = 2 +}; - int m = 0, n = 0; - for (int i = 0, j = nin-1; i < nin; j=i, ++i) +/// Divides a convex polygon of max 12 vertices into two convex polygons +/// 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; - bool inb = d[i] >= 0; - if (ina != inb) + inVertAxisDelta[inVert] = axisOffset - inVerts[inVert * 3 + axis]; + } + + 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]); - out1[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s; - out1[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s; - out1[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s; - rcVcopy(out2 + n*3, out1 + m*3); - m++; - n++; - // add the i'th point to the right polygon. Do NOT add points that are on the dividing line + float s = inVertAxisDelta[inVertB] / (inVertAxisDelta[inVertB] - inVertAxisDelta[inVertA]); + outVerts1[poly1Vert * 3 + 0] = inVerts[inVertB * 3 + 0] + (inVerts[inVertA * 3 + 0] - inVerts[inVertB * 3 + 0]) * s; + outVerts1[poly1Vert * 3 + 1] = inVerts[inVertB * 3 + 1] + (inVerts[inVertA * 3 + 1] - inVerts[inVertB * 3 + 1]) * s; + outVerts1[poly1Vert * 3 + 2] = inVerts[inVertB * 3 + 2] + (inVerts[inVertA * 3 + 2] - inVerts[inVertB * 3 + 2]) * s; + rcVcopy(&outVerts2[poly2Vert * 3], &outVerts1[poly1Vert * 3]); + poly1Vert++; + poly2Vert++; + + // add the inVertA point to the right polygon. Do NOT add points that are on the dividing line // since these were already added above - if (d[i] > 0) + if (inVertAxisDelta[inVertA] > 0) { - rcVcopy(out1 + m*3, in + i*3); - m++; + rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]); + poly1Vert++; } - else if (d[i] < 0) + else if (inVertAxisDelta[inVertA] < 0) { - rcVcopy(out2 + n*3, in + i*3); - n++; + rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]); + poly2Vert++; } } - else // same side + else { - // add the i'th point to the right polygon. Addition is done even for points on the dividing line - if (d[i] >= 0) + // add the inVertA point to the right polygon. Addition is done even for points on the dividing line + if (inVertAxisDelta[inVertA] >= 0) { - rcVcopy(out1 + m*3, in + i*3); - m++; - if (d[i] != 0) + rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]); + poly1Vert++; + if (inVertAxisDelta[inVertA] != 0) + { continue; + } } - rcVcopy(out2 + n*3, in + i*3); - n++; + rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]); + poly2Vert++; } } - *nout1 = m; - *nout2 = n; + *outVerts1Count = poly1Vert; + *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, - const unsigned char area, rcHeightfield& hf, - const float* bmin, const float* bmax, - const float cs, const float ics, const float ich, - const int flagMergeThr) + const unsigned char areaID, rcHeightfield& hf, + const float* hfBBMin, const float* hfBBMax, + const float cellSize, const float inverseCellSize, const float inverseCellHeight, + 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 h = hf.height; - float tmin[3], tmax[3]; - const float by = bmax[1] - bmin[1]; - - // Calculate the bounding box of the triangle. - rcVcopy(tmin, v0); - rcVcopy(tmax, v0); - 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. - if (!overlapBounds(bmin, bmax, tmin, tmax)) - return true; - - // 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); - + const float by = hfBBMax[1] - hfBBMin[1]; + + // Calculate the footprint of the triangle on the grid's z-axis + int z0 = (int)((triBBMin[2] - hfBBMin[2]) * inverseCellSize); + int z1 = (int)((triBBMax[2] - hfBBMin[2]) * inverseCellSize); + + // use -1 rather than 0 to cut the polygon properly at the start of the tile + z0 = rcClamp(z0, -1, h - 1); + z1 = rcClamp(z1, 0, h - 1); + // Clip the triangle into all grid cells it touches. - float buf[7*3*4]; - float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3; + float buf[7 * 3 * 4]; + float* in = buf; + float* inRow = buf + 7 * 3; + float* p1 = inRow + 7 * 3; + float* p2 = p1 + 7 * 3; rcVcopy(&in[0], v0); - rcVcopy(&in[1*3], v1); - rcVcopy(&in[2*3], v2); - int nvrow, nvIn = 3; - - for (int y = y0; y <= y1; ++y) + rcVcopy(&in[1 * 3], v1); + rcVcopy(&in[2 * 3], v2); + int nvRow; + int nvIn = 3; + + for (int z = z0; z <= z1; ++z) { // Clip polygon to row. Store the remaining polygon as well - const float cz = bmin[2] + y*cs; - dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2); + const float cellZ = hfBBMin[2] + (float)z * cellSize; + dividePoly(in, nvIn, inRow, &nvRow, p1, &nvIn, cellZ + cellSize, RC_AXIS_Z); rcSwap(in, p1); - if (nvrow < 3) continue; - // find the horizontal bounds in the row - float minX = inrow[0], maxX = inrow[0]; - for (int i=1; i inrow[i*3]) minX = inrow[i*3]; - if (maxX < inrow[i*3]) maxX = inrow[i*3]; + 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); + if (z < 0) + { + continue; + } + + // 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, nv2 = nvrow; + int nv; + int nv2 = nvRow; for (int x = x0; x <= x1; ++x) { // Clip polygon to column. store the remaining polygon as well - const float cx = bmin[0] + x*cs; - dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0); - rcSwap(inrow, p2); - if (nv < 3) continue; + const float cx = hfBBMin[0] + (float)x * cellSize; + dividePoly(inRow, nv2, p1, &nv, p2, &nv2, cx + cellSize, RC_AXIS_X); + rcSwap(inRow, p2); + + if (nv < 3) + { + continue; + } + if (x < 0) + { + continue; + } // Calculate min and max of the span. - float smin = p1[1], smax = p1[1]; - for (int i = 1; i < nv; ++i) + float spanMin = p1[1]; + float spanMax = p1[1]; + for (int vert = 1; vert < nv; ++vert) { - smin = rcMin(smin, p1[i*3+1]); - smax = rcMax(smax, p1[i*3+1]); + spanMin = rcMin(spanMin, p1[vert * 3 + 1]); + spanMax = rcMax(spanMax, p1[vert * 3 + 1]); } - 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; + 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; + } + // 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 ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT); - - if (!addSpan(hf, x, y, ismin, ismax, area, flagMergeThr)) + unsigned short spanMinCellIndex = (unsigned short)rcClamp((int)floorf(spanMin * inverseCellHeight), 0, 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, z, spanMinCellIndex, spanMaxCellIndex, areaID, flagMergeThreshold)) + { return false; + } } } return true; } -/// @par -/// -/// No spans will be added if the triangle does not overlap the heightfield grid. -/// -/// @see rcHeightfield -bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, - const unsigned char area, rcHeightfield& solid, - const int flagMergeThr) +bool rcRasterizeTriangle(rcContext* context, + const float* v0, const float* v1, const float* v2, + const unsigned char areaID, rcHeightfield& heightfield, const int flagMergeThreshold) { - 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; - const float ich = 1.0f/solid.ch; - if (!rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) + // Rasterize the single triangle. + const float inverseCellSize = 1.0f / heightfield.cs; + 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 true; } -/// @par -/// -/// Spans will only be added for triangles that overlap the heightfield grid. -/// -/// @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) +bool rcRasterizeTriangles(rcContext* context, + const float* verts, const int /*nv*/, + const int* tris, const unsigned char* triAreaIDs, const int numTris, + rcHeightfield& heightfield, const int flagMergeThreshold) { - 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; - const float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) + // Rasterize the triangles. + const float inverseCellSize = 1.0f / heightfield.cs; + const float inverseCellHeight = 1.0f / heightfield.ch; + for (int triIndex = 0; triIndex < numTris; ++triIndex) { - const float* v0 = &verts[tris[i*3+0]*3]; - const float* v1 = &verts[tris[i*3+1]*3]; - const float* v2 = &verts[tris[i*3+2]*3]; - // Rasterize. - if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) + const float* v0 = &verts[tris[triIndex * 3 + 0] * 3]; + const float* v1 = &verts[tris[triIndex * 3 + 1] * 3]; + const float* v2 = &verts[tris[triIndex * 3 + 2] * 3]; + if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold)) { - ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); + context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); return false; } } @@ -390,31 +502,26 @@ bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, return true; } -/// @par -/// -/// Spans will only be added for triangles that overlap the heightfield grid. -/// -/// @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) +bool rcRasterizeTriangles(rcContext* context, + const float* verts, const int /*nv*/, + const unsigned short* tris, const unsigned char* triAreaIDs, const int numTris, + rcHeightfield& heightfield, const int flagMergeThreshold) { - rcAssert(ctx); + rcAssert(context != NULL); - rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); - - const float ics = 1.0f/solid.cs; - const float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) + rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES); + + // Rasterize the triangles. + const float inverseCellSize = 1.0f / heightfield.cs; + const float inverseCellHeight = 1.0f / heightfield.ch; + for (int triIndex = 0; triIndex < numTris; ++triIndex) { - const float* v0 = &verts[tris[i*3+0]*3]; - const float* v1 = &verts[tris[i*3+1]*3]; - const float* v2 = &verts[tris[i*3+2]*3]; - // Rasterize. - if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) + const float* v0 = &verts[tris[triIndex * 3 + 0] * 3]; + const float* v1 = &verts[tris[triIndex * 3 + 1] * 3]; + const float* v2 = &verts[tris[triIndex * 3 + 2] * 3]; + if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold)) { - ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); + context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); return false; } } @@ -422,30 +529,25 @@ bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, return true; } -/// @par -/// -/// Spans will only be added for triangles that overlap the heightfield grid. -/// -/// @see rcHeightfield -bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, - rcHeightfield& solid, const int flagMergeThr) +bool rcRasterizeTriangles(rcContext* context, + const float* verts, const unsigned char* triAreaIDs, const int numTris, + rcHeightfield& heightfield, const int flagMergeThreshold) { - rcAssert(ctx); + rcAssert(context != NULL); + + rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES); - rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); - - const float ics = 1.0f/solid.cs; - const float ich = 1.0f/solid.ch; - // Rasterize triangles. - for (int i = 0; i < nt; ++i) + // Rasterize the triangles. + const float inverseCellSize = 1.0f / heightfield.cs; + const float inverseCellHeight = 1.0f / heightfield.ch; + for (int triIndex = 0; triIndex < numTris; ++triIndex) { - const float* v0 = &verts[(i*3+0)*3]; - const float* v1 = &verts[(i*3+1)*3]; - const float* v2 = &verts[(i*3+2)*3]; - // Rasterize. - if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) + const float* v0 = &verts[(triIndex * 3 + 0) * 3]; + const float* v1 = &verts[(triIndex * 3 + 1) * 3]; + const float* v2 = &verts[(triIndex * 3 + 2) * 3]; + if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold)) { - ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); + context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); return false; } } diff --git a/Source/ThirdParty/recastnavigation/RecastRegion.cpp b/Source/ThirdParty/recastnavigation/RecastRegion.cpp index 48318688b..4a7e841a9 100644 --- a/Source/ThirdParty/recastnavigation/RecastRegion.cpp +++ b/Source/ThirdParty/recastnavigation/RecastRegion.cpp @@ -17,7 +17,6 @@ // #include -#define _USE_MATH_DEFINES #include #include #include From 2655dd12d63b89155e7dabe5fa022e0b69e3058a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Sep 2023 13:40:01 +0200 Subject: [PATCH 26/28] Reapply e218cc417fa184cdea10c7024f174cfee505da35 but a little better --- .../ThirdParty/recastnavigation/DetourCommon.cpp | 7 +++++++ .../ThirdParty/recastnavigation/DetourCommon.h | 2 ++ .../recastnavigation/DetourNavMeshQuery.cpp | 16 +++++++++++----- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Source/ThirdParty/recastnavigation/DetourCommon.cpp b/Source/ThirdParty/recastnavigation/DetourCommon.cpp index b8457828e..8a9a4a42a 100644 --- a/Source/ThirdParty/recastnavigation/DetourCommon.cpp +++ b/Source/ThirdParty/recastnavigation/DetourCommon.cpp @@ -183,6 +183,13 @@ float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, floa return dx*dx + dz*dz; } +float dtDistancePtPtSqr2D(const float* pt, const float* p) +{ + float dx = pt[0] - p[0]; + float dz = pt[2] - p[2]; + return dx*dx + dz*dz; +} + void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts) { tc[0] = 0.0f; diff --git a/Source/ThirdParty/recastnavigation/DetourCommon.h b/Source/ThirdParty/recastnavigation/DetourCommon.h index d54bc7edc..656311e90 100644 --- a/Source/ThirdParty/recastnavigation/DetourCommon.h +++ b/Source/ThirdParty/recastnavigation/DetourCommon.h @@ -416,6 +416,8 @@ bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nve float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t); +float dtDistancePtPtSqr2D(const float* pt, const float* p); + /// Derives the centroid of a convex polygon. /// @param[out] tc The centroid of the polgyon. [(x, y, z)] /// @param[in] idx The polygon indices. [(vertIndex) * @p nidx] diff --git a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp index cebe26b5d..7faefde81 100644 --- a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp +++ b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp @@ -482,12 +482,18 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f v = &randomTile->verts[randomPoly->verts[j]*3]; dtVcopy(&verts[j*3],v); } - - const float s = frand(); - const float t = frand(); - + float pt[3]; - dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt); + int checksLimit = 100; + do + { + const float s = frand(); + const float t = frand(); + dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt); + } + while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr && checksLimit-- > 0); + if (checksLimit <= 0) + return DT_FAILURE; closestPointOnPoly(randomPolyRef, pt, pt, NULL); From 255e47fa1e83fa74ad6c7b3b1ae733b25fa517c5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Sep 2023 14:00:37 +0200 Subject: [PATCH 27/28] Fix crash due to invalid message in Assimp --- Source/Engine/Core/Types/String.cpp | 2 +- Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Core/Types/String.cpp b/Source/Engine/Core/Types/String.cpp index 98b2c08dd..dab000873 100644 --- a/Source/Engine/Core/Types/String.cpp +++ b/Source/Engine/Core/Types/String.cpp @@ -72,7 +72,7 @@ void String::Set(const char* chars, int32 length) } _length = length; } - if (chars) + if (chars && length) StringUtils::ConvertANSI2UTF16(chars, _data, length, _length); } diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index daecf8c45..d0fba8f12 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -42,7 +42,16 @@ public: void write(const char* message) override { 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); } }; From 49a6b5734a033c05cda6a127265f2ba0686f5738 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Sep 2023 14:14:57 +0200 Subject: [PATCH 28/28] Fix crash when setting material instance base material before it's loaded --- Source/Engine/Content/Assets/MaterialInstance.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index 7581692e1..3bf0c54cd 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -205,8 +205,6 @@ void MaterialInstance::Bind(BindParameters& params) Asset::LoadResult MaterialInstance::load() { - ASSERT(_baseMaterial == nullptr); - // Get main chunk auto chunk0 = GetChunk(0); if (chunk0 == nullptr || chunk0->IsMissing()) @@ -229,6 +227,7 @@ Asset::LoadResult MaterialInstance::load() else { // Clear parameters if has no material loaded + _baseMaterial = nullptr; Params.Dispose(); ParamsChanged(); }