增加GetBounds获取最大包围盒方法

This commit is contained in:
ww-rm
2025-04-15 20:20:18 +08:00
parent 2a55fd9c36
commit d2d8b7955c
8 changed files with 391 additions and 154 deletions

View File

@@ -206,49 +206,53 @@ namespace SpineViewer.Spine.Implementations.SpineObject
protected override RectangleF getCurrentBounds() protected override RectangleF getCurrentBounds()
{ {
float[] temp = new float[8]; skeleton.GetBounds(out var x, out var y, out var w, out var h);
var drawOrderItems = skeleton.DrawOrder; return new RectangleF(x, y, w, h);
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);
}
}
if (vertices != null) protected override RectangleF getBounds()
{ {
for (int ii = 0; ii < verticesLength; ii += 2) // 初始化临时对象
{ var maxDuration = 0f;
float vx = vertices[ii], vy = vertices[ii + 1]; var tmpSkeleton = new Skeleton(skeletonData) { Skin = new(Guid.NewGuid().ToString()) };
minX = Math.Min(minX, vx); var tmpAnimationState = new AnimationState(animationStateData);
minY = Math.Min(minY, vy); tmpSkeleton.FlipX = skeleton.FlipX;
maxX = Math.Max(maxX, vx); tmpSkeleton.FlipY = skeleton.FlipY;
maxY = Math.Max(maxY, vy); 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) protected override void update(float delta)

View File

@@ -210,6 +210,52 @@ namespace SpineViewer.Spine.Implementations.SpineObject
return new RectangleF(x, y, w, h); 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) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);
@@ -218,18 +264,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform(); 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) protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{ {
triangleVertices.Clear(); triangleVertices.Clear();

View File

@@ -181,6 +181,52 @@ namespace SpineViewer.Spine.Implementations.SpineObject
return new RectangleF(x, y, w, h); 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) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);
@@ -189,18 +235,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform(); 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) protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{ {
triangleVertices.Clear(); triangleVertices.Clear();

View File

@@ -5,6 +5,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq;
using SpineRuntime38; using SpineRuntime38;
using SpineRuntime38.Attachments; using SpineRuntime38.Attachments;
using SpineViewer.Utils; using SpineViewer.Utils;
@@ -14,7 +15,17 @@ namespace SpineViewer.Spine.Implementations.SpineObject
[SpineImplementation(SpineVersion.V38)] [SpineImplementation(SpineVersion.V38)]
internal class SpineObject38 : Spine.SpineObject 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 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 readonly Atlas atlas;
private SkeletonBinary? skeletonBinary; private readonly SkeletonBinary? skeletonBinary;
private SkeletonJson? skeletonJson; private readonly SkeletonJson? skeletonJson;
private SkeletonData skeletonData; private readonly SkeletonData skeletonData;
private AnimationStateData animationStateData; private readonly AnimationStateData animationStateData;
private Skeleton skeleton; private readonly Skeleton skeleton;
private AnimationState animationState; private readonly AnimationState animationState;
private SkeletonClipping clipping = new(); private readonly SkeletonClipping clipping = new();
public SpineObject38(string skelPath, string atlasPath) : base(skelPath, atlasPath) 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); 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) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);
@@ -182,18 +236,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform(); 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) protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{ {
triangleVertices.Clear(); triangleVertices.Clear();

View File

@@ -13,7 +13,17 @@ namespace SpineViewer.Spine.Implementations.SpineObject
[SpineImplementation(SpineVersion.V40)] [SpineImplementation(SpineVersion.V40)]
internal class SpineObject40 : Spine.SpineObject 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 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 readonly Atlas atlas;
private SkeletonBinary? skeletonBinary; private readonly SkeletonBinary? skeletonBinary;
private SkeletonJson? skeletonJson; private readonly SkeletonJson? skeletonJson;
private SkeletonData skeletonData; private readonly SkeletonData skeletonData;
private AnimationStateData animationStateData; private readonly AnimationStateData animationStateData;
private Skeleton skeleton; private readonly Skeleton skeleton;
private AnimationState animationState; private readonly AnimationState animationState;
private SkeletonClipping clipping = new(); private readonly SkeletonClipping clipping = new();
public SpineObject40(string skelPath, string atlasPath) : base(skelPath, atlasPath) 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 _); skeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
return new RectangleF(x, y, w, h); 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) protected override void update(float delta)
{ {
@@ -178,18 +231,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform(); 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) protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{ {
triangleVertices.Clear(); triangleVertices.Clear();

View File

@@ -13,7 +13,17 @@ namespace SpineViewer.Spine.Implementations.SpineObject
[SpineImplementation(SpineVersion.V41)] [SpineImplementation(SpineVersion.V41)]
internal class SpineObject41 : Spine.SpineObject 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 private class TextureLoader : SpineRuntime41.TextureLoader
{ {
@@ -35,17 +45,18 @@ namespace SpineViewer.Spine.Implementations.SpineObject
} }
private static TextureLoader textureLoader = new(); private static TextureLoader textureLoader = new();
private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
private Atlas atlas; private readonly Atlas atlas;
private SkeletonBinary? skeletonBinary; private readonly SkeletonBinary? skeletonBinary;
private SkeletonJson? skeletonJson; private readonly SkeletonJson? skeletonJson;
private SkeletonData skeletonData; private readonly SkeletonData skeletonData;
private AnimationStateData animationStateData; private readonly AnimationStateData animationStateData;
private Skeleton skeleton; private readonly Skeleton skeleton;
private AnimationState animationState; private readonly AnimationState animationState;
private SkeletonClipping clipping = new(); private readonly SkeletonClipping clipping = new();
public SpineObject41(string skelPath, string atlasPath) : base(skelPath, atlasPath) 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); 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) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);
@@ -178,18 +231,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform(); 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) protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{ {
triangleVertices.Clear(); triangleVertices.Clear();

View File

@@ -13,7 +13,17 @@ namespace SpineViewer.Spine.Implementations.SpineObject
[SpineImplementation(SpineVersion.V42)] [SpineImplementation(SpineVersion.V42)]
internal class Spineobject42 : Spine.SpineObject 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 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 readonly Atlas atlas;
private SkeletonBinary? skeletonBinary; private readonly SkeletonBinary? skeletonBinary;
private SkeletonJson? skeletonJson; private readonly SkeletonJson? skeletonJson;
private SkeletonData skeletonData; private readonly SkeletonData skeletonData;
private AnimationStateData animationStateData; private readonly AnimationStateData animationStateData;
private Skeleton skeleton; private readonly Skeleton skeleton;
private AnimationState animationState; private readonly AnimationState animationState;
private SkeletonClipping clipping = new(); private readonly SkeletonClipping clipping = new();
public Spineobject42(string skelPath, string atlasPath) : base(skelPath, atlasPath) 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); 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) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);
@@ -178,18 +231,6 @@ namespace SpineViewer.Spine.Implementations.SpineObject
skeleton.UpdateWorldTransform(Skeleton.Physics.Update); 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) protected override void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
{ {
triangleVertices.Clear(); triangleVertices.Clear();

View File

@@ -443,8 +443,8 @@ namespace SpineViewer.Spine
/// <summary> /// <summary>
/// 获取当前参数下包围盒最大范围, 不是精确值 /// 获取当前参数下包围盒最大范围, 不是精确值
/// </summary> /// </summary>
//public RectangleF GetBounds() { } public RectangleF GetBounds() { lock (_lock) return getBounds(); }
//protected abstract RectangleF getBounds(); protected abstract RectangleF getBounds();
/// <summary> /// <summary>
/// 更新内部状态 /// 更新内部状态