From 3044ef11564fe6c3fd33bda92920a3e06cb299bc Mon Sep 17 00:00:00 2001 From: Denis Claros Date: Fri, 3 Dec 2021 08:41:19 -0400 Subject: [PATCH 1/2] Implement new `mesh:update()` function (#1989) --- spine-solar2d/spine-solar2d/spine.lua | 119 +++++++++++++++++++------- 1 file changed, 89 insertions(+), 30 deletions(-) diff --git a/spine-solar2d/spine-solar2d/spine.lua b/spine-solar2d/spine-solar2d/spine.lua index 855da806b..8617adf29 100644 --- a/spine-solar2d/spine-solar2d/spine.lua +++ b/spine-solar2d/spine-solar2d/spine.lua @@ -89,7 +89,10 @@ spine.Skeleton.updateWorldTransform_super = spine.Skeleton.updateWorldTransform spine.Skeleton.new = function(skeletonData, group) local self = spine.Skeleton.new_super(skeletonData) self.group = group or display.newGroup() - self.drawingGroup = nil + self.drawingGroup = display.newGroup() + self.group:insert(self.drawingGroup) + self.slotData = {} + self.premultipliedAlpha = false self.batches = 0 self.tempColor = spine.Color.newWith(1, 1, 1, 1) @@ -124,6 +127,68 @@ end local worldVertices = spine.utils.newNumberArray(10000 * 8) +function spine.Skeleton:hideSlot(slot) + if not self.slotData[slot] then return end + self.slotData[slot].mesh.isVisible = false +end + +function spine.Skeleton:createSlot(slot, params) + local mesh = display.newMesh(self.drawingGroup, 0, 0, { + mode = "indexed", + vertices = params.vertices, + uvs = params.uvs, + indices = params.indices + }) + + mesh.x, mesh.y = mesh.path:getVertexOffset() + mesh.blendMode = params.blendMode + mesh.fill = params.texture + + local color = params.color + mesh:setFillColor(color.r, color.g, color.b, color.a) + + self.slotData[slot] = { + mesh = mesh, + indices = params.indices, + texture = params.texture, + uvs = params.uvs, + } +end + +function spine.Skeleton:updateSlot(slot, params) + if not self.slotData[slot] then + return self:createSlot(slot, params) + end + local slotData = self.slotData[slot] + local mesh = slotData.mesh + if #params.indices ~= #slotData.indices or #params.uvs ~= #slotData.uvs then + slotData.mesh:removeSelf() + self.slotData[slot] = nil + return self:createSlot(slot, params) + end + + mesh.path:update({ + vertices = params.vertices, + uvs = params.uvs, + indices = params.indices + }) + + mesh.isVisible = true + mesh.x, mesh.y = mesh.path:getVertexOffset() + mesh:toFront() + + local color = params.color + mesh:setFillColor(color.r, color.g, color.b, color.a) + + if slotData.texture ~= params.texture then + slotData.texture = params.texture + mesh.fill = params.texture + end + if mesh.blendMode ~= params.blendMode then + mesh.blendMode = params.blendMode + end +end + function spine.Skeleton:updateWorldTransform() spine.Skeleton.updateWorldTransform_super(self) local premultipliedAlpha = self.premultipliedAlpha @@ -132,14 +197,7 @@ function spine.Skeleton:updateWorldTransform() if (self.vertexEffect) then self.vertexEffect:beginEffect(self) end - -- Remove old drawing group, we will start anew - if self.drawingGroup and self.drawingGroup.removeSelf then self.drawingGroup:removeSelf() end - local drawingGroup = display.newGroup() - self.drawingGroup = drawingGroup - self.group:insert(drawingGroup) - local drawOrder = self.drawOrder - local currentGroup = nil local groupVertices = {} local groupIndices = {} local groupUvs = {} @@ -150,12 +208,12 @@ function spine.Skeleton:updateWorldTransform() local lastTexture = nil local blendMode = nil local lastBlendMode = nil - local renderable = { - vertices = nil, - uvs = nil - } - for _,slot in ipairs(drawOrder) do + for k, v in pairs(self.slotData) do + self:hideSlot(k) + end + + for i,slot in ipairs(drawOrder) do local attachment = slot.attachment local vertices = nil local uvs = nil @@ -205,7 +263,15 @@ function spine.Skeleton:updateWorldTransform() if not lastBlendMode then lastBlendMode = blendMode end if (texture ~= lastTexture or not colorEquals(color, lastColor) or blendMode ~= lastBlendMode) then - self:flush(groupVertices, groupUvs, groupIndices, lastTexture, lastColor, lastBlendMode, drawingGroup) + local lastSlot = drawOrder[i-1] + self:updateSlot(lastSlot, { + texture = lastTexture, + color = lastColor, + blendMode = lastBlendMode, + vertices = groupVertices, + uvs = groupUvs, + indices = groupIndices + }) lastTexture = texture lastColor:setFrom(color) lastBlendMode = blendMode @@ -233,28 +299,21 @@ function spine.Skeleton:updateWorldTransform() end if #groupVertices > 0 then - self:flush(groupVertices, groupUvs, groupIndices, texture, color, blendMode, drawingGroup) + local slot = drawOrder[#drawOrder] + self:updateSlot(slot, { + texture = texture, + color = color, + blendMode = blendMode, + vertices = groupVertices, + uvs = groupUvs, + indices = groupIndices + }) end self.clipper:clipEnd2() if (self.vertexEffect) then self.vertexEffect:endEffect() end end -function spine.Skeleton:flush(groupVertices, groupUvs, groupIndices, texture, color, blendMode, drawingGroup) - local mesh = display.newMesh(drawingGroup, 0, 0, { - mode = "indexed", - vertices = groupVertices, - uvs = groupUvs, - indices = groupIndices - }) - mesh.fill = texture - mesh:setFillColor(color.r, color.g, color.b) - mesh.alpha = color.a - mesh.blendMode = blendMode - mesh:translate(mesh.path:getVertexOffset()) - self.batches = self.batches + 1 -end - function spine.Skeleton:batch(vertices, uvs, numVertices, indices, groupVertices, groupUvs, groupIndices) local numIndices = #indices local i = 1 From bb7804ac9e653f7dbf3f347e15e02479c171bc22 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 7 Dec 2021 19:13:03 +0100 Subject: [PATCH 2/2] [unity] Fixed root motion components ignoring split translate timelines. Closes #1997. --- .../RootMotion/SkeletonRootMotionBase.cs | 43 ++++++++++++++++--- .../spine-unity/Utility/TimelineExtensions.cs | 34 +++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs index 6b344f7bd..34072190c 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs @@ -196,24 +196,39 @@ namespace Spine.Unity { public Vector2 GetAnimationRootMotion (float startTime, float endTime, Animation animation) { - var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); + TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); if (timeline != null) { return GetTimelineMovementDelta(startTime, endTime, timeline, animation); } + TranslateXTimeline xTimeline = animation.FindTimelineForBone(rootMotionBoneIndex); + TranslateYTimeline yTimeline = animation.FindTimelineForBone(rootMotionBoneIndex); + if (xTimeline != null || yTimeline != null) { + return GetTimelineMovementDelta(startTime, endTime, xTimeline, yTimeline, animation); + } return Vector2.zero; } public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) { RootMotionInfo rootMotion = new RootMotionInfo(); - var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); + float duration = animation.Duration; + float mid = duration * 0.5f; + rootMotion.timeIsPastMid = currentTime > mid; + TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); if (timeline != null) { - float duration = animation.Duration; - float mid = duration * 0.5f; rootMotion.start = timeline.Evaluate(0); rootMotion.current = timeline.Evaluate(currentTime); rootMotion.mid = timeline.Evaluate(mid); rootMotion.end = timeline.Evaluate(duration); - rootMotion.timeIsPastMid = currentTime > mid; + return rootMotion; + } + TranslateXTimeline xTimeline = animation.FindTimelineForBone(rootMotionBoneIndex); + TranslateYTimeline yTimeline = animation.FindTimelineForBone(rootMotionBoneIndex); + if (xTimeline != null || yTimeline != null) { + rootMotion.start = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0); + rootMotion.current = TimelineExtensions.Evaluate(xTimeline, yTimeline, currentTime); + rootMotion.mid = TimelineExtensions.Evaluate(xTimeline, yTimeline, mid); + rootMotion.end = TimelineExtensions.Evaluate(xTimeline, yTimeline, duration); + return rootMotion; } return rootMotion; } @@ -232,6 +247,24 @@ namespace Spine.Unity { return currentDelta; } + Vector2 GetTimelineMovementDelta (float startTime, float endTime, + TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, Animation animation) { + + Vector2 currentDelta; + if (startTime > endTime) // Looped + currentDelta = + (TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration) + - TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime)) + + (TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime) + - TimelineExtensions.Evaluate(xTimeline, yTimeline, 0)); + else if (startTime != endTime) // Non-looped + currentDelta = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime) + - TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime); + else + currentDelta = Vector2.zero; + return currentDelta; + } + void GatherTopLevelBones () { topLevelBones.Clear(); var skeleton = skeletonComponent.Skeleton; diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs index 9d9b60093..39a806e93 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs @@ -52,6 +52,27 @@ namespace Spine.Unity.AnimationTools { } } + /// Evaluates the resulting value of a pair of split translate timelines at a given time. + /// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData. + /// If no SkeletonData is given, values are returned as difference to setup pose + /// instead of absolute values. + public static Vector2 Evaluate (TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, + float time, SkeletonData skeletonData = null) { + + float x = 0, y = 0; + if (xTimeline != null && time > xTimeline.Frames[0]) x = xTimeline.GetCurveValue(time); + if (yTimeline != null && time > yTimeline.Frames[0]) y = yTimeline.GetCurveValue(time); + + if (skeletonData == null) { + return new Vector2(x, y); + } else { + var bonesItems = skeletonData.Bones.Items; + BoneData boneDataX = bonesItems[xTimeline.BoneIndex]; + BoneData boneDataY = bonesItems[yTimeline.BoneIndex]; + return new Vector2(boneDataX.X + x, boneDataY.Y + y); + } + } + /// Gets the translate timeline for a given boneIndex. /// You can get the boneIndex using SkeletonData.FindBoneIndex. /// The root bone is always boneIndex 0. @@ -67,5 +88,18 @@ namespace Spine.Unity.AnimationTools { } return null; } + + /// Gets the IBoneTimeline timeline of a given type for a given boneIndex. + /// You can get the boneIndex using SkeletonData.FindBoneIndex. + /// The root bone is always boneIndex 0. + /// This will return null if a timeline of the given type is not found. + public static T FindTimelineForBone (this Animation a, int boneIndex) where T : class, IBoneTimeline { + foreach (var timeline in a.Timelines) { + T translateTimeline = timeline as T; + if (translateTimeline != null && translateTimeline.BoneIndex == boneIndex) + return translateTimeline; + } + return null; + } } }