diff --git a/SpineViewer/Spine/Implementations/SpineObject/SpineObject21.cs b/SpineViewer/Spine/Implementations/SpineObject/SpineObject21.cs
index acb0910..e0124c0 100644
--- a/SpineViewer/Spine/Implementations/SpineObject/SpineObject21.cs
+++ b/SpineViewer/Spine/Implementations/SpineObject/SpineObject21.cs
@@ -206,49 +206,53 @@ namespace SpineViewer.Spine.Implementations.SpineObject
protected override RectangleF getCurrentBounds()
{
- float[] temp = new float[8];
- var drawOrderItems = skeleton.DrawOrder;
- float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
- for (int i = 0, n = skeleton.DrawOrder.Count; i < n; i++)
- {
- Slot slot = drawOrderItems[i];
- int verticesLength = 0;
- float[] vertices = null;
- Attachment attachment = slot.Attachment;
- var regionAttachment = attachment as RegionAttachment;
- if (regionAttachment != null)
- {
- verticesLength = 8;
- vertices = temp;
- if (vertices.Length < 8) vertices = temp = new float[8];
- regionAttachment.ComputeWorldVertices(slot.Bone, temp);
- }
- else
- {
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null)
- {
- MeshAttachment mesh = meshAttachment;
- verticesLength = mesh.Vertices.Length;
- vertices = temp;
- if (vertices.Length < verticesLength) vertices = temp = new float[verticesLength];
- mesh.ComputeWorldVertices(slot, temp);
- }
- }
+ skeleton.GetBounds(out var x, out var y, out var w, out var h);
+ return new RectangleF(x, y, w, h);
+ }
- if (vertices != null)
- {
- for (int ii = 0; ii < verticesLength; ii += 2)
- {
- float vx = vertices[ii], vy = vertices[ii + 1];
- minX = Math.Min(minX, vx);
- minY = Math.Min(minY, vy);
- maxX = Math.Max(maxX, vx);
- maxY = Math.Max(maxY, vy);
- }
- }
+ protected override RectangleF getBounds()
+ {
+ // 初始化临时对象
+ var maxDuration = 0f;
+ var tmpSkeleton = new Skeleton(skeletonData) { Skin = new(Guid.NewGuid().ToString()) };
+ var tmpAnimationState = new AnimationState(animationStateData);
+ tmpSkeleton.FlipX = skeleton.FlipX;
+ tmpSkeleton.FlipY = skeleton.FlipY;
+ tmpSkeleton.X = skeleton.X;
+ tmpSkeleton.Y = skeleton.Y;
+ foreach (var name in loadedSkins)
+ {
+ foreach (var (k, v) in skeletonData.FindSkin(name).Attachments)
+ tmpSkeleton.Skin.AddAttachment(k.Key, k.Value, v);
}
- return new RectangleF(minX, minY, maxX - minX, maxY - minY);
+ foreach (var tr in animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks[i] is not null))
+ {
+ var ani = animationState.GetCurrent(tr).Animation;
+ tmpAnimationState.SetAnimation(tr, ani, true);
+ if (ani.Duration > maxDuration) maxDuration = ani.Duration;
+ }
+ tmpSkeleton.SetSlotsToSetupPose();
+ tmpAnimationState.Update(0);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(0);
+ tmpSkeleton.UpdateWorldTransform();
+
+ // 切成 100 帧获取边界最大值
+ var bounds = getCurrentBounds();
+ for (float tick = 0, delta = maxDuration / 100; tick < maxDuration; tick += delta)
+ {
+ tmpSkeleton.GetBounds(out var x, out var y, out var w, out var h);
+ if (x < bounds.X) bounds.X = x;
+ if (y < bounds.Y) bounds.Y = y;
+ if (w > bounds.Width) bounds.Width = w;
+ if (h > bounds.Height) bounds.Height = h;
+ tmpAnimationState.Update(delta);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(delta);
+ tmpSkeleton.UpdateWorldTransform();
+ }
+
+ return bounds;
}
protected override void update(float delta)
diff --git a/SpineViewer/Spine/Implementations/SpineObject/SpineObject36.cs b/SpineViewer/Spine/Implementations/SpineObject/SpineObject36.cs
index 28b5c8b..d900252 100644
--- a/SpineViewer/Spine/Implementations/SpineObject/SpineObject36.cs
+++ b/SpineViewer/Spine/Implementations/SpineObject/SpineObject36.cs
@@ -210,6 +210,52 @@ namespace SpineViewer.Spine.Implementations.SpineObject
return new RectangleF(x, y, w, h);
}
+ protected override RectangleF getBounds()
+ {
+ // 初始化临时对象
+ var maxDuration = 0f;
+ var tmpSkeleton = new Skeleton(skeletonData) { Skin = new(Guid.NewGuid().ToString()) };
+ var tmpAnimationState = new AnimationState(animationStateData);
+ tmpSkeleton.FlipX = skeleton.FlipX;
+ tmpSkeleton.FlipY = skeleton.FlipY;
+ tmpSkeleton.X = skeleton.X;
+ tmpSkeleton.Y = skeleton.Y;
+ foreach (var name in loadedSkins)
+ {
+ foreach (var (k, v) in skeletonData.FindSkin(name).Attachments)
+ tmpSkeleton.Skin.AddAttachment(k.slotIndex, k.name, v);
+ }
+ foreach (var tr in animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null))
+ {
+ var ani = animationState.GetCurrent(tr).Animation;
+ tmpAnimationState.SetAnimation(tr, ani, true);
+ if (ani.Duration > maxDuration) maxDuration = ani.Duration;
+ }
+ tmpSkeleton.SetSlotsToSetupPose();
+ tmpAnimationState.Update(0);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(0);
+ tmpSkeleton.UpdateWorldTransform();
+
+ // 切成 100 帧获取边界最大值
+ var bounds = getCurrentBounds();
+ float[] _ = [];
+ for (float tick = 0, delta = maxDuration / 100; tick < maxDuration; tick += delta)
+ {
+ tmpSkeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
+ if (x < bounds.X) bounds.X = x;
+ if (y < bounds.Y) bounds.Y = y;
+ if (w > bounds.Width) bounds.Width = w;
+ if (h > bounds.Height) bounds.Height = h;
+ tmpAnimationState.Update(delta);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(delta);
+ tmpSkeleton.UpdateWorldTransform();
+ }
+
+ return bounds;
+ }
+
protected override void update(float delta)
{
animationState.Update(delta);
@@ -218,18 +264,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform();
}
- private SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
- {
- return spineBlendMode switch
- {
- BlendMode.Normal => SFMLBlendMode.NormalPma,
- BlendMode.Additive => SFMLBlendMode.AdditivePma,
- BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
- BlendMode.Screen => SFMLBlendMode.ScreenPma,
- _ => throw new NotImplementedException($"{spineBlendMode}"),
- };
- }
-
protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{
triangleVertices.Clear();
diff --git a/SpineViewer/Spine/Implementations/SpineObject/SpineObject37.cs b/SpineViewer/Spine/Implementations/SpineObject/SpineObject37.cs
index 0a9751b..9c75f93 100644
--- a/SpineViewer/Spine/Implementations/SpineObject/SpineObject37.cs
+++ b/SpineViewer/Spine/Implementations/SpineObject/SpineObject37.cs
@@ -181,6 +181,52 @@ namespace SpineViewer.Spine.Implementations.SpineObject
return new RectangleF(x, y, w, h);
}
+ protected override RectangleF getBounds()
+ {
+ // 初始化临时对象
+ var maxDuration = 0f;
+ var tmpSkeleton = new Skeleton(skeletonData) { Skin = new(Guid.NewGuid().ToString()) };
+ var tmpAnimationState = new AnimationState(animationStateData);
+ tmpSkeleton.ScaleX = skeleton.ScaleX;
+ tmpSkeleton.ScaleY = skeleton.ScaleY;
+ tmpSkeleton.X = skeleton.X;
+ tmpSkeleton.Y = skeleton.Y;
+ foreach (var name in loadedSkins)
+ {
+ foreach (var (k, v) in skeletonData.FindSkin(name).Attachments)
+ tmpSkeleton.Skin.AddAttachment(k.slotIndex, k.name, v);
+ }
+ foreach (var tr in animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null))
+ {
+ var ani = animationState.GetCurrent(tr).Animation;
+ tmpAnimationState.SetAnimation(tr, ani, true);
+ if (ani.Duration > maxDuration) maxDuration = ani.Duration;
+ }
+ tmpSkeleton.SetSlotsToSetupPose();
+ tmpAnimationState.Update(0);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(0);
+ tmpSkeleton.UpdateWorldTransform();
+
+ // 切成 100 帧获取边界最大值
+ var bounds = getCurrentBounds();
+ float[] _ = [];
+ for (float tick = 0, delta = maxDuration / 100; tick < maxDuration; tick += delta)
+ {
+ tmpSkeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
+ if (x < bounds.X) bounds.X = x;
+ if (y < bounds.Y) bounds.Y = y;
+ if (w > bounds.Width) bounds.Width = w;
+ if (h > bounds.Height) bounds.Height = h;
+ tmpAnimationState.Update(delta);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(delta);
+ tmpSkeleton.UpdateWorldTransform();
+ }
+
+ return bounds;
+ }
+
protected override void update(float delta)
{
animationState.Update(delta);
@@ -189,18 +235,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform();
}
- private SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
- {
- return spineBlendMode switch
- {
- BlendMode.Normal => SFMLBlendMode.NormalPma,
- BlendMode.Additive => SFMLBlendMode.AdditivePma,
- BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
- BlendMode.Screen => SFMLBlendMode.ScreenPma,
- _ => throw new NotImplementedException($"{spineBlendMode}"),
- };
- }
-
protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{
triangleVertices.Clear();
diff --git a/SpineViewer/Spine/Implementations/SpineObject/SpineObject38.cs b/SpineViewer/Spine/Implementations/SpineObject/SpineObject38.cs
index 395108d..a1ff841 100644
--- a/SpineViewer/Spine/Implementations/SpineObject/SpineObject38.cs
+++ b/SpineViewer/Spine/Implementations/SpineObject/SpineObject38.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Xml.Linq;
using SpineRuntime38;
using SpineRuntime38.Attachments;
using SpineViewer.Utils;
@@ -14,7 +15,17 @@ namespace SpineViewer.Spine.Implementations.SpineObject
[SpineImplementation(SpineVersion.V38)]
internal class SpineObject38 : Spine.SpineObject
{
- private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
+ private static SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
+ {
+ return spineBlendMode switch
+ {
+ BlendMode.Normal => SFMLBlendMode.NormalPma,
+ BlendMode.Additive => SFMLBlendMode.AdditivePma,
+ BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
+ BlendMode.Screen => SFMLBlendMode.ScreenPma,
+ _ => throw new NotImplementedException($"{spineBlendMode}"),
+ };
+ }
private class TextureLoader : SpineRuntime38.TextureLoader
{
@@ -38,18 +49,19 @@ namespace SpineViewer.Spine.Implementations.SpineObject
}
}
- private static TextureLoader textureLoader = new();
+ private static readonly TextureLoader textureLoader = new();
+ private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
- private Atlas atlas;
- private SkeletonBinary? skeletonBinary;
- private SkeletonJson? skeletonJson;
- private SkeletonData skeletonData;
- private AnimationStateData animationStateData;
+ private readonly Atlas atlas;
+ private readonly SkeletonBinary? skeletonBinary;
+ private readonly SkeletonJson? skeletonJson;
+ private readonly SkeletonData skeletonData;
+ private readonly AnimationStateData animationStateData;
- private Skeleton skeleton;
- private AnimationState animationState;
+ private readonly Skeleton skeleton;
+ private readonly AnimationState animationState;
- private SkeletonClipping clipping = new();
+ private readonly SkeletonClipping clipping = new();
public SpineObject38(string skelPath, string atlasPath) : base(skelPath, atlasPath)
{
@@ -174,6 +186,48 @@ namespace SpineViewer.Spine.Implementations.SpineObject
return new RectangleF(x, y, w, h);
}
+ protected override RectangleF getBounds()
+ {
+ // 初始化临时对象
+ var maxDuration = 0f;
+ var tmpSkeleton = new Skeleton(skeletonData) { Skin = new(Guid.NewGuid().ToString()) };
+ var tmpAnimationState = new AnimationState(animationStateData);
+ tmpSkeleton.ScaleX = skeleton.ScaleX;
+ tmpSkeleton.ScaleY = skeleton.ScaleY;
+ tmpSkeleton.X = skeleton.X;
+ tmpSkeleton.Y = skeleton.Y;
+ foreach (var sk in loadedSkins) tmpSkeleton.Skin.AddSkin(skeletonData.FindSkin(sk));
+ foreach (var tr in animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null))
+ {
+ var ani = animationState.GetCurrent(tr).Animation;
+ tmpAnimationState.SetAnimation(tr, ani, true);
+ if (ani.Duration > maxDuration) maxDuration = ani.Duration;
+ }
+ tmpSkeleton.SetSlotsToSetupPose();
+ tmpAnimationState.Update(0);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(0);
+ tmpSkeleton.UpdateWorldTransform();
+
+ // 切成 100 帧获取边界最大值
+ var bounds = getCurrentBounds();
+ float[] _ = [];
+ for (float tick = 0, delta = maxDuration / 100; tick < maxDuration; tick += delta)
+ {
+ tmpSkeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
+ if (x < bounds.X) bounds.X = x;
+ if (y < bounds.Y) bounds.Y = y;
+ if (w > bounds.Width) bounds.Width = w;
+ if (h > bounds.Height) bounds.Height = h;
+ tmpAnimationState.Update(delta);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(delta);
+ tmpSkeleton.UpdateWorldTransform();
+ }
+
+ return bounds;
+ }
+
protected override void update(float delta)
{
animationState.Update(delta);
@@ -182,18 +236,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform();
}
- private SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
- {
- return spineBlendMode switch
- {
- BlendMode.Normal => SFMLBlendMode.NormalPma,
- BlendMode.Additive => SFMLBlendMode.AdditivePma,
- BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
- BlendMode.Screen => SFMLBlendMode.ScreenPma,
- _ => throw new NotImplementedException($"{spineBlendMode}"),
- };
- }
-
protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{
triangleVertices.Clear();
diff --git a/SpineViewer/Spine/Implementations/SpineObject/SpineObject40.cs b/SpineViewer/Spine/Implementations/SpineObject/SpineObject40.cs
index 3f22c5c..df0c483 100644
--- a/SpineViewer/Spine/Implementations/SpineObject/SpineObject40.cs
+++ b/SpineViewer/Spine/Implementations/SpineObject/SpineObject40.cs
@@ -13,7 +13,17 @@ namespace SpineViewer.Spine.Implementations.SpineObject
[SpineImplementation(SpineVersion.V40)]
internal class SpineObject40 : Spine.SpineObject
{
- private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
+ private static SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
+ {
+ return spineBlendMode switch
+ {
+ BlendMode.Normal => SFMLBlendMode.NormalPma,
+ BlendMode.Additive => SFMLBlendMode.AdditivePma,
+ BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
+ BlendMode.Screen => SFMLBlendMode.ScreenPma,
+ _ => throw new NotImplementedException($"{spineBlendMode}"),
+ };
+ }
private class TextureLoader : SpineRuntime40.TextureLoader
{
@@ -34,18 +44,19 @@ namespace SpineViewer.Spine.Implementations.SpineObject
}
}
- private static TextureLoader textureLoader = new();
+ private static readonly TextureLoader textureLoader = new();
+ private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
- private Atlas atlas;
- private SkeletonBinary? skeletonBinary;
- private SkeletonJson? skeletonJson;
- private SkeletonData skeletonData;
- private AnimationStateData animationStateData;
+ private readonly Atlas atlas;
+ private readonly SkeletonBinary? skeletonBinary;
+ private readonly SkeletonJson? skeletonJson;
+ private readonly SkeletonData skeletonData;
+ private readonly AnimationStateData animationStateData;
- private Skeleton skeleton;
- private AnimationState animationState;
+ private readonly Skeleton skeleton;
+ private readonly AnimationState animationState;
- private SkeletonClipping clipping = new();
+ private readonly SkeletonClipping clipping = new();
public SpineObject40(string skelPath, string atlasPath) : base(skelPath, atlasPath)
{
@@ -169,6 +180,48 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
return new RectangleF(x, y, w, h);
}
+
+ protected override RectangleF getBounds()
+ {
+ // 初始化临时对象
+ var maxDuration = 0f;
+ var tmpSkeleton = new Skeleton(skeletonData) { Skin = new(Guid.NewGuid().ToString()) };
+ var tmpAnimationState = new AnimationState(animationStateData);
+ tmpSkeleton.ScaleX = skeleton.ScaleX;
+ tmpSkeleton.ScaleY = skeleton.ScaleY;
+ tmpSkeleton.X = skeleton.X;
+ tmpSkeleton.Y = skeleton.Y;
+ foreach (var sk in loadedSkins) tmpSkeleton.Skin.AddSkin(skeletonData.FindSkin(sk));
+ foreach (var tr in animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null))
+ {
+ var ani = animationState.GetCurrent(tr).Animation;
+ tmpAnimationState.SetAnimation(tr, ani, true);
+ if (ani.Duration > maxDuration) maxDuration = ani.Duration;
+ }
+ tmpSkeleton.SetSlotsToSetupPose();
+ tmpAnimationState.Update(0);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(0);
+ tmpSkeleton.UpdateWorldTransform();
+
+ // 切成 100 帧获取边界最大值
+ var bounds = getCurrentBounds();
+ float[] _ = [];
+ for (float tick = 0, delta = maxDuration / 100; tick < maxDuration; tick += delta)
+ {
+ tmpSkeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
+ if (x < bounds.X) bounds.X = x;
+ if (y < bounds.Y) bounds.Y = y;
+ if (w > bounds.Width) bounds.Width = w;
+ if (h > bounds.Height) bounds.Height = h;
+ tmpAnimationState.Update(delta);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(delta);
+ tmpSkeleton.UpdateWorldTransform();
+ }
+
+ return bounds;
+ }
protected override void update(float delta)
{
@@ -178,18 +231,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform();
}
- private SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
- {
- return spineBlendMode switch
- {
- BlendMode.Normal => SFMLBlendMode.NormalPma,
- BlendMode.Additive => SFMLBlendMode.AdditivePma,
- BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
- BlendMode.Screen => SFMLBlendMode.ScreenPma,
- _ => throw new NotImplementedException($"{spineBlendMode}"),
- };
- }
-
protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{
triangleVertices.Clear();
diff --git a/SpineViewer/Spine/Implementations/SpineObject/SpineObject41.cs b/SpineViewer/Spine/Implementations/SpineObject/SpineObject41.cs
index 5d4004c..e13dada 100644
--- a/SpineViewer/Spine/Implementations/SpineObject/SpineObject41.cs
+++ b/SpineViewer/Spine/Implementations/SpineObject/SpineObject41.cs
@@ -13,7 +13,17 @@ namespace SpineViewer.Spine.Implementations.SpineObject
[SpineImplementation(SpineVersion.V41)]
internal class SpineObject41 : Spine.SpineObject
{
- private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
+ private static SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
+ {
+ return spineBlendMode switch
+ {
+ BlendMode.Normal => SFMLBlendMode.NormalPma,
+ BlendMode.Additive => SFMLBlendMode.AdditivePma,
+ BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
+ BlendMode.Screen => SFMLBlendMode.ScreenPma,
+ _ => throw new NotImplementedException($"{spineBlendMode}"),
+ };
+ }
private class TextureLoader : SpineRuntime41.TextureLoader
{
@@ -35,17 +45,18 @@ namespace SpineViewer.Spine.Implementations.SpineObject
}
private static TextureLoader textureLoader = new();
+ private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
- private Atlas atlas;
- private SkeletonBinary? skeletonBinary;
- private SkeletonJson? skeletonJson;
- private SkeletonData skeletonData;
- private AnimationStateData animationStateData;
+ private readonly Atlas atlas;
+ private readonly SkeletonBinary? skeletonBinary;
+ private readonly SkeletonJson? skeletonJson;
+ private readonly SkeletonData skeletonData;
+ private readonly AnimationStateData animationStateData;
- private Skeleton skeleton;
- private AnimationState animationState;
+ private readonly Skeleton skeleton;
+ private readonly AnimationState animationState;
- private SkeletonClipping clipping = new();
+ private readonly SkeletonClipping clipping = new();
public SpineObject41(string skelPath, string atlasPath) : base(skelPath, atlasPath)
{
@@ -170,6 +181,48 @@ namespace SpineViewer.Spine.Implementations.SpineObject
return new RectangleF(x, y, w, h);
}
+ protected override RectangleF getBounds()
+ {
+ // 初始化临时对象
+ var maxDuration = 0f;
+ var tmpSkeleton = new Skeleton(skeletonData) { Skin = new(Guid.NewGuid().ToString()) };
+ var tmpAnimationState = new AnimationState(animationStateData);
+ tmpSkeleton.ScaleX = skeleton.ScaleX;
+ tmpSkeleton.ScaleY = skeleton.ScaleY;
+ tmpSkeleton.X = skeleton.X;
+ tmpSkeleton.Y = skeleton.Y;
+ foreach (var sk in loadedSkins) tmpSkeleton.Skin.AddSkin(skeletonData.FindSkin(sk));
+ foreach (var tr in animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null))
+ {
+ var ani = animationState.GetCurrent(tr).Animation;
+ tmpAnimationState.SetAnimation(tr, ani, true);
+ if (ani.Duration > maxDuration) maxDuration = ani.Duration;
+ }
+ tmpSkeleton.SetSlotsToSetupPose();
+ tmpAnimationState.Update(0);
+ tmpAnimationState.Apply(tmpSkeleton);
+ //tmpSkeleton.Update(0);
+ tmpSkeleton.UpdateWorldTransform();
+
+ // 切成 100 帧获取边界最大值
+ var bounds = getCurrentBounds();
+ float[] _ = [];
+ for (float tick = 0, delta = maxDuration / 100; tick < maxDuration; tick += delta)
+ {
+ tmpSkeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
+ if (x < bounds.X) bounds.X = x;
+ if (y < bounds.Y) bounds.Y = y;
+ if (w > bounds.Width) bounds.Width = w;
+ if (h > bounds.Height) bounds.Height = h;
+ tmpAnimationState.Update(delta);
+ tmpAnimationState.Apply(tmpSkeleton);
+ //tmpSkeleton.Update(delta);
+ tmpSkeleton.UpdateWorldTransform();
+ }
+
+ return bounds;
+ }
+
protected override void update(float delta)
{
animationState.Update(delta);
@@ -178,18 +231,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform();
}
- private SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
- {
- return spineBlendMode switch
- {
- BlendMode.Normal => SFMLBlendMode.NormalPma,
- BlendMode.Additive => SFMLBlendMode.AdditivePma,
- BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
- BlendMode.Screen => SFMLBlendMode.ScreenPma,
- _ => throw new NotImplementedException($"{spineBlendMode}"),
- };
- }
-
protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{
triangleVertices.Clear();
diff --git a/SpineViewer/Spine/Implementations/SpineObject/Spineobject42.cs b/SpineViewer/Spine/Implementations/SpineObject/Spineobject42.cs
index d001d67..063c748 100644
--- a/SpineViewer/Spine/Implementations/SpineObject/Spineobject42.cs
+++ b/SpineViewer/Spine/Implementations/SpineObject/Spineobject42.cs
@@ -13,7 +13,17 @@ namespace SpineViewer.Spine.Implementations.SpineObject
[SpineImplementation(SpineVersion.V42)]
internal class Spineobject42 : Spine.SpineObject
{
- private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
+ private SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
+ {
+ return spineBlendMode switch
+ {
+ BlendMode.Normal => SFMLBlendMode.NormalPma,
+ BlendMode.Additive => SFMLBlendMode.AdditivePma,
+ BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
+ BlendMode.Screen => SFMLBlendMode.ScreenPma,
+ _ => throw new NotImplementedException($"{spineBlendMode}"),
+ };
+ }
private class TextureLoader : SpineRuntime42.TextureLoader
{
@@ -34,18 +44,19 @@ namespace SpineViewer.Spine.Implementations.SpineObject
}
}
- private static TextureLoader textureLoader = new();
+ private static readonly TextureLoader textureLoader = new();
+ private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
- private Atlas atlas;
- private SkeletonBinary? skeletonBinary;
- private SkeletonJson? skeletonJson;
- private SkeletonData skeletonData;
- private AnimationStateData animationStateData;
+ private readonly Atlas atlas;
+ private readonly SkeletonBinary? skeletonBinary;
+ private readonly SkeletonJson? skeletonJson;
+ private readonly SkeletonData skeletonData;
+ private readonly AnimationStateData animationStateData;
- private Skeleton skeleton;
- private AnimationState animationState;
+ private readonly Skeleton skeleton;
+ private readonly AnimationState animationState;
- private SkeletonClipping clipping = new();
+ private readonly SkeletonClipping clipping = new();
public Spineobject42(string skelPath, string atlasPath) : base(skelPath, atlasPath)
{
@@ -170,6 +181,48 @@ namespace SpineViewer.Spine.Implementations.SpineObject
return new RectangleF(x, y, w, h);
}
+ protected override RectangleF getBounds()
+ {
+ // 初始化临时对象
+ var maxDuration = 0f;
+ var tmpSkeleton = new Skeleton(skeletonData) { Skin = new(Guid.NewGuid().ToString()) };
+ var tmpAnimationState = new AnimationState(animationStateData);
+ tmpSkeleton.ScaleX = skeleton.ScaleX;
+ tmpSkeleton.ScaleY = skeleton.ScaleY;
+ tmpSkeleton.X = skeleton.X;
+ tmpSkeleton.Y = skeleton.Y;
+ foreach (var sk in loadedSkins) tmpSkeleton.Skin.AddSkin(skeletonData.FindSkin(sk));
+ foreach (var tr in animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null))
+ {
+ var ani = animationState.GetCurrent(tr).Animation;
+ tmpAnimationState.SetAnimation(tr, ani, true);
+ if (ani.Duration > maxDuration) maxDuration = ani.Duration;
+ }
+ tmpSkeleton.SetSlotsToSetupPose();
+ tmpAnimationState.Update(0);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(0);
+ tmpSkeleton.UpdateWorldTransform(Skeleton.Physics.Update);
+
+ // 切成 100 帧获取边界最大值
+ var bounds = getCurrentBounds();
+ float[] _ = [];
+ for (float tick = 0, delta = maxDuration / 100; tick < maxDuration; tick += delta)
+ {
+ tmpSkeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
+ if (x < bounds.X) bounds.X = x;
+ if (y < bounds.Y) bounds.Y = y;
+ if (w > bounds.Width) bounds.Width = w;
+ if (h > bounds.Height) bounds.Height = h;
+ tmpAnimationState.Update(delta);
+ tmpAnimationState.Apply(tmpSkeleton);
+ tmpSkeleton.Update(delta);
+ tmpSkeleton.UpdateWorldTransform(Skeleton.Physics.Update);
+ }
+
+ return bounds;
+ }
+
protected override void update(float delta)
{
animationState.Update(delta);
@@ -178,18 +231,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform(Skeleton.Physics.Update);
}
- private SFML.Graphics.BlendMode GetSFMLBlendMode(BlendMode spineBlendMode)
- {
- return spineBlendMode switch
- {
- BlendMode.Normal => SFMLBlendMode.NormalPma,
- BlendMode.Additive => SFMLBlendMode.AdditivePma,
- BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
- BlendMode.Screen => SFMLBlendMode.ScreenPma,
- _ => throw new NotImplementedException($"{spineBlendMode}"),
- };
- }
-
protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{
triangleVertices.Clear();
diff --git a/SpineViewer/Spine/SpineObject.cs b/SpineViewer/Spine/SpineObject.cs
index e85367e..73b5248 100644
--- a/SpineViewer/Spine/SpineObject.cs
+++ b/SpineViewer/Spine/SpineObject.cs
@@ -443,8 +443,8 @@ namespace SpineViewer.Spine
///
/// 获取当前参数下包围盒最大范围, 不是精确值
///
- //public RectangleF GetBounds() { }
- //protected abstract RectangleF getBounds();
+ public RectangleF GetBounds() { lock (_lock) return getBounds(); }
+ protected abstract RectangleF getBounds();
///
/// 更新内部状态