From ff56152ef2b9166345405e93ba1fa4de6a1c2b11 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 15 Jul 2023 13:19:22 +0300 Subject: [PATCH 01/11] Fix releasing non-collectible types with collectible generic types --- .../Engine/Engine/NativeInterop.Unmanaged.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 17b8b73af..57454ad1e 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -244,7 +244,25 @@ namespace FlaxEngine.Interop @namespace = NativeAllocStringAnsi(type.Namespace ?? ""), typeAttributes = (uint)type.Attributes, }; - *assemblyHandle = GetAssemblyHandle(type.Assembly); + + Assembly assembly = null; + if (type.IsGenericType && !type.Assembly.IsCollectible) + { + // The owning assembly of a generic type with type arguments referencing + // collectible assemblies must be one of the collectible assemblies. + foreach (var genericType in type.GetGenericArguments()) + { + if (genericType.Assembly.IsCollectible) + { + assembly = genericType.Assembly; + break; + } + } + } + if (assembly == null) + assembly = type.Assembly; + + *assemblyHandle = GetAssemblyHandle(assembly); } [UnmanagedCallersOnly] From ba93e1e1d0bcd711a46e6a573abc2085717a2e5d Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 10:00:34 -0500 Subject: [PATCH 02/11] Add showing default materials to model entries. --- .../CustomEditors/Editors/AssetRefEditor.cs | 35 ++++---- .../Editors/ModelInstanceEntryEditor.cs | 85 +++++++++++++++++++ Source/Engine/Graphics/GPUDevice.h | 2 +- 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 17999d772..b83b44d59 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -33,7 +33,10 @@ namespace FlaxEditor.CustomEditors.Editors [CustomEditor(typeof(Asset)), DefaultEditor] public class AssetRefEditor : CustomEditor { - private AssetPicker _picker; + /// + /// The asset picker used to get a reference to an asset. + /// + public AssetPicker Picker; private ScriptType _valueType; /// @@ -44,7 +47,7 @@ namespace FlaxEditor.CustomEditors.Editors { if (HasDifferentTypes) return; - _picker = layout.Custom().CustomControl; + Picker = layout.Custom().CustomControl; _valueType = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]); var assetType = _valueType; @@ -66,7 +69,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Generic file picker assetType = ScriptType.Null; - _picker.FileExtension = assetReference.TypeName; + Picker.FileExtension = assetReference.TypeName; } else { @@ -78,23 +81,23 @@ namespace FlaxEditor.CustomEditors.Editors } } - _picker.AssetType = assetType; - _picker.Height = height; - _picker.SelectedItemChanged += OnSelectedItemChanged; + Picker.AssetType = assetType; + Picker.Height = height; + Picker.SelectedItemChanged += OnSelectedItemChanged; } private void OnSelectedItemChanged() { if (typeof(AssetItem).IsAssignableFrom(_valueType.Type)) - SetValue(_picker.SelectedItem); + SetValue(Picker.SelectedItem); else if (_valueType.Type == typeof(Guid)) - SetValue(_picker.SelectedID); + SetValue(Picker.SelectedID); else if (_valueType.Type == typeof(SceneReference)) - SetValue(new SceneReference(_picker.SelectedID)); + SetValue(new SceneReference(Picker.SelectedID)); else if (_valueType.Type == typeof(string)) - SetValue(_picker.SelectedPath); + SetValue(Picker.SelectedPath); else - SetValue(_picker.SelectedAsset); + SetValue(Picker.SelectedAsset); } /// @@ -105,15 +108,15 @@ namespace FlaxEditor.CustomEditors.Editors if (!HasDifferentValues) { if (Values[0] is AssetItem assetItem) - _picker.SelectedItem = assetItem; + Picker.SelectedItem = assetItem; else if (Values[0] is Guid guid) - _picker.SelectedID = guid; + Picker.SelectedID = guid; else if (Values[0] is SceneReference sceneAsset) - _picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID); + Picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID); else if (Values[0] is string path) - _picker.SelectedPath = path; + Picker.SelectedPath = path; else - _picker.SelectedAsset = Values[0] as Asset; + Picker.SelectedAsset = Values[0] as Asset; } } } diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index a08a254d4..6ac96fcea 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEditor.CustomEditors.Elements; +using FlaxEditor.CustomEditors.GUI; +using FlaxEditor.Scripting; using FlaxEngine; namespace FlaxEditor.CustomEditors.Editors @@ -13,6 +15,8 @@ namespace FlaxEditor.CustomEditors.Editors { private GroupElement _group; private bool _updateName; + private MaterialBase _material; + private ModelInstanceEntry _entry; /// public override void Initialize(LayoutElementsContainer layout) @@ -20,10 +24,91 @@ namespace FlaxEditor.CustomEditors.Editors _updateName = true; var group = layout.Group("Entry"); _group = group; + + _entry = (ModelInstanceEntry)Values[0]; + var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this); + var materiaLabel = new PropertyNameLabel("Material"); + materiaLabel.TooltipText = "The mesh surface material used for the rendering."; + if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel) + { + // Ensure that entry with default material set is set back to null + if (_entry.Material == staticModel.Model.MaterialSlots[entryIndex].Material) + { + staticModel.SetMaterial(entryIndex, null); + } + _material = staticModel.GetMaterial(entryIndex); + var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); + var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; + materialEditor.Values.SetDefaultValue(staticModel.Model.MaterialSlots[entryIndex].Material); + materialEditor.RefreshDefaultValue(); + materialEditor.Picker.SelectedItemChanged += () => + { + var material = materialEditor.Picker.SelectedAsset as MaterialBase; + if (!material) + { + staticModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); + materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; + } + else if (material == staticModel.Model.MaterialSlots[entryIndex]) + { + staticModel.SetMaterial(entryIndex, null); + } + else + { + staticModel.SetMaterial(entryIndex, material); + } + }; + } + else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel) + { + // Ensure that entry with default material set is set back to null + if (_entry.Material == animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) + { + animatedModel.SetMaterial(entryIndex, null); + } + _material = animatedModel.GetMaterial(entryIndex); + + var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => + { + _material = value as MaterialBase; + }); + var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; + materialEditor.Values.SetDefaultValue(animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material); + materialEditor.RefreshDefaultValue(); + materialEditor.Picker.SelectedItemChanged += () => + { + var material = materialEditor.Picker.SelectedAsset as MaterialBase; + if (!material) + { + animatedModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); + materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; + } + else if (material == animatedModel.SkinnedModel.MaterialSlots[entryIndex]) + { + animatedModel.SetMaterial(entryIndex, null); + } + else + { + animatedModel.SetMaterial(entryIndex, material); + } + }; + } + base.Initialize(group); } + /// + protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item) + { + // Skip material member as it is overridden + if (item.Info.Name == "Material") + { + return; + } + base.SpawnProperty(itemLayout, itemValues, item); + } + /// public override void Refresh() { diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h index 4dbcf6d06..422e554a0 100644 --- a/Source/Engine/Graphics/GPUDevice.h +++ b/Source/Engine/Graphics/GPUDevice.h @@ -238,7 +238,7 @@ public: /// /// Gets the default material. /// - MaterialBase* GetDefaultMaterial() const; + API_PROPERTY() MaterialBase* GetDefaultMaterial() const; /// /// Gets the default material (Deformable domain). From c8edaf5d6eabd0a99a35273652e0b810ef6bd42f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 10:10:37 -0500 Subject: [PATCH 03/11] Fix bug with not using slot material --- .../Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 6ac96fcea..a605b5464 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -50,7 +50,7 @@ namespace FlaxEditor.CustomEditors.Editors staticModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; } - else if (material == staticModel.Model.MaterialSlots[entryIndex]) + else if (material == staticModel.Model.MaterialSlots[entryIndex].Material) { staticModel.SetMaterial(entryIndex, null); } @@ -84,7 +84,7 @@ namespace FlaxEditor.CustomEditors.Editors animatedModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; } - else if (material == animatedModel.SkinnedModel.MaterialSlots[entryIndex]) + else if (material == animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) { animatedModel.SetMaterial(entryIndex, null); } @@ -112,6 +112,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Refresh() { + Debug.Log("Hit"); if (_updateName && _group != null && ParentEditor?.ParentEditor != null && From 02219beac97507d1d7e578d0715aa7b48e98b908 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 10:27:59 -0500 Subject: [PATCH 04/11] Handle edge case --- .../Editors/ModelInstanceEntryEditor.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index a605b5464..d33efe551 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -40,7 +40,7 @@ namespace FlaxEditor.CustomEditors.Editors var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; - materialEditor.Values.SetDefaultValue(staticModel.Model.MaterialSlots[entryIndex].Material); + materialEditor.Values.SetDefaultValue((staticModel.Model.MaterialSlots[entryIndex].Material) ? staticModel.Model.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); materialEditor.RefreshDefaultValue(); materialEditor.Picker.SelectedItemChanged += () => { @@ -54,6 +54,10 @@ namespace FlaxEditor.CustomEditors.Editors { staticModel.SetMaterial(entryIndex, null); } + else if (material == GPUDevice.Instance.DefaultMaterial && !staticModel.Model.MaterialSlots[entryIndex].Material) + { + staticModel.SetMaterial(entryIndex, null); + } else { staticModel.SetMaterial(entryIndex, material); @@ -69,12 +73,9 @@ namespace FlaxEditor.CustomEditors.Editors } _material = animatedModel.GetMaterial(entryIndex); - var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => - { - _material = value as MaterialBase; - }); + var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; - materialEditor.Values.SetDefaultValue(animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material); + materialEditor.Values.SetDefaultValue((animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) ? animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); materialEditor.RefreshDefaultValue(); materialEditor.Picker.SelectedItemChanged += () => { @@ -88,6 +89,10 @@ namespace FlaxEditor.CustomEditors.Editors { animatedModel.SetMaterial(entryIndex, null); } + else if (material == GPUDevice.Instance.DefaultMaterial && !animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) + { + animatedModel.SetMaterial(entryIndex, null); + } else { animatedModel.SetMaterial(entryIndex, material); From c89421438b56ae193cebfca8fec44b90c8dd9d33 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 10:42:41 -0500 Subject: [PATCH 05/11] Code cleanup --- .../Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index d33efe551..b7989cae3 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -108,16 +108,13 @@ namespace FlaxEditor.CustomEditors.Editors { // Skip material member as it is overridden if (item.Info.Name == "Material") - { return; - } base.SpawnProperty(itemLayout, itemValues, item); } /// public override void Refresh() { - Debug.Log("Hit"); if (_updateName && _group != null && ParentEditor?.ParentEditor != null && From 44518e88d567f3e3ea590d029c5ca6392fc6cb65 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 09:48:43 +0200 Subject: [PATCH 06/11] Fix crash when using Vector3 soft casting in Visual Scripts --- Source/Engine/Core/Types/Variant.cpp | 117 +++++++++++++++++++++++++ Source/Engine/Core/Types/Variant.h | 3 + Source/Engine/Scripting/Scripting.cpp | 18 +++- Source/Engine/Visject/VisjectGraph.cpp | 4 + Source/Engine/Visject/VisjectGraph.h | 1 + 5 files changed, 142 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 8232d415f..973e494f1 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -2796,6 +2796,122 @@ String Variant::ToString() const } } +void Variant::Inline() +{ + VariantType::Types type = VariantType::Null; + byte data[sizeof(Matrix)]; + if (Type.Type == VariantType::Structure && AsBlob.Data && AsBlob.Length <= sizeof(Matrix)) + { + for (int32 i = 2; i < VariantType::MAX; i++) + { + if (StringUtils::Compare(Type.TypeName, InBuiltTypesTypeNames[i]) == 0) + { + type = (VariantType::Types)i; + break; + } + } + if (type == VariantType::Null) + { + // Aliases + if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector2") == 0) + type = VariantType::Types::Vector2; + else if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector3") == 0) + type = VariantType::Types::Vector3; + else if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector4") == 0) + type = VariantType::Types::Vector4; + } + if (type != VariantType::Null) + Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length); + } + if (type != VariantType::Null) + { + switch (type) + { + case VariantType::Bool: + *this = *(bool*)data; + break; + case VariantType::Int: + *this = *(int32*)data; + break; + case VariantType::Uint: + *this = *(uint32*)data; + break; + case VariantType::Int64: + *this = *(int64*)data; + break; + case VariantType::Uint64: + *this = *(uint64*)data; + break; + case VariantType::Float: + *this = *(float*)data; + break; + case VariantType::Double: + *this = *(double*)data; + break; + case VariantType::Float2: + *this = *(Float2*)data; + break; + case VariantType::Float3: + *this = *(Float3*)data; + break; + case VariantType::Float4: + *this = *(Float4*)data; + break; + case VariantType::Color: + *this = *(Color*)data; + break; + case VariantType::Guid: + *this = *(Guid*)data; + break; + case VariantType::BoundingBox: + *this = Variant(*(BoundingBox*)data); + break; + case VariantType::BoundingSphere: + *this = *(BoundingSphere*)data; + break; + case VariantType::Quaternion: + *this = *(Quaternion*)data; + break; + case VariantType::Transform: + *this = Variant(*(Transform*)data); + break; + case VariantType::Rectangle: + *this = *(Rectangle*)data; + break; + case VariantType::Ray: + *this = Variant(*(Ray*)data); + break; + case VariantType::Matrix: + *this = Variant(*(Matrix*)data); + break; + case VariantType::Int2: + *this = *(Int2*)data; + break; + case VariantType::Int3: + *this = *(Int3*)data; + break; + case VariantType::Int4: + *this = *(Int4*)data; + break; + case VariantType::Int16: + *this = *(int16*)data; + break; + case VariantType::Uint16: + *this = *(uint16*)data; + break; + case VariantType::Double2: + *this = *(Double2*)data; + break; + case VariantType::Double3: + *this = *(Double3*)data; + break; + case VariantType::Double4: + *this = *(Double4*)data; + break; + } + } +} + bool Variant::CanCast(const Variant& v, const VariantType& to) { if (v.Type == to) @@ -3682,6 +3798,7 @@ void Variant::AllocStructure() const ScriptingType& type = typeHandle.GetType(); AsBlob.Length = type.Size; AsBlob.Data = Allocator::Allocate(AsBlob.Length); + Platform::MemoryClear(AsBlob.Data, AsBlob.Length); type.Struct.Ctor(AsBlob.Data); } else if (typeName == "System.Int16" || typeName == "System.UInt16") diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h index 7745307c3..9776c8e0b 100644 --- a/Source/Engine/Core/Types/Variant.h +++ b/Source/Engine/Core/Types/Variant.h @@ -359,6 +359,9 @@ public: void SetAsset(Asset* asset); String ToString() const; + // Inlines potential value type into in-built format (eg. Vector3 stored as Structure, or String stored as ManagedObject). + void Inline(); + FORCE_INLINE Variant Cast(const VariantType& to) const { return Cast(*this, to); diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index e2d709653..bbfa802f1 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -476,12 +476,28 @@ bool Scripting::Load() // Load FlaxEngine const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); - if (((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->Load(flaxEnginePath)) + auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine(); + if (flaxEngineModule->Assembly->Load(flaxEnginePath)) { LOG(Error, "Failed to load FlaxEngine C# assembly."); return true; } + // Insert type aliases for vector types that don't exist in C++ but are just typedef (properly redirect them to actual types) + // TODO: add support for automatic typedef aliases setup for scripting module to properly lookup type from the alias typename +#if USE_LARGE_WORLDS + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double2"]; + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double3"]; + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double4"]; +#else + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float2"]; + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float3"]; + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float4"]; +#endif + flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector2")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"]; + flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector3")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"]; + flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector4")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"]; + #if USE_EDITOR // Skip loading game modules in Editor on startup - Editor loads them later during splash screen (eg. after first compilation) static bool SkipFirstLoad = true; diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index 37154e09f..ca4495f67 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -675,6 +675,10 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value) } } } + + // For in-built structures try to convert it into internal format for better comparability with the scripting + value.Inline(); + break; } // Unpack Structure diff --git a/Source/Engine/Visject/VisjectGraph.h b/Source/Engine/Visject/VisjectGraph.h index 238823afd..0d841f3d4 100644 --- a/Source/Engine/Visject/VisjectGraph.h +++ b/Source/Engine/Visject/VisjectGraph.h @@ -246,6 +246,7 @@ public: void ProcessGroupCollections(Box* box, Node* node, Value& value); protected: + void InlineVariantStruct(Variant& v); virtual Value eatBox(Node* caller, Box* box) = 0; virtual Graph* GetCurrentGraph() const = 0; From 488958ce44b2d7cff21b7edaa9ddbb0e3dd7d3da Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 10:16:11 +0200 Subject: [PATCH 07/11] Fix `DrawSceneDepth` to properly draw scene objects when custom actors list is empty #1253 --- Source/Editor/Gizmo/SelectionOutline.cs | 2 ++ Source/Engine/Renderer/Renderer.cs | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Gizmo/SelectionOutline.cs b/Source/Editor/Gizmo/SelectionOutline.cs index ccc4bdd27..1374c7247 100644 --- a/Source/Editor/Gizmo/SelectionOutline.cs +++ b/Source/Editor/Gizmo/SelectionOutline.cs @@ -178,6 +178,8 @@ namespace FlaxEditor.Gizmo if (selection[i] is ActorNode actorNode && actorNode.Actor != null) CollectActors(actorNode.Actor); } + if (_actors.Count == 0) + return; // Render selected objects depth Renderer.DrawSceneDepth(context, task, customDepth, _actors); diff --git a/Source/Engine/Renderer/Renderer.cs b/Source/Engine/Renderer/Renderer.cs index bfb81a84c..71dca424b 100644 --- a/Source/Engine/Renderer/Renderer.cs +++ b/Source/Engine/Renderer/Renderer.cs @@ -17,10 +17,13 @@ namespace FlaxEngine [Unmanaged] public static void DrawSceneDepth(GPUContext context, SceneRenderTask task, GPUTexture output, List customActors) { - if (customActors.Count == 0) - return; - var temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME - var tempCount = temp.Length; + Actor[] temp = null; + int tempCount = 0; + if (customActors != null && customActors.Count != 0) + { + temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME + tempCount = temp.Length; + } Internal_DrawSceneDepth(FlaxEngine.Object.GetUnmanagedPtr(context), FlaxEngine.Object.GetUnmanagedPtr(task), FlaxEngine.Object.GetUnmanagedPtr(output), temp, ref tempCount); } @@ -32,7 +35,14 @@ namespace FlaxEngine [Unmanaged] public static void DrawActors(ref RenderContext renderContext, List customActors) { - DrawActors(ref renderContext, Utils.ExtractArrayFromList(customActors)); + Actor[] temp = null; + int tempCount = 0; + if (customActors != null && customActors.Count != 0) + { + temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME + tempCount = temp.Length; + } + Internal_DrawActors(ref renderContext, temp, ref tempCount); } } } From 0f613abfb96a7dcc3e29422eb06ed0dd94dd7031 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 10:54:21 +0200 Subject: [PATCH 08/11] Add `ToSpan` from 24c03c0e4b8bbbf4fb640d7b53b84bc2eaebcd57 --- Source/Engine/Core/Types/Span.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Core/Types/Span.h b/Source/Engine/Core/Types/Span.h index e7bea5fbd..e0518ec77 100644 --- a/Source/Engine/Core/Types/Span.h +++ b/Source/Engine/Core/Types/Span.h @@ -115,6 +115,12 @@ inline Span ToSpan(const T* ptr, int32 length) return Span(ptr, length); } +template +inline Span ToSpan(const Array& data) +{ + return Span((U*)data.Get(), data.Count()); +} + template inline bool SpanContains(const Span span, const T& value) { From 338499536fe4e3836568969e5493e88d92f4f732 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 10:55:00 +0200 Subject: [PATCH 09/11] Add `ModelInstanceActor::GetMaterialSlots` --- Source/Engine/Content/Assets/MaterialBase.h | 1 - Source/Engine/Level/Actors/AnimatedModel.cpp | 8 ++++++++ Source/Engine/Level/Actors/AnimatedModel.h | 1 + Source/Engine/Level/Actors/ModelInstanceActor.h | 5 +++++ Source/Engine/Level/Actors/SplineModel.cpp | 8 ++++++++ Source/Engine/Level/Actors/SplineModel.h | 1 + Source/Engine/Level/Actors/StaticModel.cpp | 8 ++++++++ Source/Engine/Level/Actors/StaticModel.h | 1 + 8 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Assets/MaterialBase.h b/Source/Engine/Content/Assets/MaterialBase.h index bca70b6da..070347348 100644 --- a/Source/Engine/Content/Assets/MaterialBase.h +++ b/Source/Engine/Content/Assets/MaterialBase.h @@ -27,7 +27,6 @@ public: /// /// Returns true if material is an material instance. /// - /// True if it's a material instance, otherwise false. virtual bool IsMaterialInstance() const = 0; public: diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 373278885..dff527924 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -898,6 +898,14 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m DrawModes |= DrawPass::GlobalSurfaceAtlas; } +const Span AnimatedModel::GetMaterialSlots() const +{ + const auto model = SkinnedModel.Get(); + if (model && !model->WaitForLoaded()) + return ToSpan(model->MaterialSlots); + return Span(); +} + MaterialBase* AnimatedModel::GetMaterial(int32 entryIndex) { if (SkinnedModel) diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 2693f573d..763629e93 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -373,6 +373,7 @@ public: bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + const Span GetMaterialSlots() const override; MaterialBase* GetMaterial(int32 entryIndex) override; bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override; bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override; diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.h b/Source/Engine/Level/Actors/ModelInstanceActor.h index 1dfd5c0e0..3faad35b3 100644 --- a/Source/Engine/Level/Actors/ModelInstanceActor.h +++ b/Source/Engine/Level/Actors/ModelInstanceActor.h @@ -35,6 +35,11 @@ public: /// API_PROPERTY() void SetEntries(const Array& value); + /// + /// Gets the material slots array set on the asset (eg. model or skinned model asset). + /// + API_PROPERTY(Sealed) virtual const Span GetMaterialSlots() const = 0; + /// /// Gets the material used to draw the meshes which are assigned to that slot (set in Entries or model's default). /// diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 843bf6076..6ca841b41 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -341,6 +341,14 @@ void SplineModel::OnParentChanged() OnSplineUpdated(); } +const Span SplineModel::GetMaterialSlots() const +{ + const auto model = Model.Get(); + if (model && !model->WaitForLoaded()) + return ToSpan(model->MaterialSlots); + return Span(); +} + MaterialBase* SplineModel::GetMaterial(int32 entryIndex) { if (Model) diff --git a/Source/Engine/Level/Actors/SplineModel.h b/Source/Engine/Level/Actors/SplineModel.h index 87f39699c..2a3d45a27 100644 --- a/Source/Engine/Level/Actors/SplineModel.h +++ b/Source/Engine/Level/Actors/SplineModel.h @@ -115,6 +115,7 @@ public: void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; void OnParentChanged() override; + const Span GetMaterialSlots() const override; MaterialBase* GetMaterial(int32 entryIndex) override; protected: diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index 778f24d65..93eef3056 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -535,6 +535,14 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod } } +const Span StaticModel::GetMaterialSlots() const +{ + const auto model = Model.Get(); + if (model && !model->WaitForLoaded()) + return ToSpan(model->MaterialSlots); + return Span(); +} + MaterialBase* StaticModel::GetMaterial(int32 entryIndex) { if (Model) diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h index 06fbdd874..600f174c9 100644 --- a/Source/Engine/Level/Actors/StaticModel.h +++ b/Source/Engine/Level/Actors/StaticModel.h @@ -166,6 +166,7 @@ public: bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + const Span GetMaterialSlots() const override; MaterialBase* GetMaterial(int32 entryIndex) override; bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override; bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override; From 8f8c0e4b819cc70de89ad20a6f04977995a2d765 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 10:55:15 +0200 Subject: [PATCH 10/11] Minor fixes for #1247 --- .../CustomEditors/Editors/AssetRefEditor.cs | 1 + .../Editors/ModelInstanceEntryEditor.cs | 111 ++++++------------ 2 files changed, 36 insertions(+), 76 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index b83b44d59..88f916ae4 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -37,6 +37,7 @@ namespace FlaxEditor.CustomEditors.Editors /// The asset picker used to get a reference to an asset. /// public AssetPicker Picker; + private ScriptType _valueType; /// diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index b7989cae3..1f04115de 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -16,7 +16,6 @@ namespace FlaxEditor.CustomEditors.Editors private GroupElement _group; private bool _updateName; private MaterialBase _material; - private ModelInstanceEntry _entry; /// public override void Initialize(LayoutElementsContainer layout) @@ -24,82 +23,59 @@ namespace FlaxEditor.CustomEditors.Editors _updateName = true; var group = layout.Group("Entry"); _group = group; - - _entry = (ModelInstanceEntry)Values[0]; + + if (ParentEditor == null) + return; + var entry = (ModelInstanceEntry)Values[0]; var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this); - var materiaLabel = new PropertyNameLabel("Material"); - materiaLabel.TooltipText = "The mesh surface material used for the rendering."; - if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel) + var materialLabel = new PropertyNameLabel("Material"); + materialLabel.TooltipText = "The mesh surface material used for the rendering."; + if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance) { // Ensure that entry with default material set is set back to null - if (_entry.Material == staticModel.Model.MaterialSlots[entryIndex].Material) + var defaultValue = GPUDevice.Instance.DefaultMaterial; { - staticModel.SetMaterial(entryIndex, null); + var slots = modelInstance.MaterialSlots; + if (entry.Material == slots[entryIndex].Material) + { + modelInstance.SetMaterial(entryIndex, null); + } + _material = modelInstance.GetMaterial(entryIndex); + if (slots[entryIndex].Material) + { + defaultValue = slots[entryIndex].Material; + } } - _material = staticModel.GetMaterial(entryIndex); var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); - var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; - materialEditor.Values.SetDefaultValue((staticModel.Model.MaterialSlots[entryIndex].Material) ? staticModel.Model.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); + var materialEditor = (AssetRefEditor)_group.Property(materialLabel, matContainer); + materialEditor.Values.SetDefaultValue(defaultValue); materialEditor.RefreshDefaultValue(); materialEditor.Picker.SelectedItemChanged += () => { + var slots = modelInstance.MaterialSlots; var material = materialEditor.Picker.SelectedAsset as MaterialBase; + var defaultMaterial = GPUDevice.Instance.DefaultMaterial; if (!material) { - staticModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); - materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; + modelInstance.SetMaterial(entryIndex, defaultMaterial); + materialEditor.Picker.SelectedAsset = defaultMaterial; } - else if (material == staticModel.Model.MaterialSlots[entryIndex].Material) + else if (material == slots[entryIndex].Material) { - staticModel.SetMaterial(entryIndex, null); + modelInstance.SetMaterial(entryIndex, null); } - else if (material == GPUDevice.Instance.DefaultMaterial && !staticModel.Model.MaterialSlots[entryIndex].Material) + else if (material == defaultMaterial && !slots[entryIndex].Material) { - staticModel.SetMaterial(entryIndex, null); + modelInstance.SetMaterial(entryIndex, null); } else { - staticModel.SetMaterial(entryIndex, material); + modelInstance.SetMaterial(entryIndex, material); } }; } - else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel) - { - // Ensure that entry with default material set is set back to null - if (_entry.Material == animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) - { - animatedModel.SetMaterial(entryIndex, null); - } - _material = animatedModel.GetMaterial(entryIndex); - - var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); - var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; - materialEditor.Values.SetDefaultValue((animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) ? animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); - materialEditor.RefreshDefaultValue(); - materialEditor.Picker.SelectedItemChanged += () => - { - var material = materialEditor.Picker.SelectedAsset as MaterialBase; - if (!material) - { - animatedModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); - materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; - } - else if (material == animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) - { - animatedModel.SetMaterial(entryIndex, null); - } - else if (material == GPUDevice.Instance.DefaultMaterial && !animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) - { - animatedModel.SetMaterial(entryIndex, null); - } - else - { - animatedModel.SetMaterial(entryIndex, material); - } - }; - } - + base.Initialize(group); } @@ -121,30 +97,13 @@ namespace FlaxEditor.CustomEditors.Editors ParentEditor.ParentEditor.Values.Count > 0) { var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this); - if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel) + if (ParentEditor.ParentEditor.Values[0] is ModelInstanceActor modelInstance) { - var model = staticModel.Model; - if (model && model.IsLoaded) + var slots = modelInstance.MaterialSlots; + if (slots != null && slots.Length > entryIndex) { - var slots = model.MaterialSlots; - if (slots != null && slots.Length > entryIndex) - { - _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; - _updateName = false; - } - } - } - else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel) - { - var model = animatedModel.SkinnedModel; - if (model && model.IsLoaded) - { - var slots = model.MaterialSlots; - if (slots != null && slots.Length > entryIndex) - { - _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; - _updateName = false; - } + _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; + _updateName = false; } } } From 60c0995bc36a14f7d30f79ebc118f1a93e555122 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 11:46:15 +0200 Subject: [PATCH 11/11] Add undo for #1247 --- .../CustomEditors/Editors/AssetRefEditor.cs | 5 + .../Editors/ModelInstanceEntryEditor.cs | 113 ++++++++++++------ 2 files changed, 79 insertions(+), 39 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 88f916ae4..e89c80372 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -38,6 +38,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public AssetPicker Picker; + private bool _isRefreshing; private ScriptType _valueType; /// @@ -89,6 +90,8 @@ namespace FlaxEditor.CustomEditors.Editors private void OnSelectedItemChanged() { + if (_isRefreshing) + return; if (typeof(AssetItem).IsAssignableFrom(_valueType.Type)) SetValue(Picker.SelectedItem); else if (_valueType.Type == typeof(Guid)) @@ -108,6 +111,7 @@ namespace FlaxEditor.CustomEditors.Editors if (!HasDifferentValues) { + _isRefreshing = true; if (Values[0] is AssetItem assetItem) Picker.SelectedItem = assetItem; else if (Values[0] is Guid guid) @@ -118,6 +122,7 @@ namespace FlaxEditor.CustomEditors.Editors Picker.SelectedPath = path; else Picker.SelectedAsset = Values[0] as Asset; + _isRefreshing = false; } } } diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 1f04115de..277f9ecb0 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -15,7 +15,11 @@ namespace FlaxEditor.CustomEditors.Editors { private GroupElement _group; private bool _updateName; + private int _entryIndex; + private bool _isRefreshing; private MaterialBase _material; + private ModelInstanceActor _modelInstance; + private AssetRefEditor _materialEditor; /// public override void Initialize(LayoutElementsContainer layout) @@ -32,58 +36,75 @@ namespace FlaxEditor.CustomEditors.Editors materialLabel.TooltipText = "The mesh surface material used for the rendering."; if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance) { - // Ensure that entry with default material set is set back to null - var defaultValue = GPUDevice.Instance.DefaultMaterial; + _entryIndex = entryIndex; + _modelInstance = modelInstance; + var slots = modelInstance.MaterialSlots; + if (entry.Material == slots[entryIndex].Material) { - var slots = modelInstance.MaterialSlots; - if (entry.Material == slots[entryIndex].Material) - { - modelInstance.SetMaterial(entryIndex, null); - } - _material = modelInstance.GetMaterial(entryIndex); - if (slots[entryIndex].Material) - { - defaultValue = slots[entryIndex].Material; - } + // Ensure that entry with default material set is set back to null + modelInstance.SetMaterial(entryIndex, null); + } + _material = modelInstance.GetMaterial(entryIndex); + var defaultValue = GPUDevice.Instance.DefaultMaterial; + if (slots[entryIndex].Material) + { + // Use default value set on asset (eg. Model Asset) + defaultValue = slots[entryIndex].Material; } - var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); - var materialEditor = (AssetRefEditor)_group.Property(materialLabel, matContainer); + // Create material picker + var materialValue = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); + var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue); materialEditor.Values.SetDefaultValue(defaultValue); materialEditor.RefreshDefaultValue(); - materialEditor.Picker.SelectedItemChanged += () => - { - var slots = modelInstance.MaterialSlots; - var material = materialEditor.Picker.SelectedAsset as MaterialBase; - var defaultMaterial = GPUDevice.Instance.DefaultMaterial; - if (!material) - { - modelInstance.SetMaterial(entryIndex, defaultMaterial); - materialEditor.Picker.SelectedAsset = defaultMaterial; - } - else if (material == slots[entryIndex].Material) - { - modelInstance.SetMaterial(entryIndex, null); - } - else if (material == defaultMaterial && !slots[entryIndex].Material) - { - modelInstance.SetMaterial(entryIndex, null); - } - else - { - modelInstance.SetMaterial(entryIndex, material); - } - }; + materialEditor.Picker.SelectedItemChanged += OnSelectedMaterialChanged; + _materialEditor = materialEditor; } base.Initialize(group); } + private void OnSelectedMaterialChanged() + { + if (_isRefreshing) + return; + _isRefreshing = true; + var slots = _modelInstance.MaterialSlots; + var material = _materialEditor.Picker.SelectedAsset as MaterialBase; + var defaultMaterial = GPUDevice.Instance.DefaultMaterial; + var value = (ModelInstanceEntry)Values[0]; + var prevMaterial = value.Material; + if (!material) + { + // Fallback to default material + _materialEditor.Picker.SelectedAsset = defaultMaterial; + value.Material = defaultMaterial; + } + else if (material == slots[_entryIndex].Material) + { + // Asset default material + value.Material = null; + } + else if (material == defaultMaterial && !slots[_entryIndex].Material) + { + // Default material while asset has no set as well + value.Material = null; + } + else + { + // Custom material + value.Material = material; + } + if (prevMaterial != value.Material) + SetValue(value); + _isRefreshing = false; + } + /// protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item) { // Skip material member as it is overridden - if (item.Info.Name == "Material") + if (item.Info.Name == "Material" && _materialEditor != null) return; base.SpawnProperty(itemLayout, itemValues, item); } @@ -91,6 +112,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Refresh() { + // Update panel title to match material slot name if (_updateName && _group != null && ParentEditor?.ParentEditor != null && @@ -102,13 +124,26 @@ namespace FlaxEditor.CustomEditors.Editors var slots = modelInstance.MaterialSlots; if (slots != null && slots.Length > entryIndex) { - _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; _updateName = false; + _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; } } } + // Refresh currently selected material + _material = _modelInstance.GetMaterial(_entryIndex); + base.Refresh(); } + + /// + protected override void Deinitialize() + { + _material = null; + _modelInstance = null; + _materialEditor = null; + + base.Deinitialize(); + } } }