From 0b7550e5caa8dcc665e7773b8544faccbe91195a Mon Sep 17 00:00:00 2001 From: Andre Mohren Date: Wed, 2 Apr 2025 15:10:11 +0200 Subject: [PATCH 01/68] Implement creation of new projects from within the editor gui. --- Source/Editor/Editor.cs | 22 +++++++++++++++++++++- Source/Editor/Modules/UIModule.cs | 12 ++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index ee9251a69..c577c4055 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -51,6 +51,7 @@ namespace FlaxEditor private readonly List _modules = new List(16); private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode, _autoExit; private string _projectToOpen; + private bool _projectIsNew; private float _lastAutoSaveTimer, _autoExitTimeout = 0.1f; private Button _saveNowButton; private Button _cancelSaveButton; @@ -731,11 +732,12 @@ namespace FlaxEditor var procSettings = new CreateProcessSettings { FileName = Platform.ExecutableFilePath, - Arguments = string.Format("-project \"{0}\"", _projectToOpen), + Arguments = string.Format("-project \"{0}\"" + (_projectIsNew ? " -new" : string.Empty), _projectToOpen), ShellExecute = true, WaitForEnd = false, HiddenWindow = false, }; + _projectIsNew = false; _projectToOpen = null; Platform.CreateProcess(ref procSettings); } @@ -784,6 +786,24 @@ namespace FlaxEditor } } + /// + /// Creates the given project. Afterwards closes this project with running editor and opens the given project. + /// + /// The project file path. + public void NewProject(string projectFilePath) + { + if (projectFilePath == null) + { + MessageBox.Show("Missing project"); + return; + } + + // Cache project path and start editor exit (it will open new instance on valid closing) + _projectToOpen = StringUtils.NormalizePath(Path.GetDirectoryName(projectFilePath)); + _projectIsNew = true; + Windows.MainWindow.Close(ClosingReason.User); + } + /// /// Closes this project with running editor and opens the given project. /// diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 67df5a2e8..1eb62c13c 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -536,6 +536,7 @@ namespace FlaxEditor.Modules _menuFileGenerateScriptsProjectFiles = cm.AddButton("Generate scripts project files", inputOptions.GenerateScriptsProject, Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync); _menuFileRecompileScripts = cm.AddButton("Recompile scripts", inputOptions.RecompileScripts, ScriptsBuilder.Compile); cm.AddSeparator(); + cm.AddButton("New project", NewProject); cm.AddButton("Open project...", OpenProject); cm.AddButton("Reload project", ReloadProject); cm.AddButton("Open project folder", () => FileSystem.ShowFileExplorer(Editor.Instance.GameProject.ProjectFolderPath)); @@ -827,6 +828,17 @@ namespace FlaxEditor.Modules MasterPanel.Offsets = new Margin(0, 0, ToolStrip.Bottom, StatusBar.Height); } + private void NewProject() + { + // Ask user to create project file + if (FileSystem.ShowSaveFileDialog(Editor.Windows.MainWindow, null, "Project files (*.flaxproj)\0*.flaxproj\0All files (*.*)\0*.*\0", false, "Create project file", out var files)) + return; + if (files != null && files.Length > 0) + { + Editor.NewProject(files[0]); + } + } + private void OpenProject() { // Ask user to select project file From 2e48be97b67c14d322e9b523c0da257eab8f70ef Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 2 Apr 2025 17:24:13 +0200 Subject: [PATCH 02/68] add getter for steering --- Source/Engine/Physics/Actors/WheeledVehicle.cpp | 5 +++++ Source/Engine/Physics/Actors/WheeledVehicle.h | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp index 010add151..3aa7ff5ce 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp +++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp @@ -200,6 +200,11 @@ void WheeledVehicle::SetSteering(float value) _steering = Math::Clamp(value, -1.0f, 1.0f); } +float WheeledVehicle::GetSteering() +{ + return _steering; +} + void WheeledVehicle::SetBrake(float value) { value = Math::Saturate(value); diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index fe518a41d..bbb3026db 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -553,6 +553,12 @@ public: /// The value (-1,1 range). API_FUNCTION() void SetSteering(float value); + /// + /// Gets the vehicle steering. Steer is the analog steer value in range (-1,1) where -1 represents the steering wheel at left lock and +1 represents the steering wheel at right lock. + /// + /// The vehicle steering. + API_FUNCTION() float GetSteering(); + /// /// Sets the input for vehicle brakes. Brake is the analog brake pedal value in range (0,1) where 1 represents the pedal fully pressed and 0 represents the pedal in its rest state. /// From dd281bbca8583f3ca433d8c903ba9b14d5163a1d Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 2 Apr 2025 19:12:36 +0200 Subject: [PATCH 03/68] add wheel information debug draw --- .../Engine/Physics/Actors/WheeledVehicle.cpp | 6 +++++ Source/Engine/Physics/Actors/WheeledVehicle.h | 25 ++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp index 3aa7ff5ce..08141e75e 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp +++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp @@ -424,6 +424,12 @@ void WheeledVehicle::OnDebugDrawSelected() { DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.TireContactPoint, 5.0f), Color::Green, 0, false); } + + if (DisplayWheelInfo) + { + const String text = String::Format(TEXT("Index: {}\nCollider: {}"), wheelIndex, wheel.Collider->GetName()); + DEBUG_DRAW_TEXT(text, currentPos, WheelInfoColor, 10, 0); + } } } diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index bbb3026db..a3d66ff29 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -5,6 +5,7 @@ #include "Engine/Physics/Actors/RigidBody.h" #include "Engine/Physics/Colliders/Collider.h" #include "Engine/Scripting/ScriptingObjectReference.h" +#include "Engine/Core/Math/Color.h" /// /// Representation of the car vehicle that uses wheels. Built on top of the RigidBody with collider representing its chassis shape and wheels. @@ -464,10 +465,22 @@ public: API_FIELD(Attributes="EditorOrder(1), EditorDisplay(\"Vehicle\")") bool UseAnalogSteering = false; + /// + /// If checked, will draw some useful information about the wheels at the position of their colliders. + /// + API_FIELD(Attributes ="EditorOrder(2), EditorDisplay(\"Vehicle\")") + bool DisplayWheelInfo = false; + + /// + /// If checked, will draw some useful information about the wheels at the position of their colliders. + /// + API_FIELD(Attributes = "EditorOrder(3), EditorDisplay(\"Vehicle\"), VisibleIf(\"DisplayWheelInfo\")") + Color WheelInfoColor = Color::White; + /// /// Gets the vehicle driving model type. /// - API_PROPERTY(Attributes="EditorOrder(2), EditorDisplay(\"Vehicle\")") DriveTypes GetDriveType() const; + API_PROPERTY(Attributes="EditorOrder(4), EditorDisplay(\"Vehicle\")") DriveTypes GetDriveType() const; /// /// Sets the vehicle driving model type. @@ -477,12 +490,12 @@ public: /// /// Gets the vehicle wheels settings. /// - API_PROPERTY(Attributes="EditorOrder(4), EditorDisplay(\"Vehicle\")") const Array& GetWheels() const; + API_PROPERTY(Attributes="EditorOrder(5), EditorDisplay(\"Vehicle\")") const Array& GetWheels() const; /// /// Gets the vehicle drive control settings. /// - API_PROPERTY(Attributes = "EditorOrder(5), EditorDisplay(\"Vehicle\")") DriveControlSettings GetDriveControl() const; + API_PROPERTY(Attributes = "EditorOrder(6), EditorDisplay(\"Vehicle\")") DriveControlSettings GetDriveControl() const; /// /// Sets the vehicle drive control settings. @@ -497,7 +510,7 @@ public: /// /// Gets the vehicle engine settings. /// - API_PROPERTY(Attributes="EditorOrder(6), EditorDisplay(\"Vehicle\")") EngineSettings GetEngine() const; + API_PROPERTY(Attributes="EditorOrder(7), EditorDisplay(\"Vehicle\")") EngineSettings GetEngine() const; /// /// Sets the vehicle engine settings. @@ -507,7 +520,7 @@ public: /// /// Gets the vehicle differential settings. /// - API_PROPERTY(Attributes="EditorOrder(7), EditorDisplay(\"Vehicle\")") DifferentialSettings GetDifferential() const; + API_PROPERTY(Attributes="EditorOrder(8), EditorDisplay(\"Vehicle\")") DifferentialSettings GetDifferential() const; /// /// Sets the vehicle differential settings. @@ -517,7 +530,7 @@ public: /// /// Gets the vehicle gearbox settings. /// - API_PROPERTY(Attributes="EditorOrder(8), EditorDisplay(\"Vehicle\")") GearboxSettings GetGearbox() const; + API_PROPERTY(Attributes="EditorOrder(9), EditorDisplay(\"Vehicle\")") GearboxSettings GetGearbox() const; /// /// Sets the vehicle gearbox settings. From 59ac8a3f602f226036ec4075a57b5738a9a39e7b Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 2 Apr 2025 19:21:24 +0200 Subject: [PATCH 04/68] move anti roll bars into Vehicle display group --- Source/Engine/Physics/Actors/WheeledVehicle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index a3d66ff29..dec7b75cd 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -540,7 +540,7 @@ public: // /// Sets axles anti roll bars to increase vehicle stability. /// - API_PROPERTY() void SetAntiRollBars(const Array& value); + API_PROPERTY(Attributes="EditorOrder(10), EditorDisplay(\"Vehicle\")") void SetAntiRollBars(const Array& value); // /// Gets axles anti roll bars. From a0b80c6096a569ece0b26291f450453d7218aeb7 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 2 Apr 2025 19:29:11 +0200 Subject: [PATCH 05/68] add getters for more commonly used vehicle fields --- Source/Engine/Physics/Actors/WheeledVehicle.cpp | 10 ++++++++++ Source/Engine/Physics/Actors/WheeledVehicle.h | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp index 08141e75e..b3b0473f1 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp +++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp @@ -213,11 +213,21 @@ void WheeledVehicle::SetBrake(float value) _tankRightBrake = value; } +float WheeledVehicle::GetBrake() +{ + return _brake; +} + void WheeledVehicle::SetHandbrake(float value) { _handBrake = Math::Saturate(value); } +float WheeledVehicle::GetHandbrake() +{ + return _handBrake; +} + void WheeledVehicle::SetTankLeftThrottle(float value) { _tankLeftThrottle = Math::Clamp(value, -1.0f, 1.0f); diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index dec7b75cd..3e93f47ec 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -578,12 +578,24 @@ public: /// The value (0,1 range). API_FUNCTION() void SetBrake(float value); + /// + /// Gets the vehicle brakes. Brake is the analog brake pedal value in range (0,1) where 1 represents the pedal fully pressed and 0 represents the pedal in its rest state. + /// + /// The vehicle brake. + API_FUNCTION() float GetBrake(); + /// /// Sets the input for vehicle handbrake. Handbrake is the analog handbrake value in range (0,1) where 1 represents the handbrake fully engaged and 0 represents the handbrake in its rest state. /// /// The value (0,1 range). API_FUNCTION() void SetHandbrake(float value); + /// + /// Gets the vehicle handbrake. Handbrake is the analog handbrake value in range (0,1) where 1 represents the handbrake fully engaged and 0 represents the handbrake in its rest state. + /// + /// The vehicle handbrake. + API_FUNCTION() float GetHandbrake(); + /// /// Sets the input for tank left track throttle. It is the analog accelerator pedal value in range (-1,1) where 1 represents the pedal fully pressed to move to forward, 0 to represents the /// pedal in its rest state and -1 represents the pedal fully pressed to move to backward. The track direction will be inverted if the vehicle current gear is rear. From 68ef6f08c6cfe6cdf09795a4fc591a009e8d65aa Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 2 Apr 2025 19:40:27 +0200 Subject: [PATCH 06/68] minor style fixes --- Source/Engine/Physics/Actors/WheeledVehicle.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index 3e93f47ec..a9efb204d 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -468,13 +468,13 @@ public: /// /// If checked, will draw some useful information about the wheels at the position of their colliders. /// - API_FIELD(Attributes ="EditorOrder(2), EditorDisplay(\"Vehicle\")") + API_FIELD(Attributes="EditorOrder(2), EditorDisplay(\"Vehicle\")") bool DisplayWheelInfo = false; /// /// If checked, will draw some useful information about the wheels at the position of their colliders. /// - API_FIELD(Attributes = "EditorOrder(3), EditorDisplay(\"Vehicle\"), VisibleIf(\"DisplayWheelInfo\")") + API_FIELD(Attributes="EditorOrder(3), EditorDisplay(\"Vehicle\"), VisibleIf(\"DisplayWheelInfo\")") Color WheelInfoColor = Color::White; /// From 1dfd717093b53a5e1f618fca821524c8e92bd80c Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 2 Apr 2025 23:38:04 +0200 Subject: [PATCH 07/68] serialize display info and color --- Source/Engine/Physics/Actors/WheeledVehicle.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp index b3b0473f1..799f4a5d4 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp +++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp @@ -471,6 +471,8 @@ void WheeledVehicle::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_GET_OTHER_OBJ(WheeledVehicle); + SERIALIZE_MEMBER(DisplayWheelInfo, DisplayWheelInfo); + SERIALIZE_MEMBER(WheelInfoColor, WheelInfoColor); SERIALIZE_MEMBER(DriveType, _driveType); SERIALIZE_MEMBER(Wheels, _wheels); SERIALIZE_MEMBER(DriveControl, _driveControl); @@ -486,6 +488,8 @@ void WheeledVehicle::Deserialize(DeserializeStream& stream, ISerializeModifier* { RigidBody::Deserialize(stream, modifier); + DESERIALIZE(DisplayWheelInfo); + DESERIALIZE(WheelInfoColor); DESERIALIZE_MEMBER(DriveType, _driveType); DESERIALIZE_MEMBER(Wheels, _wheels); DESERIALIZE_MEMBER(DriveControl, _driveControl); From 40dae18b7659b7af3ed7eb79a9911fa7d6ebcad5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 24 Apr 2025 18:48:29 -0500 Subject: [PATCH 08/68] Add new issue and request templates. --- .github/ISSUE_TEMPLATE/1-bug.yaml | 42 +++++++++++++++++++ .github/ISSUE_TEMPLATE/2-feature-request.yaml | 22 ++++++++++ ISSUE_TEMPLATE.md | 19 --------- 3 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/1-bug.yaml create mode 100644 .github/ISSUE_TEMPLATE/2-feature-request.yaml delete mode 100644 ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE/1-bug.yaml b/.github/ISSUE_TEMPLATE/1-bug.yaml new file mode 100644 index 000000000..acae26c00 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1-bug.yaml @@ -0,0 +1,42 @@ +name: Bug Report +description: File a bug report. +title: "[Bug]: " +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! Please attach any minimal repoduction projects! + - type: textarea + id: description-area + attributes: + label: Description + description: Please provide a description and what you expected to happen. + validations: + required: true + - type: textarea + id: steps-area + attributes: + label: Steps to reproduce + description: Please provide an repoduction steps. + validations: + required: true + - type: dropdown + id: version + attributes: + label: Version + description: What version of Flax are you running? + options: + - 1.8 + - 1.9 + - 1.10 + - 1.11 + - master branch + default: 2 + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant logs + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/2-feature-request.yaml b/.github/ISSUE_TEMPLATE/2-feature-request.yaml new file mode 100644 index 000000000..235c863e3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2-feature-request.yaml @@ -0,0 +1,22 @@ +name: Feature Request +description: File a feature request. +title: "[Request]: " +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out a feature request! + - type: textarea + id: description-area + attributes: + label: Description + description: Please provide a description of the feature! + validations: + required: true + - type: textarea + id: benifits-area + attributes: + label: Benifits + description: Please provide what benifits this feature would provide to the engine! + validations: + required: true \ No newline at end of file diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 798f210ef..000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,19 +0,0 @@ - - -**Issue description:** - - - -**Steps to reproduce:** - - - -**Minimal reproduction project:** - - - -**Flax version:** - - From 47a6da9e40d57c59e3eae1c051cf0fb6ce430397 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sun, 27 Apr 2025 15:56:34 +0200 Subject: [PATCH 09/68] add editor option for disabled transform gizmo brightness --- .../Editor/Gizmo/TransformGizmoBase.Draw.cs | 91 +++++++++++-------- Source/Editor/Options/VisualOptions.cs | 7 ++ 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs index bb9684869..8523b9378 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs @@ -21,13 +21,17 @@ 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"; + /// + /// Used for example when the selection can't be moved because one actor is static. + /// + private bool _isDisabled; + private void InitDrawing() { // Axis Models @@ -42,7 +46,6 @@ 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 @@ -67,17 +70,41 @@ namespace FlaxEditor.Gizmo 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); - + UpdateGizmoBrightness(options); + float opacity = options.Visual.TransformGizmoOpacity; _materialAxisX.SetParameterValue(_opacityParamName, opacity); _materialAxisY.SetParameterValue(_opacityParamName, opacity); _materialAxisZ.SetParameterValue(_opacityParamName, opacity); - _materialAxisLocked.SetParameterValue(_opacityParamName, opacity); + } + + private void UpdateGizmoBrightness(EditorOptions options) + { + _isDisabled = ShouldGizmoBeLocked(); + + float brightness = _isDisabled ? options.Visual.TransformGizmoBrighnessDisabled : options.Visual.TransformGizmoBrightness; + _materialAxisX.SetParameterValue(_brightnessParamName, brightness); + _materialAxisY.SetParameterValue(_brightnessParamName, brightness); + _materialAxisZ.SetParameterValue(_brightnessParamName, brightness); + } + + private bool ShouldGizmoBeLocked() + { + bool gizmoLocked = false; + + if (Editor.Instance.StateMachine.IsPlayMode) + { + foreach (SceneGraphNode obj in Editor.Instance.SceneEditing.Selection) + { + if (obj.CanTransform == false) + { + gizmoLocked = true; + break; + } + } + } + + return gizmoLocked; } /// @@ -88,20 +115,8 @@ 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; - } - } - } + // Update the gizmo brightness every frame to ensure it updates correctly + UpdateGizmoBrightness(Editor.Instance.Options.Options); // 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 @@ -136,37 +151,37 @@ namespace FlaxEditor.Gizmo // X axis Matrix.RotationY(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - MaterialInstance xAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX); + MaterialInstance xAxisMaterialTransform = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX; transAxisMesh.Draw(ref renderContext, xAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Y axis Matrix.RotationX(Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - MaterialInstance yAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY); + MaterialInstance yAxisMaterialTransform = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY; transAxisMesh.Draw(ref renderContext, yAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Z axis Matrix.RotationX(Mathf.Pi, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - MaterialInstance zAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ); + MaterialInstance zAxisMaterialTransform = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ; transAxisMesh.Draw(ref renderContext, zAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // 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); - MaterialInstance xyPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX); + MaterialInstance xyPlaneMaterialTransform = (_activeAxis == Axis.XY && !_isDisabled) ? _materialAxisFocus : _materialAxisX; cubeMesh.Draw(ref renderContext, xyPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // 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); - MaterialInstance zxPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisY); + MaterialInstance zxPlaneMaterialTransform = (_activeAxis == Axis.ZX && !_isDisabled) ? _materialAxisFocus : _materialAxisY; cubeMesh.Draw(ref renderContext, zxPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // 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); - MaterialInstance yzPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisZ); + MaterialInstance yzPlaneMaterialTransform = (_activeAxis == Axis.YZ && !_isDisabled) ? _materialAxisFocus : _materialAxisZ; cubeMesh.Draw(ref renderContext, yzPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Center sphere @@ -186,17 +201,17 @@ namespace FlaxEditor.Gizmo // X axis Matrix.RotationZ(Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX); + MaterialInstance xAxisMaterialRotate = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX; rotationAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Y axis - MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY); + MaterialInstance yAxisMaterialRotate = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY; rotationAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m1, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Z axis Matrix.RotationX(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ); + MaterialInstance zAxisMaterialRotate = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ; rotationAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Center box @@ -216,37 +231,37 @@ namespace FlaxEditor.Gizmo // X axis Matrix.RotationY(-Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref mx1, out m3); - MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX); + MaterialInstance xAxisMaterialRotate = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX; scaleAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Y axis Matrix.RotationX(Mathf.PiOverTwo, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY); + MaterialInstance yAxisMaterialRotate = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY; scaleAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Z axis Matrix.RotationX(Mathf.Pi, out m2); Matrix.Multiply(ref m2, ref m1, out m3); - MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ); + MaterialInstance zAxisMaterialRotate = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ; scaleAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // 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); - MaterialInstance xyPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX); + MaterialInstance xyPlaneMaterialScale = (_activeAxis == Axis.XY && !_isDisabled) ? _materialAxisFocus : _materialAxisX; cubeMesh.Draw(ref renderContext, xyPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // 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); - MaterialInstance zxPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ); + MaterialInstance zxPlaneMaterialScale = (_activeAxis == Axis.ZX && !_isDisabled) ? _materialAxisFocus : _materialAxisZ; cubeMesh.Draw(ref renderContext, zxPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // 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); - MaterialInstance yzPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY); + MaterialInstance yzPlaneMaterialScale = (_activeAxis == Axis.YZ && !_isDisabled) ? _materialAxisFocus : _materialAxisY; cubeMesh.Draw(ref renderContext, yzPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder); // Center box diff --git a/Source/Editor/Options/VisualOptions.cs b/Source/Editor/Options/VisualOptions.cs index 3a12dd0c5..45a1f1b13 100644 --- a/Source/Editor/Options/VisualOptions.cs +++ b/Source/Editor/Options/VisualOptions.cs @@ -81,6 +81,13 @@ namespace FlaxEditor.Options [EditorDisplay("Transform Gizmo", "Gizmo Opacity"), EditorOrder(211)] public float TransformGizmoOpacity { get; set; } = 1f; + /// + /// Gets or set a value indicating how bright the transform gizmo is when it is disabled, for example when one of the selected actors is static in play mode. Use a value of 0 to make the gizmo fully gray. Value over 1 will result in the gizmo emitting light. + /// + [DefaultValue(0.25f), Range(0f, 5f)] + [EditorDisplay("Transform Gizmo", "Disabled Gizmo Brightness"), EditorOrder(212)] + public float TransformGizmoBrighnessDisabled { get; set; } = 0.25f; + /// /// Gets or sets a value indicating whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance. /// From f71bdd09625cea8964ef2e9515648b7cab061e00 Mon Sep 17 00:00:00 2001 From: Norite SC <162097313+cNori@users.noreply.github.com> Date: Tue, 13 May 2025 01:13:16 +0200 Subject: [PATCH 10/68] Create GPUTextureEditor.cs --- Source/Editor/GUI/GPUTextureEditor.cs | 112 ++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Source/Editor/GUI/GPUTextureEditor.cs diff --git a/Source/Editor/GUI/GPUTextureEditor.cs b/Source/Editor/GUI/GPUTextureEditor.cs new file mode 100644 index 000000000..5dd672941 --- /dev/null +++ b/Source/Editor/GUI/GPUTextureEditor.cs @@ -0,0 +1,112 @@ +#if FLAX_EDITOR +using FlaxEditor.CustomEditors; +using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.CustomEditors.Elements; +using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Scripting; +using FlaxEditor.Tools.Foliage; +using FlaxEngine; +using FlaxEngine.GUI; +using FlaxEngine.Utilities; + +namespace FlaxEditor.GUI +{ + /// + /// basic custom property editor for GPUTexture + /// + [CustomEditor(typeof(GPUTexture))] + public class GPUTexturePropertyEditor : GenericEditor + { + public override DisplayStyle Style => DisplayStyle.Inline; + + ImageElement imageElement; + GroupElement group; + /// + /// + /// + public override void Initialize(LayoutElementsContainer layout) + { + imageElement = (group = layout.Group("")).Image(SpriteHandle.Default); + + //todo fix the AddSettingsButton func + //shit is buged + //the code below (until the Paint) is untested the Clear might not work + group.AddSettingsButton(); + group.SetupContextMenu += (ContextMenu.ContextMenu cxm, DropPanel dp) => + { + cxm.AddButton("Clear", (ContextMenuButton bt) => + { + SetValue(null); + }); + + cxm.AddSeparator(); + + //todo + //editor is needed + //cxm.AddButton("Display Full Texture", (ContextMenuButton bt) => + //{ + //}); + + //todo + // + //cxm.AddButton("Save To Asset", (ContextMenuButton bt) => + //{ + //}); + }; + Paint(); + group.Panel.Close(); + } + /// + /// + public override void Refresh() + { + Paint(); + base.Refresh(); + } + private void Paint() + { + string name = null; + string tt = null; + if (Values[0] is GPUTexture gputex) + { + name = gputex.Name; + tt += "Type: " + gputex.ResourceType.ToString() + "\n"; + tt += "Memory Usage: " + gputex.MemoryUsage + "B" + "\n"; + tt += "Format: " + gputex.Format.ToString() + "\n"; + //shorten the name it is a full path + if (name.EndsWith(".flax")) + { + if (name != ".flax")//sanity guard + { + var nameStartIndexWithEx = Globals.ProjectFolder.Length + 9 /* +9 to remove the "/Content/" */; + name = name.Substring + ( + nameStartIndexWithEx, + nameStartIndexWithEx - 5 /* -5 to remove the .flax */ + 2 + ); + + tt += "Path: " + gputex.Name.Remove(0, Globals.ProjectFolder.Length + 1); + } + } + + if (imageElement.Image.Brush is GPUTextureBrush brush) + { + brush.Texture = gputex; + imageElement.Control.Size = new Float2(group.Control.Width); + } + else + { + imageElement.Image.Brush = new GPUTextureBrush(); + Paint(); + } + } + name ??= "..."; + + DropPanel p = group.Control as DropPanel; + + p.HeaderText = name; + p.TooltipText = tt; + } + } +} +#endif From 608839b6a5eb547929dfe0c270c0a1f6cf0697ae Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 2 Jun 2025 22:33:22 +0200 Subject: [PATCH 11/68] set width of command suggestions based on longest command --- Source/Editor/Windows/OutputLogWindow.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 6fc0659d7..5c6e9a27e 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -233,7 +233,10 @@ namespace FlaxEditor.Windows else cm.ClearItems(); + float longestItemWidth = 0.0f; + // Add items + var font = Style.Current.FontMedium; ItemsListContextMenu.Item lastItem = null; foreach (var command in commands) { @@ -244,7 +247,7 @@ namespace FlaxEditor.Windows }); var flags = DebugCommands.GetCommandFlags(command); if (flags.HasFlag(DebugCommands.CommandFlags.Exec)) - lastItem.TintColor = new Color(0.85f, 0.85f, 1.0f, 1.0f); + lastItem.TintColor = new Color(0.75f, 0.75f, 1.0f, 1.0f); else if (flags.HasFlag(DebugCommands.CommandFlags.Read) && !flags.HasFlag(DebugCommands.CommandFlags.Write)) lastItem.TintColor = new Color(0.85f, 0.85f, 0.85f, 1.0f); lastItem.Focused += item => @@ -252,6 +255,10 @@ namespace FlaxEditor.Windows // Set command Set(item.Name); }; + + float width = font.MeasureText(command).X; + if (width > longestItemWidth) + longestItemWidth = width; } cm.ItemClicked += item => { @@ -262,6 +269,10 @@ namespace FlaxEditor.Windows // Setup popup var count = commands.Count(); var totalHeight = count * lastItem.Height + cm.ItemsPanel.Margin.Height + cm.ItemsPanel.Spacing * (count - 1); + + // Account for scroll bars taking up a part of the width + longestItemWidth += 25f; + cm.Width = longestItemWidth; cm.Height = 220; if (cm.Height > totalHeight) cm.Height = totalHeight; // Limit popup height if list is small From c9fe9213b33835e79432d5a4c801efd83c4aba62 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 2 Jun 2025 23:25:50 +0200 Subject: [PATCH 12/68] add showing all commands if prompt is whitespace(s) --- Source/Editor/Windows/OutputLogWindow.cs | 11 ++++++++--- Source/Engine/Debug/DebugCommands.cpp | 11 +++++++++++ Source/Engine/Debug/DebugCommands.h | 7 +++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 5c6e9a27e..7d3dfa644 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -325,12 +325,17 @@ namespace FlaxEditor.Windows // Show commands search popup based on current text input var text = Text.Trim(); - if (text.Length != 0) + bool isWhitespaceOnly = string.IsNullOrWhiteSpace(Text) && !string.IsNullOrEmpty(Text); + if (text.Length != 0 || isWhitespaceOnly) { DebugCommands.Search(text, out var matches); - if (matches.Length != 0) + if (matches.Length != 0 || isWhitespaceOnly) { - ShowPopup(ref _searchPopup, matches, text); + string[] commands = []; + if (isWhitespaceOnly) + DebugCommands.GetAllCommands(out commands); + + ShowPopup(ref _searchPopup, isWhitespaceOnly ? commands : matches, isWhitespaceOnly ? commands[0] : text); return; } } diff --git a/Source/Engine/Debug/DebugCommands.cpp b/Source/Engine/Debug/DebugCommands.cpp index 5d94cf557..9c66341ab 100644 --- a/Source/Engine/Debug/DebugCommands.cpp +++ b/Source/Engine/Debug/DebugCommands.cpp @@ -435,6 +435,17 @@ void DebugCommands::InitAsync() AsyncTask = Task::StartNew(InitCommands); } +void DebugCommands::GetAllCommands(Array& commands) +{ + EnsureInited(); + ScopeLock lock(Locker); + + for (auto& command : Commands) + { + commands.Add(command.Name); + } +} + DebugCommands::CommandFlags DebugCommands::GetCommandFlags(StringView command) { CommandFlags result = CommandFlags::None; diff --git a/Source/Engine/Debug/DebugCommands.h b/Source/Engine/Debug/DebugCommands.h index 73b2def69..6028ab9d8 100644 --- a/Source/Engine/Debug/DebugCommands.h +++ b/Source/Engine/Debug/DebugCommands.h @@ -46,6 +46,13 @@ public: /// API_FUNCTION() static void InitAsync(); + /// + /// Gets all available commands. + /// + /// The output list of all commands (unsorted). + /// TODO. + API_FUNCTION() static void GetAllCommands(API_PARAM(Out) Array& commands); + /// /// Returns flags of the command. /// From c4130aa20f649b64649d4c3e901c6546a1f50afd Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 4 Jun 2025 15:23:42 +0200 Subject: [PATCH 13/68] fix and improve show all commands on " " behavior --- Source/Editor/Windows/OutputLogWindow.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 7d3dfa644..f1efebeb4 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -335,7 +335,15 @@ namespace FlaxEditor.Windows if (isWhitespaceOnly) DebugCommands.GetAllCommands(out commands); - ShowPopup(ref _searchPopup, isWhitespaceOnly ? commands : matches, isWhitespaceOnly ? commands[0] : text); + ShowPopup(ref _searchPopup, isWhitespaceOnly ? commands : matches, text); + + if (isWhitespaceOnly) + { + // Scroll to and select first item for consistent behaviour + var firstItem = _searchPopup.ItemsPanel.Children[0] as Item; + _searchPopup.ScrollToAndHighlightItemByName(firstItem.Name); + } + return; } } From 6997cbeb477a34b92383b39d9dc4d7adfe3a5044 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 11 Jun 2025 13:08:59 +0200 Subject: [PATCH 14/68] add options for viewport icons --- Source/Editor/Options/ViewportOptions.cs | 21 +++++++++++++++++++ .../Utilities/ViewportIconsRenderer.cpp | 9 ++++---- .../Editor/Utilities/ViewportIconsRenderer.h | 15 +++++++++++++ Source/Editor/Viewport/EditorViewport.cs | 5 +++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Options/ViewportOptions.cs b/Source/Editor/Options/ViewportOptions.cs index 13ab2a8b0..aed633672 100644 --- a/Source/Editor/Options/ViewportOptions.cs +++ b/Source/Editor/Options/ViewportOptions.cs @@ -150,5 +150,26 @@ namespace FlaxEditor.Options [DefaultValue(typeof(Color), "0.5,0.5,0.5,1.0")] [EditorDisplay("Grid"), EditorOrder(310), Tooltip("The color for the viewport grid.")] public Color ViewportGridColor { get; set; } = new Color(0.5f, 0.5f, 0.5f, 1.0f); + + /// + /// Gets or sets the minimum size used for viewport icons. + /// + [DefaultValue(7.0f), Limit(1.0f, 1000.0f, 5.0f)] + [EditorDisplay("Viewport Icons"), EditorOrder(400)] + public float IconsMinimumSize { get; set; } = 7.0f; + + /// + /// Gets or sets the maximum size used for viewport icons. + /// + [DefaultValue(30.0f), Limit(1.0f, 1000.0f, 5.0f)] + [EditorDisplay("Viewport Icons"), EditorOrder(410)] + public float IconsMaximumSize { get; set; } = 30.0f; + + /// + /// Gets or sets the distance towards the camera at which the max icon scale will be applied. Set to 0 to disable scaling the icons based on the distance to the camera. + /// + [DefaultValue(1000.0f), Limit(0.0f, 20000.0f, 5.0f)] + [EditorDisplay("Viewport Icons"), EditorOrder(410)] + public float MaxSizeDistance { get; set; } = 1000.0f; } } diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index 1f721d289..690c55424 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -65,13 +65,14 @@ public: ViewportIconsRendererService ViewportIconsRendererServiceInstance; float ViewportIconsRenderer::Scale = 1.0f; +Real ViewportIconsRenderer::MinSize = 7.0f; +Real ViewportIconsRenderer::MaxSize = 30.0f; +Real ViewportIconsRenderer::MaxSizeDistance = 1000.0f; void ViewportIconsRenderer::GetBounds(const Vector3& position, const Vector3& viewPosition, BoundingSphere& bounds) { - constexpr Real minSize = 7.0; - constexpr Real maxSize = 30.0; - Real scale = Math::Square(Vector3::Distance(position, viewPosition) / 1000.0f); - Real radius = minSize + Math::Min(scale, 1.0f) * (maxSize - minSize); + Real scale = Math::Square(Vector3::Distance(position, viewPosition) / MaxSizeDistance); + Real radius = MinSize + Math::Min(scale, 1.0f) * (MaxSize - MinSize); bounds = BoundingSphere(position, radius * Scale); } diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.h b/Source/Editor/Utilities/ViewportIconsRenderer.h index c7bf7e1c3..a1e1538b8 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.h +++ b/Source/Editor/Utilities/ViewportIconsRenderer.h @@ -22,6 +22,21 @@ public: /// API_FIELD() static float Scale; + /// + /// The minimum size of the icons. + /// + API_FIELD() static Real MinSize; + + /// + /// The maximum size of the icons. + /// + API_FIELD() static Real MaxSize; + + /// + /// The distance to the camera at which the icons will be drawn at their maximum size. + /// + API_FIELD() static Real MaxSizeDistance; + /// /// Draws the icons for the actors in the given scene (or actor tree). /// diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index b4af43281..0f9fce7c0 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1292,6 +1292,11 @@ namespace FlaxEditor.Viewport _mouseSensitivity = options.Viewport.MouseSensitivity; _maxSpeedSteps = options.Viewport.TotalCameraSpeedSteps; _cameraEasingDegree = options.Viewport.CameraEasingDegree; + + ViewportIconsRenderer.MinSize = options.Viewport.IconsMinimumSize; + ViewportIconsRenderer.MaxSize = options.Viewport.IconsMaximumSize; + ViewportIconsRenderer.MaxSizeDistance = options.Viewport.MaxSizeDistance; + OnCameraMovementProgressChanged(); } From ec154b4998415c218dc5017a72813ab43407ae15 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 12 Jun 2025 19:31:32 +0200 Subject: [PATCH 15/68] remove unnecessary returns xml doc comment --- Source/Engine/Debug/DebugCommands.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Debug/DebugCommands.h b/Source/Engine/Debug/DebugCommands.h index 6028ab9d8..cfe70db73 100644 --- a/Source/Engine/Debug/DebugCommands.h +++ b/Source/Engine/Debug/DebugCommands.h @@ -50,7 +50,6 @@ public: /// Gets all available commands. /// /// The output list of all commands (unsorted). - /// TODO. API_FUNCTION() static void GetAllCommands(API_PARAM(Out) Array& commands); /// From f40c67ddf0c21a269685c640d83d7428466e0698 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Fri, 13 Jun 2025 22:05:57 +0200 Subject: [PATCH 16/68] add support to move visject socket connections --- Source/Editor/Surface/Elements/Box.cs | 12 +++- .../Surface/VisjectSurface.Connecting.cs | 37 +++++++----- .../Surface/VisjectSurface.ContextMenu.cs | 9 +-- Source/Editor/Surface/VisjectSurface.Draw.cs | 56 ++++++++++--------- Source/Editor/Surface/VisjectSurface.Input.cs | 8 +-- .../Surface/VisjectSurface.Serialization.cs | 2 +- Source/Editor/Surface/VisjectSurface.cs | 8 +-- 7 files changed, 77 insertions(+), 55 deletions(-) diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 16b6b3c16..54ea0292d 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -552,7 +552,17 @@ namespace FlaxEditor.Surface.Elements _isMouseDown = false; if (Surface.CanEdit) { - if (!IsOutput && HasSingleConnection) + if (Input.GetKey(KeyboardKeys.Control)) + { + List connectedBoxes = new List(Connections); + + for (int i = 0; i < connectedBoxes.Count; i++) + { + BreakConnection(connectedBoxes[i]); + Surface.ConnectingStart(connectedBoxes[i], true); + } + } + else if (!IsOutput && HasSingleConnection) { var connectedBox = Connections[0]; if (Surface.Undo != null && Surface.Undo.Enabled) diff --git a/Source/Editor/Surface/VisjectSurface.Connecting.cs b/Source/Editor/Surface/VisjectSurface.Connecting.cs index dbc2b7fb5..d733c7a0e 100644 --- a/Source/Editor/Surface/VisjectSurface.Connecting.cs +++ b/Source/Editor/Surface/VisjectSurface.Connecting.cs @@ -1,5 +1,6 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System.Collections.Generic; using FlaxEditor.Scripting; using FlaxEngine; @@ -233,11 +234,15 @@ namespace FlaxEditor.Surface /// Begins connecting surface objects action. /// /// The connection instigator (eg. start box). - public void ConnectingStart(IConnectionInstigator instigator) + /// If the instigator should be added to the list of instigators. + public void ConnectingStart(IConnectionInstigator instigator, bool additive = false) { if (instigator != null && instigator != _connectionInstigator) { - _connectionInstigator = instigator; + if (!additive) + _connectionInstigator.Clear(); + + _connectionInstigator.Add(instigator); StartMouseCapture(); } } @@ -257,22 +262,26 @@ namespace FlaxEditor.Surface /// The end object (eg. end box). public void ConnectingEnd(IConnectionInstigator end) { - // Ensure that there was a proper start box - if (_connectionInstigator == null) + // Ensure that there is at least one connection instigator + if (_connectionInstigator.Count == 0) return; - var start = _connectionInstigator; - _connectionInstigator = null; - - // Check if boxes are different and end box is specified - if (start == end || end == null) - return; - - // Connect them - if (start.CanConnectWith(end)) + List instigators = new List(_connectionInstigator); + for (int i = 0; i < instigators.Count; i++) { - start.Connect(end); + var start = _connectionInstigator[i]; + + // Check if boxes are different and end box is specified + if (start == end || end == null) + return; + + // Connect them + if (start.CanConnectWith(end)) + start.Connect(end); } + + // Reset instigator list + _connectionInstigator.Clear(); } } } diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index ee7dd33e5..98221ce6f 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -290,7 +290,7 @@ namespace FlaxEditor.Surface _cmStartPos = location; // Offset added in case the user doesn't like the box and wants to quickly get rid of it by clicking - OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, _connectionInstigator as Box); + OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, _connectionInstigator[0] as Box); if (!string.IsNullOrEmpty(input)) { @@ -475,9 +475,7 @@ namespace FlaxEditor.Surface private void OnPrimaryMenuVisibleChanged(Control primaryMenu) { if (!primaryMenu.Visible) - { - _connectionInstigator = null; - } + _connectionInstigator.Clear(); } /// @@ -555,8 +553,7 @@ namespace FlaxEditor.Surface } // If the user is patiently waiting for his box to get connected to the newly created one fulfill his wish! - - _connectionInstigator = startBox; + _connectionInstigator[0] = startBox; if (!IsConnecting) { diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index 5a63fe4de..dcf5e8108 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -1,5 +1,6 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System.Collections.Generic; using FlaxEditor.Surface.Elements; using FlaxEngine; @@ -126,40 +127,45 @@ namespace FlaxEditor.Surface /// Called only when user is connecting nodes. protected virtual void DrawConnectingLine() { - // Get start position - var startPos = _connectionInstigator.ConnectionOrigin; - - // Check if mouse is over any of box var cmVisible = _activeVisjectCM != null && _activeVisjectCM.Visible; var endPos = cmVisible ? _rootControl.PointFromParent(ref _cmStartPos) : _rootControl.PointFromParent(ref _mousePos); Color lineColor = Style.Colors.Connecting; - if (_lastInstigatorUnderMouse != null && !cmVisible) - { - // Check if can connect objects - bool canConnect = _connectionInstigator.CanConnectWith(_lastInstigatorUnderMouse); - lineColor = canConnect ? Style.Colors.ConnectingValid : Style.Colors.ConnectingInvalid; - endPos = _lastInstigatorUnderMouse.ConnectionOrigin; - } - Float2 actualStartPos = startPos; - Float2 actualEndPos = endPos; - - if (_connectionInstigator is Archetypes.Tools.RerouteNode) + List instigators = new List(_connectionInstigator); + for (int i = 0; i < instigators.Count; i++) { - if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true }) + IConnectionInstigator currentInstigator = instigators[i]; + Float2 currentStartPosition = currentInstigator.ConnectionOrigin; + + // Check if mouse is over any of box + if (_lastInstigatorUnderMouse != null && !cmVisible) + { + // Check if can connect objects + bool canConnect = currentInstigator.CanConnectWith(_lastInstigatorUnderMouse); + lineColor = canConnect ? Style.Colors.ConnectingValid : Style.Colors.ConnectingInvalid; + endPos = _lastInstigatorUnderMouse.ConnectionOrigin; + } + + Float2 actualStartPos = currentStartPosition; + Float2 actualEndPos = endPos; + + if (currentInstigator is Archetypes.Tools.RerouteNode) + { + if (endPos.X < currentStartPosition.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true }) + { + actualStartPos = endPos; + actualEndPos = currentStartPosition; + } + } + else if (currentInstigator is Box { IsOutput: false }) { actualStartPos = endPos; - actualEndPos = startPos; + actualEndPos = currentStartPosition; } - } - else if (_connectionInstigator is Box { IsOutput: false }) - { - actualStartPos = endPos; - actualEndPos = startPos; - } - // Draw connection - _connectionInstigator.DrawConnectingLine(ref actualStartPos, ref actualEndPos, ref lineColor); + // Draw connection + currentInstigator.DrawConnectingLine(ref actualStartPos, ref actualEndPos, ref lineColor); + } } /// diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index a874db681..25fdfe7e4 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -268,7 +268,7 @@ namespace FlaxEditor.Surface if (_leftMouseDown) { // Connecting - if (_connectionInstigator != null) + if (_connectionInstigator.Count > 0) { } // Moving @@ -438,7 +438,7 @@ namespace FlaxEditor.Surface public override bool OnMouseDown(Float2 location, MouseButton button) { // Check if user is connecting boxes - if (_connectionInstigator != null) + if (_connectionInstigator.Count > 0) return true; // Base @@ -560,7 +560,7 @@ namespace FlaxEditor.Surface _movingNodesDelta = Float2.Zero; } // Connecting - else if (_connectionInstigator != null) + else if (_connectionInstigator.Count > 0) { } // Selecting @@ -632,7 +632,7 @@ namespace FlaxEditor.Surface ShowPrimaryMenu(_cmStartPos); } // Letting go of a connection or right clicking while creating a connection - else if (!_isMovingSelection && _connectionInstigator != null && !IsPrimaryMenuOpened) + else if (!_isMovingSelection && _connectionInstigator.Count > 0 && !IsPrimaryMenuOpened) { _cmStartPos = location; Cursor = CursorType.Default; diff --git a/Source/Editor/Surface/VisjectSurface.Serialization.cs b/Source/Editor/Surface/VisjectSurface.Serialization.cs index e490d1550..cf5af9a4f 100644 --- a/Source/Editor/Surface/VisjectSurface.Serialization.cs +++ b/Source/Editor/Surface/VisjectSurface.Serialization.cs @@ -33,7 +33,7 @@ namespace FlaxEditor.Surface Enabled = false; // Clean data - _connectionInstigator = null; + _connectionInstigator.Clear(); _lastInstigatorUnderMouse = null; var failed = RootContext.Load(); diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index faecebbd3..19413ad48 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -121,7 +121,7 @@ namespace FlaxEditor.Surface /// /// The connection start. /// - protected IConnectionInstigator _connectionInstigator; + protected List _connectionInstigator = new List(); /// /// The last connection instigator under mouse. @@ -234,17 +234,17 @@ namespace FlaxEditor.Surface /// /// Gets a value indicating whether user is selecting nodes. /// - public bool IsSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator == null; + public bool IsSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator.Count == 0; /// /// Gets a value indicating whether user is moving selected nodes. /// - public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigator == null; + public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigator.Count == 0; /// /// Gets a value indicating whether user is connecting nodes. /// - public bool IsConnecting => _connectionInstigator != null; + public bool IsConnecting => _connectionInstigator.Count > 0; /// /// Gets a value indicating whether the left mouse button is down. From b44d4107c02ec12f39d02751c8c896590cd2b947 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Fri, 13 Jun 2025 22:25:02 +0200 Subject: [PATCH 17/68] fixes --- Source/Editor/Surface/VisjectSurface.Connecting.cs | 14 +++++++------- .../Editor/Surface/VisjectSurface.ContextMenu.cs | 7 ++++--- Source/Editor/Surface/VisjectSurface.Draw.cs | 2 +- Source/Editor/Surface/VisjectSurface.Input.cs | 8 ++++---- .../Editor/Surface/VisjectSurface.Serialization.cs | 2 +- Source/Editor/Surface/VisjectSurface.cs | 8 ++++---- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Connecting.cs b/Source/Editor/Surface/VisjectSurface.Connecting.cs index d733c7a0e..9b5ce1cf8 100644 --- a/Source/Editor/Surface/VisjectSurface.Connecting.cs +++ b/Source/Editor/Surface/VisjectSurface.Connecting.cs @@ -237,12 +237,12 @@ namespace FlaxEditor.Surface /// If the instigator should be added to the list of instigators. public void ConnectingStart(IConnectionInstigator instigator, bool additive = false) { - if (instigator != null && instigator != _connectionInstigator) + if (instigator != null && instigator != _connectionInstigators) { if (!additive) - _connectionInstigator.Clear(); + _connectionInstigators.Clear(); - _connectionInstigator.Add(instigator); + _connectionInstigators.Add(instigator); StartMouseCapture(); } } @@ -263,13 +263,13 @@ namespace FlaxEditor.Surface public void ConnectingEnd(IConnectionInstigator end) { // Ensure that there is at least one connection instigator - if (_connectionInstigator.Count == 0) + if (_connectionInstigators.Count == 0) return; - List instigators = new List(_connectionInstigator); + List instigators = new List(_connectionInstigators); for (int i = 0; i < instigators.Count; i++) { - var start = _connectionInstigator[i]; + var start = _connectionInstigators[i]; // Check if boxes are different and end box is specified if (start == end || end == null) @@ -281,7 +281,7 @@ namespace FlaxEditor.Surface } // Reset instigator list - _connectionInstigator.Clear(); + _connectionInstigators.Clear(); } } } diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index 98221ce6f..36ef020d3 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -290,7 +290,8 @@ namespace FlaxEditor.Surface _cmStartPos = location; // Offset added in case the user doesn't like the box and wants to quickly get rid of it by clicking - OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, _connectionInstigator[0] as Box); + Box startBox = _connectionInstigators.Count > 0 ? _connectionInstigators[0] as Box : null; + OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, startBox); if (!string.IsNullOrEmpty(input)) { @@ -475,7 +476,7 @@ namespace FlaxEditor.Surface private void OnPrimaryMenuVisibleChanged(Control primaryMenu) { if (!primaryMenu.Visible) - _connectionInstigator.Clear(); + _connectionInstigators.Clear(); } /// @@ -553,7 +554,7 @@ namespace FlaxEditor.Surface } // If the user is patiently waiting for his box to get connected to the newly created one fulfill his wish! - _connectionInstigator[0] = startBox; + _connectionInstigators[0] = startBox; if (!IsConnecting) { diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index dcf5e8108..5f8a7284a 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -131,7 +131,7 @@ namespace FlaxEditor.Surface var endPos = cmVisible ? _rootControl.PointFromParent(ref _cmStartPos) : _rootControl.PointFromParent(ref _mousePos); Color lineColor = Style.Colors.Connecting; - List instigators = new List(_connectionInstigator); + List instigators = new List(_connectionInstigators); for (int i = 0; i < instigators.Count; i++) { IConnectionInstigator currentInstigator = instigators[i]; diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 25fdfe7e4..dda112b20 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -268,7 +268,7 @@ namespace FlaxEditor.Surface if (_leftMouseDown) { // Connecting - if (_connectionInstigator.Count > 0) + if (_connectionInstigators.Count > 0) { } // Moving @@ -438,7 +438,7 @@ namespace FlaxEditor.Surface public override bool OnMouseDown(Float2 location, MouseButton button) { // Check if user is connecting boxes - if (_connectionInstigator.Count > 0) + if (_connectionInstigators.Count > 0) return true; // Base @@ -560,7 +560,7 @@ namespace FlaxEditor.Surface _movingNodesDelta = Float2.Zero; } // Connecting - else if (_connectionInstigator.Count > 0) + else if (_connectionInstigators.Count > 0) { } // Selecting @@ -632,7 +632,7 @@ namespace FlaxEditor.Surface ShowPrimaryMenu(_cmStartPos); } // Letting go of a connection or right clicking while creating a connection - else if (!_isMovingSelection && _connectionInstigator.Count > 0 && !IsPrimaryMenuOpened) + else if (!_isMovingSelection && _connectionInstigators.Count > 0 && !IsPrimaryMenuOpened) { _cmStartPos = location; Cursor = CursorType.Default; diff --git a/Source/Editor/Surface/VisjectSurface.Serialization.cs b/Source/Editor/Surface/VisjectSurface.Serialization.cs index cf5af9a4f..5bf7a9e34 100644 --- a/Source/Editor/Surface/VisjectSurface.Serialization.cs +++ b/Source/Editor/Surface/VisjectSurface.Serialization.cs @@ -33,7 +33,7 @@ namespace FlaxEditor.Surface Enabled = false; // Clean data - _connectionInstigator.Clear(); + _connectionInstigators.Clear(); _lastInstigatorUnderMouse = null; var failed = RootContext.Load(); diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index 19413ad48..f886682e4 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -121,7 +121,7 @@ namespace FlaxEditor.Surface /// /// The connection start. /// - protected List _connectionInstigator = new List(); + protected List _connectionInstigators = new List(); /// /// The last connection instigator under mouse. @@ -234,17 +234,17 @@ namespace FlaxEditor.Surface /// /// Gets a value indicating whether user is selecting nodes. /// - public bool IsSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator.Count == 0; + public bool IsSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigators.Count == 0; /// /// Gets a value indicating whether user is moving selected nodes. /// - public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigator.Count == 0; + public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigators.Count == 0; /// /// Gets a value indicating whether user is connecting nodes. /// - public bool IsConnecting => _connectionInstigator.Count > 0; + public bool IsConnecting => _connectionInstigators.Count > 0; /// /// Gets a value indicating whether the left mouse button is down. From baba151d8a5a095c2b250038ec13b9717f705136 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 14 Jun 2025 00:54:16 +0200 Subject: [PATCH 18/68] fix context menu --- Source/Editor/Surface/AnimGraphSurface.cs | 6 +- Source/Editor/Surface/BehaviorTreeSurface.cs | 4 +- .../Editor/Surface/ContextMenu/VisjectCM.cs | 34 ++++++----- .../Surface/VisjectSurface.ContextMenu.cs | 59 +++++++++---------- Source/Editor/Surface/VisualScriptSurface.cs | 4 +- 5 files changed, 54 insertions(+), 53 deletions(-) diff --git a/Source/Editor/Surface/AnimGraphSurface.cs b/Source/Editor/Surface/AnimGraphSurface.cs index 40cb1fd9a..237e9d019 100644 --- a/Source/Editor/Surface/AnimGraphSurface.cs +++ b/Source/Editor/Surface/AnimGraphSurface.cs @@ -229,20 +229,20 @@ namespace FlaxEditor.Surface } /// - protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox) + protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, List startBoxes) { // Check if show additional nodes in the current surface context if (activeCM != _cmStateMachineMenu) { _nodesCache.Get(activeCM); - base.OnShowPrimaryMenu(activeCM, location, startBox); + base.OnShowPrimaryMenu(activeCM, location, startBoxes); activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged; } else { - base.OnShowPrimaryMenu(activeCM, location, startBox); + base.OnShowPrimaryMenu(activeCM, location, startBoxes); } } diff --git a/Source/Editor/Surface/BehaviorTreeSurface.cs b/Source/Editor/Surface/BehaviorTreeSurface.cs index 9adcc9949..cf6d7b19a 100644 --- a/Source/Editor/Surface/BehaviorTreeSurface.cs +++ b/Source/Editor/Surface/BehaviorTreeSurface.cs @@ -101,12 +101,12 @@ namespace FlaxEditor.Surface } /// - protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox) + protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, List startBoxes) { activeCM.ShowExpanded = true; _nodesCache.Get(activeCM); - base.OnShowPrimaryMenu(activeCM, location, startBox); + base.OnShowPrimaryMenu(activeCM, location, startBoxes); activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged; } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 94b411fc2..de9261356 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -24,8 +24,8 @@ namespace FlaxEditor.Surface.ContextMenu /// Visject context menu item clicked delegate. /// /// The item that was clicked - /// The currently user-selected box. Can be null. - public delegate void ItemClickedDelegate(VisjectCMItem clickedItem, Elements.Box selectedBox); + /// The currently user-selected boxes. Can be empty/ null. + public delegate void ItemClickedDelegate(VisjectCMItem clickedItem, List selectedBoxes); /// /// Visject Surface node archetype spawn ability checking delegate. @@ -53,7 +53,7 @@ namespace FlaxEditor.Surface.ContextMenu private Panel _panel1; private VerticalPanel _groupsPanel; private readonly ParameterGetterDelegate _parametersGetter; - private Elements.Box _selectedBox; + private List _selectedBoxes = new List(); private NodeArchetype _parameterGetNodeArchetype; private NodeArchetype _parameterSetNodeArchetype; @@ -411,7 +411,8 @@ namespace FlaxEditor.Surface.ContextMenu if (!IsLayoutLocked) { group.UnlockChildrenRecursive(); - if (_contextSensitiveSearchEnabled && _selectedBox != null) + // TODO: Improve filtering to be based on boxes with the most common things instead of first box + if (_contextSensitiveSearchEnabled && _selectedBoxes[0] != null) UpdateFilters(); else SortGroups(); @@ -425,7 +426,8 @@ namespace FlaxEditor.Surface.ContextMenu } else if (_contextSensitiveSearchEnabled) { - group.EvaluateVisibilityWithBox(_selectedBox); + // TODO: Filtering could be improved here as well + group.EvaluateVisibilityWithBox(_selectedBoxes[0]); } Profiler.EndEvent(); @@ -461,7 +463,7 @@ namespace FlaxEditor.Surface.ContextMenu }; } if (_contextSensitiveSearchEnabled) - group.EvaluateVisibilityWithBox(_selectedBox); + group.EvaluateVisibilityWithBox(_selectedBoxes[0]); group.SortChildren(); if (ShowExpanded) group.Open(false); @@ -474,7 +476,7 @@ namespace FlaxEditor.Surface.ContextMenu if (!isLayoutLocked) { - if (_contextSensitiveSearchEnabled && _selectedBox != null) + if (_contextSensitiveSearchEnabled && _selectedBoxes[0] != null) UpdateFilters(); else SortGroups(); @@ -583,7 +585,7 @@ namespace FlaxEditor.Surface.ContextMenu private void UpdateFilters() { - if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBox == null) + if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes[0] == null) { ResetView(); Profiler.EndEvent(); @@ -592,7 +594,7 @@ namespace FlaxEditor.Surface.ContextMenu // Update groups LockChildrenRecursive(); - var contextSensitiveSelectedBox = _contextSensitiveSearchEnabled ? _selectedBox : null; + var contextSensitiveSelectedBox = _contextSensitiveSearchEnabled ? _selectedBoxes[0] : null; for (int i = 0; i < _groups.Count; i++) { _groups[i].UpdateFilter(_searchBox.Text, contextSensitiveSelectedBox); @@ -640,7 +642,7 @@ namespace FlaxEditor.Surface.ContextMenu public void OnClickItem(VisjectCMItem item) { Hide(); - ItemClicked?.Invoke(item, _selectedBox); + ItemClicked?.Invoke(item, _selectedBoxes); } /// @@ -666,12 +668,12 @@ namespace FlaxEditor.Surface.ContextMenu for (int i = 0; i < _groups.Count; i++) { _groups[i].ResetView(); - if (_contextSensitiveSearchEnabled) - _groups[i].EvaluateVisibilityWithBox(_selectedBox); + if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0) + _groups[i].EvaluateVisibilityWithBox(_selectedBoxes[0]); } UnlockChildrenRecursive(); - if (_contextSensitiveSearchEnabled && _selectedBox != null) + if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0 && _selectedBoxes[0] != null) UpdateFilters(); else SortGroups(); @@ -772,10 +774,10 @@ namespace FlaxEditor.Surface.ContextMenu /// /// Parent control to attach to it. /// Popup menu origin location in parent control coordinates. - /// The currently selected box that the new node will get connected to. Can be null - public void Show(Control parent, Float2 location, Elements.Box startBox) + /// The currently selected boxes that the new node will get connected to. Can be empty/ null + public void Show(Control parent, Float2 location, List startBoxes) { - _selectedBox = startBox; + _selectedBoxes = startBoxes; base.Show(parent, location); } diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index 36ef020d3..6505b4f64 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -252,10 +252,10 @@ namespace FlaxEditor.Surface /// /// The active context menu to show. /// The display location on the surface control. - /// The start box. - protected virtual void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox) + /// The start boxes. + protected virtual void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, List startBoxes) { - activeCM.Show(this, location, startBox); + activeCM.Show(this, location, startBoxes); } /// @@ -289,9 +289,10 @@ namespace FlaxEditor.Surface _cmStartPos = location; - // Offset added in case the user doesn't like the box and wants to quickly get rid of it by clicking - Box startBox = _connectionInstigators.Count > 0 ? _connectionInstigators[0] as Box : null; - OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, startBox); + List startBoxes = new List(_connectionInstigators.Cast()); + + // Position offset added so the user can quickly close the menu by clicking + OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, startBoxes); if (!string.IsNullOrEmpty(input)) { @@ -483,8 +484,8 @@ namespace FlaxEditor.Surface /// Handles Visject CM item click event by spawning the selected item. /// /// The item. - /// The selected box. - protected virtual void OnPrimaryMenuButtonClick(VisjectCMItem visjectCmItem, Box selectedBox) + /// The selected boxes. + protected virtual void OnPrimaryMenuButtonClick(VisjectCMItem visjectCmItem, List selectedBoxes) { if (!CanEdit) return; @@ -511,34 +512,36 @@ namespace FlaxEditor.Surface // Auto select new node Select(node); - if (selectedBox != null) + for (int i = 0; i < selectedBoxes.Count; i++) { - Box endBox = null; - foreach (var box in node.GetBoxes().Where(box => box.IsOutput != selectedBox.IsOutput)) + Box currentBox = selectedBoxes[i]; + if (currentBox != null) { - if (selectedBox.IsOutput) + Box endBox = null; + foreach (var box in node.GetBoxes().Where(box => box.IsOutput != currentBox.IsOutput)) { - if (box.CanUseType(selectedBox.CurrentType)) + if (currentBox.IsOutput) { - endBox = box; - break; + if (box.CanUseType(currentBox.CurrentType)) + { + endBox = box; + break; + } } - } - else - { - if (selectedBox.CanUseType(box.CurrentType)) + else { - endBox = box; - break; + if (currentBox.CanUseType(box.CurrentType)) + { + endBox = box; + break; + } } - } - if (endBox == null && selectedBox.CanUseType(box.CurrentType)) - { - endBox = box; + if (endBox == null && currentBox.CanUseType(box.CurrentType)) + endBox = box; } + TryConnect(currentBox, endBox); } - TryConnect(selectedBox, endBox); } } @@ -554,12 +557,8 @@ namespace FlaxEditor.Surface } // If the user is patiently waiting for his box to get connected to the newly created one fulfill his wish! - _connectionInstigators[0] = startBox; - if (!IsConnecting) - { ConnectingStart(startBox); - } ConnectingEnd(endBox); // Smart-Select next box diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index 0c11e54ff..2f70ee340 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -212,7 +212,7 @@ namespace FlaxEditor.Surface } /// - protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox) + protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, List startBoxes) { // Update nodes for method overrides Profiler.BeginEvent("Overrides"); @@ -268,7 +268,7 @@ namespace FlaxEditor.Surface // Update nodes for invoke methods (async) _nodesCache.Get(activeCM); - base.OnShowPrimaryMenu(activeCM, location, startBox); + base.OnShowPrimaryMenu(activeCM, location, startBoxes); activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged; } From 2e0c35e6e4eca5c0770aa1623f997f9b5e16888f Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 14 Jun 2025 01:04:35 +0200 Subject: [PATCH 19/68] fix out of range error when searching visject cm --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- Source/Editor/Surface/VisjectSurface.Draw.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index de9261356..42c863789 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -594,7 +594,7 @@ namespace FlaxEditor.Surface.ContextMenu // Update groups LockChildrenRecursive(); - var contextSensitiveSelectedBox = _contextSensitiveSearchEnabled ? _selectedBoxes[0] : null; + var contextSensitiveSelectedBox = _contextSensitiveSearchEnabled && _selectedBoxes.Count > 0 ? _selectedBoxes[0] : null; for (int i = 0; i < _groups.Count; i++) { _groups[i].UpdateFilter(_searchBox.Text, contextSensitiveSelectedBox); diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index 5f8a7284a..b413ae032 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -137,7 +137,7 @@ namespace FlaxEditor.Surface IConnectionInstigator currentInstigator = instigators[i]; Float2 currentStartPosition = currentInstigator.ConnectionOrigin; - // Check if mouse is over any of box + // Check if mouse is over any box if (_lastInstigatorUnderMouse != null && !cmVisible) { // Check if can connect objects From 92edb996f21df755f25613952cf52b98f7475159 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 14 Jun 2025 17:20:28 +0200 Subject: [PATCH 20/68] properly handle connecting to a box that already has a connection --- Source/Editor/Surface/Elements/Box.cs | 18 ++++++------------ .../Surface/VisjectSurface.Connecting.cs | 7 ++++++- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 54ea0292d..964b3cc69 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -544,15 +544,13 @@ namespace FlaxEditor.Surface.Elements public override void OnMouseLeave() { if (_originalTooltipText != null) - { TooltipText = _originalTooltipText; - } if (_isMouseDown) { _isMouseDown = false; if (Surface.CanEdit) { - if (Input.GetKey(KeyboardKeys.Control)) + if (IsOutput && Input.GetKey(KeyboardKeys.Control)) { List connectedBoxes = new List(Connections); @@ -564,25 +562,21 @@ namespace FlaxEditor.Surface.Elements } else if (!IsOutput && HasSingleConnection) { - var connectedBox = Connections[0]; + var otherBox = Connections[0]; if (Surface.Undo != null && Surface.Undo.Enabled) { - var action = new ConnectBoxesAction((InputBox)this, (OutputBox)connectedBox, false); - BreakConnection(connectedBox); + var action = new ConnectBoxesAction((InputBox)this, (OutputBox)otherBox, false); + BreakConnection(otherBox); action.End(); Surface.AddBatchedUndoAction(action); Surface.MarkAsEdited(); } else - { - BreakConnection(connectedBox); - } - Surface.ConnectingStart(connectedBox); + BreakConnection(otherBox); + Surface.ConnectingStart(otherBox); } else - { Surface.ConnectingStart(this); - } } } base.OnMouseLeave(); diff --git a/Source/Editor/Surface/VisjectSurface.Connecting.cs b/Source/Editor/Surface/VisjectSurface.Connecting.cs index 9b5ce1cf8..6f60ca2e2 100644 --- a/Source/Editor/Surface/VisjectSurface.Connecting.cs +++ b/Source/Editor/Surface/VisjectSurface.Connecting.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using FlaxEditor.Scripting; +using FlaxEditor.Surface.Elements; using FlaxEngine; namespace FlaxEditor.Surface @@ -269,11 +270,15 @@ namespace FlaxEditor.Surface List instigators = new List(_connectionInstigators); for (int i = 0; i < instigators.Count; i++) { - var start = _connectionInstigators[i]; + var start = instigators[i]; // Check if boxes are different and end box is specified if (start == end || end == null) return; + + // Properly handle connecting to a socket that already has a connection + if (end is Box e && !e.IsOutput && start is Box s && e.AreConnected(s)) + e.BreakConnection(s); // Connect them if (start.CanConnectWith(end)) From a4d3ede368222f87bcba6df8add9d668f848c868 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sun, 15 Jun 2025 02:08:47 +0200 Subject: [PATCH 21/68] fix context menu on reroute node --- Source/Editor/Surface/VisjectSurface.ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index 6505b4f64..5870125d4 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -289,7 +289,7 @@ namespace FlaxEditor.Surface _cmStartPos = location; - List startBoxes = new List(_connectionInstigators.Cast()); + List startBoxes = new List(_connectionInstigators.Where(c => c is Box).Cast()); // Position offset added so the user can quickly close the menu by clicking OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, startBoxes); From 53d4ea51af4ae620df77879b681969184fea49a5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 27 Jun 2025 21:18:53 -0500 Subject: [PATCH 22/68] Add prefab UI viewport scaling. --- Source/Editor/Gizmo/UIEditorGizmo.cs | 10 + Source/Editor/Modules/UIModule.cs | 100 ++++++ .../Editor/Viewport/PrefabWindowViewport.cs | 311 +++++++++++++++++- Source/Editor/Windows/Assets/PrefabWindow.cs | 10 + Source/Editor/Windows/GameWindow.cs | 186 ++++------- 5 files changed, 501 insertions(+), 116 deletions(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index bdc1c56ca..765893bed 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -155,6 +155,16 @@ namespace FlaxEditor private List _widgets; private Widget _activeWidget; + /// + /// Sets the view size. + /// + /// The new size. + public void SetViewSize(Float2 size) + { + _view.Size = size; + _view.PerformLayout(); + } + /// /// True if enable displaying UI editing background and grid elements. /// diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 19a0c1142..e62dd6b6e 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -37,6 +37,53 @@ namespace FlaxEditor.Modules private bool _progressFailed; ContextMenuSingleSelectGroup _numberOfClientsGroup = new ContextMenuSingleSelectGroup(); + + /// + /// Defines a viewport scaling option. + /// + public class ViewportScaleOption + { + /// + /// Defines the viewport scale type. + /// + public enum ViewportScaleType + { + /// + /// Resolution. + /// + Resolution = 0, + + /// + /// Aspect Ratio. + /// + Aspect = 1, + } + + /// + /// The name. + /// + public string Label; + + /// + /// The Type of scaling to do. + /// + public ViewportScaleType ScaleType; + + /// + /// The width and height to scale by. + /// + public Int2 Size; + } + + /// + /// The default viewport scaling options. + /// + public List DefaultViewportScaleOptions = new List(); + + /// + /// The user defined viewport scaling options. + /// + public List CustomViewportScaleOptions = new List(); private ContextMenuButton _menuFileSaveScenes; private ContextMenuButton _menuFileReloadScenes; @@ -371,6 +418,8 @@ namespace FlaxEditor.Modules // Update window background mainWindow.BackgroundColor = Style.Current.Background; + + InitViewportScaleOptions(); InitSharedMenus(); InitMainMenu(mainWindow); @@ -392,6 +441,57 @@ namespace FlaxEditor.Modules } } + private void InitViewportScaleOptions() + { + if (DefaultViewportScaleOptions.Count == 0) + { + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "Free Aspect", + ScaleType = ViewportScaleOption.ViewportScaleType.Aspect, + Size = new Int2(1, 1), + }); + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "16:9 Aspect", + ScaleType = ViewportScaleOption.ViewportScaleType.Aspect, + Size = new Int2(16, 9), + }); + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "16:10 Aspect", + ScaleType = ViewportScaleOption.ViewportScaleType.Aspect, + Size = new Int2(16, 10), + }); + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "1920x1080 Resolution (Full HD)", + ScaleType = ViewportScaleOption.ViewportScaleType.Resolution, + Size = new Int2(1920, 1080), + }); + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "2560x1440 Resolution (2K)", + ScaleType = ViewportScaleOption.ViewportScaleType.Resolution, + Size = new Int2(2560, 1440), + }); + } + + if (Editor.Instance.ProjectCache.TryGetCustomData("CustomViewportScalingOptions", out string data)) + { + CustomViewportScaleOptions = JsonSerializer.Deserialize>(data); + } + } + + /// + /// Saves the custom viewport scaling options. + /// + public void SaveCustomViewportScalingOptions() + { + var customOptions = JsonSerializer.Serialize(CustomViewportScaleOptions); + Editor.Instance.ProjectCache.SetCustomData("CustomViewportScalingOptions", customOptions); + } + /// public override void OnUpdate() { diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 2efe7c95f..f7c496703 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -6,6 +6,8 @@ using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.GUI.Input; +using FlaxEditor.Modules; using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; using FlaxEditor.Viewport.Cameras; @@ -13,6 +15,7 @@ using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEngine.Json; using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.Viewport @@ -71,7 +74,10 @@ namespace FlaxEditor.Viewport private PrefabUIEditorRoot _uiRoot; private bool _showUI = false; + private int _defaultScaleActiveIndex = 0; + private int _customScaleActiveIndex = -1; private ContextMenuButton _uiModeButton; + private ContextMenu _uiViewCM; /// /// Event fired when the UI Mode is toggled. @@ -213,7 +219,7 @@ namespace FlaxEditor.Viewport _uiModeButton = ViewWidgetShowMenu.AddButton("UI Mode", (button) => ShowUI = button.Checked); _uiModeButton.AutoCheck = true; _uiModeButton.VisibleChanged += control => (control as ContextMenuButton).Checked = ShowUI; - + EditorGizmoViewport.AddGizmoViewportWidgets(this, TransformGizmo); // Setup input actions @@ -222,6 +228,309 @@ namespace FlaxEditor.Viewport SetUpdate(ref _update, OnUpdate); } + /// + /// Creates the view scaling options. Needs to be called after a Prefab is valid and loaded. + /// + public void CreateViewScalingOptions() + { + // View Scaling + var uiViewCM = ViewWidgetButtonMenu.AddChildMenu("UI View Scaling"); + LoadCustomUIScalingOption(); + CreateUIViewScalingContextMenu(uiViewCM.ContextMenu); + } + + private void ChangeUIView(UIModule.ViewportScaleOption uiViewScaleOption) + { + _uiRoot.SetViewSize((Float2)uiViewScaleOption.Size); + } + + private void CreateUIViewScalingContextMenu(ContextMenu vsMenu) + { + // Add default viewport sizing options + var defaultOptions = Editor.Instance.UI.DefaultViewportScaleOptions; + for (int i = 0; i < defaultOptions.Count; i++) + { + var viewportScale = defaultOptions[i]; + + // Skip aspect ratio types in prefab + if (viewportScale.ScaleType == UIModule.ViewportScaleOption.ViewportScaleType.Aspect) + continue; + + var button = vsMenu.AddButton(viewportScale.Label); + button.CloseMenuOnClick = false; + button.Tag = viewportScale; + + // No default index is active. + if (_defaultScaleActiveIndex == -1) + { + button.Icon = SpriteHandle.Invalid; + } + // This is the active index. + else if (_defaultScaleActiveIndex == i) + { + button.Icon = Style.Current.CheckBoxTick; + ChangeUIView(viewportScale); + } + + button.Clicked += () => + { + if (button.Tag == null) + return; + + // Reset selected icon on all buttons + foreach (var child in vsMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) + { + if (cmb == button) + { + var index = defaultOptions.FindIndex(x => x == v); + _defaultScaleActiveIndex = index; + _customScaleActiveIndex = -1; // Reset custom index because default was chosen. + button.Icon = Style.Current.CheckBoxTick; + ChangeUIView(v); + } + else if (cmb.Icon != SpriteHandle.Invalid) + { + cmb.Icon = SpriteHandle.Invalid; + } + } + } + }; + } + if (defaultOptions.Count != 0) + vsMenu.AddSeparator(); + + // Add custom viewport options + var customOptions = Editor.Instance.UI.CustomViewportScaleOptions; + for (int i = 0; i < customOptions.Count; i++) + { + var viewportScale = customOptions[i]; + + // Skip aspect ratio types for prefabs + if (viewportScale.ScaleType == UIModule.ViewportScaleOption.ViewportScaleType.Aspect) + continue; + + var childCM = vsMenu.AddChildMenu(viewportScale.Label); + childCM.CloseMenuOnClick = false; + childCM.Tag = viewportScale; + + // No custom index is active. + if (_customScaleActiveIndex == -1) + { + childCM.Icon = SpriteHandle.Invalid; + } + // This is the active index. + else if (_customScaleActiveIndex == i) + { + childCM.Icon = Style.Current.CheckBoxTick; + ChangeUIView(viewportScale); + } + + var applyButton = childCM.ContextMenu.AddButton("Apply"); + applyButton.Tag = childCM.Tag = viewportScale; + applyButton.CloseMenuOnClick = false; + applyButton.Clicked += () => + { + if (childCM.Tag == null) + return; + + // Reset selected icon on all buttons + foreach (var child in vsMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) + { + if (child == childCM) + { + var index = customOptions.FindIndex(x => x == v); + _defaultScaleActiveIndex = -1; // Reset default index because custom was chosen. + _customScaleActiveIndex = index; + childCM.Icon = Style.Current.CheckBoxTick; + ChangeUIView(v); + } + else if (cmb.Icon != SpriteHandle.Invalid) + { + cmb.Icon = SpriteHandle.Invalid; + } + } + } + }; + + var deleteButton = childCM.ContextMenu.AddButton("Delete"); + deleteButton.CloseMenuOnClick = false; + deleteButton.Clicked += () => + { + if (childCM.Tag == null) + return; + + var v = (UIModule.ViewportScaleOption)childCM.Tag; + if (childCM.Icon != SpriteHandle.Invalid) + { + _customScaleActiveIndex = -1; + _defaultScaleActiveIndex = 0; + ChangeUIView(defaultOptions[0]); + } + customOptions.Remove(v); + Editor.Instance.UI.SaveCustomViewportScalingOptions(); + vsMenu.DisposeAllItems(); + CreateUIViewScalingContextMenu(vsMenu); + vsMenu.PerformLayout(); + }; + } + if (customOptions.Count != 0) + vsMenu.AddSeparator(); + + // Add button + var add = vsMenu.AddButton("Add..."); + add.CloseMenuOnClick = false; + add.Clicked += () => + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 95), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(add, new Float2(add.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalX += 10; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += 100; + nameTextBox.LocalY += 10; + + var whLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Width & Height", + HorizontalAlignment = TextAlignment.Near, + }; + whLabel.LocalX += 10; + whLabel.LocalY += 30; + + var wValue = new IntValueBox(1920) + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + MinValue = 1, + Width = 55, + }; + wValue.LocalY += 30; + wValue.LocalX += 100; + + var hValue = new IntValueBox(1080) + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + MinValue = 1, + Width = 55, + }; + hValue.LocalY += 30; + hValue.LocalX += 165; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Submit", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 60; + + submitButton.Clicked += () => + { + var name = nameTextBox.Text + " (" + wValue.Value + "x" + hValue.Value + ")"; + + var newViewportOption = new UIModule.ViewportScaleOption + { + Label = name, + ScaleType = UIModule.ViewportScaleOption.ViewportScaleType.Resolution, + Size = new Int2(wValue.Value, hValue.Value), + }; + + customOptions.Add(newViewportOption); + Editor.Instance.UI.SaveCustomViewportScalingOptions(); + vsMenu.DisposeAllItems(); + CreateUIViewScalingContextMenu(vsMenu); + vsMenu.PerformLayout(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 120; + cancelButton.LocalY += 60; + + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + hValue.Value = 9; + wValue.Value = 16; + popup.Hide(); + }; + }; + } + + /// + /// Saves the active ui scaling option. + /// + public void SaveActiveUIScalingOption() + { + var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex"; + Editor.Instance.ProjectCache.SetCustomData(defaultKey, _defaultScaleActiveIndex.ToString()); + var customKey = $"{Prefab.ID}:CustomViewportScalingIndex"; + Editor.Instance.ProjectCache.SetCustomData(customKey, _customScaleActiveIndex.ToString()); + } + + private void LoadCustomUIScalingOption() + { + Prefab.WaitForLoaded(); + var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex"; + if (Editor.Instance.ProjectCache.TryGetCustomData(defaultKey, out string defaultData)) + { + if (int.TryParse(defaultData, out var index)) + { + _defaultScaleActiveIndex = index; + if (index != -1) + { + ChangeUIView(Editor.Instance.UI.DefaultViewportScaleOptions[index]); + } + } + } + + var customKey = $"{Prefab.ID}:CustomViewportScalingIndex"; + if (Editor.Instance.ProjectCache.TryGetCustomData(customKey, out string data)) + { + if (int.TryParse(data, out var index)) + { + _customScaleActiveIndex = index; + if (index != -1) + { + ChangeUIView(Editor.Instance.UI.CustomViewportScaleOptions[index]); + } + } + } + } + private void OnUpdate(float deltaTime) { for (int i = 0; i < Gizmos.Count; i++) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 44c21d863..8202577d0 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -368,6 +368,7 @@ namespace FlaxEditor.Windows.Assets else _viewport.SetInitialUIMode(_viewport._hasUILinked); _viewport.UIModeToggled += OnUIModeToggled; + _viewport.CreateViewScalingOptions(); Graph.MainActor = _viewport.Instance; Selection.Clear(); Select(Graph.Main); @@ -564,6 +565,15 @@ namespace FlaxEditor.Windows.Assets Graph.Dispose(); } + /// + protected override void OnClose() + { + // Save current UI view size state. + _viewport.SaveActiveUIScalingOption(); + + base.OnClose(); + } + /// public EditorViewport PresenterViewport => _viewport; diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index e05f0d2db..1f5399926 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -6,6 +6,7 @@ using System.Xml; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; +using FlaxEditor.Modules; using FlaxEditor.Options; using FlaxEngine; using FlaxEngine.GUI; @@ -34,8 +35,8 @@ namespace FlaxEditor.Windows private CursorLockMode _cursorLockMode = CursorLockMode.None; // Viewport scaling variables - private List _defaultViewportScaling = new List(); - private List _customViewportScaling = new List(); + private int _defaultScaleActiveIndex = 0; + private int _customScaleActiveIndex = -1; private float _viewportAspectRatio = 1; private float _windowAspectRatio = 1; private bool _useAspect = false; @@ -234,35 +235,6 @@ namespace FlaxEditor.Windows /// public InterfaceOptions.PlayModeFocus FocusOnPlayOption { get; set; } - private enum ViewportScaleType - { - Resolution = 0, - Aspect = 1, - } - - private class ViewportScaleOptions - { - /// - /// The name. - /// - public string Label; - - /// - /// The Type of scaling to do. - /// - public ViewportScaleType ScaleType; - - /// - /// The width and height to scale by. - /// - public Int2 Size; - - /// - /// If the scaling is active. - /// - public bool Active; - } - private class PlayModeFocusOptions { /// @@ -408,7 +380,7 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand()); } - private void ChangeViewportRatio(ViewportScaleOptions v) + private void ChangeViewportRatio(UIModule.ViewportScaleOption v) { if (v == null) return; @@ -427,11 +399,11 @@ namespace FlaxEditor.Windows { switch (v.ScaleType) { - case ViewportScaleType.Aspect: + case UIModule.ViewportScaleOption.ViewportScaleType.Aspect: _useAspect = true; _freeAspect = false; break; - case ViewportScaleType.Resolution: + case UIModule.ViewportScaleOption.ViewportScaleType.Resolution: _useAspect = false; _freeAspect = false; break; @@ -629,45 +601,6 @@ namespace FlaxEditor.Windows // Viewport aspect ratio { // Create default scaling options if they dont exist from deserialization. - if (_defaultViewportScaling.Count == 0) - { - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "Free Aspect", - ScaleType = ViewportScaleType.Aspect, - Size = new Int2(1, 1), - Active = true, - }); - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "16:9 Aspect", - ScaleType = ViewportScaleType.Aspect, - Size = new Int2(16, 9), - Active = false, - }); - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "16:10 Aspect", - ScaleType = ViewportScaleType.Aspect, - Size = new Int2(16, 10), - Active = false, - }); - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "1920x1080 Resolution (Full HD)", - ScaleType = ViewportScaleType.Resolution, - Size = new Int2(1920, 1080), - Active = false, - }); - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "2560x1440 Resolution (2K)", - ScaleType = ViewportScaleType.Resolution, - Size = new Int2(2560, 1440), - Active = false, - }); - } - var vsMenu = menu.AddChildMenu("Viewport Size").ContextMenu; CreateViewportSizingContextMenu(vsMenu); @@ -771,15 +704,25 @@ namespace FlaxEditor.Windows private void CreateViewportSizingContextMenu(ContextMenu vsMenu) { // Add default viewport sizing options - for (int i = 0; i < _defaultViewportScaling.Count; i++) + var defaultOptions = Editor.UI.DefaultViewportScaleOptions; + for (int i = 0; i < defaultOptions.Count; i++) { - var viewportScale = _defaultViewportScaling[i]; + var viewportScale = defaultOptions[i]; var button = vsMenu.AddButton(viewportScale.Label); button.CloseMenuOnClick = false; - button.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; button.Tag = viewportScale; - if (viewportScale.Active) + + // No default index is active. + if (_defaultScaleActiveIndex == -1) + { + button.Icon = SpriteHandle.Invalid; + } + // This is the active index. + else if (_defaultScaleActiveIndex == i) + { + button.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(viewportScale); + } button.Clicked += () => { @@ -789,36 +732,47 @@ namespace FlaxEditor.Windows // Reset selected icon on all buttons foreach (var child in vsMenu.Items) { - if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v) + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) { if (cmb == button) { - v.Active = true; + var index = defaultOptions.FindIndex(x => x == v); + _defaultScaleActiveIndex = index; + _customScaleActiveIndex = -1; // Reset custom index because default was chosen. button.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(v); } - else if (v.Active) + else if (cmb.Icon != SpriteHandle.Invalid) { cmb.Icon = SpriteHandle.Invalid; - v.Active = false; } } } }; } - if (_defaultViewportScaling.Count != 0) + if (defaultOptions.Count != 0) vsMenu.AddSeparator(); // Add custom viewport options - for (int i = 0; i < _customViewportScaling.Count; i++) + var customOptions = Editor.UI.CustomViewportScaleOptions; + for (int i = 0; i < customOptions.Count; i++) { - var viewportScale = _customViewportScaling[i]; + var viewportScale = customOptions[i]; var childCM = vsMenu.AddChildMenu(viewportScale.Label); childCM.CloseMenuOnClick = false; - childCM.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; childCM.Tag = viewportScale; - if (viewportScale.Active) + + // No custom index is active. + if (_customScaleActiveIndex == -1) + { + childCM.Icon = SpriteHandle.Invalid; + } + // This is the active index. + else if (_customScaleActiveIndex == i) + { + childCM.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(viewportScale); + } var applyButton = childCM.ContextMenu.AddButton("Apply"); applyButton.Tag = childCM.Tag = viewportScale; @@ -831,18 +785,19 @@ namespace FlaxEditor.Windows // Reset selected icon on all buttons foreach (var child in vsMenu.Items) { - if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v) + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) { if (child == childCM) { - v.Active = true; + var index = customOptions.FindIndex(x => x == v); + _defaultScaleActiveIndex = -1; // Reset default index because custom was chosen. + _customScaleActiveIndex = index; childCM.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(v); } - else if (v.Active) + else if (cmb.Icon != SpriteHandle.Invalid) { cmb.Icon = SpriteHandle.Invalid; - v.Active = false; } } } @@ -855,20 +810,21 @@ namespace FlaxEditor.Windows if (childCM.Tag == null) return; - var v = (ViewportScaleOptions)childCM.Tag; - if (v.Active) + var v = (UIModule.ViewportScaleOption)childCM.Tag; + if (childCM.Icon != SpriteHandle.Invalid) { - v.Active = false; - _defaultViewportScaling[0].Active = true; - ChangeViewportRatio(_defaultViewportScaling[0]); + _customScaleActiveIndex = -1; + _defaultScaleActiveIndex = 0; + ChangeViewportRatio(defaultOptions[0]); } - _customViewportScaling.Remove(v); + customOptions.Remove(v); + Editor.UI.SaveCustomViewportScalingOptions(); vsMenu.DisposeAllItems(); CreateViewportSizingContextMenu(vsMenu); vsMenu.PerformLayout(); }; } - if (_customViewportScaling.Count != 0) + if (customOptions.Count != 0) vsMenu.AddSeparator(); // Add button @@ -966,19 +922,20 @@ namespace FlaxEditor.Windows submitButton.Clicked += () => { - Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleType type); + Enum.TryParse(typeDropdown.SelectedItem, out UIModule.ViewportScaleOption.ViewportScaleType type); - var combineString = type == ViewportScaleType.Aspect ? ":" : "x"; + var combineString = type == UIModule.ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x"; var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem; - var newViewportOption = new ViewportScaleOptions + var newViewportOption = new UIModule.ViewportScaleOption { ScaleType = type, Label = name, Size = new Int2(wValue.Value, hValue.Value), }; - _customViewportScaling.Add(newViewportOption); + customOptions.Add(newViewportOption); + Editor.UI.SaveCustomViewportScalingOptions(); vsMenu.DisposeAllItems(); CreateViewportSizingContextMenu(vsMenu); vsMenu.PerformLayout(); @@ -1221,8 +1178,8 @@ namespace FlaxEditor.Windows writer.WriteAttributeString("ShowGUI", ShowGUI.ToString()); writer.WriteAttributeString("EditGUI", EditGUI.ToString()); writer.WriteAttributeString("ShowDebugDraw", ShowDebugDraw.ToString()); - writer.WriteAttributeString("DefaultViewportScaling", JsonSerializer.Serialize(_defaultViewportScaling)); - writer.WriteAttributeString("CustomViewportScaling", JsonSerializer.Serialize(_customViewportScaling)); + writer.WriteAttributeString("DefaultViewportScalingIndex", _defaultScaleActiveIndex.ToString()); + writer.WriteAttributeString("CustomViewportScalingIndex", _customScaleActiveIndex.ToString()); } /// @@ -1234,22 +1191,21 @@ namespace FlaxEditor.Windows EditGUI = value1; if (bool.TryParse(node.GetAttribute("ShowDebugDraw"), out value1)) ShowDebugDraw = value1; - if (node.HasAttribute("CustomViewportScaling")) - _customViewportScaling = JsonSerializer.Deserialize>(node.GetAttribute("CustomViewportScaling")); + if (int.TryParse(node.GetAttribute("DefaultViewportScalingIndex"), out int value2)) + _defaultScaleActiveIndex = value2; + if (int.TryParse(node.GetAttribute("CustomViewportScalingIndex"), out value2)) + _customScaleActiveIndex = value2; - for (int i = 0; i < _customViewportScaling.Count; i++) + if (_defaultScaleActiveIndex != -1) { - if (_customViewportScaling[i].Active) - ChangeViewportRatio(_customViewportScaling[i]); + var options = Editor.UI.DefaultViewportScaleOptions; + ChangeViewportRatio(options[_defaultScaleActiveIndex]); } - - if (node.HasAttribute("DefaultViewportScaling")) - _defaultViewportScaling = JsonSerializer.Deserialize>(node.GetAttribute("DefaultViewportScaling")); - - for (int i = 0; i < _defaultViewportScaling.Count; i++) + + if (_customScaleActiveIndex != -1) { - if (_defaultViewportScaling[i].Active) - ChangeViewportRatio(_defaultViewportScaling[i]); + var options = Editor.UI.CustomViewportScaleOptions; + ChangeViewportRatio(options[_customScaleActiveIndex]); } } From b9cfd054c180d3268805c7f23dc8f35461bd75b2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 27 Jun 2025 21:38:10 -0500 Subject: [PATCH 23/68] Remove unused variable --- Source/Editor/Viewport/PrefabWindowViewport.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index f7c496703..2ca4feb27 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -77,7 +77,6 @@ namespace FlaxEditor.Viewport private int _defaultScaleActiveIndex = 0; private int _customScaleActiveIndex = -1; private ContextMenuButton _uiModeButton; - private ContextMenu _uiViewCM; /// /// Event fired when the UI Mode is toggled. From 52b64540ab26624f87b630606d4fc7bebd184a27 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 28 Jun 2025 10:46:11 -0500 Subject: [PATCH 24/68] Add extra index guard logic --- .../Editor/Viewport/PrefabWindowViewport.cs | 27 ++++++++++++++----- Source/Editor/Windows/GameWindow.cs | 13 +++++++-- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 2ca4feb27..10a6789a0 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -508,10 +508,17 @@ namespace FlaxEditor.Viewport { if (int.TryParse(defaultData, out var index)) { - _defaultScaleActiveIndex = index; - if (index != -1) + var options = Editor.Instance.UI.DefaultViewportScaleOptions; + if (options.Count > index) { - ChangeUIView(Editor.Instance.UI.DefaultViewportScaleOptions[index]); + _defaultScaleActiveIndex = index; + if (index != -1) + ChangeUIView(Editor.Instance.UI.DefaultViewportScaleOptions[index]); + } + // Assume option does not exist anymore so move to default. + else if (index != -1) + { + _defaultScaleActiveIndex = 0; } } } @@ -521,10 +528,18 @@ namespace FlaxEditor.Viewport { if (int.TryParse(data, out var index)) { - _customScaleActiveIndex = index; - if (index != -1) + var options = Editor.Instance.UI.CustomViewportScaleOptions; + if (options.Count > index) { - ChangeUIView(Editor.Instance.UI.CustomViewportScaleOptions[index]); + _customScaleActiveIndex = index; + if (index != -1) + ChangeUIView(options[index]); + } + // Assume option does not exist anymore so move to default. + else if (index != -1) + { + _defaultScaleActiveIndex = 0; + _customScaleActiveIndex = -1; } } } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 1f5399926..db3ea9d7e 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -1199,13 +1199,22 @@ namespace FlaxEditor.Windows if (_defaultScaleActiveIndex != -1) { var options = Editor.UI.DefaultViewportScaleOptions; - ChangeViewportRatio(options[_defaultScaleActiveIndex]); + if (options.Count > _defaultScaleActiveIndex) + ChangeViewportRatio(options[_defaultScaleActiveIndex]); + else + _defaultScaleActiveIndex = 0; } if (_customScaleActiveIndex != -1) { var options = Editor.UI.CustomViewportScaleOptions; - ChangeViewportRatio(options[_customScaleActiveIndex]); + if (options.Count > _customScaleActiveIndex) + ChangeViewportRatio(options[_customScaleActiveIndex]); + else + { + _defaultScaleActiveIndex = 0; + _customScaleActiveIndex = -1; + } } } From 5c7712daadbb49dd13388d7a2f7521fe8bf9b0ee Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Aug 2025 15:07:25 -0500 Subject: [PATCH 25/68] Add audio clip started and finished events to audio source --- Source/Engine/Audio/AudioSource.cpp | 15 ++++++++++++++- Source/Engine/Audio/AudioSource.h | 11 +++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index cff89e7e1..c15084dc3 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -168,7 +168,7 @@ void AudioSource::Play() else { // Source was nt properly added to the Audio Backend - LOG(Warning, "Cannot play unitialized audio source."); + LOG(Warning, "Cannot play uninitialized audio source."); } } @@ -395,6 +395,9 @@ void AudioSource::Update() AudioBackend::Source::VelocityChanged(SourceID, _velocity); } + if (Math::NearEqual(GetTime(), _startTime) && _isActuallyPlayingSth && _startingToPlay) + ClipStarted(); + // Reset starting to play value once time is greater than zero if (_startingToPlay && GetTime() > 0.0f) { @@ -416,6 +419,7 @@ void AudioSource::Update() { Stop(); } + ClipFinished(); } } @@ -486,6 +490,7 @@ void AudioSource::Update() { Stop(); } + ClipFinished(); } ASSERT(_streamingFirstChunk < clip->Buffers.Count()); @@ -583,3 +588,11 @@ void AudioSource::BeginPlay(SceneBeginData* data) SetTime(GetStartTime()); } } + +void AudioSource::EndPlay() +{ + Actor::EndPlay(); + + ClipStarted.UnbindAll(); + ClipFinished.UnbindAll(); +} diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index b83a2b408..4e05d1594 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -76,6 +76,16 @@ public: API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Audio Source\")") AssetReference Clip; + /// + /// Event fired when the audio clip starts. + /// + API_EVENT() Delegate<> ClipStarted; + + /// + /// Event fired when the audio clip finishes. + /// + API_EVENT() Delegate<> ClipFinished; + /// /// Gets the velocity of the source. Determines pitch in relation to AudioListener's position. Only relevant for spatial (3D) sources. /// @@ -326,4 +336,5 @@ protected: void OnDisable() override; void OnTransformChanged() override; void BeginPlay(SceneBeginData* data) override; + void EndPlay() override; }; From 0dc1e04c897606bd3ecb03b917ebef70b551b89e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Aug 2025 15:07:49 -0500 Subject: [PATCH 26/68] Add slider to audio debug editor for selecting time. --- .../Dedicated/AudioSourceEditor.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs b/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs index 1ddc1c144..0b15773b8 100644 --- a/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs @@ -13,6 +13,8 @@ namespace FlaxEditor.CustomEditors.Dedicated public class AudioSourceEditor : ActorEditor { private Label _infoLabel; + private Slider _slider; + private AudioSource.States _slideStartState; /// public override void Initialize(LayoutElementsContainer layout) @@ -28,6 +30,13 @@ namespace FlaxEditor.CustomEditors.Dedicated _infoLabel = playbackGroup.Label(string.Empty).Label; _infoLabel.AutoHeight = true; + // Play back slider + var sliderElement = playbackGroup.CustomContainer(); + _slider = sliderElement.CustomControl; + _slider.ThumbSize = new Float2(_slider.ThumbSize.X * 0.5f, _slider.ThumbSize.Y); + _slider.SlidingStart += OnSlidingStart; + _slider.SlidingEnd += OnSlidingEnd; + var grid = playbackGroup.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; @@ -40,6 +49,38 @@ namespace FlaxEditor.CustomEditors.Dedicated } } + private void OnSlidingEnd() + { + foreach (var value in Values) + { + if (value is AudioSource audioSource && audioSource.Clip) + { + switch (_slideStartState) + { + case AudioSource.States.Playing: + audioSource.Play(); + break; + case AudioSource.States.Paused: + case AudioSource.States.Stopped: + audioSource.Pause(); + break; + default: break; + } + } + } + } + + private void OnSlidingStart() + { + foreach (var value in Values) + { + if (value is AudioSource audioSource && audioSource.Clip) + { + _slideStartState = audioSource.State; + } + } + } + /// public override void Refresh() { @@ -51,7 +92,29 @@ namespace FlaxEditor.CustomEditors.Dedicated foreach (var value in Values) { if (value is AudioSource audioSource && audioSource.Clip) + { text += $"Time: {audioSource.Time:##0.0}s / {audioSource.Clip.Length:##0.0}s\n"; + _slider.Maximum = audioSource.Clip.Length; + _slider.Minimum = 0; + if (_slider.IsSliding) + { + if (audioSource.State != AudioSource.States.Playing) + { + // Play to move slider correctly + audioSource.Play(); + audioSource.Time = _slider.Value; + } + else + { + audioSource.Time = _slider.Value; + } + } + else + { + _slider.Value = audioSource.Time; + } + + } } _infoLabel.Text = text; } From 0e3a22faa0a0207f8b1acbb86de801dcbd430a2c Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Aug 2025 15:11:06 -0500 Subject: [PATCH 27/68] Fix spelling in comment. --- Source/Engine/Audio/AudioSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index c15084dc3..e17fbca0b 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -167,7 +167,7 @@ void AudioSource::Play() } else { - // Source was nt properly added to the Audio Backend + // Source was not properly added to the Audio Backend LOG(Warning, "Cannot play uninitialized audio source."); } } From 6f15ef769065ec3ac868a23ecd9aacdd55c84e72 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Aug 2025 15:15:54 -0500 Subject: [PATCH 28/68] Fix location of clip start call. --- Source/Engine/Audio/AudioSource.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index e17fbca0b..069fa29a3 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -395,15 +395,15 @@ void AudioSource::Update() AudioBackend::Source::VelocityChanged(SourceID, _velocity); } - if (Math::NearEqual(GetTime(), _startTime) && _isActuallyPlayingSth && _startingToPlay) - ClipStarted(); - // Reset starting to play value once time is greater than zero if (_startingToPlay && GetTime() > 0.0f) { _startingToPlay = false; } + if (Math::NearEqual(GetTime(), _startTime) && _isActuallyPlayingSth && _startingToPlay) + ClipStarted(); + if (!UseStreaming() && Math::NearEqual(GetTime(), 0.0f) && _isActuallyPlayingSth && !_startingToPlay) { int32 queuedBuffers; From e27880c1e65a69fddf96433dd0e93e57372f4fcc Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 2 Aug 2025 09:28:49 -0500 Subject: [PATCH 29/68] Make plugins into their own folders in VSCode. Order VSCode plugin folders with Engine at the bottom. --- .../VisualStudioCodeProjectGenerator.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs index c92ae6986..12b9d1e4e 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs @@ -648,6 +648,7 @@ namespace Flax.Build.Projects.VisualStudioCode json.AddField("**/Screenshots", true); json.AddField("**/Output", true); json.AddField("**/*.flax", true); + json.AddField("**/Plugins", true); json.EndObject(); // Extension settings @@ -683,9 +684,15 @@ namespace Flax.Build.Projects.VisualStudioCode json.EndObject(); // Referenced projects outside the current project (including engine too) - foreach (var project in Globals.Project.GetAllProjects()) + var projects = Globals.Project.GetAllProjects(); + // Move Engine to last for organizational purposes. + var engineProject = projects.First(x => x.Name == "Flax"); + var projectsWithoutEngine = projects.Where(x => x.Name != "Flax"); + projectsWithoutEngine = projectsWithoutEngine.OrderBy(x => x.Name); + var sortedProjects = projectsWithoutEngine.Concat([engineProject]); + foreach (var project in sortedProjects) { - if (!project.ProjectFolderPath.Contains(Globals.Project.ProjectFolderPath)) + if (!project.ProjectFolderPath.Equals(Globals.Project.ProjectFolderPath)) { json.BeginObject(); { From f0dea9d528a9f0f513d74a55fb16e1169dfaff79 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 10 Sep 2025 20:05:28 +0300 Subject: [PATCH 30/68] Add support for Visual Studio 2026 and v145 MSVC toolset --- .../InBuildSourceCodeEditor.cs | 4 ++++ Source/Editor/Scripting/CodeEditor.h | 5 +++++ .../VisualStudio/VisualStudioEditor.cpp | 6 ++++++ .../VisualStudio/VisualStudioEditor.h | 2 +- .../Flax.Build/Build/Builder.Projects.cs | 11 +++++++++-- Source/Tools/Flax.Build/Configuration.cs | 6 ++++++ .../Flax.Build/Platforms/GDK/GDKPlatform.cs | 5 ++++- .../Flax.Build/Platforms/UWP/UWPPlatform.cs | 10 ++++++++-- .../Flax.Build/Platforms/UWP/UWPToolchain.cs | 7 ++++++- .../Platforms/Windows/WindowsPlatform.cs | 3 ++- .../Platforms/Windows/WindowsPlatformBase.cs | 13 +++++++++++-- .../Platforms/Windows/WindowsToolchainBase.cs | 8 +++++++- .../Flax.Build/Projects/ProjectFormat.cs | 5 +++++ .../Flax.Build/Projects/ProjectGenerator.cs | 10 +++++++++- .../VisualStudio/CSProjectGenerator.cs | 4 +++- .../VisualStudio/CSSDKProjectGenerator.cs | 4 +++- .../VisualStudio/VCProjectGenerator.cs | 1 + .../VisualStudio/VisualStudioInstance.cs | 2 ++ .../VisualStudioProjectGenerator.cs | 19 +++++++++++++++++-- .../VisualStudio/VisualStudioVersion.cs | 5 +++++ 20 files changed, 114 insertions(+), 16 deletions(-) diff --git a/Source/Editor/Modules/SourceCodeEditing/InBuildSourceCodeEditor.cs b/Source/Editor/Modules/SourceCodeEditing/InBuildSourceCodeEditor.cs index 5aba20b65..a2a333805 100644 --- a/Source/Editor/Modules/SourceCodeEditing/InBuildSourceCodeEditor.cs +++ b/Source/Editor/Modules/SourceCodeEditing/InBuildSourceCodeEditor.cs @@ -54,6 +54,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing case CodeEditorTypes.VS2022: Name = "Visual Studio 2022"; break; + case CodeEditorTypes.VS2026: + Name = "Visual Studio 2026"; + break; case CodeEditorTypes.VSCode: Name = "Visual Studio Code"; break; @@ -110,6 +113,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing case CodeEditorTypes.VS2017: case CodeEditorTypes.VS2019: case CodeEditorTypes.VS2022: + case CodeEditorTypes.VS2026: // TODO: finish dynamic files adding to the project //Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync(); break; diff --git a/Source/Editor/Scripting/CodeEditor.h b/Source/Editor/Scripting/CodeEditor.h index 13edd6af1..9cc71977b 100644 --- a/Source/Editor/Scripting/CodeEditor.h +++ b/Source/Editor/Scripting/CodeEditor.h @@ -62,6 +62,11 @@ API_ENUM(Namespace="FlaxEditor", Attributes="HideInEditor") enum class CodeEdito /// VS2022, + /// + /// Visual Studio 2026 + /// + VS2026, + /// /// Visual Studio Code /// diff --git a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp index cebdc681d..5c06eec9c 100644 --- a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp @@ -43,6 +43,9 @@ VisualStudioEditor::VisualStudioEditor(VisualStudioVersion version, const String case VisualStudioVersion::VS2022: _type = CodeEditorTypes::VS2022; break; + case VisualStudioVersion::VS2026: + _type = CodeEditorTypes::VS2026; + break; default: CRASH; break; } @@ -70,6 +73,9 @@ void VisualStudioEditor::FindEditors(Array* output) VisualStudioVersion version; switch (info.VersionMajor) { + case 18: + version = VisualStudioVersion::VS2026; + break; case 17: version = VisualStudioVersion::VS2022; break; diff --git a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.h b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.h index 78f069897..1bf1f1433 100644 --- a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.h +++ b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.h @@ -10,7 +10,7 @@ /// /// Microsoft Visual Studio version types /// -DECLARE_ENUM_8(VisualStudioVersion, VS2008, VS2010, VS2012, VS2013, VS2015, VS2017, VS2019, VS2022); +DECLARE_ENUM_9(VisualStudioVersion, VS2008, VS2010, VS2012, VS2013, VS2015, VS2017, VS2019, VS2022, VS2026); /// /// Implementation of code editor utility that is using Microsoft Visual Studio. diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs index 59538364c..46925355c 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs @@ -192,6 +192,8 @@ namespace Flax.Build { // Pick the project format var projectFormats = new HashSet(); + if (Configuration.ProjectFormatVS2026) + projectFormats.Add(ProjectFormat.VisualStudio2026); if (Configuration.ProjectFormatVS2022) projectFormats.Add(ProjectFormat.VisualStudio2022); if (Configuration.ProjectFormatVS2019) @@ -209,8 +211,13 @@ namespace Flax.Build if (projectFormats.Count == 0) projectFormats.Add(Platform.BuildPlatform.DefaultProjectFormat); - // Always generate VS solution files for project (needed for C# Intellisense support) - projectFormats.Add(ProjectFormat.VisualStudio2022); + // Always generate VS solution files for project (needed for C# Intellisense support in other IDEs) + if (!projectFormats.Contains(ProjectFormat.VisualStudio2026) && + !projectFormats.Contains(ProjectFormat.VisualStudio2022) && + !projectFormats.Contains(ProjectFormat.VisualStudio)) + { + projectFormats.Add(ProjectFormat.VisualStudio2022); + } foreach (ProjectFormat projectFormat in projectFormats) GenerateProject(projectFormat); diff --git a/Source/Tools/Flax.Build/Configuration.cs b/Source/Tools/Flax.Build/Configuration.cs index f6ad3b3a5..dd6983b3b 100644 --- a/Source/Tools/Flax.Build/Configuration.cs +++ b/Source/Tools/Flax.Build/Configuration.cs @@ -201,6 +201,12 @@ namespace Flax.Build [CommandLine("vs2022", "Generates Visual Studio 2022 project format files. Valid only with -genproject option.")] public static bool ProjectFormatVS2022 = false; + /// + /// Generates Visual Studio 2026 project format files. Valid only with -genproject option. + /// + [CommandLine("vs2026", "Generates Visual Studio 2026 project format files. Valid only with -genproject option.")] + public static bool ProjectFormatVS2026 = false; + /// /// Generates Visual Studio Code project format files. Valid only with -genproject option. /// diff --git a/Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs b/Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs index 9313c65b0..422299c79 100644 --- a/Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs @@ -32,8 +32,11 @@ namespace Flax.Build.Platforms if (!toolsets.ContainsKey(WindowsPlatformToolset.v141) && !toolsets.ContainsKey(WindowsPlatformToolset.v142) && !toolsets.ContainsKey(WindowsPlatformToolset.v143) && - !toolsets.ContainsKey(WindowsPlatformToolset.v144)) + !toolsets.ContainsKey(WindowsPlatformToolset.v144) && + !toolsets.ContainsKey(WindowsPlatformToolset.v145)) + { _hasRequiredSDKsInstalled = false; + } } } } diff --git a/Source/Tools/Flax.Build/Platforms/UWP/UWPPlatform.cs b/Source/Tools/Flax.Build/Platforms/UWP/UWPPlatform.cs index 6d8b9e5b1..529e75eda 100644 --- a/Source/Tools/Flax.Build/Platforms/UWP/UWPPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/UWP/UWPPlatform.cs @@ -31,7 +31,12 @@ namespace Flax.Build.Platforms } // Visual Studio 2017+ supported only - var visualStudio = VisualStudioInstance.GetInstances().FirstOrDefault(x => x.Version == VisualStudioVersion.VisualStudio2017 || x.Version == VisualStudioVersion.VisualStudio2019 || x.Version == VisualStudioVersion.VisualStudio2022); + var visualStudio = VisualStudioInstance.GetInstances().FirstOrDefault(x => + x.Version == VisualStudioVersion.VisualStudio2017 || + x.Version == VisualStudioVersion.VisualStudio2019 || + x.Version == VisualStudioVersion.VisualStudio2022 || + x.Version == VisualStudioVersion.VisualStudio2026 + ); if (visualStudio == null) _hasRequiredSDKsInstalled = false; @@ -46,7 +51,8 @@ namespace Flax.Build.Platforms if (!toolsets.ContainsKey(WindowsPlatformToolset.v141) && !toolsets.ContainsKey(WindowsPlatformToolset.v142) && !toolsets.ContainsKey(WindowsPlatformToolset.v143) && - !toolsets.ContainsKey(WindowsPlatformToolset.v144)) + !toolsets.ContainsKey(WindowsPlatformToolset.v144) && + !toolsets.ContainsKey(WindowsPlatformToolset.v145)) { _hasRequiredSDKsInstalled = false; } diff --git a/Source/Tools/Flax.Build/Platforms/UWP/UWPToolchain.cs b/Source/Tools/Flax.Build/Platforms/UWP/UWPToolchain.cs index 983ac37d2..5458c5d7d 100644 --- a/Source/Tools/Flax.Build/Platforms/UWP/UWPToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/UWP/UWPToolchain.cs @@ -29,7 +29,12 @@ namespace Flax.Build.Platforms public UWPToolchain(UWPPlatform platform, TargetArchitecture architecture, WindowsPlatformToolset toolsetVer = WindowsPlatformToolset.Latest, WindowsPlatformSDK sdkVer = WindowsPlatformSDK.Latest) : base(platform, architecture, toolsetVer, sdkVer) { - var visualStudio = VisualStudioInstance.GetInstances().FirstOrDefault(x => x.Version == VisualStudioVersion.VisualStudio2017 || x.Version == VisualStudioVersion.VisualStudio2019 || x.Version == VisualStudioVersion.VisualStudio2022); + var visualStudio = VisualStudioInstance.GetInstances().FirstOrDefault(x => + x.Version == VisualStudioVersion.VisualStudio2017 || + x.Version == VisualStudioVersion.VisualStudio2019 || + x.Version == VisualStudioVersion.VisualStudio2022 || + x.Version == VisualStudioVersion.VisualStudio2026 + ); if (visualStudio == null) throw new Exception("Missing Visual Studio 2017 or newer. It's required to build for UWP."); _usingDirs.Add(Path.Combine(visualStudio.Path, "VC", "vcpackages")); diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatform.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatform.cs index 02c5ce8f0..a78f8c24f 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatform.cs @@ -40,7 +40,8 @@ namespace Flax.Build.Platforms !toolsets.ContainsKey(WindowsPlatformToolset.v141) && !toolsets.ContainsKey(WindowsPlatformToolset.v142) && !toolsets.ContainsKey(WindowsPlatformToolset.v143) && - !toolsets.ContainsKey(WindowsPlatformToolset.v144)) + !toolsets.ContainsKey(WindowsPlatformToolset.v144) && + !toolsets.ContainsKey(WindowsPlatformToolset.v145)) { Log.Warning("Missing MSVC toolset v140 or later (VS 2015 or later C++ build tools). Cannot build for Windows platform."); _hasRequiredSDKsInstalled = false; diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs index 3cdc2f44b..26afcd894 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs @@ -54,6 +54,11 @@ namespace Flax.Build.Platforms /// Visual Studio 2022 (v17.10 and later) /// v144 = 144, + + /// + /// Visual Studio 2026 + /// + v145 = 145, } /// @@ -252,6 +257,8 @@ namespace Flax.Build.Platforms _toolsets[WindowsPlatformToolset.v143] = toolset; else if (version.Major == 14 && version.Minor / 10 == 4) _toolsets[WindowsPlatformToolset.v144] = toolset; + else if (version.Major == 14 && version.Minor / 10 == 5) + _toolsets[WindowsPlatformToolset.v145] = toolset; else Log.Warning("Found Unsupported MSVC toolset version: " + version); } @@ -287,11 +294,12 @@ namespace Flax.Build.Platforms } } - // Visual Studio 2017-2022 - multiple instances + // Visual Studio 2017 or later - multiple instances foreach (var vs in vsInstances.Where(x => x.Version == VisualStudioVersion.VisualStudio2017 || x.Version == VisualStudioVersion.VisualStudio2019 || - x.Version == VisualStudioVersion.VisualStudio2022 + x.Version == VisualStudioVersion.VisualStudio2022 || + x.Version == VisualStudioVersion.VisualStudio2026 )) { FindMsvcToolsets(Path.Combine(vs.Path, "VC", "Tools", "MSVC")); @@ -469,6 +477,7 @@ namespace Flax.Build.Platforms case WindowsPlatformToolset.v142: case WindowsPlatformToolset.v143: case WindowsPlatformToolset.v144: + case WindowsPlatformToolset.v145: { string hostFolder = hostArchitecture == TargetArchitecture.x86 ? "HostX86" : $"Host{hostArchitecture.ToString().ToLower()}"; string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", hostFolder, architecture.ToString().ToLower(), "cl.exe"); diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 1979c4748..fe5abe4a3 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -89,7 +89,11 @@ namespace Flax.Build.Platforms // Pick the newest installed Visual Studio version if using the default toolset if (toolsetVer == WindowsPlatformToolset.Default) { - if (VisualStudioInstance.HasIDE(VisualStudioVersion.VisualStudio2022)) + if (VisualStudioInstance.HasIDE(VisualStudioVersion.VisualStudio2026)) + { + toolsetVer = WindowsPlatformToolset.v145; + } + else if (VisualStudioInstance.HasIDE(VisualStudioVersion.VisualStudio2022)) { if (toolsets.Keys.Contains(WindowsPlatformToolset.v144)) { @@ -206,6 +210,7 @@ namespace Flax.Build.Platforms case WindowsPlatformToolset.v142: case WindowsPlatformToolset.v143: case WindowsPlatformToolset.v144: + case WindowsPlatformToolset.v145: { switch (Architecture) { @@ -392,6 +397,7 @@ namespace Flax.Build.Platforms var vcToolChainDir = toolsets[Toolset]; switch (Toolset) { + case WindowsPlatformToolset.v145: case WindowsPlatformToolset.v144: case WindowsPlatformToolset.v143: case WindowsPlatformToolset.v142: diff --git a/Source/Tools/Flax.Build/Projects/ProjectFormat.cs b/Source/Tools/Flax.Build/Projects/ProjectFormat.cs index 5f5017ced..3e5fd3a70 100644 --- a/Source/Tools/Flax.Build/Projects/ProjectFormat.cs +++ b/Source/Tools/Flax.Build/Projects/ProjectFormat.cs @@ -37,6 +37,11 @@ namespace Flax.Build.Projects /// VisualStudio2022, + /// + /// Visual Studio 2026. + /// + VisualStudio2026, + /// /// Visual Studio Code. /// diff --git a/Source/Tools/Flax.Build/Projects/ProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/ProjectGenerator.cs index fbbeca14d..a6e5b79ad 100644 --- a/Source/Tools/Flax.Build/Projects/ProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/ProjectGenerator.cs @@ -84,7 +84,11 @@ namespace Flax.Build.Projects // Pick the newest installed Visual Studio version if (format == ProjectFormat.VisualStudio) { - if (VisualStudioInstance.HasIDE(VisualStudioVersion.VisualStudio2022)) + if (VisualStudioInstance.HasIDE(VisualStudioVersion.VisualStudio2026)) + { + format = ProjectFormat.VisualStudio2026; + } + else if (VisualStudioInstance.HasIDE(VisualStudioVersion.VisualStudio2022)) { format = ProjectFormat.VisualStudio2022; } @@ -113,6 +117,7 @@ namespace Flax.Build.Projects case ProjectFormat.VisualStudio2017: case ProjectFormat.VisualStudio2019: case ProjectFormat.VisualStudio2022: + case ProjectFormat.VisualStudio2026: { VisualStudioVersion vsVersion; switch (format) @@ -129,6 +134,9 @@ namespace Flax.Build.Projects case ProjectFormat.VisualStudio2022: vsVersion = VisualStudioVersion.VisualStudio2022; break; + case ProjectFormat.VisualStudio2026: + vsVersion = VisualStudioVersion.VisualStudio2026; + break; default: throw new ArgumentOutOfRangeException(nameof(format), format, null); } switch (type) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs index 35bc7b0ac..99e3b083b 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs @@ -50,7 +50,9 @@ namespace Flax.Build.Projects.VisualStudio projectTypes = ProjectTypeGuids.ToOption(ProjectTypeGuids.FlaxVS) + ';' + projectTypes; // Try to reuse the existing project guid from solution file - vsProject.ProjectGuid = GetProjectGuid(solutionPath, vsProject.Name); + vsProject.ProjectGuid = GetProjectGuid(vsProject.Path, vsProject.Name); + if (vsProject.ProjectGuid == Guid.Empty) + vsProject.ProjectGuid = GetProjectGuid(solutionPath, vsProject.Name); if (vsProject.ProjectGuid == Guid.Empty) vsProject.ProjectGuid = Guid.NewGuid(); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs index f126bd3ef..671cfb5e8 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs @@ -55,7 +55,9 @@ namespace Flax.Build.Projects.VisualStudio } // Try to reuse the existing project guid from solution file - vsProject.ProjectGuid = GetProjectGuid(solutionPath, vsProject.Name); + vsProject.ProjectGuid = GetProjectGuid(vsProject.Path, vsProject.Name); + if (vsProject.ProjectGuid == Guid.Empty) + vsProject.ProjectGuid = GetProjectGuid(solutionPath, vsProject.Name); if (vsProject.ProjectGuid == Guid.Empty) vsProject.ProjectGuid = Guid.NewGuid(); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs index 4fc77e760..1a52274a7 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs @@ -28,6 +28,7 @@ namespace Flax.Build.Projects.VisualStudio case VisualStudioVersion.VisualStudio2017: return "v141"; case VisualStudioVersion.VisualStudio2019: return "v142"; case VisualStudioVersion.VisualStudio2022: return "v143"; + case VisualStudioVersion.VisualStudio2026: return "v145"; } return string.Empty; } diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioInstance.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioInstance.cs index b3b67482f..16e5fee80 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioInstance.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioInstance.cs @@ -128,6 +128,8 @@ namespace Flax.Build.Projects.VisualStudio version = VisualStudioVersion.VisualStudio2019; else if (displayName.Contains("2022")) version = VisualStudioVersion.VisualStudio2022; + else if (displayName.Contains("2026")) + version = VisualStudioVersion.VisualStudio2026; else { Log.Warning(string.Format("Unknown Visual Studio installation. Display name: {0}", displayName)); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index 337f4497d..d657f6247 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -128,6 +128,7 @@ namespace Flax.Build.Projects.VisualStudio case VisualStudioVersion.VisualStudio2017: return "15.0"; case VisualStudioVersion.VisualStudio2019: return "16.0"; case VisualStudioVersion.VisualStudio2022: return "17.0"; + case VisualStudioVersion.VisualStudio2026: return "18.0"; } return string.Empty; @@ -193,7 +194,7 @@ namespace Flax.Build.Projects.VisualStudio } /// - public override string SolutionFileExtension => "sln"; + public override string SolutionFileExtension => /*Version >= VisualStudioVersion.VisualStudio2026 ? "slnx" :*/ "sln"; /// public override Project CreateProject() @@ -277,6 +278,20 @@ namespace Flax.Build.Projects.VisualStudio } } + if (Version >= VisualStudioVersion.VisualStudio2026) + GenerateXmlSolution(solution); + else + GenerateAsciiSolution(solution); + } + + private void GenerateXmlSolution(Solution solution) + { + // TODO: Generate the solution file in new format + GenerateAsciiSolution(solution); + } + + private void GenerateAsciiSolution(Solution solution) + { // Try to extract solution folder info from the existing solution file to make random IDs stable var solutionId = Guid.NewGuid(); var folderIds = new Dictionary(); @@ -313,7 +328,7 @@ namespace Flax.Build.Projects.VisualStudio var projects = solution.Projects.Cast().ToArray(); // Header - if (Version == VisualStudioVersion.VisualStudio2022) + if (Version >= VisualStudioVersion.VisualStudio2022) { vcSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); vcSolutionFileContent.AppendLine("# Visual Studio Version 17"); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioVersion.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioVersion.cs index 236aa5d7e..e7fc03c71 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioVersion.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioVersion.cs @@ -26,5 +26,10 @@ namespace Flax.Build.Projects.VisualStudio /// The Visual Studio 2022. /// VisualStudio2022, + + /// + /// The Visual Studio 2026. + /// + VisualStudio2026, } } From 3d182c89f31281d657a30302ced7206cb109ee04 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Sep 2025 18:04:01 +0200 Subject: [PATCH 31/68] Add `MaterialShader::BindParameters` to usable in game scripts #3668 --- Source/Engine/Graphics/Materials/IMaterial.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index 23d06589b..cd2b7a1e8 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -156,8 +156,8 @@ public: /// GPUTextureView* Input = nullptr; - BindParameters(::GPUContext* context, const ::RenderContext& renderContext); - BindParameters(::GPUContext* context, const ::RenderContext& renderContext, const ::DrawCall& drawCall, bool instanced = false); + FLAXENGINE_API BindParameters(::GPUContext* context, const ::RenderContext& renderContext); + FLAXENGINE_API BindParameters(::GPUContext* context, const ::RenderContext& renderContext, const ::DrawCall& drawCall, bool instanced = false); // Per-view shared constant buffer (see ViewData in MaterialCommon.hlsl). static GPUConstantBuffer* PerViewConstants; From a471861e9257c426404984b14e53ef61f769c243 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Sep 2025 23:15:13 +0200 Subject: [PATCH 32/68] Fix editing UI Brush in prefabs #3501 --- .../Editors/ObjectSwitcherEditor.cs | 16 +++++- .../Engine/UI/GUI/Brushes/GPUTextureBrush.cs | 28 +++++++++- Source/Engine/UI/GUI/Brushes/IBrush.cs | 4 +- .../UI/GUI/Brushes/LinearGradientBrush.cs | 28 +++++++++- Source/Engine/UI/GUI/Brushes/MaterialBrush.cs | 28 +++++++++- .../Engine/UI/GUI/Brushes/SolidColorBrush.cs | 28 +++++++++- Source/Engine/UI/GUI/Brushes/SpriteBrush.cs | 52 +++++++++++++++++- Source/Engine/UI/GUI/Brushes/TextureBrush.cs | 54 ++++++++++++++++++- Source/Engine/UI/GUI/Brushes/UIBrush.cs | 28 +++++++++- Source/Engine/UI/GUI/Brushes/VideoBrush.cs | 28 +++++++++- 10 files changed, 283 insertions(+), 11 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs index 85efbadc3..d32f08c92 100644 --- a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs @@ -3,6 +3,7 @@ using System; using FlaxEditor.GUI; using FlaxEditor.Scripting; +using FlaxEngine; using FlaxEngine.Utilities; namespace FlaxEditor.CustomEditors.Editors @@ -81,9 +82,13 @@ namespace FlaxEditor.CustomEditors.Editors private OptionType[] _options; private ScriptType _type; + private Elements.PropertiesListElement _typeItem; private ScriptType Type => Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]); + /// + public override bool RevertValueWithChildren => false; // Always revert value for a whole object + /// public override void Initialize(LayoutElementsContainer layout) { @@ -98,7 +103,8 @@ namespace FlaxEditor.CustomEditors.Editors _type = type; // Type - var typeEditor = layout.ComboBox(TypeComboBoxName, "Type of the object value. Use it to change the object."); + _typeItem = layout.AddPropertyItem(TypeComboBoxName, "Type of the object value. Use it to change the object."); + var typeEditor = _typeItem.ComboBox(); for (int i = 0; i < _options.Length; i++) { typeEditor.ComboBox.AddItem(_options[i].Name); @@ -126,6 +132,8 @@ namespace FlaxEditor.CustomEditors.Editors // Value var values = new CustomValueContainer(type, (instance, index) => instance); + if (Values.HasReferenceValue) + values.SetReferenceValue(Values.ReferenceValue); values.AddRange(Values); var editor = CustomEditorsUtil.CreateEditor(type); var style = editor.Style; @@ -170,6 +178,12 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); + // Show prefab diff when reference value type is different + var color = Color.Transparent; + if (Values.HasReferenceValue && CanRevertReferenceValue && Values[0].GetType() != Values.ReferenceValue.GetType()) + color = FlaxEngine.GUI.Style.Current.BackgroundSelected; + _typeItem.Labels[0].HighlightStripColor = color; + // Check if type has been modified outside the editor (eg. from code) if (Type != _type) { diff --git a/Source/Engine/UI/GUI/Brushes/GPUTextureBrush.cs b/Source/Engine/UI/GUI/Brushes/GPUTextureBrush.cs index fa4b5435d..781435740 100644 --- a/Source/Engine/UI/GUI/Brushes/GPUTextureBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/GPUTextureBrush.cs @@ -1,12 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// /// Implementation of for . /// /// - public sealed class GPUTextureBrush : IBrush + public sealed class GPUTextureBrush : IBrush, IEquatable { /// /// The GPU texture. @@ -47,5 +49,29 @@ namespace FlaxEngine.GUI else Render2D.DrawTexture(Texture, rect, color); } + + /// + public bool Equals(GPUTextureBrush other) + { + return other != null && Texture == other.Texture && Filter == other.Filter; + } + + /// + public override bool Equals(object obj) + { + return obj is GPUTextureBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Texture, (int)Filter); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } } diff --git a/Source/Engine/UI/GUI/Brushes/IBrush.cs b/Source/Engine/UI/GUI/Brushes/IBrush.cs index f11ff6857..861c0b7df 100644 --- a/Source/Engine/UI/GUI/Brushes/IBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/IBrush.cs @@ -1,5 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// @@ -23,7 +25,7 @@ namespace FlaxEngine.GUI /// /// Interface that unifies input source textures, sprites, render targets, and any other brushes to be used in a more generic way. /// - public interface IBrush + public interface IBrush : IComparable { /// /// Gets the size of the image brush in pixels (if relevant). diff --git a/Source/Engine/UI/GUI/Brushes/LinearGradientBrush.cs b/Source/Engine/UI/GUI/Brushes/LinearGradientBrush.cs index 1ece73d1c..d2155a31c 100644 --- a/Source/Engine/UI/GUI/Brushes/LinearGradientBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/LinearGradientBrush.cs @@ -1,12 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// /// Implementation of for linear color gradient (made of 2 color). /// /// - public sealed class LinearGradientBrush : IBrush + public sealed class LinearGradientBrush : IBrush, IEquatable { /// /// The brush start color. @@ -50,5 +52,29 @@ namespace FlaxEngine.GUI var endColor = EndColor * color; Render2D.FillRectangle(rect, startColor, startColor, endColor, endColor); } + + /// + public bool Equals(LinearGradientBrush other) + { + return other != null && StartColor == other.StartColor && EndColor == other.EndColor; + } + + /// + public override bool Equals(object obj) + { + return obj is LinearGradientBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(StartColor, EndColor); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } } diff --git a/Source/Engine/UI/GUI/Brushes/MaterialBrush.cs b/Source/Engine/UI/GUI/Brushes/MaterialBrush.cs index 13e9dab24..c610e9910 100644 --- a/Source/Engine/UI/GUI/Brushes/MaterialBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/MaterialBrush.cs @@ -1,12 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// /// Implementation of for rendering. /// /// - public sealed class MaterialBrush : IBrush + public sealed class MaterialBrush : IBrush, IEquatable { /// /// The material. @@ -38,5 +40,29 @@ namespace FlaxEngine.GUI { Render2D.DrawMaterial(Material, rect, color); } + + /// + public bool Equals(MaterialBrush other) + { + return other != null && Material == other.Material; + } + + /// + public override bool Equals(object obj) + { + return obj is MaterialBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Material); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } } diff --git a/Source/Engine/UI/GUI/Brushes/SolidColorBrush.cs b/Source/Engine/UI/GUI/Brushes/SolidColorBrush.cs index 1e4a20670..d848d9368 100644 --- a/Source/Engine/UI/GUI/Brushes/SolidColorBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/SolidColorBrush.cs @@ -1,12 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// /// Implementation of for single color fill. /// /// - public sealed class SolidColorBrush : IBrush + public sealed class SolidColorBrush : IBrush, IEquatable { /// /// The brush color. @@ -39,5 +41,29 @@ namespace FlaxEngine.GUI { Render2D.FillRectangle(rect, Color * color); } + + /// + public bool Equals(SolidColorBrush other) + { + return other != null && Color == other.Color; + } + + /// + public override bool Equals(object obj) + { + return obj is SolidColorBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Color); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } } diff --git a/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs b/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs index 205a9645c..24ca10015 100644 --- a/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs @@ -1,12 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// /// Implementation of for . /// /// - public sealed class SpriteBrush : IBrush + public sealed class SpriteBrush : IBrush, IEquatable { /// /// The sprite. @@ -47,6 +49,30 @@ namespace FlaxEngine.GUI else Render2D.DrawSprite(Sprite, rect, color); } + + /// + public bool Equals(SpriteBrush other) + { + return other != null && Sprite == other.Sprite && Filter == other.Filter; + } + + /// + public override bool Equals(object obj) + { + return obj is SpriteBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Sprite, (int)Filter); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } /// @@ -121,5 +147,29 @@ namespace FlaxEngine.GUI } #endif } + + /// + public bool Equals(Sprite9SlicingBrush other) + { + return other != null && Sprite == other.Sprite && Filter == other.Filter; + } + + /// + public override bool Equals(object obj) + { + return obj is Sprite9SlicingBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Sprite, (int)Filter); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } } diff --git a/Source/Engine/UI/GUI/Brushes/TextureBrush.cs b/Source/Engine/UI/GUI/Brushes/TextureBrush.cs index 189e222b9..e3922eebb 100644 --- a/Source/Engine/UI/GUI/Brushes/TextureBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/TextureBrush.cs @@ -1,12 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// /// Implementation of for . /// /// - public sealed class TextureBrush : IBrush + public sealed class TextureBrush : IBrush, IEquatable { /// /// The texture. @@ -47,13 +49,37 @@ namespace FlaxEngine.GUI else Render2D.DrawTexture(Texture, rect, color); } + + /// + public bool Equals(TextureBrush other) + { + return other != null && Texture == other.Texture && Filter == other.Filter; + } + + /// + public override bool Equals(object obj) + { + return obj is TextureBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Texture, (int)Filter); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } /// /// Implementation of for using 9-slicing. /// /// - public sealed class Texture9SlicingBrush : IBrush + public sealed class Texture9SlicingBrush : IBrush, IEquatable { /// /// The texture. @@ -130,5 +156,29 @@ namespace FlaxEngine.GUI } #endif } + + /// + public bool Equals(Texture9SlicingBrush other) + { + return other != null && Texture == other.Texture && Filter == other.Filter && BorderSize == other.BorderSize && Border == other.Border; + } + + /// + public override bool Equals(object obj) + { + return obj is Texture9SlicingBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Texture, (int)Filter, BorderSize, Border.GetHashCode()); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } } diff --git a/Source/Engine/UI/GUI/Brushes/UIBrush.cs b/Source/Engine/UI/GUI/Brushes/UIBrush.cs index 4441899c0..044c95285 100644 --- a/Source/Engine/UI/GUI/Brushes/UIBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/UIBrush.cs @@ -1,5 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// @@ -20,7 +22,7 @@ namespace FlaxEngine.GUI /// /// /// - public sealed class UIBrush : IBrush + public sealed class UIBrush : IBrush, IEquatable { /// /// The UI Brush asset to use. @@ -71,5 +73,29 @@ namespace FlaxEngine.GUI if (asset != null && asset.Brush != null) asset.Brush.Draw(rect, color); } + + /// + public bool Equals(UIBrush other) + { + return other != null && Asset == other.Asset; + } + + /// + public override bool Equals(object obj) + { + return obj is UIBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Asset); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } } diff --git a/Source/Engine/UI/GUI/Brushes/VideoBrush.cs b/Source/Engine/UI/GUI/Brushes/VideoBrush.cs index b8a54662c..25d942ca6 100644 --- a/Source/Engine/UI/GUI/Brushes/VideoBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/VideoBrush.cs @@ -1,12 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// /// Implementation of for frame displaying. /// /// - public sealed class VideoBrush : IBrush + public sealed class VideoBrush : IBrush, IEquatable { /// /// The video player to display frame from it. @@ -57,5 +59,29 @@ namespace FlaxEngine.GUI else Render2D.DrawTexture(texture, rect, color); } + + /// + public bool Equals(VideoBrush other) + { + return other != null && Player == other.Player && Filter == other.Filter; + } + + /// + public override bool Equals(object obj) + { + return obj is VideoBrush other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Player, (int)Filter); + } + + /// + public int CompareTo(object obj) + { + return Equals(obj) ? 1 : 0; + } } } From 9e1f488f22ebcb1912124461feb965c72fdef55b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 13 Sep 2025 14:41:11 +0200 Subject: [PATCH 33/68] Fix prefab window to use UI Mode for empty UI Control prefab #3574 --- Source/Editor/Viewport/Previews/PrefabPreview.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index 4ee763540..70316e4d9 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -112,8 +112,9 @@ namespace FlaxEditor.Viewport.Previews LinkCanvas(_instance); // Link UI control to the preview + var uiControl = _instance as UIControl; if (_uiControlLinked == null && - _instance is UIControl uiControl && + uiControl != null && uiControl.Control != null && uiControl.Control.Parent == null) { @@ -128,6 +129,12 @@ namespace FlaxEditor.Viewport.Previews _uiControlLinked.Control.Parent = _uiParentLink; _hasUILinked = true; } + + // Use UI mode when root is empty UI Control + if (_uiControlLinked == null && uiControl != null && uiControl.Control == null) + { + _hasUILinked = true; + } } private void LinkCanvas(Actor actor) From 88773e71e557b1d43afe4b31fd75754688049bc4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Sep 2025 14:25:19 +0200 Subject: [PATCH 34/68] Fix warning when cloning actor from prefab that is not a root #3578 --- Source/Engine/Level/Actor.cpp | 21 +++++++++++++++------ Source/Engine/Scripting/Scripting.cpp | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 0c0a98e14..b119797e7 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -1124,9 +1124,10 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) } else if (!parent && parentId.IsValid()) { + Guid tmpId; if (_prefabObjectID.IsValid()) LOG(Warning, "Missing parent actor {0} for \'{1}\', prefab object {2}", parentId, ToString(), _prefabObjectID); - else + else if (!modifier->IdsMapping.TryGet(parentId, tmpId) || tmpId.IsValid()) // Skip warning if object was mapped to empty id (intentionally ignored) LOG(Warning, "Missing parent actor {0} for \'{1}\'", parentId, ToString()); } } @@ -1694,7 +1695,7 @@ bool Actor::ToBytes(const Array& actors, MemoryWriteStream& output) } // Collect object ids that exist in the serialized data to allow references mapping later - Array ids(Math::RoundUpToPowerOf2(actors.Count() * 2)); + Array ids(actors.Count()); for (int32 i = 0; i < actors.Count(); i++) { // By default we collect actors and scripts (they are ManagedObjects recognized by the id) @@ -1997,19 +1998,27 @@ Actor* Actor::Clone() // Remap object ids into a new ones auto modifier = Cache::ISerializeModifier.Get(); - for (int32 i = 0; i < actors->Count(); i++) + for (const Actor* actor : *actors.Value) { - auto actor = actors->At(i); if (!actor) continue; modifier->IdsMapping.Add(actor->GetID(), Guid::New()); - for (int32 j = 0; j < actor->Scripts.Count(); j++) + for (const Script* script : actor->Scripts) { - const auto script = actor->Scripts[j]; if (script) modifier->IdsMapping.Add(script->GetID(), Guid::New()); } } + if (HasPrefabLink() && HasParent() && !IsPrefabRoot()) + { + // When cloning actor that is part of prefab (but not as whole), ignore the prefab hierarchy + Actor* parent = GetParent(); + do + { + modifier->IdsMapping.Add(parent->GetPrefabObjectID(), Guid::Empty); + parent = parent->GetParent(); + } while (parent && !parent->IsPrefabRoot()); + } // Deserialize objects Array output; diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index db6867d34..e042b4633 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -904,6 +904,8 @@ ScriptingObject* Scripting::FindObject(Guid id, const MClass* type) if (idsMapping) { idsMapping->TryGet(id, id); + if (!id.IsValid()) + return nullptr; // Skip warning if object was mapped to empty id (intentionally ignored) } // Try to find it From ada6b9140fc345ad0db7ea9447c29c0bebe26813 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Sep 2025 22:00:57 +0200 Subject: [PATCH 35/68] Minor adjustments for #3411 --- Content/Editor/Gizmo/MaterialAxisLocked.flax | 3 --- Source/Editor/Gizmo/TransformGizmoBase.Draw.cs | 17 ++++++++--------- Source/Editor/Options/VisualOptions.cs | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) delete mode 100644 Content/Editor/Gizmo/MaterialAxisLocked.flax diff --git a/Content/Editor/Gizmo/MaterialAxisLocked.flax b/Content/Editor/Gizmo/MaterialAxisLocked.flax deleted file mode 100644 index be44ece86..000000000 --- a/Content/Editor/Gizmo/MaterialAxisLocked.flax +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7518765847301a4b13625fb05d542fab4fc924190a7414d39227db817a0e29cb -size 661 diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs index 8523b9378..03c18268d 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs @@ -1,9 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using FlaxEditor.Options; -using FlaxEditor.SceneGraph; using FlaxEngine; -using System; namespace FlaxEditor.Gizmo { @@ -24,8 +22,8 @@ namespace FlaxEditor.Gizmo private MaterialBase _materialSphere; // Material Parameter Names - const String _brightnessParamName = "Brightness"; - const String _opacityParamName = "Opacity"; + private const string _brightnessParamName = "Brightness"; + private const string _opacityParamName = "Opacity"; /// /// Used for example when the selection can't be moved because one actor is static. @@ -82,7 +80,9 @@ namespace FlaxEditor.Gizmo { _isDisabled = ShouldGizmoBeLocked(); - float brightness = _isDisabled ? options.Visual.TransformGizmoBrighnessDisabled : options.Visual.TransformGizmoBrightness; + float brightness = _isDisabled ? options.Visual.TransformGizmoBrightnessDisabled : options.Visual.TransformGizmoBrightness; + if (Mathf.NearEqual(brightness, (float)_materialAxisX.GetParameterValue(_brightnessParamName))) + return; _materialAxisX.SetParameterValue(_brightnessParamName, brightness); _materialAxisY.SetParameterValue(_brightnessParamName, brightness); _materialAxisZ.SetParameterValue(_brightnessParamName, brightness); @@ -91,10 +91,10 @@ namespace FlaxEditor.Gizmo private bool ShouldGizmoBeLocked() { bool gizmoLocked = false; - - if (Editor.Instance.StateMachine.IsPlayMode) + if (Editor.Instance.StateMachine.IsPlayMode && Owner is Viewport.EditorGizmoViewport) { - foreach (SceneGraphNode obj in Editor.Instance.SceneEditing.Selection) + // Block editing static scene objects in main view during play mode + foreach (var obj in Editor.Instance.SceneEditing.Selection) { if (obj.CanTransform == false) { @@ -103,7 +103,6 @@ namespace FlaxEditor.Gizmo } } } - return gizmoLocked; } diff --git a/Source/Editor/Options/VisualOptions.cs b/Source/Editor/Options/VisualOptions.cs index 45a1f1b13..e0f0982f4 100644 --- a/Source/Editor/Options/VisualOptions.cs +++ b/Source/Editor/Options/VisualOptions.cs @@ -86,7 +86,7 @@ namespace FlaxEditor.Options /// [DefaultValue(0.25f), Range(0f, 5f)] [EditorDisplay("Transform Gizmo", "Disabled Gizmo Brightness"), EditorOrder(212)] - public float TransformGizmoBrighnessDisabled { get; set; } = 0.25f; + public float TransformGizmoBrightnessDisabled { get; set; } = 0.25f; /// /// Gets or sets a value indicating whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance. From 9749487e2449aff9e5320ae7140266dedd35c033 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Sep 2025 23:59:40 +0200 Subject: [PATCH 36/68] Move prefab and game UI size context menu to shared UI Module #3571 --- Source/Editor/Modules/UIModule.cs | 262 +++++++++++++++++ .../Editor/Viewport/PrefabWindowViewport.cs | 269 ++---------------- Source/Editor/Windows/GameWindow.cs | 269 +----------------- 3 files changed, 283 insertions(+), 517 deletions(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index e62dd6b6e..64f8c81e9 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -1185,5 +1185,267 @@ namespace FlaxEditor.Modules MenuTools = null; MenuHelp = null; } + + internal void CreateViewportSizingContextMenu(ContextMenu vsMenu, int defaultScaleActiveIndex, int customScaleActiveIndex, bool prefabViewport, Action changeView, Action changeActiveIndices) + { + // Add default viewport sizing options + var defaultOptions = DefaultViewportScaleOptions; + for (int i = 0; i < defaultOptions.Count; i++) + { + var viewportScale = defaultOptions[i]; + if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect) + continue; // Skip aspect ratio types in prefab + var button = vsMenu.AddButton(viewportScale.Label); + button.CloseMenuOnClick = false; + button.Tag = viewportScale; + + // No default index is active + if (defaultScaleActiveIndex == -1) + { + button.Icon = SpriteHandle.Invalid; + } + // This is the active index + else if (defaultScaleActiveIndex == i) + { + button.Icon = Style.Current.CheckBoxTick; + changeView(viewportScale); + } + + button.Clicked += () => + { + if (button.Tag == null) + return; + + // Reset selected icon on all buttons + foreach (var child in vsMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) + { + if (cmb == button) + { + button.Icon = Style.Current.CheckBoxTick; + var index = defaultOptions.FindIndex(x => x == v); + changeActiveIndices(index, -1); // Reset custom index because default was chosen + changeView(v); + } + else if (cmb.Icon != SpriteHandle.Invalid) + { + cmb.Icon = SpriteHandle.Invalid; + } + } + } + }; + } + if (defaultOptions.Count != 0) + vsMenu.AddSeparator(); + + // Add custom viewport options + var customOptions = CustomViewportScaleOptions; + for (int i = 0; i < customOptions.Count; i++) + { + var viewportScale = customOptions[i]; + if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect) + continue; // Skip aspect ratio types in prefab + var childCM = vsMenu.AddChildMenu(viewportScale.Label); + childCM.CloseMenuOnClick = false; + childCM.Tag = viewportScale; + + // No custom index is active + if (customScaleActiveIndex == -1) + { + childCM.Icon = SpriteHandle.Invalid; + } + // This is the active index + else if (customScaleActiveIndex == i) + { + childCM.Icon = Style.Current.CheckBoxTick; + changeView(viewportScale); + } + + var applyButton = childCM.ContextMenu.AddButton("Apply"); + applyButton.Tag = childCM.Tag = viewportScale; + applyButton.CloseMenuOnClick = false; + applyButton.Clicked += () => + { + if (childCM.Tag == null) + return; + + // Reset selected icon on all buttons + foreach (var child in vsMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) + { + if (child == childCM) + { + childCM.Icon = Style.Current.CheckBoxTick; + var index = customOptions.FindIndex(x => x == v); + changeActiveIndices(-1, index); // Reset default index because custom was chosen + changeView(v); + } + else if (cmb.Icon != SpriteHandle.Invalid) + { + cmb.Icon = SpriteHandle.Invalid; + } + } + } + }; + + var deleteButton = childCM.ContextMenu.AddButton("Delete"); + deleteButton.CloseMenuOnClick = false; + deleteButton.Clicked += () => + { + if (childCM.Tag == null) + return; + + var v = (ViewportScaleOption)childCM.Tag; + if (childCM.Icon != SpriteHandle.Invalid) + { + changeActiveIndices(-1, 0); + changeView(defaultOptions[0]); + } + customOptions.Remove(v); + SaveCustomViewportScalingOptions(); + vsMenu.DisposeAllItems(); + CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices); + vsMenu.PerformLayout(); + }; + } + if (customOptions.Count != 0) + vsMenu.AddSeparator(); + + // Add button + var add = vsMenu.AddButton("Add..."); + add.CloseMenuOnClick = false; + add.Clicked += () => + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(add, new Float2(add.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalX += 10; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += 100; + nameTextBox.LocalY += 10; + + var typeLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Type", + HorizontalAlignment = TextAlignment.Near, + }; + typeLabel.LocalX += 10; + typeLabel.LocalY += 35; + + var typeDropdown = new Dropdown + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Items = { "Aspect", "Resolution" }, + SelectedItem = "Aspect", + Visible = !prefabViewport, + Width = nameTextBox.Width + }; + typeDropdown.LocalY += 35; + typeDropdown.LocalX += 100; + + var whLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Width & Height", + HorizontalAlignment = TextAlignment.Near, + }; + whLabel.LocalX += 10; + whLabel.LocalY += 60; + + var wValue = new IntValueBox(16) + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + MinValue = 1, + Width = 55, + }; + wValue.LocalY += 60; + wValue.LocalX += 100; + + var hValue = new IntValueBox(9) + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + MinValue = 1, + Width = 55, + }; + hValue.LocalY += 60; + hValue.LocalX += 165; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Submit", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 90; + submitButton.Clicked += () => + { + Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleOption.ViewportScaleType type); + if (prefabViewport) + type = ViewportScaleOption.ViewportScaleType.Resolution; + + var combineString = type == ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x"; + var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem; + var newViewportOption = new ViewportScaleOption + { + ScaleType = type, + Label = name, + Size = new Int2(wValue.Value, hValue.Value), + }; + + customOptions.Add(newViewportOption); + SaveCustomViewportScalingOptions(); + vsMenu.DisposeAllItems(); + CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices); + vsMenu.PerformLayout(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 120; + cancelButton.LocalY += 90; + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + typeDropdown.SelectedItem = "Aspect"; + hValue.Value = 9; + wValue.Value = 16; + popup.Hide(); + }; + }; + } } } diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 1a018004e..c38b36f1e 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -73,10 +73,11 @@ namespace FlaxEditor.Viewport private PrefabUIEditorRoot _uiRoot; private bool _showUI = false; - + private int _defaultScaleActiveIndex = 0; private int _customScaleActiveIndex = -1; private ContextMenuButton _uiModeButton; + private ContextMenuChildMenu _uiViewOptions; /// /// Event fired when the UI Mode is toggled. @@ -142,6 +143,8 @@ namespace FlaxEditor.Viewport UseAutomaticTaskManagement = defaultFeatures; ShowDefaultSceneActors = defaultFeatures; TintColor = defaultFeatures ? Color.White : Color.Transparent; + if (_uiViewOptions != null) + _uiViewOptions.Visible = _showUI; UIModeToggled?.Invoke(_showUI); } } @@ -215,10 +218,10 @@ namespace FlaxEditor.Viewport _uiParentLink = _uiRoot.UIRoot; // UI mode buton - _uiModeButton = ViewWidgetShowMenu.AddButton("UI Mode", (button) => ShowUI = button.Checked); + _uiModeButton = ViewWidgetShowMenu.AddButton("UI Mode", button => ShowUI = button.Checked); _uiModeButton.AutoCheck = true; _uiModeButton.VisibleChanged += control => (control as ContextMenuButton).Checked = ShowUI; - + EditorGizmoViewport.AddGizmoViewportWidgets(this, TransformGizmo); // Setup input actions @@ -232,10 +235,16 @@ namespace FlaxEditor.Viewport /// public void CreateViewScalingOptions() { - // View Scaling - var uiViewCM = ViewWidgetButtonMenu.AddChildMenu("UI View Scaling"); + if (_uiViewOptions != null) + return; + _uiViewOptions = ViewWidgetButtonMenu.AddChildMenu("UI View Scaling"); + _uiViewOptions.Visible = _showUI; LoadCustomUIScalingOption(); - CreateUIViewScalingContextMenu(uiViewCM.ContextMenu); + Editor.Instance.UI.CreateViewportSizingContextMenu(_uiViewOptions.ContextMenu, _defaultScaleActiveIndex, _customScaleActiveIndex, true, ChangeUIView, (a, b) => + { + _defaultScaleActiveIndex = a; + _customScaleActiveIndex = b; + }); } private void ChangeUIView(UIModule.ViewportScaleOption uiViewScaleOption) @@ -243,252 +252,6 @@ namespace FlaxEditor.Viewport _uiRoot.SetViewSize((Float2)uiViewScaleOption.Size); } - private void CreateUIViewScalingContextMenu(ContextMenu vsMenu) - { - // Add default viewport sizing options - var defaultOptions = Editor.Instance.UI.DefaultViewportScaleOptions; - for (int i = 0; i < defaultOptions.Count; i++) - { - var viewportScale = defaultOptions[i]; - - // Skip aspect ratio types in prefab - if (viewportScale.ScaleType == UIModule.ViewportScaleOption.ViewportScaleType.Aspect) - continue; - - var button = vsMenu.AddButton(viewportScale.Label); - button.CloseMenuOnClick = false; - button.Tag = viewportScale; - - // No default index is active. - if (_defaultScaleActiveIndex == -1) - { - button.Icon = SpriteHandle.Invalid; - } - // This is the active index. - else if (_defaultScaleActiveIndex == i) - { - button.Icon = Style.Current.CheckBoxTick; - ChangeUIView(viewportScale); - } - - button.Clicked += () => - { - if (button.Tag == null) - return; - - // Reset selected icon on all buttons - foreach (var child in vsMenu.Items) - { - if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) - { - if (cmb == button) - { - var index = defaultOptions.FindIndex(x => x == v); - _defaultScaleActiveIndex = index; - _customScaleActiveIndex = -1; // Reset custom index because default was chosen. - button.Icon = Style.Current.CheckBoxTick; - ChangeUIView(v); - } - else if (cmb.Icon != SpriteHandle.Invalid) - { - cmb.Icon = SpriteHandle.Invalid; - } - } - } - }; - } - if (defaultOptions.Count != 0) - vsMenu.AddSeparator(); - - // Add custom viewport options - var customOptions = Editor.Instance.UI.CustomViewportScaleOptions; - for (int i = 0; i < customOptions.Count; i++) - { - var viewportScale = customOptions[i]; - - // Skip aspect ratio types for prefabs - if (viewportScale.ScaleType == UIModule.ViewportScaleOption.ViewportScaleType.Aspect) - continue; - - var childCM = vsMenu.AddChildMenu(viewportScale.Label); - childCM.CloseMenuOnClick = false; - childCM.Tag = viewportScale; - - // No custom index is active. - if (_customScaleActiveIndex == -1) - { - childCM.Icon = SpriteHandle.Invalid; - } - // This is the active index. - else if (_customScaleActiveIndex == i) - { - childCM.Icon = Style.Current.CheckBoxTick; - ChangeUIView(viewportScale); - } - - var applyButton = childCM.ContextMenu.AddButton("Apply"); - applyButton.Tag = childCM.Tag = viewportScale; - applyButton.CloseMenuOnClick = false; - applyButton.Clicked += () => - { - if (childCM.Tag == null) - return; - - // Reset selected icon on all buttons - foreach (var child in vsMenu.Items) - { - if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) - { - if (child == childCM) - { - var index = customOptions.FindIndex(x => x == v); - _defaultScaleActiveIndex = -1; // Reset default index because custom was chosen. - _customScaleActiveIndex = index; - childCM.Icon = Style.Current.CheckBoxTick; - ChangeUIView(v); - } - else if (cmb.Icon != SpriteHandle.Invalid) - { - cmb.Icon = SpriteHandle.Invalid; - } - } - } - }; - - var deleteButton = childCM.ContextMenu.AddButton("Delete"); - deleteButton.CloseMenuOnClick = false; - deleteButton.Clicked += () => - { - if (childCM.Tag == null) - return; - - var v = (UIModule.ViewportScaleOption)childCM.Tag; - if (childCM.Icon != SpriteHandle.Invalid) - { - _customScaleActiveIndex = -1; - _defaultScaleActiveIndex = 0; - ChangeUIView(defaultOptions[0]); - } - customOptions.Remove(v); - Editor.Instance.UI.SaveCustomViewportScalingOptions(); - vsMenu.DisposeAllItems(); - CreateUIViewScalingContextMenu(vsMenu); - vsMenu.PerformLayout(); - }; - } - if (customOptions.Count != 0) - vsMenu.AddSeparator(); - - // Add button - var add = vsMenu.AddButton("Add..."); - add.CloseMenuOnClick = false; - add.Clicked += () => - { - var popup = new ContextMenuBase - { - Size = new Float2(230, 95), - ClipChildren = false, - CullChildren = false, - }; - popup.Show(add, new Float2(add.Width, 0)); - - var nameLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Name", - HorizontalAlignment = TextAlignment.Near, - }; - nameLabel.LocalX += 10; - nameLabel.LocalY += 10; - - var nameTextBox = new TextBox - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - IsMultiline = false, - }; - nameTextBox.LocalX += 100; - nameTextBox.LocalY += 10; - - var whLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Width & Height", - HorizontalAlignment = TextAlignment.Near, - }; - whLabel.LocalX += 10; - whLabel.LocalY += 30; - - var wValue = new IntValueBox(1920) - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - MinValue = 1, - Width = 55, - }; - wValue.LocalY += 30; - wValue.LocalX += 100; - - var hValue = new IntValueBox(1080) - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - MinValue = 1, - Width = 55, - }; - hValue.LocalY += 30; - hValue.LocalX += 165; - - var submitButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Submit", - Width = 70, - }; - submitButton.LocalX += 40; - submitButton.LocalY += 60; - - submitButton.Clicked += () => - { - var name = nameTextBox.Text + " (" + wValue.Value + "x" + hValue.Value + ")"; - - var newViewportOption = new UIModule.ViewportScaleOption - { - Label = name, - ScaleType = UIModule.ViewportScaleOption.ViewportScaleType.Resolution, - Size = new Int2(wValue.Value, hValue.Value), - }; - - customOptions.Add(newViewportOption); - Editor.Instance.UI.SaveCustomViewportScalingOptions(); - vsMenu.DisposeAllItems(); - CreateUIViewScalingContextMenu(vsMenu); - vsMenu.PerformLayout(); - }; - - var cancelButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Cancel", - Width = 70, - }; - cancelButton.LocalX += 120; - cancelButton.LocalY += 60; - - cancelButton.Clicked += () => - { - nameTextBox.Clear(); - hValue.Value = 9; - wValue.Value = 16; - popup.Hide(); - }; - }; - } - /// /// Saves the active ui scaling option. /// @@ -522,7 +285,7 @@ namespace FlaxEditor.Viewport } } } - + var customKey = $"{Prefab.ID}:CustomViewportScalingIndex"; if (Editor.Instance.ProjectCache.TryGetCustomData(customKey, out string data)) { diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index a2b2d472d..63c2a1a48 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -606,10 +606,12 @@ namespace FlaxEditor.Windows // Viewport aspect ratio { - // Create default scaling options if they dont exist from deserialization. var vsMenu = menu.AddChildMenu("Viewport Size").ContextMenu; - - CreateViewportSizingContextMenu(vsMenu); + Editor.UI.CreateViewportSizingContextMenu(vsMenu, _defaultScaleActiveIndex, _customScaleActiveIndex, false, ChangeViewportRatio, (a, b) => + { + _defaultScaleActiveIndex = a; + _customScaleActiveIndex = b; + }); } // Take Screenshot @@ -707,267 +709,6 @@ namespace FlaxEditor.Windows } } - private void CreateViewportSizingContextMenu(ContextMenu vsMenu) - { - // Add default viewport sizing options - var defaultOptions = Editor.UI.DefaultViewportScaleOptions; - for (int i = 0; i < defaultOptions.Count; i++) - { - var viewportScale = defaultOptions[i]; - var button = vsMenu.AddButton(viewportScale.Label); - button.CloseMenuOnClick = false; - button.Tag = viewportScale; - - // No default index is active. - if (_defaultScaleActiveIndex == -1) - { - button.Icon = SpriteHandle.Invalid; - } - // This is the active index. - else if (_defaultScaleActiveIndex == i) - { - button.Icon = Style.Current.CheckBoxTick; - ChangeViewportRatio(viewportScale); - } - - button.Clicked += () => - { - if (button.Tag == null) - return; - - // Reset selected icon on all buttons - foreach (var child in vsMenu.Items) - { - if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) - { - if (cmb == button) - { - var index = defaultOptions.FindIndex(x => x == v); - _defaultScaleActiveIndex = index; - _customScaleActiveIndex = -1; // Reset custom index because default was chosen. - button.Icon = Style.Current.CheckBoxTick; - ChangeViewportRatio(v); - } - else if (cmb.Icon != SpriteHandle.Invalid) - { - cmb.Icon = SpriteHandle.Invalid; - } - } - } - }; - } - if (defaultOptions.Count != 0) - vsMenu.AddSeparator(); - - // Add custom viewport options - var customOptions = Editor.UI.CustomViewportScaleOptions; - for (int i = 0; i < customOptions.Count; i++) - { - var viewportScale = customOptions[i]; - var childCM = vsMenu.AddChildMenu(viewportScale.Label); - childCM.CloseMenuOnClick = false; - childCM.Tag = viewportScale; - - // No custom index is active. - if (_customScaleActiveIndex == -1) - { - childCM.Icon = SpriteHandle.Invalid; - } - // This is the active index. - else if (_customScaleActiveIndex == i) - { - childCM.Icon = Style.Current.CheckBoxTick; - ChangeViewportRatio(viewportScale); - } - - var applyButton = childCM.ContextMenu.AddButton("Apply"); - applyButton.Tag = childCM.Tag = viewportScale; - applyButton.CloseMenuOnClick = false; - applyButton.Clicked += () => - { - if (childCM.Tag == null) - return; - - // Reset selected icon on all buttons - foreach (var child in vsMenu.Items) - { - if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) - { - if (child == childCM) - { - var index = customOptions.FindIndex(x => x == v); - _defaultScaleActiveIndex = -1; // Reset default index because custom was chosen. - _customScaleActiveIndex = index; - childCM.Icon = Style.Current.CheckBoxTick; - ChangeViewportRatio(v); - } - else if (cmb.Icon != SpriteHandle.Invalid) - { - cmb.Icon = SpriteHandle.Invalid; - } - } - } - }; - - var deleteButton = childCM.ContextMenu.AddButton("Delete"); - deleteButton.CloseMenuOnClick = false; - deleteButton.Clicked += () => - { - if (childCM.Tag == null) - return; - - var v = (UIModule.ViewportScaleOption)childCM.Tag; - if (childCM.Icon != SpriteHandle.Invalid) - { - _customScaleActiveIndex = -1; - _defaultScaleActiveIndex = 0; - ChangeViewportRatio(defaultOptions[0]); - } - customOptions.Remove(v); - Editor.UI.SaveCustomViewportScalingOptions(); - vsMenu.DisposeAllItems(); - CreateViewportSizingContextMenu(vsMenu); - vsMenu.PerformLayout(); - }; - } - if (customOptions.Count != 0) - vsMenu.AddSeparator(); - - // Add button - var add = vsMenu.AddButton("Add..."); - add.CloseMenuOnClick = false; - add.Clicked += () => - { - var popup = new ContextMenuBase - { - Size = new Float2(230, 125), - ClipChildren = false, - CullChildren = false, - }; - popup.Show(add, new Float2(add.Width, 0)); - - var nameLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Name", - HorizontalAlignment = TextAlignment.Near, - }; - nameLabel.LocalX += 10; - nameLabel.LocalY += 10; - - var nameTextBox = new TextBox - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - IsMultiline = false, - }; - nameTextBox.LocalX += 100; - nameTextBox.LocalY += 10; - - var typeLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Type", - HorizontalAlignment = TextAlignment.Near, - }; - typeLabel.LocalX += 10; - typeLabel.LocalY += 35; - - var typeDropdown = new Dropdown - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Items = { "Aspect", "Resolution" }, - SelectedItem = "Aspect", - Width = nameTextBox.Width - }; - typeDropdown.LocalY += 35; - typeDropdown.LocalX += 100; - - var whLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Width & Height", - HorizontalAlignment = TextAlignment.Near, - }; - whLabel.LocalX += 10; - whLabel.LocalY += 60; - - var wValue = new IntValueBox(16) - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - MinValue = 1, - Width = 55, - }; - wValue.LocalY += 60; - wValue.LocalX += 100; - - var hValue = new IntValueBox(9) - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - MinValue = 1, - Width = 55, - }; - hValue.LocalY += 60; - hValue.LocalX += 165; - - var submitButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Submit", - Width = 70, - }; - submitButton.LocalX += 40; - submitButton.LocalY += 90; - - submitButton.Clicked += () => - { - Enum.TryParse(typeDropdown.SelectedItem, out UIModule.ViewportScaleOption.ViewportScaleType type); - - var combineString = type == UIModule.ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x"; - var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem; - - var newViewportOption = new UIModule.ViewportScaleOption - { - ScaleType = type, - Label = name, - Size = new Int2(wValue.Value, hValue.Value), - }; - - customOptions.Add(newViewportOption); - Editor.UI.SaveCustomViewportScalingOptions(); - vsMenu.DisposeAllItems(); - CreateViewportSizingContextMenu(vsMenu); - vsMenu.PerformLayout(); - }; - - var cancelButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Cancel", - Width = 70, - }; - cancelButton.LocalX += 120; - cancelButton.LocalY += 90; - - cancelButton.Clicked += () => - { - nameTextBox.Clear(); - typeDropdown.SelectedItem = "Aspect"; - hValue.Value = 9; - wValue.Value = 16; - popup.Hide(); - }; - }; - } - /// public override void Draw() { From 7a4072296479e5b0cb38855734dd1829b1b242ea Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 14 Sep 2025 17:51:27 -0500 Subject: [PATCH 37/68] Fix view distance light property from affecting brightness all of the time. --- Source/Engine/Level/Actors/Light.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actors/Light.h b/Source/Engine/Level/Actors/Light.h index 08d311b6d..0e2a441db 100644 --- a/Source/Engine/Level/Actors/Light.h +++ b/Source/Engine/Level/Actors/Light.h @@ -63,7 +63,8 @@ protected: { const float dst2 = (float)Vector3::DistanceSquared(viewPosition, position); const float dst = Math::Sqrt(dst2); - brightness *= Math::Remap(dst, 0.9f * ViewDistance, ViewDistance, 1.0f, 0.0f); + if (dst < ViewDistance && dst > ViewDistance * 0.9f) + brightness *= Math::Remap(dst, 0.9f * ViewDistance, ViewDistance, 1.0f, 0.0f); return dst < ViewDistance; } return true; From ae4ae7a638e26a33985836b33362942c6e65a692 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Sep 2025 15:17:18 +0200 Subject: [PATCH 38/68] Fix rich textbox vertical alignment #3502 --- .../UI/GUI/Common/RichTextBox.Parsing.cs | 29 ++++++++++++++++--- Source/Engine/UI/GUI/TextBlockStyle.cs | 3 ++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index 9a10a327c..20ef1c401 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -240,11 +240,13 @@ namespace FlaxEngine.GUI // Organize text blocks within line var horizontalAlignments = TextBlockStyle.Alignments.Baseline; + var verticalAlignments = TextBlockStyle.Alignments.Baseline; for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) { ref TextBlock textBlock = ref textBlocks[i]; var vOffset = lineSize.Y - textBlock.Bounds.Height; horizontalAlignments |= textBlock.Style.Alignment & TextBlockStyle.Alignments.HorizontalMask; + verticalAlignments |= textBlock.Style.Alignment & TextBlockStyle.Alignments.VerticalMask; switch (textBlock.Style.Alignment & TextBlockStyle.Alignments.VerticalMask) { case TextBlockStyle.Alignments.Baseline: @@ -272,14 +274,16 @@ namespace FlaxEngine.GUI } } } - var hOffset = Width - lineSize.X; + + // Organize blocks within whole container + var sizeOffset = Size - lineSize; if ((horizontalAlignments & TextBlockStyle.Alignments.Center) == TextBlockStyle.Alignments.Center) { - hOffset *= 0.5f; + sizeOffset.X *= 0.5f; for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) { ref TextBlock textBlock = ref textBlocks[i]; - textBlock.Bounds.Location.X += hOffset; + textBlock.Bounds.Location.X += sizeOffset.X; } } else if ((horizontalAlignments & TextBlockStyle.Alignments.Right) == TextBlockStyle.Alignments.Right) @@ -287,7 +291,24 @@ namespace FlaxEngine.GUI for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) { ref TextBlock textBlock = ref textBlocks[i]; - textBlock.Bounds.Location.X += hOffset; + textBlock.Bounds.Location.X += sizeOffset.X; + } + } + if ((verticalAlignments & TextBlockStyle.Alignments.Middle) == TextBlockStyle.Alignments.Middle) + { + sizeOffset.Y *= 0.5f; + for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) + { + ref TextBlock textBlock = ref textBlocks[i]; + textBlock.Bounds.Location.Y += sizeOffset.Y; + } + } + else if ((verticalAlignments & TextBlockStyle.Alignments.Bottom) == TextBlockStyle.Alignments.Bottom) + { + for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) + { + ref TextBlock textBlock = ref textBlocks[i]; + textBlock.Bounds.Location.Y += sizeOffset.Y; } } diff --git a/Source/Engine/UI/GUI/TextBlockStyle.cs b/Source/Engine/UI/GUI/TextBlockStyle.cs index 399de41a3..fee385dfa 100644 --- a/Source/Engine/UI/GUI/TextBlockStyle.cs +++ b/Source/Engine/UI/GUI/TextBlockStyle.cs @@ -1,5 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// @@ -10,6 +12,7 @@ namespace FlaxEngine.GUI /// /// Text block alignments modes. /// + [Flags] public enum Alignments { /// From 56077a268abaac7b80afeb17cc1bf34e4066f652 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Sep 2025 00:15:48 +0200 Subject: [PATCH 39/68] SImplify and cleanup GPUTexture editor #3452 #3194 --- .../Dedicated/GPUTextureEditor.cs | 63 ++++++++++ Source/Editor/GUI/GPUTextureEditor.cs | 112 ------------------ 2 files changed, 63 insertions(+), 112 deletions(-) create mode 100644 Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs delete mode 100644 Source/Editor/GUI/GPUTextureEditor.cs diff --git a/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs b/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs new file mode 100644 index 000000000..9df7ca377 --- /dev/null +++ b/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs @@ -0,0 +1,63 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +using FlaxEditor.GUI.ContextMenu; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.CustomEditors.Dedicated +{ + /// + /// Basic editor/viewer for . + /// + [CustomEditor(typeof(GPUTexture)), DefaultEditor] + public class GPUTextureEditor : CustomEditor + { + private Image _image; + + /// + public override DisplayStyle Style => DisplayStyle.Inline; + + /// + public override void Initialize(LayoutElementsContainer layout) + { + _image = new Image + { + Brush = new GPUTextureBrush(), + Size = new Float2(200, 100), + Parent = layout.ContainerControl, + }; + _image.Clicked += OnImageClicked; + } + + private void OnImageClicked(Image image, MouseButton button) + { + var texture = Values[0] as GPUTexture; + if (!texture) + return; + var menu = new ContextMenu(); + menu.AddButton("Save...", () => Screenshot.Capture(Values[0] as GPUTexture)); + menu.AddButton("Enlarge", () => _image.Size *= 2); + menu.AddButton("Shrink", () => _image.Size /= 2).Enabled = _image.Height > 32; + var location = image.PointFromScreen(Input.MouseScreenPosition); + menu.Show(image, location); + } + + /// + public override void Refresh() + { + base.Refresh(); + + var texture = Values[0] as GPUTexture; + ((GPUTextureBrush)_image.Brush).Texture = texture; + if (texture) + { + var desc = texture.Description; + _image.TooltipText = $"{texture.Name}\nType: {texture.ResourceType}\nSize: {desc.Width}x{desc.Height}\nFormat: {desc.Format}\nMemory: {Utilities.Utils.FormatBytesCount(texture.MemoryUsage)}"; + } + else + { + _image.TooltipText = "None"; + } + } + } +} diff --git a/Source/Editor/GUI/GPUTextureEditor.cs b/Source/Editor/GUI/GPUTextureEditor.cs deleted file mode 100644 index 5dd672941..000000000 --- a/Source/Editor/GUI/GPUTextureEditor.cs +++ /dev/null @@ -1,112 +0,0 @@ -#if FLAX_EDITOR -using FlaxEditor.CustomEditors; -using FlaxEditor.CustomEditors.Editors; -using FlaxEditor.CustomEditors.Elements; -using FlaxEditor.GUI.ContextMenu; -using FlaxEditor.Scripting; -using FlaxEditor.Tools.Foliage; -using FlaxEngine; -using FlaxEngine.GUI; -using FlaxEngine.Utilities; - -namespace FlaxEditor.GUI -{ - /// - /// basic custom property editor for GPUTexture - /// - [CustomEditor(typeof(GPUTexture))] - public class GPUTexturePropertyEditor : GenericEditor - { - public override DisplayStyle Style => DisplayStyle.Inline; - - ImageElement imageElement; - GroupElement group; - /// - /// - /// - public override void Initialize(LayoutElementsContainer layout) - { - imageElement = (group = layout.Group("")).Image(SpriteHandle.Default); - - //todo fix the AddSettingsButton func - //shit is buged - //the code below (until the Paint) is untested the Clear might not work - group.AddSettingsButton(); - group.SetupContextMenu += (ContextMenu.ContextMenu cxm, DropPanel dp) => - { - cxm.AddButton("Clear", (ContextMenuButton bt) => - { - SetValue(null); - }); - - cxm.AddSeparator(); - - //todo - //editor is needed - //cxm.AddButton("Display Full Texture", (ContextMenuButton bt) => - //{ - //}); - - //todo - // - //cxm.AddButton("Save To Asset", (ContextMenuButton bt) => - //{ - //}); - }; - Paint(); - group.Panel.Close(); - } - /// - /// - public override void Refresh() - { - Paint(); - base.Refresh(); - } - private void Paint() - { - string name = null; - string tt = null; - if (Values[0] is GPUTexture gputex) - { - name = gputex.Name; - tt += "Type: " + gputex.ResourceType.ToString() + "\n"; - tt += "Memory Usage: " + gputex.MemoryUsage + "B" + "\n"; - tt += "Format: " + gputex.Format.ToString() + "\n"; - //shorten the name it is a full path - if (name.EndsWith(".flax")) - { - if (name != ".flax")//sanity guard - { - var nameStartIndexWithEx = Globals.ProjectFolder.Length + 9 /* +9 to remove the "/Content/" */; - name = name.Substring - ( - nameStartIndexWithEx, - nameStartIndexWithEx - 5 /* -5 to remove the .flax */ + 2 - ); - - tt += "Path: " + gputex.Name.Remove(0, Globals.ProjectFolder.Length + 1); - } - } - - if (imageElement.Image.Brush is GPUTextureBrush brush) - { - brush.Texture = gputex; - imageElement.Control.Size = new Float2(group.Control.Width); - } - else - { - imageElement.Image.Brush = new GPUTextureBrush(); - Paint(); - } - } - name ??= "..."; - - DropPanel p = group.Control as DropPanel; - - p.HeaderText = name; - p.TooltipText = tt; - } - } -} -#endif From 521518bde4bb00803ea47b343f3f21e27ec639a7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Sep 2025 00:15:58 +0200 Subject: [PATCH 40/68] Add hack to use GPUTextureEditor on ref pickers --- Source/Editor/CustomEditors/CustomEditorsUtil.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs index 7d7ef123d..476219960 100644 --- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs +++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs @@ -87,8 +87,11 @@ namespace FlaxEditor.CustomEditors var targetTypeType = TypeUtils.GetType(targetType); if (canUseRefPicker) { + // TODO: add generic way of CustomEditor for ref pickers (use it on AssetRefEditor/GPUTextureEditor/...) if (typeof(Asset).IsAssignableFrom(targetTypeType)) return new AssetRefEditor(); + if (typeof(GPUTexture).IsAssignableFrom(targetTypeType)) + return new GPUTextureEditor(); if (typeof(FlaxEngine.Object).IsAssignableFrom(targetTypeType)) return new FlaxObjectRefEditor(); } From afdd264e639640227b17ab42008c324ad10689de Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Sep 2025 16:34:58 +0200 Subject: [PATCH 41/68] Fix compilation of Editor in Release mode --- Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs b/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs index 9df7ca377..954059a28 100644 --- a/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs @@ -52,7 +52,12 @@ namespace FlaxEditor.CustomEditors.Dedicated if (texture) { var desc = texture.Description; - _image.TooltipText = $"{texture.Name}\nType: {texture.ResourceType}\nSize: {desc.Width}x{desc.Height}\nFormat: {desc.Format}\nMemory: {Utilities.Utils.FormatBytesCount(texture.MemoryUsage)}"; +#if BUILD_RELEASE + var name = string.Empty; +#else + var name = texture.Name; +#endif + _image.TooltipText = $"{name}\nType: {texture.ResourceType}\nSize: {desc.Width}x{desc.Height}\nFormat: {desc.Format}\nMemory: {Utilities.Utils.FormatBytesCount(texture.MemoryUsage)}"; } else { From 364a52337522b7eaf1d2d05aa7dd73aff997df2f Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 16 Sep 2025 22:04:24 +0200 Subject: [PATCH 42/68] hide TrackLabel control in editor --- Source/Editor/Windows/Profiler/Timeline.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index c7a3dc7e6..ea653a5aa 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -100,6 +100,7 @@ namespace FlaxEditor.Windows.Profiler /// Timeline track label /// /// + [HideInEditor] public class TrackLabel : ContainerControl { /// From c124713e996ae63c56bd2c418fb473c3ca35be82 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 16 Sep 2025 22:05:28 +0200 Subject: [PATCH 43/68] remove create new script items when searchbox is cleared --- .../Editor/CustomEditors/Dedicated/ScriptsEditor.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 123c7252a..bb3cdd9d4 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -70,7 +70,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button { - TooltipText = "Add new scripts to the actor", + TooltipText = "Add new scripts to the actor.", AnchorPreset = AnchorPresets.MiddleCenter, Text = buttonText, Parent = this, @@ -114,7 +114,16 @@ namespace FlaxEditor.CustomEditors.Dedicated cm.TextChanged += text => { if (!IsValidScriptName(text)) + { + // Remove NewScriptItems + List die = cm.ItemsPanel.Children.FindAll(c => c is NewScriptItem); + foreach (var c in die) + { + cm.ItemsPanel.RemoveChild(c); + } + return; + } if (!cm.ItemsPanel.Children.Any(x => x.Visible && x is not NewScriptItem)) { // If there are no visible items, that means the search failed so we can find the create script button or create one if it's the first time @@ -876,7 +885,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add drag button to the group var scriptDrag = new DragImage { - TooltipText = "Script reference", + TooltipText = "Script reference.", AutoFocus = true, IsScrollable = false, Color = FlaxEngine.GUI.Style.Current.ForegroundGrey, From eea44ac89753bca4a1985f7cc50270e0f03d01af Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 16 Sep 2025 22:06:49 +0200 Subject: [PATCH 44/68] remove search query highlights showing on create new script item --- Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs | 1 + Source/Editor/GUI/ItemsListContextMenu.cs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index bb3cdd9d4..01e221755 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -36,6 +36,7 @@ namespace FlaxEditor.CustomEditors.Dedicated { ScriptName = scriptName; TooltipText = "Create a new script"; + DrawHighlights = false; } } diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 8fdf21e4c..033af782f 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -51,6 +51,11 @@ namespace FlaxEditor.GUI /// public float SortScore; + /// + /// Wether the query highlights should be draw. + /// + public bool DrawHighlights = true; + /// /// Occurs when items gets clicked by the user. /// @@ -165,7 +170,7 @@ namespace FlaxEditor.GUI Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundHighlighted); // Draw all highlights - if (_highlights != null) + if (DrawHighlights && _highlights != null) { var color = style.ProgressNormal * 0.6f; for (int i = 0; i < _highlights.Count; i++) From de2ee36529b748e0bea700862be63a20d2412553 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 16 Sep 2025 22:07:42 +0200 Subject: [PATCH 45/68] fix naming lol --- Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 01e221755..356ae5ee4 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -117,10 +117,10 @@ namespace FlaxEditor.CustomEditors.Dedicated if (!IsValidScriptName(text)) { // Remove NewScriptItems - List die = cm.ItemsPanel.Children.FindAll(c => c is NewScriptItem); - foreach (var c in die) + List newScriptItems = cm.ItemsPanel.Children.FindAll(c => c is NewScriptItem); + foreach (var item in newScriptItems) { - cm.ItemsPanel.RemoveChild(c); + cm.ItemsPanel.RemoveChild(item); } return; From 553a00750842bc57ecc3ddcee16b04d46502210c Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 16 Sep 2025 22:14:22 +0200 Subject: [PATCH 46/68] Revert "hide TrackLabel control in editor" This reverts commit 364a52337522b7eaf1d2d05aa7dd73aff997df2f. --- Source/Editor/Windows/Profiler/Timeline.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index ea653a5aa..c7a3dc7e6 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -100,7 +100,6 @@ namespace FlaxEditor.Windows.Profiler /// Timeline track label /// /// - [HideInEditor] public class TrackLabel : ContainerControl { /// From 1196db6d178d3d2b3f0898216aed88ac4c8da3c9 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 16 Sep 2025 22:54:57 +0200 Subject: [PATCH 47/68] check if control is control meant for gui editor use --- Source/Editor/Content/Create/PrefabCreateEntry.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Content/Create/PrefabCreateEntry.cs b/Source/Editor/Content/Create/PrefabCreateEntry.cs index 90cca263d..34439a989 100644 --- a/Source/Editor/Content/Create/PrefabCreateEntry.cs +++ b/Source/Editor/Content/Create/PrefabCreateEntry.cs @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; @@ -117,7 +118,8 @@ namespace FlaxEditor.Content.Create private static bool IsValid(Type type) { - return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType; + var controlTypes = Editor.Instance.CodeEditing.Controls.Get(); + return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType && controlTypes.Any(c => c.Type == type); } } From c7e403661d96c916d8392f6782884f4cffe44001 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 16 Sep 2025 17:42:24 -0500 Subject: [PATCH 48/68] Dont allow remove and duplicate options for non-resizing collections. --- Source/Editor/CustomEditors/Editors/CollectionEditor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index a0ee8d3dc..423daf0d4 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -407,7 +407,7 @@ namespace FlaxEditor.CustomEditors.Editors menu.AddButton("Copy", linkedEditor.Copy); var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index)); - b.Enabled = linkedEditor.CanPaste && !Editor._readOnly; + b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize; var paste = menu.AddButton("Paste", linkedEditor.Paste); paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly; @@ -422,7 +422,8 @@ namespace FlaxEditor.CustomEditors.Editors moveDownButton.Enabled = Index + 1 < Editor.Count; } - menu.AddButton("Remove", OnRemoveClicked); + b = menu.AddButton("Remove", OnRemoveClicked); + b.Enabled = !Editor._readOnly && Editor._canResize; menu.Show(panel, location); } From 01d1d634c2b08147ccd8ba3060cedf933bd4a2b7 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 16 Sep 2025 17:43:55 -0500 Subject: [PATCH 49/68] Fix other missing duplicate. --- Source/Editor/CustomEditors/Editors/CollectionEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 423daf0d4..b977dab63 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors menu.AddButton("Copy", linkedEditor.Copy); var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index)); - b.Enabled = linkedEditor.CanPaste && !Editor._readOnly; + b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize; b = menu.AddButton("Paste", linkedEditor.Paste); b.Enabled = linkedEditor.CanPaste && !Editor._readOnly; From 2604d5868754adfe73db5e60b245dc4138c622fe Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 18 Sep 2025 17:37:04 +0200 Subject: [PATCH 50/68] no more Linq --- Source/Editor/Content/Create/PrefabCreateEntry.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/Content/Create/PrefabCreateEntry.cs b/Source/Editor/Content/Create/PrefabCreateEntry.cs index 34439a989..e9ceaaa68 100644 --- a/Source/Editor/Content/Create/PrefabCreateEntry.cs +++ b/Source/Editor/Content/Create/PrefabCreateEntry.cs @@ -1,7 +1,6 @@ // Copyright (c) Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; @@ -119,7 +118,7 @@ namespace FlaxEditor.Content.Create private static bool IsValid(Type type) { var controlTypes = Editor.Instance.CodeEditing.Controls.Get(); - return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType && controlTypes.Any(c => c.Type == type); + return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType && controlTypes.Contains(new ScriptType(type)); } } From fd191f7ffb10583af123c890d1910adb433dc268 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 18 Sep 2025 23:24:10 +0200 Subject: [PATCH 51/68] don't clear content panel folder- and scene tree search box on script reload and enter/ exit play --- Source/Editor/Windows/ContentWindow.Search.cs | 1 + Source/Editor/Windows/ContentWindow.cs | 2 ++ Source/Editor/Windows/SceneTreeWindow.cs | 7 ++++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index f28dc4834..5a0ed63aa 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -115,6 +115,7 @@ namespace FlaxEditor.Windows var root = _root; root.LockChildrenRecursive(); + PerformLayout(); // Update tree var query = _foldersSearchBox.Text; diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 3d2ec4a66..d0cf84251 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -1085,6 +1085,8 @@ namespace FlaxEditor.Windows if (Editor.ContentDatabase.Find(_lastViewedFolderBeforeReload) is ContentFolder folder) _tree.Select(folder.Node); } + + OnFoldersSearchBoxTextChanged(); } private void Refresh() diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 75a3723cf..6b401c2cd 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -68,6 +68,7 @@ namespace FlaxEditor.Windows TooltipText = "Search the scene tree.\n\nYou can prefix your search with different search operators:\ns: -> Actor with script of type\na: -> Actor type\nc: -> Control type", }; _searchBox.TextChanged += OnSearchBoxTextChanged; + ScriptsBuilder.ScriptsReloadEnd += OnSearchBoxTextChanged; // Scene tree panel _sceneTreePanel = new Panel @@ -112,7 +113,7 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.LockFocusSelection, () => Editor.Windows.EditWin.Viewport.LockFocusSelection()); InputActions.Add(options => options.Rename, RenameSelection); } - + /// public override void OnPlayBeginning() { @@ -125,6 +126,7 @@ namespace FlaxEditor.Windows { base.OnPlayBegin(); _blockSceneTreeScroll = false; + OnSearchBoxTextChanged(); } /// @@ -139,6 +141,7 @@ namespace FlaxEditor.Windows { base.OnPlayEnd(); _blockSceneTreeScroll = true; + OnSearchBoxTextChanged(); } /// @@ -174,6 +177,7 @@ namespace FlaxEditor.Windows return; _tree.LockChildrenRecursive(); + PerformLayout(); // Update tree var query = _searchBox.Text; @@ -599,6 +603,7 @@ namespace FlaxEditor.Windows _dragHandlers = null; _tree = null; _searchBox = null; + ScriptsBuilder.ScriptsReloadEnd += OnSearchBoxTextChanged; base.OnDestroy(); } From 7183a3306eb9d4d7992e7b22fb803075fbbbe714 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 19 Sep 2025 16:57:50 +0200 Subject: [PATCH 52/68] fix typo --- Source/Editor/Windows/SceneTreeWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 6b401c2cd..ab6d2e262 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -603,7 +603,7 @@ namespace FlaxEditor.Windows _dragHandlers = null; _tree = null; _searchBox = null; - ScriptsBuilder.ScriptsReloadEnd += OnSearchBoxTextChanged; + ScriptsBuilder.ScriptsReloadEnd -= OnSearchBoxTextChanged; base.OnDestroy(); } From d1fbc66cb9860bb6827255bedc3017c95cef15cc Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 19 Sep 2025 15:58:22 -0500 Subject: [PATCH 53/68] Fix not attaching updated collision data to rigid body. --- Source/Engine/Physics/Colliders/Collider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 8249577c2..d60048ba9 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -248,7 +248,7 @@ void Collider::UpdateGeometry() const auto rigidBody = dynamic_cast(GetParent()); if (_staticActor != nullptr || (rigidBody && CanAttach(rigidBody))) { - PhysicsBackend::AttachShape(_shape, actor); + Attach(rigidBody); } else { From 47711ec5bebfabf1bd71baca76f9727d020deba1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 Sep 2025 23:10:03 +0200 Subject: [PATCH 54/68] Fix Volumetric Fog flicker on camera cuts #3443 --- Source/Engine/Renderer/VolumetricFogPass.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Renderer/VolumetricFogPass.cpp b/Source/Engine/Renderer/VolumetricFogPass.cpp index 817f25eef..2fa957ab1 100644 --- a/Source/Engine/Renderer/VolumetricFogPass.cpp +++ b/Source/Engine/Renderer/VolumetricFogPass.cpp @@ -179,6 +179,8 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context, (float)_cache.GridSizeZ); auto& fogData = renderContext.Buffers->VolumetricFogData; fogData.MaxDistance = options.Distance; + if (renderContext.Task->IsCameraCut) + _cache.HistoryWeight = 0.0f; // Init data (partial, without directional light or sky light data); GBufferPass::SetInputs(renderContext.View, _cache.Data.GBuffer); @@ -528,7 +530,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) { PROFILE_GPU("Light Scattering"); - const bool temporalHistoryIsValid = renderContext.Buffers->VolumetricFogHistory && !renderContext.Task->IsCameraCut && Float3::NearEqual(renderContext.Buffers->VolumetricFogHistory->Size3(), cache.GridSize); + const bool temporalHistoryIsValid = renderContext.Buffers->VolumetricFogHistory && Float3::NearEqual(renderContext.Buffers->VolumetricFogHistory->Size3(), cache.GridSize); const auto lightScatteringHistory = temporalHistoryIsValid ? renderContext.Buffers->VolumetricFogHistory : nullptr; context->BindUA(0, lightScattering->ViewVolume()); From 92f4327fc2ea0a1a4d3512164af98ebe1dcd8187 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 Sep 2025 00:13:52 +0200 Subject: [PATCH 55/68] Fix missing volumetric fog affecting transparent materials #3436 --- Source/Engine/Graphics/Materials/MaterialShader.h | 2 +- .../Graphics/Materials/MaterialShaderFeatures.cpp | 10 ++++++++++ .../Engine/Graphics/Materials/MaterialShaderFeatures.h | 2 +- Source/Shaders/ExponentialHeightFog.hlsl | 5 +++++ Source/Shaders/Fog.shader | 2 +- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index aedf2e870..117246671 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -10,7 +10,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 174 +#define MATERIAL_GRAPH_VERSION 175 class Material; class GPUShader; diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 0dae6c93e..a140fb577 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -2,6 +2,7 @@ #include "MaterialShaderFeatures.h" #include "Engine/Graphics/RenderTask.h" +#include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/ShadowsPass.h" @@ -24,18 +25,27 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanFog) { cache->Fog->GetExponentialHeightFogData(view, data.ExponentialHeightFog); + VolumetricFogOptions volumetricFog; + cache->Fog->GetVolumetricFogOptions(volumetricFog); + if (volumetricFog.UseVolumetricFog() && params.RenderContext.Buffers->VolumetricFog) + volumetricFogTexture = params.RenderContext.Buffers->VolumetricFog->ViewVolume(); + else + data.ExponentialHeightFog.VolumetricFogMaxDistance = -1.0f; } else { data.ExponentialHeightFog.FogMinOpacity = 1.0f; data.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f; } + params.GPUContext->BindSR(volumetricFogTextureRegisterIndex, volumetricFogTexture); // Set directional light input if (cache->DirectionalLights.HasItems()) diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h index 25689e765..54b91af23 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h @@ -25,7 +25,7 @@ struct ForwardShadingFeature : MaterialShaderFeature { enum { MaxLocalLights = 4 }; - enum { SRVs = 4 }; + enum { SRVs = 5 }; PACK_STRUCT(struct Data { diff --git a/Source/Shaders/ExponentialHeightFog.hlsl b/Source/Shaders/ExponentialHeightFog.hlsl index f6fb918f5..b5af721d8 100644 --- a/Source/Shaders/ExponentialHeightFog.hlsl +++ b/Source/Shaders/ExponentialHeightFog.hlsl @@ -92,4 +92,9 @@ float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, fl return GetExponentialHeightFog(exponentialHeightFog, posWS, camWS, skipDistance, distance(posWS, camWS)); } +float4 CombineVolumetricFog(float4 fog, float4 volumetricFog) +{ + return float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a); +} + #endif diff --git a/Source/Shaders/Fog.shader b/Source/Shaders/Fog.shader index dfea921cc..5c4344688 100644 --- a/Source/Shaders/Fog.shader +++ b/Source/Shaders/Fog.shader @@ -63,7 +63,7 @@ float4 PS_Fog(Quad_VS2PS input) : SV_Target0 #if VOLUMETRIC_FOG // Sample volumetric fog and mix it in float4 volumetricFog = IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0); - fog = float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a); + fog = CombineVolumetricFog(fog, volumetricFog); #endif return fog; From 9cc44825c6f69416e6ed20dba9456419dfa6608f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 Sep 2025 00:14:51 +0200 Subject: [PATCH 56/68] Update engine materials 92f4327fc2ea0a1a4d3512164af98ebe1dcd8187 --- Content/Editor/Camera/M_Camera.flax | 2 +- Content/Editor/CubeTexturePreviewMaterial.flax | 2 +- Content/Editor/DebugMaterials/DDGIDebugProbes.flax | 2 +- .../Editor/DebugMaterials/SingleColor/Decal.flax | 2 +- .../Editor/DebugMaterials/SingleColor/Particle.flax | 4 ++-- .../Editor/DebugMaterials/SingleColor/Surface.flax | 2 +- .../DebugMaterials/SingleColor/SurfaceAdditive.flax | 4 ++-- .../Editor/DebugMaterials/SingleColor/Terrain.flax | 2 +- Content/Editor/DefaultFontMaterial.flax | 2 +- Content/Editor/Gizmo/FoliageBrushMaterial.flax | 4 ++-- Content/Editor/Gizmo/Material.flax | 4 ++-- Content/Editor/Gizmo/MaterialWire.flax | 4 ++-- Content/Editor/Gizmo/SelectionOutlineMaterial.flax | 2 +- .../Editor/Gizmo/VertexColorsPreviewMaterial.flax | 2 +- Content/Editor/Highlight Material.flax | 4 ++-- Content/Editor/Icons/IconsMaterial.flax | 4 ++-- Content/Editor/IesProfilePreviewMaterial.flax | 2 +- .../MaterialTemplates/Features/ForwardShading.hlsl | 13 +++++++++++++ .../Editor/Particles/Particle Material Color.flax | 4 ++-- Content/Editor/Particles/Smoke Material.flax | 4 ++-- Content/Editor/SpriteMaterial.flax | 2 +- Content/Editor/Terrain/Circle Brush Material.flax | 2 +- .../Editor/Terrain/Highlight Terrain Material.flax | 2 +- Content/Editor/TexturePreviewMaterial.flax | 2 +- Content/Editor/Wires Debug Material.flax | 4 ++-- Content/Engine/DefaultDeformableMaterial.flax | 2 +- Content/Engine/DefaultMaterial.flax | 2 +- Content/Engine/DefaultRadialMenu.flax | 2 +- Content/Engine/DefaultTerrainMaterial.flax | 2 +- Content/Engine/SingleColorMaterial.flax | 2 +- Content/Engine/SkyboxMaterial.flax | 2 +- Content/Shaders/Fog.flax | 4 ++-- 32 files changed, 55 insertions(+), 42 deletions(-) diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index 5012b16e9..25b184d86 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a2936be1789e6a7c663f84ddfea8c897fe8273cd2d29910ac37720907d7b930 +oid sha256:5ef316cb161204b2c7a9dd4746c6c7281507a1e26c4c48c1a917b7645ac611f6 size 29533 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index 973a01177..3d7014519 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ecf44ea82025d0f491c68b0470e1704ca5f385bd54ad196d6912aeb2f3aee0f +oid sha256:99287e35e0c8ed22fd336cce66d8a675ad43fb318d431e63baf23670bc1212a7 size 31125 diff --git a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax index d598ac9bb..a5c8ccda0 100644 --- a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax +++ b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6e897a2fcbbb21efdd589604b4e3fc657f457ea124384842b380295af66cf13 +oid sha256:83aee3f2d5088930b7395099150a1bc062584996082cc1e13465c546d6f90fa3 size 40358 diff --git a/Content/Editor/DebugMaterials/SingleColor/Decal.flax b/Content/Editor/DebugMaterials/SingleColor/Decal.flax index 6024d3961..a85229155 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Decal.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Decal.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8301da35f0b45f58f5c59221bd22bf0a8500555d31ab84ec1c8cd5eca9fc101 +oid sha256:aa1b7b7f37b73cbcdef2faa6a6a1a1ce11c08033233606dd34e558195f4718cd size 9973 diff --git a/Content/Editor/DebugMaterials/SingleColor/Particle.flax b/Content/Editor/DebugMaterials/SingleColor/Particle.flax index 9c2c0755a..423396308 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Particle.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Particle.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e432328bb19eaa58caf35f60cd6495a46cca694314828010f3be401d7de9434 -size 32108 +oid sha256:5096f5c6aff954618c4fc863e59a76ce880879695bac1abc990fbabd0b8d330b +size 32699 diff --git a/Content/Editor/DebugMaterials/SingleColor/Surface.flax b/Content/Editor/DebugMaterials/SingleColor/Surface.flax index 0fb0d1c6f..f405ecd8b 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Surface.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Surface.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1688861997cc2e8a433cdea81ee62662f45b261bc863cdb9431beed612f0aad7 +oid sha256:0c1c2e4e2324de02092f73a0477cd909e400e99a400ce255c59751b21af3ed8e size 29306 diff --git a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax index e5bda89f6..98a9221f6 100644 --- a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax +++ b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42363fad30e29b0e0c4cf7ad9e02dc91040eb6821c3c28bd49996771e65893c4 -size 31559 +oid sha256:7b8794eaf03955f5afb9cf8a56a6327d7fe3b7c287e2eeb50c95d1a78dd2c7c4 +size 32150 diff --git a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax index 09a253a8d..2ab956aad 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb957ea2ee358b0e611d6612703261c9837099b6d03d48484cdab1151a461d8f +oid sha256:dfd8cdbfe96f53c2795b7c408a5f6c09fbf67b0d3ded6c34aacf2e34313e1925 size 21004 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index b4f659e34..71b91f9a6 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a46410b49a7e38c4c2569e4d8d8c8f4744cf26f79c632a53899ce753ab7c88a +oid sha256:9a5f051b559c86cd397dda551afd4f3b751a99bc9cb14de989a80c2a04651d82 size 29627 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index 1d8e89a65..7af84241d 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f377cc4e05d89e4edbec6382a052abe571f3261bc273cd4475541b7b7051cffb -size 37586 +oid sha256:3d2bb53a8b94d596738a6a1527d2516e032a287a7a6de6fab472be7a28978de2 +size 38177 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index cce270410..2d52c87a9 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:271c80d9d9971d96d6ea430dfaf8f8f57d9b5f9fe1770b387f426d3c8721c3d8 -size 32713 +oid sha256:261de215acfeee3d2e37bfea0c4f807c7f685f20719032f1706f82b07ae52992 +size 33304 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index 4b30df5f5..4b90f0311 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff3e7b3e77afa7191f1db9cf12f21908b80bb8f71e832c37e55547dc9dcab31c -size 31410 +oid sha256:072c5ae6e0248b8f48bb933a8bd51935218afe425ab9f6df5fb853003e545b52 +size 32001 diff --git a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax index 962179da0..7dc84ec92 100644 --- a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax +++ b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc2facc8fa980e5baa399fa7510a87d33d21bbd4c97eaab24856f6db49b13172 +oid sha256:87f9eee9186d2862aa04c837d05e4cffeb1eff8be706ea856bd74a179ad591bb size 16212 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index 305c6a4c2..b6e40d5b4 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4458a9483e81fb0526cc395f93eeae238f4f91fa5d4889e3196b6530a8f17ec2 +oid sha256:c8b6d69f23dc6dd94e03180127e1b5eafb9bb610a392d41b6aee94d3e03bebef size 30419 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index e944ae62d..4371e5753 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:391606b1f7563d9a8e414baf6c19f3d99694a28f3f574b7aca64a325294d8e39 -size 30104 +oid sha256:bde47005982dbcb96abb900d5773d5f6d707ea0eee94b7b385945e3b3b350372 +size 30695 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index 320547f9a..dfcfed0b6 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7951a44b138e60aa9dee4fdaf000eba8a7faef7b31c2e387f78b4a393d0cd0bc -size 30021 +oid sha256:c6524caaaf72bc30bc32bffcfdf26b4d1d2adc466dd5143c4b1980e62b14b97f +size 30612 diff --git a/Content/Editor/IesProfilePreviewMaterial.flax b/Content/Editor/IesProfilePreviewMaterial.flax index c7a025108..9f23ac666 100644 --- a/Content/Editor/IesProfilePreviewMaterial.flax +++ b/Content/Editor/IesProfilePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41210c6c490513503f01e8a628d80dd98e58fc0482f9966f9342700118ccd04c +oid sha256:f796e403ee85d33eb2be0a371e18085a31ca7bf970e26c19c26f9ea2b0224a33 size 20445 diff --git a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl index 22d9ca9b4..bb18b3011 100644 --- a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl +++ b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl @@ -27,6 +27,7 @@ TextureCube EnvProbe : register(t__SRV__); TextureCube SkyLightTexture : register(t__SRV__); Buffer ShadowsBuffer : register(t__SRV__); Texture2D ShadowMap : register(t__SRV__); +Texture3D VolumetricFogTexture : register(t__SRV__); @4// Forward Shading: Utilities @5// Forward Shading: Shaders @@ -147,6 +148,18 @@ void PS_Forward( // Calculate exponential height fog float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, gBuffer.ViewPos.z); + if (ExponentialHeightFog.VolumetricFogMaxDistance > 0) + { + // Sample volumetric fog and mix it in + float2 screenUV = materialInput.SvPosition.xy * ScreenSize.zw; + float3 viewVector = materialInput.WorldPosition - ViewPos; + float sceneDepth = length(viewVector); + float depthSlice = sceneDepth / ExponentialHeightFog.VolumetricFogMaxDistance; + float3 volumeUV = float3(screenUV, depthSlice); + float4 volumetricFog = VolumetricFogTexture.SampleLevel(SamplerLinearClamp, volumeUV, 0); + fog = CombineVolumetricFog(fog, volumetricFog); + } + // Apply fog to the output color #if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE output = float4(output.rgb * fog.a + fog.rgb, output.a); diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index bde834456..50cf3b09f 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b600cd725f5550de72d5a2544571ca2c1ea8de1a3d45038bac273d2b6f3b04c2 -size 30429 +oid sha256:d4fcde3b4d440561997706160c0f10e4b82e30b7c2f8aa732fd1439d9bae160e +size 31020 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index 7a8fdeb1a..38b79ec48 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96d3865771cfa47ad59e0493c8f1b6e9fd9950593b2b929c91ea2692eca71efd -size 38495 +oid sha256:1a1fdeeb738daa07787556cac664214c33633a1f9a3021aff4dc0727e105ea4c +size 39087 diff --git a/Content/Editor/SpriteMaterial.flax b/Content/Editor/SpriteMaterial.flax index 876a38a56..2c98b951e 100644 --- a/Content/Editor/SpriteMaterial.flax +++ b/Content/Editor/SpriteMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d02a6d11ea83ea6c519a1644950750e58433e6a2aea9a2daa646db1d2b0c293 +oid sha256:3059c52ee632be69bab545192e170b61ddabcf67d4848bbdb46f4c1da5414072 size 30502 diff --git a/Content/Editor/Terrain/Circle Brush Material.flax b/Content/Editor/Terrain/Circle Brush Material.flax index af191fdaa..71a54e7f6 100644 --- a/Content/Editor/Terrain/Circle Brush Material.flax +++ b/Content/Editor/Terrain/Circle Brush Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e9ef5186642a38af8ebb5856a891215686576b23841aabe16b7dde7f2bcb57f +oid sha256:bd3172fc12d5ad7c6cc862f4b62a77b781974242e14518dbf81805605941cd55 size 27676 diff --git a/Content/Editor/Terrain/Highlight Terrain Material.flax b/Content/Editor/Terrain/Highlight Terrain Material.flax index 14bd86c35..863c238b7 100644 --- a/Content/Editor/Terrain/Highlight Terrain Material.flax +++ b/Content/Editor/Terrain/Highlight Terrain Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f1a9524d9bc7ee41df761b9fb34613fc04357377a81836fb28b3ee5a6f2dcf4 +oid sha256:fbdc0533affafaaf7f8813cfdbe5a9d5596038e0e5e46ad3e970ec7ffd924fab size 21179 diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax index fac30b4f1..2ab0e2c23 100644 --- a/Content/Editor/TexturePreviewMaterial.flax +++ b/Content/Editor/TexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c178081b59417439b2d523486f3c7e4f7f391ff57e5ab5a565aadf3fd31f5488 +oid sha256:b5d4eda6497493dabf6697a9df34ec59b11183648e481399a7b1beae3c8596e1 size 10744 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index 8fff1a174..8c5a07c5d 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c31daed51b38e6aa9eeceaad85498d6ae7079f7b125c6f71f036799278c34e22 -size 30104 +oid sha256:037ea2b610ee9c7ceb2cdb680d2a5d898a0600a896df93036565e11aef02a17c +size 30695 diff --git a/Content/Engine/DefaultDeformableMaterial.flax b/Content/Engine/DefaultDeformableMaterial.flax index 8af3db999..b3be3fe58 100644 --- a/Content/Engine/DefaultDeformableMaterial.flax +++ b/Content/Engine/DefaultDeformableMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df767eeb060d058d502271f1cd89581c57ad339cd690cc9c588f9a34cc9344b1 +oid sha256:4591783ed6e6cca6b8639dd3ebb624101b623c3813b80f75e22ce606d914b8db size 18985 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index a253452df..51e67d79f 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b00388390410aabb11f1d9b032361902d2f284daa765d536c8f2a821f659effe +oid sha256:f1906849094ff4ad6022e14762961e2a235160d29427f82724d91ca8b8cfb320 size 31331 diff --git a/Content/Engine/DefaultRadialMenu.flax b/Content/Engine/DefaultRadialMenu.flax index 2b3d7f0de..8175a130a 100644 --- a/Content/Engine/DefaultRadialMenu.flax +++ b/Content/Engine/DefaultRadialMenu.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b0272c8f2df095e6609f49845a3d329daaf634e0776ca764e4c51596cac60ff +oid sha256:5ab4c3c9d3f425b136eb0cdee3342b5c99f9372e691d5bf0b1f85c7654362805 size 20514 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index 8d195ab98..cd1a11e93 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a86caeef4de5a84783ba34208701c0f272f3b4b3ff82c64c2553d6aec631e07b +oid sha256:04a300665f72ea9543edb0419f660c09bd6d41eb46021a52be4b5bb14fa257a1 size 23301 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index b6906b930..a9534c2db 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74d77dbfdf72c9281b0760a266adac7f1eb849f9656ea8da5cd8951f2fab5343 +oid sha256:5da0e59ceaa1921fce54a8602837fe4782436ba67af4c945b2827cbc672cb362 size 29507 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index 8faccf8c0..8882f13ec 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8149367ccbef36932866e6af53fedf79931f26677db5dfcce71ba33caeff5980 +oid sha256:e47dc45042b8b802c6b7dec8ffeab7feb761c7b31a116031fa6db5d6d41f7855 size 31070 diff --git a/Content/Shaders/Fog.flax b/Content/Shaders/Fog.flax index 3f934412c..9bce56170 100644 --- a/Content/Shaders/Fog.flax +++ b/Content/Shaders/Fog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e83f9dbbcf84550de09e7c63bbdd3acc6591cf6ba1bcce2a2699772122ae07f4 -size 2633 +oid sha256:72daf0834367b96cc0732e6b3a5d8944c1048f516a52146bfd47f8ad83c95b4a +size 2590 From 1c581bceafc7af25f317e21e2cea63ab8290db25 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 Sep 2025 23:52:04 +0200 Subject: [PATCH 57/68] Fix sun shadows when direction is perfectly vertical #3614 --- Source/Engine/Renderer/ShadowsPass.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 0bf2f70d1..34ff5d3a9 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -946,7 +946,10 @@ void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render Matrix shadowView, shadowProjection, shadowVP, cullingVP; // Create view matrix - Matrix::LookAt(frustumCenter + light.Direction * minExtents.Z, frustumCenter, Float3::Up, shadowView); + Float3 up = Float3::Up; + if (Math::Abs(Float3::Dot(light.Direction, up)) > 0.9f) + up = Float3::Right; + Matrix::LookAt(frustumCenter + light.Direction * minExtents.Z, frustumCenter, up, shadowView); // Create viewport for culling with extended near/far planes due to culling issues (aka pancaking) const float cullRangeExtent = 100000.0f; From 902744a0cef9631a101d57ed57022a9c08719c32 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 21 Sep 2025 15:08:35 +0200 Subject: [PATCH 58/68] Fix model import scale on nodes that could be applied multiple times on the same mesh #3562 --- Source/Engine/Tools/ModelTool/ModelTool.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index fe268c350..e0ee85cac 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1611,9 +1611,16 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option // Transform the nodes using the import transformation if (data.LODs.HasItems() && data.LODs[0].Meshes.HasItems()) { + BitArray<> visitedNodes; + visitedNodes.Resize(data.Nodes.Count()); + visitedNodes.SetAll(false); for (int i = 0; i < data.LODs[0].Meshes.Count(); ++i) { auto* meshData = data.LODs[0].Meshes[i]; + int32 nodeIndex = meshData->NodeIndex; + if (visitedNodes[nodeIndex]) + continue; + visitedNodes.Set(nodeIndex, true); Transform transform = importTransform; if (options.UseLocalOrigin) { @@ -1627,8 +1634,6 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option transform.Translation -= center; } - int32 nodeIndex = meshData->NodeIndex; - auto& node = data.Nodes[nodeIndex]; node.LocalTransform = transform.LocalToWorld(node.LocalTransform); } From 26e94f6f8ef284818f6da120cdcddec989b109e6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 21 Sep 2025 20:59:25 +0200 Subject: [PATCH 59/68] Refactor material instance to not override all public parameters by default (need explicit set) #3444 --- Source/Engine/Content/Assets/MaterialInstance.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index 7d6c9d7fc..b3710ebbd 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -90,9 +90,11 @@ void MaterialInstance::OnBaseParamsChanged() // Get the newest parameters baseParams->Clone(Params); +#if 0 // Override all public parameters by default for (auto& param : Params) param.SetIsOverride(param.IsPublic()); +#endif // Copy previous parameters values for (int32 i = 0; i < oldParams.Count(); i++) From a0f764a774580b916c7e3c9d94adbe712f152ceb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 21 Sep 2025 21:58:03 +0200 Subject: [PATCH 60/68] Add skybox rotation feature #3592 --- Content/Engine/SkyboxMaterial.flax | 4 ++-- Source/Engine/Level/Actors/Skybox.cpp | 2 ++ Source/Engine/Level/Actors/Skybox.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index 8882f13ec..7f9545007 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e47dc45042b8b802c6b7dec8ffeab7feb761c7b31a116031fa6db5d6d41f7855 -size 31070 +oid sha256:98a0d6b06a90d753e68638ac64836cb96e6edc90e5b13770b722f33512034c09 +size 31650 diff --git a/Source/Engine/Level/Actors/Skybox.cpp b/Source/Engine/Level/Actors/Skybox.cpp index 6bfc98723..e08d8a61a 100644 --- a/Source/Engine/Level/Actors/Skybox.cpp +++ b/Source/Engine/Level/Actors/Skybox.cpp @@ -129,6 +129,7 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M material->SetParameterValue(TEXT("PanoramicTexture"), PanoramicTexture.Get(), false); material->SetParameterValue(TEXT("Color"), Color * Math::Exp2(Exposure), false); material->SetParameterValue(TEXT("IsPanoramic"), PanoramicTexture != nullptr, false); + material->SetParameterValue(TEXT("RotationEuler"), _rotationEuler, false); material->Bind(bindParams); } } @@ -163,4 +164,5 @@ void Skybox::OnTransformChanged() _box = BoundingBox(_transform.Translation); _sphere = BoundingSphere(_transform.Translation, 0.0f); + _rotationEuler = GetOrientation().GetEuler() * DegreesToRadians; } diff --git a/Source/Engine/Level/Actors/Skybox.h b/Source/Engine/Level/Actors/Skybox.h index f571a211f..fb831df44 100644 --- a/Source/Engine/Level/Actors/Skybox.h +++ b/Source/Engine/Level/Actors/Skybox.h @@ -18,6 +18,7 @@ class FLAXENGINE_API Skybox : public Actor, public ISkyRenderer private: AssetReference _proxyMaterial; int32 _sceneRenderingKey = -1; + Float3 _rotationEuler; public: /// From 5f1e905e8fadab9c485e38f49a9de26eb70691d1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 21 Sep 2025 22:52:57 +0200 Subject: [PATCH 61/68] Fix crash when importing model as prefab and handle duplicated object names #3558 --- Source/Engine/ContentImporters/ImportModel.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index be71236ab..91547dc8d 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -26,6 +26,7 @@ #include "Engine/Utilities/RectPack.h" #include "Engine/Scripting/Scripting.h" #include "Engine/Profiler/ProfilerCPU.h" +#include "Editor/Utilities/EditorUtilities.h" #include "AssetsImportingManager.h" bool ImportModel::TryGetImportOptions(const StringView& path, Options& options) @@ -281,13 +282,19 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) // Import all of the objects recursive but use current model data to skip loading file again options.Cached = &cached; - Function splitImport = [&context, &autoImportOutput](Options& splitOptions, const StringView& objectName, String& outputPath, MeshData* meshData) + HashSet objectNames; + Function splitImport = [&context, &autoImportOutput, &objectNames](Options& splitOptions, const StringView& objectName, String& outputPath, MeshData* meshData) { // Recursive importing of the split object String postFix = objectName; const int32 splitPos = postFix.FindLast(TEXT('|')); - if (splitPos != -1) + if (splitPos != -1 && splitPos + 1 < postFix.Length()) postFix = postFix.Substring(splitPos + 1); + EditorUtilities::ValidatePathChars(postFix); // Ensure name is valid path + int32 duplicate = 0; + String postFixPre = postFix; + while (!objectNames.Add(postFix)) // Ensure name is unique + postFix = String::Format(TEXT("{} {}"), postFixPre, duplicate++); // TODO: check for name collisions with material/texture assets outputPath = autoImportOutput / String(StringUtils::GetFileNameWithoutExtension(context.TargetAssetPath)) + TEXT(" ") + postFix + TEXT(".flax"); splitOptions.SubAssetFolder = TEXT(" "); // Use the same folder as asset as they all are imported to the subdir for the prefab (see SubAssetFolder usage above) From a151c78412626bb0b366620d6f490795771f37bb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Sep 2025 08:42:16 +0200 Subject: [PATCH 62/68] Fix loading projects that have similar path to engine folder #3565 --- .../Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index 18459ddb5..4c9d521b4 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -192,11 +192,18 @@ namespace Flax.Build { if (string.IsNullOrEmpty(path)) return string.Empty; - if (path.StartsWith(Globals.EngineRoot)) + path = Utilities.NormalizePath(path); + if (IsMacroPath(path, Globals.EngineRoot)) path = "$(EnginePath)" + path.Substring(Globals.EngineRoot.Length); - else if (path.StartsWith(projectPath)) + else if (IsMacroPath(path, projectPath)) path = "$(ProjectPath)" + path.Substring(projectPath.Length); - return Utilities.NormalizePath(path); + return path; + } + + private static bool IsMacroPath(string path, string root) + { + root = Utilities.NormalizePath(root); + return path == root || path.StartsWith(root + '/'); } } From e9a7b1c8eb7e754d6e39aa9cd1b06b14a62cbc48 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Sep 2025 10:22:32 +0200 Subject: [PATCH 63/68] Fix using enums as Scene Animation Event parameters #3554 --- .../Editor/GUI/Timeline/Tracks/EventTrack.cs | 32 ++++++++++++++++--- .../Timeline/Tracks/KeyframesPropertyTrack.cs | 5 ++- Source/Editor/Utilities/Utils.cs | 24 ++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/Source/Editor/GUI/Timeline/Tracks/EventTrack.cs b/Source/Editor/GUI/Timeline/Tracks/EventTrack.cs index c18c64ac5..f7aba94b3 100644 --- a/Source/Editor/GUI/Timeline/Tracks/EventTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/EventTrack.cs @@ -10,6 +10,7 @@ using System.Text; using FlaxEditor.GUI.Timeline.Undo; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEngine.Json; using FlaxEngine.Utilities; namespace FlaxEditor.GUI.Timeline.Tracks @@ -54,7 +55,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks var paramTypeName = LoadName(stream); e.EventParamsTypes[i] = TypeUtils.GetManagedType(paramTypeName); if (e.EventParamsTypes[i] == null) + { + Editor.LogError($"Unknown type {paramTypeName}."); isInvalid = true; + } } if (isInvalid) @@ -82,7 +86,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks for (int j = 0; j < paramsCount; j++) { stream.Read(dataBuffer, 0, e.EventParamsSizes[j]); - key.Parameters[j] = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), e.EventParamsTypes[j]); + key.Parameters[j] = Utilities.Utils.ByteArrayToStructure(handle.AddrOfPinnedObject(), e.EventParamsTypes[j], e.EventParamsSizes[j]); } events[i] = new KeyframesEditor.Keyframe @@ -125,8 +129,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks for (int j = 0; j < paramsCount; j++) { - Marshal.StructureToPtr(key.Parameters[j], ptr, true); - Marshal.Copy(ptr, dataBuffer, 0, e.EventParamsSizes[j]); + Utilities.Utils.StructureToByteArray(key.Parameters[j], e.EventParamsSizes[j], ptr, dataBuffer); stream.Write(dataBuffer, 0, e.EventParamsSizes[j]); } } @@ -153,7 +156,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks /// /// The event key data. /// - public struct EventKey + public struct EventKey : ICloneable { /// /// The parameters values. @@ -178,6 +181,26 @@ namespace FlaxEditor.GUI.Timeline.Tracks sb.Append(')'); return sb.ToString(); } + + /// + public object Clone() + { + if (Parameters == null) + return new EventKey(); + + // Deep clone parameter values (especially boxed value types need to be duplicated to avoid referencing the same ones) + var parameters = new object[Parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { + var p = Parameters[i]; + if (p == null || p is FlaxEngine.Object) + parameters[i] = Parameters[i]; + else + parameters[i] = JsonSerializer.Deserialize(JsonSerializer.Serialize(p), p.GetType()); + } + + return new EventKey { Parameters = parameters }; + } } /// @@ -234,6 +257,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks var time = Timeline.CurrentTime; if (!TryGetValue(out var value)) value = Events.Evaluate(time); + value = ((ICloneable)value).Clone(); // Find event at the current location for (int i = Events.Keyframes.Count - 1; i >= 0; i--) diff --git a/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs b/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs index 389041381..32f1575ec 100644 --- a/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs @@ -77,7 +77,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks { var time = stream.ReadSingle(); stream.Read(dataBuffer, 0, e.ValueSize); - var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType); + var value = Utilities.Utils.ByteArrayToStructure(handle.AddrOfPinnedObject(), propertyType, e.ValueSize); keyframes[i] = new KeyframesEditor.Keyframe { @@ -142,8 +142,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks for (int i = 0; i < keyframes.Count; i++) { var keyframe = keyframes[i]; - Marshal.StructureToPtr(keyframe.Value, ptr, true); - Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize); + Utilities.Utils.StructureToByteArray(keyframe.Value, e.ValueSize, ptr, dataBuffer); stream.Write(keyframe.Time); stream.Write(dataBuffer); } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 949479783..688cec470 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -212,6 +212,10 @@ namespace FlaxEditor.Utilities if (value is FlaxEngine.Object) return value; + // For custom types use interface + if (value is ICloneable clonable) + return clonable.Clone(); + // For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor if (value != null && (!value.GetType().IsValueType || !value.GetType().IsClass)) { @@ -548,6 +552,26 @@ namespace FlaxEditor.Utilities return arr; } + internal static void StructureToByteArray(object value, int valueSize, IntPtr tempBuffer, byte[] dataBuffer) + { + var valueType = value.GetType(); + if (valueType.IsEnum) + { + var ptr = FlaxEngine.Interop.NativeInterop.ValueTypeUnboxer.GetPointer(value, valueType); + FlaxEngine.Utils.MemoryCopy(tempBuffer, ptr, (ulong)valueSize); + } + else + Marshal.StructureToPtr(value, tempBuffer, true); + Marshal.Copy(tempBuffer, dataBuffer, 0, valueSize); + } + + internal static object ByteArrayToStructure(IntPtr valuePtr, Type valueType, int valueSize) + { + if (valueType.IsEnum) + return FlaxEngine.Interop.NativeInterop.MarshalToManaged(valuePtr, valueType); + return Marshal.PtrToStructure(valuePtr, valueType); + } + internal static unsafe string ReadStr(this BinaryReader stream, int check) { int length = stream.ReadInt32(); From 5eea5a72c99ae10f69cd6f6bf6deb1bbd2ef567d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Sep 2025 11:44:11 +0200 Subject: [PATCH 64/68] Fix particle material compilation error when using position offset #3661 --- Content/Editor/MaterialTemplates/Particle.shader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index 6f0be21e0..661a8f69b 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -645,7 +645,7 @@ VertexOutput VS_Ribbon(RibbonInput input, uint vertexIndex : SV_VertexID) materialInput.TBN = output.TBN; materialInput.TwoSidedSign = 1; materialInput.SvPosition = output.Position; - materialInput.PreSkinnedPosition = Position; + materialInput.PreSkinnedPosition = position; materialInput.PreSkinnedNormal = tangentToLocal[2].xyz; materialInput.InstanceOrigin = output.InstanceOrigin; materialInput.InstanceParams = output.InstanceParams; From 4ae3d57adc531e99cbe9174a07733b8d6035c0c2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 22 Sep 2025 11:32:24 -0500 Subject: [PATCH 65/68] Resolve code suggestions --- Source/Engine/Audio/AudioSource.cpp | 8 -------- Source/Engine/Audio/AudioSource.h | 5 ++--- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index 069fa29a3..f16f95423 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -588,11 +588,3 @@ void AudioSource::BeginPlay(SceneBeginData* data) SetTime(GetStartTime()); } } - -void AudioSource::EndPlay() -{ - Actor::EndPlay(); - - ClipStarted.UnbindAll(); - ClipFinished.UnbindAll(); -} diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index 4e05d1594..9858d283a 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -79,12 +79,12 @@ public: /// /// Event fired when the audio clip starts. /// - API_EVENT() Delegate<> ClipStarted; + API_EVENT() Action ClipStarted; /// /// Event fired when the audio clip finishes. /// - API_EVENT() Delegate<> ClipFinished; + API_EVENT() Action ClipFinished; /// /// Gets the velocity of the source. Determines pitch in relation to AudioListener's position. Only relevant for spatial (3D) sources. @@ -336,5 +336,4 @@ protected: void OnDisable() override; void OnTransformChanged() override; void BeginPlay(SceneBeginData* data) override; - void EndPlay() override; }; From 70ba750a5e32ee3d2f11ee97d79a95185c4eb529 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Sep 2025 22:31:23 +0200 Subject: [PATCH 66/68] Fix code #3511 --- Source/Engine/Debug/DebugCommands.cpp | 5 +---- Source/Engine/Debug/DebugCommands.h | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Debug/DebugCommands.cpp b/Source/Engine/Debug/DebugCommands.cpp index 9c66341ab..fc568a181 100644 --- a/Source/Engine/Debug/DebugCommands.cpp +++ b/Source/Engine/Debug/DebugCommands.cpp @@ -439,11 +439,8 @@ void DebugCommands::GetAllCommands(Array& commands) { EnsureInited(); ScopeLock lock(Locker); - - for (auto& command : Commands) - { + for (const auto& command : Commands) commands.Add(command.Name); - } } DebugCommands::CommandFlags DebugCommands::GetCommandFlags(StringView command) diff --git a/Source/Engine/Debug/DebugCommands.h b/Source/Engine/Debug/DebugCommands.h index cfe70db73..f25fe0581 100644 --- a/Source/Engine/Debug/DebugCommands.h +++ b/Source/Engine/Debug/DebugCommands.h @@ -49,7 +49,7 @@ public: /// /// Gets all available commands. /// - /// The output list of all commands (unsorted). + /// The output list of all commands (unsorted). API_FUNCTION() static void GetAllCommands(API_PARAM(Out) Array& commands); /// From d8f7199c110af6a7fe7b8f3673e3ec4d0021748b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 Sep 2025 15:42:49 +0200 Subject: [PATCH 67/68] Fix importing emissive, roughness, metalness and wireframe properties of materials with Assimp #3418 --- .../ContentImporters/CreateMaterial.cpp | 12 ++++++---- .../Engine/ContentImporters/CreateMaterial.h | 8 +++++++ Source/Engine/Graphics/Models/ModelData.cpp | 2 ++ Source/Engine/Graphics/Models/ModelData.h | 9 ++++++++ .../Tools/ModelTool/ModelTool.Assimp.cpp | 23 ++++++++++++++++++- Source/Engine/Tools/ModelTool/ModelTool.cpp | 14 +++++++++++ 6 files changed, 62 insertions(+), 6 deletions(-) diff --git a/Source/Engine/ContentImporters/CreateMaterial.cpp b/Source/Engine/ContentImporters/CreateMaterial.cpp index 3991585a3..a41d09454 100644 --- a/Source/Engine/ContentImporters/CreateMaterial.cpp +++ b/Source/Engine/ContentImporters/CreateMaterial.cpp @@ -93,14 +93,15 @@ namespace }; template - void AddInput(MaterialLayer* layer, Meta11 meta, MaterialGraphBoxes box, const Guid& texture, const T& value, const T& defaultValue, const Float2& pos, ShaderGraphNode<>** outTextureNode = nullptr) + void AddInput(MaterialLayer* layer, Meta11 meta, MaterialGraphBoxes box, const Guid& texture, const T& value, const T& defaultValue, const Float2& pos, ShaderGraphNode<>** outTextureNode = nullptr, uint8 channel = MAX_uint8) { auto textureNode = AddTextureNode(layer, texture); auto valueNode = AddValueNode(layer, value, defaultValue); + auto textureNodeBox = channel == MAX_uint8 ? 1 : channel + 2; // Color or specific channel (RGBA) if (textureNode && valueNode) { auto diffuseMultiply = AddMultiplyNode(layer); - CONNECT(diffuseMultiply->Boxes[0], textureNode->Boxes[1]); + CONNECT(diffuseMultiply->Boxes[0], textureNode->Boxes[textureNodeBox]); CONNECT(diffuseMultiply->Boxes[1], valueNode->Boxes[0]); CONNECT(layer->Root->Boxes[static_cast(box)], diffuseMultiply->Boxes[2]); SET_POS(valueNode, pos + Float2(-467.7404, 91.41332)); @@ -109,7 +110,7 @@ namespace } else if (textureNode) { - CONNECT(layer->Root->Boxes[static_cast(box)], textureNode->Boxes[1]); + CONNECT(layer->Root->Boxes[static_cast(box)], textureNode->Boxes[textureNodeBox]); SET_POS(textureNode, pos + Float2(-293.5272f, -2.926111f)); } else if (valueNode) @@ -178,8 +179,9 @@ CreateAssetResult CreateMaterial::Create(CreateAssetContext& context) // Opacity AddInput(layer, meta, MaterialGraphBoxes::Opacity, options.Opacity.Texture, options.Opacity.Value, 1.0f, Float2(0, 400)); - // Opacity - AddInput(layer, meta, MaterialGraphBoxes::Roughness, options.Roughness.Texture, options.Roughness.Value, 0.5f, Float2(200, 400)); + // Roughness + Metalness + AddInput(layer, meta, MaterialGraphBoxes::Roughness, options.Roughness.Texture, options.Roughness.Value, 0.5f, Float2(200, 400), nullptr, options.Roughness.Channel); + AddInput(layer, meta, MaterialGraphBoxes::Metalness, options.Metalness.Texture, options.Metalness.Value, 0.0f, Float2(200, 600), nullptr, options.Metalness.Channel); // Normal auto normalMap = AddTextureNode(layer, options.Normals.Texture, true); diff --git a/Source/Engine/ContentImporters/CreateMaterial.h b/Source/Engine/ContentImporters/CreateMaterial.h index 6b37067e8..df0680e18 100644 --- a/Source/Engine/ContentImporters/CreateMaterial.h +++ b/Source/Engine/ContentImporters/CreateMaterial.h @@ -40,9 +40,17 @@ public: struct { float Value = 0.5f; + uint8 Channel = 0; Guid Texture = Guid::Empty; } Roughness; + struct + { + float Value = 0.0f; + uint8 Channel = 0; + Guid Texture = Guid::Empty; + } Metalness; + struct { Guid Texture = Guid::Empty; diff --git a/Source/Engine/Graphics/Models/ModelData.cpp b/Source/Engine/Graphics/Models/ModelData.cpp index 877d8c826..69ae5d9a1 100644 --- a/Source/Engine/Graphics/Models/ModelData.cpp +++ b/Source/Engine/Graphics/Models/ModelData.cpp @@ -372,6 +372,8 @@ bool MaterialSlotEntry::UsesProperties() const Opacity.TextureIndex != -1 || Math::NotNearEqual(Roughness.Value, 0.5f) || Roughness.TextureIndex != -1 || + Math::NotNearEqual(Metalness.Value, 0.5f) || + Metalness.TextureIndex != -1 || Normals.TextureIndex != -1; } diff --git a/Source/Engine/Graphics/Models/ModelData.h b/Source/Engine/Graphics/Models/ModelData.h index 8e401b973..d9167c858 100644 --- a/Source/Engine/Graphics/Models/ModelData.h +++ b/Source/Engine/Graphics/Models/ModelData.h @@ -327,14 +327,23 @@ struct FLAXENGINE_API MaterialSlotEntry { float Value = 0.5f; int32 TextureIndex = -1; + uint8 Channel = 0; } Roughness; + struct + { + float Value = 0.0f; + int32 TextureIndex = -1; + uint8 Channel = 0; + } Metalness; + struct { int32 TextureIndex = -1; } Normals; bool TwoSided = false; + bool Wireframe = false; bool UsesProperties() const; static float ShininessToRoughness(float shininess); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index f5b043994..b1340bfa7 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -528,12 +528,24 @@ bool ImportMaterials(ModelData& result, AssimpImporterData& data, String& errorM aiColor3D aColor; if (aMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, aColor) == AI_SUCCESS) materialSlot.Diffuse.Color = ToColor(aColor); + if (aMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, aColor) == AI_SUCCESS) + materialSlot.Emissive.Color = ToColor(aColor); + if (aMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, aColor) == AI_SUCCESS) + materialSlot.Emissive.Color = ToColor(aColor); bool aBoolean; if (aMaterial->Get(AI_MATKEY_TWOSIDED, aBoolean) == AI_SUCCESS) materialSlot.TwoSided = aBoolean; - bool aFloat; + if (aMaterial->Get(AI_MATKEY_ENABLE_WIREFRAME, aBoolean) == AI_SUCCESS) + materialSlot.Wireframe = aBoolean; + float aFloat; if (aMaterial->Get(AI_MATKEY_OPACITY, aFloat) == AI_SUCCESS) materialSlot.Opacity.Value = aFloat; + if (aMaterial->Get(AI_MATKEY_GLOSSINESS_FACTOR, aFloat) == AI_SUCCESS) + materialSlot.Roughness.Value = 1.0f - aFloat; + else if (aMaterial->Get(AI_MATKEY_SHININESS, aFloat) == AI_SUCCESS) + materialSlot.Roughness.Value = MaterialSlotEntry::ShininessToRoughness(aFloat); + if (aMaterial->Get(AI_MATKEY_EMISSIVE_INTENSITY, aFloat) == AI_SUCCESS) + materialSlot.Emissive.Color *= aFloat; if (EnumHasAnyFlags(data.Options.ImportTypes, ImportDataTypes::Textures)) { @@ -541,6 +553,15 @@ bool ImportMaterials(ModelData& result, AssimpImporterData& data, String& errorM ImportMaterialTexture(result, data, aMaterial, aiTextureType_EMISSIVE, materialSlot.Emissive.TextureIndex, TextureEntry::TypeHint::ColorRGB); ImportMaterialTexture(result, data, aMaterial, aiTextureType_NORMALS, materialSlot.Normals.TextureIndex, TextureEntry::TypeHint::Normals); ImportMaterialTexture(result, data, aMaterial, aiTextureType_OPACITY, materialSlot.Opacity.TextureIndex, TextureEntry::TypeHint::ColorRGBA); + ImportMaterialTexture(result, data, aMaterial, aiTextureType_METALNESS, materialSlot.Metalness.TextureIndex, TextureEntry::TypeHint::ColorRGB); + ImportMaterialTexture(result, data, aMaterial, aiTextureType_DIFFUSE_ROUGHNESS, materialSlot.Roughness.TextureIndex, TextureEntry::TypeHint::ColorRGB); + + if (materialSlot.Roughness.TextureIndex != -1 && (data.Path.EndsWith(TEXT(".gltf")) || data.Path.EndsWith(TEXT(".glb")))) + { + // glTF specification with a single metallicRoughnessTexture (G = roughness, B = metalness) + materialSlot.Roughness.Channel = 1; + materialSlot.Metalness.Channel = 2; + } if (materialSlot.Diffuse.TextureIndex != -1) { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index e0ee85cac..bddd56296 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1520,6 +1520,9 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option const Char* roughnessNames[] = { TEXT("roughness"), TEXT("rough") }; TrySetupMaterialParameter(materialInstance, ToSpan(roughnessNames, ARRAY_COUNT(roughnessNames)), material.Roughness.Value, MaterialParameterType::Float); TRY_SETUP_TEXTURE_PARAM(Roughness, roughnessNames, Texture); + const Char* metalnessNames[] = { TEXT("metalness"), TEXT("metallic") }; + TrySetupMaterialParameter(materialInstance, ToSpan(metalnessNames, ARRAY_COUNT(metalnessNames)), material.Metalness.Value, MaterialParameterType::Float); + TRY_SETUP_TEXTURE_PARAM(Metalness, metalnessNames, Texture); #undef TRY_SETUP_TEXTURE_PARAM materialInstance->Save(); @@ -1545,11 +1548,22 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option materialOptions.Opacity.Texture = data.Textures[material.Opacity.TextureIndex].AssetID; materialOptions.Roughness.Value = material.Roughness.Value; if (material.Roughness.TextureIndex != -1) + { materialOptions.Roughness.Texture = data.Textures[material.Roughness.TextureIndex].AssetID; + materialOptions.Roughness.Channel = material.Roughness.Channel; + } + materialOptions.Metalness.Value = material.Metalness.Value; + if (material.Metalness.TextureIndex != -1) + { + materialOptions.Metalness.Texture = data.Textures[material.Metalness.TextureIndex].AssetID; + materialOptions.Metalness.Channel = material.Metalness.Channel; + } if (material.Normals.TextureIndex != -1) materialOptions.Normals.Texture = data.Textures[material.Normals.TextureIndex].AssetID; if (material.TwoSided || material.Diffuse.HasAlphaMask) materialOptions.Info.CullMode = CullMode::TwoSided; + if (material.Wireframe) + materialOptions.Info.FeaturesFlags |= MaterialFeaturesFlags::Wireframe; if (!Math::IsOne(material.Opacity.Value) || material.Opacity.TextureIndex != -1) materialOptions.Info.BlendMode = MaterialBlendMode::Transparent; AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialTag, assetPath, material.AssetID, &materialOptions); From 7060cb5696976eb8029e2795b9445160de3df1cc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 Sep 2025 17:20:18 +0200 Subject: [PATCH 68/68] Fix UI Brush editing in prefab regression from a471861e9257c426404984b14e53ef61f769c243 #3694 --- Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs index d32f08c92..09670f1b2 100644 --- a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs @@ -180,7 +180,7 @@ namespace FlaxEditor.CustomEditors.Editors // Show prefab diff when reference value type is different var color = Color.Transparent; - if (Values.HasReferenceValue && CanRevertReferenceValue && Values[0].GetType() != Values.ReferenceValue.GetType()) + if (Values.HasReferenceValue && CanRevertReferenceValue && Values[0]?.GetType() != Values.ReferenceValue?.GetType()) color = FlaxEngine.GUI.Style.Current.BackgroundSelected; _typeItem.Labels[0].HighlightStripColor = color;