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