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
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`
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));
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);
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();
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/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs
index c83c3fa48..23ca7246b 100644
--- a/Source/Editor/Surface/VisjectSurface.Input.cs
+++ b/Source/Editor/Surface/VisjectSurface.Input.cs
@@ -717,6 +717,8 @@ namespace FlaxEditor.Surface
{
if (_inputBrackets.Count == 0)
{
+ if (currentInputText.StartsWith(' '))
+ currentInputText = "";
ResetInput();
ShowPrimaryMenu(_mousePos, false, currentInputText);
}
diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs
index 283133688..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)
+ if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode))
{
+ cm.AddSeparator();
cm.AddButton("New folder", NewFolder);
}
diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs
index 05a363abb..d0fc07f6b 100644
--- a/Source/Editor/Windows/ContentWindow.cs
+++ b/Source/Editor/Windows/ContentWindow.cs
@@ -721,7 +721,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);
}
diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs
index 49b03ede1..24e38d9af 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);
@@ -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);
}
}
diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs
index 8a8d543f1..359172e09 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,27 @@ namespace FlaxEngine.Interop
NativeMemory.AlignedFree(ptr);
}
- internal static T[] GCHandleArrayToManagedArray(ManagedArray ptrArray) where T : class
+ ///
+ /// 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.
+ ///
+ /// 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 +177,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 +196,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 +223,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 +1071,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 +1137,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)));
@@ -1099,12 +1148,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)
{
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
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];
}
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).
diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp
index 1bfea6f3a..dd0a60f56 100644
--- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp
+++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp
@@ -567,12 +567,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;
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:
{
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
index acc135847..6ffdabea0 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
@@ -473,7 +473,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)
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
index e0fc42c4b..71077ac2e 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
@@ -1469,7 +1469,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;
diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs
index 4fbabe986..166e78036 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)
@@ -493,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, Platform.GetEditorBinaryDirectory(), "Development/FlaxEngine.CSharp.dll"));
}
}
}
@@ -501,6 +505,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, 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;
+ }
+ }
+ }
+ if (flaxDependencyToRemove != null)
+ {
+ projects.Remove(flaxDependencyToRemove);
+ foreach (var project in projects)
+ project.Dependencies.Remove(flaxDependencyToRemove);
+ }
+ }
+
// Setup custom projects
GenerateCustomProjects?.Invoke(projects);
nativeProjectGenerator.GenerateCustomProjects(projects);
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..ef89c8b4e 100644
--- a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs
+++ b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs
@@ -1,5 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+using System.Runtime.InteropServices;
+
namespace Flax.Build.Platforms
{
///
@@ -41,5 +43,20 @@ namespace Flax.Build.Platforms
default: return false;
}
}
+
+ ///
+ /// 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)
+ return false;
+ return ret != 0;
+ }
+
+ [DllImport("c")]
+ private static unsafe extern int sysctlbyname(string name, void* oldp, ulong* oldlenp, void* newp, ulong newlen);
}
}
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));
}
}
diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs
index 1955a57ba..ecf70f9f8 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();
@@ -402,6 +416,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));
@@ -540,8 +558,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, Platform.GetEditorBinaryDirectory(), $"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..59e13aa73 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;
+ }
}
}