From 7a678641363cc57f2cc1aa0b64da10953e8408ef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 8 Aug 2026 06:43:13 +0200 Subject: [PATCH] Add material drag drop to animated models (with highlight) --- .../Editor/Viewport/ViewportDraggingHelper.cs | 38 ++++++++++++++----- Source/Editor/ViewportDebugDrawData.cs | 33 ++++++++++++++-- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/Source/Editor/Viewport/ViewportDraggingHelper.cs b/Source/Editor/Viewport/ViewportDraggingHelper.cs index 5c68b399a..423006c31 100644 --- a/Source/Editor/Viewport/ViewportDraggingHelper.cs +++ b/Source/Editor/Viewport/ViewportDraggingHelper.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.Viewport private readonly DragActorType _dragActorType; private readonly DragScriptItems _dragScriptItem; - private StaticModel _previewStaticModel; + private ModelInstanceActor _previewModel; private int _previewModelEntryIndex; private BrushSurface _previewBrushSurface; @@ -56,15 +56,15 @@ namespace FlaxEditor.Viewport internal void ClearDragEffects() { - _previewStaticModel = null; + _previewModel = null; _previewModelEntryIndex = -1; _previewBrushSurface = new BrushSurface(); } internal void CollectDrawCalls(ViewportDebugDrawData debugDrawData, ref RenderContext renderContext) { - if (_previewStaticModel) - debugDrawData.HighlightModel(_previewStaticModel, _previewModelEntryIndex); + if (_previewModel) + debugDrawData.HighlightModel(_previewModel, _previewModelEntryIndex); if (_previewBrushSurface.Brush) debugDrawData.HighlightBrushSurface(_previewBrushSurface); } @@ -127,11 +127,18 @@ namespace FlaxEditor.Viewport return; } + // TODO: refactor this to be handled by ActorNode itself for generic actor highlights if (hit is StaticModelNode staticModelNode) { - _previewStaticModel = (StaticModel)staticModelNode.Actor; + _previewModel = (StaticModel)staticModelNode.Actor; var ray = _viewport.ConvertMouseToRay(ref location); - _previewStaticModel.IntersectsEntry(ref ray, out _, out _, out _previewModelEntryIndex); + _previewModel.IntersectsEntry(ref ray, out _, out _, out _previewModelEntryIndex); + } + else if (hit is AnimatedModelNode animatedModelNode) + { + _previewModel = (AnimatedModel)animatedModelNode.Actor; + var ray = _viewport.ConvertMouseToRay(ref location); + _previewModel.IntersectsEntry(ref ray, out _, out _, out _previewModelEntryIndex); } else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) { @@ -251,14 +258,25 @@ namespace FlaxEditor.Viewport }; Spawn(actor, ref hitLocation, ref hitNormal); } + // TODO: refactor this to be handled by ActorNode itself for generic actor highlights else if (hit is StaticModelNode staticModelNode) { - var staticModel = (StaticModel)staticModelNode.Actor; + var actor = (StaticModel)staticModelNode.Actor; var ray = _viewport.ConvertMouseToRay(ref location); - if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) + if (actor.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) { - using (new UndoBlock(_owner.Undo, staticModel, "Change material")) - staticModel.SetMaterial(entryIndex, material); + using (new UndoBlock(_owner.Undo, actor, "Change material")) + actor.SetMaterial(entryIndex, material); + } + } + else if (hit is AnimatedModelNode animatedModelNode) + { + var actor = (AnimatedModel)animatedModelNode.Actor; + var ray = _viewport.ConvertMouseToRay(ref location); + if (actor.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) + { + using (new UndoBlock(_owner.Undo, actor, "Change material")) + actor.SetMaterial(entryIndex, material); } } else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) diff --git a/Source/Editor/ViewportDebugDrawData.cs b/Source/Editor/ViewportDebugDrawData.cs index d70ec9cfb..4cf7f696a 100644 --- a/Source/Editor/ViewportDebugDrawData.cs +++ b/Source/Editor/ViewportDebugDrawData.cs @@ -50,11 +50,10 @@ namespace FlaxEditor /// Highlights the model. /// /// The model. - public void HighlightModel(StaticModel model) + public void HighlightModel(ModelInstanceActor model) { - if (model.Model == null) + if (!model) return; - var entries = model.Entries; for (var i = 0; i < entries.Length; i++) HighlightModel(model, i); @@ -65,7 +64,7 @@ namespace FlaxEditor /// /// The model. /// Index of the entry to highlight. - public void HighlightModel(StaticModel model, int entryIndex) + public void HighlightModel(ModelInstanceActor model, int entryIndex) { _highlights.Add(new HighlightData { @@ -144,6 +143,32 @@ namespace FlaxEditor } } } + else if (highlight.Target is AnimatedModel animatedModel) + { + var model = animatedModel.SkinnedModel; + if (model == null) + continue; + animatedModel.Transform.GetWorld(out world); + var bounds = BoundingSphere.FromBox(animatedModel.Box); + var bones = animatedModel.SkinnedMeshBones; + + // Pick a proper LOD + Float3 center = bounds.Center - renderContext.View.Origin; + int lodIndex = RenderTools.ComputeModelLOD(model, ref center, (float)bounds.Radius, ref renderContext); + var lods = model.LODs; + if (lods == null || lods.Length < lodIndex || lodIndex < 0) + continue; + var lod = lods[lodIndex]; + + // Draw meshes + for (int meshIndex = 0; meshIndex < lod.Meshes.Length; meshIndex++) + { + if (lod.Meshes[meshIndex].MaterialSlotIndex == highlight.EntryIndex) + { + lod.Meshes[meshIndex].Draw(ref renderContext, bones, _highlightMaterial, ref world); + } + } + } } if (_highlightTriangles.Count > 0)