瀏覽代碼

implemented animation framework

Daniel Salvadori 7 年之前
父節點
當前提交
2427110420
共有 5 個文件被更改,包括 409 次插入0 次删除
  1. 129 0
      animation/animation.go
  2. 223 0
      animation/channel.go
  3. 12 0
      animation/logger.go
  4. 7 0
      core/node.go
  5. 38 0
      math32/array.go

+ 129 - 0
animation/animation.go

@@ -0,0 +1,129 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package animation
+package animation
+
+// Animation is a keyframe animation, containing channels.
+// Each channel animates a specific property of an object.
+// Animations can span multiple objects and properties.
+type Animation struct {
+	name     string     // Animation name
+	loop     bool       // Whether the animation loops
+	paused   bool       // Whether the animation is paused
+	start    float32    // Initial time offset value
+	time     float32    // Total running time
+	minTime  float32    // Minimum time value across all channels
+	maxTime  float32    // Maximum time value across all channels
+	channels []IChannel // List of channels
+}
+
+// NewAnimation creates and returns a pointer to a new Animation object.
+func NewAnimation() *Animation {
+
+	anim := new(Animation)
+	return anim
+}
+
+// SetName sets the animation name.
+func (anim *Animation) SetName(name string) {
+
+	anim.name = name
+}
+
+// Name returns the animation name.
+func (anim *Animation) Name() string {
+
+	return anim.name
+}
+
+// Reset resets the animation to the beginning.
+func (anim *Animation) Reset() {
+
+	anim.time = anim.start
+}
+
+// SetPaused sets whether the animation is paused.
+func (anim *Animation) SetPaused(state bool) {
+
+	anim.paused = state
+}
+
+// Paused returns whether the animation is paused.
+func (anim *Animation) Paused() bool {
+
+	return anim.paused
+}
+
+// SetLoop sets whether the animation is looping.
+func (anim *Animation) SetLoop(state bool) {
+
+	anim.loop = state
+}
+
+// Loop returns whether the animation is looping.
+func (anim *Animation) Loop() bool {
+
+	return anim.loop
+}
+
+// SetStart sets the initial time offset value.
+func (anim *Animation) SetStart(v float32) {
+
+	anim.start = v
+}
+
+// Update interpolates and updates the target values for each channel.
+// If the animation is paused, returns false. If the animation is not paused,
+// returns true if the input value is inside the key frames ranges or false otherwise.
+func (anim *Animation) Update(delta float32) bool {
+
+	// Check if paused
+	if anim.paused {
+		return false
+	}
+
+	// Check if input is less than minimum
+	anim.time = anim.time + delta
+	if anim.time < anim.minTime {
+		return false
+	}
+
+	// Check if input is greater than maximum
+	if anim.time > anim.maxTime {
+		if anim.loop {
+			anim.Reset()
+		} else {
+			return false
+		}
+	}
+
+	// Update all channels
+	for i := 0; i < len(anim.channels); i++ {
+		ch := anim.channels[i]
+		ch.Update(anim.time)
+	}
+
+	return true
+}
+
+// AddChannel adds a channel to the animation.
+func (anim *Animation) AddChannel(ch IChannel) {
+
+	// TODO (maybe) prevent user from adding two channels of the same type that share target ?
+
+	// Add the channel
+	anim.channels = append(anim.channels, ch)
+
+	// Update maxTime and minTime values
+	kf := ch.Keyframes()
+	firstKf := kf[0]
+	if anim.minTime > firstKf {
+		anim.minTime = firstKf
+	}
+	lastKf := kf[len(kf)-1]
+	if anim.maxTime < lastKf {
+		anim.maxTime = lastKf
+	}
+}

+ 223 - 0
animation/channel.go

