From 0e6f47b23c522684f1aeb7b41b52ede70bbb69f2 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 27 Mar 2025 09:56:21 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E4=BF=AE=E6=94=B9=E9=80=82=E9=85=8D?= =?UTF-8?q?=E5=AF=B9=E5=A4=9A=E8=BD=A8=E9=81=93=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Controls/SpinePreviewer.cs | 4 +- .../Implementations/Exporter/VideoExporter.cs | 6 +- .../Spine/Implementations/Spine/Spine21.cs | 38 +++--- .../Spine/Implementations/Spine/Spine36.cs | 38 +++--- .../Spine/Implementations/Spine/Spine37.cs | 30 ++--- .../Spine/Implementations/Spine/Spine38.cs | 30 ++--- .../Spine/Implementations/Spine/Spine40.cs | 30 ++--- .../Spine/Implementations/Spine/Spine41.cs | 30 ++--- .../Spine/Implementations/Spine/Spine42.cs | 30 ++--- SpineViewer/Spine/Spine.cs | 127 ++++++++---------- 10 files changed, 163 insertions(+), 200 deletions(-) diff --git a/SpineViewer/Controls/SpinePreviewer.cs b/SpineViewer/Controls/SpinePreviewer.cs index 527ebb8..6644b24 100644 --- a/SpineViewer/Controls/SpinePreviewer.cs +++ b/SpineViewer/Controls/SpinePreviewer.cs @@ -607,7 +607,7 @@ namespace SpineViewer.Controls lock (SpineListView.Spines) { foreach (var spine in SpineListView.Spines) - spine.CurrentAnimation = spine.CurrentAnimation; + spine.Track0Animation = spine.Track0Animation; // TODO: 多轨道重置 } } } @@ -619,7 +619,7 @@ namespace SpineViewer.Controls lock (SpineListView.Spines) { foreach (var spine in SpineListView.Spines) - spine.CurrentAnimation = spine.CurrentAnimation; + spine.Track0Animation = spine.Track0Animation; // TODO: 多轨道重置 } } IsUpdating = true; diff --git a/SpineViewer/Exporter/Implementations/Exporter/VideoExporter.cs b/SpineViewer/Exporter/Implementations/Exporter/VideoExporter.cs index 0f9e2c4..38eca9f 100644 --- a/SpineViewer/Exporter/Implementations/Exporter/VideoExporter.cs +++ b/SpineViewer/Exporter/Implementations/Exporter/VideoExporter.cs @@ -23,9 +23,9 @@ namespace SpineViewer.Exporter.Implementations.Exporter { var args = (VideoExportArgs)ExportArgs; - // 独立导出时如果 args.Duration 小于 0 则使用自己的动画时长 + // 独立导出时如果 args.Duration 小于 0 则使用 Track0 的动画时长 var duration = args.Duration; - if (duration < 0) duration = spine.CurrentAnimationDuration; + if (duration < 0) duration = spine.GetAnimationDuration(spine.Track0Animation); // TODO: 也许可以使用所有轨道的最大值 float delta = 1f / args.FPS; int total = Math.Max(1, (int)(duration * args.FPS)); // 至少导出 1 帧 @@ -75,7 +75,7 @@ namespace SpineViewer.Exporter.Implementations.Exporter public override void Export(Spine.Spine[] spines, BackgroundWorker? worker = null) { // 导出视频格式需要把模型时间都重置到 0 - foreach (var spine in spines) spine.CurrentAnimation = spine.CurrentAnimation; + foreach (var spine in spines) spine.Track0Animation = spine.Track0Animation; // TODO: 多轨道重置 base.Export(spines, worker); } } diff --git a/SpineViewer/Spine/Implementations/Spine/Spine21.cs b/SpineViewer/Spine/Implementations/Spine/Spine21.cs index d45ec4b..ccb40cb 100644 --- a/SpineViewer/Spine/Implementations/Spine/Spine21.cs +++ b/SpineViewer/Spine/Implementations/Spine/Spine21.cs @@ -82,10 +82,6 @@ namespace SpineViewer.Spine.Implementations.Spine foreach (var anime in skeletonData.Animations) animationNames.Add(anime.Name); - - // 取最后一个作为初始, 尽可能去显示非默认的内容 - CurrentAnimation = animationNames.Last(); - CurrentSkin = skinNames.Last(); } protected override void Dispose(bool disposing) @@ -113,8 +109,8 @@ namespace SpineViewer.Spine.Implementations.Spine var position = Position; var flipX = FlipX; var flipY = FlipY; - var animation = CurrentAnimation; - var skin = CurrentSkin; + var animation = Track0Animation; // TODO: 适配多轨道 + var skin = Skin; var val = Math.Max(value, SCALE_MIN); if (skeletonBinary is not null) @@ -137,8 +133,8 @@ namespace SpineViewer.Spine.Implementations.Spine Position = position; FlipX = flipX; FlipY = flipY; - CurrentAnimation = animation; - CurrentSkin = skin; + Track0Animation = animation; // TODO: 适配多轨道 + Skin = skin; } } @@ -165,7 +161,19 @@ namespace SpineViewer.Spine.Implementations.Spine set { skeleton.FlipY = value; Update(0); } } - public override string CurrentAnimation + public override string Skin + { + get => skeleton.Skin?.Name ?? "default"; + set + { + if (!skinNames.Contains(value)) return; + skeleton.SetSkin(value); + skeleton.SetSlotsToSetupPose(); + Update(0); + } + } + + public override string Track0Animation { get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; set @@ -178,18 +186,6 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentSkin - { - get => skeleton.Skin?.Name ?? "default"; - set - { - if (!skinNames.Contains(value)) return; - skeleton.SetSkin(value); - skeleton.SetSlotsToSetupPose(); - Update(0); - } - } - public override RectangleF Bounds { get diff --git a/SpineViewer/Spine/Implementations/Spine/Spine36.cs b/SpineViewer/Spine/Implementations/Spine/Spine36.cs index d6457db..f42fc0f 100644 --- a/SpineViewer/Spine/Implementations/Spine/Spine36.cs +++ b/SpineViewer/Spine/Implementations/Spine/Spine36.cs @@ -81,10 +81,6 @@ namespace SpineViewer.Spine.Implementations.Spine foreach (var anime in skeletonData.Animations) animationNames.Add(anime.Name); - - // 取最后一个作为初始, 尽可能去显示非默认的内容 - CurrentAnimation = animationNames.Last(); - CurrentSkin = skinNames.Last(); } protected override void Dispose(bool disposing) @@ -112,8 +108,8 @@ namespace SpineViewer.Spine.Implementations.Spine var position = Position; var flipX = FlipX; var flipY = FlipY; - var animation = CurrentAnimation; - var skin = CurrentSkin; + var animation = Track0Animation; // TODO: 适配多轨道 + var skin = Skin; var val = Math.Max(value, SCALE_MIN); if (skeletonBinary is not null) @@ -136,8 +132,8 @@ namespace SpineViewer.Spine.Implementations.Spine Position = position; FlipX = flipX; FlipY = flipY; - CurrentAnimation = animation; - CurrentSkin = skin; + Track0Animation = animation; // TODO: 适配多轨道 + Skin = skin; } } @@ -164,7 +160,19 @@ namespace SpineViewer.Spine.Implementations.Spine set { skeleton.FlipY = value; Update(0); } } - public override string CurrentAnimation + public override string Skin + { + get => skeleton.Skin?.Name ?? "default"; + set + { + if (!skinNames.Contains(value)) return; + skeleton.SetSkin(value); + skeleton.SetSlotsToSetupPose(); + Update(0); + } + } + + public override string Track0Animation { get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; set @@ -177,18 +185,6 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentSkin - { - get => skeleton.Skin?.Name ?? "default"; - set - { - if (!skinNames.Contains(value)) return; - skeleton.SetSkin(value); - skeleton.SetSlotsToSetupPose(); - Update(0); - } - } - public override RectangleF Bounds { get diff --git a/SpineViewer/Spine/Implementations/Spine/Spine37.cs b/SpineViewer/Spine/Implementations/Spine/Spine37.cs index 9961e48..446454b 100644 --- a/SpineViewer/Spine/Implementations/Spine/Spine37.cs +++ b/SpineViewer/Spine/Implementations/Spine/Spine37.cs @@ -79,10 +79,6 @@ namespace SpineViewer.Spine.Implementations.Spine foreach (var anime in skeletonData.Animations) animationNames.Add(anime.Name); - - // 取最后一个作为初始, 尽可能去显示非默认的内容 - CurrentAnimation = animationNames.Last(); - CurrentSkin = skinNames.Last(); } protected override void Dispose(bool disposing) @@ -137,7 +133,19 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentAnimation + public override string Skin + { + get => skeleton.Skin?.Name ?? "default"; + set + { + if (!skinNames.Contains(value)) return; + skeleton.SetSkin(value); + skeleton.SetSlotsToSetupPose(); + Update(0); + } + } + + public override string Track0Animation { get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; set @@ -150,18 +158,6 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentSkin - { - get => skeleton.Skin?.Name ?? "default"; - set - { - if (!skinNames.Contains(value)) return; - skeleton.SetSkin(value); - skeleton.SetSlotsToSetupPose(); - Update(0); - } - } - public override RectangleF Bounds { get diff --git a/SpineViewer/Spine/Implementations/Spine/Spine38.cs b/SpineViewer/Spine/Implementations/Spine/Spine38.cs index 7b5a7dd..796dd3a 100644 --- a/SpineViewer/Spine/Implementations/Spine/Spine38.cs +++ b/SpineViewer/Spine/Implementations/Spine/Spine38.cs @@ -85,10 +85,6 @@ namespace SpineViewer.Spine.Implementations.Spine foreach (var anime in skeletonData.Animations) animationNames.Add(anime.Name); - - // 取最后一个作为初始, 尽可能去显示非默认的内容 - CurrentAnimation = animationNames.Last(); - CurrentSkin = skinNames.Last(); } protected override void Dispose(bool disposing) @@ -143,7 +139,19 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentAnimation + public override string Skin + { + get => skeleton.Skin?.Name ?? "default"; + set + { + if (!skinNames.Contains(value)) return; + skeleton.SetSkin(value); + skeleton.SetSlotsToSetupPose(); + Update(0); + } + } + + public override string Track0Animation { get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; set @@ -156,18 +164,6 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentSkin - { - get => skeleton.Skin?.Name ?? "default"; - set - { - if (!skinNames.Contains(value)) return; - skeleton.SetSkin(value); - skeleton.SetSlotsToSetupPose(); - Update(0); - } - } - public override RectangleF Bounds { get diff --git a/SpineViewer/Spine/Implementations/Spine/Spine40.cs b/SpineViewer/Spine/Implementations/Spine/Spine40.cs index 9e86170..70b61bb 100644 --- a/SpineViewer/Spine/Implementations/Spine/Spine40.cs +++ b/SpineViewer/Spine/Implementations/Spine/Spine40.cs @@ -81,10 +81,6 @@ namespace SpineViewer.Spine.Implementations.Spine foreach (var anime in skeletonData.Animations) animationNames.Add(anime.Name); - - // 取最后一个作为初始, 尽可能去显示非默认的内容 - CurrentAnimation = animationNames.Last(); - CurrentSkin = skinNames.Last(); } protected override void Dispose(bool disposing) @@ -139,7 +135,19 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentAnimation + public override string Skin + { + get => skeleton.Skin?.Name ?? "default"; + set + { + if (!skinNames.Contains(value)) return; + skeleton.SetSkin(value); + skeleton.SetSlotsToSetupPose(); + Update(0); + } + } + + public override string Track0Animation { get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; set @@ -152,18 +160,6 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentSkin - { - get => skeleton.Skin?.Name ?? "default"; - set - { - if (!skinNames.Contains(value)) return; - skeleton.SetSkin(value); - skeleton.SetSlotsToSetupPose(); - Update(0); - } - } - public override RectangleF Bounds { get diff --git a/SpineViewer/Spine/Implementations/Spine/Spine41.cs b/SpineViewer/Spine/Implementations/Spine/Spine41.cs index 0c091a5..3ce9dce 100644 --- a/SpineViewer/Spine/Implementations/Spine/Spine41.cs +++ b/SpineViewer/Spine/Implementations/Spine/Spine41.cs @@ -81,10 +81,6 @@ namespace SpineViewer.Spine.Implementations.Spine foreach (var anime in skeletonData.Animations) animationNames.Add(anime.Name); - - // 取最后一个作为初始, 尽可能去显示非默认的内容 - CurrentAnimation = animationNames.Last(); - CurrentSkin = skinNames.Last(); } protected override void Dispose(bool disposing) @@ -139,7 +135,19 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentAnimation + public override string Skin + { + get => skeleton.Skin?.Name ?? "default"; + set + { + if (!skinNames.Contains(value)) return; + skeleton.SetSkin(value); + skeleton.SetSlotsToSetupPose(); + Update(0); + } + } + + public override string Track0Animation { get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; set @@ -152,18 +160,6 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentSkin - { - get => skeleton.Skin?.Name ?? "default"; - set - { - if (!skinNames.Contains(value)) return; - skeleton.SetSkin(value); - skeleton.SetSlotsToSetupPose(); - Update(0); - } - } - public override RectangleF Bounds { get diff --git a/SpineViewer/Spine/Implementations/Spine/Spine42.cs b/SpineViewer/Spine/Implementations/Spine/Spine42.cs index bd7497c..b88ca44 100644 --- a/SpineViewer/Spine/Implementations/Spine/Spine42.cs +++ b/SpineViewer/Spine/Implementations/Spine/Spine42.cs @@ -81,10 +81,6 @@ namespace SpineViewer.Spine.Implementations.Spine foreach (var anime in skeletonData.Animations) animationNames.Add(anime.Name); - - // 取最后一个作为初始, 尽可能去显示非默认的内容 - CurrentAnimation = animationNames.Last(); - CurrentSkin = skinNames.Last(); } protected override void Dispose(bool disposing) @@ -139,7 +135,19 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentAnimation + public override string Skin + { + get => skeleton.Skin?.Name ?? "default"; + set + { + if (!skinNames.Contains(value)) return; + skeleton.SetSkin(value); + skeleton.SetSlotsToSetupPose(); + Update(0); + } + } + + public override string Track0Animation { get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; set @@ -152,18 +160,6 @@ namespace SpineViewer.Spine.Implementations.Spine } } - public override string CurrentSkin - { - get => skeleton.Skin?.Name ?? "default"; - set - { - if (!skinNames.Contains(value)) return; - skeleton.SetSkin(value); - skeleton.SetSlotsToSetupPose(); - Update(0); - } - } - public override RectangleF Bounds { get diff --git a/SpineViewer/Spine/Spine.cs b/SpineViewer/Spine/Spine.cs index 4ed7d80..9878ebf 100644 --- a/SpineViewer/Spine/Spine.cs +++ b/SpineViewer/Spine/Spine.cs @@ -182,7 +182,38 @@ namespace SpineViewer.Spine { throw new NotImplementedException($"Not implemented version: {version}"); } - return (Spine)Activator.CreateInstance(spineType, skelPath, atlasPath); + + var spine = (Spine)Activator.CreateInstance(spineType, skelPath, atlasPath); + + // 统一初始化 + spine.initBounds = spine.Bounds; + + // XXX: tex 没办法在这里主动 Dispose + // 批量添加在获取预览图的时候极大概率会和预览线程死锁 + // 虽然两边不会同时调用 Draw, 但是死锁似乎和 Draw 函数有关 + // 除此之外, 似乎还和 tex 的 Dispose 有关 + // 如果不对 tex 进行 Dispose, 那么不管是否 Draw 都正常不会死锁 + var tex = new SFML.Graphics.RenderTexture(PREVIEW_WIDTH, PREVIEW_HEIGHT); + tex.SetView(spine.InitBounds.GetView(PREVIEW_WIDTH, PREVIEW_HEIGHT)); + tex.Clear(SFML.Graphics.Color.Transparent); + tex.Draw(spine); + tex.Display(); + + using (var img = tex.Texture.CopyToImage()) + { + img.SaveToMemory(out var imgBuffer, "bmp"); + using (var stream = new MemoryStream(imgBuffer)) + { + // 必须重复构造一个副本才能摆脱对流的依赖, 否则之后使用会报错 + spine.preview = new Bitmap(new Bitmap(stream)); + } + } + + // 取最后一个作为初始, 尽可能去显示非默认的内容 + spine.Skin = spine.SkinNames.Last(); + spine.Track0Animation = spine.AnimationNames.Last(); + + return spine; } /// @@ -300,26 +331,6 @@ namespace SpineViewer.Spine #region 属性 | [3] 动画 - /// - /// 包含的所有动画名称 - /// - [Browsable(false)] - public ReadOnlyCollection AnimationNames { get => animationNames.AsReadOnly(); } - protected List animationNames = [EMPTY_ANIMATION]; - - /// - /// 当前动画名称, 如果设置的动画不存在则忽略 - /// - [TypeConverter(typeof(AnimationConverter))] - [Category("[3] 动画"), DisplayName("当前动画")] - public abstract string CurrentAnimation { get; set; } - - /// - /// 当前动画时长 - /// - [Category("[3] 动画"), DisplayName("当前动画时长")] - public float CurrentAnimationDuration { get => GetAnimationDuration(CurrentAnimation); } - /// /// 包含的所有皮肤名称 /// @@ -328,11 +339,31 @@ namespace SpineViewer.Spine protected List skinNames = []; /// - /// 当前皮肤名称, 如果设置的皮肤不存在则忽略 + /// 使用的皮肤名称, 如果设置的皮肤不存在则忽略 /// [TypeConverter(typeof(SkinConverter))] - [Category("[3] 动画"), DisplayName("当前皮肤")] - public abstract string CurrentSkin { get; set; } + [Category("[3] 动画"), DisplayName("皮肤")] + public abstract string Skin { get; set; } + + /// + /// 包含的所有动画名称 + /// + [Browsable(false)] + public ReadOnlyCollection AnimationNames { get => animationNames.AsReadOnly(); } + protected List animationNames = [EMPTY_ANIMATION]; + + /// + /// 默认轨道动画名称, 如果设置的动画不存在则忽略 + /// + [TypeConverter(typeof(AnimationConverter))] + [Category("[3] 动画"), DisplayName("默认轨道动画")] + public abstract string Track0Animation { get; set; } + + /// + /// 默认轨道动画时长 + /// + [Category("[3] 动画"), DisplayName("默认轨道动画时长")] + public float Track0AnimationDuration { get => GetAnimationDuration(Track0Animation); } // TODO: 动画时长变成伪属性在面板显示 #endregion @@ -395,55 +426,15 @@ namespace SpineViewer.Spine /// 初始状态下的骨骼包围盒 /// [Browsable(false)] - public RectangleF InitBounds - { - get - { - if (initBounds is null) - { - var tmp = CurrentAnimation; - CurrentAnimation = EMPTY_ANIMATION; - initBounds = Bounds; - CurrentAnimation = tmp; - } - return (RectangleF)initBounds; - } - } - private RectangleF? initBounds = null; + public RectangleF InitBounds { get => initBounds; } + private RectangleF initBounds; /// /// 骨骼预览图 /// [Browsable(false)] - public Image Preview - { - get - { - if (preview is null) - { - // XXX: tex 没办法在这里主动 Dispose - // 批量添加在获取预览图的时候极大概率会和预览线程死锁 - // 虽然两边不会同时调用 Draw, 但是死锁似乎和 Draw 函数有关 - // 除此之外, 似乎还和 tex 的 Dispose 有关 - // 如果不对 tex 进行 Dispose, 那么不管是否 Draw 都正常不会死锁 - var tex = new SFML.Graphics.RenderTexture(PREVIEW_WIDTH, PREVIEW_HEIGHT); - tex.SetView(InitBounds.GetView(PREVIEW_WIDTH, PREVIEW_HEIGHT)); - tex.Clear(SFML.Graphics.Color.Transparent); - var tmp = CurrentAnimation; - CurrentAnimation = EMPTY_ANIMATION; - tex.Draw(this); - CurrentAnimation = tmp; - tex.Display(); - - using var img = tex.Texture.CopyToImage(); - img.SaveToMemory(out var imgBuffer, "bmp"); - using var stream = new MemoryStream(imgBuffer); - preview = new Bitmap(new Bitmap(stream)); // 必须重复构造一个副本才能摆脱对流的依赖, 否则之后使用会报错 - } - return preview; - } - } - private Image preview = null; + public Image Preview { get => preview; } + private Image preview; /// /// 获取动画时长, 如果动画不存在则返回 0