| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- // 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 collada
- import (
- "fmt"
- "github.com/g3n/engine/core"
- "github.com/g3n/engine/math32"
- "strings"
- )
- // AnimationTarget contains all animation channels for an specific target node
- type AnimationTarget struct {
- target core.INode
- matrix math32.Matrix4 // original node transformation matrix
- start float32 // initial input offset value
- last float32 // last input value
- minInput float32 // minimum input value for all channels
- maxInput float32 // maximum input value for all channels
- loop bool // animation loop flag
- rot math32.Vector3 // rotation in XYZ Euler angles
- channels []*ChannelInstance
- }
- // A ChannelInstance associates an animation parameter channel to an interpolation sampler
- type ChannelInstance struct {
- sampler *SamplerInstance
- action ActionFunc
- }
- // SamplerInstance specifies the input key frames, output values for these key frames
- // and interpolation information. It can be shared by more than one animation
- type SamplerInstance struct {
- Input []float32 // Input keys (usually time)
- Output []float32 // Outputs values for the keys
- Interp []string // Names of interpolation functions for each key frame
- InTangent []float32 // Origin tangents for Bezier interpolation
- OutTangent []float32 // End tangents for Bezier interpolation
- }
- // ActionFunc is the type for all functions that execute an specific parameter animation
- type ActionFunc func(at *AnimationTarget, v float32)
- // Reset resets the animation from the beginning
- func (at *AnimationTarget) Reset() {
- at.last = at.start
- at.target.GetNode().SetMatrix(&at.matrix)
- }
- // SetLoop sets the state of the animation loop flag
- func (at *AnimationTarget) SetLoop(loop bool) {
- at.loop = loop
- }
- // SetStart sets the initial offset value
- func (at *AnimationTarget) SetStart(v float32) {
- at.start = v
- }
- // Update interpolates the specified input value for each animation target channel
- // and executes its corresponding action function. Returns true if the input value
- // is inside the key frames ranges or false otherwise.
- func (at *AnimationTarget) Update(delta float32) bool {
- // Checks if input is less than minimum
- at.last = at.last + delta
- if at.last < at.minInput {
- return false
- }
- // Checks if input is greater than maximum
- if at.last > at.maxInput {
- if at.loop {
- at.Reset()
- } else {
- return false
- }
- }
- for i := 0; i < len(at.channels); i++ {
- ch := at.channels[i]
- // Get interpolated value
- v, ok := ch.sampler.Interpolate(at.last)
- if !ok {
- return false
- }
- // Call action func
- ch.action(at, v)
- // Sets final rotation
- at.target.GetNode().SetRotation(at.rot.X, at.rot.Y, at.rot.Z)
- }
- return true
- }
- // NewAnimationTargets creates and returns a map of all animation targets
- // contained in the decoded Collada document and for the previously decoded scene.
- // The map is indexed by the node loaderID.
- func (d *Decoder) NewAnimationTargets(scene core.INode) (map[string]*AnimationTarget, error) {
- if d.dom.LibraryAnimations == nil {
- return nil, fmt.Errorf("No animations found")
- }
- // Maps target node to its animation target instance
- targetsMap := make(map[string]*AnimationTarget)
- // For each Collada animation element
- for _, ca := range d.dom.LibraryAnimations.Animation {
- // For each Collada channel for this animation
- for _, cc := range ca.Channel {
- // Separates the channel target in target id and target action
- parts := strings.Split(cc.Target, "/")
- if len(parts) < 2 {
- return nil, fmt.Errorf("Channel target invalid")
- }
- targetID := parts[0]
- targetAction := parts[1]
- // Get the target node object referenced by the target id from the specified scene.
- target := scene.GetNode().FindLoaderID(targetID)
- if target == nil {
- return nil, fmt.Errorf("Target node id:%s not found", targetID)
- }
- // Get reference to the AnimationTarget for this target in the local map
- // If not found creates the animation target and inserts in the map
- at := targetsMap[targetID]
- if at == nil {
- at = new(AnimationTarget)
- at.target = target
- at.matrix = target.GetNode().Matrix()
- targetsMap[targetID] = at
- }
- // Creates the sampler instance specified from the channel source
- si, err := NewSamplerInstance(ca, cc.Source)
- if err != nil {
- return nil, err
- }
- // Sets the action function from the target action
- var af ActionFunc
- switch targetAction {
- case "location.X":
- af = actionPositionX
- case "location.Y":
- af = actionPositionY
- case "location.Z":
- af = actionPositionZ
- case "rotationX.ANGLE":
- af = actionRotationX
- case "rotationY.ANGLE":
- af = actionRotationY
- case "rotationZ.ANGLE":
- af = actionRotationZ
- case "scale.X":
- af = actionScaleX
- case "scale.Y":
- af = actionScaleY
- case "scale.Z":
- af = actionScaleZ
- default:
- return nil, fmt.Errorf("Unsupported channel target action:%s", targetAction)
- }
- // Creates the channel instance for this sampler and target action and adds it
- // to the current AnimationTarget
- ci := &ChannelInstance{si, af}
- at.channels = append(at.channels, ci)
- }
- }
- // Set minimum and maximum input values for each animation target
- for _, at := range targetsMap {
- at.minInput = math32.Infinity
- at.maxInput = -math32.Infinity
- for _, ch := range at.channels {
- // First key frame input
- inp := ch.sampler.Input[0]
- if inp < at.minInput {
- at.minInput = inp
- }
- // Last key frame input
- inp = ch.sampler.Input[len(ch.sampler.Input)-1]
- if inp > at.maxInput {
- at.maxInput = inp
- }
- }
- }
- return targetsMap, nil
- }
- func actionPositionX(at *AnimationTarget, v float32) {
- at.target.GetNode().SetPositionX(v)
- }
- func actionPositionY(at *AnimationTarget, v float32) {
- at.target.GetNode().SetPositionY(v)
- }
- func actionPositionZ(at *AnimationTarget, v float32) {
- at.target.GetNode().SetPositionZ(v)
- }
- func actionRotationX(at *AnimationTarget, v float32) {
- at.rot.X = math32.DegToRad(v)
- }
- func actionRotationY(at *AnimationTarget, v float32) {
- at.rot.Y = math32.DegToRad(v)
- }
- func actionRotationZ(at *AnimationTarget, v float32) {
- at.rot.Z = math32.DegToRad(v)
- }
- func actionScaleX(at *AnimationTarget, v float32) {
- at.target.GetNode().SetScaleX(v)
- }
- func actionScaleY(at *AnimationTarget, v float32) {
- at.target.GetNode().SetScaleY(v)
- }
- func actionScaleZ(at *AnimationTarget, v float32) {
- at.target.GetNode().SetScaleZ(v)
- }
- // NewSampler creates and returns a pointer to a new SamplerInstance built
- // with data from the specified Collada animation and URI
- func NewSamplerInstance(ca *Animation, uri string) (*SamplerInstance, error) {
- id := strings.TrimPrefix(uri, "#")
- var cs *Sampler
- for _, current := range ca.Sampler {
- if current.Id == id {
- cs = current
- break
- }
- }
- if cs == nil {
- return nil, fmt.Errorf("Sampler:%s not found", id)
- }
- // Get sampler inputs
- si := new(SamplerInstance)
- for _, inp := range cs.Input {
- if inp.Semantic == "INPUT" {
- data, err := findSourceFloatArray(ca, inp.Source)
- if err != nil {
- return nil, err
- }
- si.Input = data
- continue
- }
- if inp.Semantic == "OUTPUT" {
- data, err := findSourceFloatArray(ca, inp.Source)
- if err != nil {
- return nil, err
- }
- si.Output = data
- continue
- }
- if inp.Semantic == "INTERPOLATION" {
- data, err := findSourceNameArray(ca, inp.Source)
- if err != nil {
- return nil, err
- }
- si.Interp = data
- continue
- }
- if inp.Semantic == "IN_TANGENT" {
- data, err := findSourceFloatArray(ca, inp.Source)
- if err != nil {
- return nil, err
- }
- si.InTangent = data
- continue
- }
- if inp.Semantic == "OUT_TANGENT" {
- data, err := findSourceFloatArray(ca, inp.Source)
- if err != nil {
- return nil, err
- }
- si.OutTangent = data
- continue
- }
- }
- return si, nil
- }
- // Interpolate returns the interpolated output and its validity
- // for this sampler for the specified input.
- func (si *SamplerInstance) Interpolate(inp float32) (float32, bool) {
- // Test limits
- if len(si.Input) < 2 {
- return 0, false
- }
- if inp < si.Input[0] {
- return 0, false
- }
- if inp > si.Input[len(si.Input)-1] {
- return 0, false
- }
- // Find key frame interval
- var idx int
- for idx = 0; idx < len(si.Input)-1; idx++ {
- if inp >= si.Input[idx] && inp < si.Input[idx+1] {
- break
- }
- }
- // Checks if interval was found
- if idx >= len(si.Input)-1 {
- return 0, false
- }
- switch si.Interp[idx] {
- case "STEP":
- return si.linearInterp(inp, idx), true
- case "LINEAR":
- return si.linearInterp(inp, idx), true
- case "BEZIER":
- return si.bezierInterp(inp, idx), true
- case "HERMITE":
- return si.linearInterp(inp, idx), true
- case "CARDINAL":
- return si.linearInterp(inp, idx), true
- case "BSPLINE":
- return si.linearInterp(inp, idx), true
- }
- return 0, false
- }
- func (si *SamplerInstance) linearInterp(inp float32, idx int) float32 {
- k1 := si.Input[idx]
- k2 := si.Input[idx+1]
- v1 := si.Output[idx]
- v2 := si.Output[idx+1]
- return v1 + (v2-v1)*(inp-k1)/(k2-k1)
- }
- func (si *SamplerInstance) bezierInterp(inp float32, idx int) float32 {
- p0 := si.Output[idx]
- p1 := si.Output[idx+1]
- c0 := si.OutTangent[2*idx+1]
- c1 := si.InTangent[2*(idx+1)+1]
- k1 := si.Input[idx]
- k2 := si.Input[idx+1]
- s := (inp - k1) / (k2 - k1)
- out := p0*math32.Pow(1-s, 3) + 3*c0*s*math32.Pow(1-s, 2) + 3*c1*s*s*(1-s) + p1*math32.Pow(s, 3)
- return out
- }
- func findSourceNameArray(ca *Animation, uri string) ([]string, error) {
- src := findSource(ca, uri)
- if src == nil {
- return nil, fmt.Errorf("Source:%s not found", uri)
- }
- na, ok := src.ArrayElement.(*NameArray)
- if !ok {
- return nil, fmt.Errorf("Source:%s is not NameArray", uri)
- }
- return na.Data, nil
- }
- func findSourceFloatArray(ca *Animation, uri string) ([]float32, error) {
- src := findSource(ca, uri)
- if src == nil {
- return nil, fmt.Errorf("Source:%s not found", uri)
- }
- fa, ok := src.ArrayElement.(*FloatArray)
- if !ok {
- return nil, fmt.Errorf("Source:%s is not FloatArray", uri)
- }
- return fa.Data, nil
- }
- func findSource(ca *Animation, uri string) *Source {
- id := strings.TrimPrefix(uri, "#")
- for _, src := range ca.Source {
- if src.Id == id {
- return src
- }
- }
- return nil
- }
|