|
@@ -184,17 +184,23 @@ spine.Animation = function (name, timelines, duration) {
|
|
this.duration = duration;
|
|
this.duration = duration;
|
|
};
|
|
};
|
|
spine.Animation.prototype = {
|
|
spine.Animation.prototype = {
|
|
- apply: function (skeleton, time, loop) {
|
|
|
|
- if (loop && this.duration != 0) time %= this.duration;
|
|
|
|
|
|
+ apply: function (skeleton, lastTime, time, loop, events) {
|
|
|
|
+ if (loop && this.duration != 0) {
|
|
|
|
+ time %= this.duration;
|
|
|
|
+ lastTime %= this.duration;
|
|
|
|
+ }
|
|
var timelines = this.timelines;
|
|
var timelines = this.timelines;
|
|
for (var i = 0, n = timelines.length; i < n; i++)
|
|
for (var i = 0, n = timelines.length; i < n; i++)
|
|
- timelines[i].apply(skeleton, time, 1);
|
|
|
|
|
|
+ timelines[i].apply(skeleton, lastTime, time, events, 1);
|
|
},
|
|
},
|
|
- mix: function (skeleton, time, loop, alpha) {
|
|
|
|
- if (loop && this.duration != 0) time %= this.duration;
|
|
|
|
|
|
+ mix: function (skeleton, lastTime, time, loop, events, alpha) {
|
|
|
|
+ if (loop && this.duration != 0) {
|
|
|
|
+ time %= this.duration;
|
|
|
|
+ lastTime %= this.duration;
|
|
|
|
+ }
|
|
var timelines = this.timelines;
|
|
var timelines = this.timelines;
|
|
for (var i = 0, n = timelines.length; i < n; i++)
|
|
for (var i = 0, n = timelines.length; i < n; i++)
|
|
- timelines[i].apply(skeleton, time, alpha);
|
|
|
|
|
|
+ timelines[i].apply(skeleton, lastTime, time, events, alpha);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -301,7 +307,7 @@ spine.RotateTimeline.prototype = {
|
|
this.frames[frameIndex] = time;
|
|
this.frames[frameIndex] = time;
|
|
this.frames[frameIndex + 1] = angle;
|
|
this.frames[frameIndex + 1] = angle;
|
|
},
|
|
},
|
|
- apply: function (skeleton, time, alpha) {
|
|
|
|
|
|
+ apply: function (skeleton, lastTime, time, firedEvents, alpha) {
|
|
var frames = this.frames;
|
|
var frames = this.frames;
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
|
|
|
|
@@ -354,7 +360,7 @@ spine.TranslateTimeline.prototype = {
|
|
this.frames[frameIndex + 1] = x;
|
|
this.frames[frameIndex + 1] = x;
|
|
this.frames[frameIndex + 2] = y;
|
|
this.frames[frameIndex + 2] = y;
|
|
},
|
|
},
|
|
- apply: function (skeleton, time, alpha) {
|
|
|
|
|
|
+ apply: function (skeleton, lastTime, time, firedEvents, alpha) {
|
|
var frames = this.frames;
|
|
var frames = this.frames;
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
|
|
|
|
@@ -395,7 +401,7 @@ spine.ScaleTimeline.prototype = {
|
|
this.frames[frameIndex + 1] = x;
|
|
this.frames[frameIndex + 1] = x;
|
|
this.frames[frameIndex + 2] = y;
|
|
this.frames[frameIndex + 2] = y;
|
|
},
|
|
},
|
|
- apply: function (skeleton, time, alpha) {
|
|
|
|
|
|
+ apply: function (skeleton, lastTime, time, firedEvents, alpha) {
|
|
var frames = this.frames;
|
|
var frames = this.frames;
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
|
|
|
|
@@ -438,7 +444,7 @@ spine.ColorTimeline.prototype = {
|
|
this.frames[frameIndex + 3] = b;
|
|
this.frames[frameIndex + 3] = b;
|
|
this.frames[frameIndex + 4] = a;
|
|
this.frames[frameIndex + 4] = a;
|
|
},
|
|
},
|
|
- apply: function (skeleton, time, alpha) {
|
|
|
|
|
|
+ apply: function (skeleton, lastTime, time, firedEvents, alpha) {
|
|
var frames = this.frames;
|
|
var frames = this.frames;
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
|
|
|
|
@@ -485,7 +491,7 @@ spine.AttachmentTimeline = function (frameCount) {
|
|
this.curves = new spine.Curves(frameCount);
|
|
this.curves = new spine.Curves(frameCount);
|
|
this.frames = []; // time, ...
|
|
this.frames = []; // time, ...
|
|
this.frames.length = frameCount;
|
|
this.frames.length = frameCount;
|
|
- this.attachmentNames = []; // time, ...
|
|
|
|
|
|
+ this.attachmentNames = [];
|
|
this.attachmentNames.length = frameCount;
|
|
this.attachmentNames.length = frameCount;
|
|
};
|
|
};
|
|
spine.AttachmentTimeline.prototype = {
|
|
spine.AttachmentTimeline.prototype = {
|
|
@@ -497,7 +503,7 @@ spine.AttachmentTimeline.prototype = {
|
|
this.frames[frameIndex] = time;
|
|
this.frames[frameIndex] = time;
|
|
this.attachmentNames[frameIndex] = attachmentName;
|
|
this.attachmentNames[frameIndex] = attachmentName;
|
|
},
|
|
},
|
|
- apply: function (skeleton, time, alpha) {
|
|
|
|
|
|
+ apply: function (skeleton, lastTime, time, firedEvents, alpha) {
|
|
var frames = this.frames;
|
|
var frames = this.frames;
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
if (time < frames[0]) return; // Time is before first frame.
|
|
|
|
|
|
@@ -512,10 +518,92 @@ spine.AttachmentTimeline.prototype = {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+spine.EventTimeline = function (frameCount) {
|
|
|
|
+ this.frames = []; // time, ...
|
|
|
|
+ this.frames.length = frameCount;
|
|
|
|
+ this.events = [];
|
|
|
|
+ this.events.length = frameCount;
|
|
|
|
+};
|
|
|
|
+spine.EventTimeline.prototype = {
|
|
|
|
+ getFrameCount: function () {
|
|
|
|
+ return this.frames.length;
|
|
|
|
+ },
|
|
|
|
+ setFrame: function (frameIndex, time, event) {
|
|
|
|
+ this.frames[frameIndex] = time;
|
|
|
|
+ this.events[frameIndex] = event;
|
|
|
|
+ },
|
|
|
|
+ apply: function (skeleton, lastTime, time, firedEvents, alpha) {
|
|
|
|
+ if (!firedEvents) return;
|
|
|
|
+
|
|
|
|
+ var frames = this.frames;
|
|
|
|
+ var frameCount = frames.length;
|
|
|
|
+ if (lastTime >= frames[frameCount - 1]) return; // Last time is after last frame.
|
|
|
|
+
|
|
|
|
+ if (lastTime > time) { // Fire events after last time for looped animations.
|
|
|
|
+ this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha);
|
|
|
|
+ lastTime = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var frameIndex;
|
|
|
|
+ if (lastTime <= frames[0] || frameCount == 1)
|
|
|
|
+ frameIndex = 0;
|
|
|
|
+ else {
|
|
|
|
+ frameIndex = spine.binarySearch(frames, lastTime, 1);
|
|
|
|
+ var frame = frames[frameIndex];
|
|
|
|
+ while (frameIndex > 0) { // Fire multiple events with the same frame.
|
|
|
|
+ if (frames[frameIndex - 1] != frame) break;
|
|
|
|
+ frameIndex--;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ var events = this.events;
|
|
|
|
+ for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++)
|
|
|
|
+ firedEvents.push(events[frameIndex]);
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+spine.DrawOrderTimeline = function (frameCount) {
|
|
|
|
+ this.frames = []; // time, ...
|
|
|
|
+ this.frames.length = frameCount;
|
|
|
|
+ this.drawOrders = [];
|
|
|
|
+ this.drawOrders.length = frameCount;
|
|
|
|
+};
|
|
|
|
+spine.DrawOrderTimeline.prototype = {
|
|
|
|
+ getFrameCount: function () {
|
|
|
|
+ return this.frames.length;
|
|
|
|
+ },
|
|
|
|
+ setFrame: function (frameIndex, time, drawOrder) {
|
|
|
|
+ this.frames[frameIndex] = time;
|
|
|
|
+ this.drawOrders[frameIndex] = drawOrder;
|
|
|
|
+ },
|
|
|
|
+ apply: function (skeleton, lastTime, time, firedEvents, alpha) {
|
|
|
|
+ var frames = this.frames;
|
|
|
|
+ if (time < frames[0]) return; // Time is before first frame.
|
|
|
|
+
|
|
|
|
+ var frameIndex;
|
|
|
|
+ if (time >= frames[frames.length - 1]) // Time is after last frame.
|
|
|
|
+ frameIndex = frames.length - 1;
|
|
|
|
+ else
|
|
|
|
+ frameIndex = spine.binarySearch(frames, time, 1) - 1;
|
|
|
|
+
|
|
|
|
+ var drawOrder = skeleton.drawOrder;
|
|
|
|
+ var slots = skeleton.slots;
|
|
|
|
+ var drawOrderToSetupIndex = this.drawOrders[frameIndex];
|
|
|
|
+ if (!drawOrderToSetupIndex) {
|
|
|
|
+ for (var i = 0, n = slots.length; i < n; i++)
|
|
|
|
+ drawOrder[i] = slots[i];
|
|
|
|
+ } else {
|
|
|
|
+ for (var i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
|
|
|
|
+ drawOrder[i] = skeleton.slots[drawOrderToSetupIndex[i]];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
spine.SkeletonData = function () {
|
|
spine.SkeletonData = function () {
|
|
this.bones = [];
|
|
this.bones = [];
|
|
this.slots = [];
|
|
this.slots = [];
|
|
this.skins = [];
|
|
this.skins = [];
|
|
|
|
+ this.events = [];
|
|
this.animations = [];
|
|
this.animations = [];
|
|
};
|
|
};
|
|
spine.SkeletonData.prototype = {
|
|
spine.SkeletonData.prototype = {
|
|
@@ -557,6 +645,13 @@ spine.SkeletonData.prototype = {
|
|
return null;
|
|
return null;
|
|
},
|
|
},
|
|
/** @return May be null. */
|
|
/** @return May be null. */
|
|
|
|
+ findEvent: function (eventName) {
|
|
|
|
+ var events = this.events;
|
|
|
|
+ for (var i = 0, n = events.length; i < n; i++)
|
|
|
|
+ if (events[i].name == eventName) return events[i];
|
|
|
|
+ return null;
|
|
|
|
+ },
|
|
|
|
+ /** @return May be null. */
|
|
findAnimation: function (animationName) {
|
|
findAnimation: function (animationName) {
|
|
var animations = this.animations;
|
|
var animations = this.animations;
|
|
for (var i = 0, n = animations.length; i < n; i++)
|
|
for (var i = 0, n = animations.length; i < n; i++)
|
|
@@ -680,7 +775,7 @@ spine.Skeleton.prototype = {
|
|
var attachment = null;
|
|
var attachment = null;
|
|
if (attachmentName) {
|
|
if (attachmentName) {
|
|
attachment = this.getAttachment(i, attachmentName);
|
|
attachment = this.getAttachment(i, attachmentName);
|
|
- if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName;
|
|
|
|
|
|
+ if (!attachment) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName;
|
|
}
|
|
}
|
|
slot.setAttachment(attachment);
|
|
slot.setAttachment(attachment);
|
|
return;
|
|
return;
|
|
@@ -693,18 +788,38 @@ spine.Skeleton.prototype = {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+spine.EventData = function (name) {
|
|
|
|
+ this.name = name;
|
|
|
|
+}
|
|
|
|
+spine.EventData.prototype = {
|
|
|
|
+ intValue: 0,
|
|
|
|
+ floatValue: 0,
|
|
|
|
+ stringValue: null
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+spine.Event = function (data) {
|
|
|
|
+ this.data = data;
|
|
|
|
+}
|
|
|
|
+spine.Event.prototype = {
|
|
|
|
+ intValue: 0,
|
|
|
|
+ floatValue: 0,
|
|
|
|
+ stringValue: null
|
|
|
|
+};
|
|
|
|
+
|
|
spine.AttachmentType = {
|
|
spine.AttachmentType = {
|
|
region: 0,
|
|
region: 0,
|
|
boundingbox: 1
|
|
boundingbox: 1
|
|
};
|
|
};
|
|
|
|
|
|
-spine.RegionAttachment = function () {
|
|
|
|
|
|
+spine.RegionAttachment = function (name) {
|
|
|
|
+ this.name = name;
|
|
this.offset = [];
|
|
this.offset = [];
|
|
this.offset.length = 8;
|
|
this.offset.length = 8;
|
|
this.uvs = [];
|
|
this.uvs = [];
|
|
this.uvs.length = 8;
|
|
this.uvs.length = 8;
|
|
};
|
|
};
|
|
spine.RegionAttachment.prototype = {
|
|
spine.RegionAttachment.prototype = {
|
|
|
|
+ type: spine.AttachmentType.region,
|
|
x: 0, y: 0,
|
|
x: 0, y: 0,
|
|
rotation: 0,
|
|
rotation: 0,
|
|
scaleX: 1, scaleY: 1,
|
|
scaleX: 1, scaleY: 1,
|
|
@@ -782,6 +897,29 @@ spine.RegionAttachment.prototype = {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+spine.BoundingBoxAttachment = function (name) {
|
|
|
|
+ this.name = name;
|
|
|
|
+ this.vertices = [];
|
|
|
|
+};
|
|
|
|
+spine.BoundingBoxAttachment.prototype = {
|
|
|
|
+ type: spine.AttachmentType.boundingBox,
|
|
|
|
+ computeWorldVertices: function (x, y, bone, worldVertices) {
|
|
|
|
+ x += bone.worldX;
|
|
|
|
+ y += bone.worldY;
|
|
|
|
+ var m00 = bone.m00;
|
|
|
|
+ var m01 = bone.m01;
|
|
|
|
+ var m10 = bone.m10;
|
|
|
|
+ var m11 = bone.m11;
|
|
|
|
+ var vertices = this.vertices;
|
|
|
|
+ for (var i = 0, n = vertices.length; i < n; i += 2) {
|
|
|
|
+ var px = vertices[i];
|
|
|
|
+ var py = vertices[i + 1];
|
|
|
|
+ worldVertices[i] = px * m00 + py * m01 + x;
|
|
|
|
+ worldVertices[i + 1] = px * m10 + py * m11 + y;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
spine.AnimationStateData = function (skeletonData) {
|
|
spine.AnimationStateData = function (skeletonData) {
|
|
this.skeletonData = skeletonData;
|
|
this.skeletonData = skeletonData;
|
|
this.animationToMixTime = {};
|
|
this.animationToMixTime = {};
|
|
@@ -804,104 +942,186 @@ spine.AnimationStateData.prototype = {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+spine.TrackEntry = function () {};
|
|
|
|
+spine.TrackEntry.prototype = {
|
|
|
|
+ next: null, previous: null,
|
|
|
|
+ animation: null,
|
|
|
|
+ loop: false,
|
|
|
|
+ delay: 0, time: 0, lastTime: 0, endTime: 0,
|
|
|
|
+ timeScale: 1,
|
|
|
|
+ mixTime: 0, mixDuration: 0,
|
|
|
|
+ onStart: null, onEnd: null, onComplete: null, onEvent: null
|
|
|
|
+}
|
|
|
|
+
|
|
spine.AnimationState = function (stateData) {
|
|
spine.AnimationState = function (stateData) {
|
|
this.data = stateData;
|
|
this.data = stateData;
|
|
- this.queue = [];
|
|
|
|
|
|
+ this.tracks = [];
|
|
|
|
+ this.events = [];
|
|
};
|
|
};
|
|
spine.AnimationState.prototype = {
|
|
spine.AnimationState.prototype = {
|
|
- current: null,
|
|
|
|
- previous: null,
|
|
|
|
- currentTime: 0,
|
|
|
|
- previousTime: 0,
|
|
|
|
- currentLoop: false,
|
|
|
|
- previousLoop: false,
|
|
|
|
- mixTime: 0,
|
|
|
|
- mixDuration: 0,
|
|
|
|
|
|
+ onStart: null,
|
|
|
|
+ onEnd: null,
|
|
|
|
+ onComplete: null,
|
|
|
|
+ onEvent: null,
|
|
|
|
+ timeScale: 1,
|
|
update: function (delta) {
|
|
update: function (delta) {
|
|
- this.currentTime += delta;
|
|
|
|
- this.previousTime += delta;
|
|
|
|
- this.mixTime += delta;
|
|
|
|
-
|
|
|
|
- if (this.queue.length > 0) {
|
|
|
|
- var entry = this.queue[0];
|
|
|
|
- if (this.currentTime >= entry.delay) {
|
|
|
|
- this._setAnimation(entry.animation, entry.loop);
|
|
|
|
- this.queue.shift();
|
|
|
|
|
|
+ delta *= this.timeScale;
|
|
|
|
+ for (var i = 0; i < this.tracks.length; i++) {
|
|
|
|
+ var current = this.tracks[i];
|
|
|
|
+ if (!current) continue;
|
|
|
|
+
|
|
|
|
+ var trackDelta = delta * current.timeScale;
|
|
|
|
+ var time = current.time + trackDelta;
|
|
|
|
+ var endTime = current.endTime;
|
|
|
|
+
|
|
|
|
+ current.time = time;
|
|
|
|
+ if (current.previous) {
|
|
|
|
+ current.previous.time += trackDelta;
|
|
|
|
+ current.mixTime += trackDelta;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Check if completed the animation or a loop iteration.
|
|
|
|
+ if (current.loop ? (current.lastTime % endTime > time % endTime) : (current.lastTime < endTime && time >= endTime)) {
|
|
|
|
+ var count = Math.floor(time / endTime);
|
|
|
|
+ if (current.onComplete) current.onComplete(i, count);
|
|
|
|
+ if (this.onComplete) this.onComplete(i, count);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var next = current.next;
|
|
|
|
+ if (next) {
|
|
|
|
+ if (time - trackDelta > next.delay) this.setCurrent(i, next);
|
|
|
|
+ } else {
|
|
|
|
+ // End non-looping animation when it reaches its end time and there is no next entry.
|
|
|
|
+ if (!current.loop && current.lastTime >= current.endTime) this.clearTrack(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
apply: function (skeleton) {
|
|
apply: function (skeleton) {
|
|
- if (!this.current) return;
|
|
|
|
- if (this.previous) {
|
|
|
|
- this.previous.apply(skeleton, this.previousTime, this.previousLoop);
|
|
|
|
- var alpha = this.mixTime / this.mixDuration;
|
|
|
|
- if (alpha >= 1) {
|
|
|
|
- alpha = 1;
|
|
|
|
- this.previous = null;
|
|
|
|
|
|
+ for (var i = 0; i < this.tracks.length; i++) {
|
|
|
|
+ var current = this.tracks[i];
|
|
|
|
+ if (!current) continue;
|
|
|
|
+
|
|
|
|
+ this.events.length = 0;
|
|
|
|
+
|
|
|
|
+ var time = current.time;
|
|
|
|
+ var loop = current.loop;
|
|
|
|
+ if (!loop && time > current.endTime) time = current.endTime;
|
|
|
|
+
|
|
|
|
+ var previous = current.previous;
|
|
|
|
+ if (!previous)
|
|
|
|
+ current.animation.apply(skeleton, current.lastTime, time, loop, this.events);
|
|
|
|
+ else {
|
|
|
|
+ var previousTime = previous.time;
|
|
|
|
+ if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
|
|
|
|
+ previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null);
|
|
|
|
+
|
|
|
|
+ var alpha = current.mixTime / current.mixDuration;
|
|
|
|
+ if (alpha >= 1) {
|
|
|
|
+ alpha = 1;
|
|
|
|
+ current.previous = null;
|
|
|
|
+ }
|
|
|
|
+ current.animation.mix(skeleton, current.lastTime, time, loop, this.events, alpha);
|
|
}
|
|
}
|
|
- this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha);
|
|
|
|
- } else
|
|
|
|
- this.current.apply(skeleton, this.currentTime, this.currentLoop);
|
|
|
|
- },
|
|
|
|
- clearAnimation: function () {
|
|
|
|
- this.previous = null;
|
|
|
|
- this.current = null;
|
|
|
|
- this.queue.length = 0;
|
|
|
|
- },
|
|
|
|
- _setAnimation: function (animation, loop) {
|
|
|
|
- this.previous = null;
|
|
|
|
- if (animation && this.current) {
|
|
|
|
- this.mixDuration = this.data.getMix(this.current, animation);
|
|
|
|
- if (this.mixDuration > 0) {
|
|
|
|
- this.mixTime = 0;
|
|
|
|
- this.previous = this.current;
|
|
|
|
- this.previousTime = this.currentTime;
|
|
|
|
- this.previousLoop = this.currentLoop;
|
|
|
|
|
|
+
|
|
|
|
+ for (var ii = 0, nn = this.events.length; ii < nn; ii++) {
|
|
|
|
+ var event = this.events[ii];
|
|
|
|
+ if (current.onEvent != null) current.onEvent(i, event);
|
|
|
|
+ if (this.onEvent != null) this.onEvent(i, event);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ current.lastTime = current.time;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ clearTracks: function () {
|
|
|
|
+ for (var i = 0, n = this.tracks.length; i < n; i++)
|
|
|
|
+ this.clearTrack(i);
|
|
|
|
+ this.tracks.length = 0;
|
|
|
|
+ },
|
|
|
|
+ clearTrack: function (trackIndex) {
|
|
|
|
+ if (trackIndex >= this.tracks.length) return;
|
|
|
|
+ var current = this.tracks[trackIndex];
|
|
|
|
+ if (!current) return;
|
|
|
|
+
|
|
|
|
+ if (current.onEnd != null) current.onEnd(trackIndex);
|
|
|
|
+ if (this.onEnd != null) this.onEnd(trackIndex);
|
|
|
|
+
|
|
|
|
+ this.tracks[trackIndex] = null;
|
|
|
|
+ },
|
|
|
|
+ _expandToIndex: function (index) {
|
|
|
|
+ if (index < this.tracks.length) return this.tracks[index];
|
|
|
|
+ while (index >= this.tracks.length)
|
|
|
|
+ this.tracks.push(null);
|
|
|
|
+ return null;
|
|
|
|
+ },
|
|
|
|
+ setCurrent: function (index, entry) {
|
|
|
|
+ var current = this._expandToIndex(index);
|
|
|
|
+ if (current) {
|
|
|
|
+ current.previous = null;
|
|
|
|
+
|
|
|
|
+ if (current.onEnd != null) current.onEnd(index);
|
|
|
|
+ if (this.onEnd != null) this.onEnd(index);
|
|
|
|
+
|
|
|
|
+ entry.mixDuration = this.data.getMix(current.animation, entry.animation);
|
|
|
|
+ if (entry.mixDuration > 0) {
|
|
|
|
+ entry.mixTime = 0;
|
|
|
|
+ entry.previous = current;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- this.current = animation;
|
|
|
|
- this.currentLoop = loop;
|
|
|
|
- this.currentTime = 0;
|
|
|
|
|
|
+
|
|
|
|
+ this.tracks[index] = entry;
|
|
|
|
+
|
|
|
|
+ if (entry.onStart != null) entry.onStart(index);
|
|
|
|
+ if (this.onStart != null) this.onStart(index);
|
|
},
|
|
},
|
|
- /** @see #setAnimation(Animation, Boolean) */
|
|
|
|
- setAnimationByName: function (animationName, loop) {
|
|
|
|
|
|
+ setAnimationByName: function (trackIndex, animationName, loop) {
|
|
var animation = this.data.skeletonData.findAnimation(animationName);
|
|
var animation = this.data.skeletonData.findAnimation(animationName);
|
|
if (!animation) throw "Animation not found: " + animationName;
|
|
if (!animation) throw "Animation not found: " + animationName;
|
|
- this.setAnimation(animation, loop);
|
|
|
|
|
|
+ return this.setAnimation(trackIndex, animation, loop);
|
|
},
|
|
},
|
|
- /** Set the current animation. Any queued animations are cleared and the current animation time is set to 0.
|
|
|
|
- * @param animation May be null. */
|
|
|
|
- setAnimation: function (animation, loop) {
|
|
|
|
- this.queue.length = 0;
|
|
|
|
- this._setAnimation(animation, loop);
|
|
|
|
|
|
+ /** Set the current animation. Any queued animations are cleared. */
|
|
|
|
+ setAnimation: function (trackIndex, animation, loop) {
|
|
|
|
+ var entry = new spine.TrackEntry();
|
|
|
|
+ entry.animation = animation;
|
|
|
|
+ entry.loop = loop;
|
|
|
|
+ entry.endTime = animation.duration;
|
|
|
|
+ this.setCurrent(trackIndex, entry);
|
|
|
|
+ return entry;
|
|
},
|
|
},
|
|
- /** @see #addAnimation(Animation, Boolean, Number) */
|
|
|
|
- addAnimationByName: function (animationName, loop, delay) {
|
|
|
|
|
|
+ addAnimationByName: function (trackIndex, animationName, loop, delay) {
|
|
var animation = this.data.skeletonData.findAnimation(animationName);
|
|
var animation = this.data.skeletonData.findAnimation(animationName);
|
|
if (!animation) throw "Animation not found: " + animationName;
|
|
if (!animation) throw "Animation not found: " + animationName;
|
|
- this.addAnimation(animation, loop, delay);
|
|
|
|
|
|
+ return this.addAnimation(trackIndex, animation, loop, delay);
|
|
},
|
|
},
|
|
/** Adds an animation to be played delay seconds after the current or last queued animation.
|
|
/** Adds an animation to be played delay seconds after the current or last queued animation.
|
|
* @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */
|
|
* @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */
|
|
- addAnimation: function (animation, loop, delay) {
|
|
|
|
- var entry = {};
|
|
|
|
|
|
+ addAnimation: function (trackIndex, animation, loop, delay) {
|
|
|
|
+ var entry = new spine.TrackEntry();
|
|
entry.animation = animation;
|
|
entry.animation = animation;
|
|
entry.loop = loop;
|
|
entry.loop = loop;
|
|
-
|
|
|
|
- if (!delay || delay <= 0) {
|
|
|
|
- var previousAnimation = this.queue.length == 0 ? this.current : this.queue[this.queue.length - 1].animation;
|
|
|
|
- if (previousAnimation != null)
|
|
|
|
- delay = previousAnimation.duration - this.data.getMix(previousAnimation, animation) + (delay || 0);
|
|
|
|
|
|
+ entry.endTime = animation.duration;
|
|
|
|
+
|
|
|
|
+ var last = this._expandToIndex(trackIndex);
|
|
|
|
+ if (last) {
|
|
|
|
+ while (last.next)
|
|
|
|
+ last = last.next;
|
|
|
|
+ last.next = entry;
|
|
|
|
+ } else
|
|
|
|
+ this.tracks[trackIndex] = entry;
|
|
|
|
+
|
|
|
|
+ if (delay <= 0) {
|
|
|
|
+ if (last)
|
|
|
|
+ delay += last.endTime - this.data.getMix(last.animation, animation);
|
|
else
|
|
else
|
|
delay = 0;
|
|
delay = 0;
|
|
}
|
|
}
|
|
entry.delay = delay;
|
|
entry.delay = delay;
|
|
-
|
|
|
|
- this.queue.push(entry);
|
|
|
|
- },
|
|
|
|
- /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */
|
|
|
|
- isComplete: function () {
|
|
|
|
- return !this.current || this.currentTime >= this.current.duration;
|
|
|
|
|
|
+
|
|
|
|
+ return entry;
|
|
|
|
+ },
|
|
|
|
+ /** May be null. */
|
|
|
|
+ getCurrent: function (trackIndex) {
|
|
|
|
+ if (trackIndex >= this.tracks.length) return null;
|
|
|
|
+ return this.tracks[trackIndex];
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -976,6 +1196,18 @@ spine.SkeletonJson.prototype = {
|
|
if (skin.name == "default") skeletonData.defaultSkin = skin;
|
|
if (skin.name == "default") skeletonData.defaultSkin = skin;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Events.
|
|
|
|
+ var events = root["events"];
|
|
|
|
+ for (var eventName in events) {
|
|
|
|
+ if (!events.hasOwnProperty(eventName)) continue;
|
|
|
|
+ var eventMap = events[eventName];
|
|
|
|
+ var eventData = new spine.EventData(eventName);
|
|
|
|
+ eventData.intValue = eventMap["int"] || 0;
|
|
|
|
+ eventData.floatValue = eventMap["float"] || 0;
|
|
|
|
+ eventData.stringValue = eventMap["string"] || null;
|
|
|
|
+ skeletonData.events.push(eventData);
|
|
|
|
+ }
|
|
|
|
+
|
|
// Animations.
|
|
// Animations.
|
|
var animations = root["animations"];
|
|
var animations = root["animations"];
|
|
for (var animationName in animations) {
|
|
for (var animationName in animations) {
|
|
@@ -1000,6 +1232,10 @@ spine.SkeletonJson.prototype = {
|
|
attachment.width = (map["width"] || 32) * this.scale;
|
|
attachment.width = (map["width"] || 32) * this.scale;
|
|
attachment.height = (map["height"] || 32) * this.scale;
|
|
attachment.height = (map["height"] || 32) * this.scale;
|
|
attachment.updateOffset();
|
|
attachment.updateOffset();
|
|
|
|
+ } else if (type == spine.AttachmentType.boundingBox) {
|
|
|
|
+ var vertices = map["vertices"];
|
|
|
|
+ for (var i = 0, n = vertices.length; i < n; i++)
|
|
|
|
+ attachment.vertices.push(vertices[i] * scale);
|
|
}
|
|
}
|
|
|
|
|
|
return attachment;
|
|
return attachment;
|
|
@@ -1105,6 +1341,64 @@ spine.SkeletonJson.prototype = {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ var events = map["events"];
|
|
|
|
+ if (events) {
|
|
|
|
+ var timeline = new spine.EventTimeline(events.length);
|
|
|
|
+ var frameIndex = 0;
|
|
|
|
+ for (var i = 0, n = events.length; i < n; i++) {
|
|
|
|
+ var eventMap = events[i];
|
|
|
|
+ var eventData = skeletonData.findEvent(eventMap["name"]);
|
|
|
|
+ if (!eventData) throw "Event not found: " + eventMap["name"];
|
|
|
|
+ var event = new spine.Event(eventData);
|
|
|
|
+ event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue;
|
|
|
|
+ event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue;
|
|
|
|
+ event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue;
|
|
|
|
+ timeline.setFrame(frameIndex++, eventMap["time"], event);
|
|
|
|
+ }
|
|
|
|
+ timelines.push(timeline);
|
|
|
|
+ duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var drawOrderValues = map["draworder"];
|
|
|
|
+ if (drawOrderValues) {
|
|
|
|
+ var timeline = new spine.DrawOrderTimeline(drawOrderValues.length);
|
|
|
|
+ var slotCount = skeletonData.slots.length;
|
|
|
|
+ var frameIndex = 0;
|
|
|
|
+ for (var i = 0, n = drawOrderValues.length; i < n; i++) {
|
|
|
|
+ var drawOrderMap = drawOrderValues[i];
|
|
|
|
+ var drawOrder = null;
|
|
|
|
+ if (drawOrderMap["offsets"]) {
|
|
|
|
+ drawOrder = [];
|
|
|
|
+ drawOrder.length = slotCount;
|
|
|
|
+ for (var ii = slotCount - 1; ii >= 0; ii--)
|
|
|
|
+ drawOrder[ii] = -1;
|
|
|
|
+ var offsets = drawOrderMap["offsets"];
|
|
|
|
+ var unchanged = [];
|
|
|
|
+ unchanged.length = slotCount - offsets.length;
|
|
|
|
+ var originalIndex = 0, unchangedIndex = 0;
|
|
|
|
+ for (var ii = 0, nn = offsets.length; ii < nn; ii++) {
|
|
|
|
+ var offsetMap = offsets[ii];
|
|
|
|
+ var slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]);
|
|
|
|
+ if (slotIndex == -1) throw "Slot not found: " + offsetMap["slot"];
|
|
|
|
+ // Collect unchanged items.
|
|
|
|
+ while (originalIndex != slotIndex)
|
|
|
|
+ unchanged[unchangedIndex++] = originalIndex++;
|
|
|
|
+ // Set changed items.
|
|
|
|
+ drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++;
|
|
|
|
+ }
|
|
|
|
+ // Collect remaining unchanged items.
|
|
|
|
+ while (originalIndex < slotCount)
|
|
|
|
+ unchanged[unchangedIndex++] = originalIndex++;
|
|
|
|
+ // Fill in unchanged items.
|
|
|
|
+ for (var ii = slotCount - 1; ii >= 0; ii--)
|
|
|
|
+ if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
|
|
|
|
+ }
|
|
|
|
+ timeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder);
|
|
|
|
+ }
|
|
|
|
+ timelines.push(timeline);
|
|
|
|
+ duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
|
|
|
|
+ }
|
|
|
|
+
|
|
skeletonData.animations.push(new spine.Animation(name, timelines, duration));
|
|
skeletonData.animations.push(new spine.Animation(name, timelines, duration));
|
|
}
|
|
}
|
|
};
|
|
};
|
|
@@ -1118,7 +1412,7 @@ spine.SkeletonJson.readCurve = function (timeline, frameIndex, valueMap) {
|
|
};
|
|
};
|
|
spine.SkeletonJson.toColor = function (hexString, colorIndex) {
|
|
spine.SkeletonJson.toColor = function (hexString, colorIndex) {
|
|
if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString;
|
|
if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString;
|
|
- return parseInt(hexString.substring(colorIndex * 2, (colorIndex * 2)+2), 16) / 255;
|
|
|
|
|
|
+ return parseInt(hexString.substring(colorIndex * 2, (colorIndex * 2) + 2), 16) / 255;
|
|
};
|
|
};
|
|
|
|
|
|
spine.Atlas = function (atlasText, textureLoader) {
|
|
spine.Atlas = function (atlasText, textureLoader) {
|
|
@@ -1341,7 +1635,7 @@ spine.AtlasAttachmentLoader.prototype = {
|
|
newAttachment: function (skin, type, name) {
|
|
newAttachment: function (skin, type, name) {
|
|
switch (type) {
|
|
switch (type) {
|
|
case spine.AttachmentType.boundingbox:
|
|
case spine.AttachmentType.boundingbox:
|
|
- return null; // BOZO - Implement bounding boxes.
|
|
|
|
|
|
+ return new spine.BoundingBoxAttachment(name);
|
|
case spine.AttachmentType.region:
|
|
case spine.AttachmentType.region:
|
|
var region = this.atlas.findRegion(name);
|
|
var region = this.atlas.findRegion(name);
|
|
if (!region) throw "Region not found in atlas: " + name + " (" + type + ")";
|
|
if (!region) throw "Region not found in atlas: " + name + " (" + type + ")";
|
|
@@ -1359,3 +1653,151 @@ spine.AtlasAttachmentLoader.prototype = {
|
|
throw "Unknown attachment type: " + type;
|
|
throw "Unknown attachment type: " + type;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
+
|
|
|
|
+spine.SkeletonBounds = function () {
|
|
|
|
+ this.polygonPool = [];
|
|
|
|
+ this.polygons = [];
|
|
|
|
+ this.boundingBoxes = [];
|
|
|
|
+};
|
|
|
|
+spine.SkeletonBounds.prototype = {
|
|
|
|
+ minX: 0, minY: 0, maxX: 0, maxY: 0,
|
|
|
|
+ update: function (skeleton, updateAabb) {
|
|
|
|
+ var slots = skeleton.slots;
|
|
|
|
+ var slotCount = slots.length;
|
|
|
|
+ var x = skeleton.x, y = skeleton.y;
|
|
|
|
+ var boundingBoxes = this.boundingBoxes;
|
|
|
|
+ var polygonPool = this.polygonPool;
|
|
|
|
+ var polygons = this.polygons;
|
|
|
|
+
|
|
|
|
+ boundingBoxes.length = 0;
|
|
|
|
+ for (var i = 0, n = polygons.length; i < n; i++)
|
|
|
|
+ polygonPool.push(polygons[i]);
|
|
|
|
+ polygons.length = 0;
|
|
|
|
+
|
|
|
|
+ for (var i = 0; i < slotCount; i++) {
|
|
|
|
+ var slot = slots[i];
|
|
|
|
+ var boundingBox = slot.attachment;
|
|
|
|
+ if (boundingBox.type != spine.AttachmentType.boundingBox) continue;
|
|
|
|
+ boundingBoxes.push(boundingBox);
|
|
|
|
+
|
|
|
|
+ var poolCount = polygonPool.length;
|
|
|
|
+ if (poolCount > 0) {
|
|
|
|
+ polygon = polygonPool[poolCount - 1];
|
|
|
|
+ polygonPool.splice(poolCount - 1, 1);
|
|
|
|
+ } else
|
|
|
|
+ polygon = [];
|
|
|
|
+ polygons.push(polygon);
|
|
|
|
+
|
|
|
|
+ polygon.length = boundingBox.vertices.length;
|
|
|
|
+ boundingBox.computeWorldVertices(x, y, slot.bone, polygon);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (updateAabb) this.aabbCompute();
|
|
|
|
+ },
|
|
|
|
+ aabbCompute: function () {
|
|
|
|
+ var polygons = this.polygons;
|
|
|
|
+ var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE;
|
|
|
|
+ for (var i = 0, n = polygons.length; i < n; i++) {
|
|
|
|
+ var vertices = polygons[i];
|
|
|
|
+ for (var ii = 0, nn = vertices.length; ii < nn; ii += 2) {
|
|
|
|
+ var x = vertices[ii];
|
|
|
|
+ var y = vertices[ii + 1];
|
|
|
|
+ minX = Math.min(minX, x);
|
|
|
|
+ minY = Math.min(minY, y);
|
|
|
|
+ maxX = Math.max(maxX, x);
|
|
|
|
+ maxY = Math.max(maxY, y);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ this.minX = minX;
|
|
|
|
+ this.minY = minY;
|
|
|
|
+ this.maxX = maxX;
|
|
|
|
+ this.maxY = maxY;
|
|
|
|
+ },
|
|
|
|
+ /** Returns true if the axis aligned bounding box contains the point. */
|
|
|
|
+ aabbContainsPoint: function (x, y) {
|
|
|
|
+ return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY;
|
|
|
|
+ },
|
|
|
|
+ /** Returns true if the axis aligned bounding box intersects the line segment. */
|
|
|
|
+ aabbIntersectsSegment: function (x1, y1, x2, y2) {
|
|
|
|
+ var minX = this.minX, minY = this.minY, maxX = this.maxX, maxY = this.maxY;
|
|
|
|
+ if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
|
|
|
|
+ return false;
|
|
|
|
+ var m = (y2 - y1) / (x2 - x1);
|
|
|
|
+ var y = m * (minX - x1) + y1;
|
|
|
|
+ if (y > minY && y < maxY) return true;
|
|
|
|
+ y = m * (maxX - x1) + y1;
|
|
|
|
+ if (y > minY && y < maxY) return true;
|
|
|
|
+ var x = (minY - y1) / m + x1;
|
|
|
|
+ if (x > minX && x < maxX) return true;
|
|
|
|
+ x = (maxY - y1) / m + x1;
|
|
|
|
+ if (x > minX && x < maxX) return true;
|
|
|
|
+ return false;
|
|
|
|
+ },
|
|
|
|
+ /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
|
|
|
|
+ aabbIntersectsSkeleton: function (bounds) {
|
|
|
|
+ return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY;
|
|
|
|
+ },
|
|
|
|
+ /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
|
|
|
|
+ * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */
|
|
|
|
+ containsPoint: function (x, y) {
|
|
|
|
+ var polygons = this.polygons;
|
|
|
|
+ for (var i = 0, n = polygons.length; i < n; i++)
|
|
|
|
+ if (this.polygonContainsPoint(polygons[i], x, y)) return this.boundingBoxes[i];
|
|
|
|
+ return null;
|
|
|
|
+ },
|
|
|
|
+ /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
|
|
|
|
+ * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */
|
|
|
|
+ intersectsSegment: function (x1, y1, x2, y2) {
|
|
|
|
+ var polygons = this.polygons;
|
|
|
|
+ for (var i = 0, n = polygons.length; i < n; i++)
|
|
|
|
+ if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return boundingBoxes[i];
|
|
|
|
+ return null;
|
|
|
|
+ },
|
|
|
|
+ /** Returns true if the polygon contains the point. */
|
|
|
|
+ polygonContainsPoint: function (polygon, x, y) {
|
|
|
|
+ var nn = polygon.length;
|
|
|
|
+ var prevIndex = nn - 2;
|
|
|
|
+ var inside = false;
|
|
|
|
+ for (var ii = 0; ii < nn; ii += 2) {
|
|
|
|
+ var vertexY = polygon[ii + 1];
|
|
|
|
+ var prevY = polygon[prevIndex + 1];
|
|
|
|
+ if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
|
|
|
|
+ var vertexX = polygon[ii];
|
|
|
|
+ if (vertexX + (y - vertexY) / (prevY - vertexY) * (polygon[prevIndex] - vertexX) < x) inside = !inside;
|
|
|
|
+ }
|
|
|
|
+ prevIndex = ii;
|
|
|
|
+ }
|
|
|
|
+ return inside;
|
|
|
|
+ },
|
|
|
|
+ /** Returns true if the polygon contains the line segment. */
|
|
|
|
+ intersectsSegment: function (polygon, x1, y1, x2, y2) {
|
|
|
|
+ var nn = polygon.length;
|
|
|
|
+ var width12 = x1 - x2, height12 = y1 - y2;
|
|
|
|
+ var det1 = x1 * y2 - y1 * x2;
|
|
|
|
+ var x3 = polygon[nn - 2], y3 = polygon[nn - 1];
|
|
|
|
+ for (var ii = 0; ii < nn; ii += 2) {
|
|
|
|
+ var x4 = polygon[ii], y4 = polygon[ii + 1];
|
|
|
|
+ var det2 = x3 * y4 - y3 * x4;
|
|
|
|
+ var width34 = x3 - x4, height34 = y3 - y4;
|
|
|
|
+ var det3 = width12 * height34 - height12 * width34;
|
|
|
|
+ var x = (det1 * width34 - width12 * det2) / det3;
|
|
|
|
+ if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
|
|
|
|
+ var y = (det1 * height34 - height12 * det2) / det3;
|
|
|
|
+ if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
|
|
|
|
+ }
|
|
|
|
+ x3 = x4;
|
|
|
|
+ y3 = y4;
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ },
|
|
|
|
+ getPolygon: function (attachment) {
|
|
|
|
+ var index = this.boundingBoxes.indexOf(attachment);
|
|
|
|
+ return index == -1 ? null : this.polygons[index];
|
|
|
|
+ },
|
|
|
|
+ getWidth: function () {
|
|
|
|
+ return this.maxX - this.minX;
|
|
|
|
+ },
|
|
|
|
+ getHeight: function () {
|
|
|
|
+ return this.maxY - this.minY;
|
|
|
|
+ }
|
|
|
|
+};
|