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");