From 52c09b95ca12c54cc4c569b9e90a75ef8ee77887 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 9 Mar 2025 23:35:24 +0100 Subject: [PATCH] Add `Base Model` to Animation Window to preview animation via a different base skeleton with retargetting #3072 --- .../Editor/Windows/Assets/AnimationWindow.cs | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index c65b8caab..fa77d0797 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -32,6 +32,8 @@ namespace FlaxEditor.Windows.Assets private readonly AnimationWindow _window; private AnimationGraph _animGraph; + public SkinnedModel BaseModel; + public Preview(AnimationWindow window) : base(true) { @@ -46,10 +48,11 @@ namespace FlaxEditor.Windows.Assets Object.Destroy(ref _animGraph); if (!model) return; + var baseModel = BaseModel ?? model; // Use virtual animation graph to playback the animation _animGraph = FlaxEngine.Content.CreateVirtualAsset(); - _animGraph.InitAsAnimation(model, _window.Asset, true, true); + _animGraph.InitAsAnimation(baseModel, _window.Asset, true, true); PreviewActor.AnimationGraph = _animGraph; } @@ -69,6 +72,7 @@ namespace FlaxEditor.Windows.Assets /// public override void OnDestroy() { + BaseModel = null; Object.Destroy(ref _animGraph); base.OnDestroy(); @@ -148,6 +152,24 @@ namespace FlaxEditor.Windows.Assets } } + private bool ShowBaseModel => PreviewModel != null; + + [EditorDisplay("Preview"), NoSerialize, AssetReference(true), VisibleIf(nameof(ShowBaseModel))] + [Tooltip("The skinned model to use as a retarget source. Animation will be played using its skeleton and retarget into the Preview Model.")] + public SkinnedModel BaseModel + { + get => Window?._preview?.BaseModel; + set + { + if (Window == null || PreviewModel == value) + return; + + // Reinit + Window._preview.BaseModel = value; + Window._preview.SetModel(PreviewModel); + } + } + public void OnLoad(AnimationWindow window) { // Link @@ -230,7 +252,7 @@ namespace FlaxEditor.Windows.Assets private ToolStripButton _undoButton; private ToolStripButton _redoButton; private bool _isWaitingForTimelineLoad; - private SkinnedModel _initialPreviewModel; + private SkinnedModel _initialPreviewModel, _initialBaseModel; private float _initialPanel2Splitter = 0.6f; /// @@ -322,7 +344,12 @@ namespace FlaxEditor.Windows.Assets _properties.PreviewModel = _initialPreviewModel; _panel2.SplitterValue = _initialPanel2Splitter; _initialPreviewModel = null; + if (_initialBaseModel) + { + _properties.BaseModel = _initialBaseModel; + } } + _initialBaseModel = null; base.OnAssetLoaded(); } @@ -412,6 +439,8 @@ namespace FlaxEditor.Windows.Assets writer.WriteAttributeString("ShowPreviewValues", _timeline.ShowPreviewValues.ToString()); if (_properties.PreviewModel) writer.WriteAttributeString("PreviewModel", _properties.PreviewModel.ID.ToString()); + if (_properties.BaseModel) + writer.WriteAttributeString("BaseModel", _properties.BaseModel.ID.ToString()); } /// @@ -427,6 +456,8 @@ namespace FlaxEditor.Windows.Assets _timeline.ShowPreviewValues = value3; if (Guid.TryParse(node.GetAttribute("PreviewModel"), out Guid value4)) _initialPreviewModel = FlaxEngine.Content.LoadAsync(value4); + if (Guid.TryParse(node.GetAttribute("BaseModel"), out value4)) + _initialBaseModel = FlaxEngine.Content.LoadAsync(value4); } ///