diff --git a/.github/actions/vulkan/action.yml b/.github/actions/vulkan/action.yml index e0db012f7..4ab4adb59 100644 --- a/.github/actions/vulkan/action.yml +++ b/.github/actions/vulkan/action.yml @@ -3,7 +3,7 @@ description: Downloads and installs Vulkan SDK. inputs: vulkan-version: description: 'Vulkan SDK release version (e.g. 1.2.198.1).' - default: '1.2.198.1' + default: '1.3.290.0' required: false runs: using: "composite" diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 428dea933..405b78037 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -35,12 +35,12 @@ jobs: run: | .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Windows-Editor path: Output/Editor.zip - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Windows-EditorDebugSymbols path: Output/EditorDebugSymbols.zip @@ -68,7 +68,7 @@ jobs: run: | .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Windows-Game path: Output/Windows.zip @@ -101,7 +101,7 @@ jobs: run: | ./PackageEditor.sh -arch=x64 -platform=Linux -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Linux-Editor path: Output/FlaxEditorLinux.zip @@ -132,7 +132,7 @@ jobs: run: | ./PackagePlatforms.sh -arch=x64 -platform=Linux -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Linux-Game path: Output/Linux.zip @@ -162,7 +162,7 @@ jobs: run: | ./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Mac-Editor path: Output/FlaxEditorMac.zip @@ -190,7 +190,7 @@ jobs: run: | ./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Mac-Game path: Output/Mac.zip diff --git a/Content/Editor/Gizmo/MaterialAxisFocus.flax b/Content/Editor/Gizmo/MaterialAxisFocus.flax index 56c3f6560..a9de5e7e6 100644 --- a/Content/Editor/Gizmo/MaterialAxisFocus.flax +++ b/Content/Editor/Gizmo/MaterialAxisFocus.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b051fe079aa227c67f861220e35fab93c321d310f737ff663d9b77ef3fe98c3e -size 466 +oid sha256:db7018144f8a2774e1188b6f0ef7edfde3f8cb9c7f11f576163512c02f4c5a54 +size 661 diff --git a/Content/Editor/Gizmo/MaterialAxisLocked.flax b/Content/Editor/Gizmo/MaterialAxisLocked.flax new file mode 100644 index 000000000..be44ece86 --- /dev/null +++ b/Content/Editor/Gizmo/MaterialAxisLocked.flax @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7518765847301a4b13625fb05d542fab4fc924190a7414d39227db817a0e29cb +size 661 diff --git a/Content/Editor/Gizmo/MaterialAxisX.flax b/Content/Editor/Gizmo/MaterialAxisX.flax index 2bdb44208..9c7141673 100644 --- a/Content/Editor/Gizmo/MaterialAxisX.flax +++ b/Content/Editor/Gizmo/MaterialAxisX.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdffe8d74604845d50004b3f481e28c996a5b7945ae4e3e9e69975d23b2ef176 -size 302 +oid sha256:261382d0e9d30168f325eea14e96ae7abdd2c1fd8bad166f1b134df507e5fda9 +size 661 diff --git a/Content/Editor/Gizmo/MaterialAxisY.flax b/Content/Editor/Gizmo/MaterialAxisY.flax index b07d7b64a..6a16d14da 100644 --- a/Content/Editor/Gizmo/MaterialAxisY.flax +++ b/Content/Editor/Gizmo/MaterialAxisY.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1407e7ad2dbd54e0833488fd96ef4c3ede3b78fd970b3ff7e804d51dd04fdb2e -size 302 +oid sha256:f9160ba4200d01d7dfe6c529bb2f8893ee5106fa644921de51757947a0cb7bea +size 661 diff --git a/Content/Editor/Gizmo/MaterialAxisZ.flax b/Content/Editor/Gizmo/MaterialAxisZ.flax index e2bf44327..29c9a49d4 100644 --- a/Content/Editor/Gizmo/MaterialAxisZ.flax +++ b/Content/Editor/Gizmo/MaterialAxisZ.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f4a98b0028a9a9750f8dad737e5baf2bd061970f2f35fd10deacf47162cd992 -size 302 +oid sha256:5d129825361a89fca14f1dbad60a983cfac60d69284be66ca5e84041713e07a6 +size 661 diff --git a/Content/Editor/Gizmo/MaterialSphere.flax b/Content/Editor/Gizmo/MaterialSphere.flax index bf110acfb..1da1a3413 100644 --- a/Content/Editor/Gizmo/MaterialSphere.flax +++ b/Content/Editor/Gizmo/MaterialSphere.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf88f48c5de6b8fb8c7effbbdded9e6f115b1a3ac262d60785987c8f24107dfb -size 567 +oid sha256:55fe4f30e87ddda26928901beeef710bc5eaaf7712224bf633c674465cf3b4ac +size 661 diff --git a/Content/Editor/Scripting/ActorTemplate.cpp b/Content/Editor/Scripting/ActorTemplate.cpp new file mode 100644 index 000000000..826c361cc --- /dev/null +++ b/Content/Editor/Scripting/ActorTemplate.cpp @@ -0,0 +1,19 @@ +%copyright%#include "%filename%.h" + +%class%::%class%(const SpawnParams& params) + : Actor(params) +{ + +} + +void %class%::OnEnable() +{ + Actor::OnEnable(); + // Here you can add code that needs to be called when script is enabled (eg. register for events) +} + +void %class%::OnDisable() +{ + Actor::OnDisable(); + // Here you can add code that needs to be called when script is disabled (eg. unregister from events) +} diff --git a/Content/Editor/Scripting/ActorTemplate.cs b/Content/Editor/Scripting/ActorTemplate.cs new file mode 100644 index 000000000..20714b22b --- /dev/null +++ b/Content/Editor/Scripting/ActorTemplate.cs @@ -0,0 +1,39 @@ +%copyright%using System; +using System.Collections.Generic; +using FlaxEngine; + +namespace %namespace%; + +/// +/// %class% Actor. +/// +public class %class% : Actor +{ + /// + public override void OnBeginPlay() + { + base.OnBeginPlay(); + // Here you can add code that needs to be called when Actor added to the game. This is called during edit time as well. + } + + /// + public override void OnEndPlay() + { + base.OnEndPlay(); + // Here you can add code that needs to be called when Actor removed to the game. This is called during edit time as well. + } + + /// + public override void OnEnable() + { + base.OnEnable(); + // Here you can add code that needs to be called when Actor is enabled (eg. register for events). This is called during edit time as well. + } + + /// + public override void OnDisable() + { + base.OnDisable(); + // Here you can add code that needs to be called when Actor is disabled (eg. unregister from events). This is called during edit time as well. + } +} diff --git a/Content/Editor/Scripting/ActorTemplate.h b/Content/Editor/Scripting/ActorTemplate.h new file mode 100644 index 000000000..e97651a51 --- /dev/null +++ b/Content/Editor/Scripting/ActorTemplate.h @@ -0,0 +1,13 @@ +%copyright%#pragma once + +#include "Engine/Level/Actor.h" + +API_CLASS() class %module%%class% : public Actor +{ +API_AUTO_SERIALIZATION(); +DECLARE_SCENE_OBJECT(%class%); + + // [Actor] + void OnEnable() override; + void OnDisable() override; +}; diff --git a/Content/Editor/Scripting/EmptyClassTemplate.cs b/Content/Editor/Scripting/EmptyClassTemplate.cs new file mode 100644 index 000000000..613f80cfa --- /dev/null +++ b/Content/Editor/Scripting/EmptyClassTemplate.cs @@ -0,0 +1,13 @@ +%copyright%using System; +using System.Collections.Generic; +using FlaxEngine; + +namespace %namespace%; + +/// +/// %class% class. +/// +public class %class% +{ + +} diff --git a/Content/Editor/Scripting/EmptyInterfaceTemplate.cs b/Content/Editor/Scripting/EmptyInterfaceTemplate.cs new file mode 100644 index 000000000..9738379bd --- /dev/null +++ b/Content/Editor/Scripting/EmptyInterfaceTemplate.cs @@ -0,0 +1,13 @@ +%copyright%using System; +using System.Collections.Generic; +using FlaxEngine; + +namespace %namespace%; + +/// +/// %class% interface. +/// +public interface %class% +{ + +} diff --git a/Content/Editor/Scripting/EmptyStructTemplate.cs b/Content/Editor/Scripting/EmptyStructTemplate.cs new file mode 100644 index 000000000..3cca5aed4 --- /dev/null +++ b/Content/Editor/Scripting/EmptyStructTemplate.cs @@ -0,0 +1,13 @@ +%copyright%using System; +using System.Collections.Generic; +using FlaxEngine; + +namespace %namespace%; + +/// +/// %class% struct. +/// +public struct %class% +{ + +} diff --git a/Content/Editor/Scripting/GamePluginTemplate.cs b/Content/Editor/Scripting/GamePluginTemplate.cs new file mode 100644 index 000000000..d110fa68d --- /dev/null +++ b/Content/Editor/Scripting/GamePluginTemplate.cs @@ -0,0 +1,25 @@ +%copyright%using System; +using System.Collections.Generic; +using FlaxEngine; + +namespace %namespace%; + +/// +/// %class% GamePlugin. +/// +public class %class% : GamePlugin +{ + /// + public override void Initialize() + { + base.Initialize(); + + } + + /// + public override void Deinitialize() + { + base.Deinitialize(); + + } +} \ No newline at end of file diff --git a/Source/Editor/Content/AssetPickerValidator.cs b/Source/Editor/Content/AssetPickerValidator.cs index 109ea2f79..349d43cf4 100644 --- a/Source/Editor/Content/AssetPickerValidator.cs +++ b/Source/Editor/Content/AssetPickerValidator.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; -using System.IO; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.Utilities; diff --git a/Source/Editor/Content/Proxy/CSharpProxy.cs b/Source/Editor/Content/Proxy/CSharpProxy.cs index 099ad6109..f0a7997c7 100644 --- a/Source/Editor/Content/Proxy/CSharpProxy.cs +++ b/Source/Editor/Content/Proxy/CSharpProxy.cs @@ -73,7 +73,7 @@ namespace FlaxEditor.Content } /// - /// Context proxy object for C# script files. + /// Context proxy object for C# Script files. /// /// [ContentContextMenu("New/C#/C# Script")] @@ -89,6 +89,40 @@ namespace FlaxEditor.Content } } + /// + /// Context proxy object for C# Actor files. + /// + /// + [ContentContextMenu("New/C#/C# Actor")] + public class CSharpActorProxy : CSharpProxy + { + /// + public override string Name => "C# Actor"; + + /// + protected override void GetTemplatePath(out string path) + { + path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.cs"); + } + } + + /// + /// Context proxy object for C# GamePlugin files. + /// + /// + [ContentContextMenu("New/C#/C# GamePlugin")] + public class CSharpGamePluginProxy : CSharpProxy + { + /// + public override string Name => "C# GamePlugin"; + + /// + protected override void GetTemplatePath(out string path) + { + path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/GamePluginTemplate.cs"); + } + } + /// /// Context proxy object for empty C# files. /// @@ -105,4 +139,55 @@ namespace FlaxEditor.Content path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/CSharpEmptyTemplate.cs"); } } + + /// + /// Context proxy object for empty C# class files. + /// + /// + [ContentContextMenu("New/C#/C# Class")] + public class CSharpEmptyClassProxy : CSharpProxy + { + /// + public override string Name => "C# Class"; + + /// + protected override void GetTemplatePath(out string path) + { + path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyClassTemplate.cs"); + } + } + + /// + /// Context proxy object for empty C# struct files. + /// + /// + [ContentContextMenu("New/C#/C# Struct")] + public class CSharpEmptyStructProxy : CSharpProxy + { + /// + public override string Name => "C# Struct"; + + /// + protected override void GetTemplatePath(out string path) + { + path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyStructTemplate.cs"); + } + } + + /// + /// Context proxy object for empty C# interface files. + /// + /// + [ContentContextMenu("New/C#/C# Interface")] + public class CSharpEmptyInterfaceProxy : CSharpProxy + { + /// + public override string Name => "C# Interface"; + + /// + protected override void GetTemplatePath(out string path) + { + path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyInterfaceTemplate.cs"); + } + } } diff --git a/Source/Editor/Content/Proxy/CppProxy.cs b/Source/Editor/Content/Proxy/CppProxy.cs index af5f93421..3d3b6588f 100644 --- a/Source/Editor/Content/Proxy/CppProxy.cs +++ b/Source/Editor/Content/Proxy/CppProxy.cs @@ -100,6 +100,24 @@ namespace FlaxEditor.Content sourceTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.cpp"); } } + + /// + /// Context proxy object for C++ Actor files. + /// + /// + [ContentContextMenu("New/C++/C++ Actor")] + public class CppActorProxy : CppProxy + { + /// + public override string Name => "C++ Actor"; + + /// + protected override void GetTemplatePaths(out string headerTemplate, out string sourceTemplate) + { + headerTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.h"); + sourceTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.cpp"); + } + } /// /// Context proxy object for C++ Json Asset files. diff --git a/Source/Editor/Cooker/GameCooker.cpp b/Source/Editor/Cooker/GameCooker.cpp index 17392adee..36f2da11c 100644 --- a/Source/Editor/Cooker/GameCooker.cpp +++ b/Source/Editor/Cooker/GameCooker.cpp @@ -671,11 +671,14 @@ bool GameCookerImpl::Build() MCore::Thread::Attach(); // Build Started - CallEvent(GameCooker::EventType::BuildStarted); - data.Tools->OnBuildStarted(data); - for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) - Steps[stepIndex]->OnBuildStarted(data); - data.InitProgress(Steps.Count()); + if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook)) + { + CallEvent(GameCooker::EventType::BuildStarted); + data.Tools->OnBuildStarted(data); + for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) + Steps[stepIndex]->OnBuildStarted(data); + data.InitProgress(Steps.Count()); + } // Execute all steps in a sequence bool failed = false; @@ -741,10 +744,13 @@ bool GameCookerImpl::Build() } IsRunning = false; CancelFlag = 0; - for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) - Steps[stepIndex]->OnBuildEnded(data, failed); - data.Tools->OnBuildEnded(data, failed); - CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone); + if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook)) + { + for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) + Steps[stepIndex]->OnBuildEnded(data, failed); + data.Tools->OnBuildEnded(data, failed); + CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone); + } Delete(Data); Data = nullptr; diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index eb5469b47..9e4f18b2c 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -364,6 +364,33 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) } #endif const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release; + + if (platformSettings->BuildAAB) + { + // .aab + { + CreateProcessSettings procSettings; + procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT(":app:bundle") : TEXT(":app:bundleDebug")); + procSettings.WorkingDirectory = data.OriginalOutputPath; + const int32 result = Platform::CreateProcess(procSettings); + if (result != 0) + { + data.Error(String::Format(TEXT("Failed to build Gradle project into .aab package (result code: {0}). See log for more info."), result)); + return true; + } + } + // Copy result package + const String aab = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/bundle/release/app-release.aab") : TEXT("app/build/outputs/bundle/debug/app-debug.aab")); + const String outputAab = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".aab"); + if (FileSystem::CopyFile(outputAab, aab)) + { + LOG(Error, "Failed to copy .aab package from {0} to {1}", aab, outputAab); + return true; + } + LOG(Info, "Output Android AAB application package: {0} (size: {1} MB)", outputAab, FileSystem::GetFileSize(outputAab) / 1024 / 1024); + } + + // .apk { CreateProcessSettings procSettings; procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug")); @@ -371,20 +398,20 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) const int32 result = Platform::CreateProcess(procSettings); if (result != 0) { - data.Error(String::Format(TEXT("Failed to build Gradle project into package (result code: {0}). See log for more info."), result)); + data.Error(String::Format(TEXT("Failed to build Gradle project into .apk package (result code: {0}). See log for more info."), result)); return true; } } - // Copy result package const String apk = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/apk/release/app-release-unsigned.apk") : TEXT("app/build/outputs/apk/debug/app-debug.apk")); const String outputApk = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".apk"); if (FileSystem::CopyFile(outputApk, apk)) { - LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk); + LOG(Error, "Failed to copy .apk package from {0} to {1}", apk, outputApk); return true; } - LOG(Info, "Output Android application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024); + LOG(Info, "Output Android APK application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024); + return false; } diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp index 6b1a6829e..489321d99 100644 --- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp +++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp @@ -214,6 +214,33 @@ bool DeployDataStep::Perform(CookingData& data) FileSystem::NormalizePath(srcDotnet); LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), srcDotnet); + // Get major Version + Array pathParts; + srcDotnet.Split('/', pathParts); + String version; + for (int i = 0; i < pathParts.Count(); i++) + { + if (pathParts[i] == TEXT("runtimes")) + { + Array versionParts; + pathParts[i - 1].Split('.', versionParts); + if (!versionParts.IsEmpty()) + { + const String majorVersion = versionParts[0].TrimTrailing(); + int32 versionNum; + StringUtils::Parse(*majorVersion, majorVersion.Length(), &versionNum); + if (Math::IsInRange(versionNum, GAME_BUILD_DOTNET_RUNTIME_MIN_VER, GAME_BUILD_DOTNET_RUNTIME_MAX_VER)) // Check for major part + version = majorVersion; + } + } + } + + if (version.IsEmpty()) + { + data.Error(TEXT("Failed to find supported .NET version for the current host platform.")); + return true; + } + // Deploy runtime files const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll"); const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms")); @@ -226,14 +253,14 @@ bool DeployDataStep::Perform(CookingData& data) { // AOT runtime files inside Engine Platform folder packFolder /= TEXT("Dotnet"); - dstDotnetLibs /= TEXT("lib/net8.0"); - srcDotnetLibs = packFolder / TEXT("lib/net8.0"); + dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version); + srcDotnetLibs = packFolder / String::Format(TEXT("lib/net{}.0"), version); } else { // Runtime files inside Dotnet SDK folder but placed for AOT - dstDotnetLibs /= TEXT("lib/net8.0"); - srcDotnetLibs /= TEXT("../lib/net8.0"); + dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version); + srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version); } } else @@ -241,14 +268,14 @@ bool DeployDataStep::Perform(CookingData& data) if (srcDotnetFromEngine) { // Runtime files inside Engine Platform folder - dstDotnetLibs /= TEXT("lib/net8.0"); - srcDotnetLibs /= TEXT("lib/net8.0"); + dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version); + srcDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version); } else { // Runtime files inside Dotnet SDK folder dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App"); - srcDotnetLibs /= TEXT("../lib/net8.0"); + srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version); } } LOG(Info, "Copying .NET files from {} to {}", packFolder, dstDotnet); @@ -273,6 +300,7 @@ bool DeployDataStep::Perform(CookingData& data) DEPLOY_NATIVE_FILE("libmonosgen-2.0.so"); DEPLOY_NATIVE_FILE("libSystem.IO.Compression.Native.so"); DEPLOY_NATIVE_FILE("libSystem.Native.so"); + DEPLOY_NATIVE_FILE("libSystem.Globalization.Native.so"); DEPLOY_NATIVE_FILE("libSystem.Security.Cryptography.Native.Android.so"); break; case BuildPlatform::iOSARM64: diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 6b7bd4601..8c3810c3c 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -301,7 +301,7 @@ namespace FlaxEditor.CustomEditors _valueToSet = null; // Assign value - if (val is IList l && l.Count == _values.Count) + if (val is IList l && l.Count == _values.Count && _values.Count > 1) { for (int i = 0; i < _values.Count; i++) _values[i] = l[i]; diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 27e2849bd..3e6785491 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -9,6 +9,7 @@ using FlaxEditor.CustomEditors.Elements; using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Tree; +using FlaxEditor.Modules; using FlaxEditor.Scripting; using FlaxEditor.Windows; using FlaxEditor.Windows.Assets; @@ -85,12 +86,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Edit selected prefab asset var editPrefab = panel.Button("Edit Prefab"); - editPrefab.Button.Clicked += () => - { - Editor.Instance.Windows.ContentWin.ClearItemsSearch(); - Editor.Instance.Windows.ContentWin.Select(prefab); - Editor.Instance.Windows.ContentWin.Open(Editor.Instance.Windows.ContentWin.View.Selection[0]); - }; + editPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Open(Editor.Instance.ContentDatabase.FindAsset(prefab.ID)); // Viewing changes applied to this actor var viewChanges = panel.Button("View Changes"); diff --git a/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs b/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs index 5d06b4a94..f48e1b9c1 100644 --- a/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.Surface; using FlaxEngine; @@ -35,6 +36,8 @@ namespace FlaxEditor.CustomEditors.Dedicated (instance, parameter, tag) => ((AnimatedModel)instance).GetParameterValue(parameter.Identifier), (instance, value, parameter, tag) => ((AnimatedModel)instance).SetParameterValue(parameter.Identifier, value), Values); + if (!parameters.Any()) + group.Label("No parameters", TextAlignment.Center); _parametersAdded = true; } } diff --git a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs index fbb817768..3df5cb72e 100644 --- a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs @@ -53,6 +53,9 @@ public class ModelPrefabEditor : GenericEditor } } + // Creates the import path UI + Utilities.Utils.CreateImportPathUI(layout, modelPrefab.ImportPath, false); + var button = layout.Button("Reimport", "Reimports the source asset as prefab."); _reimportButton = button.Button; _reimportButton.Clicked += OnReimport; diff --git a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs index c207f8949..980ad5fa4 100644 --- a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs @@ -117,6 +117,9 @@ namespace FlaxEditor.CustomEditors.Dedicated var data = SurfaceUtils.InitGraphParameters(parametersGroup); SurfaceUtils.DisplayGraphParameters(group, data, ParameterGet, ParameterSet, Values, ParameterDefaultValue); } + + if (!parameters.Any()) + groups.Label("No parameters", TextAlignment.Center); } /// diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index e71f9a8bc..583241915 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -424,6 +424,13 @@ namespace FlaxEditor.CustomEditors.Dedicated { presenter.Undo.AddAction(multiAction); presenter.Control.Focus(); + + // Scroll to bottom of script control where a new script is added. + if (presenter.Panel.Parent is Panel p && Editor.Instance.Options.Options.Interface.ScrollToScriptOnAdd) + { + var loc = ScriptsEditor.Layout.Control.BottomLeft; + p.ScrollViewTo(loc); + } } } } diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index c3330330d..8be473bcb 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -723,10 +723,13 @@ namespace FlaxEditor.CustomEditors.Dedicated } // Refresh anchors - GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes); - if (xEq != _cachedXEq || yEq != _cachedYEq) + if (Values != null && Values[0] != null) { - RebuildLayout(); + GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes); + if (xEq != _cachedXEq || yEq != _cachedYEq) + { + RebuildLayout(); + } } base.Refresh(); diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 76f0e12cd..83fa1f129 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -292,5 +292,17 @@ namespace FlaxEditor.CustomEditors.Editors _isRefreshing = false; } } + + /// + protected override void Deinitialize() + { + if (_validator != null) + { + _validator.OnDestroy(); + _validator = null; + } + + base.Deinitialize(); + } } } diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 1f06b2704..5497d085f 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -898,9 +898,13 @@ namespace FlaxEditor.CustomEditors.Editors base.Draw(); } + /// public override void OnDestroy() { _pickerValidator.OnDestroy(); + _pickerValidator = null; + + base.OnDestroy(); } private bool ValidateActors(ActorNode node) diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index 8abe54e78..72c2b3980 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -7,6 +7,7 @@ using FlaxEditor.CustomEditors.Elements; using FlaxEditor.GUI; using FlaxEditor.GUI.Drag; using FlaxEditor.SceneGraph; +using FlaxEditor.SceneGraph.GUI; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; @@ -23,6 +24,7 @@ namespace FlaxEditor.CustomEditors.Editors public class FlaxObjectRefPickerControl : Control { private ScriptType _type; + private ActorTreeNode _linkedTreeNode; private Object _value; private string _valueName; private bool _supportsPickDropDown; @@ -300,7 +302,43 @@ namespace FlaxEditor.CustomEditors.Editors // Picker dropdown menu if (_supportsPickDropDown && (isSelected ? button2Rect : button1Rect).Contains(ref location)) + { ShowDropDownMenu(); + return true; + } + + if (button == MouseButton.Left) + { + _isMouseDown = false; + + // Highlight actor or script reference + if (!_hasValidDragOver && !IsDragOver) + { + Actor actor = _value as Actor; + if (actor == null && _value is Script script) + actor = script.Actor; + if (actor != null) + { + if (_linkedTreeNode != null && _linkedTreeNode.Actor == actor) + { + _linkedTreeNode.ExpandAllParents(); + _linkedTreeNode.StartHighlight(); + } + else + { + _linkedTreeNode = Editor.Instance.Scene.GetActorNode(actor).TreeNode; + _linkedTreeNode.ExpandAllParents(); + Editor.Instance.Windows.SceneWin.SceneTreePanel.ScrollViewTo(_linkedTreeNode, true); + _linkedTreeNode.StartHighlight(); + } + return true; + } + } + + // Reset valid drag over if still true at this point + if (_hasValidDragOver) + _hasValidDragOver = false; + } return base.OnMouseUp(location, button); } @@ -326,6 +364,12 @@ namespace FlaxEditor.CustomEditors.Editors // Check if has object selected if (_value != null) { + if (_linkedTreeNode != null) + { + _linkedTreeNode.StopHighlight(); + _linkedTreeNode = null; + } + // Select object if (_value is Actor actor) Select(actor); @@ -491,6 +535,7 @@ namespace FlaxEditor.CustomEditors.Editors _value = null; _type = ScriptType.Null; _valueName = null; + _linkedTreeNode = null; base.OnDestroy(); } diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 9e01e8323..38e380550 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -228,7 +228,6 @@ namespace FlaxEditor.CustomEditors.Editors // Handle Sliding if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded)) { - // TODO: handle linked values Float3 average = Float3.Zero; for (int i = 0; i < Values.Count; i++) { @@ -251,12 +250,24 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < Values.Count; i++) { var v = Values[i]; - if (v is Vector3 asVector3) - v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); - else if (v is Float3 asFloat3) - v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); - else if (v is Double3 asDouble3) - v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); + if (LinkValues) + { + if (v is Vector3 asVector3) + v = asVector3 + new Vector3(newValue.X, newValue.Y, newValue.Z); + else if (v is Float3 asFloat3) + v = asFloat3 + new Float3(newValue.X, newValue.Y, newValue.Z); + else if (v is Double3 asDouble3) + v = asDouble3 + new Double3(newValue.X, newValue.Y, newValue.Z); + } + else + { + if (v is Vector3 asVector3) + v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); + else if (v is Float3 asFloat3) + v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); + else if (v is Double3 asDouble3) + v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); + } newObjects[i] = v; } @@ -267,16 +278,27 @@ namespace FlaxEditor.CustomEditors.Editors } else { - // TODO: handle linked values for (int i = 0; i < Values.Count; i++) { object v = Values[i]; - if (v is Vector3 asVector3) - v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z); - else if (v is Float3 asFloat3) - v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z); - else if (v is Double3 asDouble3) - v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z); + if (LinkValues) + { + if (v is Vector3 asVector3) + v = asVector3 + new Vector3(xValue, yValue, zValue); + else if (v is Float3 asFloat3) + v = asFloat3 + new Float3(xValue, yValue, zValue); + else if (v is Double3 asDouble3) + v = asDouble3 + new Double3(xValue, yValue, zValue); + } + else + { + if (v is Vector3 asVector3) + v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z); + else if (v is Float3 asFloat3) + v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z); + else if (v is Double3 asDouble3) + v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z); + } newObjects[i] = v; } diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs index 601d20bb9..904d5bc09 100644 --- a/Source/Editor/CustomEditors/Values/ValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs @@ -386,7 +386,7 @@ namespace FlaxEditor.CustomEditors if (instanceValues.Count != Count) throw new ArgumentException(); - if (value is IList l && l.Count == Count) + if (value is IList l && l.Count == Count && Count > 1) { for (int i = 0; i < Count; i++) { diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index 885f41535..e9ee68d88 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -550,7 +550,7 @@ int32 Editor::LoadProduct() } if (!FileSystem::FileExists(files[0])) { - Platform::Fatal(TEXT("Cannot opoen selected project file because it doesn't exist.")); + Platform::Fatal(TEXT("Cannot open selected project file because it doesn't exist.")); return -1; } projectPath = StringUtils::GetDirectoryName(files[0]); diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index c1ef7c362..7aca96c4e 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -59,7 +59,7 @@ namespace FlaxEditor.GUI /// /// Initializes a new instance of the class. /// - /// The assets types that this picker accepts. + /// The asset types that this picker accepts. /// The control location. public AssetPicker(ScriptType assetType, Float2 location) : base(location, new Float2(DefaultIconSize + ButtonsOffset + ButtonsSize, DefaultIconSize)) diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 9bb9d2038..90639cebc 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -23,10 +23,19 @@ namespace FlaxEditor.GUI.Tree /// public const float DefaultNodeOffsetY = 0; + private const float _targetHighlightScale = 1.25f; + private const float _highlightScaleAnimDuration = 0.85f; + private Tree _tree; private bool _opened, _canChangeOrder; private float _animationProgress, _cachedHeight; + private bool _isHightlighted; + private float _targetHighlightTimeSec; + private float _currentHighlightTimeSec; + // Used to prevent showing highlight on double mouse click + private float _debounceHighlightTime; + private float _highlightScale; private bool _mouseOverArrow, _mouseOverHeader; private float _xOffset, _textWidth; private float _headerHeight = 16.0f; @@ -605,9 +614,47 @@ namespace FlaxEditor.GUI.Tree } } + /// + /// Adds a box around the text to highlight the node. + /// + /// The duration of the highlight in seconds. + public void StartHighlight(float durationSec = 3) + { + _isHightlighted = true; + _targetHighlightTimeSec = durationSec; + _currentHighlightTimeSec = 0; + _debounceHighlightTime = 0; + _highlightScale = 2f; + } + + /// + /// Stops any current highlight. + /// + public void StopHighlight() + { + _isHightlighted = false; + _targetHighlightTimeSec = 0; + _currentHighlightTimeSec = 0; + _debounceHighlightTime = 0; + } + /// public override void Update(float deltaTime) { + // Highlight animations + if (_isHightlighted) + { + _debounceHighlightTime += deltaTime; + _currentHighlightTimeSec += deltaTime; + + // In the first second, animate the highlight to shrink into it's resting position + if (_currentHighlightTimeSec < _highlightScaleAnimDuration) + _highlightScale = Mathf.Lerp(_highlightScale, _targetHighlightScale, _currentHighlightTimeSec); + + if (_currentHighlightTimeSec >= _targetHighlightTimeSec) + _isHightlighted = false; + } + // Drop/down animation if (_animationProgress < 1.0f) { @@ -676,6 +723,18 @@ namespace FlaxEditor.GUI.Tree textRect.Width -= 18.0f; } + float textWidth = TextFont.GetFont().MeasureText(_text).X; + Rectangle trueTextRect = textRect; + trueTextRect.Width = textWidth; + trueTextRect.Scale(_highlightScale); + + if (_isHightlighted && _debounceHighlightTime > 0.1f) + { + Color highlightBackgroundColor = Editor.Instance.Options.Options.Visual.HighlightColor; + highlightBackgroundColor = highlightBackgroundColor.AlphaMultiplied(0.3f); + Render2D.FillRectangle(trueTextRect, highlightBackgroundColor); + } + // Draw text Color textColor = CacheTextColor(); Render2D.DrawText(TextFont.GetFont(), _text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); @@ -730,6 +789,12 @@ namespace FlaxEditor.GUI.Tree } } + if (_isHightlighted && _debounceHighlightTime > 0.1f) + { + // Draw highlights + Render2D.DrawRectangle(trueTextRect, Editor.Instance.Options.Options.Visual.HighlightColor, 3); + } + // Base if (_opened) { diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs index fe2e7b9e3..77bfff4b4 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs @@ -1,6 +1,9 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using FlaxEditor.Options; +using FlaxEditor.SceneGraph; using FlaxEngine; +using System; namespace FlaxEditor.Gizmo { @@ -18,8 +21,13 @@ namespace FlaxEditor.Gizmo private MaterialInstance _materialAxisY; private MaterialInstance _materialAxisZ; private MaterialInstance _materialAxisFocus; + private MaterialInstance _materialAxisLocked; private MaterialBase _materialSphere; + // Material Parameter Names + const String _brightnessParamName = "Brightness"; + const String _opacityParamName = "Opacity"; + private void InitDrawing() { // Axis Models @@ -34,6 +42,7 @@ namespace FlaxEditor.Gizmo _materialAxisY = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialAxisY"); _materialAxisZ = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialAxisZ"); _materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialAxisFocus"); + _materialAxisLocked = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialAxisLocked"); _materialSphere = FlaxEngine.Content.LoadAsyncInternal("Editor/Gizmo/MaterialSphere"); // Ensure that every asset was loaded @@ -50,6 +59,25 @@ namespace FlaxEditor.Gizmo { Platform.Fatal("Failed to load transform gizmo resources."); } + + // Setup editor options + OnEditorOptionsChanged(Editor.Instance.Options.Options); + Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; + } + + private void OnEditorOptionsChanged(EditorOptions options) + { + float brightness = options.Visual.TransformGizmoBrightness; + _materialAxisX.SetParameterValue(_brightnessParamName, brightness); + _materialAxisY.SetParameterValue(_brightnessParamName, brightness); + _materialAxisZ.SetParameterValue(_brightnessParamName, brightness); + _materialAxisLocked.SetParameterValue(_brightnessParamName, brightness); + + float opacity = options.Visual.TransformGizmoOpacity; + _materialAxisX.SetParameterValue(_opacityParamName, opacity); + _materialAxisY.SetParameterValue(_opacityParamName, opacity); + _materialAxisZ.SetParameterValue(_opacityParamName, opacity); + _materialAxisLocked.SetParameterValue(_opacityParamName, opacity); } /// @@ -60,6 +88,21 @@ namespace FlaxEditor.Gizmo if (!_modelCube || !_modelCube.IsLoaded) return; + // Find out if any of the selected objects can not be moved + bool gizmoLocked = false; + if (Editor.Instance.StateMachine.IsPlayMode) + { + for (int i = 0; i < SelectionCount; i++) + { + var obj = GetSelectedObject(i); + if (obj.CanTransform == false) + { + gizmoLocked = true; + break; + } + } + } + // As all axisMesh have the same pivot, add a little offset to the x axisMesh, this way SortDrawCalls is able to sort the draw order // https://github.com/FlaxEngine/FlaxEngine/issues/680 @@ -92,32 +135,38 @@ namespace FlaxEditor.Gizmo // X axis Matrix.RotationY(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - transAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3); + MaterialInstance xAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX); + transAxisMesh.Draw(ref renderContext, xAxisMaterialTransform, ref m3); // Y axis Matrix.RotationX(Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - transAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3); + MaterialInstance yAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY); + transAxisMesh.Draw(ref renderContext, yAxisMaterialTransform, ref m3); // Z axis Matrix.RotationX(Mathf.Pi, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - transAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); + MaterialInstance zAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ); + transAxisMesh.Draw(ref renderContext, zAxisMaterialTransform, ref m3); // XY plane m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f)); Matrix.Multiply(ref m2, ref m1, out m3); - cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3); + MaterialInstance xyPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX); + cubeMesh.Draw(ref renderContext, xyPlaneMaterialTransform, ref m3); // ZX plane m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale)); Matrix.Multiply(ref m2, ref m1, out m3); - cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3); + MaterialInstance zxPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisY); + cubeMesh.Draw(ref renderContext, zxPlaneMaterialTransform, ref m3); // YZ plane m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale)); Matrix.Multiply(ref m2, ref m1, out m3); - cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3); + MaterialInstance yzPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisZ); + cubeMesh.Draw(ref renderContext, yzPlaneMaterialTransform, ref m3); // Center sphere Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); @@ -136,15 +185,18 @@ namespace FlaxEditor.Gizmo // X axis Matrix.RotationZ(Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - rotationAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3); + MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX); + rotationAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3); // Y axis - rotationAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1); + MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY); + rotationAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m1); // Z axis Matrix.RotationX(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - rotationAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); + MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ); + rotationAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3); // Center box Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); @@ -163,32 +215,38 @@ namespace FlaxEditor.Gizmo // X axis Matrix.RotationY(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref mx1, out m3); - scaleAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3); + MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX); + scaleAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3); // Y axis Matrix.RotationX(Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - scaleAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3); + MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY); + scaleAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m3); // Z axis Matrix.RotationX(Mathf.Pi, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - scaleAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3); + MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ); + scaleAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3); // XY plane m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f)); Matrix.Multiply(ref m2, ref m1, out m3); - cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3); + MaterialInstance xyPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX); + cubeMesh.Draw(ref renderContext, xyPlaneMaterialScale, ref m3); // ZX plane m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale)); Matrix.Multiply(ref m2, ref m1, out m3); - cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3); + MaterialInstance zxPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ); + cubeMesh.Draw(ref renderContext, zxPlaneMaterialScale, ref m3); // YZ plane m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale)); Matrix.Multiply(ref m2, ref m1, out m3); - cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3); + MaterialInstance yzPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY); + cubeMesh.Draw(ref renderContext, yzPlaneMaterialScale, ref m3); // Center box Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Selection.cs b/Source/Editor/Gizmo/TransformGizmoBase.Selection.cs index 443a107e5..deeff8cb9 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Selection.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Selection.cs @@ -169,12 +169,12 @@ namespace FlaxEditor.Gizmo closestIntersection = intersection; } - /*// Center - if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection < closestIntersection) + // Center + if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection > closestIntersection) { _activeAxis = Axis.Center; closestIntersection = intersection; - }*/ + } break; } diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs index bbd13f688..5b54a7c79 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs @@ -20,7 +20,7 @@ namespace FlaxEditor.Gizmo /// /// Offset to move axis away from center /// - private const float AxisOffset = 0.8f; + private const float AxisOffset = 1.2f; /// /// How thick the axis should be diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 8fe91c252..cab0ab462 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -501,7 +501,7 @@ namespace FlaxEditor.Gizmo _scaleDelta = Vector3.Zero; if (ActiveAxis == Axis.Center) - scaleDelta = new Vector3(scaleDelta.AvgValue); + scaleDelta = new Vector3(scaleDelta.ValuesSum); } // Apply transformation (but to the parents, not whole selection pool) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 3fa574e1b..aba177b7d 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using System.Linq; using FlaxEditor.Gizmo; @@ -647,7 +648,36 @@ namespace FlaxEditor private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, float scale, Float2 resizeAxis, CursorType cursor) { var style = Style.Current; - var rect = new Rectangle((pos + resizeAxis * 10 * scale) - size * 0.5f, size); + var control = uiControl.Control; + var rotation = control.Rotation; + var rotationInRadians = rotation * Mathf.DegreesToRadians; + var rect = new Rectangle( + (pos + + new Float2(resizeAxis.X * Mathf.Cos(rotationInRadians) - resizeAxis.Y * Mathf.Sin(rotationInRadians), + resizeAxis.Y * Mathf.Cos(rotationInRadians) + resizeAxis.X * Mathf.Sin(rotationInRadians)) * 10 * scale) - size * 0.5f, + size); + + // Find more correct cursor at different angles + var unwindRotation = Mathf.UnwindDegrees(rotation); + if (unwindRotation is (>= 45 and < 135) or (> -135 and <= -45) ) + { + switch (cursor) + { + case CursorType.SizeNESW: + cursor = CursorType.SizeNWSE; + break; + case CursorType.SizeNS: + cursor = CursorType.SizeWE; + break; + case CursorType.SizeNWSE: + cursor = CursorType.SizeNESW; + break; + case CursorType.SizeWE: + cursor = CursorType.SizeNS; + break; + default: break; + } + } if (rect.Contains(ref mousePos)) { Render2D.FillRectangle(rect, style.Foreground); diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index c42bd9d86..4de7b593b 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -1136,9 +1136,15 @@ namespace FlaxEditor.Modules Proxy.Add(new SceneAnimationProxy()); Proxy.Add(new CSharpScriptProxy()); Proxy.Add(new CSharpEmptyProxy()); + Proxy.Add(new CSharpEmptyClassProxy()); + Proxy.Add(new CSharpEmptyStructProxy()); + Proxy.Add(new CSharpEmptyInterfaceProxy()); + Proxy.Add(new CSharpActorProxy()); + Proxy.Add(new CSharpGamePluginProxy()); Proxy.Add(new CppAssetProxy()); Proxy.Add(new CppStaticClassProxy()); Proxy.Add(new CppScriptProxy()); + Proxy.Add(new CppActorProxy()); Proxy.Add(new SceneProxy()); Proxy.Add(new PrefabProxy()); Proxy.Add(new IESProfileProxy()); diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs index bb9df0950..3906fe424 100644 --- a/Source/Editor/Modules/SimulationModule.cs +++ b/Source/Editor/Modules/SimulationModule.cs @@ -99,6 +99,10 @@ namespace FlaxEditor.Modules { if (Editor.StateMachine.IsEditMode) { + // Show Game window if hidden + if (Editor.Windows.GameWin.IsHidden) + Editor.Windows.GameWin.Show(); + Editor.Log("[PlayMode] Start"); if (Editor.Options.Options.General.AutoReloadScriptsOnMainWindowFocus) @@ -131,6 +135,10 @@ namespace FlaxEditor.Modules if (!Editor.StateMachine.IsEditMode) return; + // Show Game window if hidden + if (Editor.Windows.GameWin.IsHidden) + Editor.Windows.GameWin.Show(); + var firstScene = Content.Settings.GameSettings.Load().FirstScene; if (firstScene == Guid.Empty) { diff --git a/Source/Editor/Options/Editor.cs b/Source/Editor/Options/Editor.cs index b4f2de6e4..a1836fd6d 100644 --- a/Source/Editor/Options/Editor.cs +++ b/Source/Editor/Options/Editor.cs @@ -1,7 +1,9 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System.Text; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Editors; +using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; @@ -31,11 +33,33 @@ namespace FlaxEditor.Options private void OnResetButtonClicked() { - var obj = new T(); - var str = JsonSerializer.Serialize(obj); - JsonSerializer.Deserialize(Values[0], str); - SetValue(Values[0]); - Refresh(); + var editorClassName = typeof(T).Name; + + var editorName = new StringBuilder(); + editorName.Append(editorClassName[0]); + for (var i = 1; i < editorClassName.Length; i++) + { + // Whenever there is an uppercase letter, add a space to make it more pretty for the end user + if (char.IsUpper(editorClassName[i])) + { + editorName.Append(' '); + } + editorName.Append(editorClassName[i]); + } + + var result = MessageBox.Show($"Are you sure you want to reset \"{editorName}\" to default values?", + "Reset values?", + MessageBoxButtons.YesNo + ); + + if (result == DialogResult.Yes || result == DialogResult.OK) + { + var obj = new T(); + var str = JsonSerializer.Serialize(obj); + JsonSerializer.Deserialize(Values[0], str); + SetValue(Values[0]); + Refresh(); + } } } } diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 1654df920..4cd76adc6 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -258,6 +258,13 @@ namespace FlaxEditor.Options private TextAlignment _tooltipTextAlignment = TextAlignment.Center; + /// + /// Whether to scroll to the script when a script is added to an actor. + /// + [DefaultValue(true)] + [EditorDisplay("Interface"), EditorOrder(322)] + public bool ScrollToScriptOnAdd { get; set; } = true; + /// /// Gets or sets the timestamps prefix mode for output log messages. /// diff --git a/Source/Editor/Options/VisualOptions.cs b/Source/Editor/Options/VisualOptions.cs index 4e94538e2..cb5a0a89d 100644 --- a/Source/Editor/Options/VisualOptions.cs +++ b/Source/Editor/Options/VisualOptions.cs @@ -22,37 +22,51 @@ namespace FlaxEditor.Options /// Gets or sets the first outline color. /// [DefaultValue(typeof(Color), "0.039,0.827,0.156")] - [EditorDisplay("Gizmo"), EditorOrder(100), Tooltip("The first color of the selection outline gradient.")] + [EditorDisplay("Transform Gizmo"), EditorOrder(100), Tooltip("The first color of the selection outline gradient.")] public Color SelectionOutlineColor0 { get; set; } = new Color(0.039f, 0.827f, 0.156f); /// /// Gets or sets the second outline color. /// [DefaultValue(typeof(Color), "0.019,0.615,0.101")] - [EditorDisplay("Gizmo"), EditorOrder(101), Tooltip("The second color of the selection outline gradient.")] + [EditorDisplay("Transform Gizmo"), EditorOrder(101), Tooltip("The second color of the selection outline gradient.")] public Color SelectionOutlineColor1 { get; set; } = new Color(0.019f, 0.615f, 0.101f); /// /// Gets or sets the selection outline size for UI controls. /// [DefaultValue(2.0f)] - [EditorDisplay("Gizmo", "UI Control Outline Size"), EditorOrder(100), Tooltip("The size of the selection outline for UI controls.")] + [EditorDisplay("UI Gizmo", "UI Control Outline Size"), EditorOrder(103), Tooltip("The size of the selection outline for UI controls.")] public float UISelectionOutlineSize { get; set; } = 2.0f; /// /// Gets or sets the transform gizmo size. /// [DefaultValue(1.0f), Limit(0.01f, 100.0f, 0.01f)] - [EditorDisplay("Gizmo"), EditorOrder(110), Tooltip("The transform gizmo size.")] + [EditorDisplay("Transform Gizmo"), EditorOrder(110), Tooltip("The transform gizmo size.")] public float GizmoSize { get; set; } = 1.0f; /// /// Gets or sets the color used to highlight selected meshes and CSG surfaces. /// [DefaultValue(typeof(Color), "0.0,0.533,1.0,1.0")] - [EditorDisplay("Gizmo"), EditorOrder(200), Tooltip("The color used to highlight selected meshes and CSG surfaces.")] + [EditorDisplay("Transform Gizmo"), EditorOrder(200), Tooltip("The color used to highlight selected meshes and CSG surfaces.")] public Color HighlightColor { get; set; } = new Color(0.0f, 0.533f, 1.0f, 1.0f); + /// + /// Gets or set a value indicating how bright the transform gizmo is. Value over 1 will result in the gizmo emitting light. + /// + [DefaultValue(1f), Range(0f, 5f)] + [EditorDisplay("Transform Gizmo", "Gizmo Brightness"), EditorOrder(210)] + public float TransformGizmoBrightness { get; set; } = 1f; + + /// + /// Gets or set a value indicating the opacity of the transform gizmo. + /// + [DefaultValue(1f), Range(0f, 1f)] + [EditorDisplay("Transform Gizmo", "Gizmo Opacity"), EditorOrder(211)] + public float TransformGizmoOpacity { get; set; } = 1f; + /// /// Gets or sets a value indicating whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance. /// diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index f7ad55a42..b6b69950d 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -596,11 +596,17 @@ namespace FlaxEditor.SceneGraph.GUI { bool worldPositionsStays = Root.GetKey(KeyboardKeys.Control) == false; var objects = new SceneObject[_dragActors.Objects.Count]; + var treeNodes = new TreeNode[_dragActors.Objects.Count]; for (int i = 0; i < objects.Length; i++) + { objects[i] = _dragActors.Objects[i].Actor; + treeNodes[i] = _dragActors.Objects[i].TreeNode; + } var action = new ParentActorsAction(objects, newParent, newOrder, worldPositionsStays); ActorNode.Root.Undo?.AddAction(action); action.Do(); + ParentTree.Focus(); + ParentTree.Select(treeNodes.ToList()); result = DragDropEffect.Move; } // Drag scripts diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 329c55452..dd4b1e40b 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -1216,13 +1216,16 @@ namespace FlaxEditor.Surface.Archetypes // Highlight selected blend point var style = Style.Current; var selectedIndex = _selectedAnimation.SelectedIndex; - if (selectedIndex != -1 && (ContainsFocus || IsMouseOver)) + if (selectedIndex != -1 && selectedIndex < _editor.BlendPoints.Count && (ContainsFocus || IsMouseOver)) { var point = _editor.BlendPoints[selectedIndex]; - var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected; - Render2D.PushTint(ref highlightColor); - Render2D.DrawTriangles(_selectedTriangles, _selectedColors); - Render2D.PopTint(); + if (point != null) + { + var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected; + Render2D.PushTint(ref highlightColor); + Render2D.DrawTriangles(_selectedTriangles, _selectedColors); + Render2D.PopTint(); + } } } diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index e2b330616..c4ff4a03c 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -1502,9 +1502,10 @@ namespace FlaxEditor.Surface.Archetypes { data = new object[] { - filterText.Substring(2), - new Color(1.0f, 1.0f, 1.0f, 0.2f), - new Float2(400.0f, 400.0f), + filterText.Substring(2), // Title + new Color(1.0f, 1.0f, 1.0f, 0.2f), // Color + new Float2(400.0f, 400.0f), // Size + -1, // Order }; return true; } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 94e7c21ba..564856040 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -172,10 +172,22 @@ namespace FlaxEditor.Surface /// public override void Update(float deltaTime) { - if (_isRenaming && (!_renameTextBox.IsFocused || !RootWindow.IsFocused)) + if (_isRenaming) { - Rename(_renameTextBox.Text); - StopRenaming(); + // Stop renaming when clicking anywhere else + if (!_renameTextBox.IsFocused || !RootWindow.IsFocused) + { + Rename(_renameTextBox.Text); + StopRenaming(); + } + } + else + { + // Rename on F2 + if (IsSelected && Editor.Instance.Options.Options.Input.Rename.Process(this)) + { + StartRenaming(); + } } base.Update(deltaTime); @@ -417,6 +429,7 @@ namespace FlaxEditor.Surface base.OnShowSecondaryContextMenu(menu, location); menu.AddSeparator(); + menu.AddButton("Rename", StartRenaming); ContextMenuChildMenu cmOrder = menu.AddChildMenu("Order"); { cmOrder.ContextMenu.AddButton("Bring Forward", () => diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 6ae10171f..d88f2cdab 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -383,6 +383,40 @@ namespace FlaxEditor.Utilities File.Delete(file); } + /// + /// Creates an Import path ui that show the asset import path and adds a button to show the folder in the file system. + /// + /// The parent layout container. + /// The asset item to get the import path of. + public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, Content.BinaryAssetItem assetItem) + { + assetItem.GetImportPath(out var path); + CreateImportPathUI(parentLayout, path); + } + + /// + /// Creates an Import path ui that show the import path and adds a button to show the folder in the file system. + /// + /// The parent layout container. + /// The import path. + /// Whether to use an initial layout space of 5 for separation. + public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, string path, bool useInitialSpacing = true) + { + if (!string.IsNullOrEmpty(path)) + { + if (useInitialSpacing) + parentLayout.Space(5); + parentLayout.Label("Import Path:").Label.TooltipText = "Source asset path (can be relative or absolute to the project)"; + var textBox = parentLayout.TextBox().TextBox; + textBox.TooltipText = "Path is not editable here."; + textBox.IsReadOnly = true; + textBox.Text = path; + parentLayout.Space(2); + var button = parentLayout.Button(Constants.ShowInExplorer).Button; + button.Clicked += () => FileSystem.ShowFileExplorer(Path.GetDirectoryName(path)); + } + } + /// /// Copies the directory. Supports subdirectories copy with files override option. /// diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index 734b44af7..058bf3622 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; +using System.IO; using System.Reflection; using System.Xml; using FlaxEditor.Content; @@ -207,8 +208,11 @@ namespace FlaxEditor.Windows.Assets var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings }; group.Object(importSettingsValues); + // Creates the import path UI + Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem); + layout.Space(5); - var reimportButton = group.Button("Reimport"); + var reimportButton = layout.Button("Reimport"); reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); } } diff --git a/Source/Editor/Windows/Assets/AudioClipWindow.cs b/Source/Editor/Windows/Assets/AudioClipWindow.cs index 998073858..35072620b 100644 --- a/Source/Editor/Windows/Assets/AudioClipWindow.cs +++ b/Source/Editor/Windows/Assets/AudioClipWindow.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System.IO; using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.Import; @@ -100,7 +101,10 @@ namespace FlaxEditor.Windows.Assets base.Initialize(layout); - layout.Space(10); + // Creates the import path UI + Utilities.Utils.CreateImportPathUI(layout, window.Item as BinaryAssetItem); + + layout.Space(5); var reimportButton = layout.Button("Reimport"); reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); } diff --git a/Source/Editor/Windows/Assets/CubeTextureWindow.cs b/Source/Editor/Windows/Assets/CubeTextureWindow.cs index 56585bba8..960d806d2 100644 --- a/Source/Editor/Windows/Assets/CubeTextureWindow.cs +++ b/Source/Editor/Windows/Assets/CubeTextureWindow.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System.IO; using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.Import; @@ -53,7 +54,10 @@ namespace FlaxEditor.Windows.Assets base.Initialize(layout); - layout.Space(10); + // Creates the import path UI + Utilities.Utils.CreateImportPathUI(layout, window.Item as BinaryAssetItem); + + layout.Space(5); var reimportButton = layout.Button("Reimport"); reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); } diff --git a/Source/Editor/Windows/Assets/ModelBaseWindow.cs b/Source/Editor/Windows/Assets/ModelBaseWindow.cs index 7acdc10cb..af9b4ea14 100644 --- a/Source/Editor/Windows/Assets/ModelBaseWindow.cs +++ b/Source/Editor/Windows/Assets/ModelBaseWindow.cs @@ -751,18 +751,18 @@ namespace FlaxEditor.Windows.Assets if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset)) return; - // Import Settings - { - var group = layout.Group("Import Settings"); + var group = layout.Group("Import Settings"); - var importSettingsField = typeof(ImportPropertiesProxyBase).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance); - var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings }; - group.Object(importSettingsValues); + var importSettingsField = typeof(ImportPropertiesProxyBase).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance); + var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings }; + group.Object(importSettingsValues); - layout.Space(5); - var reimportButton = group.Button("Reimport"); - reimportButton.Button.Clicked += () => ((ImportPropertiesProxyBase)Values[0]).Reimport(); - } + // Creates the import path UI + Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem); + + layout.Space(5); + var reimportButton = group.Button("Reimport"); + reimportButton.Button.Clicked += () => ((ImportPropertiesProxyBase)Values[0]).Reimport(); } } } diff --git a/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs b/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs index 9601555d2..167a86f41 100644 --- a/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs +++ b/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System.IO; using System.Linq; using System.Xml; using FlaxEditor.Content; @@ -110,9 +111,19 @@ namespace FlaxEditor.Windows.Assets { public override void Initialize(LayoutElementsContainer layout) { + var proxy = (PropertiesProxy)Values[0]; + if (proxy._window == null) + { + layout.Label("Loading...", TextAlignment.Center); + return; + } + base.Initialize(layout); + + // Creates the import path UI + Utilities.Utils.CreateImportPathUI(layout, proxy._window.Item as BinaryAssetItem); - layout.Space(10); + layout.Space(5); var reimportButton = layout.Button("Reimport"); reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); } diff --git a/Source/Editor/Windows/Assets/TextureWindow.cs b/Source/Editor/Windows/Assets/TextureWindow.cs index 8f673783a..8084847e4 100644 --- a/Source/Editor/Windows/Assets/TextureWindow.cs +++ b/Source/Editor/Windows/Assets/TextureWindow.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System.IO; using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.Import; @@ -131,11 +132,21 @@ namespace FlaxEditor.Windows.Assets { public override void Initialize(LayoutElementsContainer layout) { + var proxy = (ImportPropertiesProxy)Values[0]; + if (proxy._window == null) + { + layout.Label("Loading...", TextAlignment.Center); + return; + } + // Import settings base.Initialize(layout); + // Creates the import path UI + Utilities.Utils.CreateImportPathUI(layout, proxy._window.Item as BinaryAssetItem); + // Reimport - layout.Space(10); + layout.Space(5); var reimportButton = layout.Button("Reimport"); reimportButton.Button.Clicked += () => ((ImportPropertiesProxy)Values[0]).Reimport(); } diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index 4dd1b8eac..7e3444bf0 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -176,7 +176,7 @@ namespace FlaxEditor.Windows.Profiler private string FormatCellBytes(object x) { - return Utilities.Utils.FormatBytesCount((ulong)x); + return Utilities.Utils.FormatBytesCount(Convert.ToUInt64(x)); } /// diff --git a/Source/Engine/Content/Assets/MaterialBase.cpp b/Source/Engine/Content/Assets/MaterialBase.cpp index d537a5746..9e980687a 100644 --- a/Source/Engine/Content/Assets/MaterialBase.cpp +++ b/Source/Engine/Content/Assets/MaterialBase.cpp @@ -35,7 +35,7 @@ void MaterialBase::SetParameterValue(const StringView& name, const Variant& valu } else if (warnIfMissing) { - LOG(Warning, "Missing material parameter '{0}' in material {1}", String(name), ToString()); + LOG(Warning, "Missing material parameter '{0}' in material {1}", name, ToString()); } } diff --git a/Source/Engine/Content/Assets/MaterialBase.h b/Source/Engine/Content/Assets/MaterialBase.h index 81316b106..ec5681e27 100644 --- a/Source/Engine/Content/Assets/MaterialBase.h +++ b/Source/Engine/Content/Assets/MaterialBase.h @@ -65,7 +65,7 @@ public: /// /// The parameter name. /// The value to set. - /// True if warn if parameter is missing, otherwise will do nothing. + /// True to warn if parameter is missing, otherwise will do nothing. API_FUNCTION() void SetParameterValue(const StringView& name, const Variant& value, bool warnIfMissing = true); /// diff --git a/Source/Engine/Core/Log.cpp b/Source/Engine/Core/Log.cpp index 20689f628..a9d3268dc 100644 --- a/Source/Engine/Core/Log.cpp +++ b/Source/Engine/Core/Log.cpp @@ -25,6 +25,7 @@ namespace bool IsWindowsSingleNewLineChar = false; #endif int LogTotalErrorsCnt = 0; + int32 LogTotalWriteSize = 0; FileWriteStream* LogFile = nullptr; CriticalSection LogLocker; DateTime LogStartTime; @@ -149,10 +150,17 @@ void Log::Logger::Write(const StringView& msg) Platform::Log(msg); // Write message to log file - if (LogAfterInit) + constexpr int32 LogMaxWriteSize = 1 * 1024 * 1024; // 1GB + if (LogAfterInit && LogTotalWriteSize < LogMaxWriteSize) { + LogTotalWriteSize += length; LogFile->WriteBytes(ptr, length * sizeof(Char)); LogFile->WriteBytes(TEXT(PLATFORM_LINE_TERMINATOR), (ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1) * sizeof(Char)); + if (LogTotalWriteSize >= LogMaxWriteSize) + { + StringView endMessage(TEXT("Trimming log file.\n\n")); + LogFile->WriteBytes(endMessage.Get(), endMessage.Length() * sizeof(Char)); + } #if LOG_ENABLE_AUTO_FLUSH LogFile->Flush(); #endif diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 9035ef71f..38a494650 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -3453,6 +3453,16 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) default: return false; } + case VariantType::Null: + switch (to.Type) + { + case VariantType::Asset: + case VariantType::ManagedObject: + case VariantType::Object: + return true; + default: + return false; + } default: return false; } @@ -3963,6 +3973,23 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) return Variant((double)v.AsEnum); } break; + case VariantType::Null: + switch (to.Type) + { + case VariantType::Asset: + return Variant((Asset*)nullptr); + case VariantType::Object: + return Variant((ScriptingObject*)nullptr); + case VariantType::ManagedObject: + { + Variant result; + result.SetType(VariantType(VariantType::ManagedObject)); + result.MANAGED_GC_HANDLE = 0; + return result; + } + default: + return false; + } default: ; } LOG(Error, "Cannot cast Variant from {0} to {1}", v.Type, to); diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index 607d70473..78337dcf0 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -214,9 +214,11 @@ void MaterialParameter::SetValue(const Variant& value) break; case VariantType::Object: _asAsset = Cast(value.AsObject); + invalidType = _asAsset == nullptr && value.AsObject != nullptr; break; case VariantType::Asset: _asAsset = Cast(value.AsAsset); + invalidType = _asAsset == nullptr && value.AsAsset != nullptr; break; default: invalidType = true; @@ -239,6 +241,7 @@ void MaterialParameter::SetValue(const Variant& value) break; case VariantType::Object: _asGPUTexture = Cast(value.AsObject); + invalidType = _asGPUTexture == nullptr && value.AsObject != nullptr; break; default: invalidType = true; @@ -258,9 +261,11 @@ void MaterialParameter::SetValue(const Variant& value) break; case VariantType::Object: _asAsset = Cast(value.AsObject); + invalidType = _asAsset == nullptr && value.AsObject != nullptr; break; case VariantType::Asset: _asAsset = Cast(value.AsAsset); + invalidType = _asAsset == nullptr && value.AsAsset != nullptr; break; default: invalidType = true; @@ -273,7 +278,7 @@ void MaterialParameter::SetValue(const Variant& value) } if (invalidType) { - LOG(Error, "Invalid material parameter value type {0} to set (param type: {1})", value.Type, ScriptingEnum::ToString(_type)); + LOG(Error, "Invalid material parameter value '{}' of type '{}' to set (expected type: {})", value.ToString(), value.Type, ScriptingEnum::ToString(_type)); } } diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 73fd44d27..373339e2d 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -293,7 +293,12 @@ void AnimatedModel::SetParameterValue(const StringView& name, const Variant& val { if (param.Name == name) { - param.Value = value; + if (param.Value.Type == value.Type) + param.Value = value; + else if (Variant::CanCast(value, param.Value.Type)) + param.Value = Variant::Cast(value, param.Value.Type); + else + LOG(Warning, "Animation Graph parameter '{0}' in AnimatedModel {1} is type '{2}' and not type '{3}'.", name, ToString(), param.Value.Type, value.Type); return; } } diff --git a/Source/Engine/Level/Scripts/ModelPrefab.h b/Source/Engine/Level/Scripts/ModelPrefab.h index 0adf0edea..fd6b66097 100644 --- a/Source/Engine/Level/Scripts/ModelPrefab.h +++ b/Source/Engine/Level/Scripts/ModelPrefab.h @@ -19,7 +19,7 @@ API_CLASS(Attributes="HideInEditor") class FLAXENGINE_API ModelPrefab : public S /// /// Source model file path (absolute or relative to the project). /// - API_FIELD(Attributes="ReadOnly") String ImportPath; + API_FIELD(Attributes="ReadOnly, HideInEditor") String ImportPath; /// /// Model file import settings. diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index c42270a05..9a035f1ea 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -108,6 +108,12 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\")") TextureQuality TexturesQuality = TextureQuality::ASTC_Medium; + /// + /// Whether to build Android App Bundle (aab) side by side with apk. + /// + API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\", \"Build .aab\")") + bool BuildAAB = true; + /// /// Custom icon texture to use for the application (overrides the default one). /// diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index fd746225a..5e0b3f36d 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1386,22 +1386,26 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex FORCE_INLINE bool NeedAlphaWithTint(const Color& color) { - return (color.A * TintLayersStack.Peek().A) < 1.0f; + const float tint = TintLayersStack.Peek().A; + return color.A * tint < 1.0f; } FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2) { - return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f; + const float tint = TintLayersStack.Peek().A; + return color1.A * tint < 1.0f || color2.A * tint < 1.0f; } FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2, const Color& color3) { - return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f || (color3.A * TintLayersStack.Peek().A) < 1.0f; + const float tint = TintLayersStack.Peek().A; + return color1.A * tint < 1.0f || color2.A * tint < 1.0f || color3.A * tint < 1.0f; } FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2, const Color& color3, const Color& color4) { - return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f || (color3.A * TintLayersStack.Peek().A) < 1.0f || (color4.A * TintLayersStack.Peek().A) < 1.0f; + const float tint = TintLayersStack.Peek().A; + return color1.A * tint < 1.0f || color2.A * tint < 1.0f || color3.A * tint < 1.0f || color4.A * tint < 1.0f; } void Render2D::FillRectangle(const Rectangle& rect, const Color& color) diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 1b7131d9b..06b48d3c6 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -623,10 +623,6 @@ void ShadowsPass::SetupRenderContext(RenderContext& renderContext, RenderContext void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderLightData& light, ShadowAtlasLight& atlasLight) { - // Initialize frame-data - atlasLight.ContextIndex = 0; - atlasLight.ContextCount = 0; - // Copy light properties atlasLight.Sharpness = light.ShadowsSharpness; atlasLight.Fade = light.ShadowsStrength; @@ -1299,6 +1295,11 @@ RETRY_ATLAS_SETUP: for (RenderLightData* light : shadowedLights) { auto& atlasLight = shadows.Lights[light->ID]; + + // Reset frame-data + atlasLight.ContextIndex = 0; + atlasLight.ContextCount = 0; + if (atlasLight.Tiles[0].RectTile && atlasLight.Tiles[0].RectTile->Width == atlasLight.Resolution) { // Invalidate cache when whole atlas will be cleared diff --git a/Source/Engine/ShadersCompilation/Parser/ShaderProcessing.cpp b/Source/Engine/ShadersCompilation/Parser/ShaderProcessing.cpp index fea60c8e7..e65961038 100644 --- a/Source/Engine/ShadersCompilation/Parser/ShaderProcessing.cpp +++ b/Source/Engine/ShadersCompilation/Parser/ShaderProcessing.cpp @@ -66,10 +66,6 @@ void ShaderProcessing::Parser::init() bool ShaderProcessing::Parser::process() { const Token defineToken("#define"); - const Separator singleLineCommentSeparator('/', '/'); - const Separator multiLineCommentSeparator('/', '*'); - - // TODO: split parsing into two phrases: comments preprocessing and parsing // Read whole source code Token token; @@ -77,36 +73,8 @@ bool ShaderProcessing::Parser::process() { text.ReadToken(&token); - // Single line comment - if (token.Separator == singleLineCommentSeparator) - { - // Read whole line - text.ReadLine(); - } - // Multi line comment - else if (token.Separator == multiLineCommentSeparator) - { - // Read tokens until end sequence - char prev = ' '; - char c; - while (text.CanRead()) - { - c = text.ReadChar(); - if (prev == '*' && c == '/') - { - break; - } - prev = c; - } - - // Check if comment is valid (has end before file end) - if (!text.CanRead()) - { - OnWarning(TEXT("Missing multiline comment ending")); - } - } - // Preprocessor definition - else if (token == defineToken) + // Preprocessor definition + if (token == defineToken) { // Skip text.ReadLine(); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index df6a5e90f..0ffdc5ce1 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -717,6 +717,8 @@ bool ModelTool::ImportDataAssimp(const String& path, ModelData& data, Options& o flags |= aiProcess_FixInfacingNormals | aiProcess_GenSmoothNormals; if (options.CalculateTangents) flags |= aiProcess_CalcTangentSpace; + if (options.ReverseWindingOrder) + flags &= ~aiProcess_FlipWindingOrder; if (options.OptimizeMeshes) flags |= aiProcess_OptimizeMeshes | aiProcess_SplitLargeMeshes | aiProcess_ImproveCacheLocality; if (options.MergeMeshes) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.AutodeskFbxSdk.cpp b/Source/Engine/Tools/ModelTool/ModelTool.AutodeskFbxSdk.cpp index 03eda839c..b226aea44 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.AutodeskFbxSdk.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.AutodeskFbxSdk.cpp @@ -385,6 +385,19 @@ bool ProcessMesh(ImporterData& data, FbxMesh* fbxMesh, MeshData& mesh, String& e mesh.Indices[i] = fbxIndices[i]; } + if (data.Options.ReverseWindingOrder) + { + for (int32 i = 0; i < vertexCount; i += 3) + { + Swap(meshIndices[i + 1], meshIndices[i + 2]); + Swap(meshPositions[i + 1], meshPositions[i + 2]); + if (meshNormals) + Swap(meshNormals[i + 1], meshNormals[i + 2]); + if (meshTangents) + Swap(meshTangents[i + 1], meshTangents[i + 2]); + } + } + // Texture coordinates for (int32 channelIndex = 0; channelIndex < MODEL_MAX_UV && fbxMesh->GetElementUV(channelIndex); channelIndex++) { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index 750b0e059..9179acdf5 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -777,6 +777,24 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* } } + // Reverse winding order + if (data.Options.ReverseWindingOrder) + { + uint32* meshIndices = mesh.Indices.Get(); + Float3* meshPositions = mesh.Positions.Get(); + Float3* meshNormals = mesh.Normals.HasItems() ? mesh.Normals.Get() : nullptr; + Float3* meshTangents = mesh.Tangents.HasItems() ? mesh.Tangents.Get() : nullptr; + + for (int i = 0; i < vertexCount; i += 3) { + Swap(meshIndices[i + 1], meshIndices[i + 2]); + Swap(meshPositions[i + 1], meshPositions[i + 2]); + if (meshNormals) + Swap(meshNormals[i + 1], meshNormals[i + 2]); + if (meshTangents) + Swap(meshTangents[i + 1], meshTangents[i + 2]); + } + } + // Lightmap UVs mesh.SetLightmapUVsSource(data.Options.LightmapUVsSource); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 874017833..3bb098a60 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -667,6 +667,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(FlipNormals); SERIALIZE(CalculateTangents); SERIALIZE(SmoothingTangentsAngle); + SERIALIZE(ReverseWindingOrder); SERIALIZE(OptimizeMeshes); SERIALIZE(MergeMeshes); SERIALIZE(ImportLODs); @@ -717,6 +718,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(FlipNormals); DESERIALIZE(CalculateTangents); DESERIALIZE(SmoothingTangentsAngle); + DESERIALIZE(ReverseWindingOrder); DESERIALIZE(OptimizeMeshes); DESERIALIZE(MergeMeshes); DESERIALIZE(ImportLODs); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 515016ddf..c13e84b3c 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -170,6 +170,9 @@ public: // Specifies the maximum angle (in degrees) that may be between two vertex tangents before their tangents and bi-tangents are smoothed. The default value is 45. API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSmoothingTangentsAngle)), Limit(0, 45, 0.1f)") float SmoothingTangentsAngle = 45.0f; + // If checked, the winding order of the vertices will be reversed. + API_FIELD(Attributes="EditorOrder(47), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") + bool ReverseWindingOrder = false; // Enable/disable meshes geometry optimization. API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") bool OptimizeMeshes = true; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp index dc2b20778..37b914e9c 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp @@ -730,6 +730,9 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path if (!options.FlipY && !options.FlipX && !options.InvertGreenChannel && + !options.InvertRedChannel && + !options.InvertAlphaChannel && + !options.InvertBlueChannel && !options.ReconstructZChannel && options.Compress && type == ImageType::DDS && @@ -824,16 +827,12 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(), [&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y) { - static const DirectX::XMVECTORU32 s_selecty = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0 } } }; - + const DirectX::XMVECTORU32 s_selecty = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0 } } }; UNREFERENCED_PARAMETER(y); - for (size_t j = 0; j < w; ++j) { const DirectX::XMVECTOR value = inPixels[j]; - const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value); - outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selecty); } }, timage); @@ -844,6 +843,78 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path } SET_CURRENT_IMG(timage); } + + // Check if invert red channel + if (!keepAsIs && options.InvertRedChannel) + { + auto& timage = GET_TMP_IMG(); + result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(), + [&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y) + { + const DirectX::XMVECTORU32 s_selectx = { { { DirectX::XM_SELECT_1, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0 } } }; + UNREFERENCED_PARAMETER(y); + for (size_t j = 0; j < w; ++j) + { + const DirectX::XMVECTOR value = inPixels[j]; + const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value); + outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selectx); + } + }, timage); + if (FAILED(result)) + { + errorMsg = String::Format(TEXT("Cannot invert red channel in texture, error: {0:x}"), static_cast(result)); + return true; + } + SET_CURRENT_IMG(timage); + } + + // Check if invert blue channel + if (!keepAsIs && options.InvertBlueChannel) + { + auto& timage = GET_TMP_IMG(); + result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(), + [&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y) + { + const DirectX::XMVECTORU32 s_selectz = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0 } } }; + UNREFERENCED_PARAMETER(y); + for (size_t j = 0; j < w; ++j) + { + const DirectX::XMVECTOR value = inPixels[j]; + const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value); + outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selectz); + } + }, timage); + if (FAILED(result)) + { + errorMsg = String::Format(TEXT("Cannot invert blue channel in texture, error: {0:x}"), static_cast(result)); + return true; + } + SET_CURRENT_IMG(timage); + } + + // Check if invert alpha channel + if (!keepAsIs && options.InvertAlphaChannel) + { + auto& timage = GET_TMP_IMG(); + result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(), + [&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y) + { + const DirectX::XMVECTORU32 s_selectw = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_1 } } }; + UNREFERENCED_PARAMETER(y); + for (size_t j = 0; j < w; ++j) + { + const DirectX::XMVECTOR value = inPixels[j]; + const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value); + outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selectw); + } + }, timage); + if (FAILED(result)) + { + errorMsg = String::Format(TEXT("Cannot invert alpha channel in texture, error: {0:x}"), static_cast(result)); + return true; + } + SET_CURRENT_IMG(timage); + } // Reconstruct Z Channel if (!keepAsIs & options.ReconstructZChannel) @@ -853,10 +924,8 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(), [&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y) { - static const DirectX::XMVECTORU32 s_selectz = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0 } } }; - + const DirectX::XMVECTORU32 s_selectz = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0 } } }; UNREFERENCED_PARAMETER(y); - for (size_t j = 0; j < w; ++j) { const DirectX::XMVECTOR value = inPixels[j]; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index 1818c7246..45c4f1dc3 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -25,7 +25,7 @@ namespace String TextureTool::Options::ToString() const { - return String::Format(TEXT("Type: {}, IsAtlas: {}, NeverStream: {}, IndependentChannels: {}, sRGB: {}, GenerateMipMaps: {}, FlipY: {}, InvertGreen: {} Scale: {}, MaxSize: {}, Resize: {}, PreserveAlphaCoverage: {}, PreserveAlphaCoverageReference: {}, SizeX: {}, SizeY: {}"), + return String::Format(TEXT("Type: {}, IsAtlas: {}, NeverStream: {}, IndependentChannels: {}, sRGB: {}, GenerateMipMaps: {}, FlipY: {}, InvertRed: {}, InvertGreen: {}, InvertBlue {}, Invert Alpha {}, Scale: {}, MaxSize: {}, Resize: {}, PreserveAlphaCoverage: {}, PreserveAlphaCoverageReference: {}, SizeX: {}, SizeY: {}"), ScriptingEnum::ToString(Type), IsAtlas, NeverStream, @@ -33,7 +33,10 @@ String TextureTool::Options::ToString() const sRGB, GenerateMipMaps, FlipY, + InvertRedChannel, InvertGreenChannel, + InvertBlueChannel, + InvertAlphaChannel, Scale, MaxSize, MaxSize, @@ -74,9 +77,18 @@ void TextureTool::Options::Serialize(SerializeStream& stream, const void* otherO stream.JKEY("FlipX"); stream.Bool(FlipX); + stream.JKEY("InvertRedChannel"); + stream.Bool(InvertRedChannel); + stream.JKEY("InvertGreenChannel"); stream.Bool(InvertGreenChannel); + stream.JKEY("InvertBlueChannel"); + stream.Bool(InvertBlueChannel); + + stream.JKEY("InvertAlphaChannel"); + stream.Bool(InvertAlphaChannel); + stream.JKEY("ReconstructZChannel"); stream.Bool(ReconstructZChannel); @@ -141,7 +153,10 @@ void TextureTool::Options::Deserialize(DeserializeStream& stream, ISerializeModi GenerateMipMaps = JsonTools::GetBool(stream, "GenerateMipMaps", GenerateMipMaps); FlipY = JsonTools::GetBool(stream, "FlipY", FlipY); FlipX = JsonTools::GetBool(stream, "FlipX", FlipX); + InvertRedChannel = JsonTools::GetBool(stream, "InvertRedChannel", InvertRedChannel); InvertGreenChannel = JsonTools::GetBool(stream, "InvertGreenChannel", InvertGreenChannel); + InvertBlueChannel = JsonTools::GetBool(stream, "InvertBlueChannel", InvertBlueChannel); + InvertAlphaChannel = JsonTools::GetBool(stream, "InvertAlphaChannel", InvertAlphaChannel); ReconstructZChannel = JsonTools::GetBool(stream, "ReconstructZChannel", ReconstructZChannel); Resize = JsonTools::GetBool(stream, "Resize", Resize); KeepAspectRatio = JsonTools::GetBool(stream, "KeepAspectRatio", KeepAspectRatio); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.h b/Source/Engine/Tools/TextureTool/TextureTool.h index cdbd9ee8f..f51e1ff12 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.h +++ b/Source/Engine/Tools/TextureTool/TextureTool.h @@ -61,12 +61,24 @@ API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API TextureTool API_FIELD(Attributes="EditorOrder(71)") bool FlipX = false; - // True if to invert the green channel on a normal map. Good for OpenGL to DirectX conversion. - API_FIELD(Attributes = "EditorOrder(72)") + // Invert the red channel. + API_FIELD(Attributes = "EditorOrder(72), EditorDisplay(\"Invert Channels\"), ExpandGroups") + bool InvertRedChannel = false; + + // Invert the green channel. Good for OpenGL to DirectX conversion. + API_FIELD(Attributes = "EditorOrder(73), EditorDisplay(\"Invert Channels\")") bool InvertGreenChannel = false; + // Invert the blue channel. + API_FIELD(Attributes = "EditorOrder(74), EditorDisplay(\"Invert Channels\")") + bool InvertBlueChannel = false; + + // Invert the alpha channel. + API_FIELD(Attributes = "EditorOrder(75), EditorDisplay(\"Invert Channels\")") + bool InvertAlphaChannel = false; + // Rebuild Z (blue) channel assuming X/Y are normals. - API_FIELD(Attributes = "EditorOrder(73)") + API_FIELD(Attributes = "EditorOrder(76)") bool ReconstructZChannel = false; // Texture size scale. Allows increasing or decreasing the imported texture resolution. Default is 1. diff --git a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp index c52425fd3..2caad5b59 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp @@ -542,10 +542,10 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu // TODO: impl this LOG(Warning, "Option 'Flip X' is not supported"); } - if (options.InvertGreenChannel) + if (options.InvertRedChannel || options.InvertGreenChannel || options.InvertBlueChannel || options.InvertAlphaChannel) { // TODO: impl this - LOG(Warning, "Option 'Invert Green Channel' is not supported"); + LOG(Warning, "Option to invert channels is not supported"); } if (options.ReconstructZChannel) { diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index da8b299cb..64e8e1869 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -249,7 +249,7 @@ namespace FlaxEngine.GUI var text = ConvertedText(); // Check if sth is selected to draw selection - if (HasSelection) + if (HasSelection && IsFocused) { var leftEdge = font.GetCharPosition(text, SelectionLeft, ref _layout); var rightEdge = font.GetCharPosition(text, SelectionRight, ref _layout); @@ -294,6 +294,8 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; + else if (_isReadOnly) + color *= 0.85f; Render2D.DrawText(font, text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText)) diff --git a/Source/Engine/Utilities/TextProcessing.cpp b/Source/Engine/Utilities/TextProcessing.cpp index fb971559d..50faeb262 100644 --- a/Source/Engine/Utilities/TextProcessing.cpp +++ b/Source/Engine/Utilities/TextProcessing.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "TextProcessing.h" +#include "Engine/Core/Log.h" TextProcessing::TextProcessing(const char* input, int32 length) : _buffer(input) @@ -54,6 +55,8 @@ void TextProcessing::Setup_HLSL() 32, }; Whitespaces.Set(whitespaces, ARRAY_COUNT(whitespaces)); + SingleLineComment = SeparatorData('/', '/'); + MultiLineCommentSeparator = SeparatorData('/', '*'); } char TextProcessing::ReadChar() @@ -123,6 +126,38 @@ void TextProcessing::ReadToken(Token* token) token->Separator = Separators[s]; ReadChar(); + + // Check for comments + if (token->Separator == SingleLineComment) + { + // Read whole line + ReadLine(); + + // Read another token + ReadToken(token); + } + else if (token->Separator == MultiLineCommentSeparator) + { + // Read tokens until end sequence + char prev = ' '; + while (CanRead()) + { + c = ReadChar(); + if (prev == '*' && c == '/') + break; + prev = c; + } + + // Check if comment is valid (has end before file end) + if (!CanRead()) + { + LOG(Warning, "Missing multiline comment ending"); + } + + // Read another token + ReadToken(token); + } + return; } } diff --git a/Source/Engine/Utilities/TextProcessing.h b/Source/Engine/Utilities/TextProcessing.h index d26f52814..fa804a132 100644 --- a/Source/Engine/Utilities/TextProcessing.h +++ b/Source/Engine/Utilities/TextProcessing.h @@ -9,13 +9,13 @@ #include "Engine/Platform/StringUtils.h" /// -/// Helper class to fast ANSI text processing (tokenization, reading, streaming etc.) +/// Helper class to fast ANSI text processing (tokenization, reading, streaming etc.). /// class FLAXENGINE_API TextProcessing : public NonCopyable { public: /// - /// Separator structure + /// Separator structure. /// struct SeparatorData { @@ -200,18 +200,21 @@ public: public: /// - /// Array with all token separators + /// Array with all token separators. /// Array Separators; /// /// - /// Array with all white characters + /// Array with all white characters. /// Array Whitespaces; + SeparatorData SingleLineComment; + SeparatorData MultiLineCommentSeparator; + public: /// - /// Set separators and white chars for HLSL language + /// Sets up separators and white chars for HLSL language. /// void Setup_HLSL(); @@ -219,25 +222,22 @@ public: /// /// Returns true if there are still characters in the buffer and can read data from it /// - /// True if can read data, otherwise false FORCE_INLINE bool CanRead() const { return _position < _length; } /// - /// Peeks single character without moving forward in the buffer + /// Peeks a single character without moving forward in the buffer. /// - /// First character FORCE_INLINE char PeekChar() const { return *_cursor; } /// - /// Gets current line number + /// Gets the current line number. /// - /// Current line number FORCE_INLINE int32 GetLine() const { return _line; diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index fb1f84323..f4ae09a1a 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -256,6 +256,13 @@ namespace Flax.Build #endif if (buildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) args.Add("-nowarn:1591"); + if (buildOptions.ScriptingAPI.IgnoreSpecificWarnings.Any()) + { + foreach (var warningString in buildOptions.ScriptingAPI.IgnoreSpecificWarnings) + { + args.Add($"-nowarn:{warningString}"); + } + } // Optimizations prevent debugging, only enable in release builds by default var optimize = optimizeAssembly.HasValue ? optimizeAssembly.Value : buildData.Configuration == TargetConfiguration.Release; diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs index fb94131a1..19e04236e 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs @@ -235,6 +235,11 @@ namespace Flax.Build.NativeCpp /// public HashSet Analyzers; + /// + /// The specific warnings to ignore. + /// + public HashSet IgnoreSpecificWarnings; + /// /// True if ignore compilation warnings due to missing code documentation comments. /// @@ -265,6 +270,7 @@ namespace Flax.Build.NativeCpp FileReferences.AddRange(other.FileReferences); Analyzers.AddRange(other.Analyzers); IgnoreMissingDocumentationWarnings |= other.IgnoreMissingDocumentationWarnings; + IgnoreSpecificWarnings.AddRange(other.IgnoreSpecificWarnings); } } @@ -338,6 +344,7 @@ namespace Flax.Build.NativeCpp }, FileReferences = new HashSet(), Analyzers = new HashSet(), + IgnoreSpecificWarnings = new HashSet(), }; /// diff --git a/Source/Tools/Flax.Build/Deploy/Deployer.cs b/Source/Tools/Flax.Build/Deploy/Deployer.cs index c9809a97d..6b68a41c4 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployer.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployer.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2024 Flax Engine. All rights reserved. +#define USE_STD + using System; using System.IO; using System.Text; @@ -99,6 +101,9 @@ namespace Flax.Deploy Log.Info("Compressing game debug symbols files..."); var gamePackageZipPath = Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols.zip"); Utilities.FileDelete(gamePackageZipPath); +#if USE_STD + System.IO.Compression.ZipFile.CreateFromDirectory(Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols"), gamePackageZipPath, System.IO.Compression.CompressionLevel.Optimal, false); +#else using (var zip = new Ionic.Zip.ZipFile()) { zip.AddDirectory(Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols")); @@ -106,6 +111,7 @@ namespace Flax.Deploy zip.Comment = string.Format("Flax Game {0}.{1}.{2}\nDate: {3}", Deployer.VersionMajor, Deployer.VersionMinor, Deployer.VersionBuild, DateTime.UtcNow); zip.Save(gamePackageZipPath); } +#endif Log.Info("Compressed game debug symbols package size: " + Utilities.GetFileSize(gamePackageZipPath)); } Utilities.DirectoryDelete(Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols")); diff --git a/Source/Tools/Flax.Build/Platforms/Apple/XCode.cs b/Source/Tools/Flax.Build/Platforms/Apple/XCode.cs index ef1070191..2a7bd7560 100644 --- a/Source/Tools/Flax.Build/Platforms/Apple/XCode.cs +++ b/Source/Tools/Flax.Build/Platforms/Apple/XCode.cs @@ -32,16 +32,32 @@ namespace Flax.Build.Platforms return; try { + // Get path RootPath = Utilities.ReadProcessOutput("xcode-select", "--print-path"); if (string.IsNullOrEmpty(RootPath) || !Directory.Exists(RootPath)) return; IsValid = true; Version = new Version(1, 0); - Log.Verbose(string.Format("Found XCode at {0}", RootPath)); + + // Get version (optional) + var versionText = Utilities.ReadProcessOutput("xcodebuild", "-version"); + var versionLines = versionText?.Split('\n') ?? null; + if (versionLines != null && versionLines.Length != 0) + { + versionText = versionLines[0].Trim(); + var versionParts = versionText.Split(' '); + if (versionParts != null && versionParts.Length > 1) + { + versionText = versionParts[1].Trim(); + if (Version.TryParse(versionText, out var v)) + Version = v; + } + } } catch { } + Log.Verbose(string.Format("Found XCode {1} at {0}", RootPath, Version)); } } } diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs index a83a5a7f6..c5359f66e 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs @@ -125,6 +125,13 @@ namespace Flax.Build.Projects.VisualStudio csProjectFileContent.AppendLine(" true"); if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) csProjectFileContent.AppendLine(" 1591"); + if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings.Any()) + { + foreach (var warningString in configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings) + { + csProjectFileContent.AppendLine($" {warningString}"); + } + } csProjectFileContent.AppendLine(string.Format(" {0}\\{1}.CSharp.xml", outputPath, project.BaseName)); csProjectFileContent.AppendLine(" true"); csProjectFileContent.AppendLine(" "); @@ -156,6 +163,13 @@ namespace Flax.Build.Projects.VisualStudio csProjectFileContent.AppendLine(" true"); if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) csProjectFileContent.AppendLine(" 1591"); + if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings.Any()) + { + foreach (var warningString in configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings) + { + csProjectFileContent.AppendLine($" {warningString}"); + } + } csProjectFileContent.AppendLine(string.Format(" {0}\\{1}.CSharp.xml", outputPath, project.BaseName)); csProjectFileContent.AppendLine(" true"); csProjectFileContent.AppendLine(" "); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs index 64c333499..9059cda94 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs @@ -284,6 +284,13 @@ namespace Flax.Build.Projects.VisualStudio csProjectFileContent.AppendLine(" true"); if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) csProjectFileContent.AppendLine(" 1591"); + if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings.Any()) + { + foreach (var warningString in configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings) + { + csProjectFileContent.AppendLine($" {warningString}"); + } + } csProjectFileContent.AppendLine(string.Format(" {0}\\{1}.CSharp.xml", outputPath, project.BaseName)); csProjectFileContent.AppendLine(" true");