From d2d8b7955cd455cb44f30a9c3540953d347e4948 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Tue, 15 Apr 2025 20:20:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0GetBounds=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E5=8C=85=E5=9B=B4=E7=9B=92=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SpineObject/SpineObject21.cs | 86 ++++++++++--------- .../SpineObject/SpineObject36.cs | 58 ++++++++++--- .../SpineObject/SpineObject37.cs | 58 ++++++++++--- .../SpineObject/SpineObject38.cs | 86 ++++++++++++++----- .../SpineObject/SpineObject40.cs | 85 +++++++++++++----- .../SpineObject/SpineObject41.cs | 83 +++++++++++++----- .../SpineObject/Spineobject42.cs | 85 +++++++++++++----- SpineViewer/Spine/SpineObject.cs | 4 +- 8 files changed, 391 insertions(+), 154 deletions(-) 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(); /// /// 更新内部状态