@@ -0,0 +1,223 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package animation
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/math32"
+)
+
+// A Channel associates an animation parameter channel to an interpolation sampler
+type Channel struct {
+	keyframes          math32.ArrayF32          // Input keys (usually time)
+	values             math32.ArrayF32          // Outputs values for the keys
+	interpType         InterpolationType        // Interpolation type
+	interpAction       func(idx int, k float32) // Combined function for interpolation and update
+	updateInterpAction func()                   // Function to update interpAction based on interpolation type
+	inTangent          math32.ArrayF32          // Origin tangents for Spline interpolation
+	outTangent         math32.ArrayF32          // End tangents for Spline interpolation
+}
+
+// SetBuffers sets the keyframe and value buffers.
+func (c *Channel) SetBuffers(keyframes, values math32.ArrayF32) {
+
+	c.keyframes = keyframes
+	c.values = values
+}
+
+// Keyframes returns the keyframe buffer.
+func (c *Channel) Keyframes() math32.ArrayF32 {
+
+	return c.keyframes
+}
+
+// Values returns the value buffer.
+func (c *Channel) Values() math32.ArrayF32 {
+
+	return c.values
+}
+
+// SetInterpolationTangents sets the interpolation tangents.
+func (c *Channel) SetInterpolationTangents(inTangent, outTangent math32.ArrayF32) {
+
+	c.inTangent = inTangent
+	c.outTangent = outTangent
+}
+
+// InterpolationTangents sets the interpolation tangents
+func (c *Channel) InterpolationTangents() (inTangent, outTangent math32.ArrayF32) {
+
+	return c.inTangent, c.outTangent
+}
+
+// SetInterpolationType sets the interpolation type for this channel.
+func (c *Channel) SetInterpolationType(it InterpolationType) {
+
+	// Don't update function if not needed
+	if c.interpType == it {
+		return
+	}
+
+	// Save interpolation type
+	c.interpType = it
+
+	// Call specialized function that updates the interpAction function
+	c.updateInterpAction()
+}
+
+// InterpolationType returns the current interpolation type.
+func (c *Channel) InterpolationType() InterpolationType {
+
+	return c.interpType
+}
+
+// Update finds the keyframe preceding the specified time.
+// Then, calls a stored function to interpolate the relevant values and update the target.
+func (c *Channel) Update(time float32) {
+
+	// Test limits
+	if (len(c.keyframes) < 2) || (time < c.keyframes[0]) || (time > c.keyframes[len(c.keyframes)-1]) {
+		return
+	}
+
+	// Find key frame interval
+	var idx int
+	for idx = 0; idx < len(c.keyframes)-1; idx++ {
+		if time >= c.keyframes[idx] && time < c.keyframes[idx+1] {
+			break
+		}
+	}
+
+	// Check if last keyframe
+	if idx >= len(c.keyframes)-1 {
+		return
+	}
+
+	// Interpolate and update
+	relativeDelta := (time-c.keyframes[idx])/(c.keyframes[idx+1]-c.keyframes[idx])
+	c.interpAction(idx, relativeDelta)
+}
+
+// IChannel is the interface for all channel types.
+type IChannel interface {
+	Update(time float32)
+	Keyframes() math32.ArrayF32
+	Values() math32.ArrayF32
+	SetInterpolationType(it InterpolationType)
+}
+
+// NodeChannel is the IChannel for all node transforms.
+type NodeChannel struct {
+	Channel
+	target core.INode
+}
+
+// PositionChannel is the animation channel for a node's position.
+type PositionChannel NodeChannel
+
+func NewPositionChannel(node core.INode) *PositionChannel {
+
+	pc := new(PositionChannel)
+	pc.target = node
+	pc.updateInterpAction = func() {
+		// Get node
+		node := pc.target.GetNode()
+		// Update interpolation function
+		switch pc.interpType {
+		case STEP:
+			pc.interpAction = func(idx int, k float32) {
+				var v math32.Vector3
+				pc.values.GetVector3(idx*3, &v)
+				node.SetPositionVec(&v)
+			}
+		case LINEAR:
+			pc.interpAction = func(idx int, k float32) {
+				var v1, v2 math32.Vector3
+				pc.values.GetVector3(idx*3, &v1)
+				pc.values.GetVector3((idx+1)*3, &v2)
+				v1.Lerp(&v2, k)
+				node.SetPositionVec(&v1)
+			}
+		}
+	}
+	pc.SetInterpolationType(LINEAR)
+	return pc
+}
+
+// RotationChannel is the animation channel for a node's rotation.
+type RotationChannel NodeChannel
+
+func NewRotationChannel(node core.INode) *RotationChannel {
+
+	rc := new(RotationChannel)
+	rc.target = node
+	rc.updateInterpAction = func() {
+		// Get node
+		node := rc.target.GetNode()
+		// Update interpolation function
+		switch rc.interpType {
+		case STEP:
+			rc.interpAction = func(idx int, k float32) {
+				var q math32.Vector4
+				rc.values.GetVector4(idx*4, &q)
+				node.SetQuaternionVec(&q)
+			}
+		case LINEAR:
+			rc.interpAction = func(idx int, k float32) {
+				var q1, q2 math32.Vector4
+				rc.values.GetVector4(idx*4, &q1)
+				rc.values.GetVector4((idx+1)*4, &q2)
+				quat1 := math32.NewQuaternion(q1.X, q1.Y, q1.Z, q1.W)
+				quat2 := math32.NewQuaternion(q2.X, q2.Y, q2.Z, q2.W)
+				quat1.Slerp(quat2, k)
+				node.SetQuaternionQuat(quat1)
+			}
+		}
+	}
+	rc.SetInterpolationType(LINEAR)
+	return rc
+}
+
+// ScaleChannel is the animation channel for a node's scale.
+type ScaleChannel NodeChannel
+
+func NewScaleChannel(node core.INode) *ScaleChannel {
+
+	sc := new(ScaleChannel)
+	sc.target = node
+	sc.updateInterpAction = func() {
+		// Get node
+		node := sc.target.GetNode()
+		// Update interpolation function
+		switch sc.interpType {
+		case STEP:
+			sc.interpAction = func(idx int, k float32) {
+				var v math32.Vector3
+				sc.values.GetVector3(idx*3, &v)
+				node.SetScaleVec(&v)
+			}
+		case LINEAR:
+			sc.interpAction = func(idx int, k float32) {
+				var v1, v2 math32.Vector3
+				sc.values.GetVector3(idx*3, &v1)
+				sc.values.GetVector3((idx+1)*3, &v2)
+				v1.Lerp(&v2, k)
+				node.SetScaleVec(&v1)
+			}
+		}
+	}
+	sc.SetInterpolationType(LINEAR)
+	return sc
+}
+
+// InterpolationType specifies the interpolation type.
+type InterpolationType int
+
+// The various interpolation types.
+const (
+	STEP        = InterpolationType(iota) // The animated values remain constant to the output of the first keyframe, until the next keyframe.
+	LINEAR                                // The animated values are linearly interpolated between keyframes. Spherical linear interpolation (slerp) is used to interpolate quaternions.
+	CUBICSPLINE                           // TODO
+)

