Skip to content

Commit

Permalink
Added animation curves
Browse files Browse the repository at this point in the history
  • Loading branch information
Joseph Hager committed May 15, 2013
1 parent b210a18 commit 47e66fc
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 5 deletions.
58 changes: 55 additions & 3 deletions animation.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ type Timeline interface {
type RotateTimeline struct {
boneIndex int
frames []float32
curve *Curve
}

func NewRotateTimeline(l int) *RotateTimeline {
timeline := new(RotateTimeline)
timeline.frames = make([]float32, l*2)
timeline.curve = NewCurve(l)
return timeline
}

Expand Down Expand Up @@ -43,7 +45,7 @@ func (t *RotateTimeline) Apply(skeleton *Skeleton, time, alpha float32) {
lastFrameValue := frames[frameIndex-1]
frameTime := frames[frameIndex]
percent := 1 - (time-frameTime)/(frames[frameIndex-2]-frameTime)
// TODO: curves
percent = t.curve.CurvePercent(frameIndex/2-1, percent)
amount := frames[frameIndex+1] - lastFrameValue
for amount > 180 {
amount -= 360
Expand Down Expand Up @@ -95,11 +97,13 @@ func (t *RotateTimeline) frameCount() int {
type TranslateTimeline struct {
boneIndex int
frames []float32
curve *Curve
}

func NewTranslateTimeline(l int) *TranslateTimeline {
timeline := new(TranslateTimeline)
timeline.frames = make([]float32, l*3)
timeline.curve = NewCurve(l)
return timeline
}

Expand Down Expand Up @@ -133,13 +137,61 @@ func (t *TranslateTimeline) Apply(skeleton *Skeleton, time, alpha float32) {
lastFrameY := frames[frameIndex-1]
frameTime := frames[frameIndex]
percent := 1 - (time-frameTime)/(frames[frameIndex-3]-frameTime)

// TODO: percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent)
percent = t.curve.CurvePercent(frameIndex/3-1, percent)

bone.X += (bone.data.x + lastFrameX + (frames[frameIndex+1]-lastFrameX)*percent - bone.X) * alpha
bone.Y += (bone.data.y + lastFrameY + (frames[frameIndex+2]-lastFrameY)*percent - bone.Y) * alpha
}

type ScaleTimeline struct {
boneIndex int
frames []float32
curve *Curve
}

func NewScaleTimeline(l int) *ScaleTimeline {
timeline := new(ScaleTimeline)
timeline.frames = make([]float32, l*3)
timeline.curve = NewCurve(l)
return timeline
}

func (t *ScaleTimeline) frameCount() int {
return len(t.frames) / 3
}

func (t *ScaleTimeline) setFrame(index int, time, x, y float32) {
frameIndex := index * 3
t.frames[frameIndex] = time
t.frames[frameIndex+1] = x
t.frames[frameIndex+2] = y
}

func (t *ScaleTimeline) Apply(skeleton *Skeleton, time, alpha float32) {
frames := t.frames
if time < frames[0] {
return
}

bone := skeleton.bones[t.boneIndex]

if time >= frames[len(frames)-3] {
bone.ScaleX += (bone.data.scaleX - 1 + frames[len(frames)-2] - bone.ScaleX) * alpha
bone.ScaleY += (bone.data.scaleY - 1 + frames[len(frames)-1] - bone.ScaleY) * alpha
return
}

frameIndex := binarySearch(frames, time, 3)
lastFrameX := frames[frameIndex-2]
lastFrameY := frames[frameIndex-1]
frameTime := frames[frameIndex]
percent := 1 - (time-frameTime)/(frames[frameIndex-3]-frameTime)
percent = t.curve.CurvePercent(frameIndex/3-1, percent)

bone.ScaleX += (bone.data.scaleX - 1 + lastFrameX + (frames[frameIndex+1]-lastFrameX)*percent - bone.ScaleX) * alpha
bone.ScaleY += (bone.data.scaleY - 1 + lastFrameY + (frames[frameIndex+2]-lastFrameY)*percent - bone.ScaleY) * alpha
}

type Animation struct {
name string
timelines []Timeline
Expand Down
87 changes: 87 additions & 0 deletions curve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package spine

type Curve struct {
curves []float32
}

func NewCurve(frameCount int) *Curve {
curve := new(Curve)
curve.curves = make([]float32, (frameCount-1)*6)
return curve
}

func (c *Curve) SetLinear(index int) {
c.curves[index*6] = 0
}

func (c *Curve) SetStepped(index int) {
c.curves[index*6] = -1
}

func (c *Curve) SetCurve(index int, cx1, cy1, cx2, cy2 float32) {
subdiv_step := float32(1) / 10
subdiv_step2 := subdiv_step * subdiv_step
subdiv_step3 := subdiv_step2 * subdiv_step
pre1 := 3 * subdiv_step
pre2 := 3 * subdiv_step2
pre4 := 6 * subdiv_step2
pre5 := 6 * subdiv_step3
tmp1x := -cx1*2 + cx2
tmp1y := -cy1*2 + cy2
tmp2x := (cx1-cx2)*3 + 1
tmp2y := (cy1-cy2)*3 + 1
i := index * 6
curves := c.curves
curves[i] = cx1*pre1 + tmp1x*pre2 + tmp2x*subdiv_step3
curves[i+1] = cy1*pre1 + tmp1y*pre2 + tmp2y*subdiv_step3
curves[i+2] = tmp1x*pre4 + tmp2x*pre5
curves[i+3] = tmp1y*pre4 + tmp2y*pre5
curves[i+4] = tmp2x * pre5
curves[i+5] = tmp2y * pre5
}

func (c *Curve) CurvePercent(index int, percent float32) float32 {
if percent < 0 {
percent = 0
} else if percent > 1 {
percent = 1
}

curveIndex := index * 6
curves := c.curves
dfx := curves[curveIndex]
if dfx == 0 {
return percent
}
if dfx == -1 {
return 0
}

dfy := curves[curveIndex+1]
ddfx := curves[curveIndex+2]
ddfy := curves[curveIndex+3]
dddfx := curves[curveIndex+4]
dddfy := curves[curveIndex+5]
x := dfx
y := dfy
i := 8

for {
if x >= percent {
lastX := x - dfx
lastY := y - dfy
return lastY + (y-lastY)*(percent-lastX)/(x-lastX)
}
if i == 0 {
break
}
i -= 1
dfx += ddfx
dfy += ddfy
ddfx += dddfx
ddfy += dddfy
x += dfx
y += dfy
}
return y + (1-y)*(percent-x)/(1-x)
}
47 changes: 45 additions & 2 deletions spine.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ func Load(path string) *SkeletonData {
time := float32(valueMap["time"].(float64))
angle := float32(valueMap["angle"].(float64))
timeline.setFrame(i, time, angle)
// TODO: READ CURVE
if curve, ok := valueMap["curve"]; ok {
readCurve(timeline.curve, i, curve)
}
}
duration = float32(math.Max(float64(duration), float64(timeline.frames[timeline.frameCount()*2-2])))

Expand All @@ -182,6 +184,29 @@ func Load(path string) *SkeletonData {
n := len(timelineData)
timeline := NewTranslateTimeline(n)
timeline.boneIndex = boneIndex
for i := 0; i < n; i++ {
valueMap := timelineData[i].(map[string]interface{})
x := float32(0)
if xx, ok := valueMap["x"].(float64); ok {
x = float32(xx) * Scale
}
y := float32(0)
if yy, ok := valueMap["y"].(float64); ok {
y = float32(yy) * Scale
}
time := float32(valueMap["time"].(float64))

timeline.setFrame(i, time, x, y)
if curve, ok := valueMap["curve"]; ok {
readCurve(timeline.curve, i, curve)
}
}
duration = float32(math.Max(float64(duration), float64(timeline.frames[timeline.frameCount()*3-3])))
timelines = append(timelines, timeline)
} else if timelineType == "scale" {
n := len(timelineData)
timeline := NewScaleTimeline(n)
timeline.boneIndex = boneIndex
for i := 0; i < n; i++ {
valueMap := timelineData[i].(map[string]interface{})
x := float32(0)
Expand All @@ -195,7 +220,9 @@ func Load(path string) *SkeletonData {
time := float32(valueMap["time"].(float64))

timeline.setFrame(i, time, x, y)
// TODO: curve
if curve, ok := valueMap["curve"]; ok {
readCurve(timeline.curve, i, curve)
}
}
duration = float32(math.Max(float64(duration), float64(timeline.frames[timeline.frameCount()*3-3])))
timelines = append(timelines, timeline)
Expand All @@ -210,3 +237,19 @@ func Load(path string) *SkeletonData {

return skeletonData
}

func readCurve(curve *Curve, frameIndex int, data interface{}) {
switch t := data.(type) {
default:
case string:
if t == "stepped" {
curve.SetStepped(frameIndex)
}
case []interface{}:
a := float32(t[0].(float64))
b := float32(t[1].(float64))
c := float32(t[2].(float64))
d := float32(t[3].(float64))
curve.SetCurve(frameIndex, a, b, c, d)
}
}

0 comments on commit 47e66fc

Please sign in to comment.