// 2D Blend clip of blend tree // http://docs.unity3d.com/Documentation/Manual/2DBlending.html import Clip from './Clip'; import delaunay from '../util/delaunay'; import Vector2 from '../math/Vector2'; /** * @typedef {Object} clay.animation.Blend2DClip.IClipInput * @property {clay.Vector2} position * @property {clay.animation.Clip} clip * @property {number} offset */ /** * 2d blending node in animation blend tree. * output clip must have blend2D method * @constructor * @alias clay.animation.Blend2DClip * @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 {object[]} [opts.inputs] * @param {clay.Vector2} [opts.position] * @param {clay.animation.Clip} [opts.output] */ var Blend2DClip = function (opts) { opts = opts || {}; Clip.call(this, opts); /** * Output clip must have blend2D method * @type {clay.animation.Clip} */ this.output = opts.output || null; /** * @type {clay.animation.Blend2DClip.IClipInput[]} */ this.inputs = opts.inputs || []; /** * @type {clay.Vector2} */ this.position = new Vector2(); this._cacheTriangle = null; this._triangles = []; this._updateTriangles(); }; Blend2DClip.prototype = new Clip(); Blend2DClip.prototype.constructor = Blend2DClip; /** * @param {clay.Vector2} position * @param {clay.animation.Clip} inputClip * @param {number} [offset] * @return {clay.animation.Blend2DClip.IClipInput} */ Blend2DClip.prototype.addInput = function (position, inputClip, offset) { var obj = { position : position, clip : inputClip, offset : offset || 0 }; this.inputs.push(obj); this.life = Math.max(inputClip.life, this.life); // TODO Change to incrementally adding this._updateTriangles(); return obj; }; // Delaunay triangulate Blend2DClip.prototype._updateTriangles = function () { var inputs = this.inputs.map(function (a) { return a.position; }); this._triangles = delaunay.triangulate(inputs, 'array'); }; Blend2DClip.prototype.step = function (time, dTime, silent) { var ret = Clip.prototype.step.call(this, time); if (ret !== 'finish') { this.setTime(this.getElapsedTime()); } // PENDING Schedule if (!silent && ret !== 'paused') { this.fire('frame'); } return ret; }; Blend2DClip.prototype.setTime = function (time) { var res = this._findTriangle(this.position); if (!res) { return; } // In Barycentric var a = res[1]; // Percent of clip2 var b = res[2]; // Percent of clip3 var tri = res[0]; var in1 = this.inputs[tri.indices[0]]; var in2 = this.inputs[tri.indices[1]]; var in3 = this.inputs[tri.indices[2]]; var clip1 = in1.clip; var clip2 = in2.clip; var clip3 = in3.clip; clip1.setTime((time + in1.offset) % clip1.life); clip2.setTime((time + in2.offset) % clip2.life); clip3.setTime((time + in3.offset) % clip3.life); var c1 = clip1.output instanceof Clip ? clip1.output : clip1; var c2 = clip2.output instanceof Clip ? clip2.output : clip2; var c3 = clip3.output instanceof Clip ? clip3.output : clip3; this.output.blend2D(c1, c2, c3, a, b); }; /** * Clone a new Blend2D clip * @param {boolean} cloneInputs True if clone the input clips * @return {clay.animation.Blend2DClip} */ Blend2DClip.prototype.clone = function (cloneInputs) { var clip = Clip.prototype.clone.call(this); clip.output = this.output.clone(); for (var i = 0; i < this.inputs.length; i++) { var inputClip = cloneInputs ? this.inputs[i].clip.clone(true) : this.inputs[i].clip; clip.addInput(this.inputs[i].position, inputClip, this.inputs[i].offset); } return clip; }; Blend2DClip.prototype._findTriangle = function (position) { if (this._cacheTriangle) { var res = delaunay.contains(this._cacheTriangle.vertices, position.array); if (res) { return [this._cacheTriangle, res[0], res[1]]; } } for (var i = 0; i < this._triangles.length; i++) { var tri = this._triangles[i]; var res = delaunay.contains(tri.vertices, this.position.array); if (res) { this._cacheTriangle = tri; return [tri, res[0], res[1]]; } } }; export default Blend2DClip;