From d2dba124df294a3e221ba2775fea2f11c2a24d87 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Wed, 9 Oct 2024 13:12:48 +0200 Subject: [PATCH 01/68] add model import option to only create material slots but not import actual materials --- Source/Engine/Tools/ModelTool/ModelTool.cpp | 6 +++++- Source/Engine/Tools/ModelTool/ModelTool.h | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 408fcb887..212ccdd4f 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1430,7 +1430,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option auto& texture = data.Textures[i]; // Auto-import textures - if (autoImportOutput.IsEmpty() || EnumHasNoneFlags(options.ImportTypes, ImportDataTypes::Textures) || texture.FilePath.IsEmpty()) + if (autoImportOutput.IsEmpty() || EnumHasNoneFlags(options.ImportTypes, ImportDataTypes::Textures) || texture.FilePath.IsEmpty() || options.CreateEmptyMaterialSlots) continue; String assetPath = GetAdditionalImportPath(autoImportOutput, importedFileNames, StringUtils::GetFileNameWithoutExtension(texture.FilePath)); #if COMPILE_WITH_ASSETS_IMPORTER @@ -1486,6 +1486,10 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option } } + // The rest of the steps this function performs become irrelevant when we're only creating slots. + if (options.CreateEmptyMaterialSlots) + continue; + if (options.ImportMaterialsAsInstances) { // Create material instance diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 708b94342..2ea76383c 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -273,16 +273,19 @@ public: public: // Materials // If checked, the importer will create materials for model meshes as specified in the file. - API_FIELD(Attributes="EditorOrder(400), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") + API_FIELD(Attributes="EditorOrder(399), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") bool ImportMaterials = true; + // If checked, the importer will create empty material slots for every material. + API_FIELD(Attributes = "EditorOrder(400), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") + bool CreateEmptyMaterialSlots; // If checked, the importer will create the model's materials as instances of a base material. - API_FIELD(Attributes = "EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials)), VisibleIf(nameof(ShowGeometry))") + API_FIELD(Attributes = "EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials)), VisibleIf(nameof(ShowGeometry)), VisibleIf(nameof(CreateEmptyMaterialSlots), true)") bool ImportMaterialsAsInstances = false; // The material used as the base material that will be instanced as the imported model's material. - API_FIELD(Attributes = "EditorOrder(402), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterialsAsInstances)), VisibleIf(nameof(ShowGeometry))") + API_FIELD(Attributes = "EditorOrder(402), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterialsAsInstances)), VisibleIf(nameof(ShowGeometry)), VisibleIf(nameof(CreateEmptyMaterialSlots), true)") AssetReference InstanceToImportAs; // If checked, the importer will import texture files used by the model and any embedded texture resources. - API_FIELD(Attributes="EditorOrder(410), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") + API_FIELD(Attributes="EditorOrder(410), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry)), VisibleIf(nameof(CreateEmptyMaterialSlots), true)") bool ImportTextures = true; // If checked, the importer will try to keep the model's current overridden material slots, instead of importing materials from the source file. API_FIELD(Attributes="EditorOrder(420), EditorDisplay(\"Materials\", \"Keep Overridden Materials\"), VisibleIf(nameof(ShowGeometry))") From 9a363e2882b2cca89702ea77fea477200733e575 Mon Sep 17 00:00:00 2001 From: alsed Date: Sat, 30 Aug 2025 08:51:21 -0400 Subject: [PATCH 02/68] Implement prefab detection of skinned models --- .../Engine/ContentImporters/ImportModel.cpp | 40 ++++++++++++++----- Source/Engine/Tools/ModelTool/ModelTool.cpp | 23 +++++++---- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index be71236ab..a007a740d 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -19,6 +19,7 @@ #include "Engine/Animations/AnimEvent.h" #include "Engine/Level/Actors/EmptyActor.h" #include "Engine/Level/Actors/StaticModel.h" +#include "Engine/Level/Actors/AnimatedModel.h" #include "Engine/Level/Prefabs/Prefab.h" #include "Engine/Level/Prefabs/PrefabManager.h" #include "Engine/Level/Scripts/ModelPrefab.h" @@ -84,6 +85,7 @@ struct PrefabObject int32 NodeIndex; String Name; String AssetPath; + bool IsSkinned = false; }; void RepackMeshLightmapUVs(ModelData& data) @@ -320,8 +322,10 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) return AssetsImportingManager::Import(context.InputPath, outputPath, &splitOptions); }; + auto splitOptions = options; LOG(Info, "Splitting imported {0} meshes", meshesByName.Count()); + PrefabObject prefabObject; for (int32 groupIndex = 0; groupIndex < meshesByName.Count(); groupIndex++) { @@ -331,7 +335,17 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) prefabObject.NodeIndex = group.First()->NodeIndex; prefabObject.Name = group.First()->Name; + // Defaul value for ModelType splitOptions.Type = ModelTool::ModelType::Model; + + // Search for Skinned Model + if (group.First()->BlendShapes.HasItems()) + { + LOG(Info, "Mesh {0} is Skinned", prefabObject.Name); + splitOptions.Type = ModelTool::ModelType::SkinnedModel; + prefabObject.IsSkinned = true; + } + splitOptions.ObjectIndex = groupIndex; if (!splitImport(splitOptions, group.GetKey(), prefabObject.AssetPath, group.First())) { @@ -594,8 +608,8 @@ CreateAssetResult ImportModel::Create(CreateAssetContext& context) CreateAssetResult ImportModel::CreateModel(CreateAssetContext& context, const ModelData& modelData, const Options* options) { PROFILE_CPU(); - IMPORT_SETUP(Model, Model::SerializedVersion); - static_assert(Model::SerializedVersion == 30, "Update code."); + IMPORT_SETUP(Model, Model::SerializedVersion); + static_assert(Model::SerializedVersion == 30, "Update code."); // Save model header MemoryWriteStream stream(4096); @@ -722,22 +736,30 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M { if (e.NodeIndex == nodeIndex) { - auto* actor = New(); - actor->SetName(e.Name); - if (auto* model = Content::LoadAsync(e.AssetPath)) + if(e.IsSkinned) { - actor->Model = model; + LOG(Info,"Model {0} is Animated", e.Name); + auto* actor = New(); + actor->SetName(e.Name); + if (auto* skinnedModel = Content::LoadAsync(e.AssetPath)) + actor->SkinnedModel = skinnedModel; + nodeActors.Add(actor); + } + else + { + auto* actor = New(); + actor->SetName(e.Name); + if (auto* model = Content::LoadAsync(e.AssetPath)) + actor->Model = model; + nodeActors.Add(actor); } - nodeActors.Add(actor); } } Actor* nodeActor = nodeActors.Count() == 1 ? nodeActors[0] : New(); if (nodeActors.Count() > 1) { for (Actor* e : nodeActors) - { e->SetParent(nodeActor); - } } if (nodeActors.Count() != 1) { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index fe268c350..cc483f333 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1131,7 +1131,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option options.ImportTypes |= ImportDataTypes::Skeleton; break; case ModelType::Prefab: - options.ImportTypes = ImportDataTypes::Geometry | ImportDataTypes::Nodes | ImportDataTypes::Animations; + options.ImportTypes = ImportDataTypes::Geometry | ImportDataTypes::Nodes | ImportDataTypes::Skeleton | ImportDataTypes::Animations; if (options.ImportMaterials) options.ImportTypes |= ImportDataTypes::Materials; if (options.ImportTextures) @@ -1157,6 +1157,9 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option { for (auto& mesh : lod.Meshes) { + if (mesh->BlendShapes.IsEmpty()) + continue; + for (int32 blendShapeIndex = mesh->BlendShapes.Count() - 1; blendShapeIndex >= 0; blendShapeIndex--) { auto& blendShape = mesh->BlendShapes[blendShapeIndex]; @@ -2111,12 +2114,13 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option #undef REMAP_VERTEX_BUFFER // Remap blend shapes - dstMesh->BlendShapes.Resize(srcMesh->BlendShapes.Count()); + dstMesh->BlendShapes.Clear(); + dstMesh->BlendShapes.EnsureCapacity(srcMesh->BlendShapes.Count(), false); for (int32 blendShapeIndex = 0; blendShapeIndex < srcMesh->BlendShapes.Count(); blendShapeIndex++) { const auto& srcBlendShape = srcMesh->BlendShapes[blendShapeIndex]; - auto& dstBlendShape = dstMesh->BlendShapes[blendShapeIndex]; - + //auto& dstBlendShape = dstMesh->BlendShapes[blendShapeIndex]; + BlendShape dstBlendShape; dstBlendShape.Name = srcBlendShape.Name; dstBlendShape.Weight = srcBlendShape.Weight; dstBlendShape.Vertices.EnsureCapacity(srcBlendShape.Vertices.Count()); @@ -2125,19 +2129,21 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option auto v = srcBlendShape.Vertices[i]; v.VertexIndex = remap[v.VertexIndex]; if (v.VertexIndex != ~0u) - { dstBlendShape.Vertices.Add(v); - } } + + if (dstBlendShape.Vertices.HasItems()) + dstMesh->BlendShapes.Add(dstBlendShape); } + /* // Remove empty blend shapes for (int32 blendShapeIndex = dstMesh->BlendShapes.Count() - 1; blendShapeIndex >= 0; blendShapeIndex--) { if (dstMesh->BlendShapes[blendShapeIndex].Vertices.IsEmpty()) dstMesh->BlendShapes.RemoveAt(blendShapeIndex); } - + */ // Optimize generated LOD meshopt_optimizeVertexCache(dstMesh->Indices.Get(), dstMesh->Indices.Get(), dstMeshIndexCount, dstMeshVertexCount); meshopt_optimizeOverdraw(dstMesh->Indices.Get(), dstMesh->Indices.Get(), dstMeshIndexCount, (const float*)dstMesh->Positions.Get(), dstMeshVertexCount, sizeof(Float3), 1.05f); @@ -2182,6 +2188,9 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option { for (auto& mesh : lod.Meshes) { + if (mesh->BlendShapes.IsEmpty()) + continue; + for (auto& blendShape : mesh->BlendShapes) { // Compute min/max for used vertex indices From a8768f918eb28c0d4e2d3a6da1fe0b57861c2490 Mon Sep 17 00:00:00 2001 From: alsed Date: Sun, 7 Sep 2025 13:45:21 -0300 Subject: [PATCH 03/68] Add more conditions for skeleton import and add blendshapes for prefab recognition --- Source/Engine/ContentImporters/ImportModel.cpp | 2 +- Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp | 2 +- Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp | 2 +- Source/Engine/Tools/ModelTool/ModelTool.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index a007a740d..f1d83daa7 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -339,7 +339,7 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) splitOptions.Type = ModelTool::ModelType::Model; // Search for Skinned Model - if (group.First()->BlendShapes.HasItems()) + if (group.First()->BlendWeights.HasItems() || group.First()->BlendShapes.HasItems() ) { LOG(Info, "Mesh {0} is Skinned", prefabObject.Name); splitOptions.Type = ModelTool::ModelType::SkinnedModel; diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index f5b043994..a61f19668 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -765,7 +765,7 @@ bool ModelTool::ImportDataAssimp(const String& path, ModelData& data, Options& o } // Import skeleton - if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton)) + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton) && context.Bones.HasItems()) { data.Skeleton.Nodes.Resize(context.Nodes.Count(), false); for (int32 i = 0; i < context.Nodes.Count(); i++) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index 81ce46d6c..e530e7b7c 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -1404,7 +1404,7 @@ bool ModelTool::ImportDataOpenFBX(const String& path, ModelData& data, Options& } // Import skeleton - if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton)) + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton) && context.Bones.HasItems()) { data.Skeleton.Nodes.Resize(context.Nodes.Count(), false); for (int32 i = 0; i < context.Nodes.Count(); i++) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index cc483f333..103797781 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1184,7 +1184,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option } } } - if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton)) + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton) && data.Skeleton.Bones.HasItems()) { LOG(Info, "Imported skeleton has {0} bones and {1} nodes", data.Skeleton.Bones.Count(), data.Nodes.Count()); @@ -1324,7 +1324,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option for (int32 i = 0; i < meshesCount; i++) { const auto mesh = data.LODs[0].Meshes[i]; - if (mesh->BlendIndices.IsEmpty() || mesh->BlendWeights.IsEmpty()) + if ((mesh->BlendIndices.IsEmpty() || mesh->BlendWeights.IsEmpty()) && data.Skeleton.Bones.HasItems()) { auto indices = Int4::Zero; auto weights = Float4::UnitX; From 52ee8b395328cf6094ca0566f7d97dc0176dc780 Mon Sep 17 00:00:00 2001 From: alsed Date: Sun, 7 Sep 2025 20:40:28 -0300 Subject: [PATCH 04/68] better comment for animated prefab and remove unused code in remap blendshapes --- Source/Engine/ContentImporters/ImportModel.cpp | 6 +++--- Source/Engine/Tools/ModelTool/ModelTool.cpp | 11 +---------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index f1d83daa7..5a3a34735 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -608,8 +608,8 @@ CreateAssetResult ImportModel::Create(CreateAssetContext& context) CreateAssetResult ImportModel::CreateModel(CreateAssetContext& context, const ModelData& modelData, const Options* options) { PROFILE_CPU(); - IMPORT_SETUP(Model, Model::SerializedVersion); - static_assert(Model::SerializedVersion == 30, "Update code."); + IMPORT_SETUP(Model, Model::SerializedVersion); + static_assert(Model::SerializedVersion == 30, "Update code."); // Save model header MemoryWriteStream stream(4096); @@ -738,7 +738,7 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M { if(e.IsSkinned) { - LOG(Info,"Model {0} is Animated", e.Name); + LOG(Info,"Creating animated model prefab {0}.", e.Name); auto* actor = New(); actor->SetName(e.Name); if (auto* skinnedModel = Content::LoadAsync(e.AssetPath)) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 103797781..ebd19e169 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -2114,12 +2114,10 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option #undef REMAP_VERTEX_BUFFER // Remap blend shapes - dstMesh->BlendShapes.Clear(); dstMesh->BlendShapes.EnsureCapacity(srcMesh->BlendShapes.Count(), false); for (int32 blendShapeIndex = 0; blendShapeIndex < srcMesh->BlendShapes.Count(); blendShapeIndex++) { const auto& srcBlendShape = srcMesh->BlendShapes[blendShapeIndex]; - //auto& dstBlendShape = dstMesh->BlendShapes[blendShapeIndex]; BlendShape dstBlendShape; dstBlendShape.Name = srcBlendShape.Name; dstBlendShape.Weight = srcBlendShape.Weight; @@ -2132,18 +2130,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option dstBlendShape.Vertices.Add(v); } + // Add only valid blend shapes if (dstBlendShape.Vertices.HasItems()) dstMesh->BlendShapes.Add(dstBlendShape); } - /* - // Remove empty blend shapes - for (int32 blendShapeIndex = dstMesh->BlendShapes.Count() - 1; blendShapeIndex >= 0; blendShapeIndex--) - { - if (dstMesh->BlendShapes[blendShapeIndex].Vertices.IsEmpty()) - dstMesh->BlendShapes.RemoveAt(blendShapeIndex); - } - */ // Optimize generated LOD meshopt_optimizeVertexCache(dstMesh->Indices.Get(), dstMesh->Indices.Get(), dstMeshIndexCount, dstMeshVertexCount); meshopt_optimizeOverdraw(dstMesh->Indices.Get(), dstMesh->Indices.Get(), dstMeshIndexCount, (const float*)dstMesh->Positions.Get(), dstMeshVertexCount, sizeof(Float3), 1.05f); From 05f08db66eacad3b28bc4d0764ecbb29e0e424b3 Mon Sep 17 00:00:00 2001 From: alsed Date: Mon, 8 Sep 2025 01:28:49 -0300 Subject: [PATCH 05/68] Fixed a crash when model with no Bones was imported as Skinned Model --- Source/Engine/Tools/ModelTool/ModelTool.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index ebd19e169..6820532bb 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1119,6 +1119,12 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option options.ImportTypes |= ImportDataTypes::Textures; break; case ModelType::SkinnedModel: + if (!data.Skeleton.Bones.HasItems()) + { + LOG(Warning, "Model is not Skinned, it will be imported as Static"); + options.ImportTypes = ImportDataTypes::Geometry | ImportDataTypes::Nodes; + options.Type = ModelType::Model; + } options.ImportTypes = ImportDataTypes::Geometry | ImportDataTypes::Nodes | ImportDataTypes::Skeleton; if (options.ImportMaterials) options.ImportTypes |= ImportDataTypes::Materials; @@ -1184,7 +1190,8 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option } } } - if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton) && data.Skeleton.Bones.HasItems()) + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton) + && (data.Skeleton.Bones.HasItems() || data.LODs[0].Meshes[0]->BlendShapes.HasItems())) { LOG(Info, "Imported skeleton has {0} bones and {1} nodes", data.Skeleton.Bones.Count(), data.Nodes.Count()); From 07c1dfc61329a92fe8a52420da539bdcf9b20d5b Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 19 Sep 2025 20:23:47 +0200 Subject: [PATCH 06/68] add focus selected visject controls functionality --- Source/Editor/Options/InputOptions.cs | 4 +++ Source/Editor/Surface/VisjectSurface.Input.cs | 4 ++- Source/Editor/Surface/VisjectSurface.cs | 30 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index af919c1f3..624daf890 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -694,6 +694,10 @@ namespace FlaxEditor.Options [EditorDisplay("Node Editors"), EditorOrder(4580)] public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.A, KeyboardKeys.Alt); + [DefaultValue(typeof(InputBinding), "Shift+F")] + [EditorDisplay("Node Editors"), EditorOrder(4590)] + public InputBinding FocusSelectedNodes = new InputBinding(KeyboardKeys.F, KeyboardKeys.Shift); + #endregion } } diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 09df195eb..72594eb5f 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -29,6 +29,7 @@ namespace FlaxEditor.Surface private HashSet _movingNodes; private HashSet _temporarySelectedNodes; private readonly Stack _inputBrackets = new Stack(); + private InputBinding _focusSelectedNodeBinding; private class InputBracket { @@ -844,7 +845,8 @@ namespace FlaxEditor.Surface private void CurrentInputTextChanged(string currentInputText) { - if (string.IsNullOrEmpty(currentInputText)) + // Check if focus selected nodes binding is being pressed to prevent it triggering primary menu + if (string.IsNullOrEmpty(currentInputText) || _focusSelectedNodeBinding.Process(RootWindow)) return; if (IsPrimaryMenuOpened || !CanEdit) { diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index 8cbcb4a21..fb0d1f576 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -425,6 +425,7 @@ namespace FlaxEditor.Surface new InputActionsContainer.Binding(options => options.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); }), new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }), new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }), + new InputActionsContainer.Binding(options => options.FocusSelectedNodes, () => { ShowSelection(); }), }); Context.ControlSpawned += OnSurfaceControlSpawned; @@ -436,7 +437,10 @@ namespace FlaxEditor.Surface DragHandlers.Add(_dragAssets = new DragAssets(ValidateDragItem)); DragHandlers.Add(_dragParameters = new DragNames(SurfaceParameter.DragPrefix, ValidateDragParameter)); + OnEditorOptionsChanged(Editor.Instance.Options.Options); + ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; + Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; } private void OnScriptsReloadBegin() @@ -446,6 +450,11 @@ namespace FlaxEditor.Surface _cmPrimaryMenu = null; } + private void OnEditorOptionsChanged(EditorOptions options) + { + _focusSelectedNodeBinding = options.Input.FocusSelectedNodes; + } + /// /// Gets the display name of the connection type used in the surface. /// @@ -643,6 +652,26 @@ namespace FlaxEditor.Surface ViewCenterPosition = areaRect.Center; } + /// + /// Shows the selected controls by changing the view scale and the position. + /// + public void ShowSelection() + { + var selection = SelectedControls; + if (selection.Count == 0) + return; + + // Calculate the bounds of all selected controls + Rectangle bounds = selection[0].Bounds; + for (int i = 1; i < selection.Count; i++) + bounds = Rectangle.Union(bounds, selection[i].Bounds); + + // Add margin + bounds = bounds.MakeExpanded(250.0f); + + ShowArea(bounds); + } + /// /// Shows the given surface node by changing the view scale and the position and focuses the node. /// @@ -1066,6 +1095,7 @@ namespace FlaxEditor.Surface _cmPrimaryMenu?.Dispose(); ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; + Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; base.OnDestroy(); } From 1dd96ae9cbc219749f445ab551414150fbed25a7 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 19 Sep 2025 23:04:43 +0200 Subject: [PATCH 07/68] add undo to MoveSelectedNodes (do TODO) --- Source/Editor/Surface/SurfaceUtils.cs | 10 +++++----- Source/Editor/Surface/VisjectSurface.Input.cs | 10 +++++++++- Source/Editor/Windows/Assets/AssetEditorWindow.cs | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index d75efb5a0..bf70ca2e9 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -573,13 +573,13 @@ namespace FlaxEditor.Surface var showSearch = () => editor.ContentFinding.ShowSearch(window); // Toolstrip - saveButton = toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save", ref inputOptions.Save); + saveButton = toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save.", ref inputOptions.Save); toolStrip.AddSeparator(); - undoButton = toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); - redoButton = toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); + undoButton = toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip("Undo.", ref inputOptions.Undo); + redoButton = toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip("Redo.", ref inputOptions.Redo); toolStrip.AddSeparator(); - toolStrip.AddButton(editor.Icons.Search64, showSearch).LinkTooltip("Open content search tool", ref inputOptions.Search); - toolStrip.AddButton(editor.Icons.CenterView64, surface.ShowWholeGraph).LinkTooltip("Show whole graph"); + toolStrip.AddButton(editor.Icons.Search64, showSearch).LinkTooltip("Open content search tool.", ref inputOptions.Search); + toolStrip.AddButton(editor.Icons.CenterView64, surface.ShowWholeGraph).LinkTooltip("Show whole graph."); var gridSnapButton = toolStrip.AddButton(editor.Icons.Grid32, surface.ToggleGridSnapping); gridSnapButton.LinkTooltip("Toggle grid snapping for nodes."); gridSnapButton.AutoCheck = true; diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 72594eb5f..1ebd4fdee 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -702,13 +702,21 @@ namespace FlaxEditor.Surface private void MoveSelectedNodes(Float2 delta) { - // TODO: undo + List undoActions = new List(); + delta /= _targetScale; OnGetNodesToMove(); foreach (var node in _movingNodes) + { node.Location += delta; + if (Undo != null) + undoActions.Add(new MoveNodesAction(Context, new[] { node.ID }, delta)); + } _isMovingSelection = false; MarkAsEdited(false); + + if (undoActions.Count > 0) + Undo?.AddAction(new MultiUndoAction(undoActions, "Moved ")); } /// diff --git a/Source/Editor/Windows/Assets/AssetEditorWindow.cs b/Source/Editor/Windows/Assets/AssetEditorWindow.cs index 646112441..eeac84b60 100644 --- a/Source/Editor/Windows/Assets/AssetEditorWindow.cs +++ b/Source/Editor/Windows/Assets/AssetEditorWindow.cs @@ -53,7 +53,7 @@ namespace FlaxEditor.Windows.Assets { Parent = this }; - _toolstrip.AddButton(editor.Icons.Search64, () => Editor.Windows.ContentWin.Select(_item)).LinkTooltip("Show and select in Content Window"); + _toolstrip.AddButton(editor.Icons.Search64, () => Editor.Windows.ContentWin.Select(_item)).LinkTooltip("Show and select in Content Window."); InputActions.Add(options => options.Save, Save); From 2516820e4a32c6f6d06637134ad7b935145879f6 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 3 Oct 2025 18:46:18 +0200 Subject: [PATCH 08/68] fix reloading scripts expands all folders --- Source/Editor/Windows/ContentWindow.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index d0cf84251..390658add 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -1086,7 +1086,8 @@ namespace FlaxEditor.Windows _tree.Select(folder.Node); } - OnFoldersSearchBoxTextChanged(); + if (!string.IsNullOrWhiteSpace(_foldersSearchBox.Text)) + OnFoldersSearchBoxTextChanged(); } private void Refresh() From 7ba01a413fc198f68334cf6c5d1449d957973507 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 3 Oct 2025 18:50:41 +0200 Subject: [PATCH 09/68] Revert "fix reloading scripts expands all folders" This reverts commit 2516820e4a32c6f6d06637134ad7b935145879f6. --- Source/Editor/Windows/ContentWindow.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 390658add..d0cf84251 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -1086,8 +1086,7 @@ namespace FlaxEditor.Windows _tree.Select(folder.Node); } - if (!string.IsNullOrWhiteSpace(_foldersSearchBox.Text)) - OnFoldersSearchBoxTextChanged(); + OnFoldersSearchBoxTextChanged(); } private void Refresh() From 2d4843b1f41a6e5b2b2f5d5ca342e1ab8dc2a5e1 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 3 Oct 2025 20:41:52 +0200 Subject: [PATCH 10/68] fix reloading scripts expanding scene tree #3701 --- Source/Editor/Content/Tree/ContentTreeNode.cs | 14 ++++++-------- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 11 +++++------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 8629296fc..ca629000d 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -175,15 +175,13 @@ namespace FlaxEditor.Content } } - bool isExpanded = isAnyChildVisible; - - if (isExpanded) + if (!noFilter) { - Expand(true); - } - else - { - Collapse(true); + bool isExpanded = isAnyChildVisible; + if (isExpanded) + Expand(true); + else + Collapse(true); } Visible = isThisVisible | isAnyChildVisible; diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 5f56e918a..4607a4f63 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -324,13 +324,12 @@ namespace FlaxEditor.SceneGraph.GUI isExpanded = Editor.Instance.ProjectCache.IsExpandedActor(ref id); } - if (isExpanded) + if (!noFilter) { - Expand(true); - } - else - { - Collapse(true); + if (isExpanded) + Expand(true); + else + Collapse(true); } Visible = isThisVisible | isAnyChildVisible; From ec08a6ca7275bcc463a4a659767f960ce1b95e5d Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 3 Oct 2025 20:44:35 +0200 Subject: [PATCH 11/68] Reapply "Merge branch 'NoClearSearchboxesWhenStuffHappens' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-NoClearSearchboxesWhenStuffHappens" This reverts commit 537d8b57ca2457496322754fdc3e630d16fe28a3. --- 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..ab6d2e262 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 e78191cc827bb47a878f7d2bdd258bdbde4c7e79 Mon Sep 17 00:00:00 2001 From: Saas Date: Sat, 1 Nov 2025 17:04:58 +0100 Subject: [PATCH 12/68] fix error when clearing vj context menu --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 21d0e7470..1b14c8a9f 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -585,7 +585,7 @@ namespace FlaxEditor.Surface.ContextMenu private void UpdateFilters() { - if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes[0] == null) + if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes.Count > 0) { ResetView(); Profiler.EndEvent(); From 1188150163f087daeca203848208790fdf5e7f16 Mon Sep 17 00:00:00 2001 From: Saas Date: Sat, 1 Nov 2025 17:24:10 +0100 Subject: [PATCH 13/68] prevent node formatting tools from moving nodes with NoMove flag --- Source/Editor/Surface/VisjectSurface.Formatting.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs index e1b9a6777..26d668f64 100644 --- a/Source/Editor/Surface/VisjectSurface.Formatting.cs +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -164,7 +164,9 @@ namespace FlaxEditor.Surface /// /// List of nodes. public void StraightenGraphConnections(List nodes) - { + { + nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList(); + if (nodes.Count <= 1) return; @@ -350,8 +352,10 @@ namespace FlaxEditor.Surface /// List of nodes. /// Alignemnt type. public void AlignNodes(List nodes, NodeAlignmentType alignmentType) - { - if(nodes.Count <= 1) + { + nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList(); + + if (nodes.Count <= 1) return; var undoActions = new List(); @@ -392,6 +396,8 @@ namespace FlaxEditor.Surface /// If false will be done horizontally, if true will be done vertically. public void DistributeNodes(List nodes, bool vertically) { + nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList(); + if(nodes.Count <= 1) return; From d13b98b205e5437d473fb8354d703af4c75abdcc Mon Sep 17 00:00:00 2001 From: Saas Date: Sat, 1 Nov 2025 17:55:21 +0100 Subject: [PATCH 14/68] fix Auto Format Nodes to only produce one undo step --- .../Surface/VisjectSurface.Formatting.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs index 26d668f64..e55a4eb8a 100644 --- a/Source/Editor/Surface/VisjectSurface.Formatting.cs +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -39,6 +39,8 @@ namespace FlaxEditor.Surface if (nodes.Count <= 1) return; + List undoActions = new List(); + var nodesToVisit = new HashSet(nodes); // While we haven't formatted every node @@ -73,18 +75,23 @@ namespace FlaxEditor.Surface } } - FormatConnectedGraph(connectedNodes); + undoActions.AddRange(FormatConnectedGraph(connectedNodes)); } + + Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes")); + MarkAsEdited(false); } /// /// Formats a graph where all nodes are connected. /// /// List of connected nodes. - protected void FormatConnectedGraph(List nodes) + private List FormatConnectedGraph(List nodes) { + List undoActions = new List(); + if (nodes.Count <= 1) - return; + return undoActions; var boundingBox = GetNodesBounds(nodes); @@ -140,7 +147,6 @@ namespace FlaxEditor.Surface } // Set the node positions - var undoActions = new List(); var topRightPosition = endNodes[0].Location; for (int i = 0; i < nodes.Count; i++) { @@ -155,14 +161,14 @@ namespace FlaxEditor.Surface } } - MarkAsEdited(false); - Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes")); + return undoActions; } /// /// Straightens every connection between nodes in . /// /// List of nodes. + /// List of undo actions. public void StraightenGraphConnections(List nodes) { nodes = nodes.Where(n => !n.Archetype.Flags.HasFlag(NodeFlags.NoMove)).ToList(); From bf3403449d5c2cf9e8b001e6a6c5c9236c5388ba Mon Sep 17 00:00:00 2001 From: Saas Date: Sat, 1 Nov 2025 18:18:18 +0100 Subject: [PATCH 15/68] disable format nodes option when either a) all selected nodes have NoMove or b) clicked on node has NoMove --- Source/Editor/Surface/VisjectSurface.ContextMenu.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index d8dfb8ad3..ffb1c725a 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -410,8 +410,11 @@ namespace FlaxEditor.Surface } menu.AddSeparator(); - _cmFormatNodesMenu = menu.AddChildMenu("Format node(s)"); - _cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection; + bool allNodesNoMove = SelectedNodes.All(n => n.Archetype.Flags.HasFlag(NodeFlags.NoMove)); + bool clickedNodeNoMove = ((SelectedNodes.Count == 1 && controlUnderMouse is SurfaceNode n && n.Archetype.Flags.HasFlag(NodeFlags.NoMove))); + + _cmFormatNodesMenu = menu.AddChildMenu("Format nodes"); + _cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection && !(allNodesNoMove || clickedNodeNoMove); _cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }); _cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Straighten connections", Editor.Instance.Options.Options.Input.NodesStraightenConnections, () => { StraightenGraphConnections(SelectedNodes); }); From fbad6ba75cf2621008d822b89d59ad4be125269f Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 2 Nov 2025 00:50:07 +0100 Subject: [PATCH 16/68] add alternative node titles to pack/unpack nodes Now every pack/ unpack node can be found by searching: - Make - Construct - Compose - Break - Deconstruct - Decompose --- Source/Editor/Surface/Archetypes/Layers.cs | 4 ++++ Source/Editor/Surface/Archetypes/Packing.cs | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/Source/Editor/Surface/Archetypes/Layers.cs b/Source/Editor/Surface/Archetypes/Layers.cs index fe0053f7f..29de7cad5 100644 --- a/Source/Editor/Surface/Archetypes/Layers.cs +++ b/Source/Editor/Surface/Archetypes/Layers.cs @@ -52,6 +52,7 @@ namespace FlaxEditor.Surface.Archetypes }, new NodeArchetype { + // [Deprecated] TypeID = 3, Title = "Pack Material Layer", Description = "Pack material properties", @@ -75,6 +76,7 @@ namespace FlaxEditor.Surface.Archetypes }, new NodeArchetype { + // [Deprecated] TypeID = 4, Title = "Unpack Material Layer", Description = "Unpack material properties", @@ -120,6 +122,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 6, Title = "Pack Material Layer", Description = "Pack material properties", + AlternativeTitles = new[] { "Make Material Layer", "Construct Material Layer", "Compose Material Layer" }, Flags = NodeFlags.MaterialGraph, Size = new Float2(200, 280), Elements = new[] @@ -146,6 +149,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 7, Title = "Unpack Material Layer", Description = "Unpack material properties", + AlternativeTitles = new[] { "Break Material Layers", "Deconstruct Material Layers", "Decompose Material Layers" }, Flags = NodeFlags.MaterialGraph, Size = new Float2(210, 280), Elements = new[] diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 363d867f7..4d1e1d475 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -342,6 +342,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 20, Title = "Pack Float2", Description = "Pack components to Float2", + AlternativeTitles = new[] { "Make Float2", "Construct Float2", "Compose Float2" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 40), DefaultValues = new object[] @@ -361,6 +362,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 21, Title = "Pack Float3", Description = "Pack components to Float3", + AlternativeTitles = new[] { "Make Float3", "Construct Float3", "Compose Float3" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 60), DefaultValues = new object[] @@ -382,6 +384,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 22, Title = "Pack Float4", Description = "Pack components to Float4", + AlternativeTitles = new[] { "Make Float4", "Construct Float4", "Compose Float4" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 80), DefaultValues = new object[] @@ -405,6 +408,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 23, Title = "Pack Rotation", Description = "Pack components to Rotation", + AlternativeTitles = new[] { "Make Rotation", "Construct Rotation", "Compose Rotation" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 60), DefaultValues = new object[] @@ -426,6 +430,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 24, Title = "Pack Transform", Description = "Pack components to Transform", + AlternativeTitles = new[] { "Make Transform", "Construct Transform", "Compose Transform" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 80), Elements = new[] @@ -441,6 +446,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 25, Title = "Pack Box", Description = "Pack components to BoundingBox", + AlternativeTitles = new[] { "Make Box", "Construct Box", "Compose Box" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 40), Elements = new[] @@ -454,6 +460,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 26, Title = "Pack Structure", + AlternativeTitles = new[] { "Make Structure", "Construct Structure", "Compose Structure" }, Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch), IsInputCompatible = PackStructureNode.IsInputCompatible, IsOutputCompatible = PackStructureNode.IsOutputCompatible, @@ -479,6 +486,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 30, Title = "Unpack Float2", Description = "Unpack components from Float2", + AlternativeTitles = new[] { "Break Float2", "Deconstruct Float2", "Decompose Float2" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 40), Elements = new[] @@ -493,6 +501,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 31, Title = "Unpack Float3", Description = "Unpack components from Float3", + AlternativeTitles = new[] { "Break Float3", "Deconstruct Float3", "Decompose Float3" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 60), Elements = new[] @@ -508,6 +517,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 32, Title = "Unpack Float4", Description = "Unpack components from Float4", + AlternativeTitles = new[] { "Break Float4", "Deconstruct Float4", "Decompose Float4" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 80), Elements = new[] @@ -524,6 +534,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 33, Title = "Unpack Rotation", Description = "Unpack components from Rotation", + AlternativeTitles = new[] { "Break Rotation", "Deconstruct Rotation", "Decompose Rotation" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 60), Elements = new[] @@ -539,6 +550,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 34, Title = "Unpack Transform", Description = "Unpack components from Transform", + AlternativeTitles = new[] { "Break Transform", "Deconstruct Transform", "Decompose Transform" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 60), Elements = new[] @@ -554,6 +566,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 35, Title = "Unpack Box", Description = "Unpack components from BoundingBox", + AlternativeTitles = new[] { "Break BoundingBox", "Deconstruct BoundingBox", "Decompose BoundingBox" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 40), Elements = new[] @@ -572,6 +585,7 @@ namespace FlaxEditor.Surface.Archetypes IsOutputCompatible = UnpackStructureNode.IsOutputCompatible, GetInputOutputDescription = UnpackStructureNode.GetInputOutputDescription, Description = "Breaks the structure data to allow extracting components from it.", + AlternativeTitles = new[] { "Break Structure", "Deconstruct Structure", "Decompose Structure" }, Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(180, 20), DefaultValues = new object[] From 206b948ee1d9ecbe669d069f1e27d9ec0042b368 Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 2 Nov 2025 13:36:21 +0100 Subject: [PATCH 17/68] add "Split" for Unpack nodes --- Source/Editor/Surface/Archetypes/Layers.cs | 2 +- Source/Editor/Surface/Archetypes/Packing.cs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Layers.cs b/Source/Editor/Surface/Archetypes/Layers.cs index 29de7cad5..6c4caa6e7 100644 --- a/Source/Editor/Surface/Archetypes/Layers.cs +++ b/Source/Editor/Surface/Archetypes/Layers.cs @@ -149,7 +149,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 7, Title = "Unpack Material Layer", Description = "Unpack material properties", - AlternativeTitles = new[] { "Break Material Layers", "Deconstruct Material Layers", "Decompose Material Layers" }, + AlternativeTitles = new[] { "Break Material Layer", "Deconstruct Material Layer", "Decompose Material Layer", "Split Material Layer" }, Flags = NodeFlags.MaterialGraph, Size = new Float2(210, 280), Elements = new[] diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 4d1e1d475..8d57bc81e 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -486,7 +486,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 30, Title = "Unpack Float2", Description = "Unpack components from Float2", - AlternativeTitles = new[] { "Break Float2", "Deconstruct Float2", "Decompose Float2" }, + AlternativeTitles = new[] { "Break Float2", "Deconstruct Float2", "Decompose Float2", "Split Float2" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 40), Elements = new[] @@ -501,7 +501,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 31, Title = "Unpack Float3", Description = "Unpack components from Float3", - AlternativeTitles = new[] { "Break Float3", "Deconstruct Float3", "Decompose Float3" }, + AlternativeTitles = new[] { "Break Float3", "Deconstruct Float3", "Decompose Float3", "Split Float3" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 60), Elements = new[] @@ -517,7 +517,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 32, Title = "Unpack Float4", Description = "Unpack components from Float4", - AlternativeTitles = new[] { "Break Float4", "Deconstruct Float4", "Decompose Float4" }, + AlternativeTitles = new[] { "Break Float4", "Deconstruct Float4", "Decompose Float4", "Split Float4" }, Flags = NodeFlags.AllGraphs, Size = new Float2(150, 80), Elements = new[] @@ -534,7 +534,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 33, Title = "Unpack Rotation", Description = "Unpack components from Rotation", - AlternativeTitles = new[] { "Break Rotation", "Deconstruct Rotation", "Decompose Rotation" }, + AlternativeTitles = new[] { "Break Rotation", "Deconstruct Rotation", "Decompose Rotation", "Split Rotation" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 60), Elements = new[] @@ -550,7 +550,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 34, Title = "Unpack Transform", Description = "Unpack components from Transform", - AlternativeTitles = new[] { "Break Transform", "Deconstruct Transform", "Decompose Transform" }, + AlternativeTitles = new[] { "Break Transform", "Deconstruct Transform", "Decompose Transform", "Split Transform" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 60), Elements = new[] @@ -566,7 +566,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 35, Title = "Unpack Box", Description = "Unpack components from BoundingBox", - AlternativeTitles = new[] { "Break BoundingBox", "Deconstruct BoundingBox", "Decompose BoundingBox" }, + AlternativeTitles = new[] { "Break BoundingBox", "Deconstruct BoundingBox", "Decompose BoundingBox", "Split BoundingBox" }, Flags = NodeFlags.AllGraphs, Size = new Float2(170, 40), Elements = new[] @@ -585,7 +585,7 @@ namespace FlaxEditor.Surface.Archetypes IsOutputCompatible = UnpackStructureNode.IsOutputCompatible, GetInputOutputDescription = UnpackStructureNode.GetInputOutputDescription, Description = "Breaks the structure data to allow extracting components from it.", - AlternativeTitles = new[] { "Break Structure", "Deconstruct Structure", "Decompose Structure" }, + AlternativeTitles = new[] { "Break Structure", "Deconstruct Structure", "Decompose Structure", "Split Structure" }, Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(180, 20), DefaultValues = new object[] From b5b1d84b3f85d1f466b2c461963f699afafd1010 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 12 Dec 2025 22:44:13 +0100 Subject: [PATCH 18/68] fix silent crash sometimes occurring when dragging and dropping connections --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 1b14c8a9f..f13cb7117 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -585,7 +585,7 @@ namespace FlaxEditor.Surface.ContextMenu private void UpdateFilters() { - if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes.Count > 0) + if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes.Count > 0 && _selectedBoxes[0] == null) { ResetView(); Profiler.EndEvent(); From 88b02105b3ade4fc9c2f3bb2e795f1b029dd8e15 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 15 Dec 2025 20:36:35 -0600 Subject: [PATCH 19/68] Add direction gizmo. --- Source/Editor/Gizmo/DirectionGizmo.cs | 168 ++++++++++++++++++ .../Viewport/MainEditorGizmoViewport.cs | 3 + 2 files changed, 171 insertions(+) create mode 100644 Source/Editor/Gizmo/DirectionGizmo.cs diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs new file mode 100644 index 000000000..17e94564c --- /dev/null +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -0,0 +1,168 @@ +using System.Collections.Generic; +using FlaxEditor.Gizmo; +using FlaxEditor.Viewport; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.Gizmo; + +public class DirectionGizmo +{ + private IGizmoOwner _owner; + private ViewportProjection _viewportProjection; + private EditorViewport _viewport; + private Vector3 _gizmoCenter; + private float _axisLength = 70.0f; + private float _textAxisLength = 85.0f; + + private AxisData _xAxisData; + private AxisData _yAxisData; + private AxisData _zAxisData; + private AxisData _negXAxisData; + private AxisData _negYAxisData; + private AxisData _negZAxisData; + + private List _axisData = new List(); + + private SpriteHandle _posHandle; + private SpriteHandle _negHandle; + + private FontReference _fontReference; + + private struct ViewportProjection + { + private Matrix _viewProjection; + private BoundingFrustum _frustum; + private FlaxEngine.Viewport _viewport; + private Vector3 _origin; + + public void Init(EditorViewport editorViewport) + { + // Inline EditorViewport.ProjectPoint to save on calculation for large set of points + _viewport = new FlaxEngine.Viewport(0, 0, editorViewport.Width, editorViewport.Height); + _frustum = editorViewport.ViewFrustum; + _viewProjection = _frustum.Matrix; + _origin = editorViewport.Task.View.Origin; + } + + public void ProjectPoint(Vector3 worldSpaceLocation, out Float2 viewportSpaceLocation) + { + worldSpaceLocation -= _origin; + _viewport.Project(ref worldSpaceLocation, ref _viewProjection, out var projected); + viewportSpaceLocation = new Float2((float)projected.X, (float)projected.Y); + } + } + + private struct AxisData + { + public Float2 Delta; + public float Distance; + public string Label; + public Color AxisColor; + public bool Negative; + } + + /// + /// Constructor of the Direction Gizmo + /// + /// The owner of this object. + public DirectionGizmo(IGizmoOwner owner) + { + _owner = owner; + _viewport = owner.Viewport; + _viewportProjection.Init(owner.Viewport); + + _xAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = false }; + _yAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = false }; + _zAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = false }; + + _negXAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = true }; + _negYAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = true }; + _negZAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = true }; + _axisData.EnsureCapacity(6); + + _posHandle = Editor.Instance.Icons.VisjectBoxClosed32; + _negHandle = Editor.Instance.Icons.VisjectBoxOpen32; + + _fontReference = new FontReference(Style.Current.FontSmall); + _fontReference.Size = 8; + } + + /// + /// Used to Draw the gizmo. + /// + public void Draw() + { + _viewportProjection.Init(_owner.Viewport); + _gizmoCenter = _viewport.Task.View.WorldPosition + _viewport.Task.View.Direction * 1500; + _viewportProjection.ProjectPoint(_gizmoCenter, out var gizmoCenterScreen); + var bounds = _viewport.Bounds; + var screenLocation = bounds.Location - new Float2(-bounds.Width * 0.5f + 50, bounds.Height * 0.5f - 50); + var relativeCenter = gizmoCenterScreen - screenLocation; + + // Project unit vectors + _viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Right, out var xProjected); + _viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Up, out var yProjected); + _viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Forward, out var zProjected); + _viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Right, out var negXProjected); + _viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Up, out var negYProjected); + _viewportProjection.ProjectPoint(_gizmoCenter - Vector3.Forward, out var negZProjected); + + // Normalize by viewport height to keep size independent of FOV and viewport dimensions + float heightNormalization = _viewport.Height / 720.0f; // 720 = reference height + + Float2 xDelta = (xProjected - gizmoCenterScreen) / heightNormalization; + Float2 yDelta = (yProjected - gizmoCenterScreen) / heightNormalization; + Float2 zDelta = (zProjected - gizmoCenterScreen) / heightNormalization; + Float2 negXDelta = (negXProjected - gizmoCenterScreen) / heightNormalization; + Float2 negYDelta = (negYProjected - gizmoCenterScreen) / heightNormalization; + Float2 negZDelta = (negZProjected - gizmoCenterScreen) / heightNormalization; + + // Calculate distances from camera to determine draw order + Vector3 cameraPosition = _viewport.Task.View.Position; + float xDistance = Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Right); + float yDistance = Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Up); + float zDistance = Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Forward); + float negXDistance = Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Right); + float negYDistance = Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Up); + float negZDistance = Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Forward); + + _xAxisData.Delta = xDelta; + _xAxisData.Distance = xDistance; + _yAxisData.Delta = yDelta; + _yAxisData.Distance = yDistance; + _zAxisData.Delta = zDelta; + _zAxisData.Distance = zDistance; + _negXAxisData.Delta = negXDelta; + _negXAxisData.Distance = negXDistance; + _negYAxisData.Delta = negYDelta; + _negYAxisData.Distance = negYDistance; + _negZAxisData.Delta = negZDelta; + _negZAxisData.Distance = negZDistance; + + // Sort for correct draw order. + _axisData.Clear(); + _axisData.AddRange([_xAxisData, _yAxisData, _zAxisData, _negXAxisData, _negYAxisData, _negZAxisData]); + _axisData.Sort( (a, b) => -a.Distance.CompareTo(b.Distance)); + + // Draw in order from farthest to closest + foreach (var axis in _axisData) + { + Float2 tipScreen = relativeCenter + axis.Delta * _axisLength; + Float2 tipTextScreen = relativeCenter + axis.Delta * _textAxisLength; + + if (!axis.Negative) + { + Render2D.DrawLine(relativeCenter, tipScreen, axis.AxisColor, 2.0f); + Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor); + var font = _fontReference.GetFont(); + Render2D.DrawText(font, axis.Label, Color.Black, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); + } + else + { + Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor.RGBMultiplied(0.65f)); + Render2D.DrawSprite(_negHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor); + } + } + } +} diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 5343a1fe5..cd4b15799 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -107,6 +107,7 @@ namespace FlaxEditor.Viewport private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private EditorSpritesRenderer _editorSpritesRenderer; private ViewportRubberBandSelector _rubberBandSelector; + private DirectionGizmo _directionGizmo; /// /// Drag and drop handlers @@ -216,6 +217,7 @@ namespace FlaxEditor.Viewport // Add rubber band selector _rubberBandSelector = new ViewportRubberBandSelector(this); + _directionGizmo = new DirectionGizmo(this); // Add grid Grid = new GridGizmo(this); @@ -593,6 +595,7 @@ namespace FlaxEditor.Viewport // Draw rubber band for rectangle selection _rubberBandSelector.Draw(); + _directionGizmo.Draw(); } /// From 56e6df261d37c7adb71973d50d6dc7b0632efdc9 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 15 Dec 2025 21:22:11 -0600 Subject: [PATCH 20/68] Add hovering and selecting. --- Source/Editor/Gizmo/DirectionGizmo.cs | 113 ++++++++++++++++-- .../Viewport/MainEditorGizmoViewport.cs | 5 + 2 files changed, 108 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index 17e94564c..bfe2ebab9 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -12,8 +12,9 @@ public class DirectionGizmo private ViewportProjection _viewportProjection; private EditorViewport _viewport; private Vector3 _gizmoCenter; - private float _axisLength = 70.0f; - private float _textAxisLength = 85.0f; + private float _axisLength = 75.0f; + private float _textAxisLength = 95.0f; + private float _spriteRadius = 12.0f; private AxisData _xAxisData; private AxisData _yAxisData; @@ -23,11 +24,15 @@ public class DirectionGizmo private AxisData _negZAxisData; private List _axisData = new List(); + private int _hoveredAxisIndex = -1; private SpriteHandle _posHandle; private SpriteHandle _negHandle; private FontReference _fontReference; + + // Store sprite positions for hover detection + private List<(Float2 position, AxisDirection direction)> _spritePositions = new List<(Float2, AxisDirection)>(); private struct ViewportProjection { @@ -60,6 +65,17 @@ public class DirectionGizmo public string Label; public Color AxisColor; public bool Negative; + public AxisDirection Direction; + } + + private enum AxisDirection + { + PosX, + PosY, + PosZ, + NegX, + NegY, + NegZ } /// @@ -72,13 +88,13 @@ public class DirectionGizmo _viewport = owner.Viewport; _viewportProjection.Init(owner.Viewport); - _xAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = false }; - _yAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = false }; - _zAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = false }; + _xAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = false, Direction = AxisDirection.PosX }; + _yAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = false, Direction = AxisDirection.PosY }; + _zAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = false, Direction = AxisDirection.PosZ }; - _negXAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = true }; - _negYAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = true }; - _negZAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = true }; + _negXAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = true, Direction = AxisDirection.NegX }; + _negYAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = true, Direction = AxisDirection.NegY }; + _negZAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = true, Direction = AxisDirection.NegZ }; _axisData.EnsureCapacity(6); _posHandle = Editor.Instance.Icons.VisjectBoxClosed32; @@ -87,6 +103,66 @@ public class DirectionGizmo _fontReference = new FontReference(Style.Current.FontSmall); _fontReference.Size = 8; } + + private bool IsPointInSprite(Float2 point, Float2 spriteCenter) + { + Float2 delta = point - spriteCenter; + float distanceSq = delta.LengthSquared; + float radiusSq = _spriteRadius * _spriteRadius; + return distanceSq <= radiusSq; + } + + /// + /// Updates the gizmo for mouse interactions (hover and click detection). + /// + public void Update(Float2 mousePos) + { + _hoveredAxisIndex = -1; + + // Check which axis is being hovered - check from end (closest) to start (farthest) for proper layering + for (int i = _spritePositions.Count - 1; i >= 0; i--) + { + if (IsPointInSprite(mousePos, _spritePositions[i].position)) + { + _hoveredAxisIndex = i; + break; + } + } + } + + /// + /// Handles mouse click on direction gizmo sprites. + /// + public bool OnMouseDown(Float2 mousePos) + { + // Check which axis is being clicked - check from end (closest) to start (farthest) for proper layering + for (int i = _spritePositions.Count - 1; i >= 0; i--) + { + if (IsPointInSprite(mousePos, _spritePositions[i].position)) + { + OrientViewToAxis(_spritePositions[i].direction); + return true; + } + } + + return false; + } + + private void OrientViewToAxis(AxisDirection direction) + { + Quaternion orientation = direction switch + { + AxisDirection.PosX => Quaternion.Euler(0, 90, 0), + AxisDirection.NegX => Quaternion.Euler(0, -90, 0), + AxisDirection.PosY => Quaternion.Euler(-90, 0, 0), + AxisDirection.NegY => Quaternion.Euler(90, 0, 0), + AxisDirection.PosZ => Quaternion.Euler(0, 0, 0), + AxisDirection.NegZ => Quaternion.Euler(0, 180, 0), + _ => Quaternion.Identity + }; + + _viewport.ViewOrientation = orientation; + } /// /// Used to Draw the gizmo. @@ -144,24 +220,41 @@ public class DirectionGizmo _axisData.Clear(); _axisData.AddRange([_xAxisData, _yAxisData, _zAxisData, _negXAxisData, _negYAxisData, _negZAxisData]); _axisData.Sort( (a, b) => -a.Distance.CompareTo(b.Distance)); + + // Rebuild sprite positions list for hover detection + _spritePositions.Clear(); // Draw in order from farthest to closest - foreach (var axis in _axisData) + for (int i = 0; i < _axisData.Count; i++) { + var axis = _axisData[i]; Float2 tipScreen = relativeCenter + axis.Delta * _axisLength; Float2 tipTextScreen = relativeCenter + axis.Delta * _textAxisLength; + bool isHovered = _hoveredAxisIndex == i; + + // Store sprite position for hover detection + _spritePositions.Add((tipTextScreen, axis.Direction)); if (!axis.Negative) { Render2D.DrawLine(relativeCenter, tipScreen, axis.AxisColor, 2.0f); Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor); + var font = _fontReference.GetFont(); - Render2D.DrawText(font, axis.Label, Color.Black, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); + Color textColor = isHovered ? Color.White : Color.Black; + Render2D.DrawText(font, axis.Label, textColor, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); } else { Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor.RGBMultiplied(0.65f)); Render2D.DrawSprite(_negHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor); + + // Draw white label text on hover for negative axes + if (isHovered) + { + var font = _fontReference.GetFont(); + Render2D.DrawText(font, axis.Label, Color.White, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); + } } } } diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index cd4b15799..b5232c7e4 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -612,6 +612,8 @@ namespace FlaxEditor.Viewport { base.OnMouseMove(location); + _directionGizmo.Update(location); + // Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) && Gizmos?.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse; @@ -622,6 +624,9 @@ namespace FlaxEditor.Viewport protected override void OnLeftMouseButtonDown() { base.OnLeftMouseButtonDown(); + + if (_directionGizmo.OnMouseDown(_viewMousePos)) + return; _rubberBandSelector.TryStartingRubberBandSelection(); } From d671f57952ed9279961b0b6fc5ff1a0fec250ea1 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 15 Dec 2025 21:41:45 -0600 Subject: [PATCH 21/68] Change to container control for anchoring. --- Source/Editor/Gizmo/DirectionGizmo.cs | 43 +++++++++++-------- .../Viewport/MainEditorGizmoViewport.cs | 10 ++--- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index bfe2ebab9..350c3be5c 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -6,7 +6,7 @@ using FlaxEngine.GUI; namespace FlaxEditor.Gizmo; -public class DirectionGizmo +public class DirectionGizmo : ContainerControl { private IGizmoOwner _owner; private ViewportProjection _viewportProjection; @@ -33,7 +33,7 @@ public class DirectionGizmo // Store sprite positions for hover detection private List<(Float2 position, AxisDirection direction)> _spritePositions = new List<(Float2, AxisDirection)>(); - + private struct ViewportProjection { private Matrix _viewProjection; @@ -112,33 +112,40 @@ public class DirectionGizmo return distanceSq <= radiusSq; } - /// - /// Updates the gizmo for mouse interactions (hover and click detection). - /// - public void Update(Float2 mousePos) + /// + public override void OnMouseMove(Float2 location) { _hoveredAxisIndex = -1; + // Convert local control space to screen space + Float2 viewportLocation = PointToParent(ref location); + // Check which axis is being hovered - check from end (closest) to start (farthest) for proper layering for (int i = _spritePositions.Count - 1; i >= 0; i--) { - if (IsPointInSprite(mousePos, _spritePositions[i].position)) + if (IsPointInSprite(viewportLocation, _spritePositions[i].position)) { _hoveredAxisIndex = i; break; } } + + base.OnMouseMove(location); } - - /// - /// Handles mouse click on direction gizmo sprites. - /// - public bool OnMouseDown(Float2 mousePos) + + /// + public override bool OnMouseDown(Float2 location, MouseButton button) { + if (base.OnMouseDown(location, button)) + return true; + + // Convert local control space to screen space + Float2 viewportLocation = PointToParent(ref location); + // Check which axis is being clicked - check from end (closest) to start (farthest) for proper layering for (int i = _spritePositions.Count - 1; i >= 0; i--) { - if (IsPointInSprite(mousePos, _spritePositions[i].position)) + if (IsPointInSprite(viewportLocation, _spritePositions[i].position)) { OrientViewToAxis(_spritePositions[i].direction); return true; @@ -167,14 +174,16 @@ public class DirectionGizmo /// /// Used to Draw the gizmo. /// - public void Draw() + public override void DrawSelf() { + base.DrawSelf(); + _viewportProjection.Init(_owner.Viewport); _gizmoCenter = _viewport.Task.View.WorldPosition + _viewport.Task.View.Direction * 1500; _viewportProjection.ProjectPoint(_gizmoCenter, out var gizmoCenterScreen); - var bounds = _viewport.Bounds; - var screenLocation = bounds.Location - new Float2(-bounds.Width * 0.5f + 50, bounds.Height * 0.5f - 50); - var relativeCenter = gizmoCenterScreen - screenLocation; + + // Use the settable bounds instead of hardcoded positioning + var relativeCenter = Bounds.Location + Bounds.Size * 0.5f; // Project unit vectors _viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Right, out var xProjected); diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index b5232c7e4..fccb7a79e 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -218,6 +218,11 @@ namespace FlaxEditor.Viewport // Add rubber band selector _rubberBandSelector = new ViewportRubberBandSelector(this); _directionGizmo = new DirectionGizmo(this); + _directionGizmo.Parent = this; + _directionGizmo.AnchorPreset = AnchorPresets.TopRight; + _directionGizmo.LocalY += 30; + _directionGizmo.LocalX -= 10; + _directionGizmo.Size = new Float2(100, 100); // Add grid Grid = new GridGizmo(this); @@ -612,8 +617,6 @@ namespace FlaxEditor.Viewport { base.OnMouseMove(location); - _directionGizmo.Update(location); - // Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) && Gizmos?.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse; @@ -624,9 +627,6 @@ namespace FlaxEditor.Viewport protected override void OnLeftMouseButtonDown() { base.OnLeftMouseButtonDown(); - - if (_directionGizmo.OnMouseDown(_viewMousePos)) - return; _rubberBandSelector.TryStartingRubberBandSelection(); } From 9708601d3b6d879e52207b5d71cb35d3951c8a51 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 15 Dec 2025 21:46:03 -0600 Subject: [PATCH 22/68] Small cleanup. --- Source/Editor/Gizmo/DirectionGizmo.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index 350c3be5c..bb6823002 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -120,7 +120,7 @@ public class DirectionGizmo : ContainerControl // Convert local control space to screen space Float2 viewportLocation = PointToParent(ref location); - // Check which axis is being hovered - check from end (closest) to start (farthest) for proper layering + // Check which axis is being hovered - check from closest to farthest for proper layering for (int i = _spritePositions.Count - 1; i >= 0; i--) { if (IsPointInSprite(viewportLocation, _spritePositions[i].position)) @@ -142,7 +142,7 @@ public class DirectionGizmo : ContainerControl // Convert local control space to screen space Float2 viewportLocation = PointToParent(ref location); - // Check which axis is being clicked - check from end (closest) to start (farthest) for proper layering + // Check which axis is being clicked - check from closest to farthest for proper layering for (int i = _spritePositions.Count - 1; i >= 0; i--) { if (IsPointInSprite(viewportLocation, _spritePositions[i].position)) @@ -247,7 +247,7 @@ public class DirectionGizmo : ContainerControl if (!axis.Negative) { Render2D.DrawLine(relativeCenter, tipScreen, axis.AxisColor, 2.0f); - Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor); + Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor); var font = _fontReference.GetFont(); Color textColor = isHovered ? Color.White : Color.Black; @@ -255,8 +255,8 @@ public class DirectionGizmo : ContainerControl } else { - Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor.RGBMultiplied(0.65f)); - Render2D.DrawSprite(_negHandle, new Rectangle(tipTextScreen - new Float2(12, 12), new Float2(24, 24)), axis.AxisColor); + Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor.RGBMultiplied(0.65f)); + Render2D.DrawSprite(_negHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor); // Draw white label text on hover for negative axes if (isHovered) From b9f177d1ab17185f1ebf7ecee0bbcd8daeb1ae1b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 16 Dec 2025 17:12:54 -0600 Subject: [PATCH 23/68] Add ensure capacity to lists. --- Source/Editor/Gizmo/DirectionGizmo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index bb6823002..1c0af3d70 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -96,6 +96,7 @@ public class DirectionGizmo : ContainerControl _negYAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = true, Direction = AxisDirection.NegY }; _negZAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = true, Direction = AxisDirection.NegZ }; _axisData.EnsureCapacity(6); + _spritePositions.EnsureCapacity(6); _posHandle = Editor.Instance.Icons.VisjectBoxClosed32; _negHandle = Editor.Instance.Icons.VisjectBoxOpen32; From 2c5cc7fcc00d83fe165ef7ef09830571ee41cf8e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 16 Dec 2025 17:32:35 -0600 Subject: [PATCH 24/68] Fix double drawing. --- Source/Editor/Gizmo/DirectionGizmo.cs | 22 +++++++------------ .../Viewport/MainEditorGizmoViewport.cs | 9 ++++---- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index 1c0af3d70..082ceef0c 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -118,13 +118,10 @@ public class DirectionGizmo : ContainerControl { _hoveredAxisIndex = -1; - // Convert local control space to screen space - Float2 viewportLocation = PointToParent(ref location); - // Check which axis is being hovered - check from closest to farthest for proper layering for (int i = _spritePositions.Count - 1; i >= 0; i--) { - if (IsPointInSprite(viewportLocation, _spritePositions[i].position)) + if (IsPointInSprite(location, _spritePositions[i].position)) { _hoveredAxisIndex = i; break; @@ -140,13 +137,10 @@ public class DirectionGizmo : ContainerControl if (base.OnMouseDown(location, button)) return true; - // Convert local control space to screen space - Float2 viewportLocation = PointToParent(ref location); - // Check which axis is being clicked - check from closest to farthest for proper layering for (int i = _spritePositions.Count - 1; i >= 0; i--) { - if (IsPointInSprite(viewportLocation, _spritePositions[i].position)) + if (IsPointInSprite(location, _spritePositions[i].position)) { OrientViewToAxis(_spritePositions[i].direction); return true; @@ -182,9 +176,8 @@ public class DirectionGizmo : ContainerControl _viewportProjection.Init(_owner.Viewport); _gizmoCenter = _viewport.Task.View.WorldPosition + _viewport.Task.View.Direction * 1500; _viewportProjection.ProjectPoint(_gizmoCenter, out var gizmoCenterScreen); - - // Use the settable bounds instead of hardcoded positioning - var relativeCenter = Bounds.Location + Bounds.Size * 0.5f; + + var relativeCenter = Size * 0.5f; // Project unit vectors _viewportProjection.ProjectPoint(_gizmoCenter + Vector3.Right, out var xProjected); @@ -233,7 +226,9 @@ public class DirectionGizmo : ContainerControl // Rebuild sprite positions list for hover detection _spritePositions.Clear(); - + + Render2D.DrawSprite(_posHandle, new Rectangle(0, 0, Size), Color.Black.AlphaMultiplied(0.1f)); + // Draw in order from farthest to closest for (int i = 0; i < _axisData.Count; i++) { @@ -258,8 +253,7 @@ public class DirectionGizmo : ContainerControl { Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor.RGBMultiplied(0.65f)); Render2D.DrawSprite(_negHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor); - - // Draw white label text on hover for negative axes + if (isHovered) { var font = _fontReference.GetFont(); diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index fccb7a79e..86dba5947 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -218,11 +218,11 @@ namespace FlaxEditor.Viewport // Add rubber band selector _rubberBandSelector = new ViewportRubberBandSelector(this); _directionGizmo = new DirectionGizmo(this); - _directionGizmo.Parent = this; _directionGizmo.AnchorPreset = AnchorPresets.TopRight; - _directionGizmo.LocalY += 30; - _directionGizmo.LocalX -= 10; - _directionGizmo.Size = new Float2(100, 100); + _directionGizmo.Parent = this; + _directionGizmo.LocalY += 25; + _directionGizmo.LocalX -= 150; + _directionGizmo.Size = new Float2(150, 150); // Add grid Grid = new GridGizmo(this); @@ -600,7 +600,6 @@ namespace FlaxEditor.Viewport // Draw rubber band for rectangle selection _rubberBandSelector.Draw(); - _directionGizmo.Draw(); } /// From 1b9f6ed20f81a9b046336e72acb1e5feaf8efdeb Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 16 Dec 2025 18:16:58 -0600 Subject: [PATCH 25/68] Fix large world build. --- Source/Editor/Gizmo/DirectionGizmo.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index 082ceef0c..c9089e5fc 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -199,12 +199,12 @@ public class DirectionGizmo : ContainerControl // Calculate distances from camera to determine draw order Vector3 cameraPosition = _viewport.Task.View.Position; - float xDistance = Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Right); - float yDistance = Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Up); - float zDistance = Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Forward); - float negXDistance = Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Right); - float negYDistance = Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Up); - float negZDistance = Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Forward); + float xDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Right); + float yDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Up); + float zDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter + Vector3.Forward); + float negXDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Right); + float negYDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Up); + float negZDistance = (float)Vector3.Distance(cameraPosition, _gizmoCenter - Vector3.Forward); _xAxisData.Delta = xDelta; _xAxisData.Distance = xDistance; From f1d5c0257feef5bc0d1023a5a4457dc54bf44d91 Mon Sep 17 00:00:00 2001 From: Saas Date: Mon, 2 Feb 2026 12:50:24 +0100 Subject: [PATCH 26/68] improve radial menu control - Add AllowChangeSelectionWhenOutside to allow changing the selection from the outside the menu - Expose UpdateAngle() to get *some* kind of controller support - Expose `SelectedSegment` - Improve various doc comments --- Source/Engine/UI/GUI/Special/RadialMenu.cs | 77 +++++++++++++++------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/Source/Engine/UI/GUI/Special/RadialMenu.cs b/Source/Engine/UI/GUI/Special/RadialMenu.cs index 060c4ef90..f8309fb1e 100644 --- a/Source/Engine/UI/GUI/Special/RadialMenu.cs +++ b/Source/Engine/UI/GUI/Special/RadialMenu.cs @@ -5,7 +5,7 @@ using System; namespace FlaxEngine.GUI { /// - /// Radial menu control that arranges child controls (of type Image) in a circle. + /// Radial menu control that arranges child controls (of type ) in a circle. /// /// public class RadialMenu : ContainerControl @@ -27,7 +27,7 @@ namespace FlaxEngine.GUI private bool ShowMatProp => _material != null; /// - /// The material to use for menu background drawing. + /// The material used for menu background drawing. /// [EditorOrder(1)] public MaterialBase Material @@ -44,7 +44,7 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets the edge offset. + /// Gets or sets the offset of the outer edge from the bounds of the Control. /// [EditorOrder(2), Range(0, 1)] public float EdgeOffset @@ -59,7 +59,7 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets the thickness. + /// Gets or sets the thickness of the menu. /// [EditorOrder(3), Range(0, 1), VisibleIf(nameof(ShowMatProp))] public float Thickness @@ -74,7 +74,7 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets control background color (transparent color (alpha=0) means no background rendering). + /// Gets or sets control background color (transparent color means no background rendering). /// [VisibleIf(nameof(ShowMatProp))] public new Color BackgroundColor @@ -88,7 +88,7 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets the color of the highlight. + /// Gets or sets the color of the outer edge highlight. /// [VisibleIf(nameof(ShowMatProp))] public Color HighlightColor @@ -130,19 +130,37 @@ namespace FlaxEngine.GUI } /// - /// The selected callback + /// The selected callback. /// [HideInEditor] public Action Selected; /// - /// The allow change selection when inside + /// Invoked when the hovered segment is changed. + /// + [HideInEditor] + public Action HoveredSelectionChanged; + + /// + /// The selected segment. + /// + [HideInEditor] + public float SelectedSegment => _selectedSegment; + + /// + /// Allows the selected to change when the mouse is moved in the empty center of the menu. /// [VisibleIf(nameof(ShowMatProp))] public bool AllowChangeSelectionWhenInside; /// - /// The center as button + /// Allows the selected to change when the mouse is moved outside of the menu. + /// + [VisibleIf(nameof(ShowMatProp))] + public bool AllowChangeSelectionWhenOutside; + + /// + /// Wether the center is a button. /// [VisibleIf(nameof(ShowMatProp))] public bool CenterAsButton; @@ -225,7 +243,7 @@ namespace FlaxEngine.GUI var min = ((1 - _edgeOffset) - _thickness) * USize * 0.5f; var max = (1 - _edgeOffset) * USize * 0.5f; var val = ((USize * 0.5f) - location).Length; - if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside) + if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside || val > max && AllowChangeSelectionWhenOutside) { UpdateAngle(ref location); } @@ -276,7 +294,7 @@ namespace FlaxEngine.GUI var min = ((1 - _edgeOffset) - _thickness) * USize * 0.5f; var max = (1 - _edgeOffset) * USize * 0.5f; var val = ((USize * 0.5f) - location).Length; - if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside) + if (Mathf.IsInRange(val, min, max) || val < min && AllowChangeSelectionWhenInside || val > max && AllowChangeSelectionWhenOutside) { UpdateAngle(ref location); } @@ -347,6 +365,29 @@ namespace FlaxEngine.GUI base.PerformLayout(force); } + /// + /// Updates the current angle and selected segment of the radial menu based on the specified location inside of the control. + /// + /// The position used to determine the angle and segment selection within the radial menu. + public void UpdateAngle(ref Float2 location) + { + float previousSelectedSegment = _selectedSegment; + + var size = new Float2(USize); + var p = (size * 0.5f) - location; + var sa = (1.0f / _segmentCount) * Mathf.TwoPi; + _angle = Mathf.Atan2(p.X, p.Y); + _angle = Mathf.Ceil((_angle - (sa * 0.5f)) / sa) * sa; + _selectedSegment = _angle; + _selectedSegment = Mathf.RoundToInt((_selectedSegment < 0 ? Mathf.TwoPi + _selectedSegment : _selectedSegment) / sa); + if (float.IsNaN(_angle) || float.IsInfinity(_angle)) + _angle = 0; + _materialInstance.SetParameterValue("RadialMenu_Rotation", -_angle + Mathf.Pi); + + if (previousSelectedSegment != _selectedSegment) + HoveredSelectionChanged?.Invoke((int)_selectedSegment); + } + private void UpdateSelectionColor() { Color color; @@ -368,20 +409,6 @@ namespace FlaxEngine.GUI _materialInstance.SetParameterValue("RadialMenu_SelectionColor", color); } - private void UpdateAngle(ref Float2 location) - { - var size = new Float2(USize); - var p = (size * 0.5f) - location; - var sa = (1.0f / _segmentCount) * Mathf.TwoPi; - _angle = Mathf.Atan2(p.X, p.Y); - _angle = Mathf.Ceil((_angle - (sa * 0.5f)) / sa) * sa; - _selectedSegment = _angle; - _selectedSegment = Mathf.RoundToInt((_selectedSegment < 0 ? Mathf.TwoPi + _selectedSegment : _selectedSegment) / sa); - if (float.IsNaN(_angle) || float.IsInfinity(_angle)) - _angle = 0; - _materialInstance.SetParameterValue("RadialMenu_Rotation", -_angle + Mathf.Pi); - } - private static Float2 Rotate2D(Float2 point, float angle) { return new Float2(Mathf.Cos(angle) * point.X + Mathf.Sin(angle) * point.Y, From b0a0c5b66c3f783a5bf6de391ef26725b0a4806d Mon Sep 17 00:00:00 2001 From: Saas Date: Mon, 2 Feb 2026 12:52:54 +0100 Subject: [PATCH 27/68] change selectedSegment to int --- Source/Engine/UI/GUI/Special/RadialMenu.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Engine/UI/GUI/Special/RadialMenu.cs b/Source/Engine/UI/GUI/Special/RadialMenu.cs index f8309fb1e..3e752aa46 100644 --- a/Source/Engine/UI/GUI/Special/RadialMenu.cs +++ b/Source/Engine/UI/GUI/Special/RadialMenu.cs @@ -12,7 +12,7 @@ namespace FlaxEngine.GUI { private bool _materialIsDirty = true; private float _angle; - private float _selectedSegment; + private int _selectedSegment; private int _highlightSegment = -1; private MaterialBase _material; private MaterialInstance _materialInstance; @@ -145,7 +145,7 @@ namespace FlaxEngine.GUI /// The selected segment. /// [HideInEditor] - public float SelectedSegment => _selectedSegment; + public int SelectedSegment => _selectedSegment; /// /// Allows the selected to change when the mouse is moved in the empty center of the menu. @@ -378,8 +378,7 @@ namespace FlaxEngine.GUI var sa = (1.0f / _segmentCount) * Mathf.TwoPi; _angle = Mathf.Atan2(p.X, p.Y); _angle = Mathf.Ceil((_angle - (sa * 0.5f)) / sa) * sa; - _selectedSegment = _angle; - _selectedSegment = Mathf.RoundToInt((_selectedSegment < 0 ? Mathf.TwoPi + _selectedSegment : _selectedSegment) / sa); + _selectedSegment = Mathf.RoundToInt((_angle < 0 ? Mathf.TwoPi + _angle : _angle) / sa); if (float.IsNaN(_angle) || float.IsInfinity(_angle)) _angle = 0; _materialInstance.SetParameterValue("RadialMenu_Rotation", -_angle + Mathf.Pi); From b27e55636302ad5eaf1c4cad1027b7ced01c0fa5 Mon Sep 17 00:00:00 2001 From: Saas Date: Mon, 2 Feb 2026 21:35:40 +0100 Subject: [PATCH 28/68] expose material instance --- Source/Engine/UI/GUI/Special/RadialMenu.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/UI/GUI/Special/RadialMenu.cs b/Source/Engine/UI/GUI/Special/RadialMenu.cs index 3e752aa46..994d81ab2 100644 --- a/Source/Engine/UI/GUI/Special/RadialMenu.cs +++ b/Source/Engine/UI/GUI/Special/RadialMenu.cs @@ -129,6 +129,12 @@ namespace FlaxEngine.GUI } } + /// + /// The material instance of used to draw the menu. + /// + [HideInEditor] + public MaterialInstance MaterialInstance => _materialInstance; + /// /// The selected callback. /// From ab88a6339a111be64226b6f036f8b1317911a393 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 3 Feb 2026 21:36:32 +0100 Subject: [PATCH 29/68] add ripping apart connection with reconnect and auto connect when node is dropped over existing connection --- Source/Editor/Surface/VisjectSurface.Input.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 056987e52..e3c443a74 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -321,6 +321,33 @@ namespace FlaxEditor.Surface foreach (var node in _movingNodes) { + // Allow ripping the node from its current connection + if (RootWindow.GetKey(KeyboardKeys.Alt)) + { + InputBox nodeConnectedInput = null; + OutputBox nodeConnectedOuput = null; + + var boxes = node.GetBoxes(); + foreach (var box in boxes) + { + if (!box.IsOutput && box.Connections.Count > 0) + { + nodeConnectedInput = (InputBox)box; + continue; + } + if (box.IsOutput && box.Connections.Count > 0) + { + nodeConnectedOuput = (OutputBox)box; + continue; + } + } + + if (nodeConnectedInput != null && nodeConnectedOuput != null) + TryConnect(nodeConnectedOuput.Connections[0], nodeConnectedInput.Connections[0]); + + node.RemoveConnections(); + } + if (gridSnap) { Float2 unroundedLocation = node.Location; @@ -602,6 +629,24 @@ namespace FlaxEditor.Surface { if (_movingNodes != null && _movingNodes.Count > 0) { + // Allow dropping the node onto an existing connection and connect it + if (_movingNodes.Count == 1) + { + var mousePos = _rootControl.PointFromParent(ref _mousePos); + InputBox intersectedConnectionInputBox; + OutputBox intersectedConnectionOutputBox; + if (IntersectsConnection(mousePos, out intersectedConnectionInputBox, out intersectedConnectionOutputBox)) + { + SurfaceNode node = _movingNodes.First(); + InputBox nodeInputBox = (InputBox)node.GetBoxes().First(b => !b.IsOutput); + OutputBox nodeOutputBox = (OutputBox)node.GetBoxes().First(b => b.IsOutput); + TryConnect(intersectedConnectionOutputBox, nodeInputBox); + TryConnect(nodeOutputBox, intersectedConnectionInputBox); + + Debug.Log($"INPUT OUTPUT: {intersectedConnectionNodesXDistance}, NODE: {node.Width}"); + } + } + if (Undo != null && !_movingNodesDelta.IsZero && CanEdit) Undo.AddAction(new MoveNodesAction(Context, _movingNodes.Select(x => x.ID).ToArray(), _movingNodesDelta)); _movingNodes.Clear(); From 8d8bf87c6957090d0aae451848ee504669681d54 Mon Sep 17 00:00:00 2001 From: Saas Date: Wed, 4 Feb 2026 13:52:37 +0100 Subject: [PATCH 30/68] move nodes if needed and add undo support --- Source/Editor/Surface/VisjectSurface.Input.cs | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index e3c443a74..c2c5fac68 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -629,7 +629,7 @@ namespace FlaxEditor.Surface { if (_movingNodes != null && _movingNodes.Count > 0) { - // Allow dropping the node onto an existing connection and connect it + // Allow dropping a single node onto an existing connection and connect it if (_movingNodes.Count == 1) { var mousePos = _rootControl.PointFromParent(ref _mousePos); @@ -643,12 +643,57 @@ namespace FlaxEditor.Surface TryConnect(intersectedConnectionOutputBox, nodeInputBox); TryConnect(nodeOutputBox, intersectedConnectionInputBox); - Debug.Log($"INPUT OUTPUT: {intersectedConnectionNodesXDistance}, NODE: {node.Width}"); + float intersectedConnectionNodesXDistance = intersectedConnectionInputBox.ParentNode.Left - intersectedConnectionOutputBox.ParentNode.Right; + float paddedNodeWidth = node.Width + 2f; + if (intersectedConnectionNodesXDistance < paddedNodeWidth) + { + List visitedNodes = new List{ node }; + List movedNodes = new List(); + Float2 locationDelta = new Float2(paddedNodeWidth, 0f); + + MoveConnectedNodes(intersectedConnectionInputBox.ParentNode); + + void MoveConnectedNodes(SurfaceNode node) + { + // Only move node if it is to the right of the node we have connected the moved node to + if (node.Right > intersectedConnectionInputBox.ParentNode.Left + 15f) + { + node.Location += locationDelta; + movedNodes.Add(node); + } + + visitedNodes.Add(node); + + foreach (var box in node.GetBoxes()) + { + if (!box.HasAnyConnection || box == intersectedConnectionInputBox) + continue; + + foreach (var connectedBox in box.Connections) + { + SurfaceNode nextNode = connectedBox.ParentNode; + if (visitedNodes.Contains(nextNode)) + continue; + + MoveConnectedNodes(nextNode); + } + } + } + + Float2 nodeMoveOffset = new Float2(node.Width * 0.5f, 0f); + node.Location += nodeMoveOffset; + + var moveNodesAction = new MoveNodesAction(Context, movedNodes.Select(n => n.ID).ToArray(), locationDelta); + var moveNodeAction = new MoveNodesAction(Context, [node.ID], nodeMoveOffset); + var multiAction = new MultiUndoAction(moveNodeAction, moveNodesAction); + + AddBatchedUndoAction(multiAction); + } } } if (Undo != null && !_movingNodesDelta.IsZero && CanEdit) - Undo.AddAction(new MoveNodesAction(Context, _movingNodes.Select(x => x.ID).ToArray(), _movingNodesDelta)); + AddBatchedUndoAction(new MoveNodesAction(Context, _movingNodes.Select(x => x.ID).ToArray(), _movingNodesDelta)); _movingNodes.Clear(); } _movingNodesDelta = Float2.Zero; From 29b043342af0a73fd1241797edb627ba679a5c56 Mon Sep 17 00:00:00 2001 From: Saas Date: Wed, 4 Feb 2026 14:11:23 +0100 Subject: [PATCH 31/68] don't fall into the trap of moving nodes with NoMove flag again --- Source/Editor/Surface/VisjectSurface.Input.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index c2c5fac68..388df0b9c 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -656,7 +656,7 @@ namespace FlaxEditor.Surface void MoveConnectedNodes(SurfaceNode node) { // Only move node if it is to the right of the node we have connected the moved node to - if (node.Right > intersectedConnectionInputBox.ParentNode.Left + 15f) + if (node.Right > intersectedConnectionInputBox.ParentNode.Left + 15f && !node.Archetype.Flags.HasFlag(NodeFlags.NoMove)) { node.Location += locationDelta; movedNodes.Add(node); From a57fe6c04df80cdfe663849f514ad7640ab7006b Mon Sep 17 00:00:00 2001 From: Saas Date: Wed, 4 Feb 2026 21:31:03 +0100 Subject: [PATCH 32/68] add lazy connect feature to visject --- Source/Editor/Surface/VisjectSurface.Draw.cs | 41 ++++++++++++ Source/Editor/Surface/VisjectSurface.Input.cs | 65 ++++++++++++++++++- 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index af5893907..d79b21e9c 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -213,6 +213,44 @@ namespace FlaxEditor.Surface } } + /// + /// Draw connection hints for lazy connect feature. + /// + protected virtual void DrawLazyConnect() + { + var style = FlaxEngine.GUI.Style.Current; + + if (_lazyConnectStartNode != null) + { + Float2 upperLeft = _rootControl.PointToParent(_lazyConnectStartNode.UpperLeft); + Rectangle startNodeOutline = new Rectangle(upperLeft + 1f, _lazyConnectStartNode.Size - 1f); + startNodeOutline.Size *= ViewScale; + Render2D.DrawRectangle(startNodeOutline.MakeExpanded(4f), style.BackgroundSelected, 4f); + } + + if (_lazyConnectEndNode != null) + { + Float2 upperLeft = _rootControl.PointToParent(_lazyConnectEndNode.UpperLeft); + Rectangle startNodeOutline = new Rectangle(upperLeft + 1f, _lazyConnectEndNode.Size - 1f); + startNodeOutline.Size *= ViewScale; + Render2D.DrawRectangle(startNodeOutline.MakeExpanded(4f), style.BackgroundSelected, 4f); + } + + Rectangle startRect = new Rectangle(_rightMouseDownPos - 6f, new Float2(12f)); + Rectangle endRect = new Rectangle(_mousePos - 6f, new Float2(12f)); + + // Start and end shadows/ outlines + Render2D.FillRectangle(startRect.MakeExpanded(2.5f), Color.Black); + Render2D.FillRectangle(endRect.MakeExpanded(2.5f), Color.Black); + + Render2D.DrawLine(_rightMouseDownPos, _mousePos, Color.Black, 7.5f); + Render2D.DrawLine(_rightMouseDownPos, _mousePos, style.ForegroundGrey, 5f); + + // Draw start and end boxes over the lines to hide ugly artifacts at the ends + Render2D.FillRectangle(startRect, style.ForegroundGrey); + Render2D.FillRectangle(endRect, style.ForegroundGrey); + } + /// /// Draws the contents of the surface (nodes, connections, comments, etc.). /// @@ -260,6 +298,9 @@ namespace FlaxEditor.Surface DrawContents(); + if (_isLazyConnecting) + DrawLazyConnect(); + //Render2D.DrawText(style.FontTitle, string.Format("Scale: {0}", _rootControl.Scale), rect, Enabled ? Color.Red : Color.Black); // Draw border diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 388df0b9c..f7d90512e 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -29,6 +29,9 @@ namespace FlaxEditor.Surface private HashSet _movingNodes; private HashSet _temporarySelectedNodes; private readonly Stack _inputBrackets = new Stack(); + private bool _isLazyConnecting; + private SurfaceNode _lazyConnectStartNode; + private SurfaceNode _lazyConnectEndNode; private class InputBracket { @@ -250,8 +253,13 @@ namespace FlaxEditor.Surface // Cache mouse location _mousePos = location; + if (_isLazyConnecting && GetControlUnderMouse() is SurfaceNode nodeUnderMouse) + _lazyConnectEndNode = nodeUnderMouse; + else if (_isLazyConnecting && Nodes.Count > 0) + _lazyConnectEndNode = GetClosestNodeAtLocation(location); + // Moving around surface with mouse - if (_rightMouseDown) + if (_rightMouseDown && !_isLazyConnecting) { // Calculate delta var delta = location - _rightMouseDownPos; @@ -542,11 +550,17 @@ namespace FlaxEditor.Surface _middleMouseDownPos = location; } + if (root.GetKey(KeyboardKeys.Alt) && button == MouseButton.Right) + _isLazyConnecting = true; + // Check if any node is under the mouse SurfaceControl controlUnderMouse = GetControlUnderMouse(); var cLocation = _rootControl.PointFromParent(ref location); if (controlUnderMouse != null) { + if (controlUnderMouse is SurfaceNode node && _isLazyConnecting) + _lazyConnectStartNode = node; + // Check if mouse is over header and user is pressing mouse left button if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation)) { @@ -581,6 +595,9 @@ namespace FlaxEditor.Surface } else { + if (_isLazyConnecting && Nodes.Count > 0) + _lazyConnectStartNode = GetClosestNodeAtLocation(location); + // Cache flags and state if (_leftMouseDown) { @@ -720,12 +737,36 @@ namespace FlaxEditor.Surface { // Check if any control is under the mouse _cmStartPos = location; - if (controlUnderMouse == null) + if (controlUnderMouse == null && !_isLazyConnecting) { showPrimaryMenu = true; } } _mouseMoveAmount = 0; + + if (_isLazyConnecting) + { + if (_lazyConnectStartNode != null && _lazyConnectEndNode != null && _lazyConnectStartNode != _lazyConnectEndNode) + { + // First check if there is a type matching input and output where input + OutputBox startNodeOutput = (OutputBox)_lazyConnectStartNode.GetBoxes().FirstOrDefault(b => b.IsOutput, null); + InputBox endNodeInput = null; + + if (startNodeOutput != null) + endNodeInput = (InputBox)_lazyConnectEndNode.GetBoxes().FirstOrDefault(b => !b.IsOutput && b.CurrentType == startNodeOutput.CurrentType && !b.HasAnyConnection && b.IsActive && b.CanConnectWith(startNodeOutput), null); + + // Perform less strict checks (less ideal conditions for connection but still good) if the first checks failed + if (endNodeInput == null) + endNodeInput = (InputBox)_lazyConnectEndNode.GetBoxes().FirstOrDefault(b => !b.IsOutput && !b.HasAnyConnection && b.CanConnectWith(startNodeOutput), null); + + if (startNodeOutput != null && endNodeInput != null) + TryConnect(startNodeOutput, endNodeInput); + } + + _isLazyConnecting = false; + _lazyConnectStartNode = null; + _lazyConnectEndNode = null; + } } if (_middleMouseDown && button == MouseButton.Middle) { @@ -927,6 +968,26 @@ namespace FlaxEditor.Surface return false; } + private SurfaceNode GetClosestNodeAtLocation(Float2 location) + { + SurfaceNode currentClosestNode = null; + float currentClosestDistanceSquared = float.MaxValue; + + foreach (var node in Nodes) + { + Float2 nodeSurfaceLocation = _rootControl.PointToParent(node.Center); + + float distanceSquared = Float2.DistanceSquared(location, nodeSurfaceLocation); + if (distanceSquared < currentClosestDistanceSquared) + { + currentClosestNode = node; + currentClosestDistanceSquared = distanceSquared; + } + } + + return currentClosestNode; + } + private void ResetInput() { InputText = ""; From 3adda3629e5cdf42b6d8dd5d6a8a5731c28eafb6 Mon Sep 17 00:00:00 2001 From: Saas Date: Wed, 4 Feb 2026 23:29:41 +0100 Subject: [PATCH 33/68] don't try to auto connect to comments and particle emitter nodes --- Source/Editor/Surface/VisjectSurface.Input.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index f7d90512e..74eaa3e04 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using static FlaxEditor.Surface.Archetypes.Particles; using FlaxEditor.Options; using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Undo; @@ -253,7 +254,7 @@ namespace FlaxEditor.Surface // Cache mouse location _mousePos = location; - if (_isLazyConnecting && GetControlUnderMouse() is SurfaceNode nodeUnderMouse) + if (_isLazyConnecting && GetControlUnderMouse() is SurfaceNode nodeUnderMouse && !(nodeUnderMouse is SurfaceComment || nodeUnderMouse is ParticleEmitterNode)) _lazyConnectEndNode = nodeUnderMouse; else if (_isLazyConnecting && Nodes.Count > 0) _lazyConnectEndNode = GetClosestNodeAtLocation(location); @@ -558,7 +559,7 @@ namespace FlaxEditor.Surface var cLocation = _rootControl.PointFromParent(ref location); if (controlUnderMouse != null) { - if (controlUnderMouse is SurfaceNode node && _isLazyConnecting) + if (controlUnderMouse is SurfaceNode node && _isLazyConnecting && !(controlUnderMouse is SurfaceComment || controlUnderMouse is ParticleEmitterNode)) _lazyConnectStartNode = node; // Check if mouse is over header and user is pressing mouse left button @@ -975,6 +976,9 @@ namespace FlaxEditor.Surface foreach (var node in Nodes) { + if (node is SurfaceComment || node is ParticleEmitterNode) + continue; + Float2 nodeSurfaceLocation = _rootControl.PointToParent(node.Center); float distanceSquared = Float2.DistanceSquared(location, nodeSurfaceLocation); From 37e776e407dbbe4e05052803bbc5c493a78127ff Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 6 Feb 2026 21:56:25 -0600 Subject: [PATCH 34/68] Add HideInEditor attribute to DirectionGizmo --- Source/Editor/Gizmo/DirectionGizmo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index c9089e5fc..176832c5e 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -6,6 +6,7 @@ using FlaxEngine.GUI; namespace FlaxEditor.Gizmo; +[HideInEditor] public class DirectionGizmo : ContainerControl { private IGizmoOwner _owner; From b19611e3d2626cda2de1e05836d8062b8a28cd1e Mon Sep 17 00:00:00 2001 From: Saas Date: Sat, 7 Feb 2026 19:58:26 +0100 Subject: [PATCH 35/68] steal right fix from Tryibions PR --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index f13cb7117..e1a61fd5f 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -585,7 +585,7 @@ namespace FlaxEditor.Surface.ContextMenu private void UpdateFilters() { - if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes.Count > 0 && _selectedBoxes[0] == null) + if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes.Count == 0) { ResetView(); Profiler.EndEvent(); From 2a21141dd48aa219d3f32949ccb857dd71b6c785 Mon Sep 17 00:00:00 2001 From: Saas Date: Mon, 9 Feb 2026 15:56:37 +0100 Subject: [PATCH 36/68] focus whole graph if no selection --- Source/Editor/Surface/VisjectSurface.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index bb082aefe..a11f6710a 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -423,9 +423,9 @@ namespace FlaxEditor.Surface new InputActionsContainer.Binding(options => options.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); }), new InputActionsContainer.Binding(options => options.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); }), new InputActionsContainer.Binding(options => options.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); }), - new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }), - new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }), - new InputActionsContainer.Binding(options => options.FocusSelectedNodes, () => { ShowSelection(); }), + new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }), + new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }), + new InputActionsContainer.Binding(options => options.FocusSelectedNodes, () => { FocusSelectionOrWholeGraph(); }), }); Context.ControlSpawned += OnSurfaceControlSpawned; @@ -652,6 +652,17 @@ namespace FlaxEditor.Surface ViewCenterPosition = areaRect.Center; } + /// + /// Adjusts the view to focus on the currently selected nodes, or the entire graph if no nodes are selected. + /// + public void FocusSelectionOrWholeGraph() + { + if (SelectedNodes.Count > 0) + ShowSelection(); + else + ShowWholeGraph(); + } + /// /// Shows the selected controls by changing the view scale and the position. /// From 64c2d64d84575e62bf1b0a70093d45c90a5acb96 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 5 Mar 2026 20:14:48 +0100 Subject: [PATCH 37/68] increase distante for slot into connection feature --- Source/Editor/Surface/Elements/OutputBox.cs | 12 ++++------- Source/Editor/Surface/VisjectSurface.Input.cs | 20 ++++++++++++++----- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 7e271fef0..8836dc0dc 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -34,11 +34,6 @@ namespace FlaxEditor.Surface.Elements /// public const float DefaultConnectionOffset = 24f; - /// - /// Distance for the mouse to be considered above the connection - /// - public float MouseOverConnectionDistance => 100f / Surface.ViewScale; - /// public OutputBox(SurfaceNode parentNode, NodeElementArchetype archetype) : base(parentNode, archetype, archetype.Position + new Float2(parentNode.Archetype.Size.X, 0)) @@ -109,12 +104,13 @@ namespace FlaxEditor.Surface.Elements /// /// The other box. /// The mouse position - public bool IntersectsConnection(Box targetBox, ref Float2 mousePosition) + /// Distance at which its an intersection + public bool IntersectsConnection(Box targetBox, ref Float2 mousePosition, float distance) { float connectionOffset = Mathf.Max(0f, DefaultConnectionOffset * (1 - Editor.Instance.Options.Options.Interface.ConnectionCurvature)); Float2 start = new Float2(ConnectionOrigin.X + connectionOffset, ConnectionOrigin.Y); Float2 end = new Float2(targetBox.ConnectionOrigin.X - connectionOffset, targetBox.ConnectionOrigin.Y); - return IntersectsConnection(ref start, ref end, ref mousePosition, MouseOverConnectionDistance); + return IntersectsConnection(ref start, ref end, ref mousePosition, distance); } /// @@ -182,7 +178,7 @@ namespace FlaxEditor.Surface.Elements { // Draw all the connections var style = Surface.Style; - var mouseOverDistance = MouseOverConnectionDistance; + var mouseOverDistance = Surface.MouseOverConnectionDistance; var startPos = ConnectionOrigin; var startHighlight = ConnectionsHighlightIntensity; for (int i = 0; i < Connections.Count; i++) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 74eaa3e04..01c78ccb1 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -24,6 +24,16 @@ namespace FlaxEditor.Surface /// public bool PanWithMiddleMouse = false; + /// + /// Distance for the mouse to be considered above the connection. + /// + public float MouseOverConnectionDistance => 100f / ViewScale; + + /// + /// Distance of a node from which it is able to be slotted into an existing connection. + /// + public float SlotNodeIntoConnectionDistance => 250f / ViewScale; + private string _currentInputText = string.Empty; private Float2 _movingNodesDelta; private Float2 _gridRoundingDelta; @@ -456,7 +466,7 @@ namespace FlaxEditor.Surface if (!handled && CanEdit && CanUseNodeType(7, 29)) { var mousePos = _rootControl.PointFromParent(ref _mousePos); - if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox) && GetControlUnderMouse() == null) + if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox, MouseOverConnectionDistance) && GetControlUnderMouse() == null) { if (Undo != null) { @@ -653,7 +663,7 @@ namespace FlaxEditor.Surface var mousePos = _rootControl.PointFromParent(ref _mousePos); InputBox intersectedConnectionInputBox; OutputBox intersectedConnectionOutputBox; - if (IntersectsConnection(mousePos, out intersectedConnectionInputBox, out intersectedConnectionOutputBox)) + if (IntersectsConnection(mousePos, out intersectedConnectionInputBox, out intersectedConnectionOutputBox, SlotNodeIntoConnectionDistance)) { SurfaceNode node = _movingNodes.First(); InputBox nodeInputBox = (InputBox)node.GetBoxes().First(b => !b.IsOutput); @@ -783,7 +793,7 @@ namespace FlaxEditor.Surface { // Surface was not moved with MMB so try to remove connection underneath var mousePos = _rootControl.PointFromParent(ref location); - if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox)) + if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox, MouseOverConnectionDistance)) { var action = new EditNodeConnections(inputBox.ParentNode.Context, inputBox.ParentNode); inputBox.BreakConnection(outputBox); @@ -1180,7 +1190,7 @@ namespace FlaxEditor.Surface return new Float2(xLocation, yLocation); } - private bool IntersectsConnection(Float2 mousePosition, out InputBox inputBox, out OutputBox outputBox) + private bool IntersectsConnection(Float2 mousePosition, out InputBox inputBox, out OutputBox outputBox, float distance) { for (int i = 0; i < Nodes.Count; i++) { @@ -1190,7 +1200,7 @@ namespace FlaxEditor.Surface { for (int k = 0; k < ob.Connections.Count; k++) { - if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition)) + if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition, distance)) { outputBox = ob; inputBox = ob.Connections[k] as InputBox; From 9d478b570ff95bf9bd57bfeefb2bf6dd3e04807a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 09:11:26 +0100 Subject: [PATCH 38/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 6 ++++++ .github/workflows/build_windows.yml | 12 ++++++++++++ .github/workflows/cd.yml | 12 ++++++++++++ .github/workflows/tests.yml | 6 ++++++ Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs | 2 +- global.json | 2 +- 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 772e3f67c..c5da53a0b 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -31,6 +31,12 @@ jobs: run: | git lfs version git lfs pull + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'global.json' + search-text: 'latestMajor' + replacement-text: 'latestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 85f4e0c79..6284a0263 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -28,6 +28,12 @@ jobs: run: | git lfs version git lfs pull + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'global.json' + search-text: 'latestMajor' + replacement-text: 'latestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor @@ -53,6 +59,12 @@ jobs: run: | git lfs version git lfs pull + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'global.json' + search-text: 'latestMajor' + replacement-text: 'latestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 9b7c5c965..a6995b8c8 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -32,6 +32,12 @@ jobs: run: | dotnet --info dotnet workload --info + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'global.json' + search-text: 'latestMajor' + replacement-text: 'latestMinor' - name: Build run: | .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 @@ -65,6 +71,12 @@ jobs: run: | dotnet --info dotnet workload --info + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'global.json' + search-text: 'latestMajor' + replacement-text: 'latestMinor' - name: Build run: | .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fdc8e80c9..40d1da844 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -68,6 +68,12 @@ jobs: run: | git lfs version git lfs pull + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'global.json' + search-text: 'latestMajor' + replacement-text: 'latestMinor' - name: Build run: | .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs index 98a9ef530..d45d0dd4c 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs @@ -201,7 +201,7 @@ namespace Flax.Deploy Log.Info("Building disk image..."); if (File.Exists(dmgPath)) File.Delete(dmgPath); - Utilities.Run("hdiutil", $"create -srcFolder \"{appPath}\" -o \"{dmgPath}\"", null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("hdiutil", $"create -srcFolder \"{appPath}\" -o \"{dmgPath}\" -force", null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError); CodeSign(dmgPath); Log.Info("Output disk image size: " + Utilities.GetFileSize(dmgPath)); diff --git a/global.json b/global.json index 9498eeef6..7da276347 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.0", + "version": "8.0.100", "rollForward": "latestMajor" } } \ No newline at end of file From 22ad2d1fab623134258efb27ba55d5c587da4585 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 09:34:50 +0100 Subject: [PATCH 39/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 8 +------- .github/workflows/build_windows.yml | 16 ++-------------- .github/workflows/cd.yml | 16 ++-------------- .github/workflows/tests.yml | 8 +------- global.json | 2 +- 5 files changed, 7 insertions(+), 43 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index c5da53a0b..5da3767fd 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Setup .NET Workload run: | dotnet workload install android @@ -31,12 +31,6 @@ jobs: run: | git lfs version git lfs pull - - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 - with: - files: 'global.json' - search-text: 'latestMajor' - replacement-text: 'latestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 6284a0263..cebe141fb 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -28,12 +28,6 @@ jobs: run: | git lfs version git lfs pull - - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 - with: - files: 'global.json' - search-text: 'latestMajor' - replacement-text: 'latestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor @@ -50,7 +44,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -59,12 +53,6 @@ jobs: run: | git lfs version git lfs pull - - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 - with: - files: 'global.json' - search-text: 'latestMajor' - replacement-text: 'latestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a6995b8c8..e515c5df3 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -27,17 +27,11 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info dotnet workload --info - - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 - with: - files: 'global.json' - search-text: 'latestMajor' - replacement-text: 'latestMinor' - name: Build run: | .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 @@ -66,17 +60,11 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info dotnet workload --info - - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 - with: - files: 'global.json' - search-text: 'latestMajor' - replacement-text: 'latestMinor' - name: Build run: | .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 40d1da844..1f2a25b11 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -59,7 +59,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -68,12 +68,6 @@ jobs: run: | git lfs version git lfs pull - - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 - with: - files: 'global.json' - search-text: 'latestMajor' - replacement-text: 'latestMinor' - name: Build run: | .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 diff --git a/global.json b/global.json index 7da276347..92278e8de 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "8.0.419", "rollForward": "latestMajor" } } \ No newline at end of file From 64005c2774c9ca5253dd02244800d7d3e80fefab Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 09:49:38 +0100 Subject: [PATCH 40/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 8 +++++++- .github/workflows/build_windows.yml | 16 ++++++++++++++-- .github/workflows/cd.yml | 16 ++++++++++++++-- .github/workflows/tests.yml | 8 +++++++- global.json | 2 +- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 5da3767fd..a09af0fa7 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.419 + dotnet-version: 8.0.x - name: Setup .NET Workload run: | dotnet workload install android @@ -31,6 +31,12 @@ jobs: run: | git lfs version git lfs pull + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + search-text: 'LatestMajor' + replacement-text: 'LatestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index cebe141fb..3f8a8a989 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.419 + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -28,6 +28,12 @@ jobs: run: | git lfs version git lfs pull + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + search-text: 'LatestMajor' + replacement-text: 'LatestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor @@ -44,7 +50,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.419 + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -53,6 +59,12 @@ jobs: run: | git lfs version git lfs pull + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + search-text: 'LatestMajor' + replacement-text: 'LatestMinor' - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index e515c5df3..e5038feb9 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -27,11 +27,17 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.419 + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info dotnet workload --info + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + search-text: 'LatestMajor' + replacement-text: 'LatestMinor' - name: Build run: | .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 @@ -60,11 +66,17 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.419 + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info dotnet workload --info + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + search-text: 'LatestMajor' + replacement-text: 'LatestMinor' - name: Build run: | .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1f2a25b11..08f96fdf4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -59,7 +59,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.419 + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -68,6 +68,12 @@ jobs: run: | git lfs version git lfs pull + - name: Configure .NET version + uses: richardrigutins/replace-in-files@v2 + with: + files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + search-text: 'LatestMajor' + replacement-text: 'LatestMinor' - name: Build run: | .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 diff --git a/global.json b/global.json index 92278e8de..7da276347 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.419", + "version": "8.0.100", "rollForward": "latestMajor" } } \ No newline at end of file From 1e1f7ce56de5b5eade59855cf3407bfd947e4232 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 12:16:41 +0100 Subject: [PATCH 41/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 2 +- .github/workflows/build_windows.yml | 4 ++-- .github/workflows/cd.yml | 4 ++-- .github/workflows/tests.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index a09af0fa7..0ef942ed0 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -34,7 +34,7 @@ jobs: - name: Configure .NET version uses: richardrigutins/replace-in-files@v2 with: - files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + files: '*.csproj' search-text: 'LatestMajor' replacement-text: 'LatestMinor' - name: Build diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 3f8a8a989..aba96d3d6 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -31,7 +31,7 @@ jobs: - name: Configure .NET version uses: richardrigutins/replace-in-files@v2 with: - files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + files: '*.csproj' search-text: 'LatestMajor' replacement-text: 'LatestMinor' - name: Build @@ -62,7 +62,7 @@ jobs: - name: Configure .NET version uses: richardrigutins/replace-in-files@v2 with: - files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + files: '*.csproj' search-text: 'LatestMajor' replacement-text: 'LatestMinor' - name: Build diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index e5038feb9..d20b6b92c 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -35,7 +35,7 @@ jobs: - name: Configure .NET version uses: richardrigutins/replace-in-files@v2 with: - files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + files: '*.csproj' search-text: 'LatestMajor' replacement-text: 'LatestMinor' - name: Build @@ -74,7 +74,7 @@ jobs: - name: Configure .NET version uses: richardrigutins/replace-in-files@v2 with: - files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + files: '*.csproj' search-text: 'LatestMajor' replacement-text: 'LatestMinor' - name: Build diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 08f96fdf4..00e2d2a56 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -71,7 +71,7 @@ jobs: - name: Configure .NET version uses: richardrigutins/replace-in-files@v2 with: - files: 'Source\Tools\Flax.Build\Flax.Build.csproj' + files: '*.csproj' search-text: 'LatestMajor' replacement-text: 'LatestMinor' - name: Build From 9850761b5cc314242cdf58b1239b7342cd460efb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 12:27:50 +0100 Subject: [PATCH 42/68] Fix crash when cloth sim is still active --- Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 64fe6fd65..02e23d890 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -1180,6 +1180,8 @@ void ScenePhysX::PreSimulateCloth(int32 i) PROFILE_MEM(PhysicsCloth); auto clothPhysX = ClothsList[i]; auto& clothSettings = Cloths[clothPhysX]; + if (!clothSettings.Actor) + return; if (clothSettings.Actor->OnPreUpdate()) { From 7c5b1942e63586c1d2102bb14104ceb6a3b330c1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 12:44:42 +0100 Subject: [PATCH 43/68] Fix `MaterialBase::GetParameterValue` when parameter is not overridden #3965 --- Source/Engine/Content/Assets/MaterialBase.cpp | 4 ++-- Source/Engine/Content/Assets/MaterialBase.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Content/Assets/MaterialBase.cpp b/Source/Engine/Content/Assets/MaterialBase.cpp index 6ef5f9429..1613ef59a 100644 --- a/Source/Engine/Content/Assets/MaterialBase.cpp +++ b/Source/Engine/Content/Assets/MaterialBase.cpp @@ -19,10 +19,10 @@ Variant MaterialBase::GetParameterValue(const StringView& name) if (!IsLoaded() && WaitForLoaded()) return Variant::Null; const auto param = Params.Get(name); + if (IsMaterialInstance() && param && !param->IsOverride() && ((MaterialInstance*)this)->GetBaseMaterial()) + return ((MaterialInstance*)this)->GetBaseMaterial()->GetParameterValue(name); if (param) - { return param->GetValue(); - } LOG(Warning, "Missing material parameter '{0}' in material {1}", String(name), ToString()); return Variant::Null; } diff --git a/Source/Engine/Content/Assets/MaterialBase.h b/Source/Engine/Content/Assets/MaterialBase.h index f0c32d66a..78f244cb7 100644 --- a/Source/Engine/Content/Assets/MaterialBase.h +++ b/Source/Engine/Content/Assets/MaterialBase.h @@ -57,6 +57,8 @@ public: /// /// Gets the material parameter value. /// + /// For material instances that inherit a base material, returned value might come from base material if the current one doesn't override it. + /// The parameter name. /// The parameter value. API_FUNCTION() Variant GetParameterValue(const StringView& name); From f3e23b6420819528c33e1432470a70df34f7b219 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 12:45:06 +0100 Subject: [PATCH 44/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 9 +++++---- .github/workflows/build_windows.yml | 17 +++++++++-------- .github/workflows/cd.yml | 17 +++++++++-------- .github/workflows/tests.yml | 9 +++++---- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 0ef942ed0..a75dc91bd 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -4,6 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + DOTNET_ROLL_FORWARD: 'LatestMinor' jobs: @@ -32,11 +33,11 @@ jobs: git lfs version git lfs pull - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 + uses: jacobtomlinson/gha-find-replace@3.0.5 with: - files: '*.csproj' - search-text: 'LatestMajor' - replacement-text: 'LatestMinor' + find: "LatestMajor" + replace: "LatestMinor" + include: "**/*.csproj" - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index aba96d3d6..3c378a3e3 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -4,6 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + DOTNET_ROLL_FORWARD: 'LatestMinor' jobs: @@ -29,11 +30,11 @@ jobs: git lfs version git lfs pull - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 + uses: jacobtomlinson/gha-find-replace@3.0.5 with: - files: '*.csproj' - search-text: 'LatestMajor' - replacement-text: 'LatestMinor' + find: "LatestMajor" + replace: "LatestMinor" + include: "**/*.csproj" - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor @@ -60,11 +61,11 @@ jobs: git lfs version git lfs pull - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 + uses: jacobtomlinson/gha-find-replace@3.0.5 with: - files: '*.csproj' - search-text: 'LatestMajor' - replacement-text: 'LatestMinor' + find: "LatestMajor" + replace: "LatestMinor" + include: "**/*.csproj" - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index d20b6b92c..fc57d7737 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -7,6 +7,7 @@ on: env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + DOTNET_ROLL_FORWARD: 'LatestMinor' GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60' jobs: @@ -33,11 +34,11 @@ jobs: dotnet --info dotnet workload --info - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 + uses: jacobtomlinson/gha-find-replace@3.0.5 with: - files: '*.csproj' - search-text: 'LatestMajor' - replacement-text: 'LatestMinor' + find: "LatestMajor" + replace: "LatestMinor" + include: "**/*.csproj" - name: Build run: | .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 @@ -72,11 +73,11 @@ jobs: dotnet --info dotnet workload --info - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 + uses: jacobtomlinson/gha-find-replace@3.0.5 with: - files: '*.csproj' - search-text: 'LatestMajor' - replacement-text: 'LatestMinor' + find: "LatestMajor" + replace: "LatestMinor" + include: "**/*.csproj" - name: Build run: | .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 00e2d2a56..d3deba83d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,6 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + DOTNET_ROLL_FORWARD: 'LatestMinor' jobs: @@ -69,11 +70,11 @@ jobs: git lfs version git lfs pull - name: Configure .NET version - uses: richardrigutins/replace-in-files@v2 + uses: jacobtomlinson/gha-find-replace@3.0.5 with: - files: '*.csproj' - search-text: 'LatestMajor' - replacement-text: 'LatestMinor' + find: "LatestMajor" + replace: "LatestMinor" + include: "**/*.csproj" - name: Build run: | .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 From aeecadffb073380a4530767e054c2cb507f13dfc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 13:32:21 +0100 Subject: [PATCH 45/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 8 ++++---- .github/workflows/build_windows.yml | 16 ++++++++-------- .github/workflows/cd.yml | 16 ++++++++-------- .github/workflows/tests.yml | 8 ++++---- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index a75dc91bd..a0b78225b 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -33,11 +33,11 @@ jobs: git lfs version git lfs pull - name: Configure .NET version - uses: jacobtomlinson/gha-find-replace@3.0.5 + uses: MathieuSoysal/replace-string-in-file@v1.1.0 with: - find: "LatestMajor" - replace: "LatestMinor" - include: "**/*.csproj" + old-string: "LatestMajor" + new-string: "LatestMinor" + file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 3c378a3e3..f66c7d2ec 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -30,11 +30,11 @@ jobs: git lfs version git lfs pull - name: Configure .NET version - uses: jacobtomlinson/gha-find-replace@3.0.5 + uses: MathieuSoysal/replace-string-in-file@v1.1.0 with: - find: "LatestMajor" - replace: "LatestMinor" - include: "**/*.csproj" + old-string: "LatestMajor" + new-string: "LatestMinor" + file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor @@ -61,11 +61,11 @@ jobs: git lfs version git lfs pull - name: Configure .NET version - uses: jacobtomlinson/gha-find-replace@3.0.5 + uses: MathieuSoysal/replace-string-in-file@v1.1.0 with: - find: "LatestMajor" - replace: "LatestMinor" - include: "**/*.csproj" + old-string: "LatestMajor" + new-string: "LatestMinor" + file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index fc57d7737..dd1d73c5f 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -34,11 +34,11 @@ jobs: dotnet --info dotnet workload --info - name: Configure .NET version - uses: jacobtomlinson/gha-find-replace@3.0.5 + uses: MathieuSoysal/replace-string-in-file@v1.1.0 with: - find: "LatestMajor" - replace: "LatestMinor" - include: "**/*.csproj" + old-string: "LatestMajor" + new-string: "LatestMinor" + file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 @@ -73,11 +73,11 @@ jobs: dotnet --info dotnet workload --info - name: Configure .NET version - uses: jacobtomlinson/gha-find-replace@3.0.5 + uses: MathieuSoysal/replace-string-in-file@v1.1.0 with: - find: "LatestMajor" - replace: "LatestMinor" - include: "**/*.csproj" + old-string: "LatestMajor" + new-string: "LatestMinor" + file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d3deba83d..62e306b4f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -70,11 +70,11 @@ jobs: git lfs version git lfs pull - name: Configure .NET version - uses: jacobtomlinson/gha-find-replace@3.0.5 + uses: MathieuSoysal/replace-string-in-file@v1.1.0 with: - find: "LatestMajor" - replace: "LatestMinor" - include: "**/*.csproj" + old-string: "LatestMajor" + new-string: "LatestMinor" + file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 From d06be3d1d621b071a945ad587d1d5b24165497b1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 13:45:21 +0100 Subject: [PATCH 46/68] Fix crash on invalid prefab data size #3979 --- Source/Engine/Level/SceneObjectsFactory.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Engine/Level/SceneObjectsFactory.cpp b/Source/Engine/Level/SceneObjectsFactory.cpp index 576b95b46..59d23a994 100644 --- a/Source/Engine/Level/SceneObjectsFactory.cpp +++ b/Source/Engine/Level/SceneObjectsFactory.cpp @@ -448,8 +448,7 @@ void SceneObjectsFactory::PrefabSyncData::InitNewObjects() void SceneObjectsFactory::SetupPrefabInstances(Context& context, const PrefabSyncData& data) { PROFILE_CPU_NAMED("SetupPrefabInstances"); - const int32 count = data.Data.Size(); - ASSERT(count <= data.SceneObjects.Count()); + const int32 count = Math::Min(data.Data.Size(), data.SceneObjects.Count()); Dictionary parentIdsLookup; for (int32 i = 0; i < count; i++) { From ecc94069682bcb1141146247f47cce27a8639ad2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 13:54:14 +0100 Subject: [PATCH 47/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 8 ++------ .github/workflows/build_windows.yml | 16 ++++------------ .github/workflows/cd.yml | 16 ++++------------ .github/workflows/tests.yml | 8 ++------ 4 files changed, 12 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index a0b78225b..931abd8a7 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -32,12 +32,8 @@ jobs: run: | git lfs version git lfs pull - - name: Configure .NET version - uses: MathieuSoysal/replace-string-in-file@v1.1.0 - with: - old-string: "LatestMajor" - new-string: "LatestMinor" - file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index f66c7d2ec..c64867f42 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -29,14 +29,10 @@ jobs: run: | git lfs version git lfs pull - - name: Configure .NET version - uses: MathieuSoysal/replace-string-in-file@v1.1.0 - with: - old-string: "LatestMajor" - new-string: "LatestMinor" - file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor # Game @@ -60,12 +56,8 @@ jobs: run: | git lfs version git lfs pull - - name: Configure .NET version - uses: MathieuSoysal/replace-string-in-file@v1.1.0 - with: - old-string: "LatestMajor" - new-string: "LatestMinor" - file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index dd1d73c5f..fd7da6d1b 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -33,14 +33,10 @@ jobs: run: | dotnet --info dotnet workload --info - - name: Configure .NET version - uses: MathieuSoysal/replace-string-in-file@v1.1.0 - with: - old-string: "LatestMajor" - new-string: "LatestMinor" - file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 @@ -72,14 +68,10 @@ jobs: run: | dotnet --info dotnet workload --info - - name: Configure .NET version - uses: MathieuSoysal/replace-string-in-file@v1.1.0 - with: - old-string: "LatestMajor" - new-string: "LatestMinor" - file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 62e306b4f..7ca4e25a5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -69,14 +69,10 @@ jobs: run: | git lfs version git lfs pull - - name: Configure .NET version - uses: MathieuSoysal/replace-string-in-file@v1.1.0 - with: - old-string: "LatestMajor" - new-string: "LatestMinor" - file: Source/Tools/Flax.Build/Flax.Build.csproj - name: Build run: | + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 .\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo From 0c1da8e13be727b5bac4774dcdd47f6061ea2fde Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 15:02:58 +0100 Subject: [PATCH 48/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 9 +++++---- .github/workflows/build_windows.yml | 16 +++++++++------- .github/workflows/cd.yml | 16 +++++++++------- .github/workflows/tests.yml | 9 +++++---- Source/Tools/Flax.Build/global.json | 4 ++-- global.json | 4 ++-- 6 files changed, 32 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 931abd8a7..a899bb933 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'LatestMinor' + DOTNET_ROLL_FORWARD: 'disable' jobs: @@ -20,7 +20,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Setup .NET Workload run: | dotnet workload install android @@ -34,6 +34,7 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index c64867f42..ab4f60704 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'LatestMinor' + DOTNET_ROLL_FORWARD: 'disable' jobs: @@ -20,7 +20,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -31,8 +31,9 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor # Game @@ -47,7 +48,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -58,6 +59,7 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index fd7da6d1b..ba5a8aff6 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -7,7 +7,7 @@ on: env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'LatestMinor' + DOTNET_ROLL_FORWARD: 'disable' GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60' jobs: @@ -28,15 +28,16 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info dotnet workload --info - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 @@ -63,15 +64,16 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info dotnet workload --info - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7ca4e25a5..8e62c5551 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'LatestMinor' + DOTNET_ROLL_FORWARD: 'disable' jobs: @@ -60,7 +60,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.419 - name: Print .NET info run: | dotnet --info @@ -71,8 +71,9 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestMinor') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestMinor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 .\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo diff --git a/Source/Tools/Flax.Build/global.json b/Source/Tools/Flax.Build/global.json index 9498eeef6..51ccb9126 100644 --- a/Source/Tools/Flax.Build/global.json +++ b/Source/Tools/Flax.Build/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.0", + "version": "8.0.419", "rollForward": "latestMajor" } -} \ No newline at end of file +} diff --git a/global.json b/global.json index 7da276347..51ccb9126 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "8.0.419", "rollForward": "latestMajor" } -} \ No newline at end of file +} From 508ccb714cde9250273082a8cf1ae4f32f7cb55a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 15:18:02 +0100 Subject: [PATCH 49/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 8 ++++---- .github/workflows/build_windows.yml | 14 +++++++------- .github/workflows/cd.yml | 14 +++++++------- .github/workflows/tests.yml | 8 ++++---- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index a899bb933..a0fa73d8b 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'disable' + DOTNET_ROLL_FORWARD: 'latestFeature' jobs: @@ -34,7 +34,7 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index ab4f60704..6baec30ef 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'disable' + DOTNET_ROLL_FORWARD: 'latestFeature' jobs: @@ -31,9 +31,9 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor # Game @@ -59,7 +59,7 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ba5a8aff6..4e83d04e2 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -7,7 +7,7 @@ on: env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'disable' + DOTNET_ROLL_FORWARD: 'latestFeature' GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60' jobs: @@ -35,9 +35,9 @@ jobs: dotnet workload --info - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 @@ -71,9 +71,9 @@ jobs: dotnet workload --info - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8e62c5551..f233e51c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'disable' + DOTNET_ROLL_FORWARD: 'latestFeature' jobs: @@ -71,9 +71,9 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'disable') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'disable') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Disable') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 .\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo From 4d9898770a8f475b9e56e6b3fd46b562220e4349 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 15:45:51 +0100 Subject: [PATCH 50/68] Attempt to fix Github Action issues --- .github/workflows/build_android.yml | 8 ++++---- .github/workflows/build_windows.yml | 14 +++++++------- .github/workflows/cd.yml | 14 +++++++------- .github/workflows/tests.yml | 8 ++++---- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index a0fa73d8b..741d446f7 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'latestFeature' + DOTNET_ROLL_FORWARD: 'minor' jobs: @@ -34,7 +34,7 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 6baec30ef..f64e800b3 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'latestFeature' + DOTNET_ROLL_FORWARD: 'minor' jobs: @@ -31,9 +31,9 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor # Game @@ -59,7 +59,7 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 4e83d04e2..4155dca71 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -7,7 +7,7 @@ on: env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'latestFeature' + DOTNET_ROLL_FORWARD: 'minor' GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60' jobs: @@ -35,9 +35,9 @@ jobs: dotnet workload --info - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 @@ -71,9 +71,9 @@ jobs: dotnet workload --info - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output -dotnet=8 - name: Upload uses: actions/upload-artifact@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f233e51c6..5202bbb61 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: [push, pull_request] env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false - DOTNET_ROLL_FORWARD: 'latestFeature' + DOTNET_ROLL_FORWARD: 'minor' jobs: @@ -71,9 +71,9 @@ jobs: git lfs pull - name: Build run: | - PowerShell "(Get-Content global.json).Replace('latestMajor', 'latestFeature') | Set-Content global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'latestFeature') | Set-Content Source/Tools/Flax.Build/global.json" - PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'LatestFeature') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" + PowerShell "(Get-Content global.json).Replace('latestMajor', 'minor') | Set-Content global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/global.json).Replace('latestMajor', 'minor') | Set-Content Source/Tools/Flax.Build/global.json" + PowerShell "(Get-Content Source/Tools/Flax.Build/Flax.Build.csproj).Replace('LatestMajor', 'Minor') | Set-Content Source/Tools/Flax.Build/Flax.Build.csproj" .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 .\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo From 0fe297c3900bb44d8fc5d37f8498094aebbb8cde Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 16:57:38 +0100 Subject: [PATCH 51/68] Minor fixes to #3669 Don't change imported model type, only Prefab can do it conditionally. Add type of `PrefabObject` to extend it in future with Cameras, Lights, etc. --- .../Engine/ContentImporters/ImportModel.cpp | 74 +++++++++++-------- .../Tools/ModelTool/ModelTool.Assimp.cpp | 2 +- .../Tools/ModelTool/ModelTool.OpenFBX.cpp | 2 +- Source/Engine/Tools/ModelTool/ModelTool.cpp | 15 +--- 4 files changed, 48 insertions(+), 45 deletions(-) diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index 5448fe8fb..b2a9434c9 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -83,10 +83,14 @@ bool ImportModel::TryGetImportOptions(const StringView& path, Options& options) struct PrefabObject { + enum + { + Model, + SkinnedModel, + } Type; int32 NodeIndex; String Name; String AssetPath; - bool IsSkinned = false; }; void RepackMeshLightmapUVs(ModelData& data) @@ -282,7 +286,7 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) options.SplitObjects = false; options.ObjectIndex = -1; - // Import all of the objects recursive but use current model data to skip loading file again + // Import all the objects recursive but use current model data to skip loading file again options.Cached = &cached; HashSet objectNames; Function splitImport = [&context, &autoImportOutput, &objectNames](Options& splitOptions, const StringView& objectName, String& outputPath, MeshData* meshData) @@ -329,32 +333,32 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) return AssetsImportingManager::Import(context.InputPath, outputPath, &splitOptions); }; - auto splitOptions = options; LOG(Info, "Splitting imported {0} meshes", meshesByName.Count()); - PrefabObject prefabObject; for (int32 groupIndex = 0; groupIndex < meshesByName.Count(); groupIndex++) { auto& group = meshesByName[groupIndex]; // Cache object options (nested sub-object import removes the meshes) - prefabObject.NodeIndex = group.First()->NodeIndex; - prefabObject.Name = group.First()->Name; + MeshData* firstMesh = group.First(); + prefabObject.NodeIndex = firstMesh->NodeIndex; + prefabObject.Name = firstMesh->Name; - // Defaul value for ModelType - splitOptions.Type = ModelTool::ModelType::Model; - - // Search for Skinned Model - if (group.First()->BlendWeights.HasItems() || group.First()->BlendShapes.HasItems() ) + // Detect model type + if ((firstMesh->BlendIndices.HasItems() && firstMesh->BlendWeights.HasItems()) || firstMesh->BlendShapes.HasItems()) { - LOG(Info, "Mesh {0} is Skinned", prefabObject.Name); splitOptions.Type = ModelTool::ModelType::SkinnedModel; - prefabObject.IsSkinned = true; + prefabObject.Type = PrefabObject::SkinnedModel; + } + else + { + splitOptions.Type = ModelTool::ModelType::Model; + prefabObject.Type = PrefabObject::Model; } splitOptions.ObjectIndex = groupIndex; - if (!splitImport(splitOptions, group.GetKey(), prefabObject.AssetPath, group.First())) + if (!splitImport(splitOptions, group.GetKey(), prefabObject.AssetPath, firstMesh)) { prefabObjects.Add(prefabObject); } @@ -748,26 +752,32 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M nodeActors.Clear(); for (const PrefabObject& e : prefabObjects) { - if (e.NodeIndex == nodeIndex) + if (e.NodeIndex != nodeIndex) + continue; + Actor* a = nullptr; + switch (e.Type) { - if(e.IsSkinned) - { - LOG(Info,"Creating animated model prefab {0}.", e.Name); - auto* actor = New(); - actor->SetName(e.Name); - if (auto* skinnedModel = Content::LoadAsync(e.AssetPath)) - actor->SkinnedModel = skinnedModel; - nodeActors.Add(actor); - } - else - { - auto* actor = New(); - actor->SetName(e.Name); - if (auto* model = Content::LoadAsync(e.AssetPath)) - actor->Model = model; - nodeActors.Add(actor); - } + case PrefabObject::Model: + { + auto* actor = New(); + if (auto* model = Content::LoadAsync(e.AssetPath)) + actor->Model = model; + a = actor; + break; } + case PrefabObject::SkinnedModel: + { + auto* actor = New(); + if (auto* skinnedModel = Content::LoadAsync(e.AssetPath)) + actor->SkinnedModel = skinnedModel; + a = actor; + break; + } + default: + continue; + } + a->SetName(e.Name); + nodeActors.Add(a); } Actor* nodeActor = nodeActors.Count() == 1 ? nodeActors[0] : New(); if (nodeActors.Count() > 1) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index b93220de4..b1340bfa7 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -786,7 +786,7 @@ bool ModelTool::ImportDataAssimp(const String& path, ModelData& data, Options& o } // Import skeleton - if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton) && context.Bones.HasItems()) + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton)) { data.Skeleton.Nodes.Resize(context.Nodes.Count(), false); for (int32 i = 0; i < context.Nodes.Count(); i++) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index e530e7b7c..81ce46d6c 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -1404,7 +1404,7 @@ bool ModelTool::ImportDataOpenFBX(const String& path, ModelData& data, Options& } // Import skeleton - if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton) && context.Bones.HasItems()) + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton)) { data.Skeleton.Nodes.Resize(context.Nodes.Count(), false); for (int32 i = 0; i < context.Nodes.Count(); i++) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index e15c45c5e..48322687c 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1012,12 +1012,6 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option options.ImportTypes |= ImportDataTypes::Textures; break; case ModelType::SkinnedModel: - if (!data.Skeleton.Bones.HasItems()) - { - LOG(Warning, "Model is not Skinned, it will be imported as Static"); - options.ImportTypes = ImportDataTypes::Geometry | ImportDataTypes::Nodes; - options.Type = ModelType::Model; - } options.ImportTypes = ImportDataTypes::Geometry | ImportDataTypes::Nodes | ImportDataTypes::Skeleton; if (options.ImportMaterials) options.ImportTypes |= ImportDataTypes::Materials; @@ -1058,7 +1052,6 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option { if (mesh->BlendShapes.IsEmpty()) continue; - for (int32 blendShapeIndex = mesh->BlendShapes.Count() - 1; blendShapeIndex >= 0; blendShapeIndex--) { auto& blendShape = mesh->BlendShapes[blendShapeIndex]; @@ -1083,8 +1076,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option } } } - if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton) - && (data.Skeleton.Bones.HasItems() || data.LODs[0].Meshes[0]->BlendShapes.HasItems())) + if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Skeleton)) { LOG(Info, "Imported skeleton has {0} bones and {1} nodes", data.Skeleton.Bones.Count(), data.Nodes.Count()); @@ -1224,7 +1216,9 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option for (int32 i = 0; i < meshesCount; i++) { const auto mesh = data.LODs[0].Meshes[i]; - if ((mesh->BlendIndices.IsEmpty() || mesh->BlendWeights.IsEmpty()) && data.Skeleton.Bones.HasItems()) + + // If imported mesh has skeleton but no indices or weights then need to setup those (except in Prefab mode when we conditionally import meshes based on type) + if ((mesh->BlendIndices.IsEmpty() || mesh->BlendWeights.IsEmpty()) && data.Skeleton.Bones.HasItems() && (options.Type != ModelType::Prefab)) { auto indices = Int4::Zero; auto weights = Float4::UnitX; @@ -2101,7 +2095,6 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option { if (mesh->BlendShapes.IsEmpty()) continue; - for (auto& blendShape : mesh->BlendShapes) { // Compute min/max for used vertex indices From 5e6723aa226b112514ac283dcf6e1363f9cf07a3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 17:16:01 +0100 Subject: [PATCH 52/68] Fix missing init and serialization #2977 --- Source/Engine/Tools/ModelTool/ModelTool.cpp | 2 ++ Source/Engine/Tools/ModelTool/ModelTool.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 1c766a70f..e3da92da2 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -593,6 +593,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(SloppyOptimization); SERIALIZE(LODTargetError); SERIALIZE(ImportMaterials); + SERIALIZE(CreateEmptyMaterialSlots); SERIALIZE(ImportMaterialsAsInstances); SERIALIZE(InstanceToImportAs); SERIALIZE(ImportTextures); @@ -648,6 +649,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(SloppyOptimization); DESERIALIZE(LODTargetError); DESERIALIZE(ImportMaterials); + DESERIALIZE(CreateEmptyMaterialSlots); DESERIALIZE(ImportMaterialsAsInstances); DESERIALIZE(InstanceToImportAs); DESERIALIZE(ImportTextures); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index ecc95c601..00d7028cf 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -313,9 +313,9 @@ public: // If checked, the importer will create materials for model meshes as specified in the file. API_FIELD(Attributes="EditorOrder(399), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") bool ImportMaterials = true; - // If checked, the importer will create empty material slots for every material. + // If checked, the importer will create empty material slots for every material without importing materials nor textures. API_FIELD(Attributes="EditorOrder(400), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") - bool CreateEmptyMaterialSlots; + bool CreateEmptyMaterialSlots = false; // If checked, the importer will create the model's materials as instances of a base material. API_FIELD(Attributes="EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials)), VisibleIf(nameof(ShowGeometry)), VisibleIf(nameof(CreateEmptyMaterialSlots), true)") bool ImportMaterialsAsInstances = false; From 2c742bee41ae91056b2cc4ae7095c354564e4119 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 17:22:38 +0100 Subject: [PATCH 53/68] Fix typo from #3914 --- Source/Engine/UI/GUI/Special/RadialMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Special/RadialMenu.cs b/Source/Engine/UI/GUI/Special/RadialMenu.cs index 994d81ab2..d52294da8 100644 --- a/Source/Engine/UI/GUI/Special/RadialMenu.cs +++ b/Source/Engine/UI/GUI/Special/RadialMenu.cs @@ -130,7 +130,7 @@ namespace FlaxEngine.GUI } /// - /// The material instance of used to draw the menu. + /// The material instance of used to draw the menu. /// [HideInEditor] public MaterialInstance MaterialInstance => _materialInstance; From 914e0869b12bc1011a2453c6b04419ad534bd5c8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 12 Feb 2026 18:26:43 +0100 Subject: [PATCH 54/68] Fix warning when building C# lib with newer .NET than base version used on deps --- Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index 54136b490..164cd412f 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -245,6 +245,8 @@ namespace Flax.Build args.Add(string.Format("/nullable:{0}", buildOptions.ScriptingAPI.CSharpNullableReferences.ToString().ToLowerInvariant())); if (buildOptions.ScriptingAPI.CSharpNullableReferences == CSharpNullableReferences.Disable) args.Add("-nowarn:8632"); // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + if (buildOptions.Configuration != TargetConfiguration.Release) + args.Add("-nowarn:1701"); // Assuming assembly reference 'XXX, Version=8.0.0.0' used by 'Y' matches identity 'X, Version=9.0.0.0' of 'X', you may need to supply runtime policy #else args.Add("/langversion:7.3"); #endif From 0ac5e85cd9e55729f818c5026965078d0ba3155b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 17:29:25 +0100 Subject: [PATCH 55/68] Code cleanup #3857 --- Source/Editor/Gizmo/DirectionGizmo.cs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index 176832c5e..ce1ecee79 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using FlaxEditor.Gizmo; +// Copyright (c) Wojciech Figat. All rights reserved. + +using System.Collections.Generic; using FlaxEditor.Viewport; using FlaxEngine; using FlaxEngine.GUI; @@ -7,7 +8,7 @@ using FlaxEngine.GUI; namespace FlaxEditor.Gizmo; [HideInEditor] -public class DirectionGizmo : ContainerControl +internal class DirectionGizmo : ContainerControl { private IGizmoOwner _owner; private ViewportProjection _viewportProjection; @@ -88,11 +89,11 @@ public class DirectionGizmo : ContainerControl _owner = owner; _viewport = owner.Viewport; _viewportProjection.Init(owner.Viewport); - + _xAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = false, Direction = AxisDirection.PosX }; _yAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = false, Direction = AxisDirection.PosY }; _zAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = false, Direction = AxisDirection.PosZ }; - + _negXAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-X", AxisColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f), Negative = true, Direction = AxisDirection.NegX }; _negYAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Y", AxisColor = new Color(0.239215f, 1.0f, 0.047058f, 1.0f), Negative = true, Direction = AxisDirection.NegY }; _negZAxisData = new AxisData { Delta = new Float2(0, 0), Distance = 0, Label = "-Z", AxisColor = new Color(0.0f, 0.0235294f, 1.0f, 1.0f), Negative = true, Direction = AxisDirection.NegZ }; @@ -101,7 +102,7 @@ public class DirectionGizmo : ContainerControl _posHandle = Editor.Instance.Icons.VisjectBoxClosed32; _negHandle = Editor.Instance.Icons.VisjectBoxOpen32; - + _fontReference = new FontReference(Style.Current.FontSmall); _fontReference.Size = 8; } @@ -128,7 +129,7 @@ public class DirectionGizmo : ContainerControl break; } } - + base.OnMouseMove(location); } @@ -147,7 +148,7 @@ public class DirectionGizmo : ContainerControl return true; } } - + return false; } @@ -166,14 +167,14 @@ public class DirectionGizmo : ContainerControl _viewport.ViewOrientation = orientation; } - + /// /// Used to Draw the gizmo. /// public override void DrawSelf() { base.DrawSelf(); - + _viewportProjection.Init(_owner.Viewport); _gizmoCenter = _viewport.Task.View.WorldPosition + _viewport.Task.View.Direction * 1500; _viewportProjection.ProjectPoint(_gizmoCenter, out var gizmoCenterScreen); @@ -223,7 +224,7 @@ public class DirectionGizmo : ContainerControl // Sort for correct draw order. _axisData.Clear(); _axisData.AddRange([_xAxisData, _yAxisData, _zAxisData, _negXAxisData, _negYAxisData, _negZAxisData]); - _axisData.Sort( (a, b) => -a.Distance.CompareTo(b.Distance)); + _axisData.Sort((a, b) => -a.Distance.CompareTo(b.Distance)); // Rebuild sprite positions list for hover detection _spritePositions.Clear(); @@ -245,7 +246,7 @@ public class DirectionGizmo : ContainerControl { Render2D.DrawLine(relativeCenter, tipScreen, axis.AxisColor, 2.0f); Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor); - + var font = _fontReference.GetFont(); Color textColor = isHovered ? Color.White : Color.Black; Render2D.DrawText(font, axis.Label, textColor, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); From 0cbb2f0525e2eb6c0266327380419ef0d557206b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 17:39:03 +0100 Subject: [PATCH 56/68] Add smooth viewport orientation movement to Direction Gizmo #3857 --- Source/Editor/Gizmo/DirectionGizmo.cs | 3 +-- Source/Editor/Viewport/EditorViewport.cs | 4 ++-- Source/Editor/Viewport/MainEditorGizmoViewport.cs | 2 +- Source/Editor/Viewport/PrefabWindowViewport.cs | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index ce1ecee79..eb2e1777a 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -164,8 +164,7 @@ internal class DirectionGizmo : ContainerControl AxisDirection.NegZ => Quaternion.Euler(0, 180, 0), _ => Quaternion.Identity }; - - _viewport.ViewOrientation = orientation; + _viewport.OrientViewport(ref orientation); } /// diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 2182c2f55..5f4c47447 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1223,7 +1223,7 @@ namespace FlaxEditor.Viewport /// Orients the viewport. /// /// The orientation. - protected void OrientViewport(Quaternion orientation) + public void OrientViewport(Quaternion orientation) { OrientViewport(ref orientation); } @@ -1232,7 +1232,7 @@ namespace FlaxEditor.Viewport /// Orients the viewport. /// /// The orientation. - protected virtual void OrientViewport(ref Quaternion orientation) + public virtual void OrientViewport(ref Quaternion orientation) { if (ViewportCamera is FPSCamera fpsCamera) { diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index a76c8a16a..78e4f3eea 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -654,7 +654,7 @@ namespace FlaxEditor.Viewport } /// - protected override void OrientViewport(ref Quaternion orientation) + public override void OrientViewport(ref Quaternion orientation) { if (TransformGizmo.SelectedParents.Count != 0) FocusSelection(ref orientation); diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index a8bdc8f20..a599615de 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -681,7 +681,7 @@ namespace FlaxEditor.Viewport } /// - protected override void OrientViewport(ref Quaternion orientation) + public override void OrientViewport(ref Quaternion orientation) { if (TransformGizmo.SelectedParents.Count != 0) FocusSelection(ref orientation); From 8356009526a81878a18dfe416dbf65a977de4540 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 17:41:29 +0100 Subject: [PATCH 57/68] Add button to hide direction gizmo #3857 --- Source/Editor/Viewport/MainEditorGizmoViewport.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 78e4f3eea..6b0165190 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -115,7 +115,7 @@ namespace FlaxEditor.Viewport private ViewMode _preGameViewViewMode; private bool _gameViewWasGridShown; private bool _gameViewWasFpsCounterShown; - private bool _gameViewWasNagivationShown; + private bool _gameViewWasNavigationShown; /// /// Drag and drop handlers @@ -251,6 +251,12 @@ namespace FlaxEditor.Viewport _showNavigationButton = ViewWidgetShowMenu.AddButton("Navigation", inputOptions.ToggleNavMeshVisibility, () => ShowNavigation = !ShowNavigation); _showNavigationButton.CloseMenuOnClick = false; + // Show direction gizmo widget + var showDirectionGizmoButton = ViewWidgetShowMenu.AddButton("Direction Gizmo", () => _directionGizmo.Visible = !_directionGizmo.Visible); + showDirectionGizmoButton.AutoCheck = true; + showDirectionGizmoButton.CloseMenuOnClick = false; + showDirectionGizmoButton.Checked = _directionGizmo.Visible; + // Game View ViewWidgetButtonMenu.AddSeparator(); _toggleGameViewButton = ViewWidgetButtonMenu.AddButton("Game View", inputOptions.ToggleGameView, ToggleGameView); @@ -521,14 +527,14 @@ namespace FlaxEditor.Viewport _preGameViewViewMode = Task.ViewMode; _gameViewWasGridShown = Grid.Enabled; _gameViewWasFpsCounterShown = ShowFpsCounter; - _gameViewWasNagivationShown = ShowNavigation; + _gameViewWasNavigationShown = ShowNavigation; } // Set flags & values Task.ViewFlags = _gameViewActive ? _preGameViewFlags : ViewFlags.DefaultGame; Task.ViewMode = _gameViewActive ? _preGameViewViewMode : ViewMode.Default; ShowFpsCounter = _gameViewActive ? _gameViewWasFpsCounterShown : false; - ShowNavigation = _gameViewActive ? _gameViewWasNagivationShown : false; + ShowNavigation = _gameViewActive ? _gameViewWasNavigationShown : false; Grid.Enabled = _gameViewActive ? _gameViewWasGridShown : false; _gameViewActive = !_gameViewActive; From 27a1db617c94e3ca459486ec26c2e01d581596c4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 17:47:56 +0100 Subject: [PATCH 58/68] Add hover color highlight to match transform gizmo in direction gizmo Don't snap shapes to pixels to smooth movement during view rotation #3857 --- Source/Editor/Gizmo/DirectionGizmo.cs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index eb2e1777a..a9a60ae5a 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -174,6 +174,8 @@ internal class DirectionGizmo : ContainerControl { base.DrawSelf(); + var features = Render2D.Features; + Render2D.Features = features & ~Render2D.RenderingFeatures.VertexSnapping; _viewportProjection.Init(_owner.Viewport); _gizmoCenter = _viewport.Task.View.WorldPosition + _viewport.Task.View.Direction * 1500; _viewportProjection.ProjectPoint(_gizmoCenter, out var gizmoCenterScreen); @@ -241,26 +243,23 @@ internal class DirectionGizmo : ContainerControl // Store sprite position for hover detection _spritePositions.Add((tipTextScreen, axis.Direction)); + var axisColor = isHovered ? new Color(1.0f, 0.8980392f, 0.039215688f) : axis.AxisColor; + var font = _fontReference.GetFont(); if (!axis.Negative) { - Render2D.DrawLine(relativeCenter, tipScreen, axis.AxisColor, 2.0f); - Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor); - - var font = _fontReference.GetFont(); - Color textColor = isHovered ? Color.White : Color.Black; - Render2D.DrawText(font, axis.Label, textColor, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); + Render2D.DrawLine(relativeCenter, tipScreen, axisColor, 2.0f); + Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axisColor); + Render2D.DrawText(font, axis.Label, isHovered ? Color.Gray : Color.Black, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); } else { - Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor.RGBMultiplied(0.65f)); - Render2D.DrawSprite(_negHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axis.AxisColor); - + Render2D.DrawSprite(_posHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axisColor.RGBMultiplied(0.65f)); + Render2D.DrawSprite(_negHandle, new Rectangle(tipTextScreen - new Float2(_spriteRadius), new Float2(_spriteRadius * 2)), axisColor); if (isHovered) - { - var font = _fontReference.GetFont(); - Render2D.DrawText(font, axis.Label, Color.White, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); - } + Render2D.DrawText(font, axis.Label, Color.Black, tipTextScreen - font.MeasureText(axis.Label) * 0.5f); } } + + Render2D.Features = features; } } From 842b25f80fa33b2e5979f14fb7e6bc6763939693 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 18:11:31 +0100 Subject: [PATCH 59/68] Fix Direction Gizmo axes placement in ortho view #3857 --- Source/Editor/Gizmo/DirectionGizmo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Gizmo/DirectionGizmo.cs b/Source/Editor/Gizmo/DirectionGizmo.cs index a9a60ae5a..babebbd5b 100644 --- a/Source/Editor/Gizmo/DirectionGizmo.cs +++ b/Source/Editor/Gizmo/DirectionGizmo.cs @@ -192,6 +192,8 @@ internal class DirectionGizmo : ContainerControl // Normalize by viewport height to keep size independent of FOV and viewport dimensions float heightNormalization = _viewport.Height / 720.0f; // 720 = reference height + if (_owner.Viewport.UseOrthographicProjection) + heightNormalization /= _owner.Viewport.OrthographicScale * 0.5f; // Fix in ortho view to keep consistent size regardless of zoom level Float2 xDelta = (xProjected - gizmoCenterScreen) / heightNormalization; Float2 yDelta = (yProjected - gizmoCenterScreen) / heightNormalization; From 3f2e6d82c90d3895e6c7601c3365d7cb6e35c0ec Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 18:44:56 +0100 Subject: [PATCH 60/68] Fix regression from 21e2c830e5e85f3e8cb852658fdc7930c74d3208 when reimport Animation #3937 --- Source/Editor/Windows/Assets/AnimationWindow.cs | 3 +++ Source/Editor/Windows/Assets/AssetEditorWindow.cs | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index 8dbcc5921..69da1f9e9 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -431,6 +431,9 @@ namespace FlaxEditor.Windows.Assets _isWaitingForTimelineLoad = true; base.OnItemReimported(item); + + // Drop virtual asset state and get a new one from the reimported file + LoadFromOriginal(); } /// diff --git a/Source/Editor/Windows/Assets/AssetEditorWindow.cs b/Source/Editor/Windows/Assets/AssetEditorWindow.cs index eeac84b60..93d6c850c 100644 --- a/Source/Editor/Windows/Assets/AssetEditorWindow.cs +++ b/Source/Editor/Windows/Assets/AssetEditorWindow.cs @@ -527,6 +527,16 @@ namespace FlaxEditor.Windows.Assets return false; } + /// + /// Loads the asset from the original location to reflect the state (eg. after original asset reimport). + /// + protected virtual void LoadFromOriginal() + { + _asset = LoadAsset(); + OnAssetLoaded(); + ClearEditedFlag(); + } + /// protected override T LoadAsset() { From 9d0e4e9768b91e33bfeb2739038bfb0724f3c855 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 18:51:12 +0100 Subject: [PATCH 61/68] Fix potential stack overflow inside `CustomEditor.RebuildLayout` #3720 --- Source/Editor/CustomEditors/CustomEditor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index ff9a14ea3..253fbc590 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -52,6 +52,7 @@ namespace FlaxEditor.CustomEditors private readonly List _children = new List(); private ValueContainer _values; private bool _isSetBlocked; + private bool _isRebuilding; private bool _skipChildrenRefresh; private bool _hasValueDirty; private bool _rebuildOnRefresh; @@ -178,7 +179,7 @@ namespace FlaxEditor.CustomEditors public void RebuildLayout() { // Skip rebuilding during init - if (CurrentCustomEditor == this) + if (CurrentCustomEditor == this || _isRebuilding) return; // Special case for root objects to run normal layout build @@ -197,6 +198,7 @@ namespace FlaxEditor.CustomEditors _parent?.RebuildLayout(); return; } + _isRebuilding = true; var control = layout.ContainerControl; var parent = _parent; var parentScrollV = (_presenter?.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1; @@ -216,6 +218,7 @@ namespace FlaxEditor.CustomEditors // Restore scroll value if (parentScrollV > -1 && _presenter != null && _presenter.Panel.Parent is Panel panel && panel.VScrollBar != null) panel.VScrollBar.Value = parentScrollV; + _isRebuilding = false; } /// From a84109df2c1ce625e355e179246dcea39c339aca Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 21:30:07 +0100 Subject: [PATCH 62/68] Add `UnusedStorageLifetime` for asset file TTL #3931 --- Source/Engine/Content/Storage/ContentStorageManager.cpp | 1 + Source/Engine/Content/Storage/ContentStorageManager.h | 7 ++++++- Source/Engine/Content/Storage/FlaxStorage.cpp | 8 ++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Content/Storage/ContentStorageManager.cpp b/Source/Engine/Content/Storage/ContentStorageManager.cpp index 80744ae45..8aa36c7be 100644 --- a/Source/Engine/Content/Storage/ContentStorageManager.cpp +++ b/Source/Engine/Content/Storage/ContentStorageManager.cpp @@ -46,6 +46,7 @@ namespace ContentStorageService ContentStorageServiceInstance; +TimeSpan ContentStorageManager::UnusedStorageLifetime = TimeSpan::FromSeconds(0.5f); TimeSpan ContentStorageManager::UnusedDataChunksLifetime = TimeSpan::FromSeconds(10); FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, bool loadIt) diff --git a/Source/Engine/Content/Storage/ContentStorageManager.h b/Source/Engine/Content/Storage/ContentStorageManager.h index 0d56855ea..fac27aa7f 100644 --- a/Source/Engine/Content/Storage/ContentStorageManager.h +++ b/Source/Engine/Content/Storage/ContentStorageManager.h @@ -15,7 +15,12 @@ class FLAXENGINE_API ContentStorageManager { public: /// - /// Auto-release timeout for unused asset chunks. + /// Auto-release timeout for unused asset files. + /// + static TimeSpan UnusedStorageLifetime; + + /// + /// Auto-release timeout for unused asset data chunks. /// static TimeSpan UnusedDataChunksLifetime; diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index 6967bb5ad..e286ee326 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -286,14 +286,14 @@ FlaxStorage::LockData FlaxStorage::LockSafe() uint32 FlaxStorage::GetRefCount() const { - return (uint32)Platform::AtomicRead((int64*)&_refCount); + return (uint32)Platform::AtomicRead(&_refCount); } bool FlaxStorage::ShouldDispose() const { - return Platform::AtomicRead((int64*)&_refCount) == 0 && - Platform::AtomicRead((int64*)&_chunksLock) == 0 && - Platform::GetTimeSeconds() - _lastRefLostTime >= 0.5; // TTL in seconds + return Platform::AtomicRead(&_refCount) == 0 && + Platform::AtomicRead(&_chunksLock) == 0 && + Platform::GetTimeSeconds() - _lastRefLostTime >= ContentStorageManager::UnusedStorageLifetime.GetTotalSeconds(); } uint32 FlaxStorage::GetMemoryUsage() const From c12553812cd74acf177175206980884f426931fc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 22:31:36 +0100 Subject: [PATCH 63/68] Update `Terrain` scripting API --- Source/Editor/Editor.Build.cs | 1 + .../Editor/Tools/Terrain/EditTerrainGizmo.cs | 2 +- Source/Editor/Tools/Terrain/Paint/Mode.cs | 4 +-- Source/Editor/Tools/Terrain/Sculpt/Mode.cs | 4 +-- .../Terrain/Undo/EditTerrainMapAction.cs | 3 +- .../Viewport/Previews/MaterialPreview.cs | 3 +- .../Materials/TerrainMaterialShader.cpp | 5 ++- Source/Engine/Terrain/Terrain.Build.cs | 11 ++++++- Source/Engine/Terrain/Terrain.cpp | 10 ++++++ Source/Engine/Terrain/Terrain.h | 24 ++++++++++---- Source/Engine/Terrain/TerrainPatch.cpp | 29 +++++++++-------- Source/Engine/Terrain/TerrainPatch.h | 32 +++++++++++-------- 12 files changed, 81 insertions(+), 47 deletions(-) diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs index e75954909..3fd8ea98c 100644 --- a/Source/Editor/Editor.Build.cs +++ b/Source/Editor/Editor.Build.cs @@ -61,6 +61,7 @@ public class Editor : EditorModule options.PrivateDependencies.Add("Renderer"); options.PrivateDependencies.Add("TextureTool"); options.PrivateDependencies.Add("Particles"); + options.PrivateDependencies.Add("Terrain"); var platformToolsRoot = Path.Combine(FolderPath, "Cooker", "Platform"); var platformToolsRootExternal = Path.Combine(Globals.EngineRoot, "Source", "Platforms"); diff --git a/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs b/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs index 5fc0e894f..c6926f32e 100644 --- a/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs +++ b/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs @@ -89,7 +89,7 @@ namespace FlaxEditor.Tools.Terrain if (!terrain.HasPatch(ref patchCoord) && _planeModel) { var planeSize = 100.0f; - var patchSize = terrain.ChunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount; + var patchSize = terrain.PatchSize; Matrix world = Matrix.RotationX(-Mathf.PiOverTwo) * Matrix.Scaling(patchSize / planeSize) * Matrix.Translation(patchSize * (0.5f + patchCoord.X), 0, patchSize * (0.5f + patchCoord.Y)) * diff --git a/Source/Editor/Tools/Terrain/Paint/Mode.cs b/Source/Editor/Tools/Terrain/Paint/Mode.cs index cee3157d3..624a74c3f 100644 --- a/Source/Editor/Tools/Terrain/Paint/Mode.cs +++ b/Source/Editor/Tools/Terrain/Paint/Mode.cs @@ -69,9 +69,9 @@ namespace FlaxEditor.Tools.Terrain.Paint var splatmapIndex = ActiveSplatmapIndex; var splatmapIndexOther = (splatmapIndex + 1) % 2; var chunkSize = terrain.ChunkSize; - var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1; + var heightmapSize = terrain.HeightmapSize; var heightmapLength = heightmapSize * heightmapSize; - var patchSize = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount; + var patchSize = terrain.PatchSize; var tempBuffer = (Color32*)gizmo.GetSplatmapTempBuffer(heightmapLength * Color32.SizeInBytes, splatmapIndex).ToPointer(); var tempBufferOther = (Color32*)gizmo.GetSplatmapTempBuffer(heightmapLength * Color32.SizeInBytes, (splatmapIndex + 1) % 2).ToPointer(); var unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex; diff --git a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs index f37902f39..55d11b21a 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs @@ -70,9 +70,9 @@ namespace FlaxEditor.Tools.Terrain.Sculpt // Prepare var chunkSize = terrain.ChunkSize; - var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1; + var heightmapSize = terrain.HeightmapSize; var heightmapLength = heightmapSize * heightmapSize; - var patchSize = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount; + var patchSize = terrain.PatchSize; var tempBuffer = (float*)gizmo.GetHeightmapTempBuffer(heightmapLength * sizeof(float)).ToPointer(); var unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex; diff --git a/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs b/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs index afac0948e..93fc0470e 100644 --- a/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs +++ b/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs @@ -85,8 +85,7 @@ namespace FlaxEditor.Tools.Terrain.Undo { _terrain = terrain.ID; _patches = new List(4); - var chunkSize = terrain.ChunkSize; - var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1; + var heightmapSize = terrain.HeightmapSize; _heightmapLength = heightmapSize * heightmapSize; _heightmapDataSize = _heightmapLength * stride; diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index 8ba8a00ee..e494df14a 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -303,8 +303,7 @@ namespace FlaxEditor.Viewport.Previews { _terrain = new Terrain(); _terrain.Setup(1, 63); - var chunkSize = _terrain.ChunkSize; - var heightMapSize = chunkSize * Terrain.PatchEdgeChunksCount + 1; + var heightMapSize = _terrain.HeightmapSize; var heightMapLength = heightMapSize * heightMapSize; var heightmap = new float[heightMapLength]; var patchCoord = new Int2(0, 0); diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index b6b455f20..30aa8b280 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -88,9 +88,8 @@ void TerrainMaterialShader::Bind(BindParameters& params) } // Bind terrain textures - const auto heightmap = drawCall.Terrain.Patch->Heightmap->GetTexture(); - const auto splatmap0 = drawCall.Terrain.Patch->Splatmap[0] ? drawCall.Terrain.Patch->Splatmap[0]->GetTexture() : nullptr; - const auto splatmap1 = drawCall.Terrain.Patch->Splatmap[1] ? drawCall.Terrain.Patch->Splatmap[1]->GetTexture() : nullptr; + GPUTexture* heightmap, *splatmap0, *splatmap1; + drawCall.Terrain.Patch->GetTextures(heightmap, splatmap0, splatmap1); context->BindSR(0, heightmap); context->BindSR(1, splatmap0); context->BindSR(2, splatmap1); diff --git a/Source/Engine/Terrain/Terrain.Build.cs b/Source/Engine/Terrain/Terrain.Build.cs index 178938da8..55a590dd2 100644 --- a/Source/Engine/Terrain/Terrain.Build.cs +++ b/Source/Engine/Terrain/Terrain.Build.cs @@ -8,13 +8,22 @@ using Flax.Build.NativeCpp; /// public class Terrain : EngineModule { + /// + /// Enables terrain editing and changing at runtime. If your game doesn't use procedural terrain in game then disable this option to reduce build size. + /// + public static bool WithEditing = true; + /// public override void Setup(BuildOptions options) { base.Setup(options); - options.PrivateDependencies.Add("Physics"); + if (!WithEditing) + { + options.PublicDefinitions.Add("TERRAIN_EDITING=0"); + } + options.PrivateDependencies.Add("Physics"); if (options.Target.IsEditor) { options.PrivateDependencies.Add("ContentImporters"); diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index f0ad65894..9185f71a9 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -306,6 +306,16 @@ void Terrain::SetPhysicalMaterials(const Array(_chunkSize); } + /// + /// Gets the heightmap texture size (square) used by a single patch (shared by all chunks within that patch). + /// + /// ChunkSize * ChunksCountEdge + 1 + API_PROPERTY() int32 GetHeightmapSize() const; + + /// + /// Gets the size of the patch in world-units (square) without actor scale. + /// + /// UnitsPerVertex * ChunksCountEdge * ChunkSize + API_PROPERTY() float GetPatchSize() const; + /// /// Gets the terrain patches count. Each patch contains 16 chunks arranged into a 4x4 square. /// @@ -329,7 +344,6 @@ public: API_FUNCTION() void SetChunkOverrideMaterial(API_PARAM(Ref) const Int2& patchCoord, API_PARAM(Ref) const Int2& chunkCoord, MaterialBase* value); #if TERRAIN_EDITING - /// /// Setups the terrain patch using the specified heightmap data. /// @@ -352,10 +366,6 @@ public: /// True if failed, otherwise false. API_FUNCTION() bool SetupPatchSplatMap(API_PARAM(Ref) const Int2& patchCoord, int32 index, int32 splatMapLength, const Color32* splatMap, bool forceUseVirtualStorage = false); -#endif - -public: -#if TERRAIN_EDITING /// /// Setups the terrain. Clears the existing data. /// diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 61bbf7e3c..cadc9821c 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -17,6 +17,7 @@ #include "Engine/Threading/Threading.h" #if TERRAIN_EDITING #include "Engine/Core/Math/Packed.h" +#include "Engine/Core/Collections/ArrayExtensions.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/RenderView.h" @@ -27,11 +28,6 @@ #include "Editor/Editor.h" #include "Engine/ContentImporters/AssetsImportingManager.h" #endif -#endif -#if TERRAIN_EDITING || TERRAIN_UPDATING -#include "Engine/Core/Collections/ArrayExtensions.h" -#endif -#if USE_EDITOR #include "Engine/Debug/DebugDraw.h" #endif #if TERRAIN_USE_PHYSICS_DEBUG @@ -90,7 +86,7 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z) Splatmap[i] = nullptr; } _heightfield = nullptr; -#if TERRAIN_UPDATING +#if TERRAIN_EDITING _cachedHeightMap.Resize(0); _cachedHolesMask.Resize(0); _wasHeightModified = false; @@ -114,7 +110,7 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z) TerrainPatch::~TerrainPatch() { -#if TERRAIN_UPDATING +#if TERRAIN_EDITING SAFE_DELETE(_dataHeightmap); for (int32 i = 0; i < TERRAIN_MAX_SPLATMAPS_COUNT; i++) { @@ -134,6 +130,13 @@ RawDataAsset* TerrainPatch::GetHeightfield() const return _heightfield.Get(); } +void TerrainPatch::GetTextures(GPUTexture*& heightmap, GPUTexture*& splatmap0, GPUTexture*& splatmap1) const +{ + heightmap = Heightmap->GetTexture(); + splatmap0 = Splatmap[0] ? Splatmap[0]->GetTexture() : nullptr; + splatmap1 = Splatmap[1] ? Splatmap[1]->GetTexture() : nullptr; +} + void TerrainPatch::RemoveLightmap() { for (auto& chunk : Chunks) @@ -178,7 +181,7 @@ void TerrainPatch::UpdateTransform() _collisionVertices.Resize(0); } -#if TERRAIN_EDITING || TERRAIN_UPDATING +#if TERRAIN_EDITING bool IsValidMaterial(const JsonAssetReference& e) { @@ -217,7 +220,7 @@ struct TerrainDataUpdateInfo // When using physical materials, then get splatmaps data required for per-triangle material indices void GetSplatMaps() { -#if TERRAIN_UPDATING +#if TERRAIN_EDITING if (SplatMaps[0]) return; if (UsePhysicalMaterials()) @@ -1021,7 +1024,7 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap, _terrain->UpdateBounds(); _terrain->UpdateLayerBits(); -#if TERRAIN_UPDATING +#if TERRAIN_EDITING // Invalidate cache _cachedHeightMap.Resize(0); _cachedHolesMask.Resize(0); @@ -1169,7 +1172,7 @@ bool TerrainPatch::SetupSplatMap(int32 index, int32 splatMapLength, const Color3 } #endif -#if TERRAIN_UPDATING +#if TERRAIN_EDITING // Invalidate cache _cachedSplatMap[index].Resize(0); _wasSplatmapModified[index] = false; @@ -1191,7 +1194,7 @@ bool TerrainPatch::InitializeHeightMap() return SetupHeightMap(heightmap.Count(), heightmap.Get()); } -#if TERRAIN_UPDATING +#if TERRAIN_EDITING float* TerrainPatch::GetHeightmapData() { @@ -2631,7 +2634,7 @@ void TerrainPatch::Serialize(SerializeStream& stream, const void* otherObj) } stream.EndArray(); -#if TERRAIN_UPDATING +#if TERRAIN_EDITING SaveHeightData(); SaveSplatData(); #endif diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h index 7d85c5b1c..79e56e6da 100644 --- a/Source/Engine/Terrain/TerrainPatch.h +++ b/Source/Engine/Terrain/TerrainPatch.h @@ -12,6 +12,10 @@ struct RayCastHit; class TerrainMaterialShader; +#ifndef TERRAIN_EDITING +#define TERRAIN_EDITING 1 +#endif + /// /// Represents single terrain patch made of 16 terrain chunks. /// @@ -34,7 +38,7 @@ private: void* _physicsHeightField; CriticalSection _collisionLocker; float _collisionScaleXZ; -#if TERRAIN_UPDATING +#if TERRAIN_EDITING Array _cachedHeightMap; Array _cachedHolesMask; Array _cachedSplatMap[TERRAIN_MAX_SPLATMAPS_COUNT]; @@ -189,6 +193,8 @@ public: return _bounds; } + void GetTextures(GPUTexture*& heightmap, GPUTexture*& splatmap0, GPUTexture*& splatmap1) const; + public: /// /// Removes the lightmap data from the terrain patch. @@ -220,7 +226,7 @@ public: /// The holes mask (optional). Normalized to 0-1 range values with holes mask per-vertex. Must match the heightmap dimensions. /// If set to true patch will use virtual storage by force. Otherwise it can use normal texture asset storage on drive (valid only during Editor). Runtime-created terrain can only use virtual storage (in RAM). /// True if failed, otherwise false. - API_FUNCTION() bool SetupHeightMap(int32 heightMapLength, API_PARAM(Ref) const float* heightMap, API_PARAM(Ref) const byte* holesMask = nullptr, bool forceUseVirtualStorage = false); + API_FUNCTION() bool SetupHeightMap(int32 heightMapLength, const float* heightMap, const byte* holesMask = nullptr, bool forceUseVirtualStorage = false); /// /// Setups the terrain patch layer weights using the specified splatmaps data. @@ -230,14 +236,12 @@ public: /// The splat map. Each array item contains 4 layer weights. /// If set to true patch will use virtual storage by force. Otherwise it can use normal texture asset storage on drive (valid only during Editor). Runtime-created terrain can only use virtual storage (in RAM). /// True if failed, otherwise false. - API_FUNCTION() bool SetupSplatMap(int32 index, int32 splatMapLength, API_PARAM(Ref) const Color32* splatMap, bool forceUseVirtualStorage = false); -#endif + API_FUNCTION() bool SetupSplatMap(int32 index, int32 splatMapLength, const Color32* splatMap, bool forceUseVirtualStorage = false); -#if TERRAIN_UPDATING /// - /// Gets the raw pointer to the heightmap data. + /// Gets the raw pointer to the heightmap data. Array size is square of Terrain.HeightmapSize. /// - /// The heightmap data. + /// The heightmap data. Null if empty or failed to access it. API_FUNCTION() float* GetHeightmapData(); /// @@ -246,9 +250,9 @@ public: API_FUNCTION() void ClearHeightmapCache(); /// - /// Gets the raw pointer to the holes mask data. + /// Gets the raw pointer to the holes mask data. Array size is square of Terrain.HeightmapSize. /// - /// The holes mask data. + /// The holes mask data. Null if empty/unused or failed to access it. API_FUNCTION() byte* GetHolesMaskData(); /// @@ -257,10 +261,10 @@ public: API_FUNCTION() void ClearHolesMaskCache(); /// - /// Gets the raw pointer to the splat map data. + /// Gets the raw pointer to the splat map data. Array size is square of Terrain.HeightmapSize. /// /// The zero-based index of the splatmap texture. - /// The splat map data. + /// The splat map data. Null if empty/unused or failed to access it. API_FUNCTION() Color32* GetSplatMapData(int32 index); /// @@ -280,7 +284,7 @@ public: /// The offset from the first row and column of the heightmap data (offset destination x and z start position). /// The size of the heightmap to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() bool ModifyHeightMap(API_PARAM(Ref) const float* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize); + API_FUNCTION() bool ModifyHeightMap(const float* samples, const Int2& modifiedOffset, const Int2& modifiedSize); /// /// Modifies the terrain patch holes mask with the given samples. @@ -289,7 +293,7 @@ public: /// The offset from the first row and column of the holes map data (offset destination x and z start position). /// The size of the holes map to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() bool ModifyHolesMask(API_PARAM(Ref) const byte* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize); + API_FUNCTION() bool ModifyHolesMask(const byte* samples, const Int2& modifiedOffset, const Int2& modifiedSize); /// /// Modifies the terrain patch splat map (layers mask) with the given samples. @@ -299,7 +303,7 @@ public: /// The offset from the first row and column of the splat map data (offset destination x and z start position). /// The size of the splat map to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() bool ModifySplatMap(int32 index, API_PARAM(Ref) const Color32* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize); + API_FUNCTION() bool ModifySplatMap(int32 index, const Color32* samples, const Int2& modifiedOffset, const Int2& modifiedSize); private: bool UpdateHeightData(struct TerrainDataUpdateInfo& info, const Int2& modifiedOffset, const Int2& modifiedSize, bool wasHeightRangeChanged, bool wasHeightChanged); From 32af903a2dba8cb0084d35acb1febfd4f27dc3f3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 25 Mar 2026 22:31:53 +0100 Subject: [PATCH 64/68] Fix crash when exporting terrain that has missing patch data --- Source/Editor/Tools/Terrain/TerrainTools.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 861cb975b..8e83e77cf 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -382,7 +382,8 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder) const Int2 heightmapSize = size * Terrain::ChunksCountEdge * terrain->GetChunkSize() + 1; Array heightmap; heightmap.Resize(heightmapSize.X * heightmapSize.Y); - heightmap.SetAll(firstPatch->GetHeightmapData()[0]); + if (const float* heightmapData = firstPatch->GetHeightmapData()) + heightmap.SetAll(heightmapData[0]); // Fill heightmap with data from all patches const int32 rowSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1; @@ -392,8 +393,16 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder) const Int2 pos(patch->GetX() - start.X, patch->GetZ() - start.Y); const float* src = patch->GetHeightmapData(); float* dst = heightmap.Get() + pos.X * (rowSize - 1) + pos.Y * heightmapSize.X * (rowSize - 1); - for (int32 row = 0; row < rowSize; row++) - Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float)); + if (src) + { + for (int32 row = 0; row < rowSize; row++) + Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float)); + } + else + { + for (int32 row = 0; row < rowSize; row++) + Platform::MemoryClear(dst + row * heightmapSize.X, rowSize * sizeof(float)); + } } // Interpolate to 16-bit int From a9fbbaa88e5908948d8f93c9145d2d894d9bd511 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 26 Mar 2026 09:21:54 +0100 Subject: [PATCH 65/68] Fix spawned in Editor actor RigidBody to never be static --- .../Editor/SceneGraph/Actors/RigidBodyNode.cs | 29 +++++++++++++++++++ Source/Editor/SceneGraph/SceneGraphFactory.cs | 1 + 2 files changed, 30 insertions(+) create mode 100644 Source/Editor/SceneGraph/Actors/RigidBodyNode.cs diff --git a/Source/Editor/SceneGraph/Actors/RigidBodyNode.cs b/Source/Editor/SceneGraph/Actors/RigidBodyNode.cs new file mode 100644 index 000000000..9f9a38258 --- /dev/null +++ b/Source/Editor/SceneGraph/Actors/RigidBodyNode.cs @@ -0,0 +1,29 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +using FlaxEngine; + +namespace FlaxEditor.SceneGraph.Actors +{ + /// + /// Scene tree node for actor type. + /// + [HideInEditor] + public sealed class RigidBodyNode : ActorNode + { + /// + public RigidBodyNode(Actor actor) + : base(actor) + { + } + + /// + public override void PostSpawn() + { + base.PostSpawn(); + + if (HasPrefabLink) + return; + Actor.StaticFlags = StaticFlags.None; + } + } +} diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs index bd465aad2..8437167aa 100644 --- a/Source/Editor/SceneGraph/SceneGraphFactory.cs +++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs @@ -74,6 +74,7 @@ namespace FlaxEditor.SceneGraph CustomNodesTypes.Add(typeof(NavMesh), typeof(ActorNode)); CustomNodesTypes.Add(typeof(SpriteRender), typeof(SpriteRenderNode)); CustomNodesTypes.Add(typeof(Joint), typeof(JointNode)); + CustomNodesTypes.Add(typeof(RigidBody), typeof(RigidBodyNode)); } /// From f92ec30e1b7ffcf849b6739f7f963234a2e5fce5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 26 Mar 2026 09:26:42 +0100 Subject: [PATCH 66/68] Fix crash when using convex mesh collider with negative scale #3853 --- Source/Engine/Physics/Colliders/Collider.cpp | 31 ++++++++++++------- .../Physics/PhysX/PhysicsBackendPhysX.cpp | 7 +++-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 96bd9c7ae..28588e2a6 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -209,6 +209,13 @@ void Collider::CreateShape() // Create shape const bool isTrigger = _isTrigger && CanBeTrigger(); _shape = PhysicsBackend::CreateShape(this, shape, Material, IsActiveInHierarchy(), isTrigger); + if (!_shape) + { + LOG(Error, "Failed to create physics shape for actor '{}'", GetNamePath()); + if (shape.Type == CollisionShape::Types::ConvexMesh && Float3(shape.ConvexMesh.Scale).MinValue() <= 0) + LOG(Warning, "Convex Mesh colliders cannot have negative scale"); + return; + } PhysicsBackend::SetShapeContactOffset(_shape, _contactOffset); UpdateLayerBits(); } @@ -293,18 +300,20 @@ void Collider::BeginPlay(SceneBeginData* data) if (_shape == nullptr) { CreateShape(); - - // Check if parent is a rigidbody - const auto rigidBody = dynamic_cast(GetParent()); - if (rigidBody && CanAttach(rigidBody)) + if (_shape) { - // Attach to the rigidbody - Attach(rigidBody); - } - else - { - // Be a static collider - CreateStaticActor(); + // Check if parent is a rigidbody + const auto rigidBody = dynamic_cast(GetParent()); + if (rigidBody && CanAttach(rigidBody)) + { + // Attach to the rigidbody + Attach(rigidBody); + } + else + { + // Be a static collider + CreateStaticActor(); + } } } diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 02e23d890..5aba5343b 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -2653,10 +2653,13 @@ void* PhysicsBackend::CreateShape(PhysicsColliderActor* collider, const Collisio PxGeometryHolder geometryPhysX; GetShapeGeometry(geometry, geometryPhysX); PxShape* shapePhysX = PhysX->createShape(geometryPhysX.any(), materialsPhysX.Get(), materialsPhysX.Count(), true, shapeFlags); - shapePhysX->userData = collider; + if (shapePhysX) + { + shapePhysX->userData = collider; #if PHYSX_DEBUG_NAMING - shapePhysX->setName("Shape"); + shapePhysX->setName("Shape"); #endif + } return shapePhysX; } From 0f09cad1cfb4a4318be12c7c0bcf9b44b3236fa1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 26 Mar 2026 09:33:56 +0100 Subject: [PATCH 67/68] Update PhysX with new slim Build Rebuild for win64 and android only --- .../ARM64/libPhysXCharacterKinematic_static_64.a | 4 ++-- .../ThirdParty/ARM64/libPhysXCommon_static_64.a | 4 ++-- .../ThirdParty/ARM64/libPhysXCooking_static_64.a | 4 ++-- .../ARM64/libPhysXExtensions_static_64.a | 4 ++-- .../ARM64/libPhysXFoundation_static_64.a | 4 ++-- .../ThirdParty/ARM64/libPhysXPvdSDK_static_64.a | 3 --- .../ARM64/libPhysXVehicle2_static_64.a | 4 ++-- .../ThirdParty/ARM64/libPhysXVehicle_static_64.a | 4 ++-- .../ThirdParty/ARM64/libPhysX_static_64.a | 4 ++-- .../Binaries/ThirdParty/x64/FastXml_64.pdb | 2 +- .../Binaries/ThirdParty/x64/LowLevelAABB_64.pdb | 4 ++-- .../ThirdParty/x64/LowLevelDynamics_64.pdb | 4 ++-- .../Binaries/ThirdParty/x64/LowLevel_64.pdb | 4 ++-- .../x64/PhysXCharacterKinematic_static_64.lib | 4 ++-- .../x64/PhysXCharacterKinematic_static_64.pdb | 4 ++-- .../ThirdParty/x64/PhysXCommon_static_64.lib | 4 ++-- .../ThirdParty/x64/PhysXCommon_static_64.pdb | 4 ++-- .../ThirdParty/x64/PhysXCooking_static_64.lib | 4 ++-- .../ThirdParty/x64/PhysXCooking_static_64.pdb | 4 ++-- .../ThirdParty/x64/PhysXExtensions_static_64.lib | 4 ++-- .../ThirdParty/x64/PhysXExtensions_static_64.pdb | 4 ++-- .../ThirdParty/x64/PhysXFoundation_static_64.lib | 4 ++-- .../ThirdParty/x64/PhysXFoundation_static_64.pdb | 4 ++-- .../ThirdParty/x64/PhysXPvdSDK_static_64.lib | 3 --- .../ThirdParty/x64/PhysXPvdSDK_static_64.pdb | 3 --- .../Binaries/ThirdParty/x64/PhysXTask_64.pdb | 2 +- .../ThirdParty/x64/PhysXVehicle2_static_64.lib | 4 ++-- .../ThirdParty/x64/PhysXVehicle2_static_64.pdb | 4 ++-- .../ThirdParty/x64/PhysXVehicle_static_64.lib | 4 ++-- .../ThirdParty/x64/PhysXVehicle_static_64.pdb | 4 ++-- .../Binaries/ThirdParty/x64/PhysX_static_64.lib | 4 ++-- .../Binaries/ThirdParty/x64/PhysX_static_64.pdb | 4 ++-- .../Binaries/ThirdParty/x64/SceneQuery_64.pdb | 2 +- .../ThirdParty/x64/SimulationController_64.pdb | 4 ++-- .../PhysX/extensions/PxRepXSerializer.h | 3 +++ Source/ThirdParty/PhysX/foundation/PxHash.h | 2 +- Source/ThirdParty/PhysX/foundation/PxMath.h | 2 +- .../ThirdParty/PhysX/foundation/PxPreprocessor.h | 10 +++++++++- .../Tools/Flax.Build/Deps/Dependencies/PhysX.cs | 16 ++++++++++++++-- 39 files changed, 87 insertions(+), 73 deletions(-) delete mode 100644 Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXPvdSDK_static_64.a delete mode 100644 Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.lib delete mode 100644 Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.pdb diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCharacterKinematic_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCharacterKinematic_static_64.a index 884943f02..b532d98dc 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCharacterKinematic_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCharacterKinematic_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c07afaaa62724f7f8f532a1bc65a5fc50753e266495248a608d9f4b9476aa0b -size 2025020 +oid sha256:7b0ec52b292d42f583822883a7275bf1f311bd46866dd5e9ab3489c025a4fd5b +size 2024932 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCommon_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCommon_static_64.a index 59899248c..8ea5ddcca 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCommon_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCommon_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9817730bdeae1cfeb5207470d721b27f448cdd36b9eba85da73dec59e8a5bf99 -size 52205712 +oid sha256:1f3e7321d444eb054d7ce10e71be4deee9d58133157557e7b839ecd9ac48fc98 +size 52204304 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCooking_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCooking_static_64.a index 37e24dc4c..e0f395e4f 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCooking_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXCooking_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:524ded53592c687bc834c9db24c6ccfa8449f3392ec3fd99627c87cf7f069368 -size 111200 +oid sha256:d322d65623ce682730f28c0b390b36102eabc4ae453af9dfd813cc1e189c1628 +size 111192 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXExtensions_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXExtensions_static_64.a index b13b12074..697046535 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXExtensions_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXExtensions_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:759aa0379faa5f010c66654e46d352f24c1f837507ffa0178022546540f4ca0f -size 31683652 +oid sha256:5981d7ce59540fe829bab21485dbb83ab82f860ed15ca720f8c0d621bcd1296b +size 7873980 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXFoundation_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXFoundation_static_64.a index 19216936b..7a4e611b4 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXFoundation_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXFoundation_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6738a094c3dbe50a5820c83944b3992211bd3e372b1ccfbd2f3acb0541c04d2b -size 570452 +oid sha256:e915aa5a013c099fe6ced91c50f1fa8b5e46e3778b08d09d0a12d59f6252d87f +size 570404 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXPvdSDK_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXPvdSDK_static_64.a deleted file mode 100644 index e681dfc1b..000000000 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXPvdSDK_static_64.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7369e2822a705f592c67942c0ba354694b857fa9d83db6bed4dc0dcf3a4875f0 -size 3405028 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle2_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle2_static_64.a index edb89c6a0..78008e4b2 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle2_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle2_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bbb5ba66f5e47ee53fd099c805e94d66f47cb66c44ec2339f06b66ea89c6100 -size 1543576 +oid sha256:f6a43c86163970f92cb49f508c175e8cbd80ee83fc193d10011eb5905f20e0ab +size 1543440 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle_static_64.a index 815ea1e4a..81b2061c1 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysXVehicle_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4eff2cf32d6bfed5f32d26b5478ec4e1eda31d3959e71f79fa6e205905b1e67c -size 12277380 +oid sha256:cd5d1083b5479d322b98b56b94dc42139c5965a5c7434fe058382f760708273b +size 2364570 diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysX_static_64.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysX_static_64.a index a37ba9028..7fc068ccb 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysX_static_64.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libPhysX_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee3f4e2cc3160b7556dd8678c49f27160d401fbef729c81568dbc184b07fbab5 -size 37483230 +oid sha256:ae10a05b256896db3868df5d9914d5ffd10efbcc64dea046ba11946514625b7e +size 34853698 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/FastXml_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/FastXml_64.pdb index 43a87a79d..ab822e5d2 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/FastXml_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/FastXml_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:132883c177c47a532afd6fa33b5a2f498cd310001400062a6df33743c4d5e784 +oid sha256:dc758a8cf6f50caf75476128ceffc41afbb6b8aaa45e81cd17ef694c18bec1a3 size 118784 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelAABB_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelAABB_64.pdb index 819f1d896..acf9b53bc 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelAABB_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelAABB_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7668042f2a51ef982925d28ec57efe89f3adfc0f0bdd45c0d836ea810010d0a1 -size 593920 +oid sha256:0356328c34bc457903e70c6f3c9d984f288ff3917564cb5f5bd2efb84c8a0b9f +size 585728 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelDynamics_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelDynamics_64.pdb index e96418eed..5c8c6b364 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelDynamics_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevelDynamics_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5eb77e8531012b27e7616f097e17bd15a0d30f0314f311bacefa67610eb873c -size 1339392 +oid sha256:8a2a326575d5b6b468dc896399bb9bcb324a44f9097cb6b78457875737df7356 +size 1323008 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevel_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevel_64.pdb index a6f6b58f8..2a28827ee 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevel_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/LowLevel_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5ed568ad39c0d14de2e51b413657cc965ee2e831b4b6bc7e6418d16fa697163 -size 1257472 +oid sha256:7313a497a5a851d86d7a2f2161f5d4a01274b031bb3d3d0ccb279fc9db199e24 +size 1241088 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.lib index c9e7b5dfe..4258cc709 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:603e6025b22349137e7244bf6ca4056e09e6947660469269da1944d9f92e02fb -size 1306220 +oid sha256:46f69808637c2a42353e3935f2f6b3c3babb3bf0853e4551b70ce7e1f63a19ec +size 1319326 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.pdb index b82c855c8..2f9cdc350 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCharacterKinematic_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c870f987df7407b2edc5615a5fb4d3cb942af801d0260a13de1d27e52eac4fb -size 675840 +oid sha256:4cfb89b2836af0efe36f6e062262d4720153c9c3c396bfc6adb5098eb89e46c3 +size 667648 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.lib index 56a97a846..130cfe8a1 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2b2093cf1f72c4135f653875f0fe0fa33d6696d34c29b6ac8d4ac3bb7aa92c1 -size 26130976 +oid sha256:935f6ea3bc1162acabadeaec6b34cc81445f8039afbdc0edd284e7200cf662d8 +size 24990306 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.pdb index 4c3883939..24e7628a4 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCommon_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0c09e6550a0ab319a7cde6382cc31ba31113d54d8d779444435e56213d07285 -size 1822720 +oid sha256:65352f47a3e32ca16ae6c5cb672ff9a5ea5098a4ade33a080accf5e57054f679 +size 1781760 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.lib index 7a47a2ddb..03a130cf4 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea85451d201ceeea71fd46bb01b5dc3501a05c77538bb85ae665ec6c0385c350 -size 130200 +oid sha256:3f786c5d71caa92ad50e186da67ea1564213d593c709134a25cbced5c1184338 +size 128984 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.pdb index c79307c68..2ae2ecf13 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXCooking_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f18f710748a40b3be245f1027748ed6c9fe6058e049c3926632c0658dd983f4 -size 397312 +oid sha256:e22e8f2cf72d6c4d5fa35e426e6e0cc41e63402dc85aad5a1e65bc34bfd0146c +size 389120 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.lib index 7d543561d..4207c354d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f880ee07970656a19ac9a811f16be3edeed06834886606f2385bbcce9389ea4c -size 17300374 +oid sha256:0fce079940f5641113cb0d27c874923ab47a2e8ee286764c5711159c0e5f5bea +size 5639208 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.pdb index b6401abd3..178fb18f8 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXExtensions_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abd53b7899e7bce0b2180189d8c28df8b38107bb90cdd6078069c9d869169d9a -size 5156864 +oid sha256:7f28ef88132564ade7d813a9bfa667ce4c50e407ce3d6bcf2f386e8f5da98e16 +size 1740800 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.lib index dcb1935ee..3f08fa2c4 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f84c317ca2181e564fd7f6814898f9a1c738e3f157e5974da1fcf72d6abe8f9 -size 585126 +oid sha256:8685d51d5b7b69ee945da97e026cad77b98f91c564aae73176ca86c0d5f1d1b4 +size 571940 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.pdb index f784a8e8e..e074d0436 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXFoundation_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c4094a92b9252e8f0dee08600ef5f93fa01abb9b21fe4a2944114d41cd143ec -size 299008 +oid sha256:e0c44d3c2b487797eaefed7708d5389a366fa5217f3c5d6d06fdca00871de942 +size 315392 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.lib deleted file mode 100644 index 52b0147b0..000000000 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.lib +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:747ed5f9e098e9c8e0a75cdfdfd09e82b338904b5916cc60df45d585f398b215 -size 2073834 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.pdb deleted file mode 100644 index 3778728ef..000000000 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXPvdSDK_static_64.pdb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:227c4cddf1a1326c6f6a9cfb106c592f75433fcf047971f63c8bc992c6f16369 -size 946176 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXTask_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXTask_64.pdb index ed9215af3..06bee0418 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXTask_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXTask_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea9f74cc743bf588cbb9e6a5dbe544551eefe6be2ffa1c0232908159f3fd2612 +oid sha256:9898aebf247d35e2b27b0107fc7833abb5b2cc45a7bb5581f1f5d25a40e61fbf size 143360 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.lib index 1c7cb3bc0..b5abd5493 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c0c1865cba540abc026208ebf09653bb30f1ab9f800d65d53ea716e9b21ba64 -size 1125480 +oid sha256:56a251bf1d01a66e728618bbc53c3e54a8039c40465152f76868e37802137172 +size 1142828 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.pdb index 11ae60a06..79025e37d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle2_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f83d273d0419023214b3f5094bfc23bd567c495bcf262fb6b2d696427a390f19 -size 733184 +oid sha256:32f10a769eb31b14338b8f858b7a4d462fbc02d3b96c9f3ecc12f12aa8ccb4ce +size 724992 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.lib index c71216d38..4784302b8 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64d9bd9c981905263b5d10ab9a9b2153785b58d6405ad99ed5119d4b70b55f51 -size 5843352 +oid sha256:5ae79e8eaee91bc6db4b0b99d5c814b9609d323f7b2c0ffaeeccf8e09b4cd4be +size 1550810 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.pdb index 67447c121..47b31ec74 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysXVehicle_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eba825400abc8140c3d496ff0ad6df25ae9b4d4d2acb63db81171250d874e8b9 -size 3862528 +oid sha256:dcecb48dfed2969626a61197f6edc95dee1bdaf598c4567278704d0babffe812 +size 602112 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.lib index 8a0399bd7..beed86282 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8311b062fb2153604f3eddee2d2a5720beb7f571159e8b1ed8c9fa89d3a3d6c5 -size 34826088 +oid sha256:4d2c83f579a3c96adc604f20ad0219016cad04c21a78ac539cd38686b2556de8 +size 33511310 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.pdb index 6b3fbcb2d..1a0ffa3b8 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/PhysX_static_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a332f82798acafc6423c40e09223d7781abc43fbe5cbe1aed9069937ed7de180 -size 4558848 +oid sha256:0522612dd80ec1739c244136e42221e25a17db0d8d94bc860aa78469c0d0b329 +size 3502080 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/SceneQuery_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/SceneQuery_64.pdb index c6196ff06..1ce7cb454 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/SceneQuery_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/SceneQuery_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac1251babb3766e7e9bc2efc40873b43aa1ae55b331918c9dc790f82b9e77d4f +oid sha256:b3c3204aaded5ae0ea1acc502ca15597a2884658957ec36c778395e70321e853 size 544768 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/SimulationController_64.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/SimulationController_64.pdb index 92b08e4bc..d42fb5191 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/SimulationController_64.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/SimulationController_64.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b11081d87071515d65544c9265b9bd09bb66af0810fbd6ec03bd28512892466b -size 2699264 +oid sha256:b2986a02de98efa5ae0f1883d6ed5af91d042d3bad136c8883f6e6a47d2e91a8 +size 2674688 diff --git a/Source/ThirdParty/PhysX/extensions/PxRepXSerializer.h b/Source/ThirdParty/PhysX/extensions/PxRepXSerializer.h index 92f8211d0..0effba190 100644 --- a/Source/ThirdParty/PhysX/extensions/PxRepXSerializer.h +++ b/Source/ThirdParty/PhysX/extensions/PxRepXSerializer.h @@ -35,6 +35,8 @@ #include "common/PxBase.h" #include "extensions/PxRepXSimpleType.h" +#if PX_SERIALIZATION + #if !PX_DOXYGEN namespace physx { @@ -147,3 +149,4 @@ PX_INLINE physx::PxRepXObject PxCreateRepXObject(const TDataType* inType) /** @} */ #endif +#endif diff --git a/Source/ThirdParty/PhysX/foundation/PxHash.h b/Source/ThirdParty/PhysX/foundation/PxHash.h index 43ea55215..c85756b0a 100644 --- a/Source/ThirdParty/PhysX/foundation/PxHash.h +++ b/Source/ThirdParty/PhysX/foundation/PxHash.h @@ -86,7 +86,7 @@ PX_FORCE_INLINE uint32_t PxComputeHash(const uint64_t key) return uint32_t(UINT32_MAX & k); } -#if PX_APPLE_FAMILY +#if PX_APPLE_FAMILY || PX_EMSCRIPTEN // hash for size_t, to make gcc happy PX_INLINE uint32_t PxComputeHash(const size_t key) { diff --git a/Source/ThirdParty/PhysX/foundation/PxMath.h b/Source/ThirdParty/PhysX/foundation/PxMath.h index cff5b1795..8b071fa32 100644 --- a/Source/ThirdParty/PhysX/foundation/PxMath.h +++ b/Source/ThirdParty/PhysX/foundation/PxMath.h @@ -45,7 +45,7 @@ #pragma warning(pop) #endif -#if (PX_LINUX_FAMILY && !PX_ARM_FAMILY) +#if (PX_LINUX_FAMILY && !PX_ARM_FAMILY && !PX_EMSCRIPTEN) // Force linking against nothing newer than glibc v2.17 to remain compatible with platforms with older glibc versions __asm__(".symver expf,expf@GLIBC_2.2.5"); __asm__(".symver powf,powf@GLIBC_2.2.5"); diff --git a/Source/ThirdParty/PhysX/foundation/PxPreprocessor.h b/Source/ThirdParty/PhysX/foundation/PxPreprocessor.h index 2b23aa222..6ca05cee3 100644 --- a/Source/ThirdParty/PhysX/foundation/PxPreprocessor.h +++ b/Source/ThirdParty/PhysX/foundation/PxPreprocessor.h @@ -437,8 +437,16 @@ Use these macro definitions to create warnings for deprecated functions General defines */ +// Customization to exclude serialization code +#define PX_SERIALIZATION 0 + +// Customization to exclude soft-bodies (incl. tetrahedral meshes) +#define PX_SOFT_BODY 0 + // static assert -#if(defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || (PX_APPLE_FAMILY) || (PX_SWITCH || PX_PS4 || PX_PS5) || (PX_CLANG && PX_ARM) || (PX_CLANG && PX_A64) +#if PX_EMSCRIPTEN + #define PX_COMPILE_TIME_ASSERT(exp) +#elif(defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || (PX_APPLE_FAMILY) || (PX_SWITCH || PX_PS4 || PX_PS5) || (PX_CLANG && PX_ARM) || (PX_CLANG && PX_A64) #define PX_COMPILE_TIME_ASSERT(exp) typedef char PX_CONCAT(PxCompileTimeAssert_Dummy, __COUNTER__)[(exp) ? 1 : -1] __attribute__((unused)) #else #define PX_COMPILE_TIME_ASSERT(exp) typedef char PxCompileTimeAssert_Dummy[(exp) ? 1 : -1] diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs index 18bb4e69f..deaaa8fc5 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs @@ -48,6 +48,7 @@ namespace Flax.Deps.Dependencies throw new Exception(string.Format("Missing PhysX preset {0} (file: {1})", preset, presetPath)); var presetXml = new XmlDocument(); presetXml.Load(presetPath); + var usePVD = false; // Configure preset var cmakeSwitches = presetXml["preset"]["CMakeSwitches"]; @@ -59,6 +60,11 @@ namespace Flax.Deps.Dependencies ConfigureCmakeSwitch(cmakeSwitches, "NV_USE_STATIC_WINCRT", "False"); ConfigureCmakeSwitch(cmakeSwitches, "NV_USE_DEBUG_WINCRT", "False"); ConfigureCmakeSwitch(cmakeSwitches, "PX_FLOAT_POINT_PRECISE_MATH", "False"); + if (usePVD) + { + // PVD depends on metadata and serialization code striped from shipping builds + ConfigureCmakeSwitch(cmakeSwitches, "PX_SERIALIZATION", "True"); + } var cmakeParams = presetXml["preset"]["CMakeParams"]; switch (targetPlatform) { @@ -286,12 +292,18 @@ namespace Flax.Deps.Dependencies Log.Verbose("Copy PhysX binaries from " + srcBinaries); foreach (var physXLib in defaultPhysXLibs) { + var remove = !usePVD && physXLib.Contains("Pvd"); var filename = suppressBitsPostfix ? string.Format("{0}{1}_static", binariesPrefix, physXLib) : string.Format("{0}{1}_static_{2}", binariesPrefix, physXLib, bits); filename += binariesExtension; - Utilities.FileCopy(Path.Combine(srcBinaries, filename), Path.Combine(dstBinaries, filename)); + if (remove) + Utilities.FileDelete(Path.Combine(dstBinaries, filename)); + else + Utilities.FileCopy(Path.Combine(srcBinaries, filename), Path.Combine(dstBinaries, filename)); var filenamePdb = Path.ChangeExtension(filename, "pdb"); - if (File.Exists(Path.Combine(srcBinaries, filenamePdb))) + if (remove) + Utilities.FileDelete(Path.Combine(dstBinaries, filenamePdb)); + else if (File.Exists(Path.Combine(srcBinaries, filenamePdb))) Utilities.FileCopy(Path.Combine(srcBinaries, filenamePdb), Path.Combine(dstBinaries, filenamePdb)); // Strip debug symbols to reduce binaries size From a48ce78733ec1b286d72f8c61ba6e30ff3e39d09 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 26 Mar 2026 12:28:31 +0100 Subject: [PATCH 68/68] Fix crash when loading `CollisionData` before physics init #3971 --- Source/Engine/Content/Asset.cpp | 34 +++++++++++++++++++++++ Source/Engine/Content/Asset.h | 4 +++ Source/Engine/Content/Assets/Material.cpp | 13 ++------- Source/Engine/Physics/CollisionData.cpp | 2 ++ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index 7a35d3b28..a9b1a7fc9 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -8,6 +8,8 @@ #include "Loading/Tasks/LoadAssetTask.h" #include "Engine/Core/Log.h" #include "Engine/Core/LogContext.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Physics/Physics.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Scripting/ManagedCLR/MCore.h" @@ -703,6 +705,38 @@ void Asset::onUnload_MainThread() OnUnloaded(this); } +bool Asset::WaitForInitGraphics() +{ +#define IS_GPU_NOT_READY() (GPUDevice::Instance == nullptr || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready) + if (!IsInMainThread() && IS_GPU_NOT_READY()) + { + PROFILE_CPU(); + ZoneColor(TracyWaitZoneColor); + int32 timeout = 1000; + while (IS_GPU_NOT_READY() && timeout-- > 0) + Platform::Sleep(1); + if (IS_GPU_NOT_READY()) + return true; + } +#undef IS_GPU_NOT_READY + return false; +} + +bool Asset::WaitForInitPhysics() +{ + if (!IsInMainThread() && !Physics::DefaultScene) + { + PROFILE_CPU(); + ZoneColor(TracyWaitZoneColor); + int32 timeout = 1000; + while (!Physics::DefaultScene && timeout-- > 0) + Platform::Sleep(1); + if (!Physics::DefaultScene) + return true; + } + return false; +} + #if USE_EDITOR bool Asset::OnCheckSave(const StringView& path) const diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h index e6607e62c..a7913a5a5 100644 --- a/Source/Engine/Content/Asset.h +++ b/Source/Engine/Content/Asset.h @@ -285,6 +285,10 @@ protected: virtual void onRename(const StringView& newPath) = 0; #endif + // Utilities to ensure specific engine systems are initialized before loading asset (eg. assets can be loaded during engine startup). + static bool WaitForInitGraphics(); + static bool WaitForInitPhysics(); + public: // [ManagedScriptingObject] String ToString() const override; diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index b4cf55d4d..e46060397 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -5,7 +5,6 @@ #include "Engine/Core/Types/DataContainer.h" #include "Engine/Content/Upgraders/ShaderAssetUpgrader.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" -#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/Materials/MaterialShader.h" #include "Engine/Graphics/Shaders/Cache/ShaderCacheManager.h" @@ -156,16 +155,8 @@ Asset::LoadResult Material::load() FlaxChunk* materialParamsChunk; // Wait for the GPU Device to be ready (eg. case when loading material before GPU init) -#define IS_GPU_NOT_READY() (GPUDevice::Instance == nullptr || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready) - if (!IsInMainThread() && IS_GPU_NOT_READY()) - { - int32 timeout = 1000; - while (IS_GPU_NOT_READY() && timeout-- > 0) - Platform::Sleep(1); - if (IS_GPU_NOT_READY()) - return LoadResult::InvalidData; - } -#undef IS_GPU_NOT_READY + if (WaitForInitGraphics()) + return LoadResult::CannotLoadData; // If engine was compiled with shaders compiling service: // - Material should be changed in need to convert it to the newer version (via Visject Surface) diff --git a/Source/Engine/Physics/CollisionData.cpp b/Source/Engine/Physics/CollisionData.cpp index c65ea8a6b..94cef111e 100644 --- a/Source/Engine/Physics/CollisionData.cpp +++ b/Source/Engine/Physics/CollisionData.cpp @@ -257,6 +257,8 @@ Asset::LoadResult CollisionData::load() CollisionData::LoadResult CollisionData::load(const SerializedOptions* options, byte* dataPtr, int32 dataSize) { + if (WaitForInitPhysics()) + return LoadResult::CannotLoadData; PROFILE_MEM(Physics); // Load options