Merge model and skinned model windows code into shared base class

This commit is contained in:
Wojtek Figat
2025-01-12 01:04:56 +01:00
parent 1b97e49ed9
commit 506efb7538
10 changed files with 701 additions and 1016 deletions

View File

@@ -1,11 +1,15 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.Reflection;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Tabs;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Utilities;
@@ -93,6 +97,306 @@ namespace FlaxEditor.Windows.Assets
}
}
protected class MeshesPropertiesProxyBase : PropertiesProxyBase
{
private readonly List<ComboBox> _materialSlotComboBoxes = new List<ComboBox>();
private readonly List<CheckBox> _isolateCheckBoxes = new List<CheckBox>();
private readonly List<CheckBox> _highlightCheckBoxes = new List<CheckBox>();
public override void OnLoad(TWindow window)
{
base.OnLoad(window);
Window._isolateIndex = -1;
Window._highlightIndex = -1;
}
public override void OnClean()
{
Window._isolateIndex = -1;
Window._highlightIndex = -1;
base.OnClean();
}
/// <summary>
/// Updates the highlight/isolate effects on UI.
/// </summary>
public void UpdateEffectsOnUI()
{
Window._skipEffectsGuiEvents = true;
for (int i = 0; i < _isolateCheckBoxes.Count; i++)
{
var checkBox = _isolateCheckBoxes[i];
checkBox.Checked = Window._isolateIndex == ((MeshBase)checkBox.Tag).MaterialSlotIndex;
}
for (int i = 0; i < _highlightCheckBoxes.Count; i++)
{
var checkBox = _highlightCheckBoxes[i];
checkBox.Checked = Window._highlightIndex == ((MeshBase)checkBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
/// <summary>
/// Updates the material slots UI parts. Should be called after material slot rename.
/// </summary>
public void UpdateMaterialSlotsUI()
{
Window._skipEffectsGuiEvents = true;
// Generate material slots labels (with index prefix)
var slots = Asset.MaterialSlots;
var slotsLabels = new string[slots.Length];
for (int i = 0; i < slots.Length; i++)
{
slotsLabels[i] = string.Format("[{0}] {1}", i, slots[i].Name);
}
// Update comboboxes
for (int i = 0; i < _materialSlotComboBoxes.Count; i++)
{
var comboBox = _materialSlotComboBoxes[i];
comboBox.SetItems(slotsLabels);
comboBox.SelectedIndex = ((MeshBase)comboBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
/// <summary>
/// Sets the material slot index to the mesh.
/// </summary>
/// <param name="mesh">The mesh.</param>
/// <param name="newSlotIndex">New index of the material slot to use.</param>
public void SetMaterialSlot(MeshBase mesh, int newSlotIndex)
{
if (Window._skipEffectsGuiEvents)
return;
mesh.MaterialSlotIndex = newSlotIndex == -1 ? 0 : newSlotIndex;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
Window.MarkAsEdited();
}
/// <summary>
/// Sets the material slot to isolate.
/// </summary>
/// <param name="mesh">The mesh.</param>
public void SetIsolate(MeshBase mesh)
{
if (Window._skipEffectsGuiEvents)
return;
Window._isolateIndex = mesh != null ? mesh.MaterialSlotIndex : -1;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
}
/// <summary>
/// Sets the material slot index to highlight.
/// </summary>
/// <param name="mesh">The mesh.</param>
public void SetHighlight(MeshBase mesh)
{
if (Window._skipEffectsGuiEvents)
return;
Window._highlightIndex = mesh != null ? mesh.MaterialSlotIndex : -1;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
}
protected virtual void OnGeneral(LayoutElementsContainer layout)
{
}
protected class ProxyEditor : ProxyEditorBase
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (MeshesPropertiesProxyBase)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
proxy._materialSlotComboBoxes.Clear();
proxy._isolateCheckBoxes.Clear();
proxy._highlightCheckBoxes.Clear();
var countLODs = proxy.Asset.LODsCount;
var loadedLODs = proxy.Asset.LoadedLODs;
// General properties
{
var group = layout.Group("General");
var minScreenSize = group.FloatValue("Min Screen Size", "The minimum screen size to draw model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.");
minScreenSize.ValueBox.MinValue = 0.0f;
minScreenSize.ValueBox.MaxValue = 1.0f;
minScreenSize.ValueBox.Value = proxy.Asset.MinScreenSize;
minScreenSize.ValueBox.ValueChanged += () =>
{
proxy.Asset.MinScreenSize = minScreenSize.ValueBox.Value;
proxy.Window.MarkAsEdited();
};
}
proxy.OnGeneral(layout);
// Group per LOD
for (int lodIndex = 0; lodIndex < countLODs; lodIndex++)
{
var group = layout.Group("LOD " + lodIndex);
if (lodIndex < countLODs - loadedLODs)
{
group.Label("Loading LOD...");
continue;
}
var lod = proxy.Asset.GetLOD(lodIndex);
proxy.Asset.GetMeshes(out var meshes, lodIndex);
int triangleCount = 0, vertexCount = 0;
for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
{
var mesh = meshes[meshIndex];
triangleCount += mesh.TriangleCount;
vertexCount += mesh.VertexCount;
}
group.Label(string.Format("Triangles: {0:N0} Vertices: {1:N0}", triangleCount, vertexCount)).AddCopyContextMenu();
group.Label("Size: " + lod.Box.Size).AddCopyContextMenu();
var screenSize = group.FloatValue("Screen Size", "The screen size to switch LODs. Bottom limit of the model screen size to render this LOD.");
screenSize.ValueBox.MinValue = 0.0f;
screenSize.ValueBox.MaxValue = 10.0f;
screenSize.ValueBox.Value = lod.ScreenSize;
screenSize.ValueBox.ValueChanged += () =>
{
lod.ScreenSize = screenSize.ValueBox.Value;
proxy.Window.MarkAsEdited();
};
// Every mesh properties
for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
{
var mesh = meshes[meshIndex];
group.Label($"Mesh {meshIndex} (tris: {mesh.TriangleCount:N0}, vert: {mesh.VertexCount:N0})").AddCopyContextMenu();
// Material Slot
var materialSlot = group.ComboBox("Material Slot", "Material slot used by this mesh during rendering");
materialSlot.ComboBox.Tag = mesh;
materialSlot.ComboBox.SelectedIndexChanged += comboBox => proxy.SetMaterialSlot((MeshBase)comboBox.Tag, comboBox.SelectedIndex);
proxy._materialSlotComboBoxes.Add(materialSlot.ComboBox);
// Isolate
var isolate = group.Checkbox("Isolate", "Shows only this mesh (and meshes using the same material slot)");
isolate.CheckBox.Tag = mesh;
isolate.CheckBox.StateChanged += (box) => proxy.SetIsolate(box.Checked ? (MeshBase)box.Tag : null);
proxy._isolateCheckBoxes.Add(isolate.CheckBox);
// Highlight
var highlight = group.Checkbox("Highlight", "Highlights this mesh with a tint color (and meshes using the same material slot)");
highlight.CheckBox.Tag = mesh;
highlight.CheckBox.StateChanged += (box) => proxy.SetHighlight(box.Checked ? (MeshBase)box.Tag : null);
proxy._highlightCheckBoxes.Add(highlight.CheckBox);
}
}
// Refresh UI
proxy.UpdateMaterialSlotsUI();
}
}
}
protected class MaterialsPropertiesProxyBase : PropertiesProxyBase
{
[Collection(CanReorderItems = true, NotNullItems = true, OverrideEditorTypeName = "FlaxEditor.CustomEditors.Editors.GenericEditor", Spacing = 10)]
[EditorOrder(10), EditorDisplay("Materials", EditorDisplayAttribute.InlineStyle)]
public MaterialSlot[] MaterialSlots
{
get => Asset != null ? Asset.MaterialSlots : null;
set
{
if (Asset != null)
{
if (Asset.MaterialSlots.Length != value.Length)
{
MaterialBase[] materials = new MaterialBase[value.Length];
string[] names = new string[value.Length];
ShadowsCastingMode[] shadowsModes = new ShadowsCastingMode[value.Length];
for (int i = 0; i < value.Length; i++)
{
if (value[i] != null)
{
materials[i] = value[i].Material;
names[i] = value[i].Name;
shadowsModes[i] = value[i].ShadowsMode;
}
else
{
materials[i] = null;
names[i] = "Material " + i;
shadowsModes[i] = ShadowsCastingMode.All;
}
}
Asset.SetupMaterialSlots(value.Length);
var slots = Asset.MaterialSlots;
for (int i = 0; i < slots.Length; i++)
{
slots[i].Material = materials[i];
slots[i].Name = names[i];
slots[i].ShadowsMode = shadowsModes[i];
}
UpdateMaterialSlotsUI();
}
}
}
}
private readonly List<ComboBox> _materialSlotComboBoxes = new List<ComboBox>();
/// <summary>
/// Updates the material slots UI parts. Should be called after material slot rename.
/// </summary>
public void UpdateMaterialSlotsUI()
{
Window._skipEffectsGuiEvents = true;
// Generate material slots labels (with index prefix)
var slots = Asset.MaterialSlots;
var slotsLabels = new string[slots.Length];
for (int i = 0; i < slots.Length; i++)
{
slotsLabels[i] = string.Format("[{0}] {1}", i, slots[i].Name);
}
// Update comboboxes
for (int i = 0; i < _materialSlotComboBoxes.Count; i++)
{
var comboBox = _materialSlotComboBoxes[i];
comboBox.SetItems(slotsLabels);
comboBox.SelectedIndex = ((Mesh)comboBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
protected class ProxyEditor : ProxyEditorBase
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (MaterialsPropertiesProxyBase)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
base.Initialize(layout);
}
}
}
protected class UVsPropertiesProxyBase : PropertiesProxyBase
{
public enum UVChannel
@@ -375,10 +679,52 @@ namespace FlaxEditor.Windows.Assets
}
}
protected class ImportPropertiesProxyBase : PropertiesProxyBase
{
private ModelImportSettings ImportSettings;
/// <inheritdoc />
public override void OnLoad(TWindow window)
{
base.OnLoad(window);
ImportSettings = window._importSettings;
}
public void Reimport()
{
Editor.Instance.ContentImporting.Reimport((BinaryAssetItem)Window.Item, ImportSettings, true);
}
protected class ProxyEditor : ProxyEditorBase
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (ImportPropertiesProxyBase)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
// Import Settings
{
var group = layout.Group("Import Settings");
var importSettingsField = typeof(ImportPropertiesProxyBase).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance);
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
layout.Space(5);
var reimportButton = group.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxyBase)Values[0]).Reimport();
}
}
}
}
protected readonly SplitPanel _split;
protected readonly Tabs _tabs;
protected readonly ToolStripButton _saveButton;
protected ModelImportSettings _importSettings = new ModelImportSettings();
protected bool _refreshOnLODsLoaded;
protected bool _skipEffectsGuiEvents;
protected int _isolateIndex = -1;
@@ -416,6 +762,11 @@ namespace FlaxEditor.Windows.Assets
};
}
/// <summary>
/// Updates the highlight/isolate effects on a model asset.
/// </summary>
protected abstract void UpdateEffectsOnAsset();
/// <inheritdoc />
protected override void UpdateToolstrip()
{
@@ -430,8 +781,8 @@ namespace FlaxEditor.Windows.Assets
_meshData?.WaitForMeshDataRequestEnd();
foreach (var child in _tabs.Children)
{
if (child is Tab tab && tab.Proxy.Window != null)
tab.Proxy.OnClean();
if (child is Tab tab && tab.Proxy?.Window != null)
tab.Proxy?.OnClean();
}
base.UnlinkItem();
@@ -440,11 +791,14 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
protected override void OnAssetLoaded()
{
_refreshOnLODsLoaded = true;
Editor.TryRestoreImportOptions(ref _importSettings.Settings, Item.Path);
UpdateEffectsOnAsset();
foreach (var child in _tabs.Children)
{
if (child is Tab tab)
{
tab.Proxy.OnLoad((TWindow)this);
tab.Proxy?.OnLoad((TWindow)this);
tab.Presenter.BuildLayout();
}
}

View File

@@ -1,14 +1,11 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
using FlaxEditor.CustomEditors;
using FlaxEditor.GUI;
using FlaxEditor.Scripting;
using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
@@ -55,386 +52,113 @@ namespace FlaxEditor.Windows.Assets
}
}
[CustomEditor(typeof(ProxyEditor))]
private sealed class MeshesPropertiesProxy : PropertiesProxyBase
[CustomEditor(typeof(MeshesPropertiesProxy.ProxyEditor))]
private sealed class MeshesPropertiesProxy : MeshesPropertiesProxyBase
{
private readonly List<ComboBox> _materialSlotComboBoxes = new List<ComboBox>();
private readonly List<CheckBox> _isolateCheckBoxes = new List<CheckBox>();
private readonly List<CheckBox> _highlightCheckBoxes = new List<CheckBox>();
private CustomEditors.Elements.IntegerValueElement _sdfModelLodIndex;
private CustomEditorPresenter _presenter;
public override void OnLoad(ModelWindow window)
protected override void OnGeneral(LayoutElementsContainer layout)
{
base.OnLoad(window);
base.OnGeneral(layout);
Window._isolateIndex = -1;
Window._highlightIndex = -1;
}
_presenter = layout.Presenter;
public override void OnClean()
{
Window._isolateIndex = -1;
Window._highlightIndex = -1;
base.OnClean();
}
private void UpdateEffectsOnUI()
{
Window._skipEffectsGuiEvents = true;
for (int i = 0; i < _isolateCheckBoxes.Count; i++)
// SDF
{
var checkBox = _isolateCheckBoxes[i];
checkBox.Checked = Window._isolateIndex == ((Mesh)checkBox.Tag).MaterialSlotIndex;
}
var group = layout.Group("SDF");
var sdfOptions = Window._sdfOptions;
for (int i = 0; i < _highlightCheckBoxes.Count; i++)
{
var checkBox = _highlightCheckBoxes[i];
checkBox.Checked = Window._highlightIndex == ((Mesh)checkBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
private void UpdateMaterialSlotsUI()
{
Window._skipEffectsGuiEvents = true;
// Generate material slots labels (with index prefix)
var slots = Asset.MaterialSlots;
var slotsLabels = new string[slots.Length];
for (int i = 0; i < slots.Length; i++)
{
slotsLabels[i] = string.Format("[{0}] {1}", i, slots[i].Name);
}
// Update comboboxes
for (int i = 0; i < _materialSlotComboBoxes.Count; i++)
{
var comboBox = _materialSlotComboBoxes[i];
comboBox.SetItems(slotsLabels);
comboBox.SelectedIndex = ((Mesh)comboBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
private void SetMaterialSlot(Mesh mesh, int newSlotIndex)
{
if (Window._skipEffectsGuiEvents)
return;
mesh.MaterialSlotIndex = newSlotIndex == -1 ? 0 : newSlotIndex;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
Window.MarkAsEdited();
}
private void SetIsolate(Mesh mesh)
{
if (Window._skipEffectsGuiEvents)
return;
Window._isolateIndex = mesh != null ? mesh.MaterialSlotIndex : -1;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
}
private void SetHighlight(Mesh mesh)
{
if (Window._skipEffectsGuiEvents)
return;
Window._highlightIndex = mesh != null ? mesh.MaterialSlotIndex : -1;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
}
private class ProxyEditor : ProxyEditorBase
{
private CustomEditors.Elements.IntegerValueElement _sdfModelLodIndex;
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (MeshesPropertiesProxy)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
proxy._materialSlotComboBoxes.Clear();
proxy._isolateCheckBoxes.Clear();
proxy._highlightCheckBoxes.Clear();
var lods = proxy.Asset.LODs;
var loadedLODs = proxy.Asset.LoadedLODs;
// General properties
var sdf = Asset.SDF;
if (sdf.Texture != null)
{
var group = layout.Group("General");
var minScreenSize = group.FloatValue("Min Screen Size", "The minimum screen size to draw model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.");
minScreenSize.ValueBox.MinValue = 0.0f;
minScreenSize.ValueBox.MaxValue = 1.0f;
minScreenSize.ValueBox.Value = proxy.Asset.MinScreenSize;
minScreenSize.ValueBox.BoxValueChanged += b =>
{
proxy.Asset.MinScreenSize = b.Value;
proxy.Window.MarkAsEdited();
};
var size = sdf.Texture.Size3;
group.Label($"SDF Texture {size.X}x{size.Y}x{size.Z} ({Utilities.Utils.FormatBytesCount(sdf.Texture.MemoryUsage)})").AddCopyContextMenu();
}
else
{
group.Label("No SDF");
}
// SDF
var resolution = group.FloatValue("Resolution Scale", Window.Editor.CodeDocs.GetTooltip(typeof(ModelTool.Options), nameof(ModelImportSettings.Settings.SDFResolution)));
resolution.ValueBox.MinValue = 0.0001f;
resolution.ValueBox.MaxValue = 100.0f;
resolution.ValueBox.Value = sdf.Texture != null ? sdf.ResolutionScale : 1.0f;
resolution.ValueBox.BoxValueChanged += b => { Window._importSettings.Settings.SDFResolution = b.Value; };
Window._importSettings.Settings.SDFResolution = sdf.ResolutionScale;
var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage).");
gpu.CheckBox.Checked = sdfOptions.GPU;
var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh.");
var backfacesThreshold = backfacesThresholdProp.FloatValue();
var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last();
backfacesThreshold.ValueBox.MinValue = 0.001f;
backfacesThreshold.ValueBox.MaxValue = 1.0f;
backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold;
backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; };
// Toggle Backfaces Threshold visibility (CPU-only option)
gpu.CheckBox.StateChanged += c =>
{
var group = layout.Group("SDF");
var sdfOptions = proxy.Window._sdfOptions;
Window._sdfOptions.GPU = c.Checked;
backfacesThresholdLabel.Visible = !c.Checked;
backfacesThreshold.ValueBox.Visible = !c.Checked;
};
backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked;
backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked;
var sdf = proxy.Asset.SDF;
if (sdf.Texture != null)
{
var size = sdf.Texture.Size3;
group.Label($"SDF Texture {size.X}x{size.Y}x{size.Z} ({Utilities.Utils.FormatBytesCount(sdf.Texture.MemoryUsage)})").AddCopyContextMenu();
}
else
{
group.Label("No SDF");
}
var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building.");
lodIndex.IntValue.MinValue = 0;
lodIndex.IntValue.MaxValue = Asset.LODsCount - 1;
lodIndex.IntValue.Value = sdf.Texture != null ? sdf.LOD : 6;
_sdfModelLodIndex = lodIndex;
var resolution = group.FloatValue("Resolution Scale", proxy.Window.Editor.CodeDocs.GetTooltip(typeof(ModelTool.Options), nameof(ModelImportSettings.Settings.SDFResolution)));
resolution.ValueBox.MinValue = 0.0001f;
resolution.ValueBox.MaxValue = 100.0f;
resolution.ValueBox.Value = sdf.Texture != null ? sdf.ResolutionScale : 1.0f;
resolution.ValueBox.BoxValueChanged += b => { proxy.Window._importSettings.Settings.SDFResolution = b.Value; };
proxy.Window._importSettings.Settings.SDFResolution = sdf.ResolutionScale;
var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage).");
gpu.CheckBox.Checked = sdfOptions.GPU;
var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh.");
var backfacesThreshold = backfacesThresholdProp.FloatValue();
var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last();
backfacesThreshold.ValueBox.MinValue = 0.001f;
backfacesThreshold.ValueBox.MaxValue = 1.0f;
backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold;
backfacesThreshold.ValueBox.BoxValueChanged += b => { proxy.Window._sdfOptions.BackfacesThreshold = b.Value; };
// Toggle Backfaces Threshold visibility (CPU-only option)
gpu.CheckBox.StateChanged += c =>
{
proxy.Window._sdfOptions.GPU = c.Checked;
backfacesThresholdLabel.Visible = !c.Checked;
backfacesThreshold.ValueBox.Visible = !c.Checked;
};
backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked;
backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked;
var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building.");
lodIndex.IntValue.MinValue = 0;
lodIndex.IntValue.MaxValue = lods.Length - 1;
lodIndex.IntValue.Value = sdf.Texture != null ? sdf.LOD : 6;
_sdfModelLodIndex = lodIndex;
var buttons = group.CustomContainer<UniformGridPanel>();
var gridControl = buttons.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
gridControl.SlotsHorizontally = 2;
gridControl.SlotsVertically = 1;
var rebuildButton = buttons.Button("Rebuild", "Rebuilds the model SDF.").Button;
rebuildButton.Clicked += OnRebuildSDF;
var removeButton = buttons.Button("Remove", "Removes the model SDF data from the asset.").Button;
removeButton.Enabled = sdf.Texture != null;
removeButton.Clicked += OnRemoveSDF;
}
// Group per LOD
for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
{
var group = layout.Group("LOD " + lodIndex);
if (lodIndex < lods.Length - loadedLODs)
{
group.Label("Loading LOD...");
continue;
}
var lod = lods[lodIndex];
var meshes = lod.Meshes;
int triangleCount = 0, vertexCount = 0;
for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
{
var mesh = meshes[meshIndex];
triangleCount += mesh.TriangleCount;
vertexCount += mesh.VertexCount;
}
group.Label(string.Format("Triangles: {0:N0} Vertices: {1:N0}", triangleCount, vertexCount)).AddCopyContextMenu();
group.Label("Size: " + lod.Box.Size).AddCopyContextMenu();
var screenSize = group.FloatValue("Screen Size", "The screen size to switch LODs. Bottom limit of the model screen size to render this LOD.");
screenSize.ValueBox.MinValue = 0.0f;
screenSize.ValueBox.MaxValue = 10.0f;
screenSize.ValueBox.Value = lod.ScreenSize;
screenSize.ValueBox.BoxValueChanged += b =>
{
lod.ScreenSize = b.Value;
proxy.Window.MarkAsEdited();
};
// Every mesh properties
for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
{
var mesh = meshes[meshIndex];
group.Label($"Mesh {meshIndex} (tris: {mesh.TriangleCount:N0}, verts: {mesh.VertexCount:N0})").AddCopyContextMenu();
// Material Slot
var materialSlot = group.ComboBox("Material Slot", "Material slot used by this mesh during rendering");
materialSlot.ComboBox.Tag = mesh;
materialSlot.ComboBox.SelectedIndexChanged += comboBox => proxy.SetMaterialSlot((Mesh)comboBox.Tag, comboBox.SelectedIndex);
proxy._materialSlotComboBoxes.Add(materialSlot.ComboBox);
// Isolate
var isolate = group.Checkbox("Isolate", "Shows only this mesh (and meshes using the same material slot)");
isolate.CheckBox.Tag = mesh;
isolate.CheckBox.StateChanged += (box) => proxy.SetIsolate(box.Checked ? (Mesh)box.Tag : null);
proxy._isolateCheckBoxes.Add(isolate.CheckBox);
// Highlight
var highlight = group.Checkbox("Highlight", "Highlights this mesh with a tint color (and meshes using the same material slot)");
highlight.CheckBox.Tag = mesh;
highlight.CheckBox.StateChanged += (box) => proxy.SetHighlight(box.Checked ? (Mesh)box.Tag : null);
proxy._highlightCheckBoxes.Add(highlight.CheckBox);
}
}
// Refresh UI
proxy.UpdateMaterialSlotsUI();
var buttons = group.CustomContainer<UniformGridPanel>();
var gridControl = buttons.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
gridControl.SlotsHorizontally = 2;
gridControl.SlotsVertically = 1;
var rebuildButton = buttons.Button("Rebuild", "Rebuilds the model SDF.").Button;
rebuildButton.Clicked += OnRebuildSDF;
var removeButton = buttons.Button("Remove", "Removes the model SDF data from the asset.").Button;
removeButton.Enabled = sdf.Texture != null;
removeButton.Clicked += OnRemoveSDF;
}
}
private void OnRebuildSDF()
private void OnRebuildSDF()
{
Window.Enabled = false;
Task.Run(() =>
{
var proxy = (MeshesPropertiesProxy)Values[0];
proxy.Window.Enabled = false;
Task.Run(() =>
var sdfOptions = Window._sdfOptions;
bool failed = Asset.GenerateSDF(Window._importSettings.Settings.SDFResolution, _sdfModelLodIndex.Value, true, sdfOptions.BackfacesThreshold, sdfOptions.GPU);
FlaxEngine.Scripting.InvokeOnUpdate(() =>
{
var sdfOptions = proxy.Window._sdfOptions;
bool failed = proxy.Asset.GenerateSDF(proxy.Window._importSettings.Settings.SDFResolution, _sdfModelLodIndex.Value, true, sdfOptions.BackfacesThreshold, sdfOptions.GPU);
FlaxEngine.Scripting.InvokeOnUpdate(() =>
{
proxy.Window.Enabled = true;
if (!failed)
proxy.Window.MarkAsEdited();
Presenter.BuildLayoutOnUpdate();
Window.Enabled = true;
if (!failed)
Window.MarkAsEdited();
_presenter.BuildLayoutOnUpdate();
// Save some SDF options locally in the project cache
proxy.Window.Editor.ProjectCache.SetCustomData(JsonSerializer.GetStringID(proxy.Window.Item.ID) + ".SDF", JsonSerializer.Serialize(sdfOptions));
});
// Save some SDF options locally in the project cache
Window.Editor.ProjectCache.SetCustomData(JsonSerializer.GetStringID(Window.Item.ID) + ".SDF", JsonSerializer.Serialize(sdfOptions));
});
}
});
}
private void OnRemoveSDF()
{
var proxy = (MeshesPropertiesProxy)Values[0];
proxy.Asset.SetSDF(new ModelBase.SDFData());
proxy.Window.MarkAsEdited();
Presenter.BuildLayoutOnUpdate();
}
internal override void RefreshInternal()
{
// Skip updates when model is not loaded
var proxy = (MeshesPropertiesProxy)Values[0];
if (proxy.Asset == null || !proxy.Asset.IsLoaded)
return;
base.RefreshInternal();
}
private void OnRemoveSDF()
{
Asset.SetSDF(new ModelBase.SDFData());
Window.MarkAsEdited();
_presenter.BuildLayoutOnUpdate();
}
}
[CustomEditor(typeof(ProxyEditor))]
private sealed class MaterialsPropertiesProxy : PropertiesProxyBase
private sealed class MaterialsPropertiesProxy : MaterialsPropertiesProxyBase
{
[Collection(CanReorderItems = true, NotNullItems = true, OverrideEditorTypeName = "FlaxEditor.CustomEditors.Editors.GenericEditor", Spacing = 10)]
[EditorOrder(10), EditorDisplay("Materials", EditorDisplayAttribute.InlineStyle)]
public MaterialSlot[] MaterialSlots
{
get => Asset != null ? Asset.MaterialSlots : null;
set
{
if (Asset != null)
{
if (Asset.MaterialSlots.Length != value.Length)
{
MaterialBase[] materials = new MaterialBase[value.Length];
string[] names = new string[value.Length];
ShadowsCastingMode[] shadowsModes = new ShadowsCastingMode[value.Length];
for (int i = 0; i < value.Length; i++)
{
if (value[i] != null)
{
materials[i] = value[i].Material;
names[i] = value[i].Name;
shadowsModes[i] = value[i].ShadowsMode;
}
else
{
materials[i] = null;
names[i] = "Material " + i;
shadowsModes[i] = ShadowsCastingMode.All;
}
}
Asset.SetupMaterialSlots(value.Length);
var slots = Asset.MaterialSlots;
for (int i = 0; i < slots.Length; i++)
{
slots[i].Material = materials[i];
slots[i].Name = names[i];
slots[i].ShadowsMode = shadowsModes[i];
}
UpdateMaterialSlotsUI();
}
}
}
}
private readonly List<ComboBox> _materialSlotComboBoxes = new List<ComboBox>();
/// <summary>
/// Updates the material slots UI parts. Should be called after material slot rename.
/// </summary>
public void UpdateMaterialSlotsUI()
{
Window._skipEffectsGuiEvents = true;
// Generate material slots labels (with index prefix)
var slots = Asset.MaterialSlots;
var slotsLabels = new string[slots.Length];
for (int i = 0; i < slots.Length; i++)
{
slotsLabels[i] = string.Format("[{0}] {1}", i, slots[i].Name);
}
// Update comboboxes
for (int i = 0; i < _materialSlotComboBoxes.Count; i++)
{
var comboBox = _materialSlotComboBoxes[i];
comboBox.SetItems(slotsLabels);
comboBox.SelectedIndex = ((Mesh)comboBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
private class ProxyEditor : ProxyEditorBase
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (MaterialsPropertiesProxy)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
base.Initialize(layout);
}
}
}
[CustomEditor(typeof(ProxyEditor))]
@@ -443,45 +167,8 @@ namespace FlaxEditor.Windows.Assets
}
[CustomEditor(typeof(ProxyEditor))]
private sealed class ImportPropertiesProxy : PropertiesProxyBase
private sealed class ImportPropertiesProxy : ImportPropertiesProxyBase
{
private ModelImportSettings ImportSettings;
/// <inheritdoc />
public override void OnLoad(ModelWindow window)
{
base.OnLoad(window);
ImportSettings = window._importSettings;
}
public void Reimport()
{
Editor.Instance.ContentImporting.Reimport((BinaryAssetItem)Window.Item, ImportSettings, true);
}
private class ProxyEditor : ProxyEditorBase
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (ImportPropertiesProxy)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
// Import Settings
{
var group = layout.Group("Import Settings");
var importSettingsField = typeof(ImportPropertiesProxy).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance);
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
layout.Space(5);
var reimportButton = group.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxy)Values[0]).Reimport();
}
}
}
}
private class MeshesTab : Tab
@@ -532,7 +219,6 @@ namespace FlaxEditor.Windows.Assets
private readonly ModelPreview _preview;
private StaticModel _highlightActor;
private ModelImportSettings _importSettings = new ModelImportSettings();
private ModelSdfOptions _sdfOptions;
private ToolStripButton _showCurrentLODButton;
@@ -579,10 +265,8 @@ namespace FlaxEditor.Windows.Assets
_preview.Task.AddCustomActor(_highlightActor);
}
/// <summary>
/// Updates the highlight/isolate effects on a model asset.
/// </summary>
private void UpdateEffectsOnAsset()
/// <inheritdoc />
protected override void UpdateEffectsOnAsset()
{
var entries = _preview.PreviewActor.Entries;
if (entries != null)
@@ -688,12 +372,7 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
protected override void OnAssetLoaded()
{
_refreshOnLODsLoaded = true;
_preview.ViewportCamera.SetArcBallView(Asset.GetBox());
Editor.TryRestoreImportOptions(ref _importSettings.Settings, Item.Path);
UpdateEffectsOnAsset();
// TODO: disable streaming for this model
base.OnAssetLoaded();
}

View File

@@ -4,9 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.CustomEditors.GUI;
@@ -54,220 +52,8 @@ namespace FlaxEditor.Windows.Assets
}
[CustomEditor(typeof(ProxyEditor))]
private sealed class MeshesPropertiesProxy : PropertiesProxyBase
private sealed class MeshesPropertiesProxy : MeshesPropertiesProxyBase
{
private readonly List<ComboBox> _materialSlotComboBoxes = new List<ComboBox>();
private readonly List<CheckBox> _isolateCheckBoxes = new List<CheckBox>();
private readonly List<CheckBox> _highlightCheckBoxes = new List<CheckBox>();
public override void OnLoad(SkinnedModelWindow window)
{
base.OnLoad(window);
Window._isolateIndex = -1;
Window._highlightIndex = -1;
}
public override void OnClean()
{
Window._isolateIndex = -1;
Window._highlightIndex = -1;
base.OnClean();
}
/// <summary>
/// Updates the highlight/isolate effects on UI.
/// </summary>
public void UpdateEffectsOnUI()
{
Window._skipEffectsGuiEvents = true;
for (int i = 0; i < _isolateCheckBoxes.Count; i++)
{
var checkBox = _isolateCheckBoxes[i];
checkBox.Checked = Window._isolateIndex == ((SkinnedMesh)checkBox.Tag).MaterialSlotIndex;
}
for (int i = 0; i < _highlightCheckBoxes.Count; i++)
{
var checkBox = _highlightCheckBoxes[i];
checkBox.Checked = Window._highlightIndex == ((SkinnedMesh)checkBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
/// <summary>
/// Updates the material slots UI parts. Should be called after material slot rename.
/// </summary>
public void UpdateMaterialSlotsUI()
{
Window._skipEffectsGuiEvents = true;
// Generate material slots labels (with index prefix)
var slots = Asset.MaterialSlots;
var slotsLabels = new string[slots.Length];
for (int i = 0; i < slots.Length; i++)
{
slotsLabels[i] = string.Format("[{0}] {1}", i, slots[i].Name);
}
// Update comboboxes
for (int i = 0; i < _materialSlotComboBoxes.Count; i++)
{
var comboBox = _materialSlotComboBoxes[i];
comboBox.SetItems(slotsLabels);
comboBox.SelectedIndex = ((SkinnedMesh)comboBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
/// <summary>
/// Sets the material slot index to the mesh.
/// </summary>
/// <param name="mesh">The mesh.</param>
/// <param name="newSlotIndex">New index of the material slot to use.</param>
public void SetMaterialSlot(SkinnedMesh mesh, int newSlotIndex)
{
if (Window._skipEffectsGuiEvents)
return;
mesh.MaterialSlotIndex = newSlotIndex == -1 ? 0 : newSlotIndex;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
Window.MarkAsEdited();
}
/// <summary>
/// Sets the material slot to isolate.
/// </summary>
/// <param name="mesh">The mesh.</param>
public void SetIsolate(SkinnedMesh mesh)
{
if (Window._skipEffectsGuiEvents)
return;
Window._isolateIndex = mesh != null ? mesh.MaterialSlotIndex : -1;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
}
/// <summary>
/// Sets the material slot index to highlight.
/// </summary>
/// <param name="mesh">The mesh.</param>
public void SetHighlight(SkinnedMesh mesh)
{
if (Window._skipEffectsGuiEvents)
return;
Window._highlightIndex = mesh != null ? mesh.MaterialSlotIndex : -1;
Window.UpdateEffectsOnAsset();
UpdateEffectsOnUI();
}
private class ProxyEditor : ProxyEditorBase
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (MeshesPropertiesProxy)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
proxy._materialSlotComboBoxes.Clear();
proxy._isolateCheckBoxes.Clear();
proxy._highlightCheckBoxes.Clear();
var lods = proxy.Asset.LODs;
var loadedLODs = proxy.Asset.LoadedLODs;
// General properties
{
var group = layout.Group("General");
var minScreenSize = group.FloatValue("Min Screen Size", "The minimum screen size to draw model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.");
minScreenSize.ValueBox.MinValue = 0.0f;
minScreenSize.ValueBox.MaxValue = 1.0f;
minScreenSize.ValueBox.Value = proxy.Asset.MinScreenSize;
minScreenSize.ValueBox.ValueChanged += () =>
{
proxy.Asset.MinScreenSize = minScreenSize.ValueBox.Value;
proxy.Window.MarkAsEdited();
};
}
// Group per LOD
for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
{
var group = layout.Group("LOD " + lodIndex);
if (lodIndex < lods.Length - loadedLODs)
{
group.Label("Loading LOD...");
continue;
}
var lod = lods[lodIndex];
var meshes = lod.Meshes;
int triangleCount = 0, vertexCount = 0;
for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
{
var mesh = meshes[meshIndex];
triangleCount += mesh.TriangleCount;
vertexCount += mesh.VertexCount;
}
group.Label(string.Format("Triangles: {0:N0} Vertices: {1:N0}", triangleCount, vertexCount)).AddCopyContextMenu();
group.Label("Size: " + lod.Box.Size);
var screenSize = group.FloatValue("Screen Size", "The screen size to switch LODs. Bottom limit of the model screen size to render this LOD.");
screenSize.ValueBox.MinValue = 0.0f;
screenSize.ValueBox.MaxValue = 10.0f;
screenSize.ValueBox.Value = lod.ScreenSize;
screenSize.ValueBox.ValueChanged += () =>
{
lod.ScreenSize = screenSize.ValueBox.Value;
proxy.Window.MarkAsEdited();
};
// Every mesh properties
for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
{
var mesh = meshes[meshIndex];
group.Label($"Mesh {meshIndex} (tris: {mesh.TriangleCount:N0}, verts: {mesh.VertexCount:N0})").AddCopyContextMenu();
// Material Slot
var materialSlot = group.ComboBox("Material Slot", "Material slot used by this mesh during rendering");
materialSlot.ComboBox.Tag = mesh;
materialSlot.ComboBox.SelectedIndexChanged += comboBox => proxy.SetMaterialSlot((SkinnedMesh)comboBox.Tag, comboBox.SelectedIndex);
proxy._materialSlotComboBoxes.Add(materialSlot.ComboBox);
// Isolate
var isolate = group.Checkbox("Isolate", "Shows only this mesh (and meshes using the same material slot)");
isolate.CheckBox.Tag = mesh;
isolate.CheckBox.StateChanged += (box) => proxy.SetIsolate(box.Checked ? (SkinnedMesh)box.Tag : null);
proxy._isolateCheckBoxes.Add(isolate.CheckBox);
// Highlight
var highlight = group.Checkbox("Highlight", "Highlights this mesh with a tint color (and meshes using the same material slot)");
highlight.CheckBox.Tag = mesh;
highlight.CheckBox.StateChanged += (box) => proxy.SetHighlight(box.Checked ? (SkinnedMesh)box.Tag : null);
proxy._highlightCheckBoxes.Add(highlight.CheckBox);
}
}
// Refresh UI
proxy.UpdateMaterialSlotsUI();
}
internal override void RefreshInternal()
{
// Skip updates when model is not loaded
var proxy = (MeshesPropertiesProxy)Values[0];
if (proxy.Asset == null || !proxy.Asset.IsLoaded)
return;
base.RefreshInternal();
}
}
}
[CustomEditor(typeof(ProxyEditor))]
@@ -393,107 +179,12 @@ namespace FlaxEditor.Windows.Assets
}
}
}
internal override void RefreshInternal()
{
// Skip updates when model is not loaded
var proxy = (MeshesPropertiesProxy)Values[0];
if (proxy.Asset == null || !proxy.Asset.IsLoaded)
return;
base.RefreshInternal();
}
}
}
[CustomEditor(typeof(ProxyEditor))]
private sealed class MaterialsPropertiesProxy : PropertiesProxyBase
private sealed class MaterialsPropertiesProxy : MaterialsPropertiesProxyBase
{
[Collection(CanReorderItems = true, NotNullItems = true, OverrideEditorTypeName = "FlaxEditor.CustomEditors.Editors.GenericEditor", Spacing = 10)]
[EditorOrder(10), EditorDisplay("Materials", EditorDisplayAttribute.InlineStyle)]
public MaterialSlot[] MaterialSlots
{
get => Asset != null ? Asset.MaterialSlots : null;
set
{
if (Asset != null)
{
if (Asset.MaterialSlots.Length != value.Length)
{
MaterialBase[] materials = new MaterialBase[value.Length];
string[] names = new string[value.Length];
ShadowsCastingMode[] shadowsModes = new ShadowsCastingMode[value.Length];
for (int i = 0; i < value.Length; i++)
{
if (value[i] != null)
{
materials[i] = value[i].Material;
names[i] = value[i].Name;
shadowsModes[i] = value[i].ShadowsMode;
}
else
{
materials[i] = null;
names[i] = "Material " + i;
shadowsModes[i] = ShadowsCastingMode.All;
}
}
Asset.SetupMaterialSlots(value.Length);
var slots = Asset.MaterialSlots;
for (int i = 0; i < slots.Length; i++)
{
slots[i].Material = materials[i];
slots[i].Name = names[i];
slots[i].ShadowsMode = shadowsModes[i];
}
UpdateMaterialSlotsUI();
}
}
}
}
private readonly List<ComboBox> _materialSlotComboBoxes = new List<ComboBox>();
/// <summary>
/// Updates the material slots UI parts. Should be called after material slot rename.
/// </summary>
public void UpdateMaterialSlotsUI()
{
Window._skipEffectsGuiEvents = true;
// Generate material slots labels (with index prefix)
var slots = Asset.MaterialSlots;
var slotsLabels = new string[slots.Length];
for (int i = 0; i < slots.Length; i++)
{
slotsLabels[i] = string.Format("[{0}] {1}", i, slots[i].Name);
}
// Update comboboxes
for (int i = 0; i < _materialSlotComboBoxes.Count; i++)
{
var comboBox = _materialSlotComboBoxes[i];
comboBox.SetItems(slotsLabels);
comboBox.SelectedIndex = ((SkinnedMesh)comboBox.Tag).MaterialSlotIndex;
}
Window._skipEffectsGuiEvents = false;
}
private class ProxyEditor : ProxyEditorBase
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (MaterialsPropertiesProxy)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
base.Initialize(layout);
}
}
}
[CustomEditor(typeof(ProxyEditor))]
@@ -788,45 +479,8 @@ namespace FlaxEditor.Windows.Assets
}
[CustomEditor(typeof(ProxyEditor))]
private sealed class ImportPropertiesProxy : PropertiesProxyBase
private sealed class ImportPropertiesProxy : ImportPropertiesProxyBase
{
private ModelImportSettings ImportSettings = new ModelImportSettings();
/// <inheritdoc />
public override void OnLoad(SkinnedModelWindow window)
{
base.OnLoad(window);
Editor.TryRestoreImportOptions(ref ImportSettings.Settings, window.Item.Path);
}
public void Reimport()
{
Editor.Instance.ContentImporting.Reimport((BinaryAssetItem)Window.Item, ImportSettings, true);
}
private class ProxyEditor : ProxyEditorBase
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (ImportPropertiesProxy)Values[0];
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
// Import Settings
{
var group = layout.Group("Import Settings");
var importSettingsField = typeof(ImportPropertiesProxy).GetField("ImportSettings", BindingFlags.NonPublic | BindingFlags.Instance);
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
layout.Space(5);
var reimportButton = group.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxy)Values[0]).Reimport();
}
}
}
}
private class MeshesTab : Tab
@@ -960,10 +614,8 @@ namespace FlaxEditor.Windows.Assets
_preview.Task.AddCustomActor(_highlightActor);
}
/// <summary>
/// Updates the highlight/isolate effects on a model asset.
/// </summary>
private void UpdateEffectsOnAsset()
/// <inheritdoc />
protected override void UpdateEffectsOnAsset()
{
var entries = _preview.PreviewActor.Entries;
if (entries != null)
@@ -1071,11 +723,7 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
protected override void OnAssetLoaded()
{
_refreshOnLODsLoaded = true;
_preview.ViewportCamera.SetArcBallView(_preview.GetBounds());
UpdateEffectsOnAsset();
// TODO: disable streaming for this model
// Reset any root motion
_preview.PreviewActor.ResetLocalTransform();