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..b7989cae3 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,94 @@ 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) ? staticModel.Model.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); + 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].Material) + { + staticModel.SetMaterial(entryIndex, null); + } + else if (material == GPUDevice.Instance.DefaultMaterial && !staticModel.Model.MaterialSlots[entryIndex].Material) + { + 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) ? 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); } + /// + 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).