animation/TrackClip.js

import Clip from './Clip';

import quat from '../glmatrix/quat';
import vec3 from '../glmatrix/vec3';


/**
 *
 * Animation clip that manage a collection of {@link clay.animation.SamplerTrack}
 * @constructor
 * @alias clay.animation.TrackClip
 *
 * @extends clay.animation.Clip
 * @param {Object} [opts]
 * @param {string} [opts.name]
 * @param {Object} [opts.target]
 * @param {number} [opts.life]
 * @param {number} [opts.delay]
 * @param {number} [opts.gap]
 * @param {number} [opts.playbackRatio]
 * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation
 * @param {string|Function} [opts.easing]
 * @param {Function} [opts.onframe]
 * @param {Function} [opts.onfinish]
 * @param {Function} [opts.onrestart]
 * @param {Array.<clay.animation.SamplerTrack>} [opts.tracks]
 */
var TrackClip = function (opts) {

    opts = opts || {};

    Clip.call(this, opts);

    /**
     *
     * @type {clay.animation.SamplerTrack[]}
     */
    this.tracks = opts.tracks || [];

    this.calcLifeFromTracks();
};

TrackClip.prototype = Object.create(Clip.prototype);

TrackClip.prototype.constructor = TrackClip;

TrackClip.prototype.step = function (time, dTime, silent) {

    var ret = Clip.prototype.step.call(this, time, dTime, true);

    if (ret !== 'finish') {
        var time = this.getElapsedTime();
        // TODO life may be changed.
        if (this._range) {
            time = this._range[0] + time;
        }
        this.setTime(time);
    }

    // PENDING Schedule
    if (!silent && ret !== 'paused') {
        this.fire('frame');
    }

    return ret;
};

/**
 * @param {Array.<number>} range
 */
TrackClip.prototype.setRange = function (range) {
    this.calcLifeFromTracks();
    this._range = range;
    if (range) {
        range[1] = Math.min(range[1], this.life);
        range[0] = Math.min(range[0], this.life);
        this.life = (range[1] - range[0]);
    }
};

TrackClip.prototype.setTime = function (time) {
    for (var i = 0; i < this.tracks.length; i++) {
        this.tracks[i].setTime(time);
    }
};

TrackClip.prototype.calcLifeFromTracks = function () {
    this.life = 0;
    for (var i = 0; i < this.tracks.length; i++) {
        this.life = Math.max(this.life, this.tracks[i].getMaxTime());
    }
};

/**
 * @param {clay.animation.SamplerTrack} track
 */
TrackClip.prototype.addTrack = function (track) {
    this.tracks.push(track);
    this.calcLifeFromTracks();
};

/**
 * @param {clay.animation.SamplerTrack} track
 */
TrackClip.prototype.removeTarck = function (track) {
    var idx = this.tracks.indexOf(track);
    if (idx >= 0) {
        this.tracks.splice(idx, 1);
    }
};

/**
 * @param {number} startTime
 * @param {number} endTime
 * @param {boolean} isLoop
 * @return {clay.animation.TrackClip}
 */
TrackClip.prototype.getSubClip = function (startTime, endTime, isLoop) {
    var subClip = new TrackClip({
        name: this.name
    });

    for (var i = 0; i < this.tracks.length; i++) {
        var subTrack = this.tracks[i].getSubTrack(startTime, endTime);
        subClip.addTrack(subTrack);
    }

    if (isLoop !== undefined) {
        subClip.setLoop(isLoop);
    }

    subClip.life = endTime - startTime;

    return subClip;
};

/**
 * 1d blending from two skinning clips
 * @param  {clay.animation.TrackClip} clip1
 * @param  {clay.animation.TrackClip} clip2
 * @param  {number} w
 */
TrackClip.prototype.blend1D = function (clip1, clip2, w) {
    for (var i = 0; i < this.tracks.length; i++) {
        var c1 = clip1.tracks[i];
        var c2 = clip2.tracks[i];
        var tClip = this.tracks[i];

        tClip.blend1D(c1, c2, w);
    }
};

/**
 * Additive blending from two skinning clips
 * @param  {clay.animation.TrackClip} clip1
 * @param  {clay.animation.TrackClip} clip2
 */
TrackClip.prototype.additiveBlend = function (clip1, clip2) {
    for (var i = 0; i < this.tracks.length; i++) {
        var c1 = clip1.tracks[i];
        var c2 = clip2.tracks[i];
        var tClip = this.tracks[i];

        tClip.additiveBlend(c1, c2);
    }
};

/**
 * Subtractive blending from two skinning clips
 * @param  {clay.animation.TrackClip} clip1
 * @param  {clay.animation.TrackClip} clip2
 */
TrackClip.prototype.subtractiveBlend = function (clip1, clip2) {
    for (var i = 0; i < this.tracks.length; i++) {
        var c1 = clip1.tracks[i];
        var c2 = clip2.tracks[i];
        var tClip = this.tracks[i];

        tClip.subtractiveBlend(c1, c2);
    }
};

/**
 * 2D blending from three skinning clips
 * @param  {clay.animation.TrackClip} clip1
 * @param  {clay.animation.TrackClip} clip2
 * @param  {clay.animation.TrackClip} clip3
 * @param  {number} f
 * @param  {number} g
 */
TrackClip.prototype.blend2D = function (clip1, clip2, clip3, f, g) {
    for (var i = 0; i < this.tracks.length; i++) {
        var c1 = clip1.tracks[i];
        var c2 = clip2.tracks[i];
        var c3 = clip3.tracks[i];
        var tClip = this.tracks[i];

        tClip.blend2D(c1, c2, c3, f, g);
    }
};

/**
 * Copy SRT of all joints clips from another TrackClip
 * @param  {clay.animation.TrackClip} clip
 */
TrackClip.prototype.copy = function (clip) {
    for (var i = 0; i < this.tracks.length; i++) {
        var sTrack = clip.tracks[i];
        var tTrack = this.tracks[i];

        vec3.copy(tTrack.position, sTrack.position);
        vec3.copy(tTrack.scale, sTrack.scale);
        quat.copy(tTrack.rotation, sTrack.rotation);
    }
};

TrackClip.prototype.clone = function () {
    var clip = Clip.prototype.clone.call(this);
    for (var i = 0; i < this.tracks.length; i++) {
        clip.addTrack(this.tracks[i].clone());
    }
    clip.life = this.life;
    return clip;
};

export default TrackClip;