+ 12 - 0
animation/logger.go

@@ -0,0 +1,12 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package animation
+
+import (
+	"github.com/g3n/engine/util/logger"
+)
+
+// Package logger
+var log = logger.New("ANIMATION", logger.Default)

+ 7 - 0
core/node.go

@@ -481,6 +481,13 @@ func (n *Node) SetQuaternion(x, y, z, w float32) {
 	n.changed = true
 }
 
+// SetQuaternionVec sets the quaternion based on the specified quaternion unit multiples vector.
+func (n *Node) SetQuaternionVec(q *math32.Vector4) {
+
+	n.quaternion.Set(q.X, q.Y, q.Z, q.W)
+	n.changed = true
+}
+
 // SetQuaternionQuat sets the quaternion based on the specified quaternion pointer.
 func (n *Node) SetQuaternionQuat(q *math32.Quaternion) {
 

+ 38 - 0
math32/array.go

@@ -59,6 +59,14 @@ func (a *ArrayF32) AppendVector3(v ...*Vector3) {
 	}
 }
 
+// AppendVector4 appends any number of Vector4 to the array
+func (a *ArrayF32) AppendVector4(v ...*Vector4) {
+
+	for i := 0; i < len(v); i++ {
+		*a = append(*a, v[i].X, v[i].Y, v[i].Z, v[i].W)
+	}
+}
+
 // AppendColor appends any number of Color to the array
 func (a *ArrayF32) AppendColor(v ...*Color) {
 
@@ -92,6 +100,16 @@ func (a ArrayF32) GetVector3(pos int, v *Vector3) {
 	v.Z = a[pos+2]
 }
 
+// GetVector4 stores in the specified Vector4 the
+// values from the array starting at the specified pos.
+func (a ArrayF32) GetVector4(pos int, v *Vector4) {
+
+	v.X = a[pos]
+	v.Y = a[pos+1]
+	v.Z = a[pos+2]
+	v.W = a[pos+3]
+}
+
 // GetColor stores in the specified Color the
 // values from the array starting at the specified pos
 func (a ArrayF32) GetColor(pos int, v *Color) {
@@ -101,6 +119,16 @@ func (a ArrayF32) GetColor(pos int, v *Color) {
 	v.B = a[pos+2]
 }
 
+// GetColor4 stores in the specified Color the
+// values from the array starting at the specified pos
+func (a ArrayF32) GetColor4(pos int, v *Color4) {
+
+	v.R = a[pos]
+	v.G = a[pos+1]
+	v.B = a[pos+2]
+	v.A = a[pos+3]
+}
+
 // Set sets the values of the array starting at the specified pos
 // from the specified values
 func (a ArrayF32) Set(pos int, v ...float32) {
@@ -127,6 +155,16 @@ func (a ArrayF32) SetVector3(pos int, v *Vector3) {
 	a[pos+2] = v.Z
 }
 
+// SetVector4 sets the values of the array at the specified pos
+// from the XYZ values of the specified Vector4
+func (a ArrayF32) SetVector4(pos int, v *Vector4) {
+
+	a[pos] = v.X
+	a[pos+1] = v.Y
+	a[pos+2] = v.Z
+	a[pos+3] = v.W
+}
+
 // SetColor sets the values of the array at the specified pos
 // from the RGB values of the specified Color
 func (a ArrayF32) SetColor(pos int, v *Color) {