From 6dd0957c4ad8f6650c3c4214ab95fb9a445b5d83 Mon Sep 17 00:00:00 2001 From: Crawcik Date: Thu, 14 Sep 2023 22:34:03 +0200 Subject: [PATCH 01/89] Adding better finding dotnet root location for mac&unix --- .../Flax.Build/Build/DotNet/DotNetSdk.cs | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index d3a706a43..e61642379 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -192,18 +192,18 @@ namespace Flax.Build case TargetPlatform.Linux: { // TODO: Support /etc/dotnet/install_location - - // Detect custom RID in some distros - rid = Utilities.ReadProcessOutput("dotnet", "--info").Split('\n').FirstOrDefault(x => x.StartsWith(" RID:"), "").Replace("RID:", "").Trim(); + rid = ""; ridFallback = $"linux-{arch}"; - if (rid == ridFallback) - ridFallback = ""; if (string.IsNullOrEmpty(dotnetPath)) + dotnetPath ??= SearchForDotnetLocationLinux(); + + if (dotnetPath == null) { - dotnetPath = "/usr/lib/dotnet/"; - if (!Directory.Exists(dotnetPath)) - dotnetPath = "/usr/share/dotnet/"; + rid = Utilities.ReadProcessOutput("dotnet", "--info").Split('\n').FirstOrDefault(x => x.StartsWith(" RID:"), "").Replace("RID:", "").Trim(); + if (rid == ridFallback) + ridFallback = ""; } + break; } case TargetPlatform.Mac: @@ -211,7 +211,12 @@ namespace Flax.Build rid = $"osx-{arch}"; ridFallback = ""; if (string.IsNullOrEmpty(dotnetPath)) - dotnetPath = "/usr/local/share/dotnet/"; + { + if (Directory.Exists("/usr/local/share/dotnet")) // Officialy recommended dotnet location + dotnetPath = "/usr/local/share/dotnet"; + else if (Environment.GetEnvironmentVariable("PATH") is string globalBinPath) + dotnetPath = globalBinPath.Split(':').FirstOrDefault(x => File.Exists(Path.Combine(x, "dotnet"))); + } break; } default: throw new InvalidPlatformException(platform); @@ -441,5 +446,18 @@ namespace Flax.Build // TODO: reject 'future' versions like .Net 8? return versions.OrderByDescending(ParseVersion).FirstOrDefault(); } + + private static string SearchForDotnetLocationLinux() + { + if (File.Exists("/etc/dotnet/install_location")) // Officialy recommended dotnet location file + return File.ReadAllText("/etc/dotnet/install_location").Trim(); + if (Directory.Exists("/usr/share/dotnet")) // Officialy recommended dotnet location + return"/usr/share/dotnet"; + if (Directory.Exists("/usr/lib/dotnet")) // Deprecated recommended dotnet location + return "/usr/lib/dotnet"; + if (Environment.GetEnvironmentVariable("PATH") is string globalBinPath) // Searching for dotnet binary + return globalBinPath.Split(':').FirstOrDefault(x => File.Exists(Path.Combine(x, "dotnet"))); + return null; + } } } From 73bf28dc4712da7d465dda33299e107cfbc9df46 Mon Sep 17 00:00:00 2001 From: Crawcik Date: Thu, 14 Sep 2023 22:37:56 +0200 Subject: [PATCH 02/89] Change of priorities on RID --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index e61642379..ecb6c9fea 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -192,17 +192,13 @@ namespace Flax.Build case TargetPlatform.Linux: { // TODO: Support /etc/dotnet/install_location - rid = ""; - ridFallback = $"linux-{arch}"; + rid = $"linux-{arch}"; + ridFallback = ""; if (string.IsNullOrEmpty(dotnetPath)) dotnetPath ??= SearchForDotnetLocationLinux(); if (dotnetPath == null) - { - rid = Utilities.ReadProcessOutput("dotnet", "--info").Split('\n').FirstOrDefault(x => x.StartsWith(" RID:"), "").Replace("RID:", "").Trim(); - if (rid == ridFallback) - ridFallback = ""; - } + ridFallback = Utilities.ReadProcessOutput("dotnet", "--info").Split('\n').FirstOrDefault(x => x.StartsWith(" RID:"), "").Replace("RID:", "").Trim(); break; } From 64a5d895bd607a90bce13832ba353c7459578b7b Mon Sep 17 00:00:00 2001 From: Crawcik Date: Thu, 14 Sep 2023 23:18:12 +0200 Subject: [PATCH 03/89] Better version checking --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index ecb6c9fea..4b430702f 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -428,7 +428,8 @@ namespace Flax.Build version = version.Substring(0, version.IndexOf("-")); rev = 0; } - Version ver = new Version(version); + if (!Version.TryParse(version, out var ver)) + return null; return new Version(ver.Major, ver.Minor, ver.Build, rev); } From 1dd7a27568e6d4d1438bcb849f210960bf02dbdf Mon Sep 17 00:00:00 2001 From: Crawcik Date: Thu, 14 Sep 2023 23:19:36 +0200 Subject: [PATCH 04/89] Checking for executable --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index 4b430702f..66b578693 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -448,9 +448,9 @@ namespace Flax.Build { if (File.Exists("/etc/dotnet/install_location")) // Officialy recommended dotnet location file return File.ReadAllText("/etc/dotnet/install_location").Trim(); - if (Directory.Exists("/usr/share/dotnet")) // Officialy recommended dotnet location + if (File.Exists("/usr/share/dotnet/dotnet")) // Officialy recommended dotnet location return"/usr/share/dotnet"; - if (Directory.Exists("/usr/lib/dotnet")) // Deprecated recommended dotnet location + if (File.Exists("/usr/lib/dotnet/dotnet")) // Deprecated recommended dotnet location return "/usr/lib/dotnet"; if (Environment.GetEnvironmentVariable("PATH") is string globalBinPath) // Searching for dotnet binary return globalBinPath.Split(':').FirstOrDefault(x => File.Exists(Path.Combine(x, "dotnet"))); From 2d2bfd9cd1c066c758de96c5314a36964be34d0c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 16 Sep 2023 14:14:26 +0300 Subject: [PATCH 05/89] 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 06/89] 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 07/89] 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 08/89] 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 09/89] 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 10/89] 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 11/89] 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 12/89] 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 13/89] 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 14/89] 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 15/89] 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 16/89] 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 17/89] 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 18/89] 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 19/89] 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 20/89] 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 21/89] 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 22/89] 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 23/89] 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 24/89] 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 25/89] 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 26/89] 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 27/89] 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 28/89] 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 29/89] 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 30/89] 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 31/89] 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 32/89] 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(); } From 4c5168a976f7f4b599c7ec513e3e99b2b892c087 Mon Sep 17 00:00:00 2001 From: Denys Date: Tue, 19 Sep 2023 16:50:17 +0200 Subject: [PATCH 33/89] Use `xdg-open` to open file manager in Linux `nautilus` is GNOME file manager. Other distros might use another FM. As a common ground we can use `xdg-open` which is a part of `freedesktop.org` and should be available almost everywhere. --- Source/Engine/Platform/Linux/LinuxFileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp index f0d5ff499..279813470 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp @@ -136,7 +136,7 @@ bool LinuxFileSystem::ShowFileExplorer(const StringView& path) { const StringAsANSI<> pathAnsi(*path, path.Length()); char cmd[2048]; - sprintf(cmd, "nautilus %s &", pathAnsi.Get()); + sprintf(cmd, "xdg-open %s &", pathAnsi.Get()); system(cmd); return false; } From a56ce92867fc410dfe66cde64bea920ecc861d3c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Sep 2023 23:07:10 +0200 Subject: [PATCH 34/89] Fix crash when importing model with materials and `Split Objects` enabled --- Source/Editor/GUI/ComboBox.cs | 2 +- .../Engine/Tools/ModelTool/ModelTool.Assimp.cpp | 8 ++++---- .../Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp | 8 ++++---- Source/Engine/Tools/ModelTool/ModelTool.cpp | 16 ++++++++++++++++ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index da2106450..0417cc7e3 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -545,7 +545,7 @@ namespace FlaxEditor.GUI Render2D.DrawRectangle(clientRect.MakeExpanded(-2.0f), borderColor); // Check if has selected item - if (_selectedIndices.Count > 0) + if (_selectedIndices != null && _selectedIndices.Count > 0) { string text = _selectedIndices.Count == 1 ? _items[_selectedIndices[0]] : "Multiple Values"; diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index d0fba8f12..ab9b6b606 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -716,13 +716,13 @@ bool ModelTool::ImportDataAssimp(const char* path, ImportedModelData& data, Opti if (EnumHasAnyFlags(data.Types, ImportDataTypes::Geometry) && context->Scene->HasMeshes()) { const int meshCount = context->Scene->mNumMeshes; - if (options.SplitObjects && options.ObjectIndex == -1) + if (options.SplitObjects && options.ObjectIndex == -1 && meshCount > 1) { // Import the first object within this call options.SplitObjects = false; options.ObjectIndex = 0; - if (meshCount > 1 && options.OnSplitImport.IsBinded()) + if (options.OnSplitImport.IsBinded()) { // Split all animations into separate assets LOG(Info, "Splitting imported {0} meshes", meshCount); @@ -789,13 +789,13 @@ bool ModelTool::ImportDataAssimp(const char* path, ImportedModelData& data, Opti if (EnumHasAnyFlags(data.Types, ImportDataTypes::Animations) && context->Scene->HasAnimations()) { const int32 animCount = (int32)context->Scene->mNumAnimations; - if (options.SplitObjects && options.ObjectIndex == -1) + if (options.SplitObjects && options.ObjectIndex == -1 && animCount > 1) { // Import the first object within this call options.SplitObjects = false; options.ObjectIndex = 0; - if (animCount > 1 && options.OnSplitImport.IsBinded()) + if (options.OnSplitImport.IsBinded()) { // Split all animations into separate assets LOG(Info, "Splitting imported {0} animations", animCount); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index 1eb9e1f27..ab55df7f6 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -1245,13 +1245,13 @@ bool ModelTool::ImportDataOpenFBX(const char* path, ImportedModelData& data, Opt if (EnumHasAnyFlags(data.Types, ImportDataTypes::Geometry) && context->Scene->getMeshCount() > 0) { const int meshCount = context->Scene->getMeshCount(); - if (options.SplitObjects && options.ObjectIndex == -1) + if (options.SplitObjects && options.ObjectIndex == -1 && meshCount > 1) { // Import the first object within this call options.SplitObjects = false; options.ObjectIndex = 0; - if (meshCount > 1 && options.OnSplitImport.IsBinded()) + if (options.OnSplitImport.IsBinded()) { // Split all animations into separate assets LOG(Info, "Splitting imported {0} meshes", meshCount); @@ -1328,13 +1328,13 @@ bool ModelTool::ImportDataOpenFBX(const char* path, ImportedModelData& data, Opt if (EnumHasAnyFlags(data.Types, ImportDataTypes::Animations)) { const int animCount = context->Scene->getAnimationStackCount(); - if (options.SplitObjects && options.ObjectIndex == -1) + if (options.SplitObjects && options.ObjectIndex == -1 && animCount > 1) { // Import the first object within this call options.SplitObjects = false; options.ObjectIndex = 0; - if (animCount > 1 && options.OnSplitImport.IsBinded()) + if (options.OnSplitImport.IsBinded()) { // Split all animations into separate assets LOG(Info, "Splitting imported {0} animations", animCount); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 19f203667..99873c016 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -16,6 +16,7 @@ #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/Models/ModelData.h" #include "Engine/Content/Assets/Model.h" +#include "Engine/Content/Content.h" #include "Engine/Serialization/MemoryWriteStream.h" #if USE_EDITOR #include "Engine/Core/Types/DateTime.h" @@ -909,6 +910,10 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op { auto& texture = data.Textures[i]; + // When splitting imported meshes allow only the first mesh to import assets (mesh[0] is imported after all following ones so import assets during mesh[1]) + if (!options.SplitObjects && options.ObjectIndex != 1 && options.ObjectIndex != -1) + continue; + // Auto-import textures if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Textures) == ImportDataTypes::None || texture.FilePath.IsEmpty()) continue; @@ -994,6 +999,17 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op materialOptions.Info.CullMode = CullMode::TwoSided; if (!Math::IsOne(material.Opacity.Value) || material.Opacity.TextureIndex != -1) materialOptions.Info.BlendMode = MaterialBlendMode::Transparent; + + // When splitting imported meshes allow only the first mesh to import assets (mesh[0] is imported after all following ones so import assets during mesh[1]) + if (!options.SplitObjects && options.ObjectIndex != 1 && options.ObjectIndex != -1) + { + // Find that asset create previously + AssetInfo info; + if (Content::GetAssetInfo(assetPath, info)) + material.AssetID = info.ID; + continue; + } + AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialTag, assetPath, material.AssetID, &materialOptions); #endif } From 4a1787dfbe310c776a6ccd6ae6554222b3797c68 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 20 Sep 2023 00:06:22 +0300 Subject: [PATCH 35/89] Generate project files at startup when project Cache was cleared --- Source/Editor/States/LoadingState.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Editor/States/LoadingState.cs b/Source/Editor/States/LoadingState.cs index 746c8d92b..698dc192f 100644 --- a/Source/Editor/States/LoadingState.cs +++ b/Source/Editor/States/LoadingState.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.IO; using FlaxEngine; using FlaxEngine.Utilities; @@ -54,6 +55,13 @@ namespace FlaxEditor.States } else if (Editor.Options.Options.General.ForceScriptCompilationOnStartup && !skipCompile) { + // Generate project files when Cache is missing or was cleared previously + if (!Directory.Exists(Path.Combine(Editor.GameProject?.ProjectFolderPath, "Cache", "Intermediate")) || + !Directory.Exists(Path.Combine(Editor.GameProject?.ProjectFolderPath, "Cache", "Projects"))) + { + var customArgs = Editor.Instance.CodeEditing.SelectedEditor.GenerateProjectCustomArgs; + ScriptsBuilder.GenerateProject(customArgs); + } // Compile scripts before loading any scenes, then we load them and can open scenes ScriptsBuilder.Compile(); } From 7e81fdbd758fab7c089b1dcc47f3b7c81cd384cd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Sep 2023 23:45:30 +0200 Subject: [PATCH 36/89] Fix crash when D3D11 backend fails to create shader due to driver failure --- Source/Engine/Graphics/Shaders/GPUShader.cpp | 2 +- .../DirectX/DX11/GPUBufferDX11.cpp | 6 +-- .../DirectX/DX11/GPUDeviceDX11.cpp | 28 +++++++------- .../DirectX/DX11/GPUSamplerDX11.cpp | 2 +- .../DirectX/DX11/GPUShaderDX11.cpp | 21 ++++++---- .../DirectX/DX11/GPUSwapChainDX11.cpp | 24 ++++++------ .../DirectX/DX11/GPUTextureDX11.cpp | 38 +++++++++---------- .../DirectX/DX12/CommandAllocatorPoolDX12.cpp | 4 +- .../DirectX/DX12/CommandQueueDX12.cpp | 4 +- .../DirectX/DX12/DescriptorHeapDX12.cpp | 4 +- .../DirectX/DX12/GPUBufferDX12.cpp | 2 +- .../DirectX/DX12/GPUContextDX12.cpp | 2 +- .../DirectX/DX12/GPUDeviceDX12.cpp | 28 +++++++------- .../DirectX/DX12/GPUSwapChainDX12.cpp | 18 ++++----- .../DirectX/DX12/GPUTextureDX12.cpp | 4 +- .../DirectX/DX12/GPUTimerQueryDX12.cpp | 2 +- .../DirectX/DX12/QueryHeapDX12.cpp | 6 +-- .../DirectX/DX12/UploadBufferDX12.cpp | 4 +- .../GraphicsDevice/DirectX/RenderToolsDX.h | 8 ++-- Source/Engine/Renderer/HistogramPass.cpp | 2 +- 20 files changed, 108 insertions(+), 101 deletions(-) diff --git a/Source/Engine/Graphics/Shaders/GPUShader.cpp b/Source/Engine/Graphics/Shaders/GPUShader.cpp index 3639e01b1..e76ee9996 100644 --- a/Source/Engine/Graphics/Shaders/GPUShader.cpp +++ b/Source/Engine/Graphics/Shaders/GPUShader.cpp @@ -120,7 +120,7 @@ bool GPUShader::Create(MemoryReadStream& stream) GPUShaderProgram* shader = CreateGPUShaderProgram(type, initializer, cache, cacheSize, stream); if (shader == nullptr) { - LOG(Warning, "Failed to create shader program."); + LOG(Error, "Failed to create {} Shader program '{}'.", ::ToString(type), String(initializer.Name)); return true; } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUBufferDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUBufferDX11.cpp index b83ab6a08..352fd921a 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUBufferDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUBufferDX11.cpp @@ -105,7 +105,7 @@ bool GPUBufferDX11::OnInit() data.SysMemPitch = bufferDesc.ByteWidth; data.SysMemSlicePitch = 0; } - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateBuffer(&bufferDesc, _desc.InitData ? &data : nullptr, &_resource)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateBuffer(&bufferDesc, _desc.InitData ? &data : nullptr, &_resource)); // Set state DX_SET_DEBUG_NAME(_resource, GetName()); @@ -135,7 +135,7 @@ bool GPUBufferDX11::OnInit() srvDesc.Buffer.NumElements = numElements; } ID3D11ShaderResourceView* srv; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateShaderResourceView(_resource, &srvDesc, &srv)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateShaderResourceView(_resource, &srvDesc, &srv)); _view.SetSRV(srv); } if (useUAV) @@ -156,7 +156,7 @@ bool GPUBufferDX11::OnInit() else uavDesc.Format = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindUnorderedAccessFormat(_desc.Format)); ID3D11UnorderedAccessView* uav; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateUnorderedAccessView(_resource, &uavDesc, &uav)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateUnorderedAccessView(_resource, &uavDesc, &uav)); _view.SetUAV(uav); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp index 10e60aebc..c29c6254d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp @@ -143,7 +143,7 @@ GPUDevice* GPUDeviceDX11::Create() if (tempAdapter && TryCreateDevice(tempAdapter, maxAllowedFeatureLevel, &adapter.MaxFeatureLevel)) { adapter.Index = index; - VALIDATE_DIRECTX_RESULT(tempAdapter->GetDesc(&adapter.Description)); + VALIDATE_DIRECTX_CALL(tempAdapter->GetDesc(&adapter.Description)); uint32 outputs = RenderToolsDX::CountAdapterOutputs(tempAdapter); LOG(Info, "Adapter {1}: '{0}', DirectX {2}", adapter.Description.Description, index, RenderToolsDX::GetFeatureLevelString(adapter.MaxFeatureLevel)); @@ -163,7 +163,7 @@ GPUDevice* GPUDeviceDX11::Create() if (tempAdapter && TryCreateDevice(tempAdapter, maxAllowedFeatureLevel, &adapter.MaxFeatureLevel)) { DXGI_ADAPTER_DESC desc; - VALIDATE_DIRECTX_RESULT(tempAdapter->GetDesc(&desc)); + VALIDATE_DIRECTX_CALL(tempAdapter->GetDesc(&desc)); for (int i = 0; i < adapters.Count(); i++) { if (adapters[i].Description.AdapterLuid.LowPart == desc.AdapterLuid.LowPart && @@ -274,7 +274,7 @@ ID3D11BlendState* GPUDeviceDX11::GetBlendState(const BlendingMode& blending) #endif // Create object - VALIDATE_DIRECTX_RESULT(_device->CreateBlendState(&desc, &blendState)); + VALIDATE_DIRECTX_CALL(_device->CreateBlendState(&desc, &blendState)); // Cache blend state BlendStates.Add(blending, blendState); @@ -333,7 +333,7 @@ bool GPUDeviceDX11::Init() // Create DirectX device D3D_FEATURE_LEVEL createdFeatureLevel = static_cast(0); auto targetFeatureLevel = GetD3DFeatureLevel(); - VALIDATE_DIRECTX_RESULT(D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, &targetFeatureLevel, 1, D3D11_SDK_VERSION, &_device, &createdFeatureLevel, &_imContext)); + VALIDATE_DIRECTX_CALL(D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, &targetFeatureLevel, 1, D3D11_SDK_VERSION, &_device, &createdFeatureLevel, &_imContext)); // Validate result ASSERT(_device); @@ -409,7 +409,7 @@ bool GPUDeviceDX11::Init() // Init debug layer #if GPU_ENABLE_DIAGNOSTICS ComPtr infoQueue; - VALIDATE_DIRECTX_RESULT(_device->QueryInterface(IID_PPV_ARGS(&infoQueue))); + VALIDATE_DIRECTX_CALL(_device->QueryInterface(IID_PPV_ARGS(&infoQueue))); if (infoQueue) { D3D11_INFO_QUEUE_FILTER filter; @@ -457,7 +457,7 @@ bool GPUDeviceDX11::Init() samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; result = _device->CreateSamplerState(&samplerDesc, &_samplerLinearClamp); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); // Point Clamp samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; @@ -467,7 +467,7 @@ bool GPUDeviceDX11::Init() samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; result = _device->CreateSamplerState(&samplerDesc, &_samplerPointClamp); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); // Linear Wrap samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; @@ -477,7 +477,7 @@ bool GPUDeviceDX11::Init() samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; result = _device->CreateSamplerState(&samplerDesc, &_samplerLinearWrap); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); // Point Wrap samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; @@ -487,7 +487,7 @@ bool GPUDeviceDX11::Init() samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; result = _device->CreateSamplerState(&samplerDesc, &_samplerPointWrap); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); // Shadow samplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT; @@ -500,7 +500,7 @@ bool GPUDeviceDX11::Init() samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; result = _device->CreateSamplerState(&samplerDesc, &_samplerShadow); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); // Shadow PCF samplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; @@ -514,7 +514,7 @@ bool GPUDeviceDX11::Init() samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; result = _device->CreateSamplerState(&samplerDesc, &_samplerShadowPCF); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); } // Rasterizer States @@ -534,7 +534,7 @@ bool GPUDeviceDX11::Init() rDesc.AntialiasedLineEnable = !!wireframe; \ rDesc.DepthClipEnable = !!depthClip; \ result = _device->CreateRasterizerState(&rDesc, &RasterizerStates[index]); \ - LOG_DIRECTX_RESULT_WITH_RETURN(result) + LOG_DIRECTX_RESULT_WITH_RETURN(result, true) CREATE_RASTERIZER_STATE(CullMode::Normal, D3D11_CULL_BACK, false, false); CREATE_RASTERIZER_STATE(CullMode::Inverted, D3D11_CULL_FRONT, false, false); CREATE_RASTERIZER_STATE(CullMode::TwoSided, D3D11_CULL_NONE, false, false); @@ -568,7 +568,7 @@ bool GPUDeviceDX11::Init() dsDesc.DepthFunc = (D3D11_COMPARISON_FUNC)depthFunc; \ index = (int32)depthFunc + (depthEnable ? 0 : 9) + (depthWrite ? 0 : 18); \ HRESULT result = _device->CreateDepthStencilState(&dsDesc, &DepthStencilStates[index]); \ - LOG_DIRECTX_RESULT_WITH_RETURN(result); } + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); } CREATE_DEPTH_STENCIL_STATE(false, false); CREATE_DEPTH_STENCIL_STATE(false, true); CREATE_DEPTH_STENCIL_STATE(true, true); @@ -666,7 +666,7 @@ void GPUDeviceDX11::DrawEnd() #if GPU_ENABLE_DIAGNOSTICS // Flush debug messages queue ComPtr infoQueue; - VALIDATE_DIRECTX_RESULT(_device->QueryInterface(IID_PPV_ARGS(&infoQueue))); + VALIDATE_DIRECTX_CALL(_device->QueryInterface(IID_PPV_ARGS(&infoQueue))); if (infoQueue) { Array data; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.cpp index 428390637..fadb20ead 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.cpp @@ -106,7 +106,7 @@ bool GPUSamplerDX11::OnInit() samplerDesc.MinLOD = _desc.MinMipLevel; samplerDesc.MaxLOD = _desc.MaxMipLevel; HRESULT result = _device->GetDevice()->CreateSamplerState(&samplerDesc, &SamplerState); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); ASSERT(SamplerState != nullptr); _memoryUsage = sizeof(D3D11_SAMPLER_DESC); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp index 0e4b7ac0c..fd14f0aeb 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp @@ -10,6 +10,7 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, MemoryReadStream& stream) { GPUShaderProgram* shader = nullptr; + HRESULT result; switch (type) { case ShaderStage::Vertex: @@ -90,12 +91,13 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const if (inputLayoutSize > 0) { // Create input layout - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateInputLayout(inputLayoutDesc, inputLayoutSize, cacheBytes, cacheSize, &inputLayout)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateInputLayout(inputLayoutDesc, inputLayoutSize, cacheBytes, cacheSize, &inputLayout)); } // Create shader ID3D11VertexShader* buffer = nullptr; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateVertexShader(cacheBytes, cacheSize, nullptr, &buffer)); + result = _device->GetDevice()->CreateVertexShader(cacheBytes, cacheSize, nullptr, &buffer); + LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr); // Create object shader = New(initializer, buffer, inputLayout, inputLayoutSize); @@ -109,7 +111,8 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const // Create shader ID3D11HullShader* buffer = nullptr; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateHullShader(cacheBytes, cacheSize, nullptr, &buffer)); + result = _device->GetDevice()->CreateHullShader(cacheBytes, cacheSize, nullptr, &buffer); + LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr); // Create object shader = New(initializer, buffer, controlPointsCount); @@ -119,7 +122,8 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const { // Create shader ID3D11DomainShader* buffer = nullptr; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateDomainShader(cacheBytes, cacheSize, nullptr, &buffer)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateDomainShader(cacheBytes, cacheSize, nullptr, &buffer)); + LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr); // Create object shader = New(initializer, buffer); @@ -129,7 +133,8 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const { // Create shader ID3D11GeometryShader* buffer = nullptr; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateGeometryShader(cacheBytes, cacheSize, nullptr, &buffer)); + result = _device->GetDevice()->CreateGeometryShader(cacheBytes, cacheSize, nullptr, &buffer); + LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr); // Create object shader = New(initializer, buffer); @@ -139,7 +144,8 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const { // Create shader ID3D11PixelShader* buffer = nullptr; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreatePixelShader(cacheBytes, cacheSize, nullptr, &buffer)); + result = _device->GetDevice()->CreatePixelShader(cacheBytes, cacheSize, nullptr, &buffer); + LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr); // Create object shader = New(initializer, buffer); @@ -149,7 +155,8 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const { // Create shader ID3D11ComputeShader* buffer = nullptr; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateComputeShader(cacheBytes, cacheSize, nullptr, &buffer)); + result = _device->GetDevice()->CreateComputeShader(cacheBytes, cacheSize, nullptr, &buffer); + LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr); // Create object shader = New(initializer, buffer); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSwapChainDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSwapChainDX11.cpp index 7b9564f54..d5c0e5c55 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSwapChainDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSwapChainDX11.cpp @@ -28,13 +28,13 @@ GPUSwapChainDX11::GPUSwapChainDX11(GPUDeviceDX11* device, Window* window) void GPUSwapChainDX11::getBackBuffer() { - VALIDATE_DIRECTX_RESULT(_swapChain->GetBuffer(0, __uuidof(_backBuffer), reinterpret_cast(&_backBuffer))); + VALIDATE_DIRECTX_CALL(_swapChain->GetBuffer(0, __uuidof(_backBuffer), reinterpret_cast(&_backBuffer))); ID3D11RenderTargetView* rtv; ID3D11ShaderResourceView* srv; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateRenderTargetView(_backBuffer, nullptr, &rtv)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateRenderTargetView(_backBuffer, nullptr, &rtv)); #if GPU_USE_WINDOW_SRV - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateShaderResourceView(_backBuffer, nullptr, &srv)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateShaderResourceView(_backBuffer, nullptr, &srv)); #else srv = nullptr; #endif @@ -55,7 +55,7 @@ void GPUSwapChainDX11::OnReleaseGPU() // Disable fullscreen mode if (_swapChain) { - VALIDATE_DIRECTX_RESULT(_swapChain->SetFullscreenState(false, nullptr)); + VALIDATE_DIRECTX_CALL(_swapChain->SetFullscreenState(false, nullptr)); } #endif @@ -78,7 +78,7 @@ bool GPUSwapChainDX11::IsFullscreen() // Get state BOOL state; - VALIDATE_DIRECTX_RESULT(_swapChain->GetFullscreenState(&state, nullptr)); + VALIDATE_DIRECTX_CALL(_swapChain->GetFullscreenState(&state, nullptr)); return state == TRUE; } @@ -229,21 +229,21 @@ bool GPUSwapChainDX11::Resize(int32 width, int32 height) // Create swap chain #if PLATFORM_WINDOWS auto dxgi = _device->GetDXGIFactory(); - VALIDATE_DIRECTX_RESULT(dxgi->CreateSwapChain(_device->GetDevice(), &swapChainDesc, &_swapChain)); + VALIDATE_DIRECTX_CALL(dxgi->CreateSwapChain(_device->GetDevice(), &swapChainDesc, &_swapChain)); ASSERT(_swapChain); // Disable DXGI changes to the window - VALIDATE_DIRECTX_RESULT(dxgi->MakeWindowAssociation(_windowHandle, DXGI_MWA_NO_ALT_ENTER)); + VALIDATE_DIRECTX_CALL(dxgi->MakeWindowAssociation(_windowHandle, DXGI_MWA_NO_ALT_ENTER)); #else auto dxgiFactory = (IDXGIFactory2*)_device->GetDXGIFactory(); - VALIDATE_DIRECTX_RESULT(dxgiFactory->CreateSwapChainForCoreWindow(_device->GetDevice(), static_cast(_windowHandle), &swapChainDesc, nullptr, &_swapChain)); + VALIDATE_DIRECTX_CALL(dxgiFactory->CreateSwapChainForCoreWindow(_device->GetDevice(), static_cast(_windowHandle), &swapChainDesc, nullptr, &_swapChain)); ASSERT(_swapChain); // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and // ensures that the application will only render after each VSync, minimizing power consumption. ComPtr dxgiDevice; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->QueryInterface(IID_PPV_ARGS(&dxgiDevice))); - VALIDATE_DIRECTX_RESULT(dxgiDevice->SetMaximumFrameLatency(1)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->QueryInterface(IID_PPV_ARGS(&dxgiDevice))); + VALIDATE_DIRECTX_CALL(dxgiDevice->SetMaximumFrameLatency(1)); #endif } else @@ -252,10 +252,10 @@ bool GPUSwapChainDX11::Resize(int32 width, int32 height) #if PLATFORM_WINDOWS _swapChain->GetDesc(&swapChainDesc); - VALIDATE_DIRECTX_RESULT(_swapChain->ResizeBuffers(swapChainDesc.BufferCount, width, height, swapChainDesc.BufferDesc.Format, swapChainDesc.Flags)); + VALIDATE_DIRECTX_CALL(_swapChain->ResizeBuffers(swapChainDesc.BufferCount, width, height, swapChainDesc.BufferDesc.Format, swapChainDesc.Flags)); #else _swapChain->GetDesc1(&swapChainDesc); - VALIDATE_DIRECTX_RESULT(_swapChain->ResizeBuffers(swapChainDesc.BufferCount, width, height, swapChainDesc.Format, swapChainDesc.Flags)); + VALIDATE_DIRECTX_CALL(_swapChain->ResizeBuffers(swapChainDesc.BufferCount, width, height, swapChainDesc.Format, swapChainDesc.Flags)); #endif } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp index 73be91108..3d1979954 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp @@ -87,7 +87,7 @@ bool GPUTextureDX11::OnInit() result = device->CreateTexture2D(&textureDesc, nullptr, &texture); _resource = texture; } - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); ASSERT(_resource != nullptr); DX_SET_DEBUG_NAME(_resource, GetName()); @@ -135,7 +135,7 @@ void GPUTextureDX11::OnResidentMipsChanged() } ID3D11ShaderResourceView* srView = nullptr; if (mipLevels != 0) - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateShaderResourceView(_resource, &srDesc, &srView)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateShaderResourceView(_resource, &srDesc, &srView)); GPUTextureViewDX11& view = IsVolume() ? _handleVolume : _handlesPerSlice[0]; if (view.GetParent() == nullptr) view.Init(this, nullptr, srView, nullptr, nullptr, Format(), MultiSampleLevel()); @@ -201,7 +201,7 @@ void GPUTextureDX11::initHandles() srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; srDesc.Texture3D.MostDetailedMip = 0; srDesc.Texture3D.MipLevels = mipLevels; - VALIDATE_DIRECTX_RESULT(device->CreateShaderResourceView(_resource, &srDesc, &srView)); + VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView)); } if (useRTV) { @@ -209,7 +209,7 @@ void GPUTextureDX11::initHandles() rtDesc.Texture3D.MipSlice = 0; rtDesc.Texture3D.FirstWSlice = 0; rtDesc.Texture3D.WSize = Depth(); - VALIDATE_DIRECTX_RESULT(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); + VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); } if (useUAV) { @@ -217,7 +217,7 @@ void GPUTextureDX11::initHandles() uaDesc.Texture3D.MipSlice = 0; uaDesc.Texture3D.WSize = Depth(); uaDesc.Texture3D.FirstWSlice = 0; - VALIDATE_DIRECTX_RESULT(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView)); + VALIDATE_DIRECTX_CALL(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView)); } _handleVolume.Init(this, rtView, srView, nullptr, uaView, format, msaa); @@ -232,7 +232,7 @@ void GPUTextureDX11::initHandles() for (int32 sliceIndex = 0; sliceIndex < Depth(); sliceIndex++) { rtDesc.Texture3D.FirstWSlice = sliceIndex; - VALIDATE_DIRECTX_RESULT(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); + VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); _handlesPerSlice[sliceIndex].Init(this, rtView, nullptr, nullptr, nullptr, format, msaa); } } @@ -263,7 +263,7 @@ void GPUTextureDX11::initHandles() dsDesc.Texture2DArray.FirstArraySlice = arrayIndex; dsDesc.Texture2DArray.MipSlice = 0; } - VALIDATE_DIRECTX_RESULT(device->CreateDepthStencilView(_resource, &dsDesc, &dsView)); + VALIDATE_DIRECTX_CALL(device->CreateDepthStencilView(_resource, &dsDesc, &dsView)); } if (useRTV) { @@ -281,7 +281,7 @@ void GPUTextureDX11::initHandles() rtDesc.Texture2DArray.FirstArraySlice = arrayIndex; rtDesc.Texture2DArray.MipSlice = 0; } - VALIDATE_DIRECTX_RESULT(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); + VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); } if (useSRV) { @@ -305,7 +305,7 @@ void GPUTextureDX11::initHandles() srDesc.Texture2DArray.MipLevels = mipLevels; srDesc.Texture2DArray.MostDetailedMip = 0; } - VALIDATE_DIRECTX_RESULT(device->CreateShaderResourceView(_resource, &srDesc, &srView)); + VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView)); } } @@ -322,7 +322,7 @@ void GPUTextureDX11::initHandles() dsDesc.Texture2DArray.ArraySize = arraySize; dsDesc.Texture2DArray.FirstArraySlice = 0; dsDesc.Texture2DArray.MipSlice = 0; - VALIDATE_DIRECTX_RESULT(device->CreateDepthStencilView(_resource, &dsDesc, &dsView)); + VALIDATE_DIRECTX_CALL(device->CreateDepthStencilView(_resource, &dsDesc, &dsView)); } if (useRTV) { @@ -330,7 +330,7 @@ void GPUTextureDX11::initHandles() rtDesc.Texture2DArray.ArraySize = arraySize; rtDesc.Texture2DArray.FirstArraySlice = 0; rtDesc.Texture2DArray.MipSlice = 0; - VALIDATE_DIRECTX_RESULT(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); + VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); } if (useSRV) { @@ -348,7 +348,7 @@ void GPUTextureDX11::initHandles() srDesc.Texture2DArray.MipLevels = mipLevels; srDesc.Texture2DArray.MostDetailedMip = 0; } - VALIDATE_DIRECTX_RESULT(device->CreateShaderResourceView(_resource, &srDesc, &srView)); + VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView)); } if (useUAV) { @@ -356,7 +356,7 @@ void GPUTextureDX11::initHandles() uaDesc.Texture2DArray.MipSlice = 0; uaDesc.Texture2DArray.ArraySize = arraySize; uaDesc.Texture2DArray.FirstArraySlice = 0; - VALIDATE_DIRECTX_RESULT(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView)); + VALIDATE_DIRECTX_CALL(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView)); } _handleArray.Init(this, rtView, srView, dsView, uaView, format, msaa); } @@ -386,7 +386,7 @@ void GPUTextureDX11::initHandles() dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; dsDesc.Texture2D.MipSlice = 0; } - VALIDATE_DIRECTX_RESULT(device->CreateDepthStencilView(_resource, &dsDesc, &dsView)); + VALIDATE_DIRECTX_CALL(device->CreateDepthStencilView(_resource, &dsDesc, &dsView)); } if (useRTV) { @@ -406,7 +406,7 @@ void GPUTextureDX11::initHandles() rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; rtDesc.Texture2D.MipSlice = 0; } - VALIDATE_DIRECTX_RESULT(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); + VALIDATE_DIRECTX_CALL(device->CreateRenderTargetView(_resource, &rtDesc, &rtView)); } if (useSRV) { @@ -426,13 +426,13 @@ void GPUTextureDX11::initHandles() srDesc.Texture2D.MostDetailedMip = 0; srDesc.Texture2D.MipLevels = mipLevels; } - VALIDATE_DIRECTX_RESULT(device->CreateShaderResourceView(_resource, &srDesc, &srView)); + VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView)); } if (useUAV) { uaDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; uaDesc.Texture2D.MipSlice = 0; - VALIDATE_DIRECTX_RESULT(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView)); + VALIDATE_DIRECTX_CALL(device->CreateUnorderedAccessView(_resource, &uaDesc, &uaView)); } _handlesPerSlice[0].Init(this, rtView, srView, dsView, uaView, format, msaa); } @@ -521,7 +521,7 @@ void GPUTextureDX11::initHandles() dsDesc.Flags = D3D11_DSV_READ_ONLY_DEPTH; if (PixelFormatExtensions::HasStencil(format)) dsDesc.Flags |= D3D11_DSV_READ_ONLY_STENCIL; - VALIDATE_DIRECTX_RESULT(device->CreateDepthStencilView(_resource, &dsDesc, &dsView)); + VALIDATE_DIRECTX_CALL(device->CreateDepthStencilView(_resource, &dsDesc, &dsView)); } ASSERT(!useRTV); rtView = nullptr; @@ -543,7 +543,7 @@ void GPUTextureDX11::initHandles() srDesc.Texture2D.MostDetailedMip = 0; srDesc.Texture2D.MipLevels = mipLevels; } - VALIDATE_DIRECTX_RESULT(device->CreateShaderResourceView(_resource, &srDesc, &srView)); + VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView)); } _handleReadOnlyDepth.Init(this, rtView, srView, dsView, nullptr, format, msaa); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandAllocatorPoolDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandAllocatorPoolDX12.cpp index f0ad790af..9bb185030 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandAllocatorPoolDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandAllocatorPoolDX12.cpp @@ -31,7 +31,7 @@ ID3D12CommandAllocator* CommandAllocatorPoolDX12::RequestAllocator(uint64 comple if (firstPair.First <= completedFenceValue) { allocator = firstPair.Second; - VALIDATE_DIRECTX_RESULT(allocator->Reset()); + VALIDATE_DIRECTX_CALL(allocator->Reset()); _ready.RemoveAtKeepOrder(0); } } @@ -39,7 +39,7 @@ ID3D12CommandAllocator* CommandAllocatorPoolDX12::RequestAllocator(uint64 comple // If no allocators were ready to be reused, create a new one if (allocator == nullptr) { - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommandAllocator(_type, IID_PPV_ARGS(&allocator))); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateCommandAllocator(_type, IID_PPV_ARGS(&allocator))); #if GPU_ENABLE_RESOURCE_NAMING Char name[32]; swprintf(name, 32, L"CommandAllocator %u", _pool.Count()); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp index 8f78b749e..3235f4015 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp @@ -111,7 +111,7 @@ bool CommandQueueDX12::Init() desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; desc.NodeMask = 0; HRESULT result = _device->GetDevice()->CreateCommandQueue(&desc, IID_PPV_ARGS(&_commandQueue)); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); #if GPU_ENABLE_RESOURCE_NAMING _commandQueue->SetName(TEXT("CommandQueueDX12::CommandQueue")); #endif @@ -148,7 +148,7 @@ void CommandQueueDX12::WaitForGPU() uint64 CommandQueueDX12::ExecuteCommandList(ID3D12CommandList* list) { - VALIDATE_DIRECTX_RESULT((static_cast(list))->Close()); + VALIDATE_DIRECTX_CALL((static_cast(list))->Close()); _commandQueue->ExecuteCommandLists(1, &list); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp index 238318b2c..2ee2ea023 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp @@ -73,7 +73,7 @@ bool DescriptorHeapWithSlotsDX12::Create(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 // Create heap const HRESULT result = _device->GetDevice()->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), reinterpret_cast(&_heap)); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); // Setup _type = type; @@ -196,7 +196,7 @@ bool DescriptorHeapRingBufferDX12::Init() desc.Flags = _shaderVisible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE; desc.NodeMask = 0; const HRESULT result = _device->GetDevice()->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&_heap)); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); // Setup _firstFree = 0; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp index 5d2d97f0b..8f948d246 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp @@ -136,7 +136,7 @@ bool GPUBufferDX12::OnInit() // Create resource ID3D12Resource* resource; D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COPY_DEST; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, initialState, nullptr, IID_PPV_ARGS(&resource))); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, initialState, nullptr, IID_PPV_ARGS(&resource))); // Set state initResource(resource, initialState, 1); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 54f1a4290..e186054c2 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -83,7 +83,7 @@ GPUContextDX12::GPUContextDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE ty FrameFenceValues[0] = 0; FrameFenceValues[1] = 0; _currentAllocator = _device->GetCommandQueue()->RequestAllocator(); - VALIDATE_DIRECTX_RESULT(device->GetDevice()->CreateCommandList(0, type, _currentAllocator, nullptr, IID_PPV_ARGS(&_commandList))); + VALIDATE_DIRECTX_CALL(device->GetDevice()->CreateCommandList(0, type, _currentAllocator, nullptr, IID_PPV_ARGS(&_commandList))); #if GPU_ENABLE_RESOURCE_NAMING _commandList->SetName(TEXT("GPUContextDX12::CommandList")); #endif diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 047b69bb5..e2266f551 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -77,7 +77,7 @@ GPUDevice* GPUDeviceDX12::Create() #endif #ifdef __ID3D12DeviceRemovedExtendedDataSettings_FWD_DEFINED__ ComPtr dredSettings; - VALIDATE_DIRECTX_RESULT(D3D12GetDebugInterface(IID_PPV_ARGS(&dredSettings))); + VALIDATE_DIRECTX_CALL(D3D12GetDebugInterface(IID_PPV_ARGS(&dredSettings))); if (dredSettings) { // Turn on AutoBreadcrumbs and Page Fault reporting @@ -116,7 +116,7 @@ GPUDevice* GPUDeviceDX12::Create() { adapter.Index = index; adapter.MaxFeatureLevel = D3D_FEATURE_LEVEL_12_0; - VALIDATE_DIRECTX_RESULT(tempAdapter->GetDesc(&adapter.Description)); + VALIDATE_DIRECTX_CALL(tempAdapter->GetDesc(&adapter.Description)); uint32 outputs = RenderToolsDX::CountAdapterOutputs(tempAdapter); // Send that info to the log @@ -137,7 +137,7 @@ GPUDevice* GPUDeviceDX12::Create() if (tempAdapter && CheckDX12Support(tempAdapter)) { DXGI_ADAPTER_DESC desc; - VALIDATE_DIRECTX_RESULT(tempAdapter->GetDesc(&desc)); + VALIDATE_DIRECTX_CALL(tempAdapter->GetDesc(&desc)); for (int i = 0; i < adapters.Count(); i++) { if (adapters[i].Description.AdapterLuid.LowPart == desc.AdapterLuid.LowPart && @@ -254,7 +254,7 @@ bool GPUDeviceDX12::Init() #if PLATFORM_XBOX_SCARLETT params.DisableDXR = TRUE; #endif - VALIDATE_DIRECTX_RESULT(D3D12XboxCreateDevice(nullptr, ¶ms, IID_GRAPHICS_PPV_ARGS(&_device))); + VALIDATE_DIRECTX_CALL(D3D12XboxCreateDevice(nullptr, ¶ms, IID_GRAPHICS_PPV_ARGS(&_device))); // Setup adapter D3D12XBOX_GPU_HARDWARE_CONFIGURATION hwConfig = {}; @@ -319,12 +319,12 @@ bool GPUDeviceDX12::Init() } // Create DirectX device - VALIDATE_DIRECTX_RESULT(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&_device))); + VALIDATE_DIRECTX_CALL(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&_device))); // Debug Layer #if GPU_ENABLE_DIAGNOSTICS ComPtr infoQueue; - VALIDATE_DIRECTX_RESULT(_device->QueryInterface(IID_PPV_ARGS(&infoQueue))); + VALIDATE_DIRECTX_CALL(_device->QueryInterface(IID_PPV_ARGS(&infoQueue))); if (infoQueue) { D3D12_INFO_QUEUE_FILTER filter; @@ -363,7 +363,7 @@ bool GPUDeviceDX12::Init() // Spawn some info about the hardware D3D12_FEATURE_DATA_D3D12_OPTIONS options; - VALIDATE_DIRECTX_RESULT(_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))); + VALIDATE_DIRECTX_CALL(_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))); LOG(Info, "Tiled Resources Tier: {0}", (int32)options.TiledResourcesTier); LOG(Info, "Resource Binding Tier: {0}", (int32)options.ResourceBindingTier); LOG(Info, "Conservative Rasterization Tier: {0}", (int32)options.ConservativeRasterizationTier); @@ -662,10 +662,10 @@ bool GPUDeviceDX12::Init() // Serialize ComPtr signature; ComPtr error; - VALIDATE_DIRECTX_RESULT(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); + VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); // Create - VALIDATE_DIRECTX_RESULT(_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_rootSignature))); + VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_rootSignature))); } // Upload buffer @@ -896,14 +896,14 @@ void GPUDeviceDX12::OnResumed() void GPUDeviceDX12::updateFrameEvents() { ComPtr dxgiDevice; - VALIDATE_DIRECTX_RESULT(_device->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxgiDevice))); + VALIDATE_DIRECTX_CALL(_device->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxgiDevice))); ComPtr dxgiAdapter; - VALIDATE_DIRECTX_RESULT(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf())); + VALIDATE_DIRECTX_CALL(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf())); dxgiAdapter->GetDesc(&_adapter->Description); ComPtr dxgiOutput; - VALIDATE_DIRECTX_RESULT(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); - VALIDATE_DIRECTX_RESULT(_device->SetFrameIntervalX(dxgiOutput.Get(), D3D12XBOX_FRAME_INTERVAL_60_HZ, DX12_BACK_BUFFER_COUNT - 1u, D3D12XBOX_FRAME_INTERVAL_FLAG_NONE)); - VALIDATE_DIRECTX_RESULT(_device->ScheduleFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, 0U, nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); + VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); + VALIDATE_DIRECTX_CALL(_device->SetFrameIntervalX(dxgiOutput.Get(), D3D12XBOX_FRAME_INTERVAL_60_HZ, DX12_BACK_BUFFER_COUNT - 1u, D3D12XBOX_FRAME_INTERVAL_FLAG_NONE)); + VALIDATE_DIRECTX_CALL(_device->ScheduleFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, 0U, nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } #endif diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp index 85d79582f..8a9ca1c64 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp @@ -66,7 +66,7 @@ void GPUSwapChainDX12::OnReleaseGPU() // Disable fullscreen mode if (_swapChain) { - VALIDATE_DIRECTX_RESULT(_swapChain->SetFullscreenState(false, nullptr)); + VALIDATE_DIRECTX_CALL(_swapChain->SetFullscreenState(false, nullptr)); } #endif @@ -100,7 +100,7 @@ bool GPUSwapChainDX12::IsFullscreen() // Get state BOOL state; - VALIDATE_DIRECTX_RESULT(_swapChain->GetFullscreenState(&state, nullptr)); + VALIDATE_DIRECTX_CALL(_swapChain->GetFullscreenState(&state, nullptr)); return state == TRUE; #endif } @@ -221,7 +221,7 @@ bool GPUSwapChainDX12::Resize(int32 width, int32 height) // Create swap chain (it needs the queue so that it can force a flush on it) IDXGISwapChain1* swapChain; auto dxgiFactory = _device->GetDXGIFactory(); - VALIDATE_DIRECTX_RESULT(dxgiFactory->CreateSwapChainForHwnd(_device->GetCommandQueueDX12(), _windowHandle, &swapChainDesc, &fullscreenDesc, nullptr, &swapChain)); + VALIDATE_DIRECTX_CALL(dxgiFactory->CreateSwapChainForHwnd(_device->GetCommandQueueDX12(), _windowHandle, &swapChainDesc, &fullscreenDesc, nullptr, &swapChain)); _swapChain = static_cast(swapChain); ASSERT(_swapChain); DX_SET_DEBUG_NAME_EX(_swapChain, TEXT("RenderOutput"), TEXT("SwapChain"), TEXT("")); @@ -229,7 +229,7 @@ bool GPUSwapChainDX12::Resize(int32 width, int32 height) _backBuffers.Resize(swapChainDesc.BufferCount); // Disable DXGI changes to the window - VALIDATE_DIRECTX_RESULT(dxgiFactory->MakeWindowAssociation(_windowHandle, DXGI_MWA_NO_ALT_ENTER)); + VALIDATE_DIRECTX_CALL(dxgiFactory->MakeWindowAssociation(_windowHandle, DXGI_MWA_NO_ALT_ENTER)); } else { @@ -237,7 +237,7 @@ bool GPUSwapChainDX12::Resize(int32 width, int32 height) _swapChain->GetDesc1(&swapChainDesc); - VALIDATE_DIRECTX_RESULT(_swapChain->ResizeBuffers(swapChainDesc.BufferCount, width, height, swapChainDesc.Format, swapChainDesc.Flags)); + VALIDATE_DIRECTX_CALL(_swapChain->ResizeBuffers(swapChainDesc.BufferCount, width, height, swapChainDesc.Format, swapChainDesc.Flags)); } _currentFrameIndex = _swapChain->GetCurrentBackBufferIndex(); @@ -316,7 +316,7 @@ void GPUSwapChainDX12::getBackBuffer() swapChainBufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = swapChainBufferDesc.Format; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource( + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateCommittedResource( &swapChainHeapProperties, D3D12_HEAP_FLAG_ALLOW_DISPLAY, &swapChainBufferDesc, @@ -324,7 +324,7 @@ void GPUSwapChainDX12::getBackBuffer() &swapChainOptimizedClearValue, IID_GRAPHICS_PPV_ARGS(&backbuffer))); #else - VALIDATE_DIRECTX_RESULT(_swapChain->GetBuffer(i, IID_PPV_ARGS(&backbuffer))); + VALIDATE_DIRECTX_CALL(_swapChain->GetBuffer(i, IID_PPV_ARGS(&backbuffer))); #endif DX_SET_DEBUG_NAME_EX(backbuffer, TEXT("RenderOutput"), TEXT("BackBuffer"), i); _backBuffers[i].Setup(this, backbuffer); @@ -337,7 +337,7 @@ void GPUSwapChainDX12::Begin(RenderTask* task) { // Wait until frame start is signaled _framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &_framePipelineToken)); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &_framePipelineToken)); GPUSwapChain::Begin(task); } @@ -366,7 +366,7 @@ void GPUSwapChainDX12::Present(bool vsync) planeParameters.Token = _framePipelineToken; planeParameters.ResourceCount = 1; planeParameters.ppResources = &backBuffer; - VALIDATE_DIRECTX_RESULT(_device->GetCommandQueueDX12()->PresentX(1, &planeParameters, nullptr)); + VALIDATE_DIRECTX_CALL(_device->GetCommandQueueDX12()->PresentX(1, &planeParameters, nullptr)); // Base GPUSwapChain::Present(vsync); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index dd7402097..b89d65275 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -113,7 +113,7 @@ bool GPUTextureDX12::OnInit() resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; auto result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&resource)); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); initResource(resource, D3D12_RESOURCE_STATE_COPY_DEST, 1); DX_SET_DEBUG_NAME(_resource, GetName()); _memoryUsage = totalSize; @@ -184,7 +184,7 @@ bool GPUTextureDX12::OnInit() // Create texture auto result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, initialState, clearValuePtr, IID_PPV_ARGS(&resource)); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); // Set state bool isRead = useSRV || useUAV; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTimerQueryDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTimerQueryDX12.cpp index 6df638d23..7c814baed 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTimerQueryDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTimerQueryDX12.cpp @@ -37,7 +37,7 @@ void GPUTimerQueryDX12::End() heap.EndQuery(context, _end); const auto queue = _device->GetCommandQueue()->GetCommandQueue(); - VALIDATE_DIRECTX_RESULT(queue->GetTimestampFrequency(&_gpuFrequency)); + VALIDATE_DIRECTX_CALL(queue->GetTimestampFrequency(&_gpuFrequency)); _endCalled = true; } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp index dc6939cff..ea6af0a2d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp @@ -41,7 +41,7 @@ bool QueryHeapDX12::Init() heapDesc.Count = _queryHeapCount; heapDesc.NodeMask = 0; HRESULT result = _device->GetDevice()->CreateQueryHeap(&heapDesc, IID_PPV_ARGS(&_queryHeap)); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); DX_SET_DEBUG_NAME(_queryHeap, "Query Heap"); // Create the result buffer @@ -64,7 +64,7 @@ bool QueryHeapDX12::Init() resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; result = _device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&_resultBuffer)); - LOG_DIRECTX_RESULT_WITH_RETURN(result); + LOG_DIRECTX_RESULT_WITH_RETURN(result, true); DX_SET_DEBUG_NAME(_resultBuffer, "Query Heap Result Buffer"); // Start out with an open query batch @@ -181,7 +181,7 @@ void* QueryHeapDX12::ResolveQuery(ElementHandle& handle) range.Begin = batch.Start * _resultSize; range.End = range.Begin + batch.Count * _resultSize; void* mapped = nullptr; - VALIDATE_DIRECTX_RESULT(_resultBuffer->Map(0, &range, &mapped)); + VALIDATE_DIRECTX_CALL(_resultBuffer->Map(0, &range, &mapped)); // Copy the results data Platform::MemoryCopy(_resultData.Get() + range.Begin, (byte*)mapped + range.Begin, batch.Count * _resultSize); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp index 27aa4c697..11f1b7ffe 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp @@ -229,7 +229,7 @@ UploadBufferPageDX12::UploadBufferPageDX12(GPUDeviceDX12* device, uint64 size) resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; ID3D12Resource* resource; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&resource))); + VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&resource))); // Set state initResource(resource, D3D12_RESOURCE_STATE_GENERIC_READ, 1); @@ -238,7 +238,7 @@ UploadBufferPageDX12::UploadBufferPageDX12(GPUDeviceDX12* device, uint64 size) GPUAddress = _resource->GetGPUVirtualAddress(); // Map buffer - VALIDATE_DIRECTX_RESULT(_resource->Map(0, nullptr, &CPUAddress)); + VALIDATE_DIRECTX_CALL(_resource->Map(0, nullptr, &CPUAddress)); } void UploadBufferPageDX12::OnReleaseGPU() diff --git a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h index 61d32cff0..ef783b218 100644 --- a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h +++ b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h @@ -272,15 +272,15 @@ namespace RenderToolsDX #if GPU_ENABLE_ASSERTION // DirectX results validation -#define VALIDATE_DIRECTX_RESULT(x) { HRESULT result = x; if (FAILED(result)) RenderToolsDX::ValidateD3DResult(result, __FILE__, __LINE__); } +#define VALIDATE_DIRECTX_CALL(x) { HRESULT result = x; if (FAILED(result)) RenderToolsDX::ValidateD3DResult(result, __FILE__, __LINE__); } #define LOG_DIRECTX_RESULT(result) if (FAILED(result)) RenderToolsDX::LogD3DResult(result, __FILE__, __LINE__) -#define LOG_DIRECTX_RESULT_WITH_RETURN(result) if (FAILED(result)) { RenderToolsDX::LogD3DResult(result, __FILE__, __LINE__); return true; } +#define LOG_DIRECTX_RESULT_WITH_RETURN(result, returnValue) if (FAILED(result)) { RenderToolsDX::LogD3DResult(result, __FILE__, __LINE__); return returnValue; } #else -#define VALIDATE_DIRECTX_RESULT(x) x +#define VALIDATE_DIRECTX_CALL(x) x #define LOG_DIRECTX_RESULT(result) if(FAILED(result)) RenderToolsDX::LogD3DResult(result) -#define LOG_DIRECTX_RESULT_WITH_RETURN(result) if(FAILED(result)) { RenderToolsDX::LogD3DResult(result); return true; } +#define LOG_DIRECTX_RESULT_WITH_RETURN(result, returnValue) if(FAILED(result)) { RenderToolsDX::LogD3DResult(result); return returnValue; } #endif diff --git a/Source/Engine/Renderer/HistogramPass.cpp b/Source/Engine/Renderer/HistogramPass.cpp index ed28d27b3..77d228430 100644 --- a/Source/Engine/Renderer/HistogramPass.cpp +++ b/Source/Engine/Renderer/HistogramPass.cpp @@ -26,7 +26,7 @@ PACK_STRUCT(struct HistogramData { GPUBuffer* HistogramPass::Render(RenderContext& renderContext, GPUTexture* colorBuffer) { auto device = GPUDevice::Instance; - auto context = device->GetMainContext();; + auto context = device->GetMainContext(); if (checkIfSkipPass() || !_isSupported) return nullptr; From f40657ea043793670caa3305f9c7cc9bd5309be2 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Tue, 19 Sep 2023 20:58:12 -0700 Subject: [PATCH 37/89] macOS support fixes * Adding macOS FileSystemWatcher, this should allow files to be monitored and update like the other OSs * Reworked how macOS launches processes to use NSTask which just deals with escaped and unescaped paths better * Made a change to the ScriptsBuilder::RunBuildTool, this was adding the escaped values to the path, in reality it should be up to the underlying OS to make sure things are properly escaped, so removed those as they just end up causing issues. Also instead of appending the args to the fileName we just properly use the Arguments variable on the CreateProcessSettings * No longer use open in order to show files in the finder, we use the proper method selectFile * made a slight cleanup change to the MacPlatform Tick function * Added ToNSString functions just to make that easier * Added a ParseArguments function that will take a string and turn it into an array for NSTask --- Source/Editor/Scripting/ScriptsBuilder.cpp | 9 +- .../Engine/Platform/Apple/ApplePlatform.cpp | 40 ++++++ Source/Engine/Platform/Apple/AppleUtils.h | 3 + Source/Engine/Platform/FileSystemWatcher.h | 2 + Source/Engine/Platform/Mac/MacFileSystem.cpp | 2 +- .../Platform/Mac/MacFileSystemWatcher.cpp | 100 +++++++++++++++ .../Platform/Mac/MacFileSystemWatcher.h | 39 ++++++ Source/Engine/Platform/Mac/MacPlatform.cpp | 115 ++++++++++++------ Source/Engine/Platform/Types.h | 4 +- 9 files changed, 268 insertions(+), 46 deletions(-) create mode 100644 Source/Engine/Platform/Mac/MacFileSystemWatcher.cpp create mode 100644 Source/Engine/Platform/Mac/MacFileSystemWatcher.h diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 3082a531e..8807379cf 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -246,20 +246,17 @@ bool ScriptsBuilder::RunBuildTool(const StringView& args, const StringView& work Log::FileNotFoundException(monoPath).SetLevel(LogType::Fatal); return true; } - //const String monoPath = TEXT("mono"); - cmdLine.Append(TEXT("\"")); + const String monoPath = TEXT("mono"); cmdLine.Append(monoPath); - cmdLine.Append(TEXT("\" ")); + cmdLine.Append(TEXT(" ")); // TODO: Set env var for the mono MONO_GC_PARAMS=nursery-size64m to boost build performance -> profile it #endif - cmdLine.Append(TEXT("\"")); cmdLine.Append(buildToolPath); - cmdLine.Append(TEXT("\" ")); - cmdLine.Append(args.Get(), args.Length()); // Call build tool CreateProcessSettings procSettings; procSettings.FileName = StringView(*cmdLine, cmdLine.Length()); + procSettings.Arguments = args.Get(); procSettings.WorkingDirectory = workingDir; const int32 result = Platform::CreateProcess(procSettings); if (result != 0) diff --git a/Source/Engine/Platform/Apple/ApplePlatform.cpp b/Source/Engine/Platform/Apple/ApplePlatform.cpp index 8ae5a484f..eeef5b91f 100644 --- a/Source/Engine/Platform/Apple/ApplePlatform.cpp +++ b/Source/Engine/Platform/Apple/ApplePlatform.cpp @@ -70,6 +70,46 @@ CFStringRef AppleUtils::ToString(const StringView& str) return CFStringCreateWithBytes(nullptr, (const UInt8*)str.GetText(), str.Length() * sizeof(Char), kCFStringEncodingUTF16LE, false); } +NSString* AppleUtils::ToNSString(const StringView& str) +{ + NSString* ret = !str.IsEmpty() ? [[NSString alloc] initWithBytes: (const UInt8*)str.Get() length: str.Length() * sizeof(Char) encoding: NSUTF16LittleEndianStringEncoding] : nil; + return ret ? ret : @""; +} + +NSString* AppleUtils::ToNSString(const char* string) +{ + NSString* ret = string ? [NSString stringWithUTF8String: string] : nil; + return ret ? ret : @""; +} + + +NSArray* AppleUtils::ParseArguments(NSString* argsString) { + NSMutableArray *argsArray = [NSMutableArray array]; + NSScanner *scanner = [NSScanner scannerWithString:argsString]; + NSString *currentArg = nil; + BOOL insideQuotes = NO; + + while (![scanner isAtEnd]) { + if (insideQuotes) { + [scanner scanUpToString:@"\"" intoString:¤tArg]; + [scanner scanString:@"\"" intoString:NULL]; + insideQuotes = NO; + } else { + [scanner scanUpToString:@" " intoString:¤tArg]; + [scanner scanString:@" " intoString:NULL]; + } + + if ([currentArg isEqualToString:@"\""]) { + insideQuotes = YES; + } else if (currentArg) { + [argsArray addObject:currentArg]; + } + } + + return [argsArray copy]; +} + + typedef uint16_t offset_t; #define align_mem_up(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) diff --git a/Source/Engine/Platform/Apple/AppleUtils.h b/Source/Engine/Platform/Apple/AppleUtils.h index d6f639042..7144ba14b 100644 --- a/Source/Engine/Platform/Apple/AppleUtils.h +++ b/Source/Engine/Platform/Apple/AppleUtils.h @@ -13,6 +13,9 @@ class AppleUtils public: static String ToString(CFStringRef str); static CFStringRef ToString(const StringView& str); + static NSString* ToNSString(const StringView& str); + static NSString* ToNSString(const char* string); + static NSArray* ParseArguments(NSString* argsString); #if PLATFORM_MAC static Float2 PosToCoca(const Float2& pos); static Float2 CocaToPos(const Float2& pos); diff --git a/Source/Engine/Platform/FileSystemWatcher.h b/Source/Engine/Platform/FileSystemWatcher.h index c6b44ca80..1f02f5725 100644 --- a/Source/Engine/Platform/FileSystemWatcher.h +++ b/Source/Engine/Platform/FileSystemWatcher.h @@ -6,6 +6,8 @@ #include "Windows/WindowsFileSystemWatcher.h" #elif PLATFORM_LINUX #include "Linux/LinuxFileSystemWatcher.h" +#elif PLATFORM_MAC +#include "Mac/MacFileSystemWatcher.h" #else #include "Base/FileSystemWatcherBase.h" #endif diff --git a/Source/Engine/Platform/Mac/MacFileSystem.cpp b/Source/Engine/Platform/Mac/MacFileSystem.cpp index 5efa1905b..c765feef4 100644 --- a/Source/Engine/Platform/Mac/MacFileSystem.cpp +++ b/Source/Engine/Platform/Mac/MacFileSystem.cpp @@ -132,7 +132,7 @@ bool MacFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const StringVie bool MacFileSystem::ShowFileExplorer(const StringView& path) { - return Platform::StartProcess(TEXT("open"), String::Format(TEXT("\"{0}\""), path), StringView::Empty) != 0; + return [[NSWorkspace sharedWorkspace] selectFile: AppleUtils::ToNSString(FileSystem::ConvertRelativePathToAbsolute(path)) inFileViewerRootedAtPath: @""]; } #endif diff --git a/Source/Engine/Platform/Mac/MacFileSystemWatcher.cpp b/Source/Engine/Platform/Mac/MacFileSystemWatcher.cpp new file mode 100644 index 000000000..be13e2737 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacFileSystemWatcher.cpp @@ -0,0 +1,100 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC +#include "MacFileSystemWatcher.h" +#include "Engine/Platform/Apple/AppleUtils.h" +#include "Engine/Platform/CriticalSection.h" +#include "Engine/Platform/Thread.h" +#include "Engine/Threading/ThreadSpawner.h" +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Types/StringView.h" + +void DirectoryWatchCallback( ConstFSEventStreamRef StreamRef, void* FileWatcherPtr, size_t EventCount, void* EventPaths, const FSEventStreamEventFlags EventFlags[], const FSEventStreamEventId EventIDs[] ) +{ + MacFileSystemWatcher* macFileSystemWatcher = (MacFileSystemWatcher*)FileWatcherPtr; + if (macFileSystemWatcher) + { + CFArrayRef EventPathArray = (CFArrayRef)EventPaths; + for( size_t EventIndex = 0; EventIndex < EventCount; ++EventIndex ) + { + const FSEventStreamEventFlags Flags = EventFlags[EventIndex]; + if( !(Flags & kFSEventStreamEventFlagItemIsFile) && !(Flags & kFSEventStreamEventFlagItemIsDir) ) + { + // events about symlinks don't concern us + continue; + } + + auto action = FileSystemAction::Unknown; + + const bool added = ( Flags & kFSEventStreamEventFlagItemCreated ); + const bool renamed = ( Flags & kFSEventStreamEventFlagItemRenamed ); + const bool modified = ( Flags & kFSEventStreamEventFlagItemModified ); + const bool removed = ( Flags & kFSEventStreamEventFlagItemRemoved ); + + if (added) + { + action = FileSystemAction::Create; + } + + if (renamed || modified) + { + action = FileSystemAction::Delete; + } + + if (removed) + { + action = FileSystemAction::Modify; + } + + const String resolvedPath = AppleUtils::ToString((CFStringRef)CFArrayGetValueAtIndex(EventPathArray,EventIndex)); + + macFileSystemWatcher->OnEvent(resolvedPath, action); + } + } +} + +MacFileSystemWatcher::MacFileSystemWatcher(const String& directory, bool withSubDirs) + : FileSystemWatcherBase(directory, withSubDirs) +{ + + CFStringRef FullPathMac = AppleUtils::ToString(StringView(directory)); + CFArrayRef PathsToWatch = CFArrayCreate(NULL, (const void**)&FullPathMac, 1, NULL); + + CFAbsoluteTime Latency = 0.2; + + FSEventStreamContext Context; + Context.version = 0; + Context.info = this; + Context.retain = NULL; + Context.release = NULL; + Context.copyDescription = NULL; + + EventStream = FSEventStreamCreate( NULL, + &DirectoryWatchCallback, + &Context, + PathsToWatch, + kFSEventStreamEventIdSinceNow, + Latency, + kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents + ); + + CFRelease(PathsToWatch); + CFRelease(FullPathMac); + + FSEventStreamScheduleWithRunLoop( EventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); + FSEventStreamStart( EventStream ); + + IsRunning = true; +} + +MacFileSystemWatcher::~MacFileSystemWatcher() +{ + if (IsRunning) + { + FSEventStreamStop(EventStream); + FSEventStreamUnscheduleFromRunLoop(EventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + FSEventStreamInvalidate(EventStream); + FSEventStreamRelease(EventStream); + } +} +#endif diff --git a/Source/Engine/Platform/Mac/MacFileSystemWatcher.h b/Source/Engine/Platform/Mac/MacFileSystemWatcher.h new file mode 100644 index 000000000..97ca20bda --- /dev/null +++ b/Source/Engine/Platform/Mac/MacFileSystemWatcher.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC +#include "Engine/Platform/Base/FileSystemWatcherBase.h" + +#include + + +/// +/// Mac platform implementation of the file system watching object. +/// +class FLAXENGINE_API MacFileSystemWatcher : public FileSystemWatcherBase +{ +public: + + /// + /// Initializes a new instance of the class. + /// + /// The directory to watch. + /// True if monitor the directory tree rooted at the specified directory or just a given directory. + MacFileSystemWatcher(const String& directory, bool withSubDirs); + + /// + /// Finalizes an instance of the class. + /// + ~MacFileSystemWatcher(); + +public: + + + +private: + + FSEventStreamRef EventStream; + bool IsRunning; +}; +#endif diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index 8cba5e1dc..4c965f549 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -324,13 +324,16 @@ void MacPlatform::BeforeRun() void MacPlatform::Tick() { // Process system events - while (true) + NSEvent* event = nil; + do { - NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event == nil) - break; - [NSApp sendEvent:event]; - } + NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES]; + if (event) + { + [NSApp sendEvent:event]; + } + + } while(event); ApplePlatform::Tick(); } @@ -461,50 +464,88 @@ 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"); + NSTask *task = [[NSTask alloc] init]; + task.launchPath = AppleUtils::ToNSString(exePath); + task.arguments = AppleUtils::ParseArguments(AppleUtils::ToNSString(settings.Arguments)); + if (cwd.Length() != 0) - { - Platform::SetWorkingDirectory(cwd); - } - if (!pipe) - { - LOG(Warning, "Failed to start process, errno={}", errno); - return -1; - } - - // TODO: environment - + task.currentDirectoryPath = AppleUtils::ToNSString(cwd); + + int32 returnCode = 0; + if (settings.WaitForEnd) { + id outputObserver = nil; + if (captureStdOut) { - char lineBuffer[1024]; - while (fgets(lineBuffer, sizeof(lineBuffer), pipe) != NULL) + NSPipe *stdoutPipe = [NSPipe pipe]; + [task setStandardOutput:stdoutPipe]; + + outputObserver = [[NSNotificationCenter defaultCenter] + addObserverForName: NSFileHandleDataAvailableNotification + object: [stdoutPipe fileHandleForReading] + queue: nil + usingBlock:^(NSNotification* notification) { - char* p = lineBuffer + strlen(lineBuffer) - 1; - if (*p == '\n') *p = 0; - String line(lineBuffer); - if (settings.SaveOutput) - settings.Output.Add(line.Get(), line.Length()); - if (settings.LogOutput) - Log::Logger::Write(LogType::Info, line); + NSData* data = [stdoutPipe fileHandleForReading].availableData; + if (data.length) + { + String line((char*)data.bytes); + if (settings.SaveOutput) + settings.Output.Add(line.Get(), line.Length()); + if (settings.LogOutput) + Log::Logger::Write(LogType::Info, line); + [[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify]; + } } + ]; + + [[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify]; } - else + + String exception; + + @try { - while (!feof(pipe)) - { - sleep(1); - } + [task launch]; + [task waitUntilExit]; + } + @catch (NSException* e) + { + exception = e.reason.UTF8String; + } + + if (!exception.IsEmpty()) + { + LOG(Error, "Failed to run command {0} {1} with error {2}", settings.FileName, settings.Arguments, exception); + return -1; + } + + returnCode = [task terminationStatus]; + } + else { + + String exception; + + @try + { + [task launch]; + } + @catch (NSException* e) + { + exception = e.reason.UTF8String; + } + + if (!exception.IsEmpty()) + { + LOG(Error, "Failed to run command {0} {1} with error {2}", settings.FileName, settings.Arguments, exception); + return -1; } } + return returnCode; } diff --git a/Source/Engine/Platform/Types.h b/Source/Engine/Platform/Types.h index 76135dcaf..863743121 100644 --- a/Source/Engine/Platform/Types.h +++ b/Source/Engine/Platform/Types.h @@ -237,8 +237,8 @@ class UnixConditionVariable; typedef UnixConditionVariable ConditionVariable; class MacFileSystem; typedef MacFileSystem FileSystem; -class FileSystemWatcherBase; -typedef FileSystemWatcherBase FileSystemWatcher; +class MacFileSystemWatcher; +typedef MacFileSystemWatcher FileSystemWatcher; class UnixFile; typedef UnixFile File; class MacPlatform; From e3cf9c05e435edd043d18193d19c5b1f73ce2124 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 09:06:54 +0200 Subject: [PATCH 38/89] Fix logging macOS process to remove redundant newlines --- Source/Engine/Core/Log.cpp | 4 ++-- Source/Engine/Platform/Mac/MacPlatform.cpp | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Core/Log.cpp b/Source/Engine/Core/Log.cpp index fea72bfe8..1147c4ff1 100644 --- a/Source/Engine/Core/Log.cpp +++ b/Source/Engine/Core/Log.cpp @@ -223,10 +223,10 @@ void Log::Logger::ProcessLogMessage(LogType type, const StringView& msg, fmt_fla else { //w.append(msg.Get(), msg.Get() + msg.Length()); - fmt_flax::format(w, TEXT("{}"), (const Char*)msg.Get()); + fmt_flax::format(w, TEXT("{}"), msg); } #else - fmt_flax::format(w, TEXT("{}"), (const Char*)msg.Get()); + fmt_flax::format(w, TEXT("{}"), msg); #endif } diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index 4c965f549..8f566073f 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -327,7 +327,7 @@ void MacPlatform::Tick() NSEvent* event = nil; do { - NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES]; + event = [NSApp nextEventMatchingMask: NSEventMaskAny untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES]; if (event) { [NSApp sendEvent:event]; @@ -464,17 +464,15 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) } } } - + NSTask *task = [[NSTask alloc] init]; task.launchPath = AppleUtils::ToNSString(exePath); task.arguments = AppleUtils::ParseArguments(AppleUtils::ToNSString(settings.Arguments)); - + if (cwd.Length() != 0) task.currentDirectoryPath = AppleUtils::ToNSString(cwd); - - + int32 returnCode = 0; - if (settings.WaitForEnd) { id outputObserver = nil; @@ -493,11 +491,16 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) NSData* data = [stdoutPipe fileHandleForReading].availableData; if (data.length) { - String line((char*)data.bytes); + String line((const char*)data.bytes, data.length); if (settings.SaveOutput) settings.Output.Add(line.Get(), line.Length()); if (settings.LogOutput) - Log::Logger::Write(LogType::Info, line); + { + StringView lineView(line); + if (line[line.Length() - 1] == '\n') + lineView = StringView(line.Get(), line.Length() - 1); + Log::Logger::Write(LogType::Info, lineView); + } [[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify]; } } From 1740cbf2eb90dc4080b3351c4a83c4c18e99c998 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 09:21:56 +0200 Subject: [PATCH 39/89] Improve process starting on mac --- Source/Engine/Platform/Mac/MacPlatform.cpp | 30 ++++++---------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index 8f566073f..5750cd260 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -432,13 +432,6 @@ Window* MacPlatform::CreateWindow(const CreateWindowSettings& settings) int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) { LOG(Info, "Command: {0} {1}", settings.FileName, settings.Arguments); - String cwd; - if (settings.WorkingDirectory.HasChars()) - { - LOG(Info, "Working directory: {0}", settings.WorkingDirectory); - cwd = Platform::GetWorkingDirectory(); - Platform::SetWorkingDirectory(settings.WorkingDirectory); - } const bool captureStdOut = settings.LogOutput || settings.SaveOutput; // Special case if filename points to the app package (use actual executable) @@ -468,9 +461,8 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) NSTask *task = [[NSTask alloc] init]; task.launchPath = AppleUtils::ToNSString(exePath); task.arguments = AppleUtils::ParseArguments(AppleUtils::ToNSString(settings.Arguments)); - - if (cwd.Length() != 0) - task.currentDirectoryPath = AppleUtils::ToNSString(cwd); + if (settings.WorkingDirectory.HasChars()) + task.currentDirectoryPath = AppleUtils::ToNSString(settings.WorkingDirectory); int32 returnCode = 0; if (settings.WaitForEnd) @@ -510,29 +502,25 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) } String exception; - @try { [task launch]; [task waitUntilExit]; + returnCode = [task terminationStatus]; } @catch (NSException* e) { exception = e.reason.UTF8String; } - if (!exception.IsEmpty()) { LOG(Error, "Failed to run command {0} {1} with error {2}", settings.FileName, settings.Arguments, exception); - return -1; + returnCode = -1; } - - returnCode = [task terminationStatus]; } - else { - - String exception; - + else + { + String exception; @try { [task launch]; @@ -541,14 +529,12 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) { exception = e.reason.UTF8String; } - if (!exception.IsEmpty()) { LOG(Error, "Failed to run command {0} {1} with error {2}", settings.FileName, settings.Arguments, exception); - return -1; + returnCode = -1; } } - return returnCode; } From 34a36d822a2dde52e179a68ba49c4da3831d7949 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 10:06:16 +0200 Subject: [PATCH 40/89] Improve parsing command line in build tools when using lots of quotes --- .../Tools/Flax.Build.Tests/TestCommandLine.cs | 6 ++++ Source/Tools/Flax.Build/CommandLine.cs | 34 ++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Source/Tools/Flax.Build.Tests/TestCommandLine.cs b/Source/Tools/Flax.Build.Tests/TestCommandLine.cs index db530b70d..905dc99af 100644 --- a/Source/Tools/Flax.Build.Tests/TestCommandLine.cs +++ b/Source/Tools/Flax.Build.Tests/TestCommandLine.cs @@ -12,6 +12,7 @@ namespace Flax.Build.Tests [Test, Sequential] public void TestParseOptionOnly([Values( "-something", + "something", " \t \t-\t \tsomething\t ", "-something=")] string commandLine) @@ -25,6 +26,11 @@ namespace Flax.Build.Tests [Test, Sequential] public void TestParseOneValue([Values( "-something=value", + "something=value", + "-something=\"value\"", + "-something=\\\"value\\\"", + "\"-something=\"value\"\"", + "\"-something=\\\"value\\\"\"", " \t \t-\t \tsomething\t =value ", "-something=value ")] string commandLine) diff --git a/Source/Tools/Flax.Build/CommandLine.cs b/Source/Tools/Flax.Build/CommandLine.cs index 91b6febce..f0a631145 100644 --- a/Source/Tools/Flax.Build/CommandLine.cs +++ b/Source/Tools/Flax.Build/CommandLine.cs @@ -249,6 +249,8 @@ namespace Flax.Build var wholeQuote = commandLine[i] == '\"'; if (wholeQuote) i++; + if (i == length) + break; if (commandLine[i] == '-') i++; else if (commandLine[i] == '/') @@ -279,7 +281,7 @@ namespace Flax.Build }); if (wholeQuote) i++; - if (i != length && commandLine[i] != '\"') + if (i < length && commandLine[i] != '\"') i++; continue; } @@ -287,23 +289,36 @@ namespace Flax.Build // Read value i++; int valueStart, valueEnd; - if (commandLine[i] == '\"') + if (commandLine.Length > i + 1 && commandLine[i] == '\\' && commandLine[i + 1] == '\"') { - valueStart = i + 1; + valueStart = i + 2; i++; - while (i < length && commandLine[i] != '\"') + while (i + 1 < length && commandLine[i] != '\\' && commandLine[i + 1] != '\"') i++; valueEnd = i; - i++; + i += 2; + if (wholeQuote) + { + while (i < length && commandLine[i] != '\"') + i++; + i++; + } } - else if (commandLine[i] == '\'') + else if (commandLine[i] == '\"' || commandLine[i] == '\'') { + var quoteChar = commandLine[i]; valueStart = i + 1; i++; - while (i < length && commandLine[i] != '\'') + while (i < length && commandLine[i] != quoteChar) i++; valueEnd = i; i++; + if (wholeQuote) + { + while (i < length && commandLine[i] != '\"') + i++; + i++; + } } else if (wholeQuote) { @@ -321,10 +336,13 @@ namespace Flax.Build valueEnd = i; } string value = commandLine.Substring(valueStart, valueEnd - valueStart); + value = value.Trim(); + if (value.StartsWith("\\\"") && value.EndsWith("\\\"")) + value = value.Substring(2, value.Length - 4); options.Add(new Option { Name = name, - Value = value.Trim() + Value = value }); } From 5c5c64cf761ebc79c4f53fb6eec6c88d0536a93d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 11:52:26 +0200 Subject: [PATCH 41/89] Update macOS app cooking to properly execute tools --- .../Cooker/Platform/Mac/MacPlatformTools.cpp | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp b/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp index c059acba9..b66a96a9b 100644 --- a/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp @@ -5,6 +5,7 @@ #include "MacPlatformTools.h" #include "Engine/Platform/File.h" #include "Engine/Platform/FileSystem.h" +#include "Engine/Platform/CreateProcessSettings.h" #include "Engine/Platform/Mac/MacPlatformSettings.h" #include "Engine/Core/Config/GameSettings.h" #include "Engine/Core/Config/BuildSettings.h" @@ -124,17 +125,35 @@ bool MacPlatformTools::OnPostProcess(CookingData& data) LOG(Error, "Failed to export application icon."); return true; } - bool failed = Platform::RunProcess(TEXT("sips -z 16 16 icon_1024x1024.png --out icon_16x16.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 32 32 icon_1024x1024.png --out icon_16x16@2x.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 32 32 icon_1024x1024.png --out icon_32x32.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 64 64 icon_1024x1024.png --out icon_32x32@2x.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 128 128 icon_1024x1024.png --out icon_128x128.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 256 256 icon_1024x1024.png --out icon_128x128@2x.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 256 256 icon_1024x1024.png --out icon_256x256.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 512 512 icon_1024x1024.png --out icon_256x256@2x.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 512 512 icon_1024x1024.png --out icon_512x512.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("sips -z 1024 1024 icon_1024x1024.png --out icon_512x512@2x.png"), tmpFolderPath); - failed |= Platform::RunProcess(TEXT("iconutil -c icns icon.iconset"), iconFolderPath); + CreateProcessSettings procSettings; + procSettings.HiddenWindow = true; + procSettings.FileName = TEXT("/usr/bin/sips"); + procSettings.WorkingDirectory = tmpFolderPath; + procSettings.Arguments = TEXT("-z 16 16 icon_1024x1024.png --out icon_16x16.png"); + bool failed = false; + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 32 32 icon_1024x1024.png --out icon_16x16@2x.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 32 32 icon_1024x1024.png --out icon_32x32.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 64 64 icon_1024x1024.png --out icon_32x32@2x.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 128 128 icon_1024x1024.png --out icon_128x128.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 256 256 icon_1024x1024.png --out icon_128x128@2x.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 256 256 icon_1024x1024.png --out icon_256x256.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 512 512 icon_1024x1024.png --out icon_256x256@2x.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 512 512 icon_1024x1024.png --out icon_512x512.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.Arguments = TEXT("-z 1024 1024 icon_1024x1024.png --out icon_512x512@2x.png"); + failed |= Platform::CreateProcess(procSettings); + procSettings.FileName = TEXT("/usr/bin/iconutil"); + procSettings.Arguments = TEXT("-c icns icon.iconset"); + procSettings.WorkingDirectory = iconFolderPath; + failed |= Platform::CreateProcess(procSettings); if (failed) { LOG(Error, "Failed to export application icon."); @@ -210,16 +229,22 @@ bool MacPlatformTools::OnPostProcess(CookingData& data) return false; GameCooker::PackageFiles(); LOG(Info, "Building app package..."); - const String dmgPath = data.OriginalOutputPath / appName + TEXT(".dmg"); - const String dmgCommand = String::Format(TEXT("hdiutil create {0}.dmg -volname {0} -fs HFS+ -srcfolder {0}.app"), appName); - const int32 result = Platform::RunProcess(dmgCommand, data.OriginalOutputPath); - if (result != 0) { - data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result)); - return true; + const String dmgPath = data.OriginalOutputPath / appName + TEXT(".dmg"); + CreateProcessSettings procSettings; + procSettings.HiddenWindow = true; + procSettings.WorkingDirectory = data.OriginalOutputPath; + procSettings.FileName = TEXT("/usr/bin/hdiutil"); + procSettings.Arguments = String::Format(TEXT("create {0}.dmg -volname {0} -fs HFS+ -srcfolder {0}.app"), appName); + const int32 result = Platform::CreateProcess(procSettings); + if (result != 0) + { + data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result)); + return true; + } + // TODO: sign dmg + LOG(Info, "Output application package: {0} (size: {1} MB)", dmgPath, FileSystem::GetFileSize(dmgPath) / 1024 / 1024); } - // TODO: sign dmg - LOG(Info, "Output application package: {0} (size: {1} MB)", dmgPath, FileSystem::GetFileSize(dmgPath) / 1024 / 1024); return false; } From 8bd66495101e40bd6626a30d0c36227dd8b2d33f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 14:17:11 +0200 Subject: [PATCH 42/89] Refactor managed array unboxing to handle case of C# array passes as object #1415 --- Source/Engine/Engine/NativeInterop.Unmanaged.cs | 13 +++++++++++++ Source/Engine/Scripting/ManagedCLR/MCore.h | 1 + Source/Engine/Scripting/ManagedCLR/MUtils.h | 5 +++-- Source/Engine/Scripting/Runtime/DotNet.cpp | 6 ++++++ Source/Engine/Scripting/Runtime/Mono.cpp | 5 +++++ Source/Engine/Scripting/Runtime/None.cpp | 5 +++++ 6 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 670b7fff5..423aae6a5 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -559,6 +559,19 @@ namespace FlaxEngine.Interop return managedArray.Pointer; } + [UnmanagedCallersOnly] + internal static IntPtr GetArray(ManagedHandle handle) + { + if (!handle.IsAllocated) + return IntPtr.Zero; + object value = handle.Target; + if (value is ManagedArray) + return (IntPtr)handle; + if (value is Array) + return Invoker.MarshalReturnValueGeneric(value.GetType(), value); + return IntPtr.Zero; + } + [UnmanagedCallersOnly] internal static int GetArrayLength(ManagedHandle arrayHandle) { diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index e1de3c207..7c61031c7 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -85,6 +85,7 @@ public: static MClass* GetClass(MClass* elementKlass); static int32 GetLength(const MArray* obj); static void* GetAddress(const MArray* obj); + static MArray* Unbox(MObject* obj); template FORCE_INLINE static T* GetAddress(const MArray* obj) diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.h b/Source/Engine/Scripting/ManagedCLR/MUtils.h index 7b340e5c6..a9b2d8414 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.h +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.h @@ -363,11 +363,12 @@ struct MConverter> void Unbox(Array& result, MObject* data) { - const int32 length = data ? MCore::Array::GetLength((MArray*)data) : 0; + MArray* array = MCore::Array::Unbox(data); + const int32 length = array ? MCore::Array::GetLength(array) : 0; result.Resize(length); MConverter converter; Span resultSpan(result.Get(), length); - converter.ToNativeArray(resultSpan, (MArray*)data); + converter.ToNativeArray(resultSpan, array); } }; diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 2157e8dff..c859fa961 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -408,6 +408,12 @@ void* MCore::Array::GetAddress(const MArray* obj) return CallStaticMethod(GetArrayPointerPtr, (void*)obj); } +MArray* MCore::Array::Unbox(MObject* obj) +{ + static void* GetArrayPtr = GetStaticMethodPointer(TEXT("GetArray")); + return (MArray*)CallStaticMethod(GetArrayPtr, (void*)obj); +} + MGCHandle MCore::GCHandle::New(MObject* obj, bool pinned) { ASSERT(obj); diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index 0a60db42f..da21e90d0 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -804,6 +804,11 @@ void* MCore::Array::GetAddress(const MArray* obj) return mono_array_addr_with_size((MonoArray*)obj, 0, 0); } +MArray* MCore::Array::Unbox(MObject* obj) +{ + return (MArray*)obj; +} + MGCHandle MCore::GCHandle::New(MObject* obj, bool pinned) { return mono_gchandle_new(obj, pinned); diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index ef9c118cf..39d85ad84 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -141,6 +141,11 @@ void* MCore::Array::GetAddress(const MArray* obj) return nullptr; } +MArray* MCore::Array::Unbox(MObject* obj) +{ + return nullptr; +} + MGCHandle MCore::GCHandle::New(MObject* obj, bool pinned) { return (MGCHandle)(uintptr)obj; From 8cd8ae93c3281e525970931eb380fc05e6ea8afb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 15:01:02 +0200 Subject: [PATCH 43/89] Add `ObjectRadius` to `DrawCall` to properly cull local lights and env probes in Forward Shading --- .../Graphics/Materials/MaterialShaderFeatures.cpp | 10 ++++++---- Source/Engine/Graphics/Models/Mesh.cpp | 3 +++ Source/Engine/Graphics/Models/SkinnedMesh.cpp | 2 ++ Source/Engine/Level/Actors/Skybox.cpp | 1 + Source/Engine/Level/Actors/SplineModel.cpp | 1 + Source/Engine/Particles/Particles.cpp | 3 ++- Source/Engine/Renderer/DrawCall.h | 5 +++++ Source/Engine/Renderer/GBufferPass.cpp | 1 + Source/Engine/Renderer/RenderList.cpp | 1 + Source/Engine/Terrain/TerrainChunk.cpp | 6 ++++-- Source/Engine/Terrain/TerrainChunk.h | 3 ++- Source/Engine/UI/TextRender.cpp | 1 + 12 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index b3069b4db..d8d69dc3e 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -76,11 +76,11 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanEnvironmentProbes.Count(); i++) { const auto p = cache->EnvironmentProbes[i]; - if (p->GetSphere().Contains(drawCallOrigin) != ContainmentType::Disjoint) + if (CollisionsHelper::SphereIntersectsSphere(objectBoundsWorld, p->GetSphere())) { probe = p; break; @@ -99,10 +99,12 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanPointLights.Count() && data.LocalLightsCount < MaxLocalLights; i++) { const auto& light = cache->PointLights[i]; - if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.ObjectPosition) != ContainmentType::Disjoint) + if (CollisionsHelper::SphereIntersectsSphere(objectBounds, BoundingSphere(light.Position, light.Radius))) { light.SetupLightData(&data.LocalLights[data.LocalLightsCount], false); data.LocalLightsCount++; @@ -111,7 +113,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanSpotLights.Count() && data.LocalLightsCount < MaxLocalLights; i++) { const auto& light = cache->SpotLights[i]; - if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.ObjectPosition) != ContainmentType::Disjoint) + if (CollisionsHelper::SphereIntersectsSphere(objectBounds, BoundingSphere(light.Position, light.Radius))) { light.SetupLightData(&data.LocalLights[data.LocalLightsCount], false); data.LocalLightsCount++; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index e5ba5488d..558b53b18 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -429,6 +429,7 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons drawCall.Material = material; drawCall.World = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue(); drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = world; drawCall.Surface.Lightmap = nullptr; @@ -495,6 +496,7 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition? drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr; @@ -555,6 +557,7 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition? drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr; diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index b6c966508..f6d24c335 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -198,6 +198,7 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition? drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Lightmap = nullptr; @@ -258,6 +259,7 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition? drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Lightmap = nullptr; diff --git a/Source/Engine/Level/Actors/Skybox.cpp b/Source/Engine/Level/Actors/Skybox.cpp index 7d35a2776..455c229c0 100644 --- a/Source/Engine/Level/Actors/Skybox.cpp +++ b/Source/Engine/Level/Actors/Skybox.cpp @@ -100,6 +100,7 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M Platform::MemoryClear(&drawCall, sizeof(DrawCall)); drawCall.World = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = GetPerInstanceRandom(); diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 6ca841b41..6b7916e4d 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -405,6 +405,7 @@ void SplineModel::Draw(RenderContext& renderContext) const Transform splineTransform = GetTransform(); renderContext.View.GetWorldMatrix(splineTransform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; // TODO: use radius for the spline chunk rather than whole spline const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant(); for (int32 segment = 0; segment < _instances.Count(); segment++) { diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 873c4bd52..52234fd7a 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -938,7 +938,8 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe // Setup a draw call common data DrawCall drawCall; drawCall.PerInstanceRandom = effect->GetPerInstanceRandom(); - drawCall.ObjectPosition = effect->GetPosition(); + drawCall.ObjectPosition = effect->GetSphere().Center - view.Origin; + drawCall.ObjectRadius = effect->GetSphere().Radius; // Draw all emitters for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++) diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index 9fd6c13b5..d51102166 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -258,6 +258,11 @@ struct DrawCall /// Float3 ObjectPosition; + /// + /// Object bounding sphere radius that contains it whole (sphere at ObjectPosition). + /// + float ObjectRadius; + /// /// The world matrix determinant sign (used for geometry that is two sided or has inverse scale - needs to flip normal vectors and change triangles culling). /// diff --git a/Source/Engine/Renderer/GBufferPass.cpp b/Source/Engine/Renderer/GBufferPass.cpp index 0e5e8508a..f824d2161 100644 --- a/Source/Engine/Renderer/GBufferPass.cpp +++ b/Source/Engine/Renderer/GBufferPass.cpp @@ -464,6 +464,7 @@ void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* light transform.Scale *= decal->GetSize(); renderContext.View.GetWorldMatrix(transform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = decal->GetSphere().Radius; context->ResetRenderTarget(); diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 1a4cc0205..f24f25128 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -855,6 +855,7 @@ DRAW: { auto& batch = BatchedDrawCalls.Get()[list.PreBatchedDrawCalls.Get()[i]]; auto drawCall = batch.DrawCall; + drawCall.ObjectRadius = 0.0f; bindParams.FirstDrawCall = &drawCall; const auto* instancesData = batch.Instances.Get(); diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index 7db786ec2..737ce08f8 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -45,7 +45,7 @@ bool TerrainChunk::PrepareDraw(const RenderContext& renderContext) // Calculate chunk distance to view const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); - const float distance = Float3::Distance(_boundsCenter - lodView->Origin, lodView->Position); + const float distance = Float3::Distance(_sphere.Center - lodView->Origin, lodView->Position); lod = (int32)Math::Pow(distance / chunkEdgeSize, lodDistribution); lod += lodBias; @@ -88,6 +88,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const drawCall.Material = _cachedDrawMaterial; renderContext.View.GetWorldMatrix(_transform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; drawCall.Terrain.Patch = _patch; drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias; drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z)); @@ -145,6 +146,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi drawCall.Material = material; renderContext.View.GetWorldMatrix(_transform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; drawCall.Terrain.Patch = _patch; drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias; drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z)); @@ -202,7 +204,7 @@ void TerrainChunk::UpdateBounds() OrientedBoundingBox obb(Vector3::Zero, Vector3::One); obb.Transform(localTransform); obb.GetBoundingBox(_bounds); - _boundsCenter = _bounds.GetCenter(); + BoundingSphere::FromBox(_bounds, _sphere); _bounds.Minimum -= boundsExtent; _bounds.Maximum += boundsExtent; diff --git a/Source/Engine/Terrain/TerrainChunk.h b/Source/Engine/Terrain/TerrainChunk.h index dd74a18a1..884160b45 100644 --- a/Source/Engine/Terrain/TerrainChunk.h +++ b/Source/Engine/Terrain/TerrainChunk.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Core/Math/BoundingBox.h" +#include "Engine/Core/Math/BoundingSphere.h" #include "Engine/Core/Math/Matrix.h" #include "Engine/Core/Math/Transform.h" #include "Engine/Core/ISerializable.h" @@ -29,7 +30,7 @@ private: Float4 _heightmapUVScaleBias; Transform _transform; BoundingBox _bounds; - Vector3 _boundsCenter; + BoundingSphere _sphere; float _perInstanceRandom; float _yOffset, _yHeight; diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index cd60abddf..d161db0ee 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -366,6 +366,7 @@ void TextRender::Draw(RenderContext& renderContext) DrawCall drawCall; drawCall.World = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; drawCall.Surface.GeometrySize = _localBox.GetSize(); drawCall.Surface.PrevWorld = _drawState.PrevWorld; drawCall.Surface.Lightmap = nullptr; From 4611de5ed61453a290ec8d92f20a6de675928ec1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 16:02:15 +0200 Subject: [PATCH 44/89] Bump up build number --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 50fcaeda3..fbf48cc2b 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,7 @@ "Version": { "Major": 1, "Minor": 6, - "Build": 6344 + "Build": 6345 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.", From 3ede4c21923e85e2868ec82800b10fd3029cbe59 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 16:02:24 +0200 Subject: [PATCH 45/89] Fix typo --- Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp index fd14f0aeb..b58684a4e 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp @@ -122,7 +122,7 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const { // Create shader ID3D11DomainShader* buffer = nullptr; - VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateDomainShader(cacheBytes, cacheSize, nullptr, &buffer)); + result = _device->GetDevice()->CreateDomainShader(cacheBytes, cacheSize, nullptr, &buffer); LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr); // Create object From 821c373ae2fe403d558079a17ff9c413465e7014 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Wed, 20 Sep 2023 09:12:48 -0700 Subject: [PATCH 46/89] Allow for better support for running on m1/2 machines * So we need to account for 2 possible situations where you are running under and emulated process and a native process with a different target host in this case x64 --- .../Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 8 ++++++-- .../Flax.Build/Platforms/Mac/MacPlatform.cs | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index 161a0cc15..20f64b92e 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -215,8 +215,12 @@ namespace Flax.Build } } - // Use x64 when cross-compiling from ARM64 - if (architecture == TargetArchitecture.ARM64 && (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64)) + bool isRunningOnArm64Targetx64 = architecture == TargetArchitecture.ARM64 && (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64); + + // We need to support two paths here: + // 1. We are running an x64 binary and we are running on an arm64 host machine + // 2. We are running an Arm64 binary and we are targeting an x64 host machine + if (Flax.Build.Platforms.MacPlatform.GetProcessIsTranslated() || isRunningOnArm64Targetx64) { rid = "osx-x64"; dotnetPath = Path.Combine(dotnetPath, "x64"); diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs index 53d299515..a680fbb1a 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs @@ -1,4 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Runtime.InteropServices; namespace Flax.Build.Platforms { @@ -41,5 +42,20 @@ namespace Flax.Build.Platforms default: return false; } } + + [DllImport ("c")] + public static unsafe extern int sysctlbyname ( + string name, void* oldp, ulong *oldlenp, void* newp, ulong newlen); + + public unsafe static bool GetProcessIsTranslated() + { + int ret = 0; + ulong size = sizeof (int); + if (sysctlbyname ("sysctl.proc_translated", &ret, &size, null, 0) == -1) { + return false; + } + + return ret != 0; + } } } From dfb9f1985ff11f824b7e5c3ffa741f502f078cb9 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Sep 2023 13:19:03 -0500 Subject: [PATCH 47/89] Improve input editors to use combo box. --- .../CustomEditors/Editors/InputEditor.cs | 82 ++++++++++++------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/InputEditor.cs b/Source/Editor/CustomEditors/Editors/InputEditor.cs index 416626e81..7a356c6cd 100644 --- a/Source/Editor/CustomEditors/Editors/InputEditor.cs +++ b/Source/Editor/CustomEditors/Editors/InputEditor.cs @@ -1,8 +1,10 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using FlaxEditor.CustomEditors.GUI; +using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; using FlaxEngine; -using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Editors { @@ -12,7 +14,7 @@ namespace FlaxEditor.CustomEditors.Editors [CustomEditor(typeof(InputEvent)), DefaultEditor] public class InputEventEditor : CustomEditor { - private Dropdown _dropdown; + private ComboBox _comboBox; /// public override DisplayStyle Style => DisplayStyle.Inline; @@ -20,23 +22,30 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var dropdownElement = layout.Custom(); - _dropdown = dropdownElement.CustomControl; - var names = new List(); + LinkedLabel.SetupContextMenu += OnSetupContextMenu; + var comboBoxElement = layout.ComboBox(); + _comboBox = comboBoxElement.ComboBox; + var names = new List(); foreach (var mapping in Input.ActionMappings) { if (!names.Contains(mapping.Name)) names.Add(mapping.Name); } - _dropdown.Items = names; + _comboBox.Items = names; if (Values[0] is InputEvent inputEvent && names.Contains(inputEvent.Name)) - _dropdown.SelectedItem = inputEvent.Name; - _dropdown.SelectedIndexChanged += OnSelectedIndexChanged; + _comboBox.SelectedItem = inputEvent.Name; + _comboBox.SelectedIndexChanged += OnSelectedIndexChanged; } - private void OnSelectedIndexChanged(Dropdown dropdown) + private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor) { - SetValue(new InputEvent(dropdown.SelectedItem)); + var button = menu.AddButton("Reset to null"); + button.Clicked += () => _comboBox.SelectedItem = null; + } + + private void OnSelectedIndexChanged(ComboBox comboBox) + { + SetValue(comboBox.SelectedItem == null ? null : new InputEvent(comboBox.SelectedItem)); } /// @@ -49,17 +58,21 @@ namespace FlaxEditor.CustomEditors.Editors } else { - if (Values[0] is InputEvent inputEvent && _dropdown.Items.Contains(inputEvent.Name)) - _dropdown.SelectedItem = inputEvent.Name; + if (Values[0] is InputEvent inputEvent && _comboBox.Items.Contains(inputEvent.Name)) + _comboBox.SelectedItem = inputEvent.Name; + else + _comboBox.SelectedItem = null; } } /// protected override void Deinitialize() { - if (_dropdown != null) - _dropdown.SelectedIndexChanged -= OnSelectedIndexChanged; - _dropdown = null; + if (LinkedLabel != null) + LinkedLabel.SetupContextMenu -= OnSetupContextMenu; + if (_comboBox != null) + _comboBox.SelectedIndexChanged -= OnSelectedIndexChanged; + _comboBox = null; } } @@ -69,7 +82,7 @@ namespace FlaxEditor.CustomEditors.Editors [CustomEditor(typeof(InputAxis)), DefaultEditor] public class InputAxisEditor : CustomEditor { - private Dropdown _dropdown; + private ComboBox _comboBox; /// public override DisplayStyle Style => DisplayStyle.Inline; @@ -77,23 +90,30 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var dropdownElement = layout.Custom(); - _dropdown = dropdownElement.CustomControl; - var names = new List(); + LinkedLabel.SetupContextMenu += OnSetupContextMenu; + var comboBoxElement = layout.ComboBox(); + _comboBox = comboBoxElement.ComboBox; + var names = new List(); foreach (var mapping in Input.AxisMappings) { if (!names.Contains(mapping.Name)) names.Add(mapping.Name); } - _dropdown.Items = names; + _comboBox.Items = names; if (Values[0] is InputAxis inputAxis && names.Contains(inputAxis.Name)) - _dropdown.SelectedItem = inputAxis.Name; - _dropdown.SelectedIndexChanged += OnSelectedIndexChanged; + _comboBox.SelectedItem = inputAxis.Name; + _comboBox.SelectedIndexChanged += OnSelectedIndexChanged; } - private void OnSelectedIndexChanged(Dropdown dropdown) + private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor) { - SetValue(new InputAxis(dropdown.SelectedItem)); + var button = menu.AddButton("Reset to null"); + button.Clicked += () => _comboBox.SelectedItem = null; + } + + private void OnSelectedIndexChanged(ComboBox comboBox) + { + SetValue(comboBox.SelectedItem == null ? null : new InputAxis(comboBox.SelectedItem)); } /// @@ -106,17 +126,21 @@ namespace FlaxEditor.CustomEditors.Editors } else { - if (Values[0] is InputAxis inputAxis && _dropdown.Items.Contains(inputAxis.Name)) - _dropdown.SelectedItem = inputAxis.Name; + if (Values[0] is InputAxis inputAxis && _comboBox.Items.Contains(inputAxis.Name)) + _comboBox.SelectedItem = inputAxis.Name; + else + _comboBox.SelectedItem = null; } } /// protected override void Deinitialize() { - if (_dropdown != null) - _dropdown.SelectedIndexChanged -= OnSelectedIndexChanged; - _dropdown = null; + if (LinkedLabel != null) + LinkedLabel.SetupContextMenu -= OnSetupContextMenu; + if (_comboBox != null) + _comboBox.SelectedIndexChanged -= OnSelectedIndexChanged; + _comboBox = null; } } } From fea296bcbb677f51853655d223cef4f74373ef17 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 20 Sep 2023 21:59:37 +0300 Subject: [PATCH 48/89] Fix white window flickering in context menus --- Source/Engine/Platform/CreateWindowSettings.cs | 1 + Source/Engine/Platform/CreateWindowSettings.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/CreateWindowSettings.cs b/Source/Engine/Platform/CreateWindowSettings.cs index 1e5d17484..d4d9ce727 100644 --- a/Source/Engine/Platform/CreateWindowSettings.cs +++ b/Source/Engine/Platform/CreateWindowSettings.cs @@ -23,6 +23,7 @@ namespace FlaxEngine AllowDragAndDrop = true, IsRegularWindow = true, HasSizingFrame = true, + ShowAfterFirstPaint = true, }; } } diff --git a/Source/Engine/Platform/CreateWindowSettings.h b/Source/Engine/Platform/CreateWindowSettings.h index 71bbf58c3..1ff596df9 100644 --- a/Source/Engine/Platform/CreateWindowSettings.h +++ b/Source/Engine/Platform/CreateWindowSettings.h @@ -131,7 +131,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(CreateWindowSettings); /// /// Enable/disable window auto-show after the first paint. /// - API_FIELD() bool ShowAfterFirstPaint = false; + API_FIELD() bool ShowAfterFirstPaint = true; /// /// The custom data (platform dependant). From 812813251dff853d1e3969e3e7c520709f84403f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Sep 2023 14:42:18 -0500 Subject: [PATCH 49/89] Fix being able to create folder from project folder. --- Source/Editor/Windows/ContentWindow.ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 283133688..f460034d0 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -147,7 +147,7 @@ namespace FlaxEditor.Windows cm.AddSeparator(); - if (!isRootFolder) + if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode)) { cm.AddButton("New folder", NewFolder); } From d328f0adc4b69a0485b59451f783edefb0c63f1e Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 20 Sep 2023 23:13:07 +0300 Subject: [PATCH 50/89] Update build scripts to detect only supported versions of MSBuild --- Development/Scripts/Windows/CallBuildTool.bat | 12 +--- .../Scripts/Windows/GetMSBuildPath.bat | 66 ++++--------------- 2 files changed, 14 insertions(+), 64 deletions(-) diff --git a/Development/Scripts/Windows/CallBuildTool.bat b/Development/Scripts/Windows/CallBuildTool.bat index 9f60368c5..22c2d1dd7 100644 --- a/Development/Scripts/Windows/CallBuildTool.bat +++ b/Development/Scripts/Windows/CallBuildTool.bat @@ -11,16 +11,6 @@ for %%I in (Source\Logo.png) do if %%~zI LSS 2000 ( call "Development\Scripts\Windows\GetMSBuildPath.bat" if errorlevel 1 goto Error_NoVisualStudioEnvironment -if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto Compile -for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do ( - for %%j in (15.0, Current) do ( - if exist "%%i\MSBuild\%%j\Bin\MSBuild.exe" ( - set MSBUILD_PATH="%%i\MSBuild\%%j\Bin\MSBuild.exe" - goto Compile - ) - ) -) - :Compile md Cache\Intermediate >nul 2>nul dir /s /b Source\Tools\Flax.Build\*.cs >Cache\Intermediate\Flax.Build.Files.txt @@ -44,7 +34,7 @@ goto Exit echo CallBuildTool ERROR: The script is in invalid directory. goto Exit :Error_NoVisualStudioEnvironment -echo CallBuildTool ERROR: Missing Visual Studio 2015 or newer. +echo CallBuildTool ERROR: Missing Visual Studio 2022 or newer. goto Exit :Error_CompilationFailed echo CallBuildTool ERROR: Failed to compile Flax.Build project. diff --git a/Development/Scripts/Windows/GetMSBuildPath.bat b/Development/Scripts/Windows/GetMSBuildPath.bat index b20230118..f44155272 100644 --- a/Development/Scripts/Windows/GetMSBuildPath.bat +++ b/Development/Scripts/Windows/GetMSBuildPath.bat @@ -4,66 +4,26 @@ rem Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. set MSBUILD_PATH= +rem Look for MSBuild version 17.0 or later if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto VsWhereNotFound -for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do ( - if exist "%%i\MSBuild\15.0\Bin\MSBuild.exe" ( - set MSBUILD_PATH="%%i\MSBuild\15.0\Bin\MSBuild.exe" - goto End - ) -) -for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath') do ( - if exist "%%i\MSBuild\15.0\Bin\MSBuild.exe" ( - set MSBUILD_PATH="%%i\MSBuild\15.0\Bin\MSBuild.exe" - goto End - ) +for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -version 17.0 -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do ( if exist "%%i\MSBuild\Current\Bin\MSBuild.exe" ( set MSBUILD_PATH="%%i\MSBuild\Current\Bin\MSBuild.exe" goto End ) ) -:VsWhereNotFound -if exist "%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe" ( - set MSBUILD_PATH="%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe" - goto End +rem Look for MSBuild version 17.0 or later in pre-release versions +for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -version 17.0 -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath') do ( + if exist "%%i\MSBuild\Current\Bin\MSBuild.exe" ( + set MSBUILD_PATH="%%i\MSBuild\Current\Bin\MSBuild.exe" + goto End + ) ) - -call :GetInstallPath Microsoft\VisualStudio\SxS\VS7 15.0 MSBuild\15.0\bin\MSBuild.exe -if not errorlevel 1 goto End -call :GetInstallPath Microsoft\MSBuild\ToolsVersions\14.0 MSBuildToolsPath MSBuild.exe -if not errorlevel 1 goto End -call :GetInstallPath Microsoft\MSBuild\ToolsVersions\12.0 MSBuildToolsPath MSBuild.exe -if not errorlevel 1 goto End -call :GetInstallPath Microsoft\MSBuild\ToolsVersions\4.0 MSBuildToolsPath MSBuild.exe -if not errorlevel 1 goto End - +echo GetMSBuildPath ERROR: Could not find MSBuild version 17.0 or later. +exit /B 1 +:VsWhereNotFound +echo GetMSBuildPath ERROR: vswhere.exe was not found. exit /B 1 :End -exit /B 0 - -:GetInstallPath -for /f "tokens=2,*" %%A in ('REG.exe query HKCU\SOFTWARE\%1 /v %2 2^>Nul') do ( - if exist "%%B%%3" ( - set MSBUILD_PATH="%%B%3" - exit /B 0 - ) -) -for /f "tokens=2,*" %%A in ('REG.exe query HKLM\SOFTWARE\%1 /v %2 2^>Nul') do ( - if exist "%%B%3" ( - set MSBUILD_PATH="%%B%3" - exit /B 0 - ) -) -for /f "tokens=2,*" %%A in ('REG.exe query HKCU\SOFTWARE\Wow6432Node\%1 /v %2 2^>Nul') do ( - if exist "%%B%%3" ( - set MSBUILD_PATH="%%B%3" - exit /B 0 - ) -) -for /f "tokens=2,*" %%A in ('REG.exe query HKLM\SOFTWARE\Wow6432Node\%1 /v %2 2^>Nul') do ( - if exist "%%B%3" ( - set MSBUILD_PATH="%%B%3" - exit /B 0 - ) -) -exit /B 1 +exit /B 0 \ No newline at end of file From 07da23338b94c5126af1367a6f2aa3c9a126a1ce Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 20 Sep 2023 23:13:47 +0300 Subject: [PATCH 51/89] Update README --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dc5abb84f..fac631a6a 100644 --- a/README.md +++ b/README.md @@ -31,19 +31,20 @@ Follow the instructions below to compile and run the engine from source. * Install Visual Studio 2022 or newer * Install Windows 8.1 SDK or newer (via Visual Studio Installer) * Install Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer) -* Install .Net 7 SDK (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) +* Install .NET 7 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) * Install Git with LFS * Clone repo (with LFS) * Run **GenerateProjectFiles.bat** * Open `Flax.sln` and set solution configuration to **Editor.Development** and solution platform to **Win64** * Set Flax (C++) or FlaxEngine (C#) as startup project * Compile Flax project (hit F7 or CTRL+Shift+B) +* Optionally set Debug Type to **Managed Only (.NET Core)** to debug C#-only, or **Mixed (.NET Core)** to debug both C++ and C# * Run Flax (hit F5 key) ## Linux * Install Visual Studio Code -* Install .Net 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) +* Install .NET 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) * Ubuntu: `sudo apt install dotnet-sdk-7.0` * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Ubuntu: `sudo apt install vulkan-sdk` @@ -66,7 +67,7 @@ Follow the instructions below to compile and run the engine from source. ## Mac * Install XCode -* Install .Net 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) +* Install .NET 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Clone repo (with LFS) * Run `GenerateProjectFiles.command` From bcce08e81c4d9a5c2255f3c916f66245acc40d71 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 22:14:54 +0200 Subject: [PATCH 52/89] Codestyle fix and doc --- .../Flax.Build/Platforms/Mac/MacPlatform.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs index a680fbb1a..ef89c8b4e 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs @@ -1,4 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + using System.Runtime.InteropServices; namespace Flax.Build.Platforms @@ -43,19 +44,19 @@ namespace Flax.Build.Platforms } } - [DllImport ("c")] - public static unsafe extern int sysctlbyname ( - string name, void* oldp, ulong *oldlenp, void* newp, ulong newlen); - + /// + /// Returns true if running an x64 binary an arm64 host machine. + /// public unsafe static bool GetProcessIsTranslated() { int ret = 0; - ulong size = sizeof (int); - if (sysctlbyname ("sysctl.proc_translated", &ret, &size, null, 0) == -1) { + ulong size = sizeof(int); + if (sysctlbyname("sysctl.proc_translated", &ret, &size, null, 0) == -1) return false; - } - return ret != 0; } + + [DllImport("c")] + private static unsafe extern int sysctlbyname(string name, void* oldp, ulong* oldlenp, void* newp, ulong newlen); } } From cadd0d2356d21bd09bf1a598d802154d4b775555 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Sep 2023 15:18:33 -0500 Subject: [PATCH 53/89] Fix visuals --- Source/Editor/Windows/ContentWindow.ContextMenu.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index f460034d0..6923d634d 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -145,10 +145,9 @@ namespace FlaxEditor.Windows cm.AddButton("Refresh all thumbnails", RefreshViewItemsThumbnails); } - cm.AddSeparator(); - if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode)) { + cm.AddSeparator(); cm.AddButton("New folder", NewFolder); } From ffbd14ad495fad820ad14b89fffb6ad18be3908a Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Sep 2023 15:35:36 -0500 Subject: [PATCH 54/89] Fix right clicking on source folder to sometimes select non-visible child. --- Source/Engine/UI/GUI/ContainerControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs index 2de96d0b3..8f1c31e89 100644 --- a/Source/Engine/UI/GUI/ContainerControl.cs +++ b/Source/Engine/UI/GUI/ContainerControl.cs @@ -360,7 +360,7 @@ namespace FlaxEngine.GUI { var containerControl = child as ContainerControl; var childAtRecursive = containerControl?.GetChildAtRecursive(childLocation); - if (childAtRecursive != null) + if (childAtRecursive != null && childAtRecursive.Visible) { child = childAtRecursive; } From da7ba0ecbaa33313558cb16da793db7017bfaa6c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 22:41:47 +0200 Subject: [PATCH 55/89] Fix regression on cooking ios game --- .../Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp index 8b0cf06a8..85a7f99fa 100644 --- a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp @@ -260,15 +260,20 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data) { LOG(Info, "Building app package..."); const Char* configuration = data.Configuration == BuildConfiguration::Release ? TEXT("Release") : TEXT("Debug"); - String command = String::Format(TEXT("xcodebuild -project FlaxGame.xcodeproj -configuration {} -scheme FlaxGame -archivePath FlaxGame.xcarchive archive"), configuration); - int32 result = Platform::RunProcess(command, data.OriginalOutputPath); + CreateProcessSettings procSettings; + procSettings.HiddenWindow = true; + procSettings.WorkingDirectory = data.OriginalOutputPath; + procSettings.FileName = TEXT("/usr/bin/xcodebuild"); + procSettings.Arguments = String::Format(TEXT("-project FlaxGame.xcodeproj -configuration {} -scheme FlaxGame -archivePath FlaxGame.xcarchive archive"), configuration); + int32 result = Platform::CreateProcess(procSettings); if (result != 0) { data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result)); return true; } - command = TEXT("xcodebuild -exportArchive -archivePath FlaxGame.xcarchive -allowProvisioningUpdates -exportPath . -exportOptionsPlist ExportOptions.plist"); - result = Platform::RunProcess(command, data.OriginalOutputPath); + procSettings.FileName = TEXT("/usr/bin/xcodebuild"); + procSettings.Arguments = TEXT("-exportArchive -archivePath FlaxGame.xcarchive -allowProvisioningUpdates -exportPath . -exportOptionsPlist ExportOptions.plist"); + result = Platform::CreateProcess(procSettings); if (result != 0) { data.Error(String::Format(TEXT("Failed to package app (result code: {0}). See log for more info."), result)); From ed51791b581d5382229503df28432be0730585ad Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 21 Sep 2023 00:59:09 +0300 Subject: [PATCH 56/89] Prevent generating VS configurations for Win32 and engine projects --- Source/Tools/Flax.Build/Build/Builder.Projects.cs | 4 ++++ .../Projects/VisualStudio/VisualStudioProjectGenerator.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs index 4fbabe986..790d45515 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs @@ -126,6 +126,10 @@ namespace Flax.Build if (!platform.HasRequiredSDKsInstalled && (!projectInfo.IsCSharpOnlyProject || platform != Platform.BuildPlatform)) continue; + // Prevent generating configuration data for Windows x86 + if (architecture == TargetArchitecture.x86 && targetPlatform == TargetPlatform.Windows) + continue; + string configurationText = targetName + '.' + platformName + '.' + configurationName; string architectureName = architecture.ToString(); if (platform is IProjectCustomizer customizer) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index 1955a57ba..035c588d6 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -402,6 +402,10 @@ namespace Flax.Build.Projects.VisualStudio if (project.Configurations == null || project.Configurations.Count == 0) throw new Exception("Missing configurations for project " + project.Name); + // Prevent generating default Debug|AnyCPU and Release|AnyCPU configurations from Flax projects + if (project.Name == "BuildScripts" || project.Name == "Flax.Build" || project.Name == "Flax.Build.Tests") + continue; + foreach (var configuration in project.Configurations) { configurations.Add(new SolutionConfiguration(configuration)); From eb5a1f11032fdecc3662a3cc2c3153ea7d86fc98 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Sep 2023 17:49:17 -0500 Subject: [PATCH 57/89] Fix max and min on double click main menu buttons for custom menu bar --- Source/Editor/GUI/MainMenu.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index 370ad056b..b313fed9f 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -292,7 +292,8 @@ namespace FlaxEditor.GUI return true; #if PLATFORM_WINDOWS - if (_useCustomWindowSystem) + var child = GetChildAtRecursive(location); + if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton) { if (_window.IsMaximized) _window.Restore(); From f315286e2602ae873dfb79607901feb39dfe6ed7 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Sep 2023 19:08:36 -0500 Subject: [PATCH 58/89] Change wording to "Set To Null" --- Source/Editor/CustomEditors/Editors/InputEditor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/InputEditor.cs b/Source/Editor/CustomEditors/Editors/InputEditor.cs index 7a356c6cd..7b2682a84 100644 --- a/Source/Editor/CustomEditors/Editors/InputEditor.cs +++ b/Source/Editor/CustomEditors/Editors/InputEditor.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.CustomEditors.Editors private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor) { - var button = menu.AddButton("Reset to null"); + var button = menu.AddButton("Set to null"); button.Clicked += () => _comboBox.SelectedItem = null; } @@ -107,7 +107,7 @@ namespace FlaxEditor.CustomEditors.Editors private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor) { - var button = menu.AddButton("Reset to null"); + var button = menu.AddButton("Set to null"); button.Clicked += () => _comboBox.SelectedItem = null; } From d4c71b5fd911cc04a2930022de99cdc43a5d49f5 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Wed, 20 Sep 2023 23:16:38 -0700 Subject: [PATCH 59/89] Launching Rider on macOS * Fixing issues where when the editor would try and launch rider on an M1/2 machine it would not properly open because it was the wrong architecture which is now resolved because open will do the right thing * Making the ParseArgs function a little more resilient against escaped arguments --- .../Scripting/CodeEditors/RiderCodeEditor.cpp | 14 +++++++ .../Engine/Platform/Apple/ApplePlatform.cpp | 37 +++++++++++-------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 05608d71e..7f5ca6f17 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -268,8 +268,16 @@ void RiderCodeEditor::OpenFile(const String& path, int32 line) // Open file line = line > 0 ? line : 1; CreateProcessSettings procSettings; + +#if !PLATFORM_MAC procSettings.FileName = _execPath; procSettings.Arguments = String::Format(TEXT("\"{0}\" --line {2} \"{1}\""), _solutionPath, path, line); +#else + // This follows pretty much how all the other engines open rider which deals with cross architecture issues + procSettings.FileName = "/usr/bin/open"; + procSettings.Arguments = String::Format(TEXT("-n -a \"{0}\" --args \"{1}\" --line {3} \"{2}\""), _execPath, _solutionPath, path, line); +#endif + procSettings.HiddenWindow = false; procSettings.WaitForEnd = false; procSettings.LogOutput = false; @@ -287,8 +295,14 @@ void RiderCodeEditor::OpenSolution() // Open solution CreateProcessSettings procSettings; +#if !PLATFORM_MAC procSettings.FileName = _execPath; procSettings.Arguments = String::Format(TEXT("\"{0}\""), _solutionPath); +#else + // This follows pretty much how all the other engines open rider which deals with cross architecture issues + procSettings.FileName = "/usr/bin/open"; + procSettings.Arguments = String::Format(TEXT("-n -a \"{0}\" \"{1}\""), _execPath, _solutionPath); +#endif procSettings.HiddenWindow = false; procSettings.WaitForEnd = false; procSettings.LogOutput = false; diff --git a/Source/Engine/Platform/Apple/ApplePlatform.cpp b/Source/Engine/Platform/Apple/ApplePlatform.cpp index eeef5b91f..473fc0987 100644 --- a/Source/Engine/Platform/Apple/ApplePlatform.cpp +++ b/Source/Engine/Platform/Apple/ApplePlatform.cpp @@ -85,27 +85,34 @@ NSString* AppleUtils::ToNSString(const char* string) NSArray* AppleUtils::ParseArguments(NSString* argsString) { NSMutableArray *argsArray = [NSMutableArray array]; - NSScanner *scanner = [NSScanner scannerWithString:argsString]; - NSString *currentArg = nil; + NSMutableString *currentArg = [NSMutableString string]; BOOL insideQuotes = NO; - while (![scanner isAtEnd]) { - if (insideQuotes) { - [scanner scanUpToString:@"\"" intoString:¤tArg]; - [scanner scanString:@"\"" intoString:NULL]; - insideQuotes = NO; - } else { - [scanner scanUpToString:@" " intoString:¤tArg]; - [scanner scanString:@" " intoString:NULL]; - } + for (NSInteger i = 0; i < argsString.length; ++i) { + unichar c = [argsString characterAtIndex:i]; - if ([currentArg isEqualToString:@"\""]) { - insideQuotes = YES; - } else if (currentArg) { - [argsArray addObject:currentArg]; + if (c == '\"') { + if (insideQuotes) { + [argsArray addObject:[currentArg copy]]; + [currentArg setString:@""]; + insideQuotes = NO; + } else { + insideQuotes = YES; + } + } else if (c == ' ' && !insideQuotes) { + if (currentArg.length > 0) { + [argsArray addObject:[currentArg copy]]; + [currentArg setString:@""]; + } + } else { + [currentArg appendFormat:@"%C", c]; } } + if (currentArg.length > 0) { + [argsArray addObject:[currentArg copy]]; + } + return [argsArray copy]; } From a420ca72d670def12cd4fd8c2675291f159d8ecc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Sep 2023 11:23:57 +0200 Subject: [PATCH 60/89] Fix Visual Studio solution project to ensure that main project is the first one --- .../VisualStudio/VisualStudioProjectGenerator.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index 1955a57ba..29b75e4c5 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -236,6 +236,20 @@ namespace Flax.Build.Projects.VisualStudio /// public override void GenerateSolution(Solution solution) { + // Ensure that the main project is the first one (initially selected by Visual Studio) + if (solution.MainProject != null && solution.Projects.Length != 0 && solution.Projects[0] != solution.MainProject) + { + for (int i = 1; i < solution.Projects.Length; i++) + { + if (solution.Projects[i] == solution.MainProject) + { + solution.Projects[i] = solution.Projects[0]; + solution.Projects[0] = solution.MainProject; + break; + } + } + } + // Try to extract info from the existing solution file to make random IDs stable var solutionId = Guid.NewGuid(); var folderIds = new Dictionary(); From d94c0d3323d50a8110200c5ed47dbc691e049c60 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Sep 2023 11:26:04 +0200 Subject: [PATCH 61/89] Fix C#-only game projects to reference `precompiled DLLs` instead of `FlaxEngine.csproj` --- .../Flax.Build/Build/Builder.Projects.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs index 4fbabe986..149829154 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs @@ -501,6 +501,35 @@ namespace Flax.Build } } + // When generating C#-only projects for Game that uses source-engine distribution replace dependencies on FlaxEngine with fixed dll file refs to fix Intellisense issues + if (rootProject.IsCSharpOnlyProject) + { + Project flaxDependencyToRemove = null; + foreach (var project in projects) + { + if (project.BaseName != "FlaxEngine") + { + var flaxDependency = project.Dependencies.FirstOrDefault(x => x.BaseName == "FlaxEngine"); + if (flaxDependency != null) + { + project.Dependencies.Remove(flaxDependency); + + // TODO: instead of this hack find a way to reference the prebuilt target bindings binary (example: game C# project references FlaxEngine C# prebuilt dll) + project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, "Binaries/Editor/Win64/Development/FlaxEngine.CSharp.dll")); + + // Remove FlaxEngine from projects to prevent duplicated types errors in Intellisense (eg. Actor type defined in both FlaxEngine.CSharp.dll and FlaxEngine.csproj) + flaxDependencyToRemove = flaxDependency; + } + } + } + if (flaxDependencyToRemove != null) + { + projects.Remove(flaxDependencyToRemove); + foreach (var project in projects) + project.Dependencies.Remove(flaxDependencyToRemove); + } + } + // Setup custom projects GenerateCustomProjects?.Invoke(projects); nativeProjectGenerator.GenerateCustomProjects(projects); From 8f668c85065163ef7d8de308afce7084fdb7111e Mon Sep 17 00:00:00 2001 From: MineBill Date: Thu, 21 Sep 2023 12:39:19 +0300 Subject: [PATCH 62/89] Ensure floating numbers are formatted with enough precision --- Source/Engine/Visject/ShaderGraphValue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Visject/ShaderGraphValue.cpp b/Source/Engine/Visject/ShaderGraphValue.cpp index b55da8cdb..56d7c9d06 100644 --- a/Source/Engine/Visject/ShaderGraphValue.cpp +++ b/Source/Engine/Visject/ShaderGraphValue.cpp @@ -40,11 +40,11 @@ ShaderGraphValue::ShaderGraphValue(const Variant& v) break; case VariantType::Float: Type = VariantType::Types::Float; - Value = String::Format(TEXT("{}"), v.AsFloat); + Value = String::Format(TEXT("{:.8f}"), v.AsFloat); break; case VariantType::Double: Type = VariantType::Types::Float; - Value = String::Format(TEXT("{}"), (float)v.AsDouble); + Value = String::Format(TEXT("{:.8f}"), (float)v.AsDouble); break; case VariantType::Float2: { From ad29dd0c929aa76b949b0bbb1dc6281372eaee0f Mon Sep 17 00:00:00 2001 From: Luke Schneider Date: Thu, 21 Sep 2023 08:37:13 -0500 Subject: [PATCH 63/89] MMB Panning Added MMB panning to VisjectSurfaces (materials/etc). --- Source/Editor/Surface/VisjectSurface.Input.cs | 35 ++++++++++++++++++- Source/Editor/Surface/VisjectSurface.cs | 10 ++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 7264321c3..517040cb7 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -213,6 +213,21 @@ namespace FlaxEditor.Surface return; } + if (_middleMouseDown) { + // Calculate delta + var delta = location - _middleMouseDownPos; + if (delta.LengthSquared > 0.01f) { + // Move view + _mouseMoveAmount += delta.Length; + _rootControl.Location += delta; + _middleMouseDownPos = location; + Cursor = CursorType.SizeAll; + } + + // Handled + return; + } + // Check if user is selecting or moving node(s) if (_leftMouseDown) { @@ -269,6 +284,10 @@ namespace FlaxEditor.Surface _rightMouseDown = false; Cursor = CursorType.Default; } + if (_middleMouseDown) { + _middleMouseDown = false; + Cursor = CursorType.Default; + } _isMovingSelection = false; ConnectingEnd(null); @@ -380,6 +399,7 @@ namespace FlaxEditor.Surface _isMovingSelection = false; _rightMouseDown = false; _leftMouseDown = false; + _middleMouseDown = false; return true; } @@ -398,6 +418,12 @@ namespace FlaxEditor.Surface { _rightMouseDown = true; _rightMouseDownPos = location; + Debug.Log("The right mouse button is down in a window"); + } + if (button == MouseButton.Middle) { + _middleMouseDown = true; + _middleMouseDownPos = location; + Debug.Log("The middle mouse button is down in a window"); } // Check if any node is under the mouse @@ -444,7 +470,7 @@ namespace FlaxEditor.Surface Focus(); return true; } - if (_rightMouseDown) + if (_rightMouseDown || _middleMouseDown) { // Start navigating StartMouseCapture(); @@ -513,6 +539,12 @@ namespace FlaxEditor.Surface } _mouseMoveAmount = 0; } + if (_middleMouseDown && button == MouseButton.Middle) { + _middleMouseDown = false; + EndMouseCapture(); + Cursor = CursorType.Default; + _mouseMoveAmount = 0; + } // Base bool handled = base.OnMouseUp(location, button); @@ -523,6 +555,7 @@ namespace FlaxEditor.Surface // Clear flags _rightMouseDown = false; _leftMouseDown = false; + _middleMouseDown = false; return true; } diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index 22aba4855..29dbeb1c6 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -59,6 +59,11 @@ namespace FlaxEditor.Surface /// protected bool _rightMouseDown; + /// + /// The middle mouse down flag. + /// + protected bool _middleMouseDown; + /// /// The left mouse down position. /// @@ -69,6 +74,11 @@ namespace FlaxEditor.Surface /// protected Float2 _rightMouseDownPos = Float2.Minimum; + /// + /// The middle mouse down position. + /// + protected Float2 _middleMouseDownPos = Float2.Minimum; + /// /// The mouse position. /// From 65bf13ea4f1d4ba3f698c1b48deb43d24fd5571a Mon Sep 17 00:00:00 2001 From: Luke Schneider Date: Thu, 21 Sep 2023 08:38:18 -0500 Subject: [PATCH 64/89] Removed debug messages --- Source/Editor/Surface/VisjectSurface.Input.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 517040cb7..f51d89e82 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -418,12 +418,10 @@ namespace FlaxEditor.Surface { _rightMouseDown = true; _rightMouseDownPos = location; - Debug.Log("The right mouse button is down in a window"); } if (button == MouseButton.Middle) { _middleMouseDown = true; _middleMouseDownPos = location; - Debug.Log("The middle mouse button is down in a window"); } // Check if any node is under the mouse From d19984d3e9b54b87b6caa494d5c19e6cbb735834 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 21 Sep 2023 18:02:00 +0200 Subject: [PATCH 65/89] - Pressing space to open visject context menu now ignores the space char as input --- Source/Editor/Surface/VisjectSurface.Input.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 7264321c3..afc1c1363 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -706,6 +706,8 @@ namespace FlaxEditor.Surface { if (_inputBrackets.Count == 0) { + if (currentInputText.StartsWith(' ')) + currentInputText = ""; ResetInput(); ShowPrimaryMenu(_mousePos, false, currentInputText); } From 7d69abadb705ffc7fcde92a820bace208fe3768e Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 21 Sep 2023 19:42:52 +0300 Subject: [PATCH 66/89] Fix script reload crash with .NET libraries --- Source/Engine/Engine/NativeInterop.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 8a8d543f1..b1d2e0cde 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -1099,12 +1099,12 @@ namespace FlaxEngine.Interop private delegate IntPtr InvokeThunkDelegate(ManagedHandle instanceHandle, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7); /// - /// Returns all types that that owned by this assembly. + /// Returns all types owned by this assembly. /// private static Type[] GetAssemblyTypes(Assembly assembly) { var referencedAssemblies = assembly.GetReferencedAssemblies(); - var allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + var allAssemblies = Utils.GetAssemblies(); var referencedTypes = new List(); foreach (var assemblyName in referencedAssemblies) { From bf68d17afe060354ea7fba0883b790cf5db83d3e Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 21 Sep 2023 19:18:11 +0300 Subject: [PATCH 67/89] Create VS folder structure for BuildScripts and FlaxEngine source files --- .../VisualStudio/CSSDKProjectGenerator.cs | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs index 5fba0dc43..d8aece342 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs @@ -174,8 +174,35 @@ namespace Flax.Build.Projects.VisualStudio else fileType = "None"; - var projectPath = Utilities.MakePathRelativeTo(file, projectDirectory); - csProjectFileContent.AppendLine(string.Format(" <{0} Include=\"{1}\" />", fileType, projectPath)); + var filePath = file.Replace('/', '\\'); // Normalize path + var projectPath = Utilities.MakePathRelativeTo(filePath, projectDirectory); + string linkPath = null; + if (projectPath.StartsWith(@"..\..\..\")) + { + // Create folder structure for project external files + var sourceIndex = filePath.LastIndexOf(@"\Source\"); + if (sourceIndex != -1) + { + projectPath = filePath; + string fileProjectRoot = filePath.Substring(0, sourceIndex); + string fileProjectName = Path.GetFileName(fileProjectRoot); + string fileProjectRelativePath = filePath.Substring(sourceIndex + 1); + + // Remove Source-directory from path + if (fileProjectRelativePath.IndexOf('\\') != -1) + fileProjectRelativePath = fileProjectRelativePath.Substring(fileProjectRelativePath.IndexOf('\\') + 1); + + if (fileProjectRoot == project.SourceFolderPath) + linkPath = fileProjectRelativePath; + else // BuildScripts project + linkPath = Path.Combine(fileProjectName, fileProjectRelativePath); + } + } + + if (!string.IsNullOrEmpty(linkPath)) + csProjectFileContent.AppendLine(string.Format(" <{0} Include=\"{1}\" Link=\"{2}\" />", fileType, projectPath, linkPath)); + else + csProjectFileContent.AppendLine(string.Format(" <{0} Include=\"{1}\" />", fileType, projectPath)); } if (project.GeneratedSourceFiles != null) @@ -188,7 +215,8 @@ namespace Flax.Build.Projects.VisualStudio else fileType = "None"; - csProjectFileContent.AppendLine(string.Format(" <{0} Visible=\"false\" Include=\"{1}\" />", fileType, file)); + var filePath = file.Replace('/', '\\'); + csProjectFileContent.AppendLine(string.Format(" <{0} Visible=\"false\" Include=\"{1}\" />", fileType, filePath)); } } From bbf88832754c9f3ecc543b70fcf85f3e1dc92419 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 21 Sep 2023 20:12:29 +0300 Subject: [PATCH 68/89] Fix docking hint areas not hiding after dragging a window --- Source/Editor/GUI/Docking/DockHintWindow.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/GUI/Docking/DockHintWindow.cs b/Source/Editor/GUI/Docking/DockHintWindow.cs index 6dc700731..52c5dcd3c 100644 --- a/Source/Editor/GUI/Docking/DockHintWindow.cs +++ b/Source/Editor/GUI/Docking/DockHintWindow.cs @@ -476,6 +476,7 @@ namespace FlaxEditor.GUI.Docking settings.ShowInTaskbar = false; settings.ActivateWhenFirstShown = false; settings.IsTopmost = true; + settings.ShowAfterFirstPaint = false; win = Platform.CreateWindow(ref settings); From 521b1803521f2de86e76c8414d0f282231745573 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 21 Sep 2023 18:56:45 -0500 Subject: [PATCH 69/89] Fix issue with copying and poasting in same folder. --- Source/Editor/Windows/ContentWindow.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 2628735eb..285302d03 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -722,7 +722,12 @@ namespace FlaxEditor.Windows { var item = Editor.ContentDatabase.Find(sourcePath); if (item != null) - Editor.ContentDatabase.Copy(item, Path.Combine(CurrentViewFolder.Path, item.FileName)); + { + var newPath = StringUtils.NormalizePath(Path.Combine(CurrentViewFolder.Path, item.FileName)); + if (sourcePath.Equals(newPath)) + newPath = GetClonedAssetPath(item); + Editor.ContentDatabase.Copy(item, newPath); + } else importFiles.Add(sourcePath); } From 7012832050829eef4f90758a735622e38944b97e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 21 Sep 2023 21:29:21 -0500 Subject: [PATCH 70/89] Add filter to content window to hide generated files. Hide by default. --- Source/Editor/Windows/ContentWindow.cs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 2628735eb..2151028fa 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -46,7 +46,7 @@ namespace FlaxEditor.Windows private TextBox _itemsSearchBox; private ViewDropdown _viewDropdown; private SortType _sortType; - private bool _showEngineFiles = true, _showPluginsFiles = true, _showAllFiles = true; + private bool _showEngineFiles = true, _showPluginsFiles = true, _showAllFiles = true, _showGeneratedFiles = false; private RootContentTreeNode _root; @@ -106,6 +106,19 @@ namespace FlaxEditor.Windows } } + internal bool ShowGeneratedFiles + { + get => _showGeneratedFiles; + set + { + if (_showGeneratedFiles != value) + { + _showGeneratedFiles = value; + RefreshView(); + } + } + } + internal bool ShowAllFiles { get => _showAllFiles; @@ -314,6 +327,12 @@ namespace FlaxEditor.Windows b.Checked = ShowPluginsFiles; b.CloseMenuOnClick = false; b.AutoCheck = true; + + b = show.ContextMenu.AddButton("Generated files", () => ShowGeneratedFiles = !ShowGeneratedFiles); + b.TooltipText = "Shows generated files"; + b.Checked = ShowGeneratedFiles; + b.CloseMenuOnClick = false; + b.AutoCheck = true; b = show.ContextMenu.AddButton("All files", () => ShowAllFiles = !ShowAllFiles); b.TooltipText = "Shows all files including other than assets and source code"; @@ -969,6 +988,8 @@ namespace FlaxEditor.Windows var items = target.Folder.Children; if (!_showAllFiles) items = items.Where(x => !(x is FileItem)).ToList(); + if (!_showGeneratedFiles) + items = items.Where(x => !(x.Path.Contains(".Gen.cs") || x.Path.Contains(".Gen.h") || x.Path.Contains(".Gen.cpp") || x.Path.Contains(".csproj") || x.Path.Contains(".CSharp"))).ToList(); _view.ShowItems(items, _sortType, false, true); } } @@ -1145,6 +1166,7 @@ namespace FlaxEditor.Windows writer.WriteAttributeString("ShowEngineFiles", ShowEngineFiles.ToString()); writer.WriteAttributeString("ShowPluginsFiles", ShowPluginsFiles.ToString()); writer.WriteAttributeString("ShowAllFiles", ShowAllFiles.ToString()); + writer.WriteAttributeString("ShowGeneratedFiles", ShowGeneratedFiles.ToString()); writer.WriteAttributeString("ViewType", _view.ViewType.ToString()); } @@ -1162,6 +1184,8 @@ namespace FlaxEditor.Windows ShowPluginsFiles = value2; if (bool.TryParse(node.GetAttribute("ShowAllFiles"), out value2)) ShowAllFiles = value2; + if (bool.TryParse(node.GetAttribute("ShowGeneratedFiles"), out value2)) + ShowGeneratedFiles = value2; if (Enum.TryParse(node.GetAttribute("ViewType"), out ContentViewType viewType)) _view.ViewType = viewType; } From 1714bc243a28740a0521ef922d8c7cc4102695b2 Mon Sep 17 00:00:00 2001 From: MineBill Date: Fri, 22 Sep 2023 11:03:59 +0300 Subject: [PATCH 71/89] Fix not capturing the the Debug.Log line in the DebugLogWindow --- Source/Editor/Windows/DebugLogWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index 49b03ede1..09a93d300 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -544,7 +544,7 @@ namespace FlaxEditor.Windows if (noLocation) { desc.LocationFile = match.Groups[2].Value; - int.TryParse(match.Groups[5].Value, out desc.LocationLine); + int.TryParse(match.Groups[4].Value, out desc.LocationLine); noLocation = false; } fineStackTrace.AppendLine(match.Groups[0].Value); From 017967d5f83914c61dc08c30af3c2e8343816aa7 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Fri, 22 Sep 2023 02:13:49 -0700 Subject: [PATCH 72/89] Fixing non-windows platforms csproj files * Seems there was a hardcoded path ? for the prebuilt binaries for the FlaxEngine dll --- Source/Tools/Flax.Build/Build/Builder.Projects.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs index 4b606a36d..52c6d4ac9 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs @@ -497,7 +497,7 @@ namespace Flax.Build else if (dependencyModule.BinaryModuleName == "FlaxEngine") { // TODO: instead of this hack find a way to reference the prebuilt target bindings binary (example: game C# project references FlaxEngine C# prebuilt dll) - project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, "Binaries/Editor/Win64/Development/FlaxEngine.CSharp.dll")); + project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, $"Binaries/Editor/{project.Configurations[0].PlatformName}/Development/FlaxEngine.CSharp.dll")); } } } @@ -519,7 +519,7 @@ namespace Flax.Build project.Dependencies.Remove(flaxDependency); // TODO: instead of this hack find a way to reference the prebuilt target bindings binary (example: game C# project references FlaxEngine C# prebuilt dll) - project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, "Binaries/Editor/Win64/Development/FlaxEngine.CSharp.dll")); + project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, $"Binaries/Editor/{project.Configurations[0].PlatformName}/Development/FlaxEngine.CSharp.dll")); // Remove FlaxEngine from projects to prevent duplicated types errors in Intellisense (eg. Actor type defined in both FlaxEngine.CSharp.dll and FlaxEngine.csproj) flaxDependencyToRemove = flaxDependency; From 1537f49e736750898e0e4b1d3a92aaaa65c5d9b7 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Fri, 22 Sep 2023 02:41:09 -0700 Subject: [PATCH 73/89] Fixing Editor Path * Again the path was hardcoded to win64 --- .../VisualStudio/VisualStudioProjectGenerator.cs | 10 ++++++++-- Source/Tools/Flax.Build/Utilities/Utilities.cs | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index 6653a8dac..16ab543ad 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -405,6 +405,8 @@ namespace Flax.Build.Projects.VisualStudio vcSolutionFileContent.AppendLine("EndProject"); } + var globalPlatformName = ""; + // Global configuration { vcSolutionFileContent.AppendLine("Global"); @@ -422,6 +424,10 @@ namespace Flax.Build.Projects.VisualStudio foreach (var configuration in project.Configurations) { + // We just grab the platform name from the first config + if (string.IsNullOrEmpty(globalPlatformName)) + globalPlatformName = configuration.PlatformName; + configurations.Add(new SolutionConfiguration(configuration)); } } @@ -558,8 +564,8 @@ namespace Flax.Build.Projects.VisualStudio { var profiles = new Dictionary(); var profile = new StringBuilder(); - var editorPath = Path.Combine(Globals.EngineRoot, "Binaries/Editor/Win64/Development/FlaxEditor.exe").Replace('/', '\\').Replace("\\", "\\\\"); - var workspacePath = solutionDirectory.Replace('/', '\\').Replace("\\", "\\\\"); + var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, $"Binaries/Editor/{globalPlatformName}/Development/FlaxEditor{Utilities.GetPlatformExecutableExt()}")); + var workspacePath = Utilities.NormalizePath(solutionDirectory); foreach (var project in projects) { if (project.Type == TargetType.DotNetCore) diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index af6777199..994e11c8e 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using System.Text; namespace Flax.Build @@ -746,5 +747,19 @@ namespace Flax.Build text = text.Replace(findWhat, replaceWith); File.WriteAllText(file, text); } + + /// + /// Returns back the exe ext for the current platform + /// + public static string GetPlatformExecutableExt() + { + var extEnding = ".exe"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + extEnding = ""; + } + + return extEnding; + } } } From ab7ca683bb684745c2ee0751e5d178a490000289 Mon Sep 17 00:00:00 2001 From: Andrew Spiering Date: Fri, 22 Sep 2023 02:59:38 -0700 Subject: [PATCH 74/89] Addressing Feedback * use a better method of getting Editor binary folder --- Source/Tools/Flax.Build/Build/Builder.Projects.cs | 4 ++-- .../VisualStudio/VisualStudioProjectGenerator.cs | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs index 52c6d4ac9..166e78036 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs @@ -497,7 +497,7 @@ namespace Flax.Build else if (dependencyModule.BinaryModuleName == "FlaxEngine") { // TODO: instead of this hack find a way to reference the prebuilt target bindings binary (example: game C# project references FlaxEngine C# prebuilt dll) - project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, $"Binaries/Editor/{project.Configurations[0].PlatformName}/Development/FlaxEngine.CSharp.dll")); + project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), "Development/FlaxEngine.CSharp.dll")); } } } @@ -519,7 +519,7 @@ namespace Flax.Build project.Dependencies.Remove(flaxDependency); // TODO: instead of this hack find a way to reference the prebuilt target bindings binary (example: game C# project references FlaxEngine C# prebuilt dll) - project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, $"Binaries/Editor/{project.Configurations[0].PlatformName}/Development/FlaxEngine.CSharp.dll")); + project.CSharp.FileReferences.Add(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), "Development/FlaxEngine.CSharp.dll")); // Remove FlaxEngine from projects to prevent duplicated types errors in Intellisense (eg. Actor type defined in both FlaxEngine.CSharp.dll and FlaxEngine.csproj) flaxDependencyToRemove = flaxDependency; diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index 16ab543ad..8cbd6b01a 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -405,8 +405,6 @@ namespace Flax.Build.Projects.VisualStudio vcSolutionFileContent.AppendLine("EndProject"); } - var globalPlatformName = ""; - // Global configuration { vcSolutionFileContent.AppendLine("Global"); @@ -423,11 +421,7 @@ namespace Flax.Build.Projects.VisualStudio continue; foreach (var configuration in project.Configurations) - { - // We just grab the platform name from the first config - if (string.IsNullOrEmpty(globalPlatformName)) - globalPlatformName = configuration.PlatformName; - + { configurations.Add(new SolutionConfiguration(configuration)); } } @@ -564,7 +558,7 @@ namespace Flax.Build.Projects.VisualStudio { var profiles = new Dictionary(); var profile = new StringBuilder(); - var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, $"Binaries/Editor/{globalPlatformName}/Development/FlaxEditor{Utilities.GetPlatformExecutableExt()}")); + var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), $"Development/FlaxEditor{Utilities.GetPlatformExecutableExt()}")); var workspacePath = Utilities.NormalizePath(solutionDirectory); foreach (var project in projects) { From abcfc8af35d45226623ab6050b88c20914e05cf1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Sep 2023 12:04:41 +0200 Subject: [PATCH 75/89] Add using exact C# method profiler data for profiler events in overriden thunk method callbacks --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index da94a0056..4e5d5c158 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1452,7 +1452,8 @@ namespace Flax.Build.Bindings var useThunk = buildData.Platform.HasDynamicCodeExecutionSupport && Configuration.AOTMode == DotNetAOTModes.None; if (useThunk) { - contents.AppendLine($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::{functionInfo.Name}\");"); + //contents.AppendLine($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::{functionInfo.Name}\");"); + contents.AppendLine(" PROFILE_CPU_SRC_LOC(method->ProfilerData);"); // Convert parameters into managed format as boxed values var thunkParams = string.Empty; From 31b3f56ddb35830398b82d915430ff42d63b79f8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Sep 2023 12:37:13 +0200 Subject: [PATCH 76/89] Fix visibility of some internal `NativeInterop` methods for game bindings --- Source/Engine/Engine/NativeInterop.cs | 53 ++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 8a8d543f1..dfef614b3 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -24,7 +24,8 @@ namespace FlaxEngine.Interop /// /// Provides a Mono-like API for native code to access managed runtime. /// - internal static unsafe partial class NativeInterop + [HideInEditor] + public static unsafe partial class NativeInterop { internal static Dictionary AssemblyLocations = new(); @@ -147,7 +148,13 @@ namespace FlaxEngine.Interop NativeMemory.AlignedFree(ptr); } - internal static T[] GCHandleArrayToManagedArray(ManagedArray ptrArray) where T : class + /// + /// Converts array of GC Handles from native runtime to managed array. + /// + /// Array element type. + /// Input array. + /// Output array. + public static T[] GCHandleArrayToManagedArray(ManagedArray ptrArray) where T : class { Span span = ptrArray.ToSpan(); T[] managedArray = new T[ptrArray.Length]; @@ -156,7 +163,12 @@ namespace FlaxEngine.Interop return managedArray; } - internal static IntPtr[] ManagedArrayToGCHandleArray(Array array) + /// + /// Converts managed array wrapper into array of GC Handles for native runtime. + /// + /// Input array. + /// Output array. + public static IntPtr[] ManagedArrayToGCHandleArray(Array array) { if (array.Length == 0) return Array.Empty(); @@ -170,13 +182,26 @@ namespace FlaxEngine.Interop return pointerArray; } - internal static ManagedArray ManagedArrayToGCHandleWrappedArray(Array array) + /// + /// Converts managed array wrapper into array of GC Handles for native runtime. + /// + /// Input array. + /// Output array. + public static ManagedArray ManagedArrayToGCHandleWrappedArray(Array array) { IntPtr[] pointerArray = ManagedArrayToGCHandleArray(array); return ManagedArray.WrapNewArray(pointerArray, array.GetType()); } - internal static TDst[] ConvertArray(Span src, Func convertFunc) + /// + /// Converts array with a custom converter function for each element. + /// + /// Input data type. + /// Output data type. + /// The input array. + /// Converter callback. + /// The output array. + public static TDst[] ConvertArray(Span src, Func convertFunc) { TDst[] dst = new TDst[src.Length]; for (int i = 0; i < src.Length; i++) @@ -184,7 +209,15 @@ namespace FlaxEngine.Interop return dst; } - internal static TDst[] ConvertArray(TSrc[] src, Func convertFunc) + /// + /// Converts array with a custom converter function for each element. + /// + /// Input data type. + /// Output data type. + /// The input array. + /// Converter callback. + /// The output array. + public static TDst[] ConvertArray(TSrc[] src, Func convertFunc) { TDst[] dst = new TDst[src.Length]; for (int i = 0; i < src.Length; i++) @@ -1024,11 +1057,12 @@ namespace FlaxEngine.Interop private static uint pinnedBoxedValuesPointer = 0; private static (IntPtr ptr, int size)[] pinnedAllocations = new (IntPtr ptr, int size)[256]; private static uint pinnedAllocationsPointer = 0; - + private delegate TInternal ToNativeDelegate(T value); + private delegate IntPtr UnboxerDelegate(object value, object converter); - private static ConcurrentDictionary unboxers = new (1, 3); + private static ConcurrentDictionary unboxers = new(1, 3); private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic); private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic); @@ -1089,7 +1123,8 @@ namespace FlaxEngine.Interop return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox(value))); } - private static IntPtr UnboxPointerWithConverter(object value, object converter) where T : struct where TInternal : struct + private static IntPtr UnboxPointerWithConverter(object value, object converter) where T : struct + where TInternal : struct { ToNativeDelegate toNative = Unsafe.As>(converter); return PinValue(toNative(Unsafe.Unbox(value))); From a0de513a43e8c68a86c771468367153c2282cee9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Sep 2023 12:37:35 +0200 Subject: [PATCH 77/89] Optimize updating scripts tick arrays --- Source/Engine/Level/Scene/SceneTicking.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Level/Scene/SceneTicking.cpp b/Source/Engine/Level/Scene/SceneTicking.cpp index 235d7bbab..34de25a2e 100644 --- a/Source/Engine/Level/Scene/SceneTicking.cpp +++ b/Source/Engine/Level/Scene/SceneTicking.cpp @@ -32,7 +32,7 @@ void SceneTicking::TickData::RemoveTick(void* callee) { for (int32 i = 0; i < Ticks.Count(); i++) { - if (Ticks[i].Callee == callee) + if (Ticks.Get()[i].Callee == callee) { Ticks.RemoveAt(i); break; @@ -45,7 +45,7 @@ void SceneTicking::TickData::Tick() TickScripts(Scripts); for (int32 i = 0; i < Ticks.Count(); i++) - Ticks[i].Call(); + Ticks.Get()[i].Call(); } #if USE_EDITOR @@ -54,7 +54,7 @@ void SceneTicking::TickData::RemoveTickExecuteInEditor(void* callee) { for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++) { - if (TicksExecuteInEditor[i].Callee == callee) + if (TicksExecuteInEditor.Get()[i].Callee == callee) { TicksExecuteInEditor.RemoveAt(i); break; @@ -67,7 +67,7 @@ void SceneTicking::TickData::TickExecuteInEditor() TickScripts(ScriptsExecuteInEditor); for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++) - TicksExecuteInEditor[i].Call(); + TicksExecuteInEditor.Get()[i].Call(); } #endif From 62c2008cbcb1f395a4e3c20f19263187381b0caa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Sep 2023 12:50:50 +0200 Subject: [PATCH 78/89] Fix crash in C# `JobSystem.Dispatch` due to GC collecting Delegate object --- Source/Engine/Engine/NativeInterop.cs | 14 ++++++++++++++ .../Bindings/BindingsGenerator.CSharp.cs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index dfef614b3..e0ef84a61 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -148,6 +148,20 @@ namespace FlaxEngine.Interop NativeMemory.AlignedFree(ptr); } + /// + /// Converts a delegate into a function pointer that is callable from unmanaged code via but cached delegate to prevent collecting it by GC. + /// + /// The type of delegate to convert. + /// The delegate to be passed to unmanaged code. + /// A value that can be passed to unmanaged code, which, in turn, can use it to call the underlying managed delegate. + public static IntPtr GetFunctionPointerForDelegate(TDelegate d) where TDelegate : notnull + { + // Example use-case: C# script runs actions via JobSystem.Dispatch which causes crash due to GC collecting Delegate object + ManagedHandle.Alloc(d, GCHandleType.Weak); + + return Marshal.GetFunctionPointerForDelegate(d); + } + /// /// Converts array of GC Handles from native runtime to managed array. /// diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 6a9aa7669..3685cb271 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -464,7 +464,7 @@ namespace Flax.Build.Bindings return "FlaxEngine.Object.GetUnmanagedPtr({0})"; case "Function": // delegate - return "Marshal.GetFunctionPointerForDelegate({0})"; + return "NativeInterop.GetFunctionPointerForDelegate({0})"; default: var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) From 787479357e14632661f704fabd1edea2ecb4c3b3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Sep 2023 12:57:07 +0200 Subject: [PATCH 79/89] Codestyle fix #1495 --- .../Projects/VisualStudio/VisualStudioProjectGenerator.cs | 2 +- Source/Tools/Flax.Build/Utilities/Utilities.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index 8cbd6b01a..ecf70f9f8 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -421,7 +421,7 @@ namespace Flax.Build.Projects.VisualStudio continue; foreach (var configuration in project.Configurations) - { + { configurations.Add(new SolutionConfiguration(configuration)); } } diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 994e11c8e..59e13aa73 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -754,9 +754,9 @@ namespace Flax.Build public static string GetPlatformExecutableExt() { var extEnding = ".exe"; - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - extEnding = ""; + extEnding = ""; } return extEnding; From 08d04ea6fb5cc4d231926bc60afb065467936156 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 22 Sep 2023 16:54:23 +0300 Subject: [PATCH 80/89] Fix exceptions in Debug Log Window not using correct line numbers Additions to fix in commit 1714bc243a28740a0521ef922d8c7cc4102695b2 --- Source/Editor/Windows/DebugLogWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index 09a93d300..24e38d9af 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -573,7 +573,7 @@ namespace FlaxEditor.Windows if (match.Success) { desc.LocationFile = match.Groups[2].Value; - int.TryParse(match.Groups[3].Value, out desc.LocationLine); + int.TryParse(match.Groups[4].Value, out desc.LocationLine); } } From df23174debbbaffd9a3312dc8bf788711b17078b Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Fri, 22 Sep 2023 19:23:16 +0200 Subject: [PATCH 81/89] - Added proper control backspace support --- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index a4ccbe5d2..60b201986 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -11,6 +11,11 @@ namespace FlaxEngine.GUI /// public abstract class TextBoxBase : ContainerControl { + /// + /// The delete control character (used for text filtering). + /// + protected const char DelChar = (char)0x7F; + /// /// The text separators (used for words skipping). /// @@ -351,6 +356,10 @@ namespace FlaxEngine.GUI if (value.IndexOf('\r') != -1) value = value.Replace("\r", ""); + // Filter text (handle backspace control character) + if(value.IndexOf(DelChar) != -1) + value = value.Replace(DelChar.ToString(), ""); + // Clamp length if (value.Length > MaxLength) value = value.Substring(0, MaxLength); @@ -673,6 +682,8 @@ namespace FlaxEngine.GUI // Filter text if (str.IndexOf('\r') != -1) str = str.Replace("\r", ""); + if (str.IndexOf(DelChar) != -1) + str = str.Replace(DelChar.ToString(), ""); if (!IsMultiline && str.IndexOf('\n') != -1) str = str.Replace("\n", ""); @@ -1327,6 +1338,15 @@ namespace FlaxEngine.GUI if (IsReadOnly) return true; + if (ctrDown) + { + int prevWordBegin = FindPrevWordBegin(); + _text = _text.Remove(prevWordBegin, CaretPosition-prevWordBegin); + SetSelection(prevWordBegin); + OnTextChanged(); + return true; + } + int left = SelectionLeft; if (HasSelection) { From 248304a78f3ded68bc05e2cc0b488e02619f54a5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Sep 2023 19:43:00 +0200 Subject: [PATCH 82/89] Fix regression of importing only a single material for models with split objects enabled #1483 --- .../Tools/ModelTool/ModelTool.Assimp.cpp | 7 ++++++- .../Tools/ModelTool/ModelTool.OpenFBX.cpp | 18 ++++++++++++++++-- Source/Engine/Tools/ModelTool/ModelTool.cpp | 4 ---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index ab9b6b606..7eff75514 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -566,12 +566,17 @@ bool ImportMaterials(ImportedModelData& result, AssimpImporterData& data, String return false; } +bool IsMeshInvalid(const aiMesh* aMesh) +{ + return aMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE || aMesh->mNumVertices == 0 || aMesh->mNumFaces == 0 || aMesh->mFaces[0].mNumIndices != 3; +} + bool ImportMesh(int32 i, ImportedModelData& result, AssimpImporterData& data, String& errorMsg) { const auto aMesh = data.Scene->mMeshes[i]; // Skip invalid meshes - if (aMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE || aMesh->mNumVertices == 0 || aMesh->mNumFaces == 0 || aMesh->mFaces[0].mNumIndices != 3) + if (IsMeshInvalid(aMesh)) return false; // Skip unused meshes diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index ab55df7f6..215eadaba 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -923,8 +923,6 @@ bool ImportMesh(int32 index, ImportedModelData& result, OpenFbxImporterData& dat const auto aMesh = data.Scene->getMesh(index); const auto aGeometry = aMesh->getGeometry(); const auto trianglesCount = aGeometry->getVertexCount() / 3; - - // Skip invalid meshes if (IsMeshInvalid(aMesh)) return false; @@ -1272,6 +1270,22 @@ bool ModelTool::ImportDataOpenFBX(const char* path, ImportedModelData& data, Opt const auto meshIndex = Math::Clamp(options.ObjectIndex, 0, meshCount - 1); if (ImportMesh(meshIndex, data, *context, errorMsg)) return true; + + // Let the firstly imported mesh import all materials from all meshes (index 0 is importing all following ones before itself during splitting - see code above) + if (options.ObjectIndex == 1) + { + for (int32 i = 0; i < meshCount; i++) + { + const auto aMesh = context->Scene->getMesh(i); + if (i == 1 || IsMeshInvalid(aMesh)) + continue; + for (int32 j = 0; j < aMesh->getMaterialCount(); j++) + { + const ofbx::Material* aMaterial = aMesh->getMaterial(j); + context->AddMaterial(data, aMaterial); + } + } + } } else { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 99873c016..15c7cbc32 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -910,10 +910,6 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op { auto& texture = data.Textures[i]; - // When splitting imported meshes allow only the first mesh to import assets (mesh[0] is imported after all following ones so import assets during mesh[1]) - if (!options.SplitObjects && options.ObjectIndex != 1 && options.ObjectIndex != -1) - continue; - // Auto-import textures if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Textures) == ImportDataTypes::None || texture.FilePath.IsEmpty()) continue; From 963300c2cbe2f4906e4ea5f1c37ed1a30c74e23b Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 23 Sep 2023 11:56:45 +0200 Subject: [PATCH 83/89] - Implemented shift tab support for Container Controls --- Source/Editor/GUI/Dialogs/Dialog.cs | 6 ++++- Source/Editor/Windows/EditorWindow.cs | 3 ++- Source/Engine/UI/GUI/ContainerControl.cs | 28 +++++++++++++++++++----- Source/Engine/UI/GUI/Control.cs | 1 + Source/Engine/UI/GUI/Enums.cs | 5 +++++ 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/Dialog.cs b/Source/Editor/GUI/Dialogs/Dialog.cs index 8910cfe49..c7d565f9c 100644 --- a/Source/Editor/GUI/Dialogs/Dialog.cs +++ b/Source/Editor/GUI/Dialogs/Dialog.cs @@ -290,7 +290,11 @@ namespace FlaxEditor.GUI.Dialogs OnCancel(); return true; case KeyboardKeys.Tab: - Root?.Navigate(NavDirection.Next); + if (Root != null) + { + bool shiftDown = Root.GetKey(KeyboardKeys.Shift); + Root?.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next); + } return true; } return false; diff --git a/Source/Editor/Windows/EditorWindow.cs b/Source/Editor/Windows/EditorWindow.cs index 1ff0363f1..60817a51b 100644 --- a/Source/Editor/Windows/EditorWindow.cs +++ b/Source/Editor/Windows/EditorWindow.cs @@ -207,7 +207,8 @@ namespace FlaxEditor.Windows case KeyboardKeys.Tab: if (CanUseNavigation && Root != null) { - Root.Navigate(NavDirection.Next); + bool shiftDown = Root.GetKey(KeyboardKeys.Shift); + Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next); return true; } break; diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs index 2de96d0b3..d8d5c6e5f 100644 --- a/Source/Engine/UI/GUI/ContainerControl.cs +++ b/Source/Engine/UI/GUI/ContainerControl.cs @@ -507,15 +507,19 @@ namespace FlaxEngine.GUI // Perform automatic navigation based on the layout var result = NavigationRaycast(direction, location, visited); - if (result == null && direction == NavDirection.Next) + var rightMostLocation = location; + if (result == null && (direction == NavDirection.Next || direction == NavDirection.Previous)) { // Try wrap the navigation over the layout based on the direction var visitedWrap = new List(visited); - result = NavigationWrap(direction, location, visitedWrap); + result = NavigationWrap(direction, location, visitedWrap, out rightMostLocation); } if (result != null) { - result = result.OnNavigate(direction, result.PointFromParent(location), this, visited); + // HACK: only the 'previous' direction needs the rightMostLocation so i used a ternary conditional operator. + // The rightMostLocation can probably become a 'desired raycast origin' that gets calculated correctly in the NavigationWrap method. + var useLocation = direction == NavDirection.Previous ? rightMostLocation : location; + result = result.OnNavigate(direction, result.PointFromParent(useLocation), this, visited); if (result != null) return result; } @@ -551,8 +555,9 @@ namespace FlaxEngine.GUI /// The navigation direction. /// The navigation start location (in the control-space). /// The list with visited controls. Used to skip recursive navigation calls when doing traversal across the UI hierarchy. + /// Returns the rightmost location of the parent container for the raycast used by the child container /// The target navigation control or null if didn't performed any navigation. - protected virtual Control NavigationWrap(NavDirection direction, Float2 location, List visited) + protected virtual Control NavigationWrap(NavDirection direction, Float2 location, List visited, out Float2 rightMostLocation) { // This searches form a child that calls this navigation event (see Control.OnNavigate) to determinate the layout wrapping size based on that child size var currentChild = RootWindow?.FocusedControl; @@ -566,15 +571,22 @@ namespace FlaxEngine.GUI case NavDirection.Next: predictedLocation = new Float2(0, location.Y + layoutSize.Y); break; + case NavDirection.Previous: + predictedLocation = new Float2(Size.X, location.Y - layoutSize.Y); + break; } if (new Rectangle(Float2.Zero, Size).Contains(ref predictedLocation)) { var result = NavigationRaycast(direction, predictedLocation, visited); if (result != null) - return result; + { + rightMostLocation = predictedLocation; + return result; + } } } - return Parent?.NavigationWrap(direction, PointToParent(ref location), visited); + rightMostLocation = location; + return Parent?.NavigationWrap(direction, PointToParent(ref location), visited, out rightMostLocation); } private static bool CanGetAutoFocus(Control c) @@ -613,6 +625,10 @@ namespace FlaxEngine.GUI uiDir1 = new Float2(1, 0); uiDir2 = new Float2(0, 1); break; + case NavDirection.Previous: + uiDir1 = new Float2(-1, 0); + uiDir2 = new Float2(0, -1); + break; } Control result = null; var minDistance = float.MaxValue; diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs index 3bc2610a4..747d407e9 100644 --- a/Source/Engine/UI/GUI/Control.cs +++ b/Source/Engine/UI/GUI/Control.cs @@ -634,6 +634,7 @@ namespace FlaxEngine.GUI case NavDirection.Left: return new Float2(0, size.Y * 0.5f); case NavDirection.Right: return new Float2(size.X, size.Y * 0.5f); case NavDirection.Next: return Float2.Zero; + case NavDirection.Previous: return new Float2(Size.X, Size.Y); default: return size * 0.5f; } } diff --git a/Source/Engine/UI/GUI/Enums.cs b/Source/Engine/UI/GUI/Enums.cs index d0b4fb61c..9672dcb9b 100644 --- a/Source/Engine/UI/GUI/Enums.cs +++ b/Source/Engine/UI/GUI/Enums.cs @@ -202,5 +202,10 @@ namespace FlaxEngine.GUI /// The next item (right with layout wrapping). /// Next, + + /// + /// The previous item (left with layout wrapping). + /// + Previous, } } From b6ad2523050e5c0b284e058a331da9e3b5afd26a Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 23 Sep 2023 19:14:06 +0200 Subject: [PATCH 84/89] - Removed unnecessary null-conditional operator - Removed unnecessary Float2 allocation --- Source/Editor/GUI/Dialogs/Dialog.cs | 2 +- Source/Engine/UI/GUI/Control.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/Dialog.cs b/Source/Editor/GUI/Dialogs/Dialog.cs index c7d565f9c..07fc3ff0d 100644 --- a/Source/Editor/GUI/Dialogs/Dialog.cs +++ b/Source/Editor/GUI/Dialogs/Dialog.cs @@ -293,7 +293,7 @@ namespace FlaxEditor.GUI.Dialogs if (Root != null) { bool shiftDown = Root.GetKey(KeyboardKeys.Shift); - Root?.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next); + Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next); } return true; } diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs index 747d407e9..5cb9501c0 100644 --- a/Source/Engine/UI/GUI/Control.cs +++ b/Source/Engine/UI/GUI/Control.cs @@ -634,7 +634,7 @@ namespace FlaxEngine.GUI case NavDirection.Left: return new Float2(0, size.Y * 0.5f); case NavDirection.Right: return new Float2(size.X, size.Y * 0.5f); case NavDirection.Next: return Float2.Zero; - case NavDirection.Previous: return new Float2(Size.X, Size.Y); + case NavDirection.Previous: return size; default: return size * 0.5f; } } From 9021deb49ecfbed77c135506f29f33b8ba8acf5c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 24 Sep 2023 15:38:12 +0300 Subject: [PATCH 85/89] Fix Vulkan shader compilation with source files missing the last newline --- Source/Engine/ContentImporters/ImportShader.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Engine/ContentImporters/ImportShader.cpp b/Source/Engine/ContentImporters/ImportShader.cpp index 9cfb1242c..bf5b5581d 100644 --- a/Source/Engine/ContentImporters/ImportShader.cpp +++ b/Source/Engine/ContentImporters/ImportShader.cpp @@ -32,14 +32,21 @@ CreateAssetResult ImportShader::Import(CreateAssetContext& context) LOG(Warning, "Empty shader source file."); return CreateAssetResult::Error; } + + // Ensure the source code has an empty line at the end (expected by glslang) + auto sourceCodeChunkSize = sourceCodeSize + 1; + if (sourceCodeText[sourceCodeSize - 1] != '\n') + sourceCodeChunkSize++; + const auto& sourceCodeChunk = context.Data.Header.Chunks[SourceCodeChunk]; - sourceCodeChunk->Data.Allocate(sourceCodeSize + 1); + sourceCodeChunk->Data.Allocate(sourceCodeChunkSize); const auto sourceCode = sourceCodeChunk->Get(); Platform::MemoryCopy(sourceCode, sourceCodeText.Get(), sourceCodeSize); + sourceCode[sourceCodeChunkSize - 2] = '\n'; // Encrypt source code - Encryption::EncryptBytes(sourceCode, sourceCodeSize); - sourceCode[sourceCodeSize] = 0; + Encryption::EncryptBytes(sourceCode, sourceCodeChunkSize - 1); + sourceCode[sourceCodeChunkSize - 1] = 0; // Set Custom Data with Header ShaderStorage::Header20 shaderHeader; From 90a4fba767d2f538c469b171964861d0a37122c6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 25 Sep 2023 15:39:00 +0200 Subject: [PATCH 86/89] Improve #1490 performance to use `Ordinal` string comparision and just check for extension on some generated files --- Source/Editor/Windows/ContentWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 6e0651d60..c65735c4c 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -994,7 +994,7 @@ namespace FlaxEditor.Windows if (!_showAllFiles) items = items.Where(x => !(x is FileItem)).ToList(); if (!_showGeneratedFiles) - items = items.Where(x => !(x.Path.Contains(".Gen.cs") || x.Path.Contains(".Gen.h") || x.Path.Contains(".Gen.cpp") || x.Path.Contains(".csproj") || x.Path.Contains(".CSharp"))).ToList(); + items = items.Where(x => !(x.Path.EndsWith(".Gen.cs", StringComparison.Ordinal) || x.Path.EndsWith(".Gen.h", StringComparison.Ordinal) || x.Path.EndsWith(".Gen.cpp", StringComparison.Ordinal) || x.Path.EndsWith(".csproj", StringComparison.Ordinal) || x.Path.Contains(".CSharp"))).ToList(); _view.ShowItems(items, _sortType, false, true); } } From 212a2e1dedded799c88a9be9c686f9dee87a9e7f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 25 Sep 2023 15:42:23 +0200 Subject: [PATCH 87/89] Format code from recent PRs --- Source/Editor/Surface/VisjectSurface.Input.cs | 19 ++++++++++++------- Source/Editor/Surface/VisjectSurface.cs | 2 +- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 10 +++++----- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 6830541b9..5e30ff110 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -213,10 +213,12 @@ namespace FlaxEditor.Surface return; } - if (_middleMouseDown) { + if (_middleMouseDown) + { // Calculate delta var delta = location - _middleMouseDownPos; - if (delta.LengthSquared > 0.01f) { + if (delta.LengthSquared > 0.01f) + { // Move view _mouseMoveAmount += delta.Length; _rootControl.Location += delta; @@ -284,7 +286,8 @@ namespace FlaxEditor.Surface _rightMouseDown = false; Cursor = CursorType.Default; } - if (_middleMouseDown) { + if (_middleMouseDown) + { _middleMouseDown = false; Cursor = CursorType.Default; } @@ -310,7 +313,7 @@ namespace FlaxEditor.Surface if (IsMouseOver && !_leftMouseDown && !IsPrimaryMenuOpened) { var nextViewScale = ViewScale + delta * 0.1f; - + if (delta > 0 && !_rightMouseDown) { // Scale towards mouse when zooming in @@ -325,7 +328,7 @@ namespace FlaxEditor.Surface ViewScale = nextViewScale; ViewCenterPosition = viewCenter; } - + return true; } @@ -419,7 +422,8 @@ namespace FlaxEditor.Surface _rightMouseDown = true; _rightMouseDownPos = location; } - if (button == MouseButton.Middle) { + if (button == MouseButton.Middle) + { _middleMouseDown = true; _middleMouseDownPos = location; } @@ -537,7 +541,8 @@ namespace FlaxEditor.Surface } _mouseMoveAmount = 0; } - if (_middleMouseDown && button == MouseButton.Middle) { + if (_middleMouseDown && button == MouseButton.Middle) + { _middleMouseDown = false; EndMouseCapture(); Cursor = CursorType.Default; diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index 29dbeb1c6..6aed7cf68 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -912,7 +912,7 @@ namespace FlaxEditor.Surface { return _context.FindNode(id); } - + /// /// Adds the undo action to be batched (eg. if multiple undo actions is performed in a sequence during single update). /// diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 60b201986..9560d863a 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -15,7 +15,7 @@ namespace FlaxEngine.GUI /// The delete control character (used for text filtering). /// protected const char DelChar = (char)0x7F; - + /// /// The text separators (used for words skipping). /// @@ -357,9 +357,9 @@ namespace FlaxEngine.GUI value = value.Replace("\r", ""); // Filter text (handle backspace control character) - if(value.IndexOf(DelChar) != -1) + if (value.IndexOf(DelChar) != -1) value = value.Replace(DelChar.ToString(), ""); - + // Clamp length if (value.Length > MaxLength) value = value.Substring(0, MaxLength); @@ -1341,12 +1341,12 @@ namespace FlaxEngine.GUI if (ctrDown) { int prevWordBegin = FindPrevWordBegin(); - _text = _text.Remove(prevWordBegin, CaretPosition-prevWordBegin); + _text = _text.Remove(prevWordBegin, CaretPosition - prevWordBegin); SetSelection(prevWordBegin); OnTextChanged(); return true; } - + int left = SelectionLeft; if (HasSelection) { From 96b6313acba1bd9bb07f7179c53ce6b29d86b871 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 25 Sep 2023 16:30:24 +0200 Subject: [PATCH 88/89] Fix crash when loading C# assembly from non-ASNSI path #1439 --- .../Engine/Engine/NativeInterop.Unmanaged.cs | 61 +++++++++++-------- Source/Engine/Scripting/Runtime/DotNet.cpp | 8 +-- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 423aae6a5..8198b0aed 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -851,39 +851,46 @@ namespace FlaxEngine.Interop *assemblyFullName = NativeAllocStringAnsi(flaxEngineAssembly.FullName); return GetAssemblyHandle(flaxEngineAssembly); } + try + { + string assemblyPath = Marshal.PtrToStringAnsi(assemblyPathPtr); - string assemblyPath = Marshal.PtrToStringAnsi(assemblyPathPtr); - - Assembly assembly; + Assembly assembly; #if FLAX_EDITOR - // Load assembly from loaded bytes to prevent file locking in Editor - var assemblyBytes = File.ReadAllBytes(assemblyPath); - using MemoryStream stream = new MemoryStream(assemblyBytes); - var pdbPath = Path.ChangeExtension(assemblyPath, "pdb"); - if (File.Exists(pdbPath)) - { - // Load including debug symbols - using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open); - assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream); - } - else - { - assembly = scriptingAssemblyLoadContext.LoadFromStream(stream); - } + // Load assembly from loaded bytes to prevent file locking in Editor + var assemblyBytes = File.ReadAllBytes(assemblyPath); + using MemoryStream stream = new MemoryStream(assemblyBytes); + var pdbPath = Path.ChangeExtension(assemblyPath, "pdb"); + if (File.Exists(pdbPath)) + { + // Load including debug symbols + using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open); + assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream); + } + else + { + assembly = scriptingAssemblyLoadContext.LoadFromStream(stream); + } #else - // Load assembly from file - assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(assemblyPath); + // Load assembly from file + assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(assemblyPath); #endif - if (assembly == null) - return new ManagedHandle(); - NativeLibrary.SetDllImportResolver(assembly, NativeLibraryImportResolver); + if (assembly == null) + return new ManagedHandle(); + NativeLibrary.SetDllImportResolver(assembly, NativeLibraryImportResolver); - // Assemblies loaded via streams have no Location: https://github.com/dotnet/runtime/issues/12822 - AssemblyLocations.Add(assembly.FullName, assemblyPath); + // Assemblies loaded via streams have no Location: https://github.com/dotnet/runtime/issues/12822 + AssemblyLocations.Add(assembly.FullName, assemblyPath); - *assemblyName = NativeAllocStringAnsi(assembly.GetName().Name); - *assemblyFullName = NativeAllocStringAnsi(assembly.FullName); - return GetAssemblyHandle(assembly); + *assemblyName = NativeAllocStringAnsi(assembly.GetName().Name); + *assemblyFullName = NativeAllocStringAnsi(assembly.FullName); + return GetAssemblyHandle(assembly); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + return new ManagedHandle(); } [UnmanagedCallersOnly] diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index c859fa961..eccd3cf71 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -704,12 +704,10 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa // TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+) // Open .Net assembly const StringAnsi assemblyPathAnsi = assemblyPath.ToStringAnsi(); - const char* name; - const char* fullname; + const char* name = nullptr; + const char* fullname = nullptr; static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage")); _handle = CallStaticMethod(LoadAssemblyImagePtr, assemblyPathAnsi.Get(), &name, &fullname); - _name = name; - _fullname = fullname; MCore::GC::FreeMemory((void*)name); MCore::GC::FreeMemory((void*)fullname); if (_handle == nullptr) @@ -717,6 +715,8 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa Log::CLRInnerException(TEXT(".NET assembly image is invalid at ") + assemblyPath); return true; } + _name = name; + _fullname = fullname; CachedAssemblyHandles.Add(_handle, this); // Provide new path of hot-reloaded native library path for managed DllImport From c374127de1d7979f218a49e0f29f5ba9cbed4886 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 25 Sep 2023 16:34:01 +0200 Subject: [PATCH 89/89] Fix error when loading project on non-ASCII path #1439 --- Source/Engine/Engine/NativeInterop.Unmanaged.cs | 2 +- Source/Engine/Scripting/Runtime/DotNet.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 8198b0aed..cfa71f97f 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -853,7 +853,7 @@ namespace FlaxEngine.Interop } try { - string assemblyPath = Marshal.PtrToStringAnsi(assemblyPathPtr); + string assemblyPath = Marshal.PtrToStringUni(assemblyPathPtr); Assembly assembly; #if FLAX_EDITOR diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index eccd3cf71..4a84fca0a 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -703,11 +703,10 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa { // TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+) // Open .Net assembly - const StringAnsi assemblyPathAnsi = assemblyPath.ToStringAnsi(); const char* name = nullptr; const char* fullname = nullptr; static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage")); - _handle = CallStaticMethod(LoadAssemblyImagePtr, assemblyPathAnsi.Get(), &name, &fullname); + _handle = CallStaticMethod(LoadAssemblyImagePtr, assemblyPath.Get(), &name, &fullname); MCore::GC::FreeMemory((void*)name); MCore::GC::FreeMemory((void*)fullname); if (_handle == nullptr)