Flip timelines for spine-lua, spine-corona, spine-love.
@ -38,4 +38,3 @@ Runtime:addEventListener("enterFrame", function (event)
|
|||||||
state:apply(skeleton)
|
state:apply(skeleton)
|
||||||
skeleton:updateWorldTransform()
|
skeleton:updateWorldTransform()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@ -1,290 +0,0 @@
|
|||||||
|
|
||||||
dragon.png
|
|
||||||
format: RGBA4444
|
|
||||||
filter: Nearest,Nearest
|
|
||||||
repeat: none
|
|
||||||
L_rear_thigh
|
|
||||||
rotate: false
|
|
||||||
xy: 895, 20
|
|
||||||
size: 91, 148
|
|
||||||
orig: 91, 149
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_wing01
|
|
||||||
rotate: false
|
|
||||||
xy: 814, 672
|
|
||||||
size: 191, 256
|
|
||||||
orig: 191, 256
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_wing02
|
|
||||||
rotate: false
|
|
||||||
xy: 714, 189
|
|
||||||
size: 179, 269
|
|
||||||
orig: 179, 269
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_wing03
|
|
||||||
rotate: false
|
|
||||||
xy: 785, 463
|
|
||||||
size: 186, 207
|
|
||||||
orig: 186, 207
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_wing05
|
|
||||||
rotate: true
|
|
||||||
xy: 2, 9
|
|
||||||
size: 218, 213
|
|
||||||
orig: 218, 213
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_wing06
|
|
||||||
rotate: false
|
|
||||||
xy: 2, 229
|
|
||||||
size: 192, 331
|
|
||||||
orig: 192, 331
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing01
|
|
||||||
rotate: true
|
|
||||||
xy: 502, 709
|
|
||||||
size: 219, 310
|
|
||||||
orig: 219, 310
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing02
|
|
||||||
rotate: true
|
|
||||||
xy: 204, 463
|
|
||||||
size: 203, 305
|
|
||||||
orig: 203, 305
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing03
|
|
||||||
rotate: false
|
|
||||||
xy: 511, 460
|
|
||||||
size: 272, 247
|
|
||||||
orig: 272, 247
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing05
|
|
||||||
rotate: false
|
|
||||||
xy: 196, 232
|
|
||||||
size: 251, 229
|
|
||||||
orig: 251, 229
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing06
|
|
||||||
rotate: false
|
|
||||||
xy: 2, 562
|
|
||||||
size: 200, 366
|
|
||||||
orig: 200, 366
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing07
|
|
||||||
rotate: true
|
|
||||||
xy: 449, 258
|
|
||||||
size: 200, 263
|
|
||||||
orig: 200, 263
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing08
|
|
||||||
rotate: false
|
|
||||||
xy: 467, 2
|
|
||||||
size: 234, 254
|
|
||||||
orig: 234, 254
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing09
|
|
||||||
rotate: false
|
|
||||||
xy: 217, 26
|
|
||||||
size: 248, 204
|
|
||||||
orig: 248, 204
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
back
|
|
||||||
rotate: false
|
|
||||||
xy: 703, 2
|
|
||||||
size: 190, 185
|
|
||||||
orig: 190, 185
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
chest
|
|
||||||
rotate: true
|
|
||||||
xy: 895, 170
|
|
||||||
size: 136, 122
|
|
||||||
orig: 136, 122
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
front_toeA
|
|
||||||
rotate: false
|
|
||||||
xy: 976, 972
|
|
||||||
size: 29, 50
|
|
||||||
orig: 29, 50
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
head
|
|
||||||
rotate: false
|
|
||||||
xy: 204, 668
|
|
||||||
size: 296, 260
|
|
||||||
orig: 296, 260
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
logo
|
|
||||||
rotate: false
|
|
||||||
xy: 2, 930
|
|
||||||
size: 897, 92
|
|
||||||
orig: 897, 92
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
tail01
|
|
||||||
rotate: false
|
|
||||||
xy: 895, 308
|
|
||||||
size: 120, 153
|
|
||||||
orig: 120, 153
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
tail03
|
|
||||||
rotate: false
|
|
||||||
xy: 901, 930
|
|
||||||
size: 73, 92
|
|
||||||
orig: 73, 92
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
|
|
||||||
dragon2.png
|
|
||||||
format: RGBA4444
|
|
||||||
filter: Nearest,Nearest
|
|
||||||
repeat: none
|
|
||||||
L_front_leg
|
|
||||||
rotate: true
|
|
||||||
xy: 391, 141
|
|
||||||
size: 84, 57
|
|
||||||
orig: 84, 57
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_front_thigh
|
|
||||||
rotate: false
|
|
||||||
xy: 446, 269
|
|
||||||
size: 84, 72
|
|
||||||
orig: 84, 72
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_rear_leg
|
|
||||||
rotate: true
|
|
||||||
xy: 888, 342
|
|
||||||
size: 168, 132
|
|
||||||
orig: 206, 177
|
|
||||||
offset: 19, 20
|
|
||||||
index: -1
|
|
||||||
L_wing04
|
|
||||||
rotate: false
|
|
||||||
xy: 256, 227
|
|
||||||
size: 188, 135
|
|
||||||
orig: 188, 135
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_wing07
|
|
||||||
rotate: false
|
|
||||||
xy: 2, 109
|
|
||||||
size: 159, 255
|
|
||||||
orig: 159, 255
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_wing08
|
|
||||||
rotate: true
|
|
||||||
xy: 705, 346
|
|
||||||
size: 164, 181
|
|
||||||
orig: 164, 181
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
L_wing09
|
|
||||||
rotate: false
|
|
||||||
xy: 499, 343
|
|
||||||
size: 204, 167
|
|
||||||
orig: 204, 167
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_front_leg
|
|
||||||
rotate: false
|
|
||||||
xy: 273, 34
|
|
||||||
size: 101, 89
|
|
||||||
orig: 101, 89
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_front_thigh
|
|
||||||
rotate: false
|
|
||||||
xy: 163, 106
|
|
||||||
size: 108, 108
|
|
||||||
orig: 108, 108
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_rear_leg
|
|
||||||
rotate: false
|
|
||||||
xy: 273, 125
|
|
||||||
size: 116, 100
|
|
||||||
orig: 116, 100
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_rear_thigh
|
|
||||||
rotate: false
|
|
||||||
xy: 163, 216
|
|
||||||
size: 91, 148
|
|
||||||
orig: 91, 149
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
R_wing04
|
|
||||||
rotate: false
|
|
||||||
xy: 2, 366
|
|
||||||
size: 279, 144
|
|
||||||
orig: 279, 144
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
chin
|
|
||||||
rotate: false
|
|
||||||
xy: 283, 364
|
|
||||||
size: 214, 146
|
|
||||||
orig: 214, 146
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
front_toeB
|
|
||||||
rotate: false
|
|
||||||
xy: 590, 284
|
|
||||||
size: 56, 57
|
|
||||||
orig: 56, 57
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
rear-toe
|
|
||||||
rotate: true
|
|
||||||
xy: 2, 2
|
|
||||||
size: 105, 77
|
|
||||||
orig: 109, 77
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
tail02
|
|
||||||
rotate: true
|
|
||||||
xy: 151, 9
|
|
||||||
size: 95, 120
|
|
||||||
orig: 95, 120
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
tail04
|
|
||||||
rotate: false
|
|
||||||
xy: 532, 270
|
|
||||||
size: 56, 71
|
|
||||||
orig: 56, 71
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
tail05
|
|
||||||
rotate: false
|
|
||||||
xy: 648, 282
|
|
||||||
size: 52, 59
|
|
||||||
orig: 52, 59
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
tail06
|
|
||||||
rotate: true
|
|
||||||
xy: 81, 12
|
|
||||||
size: 95, 68
|
|
||||||
orig: 95, 68
|
|
||||||
offset: 0, 0
|
|
||||||
index: -1
|
|
||||||
39
spine-corona/examples/hero.lua
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
-- This skeleton uses IK for the feet.
|
||||||
|
|
||||||
|
local spine = require "spine-corona.spine"
|
||||||
|
|
||||||
|
local json = spine.SkeletonJson.new()
|
||||||
|
local skeletonData = json:readSkeletonDataFile("examples/hero/hero.json")
|
||||||
|
|
||||||
|
local skeleton = spine.Skeleton.new(skeletonData)
|
||||||
|
function skeleton:createImage (attachment)
|
||||||
|
return display.newImage("examples/hero/images/" .. attachment.name .. ".png")
|
||||||
|
end
|
||||||
|
skeleton.group.x = 195
|
||||||
|
skeleton.group.y = 385
|
||||||
|
skeleton.flipX = false
|
||||||
|
skeleton.flipY = false
|
||||||
|
skeleton.debug = true -- Omit or set to false to not draw debug lines on top of the images.
|
||||||
|
skeleton:setToSetupPose()
|
||||||
|
|
||||||
|
-- AnimationStateData defines crossfade durations between animations.
|
||||||
|
local stateData = spine.AnimationStateData.new(skeletonData)
|
||||||
|
-- AnimationState has a queue of animations and can apply them with crossfading.
|
||||||
|
local state = spine.AnimationState.new(stateData)
|
||||||
|
--state:setAnimationByName(0, "Idle", true, 0)
|
||||||
|
state:setAnimationByName(0, "Walk", true, 0)
|
||||||
|
|
||||||
|
local lastTime = 0
|
||||||
|
local animationTime = 0
|
||||||
|
Runtime:addEventListener("enterFrame", function (event)
|
||||||
|
-- Compute time in seconds since last frame.
|
||||||
|
local currentTime = event.time / 1000
|
||||||
|
local delta = currentTime - lastTime
|
||||||
|
lastTime = currentTime
|
||||||
|
|
||||||
|
-- Update the state with the delta time, apply it, and update the world transforms.
|
||||||
|
state:update(delta)
|
||||||
|
state:apply(skeleton)
|
||||||
|
skeleton:updateWorldTransform()
|
||||||
|
end)
|
||||||
5
spine-corona/examples/hero/hero.json
Normal file
BIN
spine-corona/examples/hero/images/body.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
spine-corona/examples/hero/images/cape.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
spine-corona/examples/hero/images/eyes.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
spine-corona/examples/hero/images/fingers.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
spine-corona/examples/hero/images/foot1.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
spine-corona/examples/hero/images/foot2.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
spine-corona/examples/hero/images/forearm1.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
spine-corona/examples/hero/images/forearm2.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
spine-corona/examples/hero/images/hand1.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
spine-corona/examples/hero/images/hand2.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
spine-corona/examples/hero/images/head.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
5
spine-corona/examples/hero/images/license.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Copyright (c) 2014, XDTech
|
||||||
|
|
||||||
|
The project file and images in this "Hero" project are provided for
|
||||||
|
demonstration purposes only and may not be redistributed for any reason nor
|
||||||
|
used as the basis for derivative work.
|
||||||
BIN
spine-corona/examples/hero/images/mantles.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
spine-corona/examples/hero/images/mouth.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
spine-corona/examples/hero/images/shin1.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
spine-corona/examples/hero/images/shin2.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
spine-corona/examples/hero/images/sword.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
spine-corona/examples/hero/images/thigh1.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
spine-corona/examples/hero/images/thigh2.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
spine-corona/examples/hero/images/upperarm1.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
spine-corona/examples/hero/images/upperarm2.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
require "examples.spineboy"
|
-- require "examples.spineboy"
|
||||||
-- require "examples.goblins"
|
-- require "examples.goblins"
|
||||||
-- require "examples.dragon"
|
-- require "examples.dragon"
|
||||||
|
require "examples.hero"
|
||||||
|
|||||||
@ -128,10 +128,11 @@ function spine.Skeleton.new (skeletonData, group)
|
|||||||
end
|
end
|
||||||
-- Position image based on attachment and bone.
|
-- Position image based on attachment and bone.
|
||||||
if image ~= spine.Skeleton.failed then
|
if image ~= spine.Skeleton.failed then
|
||||||
local flipX, flipY = ((self.flipX and -1) or 1), ((self.flipY and -1) or 1)
|
local bone = slot.bone
|
||||||
|
local flipX, flipY = ((bone.worldFlipX and -1) or 1), ((bone.worldFlipY and -1) or 1)
|
||||||
|
|
||||||
local x = slot.bone.worldX + attachment.x * slot.bone.m00 + attachment.y * slot.bone.m01
|
local x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01
|
||||||
local y = -(slot.bone.worldY + attachment.x * slot.bone.m10 + attachment.y * slot.bone.m11)
|
local y = -(bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11)
|
||||||
if not image.lastX then
|
if not image.lastX then
|
||||||
image.x, image.y = x, y
|
image.x, image.y = x, y
|
||||||
image.lastX, image.lastY = x, y
|
image.lastX, image.lastY = x, y
|
||||||
@ -145,16 +146,16 @@ function spine.Skeleton.new (skeletonData, group)
|
|||||||
-- Fix scaling when attachment is rotated 90 or -90.
|
-- Fix scaling when attachment is rotated 90 or -90.
|
||||||
local rotation = math.abs(attachment.rotation) % 180
|
local rotation = math.abs(attachment.rotation) % 180
|
||||||
if (rotation == 90) then
|
if (rotation == 90) then
|
||||||
xScale = xScale * slot.bone.worldScaleY
|
xScale = xScale * bone.worldScaleY
|
||||||
yScale = yScale * slot.bone.worldScaleX
|
yScale = yScale * bone.worldScaleX
|
||||||
else
|
else
|
||||||
xScale = xScale * slot.bone.worldScaleX
|
xScale = xScale * bone.worldScaleX
|
||||||
yScale = yScale * slot.bone.worldScaleY
|
yScale = yScale * bone.worldScaleY
|
||||||
if rotation ~= 0 and xScale ~= yScale and not image.rotationWarning then
|
if rotation ~= 0 and xScale ~= yScale and not image.rotationWarning then
|
||||||
image.rotationWarning = true
|
image.rotationWarning = true
|
||||||
print("WARNING: Non-uniform bone scaling with attachments not rotated to\n"
|
print("WARNING: Non-uniform bone scaling with attachments not rotated to\n"
|
||||||
.." cardinal angles will not work as expected with Corona.\n"
|
.." cardinal angles will not work as expected with Corona.\n"
|
||||||
.." Bone: "..slot.bone.data.name..", slot: "..slot.data.name..", attachment: "..attachment.name)
|
.." Bone: "..bone.data.name..", slot: "..slot.data.name..", attachment: "..attachment.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not image.lastScaleX then
|
if not image.lastScaleX then
|
||||||
@ -165,7 +166,7 @@ function spine.Skeleton.new (skeletonData, group)
|
|||||||
image.lastScaleX, image.lastScaleY = xScale, yScale
|
image.lastScaleX, image.lastScaleY = xScale, yScale
|
||||||
end
|
end
|
||||||
|
|
||||||
rotation = -(slot.bone.worldRotation + attachment.rotation) * flipX * flipY
|
rotation = -(bone.worldRotation + attachment.rotation) * flipX * flipY
|
||||||
if not image.lastRotation then
|
if not image.lastRotation then
|
||||||
image.rotation = rotation
|
image.rotation = rotation
|
||||||
image.lastRotation = rotation
|
image.lastRotation = rotation
|
||||||
@ -199,13 +200,13 @@ function spine.Skeleton.new (skeletonData, group)
|
|||||||
bone.line.x = bone.worldX
|
bone.line.x = bone.worldX
|
||||||
bone.line.y = -bone.worldY
|
bone.line.y = -bone.worldY
|
||||||
bone.line.rotation = -bone.worldRotation
|
bone.line.rotation = -bone.worldRotation
|
||||||
if self.flipX then
|
if bone.worldFlipX then
|
||||||
bone.line.xScale = -1
|
bone.line.xScale = -1
|
||||||
bone.line.rotation = -bone.line.rotation
|
bone.line.rotation = -bone.line.rotation
|
||||||
else
|
else
|
||||||
bone.line.xScale = 1
|
bone.line.xScale = 1
|
||||||
end
|
end
|
||||||
if self.flipY then
|
if bone.worldFlipY then
|
||||||
bone.line.yScale = -1
|
bone.line.yScale = -1
|
||||||
bone.line.rotation = -bone.line.rotation
|
bone.line.rotation = -bone.line.rotation
|
||||||
else
|
else
|
||||||
|
|||||||
@ -83,6 +83,22 @@ local function binarySearch (values, target, step)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function binarySearch1 (values, target)
|
||||||
|
local low = 0
|
||||||
|
local high = math.floor(#values - 1)
|
||||||
|
if high == 0 then return 1 end
|
||||||
|
local current = math.floor(high / 2)
|
||||||
|
while true do
|
||||||
|
if values[current + 1] <= target then
|
||||||
|
low = current + 1
|
||||||
|
else
|
||||||
|
high = current
|
||||||
|
end
|
||||||
|
if low == high then return low + 1 end
|
||||||
|
current = math.floor((low + high) / 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function linearSearch (values, target, step)
|
local function linearSearch (values, target, step)
|
||||||
for i = 0, #values, step do
|
for i = 0, #values, step do
|
||||||
if (values[i] > target) then return i end
|
if (values[i] > target) then return i end
|
||||||
@ -419,7 +435,7 @@ function Animation.AttachmentTimeline.new ()
|
|||||||
if time >= frames[#frames] then -- Time is after last frame.
|
if time >= frames[#frames] then -- Time is after last frame.
|
||||||
frameIndex = #frames
|
frameIndex = #frames
|
||||||
else
|
else
|
||||||
frameIndex = binarySearch(frames, time, 1) - 1
|
frameIndex = binarySearch1(frames, time) - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local attachmentName = self.attachmentNames[frameIndex]
|
local attachmentName = self.attachmentNames[frameIndex]
|
||||||
@ -477,7 +493,7 @@ function Animation.EventTimeline.new ()
|
|||||||
if lastTime < frames[0] then
|
if lastTime < frames[0] then
|
||||||
frameIndex = 0
|
frameIndex = 0
|
||||||
else
|
else
|
||||||
frameIndex = binarySearch(frames, lastTime, 1)
|
frameIndex = binarySearch1(frames, lastTime)
|
||||||
local frame = frames[frameIndex]
|
local frame = frames[frameIndex]
|
||||||
while frameIndex > 0 do -- Fire multiple events with the same frame.
|
while frameIndex > 0 do -- Fire multiple events with the same frame.
|
||||||
if frames[frameIndex - 1] ~= frame then break end
|
if frames[frameIndex - 1] ~= frame then break end
|
||||||
@ -522,7 +538,7 @@ function Animation.DrawOrderTimeline.new ()
|
|||||||
if time >= frames[#frames] then -- Time is after last frame.
|
if time >= frames[#frames] then -- Time is after last frame.
|
||||||
frameIndex = #frames
|
frameIndex = #frames
|
||||||
else
|
else
|
||||||
frameIndex = binarySearch(frames, time, 1) - 1
|
frameIndex = binarySearch1(frames, time) - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local drawOrder = skeleton.drawOrder
|
local drawOrder = skeleton.drawOrder
|
||||||
@ -542,4 +558,147 @@ function Animation.DrawOrderTimeline.new ()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Animation.FfdTimeline = {}
|
||||||
|
function Animation.FfdTimeline.new ()
|
||||||
|
local self = Animation.CurveTimeline.new()
|
||||||
|
self.frames = {}
|
||||||
|
self.frameVertices = {}
|
||||||
|
self.slotIndex = -1
|
||||||
|
|
||||||
|
function self:getDuration ()
|
||||||
|
return self.frames[#self.frames]
|
||||||
|
end
|
||||||
|
|
||||||
|
function self:getFrameCount ()
|
||||||
|
return #self.frames + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function self:setFrame (frameIndex, time, vertices)
|
||||||
|
self.frames[frameIndex] = time
|
||||||
|
self.frameVertices[frameIndex] = vertices
|
||||||
|
end
|
||||||
|
|
||||||
|
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
|
||||||
|
local slot = skeleton.slots[self.slotIndex]
|
||||||
|
if slot.attachment ~= attachment then return end
|
||||||
|
|
||||||
|
local frames = self.frames
|
||||||
|
if time < frames[0] then -- Time is before first frame.
|
||||||
|
slot.attachmentVerticesCount = 0
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local frameVertices = self.frameVertices
|
||||||
|
local vertexCount = #frameVertices[0]
|
||||||
|
local vertices = slot.attachmentVertices
|
||||||
|
if #vertices < vertexCount then
|
||||||
|
vertices = {}
|
||||||
|
vertices[vertexCount] = 0
|
||||||
|
slot.attachmentVertices = vertices
|
||||||
|
elseif #vertices < vertexCount then
|
||||||
|
alpha = 1 -- Don't mix from uninitialized slot vertices.
|
||||||
|
end
|
||||||
|
slot.attachmentVerticesCount = vertexCount
|
||||||
|
|
||||||
|
if time >= frames[#frames] then -- Time is after last frame.
|
||||||
|
local lastVertices = frameVertices[#frames.Length]
|
||||||
|
if alpha < 1 then
|
||||||
|
for i = 0, vertexCount do
|
||||||
|
local vertex = vertices[i]
|
||||||
|
vertices[i] = vertex + (lastVertices[i] - vertex) * alpha
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i = 0, vertexCount do
|
||||||
|
vertices[i] = lastVertices[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Interpolate between the previous frame and the current frame.
|
||||||
|
local frameIndex = binarySearch1(frames, time)
|
||||||
|
local frameTime = frames[frameIndex]
|
||||||
|
local percent = 1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime)
|
||||||
|
if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
|
||||||
|
percent = self:getCurvePercent(frameIndex - 1, percent)
|
||||||
|
|
||||||
|
local prevVertices = frameVertices[frameIndex - 1]
|
||||||
|
local nextVertices = frameVertices[frameIndex]
|
||||||
|
|
||||||
|
if alpha < 1 then
|
||||||
|
for i = 0, vertexCount do
|
||||||
|
local prev = prevVertices[i]
|
||||||
|
local vertices = vertices[i]
|
||||||
|
vertices[i] = vertices + (prev + (nextVertices[i] - prev) * percent - vertices) * alpha
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i = 0, vertexCount do
|
||||||
|
local prev = prevVertices[i]
|
||||||
|
vertices[i] = prev + (nextVertices[i] - prev) * percent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
Animation.FlipXTimeline = {}
|
||||||
|
function Animation.FlipXTimeline.new ()
|
||||||
|
local self = {
|
||||||
|
frames = {}, -- time, flip, ...
|
||||||
|
boneIndex = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
function self:getDuration ()
|
||||||
|
return self.frames[#self.frames - 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
function self:getFrameCount ()
|
||||||
|
return (#self.frames + 1) / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
function self:setFrame (frameIndex, time, flip)
|
||||||
|
frameIndex = frameIndex * 2
|
||||||
|
self.frames[frameIndex] = time
|
||||||
|
self.frames[frameIndex + 1] = flip
|
||||||
|
end
|
||||||
|
|
||||||
|
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
|
||||||
|
local frames = self.frames
|
||||||
|
if time < frames[0] then
|
||||||
|
if lastTime > time then self:apply(skeleton, lastTime, 999999, null, 0) end
|
||||||
|
return
|
||||||
|
elseif lastTime > time then
|
||||||
|
lastTime = -1
|
||||||
|
end
|
||||||
|
|
||||||
|
local frameIndex
|
||||||
|
if time >= frames[#frames - 1] then
|
||||||
|
frameIndex = #frames - 1
|
||||||
|
else
|
||||||
|
frameIndex = binarySearch(frames, time, 2) - 2
|
||||||
|
end
|
||||||
|
if frames[frameIndex] < lastTime then return end
|
||||||
|
|
||||||
|
self:setFlip(skeleton.bones[self.boneIndex], frames[frameIndex + 1])
|
||||||
|
end
|
||||||
|
|
||||||
|
function self:setFlip (bone, flip)
|
||||||
|
bone.flipX = flip
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
Animation.FlipYTimeline = {}
|
||||||
|
function Animation.FlipYTimeline.new ()
|
||||||
|
local self = Animation.FlipXTimeline.new()
|
||||||
|
|
||||||
|
function self:setFlip (bone, flip)
|
||||||
|
bone.flipY = flip
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
return Animation
|
return Animation
|
||||||
|
|||||||
@ -30,19 +30,23 @@
|
|||||||
|
|
||||||
local Bone = {}
|
local Bone = {}
|
||||||
|
|
||||||
function Bone.new (data, parent)
|
function Bone.new (data, skeleton, parent)
|
||||||
if not data then error("data cannot be nil", 2) end
|
if not data then error("data cannot be nil", 2) end
|
||||||
|
if not skeleton then error("skeleton cannot be nil", 2) end
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
data = data,
|
data = data,
|
||||||
|
skeleton = skeleton,
|
||||||
parent = parent,
|
parent = parent,
|
||||||
x = 0, y = 0,
|
x = 0, y = 0,
|
||||||
rotation = 0,
|
rotation = 0,
|
||||||
scaleX = 1, scaleY = 1,
|
scaleX = 1, scaleY = 1,
|
||||||
|
flipX = false, flipY = false,
|
||||||
m00 = 0, m01 = 0, worldX = 0, -- a b x
|
m00 = 0, m01 = 0, worldX = 0, -- a b x
|
||||||
m10 = 0, m11 = 0, worldY = 0, -- c d y
|
m10 = 0, m11 = 0, worldY = 0, -- c d y
|
||||||
worldRotation = 0,
|
worldRotation = 0,
|
||||||
worldScaleX = 1, worldScaleY = 1,
|
worldScaleX = 1, worldScaleY = 1,
|
||||||
|
worldFlipX = false, worldFlipY = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
function self:updateWorldTransform (flipX, flipY)
|
function self:updateWorldTransform (flipX, flipY)
|
||||||
@ -62,13 +66,16 @@ function Bone.new (data, parent)
|
|||||||
else
|
else
|
||||||
self.worldRotation = self.rotation
|
self.worldRotation = self.rotation
|
||||||
end
|
end
|
||||||
|
self.worldFlipX = parent.worldFlipX ~= self.flipX
|
||||||
|
self.worldFlipY = parent.worldFlipY ~= self.flipY
|
||||||
else
|
else
|
||||||
if flipX then
|
local skeletonFlipX, skeletonFlipY = self.skeleton.flipX, self.skeleton.flipY
|
||||||
|
if skeletonFlipX then
|
||||||
self.worldX = -self.x
|
self.worldX = -self.x
|
||||||
else
|
else
|
||||||
self.worldX = self.x
|
self.worldX = self.x
|
||||||
end
|
end
|
||||||
if flipY then
|
if skeletonFlipY then
|
||||||
self.worldY = -self.y
|
self.worldY = -self.y
|
||||||
else
|
else
|
||||||
self.worldY = self.y
|
self.worldY = self.y
|
||||||
@ -76,21 +83,25 @@ function Bone.new (data, parent)
|
|||||||
self.worldScaleX = self.scaleX
|
self.worldScaleX = self.scaleX
|
||||||
self.worldScaleY = self.scaleY
|
self.worldScaleY = self.scaleY
|
||||||
self.worldRotation = self.rotation
|
self.worldRotation = self.rotation
|
||||||
|
self.worldFlipX = skeletonFlipX ~= self.flipX
|
||||||
|
self.worldFlipY = skeletonFlipY ~= self.flipY
|
||||||
end
|
end
|
||||||
local radians = math.rad(self.worldRotation)
|
local radians = math.rad(self.worldRotation)
|
||||||
local cos = math.cos(radians)
|
local cos = math.cos(radians)
|
||||||
local sin = math.sin(radians)
|
local sin = math.sin(radians)
|
||||||
self.m00 = cos * self.worldScaleX
|
if self.worldFlipX then
|
||||||
self.m10 = sin * self.worldScaleX
|
self.m00 = -cos * self.worldScaleX
|
||||||
self.m01 = -sin * self.worldScaleY
|
self.m01 = sin * self.worldScaleY
|
||||||
self.m11 = cos * self.worldScaleY
|
else
|
||||||
if flipX then
|
self.m00 = cos * self.worldScaleX
|
||||||
self.m00 = -self.m00
|
self.m01 = -sin * self.worldScaleY
|
||||||
self.m01 = -self.m01
|
|
||||||
end
|
end
|
||||||
if flipY then
|
if self.worldFlipY then
|
||||||
self.m10 = -self.m10
|
self.m10 = -sin * self.worldScaleX
|
||||||
self.m11 = -self.m11
|
self.m11 = -cos * self.worldScaleY
|
||||||
|
else
|
||||||
|
self.m10 = sin * self.worldScaleX
|
||||||
|
self.m11 = cos * self.worldScaleY
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -101,6 +112,8 @@ function Bone.new (data, parent)
|
|||||||
self.rotation = data.rotation
|
self.rotation = data.rotation
|
||||||
self.scaleX = data.scaleX
|
self.scaleX = data.scaleX
|
||||||
self.scaleY = data.scaleY
|
self.scaleY = data.scaleY
|
||||||
|
self.flipX = data.flipX
|
||||||
|
self.flipY = data.flipY
|
||||||
end
|
end
|
||||||
|
|
||||||
self:setToSetupPose()
|
self:setToSetupPose()
|
||||||
|
|||||||
@ -51,7 +51,7 @@ function Skeleton.new (skeletonData)
|
|||||||
|
|
||||||
function self:updateWorldTransform ()
|
function self:updateWorldTransform ()
|
||||||
for i,bone in ipairs(self.bones) do
|
for i,bone in ipairs(self.bones) do
|
||||||
bone:updateWorldTransform(self.flipX, self.flipY)
|
bone:updateWorldTransform()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -165,12 +165,12 @@ function Skeleton.new (skeletonData)
|
|||||||
for i,boneData in ipairs(skeletonData.bones) do
|
for i,boneData in ipairs(skeletonData.bones) do
|
||||||
local parent
|
local parent
|
||||||
if boneData.parent then parent = self.bones[spine.utils.indexOf(skeletonData.bones, boneData.parent)] end
|
if boneData.parent then parent = self.bones[spine.utils.indexOf(skeletonData.bones, boneData.parent)] end
|
||||||
table.insert(self.bones, Bone.new(boneData, parent))
|
table.insert(self.bones, Bone.new(boneData, self, parent))
|
||||||
end
|
end
|
||||||
|
|
||||||
for i,slotData in ipairs(skeletonData.slots) do
|
for i,slotData in ipairs(skeletonData.slots) do
|
||||||
local bone = self.bones[spine.utils.indexOf(skeletonData.bones, slotData.boneData)]
|
local bone = self.bones[spine.utils.indexOf(skeletonData.bones, slotData.boneData)]
|
||||||
local slot = Slot.new(slotData, self, bone)
|
local slot = Slot.new(slotData, bone)
|
||||||
table.insert(self.slots, slot)
|
table.insert(self.slots, slot)
|
||||||
self.slotsByName[slot.data.name] = slot
|
self.slotsByName[slot.data.name] = slot
|
||||||
table.insert(self.drawOrder, slot)
|
table.insert(self.drawOrder, slot)
|
||||||
|
|||||||
@ -86,6 +86,8 @@ function SkeletonJson.new (attachmentLoader)
|
|||||||
else
|
else
|
||||||
boneData.scaleY = 1
|
boneData.scaleY = 1
|
||||||
end
|
end
|
||||||
|
boneData.flipX = boneMap["flipX"] or false
|
||||||
|
boneData.flipY = boneMap["flipY"] or false
|
||||||
if boneMap["inheritScale"] == false then
|
if boneMap["inheritScale"] == false then
|
||||||
boneData.inheritScale = false
|
boneData.inheritScale = false
|
||||||
else
|
else
|
||||||
@ -375,6 +377,27 @@ function SkeletonJson.new (attachmentLoader)
|
|||||||
table.insert(timelines, timeline)
|
table.insert(timelines, timeline)
|
||||||
duration = math.max(duration, timeline:getDuration())
|
duration = math.max(duration, timeline:getDuration())
|
||||||
|
|
||||||
|
elseif timelineName == "flipX" or timelineName == "flipY" then
|
||||||
|
local x = timelineName == "flipX"
|
||||||
|
local timeline, field
|
||||||
|
if x then
|
||||||
|
timeline = Animation.FlipXTimeline.new()
|
||||||
|
field = "x"
|
||||||
|
else
|
||||||
|
timeline = Animation.FlipYTimeline.new();
|
||||||
|
field = "y"
|
||||||
|
end
|
||||||
|
timeline.boneIndex = boneIndex
|
||||||
|
|
||||||
|
local frameIndex = 0
|
||||||
|
for i,valueMap in ipairs(values) do
|
||||||
|
local flip
|
||||||
|
timeline:setFrame(frameIndex, valueMap["time"], valueMap[field] or false)
|
||||||
|
frameIndex = frameIndex + 1
|
||||||
|
end
|
||||||
|
table.insert(timelines, timeline)
|
||||||
|
duration = math.max(duration, timeline:getDuration())
|
||||||
|
|
||||||
else
|
else
|
||||||
error("Invalid timeline type for a bone: " .. timelineName .. " (" .. boneName .. ")")
|
error("Invalid timeline type for a bone: " .. timelineName .. " (" .. boneName .. ")")
|
||||||
end
|
end
|
||||||
|
|||||||
@ -29,19 +29,18 @@
|
|||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
local Slot = {}
|
local Slot = {}
|
||||||
function Slot.new (slotData, skeleton, bone)
|
function Slot.new (slotData, bone)
|
||||||
if not slotData then error("slotData cannot be nil", 2) end
|
if not slotData then error("slotData cannot be nil", 2) end
|
||||||
if not skeleton then error("skeleton cannot be nil", 2) end
|
|
||||||
if not bone then error("bone cannot be nil", 2) end
|
if not bone then error("bone cannot be nil", 2) end
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
data = slotData,
|
data = slotData,
|
||||||
skeleton = skeleton,
|
|
||||||
bone = bone,
|
bone = bone,
|
||||||
r = 1, g = 1, b = 1, a = 1,
|
r = 1, g = 1, b = 1, a = 1,
|
||||||
attachment = nil,
|
attachment = nil,
|
||||||
attachmentTime = 0,
|
attachmentTime = 0,
|
||||||
attachmentVertices = nil
|
attachmentVertices = nil,
|
||||||
|
attachmentVerticesCount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function self:setColor (r, g, b, a)
|
function self:setColor (r, g, b, a)
|
||||||
@ -53,15 +52,16 @@ function Slot.new (slotData, skeleton, bone)
|
|||||||
|
|
||||||
function self:setAttachment (attachment)
|
function self:setAttachment (attachment)
|
||||||
self.attachment = attachment
|
self.attachment = attachment
|
||||||
self.attachmentTime = self.skeleton.time
|
self.attachmentTime = self.bone.skeleton.time
|
||||||
|
self.attachmentVerticesCount = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function self:setAttachmentTime (time)
|
function self:setAttachmentTime (time)
|
||||||
self.attachmentTime = self.skeleton.time - time
|
self.attachmentTime = self.bone.skeleton.time - time
|
||||||
end
|
end
|
||||||
|
|
||||||
function self:getAttachmentTime ()
|
function self:getAttachmentTime ()
|
||||||
return self.skeleton.time - self.attachmentTime
|
return self.bone.skeleton.time - self.attachmentTime
|
||||||
end
|
end
|
||||||
|
|
||||||
function self:setToSetupPose ()
|
function self:setToSetupPose ()
|
||||||
@ -71,7 +71,7 @@ function Slot.new (slotData, skeleton, bone)
|
|||||||
|
|
||||||
local attachment
|
local attachment
|
||||||
if data.attachmentName then
|
if data.attachmentName then
|
||||||
attachment = self.skeleton:getAttachment(data.name, data.attachmentName)
|
attachment = self.bone.skeleton:getAttachment(data.name, data.attachmentName)
|
||||||
end
|
end
|
||||||
self:setAttachment(attachment)
|
self:setAttachment(attachment)
|
||||||
end
|
end
|
||||||
|
|||||||