mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
Merge branch '4.1-beta' into 4.2-beta
This commit is contained in:
commit
d297e1b4ce
@ -91,10 +91,7 @@ public class SkeletonRenderer {
|
|||||||
Object[] drawOrder = skeleton.drawOrder.items;
|
Object[] drawOrder = skeleton.drawOrder.items;
|
||||||
for (int i = 0, n = skeleton.drawOrder.size; i < n; i++) {
|
for (int i = 0, n = skeleton.drawOrder.size; i < n; i++) {
|
||||||
Slot slot = (Slot)drawOrder[i];
|
Slot slot = (Slot)drawOrder[i];
|
||||||
if (!slot.bone.active) {
|
if (!slot.bone.active) continue;
|
||||||
clipper.clipEnd(slot);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Attachment attachment = slot.attachment;
|
Attachment attachment = slot.attachment;
|
||||||
if (attachment instanceof RegionAttachment) {
|
if (attachment instanceof RegionAttachment) {
|
||||||
RegionAttachment region = (RegionAttachment)attachment;
|
RegionAttachment region = (RegionAttachment)attachment;
|
||||||
@ -129,8 +126,8 @@ public class SkeletonRenderer {
|
|||||||
batch.draw(region.getRegion().getTexture(), vertices, 0, 20);
|
batch.draw(region.getRegion().getTexture(), vertices, 0, 20);
|
||||||
|
|
||||||
} else if (attachment instanceof ClippingAttachment) {
|
} else if (attachment instanceof ClippingAttachment) {
|
||||||
clipper.clipStart(slot, (ClippingAttachment)attachment);
|
throw new RuntimeException(batch.getClass().getSimpleName()
|
||||||
continue;
|
+ " cannot perform clipping, PolygonSpriteBatch or TwoColorPolygonBatch is required.");
|
||||||
|
|
||||||
} else if (attachment instanceof MeshAttachment) {
|
} else if (attachment instanceof MeshAttachment) {
|
||||||
throw new RuntimeException(batch.getClass().getSimpleName()
|
throw new RuntimeException(batch.getClass().getSimpleName()
|
||||||
@ -140,10 +137,7 @@ public class SkeletonRenderer {
|
|||||||
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
||||||
if (attachmentSkeleton != null) draw(batch, attachmentSkeleton);
|
if (attachmentSkeleton != null) draw(batch, attachmentSkeleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
clipper.clipEnd(slot);
|
|
||||||
}
|
}
|
||||||
clipper.clipEnd();
|
|
||||||
if (vertexEffect != null) vertexEffect.end();
|
if (vertexEffect != null) vertexEffect.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -56,8 +56,8 @@ You can include a module in your project via a `<script>` tag from the [unpkg](h
|
|||||||
<script src="https://unpkg.com/@esotericsoftware/spine-player@4.0.*/dist/iife/spine-player.js">
|
<script src="https://unpkg.com/@esotericsoftware/spine-player@4.0.*/dist/iife/spine-player.js">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/@esotericsoftware/spine-player@4.0.*/dist/spine-player.css">
|
<link rel="stylesheet" href="https://unpkg.com/@esotericsoftware/spine-player@4.0.*/dist/spine-player.css">
|
||||||
|
|
||||||
// spine-ts WebGL
|
// spine-ts ThreeJS
|
||||||
<script src="https://unpkg.com/@esotericsoftware/spine-threejs@4.0.*/dist/iife/spine-webgl.js">
|
<script src="https://unpkg.com/@esotericsoftware/spine-threejs@4.0.*/dist/iife/spine-threejs.js">
|
||||||
```
|
```
|
||||||
|
|
||||||
We also provide `js.map` source maps. They will be automatically fetched from unpkg when debugging code of a spine-module in Chrome, Firefox, or Safari, mapping the JavaScript code back to its original TypeScript sources.
|
We also provide `js.map` source maps. They will be automatically fetched from unpkg when debugging code of a spine-module in Chrome, Firefox, or Safari, mapping the JavaScript code back to its original TypeScript sources.
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import { AssetManagerBase, Downloader } from "@esotericsoftware/spine-core"
|
|||||||
import { CanvasTexture } from "./CanvasTexture";
|
import { CanvasTexture } from "./CanvasTexture";
|
||||||
|
|
||||||
export class AssetManager extends AssetManagerBase {
|
export class AssetManager extends AssetManagerBase {
|
||||||
constructor (pathPrefix: string = "", downloader: Downloader = null) {
|
constructor (pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
||||||
super((image: HTMLImageElement) => { return new CanvasTexture(image); }, pathPrefix, downloader);
|
super((image: HTMLImageElement | ImageBitmap) => { return new CanvasTexture(image); }, pathPrefix, downloader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
import { Texture, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
|
import { Texture, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
|
||||||
|
|
||||||
export class CanvasTexture extends Texture {
|
export class CanvasTexture extends Texture {
|
||||||
constructor (image: HTMLImageElement) {
|
constructor (image: HTMLImageElement | ImageBitmap) {
|
||||||
super(image);
|
super(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export class SkeletonRenderer {
|
|||||||
|
|
||||||
let w = region.width, h = region.height;
|
let w = region.width, h = region.height;
|
||||||
ctx.translate(w / 2, h / 2);
|
ctx.translate(w / 2, h / 2);
|
||||||
if (attachment.region.degrees == 90) {
|
if (attachment.region!.degrees == 90) {
|
||||||
let t = w;
|
let t = w;
|
||||||
w = h;
|
w = h;
|
||||||
h = t;
|
h = t;
|
||||||
@ -107,9 +107,9 @@ export class SkeletonRenderer {
|
|||||||
let skeletonColor = skeleton.color;
|
let skeletonColor = skeleton.color;
|
||||||
let drawOrder = skeleton.drawOrder;
|
let drawOrder = skeleton.drawOrder;
|
||||||
|
|
||||||
let blendMode: BlendMode = null;
|
let blendMode: BlendMode | null = null;
|
||||||
let vertices: ArrayLike<number> = this.vertices;
|
let vertices: ArrayLike<number> = this.vertices;
|
||||||
let triangles: Array<number> = null;
|
let triangles: Array<number> | null = null;
|
||||||
|
|
||||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||||
let slot = drawOrder[i];
|
let slot = drawOrder[i];
|
||||||
@ -127,7 +127,8 @@ export class SkeletonRenderer {
|
|||||||
let mesh = <MeshAttachment>attachment;
|
let mesh = <MeshAttachment>attachment;
|
||||||
vertices = this.computeMeshVertices(slot, mesh, false);
|
vertices = this.computeMeshVertices(slot, mesh, false);
|
||||||
triangles = mesh.triangles;
|
triangles = mesh.triangles;
|
||||||
texture = (<TextureAtlasRegion>mesh.region.renderObject).page.texture.getImage() as HTMLImageElement;
|
let region = (<TextureAtlasRegion>mesh.region!.renderObject);
|
||||||
|
texture = region.page.texture!.getImage() as HTMLImageElement;
|
||||||
} else
|
} else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@ -42,8 +42,8 @@ import { SequenceMode, SequenceModeValues } from "./attachments/Sequence";
|
|||||||
export class Animation {
|
export class Animation {
|
||||||
/** The animation's name, which is unique across all animations in the skeleton. */
|
/** The animation's name, which is unique across all animations in the skeleton. */
|
||||||
name: string;
|
name: string;
|
||||||
timelines: Array<Timeline> = null;
|
timelines: Array<Timeline> = [];
|
||||||
timelineIds: StringSet = null;
|
timelineIds: StringSet = new StringSet();
|
||||||
|
|
||||||
/** The duration of the animation in seconds, which is the highest time of all keys in the timeline. */
|
/** The duration of the animation in seconds, which is the highest time of all keys in the timeline. */
|
||||||
duration: number;
|
duration: number;
|
||||||
@ -58,7 +58,7 @@ export class Animation {
|
|||||||
setTimelines (timelines: Array<Timeline>) {
|
setTimelines (timelines: Array<Timeline>) {
|
||||||
if (!timelines) throw new Error("timelines cannot be null.");
|
if (!timelines) throw new Error("timelines cannot be null.");
|
||||||
this.timelines = timelines;
|
this.timelines = timelines;
|
||||||
this.timelineIds = new StringSet();
|
this.timelineIds.clear();
|
||||||
for (var i = 0; i < timelines.length; i++)
|
for (var i = 0; i < timelines.length; i++)
|
||||||
this.timelineIds.addAll(timelines[i].getPropertyIds());
|
this.timelineIds.addAll(timelines[i].getPropertyIds());
|
||||||
}
|
}
|
||||||
@ -155,8 +155,8 @@ const Property = {
|
|||||||
|
|
||||||
/** The interface for all timelines. */
|
/** The interface for all timelines. */
|
||||||
export abstract class Timeline {
|
export abstract class Timeline {
|
||||||
propertyIds: string[] = null;
|
propertyIds: string[];
|
||||||
frames: NumberArrayLike = null;
|
frames: NumberArrayLike;
|
||||||
|
|
||||||
constructor (frameCount: number, propertyIds: string[]) {
|
constructor (frameCount: number, propertyIds: string[]) {
|
||||||
this.propertyIds = propertyIds;
|
this.propertyIds = propertyIds;
|
||||||
@ -179,7 +179,7 @@ export abstract class Timeline {
|
|||||||
return this.frames[this.frames.length - this.getFrameEntries()];
|
return this.frames[this.frames.length - this.getFrameEntries()];
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void;
|
abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event> | null, alpha: number, blend: MixBlend, direction: MixDirection): void;
|
||||||
|
|
||||||
static search1 (frames: NumberArrayLike, time: number) {
|
static search1 (frames: NumberArrayLike, time: number) {
|
||||||
let n = frames.length;
|
let n = frames.length;
|
||||||
@ -208,7 +208,7 @@ export interface SlotTimeline {
|
|||||||
|
|
||||||
/** The base class for timelines that use interpolation between key frame values. */
|
/** The base class for timelines that use interpolation between key frame values. */
|
||||||
export abstract class CurveTimeline extends Timeline {
|
export abstract class CurveTimeline extends Timeline {
|
||||||
protected curves: NumberArrayLike = null; // type, x, y, ...
|
protected curves: NumberArrayLike; // type, x, y, ...
|
||||||
|
|
||||||
constructor (frameCount: number, bezierCount: number, propertyIds: string[]) {
|
constructor (frameCount: number, bezierCount: number, propertyIds: string[]) {
|
||||||
super(frameCount, propertyIds);
|
super(frameCount, propertyIds);
|
||||||
@ -369,7 +369,7 @@ export class RotateTimeline extends CurveTimeline1 implements BoneTimeline {
|
|||||||
this.boneIndex = boneIndex;
|
this.boneIndex = boneIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
|
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event> | null, alpha: number, blend: MixBlend, direction: MixDirection) {
|
||||||
let bone = skeleton.bones[this.boneIndex];
|
let bone = skeleton.bones[this.boneIndex];
|
||||||
if (!bone.active) return;
|
if (!bone.active) return;
|
||||||
|
|
||||||
@ -1176,9 +1176,9 @@ export class RGBA2Timeline extends CurveTimeline implements SlotTimeline {
|
|||||||
if (!slot.bone.active) return;
|
if (!slot.bone.active) return;
|
||||||
|
|
||||||
let frames = this.frames;
|
let frames = this.frames;
|
||||||
let light = slot.color, dark = slot.darkColor;
|
let light = slot.color, dark = slot.darkColor!;
|
||||||
if (time < frames[0]) {
|
if (time < frames[0]) {
|
||||||
let setupLight = slot.data.color, setupDark = slot.data.darkColor;
|
let setupLight = slot.data.color, setupDark = slot.data.darkColor!;
|
||||||
switch (blend) {
|
switch (blend) {
|
||||||
case MixBlend.setup:
|
case MixBlend.setup:
|
||||||
light.setFromColor(setupLight);
|
light.setFromColor(setupLight);
|
||||||
@ -1245,7 +1245,7 @@ export class RGBA2Timeline extends CurveTimeline implements SlotTimeline {
|
|||||||
} else {
|
} else {
|
||||||
if (blend == MixBlend.setup) {
|
if (blend == MixBlend.setup) {
|
||||||
light.setFromColor(slot.data.color);
|
light.setFromColor(slot.data.color);
|
||||||
let setupDark = slot.data.darkColor;
|
let setupDark = slot.data.darkColor!;
|
||||||
dark.r = setupDark.r;
|
dark.r = setupDark.r;
|
||||||
dark.g = setupDark.g;
|
dark.g = setupDark.g;
|
||||||
dark.b = setupDark.b;
|
dark.b = setupDark.b;
|
||||||
@ -1291,9 +1291,9 @@ export class RGB2Timeline extends CurveTimeline implements SlotTimeline {
|
|||||||
if (!slot.bone.active) return;
|
if (!slot.bone.active) return;
|
||||||
|
|
||||||
let frames = this.frames;
|
let frames = this.frames;
|
||||||
let light = slot.color, dark = slot.darkColor;
|
let light = slot.color, dark = slot.darkColor!;
|
||||||
if (time < frames[0]) {
|
if (time < frames[0]) {
|
||||||
let setupLight = slot.data.color, setupDark = slot.data.darkColor;
|
let setupLight = slot.data.color, setupDark = slot.data.darkColor!;
|
||||||
switch (blend) {
|
switch (blend) {
|
||||||
case MixBlend.setup:
|
case MixBlend.setup:
|
||||||
light.r = setupLight.r;
|
light.r = setupLight.r;
|
||||||
@ -1360,7 +1360,7 @@ export class RGB2Timeline extends CurveTimeline implements SlotTimeline {
|
|||||||
dark.b = b2;
|
dark.b = b2;
|
||||||
} else {
|
} else {
|
||||||
if (blend == MixBlend.setup) {
|
if (blend == MixBlend.setup) {
|
||||||
let setupLight = slot.data.color, setupDark = slot.data.darkColor;
|
let setupLight = slot.data.color, setupDark = slot.data.darkColor!;
|
||||||
light.r = setupLight.r;
|
light.r = setupLight.r;
|
||||||
light.g = setupLight.g;
|
light.g = setupLight.g;
|
||||||
light.b = setupLight.b;
|
light.b = setupLight.b;
|
||||||
@ -1383,7 +1383,7 @@ export class AttachmentTimeline extends Timeline implements SlotTimeline {
|
|||||||
slotIndex = 0;
|
slotIndex = 0;
|
||||||
|
|
||||||
/** The attachment name for each key frame. May contain null values to clear the attachment. */
|
/** The attachment name for each key frame. May contain null values to clear the attachment. */
|
||||||
attachmentNames: Array<string>;
|
attachmentNames: Array<string | null>;
|
||||||
|
|
||||||
constructor (frameCount: number, slotIndex: number) {
|
constructor (frameCount: number, slotIndex: number) {
|
||||||
super(frameCount, [
|
super(frameCount, [
|
||||||
@ -1398,7 +1398,7 @@ export class AttachmentTimeline extends Timeline implements SlotTimeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the time in seconds and the attachment name for the specified key frame. */
|
/** Sets the time in seconds and the attachment name for the specified key frame. */
|
||||||
setFrame (frame: number, time: number, attachmentName: string) {
|
setFrame (frame: number, time: number, attachmentName: string | null) {
|
||||||
this.frames[frame] = time;
|
this.frames[frame] = time;
|
||||||
this.attachmentNames[frame] = attachmentName;
|
this.attachmentNames[frame] = attachmentName;
|
||||||
}
|
}
|
||||||
@ -1420,7 +1420,7 @@ export class AttachmentTimeline extends Timeline implements SlotTimeline {
|
|||||||
this.setAttachment(skeleton, slot, this.attachmentNames[Timeline.search1(this.frames, time)]);
|
this.setAttachment(skeleton, slot, this.attachmentNames[Timeline.search1(this.frames, time)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string) {
|
setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string | null) {
|
||||||
slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
|
slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1430,10 +1430,10 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline {
|
|||||||
slotIndex = 0;
|
slotIndex = 0;
|
||||||
|
|
||||||
/** The attachment that will be deformed. */
|
/** The attachment that will be deformed. */
|
||||||
attachment: VertexAttachment = null;
|
attachment: VertexAttachment;
|
||||||
|
|
||||||
/** The vertices for each key frame. */
|
/** The vertices for each key frame. */
|
||||||
vertices: Array<NumberArrayLike> = null;
|
vertices: Array<NumberArrayLike>;
|
||||||
|
|
||||||
constructor (frameCount: number, bezierCount: number, slotIndex: number, attachment: VertexAttachment) {
|
constructor (frameCount: number, bezierCount: number, slotIndex: number, attachment: VertexAttachment) {
|
||||||
super(frameCount, bezierCount, [
|
super(frameCount, bezierCount, [
|
||||||
@ -1508,7 +1508,8 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline {
|
|||||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
|
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
|
||||||
let slot: Slot = skeleton.slots[this.slotIndex];
|
let slot: Slot = skeleton.slots[this.slotIndex];
|
||||||
if (!slot.bone.active) return;
|
if (!slot.bone.active) return;
|
||||||
let slotAttachment: Attachment = slot.getAttachment();
|
let slotAttachment: Attachment | null = slot.getAttachment();
|
||||||
|
if (!slotAttachment) return;
|
||||||
if (!(slotAttachment instanceof VertexAttachment) || (<VertexAttachment>slotAttachment).timelineAttahment != this.attachment) return;
|
if (!(slotAttachment instanceof VertexAttachment) || (<VertexAttachment>slotAttachment).timelineAttahment != this.attachment) return;
|
||||||
|
|
||||||
let deform: Array<number> = slot.deform;
|
let deform: Array<number> = slot.deform;
|
||||||
@ -1685,7 +1686,7 @@ export class EventTimeline extends Timeline {
|
|||||||
static propertyIds = ["" + Property.event];
|
static propertyIds = ["" + Property.event];
|
||||||
|
|
||||||
/** The event for each key frame. */
|
/** The event for each key frame. */
|
||||||
events: Array<Event> = null;
|
events: Array<Event>;
|
||||||
|
|
||||||
constructor (frameCount: number) {
|
constructor (frameCount: number) {
|
||||||
super(frameCount, EventTimeline.propertyIds);
|
super(frameCount, EventTimeline.propertyIds);
|
||||||
@ -1738,11 +1739,11 @@ export class DrawOrderTimeline extends Timeline {
|
|||||||
static propertyIds = ["" + Property.drawOrder];
|
static propertyIds = ["" + Property.drawOrder];
|
||||||
|
|
||||||
/** The draw order for each key frame. See {@link #setFrame(int, float, int[])}. */
|
/** The draw order for each key frame. See {@link #setFrame(int, float, int[])}. */
|
||||||
drawOrders: Array<Array<number>> = null;
|
drawOrders: Array<Array<number> | null>;
|
||||||
|
|
||||||
constructor (frameCount: number) {
|
constructor (frameCount: number) {
|
||||||
super(frameCount, DrawOrderTimeline.propertyIds);
|
super(frameCount, DrawOrderTimeline.propertyIds);
|
||||||
this.drawOrders = new Array<Array<number>>(frameCount);
|
this.drawOrders = new Array<Array<number> | null>(frameCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFrameCount () {
|
getFrameCount () {
|
||||||
@ -1752,7 +1753,7 @@ export class DrawOrderTimeline extends Timeline {
|
|||||||
/** Sets the time in seconds and the draw order for the specified key frame.
|
/** Sets the time in seconds and the draw order for the specified key frame.
|
||||||
* @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose
|
* @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose
|
||||||
* draw order. */
|
* draw order. */
|
||||||
setFrame (frame: number, time: number, drawOrder: Array<number>) {
|
setFrame (frame: number, time: number, drawOrder: Array<number> | null) {
|
||||||
this.frames[frame] = time;
|
this.frames[frame] = time;
|
||||||
this.drawOrders[frame] = drawOrder;
|
this.drawOrders[frame] = drawOrder;
|
||||||
}
|
}
|
||||||
@ -1768,7 +1769,8 @@ export class DrawOrderTimeline extends Timeline {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let drawOrderToSetupIndex = this.drawOrders[Timeline.search1(this.frames, time)];
|
let idx = Timeline.search1(this.frames, time);
|
||||||
|
let drawOrderToSetupIndex = this.drawOrders[idx];
|
||||||
if (!drawOrderToSetupIndex)
|
if (!drawOrderToSetupIndex)
|
||||||
Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
|
Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
|
||||||
else {
|
else {
|
||||||
@ -2157,7 +2159,7 @@ export class SequenceTimeline extends Timeline implements SlotTimeline {
|
|||||||
|
|
||||||
constructor (frameCount: number, slotIndex: number, attachment: HasTextureRegion) {
|
constructor (frameCount: number, slotIndex: number, attachment: HasTextureRegion) {
|
||||||
super(frameCount, [
|
super(frameCount, [
|
||||||
Property.sequence + "|" + slotIndex + "|" + attachment.sequence.id
|
Property.sequence + "|" + slotIndex + "|" + attachment.sequence!.id
|
||||||
]);
|
]);
|
||||||
this.slotIndex = slotIndex;
|
this.slotIndex = slotIndex;
|
||||||
this.attachment = attachment;
|
this.attachment = attachment;
|
||||||
@ -2207,7 +2209,7 @@ export class SequenceTimeline extends Timeline implements SlotTimeline {
|
|||||||
let modeAndIndex = frames[i + SequenceTimeline.MODE];
|
let modeAndIndex = frames[i + SequenceTimeline.MODE];
|
||||||
let delay = frames[i + SequenceTimeline.DELAY];
|
let delay = frames[i + SequenceTimeline.DELAY];
|
||||||
|
|
||||||
let index = modeAndIndex >> 4, count = this.attachment.sequence.regions.length;
|
let index = modeAndIndex >> 4, count = this.attachment.sequence!.regions.length;
|
||||||
let mode = SequenceModeValues[modeAndIndex & 0xf];
|
let mode = SequenceModeValues[modeAndIndex & 0xf];
|
||||||
if (mode != SequenceMode.hold) {
|
if (mode != SequenceMode.hold) {
|
||||||
index += (((time - before) / delay + 0.00001) | 0);
|
index += (((time - before) / delay + 0.00001) | 0);
|
||||||
|
|||||||
@ -40,16 +40,16 @@ import { Event } from "./Event";
|
|||||||
*
|
*
|
||||||
* See [Applying Animations](http://esotericsoftware.com/spine-applying-animations/) in the Spine Runtimes Guide. */
|
* See [Applying Animations](http://esotericsoftware.com/spine-applying-animations/) in the Spine Runtimes Guide. */
|
||||||
export class AnimationState {
|
export class AnimationState {
|
||||||
|
static _emptyAnimation = new Animation("<empty>", [], 0);
|
||||||
private static emptyAnimation (): Animation {
|
private static emptyAnimation (): Animation {
|
||||||
if (!_emptyAnimation) _emptyAnimation = new Animation("<empty>", [], 0);
|
return AnimationState._emptyAnimation;
|
||||||
return _emptyAnimation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The AnimationStateData to look up mix durations. */
|
/** The AnimationStateData to look up mix durations. */
|
||||||
data: AnimationStateData = null;
|
data: AnimationStateData;
|
||||||
|
|
||||||
/** The list of tracks that currently have animations, which may contain null entries. */
|
/** The list of tracks that currently have animations, which may contain null entries. */
|
||||||
tracks = new Array<TrackEntry>();
|
tracks = new Array<TrackEntry | null>();
|
||||||
|
|
||||||
/** Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower
|
/** Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower
|
||||||
* or faster. Defaults to 1.
|
* or faster. Defaults to 1.
|
||||||
@ -113,7 +113,7 @@ export class AnimationState {
|
|||||||
}
|
}
|
||||||
if (current.mixingFrom && this.updateMixingFrom(current, delta)) {
|
if (current.mixingFrom && this.updateMixingFrom(current, delta)) {
|
||||||
// End mixing from entries once all have completed.
|
// End mixing from entries once all have completed.
|
||||||
let from = current.mixingFrom;
|
let from: TrackEntry | null = current.mixingFrom;
|
||||||
current.mixingFrom = null;
|
current.mixingFrom = null;
|
||||||
if (from) from.mixingTo = null;
|
if (from) from.mixingTo = null;
|
||||||
while (from) {
|
while (from) {
|
||||||
@ -181,12 +181,12 @@ export class AnimationState {
|
|||||||
|
|
||||||
// Apply current entry.
|
// Apply current entry.
|
||||||
let animationLast = current.animationLast, animationTime = current.getAnimationTime(), applyTime = animationTime;
|
let animationLast = current.animationLast, animationTime = current.getAnimationTime(), applyTime = animationTime;
|
||||||
let applyEvents = events;
|
let applyEvents: Event[] | null = events;
|
||||||
if (current.reverse) {
|
if (current.reverse) {
|
||||||
applyTime = current.animation.duration - applyTime;
|
applyTime = current.animation!.duration - applyTime;
|
||||||
applyEvents = null;
|
applyEvents = null;
|
||||||
}
|
}
|
||||||
let timelines = current.animation.timelines;
|
let timelines = current.animation!.timelines;
|
||||||
let timelineCount = timelines.length;
|
let timelineCount = timelines.length;
|
||||||
if ((i == 0 && mix == 1) || blend == MixBlend.add) {
|
if ((i == 0 && mix == 1) || blend == MixBlend.add) {
|
||||||
for (let ii = 0; ii < timelineCount; ii++) {
|
for (let ii = 0; ii < timelineCount; ii++) {
|
||||||
@ -246,7 +246,7 @@ export class AnimationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyMixingFrom (to: TrackEntry, skeleton: Skeleton, blend: MixBlend) {
|
applyMixingFrom (to: TrackEntry, skeleton: Skeleton, blend: MixBlend) {
|
||||||
let from = to.mixingFrom;
|
let from = to.mixingFrom!;
|
||||||
if (from.mixingFrom) this.applyMixingFrom(from, skeleton, blend);
|
if (from.mixingFrom) this.applyMixingFrom(from, skeleton, blend);
|
||||||
|
|
||||||
let mix = 0;
|
let mix = 0;
|
||||||
@ -260,13 +260,13 @@ export class AnimationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
|
let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
|
||||||
let timelines = from.animation.timelines;
|
let timelines = from.animation!.timelines;
|
||||||
let timelineCount = timelines.length;
|
let timelineCount = timelines.length;
|
||||||
let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
|
let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
|
||||||
let animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime;
|
let animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime;
|
||||||
let events = null;
|
let events = null;
|
||||||
if (from.reverse)
|
if (from.reverse)
|
||||||
applyTime = from.animation.duration - applyTime;
|
applyTime = from.animation!.duration - applyTime;
|
||||||
else if (mix < from.eventThreshold)
|
else if (mix < from.eventThreshold)
|
||||||
events = this.events;
|
events = this.events;
|
||||||
|
|
||||||
@ -349,7 +349,7 @@ export class AnimationState {
|
|||||||
if (slot.attachmentState <= this.unkeyedState) slot.attachmentState = this.unkeyedState + SETUP;
|
if (slot.attachmentState <= this.unkeyedState) slot.attachmentState = this.unkeyedState + SETUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string, attachments: boolean) {
|
setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string | null, attachments: boolean) {
|
||||||
slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName));
|
slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName));
|
||||||
if (attachments) slot.attachmentState = this.unkeyedState + CURRENT;
|
if (attachments) slot.attachmentState = this.unkeyedState + CURRENT;
|
||||||
}
|
}
|
||||||
@ -645,7 +645,7 @@ export class AnimationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @param last May be null. */
|
/** @param last May be null. */
|
||||||
trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) {
|
trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry | null) {
|
||||||
let entry = this.trackEntryPool.obtain();
|
let entry = this.trackEntryPool.obtain();
|
||||||
entry.reset();
|
entry.reset();
|
||||||
entry.trackIndex = trackIndex;
|
entry.trackIndex = trackIndex;
|
||||||
@ -674,7 +674,7 @@ export class AnimationState {
|
|||||||
|
|
||||||
entry.alpha = 1;
|
entry.alpha = 1;
|
||||||
entry.mixTime = 0;
|
entry.mixTime = 0;
|
||||||
entry.mixDuration = !last ? 0 : this.data.getMix(last.animation, animation);
|
entry.mixDuration = !last ? 0 : this.data.getMix(last.animation!, animation);
|
||||||
entry.interruptAlpha = 1;
|
entry.interruptAlpha = 1;
|
||||||
entry.totalAlpha = 0;
|
entry.totalAlpha = 0;
|
||||||
entry.mixBlend = MixBlend.replace;
|
entry.mixBlend = MixBlend.replace;
|
||||||
@ -710,8 +710,8 @@ export class AnimationState {
|
|||||||
|
|
||||||
computeHold (entry: TrackEntry) {
|
computeHold (entry: TrackEntry) {
|
||||||
let to = entry.mixingTo;
|
let to = entry.mixingTo;
|
||||||
let timelines = entry.animation.timelines;
|
let timelines = entry.animation!.timelines;
|
||||||
let timelinesCount = entry.animation.timelines.length;
|
let timelinesCount = entry.animation!.timelines.length;
|
||||||
let timelineMode = entry.timelineMode;
|
let timelineMode = entry.timelineMode;
|
||||||
timelineMode.length = timelinesCount;
|
timelineMode.length = timelinesCount;
|
||||||
let timelineHoldMix = entry.timelineHoldMix;
|
let timelineHoldMix = entry.timelineHoldMix;
|
||||||
@ -731,11 +731,11 @@ export class AnimationState {
|
|||||||
if (!propertyIDs.addAll(ids))
|
if (!propertyIDs.addAll(ids))
|
||||||
timelineMode[i] = SUBSEQUENT;
|
timelineMode[i] = SUBSEQUENT;
|
||||||
else if (!to || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
|
else if (!to || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
|
||||||
|| timeline instanceof EventTimeline || !to.animation.hasTimeline(ids)) {
|
|| timeline instanceof EventTimeline || !to.animation!.hasTimeline(ids)) {
|
||||||
timelineMode[i] = FIRST;
|
timelineMode[i] = FIRST;
|
||||||
} else {
|
} else {
|
||||||
for (let next = to.mixingTo; next; next = next.mixingTo) {
|
for (let next = to.mixingTo; next; next = next!.mixingTo) {
|
||||||
if (next.animation.hasTimeline(ids)) continue;
|
if (next.animation!.hasTimeline(ids)) continue;
|
||||||
if (entry.mixDuration > 0) {
|
if (entry.mixDuration > 0) {
|
||||||
timelineMode[i] = HOLD_MIX;
|
timelineMode[i] = HOLD_MIX;
|
||||||
timelineHoldMix[i] = next;
|
timelineHoldMix[i] = next;
|
||||||
@ -784,26 +784,26 @@ export class AnimationState {
|
|||||||
* References to a track entry must not be kept after the {@link AnimationStateListener#dispose()} event occurs. */
|
* References to a track entry must not be kept after the {@link AnimationStateListener#dispose()} event occurs. */
|
||||||
export class TrackEntry {
|
export class TrackEntry {
|
||||||
/** The animation to apply for this track entry. */
|
/** The animation to apply for this track entry. */
|
||||||
animation: Animation = null;
|
animation: Animation | null = null;
|
||||||
|
|
||||||
previous: TrackEntry = null;
|
previous: TrackEntry | null = null;
|
||||||
|
|
||||||
/** The animation queued to start after this animation, or null. `next` makes up a linked list. */
|
/** The animation queued to start after this animation, or null. `next` makes up a linked list. */
|
||||||
next: TrackEntry = null;
|
next: TrackEntry | null = null;
|
||||||
|
|
||||||
/** The track entry for the previous animation when mixing from the previous animation to this animation, or null if no
|
/** The track entry for the previous animation when mixing from the previous animation to this animation, or null if no
|
||||||
* mixing is currently occuring. When mixing from multiple animations, `mixingFrom` makes up a linked list. */
|
* mixing is currently occuring. When mixing from multiple animations, `mixingFrom` makes up a linked list. */
|
||||||
mixingFrom: TrackEntry = null;
|
mixingFrom: TrackEntry | null = null;
|
||||||
|
|
||||||
/** The track entry for the next animation when mixing from this animation to the next animation, or null if no mixing is
|
/** The track entry for the next animation when mixing from this animation to the next animation, or null if no mixing is
|
||||||
* currently occuring. When mixing to multiple animations, `mixingTo` makes up a linked list. */
|
* currently occuring. When mixing to multiple animations, `mixingTo` makes up a linked list. */
|
||||||
mixingTo: TrackEntry = null;
|
mixingTo: TrackEntry | null = null;
|
||||||
|
|
||||||
/** The listener for events generated by this track entry, or null.
|
/** The listener for events generated by this track entry, or null.
|
||||||
*
|
*
|
||||||
* A track entry returned from {@link AnimationState#setAnimation()} is already the current animation
|
* A track entry returned from {@link AnimationState#setAnimation()} is already the current animation
|
||||||
* for the track, so the track entry listener {@link AnimationStateListener#start()} will not be called. */
|
* for the track, so the track entry listener {@link AnimationStateListener#start()} will not be called. */
|
||||||
listener: AnimationStateListener = null;
|
listener: AnimationStateListener | null = null;
|
||||||
|
|
||||||
/** The index of the track where this track entry is either current or queued.
|
/** The index of the track where this track entry is either current or queued.
|
||||||
*
|
*
|
||||||
@ -999,7 +999,7 @@ export class TrackEntry {
|
|||||||
export class EventQueue {
|
export class EventQueue {
|
||||||
objects: Array<any> = [];
|
objects: Array<any> = [];
|
||||||
drainDisabled = false;
|
drainDisabled = false;
|
||||||
animState: AnimationState = null;
|
animState: AnimationState;
|
||||||
|
|
||||||
constructor (animState: AnimationState) {
|
constructor (animState: AnimationState) {
|
||||||
this.animState = animState;
|
this.animState = animState;
|
||||||
@ -1051,35 +1051,47 @@ export class EventQueue {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case EventType.start:
|
case EventType.start:
|
||||||
if (entry.listener && entry.listener.start) entry.listener.start(entry);
|
if (entry.listener && entry.listener.start) entry.listener.start(entry);
|
||||||
for (let ii = 0; ii < listeners.length; ii++)
|
for (let ii = 0; ii < listeners.length; ii++) {
|
||||||
if (listeners[ii].start) listeners[ii].start(entry);
|
let listener = listeners[ii];
|
||||||
|
if (listener.start) listener.start(entry);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case EventType.interrupt:
|
case EventType.interrupt:
|
||||||
if (entry.listener && entry.listener.interrupt) entry.listener.interrupt(entry);
|
if (entry.listener && entry.listener.interrupt) entry.listener.interrupt(entry);
|
||||||
for (let ii = 0; ii < listeners.length; ii++)
|
for (let ii = 0; ii < listeners.length; ii++) {
|
||||||
if (listeners[ii].interrupt) listeners[ii].interrupt(entry);
|
let listener = listeners[ii];
|
||||||
|
if (listener.interrupt) listener.interrupt(entry);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case EventType.end:
|
case EventType.end:
|
||||||
if (entry.listener && entry.listener.end) entry.listener.end(entry);
|
if (entry.listener && entry.listener.end) entry.listener.end(entry);
|
||||||
for (let ii = 0; ii < listeners.length; ii++)
|
for (let ii = 0; ii < listeners.length; ii++) {
|
||||||
if (listeners[ii].end) listeners[ii].end(entry);
|
let listener = listeners[ii];
|
||||||
|
if (listener.end) listener.end(entry);
|
||||||
|
}
|
||||||
// Fall through.
|
// Fall through.
|
||||||
case EventType.dispose:
|
case EventType.dispose:
|
||||||
if (entry.listener && entry.listener.dispose) entry.listener.dispose(entry);
|
if (entry.listener && entry.listener.dispose) entry.listener.dispose(entry);
|
||||||
for (let ii = 0; ii < listeners.length; ii++)
|
for (let ii = 0; ii < listeners.length; ii++) {
|
||||||
if (listeners[ii].dispose) listeners[ii].dispose(entry);
|
let listener = listeners[ii];
|
||||||
|
if (listener.dispose) listener.dispose(entry);
|
||||||
|
}
|
||||||
this.animState.trackEntryPool.free(entry);
|
this.animState.trackEntryPool.free(entry);
|
||||||
break;
|
break;
|
||||||
case EventType.complete:
|
case EventType.complete:
|
||||||
if (entry.listener && entry.listener.complete) entry.listener.complete(entry);
|
if (entry.listener && entry.listener.complete) entry.listener.complete(entry);
|
||||||
for (let ii = 0; ii < listeners.length; ii++)
|
for (let ii = 0; ii < listeners.length; ii++) {
|
||||||
if (listeners[ii].complete) listeners[ii].complete(entry);
|
let listener = listeners[ii];
|
||||||
|
if (listener.complete) listener.complete(entry);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case EventType.event:
|
case EventType.event:
|
||||||
let event = objects[i++ + 2] as Event;
|
let event = objects[i++ + 2] as Event;
|
||||||
if (entry.listener && entry.listener.event) entry.listener.event(entry, event);
|
if (entry.listener && entry.listener.event) entry.listener.event(entry, event);
|
||||||
for (let ii = 0; ii < listeners.length; ii++)
|
for (let ii = 0; ii < listeners.length; ii++) {
|
||||||
if (listeners[ii].event) listeners[ii].event(entry, event);
|
let listener = listeners[ii];
|
||||||
|
if (listener.event) listener.event(entry, event);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1104,24 +1116,24 @@ export enum EventType {
|
|||||||
* {@link AnimationState#addListener()}. */
|
* {@link AnimationState#addListener()}. */
|
||||||
export interface AnimationStateListener {
|
export interface AnimationStateListener {
|
||||||
/** Invoked when this entry has been set as the current entry. */
|
/** Invoked when this entry has been set as the current entry. */
|
||||||
start?(entry: TrackEntry): void;
|
start?: (entry: TrackEntry) => void;
|
||||||
|
|
||||||
/** Invoked when another entry has replaced this entry as the current entry. This entry may continue being applied for
|
/** Invoked when another entry has replaced this entry as the current entry. This entry may continue being applied for
|
||||||
* mixing. */
|
* mixing. */
|
||||||
interrupt?(entry: TrackEntry): void;
|
interrupt?: (entry: TrackEntry) => void;
|
||||||
|
|
||||||
/** Invoked when this entry is no longer the current entry and will never be applied again. */
|
/** Invoked when this entry is no longer the current entry and will never be applied again. */
|
||||||
end?(entry: TrackEntry): void;
|
end?: (entry: TrackEntry) => void;
|
||||||
|
|
||||||
/** Invoked when this entry will be disposed. This may occur without the entry ever being set as the current entry.
|
/** Invoked when this entry will be disposed. This may occur without the entry ever being set as the current entry.
|
||||||
* References to the entry should not be kept after dispose is called, as it may be destroyed or reused. */
|
* References to the entry should not be kept after dispose is called, as it may be destroyed or reused. */
|
||||||
dispose?(entry: TrackEntry): void;
|
dispose?: (entry: TrackEntry) => void;
|
||||||
|
|
||||||
/** Invoked every time this entry's animation completes a loop. */
|
/** Invoked every time this entry's animation completes a loop. */
|
||||||
complete?(entry: TrackEntry): void;
|
complete?: (entry: TrackEntry) => void;
|
||||||
|
|
||||||
/** Invoked when this entry's animation triggers an event. */
|
/** Invoked when this entry's animation triggers an event. */
|
||||||
event?(entry: TrackEntry, event: Event): void;
|
event?: (entry: TrackEntry, event: Event) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AnimationStateAdapter implements AnimationStateListener {
|
export abstract class AnimationStateAdapter implements AnimationStateListener {
|
||||||
@ -1181,5 +1193,3 @@ export const HOLD_MIX = 4;
|
|||||||
|
|
||||||
export const SETUP = 1;
|
export const SETUP = 1;
|
||||||
export const CURRENT = 2;
|
export const CURRENT = 2;
|
||||||
|
|
||||||
let _emptyAnimation: Animation = null;
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ import { StringMap } from "./Utils";
|
|||||||
/** Stores mix (crossfade) durations to be applied when {@link AnimationState} animations are changed. */
|
/** Stores mix (crossfade) durations to be applied when {@link AnimationState} animations are changed. */
|
||||||
export class AnimationStateData {
|
export class AnimationStateData {
|
||||||
/** The SkeletonData to look up animations when they are specified by name. */
|
/** The SkeletonData to look up animations when they are specified by name. */
|
||||||
skeletonData: SkeletonData = null;
|
skeletonData: SkeletonData;
|
||||||
|
|
||||||
animationToMixTime: StringMap<number> = {};
|
animationToMixTime: StringMap<number> = {};
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import { TextureAtlas } from "./TextureAtlas";
|
|||||||
import { Disposable, StringMap } from "./Utils";
|
import { Disposable, StringMap } from "./Utils";
|
||||||
|
|
||||||
export class AssetManagerBase implements Disposable {
|
export class AssetManagerBase implements Disposable {
|
||||||
private pathPrefix: string = null;
|
private pathPrefix: string = "";
|
||||||
private textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture;
|
private textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture;
|
||||||
private downloader: Downloader;
|
private downloader: Downloader;
|
||||||
private assets: StringMap<any> = {};
|
private assets: StringMap<any> = {};
|
||||||
@ -40,10 +40,10 @@ export class AssetManagerBase implements Disposable {
|
|||||||
private toLoad = 0;
|
private toLoad = 0;
|
||||||
private loaded = 0;
|
private loaded = 0;
|
||||||
|
|
||||||
constructor (textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture, pathPrefix: string = "", downloader: Downloader = null) {
|
constructor (textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture, pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
||||||
this.textureLoader = textureLoader;
|
this.textureLoader = textureLoader;
|
||||||
this.pathPrefix = pathPrefix;
|
this.pathPrefix = pathPrefix;
|
||||||
this.downloader = downloader || new Downloader();
|
this.downloader = downloader;
|
||||||
}
|
}
|
||||||
|
|
||||||
private start (path: string): string {
|
private start (path: string): string {
|
||||||
@ -85,8 +85,8 @@ export class AssetManagerBase implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadBinary (path: string,
|
loadBinary (path: string,
|
||||||
success: (path: string, binary: Uint8Array) => void = null,
|
success: (path: string, binary: Uint8Array) => void = () => { },
|
||||||
error: (path: string, message: string) => void = null) {
|
error: (path: string, message: string) => void = () => { }) {
|
||||||
path = this.start(path);
|
path = this.start(path);
|
||||||
|
|
||||||
this.downloader.downloadBinary(path, (data: Uint8Array): void => {
|
this.downloader.downloadBinary(path, (data: Uint8Array): void => {
|
||||||
@ -97,8 +97,8 @@ export class AssetManagerBase implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadText (path: string,
|
loadText (path: string,
|
||||||
success: (path: string, text: string) => void = null,
|
success: (path: string, text: string) => void = () => { },
|
||||||
error: (path: string, message: string) => void = null) {
|
error: (path: string, message: string) => void = () => { }) {
|
||||||
path = this.start(path);
|
path = this.start(path);
|
||||||
|
|
||||||
this.downloader.downloadText(path, (data: string): void => {
|
this.downloader.downloadText(path, (data: string): void => {
|
||||||
@ -109,8 +109,8 @@ export class AssetManagerBase implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadJson (path: string,
|
loadJson (path: string,
|
||||||
success: (path: string, object: object) => void = null,
|
success: (path: string, object: object) => void = () => { },
|
||||||
error: (path: string, message: string) => void = null) {
|
error: (path: string, message: string) => void = () => { }) {
|
||||||
path = this.start(path);
|
path = this.start(path);
|
||||||
|
|
||||||
this.downloader.downloadJson(path, (data: object): void => {
|
this.downloader.downloadJson(path, (data: object): void => {
|
||||||
@ -121,8 +121,8 @@ export class AssetManagerBase implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadTexture (path: string,
|
loadTexture (path: string,
|
||||||
success: (path: string, texture: Texture) => void = null,
|
success: (path: string, texture: Texture) => void = () => { },
|
||||||
error: (path: string, message: string) => void = null) {
|
error: (path: string, message: string) => void = () => { }) {
|
||||||
path = this.start(path);
|
path = this.start(path);
|
||||||
|
|
||||||
let isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
|
let isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
|
||||||
@ -152,9 +152,9 @@ export class AssetManagerBase implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadTextureAtlas (path: string,
|
loadTextureAtlas (path: string,
|
||||||
success: (path: string, atlas: TextureAtlas) => void = null,
|
success: (path: string, atlas: TextureAtlas) => void = () => { },
|
||||||
error: (path: string, message: string) => void = null,
|
error: (path: string, message: string) => void = () => { },
|
||||||
fileAlias: { [keyword: string]: string } = null
|
fileAlias?: { [keyword: string]: string }
|
||||||
) {
|
) {
|
||||||
let index = path.lastIndexOf("/");
|
let index = path.lastIndexOf("/");
|
||||||
let parent = index >= 0 ? path.substring(0, index + 1) : "";
|
let parent = index >= 0 ? path.substring(0, index + 1) : "";
|
||||||
@ -165,7 +165,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
let atlas = new TextureAtlas(atlasText);
|
let atlas = new TextureAtlas(atlasText);
|
||||||
let toLoad = atlas.pages.length, abort = false;
|
let toLoad = atlas.pages.length, abort = false;
|
||||||
for (let page of atlas.pages) {
|
for (let page of atlas.pages) {
|
||||||
this.loadTexture(fileAlias == null ? parent + page.name : fileAlias[page.name],
|
this.loadTexture(!fileAlias ? parent + page.name : fileAlias[page.name!],
|
||||||
(imagePath: string, texture: Texture) => {
|
(imagePath: string, texture: Texture) => {
|
||||||
if (!abort) {
|
if (!abort) {
|
||||||
page.setTexture(texture);
|
page.setTexture(texture);
|
||||||
@ -179,7 +179,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.error(error, path, `Couldn't parse texture atlas ${path}: ${e.message}`);
|
this.error(error, path, `Couldn't parse texture atlas ${path}: ${(e as any).message}`);
|
||||||
}
|
}
|
||||||
}, (status: number, responseText: string): void => {
|
}, (status: number, responseText: string): void => {
|
||||||
this.error(error, path, `Couldn't load texture atlas ${path}: status ${status}, ${responseText}`);
|
this.error(error, path, `Couldn't load texture atlas ${path}: status ${status}, ${responseText}`);
|
||||||
|
|||||||
@ -43,7 +43,7 @@ import { Sequence } from "./attachments/Sequence"
|
|||||||
* See [Loading skeleton data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the
|
* See [Loading skeleton data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the
|
||||||
* Spine Runtimes Guide. */
|
* Spine Runtimes Guide. */
|
||||||
export class AtlasAttachmentLoader implements AttachmentLoader {
|
export class AtlasAttachmentLoader implements AttachmentLoader {
|
||||||
atlas: TextureAtlas = null;
|
atlas: TextureAtlas;
|
||||||
|
|
||||||
constructor (atlas: TextureAtlas) {
|
constructor (atlas: TextureAtlas) {
|
||||||
this.atlas = atlas;
|
this.atlas = atlas;
|
||||||
@ -53,14 +53,15 @@ export class AtlasAttachmentLoader implements AttachmentLoader {
|
|||||||
let regions = sequence.regions;
|
let regions = sequence.regions;
|
||||||
for (let i = 0, n = regions.length; i < n; i++) {
|
for (let i = 0, n = regions.length; i < n; i++) {
|
||||||
let path = sequence.getPath(basePath, i);
|
let path = sequence.getPath(basePath, i);
|
||||||
regions[i] = this.atlas.findRegion(path);
|
let region = this.atlas.findRegion(path);
|
||||||
|
if (region == null) throw new Error("Region not found in atlas: " + path + " (sequence: " + name + ")");
|
||||||
|
regions[i] = region;
|
||||||
regions[i].renderObject = regions[i];
|
regions[i].renderObject = regions[i];
|
||||||
if (regions[i] == null) throw new Error("Region not found in atlas: " + path + " (sequence: " + name + ")");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newRegionAttachment (skin: Skin, name: string, path: string, sequence: Sequence): RegionAttachment {
|
newRegionAttachment (skin: Skin, name: string, path: string, sequence: Sequence): RegionAttachment {
|
||||||
let attachment = new RegionAttachment(name);
|
let attachment = new RegionAttachment(name, path);
|
||||||
if (sequence != null) {
|
if (sequence != null) {
|
||||||
this.loadSequence(name, path, sequence);
|
this.loadSequence(name, path, sequence);
|
||||||
} else {
|
} else {
|
||||||
@ -73,7 +74,7 @@ export class AtlasAttachmentLoader implements AttachmentLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newMeshAttachment (skin: Skin, name: string, path: string, sequence: Sequence): MeshAttachment {
|
newMeshAttachment (skin: Skin, name: string, path: string, sequence: Sequence): MeshAttachment {
|
||||||
let attachment = new MeshAttachment(name);
|
let attachment = new MeshAttachment(name, path);
|
||||||
if (sequence != null) {
|
if (sequence != null) {
|
||||||
this.loadSequence(name, path, sequence);
|
this.loadSequence(name, path, sequence);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -39,13 +39,13 @@ import { MathUtils, Vector2 } from "./Utils";
|
|||||||
* constraint or application code modifies the world transform after it was computed from the local transform. */
|
* constraint or application code modifies the world transform after it was computed from the local transform. */
|
||||||
export class Bone implements Updatable {
|
export class Bone implements Updatable {
|
||||||
/** The bone's setup pose data. */
|
/** The bone's setup pose data. */
|
||||||
data: BoneData = null;
|
data: BoneData;
|
||||||
|
|
||||||
/** The skeleton this bone belongs to. */
|
/** The skeleton this bone belongs to. */
|
||||||
skeleton: Skeleton = null;
|
skeleton: Skeleton;
|
||||||
|
|
||||||
/** The parent bone, or null if this is the root bone. */
|
/** The parent bone, or null if this is the root bone. */
|
||||||
parent: Bone = null;
|
parent: Bone | null = null;
|
||||||
|
|
||||||
/** The immediate children of this bone. */
|
/** The immediate children of this bone. */
|
||||||
children = new Array<Bone>();
|
children = new Array<Bone>();
|
||||||
@ -114,7 +114,7 @@ export class Bone implements Updatable {
|
|||||||
active = false;
|
active = false;
|
||||||
|
|
||||||
/** @param parent May be null. */
|
/** @param parent May be null. */
|
||||||
constructor (data: BoneData, skeleton: Skeleton, parent: Bone) {
|
constructor (data: BoneData, skeleton: Skeleton, parent: Bone | null) {
|
||||||
if (!data) throw new Error("data cannot be null.");
|
if (!data) throw new Error("data cannot be null.");
|
||||||
if (!skeleton) throw new Error("skeleton cannot be null.");
|
if (!skeleton) throw new Error("skeleton cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|||||||
@ -35,10 +35,10 @@ export class BoneData {
|
|||||||
index: number = 0;
|
index: number = 0;
|
||||||
|
|
||||||
/** The name of the bone, which is unique across all bones in the skeleton. */
|
/** The name of the bone, which is unique across all bones in the skeleton. */
|
||||||
name: string = null;
|
name: string;
|
||||||
|
|
||||||
/** @returns May be null. */
|
/** @returns May be null. */
|
||||||
parent: BoneData = null;
|
parent: BoneData | null = null;
|
||||||
|
|
||||||
/** The bone's length. */
|
/** The bone's length. */
|
||||||
length: number = 0;
|
length: number = 0;
|
||||||
@ -76,7 +76,7 @@ export class BoneData {
|
|||||||
* rendered at runtime. */
|
* rendered at runtime. */
|
||||||
color = new Color();
|
color = new Color();
|
||||||
|
|
||||||
constructor (index: number, name: string, parent: BoneData) {
|
constructor (index: number, name: string, parent: BoneData | null) {
|
||||||
if (index < 0) throw new Error("index must be >= 0.");
|
if (index < 0) throw new Error("index must be >= 0.");
|
||||||
if (!name) throw new Error("name cannot be null.");
|
if (!name) throw new Error("name cannot be null.");
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
|||||||
@ -35,10 +35,10 @@ import { EventData } from "./EventData";
|
|||||||
* AnimationStateListener {@link AnimationStateListener#event()}, and
|
* AnimationStateListener {@link AnimationStateListener#event()}, and
|
||||||
* [Events](http://esotericsoftware.com/spine-events) in the Spine User Guide. */
|
* [Events](http://esotericsoftware.com/spine-events) in the Spine User Guide. */
|
||||||
export class Event {
|
export class Event {
|
||||||
data: EventData = null;
|
data: EventData;
|
||||||
intValue: number = 0;
|
intValue: number = 0;
|
||||||
floatValue: number = 0;
|
floatValue: number = 0;
|
||||||
stringValue: string = null;
|
stringValue: string | null = null;
|
||||||
time: number = 0;
|
time: number = 0;
|
||||||
volume: number = 0;
|
volume: number = 0;
|
||||||
balance: number = 0;
|
balance: number = 0;
|
||||||
|
|||||||
@ -31,11 +31,11 @@
|
|||||||
*
|
*
|
||||||
* See [Events](http://esotericsoftware.com/spine-events) in the Spine User Guide. */
|
* See [Events](http://esotericsoftware.com/spine-events) in the Spine User Guide. */
|
||||||
export class EventData {
|
export class EventData {
|
||||||
name: string = null;
|
name: string;
|
||||||
intValue: number = 0;
|
intValue: number = 0;
|
||||||
floatValue: number = 0;
|
floatValue: number = 0;
|
||||||
stringValue: string = null;
|
stringValue: string | null = null;
|
||||||
audioPath: string = null;
|
audioPath: string | null = null;
|
||||||
volume: number = 0;
|
volume: number = 0;
|
||||||
balance: number = 0;
|
balance: number = 0;
|
||||||
|
|
||||||
|
|||||||
@ -40,13 +40,13 @@ import { MathUtils } from "./Utils";
|
|||||||
* See [IK constraints](http://esotericsoftware.com/spine-ik-constraints) in the Spine User Guide. */
|
* See [IK constraints](http://esotericsoftware.com/spine-ik-constraints) in the Spine User Guide. */
|
||||||
export class IkConstraint implements Updatable {
|
export class IkConstraint implements Updatable {
|
||||||
/** The IK constraint's setup pose data. */
|
/** The IK constraint's setup pose data. */
|
||||||
data: IkConstraintData = null;
|
data: IkConstraintData;
|
||||||
|
|
||||||
/** The bones that will be modified by this IK constraint. */
|
/** The bones that will be modified by this IK constraint. */
|
||||||
bones: Array<Bone> = null;
|
bones: Array<Bone>;
|
||||||
|
|
||||||
/** The bone that is the IK target. */
|
/** The bone that is the IK target. */
|
||||||
target: Bone = null;
|
target: Bone;
|
||||||
|
|
||||||
/** Controls the bend direction of the IK bones, either 1 or -1. */
|
/** Controls the bend direction of the IK bones, either 1 or -1. */
|
||||||
bendDirection = 0;
|
bendDirection = 0;
|
||||||
@ -76,9 +76,14 @@ export class IkConstraint implements Updatable {
|
|||||||
this.stretch = data.stretch;
|
this.stretch = data.stretch;
|
||||||
|
|
||||||
this.bones = new Array<Bone>();
|
this.bones = new Array<Bone>();
|
||||||
for (let i = 0; i < data.bones.length; i++)
|
for (let i = 0; i < data.bones.length; i++) {
|
||||||
this.bones.push(skeleton.findBone(data.bones[i].name));
|
let bone = skeleton.findBone(data.bones[i].name);
|
||||||
this.target = skeleton.findBone(data.target.name);
|
if (!bone) throw new Error(`Couldn't find bone ${data.bones[i].name}`);
|
||||||
|
this.bones.push(bone);
|
||||||
|
}
|
||||||
|
let target = skeleton.findBone(data.target.name);
|
||||||
|
if (!target) throw new Error(`Couldn't find bone ${data.target.name}`);
|
||||||
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
isActive () {
|
isActive () {
|
||||||
@ -102,6 +107,7 @@ export class IkConstraint implements Updatable {
|
|||||||
/** Applies 1 bone IK. The target is specified in the world coordinate system. */
|
/** Applies 1 bone IK. The target is specified in the world coordinate system. */
|
||||||
apply1 (bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) {
|
apply1 (bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) {
|
||||||
let p = bone.parent;
|
let p = bone.parent;
|
||||||
|
if (!p) throw new Error("IK bone must have parent.");
|
||||||
let pa = p.a, pb = p.b, pc = p.c, pd = p.d;
|
let pa = p.a, pb = p.b, pc = p.c, pd = p.d;
|
||||||
let rotationIK = -bone.ashearX - bone.arotation, tx = 0, ty = 0;
|
let rotationIK = -bone.ashearX - bone.arotation, tx = 0, ty = 0;
|
||||||
|
|
||||||
@ -183,6 +189,7 @@ export class IkConstraint implements Updatable {
|
|||||||
cwy = c * cx + d * cy + parent.worldY;
|
cwy = c * cx + d * cy + parent.worldY;
|
||||||
}
|
}
|
||||||
let pp = parent.parent;
|
let pp = parent.parent;
|
||||||
|
if (!pp) throw new Error("IK parent must itself have a parent.");
|
||||||
a = pp.a;
|
a = pp.a;
|
||||||
b = pp.b;
|
b = pp.b;
|
||||||
c = pp.c;
|
c = pp.c;
|
||||||
|
|||||||
@ -39,7 +39,12 @@ export class IkConstraintData extends ConstraintData {
|
|||||||
bones = new Array<BoneData>();
|
bones = new Array<BoneData>();
|
||||||
|
|
||||||
/** The bone that is the IK target. */
|
/** The bone that is the IK target. */
|
||||||
target: BoneData = null;
|
private _target: BoneData | null = null;
|
||||||
|
public set target (boneData: BoneData) { this._target = boneData; }
|
||||||
|
public get target () {
|
||||||
|
if (!this._target) throw new Error("BoneData not set.")
|
||||||
|
else return this._target;
|
||||||
|
}
|
||||||
|
|
||||||
/** Controls the bend direction of the IK bones, either 1 or -1. */
|
/** Controls the bend direction of the IK bones, either 1 or -1. */
|
||||||
bendDirection = 1;
|
bendDirection = 1;
|
||||||
|
|||||||
@ -45,13 +45,13 @@ export class PathConstraint implements Updatable {
|
|||||||
static epsilon = 0.00001;
|
static epsilon = 0.00001;
|
||||||
|
|
||||||
/** The path constraint's setup pose data. */
|
/** The path constraint's setup pose data. */
|
||||||
data: PathConstraintData = null;
|
data: PathConstraintData;
|
||||||
|
|
||||||
/** The bones that will be modified by this path constraint. */
|
/** The bones that will be modified by this path constraint. */
|
||||||
bones: Array<Bone> = null;
|
bones: Array<Bone>;
|
||||||
|
|
||||||
/** The slot whose path attachment will be used to constrained the bones. */
|
/** The slot whose path attachment will be used to constrained the bones. */
|
||||||
target: Slot = null;
|
target: Slot;
|
||||||
|
|
||||||
/** The position along the path. */
|
/** The position along the path. */
|
||||||
position = 0;
|
position = 0;
|
||||||
@ -76,9 +76,14 @@ export class PathConstraint implements Updatable {
|
|||||||
if (!skeleton) throw new Error("skeleton cannot be null.");
|
if (!skeleton) throw new Error("skeleton cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.bones = new Array<Bone>();
|
this.bones = new Array<Bone>();
|
||||||
for (let i = 0, n = data.bones.length; i < n; i++)
|
for (let i = 0, n = data.bones.length; i < n; i++) {
|
||||||
this.bones.push(skeleton.findBone(data.bones[i].name));
|
let bone = skeleton.findBone(data.bones[i].name);
|
||||||
this.target = skeleton.findSlot(data.target.name);
|
if (!bone) throw new Error(`Couldn't find bone ${data.bones[i].name}.`);
|
||||||
|
this.bones.push(bone);
|
||||||
|
}
|
||||||
|
let target = skeleton.findSlot(data.target.name);
|
||||||
|
if (!target) throw new Error(`Couldn't find target bone ${data.target.name}`);
|
||||||
|
this.target = target;
|
||||||
this.position = data.position;
|
this.position = data.position;
|
||||||
this.spacing = data.spacing;
|
this.spacing = data.spacing;
|
||||||
this.mixRotate = data.mixRotate;
|
this.mixRotate = data.mixRotate;
|
||||||
@ -102,7 +107,7 @@ export class PathConstraint implements Updatable {
|
|||||||
|
|
||||||
let bones = this.bones;
|
let bones = this.bones;
|
||||||
let boneCount = bones.length, spacesCount = tangents ? boneCount : boneCount + 1;
|
let boneCount = bones.length, spacesCount = tangents ? boneCount : boneCount + 1;
|
||||||
let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array<number> = scale ? this.lengths = Utils.setArraySize(this.lengths, boneCount) : null;
|
let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array<number> = scale ? this.lengths = Utils.setArraySize(this.lengths, boneCount) : [];
|
||||||
let spacing = this.spacing;
|
let spacing = this.spacing;
|
||||||
|
|
||||||
switch (data.spacingMode) {
|
switch (data.spacingMode) {
|
||||||
@ -222,7 +227,7 @@ export class PathConstraint implements Updatable {
|
|||||||
computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean) {
|
computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean) {
|
||||||
let target = this.target;
|
let target = this.target;
|
||||||
let position = this.position;
|
let position = this.position;
|
||||||
let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array<number> = null;
|
let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array<number> = this.world;
|
||||||
let closed = path.closed;
|
let closed = path.closed;
|
||||||
let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE;
|
let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE;
|
||||||
|
|
||||||
|
|||||||
@ -41,16 +41,21 @@ export class PathConstraintData extends ConstraintData {
|
|||||||
bones = new Array<BoneData>();
|
bones = new Array<BoneData>();
|
||||||
|
|
||||||
/** The slot whose path attachment will be used to constrained the bones. */
|
/** The slot whose path attachment will be used to constrained the bones. */
|
||||||
target: SlotData = null;
|
private _target: SlotData | null = null;
|
||||||
|
public set target (slotData: SlotData) { this._target = slotData; }
|
||||||
|
public get target () {
|
||||||
|
if (!this._target) throw new Error("SlotData not set.")
|
||||||
|
else return this._target;
|
||||||
|
}
|
||||||
|
|
||||||
/** The mode for positioning the first bone on the path. */
|
/** The mode for positioning the first bone on the path. */
|
||||||
positionMode: PositionMode = null;
|
positionMode: PositionMode = PositionMode.Fixed;
|
||||||
|
|
||||||
/** The mode for positioning the bones after the first bone on the path. */
|
/** The mode for positioning the bones after the first bone on the path. */
|
||||||
spacingMode: SpacingMode = null;
|
spacingMode: SpacingMode = SpacingMode.Fixed;
|
||||||
|
|
||||||
/** The mode for adjusting the rotation of the bones. */
|
/** The mode for adjusting the rotation of the bones. */
|
||||||
rotateMode: RotateMode = null;
|
rotateMode: RotateMode = RotateMode.Chain;
|
||||||
|
|
||||||
/** An offset added to the constrained bone rotation. */
|
/** An offset added to the constrained bone rotation. */
|
||||||
offsetRotation: number = 0;
|
offsetRotation: number = 0;
|
||||||
|
|||||||
@ -46,34 +46,34 @@ import { Color, Utils, MathUtils, Vector2, NumberArrayLike } from "./Utils";
|
|||||||
* See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */
|
* See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */
|
||||||
export class Skeleton {
|
export class Skeleton {
|
||||||
/** The skeleton's setup pose data. */
|
/** The skeleton's setup pose data. */
|
||||||
data: SkeletonData = null;
|
data: SkeletonData;
|
||||||
|
|
||||||
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
|
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
|
||||||
bones: Array<Bone> = null;
|
bones: Array<Bone>;
|
||||||
|
|
||||||
/** The skeleton's slots. */
|
/** The skeleton's slots. */
|
||||||
slots: Array<Slot> = null;
|
slots: Array<Slot>;
|
||||||
|
|
||||||
/** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */
|
/** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */
|
||||||
drawOrder: Array<Slot> = null;
|
drawOrder: Array<Slot>;
|
||||||
|
|
||||||
/** The skeleton's IK constraints. */
|
/** The skeleton's IK constraints. */
|
||||||
ikConstraints: Array<IkConstraint> = null;
|
ikConstraints: Array<IkConstraint>;
|
||||||
|
|
||||||
/** The skeleton's transform constraints. */
|
/** The skeleton's transform constraints. */
|
||||||
transformConstraints: Array<TransformConstraint> = null;
|
transformConstraints: Array<TransformConstraint>;
|
||||||
|
|
||||||
/** The skeleton's path constraints. */
|
/** The skeleton's path constraints. */
|
||||||
pathConstraints: Array<PathConstraint> = null;
|
pathConstraints: Array<PathConstraint>;
|
||||||
|
|
||||||
/** The list of bones and constraints, sorted in the order they should be updated, as computed by {@link #updateCache()}. */
|
/** The list of bones and constraints, sorted in the order they should be updated, as computed by {@link #updateCache()}. */
|
||||||
_updateCache = new Array<Updatable>();
|
_updateCache = new Array<Updatable>();
|
||||||
|
|
||||||
/** The skeleton's current skin. May be null. */
|
/** The skeleton's current skin. May be null. */
|
||||||
skin: Skin = null;
|
skin: Skin | null = null;
|
||||||
|
|
||||||
/** The color to tint all the skeleton's attachments. */
|
/** The color to tint all the skeleton's attachments. */
|
||||||
color: Color = null;
|
color: Color;
|
||||||
|
|
||||||
/** Scales the entire skeleton on the X axis. This affects all bones, even if the bone's transform mode disallows scale
|
/** Scales the entire skeleton on the X axis. This affects all bones, even if the bone's transform mode disallows scale
|
||||||
* inheritance. */
|
* inheritance. */
|
||||||
@ -155,7 +155,7 @@ export class Skeleton {
|
|||||||
if (this.skin) {
|
if (this.skin) {
|
||||||
let skinBones = this.skin.bones;
|
let skinBones = this.skin.bones;
|
||||||
for (let i = 0, n = this.skin.bones.length; i < n; i++) {
|
for (let i = 0, n = this.skin.bones.length; i < n; i++) {
|
||||||
let bone = this.bones[skinBones[i].index];
|
let bone: Bone | null = this.bones[skinBones[i].index];
|
||||||
do {
|
do {
|
||||||
bone.sorted = false;
|
bone.sorted = false;
|
||||||
bone.active = true;
|
bone.active = true;
|
||||||
@ -201,7 +201,7 @@ export class Skeleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sortIkConstraint (constraint: IkConstraint) {
|
sortIkConstraint (constraint: IkConstraint) {
|
||||||
constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
|
constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!;
|
||||||
if (!constraint.active) return;
|
if (!constraint.active) return;
|
||||||
|
|
||||||
let target = constraint.target;
|
let target = constraint.target;
|
||||||
@ -226,7 +226,7 @@ export class Skeleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sortPathConstraint (constraint: PathConstraint) {
|
sortPathConstraint (constraint: PathConstraint) {
|
||||||
constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
|
constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!;
|
||||||
if (!constraint.active) return;
|
if (!constraint.active) return;
|
||||||
|
|
||||||
let slot = constraint.target;
|
let slot = constraint.target;
|
||||||
@ -255,7 +255,7 @@ export class Skeleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sortTransformConstraint (constraint: TransformConstraint) {
|
sortTransformConstraint (constraint: TransformConstraint) {
|
||||||
constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
|
constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!;
|
||||||
if (!constraint.active) return;
|
if (!constraint.active) return;
|
||||||
|
|
||||||
this.sortBone(constraint.target);
|
this.sortBone(constraint.target);
|
||||||
@ -265,7 +265,7 @@ export class Skeleton {
|
|||||||
if (constraint.data.local) {
|
if (constraint.data.local) {
|
||||||
for (let i = 0; i < boneCount; i++) {
|
for (let i = 0; i < boneCount; i++) {
|
||||||
let child = constrained[i];
|
let child = constrained[i];
|
||||||
this.sortBone(child.parent);
|
this.sortBone(child.parent!);
|
||||||
this.sortBone(child);
|
this.sortBone(child);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -307,6 +307,7 @@ export class Skeleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sortBone (bone: Bone) {
|
sortBone (bone: Bone) {
|
||||||
|
if (!bone) return;
|
||||||
if (bone.sorted) return;
|
if (bone.sorted) return;
|
||||||
let parent = bone.parent;
|
let parent = bone.parent;
|
||||||
if (parent) this.sortBone(parent);
|
if (parent) this.sortBone(parent);
|
||||||
@ -348,6 +349,7 @@ export class Skeleton {
|
|||||||
updateWorldTransformWith (parent: Bone) {
|
updateWorldTransformWith (parent: Bone) {
|
||||||
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
|
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
|
||||||
let rootBone = this.getRootBone();
|
let rootBone = this.getRootBone();
|
||||||
|
if (!rootBone) throw new Error("Root bone must not be null.");
|
||||||
let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||||
rootBone.worldX = pa * this.x + pb * this.y + parent.worldX;
|
rootBone.worldX = pa * this.x + pb * this.y + parent.worldX;
|
||||||
rootBone.worldY = pc * this.x + pd * this.y + parent.worldY;
|
rootBone.worldY = pc * this.x + pd * this.y + parent.worldY;
|
||||||
@ -484,7 +486,7 @@ export class Skeleton {
|
|||||||
let slot = slots[i];
|
let slot = slots[i];
|
||||||
let name = slot.data.attachmentName;
|
let name = slot.data.attachmentName;
|
||||||
if (name) {
|
if (name) {
|
||||||
let attachment: Attachment = newSkin.getAttachment(i, name);
|
let attachment = newSkin.getAttachment(i, name);
|
||||||
if (attachment) slot.setAttachment(attachment);
|
if (attachment) slot.setAttachment(attachment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -500,8 +502,10 @@ export class Skeleton {
|
|||||||
*
|
*
|
||||||
* See {@link #getAttachment()}.
|
* See {@link #getAttachment()}.
|
||||||
* @returns May be null. */
|
* @returns May be null. */
|
||||||
getAttachmentByName (slotName: string, attachmentName: string): Attachment {
|
getAttachmentByName (slotName: string, attachmentName: string): Attachment | null {
|
||||||
return this.getAttachment(this.data.findSlot(slotName).index, attachmentName);
|
let slot = this.data.findSlot(slotName);
|
||||||
|
if (!slot) throw new Error(`Can't find slot with name ${slotName}`);
|
||||||
|
return this.getAttachment(slot.index, attachmentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot index and
|
/** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot index and
|
||||||
@ -509,10 +513,10 @@ export class Skeleton {
|
|||||||
*
|
*
|
||||||
* See [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide.
|
* See [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide.
|
||||||
* @returns May be null. */
|
* @returns May be null. */
|
||||||
getAttachment (slotIndex: number, attachmentName: string): Attachment {
|
getAttachment (slotIndex: number, attachmentName: string): Attachment | null {
|
||||||
if (!attachmentName) throw new Error("attachmentName cannot be null.");
|
if (!attachmentName) throw new Error("attachmentName cannot be null.");
|
||||||
if (this.skin) {
|
if (this.skin) {
|
||||||
let attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName);
|
let attachment = this.skin.getAttachment(slotIndex, attachmentName);
|
||||||
if (attachment) return attachment;
|
if (attachment) return attachment;
|
||||||
}
|
}
|
||||||
if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName);
|
if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName);
|
||||||
@ -528,7 +532,7 @@ export class Skeleton {
|
|||||||
for (let i = 0, n = slots.length; i < n; i++) {
|
for (let i = 0, n = slots.length; i < n; i++) {
|
||||||
let slot = slots[i];
|
let slot = slots[i];
|
||||||
if (slot.data.name == slotName) {
|
if (slot.data.name == slotName) {
|
||||||
let attachment: Attachment = null;
|
let attachment: Attachment | null = null;
|
||||||
if (attachmentName) {
|
if (attachmentName) {
|
||||||
attachment = this.getAttachment(i, attachmentName);
|
attachment = this.getAttachment(i, attachmentName);
|
||||||
if (!attachment) throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName);
|
if (!attachment) throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName);
|
||||||
@ -602,7 +606,7 @@ export class Skeleton {
|
|||||||
let slot = drawOrder[i];
|
let slot = drawOrder[i];
|
||||||
if (!slot.bone.active) continue;
|
if (!slot.bone.active) continue;
|
||||||
let verticesLength = 0;
|
let verticesLength = 0;
|
||||||
let vertices: NumberArrayLike = null;
|
let vertices: NumberArrayLike | null = null;
|
||||||
let attachment = slot.getAttachment();
|
let attachment = slot.getAttachment();
|
||||||
if (attachment instanceof RegionAttachment) {
|
if (attachment instanceof RegionAttachment) {
|
||||||
verticesLength = 8;
|
verticesLength = 8;
|
||||||
|
|||||||
@ -56,7 +56,7 @@ export class SkeletonBinary {
|
|||||||
* See [Scaling](http://esotericsoftware.com/spine-loading-skeleton-data#Scaling) in the Spine Runtimes Guide. */
|
* See [Scaling](http://esotericsoftware.com/spine-loading-skeleton-data#Scaling) in the Spine Runtimes Guide. */
|
||||||
scale = 1;
|
scale = 1;
|
||||||
|
|
||||||
attachmentLoader: AttachmentLoader = null;
|
attachmentLoader: AttachmentLoader;
|
||||||
private linkedMeshes = new Array<LinkedMesh>();
|
private linkedMeshes = new Array<LinkedMesh>();
|
||||||
|
|
||||||
constructor (attachmentLoader: AttachmentLoader) {
|
constructor (attachmentLoader: AttachmentLoader) {
|
||||||
@ -91,13 +91,17 @@ export class SkeletonBinary {
|
|||||||
let n = 0;
|
let n = 0;
|
||||||
// Strings.
|
// Strings.
|
||||||
n = input.readInt(true)
|
n = input.readInt(true)
|
||||||
for (let i = 0; i < n; i++)
|
for (let i = 0; i < n; i++) {
|
||||||
input.strings.push(input.readString());
|
let str = input.readString();
|
||||||
|
if (!str) throw new Error("String in string table must not be null.");
|
||||||
|
input.strings.push(str);
|
||||||
|
}
|
||||||
|
|
||||||
// Bones.
|
// Bones.
|
||||||
n = input.readInt(true)
|
n = input.readInt(true)
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
let name = input.readString();
|
let name = input.readString();
|
||||||
|
if (!name) throw new Error("Bone name must not be null.");
|
||||||
let parent = i == 0 ? null : skeletonData.bones[input.readInt(true)];
|
let parent = i == 0 ? null : skeletonData.bones[input.readInt(true)];
|
||||||
let data = new BoneData(i, name, parent);
|
let data = new BoneData(i, name, parent);
|
||||||
data.rotation = input.readFloat();
|
data.rotation = input.readFloat();
|
||||||
@ -118,6 +122,7 @@ export class SkeletonBinary {
|
|||||||
n = input.readInt(true);
|
n = input.readInt(true);
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
let slotName = input.readString();
|
let slotName = input.readString();
|
||||||
|
if (!slotName) throw new Error("Slot name must not be null.");
|
||||||
let boneData = skeletonData.bones[input.readInt(true)];
|
let boneData = skeletonData.bones[input.readInt(true)];
|
||||||
let data = new SlotData(i, slotName, boneData);
|
let data = new SlotData(i, slotName, boneData);
|
||||||
Color.rgba8888ToColor(data.color, input.readInt32());
|
Color.rgba8888ToColor(data.color, input.readInt32());
|
||||||
@ -133,7 +138,9 @@ export class SkeletonBinary {
|
|||||||
// IK constraints.
|
// IK constraints.
|
||||||
n = input.readInt(true);
|
n = input.readInt(true);
|
||||||
for (let i = 0, nn; i < n; i++) {
|
for (let i = 0, nn; i < n; i++) {
|
||||||
let data = new IkConstraintData(input.readString());
|
let name = input.readString();
|
||||||
|
if (!name) throw new Error("IK constraint data name must not be null.");
|
||||||
|
let data = new IkConstraintData(name);
|
||||||
data.order = input.readInt(true);
|
data.order = input.readInt(true);
|
||||||
data.skinRequired = input.readBoolean();
|
data.skinRequired = input.readBoolean();
|
||||||
nn = input.readInt(true);
|
nn = input.readInt(true);
|
||||||
@ -152,7 +159,9 @@ export class SkeletonBinary {
|
|||||||
// Transform constraints.
|
// Transform constraints.
|
||||||
n = input.readInt(true);
|
n = input.readInt(true);
|
||||||
for (let i = 0, nn; i < n; i++) {
|
for (let i = 0, nn; i < n; i++) {
|
||||||
let data = new TransformConstraintData(input.readString());
|
let name = input.readString();
|
||||||
|
if (!name) throw new Error("Transform constraint data name must not be null.");
|
||||||
|
let data = new TransformConstraintData(name);
|
||||||
data.order = input.readInt(true);
|
data.order = input.readInt(true);
|
||||||
data.skinRequired = input.readBoolean();
|
data.skinRequired = input.readBoolean();
|
||||||
nn = input.readInt(true);
|
nn = input.readInt(true);
|
||||||
@ -179,7 +188,9 @@ export class SkeletonBinary {
|
|||||||
// Path constraints.
|
// Path constraints.
|
||||||
n = input.readInt(true);
|
n = input.readInt(true);
|
||||||
for (let i = 0, nn; i < n; i++) {
|
for (let i = 0, nn; i < n; i++) {
|
||||||
let data = new PathConstraintData(input.readString());
|
let name = input.readString();
|
||||||
|
if (!name) throw new Error("Path constraint data name must not be null.");
|
||||||
|
let data = new PathConstraintData(name);
|
||||||
data.order = input.readInt(true);
|
data.order = input.readInt(true);
|
||||||
data.skinRequired = input.readBoolean();
|
data.skinRequired = input.readBoolean();
|
||||||
nn = input.readInt(true);
|
nn = input.readInt(true);
|
||||||
@ -211,8 +222,11 @@ export class SkeletonBinary {
|
|||||||
{
|
{
|
||||||
let i = skeletonData.skins.length;
|
let i = skeletonData.skins.length;
|
||||||
Utils.setArraySize(skeletonData.skins, n = i + input.readInt(true));
|
Utils.setArraySize(skeletonData.skins, n = i + input.readInt(true));
|
||||||
for (; i < n; i++)
|
for (; i < n; i++) {
|
||||||
skeletonData.skins[i] = this.readSkin(input, skeletonData, false, nonessential);
|
let skin = this.readSkin(input, skeletonData, false, nonessential);
|
||||||
|
if (!skin) throw new Error("readSkin() should not have returned null.");
|
||||||
|
skeletonData.skins[i] = skin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Linked meshes.
|
// Linked meshes.
|
||||||
@ -220,7 +234,10 @@ export class SkeletonBinary {
|
|||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
let linkedMesh = this.linkedMeshes[i];
|
let linkedMesh = this.linkedMeshes[i];
|
||||||
let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
|
let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
|
||||||
|
if (!skin) throw new Error("Not skin found for linked mesh.");
|
||||||
|
if (!linkedMesh.parent) throw new Error("Linked mesh parent must not be null");
|
||||||
let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
||||||
|
if (!parent) throw new Error(`Parent mesh not found: ${linkedMesh.parent}`);
|
||||||
linkedMesh.mesh.timelineAttahment = linkedMesh.inheritTimeline ? parent as VertexAttachment : linkedMesh.mesh;
|
linkedMesh.mesh.timelineAttahment = linkedMesh.inheritTimeline ? parent as VertexAttachment : linkedMesh.mesh;
|
||||||
linkedMesh.mesh.setParentMesh(parent as MeshAttachment);
|
linkedMesh.mesh.setParentMesh(parent as MeshAttachment);
|
||||||
if (linkedMesh.mesh.region != null) linkedMesh.mesh.updateRegion();
|
if (linkedMesh.mesh.region != null) linkedMesh.mesh.updateRegion();
|
||||||
@ -230,7 +247,9 @@ export class SkeletonBinary {
|
|||||||
// Events.
|
// Events.
|
||||||
n = input.readInt(true);
|
n = input.readInt(true);
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
let data = new EventData(input.readStringRef());
|
let eventName = input.readStringRef();
|
||||||
|
if (!eventName) throw new Error
|
||||||
|
let data = new EventData(eventName);
|
||||||
data.intValue = input.readInt(false);
|
data.intValue = input.readInt(false);
|
||||||
data.floatValue = input.readFloat();
|
data.floatValue = input.readFloat();
|
||||||
data.stringValue = input.readString();
|
data.stringValue = input.readString();
|
||||||
@ -244,12 +263,15 @@ export class SkeletonBinary {
|
|||||||
|
|
||||||
// Animations.
|
// Animations.
|
||||||
n = input.readInt(true);
|
n = input.readInt(true);
|
||||||
for (let i = 0; i < n; i++)
|
for (let i = 0; i < n; i++) {
|
||||||
skeletonData.animations.push(this.readAnimation(input, input.readString(), skeletonData));
|
let animationName = input.readString();
|
||||||
|
if (!animationName) throw new Error("Animatio name must not be null.");
|
||||||
|
skeletonData.animations.push(this.readAnimation(input, animationName, skeletonData));
|
||||||
|
}
|
||||||
return skeletonData;
|
return skeletonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readSkin (input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin {
|
private readSkin (input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin | null {
|
||||||
let skin = null;
|
let skin = null;
|
||||||
let slotCount = 0;
|
let slotCount = 0;
|
||||||
|
|
||||||
@ -258,7 +280,9 @@ export class SkeletonBinary {
|
|||||||
if (slotCount == 0) return null;
|
if (slotCount == 0) return null;
|
||||||
skin = new Skin("default");
|
skin = new Skin("default");
|
||||||
} else {
|
} else {
|
||||||
skin = new Skin(input.readStringRef());
|
let skinName = input.readStringRef();
|
||||||
|
if (!skinName) throw new Error("Skin name must not be null.");
|
||||||
|
skin = new Skin(skinName);
|
||||||
skin.bones.length = input.readInt(true);
|
skin.bones.length = input.readInt(true);
|
||||||
for (let i = 0, n = skin.bones.length; i < n; i++)
|
for (let i = 0, n = skin.bones.length; i < n; i++)
|
||||||
skin.bones[i] = skeletonData.bones[input.readInt(true)];
|
skin.bones[i] = skeletonData.bones[input.readInt(true)];
|
||||||
@ -277,6 +301,7 @@ export class SkeletonBinary {
|
|||||||
let slotIndex = input.readInt(true);
|
let slotIndex = input.readInt(true);
|
||||||
for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
|
for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
|
||||||
let name = input.readStringRef();
|
let name = input.readStringRef();
|
||||||
|
if (!name) throw new Error("Attachment name must not be null");
|
||||||
let attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
|
let attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
|
||||||
if (attachment) skin.setAttachment(slotIndex, name, attachment);
|
if (attachment) skin.setAttachment(slotIndex, name, attachment);
|
||||||
}
|
}
|
||||||
@ -284,7 +309,7 @@ export class SkeletonBinary {
|
|||||||
return skin;
|
return skin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readAttachment (input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment {
|
private readAttachment (input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment | null {
|
||||||
let scale = this.scale;
|
let scale = this.scale;
|
||||||
|
|
||||||
let name = input.readStringRef();
|
let name = input.readStringRef();
|
||||||
@ -327,7 +352,7 @@ export class SkeletonBinary {
|
|||||||
let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name);
|
let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name);
|
||||||
if (!box) return null;
|
if (!box) return null;
|
||||||
box.worldVerticesLength = vertexCount << 1;
|
box.worldVerticesLength = vertexCount << 1;
|
||||||
box.vertices = vertices.vertices;
|
box.vertices = vertices.vertices!;
|
||||||
box.bones = vertices.bones;
|
box.bones = vertices.bones;
|
||||||
if (nonessential) Color.rgba8888ToColor(box.color, color);
|
if (nonessential) Color.rgba8888ToColor(box.color, color);
|
||||||
return box;
|
return box;
|
||||||
@ -341,7 +366,7 @@ export class SkeletonBinary {
|
|||||||
let vertices = this.readVertices(input, vertexCount);
|
let vertices = this.readVertices(input, vertexCount);
|
||||||
let hullLength = input.readInt(true);
|
let hullLength = input.readInt(true);
|
||||||
let sequence = this.readSequence(input);
|
let sequence = this.readSequence(input);
|
||||||
let edges = null;
|
let edges: number[] = [];
|
||||||
let width = 0, height = 0;
|
let width = 0, height = 0;
|
||||||
if (nonessential) {
|
if (nonessential) {
|
||||||
edges = this.readShortArray(input);
|
edges = this.readShortArray(input);
|
||||||
@ -355,7 +380,7 @@ export class SkeletonBinary {
|
|||||||
mesh.path = path;
|
mesh.path = path;
|
||||||
Color.rgba8888ToColor(mesh.color, color);
|
Color.rgba8888ToColor(mesh.color, color);
|
||||||
mesh.bones = vertices.bones;
|
mesh.bones = vertices.bones;
|
||||||
mesh.vertices = vertices.vertices;
|
mesh.vertices = vertices.vertices!;
|
||||||
mesh.worldVerticesLength = vertexCount << 1;
|
mesh.worldVerticesLength = vertexCount << 1;
|
||||||
mesh.triangles = triangles;
|
mesh.triangles = triangles;
|
||||||
mesh.regionUVs = uvs;
|
mesh.regionUVs = uvs;
|
||||||
@ -410,7 +435,7 @@ export class SkeletonBinary {
|
|||||||
path.closed = closed;
|
path.closed = closed;
|
||||||
path.constantSpeed = constantSpeed;
|
path.constantSpeed = constantSpeed;
|
||||||
path.worldVerticesLength = vertexCount << 1;
|
path.worldVerticesLength = vertexCount << 1;
|
||||||
path.vertices = vertices.vertices;
|
path.vertices = vertices.vertices!;
|
||||||
path.bones = vertices.bones;
|
path.bones = vertices.bones;
|
||||||
path.lengths = lengths;
|
path.lengths = lengths;
|
||||||
if (nonessential) Color.rgba8888ToColor(path.color, color);
|
if (nonessential) Color.rgba8888ToColor(path.color, color);
|
||||||
@ -440,7 +465,7 @@ export class SkeletonBinary {
|
|||||||
if (!clip) return null;
|
if (!clip) return null;
|
||||||
clip.endSlot = skeletonData.slots[endSlotIndex];
|
clip.endSlot = skeletonData.slots[endSlotIndex];
|
||||||
clip.worldVerticesLength = vertexCount << 1;
|
clip.worldVerticesLength = vertexCount << 1;
|
||||||
clip.vertices = vertices.vertices;
|
clip.vertices = vertices.vertices!;
|
||||||
clip.bones = vertices.bones;
|
clip.bones = vertices.bones;
|
||||||
if (nonessential) Color.rgba8888ToColor(clip.color, color);
|
if (nonessential) Color.rgba8888ToColor(clip.color, color);
|
||||||
return clip;
|
return clip;
|
||||||
@ -866,6 +891,7 @@ export class SkeletonBinary {
|
|||||||
let slotIndex = input.readInt(true);
|
let slotIndex = input.readInt(true);
|
||||||
for (let iii = 0, nnn = input.readInt(true); iii < nnn; iii++) {
|
for (let iii = 0, nnn = input.readInt(true); iii < nnn; iii++) {
|
||||||
let attachmentName = input.readStringRef();
|
let attachmentName = input.readStringRef();
|
||||||
|
if (!attachmentName) throw new Error("attachmentName must not be null.");
|
||||||
let attachment = skin.getAttachment(slotIndex, attachmentName);
|
let attachment = skin.getAttachment(slotIndex, attachmentName);
|
||||||
let timelineType = input.readByte();
|
let timelineType = input.readByte();
|
||||||
let frameCount = input.readInt(true);
|
let frameCount = input.readInt(true);
|
||||||
@ -1041,12 +1067,12 @@ export class BinaryInput {
|
|||||||
return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
|
return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
readStringRef (): string {
|
readStringRef (): string | null {
|
||||||
let index = this.readInt(true);
|
let index = this.readInt(true);
|
||||||
return index == 0 ? null : this.strings[index - 1];
|
return index == 0 ? null : this.strings[index - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
readString (): string {
|
readString (): string | null {
|
||||||
let byteCount = this.readInt(true);
|
let byteCount = this.readInt(true);
|
||||||
switch (byteCount) {
|
switch (byteCount) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -1089,12 +1115,12 @@ export class BinaryInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LinkedMesh {
|
class LinkedMesh {
|
||||||
parent: string; skin: string;
|
parent: string | null; skin: string | null;
|
||||||
slotIndex: number;
|
slotIndex: number;
|
||||||
mesh: MeshAttachment;
|
mesh: MeshAttachment;
|
||||||
inheritTimeline: boolean;
|
inheritTimeline: boolean;
|
||||||
|
|
||||||
constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) {
|
constructor (mesh: MeshAttachment, skin: string | null, slotIndex: number, parent: string | null, inheritDeform: boolean) {
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
this.slotIndex = slotIndex;
|
this.slotIndex = slotIndex;
|
||||||
@ -1104,7 +1130,7 @@ class LinkedMesh {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Vertices {
|
class Vertices {
|
||||||
constructor (public bones: Array<number> = null, public vertices: Array<number> | Float32Array = null) { }
|
constructor (public bones: Array<number> | null = null, public vertices: Array<number> | Float32Array | null = null) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AttachmentType { Region, BoundingBox, Mesh, LinkedMesh, Path, Point, Clipping }
|
enum AttachmentType { Region, BoundingBox, Mesh, LinkedMesh, Path, Point, Clipping }
|
||||||
|
|||||||
@ -153,7 +153,7 @@ export class SkeletonBounds {
|
|||||||
|
|
||||||
/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
|
/** 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. */
|
* efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */
|
||||||
containsPoint (x: number, y: number): BoundingBoxAttachment {
|
containsPoint (x: number, y: number): BoundingBoxAttachment | null {
|
||||||
let polygons = this.polygons;
|
let polygons = this.polygons;
|
||||||
for (let i = 0, n = polygons.length; i < n; i++)
|
for (let i = 0, n = polygons.length; i < n; i++)
|
||||||
if (this.containsPointPolygon(polygons[i], x, y)) return this.boundingBoxes[i];
|
if (this.containsPointPolygon(polygons[i], x, y)) return this.boundingBoxes[i];
|
||||||
|
|||||||
@ -40,8 +40,8 @@ export class SkeletonClipping {
|
|||||||
clippedTriangles = new Array<number>();
|
clippedTriangles = new Array<number>();
|
||||||
private scratch = new Array<number>();
|
private scratch = new Array<number>();
|
||||||
|
|
||||||
private clipAttachment: ClippingAttachment;
|
private clipAttachment: ClippingAttachment | null = null;
|
||||||
private clippingPolygons: Array<Array<number>>;
|
private clippingPolygons: Array<Array<number>> | null = null;
|
||||||
|
|
||||||
clipStart (slot: Slot, clip: ClippingAttachment): number {
|
clipStart (slot: Slot, clip: ClippingAttachment): number {
|
||||||
if (this.clipAttachment) return 0;
|
if (this.clipAttachment) return 0;
|
||||||
@ -85,8 +85,8 @@ export class SkeletonClipping {
|
|||||||
|
|
||||||
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
|
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
|
||||||
let clippedTriangles = this.clippedTriangles;
|
let clippedTriangles = this.clippedTriangles;
|
||||||
let polygons = this.clippingPolygons;
|
let polygons = this.clippingPolygons!;
|
||||||
let polygonsCount = this.clippingPolygons.length;
|
let polygonsCount = polygons.length;
|
||||||
let vertexSize = twoColor ? 12 : 8;
|
let vertexSize = twoColor ? 12 : 8;
|
||||||
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
@ -234,7 +234,7 @@ export class SkeletonClipping {
|
|||||||
let clipped = false;
|
let clipped = false;
|
||||||
|
|
||||||
// Avoid copy at the end.
|
// Avoid copy at the end.
|
||||||
let input: Array<number> = null;
|
let input: Array<number>;
|
||||||
if (clippingArea.length % 4 >= 2) {
|
if (clippingArea.length % 4 >= 2) {
|
||||||
input = output;
|
input = output;
|
||||||
output = this.scratch;
|
output = this.scratch;
|
||||||
|
|||||||
@ -43,7 +43,7 @@ import { TransformConstraintData } from "./TransformConstraintData";
|
|||||||
export class SkeletonData {
|
export class SkeletonData {
|
||||||
|
|
||||||
/** The skeleton's name, which by default is the name of the skeleton data file, if possible. May be null. */
|
/** The skeleton's name, which by default is the name of the skeleton data file, if possible. May be null. */
|
||||||
name: string = null;
|
name: string | null = null;
|
||||||
|
|
||||||
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
|
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
|
||||||
bones = new Array<BoneData>(); // Ordered parents first.
|
bones = new Array<BoneData>(); // Ordered parents first.
|
||||||
@ -56,7 +56,7 @@ export class SkeletonData {
|
|||||||
*
|
*
|
||||||
* See {@link Skeleton#getAttachmentByName()}.
|
* See {@link Skeleton#getAttachmentByName()}.
|
||||||
* May be null. */
|
* May be null. */
|
||||||
defaultSkin: Skin = null;
|
defaultSkin: Skin | null = null;
|
||||||
|
|
||||||
/** The skeleton's events. */
|
/** The skeleton's events. */
|
||||||
events = new Array<EventData>();
|
events = new Array<EventData>();
|
||||||
@ -86,20 +86,20 @@ export class SkeletonData {
|
|||||||
height: number = 0;
|
height: number = 0;
|
||||||
|
|
||||||
/** The Spine version used to export the skeleton data, or null. */
|
/** The Spine version used to export the skeleton data, or null. */
|
||||||
version: string = null;
|
version: string | null = null;
|
||||||
|
|
||||||
/** The skeleton data hash. This value will change if any of the skeleton data has changed. May be null. */
|
/** The skeleton data hash. This value will change if any of the skeleton data has changed. May be null. */
|
||||||
hash: string = null;
|
hash: string | null = null;
|
||||||
|
|
||||||
// Nonessential
|
// Nonessential
|
||||||
/** The dopesheet FPS in Spine. Available only when nonessential data was exported. */
|
/** The dopesheet FPS in Spine. Available only when nonessential data was exported. */
|
||||||
fps = 0;
|
fps = 0;
|
||||||
|
|
||||||
/** The path to the images directory as defined in Spine. Available only when nonessential data was exported. May be null. */
|
/** The path to the images directory as defined in Spine. Available only when nonessential data was exported. May be null. */
|
||||||
imagesPath: string = null;
|
imagesPath: string | null = null;
|
||||||
|
|
||||||
/** The path to the audio directory as defined in Spine. Available only when nonessential data was exported. May be null. */
|
/** The path to the audio directory as defined in Spine. Available only when nonessential data was exported. May be null. */
|
||||||
audioPath: string = null;
|
audioPath: string | null = null;
|
||||||
|
|
||||||
/** Finds a bone by comparing each bone's name. It is more efficient to cache the results of this method than to call it
|
/** Finds a bone by comparing each bone's name. It is more efficient to cache the results of this method than to call it
|
||||||
* multiple times.
|
* multiple times.
|
||||||
|
|||||||
@ -51,7 +51,7 @@ import { HasTextureRegion } from "./attachments/HasTextureRegion";
|
|||||||
* [JSON and binary data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the Spine
|
* [JSON and binary data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the Spine
|
||||||
* Runtimes Guide. */
|
* Runtimes Guide. */
|
||||||
export class SkeletonJson {
|
export class SkeletonJson {
|
||||||
attachmentLoader: AttachmentLoader = null;
|
attachmentLoader: AttachmentLoader;
|
||||||
|
|
||||||
/** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
|
/** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
|
||||||
* runtime than were used in Spine.
|
* runtime than were used in Spine.
|
||||||
@ -87,7 +87,7 @@ export class SkeletonJson {
|
|||||||
for (let i = 0; i < root.bones.length; i++) {
|
for (let i = 0; i < root.bones.length; i++) {
|
||||||
let boneMap = root.bones[i];
|
let boneMap = root.bones[i];
|
||||||
|
|
||||||
let parent: BoneData = null;
|
let parent: BoneData | null = null;
|
||||||
let parentName: string = getValue(boneMap, "parent", null);
|
let parentName: string = getValue(boneMap, "parent", null);
|
||||||
if (parentName) parent = skeletonData.findBone(parentName);
|
if (parentName) parent = skeletonData.findBone(parentName);
|
||||||
let data = new BoneData(skeletonData.bones.length, boneMap.name, parent);
|
let data = new BoneData(skeletonData.bones.length, boneMap.name, parent);
|
||||||
@ -114,6 +114,7 @@ export class SkeletonJson {
|
|||||||
for (let i = 0; i < root.slots.length; i++) {
|
for (let i = 0; i < root.slots.length; i++) {
|
||||||
let slotMap = root.slots[i];
|
let slotMap = root.slots[i];
|
||||||
let boneData = skeletonData.findBone(slotMap.bone);
|
let boneData = skeletonData.findBone(slotMap.bone);
|
||||||
|
if (!boneData) throw new Error(`Couldn't find bone ${slotMap.bone} for slot ${slotMap.name}`);
|
||||||
let data = new SlotData(skeletonData.slots.length, slotMap.name, boneData);
|
let data = new SlotData(skeletonData.slots.length, slotMap.name, boneData);
|
||||||
|
|
||||||
let color: string = getValue(slotMap, "color", null);
|
let color: string = getValue(slotMap, "color", null);
|
||||||
@ -136,10 +137,15 @@ export class SkeletonJson {
|
|||||||
data.order = getValue(constraintMap, "order", 0);
|
data.order = getValue(constraintMap, "order", 0);
|
||||||
data.skinRequired = getValue(constraintMap, "skin", false);
|
data.skinRequired = getValue(constraintMap, "skin", false);
|
||||||
|
|
||||||
for (let ii = 0; ii < constraintMap.bones.length; ii++)
|
for (let ii = 0; ii < constraintMap.bones.length; ii++) {
|
||||||
data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
|
let bone = skeletonData.findBone(constraintMap.bones[ii]);
|
||||||
|
if (!bone) throw new Error(`Couldn't find bone ${constraintMap.bones[ii]} for IK constraint ${constraintMap.name}.`);
|
||||||
|
data.bones.push(bone);
|
||||||
|
}
|
||||||
|
|
||||||
data.target = skeletonData.findBone(constraintMap.target);
|
let target = skeletonData.findBone(constraintMap.target);;
|
||||||
|
if (!target) throw new Error(`Couldn't find target bone ${constraintMap.target} for IK constraint ${constraintMap.name}.`);
|
||||||
|
data.target = target;
|
||||||
|
|
||||||
data.mix = getValue(constraintMap, "mix", 1);
|
data.mix = getValue(constraintMap, "mix", 1);
|
||||||
data.softness = getValue(constraintMap, "softness", 0) * scale;
|
data.softness = getValue(constraintMap, "softness", 0) * scale;
|
||||||
@ -160,11 +166,17 @@ export class SkeletonJson {
|
|||||||
data.order = getValue(constraintMap, "order", 0);
|
data.order = getValue(constraintMap, "order", 0);
|
||||||
data.skinRequired = getValue(constraintMap, "skin", false);
|
data.skinRequired = getValue(constraintMap, "skin", false);
|
||||||
|
|
||||||
for (let ii = 0; ii < constraintMap.bones.length; ii++)
|
for (let ii = 0; ii < constraintMap.bones.length; ii++) {
|
||||||
data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
|
let boneName = constraintMap.bones[ii];
|
||||||
|
let bone = skeletonData.findBone(boneName);
|
||||||
|
if (!bone) throw new Error(`Couldn't find bone ${boneName} for transform constraint ${constraintMap.name}.`);
|
||||||
|
data.bones.push(bone);
|
||||||
|
}
|
||||||
|
|
||||||
let targetName: string = constraintMap.target;
|
let targetName: string = constraintMap.target;
|
||||||
data.target = skeletonData.findBone(targetName);
|
let target = skeletonData.findBone(targetName);
|
||||||
|
if (!target) throw new Error(`Couldn't find target bone ${targetName} for transform constraint ${constraintMap.name}.`);
|
||||||
|
data.target = target;
|
||||||
|
|
||||||
data.local = getValue(constraintMap, "local", false);
|
data.local = getValue(constraintMap, "local", false);
|
||||||
data.relative = getValue(constraintMap, "relative", false);
|
data.relative = getValue(constraintMap, "relative", false);
|
||||||
@ -194,11 +206,17 @@ export class SkeletonJson {
|
|||||||
data.order = getValue(constraintMap, "order", 0);
|
data.order = getValue(constraintMap, "order", 0);
|
||||||
data.skinRequired = getValue(constraintMap, "skin", false);
|
data.skinRequired = getValue(constraintMap, "skin", false);
|
||||||
|
|
||||||
for (let ii = 0; ii < constraintMap.bones.length; ii++)
|
for (let ii = 0; ii < constraintMap.bones.length; ii++) {
|
||||||
data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
|
let boneName = constraintMap.bones[ii];
|
||||||
|
let bone = skeletonData.findBone(boneName);
|
||||||
|
if (!bone) throw new Error(`Couldn't find bone ${boneName} for path constraint ${constraintMap.name}.`);
|
||||||
|
data.bones.push(bone);
|
||||||
|
}
|
||||||
|
|
||||||
let targetName: string = constraintMap.target;
|
let targetName: string = constraintMap.target;
|
||||||
data.target = skeletonData.findSlot(targetName);
|
let target = skeletonData.findSlot(targetName);
|
||||||
|
if (!target) throw new Error(`Couldn't find target slot ${targetName} for path constraint ${constraintMap.name}.`);
|
||||||
|
data.target = target;
|
||||||
|
|
||||||
data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, "positionMode", "Percent"));
|
data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, "positionMode", "Percent"));
|
||||||
data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, "spacingMode", "Length"));
|
data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, "spacingMode", "Length"));
|
||||||
@ -223,27 +241,44 @@ export class SkeletonJson {
|
|||||||
let skin = new Skin(skinMap.name);
|
let skin = new Skin(skinMap.name);
|
||||||
|
|
||||||
if (skinMap.bones) {
|
if (skinMap.bones) {
|
||||||
for (let ii = 0; ii < skinMap.bones.length; ii++)
|
for (let ii = 0; ii < skinMap.bones.length; ii++) {
|
||||||
skin.bones.push(skeletonData.findBone(skinMap.bones[ii]));
|
let boneName = skinMap.bones[ii];
|
||||||
|
let bone = skeletonData.findBone(boneName);
|
||||||
|
if (!bone) throw new Error(`Couldn't find bone ${boneName} for skin ${skinMap.name}.`);
|
||||||
|
skin.bones.push(bone);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skinMap.ik) {
|
if (skinMap.ik) {
|
||||||
for (let ii = 0; ii < skinMap.ik.length; ii++)
|
for (let ii = 0; ii < skinMap.ik.length; ii++) {
|
||||||
skin.constraints.push(skeletonData.findIkConstraint(skinMap.ik[ii]));
|
let constraintName = skinMap.ik[ii];
|
||||||
|
let constraint = skeletonData.findIkConstraint(constraintName);
|
||||||
|
if (!constraint) throw new Error(`Couldn't find IK constraint ${constraintName} for skin ${skinMap.name}.`);
|
||||||
|
skin.constraints.push(constraint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skinMap.transform) {
|
if (skinMap.transform) {
|
||||||
for (let ii = 0; ii < skinMap.transform.length; ii++)
|
for (let ii = 0; ii < skinMap.transform.length; ii++) {
|
||||||
skin.constraints.push(skeletonData.findTransformConstraint(skinMap.transform[ii]));
|
let constraintName = skinMap.transform[ii];
|
||||||
|
let constraint = skeletonData.findTransformConstraint(constraintName);
|
||||||
|
if (!constraint) throw new Error(`Couldn't find transform constraint ${constraintName} for skin ${skinMap.name}.`);
|
||||||
|
skin.constraints.push(constraint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skinMap.path) {
|
if (skinMap.path) {
|
||||||
for (let ii = 0; ii < skinMap.path.length; ii++)
|
for (let ii = 0; ii < skinMap.path.length; ii++) {
|
||||||
skin.constraints.push(skeletonData.findPathConstraint(skinMap.path[ii]));
|
let constraintName = skinMap.path[ii];
|
||||||
|
let constraint = skeletonData.findPathConstraint(constraintName);
|
||||||
|
if (!constraint) throw new Error(`Couldn't find path constraint ${constraintName} for skin ${skinMap.name}.`);
|
||||||
|
skin.constraints.push(constraint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let slotName in skinMap.attachments) {
|
for (let slotName in skinMap.attachments) {
|
||||||
let slot = skeletonData.findSlot(slotName);
|
let slot = skeletonData.findSlot(slotName);
|
||||||
|
if (!slot) throw new Error(`Couldn't find slot ${slotName} for skin ${skinMap.name}.`);
|
||||||
let slotMap = skinMap.attachments[slotName];
|
let slotMap = skinMap.attachments[slotName];
|
||||||
for (let entryName in slotMap) {
|
for (let entryName in slotMap) {
|
||||||
let attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData);
|
let attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData);
|
||||||
@ -259,7 +294,9 @@ export class SkeletonJson {
|
|||||||
for (let i = 0, n = this.linkedMeshes.length; i < n; i++) {
|
for (let i = 0, n = this.linkedMeshes.length; i < n; i++) {
|
||||||
let linkedMesh = this.linkedMeshes[i];
|
let linkedMesh = this.linkedMeshes[i];
|
||||||
let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
|
let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
|
||||||
|
if (!skin) throw new Error(`Skin not found: ${linkedMesh.skin}`);
|
||||||
let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
||||||
|
if (!parent) throw new Error(`Parent mesh not found: ${linkedMesh.parent}`);
|
||||||
linkedMesh.mesh.timelineAttahment = linkedMesh.inheritTimeline ? <VertexAttachment>parent : <VertexAttachment>linkedMesh.mesh;
|
linkedMesh.mesh.timelineAttahment = linkedMesh.inheritTimeline ? <VertexAttachment>parent : <VertexAttachment>linkedMesh.mesh;
|
||||||
linkedMesh.mesh.setParentMesh(<MeshAttachment>parent);
|
linkedMesh.mesh.setParentMesh(<MeshAttachment>parent);
|
||||||
if (linkedMesh.mesh.region != null) linkedMesh.mesh.updateRegion();
|
if (linkedMesh.mesh.region != null) linkedMesh.mesh.updateRegion();
|
||||||
@ -294,7 +331,7 @@ export class SkeletonJson {
|
|||||||
return skeletonData;
|
return skeletonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
readAttachment (map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment {
|
readAttachment (map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment | null {
|
||||||
let scale = this.scale;
|
let scale = this.scale;
|
||||||
name = getValue(map, "name", name);
|
name = getValue(map, "name", name);
|
||||||
|
|
||||||
@ -452,7 +489,9 @@ export class SkeletonJson {
|
|||||||
if (map.slots) {
|
if (map.slots) {
|
||||||
for (let slotName in map.slots) {
|
for (let slotName in map.slots) {
|
||||||
let slotMap = map.slots[slotName];
|
let slotMap = map.slots[slotName];
|
||||||
let slotIndex = skeletonData.findSlot(slotName).index;
|
let slot = skeletonData.findSlot(slotName);
|
||||||
|
if (!slot) throw new Error("Slot not found: " + slotName);
|
||||||
|
let slotIndex = slot.index;
|
||||||
for (let timelineName in slotMap) {
|
for (let timelineName in slotMap) {
|
||||||
let timelineMap = slotMap[timelineName];
|
let timelineMap = slotMap[timelineName];
|
||||||
if (!timelineMap) continue;
|
if (!timelineMap) continue;
|
||||||
@ -603,7 +642,9 @@ export class SkeletonJson {
|
|||||||
if (map.bones) {
|
if (map.bones) {
|
||||||
for (let boneName in map.bones) {
|
for (let boneName in map.bones) {
|
||||||
let boneMap = map.bones[boneName];
|
let boneMap = map.bones[boneName];
|
||||||
let boneIndex = skeletonData.findBone(boneName).index;
|
let bone = skeletonData.findBone(boneName);
|
||||||
|
if (!bone) throw new Error("Bone not found: " + boneName);
|
||||||
|
let boneIndex = bone.index;
|
||||||
for (let timelineName in boneMap) {
|
for (let timelineName in boneMap) {
|
||||||
let timelineMap = boneMap[timelineName];
|
let timelineMap = boneMap[timelineName];
|
||||||
let frames = timelineMap.length;
|
let frames = timelineMap.length;
|
||||||
@ -651,6 +692,7 @@ export class SkeletonJson {
|
|||||||
if (!keyMap) continue;
|
if (!keyMap) continue;
|
||||||
|
|
||||||
let constraint = skeletonData.findIkConstraint(constraintName);
|
let constraint = skeletonData.findIkConstraint(constraintName);
|
||||||
|
if (!constraint) throw new Error("IK Constraint not found: " + constraintName);
|
||||||
let constraintIndex = skeletonData.ikConstraints.indexOf(constraint);
|
let constraintIndex = skeletonData.ikConstraints.indexOf(constraint);
|
||||||
let timeline = new IkConstraintTimeline(constraintMap.length, constraintMap.length << 1, constraintIndex);
|
let timeline = new IkConstraintTimeline(constraintMap.length, constraintMap.length << 1, constraintIndex);
|
||||||
|
|
||||||
@ -692,6 +734,7 @@ export class SkeletonJson {
|
|||||||
if (!keyMap) continue;
|
if (!keyMap) continue;
|
||||||
|
|
||||||
let constraint = skeletonData.findTransformConstraint(constraintName);
|
let constraint = skeletonData.findTransformConstraint(constraintName);
|
||||||
|
if (!constraint) throw new Error("Transform constraint not found: " + constraintName);
|
||||||
let constraintIndex = skeletonData.transformConstraints.indexOf(constraint);
|
let constraintIndex = skeletonData.transformConstraints.indexOf(constraint);
|
||||||
let timeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length * 6, constraintIndex);
|
let timeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length * 6, constraintIndex);
|
||||||
|
|
||||||
@ -746,6 +789,7 @@ export class SkeletonJson {
|
|||||||
for (let constraintName in map.path) {
|
for (let constraintName in map.path) {
|
||||||
let constraintMap = map.path[constraintName];
|
let constraintMap = map.path[constraintName];
|
||||||
let constraint = skeletonData.findPathConstraint(constraintName);
|
let constraint = skeletonData.findPathConstraint(constraintName);
|
||||||
|
if (!constraint) throw new Error("Path constraint not found: " + constraintName);
|
||||||
let constraintIndex = skeletonData.pathConstraints.indexOf(constraint);
|
let constraintIndex = skeletonData.pathConstraints.indexOf(constraint);
|
||||||
for (let timelineName in constraintMap) {
|
for (let timelineName in constraintMap) {
|
||||||
let timelineMap = constraintMap[timelineName];
|
let timelineMap = constraintMap[timelineName];
|
||||||
@ -799,9 +843,12 @@ export class SkeletonJson {
|
|||||||
for (let attachmentsName in map.attachments) {
|
for (let attachmentsName in map.attachments) {
|
||||||
let attachmentsMap = map.attachments[attachmentsName];
|
let attachmentsMap = map.attachments[attachmentsName];
|
||||||
let skin = skeletonData.findSkin(attachmentsName);
|
let skin = skeletonData.findSkin(attachmentsName);
|
||||||
|
if (!skin) throw new Error("Skin not found: " + attachmentsName);
|
||||||
for (let slotMapName in attachmentsMap) {
|
for (let slotMapName in attachmentsMap) {
|
||||||
let slotMap = attachmentsMap[slotMapName];
|
let slotMap = attachmentsMap[slotMapName];
|
||||||
let slotIndex = skeletonData.findSlot(slotMapName).index;
|
let slot = skeletonData.findSlot(slotMapName);
|
||||||
|
if (!slot) throw new Error("Slot not found: " + slotMapName);
|
||||||
|
let slotIndex = slot.index;
|
||||||
for (let attachmentMapName in slotMap) {
|
for (let attachmentMapName in slotMap) {
|
||||||
let attachmentMap = slotMap[attachmentMapName];
|
let attachmentMap = slotMap[attachmentMapName];
|
||||||
let attachment = <VertexAttachment>skin.getAttachment(slotIndex, attachmentMapName);
|
let attachment = <VertexAttachment>skin.getAttachment(slotIndex, attachmentMapName);
|
||||||
@ -877,7 +924,7 @@ export class SkeletonJson {
|
|||||||
let frame = 0;
|
let frame = 0;
|
||||||
for (let i = 0; i < map.drawOrder.length; i++, frame++) {
|
for (let i = 0; i < map.drawOrder.length; i++, frame++) {
|
||||||
let drawOrderMap = map.drawOrder[i];
|
let drawOrderMap = map.drawOrder[i];
|
||||||
let drawOrder: Array<number> = null;
|
let drawOrder: Array<number> | null = null;
|
||||||
let offsets = getValue(drawOrderMap, "offsets", null);
|
let offsets = getValue(drawOrderMap, "offsets", null);
|
||||||
if (offsets) {
|
if (offsets) {
|
||||||
drawOrder = Utils.newArray<number>(slotCount, -1);
|
drawOrder = Utils.newArray<number>(slotCount, -1);
|
||||||
@ -885,7 +932,9 @@ export class SkeletonJson {
|
|||||||
let originalIndex = 0, unchangedIndex = 0;
|
let originalIndex = 0, unchangedIndex = 0;
|
||||||
for (let ii = 0; ii < offsets.length; ii++) {
|
for (let ii = 0; ii < offsets.length; ii++) {
|
||||||
let offsetMap = offsets[ii];
|
let offsetMap = offsets[ii];
|
||||||
let slotIndex = skeletonData.findSlot(offsetMap.slot).index;
|
let slot = skeletonData.findSlot(offsetMap.slot);
|
||||||
|
if (!slot) throw new Error("Slot not found: " + slot);
|
||||||
|
let slotIndex = slot.index;
|
||||||
// Collect unchanged items.
|
// Collect unchanged items.
|
||||||
while (originalIndex != slotIndex)
|
while (originalIndex != slotIndex)
|
||||||
unchanged[unchangedIndex++] = originalIndex++;
|
unchanged[unchangedIndex++] = originalIndex++;
|
||||||
@ -911,6 +960,7 @@ export class SkeletonJson {
|
|||||||
for (let i = 0; i < map.events.length; i++, frame++) {
|
for (let i = 0; i < map.events.length; i++, frame++) {
|
||||||
let eventMap = map.events[i];
|
let eventMap = map.events[i];
|
||||||
let eventData = skeletonData.findEvent(eventMap.name);
|
let eventData = skeletonData.findEvent(eventMap.name);
|
||||||
|
if (!eventData) throw new Error("Event not found: " + eventMap.name);
|
||||||
let event = new Event(Utils.toSinglePrecision(getValue(eventMap, "time", 0)), eventData);
|
let event = new Event(Utils.toSinglePrecision(getValue(eventMap, "time", 0)), eventData);
|
||||||
event.intValue = getValue(eventMap, "int", eventData.intValue);
|
event.intValue = getValue(eventMap, "int", eventData.intValue);
|
||||||
event.floatValue = getValue(eventMap, "float", eventData.floatValue);
|
event.floatValue = getValue(eventMap, "float", eventData.floatValue);
|
||||||
|
|||||||
@ -36,7 +36,7 @@ import { StringMap } from "./Utils";
|
|||||||
|
|
||||||
/** Stores an entry in the skin consisting of the slot index, name, and attachment **/
|
/** Stores an entry in the skin consisting of the slot index, name, and attachment **/
|
||||||
export class SkinEntry {
|
export class SkinEntry {
|
||||||
constructor (public slotIndex: number = 0, public name: string = null, public attachment: Attachment = null) { }
|
constructor (public slotIndex: number = 0, public name: string, public attachment: Attachment) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stores attachments by slot index and attachment name.
|
/** Stores attachments by slot index and attachment name.
|
||||||
@ -45,7 +45,7 @@ export class SkinEntry {
|
|||||||
* [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide. */
|
* [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide. */
|
||||||
export class Skin {
|
export class Skin {
|
||||||
/** The skin's name, which is unique across all skins in the skeleton. */
|
/** The skin's name, which is unique across all skins in the skeleton. */
|
||||||
name: string = null;
|
name: string;
|
||||||
|
|
||||||
attachments = new Array<StringMap<Attachment>>();
|
attachments = new Array<StringMap<Attachment>>();
|
||||||
bones = Array<BoneData>();
|
bones = Array<BoneData>();
|
||||||
@ -140,7 +140,7 @@ export class Skin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the attachment for the specified slot index and name, or null. */
|
/** Returns the attachment for the specified slot index and name, or null. */
|
||||||
getAttachment (slotIndex: number, name: string): Attachment {
|
getAttachment (slotIndex: number, name: string): Attachment | null {
|
||||||
let dictionary = this.attachments[slotIndex];
|
let dictionary = this.attachments[slotIndex];
|
||||||
return dictionary ? dictionary[name] : null;
|
return dictionary ? dictionary[name] : null;
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ export class Skin {
|
|||||||
/** Removes the attachment in the skin for the specified slot index and name, if any. */
|
/** Removes the attachment in the skin for the specified slot index and name, if any. */
|
||||||
removeAttachment (slotIndex: number, name: string) {
|
removeAttachment (slotIndex: number, name: string) {
|
||||||
let dictionary = this.attachments[slotIndex];
|
let dictionary = this.attachments[slotIndex];
|
||||||
if (dictionary) dictionary[name] = null;
|
if (dictionary) delete dictionary[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns all attachments in this skin. */
|
/** Returns all attachments in this skin. */
|
||||||
|
|||||||
@ -38,26 +38,26 @@ import { Color } from "./Utils";
|
|||||||
* across multiple skeletons. */
|
* across multiple skeletons. */
|
||||||
export class Slot {
|
export class Slot {
|
||||||
/** The slot's setup pose data. */
|
/** The slot's setup pose data. */
|
||||||
data: SlotData = null;
|
data: SlotData;
|
||||||
|
|
||||||
/** The bone this slot belongs to. */
|
/** The bone this slot belongs to. */
|
||||||
bone: Bone = null;
|
bone: Bone;
|
||||||
|
|
||||||
/** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two
|
/** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two
|
||||||
* color tinting. */
|
* color tinting. */
|
||||||
color: Color = null;
|
color: Color;
|
||||||
|
|
||||||
/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
|
/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
|
||||||
* color's alpha is not used. */
|
* color's alpha is not used. */
|
||||||
darkColor: Color = null;
|
darkColor: Color | null = null;
|
||||||
|
|
||||||
attachment: Attachment = null;
|
attachment: Attachment | null = null;
|
||||||
|
|
||||||
attachmentState: number = 0;
|
attachmentState: number = 0;
|
||||||
|
|
||||||
/** The index of the texture region to display when the slot's attachment has a {@link Sequence}. -1 represents the
|
/** The index of the texture region to display when the slot's attachment has a {@link Sequence}. -1 represents the
|
||||||
* {@link Sequence#getSetupIndex()}. */
|
* {@link Sequence#getSetupIndex()}. */
|
||||||
sequenceIndex: number;
|
sequenceIndex: number = -1;
|
||||||
|
|
||||||
/** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
|
/** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
|
||||||
* weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions.
|
* weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions.
|
||||||
@ -81,14 +81,14 @@ export class Slot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The current attachment for the slot, or null if the slot has no attachment. */
|
/** The current attachment for the slot, or null if the slot has no attachment. */
|
||||||
getAttachment (): Attachment {
|
getAttachment (): Attachment | null {
|
||||||
return this.attachment;
|
return this.attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the slot's attachment and, if the attachment changed, resets {@link #sequenceIndex} and clears the {@link #deform}.
|
/** Sets the slot's attachment and, if the attachment changed, resets {@link #sequenceIndex} and clears the {@link #deform}.
|
||||||
* The deform is not cleared if the old attachment has the same {@link VertexAttachment#getTimelineAttachment()} as the
|
* The deform is not cleared if the old attachment has the same {@link VertexAttachment#getTimelineAttachment()} as the
|
||||||
* specified attachment. */
|
* specified attachment. */
|
||||||
setAttachment (attachment: Attachment) {
|
setAttachment (attachment: Attachment | null) {
|
||||||
if (this.attachment == attachment) return;
|
if (this.attachment == attachment) return;
|
||||||
if (!(attachment instanceof VertexAttachment) || !(this.attachment instanceof VertexAttachment)
|
if (!(attachment instanceof VertexAttachment) || !(this.attachment instanceof VertexAttachment)
|
||||||
|| (<VertexAttachment>attachment).timelineAttahment != (<VertexAttachment>this.attachment).timelineAttahment) {
|
|| (<VertexAttachment>attachment).timelineAttahment != (<VertexAttachment>this.attachment).timelineAttahment) {
|
||||||
@ -101,7 +101,7 @@ export class Slot {
|
|||||||
/** Sets this slot to the setup pose. */
|
/** Sets this slot to the setup pose. */
|
||||||
setToSetupPose () {
|
setToSetupPose () {
|
||||||
this.color.setFromColor(this.data.color);
|
this.color.setFromColor(this.data.color);
|
||||||
if (this.darkColor) this.darkColor.setFromColor(this.data.darkColor);
|
if (this.darkColor) this.darkColor.setFromColor(this.data.darkColor!);
|
||||||
if (!this.data.attachmentName)
|
if (!this.data.attachmentName)
|
||||||
this.attachment = null;
|
this.attachment = null;
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -36,10 +36,10 @@ export class SlotData {
|
|||||||
index: number = 0;
|
index: number = 0;
|
||||||
|
|
||||||
/** The name of the slot, which is unique across all slots in the skeleton. */
|
/** The name of the slot, which is unique across all slots in the skeleton. */
|
||||||
name: string = null;
|
name: string;
|
||||||
|
|
||||||
/** The bone this slot belongs to. */
|
/** The bone this slot belongs to. */
|
||||||
boneData: BoneData = null;
|
boneData: BoneData;
|
||||||
|
|
||||||
/** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two
|
/** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two
|
||||||
* color tinting. */
|
* color tinting. */
|
||||||
@ -47,13 +47,13 @@ export class SlotData {
|
|||||||
|
|
||||||
/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
|
/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
|
||||||
* color's alpha is not used. */
|
* color's alpha is not used. */
|
||||||
darkColor: Color = null;
|
darkColor: Color | null = null;
|
||||||
|
|
||||||
/** The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible. */
|
/** The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible. */
|
||||||
attachmentName: string = null;
|
attachmentName: string | null = null;
|
||||||
|
|
||||||
/** The blend mode for drawing the slot's attachment. */
|
/** The blend mode for drawing the slot's attachment. */
|
||||||
blendMode: BlendMode = null;
|
blendMode: BlendMode = BlendMode.Normal;
|
||||||
|
|
||||||
constructor (index: number, name: string, boneData: BoneData) {
|
constructor (index: number, name: string, boneData: BoneData) {
|
||||||
if (index < 0) throw new Error("index must be >= 0.");
|
if (index < 0) throw new Error("index must be >= 0.");
|
||||||
|
|||||||
@ -38,66 +38,64 @@ export class TextureAtlas implements Disposable {
|
|||||||
constructor (atlasText: string) {
|
constructor (atlasText: string) {
|
||||||
let reader = new TextureAtlasReader(atlasText);
|
let reader = new TextureAtlasReader(atlasText);
|
||||||
let entry = new Array<string>(4);
|
let entry = new Array<string>(4);
|
||||||
let page: TextureAtlasPage = null;
|
|
||||||
let region: TextureAtlasRegion = null;
|
|
||||||
|
|
||||||
let pageFields: StringMap<Function> = {};
|
let pageFields: StringMap<(page: TextureAtlasPage) => void> = {};
|
||||||
pageFields["size"] = () => {
|
pageFields["size"] = (page: TextureAtlasPage) => {
|
||||||
page.width = parseInt(entry[1]);
|
page!.width = parseInt(entry[1]);
|
||||||
page.height = parseInt(entry[2]);
|
page!.height = parseInt(entry[2]);
|
||||||
};
|
};
|
||||||
pageFields["format"] = () => {
|
pageFields["format"] = () => {
|
||||||
// page.format = Format[tuple[0]]; we don't need format in WebGL
|
// page.format = Format[tuple[0]]; we don't need format in WebGL
|
||||||
};
|
};
|
||||||
pageFields["filter"] = () => {
|
pageFields["filter"] = (page: TextureAtlasPage) => {
|
||||||
page.minFilter = Utils.enumValue(TextureFilter, entry[1]);
|
page!.minFilter = Utils.enumValue(TextureFilter, entry[1]);
|
||||||
page.magFilter = Utils.enumValue(TextureFilter, entry[2]);
|
page!.magFilter = Utils.enumValue(TextureFilter, entry[2]);
|
||||||
};
|
};
|
||||||
pageFields["repeat"] = () => {
|
pageFields["repeat"] = (page: TextureAtlasPage) => {
|
||||||
if (entry[1].indexOf('x') != -1) page.uWrap = TextureWrap.Repeat;
|
if (entry[1].indexOf('x') != -1) page!.uWrap = TextureWrap.Repeat;
|
||||||
if (entry[1].indexOf('y') != -1) page.vWrap = TextureWrap.Repeat;
|
if (entry[1].indexOf('y') != -1) page!.vWrap = TextureWrap.Repeat;
|
||||||
};
|
};
|
||||||
pageFields["pma"] = () => {
|
pageFields["pma"] = (page: TextureAtlasPage) => {
|
||||||
page.pma = entry[1] == "true";
|
page!.pma = entry[1] == "true";
|
||||||
};
|
};
|
||||||
|
|
||||||
var regionFields: StringMap<Function> = {};
|
var regionFields: StringMap<(region: TextureAtlasRegion) => void> = {};
|
||||||
regionFields["xy"] = () => { // Deprecated, use bounds.
|
regionFields["xy"] = (region: TextureAtlasRegion) => { // Deprecated, use bounds.
|
||||||
region.x = parseInt(entry[1]);
|
region.x = parseInt(entry[1]);
|
||||||
region.y = parseInt(entry[2]);
|
region.y = parseInt(entry[2]);
|
||||||
};
|
};
|
||||||
regionFields["size"] = () => { // Deprecated, use bounds.
|
regionFields["size"] = (region: TextureAtlasRegion) => { // Deprecated, use bounds.
|
||||||
region.width = parseInt(entry[1]);
|
region.width = parseInt(entry[1]);
|
||||||
region.height = parseInt(entry[2]);
|
region.height = parseInt(entry[2]);
|
||||||
};
|
};
|
||||||
regionFields["bounds"] = () => {
|
regionFields["bounds"] = (region: TextureAtlasRegion) => {
|
||||||
region.x = parseInt(entry[1]);
|
region.x = parseInt(entry[1]);
|
||||||
region.y = parseInt(entry[2]);
|
region.y = parseInt(entry[2]);
|
||||||
region.width = parseInt(entry[3]);
|
region.width = parseInt(entry[3]);
|
||||||
region.height = parseInt(entry[4]);
|
region.height = parseInt(entry[4]);
|
||||||
};
|
};
|
||||||
regionFields["offset"] = () => { // Deprecated, use offsets.
|
regionFields["offset"] = (region: TextureAtlasRegion) => { // Deprecated, use offsets.
|
||||||
region.offsetX = parseInt(entry[1]);
|
region.offsetX = parseInt(entry[1]);
|
||||||
region.offsetY = parseInt(entry[2]);
|
region.offsetY = parseInt(entry[2]);
|
||||||
};
|
};
|
||||||
regionFields["orig"] = () => { // Deprecated, use offsets.
|
regionFields["orig"] = (region: TextureAtlasRegion) => { // Deprecated, use offsets.
|
||||||
region.originalWidth = parseInt(entry[1]);
|
region.originalWidth = parseInt(entry[1]);
|
||||||
region.originalHeight = parseInt(entry[2]);
|
region.originalHeight = parseInt(entry[2]);
|
||||||
};
|
};
|
||||||
regionFields["offsets"] = () => {
|
regionFields["offsets"] = (region: TextureAtlasRegion) => {
|
||||||
region.offsetX = parseInt(entry[1]);
|
region.offsetX = parseInt(entry[1]);
|
||||||
region.offsetY = parseInt(entry[2]);
|
region.offsetY = parseInt(entry[2]);
|
||||||
region.originalWidth = parseInt(entry[3]);
|
region.originalWidth = parseInt(entry[3]);
|
||||||
region.originalHeight = parseInt(entry[4]);
|
region.originalHeight = parseInt(entry[4]);
|
||||||
};
|
};
|
||||||
regionFields["rotate"] = () => {
|
regionFields["rotate"] = (region: TextureAtlasRegion) => {
|
||||||
let value = entry[1];
|
let value = entry[1];
|
||||||
if (value == "true")
|
if (value == "true")
|
||||||
region.degrees = 90;
|
region.degrees = 90;
|
||||||
else if (value != "false")
|
else if (value != "false")
|
||||||
region.degrees = parseInt(value);
|
region.degrees = parseInt(value);
|
||||||
};
|
};
|
||||||
regionFields["index"] = () => {
|
regionFields["index"] = (region: TextureAtlasRegion) => {
|
||||||
region.index = parseInt(entry[1]);
|
region.index = parseInt(entry[1]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -113,38 +111,34 @@ export class TextureAtlas implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Page and region entries.
|
// Page and region entries.
|
||||||
let names: string[] = null;
|
let page: TextureAtlasPage | null = null;
|
||||||
let values: number[][] = null;
|
let names: string[] | null = null;
|
||||||
|
let values: number[][] | null = null;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (line === null) break;
|
if (line === null) break;
|
||||||
if (line.trim().length == 0) {
|
if (line.trim().length == 0) {
|
||||||
page = null;
|
page = null;
|
||||||
line = reader.readLine();
|
line = reader.readLine();
|
||||||
} else if (!page) {
|
} else if (!page) {
|
||||||
page = new TextureAtlasPage();
|
page = new TextureAtlasPage(line.trim());
|
||||||
page.name = line.trim();
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (reader.readEntry(entry, line = reader.readLine()) == 0) break;
|
if (reader.readEntry(entry, line = reader.readLine()) == 0) break;
|
||||||
let field: Function = pageFields[entry[0]];
|
let field = pageFields[entry[0]];
|
||||||
if (field) field();
|
if (field) field(page);
|
||||||
}
|
}
|
||||||
this.pages.push(page);
|
this.pages.push(page);
|
||||||
} else {
|
} else {
|
||||||
region = new TextureAtlasRegion();
|
let region = new TextureAtlasRegion(page, line);
|
||||||
|
|
||||||
region.page = page;
|
|
||||||
region.name = line;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
let count = reader.readEntry(entry, line = reader.readLine());
|
let count = reader.readEntry(entry, line = reader.readLine());
|
||||||
if (count == 0) break;
|
if (count == 0) break;
|
||||||
let field: Function = regionFields[entry[0]];
|
let field = regionFields[entry[0]];
|
||||||
if (field)
|
if (field)
|
||||||
field();
|
field(region);
|
||||||
else {
|
else {
|
||||||
if (!names) {
|
if (!names) names = [];
|
||||||
names = [];
|
if (!values) values = [];
|
||||||
values = [];
|
|
||||||
}
|
|
||||||
names.push(entry[0]);
|
names.push(entry[0]);
|
||||||
let entryValues: number[] = [];
|
let entryValues: number[] = [];
|
||||||
for (let i = 0; i < count; i++)
|
for (let i = 0; i < count; i++)
|
||||||
@ -156,7 +150,7 @@ export class TextureAtlas implements Disposable {
|
|||||||
region.originalWidth = region.width;
|
region.originalWidth = region.width;
|
||||||
region.originalHeight = region.height;
|
region.originalHeight = region.height;
|
||||||
}
|
}
|
||||||
if (names && names.length > 0) {
|
if (names && names.length > 0 && values && values.length > 0) {
|
||||||
region.names = names;
|
region.names = names;
|
||||||
region.values = values;
|
region.values = values;
|
||||||
names = null;
|
names = null;
|
||||||
@ -176,7 +170,7 @@ export class TextureAtlas implements Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findRegion (name: string): TextureAtlasRegion {
|
findRegion (name: string): TextureAtlasRegion | null {
|
||||||
for (let i = 0; i < this.regions.length; i++) {
|
for (let i = 0; i < this.regions.length; i++) {
|
||||||
if (this.regions[i].name == name) {
|
if (this.regions[i].name == name) {
|
||||||
return this.regions[i];
|
return this.regions[i];
|
||||||
@ -192,26 +186,26 @@ export class TextureAtlas implements Disposable {
|
|||||||
|
|
||||||
dispose () {
|
dispose () {
|
||||||
for (let i = 0; i < this.pages.length; i++) {
|
for (let i = 0; i < this.pages.length; i++) {
|
||||||
this.pages[i].texture.dispose();
|
this.pages[i].texture?.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextureAtlasReader {
|
class TextureAtlasReader {
|
||||||
lines: Array<string> = null;
|
lines: Array<string>;
|
||||||
index: number = 0;
|
index: number = 0;
|
||||||
|
|
||||||
constructor (text: string) {
|
constructor (text: string) {
|
||||||
this.lines = text.split(/\r\n|\r|\n/);
|
this.lines = text.split(/\r\n|\r|\n/);
|
||||||
}
|
}
|
||||||
|
|
||||||
readLine (): string {
|
readLine (): string | null {
|
||||||
if (this.index >= this.lines.length)
|
if (this.index >= this.lines.length)
|
||||||
return null;
|
return null;
|
||||||
return this.lines[this.index++];
|
return this.lines[this.index++];
|
||||||
}
|
}
|
||||||
|
|
||||||
readEntry (entry: string[], line: string): number {
|
readEntry (entry: string[], line: string | null): number {
|
||||||
if (!line) return 0;
|
if (!line) return 0;
|
||||||
line = line.trim();
|
line = line.trim();
|
||||||
if (line.length == 0) return 0;
|
if (line.length == 0) return 0;
|
||||||
@ -233,16 +227,20 @@ class TextureAtlasReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TextureAtlasPage {
|
export class TextureAtlasPage {
|
||||||
name: string = null;
|
name: string;
|
||||||
minFilter: TextureFilter = TextureFilter.Nearest;
|
minFilter: TextureFilter = TextureFilter.Nearest;
|
||||||
magFilter: TextureFilter = TextureFilter.Nearest;
|
magFilter: TextureFilter = TextureFilter.Nearest;
|
||||||
uWrap: TextureWrap = TextureWrap.ClampToEdge;
|
uWrap: TextureWrap = TextureWrap.ClampToEdge;
|
||||||
vWrap: TextureWrap = TextureWrap.ClampToEdge;
|
vWrap: TextureWrap = TextureWrap.ClampToEdge;
|
||||||
texture: Texture = null;
|
texture: Texture | null = null;
|
||||||
width: number = 0;
|
width: number = 0;
|
||||||
height: number = 0;
|
height: number = 0;
|
||||||
pma: boolean = false;
|
pma: boolean = false;
|
||||||
|
|
||||||
|
constructor (name: string) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
setTexture (texture: Texture) {
|
setTexture (texture: Texture) {
|
||||||
this.texture = texture;
|
this.texture = texture;
|
||||||
texture.setFilters(this.minFilter, this.magFilter);
|
texture.setFilters(this.minFilter, this.magFilter);
|
||||||
@ -251,8 +249,8 @@ export class TextureAtlasPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TextureAtlasRegion extends TextureRegion {
|
export class TextureAtlasRegion extends TextureRegion {
|
||||||
page: TextureAtlasPage = null;
|
page: TextureAtlasPage;
|
||||||
name: string = null;
|
name: string;
|
||||||
x: number = 0;
|
x: number = 0;
|
||||||
y: number = 0;
|
y: number = 0;
|
||||||
offsetX: number = 0;
|
offsetX: number = 0;
|
||||||
@ -261,6 +259,12 @@ export class TextureAtlasRegion extends TextureRegion {
|
|||||||
originalHeight: number = 0;
|
originalHeight: number = 0;
|
||||||
index: number = 0;
|
index: number = 0;
|
||||||
degrees: number = 0;
|
degrees: number = 0;
|
||||||
names: string[] = null;
|
names: string[] | null = null;
|
||||||
values: number[][] = null;
|
values: number[][] | null = null;
|
||||||
|
|
||||||
|
constructor (page: TextureAtlasPage, name: string) {
|
||||||
|
super();
|
||||||
|
this.page = page;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,13 +41,13 @@ import { Vector2, MathUtils } from "./Utils";
|
|||||||
export class TransformConstraint implements Updatable {
|
export class TransformConstraint implements Updatable {
|
||||||
|
|
||||||
/** The transform constraint's setup pose data. */
|
/** The transform constraint's setup pose data. */
|
||||||
data: TransformConstraintData = null;
|
data: TransformConstraintData;
|
||||||
|
|
||||||
/** The bones that will be modified by this transform constraint. */
|
/** The bones that will be modified by this transform constraint. */
|
||||||
bones: Array<Bone> = null;
|
bones: Array<Bone>;
|
||||||
|
|
||||||
/** The target bone whose world transform will be copied to the constrained bones. */
|
/** The target bone whose world transform will be copied to the constrained bones. */
|
||||||
target: Bone = null;
|
target: Bone;
|
||||||
|
|
||||||
mixRotate = 0; mixX = 0; mixY = 0; mixScaleX = 0; mixScaleY = 0; mixShearY = 0;
|
mixRotate = 0; mixX = 0; mixY = 0; mixScaleX = 0; mixScaleY = 0; mixShearY = 0;
|
||||||
|
|
||||||
@ -65,9 +65,14 @@ export class TransformConstraint implements Updatable {
|
|||||||
this.mixScaleY = data.mixScaleY;
|
this.mixScaleY = data.mixScaleY;
|
||||||
this.mixShearY = data.mixShearY;
|
this.mixShearY = data.mixShearY;
|
||||||
this.bones = new Array<Bone>();
|
this.bones = new Array<Bone>();
|
||||||
for (let i = 0; i < data.bones.length; i++)
|
for (let i = 0; i < data.bones.length; i++) {
|
||||||
this.bones.push(skeleton.findBone(data.bones[i].name));
|
let bone = skeleton.findBone(data.bones[i].name);
|
||||||
this.target = skeleton.findBone(data.target.name);
|
if (!bone) throw new Error(`Couldn't find bone ${data.bones[i].name}.`);
|
||||||
|
this.bones.push(bone);
|
||||||
|
}
|
||||||
|
let target = skeleton.findBone(data.target.name);
|
||||||
|
if (!target) throw new Error(`Couldn't find target bone ${data.target.name}.`);
|
||||||
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
isActive () {
|
isActive () {
|
||||||
|
|||||||
@ -39,7 +39,12 @@ export class TransformConstraintData extends ConstraintData {
|
|||||||
bones = new Array<BoneData>();
|
bones = new Array<BoneData>();
|
||||||
|
|
||||||
/** The target bone whose world transform will be copied to the constrained bones. */
|
/** The target bone whose world transform will be copied to the constrained bones. */
|
||||||
target: BoneData = null;
|
private _target: BoneData | null = null;
|
||||||
|
public set target (boneData: BoneData) { this._target = boneData; }
|
||||||
|
public get target () {
|
||||||
|
if (!this._target) throw new Error("BoneData not set.")
|
||||||
|
else return this._target;
|
||||||
|
}
|
||||||
|
|
||||||
mixRotate = 0;
|
mixRotate = 0;
|
||||||
mixX = 0;
|
mixX = 0;
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export interface StringMap<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class IntSet {
|
export class IntSet {
|
||||||
array = new Array<number>();
|
array = new Array<number | undefined>();
|
||||||
|
|
||||||
add (value: number): boolean {
|
add (value: number): boolean {
|
||||||
let contains = this.contains(value);
|
let contains = this.contains(value);
|
||||||
@ -354,7 +354,7 @@ export class Pool<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
obtain () {
|
obtain () {
|
||||||
return this.items.length > 0 ? this.items.pop() : this.instantiator();
|
return this.items.length > 0 ? this.items.pop()! : this.instantiator();
|
||||||
}
|
}
|
||||||
|
|
||||||
free (item: T) {
|
free (item: T) {
|
||||||
|
|||||||
@ -53,12 +53,12 @@ export abstract class VertexAttachment extends Attachment {
|
|||||||
/** The bones which affect the {@link #getVertices()}. The array entries are, for each vertex, the number of bones affecting
|
/** The bones which affect the {@link #getVertices()}. The array entries are, for each vertex, the number of bones affecting
|
||||||
* the vertex followed by that many bone indices, which is the index of the bone in {@link Skeleton#bones}. Will be null
|
* the vertex followed by that many bone indices, which is the index of the bone in {@link Skeleton#bones}. Will be null
|
||||||
* if this attachment has no weights. */
|
* if this attachment has no weights. */
|
||||||
bones: Array<number> = null;
|
bones: Array<number> | null = null;
|
||||||
|
|
||||||
/** The vertex positions in the bone's coordinate system. For a non-weighted attachment, the values are `x,y`
|
/** The vertex positions in the bone's coordinate system. For a non-weighted attachment, the values are `x,y`
|
||||||
* entries for each vertex. For a weighted attachment, the values are `x,y,weight` entries for each bone affecting
|
* entries for each vertex. For a weighted attachment, the values are `x,y,weight` entries for each bone affecting
|
||||||
* each vertex. */
|
* each vertex. */
|
||||||
vertices: NumberArrayLike = null;
|
vertices: NumberArrayLike = [];
|
||||||
|
|
||||||
/** The maximum number of world vertex values that can be output by
|
/** The maximum number of world vertex values that can be output by
|
||||||
* {@link #computeWorldVertices()} using the `count` parameter. */
|
* {@link #computeWorldVertices()} using the `count` parameter. */
|
||||||
@ -152,8 +152,7 @@ export abstract class VertexAttachment extends Attachment {
|
|||||||
if (this.vertices) {
|
if (this.vertices) {
|
||||||
attachment.vertices = Utils.newFloatArray(this.vertices.length);
|
attachment.vertices = Utils.newFloatArray(this.vertices.length);
|
||||||
Utils.arrayCopy(this.vertices, 0, attachment.vertices, 0, this.vertices.length);
|
Utils.arrayCopy(this.vertices, 0, attachment.vertices, 0, this.vertices.length);
|
||||||
} else
|
}
|
||||||
attachment.vertices = null;
|
|
||||||
|
|
||||||
attachment.worldVerticesLength = this.worldVerticesLength;
|
attachment.worldVerticesLength = this.worldVerticesLength;
|
||||||
attachment.timelineAttahment = this.timelineAttahment;
|
attachment.timelineAttahment = this.timelineAttahment;
|
||||||
|
|||||||
@ -42,10 +42,10 @@ import { Sequence } from "./Sequence";
|
|||||||
* Runtimes Guide. */
|
* Runtimes Guide. */
|
||||||
export interface AttachmentLoader {
|
export interface AttachmentLoader {
|
||||||
/** @return May be null to not load an attachment. */
|
/** @return May be null to not load an attachment. */
|
||||||
newRegionAttachment (skin: Skin, name: string, path: string, sequence: Sequence): RegionAttachment;
|
newRegionAttachment (skin: Skin, name: string, path: string, sequence: Sequence | null): RegionAttachment;
|
||||||
|
|
||||||
/** @return May be null to not load an attachment. */
|
/** @return May be null to not load an attachment. */
|
||||||
newMeshAttachment (skin: Skin, name: string, path: string, sequence: Sequence): MeshAttachment;
|
newMeshAttachment (skin: Skin, name: string, path: string, sequence: Sequence | null): MeshAttachment;
|
||||||
|
|
||||||
/** @return May be null to not load an attachment. */
|
/** @return May be null to not load an attachment. */
|
||||||
newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment;
|
newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment;
|
||||||
|
|||||||
@ -35,7 +35,7 @@ import { VertexAttachment, Attachment } from "./Attachment";
|
|||||||
export class ClippingAttachment extends VertexAttachment {
|
export class ClippingAttachment extends VertexAttachment {
|
||||||
/** Clipping is performed between the clipping polygon's slot and the end slot. Returns null if clipping is done until the end of
|
/** Clipping is performed between the clipping polygon's slot and the end slot. Returns null if clipping is done until the end of
|
||||||
* the skeleton's rendering. */
|
* the skeleton's rendering. */
|
||||||
endSlot: SlotData = null;
|
endSlot: SlotData | null = null;
|
||||||
|
|
||||||
// Nonessential.
|
// Nonessential.
|
||||||
/** The color of the clipping polygon as it was in Spine. Available only when nonessential data was exported. Clipping polygons
|
/** The color of the clipping polygon as it was in Spine. Available only when nonessential data was exported. Clipping polygons
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export interface HasTextureRegion {
|
|||||||
|
|
||||||
/** The region used to draw the attachment. After setting the region or if the region's properties are changed,
|
/** The region used to draw the attachment. After setting the region or if the region's properties are changed,
|
||||||
* {@link #updateRegion()} must be called. */
|
* {@link #updateRegion()} must be called. */
|
||||||
region: TextureRegion;
|
region: TextureRegion | null;
|
||||||
|
|
||||||
/** Updates any values the attachment calculates using the {@link #getRegion()}. Must be called after setting the
|
/** Updates any values the attachment calculates using the {@link #getRegion()}. Must be called after setting the
|
||||||
* {@link #getRegion()} or if the region's properties are changed. */
|
* {@link #getRegion()} or if the region's properties are changed. */
|
||||||
@ -46,5 +46,5 @@ export interface HasTextureRegion {
|
|||||||
/** The color to tint the attachment. */
|
/** The color to tint the attachment. */
|
||||||
color: Color;
|
color: Color;
|
||||||
|
|
||||||
sequence: Sequence;
|
sequence: Sequence | null;
|
||||||
}
|
}
|
||||||
@ -40,21 +40,21 @@ import { Slot } from "../Slot";
|
|||||||
*
|
*
|
||||||
* See [Mesh attachments](http://esotericsoftware.com/spine-meshes) in the Spine User Guide. */
|
* See [Mesh attachments](http://esotericsoftware.com/spine-meshes) in the Spine User Guide. */
|
||||||
export class MeshAttachment extends VertexAttachment implements HasTextureRegion {
|
export class MeshAttachment extends VertexAttachment implements HasTextureRegion {
|
||||||
region: TextureRegion = null;
|
region: TextureRegion | null = null;
|
||||||
|
|
||||||
/** The name of the texture region for this attachment. */
|
/** The name of the texture region for this attachment. */
|
||||||
path: string = null;
|
path: string;
|
||||||
|
|
||||||
/** The UV pair for each vertex, normalized within the texture region. */
|
/** The UV pair for each vertex, normalized within the texture region. */
|
||||||
regionUVs: NumberArrayLike = null;
|
regionUVs: NumberArrayLike = [];
|
||||||
|
|
||||||
/** The UV pair for each vertex, normalized within the entire texture.
|
/** The UV pair for each vertex, normalized within the entire texture.
|
||||||
*
|
*
|
||||||
* See {@link #updateUVs}. */
|
* See {@link #updateUVs}. */
|
||||||
uvs: NumberArrayLike = null;
|
uvs: NumberArrayLike = [];
|
||||||
|
|
||||||
/** Triplets of vertex indices which describe the mesh's triangulation. */
|
/** Triplets of vertex indices which describe the mesh's triangulation. */
|
||||||
triangles: Array<number> = null;
|
triangles: Array<number> = [];
|
||||||
|
|
||||||
/** The color to tint the mesh. */
|
/** The color to tint the mesh. */
|
||||||
color = new Color(1, 1, 1, 1);
|
color = new Color(1, 1, 1, 1);
|
||||||
@ -70,28 +70,30 @@ export class MeshAttachment extends VertexAttachment implements HasTextureRegion
|
|||||||
|
|
||||||
/** Vertex index pairs describing edges for controling triangulation. Mesh triangles will never cross edges. Only available if
|
/** Vertex index pairs describing edges for controling triangulation. Mesh triangles will never cross edges. Only available if
|
||||||
* nonessential data was exported. Triangulation is not performed at runtime. */
|
* nonessential data was exported. Triangulation is not performed at runtime. */
|
||||||
edges: Array<number> = null;
|
edges: Array<number> = [];
|
||||||
|
|
||||||
private parentMesh: MeshAttachment = null;
|
private parentMesh: MeshAttachment | null = null;
|
||||||
|
|
||||||
sequence: Sequence = null;
|
sequence: Sequence | null = null;
|
||||||
|
|
||||||
tempColor = new Color(0, 0, 0, 0);
|
tempColor = new Color(0, 0, 0, 0);
|
||||||
|
|
||||||
constructor (name: string) {
|
constructor (name: string, path: string) {
|
||||||
super(name);
|
super(name);
|
||||||
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Calculates {@link #uvs} using the {@link #regionUVs} and region. Must be called if the region, the region's properties, or
|
/** Calculates {@link #uvs} using the {@link #regionUVs} and region. Must be called if the region, the region's properties, or
|
||||||
* the {@link #regionUVs} are changed. */
|
* the {@link #regionUVs} are changed. */
|
||||||
updateRegion () {
|
updateRegion () {
|
||||||
|
if (!this.region) throw new Error("Region not set.");
|
||||||
let regionUVs = this.regionUVs;
|
let regionUVs = this.regionUVs;
|
||||||
if (!this.uvs || this.uvs.length != regionUVs.length) this.uvs = Utils.newFloatArray(regionUVs.length);
|
if (!this.uvs || this.uvs.length != regionUVs.length) this.uvs = Utils.newFloatArray(regionUVs.length);
|
||||||
let uvs = this.uvs;
|
let uvs = this.uvs;
|
||||||
let n = this.uvs.length;
|
let n = this.uvs.length;
|
||||||
let u = this.region.u, v = this.region.v, width = 0, height = 0;
|
let u = this.region.u, v = this.region.v, width = 0, height = 0;
|
||||||
if (this.region instanceof TextureAtlasRegion) {
|
if (this.region instanceof TextureAtlasRegion) {
|
||||||
let region = this.region, image = region.page.texture.getImage();
|
let region = this.region, image = region.page!.texture!.getImage();
|
||||||
let textureWidth = image.width, textureHeight = image.height;
|
let textureWidth = image.width, textureHeight = image.height;
|
||||||
switch (region.degrees) {
|
switch (region.degrees) {
|
||||||
case 90:
|
case 90:
|
||||||
@ -167,9 +169,8 @@ export class MeshAttachment extends VertexAttachment implements HasTextureRegion
|
|||||||
copy (): Attachment {
|
copy (): Attachment {
|
||||||
if (this.parentMesh) return this.newLinkedMesh();
|
if (this.parentMesh) return this.newLinkedMesh();
|
||||||
|
|
||||||
let copy = new MeshAttachment(this.name);
|
let copy = new MeshAttachment(this.name, this.path);
|
||||||
copy.region = this.region;
|
copy.region = this.region;
|
||||||
copy.path = this.path;
|
|
||||||
copy.color.setFromColor(this.color);
|
copy.color.setFromColor(this.color);
|
||||||
|
|
||||||
this.copyTo(copy);
|
this.copyTo(copy);
|
||||||
@ -201,9 +202,8 @@ export class MeshAttachment extends VertexAttachment implements HasTextureRegion
|
|||||||
|
|
||||||
/** Returns a new mesh with the {@link #parentMesh} set to this mesh's parent mesh, if any, else to this mesh. **/
|
/** Returns a new mesh with the {@link #parentMesh} set to this mesh's parent mesh, if any, else to this mesh. **/
|
||||||
newLinkedMesh (): MeshAttachment {
|
newLinkedMesh (): MeshAttachment {
|
||||||
let copy = new MeshAttachment(this.name);
|
let copy = new MeshAttachment(this.name, this.path);
|
||||||
copy.region = this.region;
|
copy.region = this.region;
|
||||||
copy.path = this.path;
|
|
||||||
copy.color.setFromColor(this.color);
|
copy.color.setFromColor(this.color);
|
||||||
copy.timelineAttahment = this.timelineAttahment;
|
copy.timelineAttahment = this.timelineAttahment;
|
||||||
copy.setParentMesh(this.parentMesh ? this.parentMesh : this);
|
copy.setParentMesh(this.parentMesh ? this.parentMesh : this);
|
||||||
|
|||||||
@ -36,7 +36,7 @@ import { VertexAttachment, Attachment } from "./Attachment";
|
|||||||
export class PathAttachment extends VertexAttachment {
|
export class PathAttachment extends VertexAttachment {
|
||||||
|
|
||||||
/** The lengths along the path in the setup pose from the start of the path to the end of each Bezier curve. */
|
/** The lengths along the path in the setup pose from the start of the path to the end of each Bezier curve. */
|
||||||
lengths: Array<number> = null;
|
lengths: Array<number> = [];
|
||||||
|
|
||||||
/** If true, the start and end knots are connected. */
|
/** If true, the start and end knots are connected. */
|
||||||
closed = false;
|
closed = false;
|
||||||
|
|||||||
@ -64,11 +64,11 @@ export class RegionAttachment extends Attachment implements HasTextureRegion {
|
|||||||
color = new Color(1, 1, 1, 1);
|
color = new Color(1, 1, 1, 1);
|
||||||
|
|
||||||
/** The name of the texture region for this attachment. */
|
/** The name of the texture region for this attachment. */
|
||||||
path: string = null;
|
path: string;
|
||||||
|
|
||||||
private rendererObject: any = null;
|
private rendererObject: any = null;
|
||||||
region: TextureRegion = null;
|
region: TextureRegion | null = null;
|
||||||
sequence: Sequence = null;
|
sequence: Sequence | null = null;
|
||||||
|
|
||||||
/** For each of the 4 vertices, a pair of <code>x,y</code> values that is the local position of the vertex.
|
/** For each of the 4 vertices, a pair of <code>x,y</code> values that is the local position of the vertex.
|
||||||
*
|
*
|
||||||
@ -79,12 +79,14 @@ export class RegionAttachment extends Attachment implements HasTextureRegion {
|
|||||||
|
|
||||||
tempColor = new Color(1, 1, 1, 1);
|
tempColor = new Color(1, 1, 1, 1);
|
||||||
|
|
||||||
constructor (name: string) {
|
constructor (name: string, path: string) {
|
||||||
super(name);
|
super(name);
|
||||||
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Calculates the {@link #offset} using the region settings. Must be called after changing region settings. */
|
/** Calculates the {@link #offset} using the region settings. Must be called after changing region settings. */
|
||||||
updateRegion (): void {
|
updateRegion (): void {
|
||||||
|
if (!this.region) throw new Error("Region not set.");
|
||||||
let region = this.region;
|
let region = this.region;
|
||||||
let regionScaleX = this.width / this.region.originalWidth * this.scaleX;
|
let regionScaleX = this.width / this.region.originalWidth * this.scaleX;
|
||||||
let regionScaleY = this.height / this.region.originalHeight * this.scaleY;
|
let regionScaleY = this.height / this.region.originalHeight * this.scaleY;
|
||||||
@ -179,10 +181,9 @@ export class RegionAttachment extends Attachment implements HasTextureRegion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
copy (): Attachment {
|
copy (): Attachment {
|
||||||
let copy = new RegionAttachment(this.name);
|
let copy = new RegionAttachment(this.name, this.path);
|
||||||
copy.region = this.region;
|
copy.region = this.region;
|
||||||
copy.rendererObject = this.rendererObject;
|
copy.rendererObject = this.rendererObject;
|
||||||
copy.path = this.path;
|
|
||||||
copy.x = this.x;
|
copy.x = this.x;
|
||||||
copy.y = this.y;
|
copy.y = this.y;
|
||||||
copy.scaleX = this.scaleX;
|
copy.scaleX = this.scaleX;
|
||||||
|
|||||||
@ -31,49 +31,49 @@ import { Animation, AnimationState, AnimationStateData, AtlasAttachmentLoader, B
|
|||||||
import { AssetManager, GLTexture, Input, LoadingScreen, ManagedWebGLRenderingContext, ResizeMode, SceneRenderer, Vector3 } from "@esotericsoftware/spine-webgl"
|
import { AssetManager, GLTexture, Input, LoadingScreen, ManagedWebGLRenderingContext, ResizeMode, SceneRenderer, Vector3 } from "@esotericsoftware/spine-webgl"
|
||||||
|
|
||||||
export interface SpinePlayerConfig {
|
export interface SpinePlayerConfig {
|
||||||
/* The URL of the skeleton JSON file (.json). */
|
/* The URL of the skeleton JSON file (.json). Undefined if binaryUrl is given. */
|
||||||
jsonUrl: string
|
jsonUrl?: string
|
||||||
|
|
||||||
/* Optional: The name of a field in the JSON that holds the skeleton data. Default: none */
|
/* Optional: The name of a field in the JSON that holds the skeleton data. Default: none */
|
||||||
jsonField: string
|
jsonField?: string
|
||||||
|
|
||||||
/* The URL of the skeleton binary file (.skel). */
|
/* The URL of the skeleton binary file (.skel). Undefined if jsonUrl is given. */
|
||||||
binaryUrl: string
|
binaryUrl?: string
|
||||||
|
|
||||||
/* The URL of the skeleton atlas file (.atlas). Atlas page images are automatically resolved. */
|
/* The URL of the skeleton atlas file (.atlas). Atlas page images are automatically resolved. */
|
||||||
atlasUrl: string
|
atlasUrl?: string
|
||||||
|
|
||||||
/* Raw data URIs, mapping a path to base64 encoded raw data. When player's asset manager resolves the jsonUrl, binaryUrl,
|
/* Raw data URIs, mapping a path to base64 encoded raw data. When player's asset manager resolves the jsonUrl, binaryUrl,
|
||||||
atlasUrl, or the image paths referenced in the atlas, it will first look for that path in the raw data URIs. This
|
atlasUrl, or the image paths referenced in the atlas, it will first look for that path in the raw data URIs. This
|
||||||
allows embedding assets directly in HTML/JS. Default: none */
|
allows embedding assets directly in HTML/JS. Default: none */
|
||||||
rawDataURIs: StringMap<string>
|
rawDataURIs?: StringMap<string>
|
||||||
|
|
||||||
/* Optional: The name of the animation to be played. Default: empty animation */
|
/* Optional: The name of the animation to be played. Default: empty animation */
|
||||||
animation: string
|
animation?: string
|
||||||
|
|
||||||
/* Optional: List of animation names from which the user can choose. Default: all animations */
|
/* Optional: List of animation names from which the user can choose. Default: all animations */
|
||||||
animations: string[]
|
animations?: string[]
|
||||||
|
|
||||||
/* Optional: The default mix time used to switch between two animations. Default: 0.25 */
|
/* Optional: The default mix time used to switch between two animations. Default: 0.25 */
|
||||||
defaultMix: number
|
defaultMix?: number
|
||||||
|
|
||||||
/* Optional: The name of the skin to be set. Default: the default skin */
|
/* Optional: The name of the skin to be set. Default: the default skin */
|
||||||
skin: string
|
skin?: string
|
||||||
|
|
||||||
/* Optional: List of skin names from which the user can choose. Default: all skins */
|
/* Optional: List of skin names from which the user can choose. Default: all skins */
|
||||||
skins: string[]
|
skins?: string[]
|
||||||
|
|
||||||
/* Optional: Whether the skeleton's atlas images use premultiplied alpha. Default: true */
|
/* Optional: Whether the skeleton's atlas images use premultiplied alpha. Default: true */
|
||||||
premultipliedAlpha: boolean
|
premultipliedAlpha?: boolean
|
||||||
|
|
||||||
/* Optional: Whether to show the player controls. When false, no external CSS file is needed. Default: true */
|
/* Optional: Whether to show the player controls. When false, no external CSS file is needed. Default: true */
|
||||||
showControls: boolean
|
showControls?: boolean
|
||||||
|
|
||||||
/* Optional: Whether to show the loading animation. Default: true */
|
/* Optional: Whether to show the loading animation. Default: true */
|
||||||
showLoading: boolean
|
showLoading?: boolean
|
||||||
|
|
||||||
/* Optional: Which debugging visualizations are shown. Default: none */
|
/* Optional: Which debugging visualizations are shown. Default: none */
|
||||||
debug: {
|
debug?: {
|
||||||
bones: boolean
|
bones: boolean
|
||||||
regions: boolean
|
regions: boolean
|
||||||
meshes: boolean
|
meshes: boolean
|
||||||
@ -86,81 +86,81 @@ export interface SpinePlayerConfig {
|
|||||||
|
|
||||||
/* Optional: The position and size of the viewport in the skeleton's world coordinates. Default: the bounding box that fits
|
/* Optional: The position and size of the viewport in the skeleton's world coordinates. Default: the bounding box that fits
|
||||||
the current animation, 10% padding, 0.25 transition time */
|
the current animation, 10% padding, 0.25 transition time */
|
||||||
viewport: {
|
viewport?: {
|
||||||
/* Optional: The position and size of the viewport in the skeleton's world coordinates. Default: the bounding box that
|
/* Optional: The position and size of the viewport in the skeleton's world coordinates. Default: the bounding box that
|
||||||
fits the current animation */
|
fits the current animation */
|
||||||
x: number
|
x?: number
|
||||||
y: number
|
y?: number
|
||||||
width: number
|
width?: number
|
||||||
height: number
|
height?: number
|
||||||
|
|
||||||
/* Optional: Padding around the viewport size, given as a number or percentage (eg "25%"). Default: 10% */
|
/* Optional: Padding around the viewport size, given as a number or percentage (eg "25%"). Default: 10% */
|
||||||
padLeft: string | number
|
padLeft?: string | number
|
||||||
padRight: string | number
|
padRight?: string | number
|
||||||
padTop: string | number
|
padTop?: string | number
|
||||||
padBottom: string | number
|
padBottom?: string | number
|
||||||
|
|
||||||
/* Optional: Whether to draw lines showing the viewport bounds. Default: false */
|
/* Optional: Whether to draw lines showing the viewport bounds. Default: false */
|
||||||
debugRender: boolean,
|
debugRender?: boolean,
|
||||||
|
|
||||||
/* Optional: When the current viewport changes, the time to animate to the new viewport. Default: 0.25 */
|
/* Optional: When the current viewport changes, the time to animate to the new viewport. Default: 0.25 */
|
||||||
transitionTime: number
|
transitionTime?: number
|
||||||
|
|
||||||
/* Optional: Viewports for specific animations. Default: none */
|
/* Optional: Viewports for specific animations. Default: none */
|
||||||
animations: StringMap<Viewport>
|
animations?: StringMap<Viewport>
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optional: Whether the canvas is transparent, allowing the web page behind the canvas to show through when
|
/* Optional: Whether the canvas is transparent, allowing the web page behind the canvas to show through when
|
||||||
backgroundColor alpha is < ff. Default: false */
|
backgroundColor alpha is < ff. Default: false */
|
||||||
alpha: boolean
|
alpha?: boolean
|
||||||
|
|
||||||
/* Optional: The canvas background color, given in the format #rrggbb or #rrggbbaa. Default: #000000ff (black) or when
|
/* Optional: The canvas background color, given in the format #rrggbb or #rrggbbaa. Default: #000000ff (black) or when
|
||||||
alpha is true #00000000 (transparent) */
|
alpha is true #00000000 (transparent) */
|
||||||
backgroundColor: string
|
backgroundColor?: string
|
||||||
|
|
||||||
/* Optional: The background color used in fullscreen mode, given in the format #rrggbb or #rrggbbaa. Default: backgroundColor */
|
/* Optional: The background color used in fullscreen mode, given in the format #rrggbb or #rrggbbaa. Default: backgroundColor */
|
||||||
fullScreenBackgroundColor: string
|
fullScreenBackgroundColor?: string
|
||||||
|
|
||||||
/* Optional: An image to draw behind the skeleton. Default: none */
|
/* Optional: An image to draw behind the skeleton. Default: none */
|
||||||
backgroundImage: {
|
backgroundImage?: {
|
||||||
url: string
|
url: string
|
||||||
|
|
||||||
/* Optional: The position and size of the background image in the skeleton's world coordinates. Default: fills the viewport */
|
/* Optional: The position and size of the background image in the skeleton's world coordinates. Default: fills the viewport */
|
||||||
x: number
|
x?: number
|
||||||
y: number
|
y?: number
|
||||||
width: number
|
width?: number
|
||||||
height: number
|
height?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optional: Whether mipmapping and anisotropic filtering are used for highest quality scaling when available, otherwise the
|
/* Optional: Whether mipmapping and anisotropic filtering are used for highest quality scaling when available, otherwise the
|
||||||
filter settings from the texture atlas are used. Default: true */
|
filter settings from the texture atlas are used. Default: true */
|
||||||
mipmaps: true
|
mipmaps?: boolean
|
||||||
|
|
||||||
/* Optional: List of bone names that the user can drag to position. Default: none */
|
/* Optional: List of bone names that the user can drag to position. Default: none */
|
||||||
controlBones: string[]
|
controlBones?: string[]
|
||||||
|
|
||||||
/* Optional: Callback when the skeleton and its assets have been successfully loaded. If an animation is set on track 0,
|
/* Optional: Callback when the skeleton and its assets have been successfully loaded. If an animation is set on track 0,
|
||||||
the player won't set its own animation. Default: none */
|
the player won't set its own animation. Default: none */
|
||||||
success: (player: SpinePlayer) => void
|
success?: (player: SpinePlayer) => void
|
||||||
|
|
||||||
/* Optional: Callback when the skeleton could not be loaded or rendered. Default: none */
|
/* Optional: Callback when the skeleton could not be loaded or rendered. Default: none */
|
||||||
error: (player: SpinePlayer, msg: string) => void
|
error?: (player: SpinePlayer, msg: string) => void
|
||||||
|
|
||||||
/* Optional: Callback at the start of each frame, before the skeleton is posed or drawn. Default: none */
|
/* Optional: Callback at the start of each frame, before the skeleton is posed or drawn. Default: none */
|
||||||
frame: (player: SpinePlayer, delta: number) => void
|
frame?: (player: SpinePlayer, delta: number) => void
|
||||||
|
|
||||||
/* Optional: Callback after the skeleton is posed each frame, before it is drawn. Default: none */
|
/* Optional: Callback after the skeleton is posed each frame, before it is drawn. Default: none */
|
||||||
update: (player: SpinePlayer, delta: number) => void
|
update?: (player: SpinePlayer, delta: number) => void
|
||||||
|
|
||||||
/* Optional: Callback after the skeleton is drawn each frame. Default: none */
|
/* Optional: Callback after the skeleton is drawn each frame. Default: none */
|
||||||
draw: (player: SpinePlayer, delta: number) => void
|
draw?: (player: SpinePlayer, delta: number) => void
|
||||||
|
|
||||||
/* Optional: Callback each frame before the skeleton is loaded. Default: none */
|
/* Optional: Callback each frame before the skeleton is loaded. Default: none */
|
||||||
loading: (player: SpinePlayer, delta: number) => void
|
loading?: (player: SpinePlayer, delta: number) => void
|
||||||
|
|
||||||
/* Optional: The downloader used by the player's asset manager. Passing the same downloader to multiple players using the
|
/* Optional: The downloader used by the player's asset manager. Passing the same downloader to multiple players using the
|
||||||
same assets ensures the assets are only downloaded once. Default: new instance */
|
same assets ensures the assets are only downloaded once. Default: new instance */
|
||||||
downloader: Downloader
|
downloader?: Downloader
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Viewport {
|
export interface Viewport {
|
||||||
@ -181,31 +181,31 @@ export interface Viewport {
|
|||||||
export class SpinePlayer implements Disposable {
|
export class SpinePlayer implements Disposable {
|
||||||
public parent: HTMLElement;
|
public parent: HTMLElement;
|
||||||
public dom: HTMLElement;
|
public dom: HTMLElement;
|
||||||
public canvas: HTMLCanvasElement;
|
public canvas: HTMLCanvasElement | null = null;
|
||||||
public context: ManagedWebGLRenderingContext;
|
public context: ManagedWebGLRenderingContext | null = null;
|
||||||
public sceneRenderer: SceneRenderer;
|
public sceneRenderer: SceneRenderer | null = null;
|
||||||
public loadingScreen: LoadingScreen;
|
public loadingScreen: LoadingScreen | null = null;
|
||||||
public assetManager: AssetManager;
|
public assetManager: AssetManager | null = null;
|
||||||
public bg = new Color();
|
public bg = new Color();
|
||||||
public bgFullscreen = new Color();
|
public bgFullscreen = new Color();
|
||||||
|
|
||||||
private playerControls: HTMLElement;
|
private playerControls: HTMLElement | null = null;
|
||||||
private timelineSlider: Slider;
|
private timelineSlider: Slider | null = null;
|
||||||
private playButton: HTMLElement;
|
private playButton: HTMLElement | null = null;
|
||||||
private skinButton: HTMLElement;
|
private skinButton: HTMLElement | null = null;
|
||||||
private animationButton: HTMLElement;
|
private animationButton: HTMLElement | null = null;
|
||||||
|
|
||||||
private playTime = 0;
|
private playTime = 0;
|
||||||
private selectedBones: Bone[];
|
private selectedBones: (Bone | null)[] = [];
|
||||||
private cancelId = 0;
|
private cancelId = 0;
|
||||||
popup: Popup;
|
popup: Popup | null = null;
|
||||||
|
|
||||||
/* True if the player is unable to load or render the skeleton. */
|
/* True if the player is unable to load or render the skeleton. */
|
||||||
public error: boolean;
|
public error: boolean = false;
|
||||||
/* The player's skeleton. Null until loading is complete (access after config.success). */
|
/* The player's skeleton. Null until loading is complete (access after config.success). */
|
||||||
public skeleton: Skeleton;
|
public skeleton: Skeleton | null = null;
|
||||||
/* The animation state controlling the skeleton. Null until loading is complete (access after config.success). */
|
/* The animation state controlling the skeleton. Null until loading is complete (access after config.success). */
|
||||||
public animationState: AnimationState;
|
public animationState: AnimationState | null = null;
|
||||||
|
|
||||||
public paused = true;
|
public paused = true;
|
||||||
public speed = 1;
|
public speed = 1;
|
||||||
@ -214,14 +214,15 @@ export class SpinePlayer implements Disposable {
|
|||||||
private disposed = false;
|
private disposed = false;
|
||||||
|
|
||||||
private viewport: Viewport = {} as Viewport;
|
private viewport: Viewport = {} as Viewport;
|
||||||
private currentViewport: Viewport;
|
private currentViewport: Viewport = {} as Viewport;
|
||||||
private previousViewport: Viewport;
|
private previousViewport: Viewport = {} as Viewport;
|
||||||
private viewportTransitionStart = 0;
|
private viewportTransitionStart = 0;
|
||||||
private eventListeners: Array<{ target: any, event: any, func: any }> = [];
|
private eventListeners: Array<{ target: any, event: any, func: any }> = [];
|
||||||
|
|
||||||
constructor (parent: HTMLElement | string, private config: SpinePlayerConfig) {
|
constructor (parent: HTMLElement | string, private config: SpinePlayerConfig) {
|
||||||
this.parent = typeof parent === "string" ? document.getElementById(parent) : parent;
|
let parentDom = typeof parent === "string" ? document.getElementById(parent) : parent;
|
||||||
if (!this.parent) throw new Error("SpinePlayer parent not found: " + parent);
|
if (parentDom == null) throw new Error("SpinePlayer parent not found: " + parent);
|
||||||
|
this.parent = parentDom;
|
||||||
|
|
||||||
if (config.showControls === void 0) config.showControls = true;
|
if (config.showControls === void 0) config.showControls = true;
|
||||||
let controls = config.showControls ? /*html*/`
|
let controls = config.showControls ? /*html*/`
|
||||||
@ -244,7 +245,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
try {
|
try {
|
||||||
this.validateConfig(config);
|
this.validateConfig(config);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError(e.message, e);
|
this.showError((e as any).message, e as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initialize();
|
this.initialize();
|
||||||
@ -257,9 +258,9 @@ export class SpinePlayer implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispose (): void {
|
dispose (): void {
|
||||||
this.sceneRenderer.dispose();
|
this.sceneRenderer?.dispose();
|
||||||
if (this.loadingScreen) this.loadingScreen.dispose();
|
this.loadingScreen?.dispose();
|
||||||
this.assetManager.dispose();
|
this.assetManager?.dispose();
|
||||||
for (var i = 0; i < this.eventListeners.length; i++) {
|
for (var i = 0; i < this.eventListeners.length; i++) {
|
||||||
var eventListener = this.eventListeners[i];
|
var eventListener = this.eventListeners[i];
|
||||||
eventListener.target.removeEventListener(eventListener.event, eventListener.func);
|
eventListener.target.removeEventListener(eventListener.event, eventListener.func);
|
||||||
@ -280,29 +281,38 @@ export class SpinePlayer implements Disposable {
|
|||||||
if (!config.atlasUrl) throw new Error("A URL must be specified for the atlas file.");
|
if (!config.atlasUrl) throw new Error("A URL must be specified for the atlas file.");
|
||||||
if (!config.backgroundColor) config.backgroundColor = config.alpha ? "00000000" : "000000";
|
if (!config.backgroundColor) config.backgroundColor = config.alpha ? "00000000" : "000000";
|
||||||
if (!config.fullScreenBackgroundColor) config.fullScreenBackgroundColor = config.backgroundColor;
|
if (!config.fullScreenBackgroundColor) config.fullScreenBackgroundColor = config.backgroundColor;
|
||||||
if (config.backgroundImage && !config.backgroundImage.url) config.backgroundImage = null;
|
if (config.backgroundImage && !config.backgroundImage.url) config.backgroundImage = undefined;
|
||||||
if (config.premultipliedAlpha === void 0) config.premultipliedAlpha = true;
|
if (config.premultipliedAlpha === void 0) config.premultipliedAlpha = true;
|
||||||
if (config.mipmaps === void 0) config.mipmaps = true;
|
if (config.mipmaps === void 0) config.mipmaps = true;
|
||||||
if (!config.debug) config.debug = {} as any;
|
if (!config.debug) config.debug = {
|
||||||
|
bones: false,
|
||||||
|
clipping: false,
|
||||||
|
bounds: false,
|
||||||
|
hulls: false,
|
||||||
|
meshes: false,
|
||||||
|
paths: false,
|
||||||
|
points: false,
|
||||||
|
regions: false
|
||||||
|
};
|
||||||
if (config.animations && config.animation && config.animations.indexOf(config.animation) < 0)
|
if (config.animations && config.animation && config.animations.indexOf(config.animation) < 0)
|
||||||
throw new Error("Animation '" + config.animation + "' is not in the config animation list: " + toString(config.animations));
|
throw new Error("Animation '" + config.animation + "' is not in the config animation list: " + toString(config.animations));
|
||||||
if (config.skins && config.skin && config.skins.indexOf(config.skin) < 0)
|
if (config.skins && config.skin && config.skins.indexOf(config.skin) < 0)
|
||||||
throw new Error("Default skin '" + config.skin + "' is not in the config skins list: " + toString(config.skins));
|
throw new Error("Default skin '" + config.skin + "' is not in the config skins list: " + toString(config.skins));
|
||||||
if (!config.viewport) config.viewport = {} as any;
|
if (!config.viewport) config.viewport = {} as any;
|
||||||
if (!config.viewport.animations) config.viewport.animations = {};
|
if (!config.viewport!.animations) config.viewport!.animations = {};
|
||||||
if (config.viewport.debugRender === void 0) config.viewport.debugRender = false;
|
if (config.viewport!.debugRender === void 0) config.viewport!.debugRender = false;
|
||||||
if (config.viewport.transitionTime === void 0) config.viewport.transitionTime = 0.25;
|
if (config.viewport!.transitionTime === void 0) config.viewport!.transitionTime = 0.25;
|
||||||
if (!config.controlBones) config.controlBones = [];
|
if (!config.controlBones) config.controlBones = [];
|
||||||
if (config.showLoading === void 0) config.showLoading = true;
|
if (config.showLoading === void 0) config.showLoading = true;
|
||||||
if (config.defaultMix === void 0) config.defaultMix = 0.25;
|
if (config.defaultMix === void 0) config.defaultMix = 0.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
private initialize (): HTMLElement {
|
private initialize (): HTMLElement | null {
|
||||||
let config = this.config;
|
let config = this.config;
|
||||||
let dom = this.dom;
|
let dom = this.dom;
|
||||||
|
|
||||||
if (!config.alpha) { // Prevents a flash before the first frame is drawn.
|
if (!config.alpha) { // Prevents a flash before the first frame is drawn.
|
||||||
let hex = config.backgroundColor;
|
let hex = config.backgroundColor!;
|
||||||
this.dom.style.backgroundColor = (hex.charAt(0) == '#' ? hex : "#" + hex).substr(0, 7);
|
this.dom.style.backgroundColor = (hex.charAt(0) == '#' ? hex : "#" + hex).substr(0, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +325,8 @@ export class SpinePlayer implements Disposable {
|
|||||||
this.sceneRenderer = new SceneRenderer(this.canvas, this.context, true);
|
this.sceneRenderer = new SceneRenderer(this.canvas, this.context, true);
|
||||||
if (config.showLoading) this.loadingScreen = new LoadingScreen(this.sceneRenderer);
|
if (config.showLoading) this.loadingScreen = new LoadingScreen(this.sceneRenderer);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError("Sorry, your browser does not support \nPlease use the latest version of Firefox, Chrome, Edge, or Safari.", e);
|
this.showError("Sorry, your browser does not support \nPlease use the latest version of Firefox, Chrome, Edge, or Safari.", e as any);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the assets.
|
// Load the assets.
|
||||||
@ -327,13 +338,13 @@ export class SpinePlayer implements Disposable {
|
|||||||
if (config.jsonUrl)
|
if (config.jsonUrl)
|
||||||
this.assetManager.loadJson(config.jsonUrl);
|
this.assetManager.loadJson(config.jsonUrl);
|
||||||
else
|
else
|
||||||
this.assetManager.loadBinary(config.binaryUrl);
|
this.assetManager.loadBinary(config.binaryUrl!);
|
||||||
this.assetManager.loadTextureAtlas(config.atlasUrl);
|
this.assetManager.loadTextureAtlas(config.atlasUrl!);
|
||||||
if (config.backgroundImage) this.assetManager.loadTexture(config.backgroundImage.url);
|
if (config.backgroundImage) this.assetManager.loadTexture(config.backgroundImage.url);
|
||||||
|
|
||||||
// Setup the UI elements.
|
// Setup the UI elements.
|
||||||
this.bg.setFromString(config.backgroundColor);
|
this.bg.setFromString(config.backgroundColor!);
|
||||||
this.bgFullscreen.setFromString(config.fullScreenBackgroundColor);
|
this.bgFullscreen.setFromString(config.fullScreenBackgroundColor!);
|
||||||
if (config.showControls) {
|
if (config.showControls) {
|
||||||
this.playerControls = dom.children[1] as HTMLElement;
|
this.playerControls = dom.children[1] as HTMLElement;
|
||||||
let controls = this.playerControls.children;
|
let controls = this.playerControls.children;
|
||||||
@ -351,18 +362,18 @@ export class SpinePlayer implements Disposable {
|
|||||||
timeline.appendChild(this.timelineSlider.create());
|
timeline.appendChild(this.timelineSlider.create());
|
||||||
this.timelineSlider.change = (percentage) => {
|
this.timelineSlider.change = (percentage) => {
|
||||||
this.pause();
|
this.pause();
|
||||||
let animationDuration = this.animationState.getCurrent(0).animation.duration;
|
let animationDuration = this.animationState!.getCurrent(0)!.animation!.duration;
|
||||||
let time = animationDuration * percentage;
|
let time = animationDuration * percentage;
|
||||||
this.animationState.update(time - this.playTime);
|
this.animationState!.update(time - this.playTime);
|
||||||
this.animationState.apply(this.skeleton);
|
this.animationState!.apply(this.skeleton!);
|
||||||
this.skeleton.updateWorldTransform();
|
this.skeleton!.updateWorldTransform();
|
||||||
this.playTime = time;
|
this.playTime = time;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.playButton.onclick = () => (this.paused ? this.play() : this.pause());
|
this.playButton.onclick = () => (this.paused ? this.play() : this.pause());
|
||||||
speedButton.onclick = () => this.showSpeedDialog(speedButton);
|
speedButton.onclick = () => this.showSpeedDialog(speedButton);
|
||||||
this.animationButton.onclick = () => this.showAnimationsDialog(this.animationButton);
|
this.animationButton.onclick = () => this.showAnimationsDialog(this.animationButton!);
|
||||||
this.skinButton.onclick = () => this.showSkinsDialog(this.skinButton);
|
this.skinButton.onclick = () => this.showSkinsDialog(this.skinButton!);
|
||||||
settingsButton.onclick = () => this.showSettingsDialog(settingsButton);
|
settingsButton.onclick = () => this.showSettingsDialog(settingsButton);
|
||||||
|
|
||||||
let oldWidth = this.canvas.clientWidth, oldHeight = this.canvas.clientHeight;
|
let oldWidth = this.canvas.clientWidth, oldHeight = this.canvas.clientHeight;
|
||||||
@ -372,13 +383,13 @@ export class SpinePlayer implements Disposable {
|
|||||||
let fullscreenChanged = () => {
|
let fullscreenChanged = () => {
|
||||||
isFullscreen = !isFullscreen;
|
isFullscreen = !isFullscreen;
|
||||||
if (!isFullscreen) {
|
if (!isFullscreen) {
|
||||||
this.canvas.style.width = oldWidth + "px";
|
this.canvas!.style.width = oldWidth + "px";
|
||||||
this.canvas.style.height = oldHeight + "px";
|
this.canvas!.style.height = oldHeight + "px";
|
||||||
this.drawFrame(false);
|
this.drawFrame(false);
|
||||||
// Got to reset the style to whatever the user set after the next layouting.
|
// Got to reset the style to whatever the user set after the next layouting.
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this.canvas.style.width = oldStyleWidth;
|
this.canvas!.style.width = oldStyleWidth;
|
||||||
this.canvas.style.height = oldStyleHeight;
|
this.canvas!.style.height = oldStyleHeight;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -394,10 +405,10 @@ export class SpinePlayer implements Disposable {
|
|||||||
else if (doc.webkitExitFullscreen) doc.webkitExitFullscreen()
|
else if (doc.webkitExitFullscreen) doc.webkitExitFullscreen()
|
||||||
else if (doc.msExitFullscreen) doc.msExitFullscreen();
|
else if (doc.msExitFullscreen) doc.msExitFullscreen();
|
||||||
} else {
|
} else {
|
||||||
oldWidth = this.canvas.clientWidth;
|
oldWidth = this.canvas!.clientWidth;
|
||||||
oldHeight = this.canvas.clientHeight;
|
oldHeight = this.canvas!.clientHeight;
|
||||||
oldStyleWidth = this.canvas.style.width;
|
oldStyleWidth = this.canvas!.style.width;
|
||||||
oldStyleHeight = this.canvas.style.height;
|
oldStyleHeight = this.canvas!.style.height;
|
||||||
if (player.requestFullscreen) player.requestFullscreen();
|
if (player.requestFullscreen) player.requestFullscreen();
|
||||||
else if (player.webkitRequestFullScreen) player.webkitRequestFullScreen();
|
else if (player.webkitRequestFullScreen) player.webkitRequestFullScreen();
|
||||||
else if (player.mozRequestFullScreen) player.mozRequestFullScreen();
|
else if (player.mozRequestFullScreen) player.mozRequestFullScreen();
|
||||||
@ -413,18 +424,18 @@ export class SpinePlayer implements Disposable {
|
|||||||
private loadSkeleton () {
|
private loadSkeleton () {
|
||||||
if (this.error) return;
|
if (this.error) return;
|
||||||
|
|
||||||
if (this.assetManager.hasErrors())
|
if (this.assetManager!.hasErrors())
|
||||||
this.showError("Error: Assets could not be loaded.\n" + toString(this.assetManager.getErrors()));
|
this.showError("Error: Assets could not be loaded.\n" + toString(this.assetManager!.getErrors()));
|
||||||
|
|
||||||
let config = this.config;
|
let config = this.config;
|
||||||
|
|
||||||
// Configure filtering, don't use mipmaps in WebGL1 if the atlas page is non-POT
|
// Configure filtering, don't use mipmaps in WebGL1 if the atlas page is non-POT
|
||||||
let atlas = this.assetManager.require(config.atlasUrl) as TextureAtlas;
|
let atlas = this.assetManager!.require(config.atlasUrl!) as TextureAtlas;
|
||||||
let gl = this.context.gl, anisotropic = gl.getExtension("EXT_texture_filter_anisotropic");
|
let gl = this.context!.gl, anisotropic = gl.getExtension("EXT_texture_filter_anisotropic");
|
||||||
let isWebGL1 = gl.getParameter(gl.VERSION).indexOf("WebGL 1.0") != -1;
|
let isWebGL1 = gl.getParameter(gl.VERSION).indexOf("WebGL 1.0") != -1;
|
||||||
for (let page of atlas.pages) {
|
for (let page of atlas.pages) {
|
||||||
let minFilter = page.minFilter;
|
let minFilter = page.minFilter;
|
||||||
var useMipMaps: boolean = config.mipmaps;
|
var useMipMaps: boolean = config.mipmaps!;
|
||||||
var isPOT = MathUtils.isPowerOfTwo(page.width) && MathUtils.isPowerOfTwo(page.height);
|
var isPOT = MathUtils.isPowerOfTwo(page.width) && MathUtils.isPowerOfTwo(page.height);
|
||||||
if (isWebGL1 && !isPOT) useMipMaps = false;
|
if (isWebGL1 && !isPOT) useMipMaps = false;
|
||||||
|
|
||||||
@ -434,7 +445,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
minFilter = TextureFilter.MipMapLinearLinear;
|
minFilter = TextureFilter.MipMapLinearLinear;
|
||||||
} else
|
} else
|
||||||
minFilter = TextureFilter.Linear; // Don't use mipmaps without anisotropic.
|
minFilter = TextureFilter.Linear; // Don't use mipmaps without anisotropic.
|
||||||
page.texture.setFilters(minFilter, TextureFilter.Nearest);
|
page.texture!.setFilters(minFilter, TextureFilter.Nearest);
|
||||||
}
|
}
|
||||||
if (minFilter != TextureFilter.Nearest && minFilter != TextureFilter.Linear) (page.texture as GLTexture).update(true);
|
if (minFilter != TextureFilter.Nearest && minFilter != TextureFilter.Linear) (page.texture as GLTexture).update(true);
|
||||||
}
|
}
|
||||||
@ -443,7 +454,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
let skeletonData: SkeletonData;
|
let skeletonData: SkeletonData;
|
||||||
if (config.jsonUrl) {
|
if (config.jsonUrl) {
|
||||||
try {
|
try {
|
||||||
let jsonData = this.assetManager.remove(config.jsonUrl);
|
let jsonData = this.assetManager!.remove(config.jsonUrl);
|
||||||
if (!jsonData) throw new Error("Empty JSON data.");
|
if (!jsonData) throw new Error("Empty JSON data.");
|
||||||
if (config.jsonField) {
|
if (config.jsonField) {
|
||||||
jsonData = jsonData[config.jsonField];
|
jsonData = jsonData[config.jsonField];
|
||||||
@ -452,32 +463,34 @@ export class SpinePlayer implements Disposable {
|
|||||||
let json = new SkeletonJson(new AtlasAttachmentLoader(atlas));
|
let json = new SkeletonJson(new AtlasAttachmentLoader(atlas));
|
||||||
skeletonData = json.readSkeletonData(jsonData);
|
skeletonData = json.readSkeletonData(jsonData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError(`Error: Could not load skeleton JSON.\n${e.message}`, e);
|
this.showError(`Error: Could not load skeleton JSON.\n${(e as any).message}`, e as any);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let binaryData = this.assetManager.remove(config.binaryUrl);
|
let binaryData = this.assetManager!.remove(config.binaryUrl!);
|
||||||
let binary = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
|
let binary = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
|
||||||
try {
|
try {
|
||||||
skeletonData = binary.readSkeletonData(binaryData);
|
skeletonData = binary.readSkeletonData(binaryData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError(`Error: Could not load skeleton binary.\n${e.message}`, e);
|
this.showError(`Error: Could not load skeleton binary.\n${(e as any).message}`, e as any);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.skeleton = new Skeleton(skeletonData);
|
this.skeleton = new Skeleton(skeletonData);
|
||||||
let stateData = new AnimationStateData(skeletonData);
|
let stateData = new AnimationStateData(skeletonData);
|
||||||
stateData.defaultMix = config.defaultMix;
|
stateData.defaultMix = config.defaultMix!;
|
||||||
this.animationState = new AnimationState(stateData);
|
this.animationState = new AnimationState(stateData);
|
||||||
|
|
||||||
// Check if all control bones are in the skeleton
|
// Check if all control bones are in the skeleton
|
||||||
config.controlBones.forEach(bone => {
|
config.controlBones!.forEach(bone => {
|
||||||
if (!skeletonData.findBone(bone)) this.showError(`Error: Control bone does not exist in skeleton: ${bone}`);
|
if (!skeletonData.findBone(bone)) this.showError(`Error: Control bone does not exist in skeleton: ${bone}`);
|
||||||
})
|
})
|
||||||
|
|
||||||
// Setup skin.
|
// Setup skin.
|
||||||
if (!config.skin && skeletonData.skins.length) config.skin = skeletonData.skins[0].name;
|
if (!config.skin && skeletonData.skins.length) config.skin = skeletonData.skins[0].name;
|
||||||
if (config.skins && config.skin.length) {
|
if (config.skins && config.skin!.length) {
|
||||||
config.skins.forEach(skin => {
|
config.skins.forEach(skin => {
|
||||||
if (!this.skeleton.data.findSkin(skin))
|
if (!this.skeleton!.data.findSkin(skin))
|
||||||
this.showError(`Error: Skin in config list does not exist in skeleton: ${skin}`);
|
this.showError(`Error: Skin in config list does not exist in skeleton: ${skin}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -489,7 +502,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if all animations given a viewport exist.
|
// Check if all animations given a viewport exist.
|
||||||
Object.getOwnPropertyNames(config.viewport.animations).forEach((animation: string) => {
|
Object.getOwnPropertyNames(config.viewport!.animations).forEach((animation: string) => {
|
||||||
if (!skeletonData.findAnimation(animation))
|
if (!skeletonData.findAnimation(animation))
|
||||||
this.showError(`Error: Animation for which a viewport was specified does not exist in skeleton: ${animation}`);
|
this.showError(`Error: Animation for which a viewport was specified does not exist in skeleton: ${animation}`);
|
||||||
});
|
});
|
||||||
@ -497,7 +510,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
// Setup the animations after the viewport, so default bounds don't get messed up.
|
// Setup the animations after the viewport, so default bounds don't get messed up.
|
||||||
if (config.animations && config.animations.length) {
|
if (config.animations && config.animations.length) {
|
||||||
config.animations.forEach(animation => {
|
config.animations.forEach(animation => {
|
||||||
if (!this.skeleton.data.findAnimation(animation))
|
if (!this.skeleton!.data.findAnimation(animation))
|
||||||
this.showError(`Error: Animation in config list does not exist in skeleton: ${animation}`);
|
this.showError(`Error: Animation in config list does not exist in skeleton: ${animation}`);
|
||||||
});
|
});
|
||||||
if (!config.animation) config.animation = config.animations[0];
|
if (!config.animation) config.animation = config.animations[0];
|
||||||
@ -511,8 +524,8 @@ export class SpinePlayer implements Disposable {
|
|||||||
|
|
||||||
if (config.showControls) {
|
if (config.showControls) {
|
||||||
// Hide skin and animation if there's only the default skin / no animation
|
// Hide skin and animation if there's only the default skin / no animation
|
||||||
if (skeletonData.skins.length == 1 || (config.skins && config.skins.length == 1)) this.skinButton.classList.add("spine-player-hidden");
|
if (skeletonData.skins.length == 1 || (config.skins && config.skins.length == 1)) this.skinButton!.classList.add("spine-player-hidden");
|
||||||
if (skeletonData.animations.length == 1 || (config.animations && config.animations.length == 1)) this.animationButton.classList.add("spine-player-hidden");
|
if (skeletonData.animations.length == 1 || (config.animations && config.animations.length == 1)) this.animationButton!.classList.add("spine-player-hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.success) config.success(this);
|
if (config.success) config.success(this);
|
||||||
@ -525,37 +538,38 @@ export class SpinePlayer implements Disposable {
|
|||||||
} else {
|
} else {
|
||||||
entry = this.animationState.setEmptyAnimation(0);
|
entry = this.animationState.setEmptyAnimation(0);
|
||||||
entry.trackEnd = 100000000;
|
entry.trackEnd = 100000000;
|
||||||
this.setViewport(entry.animation);
|
this.setViewport(entry.animation!);
|
||||||
this.pause();
|
this.pause();
|
||||||
}
|
}
|
||||||
} else if (!this.currentViewport) {
|
} else if (!this.currentViewport) {
|
||||||
this.setViewport(entry.animation);
|
this.setViewport(entry.animation!);
|
||||||
this.play();
|
this.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupInput () {
|
private setupInput () {
|
||||||
let config = this.config;
|
let config = this.config;
|
||||||
let controlBones = config.controlBones;
|
let controlBones = config.controlBones!;
|
||||||
if (!controlBones.length && !config.showControls) return;
|
if (!controlBones.length && !config.showControls) return;
|
||||||
let selectedBones = this.selectedBones = new Array<Bone>(controlBones.length);
|
let selectedBones = this.selectedBones = new Array<Bone | null>(controlBones.length);
|
||||||
let canvas = this.canvas;
|
let canvas = this.canvas!;
|
||||||
let target: Bone = null;
|
let target: Bone | null = null;
|
||||||
let offset = new Vector2();
|
let offset = new Vector2();
|
||||||
let coords = new Vector3();
|
let coords = new Vector3();
|
||||||
let mouse = new Vector3();
|
let mouse = new Vector3();
|
||||||
let position = new Vector2();
|
let position = new Vector2();
|
||||||
let skeleton = this.skeleton;
|
let skeleton = this.skeleton!;
|
||||||
let renderer = this.sceneRenderer;
|
let renderer = this.sceneRenderer!;
|
||||||
|
|
||||||
let closest = function (x: number, y: number): Bone {
|
let closest = function (x: number, y: number): Bone | null {
|
||||||
mouse.set(x, canvas.clientHeight - y, 0)
|
mouse.set(x, canvas.clientHeight - y, 0)
|
||||||
offset.x = offset.y = 0;
|
offset.x = offset.y = 0;
|
||||||
let bestDistance = 24, index = 0;
|
let bestDistance = 24, index = 0;
|
||||||
let best: Bone;
|
let best: Bone | null = null;
|
||||||
for (let i = 0; i < controlBones.length; i++) {
|
for (let i = 0; i < controlBones.length; i++) {
|
||||||
selectedBones[i] = null;
|
selectedBones[i] = null;
|
||||||
let bone = skeleton.findBone(controlBones[i]);
|
let bone = skeleton.findBone(controlBones[i]);
|
||||||
|
if (!bone) continue;
|
||||||
let distance = renderer.camera.worldToScreen(
|
let distance = renderer.camera.worldToScreen(
|
||||||
coords.set(bone.worldX, bone.worldY, 0),
|
coords.set(bone.worldX, bone.worldY, 0),
|
||||||
canvas.clientWidth, canvas.clientHeight).distance(mouse);
|
canvas.clientWidth, canvas.clientHeight).distance(mouse);
|
||||||
@ -624,17 +638,17 @@ export class SpinePlayer implements Disposable {
|
|||||||
let mouseOverControls = true, mouseOverCanvas = false;
|
let mouseOverControls = true, mouseOverCanvas = false;
|
||||||
let handleHover = (mouseX: number, mouseY: number) => {
|
let handleHover = (mouseX: number, mouseY: number) => {
|
||||||
let popup = findWithClass(this.dom, "spine-player-popup");
|
let popup = findWithClass(this.dom, "spine-player-popup");
|
||||||
mouseOverControls = overlap(mouseX, mouseY, this.playerControls.getBoundingClientRect());
|
mouseOverControls = overlap(mouseX, mouseY, this.playerControls!.getBoundingClientRect());
|
||||||
mouseOverCanvas = overlap(mouseX, mouseY, canvas.getBoundingClientRect());
|
mouseOverCanvas = overlap(mouseX, mouseY, canvas.getBoundingClientRect());
|
||||||
clearTimeout(this.cancelId);
|
clearTimeout(this.cancelId);
|
||||||
let hide = !popup && !mouseOverControls && !mouseOverCanvas && !this.paused;
|
let hide = !popup && !mouseOverControls && !mouseOverCanvas && !this.paused;
|
||||||
if (hide)
|
if (hide)
|
||||||
this.playerControls.classList.add("spine-player-controls-hidden");
|
this.playerControls!.classList.add("spine-player-controls-hidden");
|
||||||
else
|
else
|
||||||
this.playerControls.classList.remove("spine-player-controls-hidden");
|
this.playerControls!.classList.remove("spine-player-controls-hidden");
|
||||||
if (!mouseOverControls && !popup && !this.paused) {
|
if (!mouseOverControls && !popup && !this.paused) {
|
||||||
this.cancelId = setTimeout(() => {
|
this.cancelId = setTimeout(() => {
|
||||||
if (!this.paused) this.playerControls.classList.add("spine-player-controls-hidden");
|
if (!this.paused) this.playerControls!.classList.add("spine-player-controls-hidden");
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -646,17 +660,17 @@ export class SpinePlayer implements Disposable {
|
|||||||
let config = this.config;
|
let config = this.config;
|
||||||
if (config.showControls) {
|
if (config.showControls) {
|
||||||
this.cancelId = setTimeout(() => {
|
this.cancelId = setTimeout(() => {
|
||||||
if (!this.paused) this.playerControls.classList.add("spine-player-controls-hidden");
|
if (!this.paused) this.playerControls!.classList.add("spine-player-controls-hidden");
|
||||||
}, 1000);
|
}, 1000);
|
||||||
this.playButton.classList.remove("spine-player-button-icon-play");
|
this.playButton!.classList.remove("spine-player-button-icon-play");
|
||||||
this.playButton.classList.add("spine-player-button-icon-pause");
|
this.playButton!.classList.add("spine-player-button-icon-pause");
|
||||||
|
|
||||||
// If no config animation, set one when first clicked.
|
// If no config animation, set one when first clicked.
|
||||||
if (!config.animation) {
|
if (!config.animation) {
|
||||||
if (config.animations && config.animations.length)
|
if (config.animations && config.animations.length)
|
||||||
config.animation = config.animations[0];
|
config.animation = config.animations[0];
|
||||||
else if (this.skeleton.data.animations.length)
|
else if (this.skeleton!.data.animations.length)
|
||||||
config.animation = this.skeleton.data.animations[0].name;
|
config.animation = this.skeleton!.data.animations[0].name;
|
||||||
if (config.animation) this.setAnimation(config.animation);
|
if (config.animation) this.setAnimation(config.animation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -665,33 +679,37 @@ export class SpinePlayer implements Disposable {
|
|||||||
pause () {
|
pause () {
|
||||||
this.paused = true;
|
this.paused = true;
|
||||||
if (this.config.showControls) {
|
if (this.config.showControls) {
|
||||||
this.playerControls.classList.remove("spine-player-controls-hidden");
|
this.playerControls!.classList.remove("spine-player-controls-hidden");
|
||||||
clearTimeout(this.cancelId);
|
clearTimeout(this.cancelId);
|
||||||
this.playButton.classList.remove("spine-player-button-icon-pause");
|
this.playButton!.classList.remove("spine-player-button-icon-pause");
|
||||||
this.playButton.classList.add("spine-player-button-icon-play");
|
this.playButton!.classList.add("spine-player-button-icon-play");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sets a new animation and viewport on track 0. */
|
/* Sets a new animation and viewport on track 0. */
|
||||||
setAnimation (animation: string | Animation, loop: boolean = true): TrackEntry {
|
setAnimation (animation: string | Animation, loop: boolean = true): TrackEntry {
|
||||||
animation = this.setViewport(animation);
|
animation = this.setViewport(animation);
|
||||||
return this.animationState.setAnimationWith(0, animation, loop);
|
return this.animationState!.setAnimationWith(0, animation, loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adds a new animation and viewport on track 0. */
|
/* Adds a new animation and viewport on track 0. */
|
||||||
addAnimation (animation: string | Animation, loop: boolean = true, delay: number = 0): TrackEntry {
|
addAnimation (animation: string | Animation, loop: boolean = true, delay: number = 0): TrackEntry {
|
||||||
animation = this.setViewport(animation);
|
animation = this.setViewport(animation);
|
||||||
return this.animationState.addAnimationWith(0, animation, loop, delay);
|
return this.animationState!.addAnimationWith(0, animation, loop, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sets the viewport for the specified animation. */
|
/* Sets the viewport for the specified animation. */
|
||||||
setViewport (animation: string | Animation): Animation {
|
setViewport (animation: string | Animation): Animation {
|
||||||
if (typeof animation == "string") animation = this.skeleton.data.findAnimation(animation);
|
if (typeof animation == "string") {
|
||||||
|
let foundAnimation = this.skeleton!.data.findAnimation(animation);
|
||||||
|
if (!foundAnimation) throw new Error("Animation not found: " + animation);
|
||||||
|
animation = foundAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
this.previousViewport = this.currentViewport;
|
this.previousViewport = this.currentViewport;
|
||||||
|
|
||||||
// Determine the base viewport.
|
// Determine the base viewport.
|
||||||
let globalViewport = this.config.viewport;
|
let globalViewport = this.config.viewport!;
|
||||||
let viewport = this.currentViewport = {
|
let viewport = this.currentViewport = {
|
||||||
padLeft: globalViewport.padLeft !== void 0 ? globalViewport.padLeft : "10%",
|
padLeft: globalViewport.padLeft !== void 0 ? globalViewport.padLeft : "10%",
|
||||||
padRight: globalViewport.padRight !== void 0 ? globalViewport.padRight : "10%",
|
padRight: globalViewport.padRight !== void 0 ? globalViewport.padRight : "10%",
|
||||||
@ -707,7 +725,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
this.calculateAnimationViewport(animation, viewport);
|
this.calculateAnimationViewport(animation, viewport);
|
||||||
|
|
||||||
// Override with the animation specific viewport for the final result.
|
// Override with the animation specific viewport for the final result.
|
||||||
let userAnimViewport = this.config.viewport.animations[animation.name];
|
let userAnimViewport = this.config.viewport!.animations![animation.name];
|
||||||
if (userAnimViewport) {
|
if (userAnimViewport) {
|
||||||
if (userAnimViewport.x !== void 0 && userAnimViewport.y !== void 0 && userAnimViewport.width && userAnimViewport.height) {
|
if (userAnimViewport.x !== void 0 && userAnimViewport.y !== void 0 && userAnimViewport.width && userAnimViewport.height) {
|
||||||
viewport.x = userAnimViewport.x;
|
viewport.x = userAnimViewport.x;
|
||||||
@ -738,16 +756,16 @@ export class SpinePlayer implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private calculateAnimationViewport (animation: Animation, viewport: Viewport) {
|
private calculateAnimationViewport (animation: Animation, viewport: Viewport) {
|
||||||
this.skeleton.setToSetupPose();
|
this.skeleton!.setToSetupPose();
|
||||||
|
|
||||||
let steps = 100, stepTime = animation.duration ? animation.duration / steps : 0, time = 0;
|
let steps = 100, stepTime = animation.duration ? animation.duration / steps : 0, time = 0;
|
||||||
let minX = 100000000, maxX = -100000000, minY = 100000000, maxY = -100000000;
|
let minX = 100000000, maxX = -100000000, minY = 100000000, maxY = -100000000;
|
||||||
let offset = new Vector2(), size = new Vector2();
|
let offset = new Vector2(), size = new Vector2();
|
||||||
|
|
||||||
for (let i = 0; i < steps; i++, time += stepTime) {
|
for (let i = 0; i < steps; i++, time += stepTime) {
|
||||||
animation.apply(this.skeleton, time, time, false, null, 1, MixBlend.setup, MixDirection.mixIn);
|
animation.apply(this.skeleton!, time, time, false, [], 1, MixBlend.setup, MixDirection.mixIn);
|
||||||
this.skeleton.updateWorldTransform();
|
this.skeleton!.updateWorldTransform();
|
||||||
this.skeleton.getBounds(offset, size);
|
this.skeleton!.getBounds(offset, size);
|
||||||
|
|
||||||
if (!isNaN(offset.x) && !isNaN(offset.y) && !isNaN(size.x) && !isNaN(size.y)) {
|
if (!isNaN(offset.x) && !isNaN(offset.y) && !isNaN(size.x) && !isNaN(size.y)) {
|
||||||
minX = Math.min(offset.x, minX);
|
minX = Math.min(offset.x, minX);
|
||||||
@ -778,13 +796,13 @@ export class SpinePlayer implements Disposable {
|
|||||||
let delta = this.time.delta;
|
let delta = this.time.delta;
|
||||||
|
|
||||||
// Load the skeleton if the assets are ready.
|
// Load the skeleton if the assets are ready.
|
||||||
let loading = this.assetManager.isLoadingComplete();
|
let loading = this.assetManager!.isLoadingComplete();
|
||||||
if (!this.skeleton && loading) this.loadSkeleton();
|
if (!this.skeleton && loading) this.loadSkeleton();
|
||||||
let skeleton = this.skeleton;
|
let skeleton = this.skeleton!;
|
||||||
let config = this.config;
|
let config = this.config!;
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
// Resize the canvas.
|
// Resize the canvas.
|
||||||
let renderer = this.sceneRenderer;
|
let renderer = this.sceneRenderer!;
|
||||||
renderer.resize(ResizeMode.Expand);
|
renderer.resize(ResizeMode.Expand);
|
||||||
|
|
||||||
let playDelta = this.paused ? 0 : delta * this.speed;
|
let playDelta = this.paused ? 0 : delta * this.speed;
|
||||||
@ -792,19 +810,19 @@ export class SpinePlayer implements Disposable {
|
|||||||
|
|
||||||
// Update animation time and pose the skeleton.
|
// Update animation time and pose the skeleton.
|
||||||
if (!this.paused) {
|
if (!this.paused) {
|
||||||
this.animationState.update(playDelta);
|
this.animationState!.update(playDelta);
|
||||||
this.animationState.apply(skeleton);
|
this.animationState!.apply(skeleton);
|
||||||
skeleton.updateWorldTransform();
|
skeleton.updateWorldTransform();
|
||||||
|
|
||||||
if (config.showControls) {
|
if (config.showControls) {
|
||||||
this.playTime += playDelta;
|
this.playTime += playDelta;
|
||||||
let entry = this.animationState.getCurrent(0);
|
let entry = this.animationState!.getCurrent(0);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
let duration = entry.animation.duration;
|
let duration = entry.animation!.duration;
|
||||||
while (this.playTime >= duration && duration != 0)
|
while (this.playTime >= duration && duration != 0)
|
||||||
this.playTime -= duration;
|
this.playTime -= duration;
|
||||||
this.playTime = Math.max(0, Math.min(this.playTime, duration));
|
this.playTime = Math.max(0, Math.min(this.playTime, duration));
|
||||||
this.timelineSlider.setValue(this.playTime / duration);
|
this.timelineSlider!.setValue(this.playTime / duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -817,7 +835,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
viewport.height = this.currentViewport.height + (this.currentViewport.padBottom as number) + (this.currentViewport.padTop as number)
|
viewport.height = this.currentViewport.height + (this.currentViewport.padBottom as number) + (this.currentViewport.padTop as number)
|
||||||
|
|
||||||
if (this.previousViewport) {
|
if (this.previousViewport) {
|
||||||
let transitionAlpha = (performance.now() - this.viewportTransitionStart) / 1000 / config.viewport.transitionTime;
|
let transitionAlpha = (performance.now() - this.viewportTransitionStart) / 1000 / config.viewport!.transitionTime!;
|
||||||
if (transitionAlpha < 1) {
|
if (transitionAlpha < 1) {
|
||||||
let x = this.previousViewport.x - (this.previousViewport.padLeft as number);
|
let x = this.previousViewport.x - (this.previousViewport.padLeft as number);
|
||||||
let y = this.previousViewport.y - (this.previousViewport.padBottom as number);
|
let y = this.previousViewport.y - (this.previousViewport.padBottom as number);
|
||||||
@ -830,13 +848,13 @@ export class SpinePlayer implements Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.camera.zoom = this.canvas.height / this.canvas.width > viewport.height / viewport.width
|
renderer.camera.zoom = this.canvas!.height / this.canvas!.width > viewport.height / viewport.width
|
||||||
? viewport.width / this.canvas.width : viewport.height / this.canvas.height;
|
? viewport.width / this.canvas!.width : viewport.height / this.canvas!.height;
|
||||||
renderer.camera.position.x = viewport.x + viewport.width / 2;
|
renderer.camera.position.x = viewport.x + viewport.width / 2;
|
||||||
renderer.camera.position.y = viewport.y + viewport.height / 2;
|
renderer.camera.position.y = viewport.y + viewport.height / 2;
|
||||||
|
|
||||||
// Clear the screen.
|
// Clear the screen.
|
||||||
let gl = this.context.gl;
|
let gl = this.context!.gl;
|
||||||
gl.clearColor(bg.r, bg.g, bg.b, bg.a);
|
gl.clearColor(bg.r, bg.g, bg.b, bg.a);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
@ -847,7 +865,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
// Draw the background image.
|
// Draw the background image.
|
||||||
let bgImage = config.backgroundImage;
|
let bgImage = config.backgroundImage;
|
||||||
if (bgImage) {
|
if (bgImage) {
|
||||||
let texture = this.assetManager.require(bgImage.url);
|
let texture = this.assetManager!.require(bgImage.url);
|
||||||
if (bgImage.x !== void 0 && bgImage.y !== void 0 && bgImage.width && bgImage.height)
|
if (bgImage.x !== void 0 && bgImage.y !== void 0 && bgImage.width && bgImage.height)
|
||||||
renderer.drawTexture(texture, bgImage.x, bgImage.y, bgImage.width, bgImage.height);
|
renderer.drawTexture(texture, bgImage.x, bgImage.y, bgImage.width, bgImage.height);
|
||||||
else
|
else
|
||||||
@ -856,19 +874,19 @@ export class SpinePlayer implements Disposable {
|
|||||||
|
|
||||||
// Draw the skeleton and debug output.
|
// Draw the skeleton and debug output.
|
||||||
renderer.drawSkeleton(skeleton, config.premultipliedAlpha);
|
renderer.drawSkeleton(skeleton, config.premultipliedAlpha);
|
||||||
if ((renderer.skeletonDebugRenderer.drawBones = config.debug.bones)
|
if ((renderer.skeletonDebugRenderer.drawBones = config.debug!.bones!)
|
||||||
|| (renderer.skeletonDebugRenderer.drawBoundingBoxes = config.debug.bounds)
|
|| (renderer.skeletonDebugRenderer.drawBoundingBoxes = config.debug!.bounds!)
|
||||||
|| (renderer.skeletonDebugRenderer.drawClipping = config.debug.clipping)
|
|| (renderer.skeletonDebugRenderer.drawClipping = config.debug!.clipping!)
|
||||||
|| (renderer.skeletonDebugRenderer.drawMeshHull = config.debug.hulls)
|
|| (renderer.skeletonDebugRenderer.drawMeshHull = config.debug!.hulls!)
|
||||||
|| (renderer.skeletonDebugRenderer.drawPaths = config.debug.paths)
|
|| (renderer.skeletonDebugRenderer.drawPaths = config.debug!.paths!)
|
||||||
|| (renderer.skeletonDebugRenderer.drawRegionAttachments = config.debug.regions)
|
|| (renderer.skeletonDebugRenderer.drawRegionAttachments = config.debug!.regions!)
|
||||||
|| (renderer.skeletonDebugRenderer.drawMeshTriangles = config.debug.meshes)
|
|| (renderer.skeletonDebugRenderer.drawMeshTriangles = config.debug!.meshes!)
|
||||||
) {
|
) {
|
||||||
renderer.drawSkeletonDebug(skeleton, config.premultipliedAlpha);
|
renderer.drawSkeletonDebug(skeleton, config.premultipliedAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the control bones.
|
// Draw the control bones.
|
||||||
let controlBones = config.controlBones;
|
let controlBones = config.controlBones!;
|
||||||
if (controlBones.length) {
|
if (controlBones.length) {
|
||||||
let selectedBones = this.selectedBones;
|
let selectedBones = this.selectedBones;
|
||||||
gl.lineWidth(2);
|
gl.lineWidth(2);
|
||||||
@ -883,7 +901,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw the viewport bounds.
|
// Draw the viewport bounds.
|
||||||
if (config.viewport.debugRender) {
|
if (config.viewport!.debugRender) {
|
||||||
gl.lineWidth(1);
|
gl.lineWidth(1);
|
||||||
renderer.rect(false, this.currentViewport.x, this.currentViewport.y, this.currentViewport.width, this.currentViewport.height, Color.GREEN);
|
renderer.rect(false, this.currentViewport.x, this.currentViewport.y, this.currentViewport.width, this.currentViewport.height, Color.GREEN);
|
||||||
renderer.rect(false, viewport.x, viewport.y, viewport.width, viewport.height, Color.RED);
|
renderer.rect(false, viewport.x, viewport.y, viewport.width, viewport.height, Color.RED);
|
||||||
@ -896,12 +914,12 @@ export class SpinePlayer implements Disposable {
|
|||||||
|
|
||||||
// Draw the loading screen.
|
// Draw the loading screen.
|
||||||
if (config.showLoading) {
|
if (config.showLoading) {
|
||||||
this.loadingScreen.backgroundColor.setFromColor(bg);
|
this.loadingScreen!.backgroundColor.setFromColor(bg);
|
||||||
this.loadingScreen.draw(loading);
|
this.loadingScreen!.draw(loading);
|
||||||
}
|
}
|
||||||
if (loading && config.loading) config.loading(this, delta);
|
if (loading && config.loading) config.loading(this, delta);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError(`Error: Unable to render skeleton.\n${e.message}`, e);
|
this.showError(`Error: Unable to render skeleton.\n${(e as any).message}`, e as any);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,14 +928,14 @@ export class SpinePlayer implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private hidePopup (id: string): boolean {
|
private hidePopup (id: string): boolean {
|
||||||
return this.popup && this.popup.hide(id);
|
return this.popup != null && this.popup.hide(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private showSpeedDialog (speedButton: HTMLElement) {
|
private showSpeedDialog (speedButton: HTMLElement) {
|
||||||
let id = "speed";
|
let id = "speed";
|
||||||
if (this.hidePopup(id)) return;
|
if (this.hidePopup(id)) return;
|
||||||
|
|
||||||
let popup = new Popup(id, speedButton, this, this.playerControls, /*html*/`
|
let popup = new Popup(id, speedButton, this, this.playerControls!, /*html*/`
|
||||||
<div class="spine-player-popup-title">Speed</div>
|
<div class="spine-player-popup-title">Speed</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="spine-player-row" style="align-items:center;padding:8px">
|
<div class="spine-player-row" style="align-items:center;padding:8px">
|
||||||
@ -938,7 +956,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
if (this.hidePopup(id)) return;
|
if (this.hidePopup(id)) return;
|
||||||
if (!this.skeleton || !this.skeleton.data.animations.length) return;
|
if (!this.skeleton || !this.skeleton.data.animations.length) return;
|
||||||
|
|
||||||
let popup = new Popup(id, animationsButton, this, this.playerControls,
|
let popup = new Popup(id, animationsButton, this, this.playerControls!,
|
||||||
/*html*/`<div class="spine-player-popup-title">Animations</div><hr><ul class="spine-player-list"></ul>`);
|
/*html*/`<div class="spine-player-popup-title">Animations</div><hr><ul class="spine-player-list"></ul>`);
|
||||||
|
|
||||||
let rows = findWithClass(popup.dom, "spine-player-list");
|
let rows = findWithClass(popup.dom, "spine-player-list");
|
||||||
@ -968,7 +986,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
if (this.hidePopup(id)) return;
|
if (this.hidePopup(id)) return;
|
||||||
if (!this.skeleton || !this.skeleton.data.animations.length) return;
|
if (!this.skeleton || !this.skeleton.data.animations.length) return;
|
||||||
|
|
||||||
let popup = new Popup(id, skinButton, this, this.playerControls,
|
let popup = new Popup(id, skinButton, this, this.playerControls!,
|
||||||
/*html*/`<div class="spine-player-popup-title">Skins</div><hr><ul class="spine-player-list"></ul>`);
|
/*html*/`<div class="spine-player-popup-title">Skins</div><hr><ul class="spine-player-list"></ul>`);
|
||||||
|
|
||||||
let rows = findWithClass(popup.dom, "spine-player-list");
|
let rows = findWithClass(popup.dom, "spine-player-list");
|
||||||
@ -984,8 +1002,8 @@ export class SpinePlayer implements Disposable {
|
|||||||
removeClass(rows.children, "selected");
|
removeClass(rows.children, "selected");
|
||||||
row.classList.add("selected");
|
row.classList.add("selected");
|
||||||
this.config.skin = skin.name;
|
this.config.skin = skin.name;
|
||||||
this.skeleton.setSkinByName(this.config.skin);
|
this.skeleton!.setSkinByName(this.config.skin);
|
||||||
this.skeleton.setSlotsToSetupPose();
|
this.skeleton!.setSlotsToSetupPose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
popup.show();
|
popup.show();
|
||||||
@ -996,7 +1014,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
if (this.hidePopup(id)) return;
|
if (this.hidePopup(id)) return;
|
||||||
if (!this.skeleton || !this.skeleton.data.animations.length) return;
|
if (!this.skeleton || !this.skeleton.data.animations.length) return;
|
||||||
|
|
||||||
let popup = new Popup(id, settingsButton, this, this.playerControls, /*html*/`<div class="spine-player-popup-title">Debug</div><hr><ul class="spine-player-list"></li>`);
|
let popup = new Popup(id, settingsButton, this, this.playerControls!, /*html*/`<div class="spine-player-popup-title">Debug</div><hr><ul class="spine-player-list"></li>`);
|
||||||
|
|
||||||
let rows = findWithClass(popup.dom, "spine-player-list");
|
let rows = findWithClass(popup.dom, "spine-player-list");
|
||||||
let makeItem = (label: string, name: string) => {
|
let makeItem = (label: string, name: string) => {
|
||||||
@ -1019,7 +1037,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
popup.show();
|
popup.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private showError (message: string, error: Error = null) {
|
private showError (message: string, error?: Error) {
|
||||||
if (this.error) {
|
if (this.error) {
|
||||||
if (error) throw error; // Don't lose error if showError throws, is caught, and showError is called again.
|
if (error) throw error; // Don't lose error if showError throws, is caught, and showError is called again.
|
||||||
} else {
|
} else {
|
||||||
@ -1056,6 +1074,7 @@ class Popup {
|
|||||||
this.player.popup = null;
|
this.player.popup = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
show () {
|
show () {
|
||||||
@ -1094,9 +1113,9 @@ class Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Switch {
|
class Switch {
|
||||||
private switch: HTMLElement;
|
private switch: HTMLElement | null = null;
|
||||||
private enabled = false;
|
private enabled = false;
|
||||||
public change: (value: boolean) => void;
|
public change: (value: boolean) => void = () => { };
|
||||||
|
|
||||||
constructor (private text: string) { }
|
constructor (private text: string) { }
|
||||||
|
|
||||||
@ -1117,10 +1136,9 @@ class Switch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setEnabled (enabled: boolean) {
|
setEnabled (enabled: boolean) {
|
||||||
if (enabled) this.switch.classList.add("active");
|
if (enabled) this.switch?.classList.add("active");
|
||||||
else this.switch.classList.remove("active");
|
else this.switch?.classList.remove("active");
|
||||||
this.enabled = enabled
|
this.enabled = enabled;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isEnabled (): boolean {
|
isEnabled (): boolean {
|
||||||
@ -1129,10 +1147,10 @@ class Switch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Slider {
|
class Slider {
|
||||||
private slider: HTMLElement;
|
private slider: HTMLElement | null = null;
|
||||||
private value: HTMLElement;
|
private value: HTMLElement | null = null;
|
||||||
private knob: HTMLElement;
|
private knob: HTMLElement | null = null;
|
||||||
public change: (percentage: number) => void;
|
public change: (percentage: number) => void = () => { };
|
||||||
|
|
||||||
constructor (public snaps = 0, public snapPercentage = 0.1, public big = false) { }
|
constructor (public snaps = 0, public snapPercentage = 0.1, public big = false) { }
|
||||||
|
|
||||||
@ -1150,18 +1168,18 @@ class Slider {
|
|||||||
new Input(this.slider).addListener({
|
new Input(this.slider).addListener({
|
||||||
down: (x, y) => {
|
down: (x, y) => {
|
||||||
dragging = true;
|
dragging = true;
|
||||||
this.value.classList.add("hovering");
|
this.value?.classList.add("hovering");
|
||||||
},
|
},
|
||||||
up: (x, y) => {
|
up: (x, y) => {
|
||||||
dragging = false;
|
dragging = false;
|
||||||
if (this.change) this.change(this.setValue(x / this.slider.clientWidth));
|
if (this.change) this.change(this.setValue(x / this.slider!.clientWidth));
|
||||||
this.value.classList.remove("hovering");
|
this.value?.classList.remove("hovering");
|
||||||
},
|
},
|
||||||
moved: (x, y) => {
|
moved: (x, y) => {
|
||||||
if (dragging && this.change) this.change(this.setValue(x / this.slider.clientWidth));
|
if (dragging && this.change) this.change(this.setValue(x / this.slider!.clientWidth));
|
||||||
},
|
},
|
||||||
dragged: (x, y) => {
|
dragged: (x, y) => {
|
||||||
if (this.change) this.change(this.setValue(x / this.slider.clientWidth));
|
if (this.change) this.change(this.setValue(x / this.slider!.clientWidth));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1180,7 +1198,7 @@ class Slider {
|
|||||||
percentage = percentage - modulo + snap;
|
percentage = percentage - modulo + snap;
|
||||||
percentage = Math.max(0, Math.min(1, percentage));
|
percentage = Math.max(0, Math.min(1, percentage));
|
||||||
}
|
}
|
||||||
this.value.style.width = "" + (percentage * 100) + "%";
|
this.value!.style.width = "" + (percentage * 100) + "%";
|
||||||
// this.knob.style.left = "" + (-8 + percentage * this.slider.clientWidth) + "px";
|
// this.knob.style.left = "" + (-8 + percentage * this.slider.clientWidth) + "px";
|
||||||
return percentage;
|
return percentage;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,8 +31,8 @@ import { AssetManagerBase, Downloader } from "@esotericsoftware/spine-core"
|
|||||||
import { ThreeJsTexture } from "./ThreeJsTexture";
|
import { ThreeJsTexture } from "./ThreeJsTexture";
|
||||||
|
|
||||||
export class AssetManager extends AssetManagerBase {
|
export class AssetManager extends AssetManagerBase {
|
||||||
constructor (pathPrefix: string = "", downloader: Downloader = null) {
|
constructor (pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
||||||
super((image: HTMLImageElement) => {
|
super((image: HTMLImageElement | ImageBitmap) => {
|
||||||
return new ThreeJsTexture(image);
|
return new ThreeJsTexture(image);
|
||||||
}, pathPrefix, downloader);
|
}, pathPrefix, downloader);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ export class MeshBatcher extends THREE.Mesh {
|
|||||||
geo.setAttribute("color", new THREE.InterleavedBufferAttribute(vertexBuffer, 4, 3, false));
|
geo.setAttribute("color", new THREE.InterleavedBufferAttribute(vertexBuffer, 4, 3, false));
|
||||||
geo.setAttribute("uv", new THREE.InterleavedBufferAttribute(vertexBuffer, 2, 7, false));
|
geo.setAttribute("uv", new THREE.InterleavedBufferAttribute(vertexBuffer, 2, 7, false));
|
||||||
geo.setIndex(new THREE.BufferAttribute(indices, 1));
|
geo.setIndex(new THREE.BufferAttribute(indices, 1));
|
||||||
geo.getIndex().usage = WebGLRenderingContext.DYNAMIC_DRAW;
|
geo.getIndex()!.usage = WebGLRenderingContext.DYNAMIC_DRAW;
|
||||||
geo.drawRange.start = 0;
|
geo.drawRange.start = 0;
|
||||||
geo.drawRange.count = 0;
|
geo.drawRange.count = 0;
|
||||||
this.geometry = geo;
|
this.geometry = geo;
|
||||||
@ -134,9 +134,11 @@ export class MeshBatcher extends THREE.Mesh {
|
|||||||
this.vertexBuffer.updateRange.count = this.verticesLength;
|
this.vertexBuffer.updateRange.count = this.verticesLength;
|
||||||
let geo = (<THREE.BufferGeometry>this.geometry);
|
let geo = (<THREE.BufferGeometry>this.geometry);
|
||||||
this.closeMaterialGroups();
|
this.closeMaterialGroups();
|
||||||
geo.getIndex().needsUpdate = this.indicesLength > 0;
|
let index = geo.getIndex();
|
||||||
geo.getIndex().updateRange.offset = 0;
|
if (!index) throw new Error("BufferAttribute must not be null.");
|
||||||
geo.getIndex().updateRange.count = this.indicesLength;
|
index.needsUpdate = this.indicesLength > 0;
|
||||||
|
index.updateRange.offset = 0;
|
||||||
|
index.updateRange.count = this.indicesLength;
|
||||||
geo.drawRange.start = 0;
|
geo.drawRange.start = 0;
|
||||||
geo.drawRange.count = this.indicesLength;
|
geo.drawRange.count = this.indicesLength;
|
||||||
}
|
}
|
||||||
@ -168,7 +170,7 @@ export class MeshBatcher extends THREE.Mesh {
|
|||||||
for (let i = 0; i < this.material.length; i++) {
|
for (let i = 0; i < this.material.length; i++) {
|
||||||
const meshMaterial = this.material[i] as SkeletonMeshMaterial;
|
const meshMaterial = this.material[i] as SkeletonMeshMaterial;
|
||||||
|
|
||||||
if (meshMaterial.uniforms.map.value === null) {
|
if (!meshMaterial.uniforms.map.value) {
|
||||||
updateMeshMaterial(meshMaterial, slotTexture, blending);
|
updateMeshMaterial(meshMaterial, slotTexture, blending);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,8 +73,9 @@ export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
|
|||||||
alphaTest: 0.0
|
alphaTest: 0.0
|
||||||
};
|
};
|
||||||
customizer(parameters);
|
customizer(parameters);
|
||||||
if (parameters.alphaTest > 0) {
|
if (parameters.alphaTest && parameters.alphaTest > 0) {
|
||||||
parameters.defines = { "USE_SPINE_ALPHATEST": 1 };
|
parameters.defines = { "USE_SPINE_ALPHATEST": 1 };
|
||||||
|
if (!parameters.uniforms) parameters.uniforms = {};
|
||||||
parameters.uniforms["alphaTest"] = { value: parameters.alphaTest };
|
parameters.uniforms["alphaTest"] = { value: parameters.alphaTest };
|
||||||
}
|
}
|
||||||
super(parameters);
|
super(parameters);
|
||||||
@ -89,7 +90,7 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
skeleton: Skeleton;
|
skeleton: Skeleton;
|
||||||
state: AnimationState;
|
state: AnimationState;
|
||||||
zOffset: number = 0.1;
|
zOffset: number = 0.1;
|
||||||
vertexEffect: VertexEffect;
|
vertexEffect: VertexEffect | null = null;
|
||||||
|
|
||||||
private batches = new Array<MeshBatcher>();
|
private batches = new Array<MeshBatcher>();
|
||||||
private nextBatchIndex = 0;
|
private nextBatchIndex = 0;
|
||||||
@ -152,17 +153,11 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
let tempUv = this.tempUv;
|
let tempUv = this.tempUv;
|
||||||
let tempLight = this.tempLight;
|
let tempLight = this.tempLight;
|
||||||
let tempDark = this.tempDark;
|
let tempDark = this.tempDark;
|
||||||
|
|
||||||
var numVertices = 0;
|
|
||||||
var verticesLength = 0;
|
|
||||||
var indicesLength = 0;
|
|
||||||
|
|
||||||
let blendMode: BlendMode = null;
|
|
||||||
let clipper = this.clipper;
|
let clipper = this.clipper;
|
||||||
|
|
||||||
let vertices: NumberArrayLike = this.vertices;
|
let vertices: NumberArrayLike = this.vertices;
|
||||||
let triangles: Array<number> = null;
|
let triangles: Array<number> | null = null;
|
||||||
let uvs: NumberArrayLike = null;
|
let uvs: NumberArrayLike | null = null;
|
||||||
let drawOrder = this.skeleton.drawOrder;
|
let drawOrder = this.skeleton.drawOrder;
|
||||||
let batch = this.nextBatch();
|
let batch = this.nextBatch();
|
||||||
batch.begin();
|
batch.begin();
|
||||||
@ -176,8 +171,8 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let attachment = slot.getAttachment();
|
let attachment = slot.getAttachment();
|
||||||
let attachmentColor: Color = null;
|
let attachmentColor: Color | null;
|
||||||
let texture: ThreeJsTexture = null;
|
let texture: ThreeJsTexture | null;
|
||||||
let numFloats = 0;
|
let numFloats = 0;
|
||||||
if (attachment instanceof RegionAttachment) {
|
if (attachment instanceof RegionAttachment) {
|
||||||
let region = <RegionAttachment>attachment;
|
let region = <RegionAttachment>attachment;
|
||||||
@ -187,7 +182,7 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
region.computeWorldVertices(slot, vertices, 0, vertexSize);
|
region.computeWorldVertices(slot, vertices, 0, vertexSize);
|
||||||
triangles = SkeletonMesh.QUAD_TRIANGLES;
|
triangles = SkeletonMesh.QUAD_TRIANGLES;
|
||||||
uvs = region.uvs;
|
uvs = region.uvs;
|
||||||
texture = <ThreeJsTexture>(<TextureAtlasRegion>region.region.renderObject).page.texture;
|
texture = <ThreeJsTexture>(<TextureAtlasRegion>region.region!.renderObject).page.texture;
|
||||||
} else if (attachment instanceof MeshAttachment) {
|
} else if (attachment instanceof MeshAttachment) {
|
||||||
let mesh = <MeshAttachment>attachment;
|
let mesh = <MeshAttachment>attachment;
|
||||||
attachmentColor = mesh.color;
|
attachmentColor = mesh.color;
|
||||||
@ -199,7 +194,7 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, vertices, 0, vertexSize);
|
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, vertices, 0, vertexSize);
|
||||||
triangles = mesh.triangles;
|
triangles = mesh.triangles;
|
||||||
uvs = mesh.uvs;
|
uvs = mesh.uvs;
|
||||||
texture = <ThreeJsTexture>(<TextureAtlasRegion>mesh.region.renderObject).page.texture;
|
texture = <ThreeJsTexture>(<TextureAtlasRegion>mesh.region!.renderObject).page.texture;
|
||||||
} else if (attachment instanceof ClippingAttachment) {
|
} else if (attachment instanceof ClippingAttachment) {
|
||||||
let clip = <ClippingAttachment>(attachment);
|
let clip = <ClippingAttachment>(attachment);
|
||||||
clipper.clipStart(slot, clip);
|
clipper.clipStart(slot, clip);
|
||||||
@ -226,7 +221,7 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
let finalIndicesLength: number;
|
let finalIndicesLength: number;
|
||||||
|
|
||||||
if (clipper.isClipping()) {
|
if (clipper.isClipping()) {
|
||||||
clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, null, false);
|
clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, tempLight, false);
|
||||||
let clippedVertices = clipper.clippedVertices;
|
let clippedVertices = clipper.clippedVertices;
|
||||||
let clippedTriangles = clipper.clippedTriangles;
|
let clippedTriangles = clipper.clippedTriangles;
|
||||||
if (this.vertexEffect != null) {
|
if (this.vertexEffect != null) {
|
||||||
|
|||||||
@ -33,8 +33,9 @@ import * as THREE from "three";
|
|||||||
export class ThreeJsTexture extends Texture {
|
export class ThreeJsTexture extends Texture {
|
||||||
texture: THREE.Texture;
|
texture: THREE.Texture;
|
||||||
|
|
||||||
constructor (image: HTMLImageElement) {
|
constructor (image: HTMLImageElement | ImageBitmap) {
|
||||||
super(image);
|
super(image);
|
||||||
|
if (image instanceof ImageBitmap) throw new Error("ImageBitmap not supported.");
|
||||||
this.texture = new THREE.Texture(image);
|
this.texture = new THREE.Texture(image);
|
||||||
this.texture.flipY = false;
|
this.texture.flipY = false;
|
||||||
this.texture.needsUpdate = true;
|
this.texture.needsUpdate = true;
|
||||||
|
|||||||
@ -234,7 +234,7 @@
|
|||||||
function setupUI() {
|
function setupUI() {
|
||||||
let formatList = $("#formatList");
|
let formatList = $("#formatList");
|
||||||
formatList.append($("<option>Binary</option>"));
|
formatList.append($("<option>Binary</option>"));
|
||||||
formatList.append($("<option>JSON</option>"));
|
formatList.append($("<option selected>JSON</option>"));
|
||||||
let skeletonList = $("#skeletonList");
|
let skeletonList = $("#skeletonList");
|
||||||
for (let skeletonName in skeletons) {
|
for (let skeletonName in skeletons) {
|
||||||
let option = $("<option></option>");
|
let option = $("<option></option>");
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import { GLTexture } from "./GLTexture";
|
|||||||
|
|
||||||
|
|
||||||
export class AssetManager extends AssetManagerBase {
|
export class AssetManager extends AssetManagerBase {
|
||||||
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "", downloader: Downloader = null) {
|
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
||||||
super((image: HTMLImageElement | ImageBitmap) => {
|
super((image: HTMLImageElement | ImageBitmap) => {
|
||||||
return new GLTexture(context, image);
|
return new GLTexture(context, image);
|
||||||
}, pathPrefix, downloader);
|
}, pathPrefix, downloader);
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import { ManagedWebGLRenderingContext } from "./WebGL";
|
|||||||
|
|
||||||
export class GLTexture extends Texture implements Disposable, Restorable {
|
export class GLTexture extends Texture implements Disposable, Restorable {
|
||||||
context: ManagedWebGLRenderingContext;
|
context: ManagedWebGLRenderingContext;
|
||||||
private texture: WebGLTexture = null;
|
private texture: WebGLTexture | null = null;
|
||||||
private boundUnit = 0;
|
private boundUnit = 0;
|
||||||
private useMipMaps = false;
|
private useMipMaps = false;
|
||||||
|
|
||||||
|
|||||||
@ -32,8 +32,8 @@ export class Input {
|
|||||||
mouseX = 0;
|
mouseX = 0;
|
||||||
mouseY = 0;
|
mouseY = 0;
|
||||||
buttonDown = false;
|
buttonDown = false;
|
||||||
touch0: Touch = null;
|
touch0: Touch | null = null;
|
||||||
touch1: Touch = null;
|
touch1: Touch | null = null;
|
||||||
initialPinchDistance = 0;
|
initialPinchDistance = 0;
|
||||||
private listeners = new Array<InputListener>();
|
private listeners = new Array<InputListener>();
|
||||||
private eventListeners: Array<{ target: any, event: any, func: any }> = [];
|
private eventListeners: Array<{ target: any, event: any, func: any }> = [];
|
||||||
@ -104,6 +104,7 @@ export class Input {
|
|||||||
if (!this.touch0 || !this.touch1) {
|
if (!this.touch0 || !this.touch1) {
|
||||||
var touches = ev.changedTouches;
|
var touches = ev.changedTouches;
|
||||||
let nativeTouch = touches.item(0);
|
let nativeTouch = touches.item(0);
|
||||||
|
if (!nativeTouch) return;
|
||||||
let rect = element.getBoundingClientRect();
|
let rect = element.getBoundingClientRect();
|
||||||
let x = nativeTouch.clientX - rect.left;
|
let x = nativeTouch.clientX - rect.left;
|
||||||
let y = nativeTouch.clientY - rect.top;
|
let y = nativeTouch.clientY - rect.top;
|
||||||
@ -180,7 +181,7 @@ export class Input {
|
|||||||
this.mouseX = this.touch0.x;
|
this.mouseX = this.touch0.x;
|
||||||
this.mouseX = this.touch0.x;
|
this.mouseX = this.touch0.x;
|
||||||
this.buttonDown = true;
|
this.buttonDown = true;
|
||||||
this.listeners.map((listener) => { if (listener.down) listener.down(this.touch0.x, this.touch0.y) });
|
this.listeners.map((listener) => { if (listener.down) listener.down(this.touch0!.x, this.touch0!.y) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,8 +40,8 @@ const logoWidth = 165, logoHeight = 108, spinnerSize = 163;
|
|||||||
|
|
||||||
export class LoadingScreen implements Disposable {
|
export class LoadingScreen implements Disposable {
|
||||||
private renderer: SceneRenderer;
|
private renderer: SceneRenderer;
|
||||||
private logo: GLTexture = null;
|
private logo: GLTexture | null = null;
|
||||||
private spinner: GLTexture = null;
|
private spinner: GLTexture | null = null;
|
||||||
private angle = 0;
|
private angle = 0;
|
||||||
private fadeOut = 0;
|
private fadeOut = 0;
|
||||||
private fadeIn = 0;
|
private fadeIn = 0;
|
||||||
@ -70,8 +70,8 @@ export class LoadingScreen implements Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dispose (): void {
|
dispose (): void {
|
||||||
this.logo.dispose();
|
this.logo?.dispose();
|
||||||
this.spinner.dispose();
|
this.spinner?.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
draw (complete = false) {
|
draw (complete = false) {
|
||||||
@ -122,7 +122,7 @@ export class LoadingScreen implements Disposable {
|
|||||||
renderer.camera.zoom = Math.max(1, spinnerSize / canvas.height);
|
renderer.camera.zoom = Math.max(1, spinnerSize / canvas.height);
|
||||||
renderer.begin();
|
renderer.begin();
|
||||||
renderer.drawTexture(this.logo, (canvas.width - logoWidth) / 2, (canvas.height - logoHeight) / 2, logoWidth, logoHeight, tempColor);
|
renderer.drawTexture(this.logo, (canvas.width - logoWidth) / 2, (canvas.height - logoHeight) / 2, logoWidth, logoHeight, tempColor);
|
||||||
renderer.drawTextureRotated(this.spinner, (canvas.width - spinnerSize) / 2, (canvas.height - spinnerSize) / 2, spinnerSize, spinnerSize, spinnerSize / 2, spinnerSize / 2, this.angle, tempColor);
|
if (this.spinner) renderer.drawTextureRotated(this.spinner, (canvas.width - spinnerSize) / 2, (canvas.height - spinnerSize) / 2, spinnerSize, spinnerSize, spinnerSize / 2, spinnerSize / 2, this.angle, tempColor);
|
||||||
renderer.end();
|
renderer.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import { Vector2 } from "@esotericsoftware/spine-core";
|
||||||
import { Vector3 } from "./Vector3";
|
import { Vector3 } from "./Vector3";
|
||||||
|
|
||||||
export const M00 = 0;
|
export const M00 = 0;
|
||||||
@ -50,9 +51,9 @@ export class Matrix4 {
|
|||||||
temp: Float32Array = new Float32Array(16);
|
temp: Float32Array = new Float32Array(16);
|
||||||
values: Float32Array = new Float32Array(16);
|
values: Float32Array = new Float32Array(16);
|
||||||
|
|
||||||
private static xAxis: Vector3 = null;
|
private static xAxis = new Vector3();
|
||||||
private static yAxis: Vector3 = null;
|
private static yAxis = new Vector3();
|
||||||
private static zAxis: Vector3 = null;
|
private static zAxis = new Vector3();
|
||||||
private static tmpMatrix = new Matrix4();
|
private static tmpMatrix = new Matrix4();
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
@ -305,7 +306,6 @@ export class Matrix4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lookAt (position: Vector3, direction: Vector3, up: Vector3) {
|
lookAt (position: Vector3, direction: Vector3, up: Vector3) {
|
||||||
Matrix4.initTemps();
|
|
||||||
let xAxis = Matrix4.xAxis, yAxis = Matrix4.yAxis, zAxis = Matrix4.zAxis;
|
let xAxis = Matrix4.xAxis, yAxis = Matrix4.yAxis, zAxis = Matrix4.zAxis;
|
||||||
zAxis.setFrom(direction).normalize();
|
zAxis.setFrom(direction).normalize();
|
||||||
xAxis.setFrom(direction).normalize();
|
xAxis.setFrom(direction).normalize();
|
||||||
@ -331,10 +331,4 @@ export class Matrix4 {
|
|||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static initTemps () {
|
|
||||||
if (Matrix4.xAxis === null) Matrix4.xAxis = new Vector3();
|
|
||||||
if (Matrix4.yAxis === null) Matrix4.yAxis = new Vector3();
|
|
||||||
if (Matrix4.zAxis === null) Matrix4.zAxis = new Vector3();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,11 +35,11 @@ import { ManagedWebGLRenderingContext } from "./WebGL";
|
|||||||
export class Mesh implements Disposable, Restorable {
|
export class Mesh implements Disposable, Restorable {
|
||||||
private context: ManagedWebGLRenderingContext;
|
private context: ManagedWebGLRenderingContext;
|
||||||
private vertices: Float32Array;
|
private vertices: Float32Array;
|
||||||
private verticesBuffer: WebGLBuffer;
|
private verticesBuffer: WebGLBuffer | null = null;
|
||||||
private verticesLength = 0;
|
private verticesLength = 0;
|
||||||
private dirtyVertices = false;
|
private dirtyVertices = false;
|
||||||
private indices: Uint16Array;
|
private indices: Uint16Array;
|
||||||
private indicesBuffer: WebGLBuffer;
|
private indicesBuffer: WebGLBuffer | null = null;
|
||||||
private indicesLength = 0;
|
private indicesLength = 0;
|
||||||
private dirtyIndices = false;
|
private dirtyIndices = false;
|
||||||
private elementsPerVertex = 0;
|
private elementsPerVertex = 0;
|
||||||
|
|||||||
@ -35,17 +35,17 @@ import { ManagedWebGLRenderingContext } from "./WebGL";
|
|||||||
|
|
||||||
export class PolygonBatcher implements Disposable {
|
export class PolygonBatcher implements Disposable {
|
||||||
private context: ManagedWebGLRenderingContext;
|
private context: ManagedWebGLRenderingContext;
|
||||||
private drawCalls: number;
|
private drawCalls = 0;
|
||||||
private isDrawing = false;
|
private isDrawing = false;
|
||||||
private mesh: Mesh;
|
private mesh: Mesh;
|
||||||
private shader: Shader = null;
|
private shader: Shader | null = null;
|
||||||
private lastTexture: GLTexture = null;
|
private lastTexture: GLTexture | null = null;
|
||||||
private verticesLength = 0;
|
private verticesLength = 0;
|
||||||
private indicesLength = 0;
|
private indicesLength = 0;
|
||||||
private srcColorBlend: number;
|
private srcColorBlend: number;
|
||||||
private srcAlphaBlend: number;
|
private srcAlphaBlend: number;
|
||||||
private dstBlend: number;
|
private dstBlend: number;
|
||||||
private cullWasEnabled: boolean;
|
private cullWasEnabled = false;
|
||||||
|
|
||||||
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, twoColorTint: boolean = true, maxVertices: number = 10920) {
|
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, twoColorTint: boolean = true, maxVertices: number = 10920) {
|
||||||
if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
|
if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
|
||||||
@ -110,7 +110,8 @@ export class PolygonBatcher implements Disposable {
|
|||||||
|
|
||||||
flush () {
|
flush () {
|
||||||
if (this.verticesLength == 0) return;
|
if (this.verticesLength == 0) return;
|
||||||
|
if (!this.lastTexture) throw new Error("No texture set.");
|
||||||
|
if (!this.shader) throw new Error("No shader set.");
|
||||||
this.lastTexture.bind();
|
this.lastTexture.bind();
|
||||||
this.mesh.draw(this.shader, this.context.gl.TRIANGLES);
|
this.mesh.draw(this.shader, this.context.gl.TRIANGLES);
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,7 @@ export class SceneRenderer implements Disposable {
|
|||||||
private batcherShader: Shader;
|
private batcherShader: Shader;
|
||||||
private shapes: ShapeRenderer;
|
private shapes: ShapeRenderer;
|
||||||
private shapesShader: Shader;
|
private shapesShader: Shader;
|
||||||
private activeRenderer: PolygonBatcher | ShapeRenderer | SkeletonDebugRenderer;
|
private activeRenderer: PolygonBatcher | ShapeRenderer | SkeletonDebugRenderer | null = null;
|
||||||
skeletonRenderer: SkeletonRenderer;
|
skeletonRenderer: SkeletonRenderer;
|
||||||
skeletonDebugRenderer: SkeletonDebugRenderer;
|
skeletonDebugRenderer: SkeletonDebugRenderer;
|
||||||
|
|
||||||
@ -92,15 +92,15 @@ export class SceneRenderer implements Disposable {
|
|||||||
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd);
|
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawSkeletonDebug (skeleton: Skeleton, premultipliedAlpha = false, ignoredBones: Array<string> = null) {
|
drawSkeletonDebug (skeleton: Skeleton, premultipliedAlpha = false, ignoredBones?: Array<string>) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.skeletonDebugRenderer.premultipliedAlpha = premultipliedAlpha;
|
this.skeletonDebugRenderer.premultipliedAlpha = premultipliedAlpha;
|
||||||
this.skeletonDebugRenderer.draw(this.shapes, skeleton, ignoredBones);
|
this.skeletonDebugRenderer.draw(this.shapes, skeleton, ignoredBones);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawTexture (texture: GLTexture, x: number, y: number, width: number, height: number, color: Color = null) {
|
drawTexture (texture: GLTexture, x: number, y: number, width: number, height: number, color?: Color) {
|
||||||
this.enableRenderer(this.batcher);
|
this.enableRenderer(this.batcher);
|
||||||
if (color === null) color = WHITE;
|
if (!color) color = WHITE;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
quad[i++] = x;
|
quad[i++] = x;
|
||||||
quad[i++] = y;
|
quad[i++] = y;
|
||||||
@ -161,9 +161,9 @@ export class SceneRenderer implements Disposable {
|
|||||||
this.batcher.draw(texture, quad, QUAD_TRIANGLES);
|
this.batcher.draw(texture, quad, QUAD_TRIANGLES);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawTextureUV (texture: GLTexture, x: number, y: number, width: number, height: number, u: number, v: number, u2: number, v2: number, color: Color = null) {
|
drawTextureUV (texture: GLTexture, x: number, y: number, width: number, height: number, u: number, v: number, u2: number, v2: number, color?: Color) {
|
||||||
this.enableRenderer(this.batcher);
|
this.enableRenderer(this.batcher);
|
||||||
if (color === null) color = WHITE;
|
if (!color) color = WHITE;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
quad[i++] = x;
|
quad[i++] = x;
|
||||||
quad[i++] = y;
|
quad[i++] = y;
|
||||||
@ -224,9 +224,9 @@ export class SceneRenderer implements Disposable {
|
|||||||
this.batcher.draw(texture, quad, QUAD_TRIANGLES);
|
this.batcher.draw(texture, quad, QUAD_TRIANGLES);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawTextureRotated (texture: GLTexture, x: number, y: number, width: number, height: number, pivotX: number, pivotY: number, angle: number, color: Color = null) {
|
drawTextureRotated (texture: GLTexture, x: number, y: number, width: number, height: number, pivotX: number, pivotY: number, angle: number, color?: Color) {
|
||||||
this.enableRenderer(this.batcher);
|
this.enableRenderer(this.batcher);
|
||||||
if (color === null) color = WHITE;
|
if (!color) color = WHITE;
|
||||||
|
|
||||||
// bottom left and top right corner points relative to origin
|
// bottom left and top right corner points relative to origin
|
||||||
let worldOriginX = x + pivotX;
|
let worldOriginX = x + pivotX;
|
||||||
@ -354,9 +354,9 @@ export class SceneRenderer implements Disposable {
|
|||||||
this.batcher.draw(texture, quad, QUAD_TRIANGLES);
|
this.batcher.draw(texture, quad, QUAD_TRIANGLES);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawRegion (region: TextureAtlasRegion, x: number, y: number, width: number, height: number, color: Color = null) {
|
drawRegion (region: TextureAtlasRegion, x: number, y: number, width: number, height: number, color?: Color) {
|
||||||
this.enableRenderer(this.batcher);
|
this.enableRenderer(this.batcher);
|
||||||
if (color === null) color = WHITE;
|
if (!color) color = WHITE;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
quad[i++] = x;
|
quad[i++] = x;
|
||||||
quad[i++] = y;
|
quad[i++] = y;
|
||||||
@ -417,42 +417,42 @@ export class SceneRenderer implements Disposable {
|
|||||||
this.batcher.draw(<GLTexture>region.page.texture, quad, QUAD_TRIANGLES);
|
this.batcher.draw(<GLTexture>region.page.texture, quad, QUAD_TRIANGLES);
|
||||||
}
|
}
|
||||||
|
|
||||||
line (x: number, y: number, x2: number, y2: number, color: Color = null, color2: Color = null) {
|
line (x: number, y: number, x2: number, y2: number, color?: Color, color2?: Color) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.shapes.line(x, y, x2, y2, color);
|
this.shapes.line(x, y, x2, y2, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
triangle (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, color: Color = null, color2: Color = null, color3: Color = null) {
|
triangle (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, color?: Color, color2?: Color, color3?: Color) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.shapes.triangle(filled, x, y, x2, y2, x3, y3, color, color2, color3);
|
this.shapes.triangle(filled, x, y, x2, y2, x3, y3, color, color2, color3);
|
||||||
}
|
}
|
||||||
|
|
||||||
quad (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, color: Color = null, color2: Color = null, color3: Color = null, color4: Color = null) {
|
quad (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, color?: Color, color2?: Color, color3?: Color, color4?: Color) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.shapes.quad(filled, x, y, x2, y2, x3, y3, x4, y4, color, color2, color3, color4);
|
this.shapes.quad(filled, x, y, x2, y2, x3, y3, x4, y4, color, color2, color3, color4);
|
||||||
}
|
}
|
||||||
|
|
||||||
rect (filled: boolean, x: number, y: number, width: number, height: number, color: Color = null) {
|
rect (filled: boolean, x: number, y: number, width: number, height: number, color?: Color) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.shapes.rect(filled, x, y, width, height, color);
|
this.shapes.rect(filled, x, y, width, height, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
rectLine (filled: boolean, x1: number, y1: number, x2: number, y2: number, width: number, color: Color = null) {
|
rectLine (filled: boolean, x1: number, y1: number, x2: number, y2: number, width: number, color?: Color) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.shapes.rectLine(filled, x1, y1, x2, y2, width, color);
|
this.shapes.rectLine(filled, x1, y1, x2, y2, width, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
polygon (polygonVertices: ArrayLike<number>, offset: number, count: number, color: Color = null) {
|
polygon (polygonVertices: ArrayLike<number>, offset: number, count: number, color?: Color) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.shapes.polygon(polygonVertices, offset, count, color);
|
this.shapes.polygon(polygonVertices, offset, count, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
circle (filled: boolean, x: number, y: number, radius: number, color: Color = null, segments: number = 0) {
|
circle (filled: boolean, x: number, y: number, radius: number, color?: Color, segments: number = 0) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.shapes.circle(filled, x, y, radius, color, segments);
|
this.shapes.circle(filled, x, y, radius, color, segments);
|
||||||
}
|
}
|
||||||
|
|
||||||
curve (x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, segments: number, color: Color = null) {
|
curve (x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, segments: number, color?: Color) {
|
||||||
this.enableRenderer(this.shapes);
|
this.enableRenderer(this.shapes);
|
||||||
this.shapes.curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments, color);
|
this.shapes.curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments, color);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,11 +39,11 @@ export class Shader implements Disposable, Restorable {
|
|||||||
public static SAMPLER = "u_texture";
|
public static SAMPLER = "u_texture";
|
||||||
|
|
||||||
private context: ManagedWebGLRenderingContext;
|
private context: ManagedWebGLRenderingContext;
|
||||||
private vs: WebGLShader = null;
|
private vs: WebGLShader | null = null;
|
||||||
private vsSource: string;
|
private vsSource: string;
|
||||||
private fs: WebGLShader = null;
|
private fs: WebGLShader | null = null;
|
||||||
private fsSource: string;
|
private fsSource: string;
|
||||||
private program: WebGLProgram = null;
|
private program: WebGLProgram | null = null;
|
||||||
private tmp2x2: Float32Array = new Float32Array(2 * 2);
|
private tmp2x2: Float32Array = new Float32Array(2 * 2);
|
||||||
private tmp3x3: Float32Array = new Float32Array(3 * 3);
|
private tmp3x3: Float32Array = new Float32Array(3 * 3);
|
||||||
private tmp4x4: Float32Array = new Float32Array(4 * 4);
|
private tmp4x4: Float32Array = new Float32Array(4 * 4);
|
||||||
@ -66,7 +66,9 @@ export class Shader implements Disposable, Restorable {
|
|||||||
let gl = this.context.gl;
|
let gl = this.context.gl;
|
||||||
try {
|
try {
|
||||||
this.vs = this.compileShader(gl.VERTEX_SHADER, this.vertexShader);
|
this.vs = this.compileShader(gl.VERTEX_SHADER, this.vertexShader);
|
||||||
|
if (!this.vs) throw new Error("Couldn't compile vertex shader.");
|
||||||
this.fs = this.compileShader(gl.FRAGMENT_SHADER, this.fragmentShader);
|
this.fs = this.compileShader(gl.FRAGMENT_SHADER, this.fragmentShader);
|
||||||
|
if (!this.fs) throw new Error("Couldn#t compile fragment shader.");
|
||||||
this.program = this.compileProgram(this.vs, this.fs);
|
this.program = this.compileProgram(this.vs, this.fs);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.dispose();
|
this.dispose();
|
||||||
@ -77,6 +79,7 @@ export class Shader implements Disposable, Restorable {
|
|||||||
private compileShader (type: number, source: string) {
|
private compileShader (type: number, source: string) {
|
||||||
let gl = this.context.gl;
|
let gl = this.context.gl;
|
||||||
let shader = gl.createShader(type);
|
let shader = gl.createShader(type);
|
||||||
|
if (!shader) throw new Error("Couldn't create shader.");
|
||||||
gl.shaderSource(shader, source);
|
gl.shaderSource(shader, source);
|
||||||
gl.compileShader(shader);
|
gl.compileShader(shader);
|
||||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||||
@ -90,6 +93,7 @@ export class Shader implements Disposable, Restorable {
|
|||||||
private compileProgram (vs: WebGLShader, fs: WebGLShader) {
|
private compileProgram (vs: WebGLShader, fs: WebGLShader) {
|
||||||
let gl = this.context.gl;
|
let gl = this.context.gl;
|
||||||
let program = gl.createProgram();
|
let program = gl.createProgram();
|
||||||
|
if (!program) throw new Error("Couldn't compile program.");
|
||||||
gl.attachShader(program, vs);
|
gl.attachShader(program, vs);
|
||||||
gl.attachShader(program, fs);
|
gl.attachShader(program, fs);
|
||||||
gl.linkProgram(program);
|
gl.linkProgram(program);
|
||||||
@ -152,8 +156,9 @@ export class Shader implements Disposable, Restorable {
|
|||||||
gl.uniformMatrix4fv(this.getUniformLocation(uniform), false, this.tmp4x4);
|
gl.uniformMatrix4fv(this.getUniformLocation(uniform), false, this.tmp4x4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUniformLocation (uniform: string): WebGLUniformLocation {
|
public getUniformLocation (uniform: string): WebGLUniformLocation | null {
|
||||||
let gl = this.context.gl;
|
let gl = this.context.gl;
|
||||||
|
if (!this.program) throw new Error("Shader not compiled.");
|
||||||
let location = gl.getUniformLocation(this.program, uniform);
|
let location = gl.getUniformLocation(this.program, uniform);
|
||||||
if (!location && !gl.isContextLost()) throw new Error(`Couldn't find location for uniform ${uniform}`);
|
if (!location && !gl.isContextLost()) throw new Error(`Couldn't find location for uniform ${uniform}`);
|
||||||
return location;
|
return location;
|
||||||
@ -161,6 +166,7 @@ export class Shader implements Disposable, Restorable {
|
|||||||
|
|
||||||
public getAttributeLocation (attribute: string): number {
|
public getAttributeLocation (attribute: string): number {
|
||||||
let gl = this.context.gl;
|
let gl = this.context.gl;
|
||||||
|
if (!this.program) throw new Error("Shader not compiled.");
|
||||||
let location = gl.getAttribLocation(this.program, attribute);
|
let location = gl.getAttribLocation(this.program, attribute);
|
||||||
if (location == -1 && !gl.isContextLost()) throw new Error(`Couldn't find location for attribute ${attribute}`);
|
if (location == -1 && !gl.isContextLost()) throw new Error(`Couldn't find location for attribute ${attribute}`);
|
||||||
return location;
|
return location;
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export class ShapeRenderer implements Disposable {
|
|||||||
private mesh: Mesh;
|
private mesh: Mesh;
|
||||||
private shapeType = ShapeType.Filled;
|
private shapeType = ShapeType.Filled;
|
||||||
private color = new Color(1, 1, 1, 1);
|
private color = new Color(1, 1, 1, 1);
|
||||||
private shader: Shader;
|
private shader: Shader | null = null;
|
||||||
private vertexIndex = 0;
|
private vertexIndex = 0;
|
||||||
private tmp = new Vector2();
|
private tmp = new Vector2();
|
||||||
private srcColorBlend: number;
|
private srcColorBlend: number;
|
||||||
@ -85,28 +85,28 @@ export class ShapeRenderer implements Disposable {
|
|||||||
this.color.set(r, g, b, a);
|
this.color.set(r, g, b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
point (x: number, y: number, color: Color = null) {
|
point (x: number, y: number, color?: Color) {
|
||||||
this.check(ShapeType.Point, 1);
|
this.check(ShapeType.Point, 1);
|
||||||
if (color === null) color = this.color;
|
if (!color) color = this.color;
|
||||||
this.vertex(x, y, color);
|
this.vertex(x, y, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
line (x: number, y: number, x2: number, y2: number, color: Color = null) {
|
line (x: number, y: number, x2: number, y2: number, color?: Color) {
|
||||||
this.check(ShapeType.Line, 2);
|
this.check(ShapeType.Line, 2);
|
||||||
let vertices = this.mesh.getVertices();
|
let vertices = this.mesh.getVertices();
|
||||||
let idx = this.vertexIndex;
|
let idx = this.vertexIndex;
|
||||||
if (color === null) color = this.color;
|
if (!color) color = this.color;
|
||||||
this.vertex(x, y, color);
|
this.vertex(x, y, color);
|
||||||
this.vertex(x2, y2, color);
|
this.vertex(x2, y2, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
triangle (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, color: Color = null, color2: Color = null, color3: Color = null) {
|
triangle (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, color?: Color, color2?: Color, color3?: Color) {
|
||||||
this.check(filled ? ShapeType.Filled : ShapeType.Line, 3);
|
this.check(filled ? ShapeType.Filled : ShapeType.Line, 3);
|
||||||
let vertices = this.mesh.getVertices();
|
let vertices = this.mesh.getVertices();
|
||||||
let idx = this.vertexIndex;
|
let idx = this.vertexIndex;
|
||||||
if (color === null) color = this.color;
|
if (!color) color = this.color;
|
||||||
if (color2 === null) color2 = this.color;
|
if (!color2) color2 = this.color;
|
||||||
if (color3 === null) color3 = this.color;
|
if (!color3) color3 = this.color;
|
||||||
if (filled) {
|
if (filled) {
|
||||||
this.vertex(x, y, color);
|
this.vertex(x, y, color);
|
||||||
this.vertex(x2, y2, color2);
|
this.vertex(x2, y2, color2);
|
||||||
@ -123,14 +123,14 @@ export class ShapeRenderer implements Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
quad (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, color: Color = null, color2: Color = null, color3: Color = null, color4: Color = null) {
|
quad (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, color?: Color, color2?: Color, color3?: Color, color4?: Color) {
|
||||||
this.check(filled ? ShapeType.Filled : ShapeType.Line, 3);
|
this.check(filled ? ShapeType.Filled : ShapeType.Line, 3);
|
||||||
let vertices = this.mesh.getVertices();
|
let vertices = this.mesh.getVertices();
|
||||||
let idx = this.vertexIndex;
|
let idx = this.vertexIndex;
|
||||||
if (color === null) color = this.color;
|
if (!color) color = this.color;
|
||||||
if (color2 === null) color2 = this.color;
|
if (!color2) color2 = this.color;
|
||||||
if (color3 === null) color3 = this.color;
|
if (!color3) color3 = this.color;
|
||||||
if (color4 === null) color4 = this.color;
|
if (!color4) color4 = this.color;
|
||||||
if (filled) {
|
if (filled) {
|
||||||
this.vertex(x, y, color); this.vertex(x2, y2, color2); this.vertex(x3, y3, color3);
|
this.vertex(x, y, color); this.vertex(x2, y2, color2); this.vertex(x3, y3, color3);
|
||||||
this.vertex(x3, y3, color3); this.vertex(x4, y4, color4); this.vertex(x, y, color);
|
this.vertex(x3, y3, color3); this.vertex(x4, y4, color4); this.vertex(x, y, color);
|
||||||
@ -142,13 +142,13 @@ export class ShapeRenderer implements Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rect (filled: boolean, x: number, y: number, width: number, height: number, color: Color = null) {
|
rect (filled: boolean, x: number, y: number, width: number, height: number, color?: Color) {
|
||||||
this.quad(filled, x, y, x + width, y, x + width, y + height, x, y + height, color, color, color, color);
|
this.quad(filled, x, y, x + width, y, x + width, y + height, x, y + height, color, color, color, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
rectLine (filled: boolean, x1: number, y1: number, x2: number, y2: number, width: number, color: Color = null) {
|
rectLine (filled: boolean, x1: number, y1: number, x2: number, y2: number, width: number, color?: Color) {
|
||||||
this.check(filled ? ShapeType.Filled : ShapeType.Line, 8);
|
this.check(filled ? ShapeType.Filled : ShapeType.Line, 8);
|
||||||
if (color === null) color = this.color;
|
if (!color) color = this.color;
|
||||||
let t = this.tmp.set(y2 - y1, x1 - x2);
|
let t = this.tmp.set(y2 - y1, x1 - x2);
|
||||||
t.normalize();
|
t.normalize();
|
||||||
width *= 0.5;
|
width *= 0.5;
|
||||||
@ -181,10 +181,10 @@ export class ShapeRenderer implements Disposable {
|
|||||||
this.line(x - size, y + size, x + size, y - size);
|
this.line(x - size, y + size, x + size, y - size);
|
||||||
}
|
}
|
||||||
|
|
||||||
polygon (polygonVertices: ArrayLike<number>, offset: number, count: number, color: Color = null) {
|
polygon (polygonVertices: ArrayLike<number>, offset: number, count: number, color?: Color) {
|
||||||
if (count < 3) throw new Error("Polygon must contain at least 3 vertices");
|
if (count < 3) throw new Error("Polygon must contain at least 3 vertices");
|
||||||
this.check(ShapeType.Line, count * 2);
|
this.check(ShapeType.Line, count * 2);
|
||||||
if (color === null) color = this.color;
|
if (color) color = this.color;
|
||||||
let vertices = this.mesh.getVertices();
|
let vertices = this.mesh.getVertices();
|
||||||
let idx = this.vertexIndex;
|
let idx = this.vertexIndex;
|
||||||
|
|
||||||
@ -210,15 +210,15 @@ export class ShapeRenderer implements Disposable {
|
|||||||
y2 = polygonVertices[i + 3];
|
y2 = polygonVertices[i + 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.vertex(x1, y1, color);
|
this.vertex(x1, y1, color!);
|
||||||
this.vertex(x2, y2, color);
|
this.vertex(x2, y2, color!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
circle (filled: boolean, x: number, y: number, radius: number, color: Color = null, segments: number = 0) {
|
circle (filled: boolean, x: number, y: number, radius: number, color?: Color, segments: number = 0) {
|
||||||
if (segments === 0) segments = Math.max(1, (6 * MathUtils.cbrt(radius)) | 0);
|
if (segments == 0) segments = Math.max(1, (6 * MathUtils.cbrt(radius)) | 0);
|
||||||
if (segments <= 0) throw new Error("segments must be > 0.");
|
if (segments <= 0) throw new Error("segments must be > 0.");
|
||||||
if (color === null) color = this.color;
|
if (!color) color = this.color;
|
||||||
let angle = 2 * MathUtils.PI / segments;
|
let angle = 2 * MathUtils.PI / segments;
|
||||||
let cos = Math.cos(angle);
|
let cos = Math.cos(angle);
|
||||||
let sin = Math.sin(angle);
|
let sin = Math.sin(angle);
|
||||||
@ -256,9 +256,9 @@ export class ShapeRenderer implements Disposable {
|
|||||||
this.vertex(x + cx, y + cy, color);
|
this.vertex(x + cx, y + cy, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
curve (x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, segments: number, color: Color = null) {
|
curve (x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, segments: number, color?: Color) {
|
||||||
this.check(ShapeType.Line, segments * 2 + 2);
|
this.check(ShapeType.Line, segments * 2 + 2);
|
||||||
if (color === null) color = this.color;
|
if (color) color = this.color;
|
||||||
|
|
||||||
// Algorithm from: http://www.antigrain.com/research/bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION
|
// Algorithm from: http://www.antigrain.com/research/bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION
|
||||||
let subdiv_step = 1 / segments;
|
let subdiv_step = 1 / segments;
|
||||||
@ -289,17 +289,17 @@ export class ShapeRenderer implements Disposable {
|
|||||||
let dddfy = tmp2y * pre5;
|
let dddfy = tmp2y * pre5;
|
||||||
|
|
||||||
while (segments-- > 0) {
|
while (segments-- > 0) {
|
||||||
this.vertex(fx, fy, color);
|
this.vertex(fx, fy, color!);
|
||||||
fx += dfx;
|
fx += dfx;
|
||||||
fy += dfy;
|
fy += dfy;
|
||||||
dfx += ddfx;
|
dfx += ddfx;
|
||||||
dfy += ddfy;
|
dfy += ddfy;
|
||||||
ddfx += dddfx;
|
ddfx += dddfx;
|
||||||
ddfy += dddfy;
|
ddfy += dddfy;
|
||||||
this.vertex(fx, fy, color);
|
this.vertex(fx, fy, color!);
|
||||||
}
|
}
|
||||||
this.vertex(fx, fy, color);
|
this.vertex(fx, fy, color!);
|
||||||
this.vertex(x2, y2, color);
|
this.vertex(x2, y2, color!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private vertex (x: number, y: number, color: Color) {
|
private vertex (x: number, y: number, color: Color) {
|
||||||
@ -324,6 +324,7 @@ export class ShapeRenderer implements Disposable {
|
|||||||
|
|
||||||
private flush () {
|
private flush () {
|
||||||
if (this.vertexIndex == 0) return;
|
if (this.vertexIndex == 0) return;
|
||||||
|
if (!this.shader) throw new Error("No shader set.");
|
||||||
this.mesh.setVerticesLength(this.vertexIndex);
|
this.mesh.setVerticesLength(this.vertexIndex);
|
||||||
this.mesh.draw(this.shader, this.shapeType);
|
this.mesh.draw(this.shader, this.shapeType);
|
||||||
this.vertexIndex = 0;
|
this.vertexIndex = 0;
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export class SkeletonDebugRenderer implements Disposable {
|
|||||||
this.context = context instanceof ManagedWebGLRenderingContext ? context : new ManagedWebGLRenderingContext(context);
|
this.context = context instanceof ManagedWebGLRenderingContext ? context : new ManagedWebGLRenderingContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw (shapes: ShapeRenderer, skeleton: Skeleton, ignoredBones: Array<string> = null) {
|
draw (shapes: ShapeRenderer, skeleton: Skeleton, ignoredBones?: Array<string>) {
|
||||||
let skeletonX = skeleton.x;
|
let skeletonX = skeleton.x;
|
||||||
let skeletonY = skeleton.y;
|
let skeletonY = skeleton.y;
|
||||||
let gl = this.context.gl;
|
let gl = this.context.gl;
|
||||||
|
|||||||
@ -41,13 +41,13 @@ export class SkeletonRenderer {
|
|||||||
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
||||||
|
|
||||||
premultipliedAlpha = false;
|
premultipliedAlpha = false;
|
||||||
vertexEffect: VertexEffect = null;
|
vertexEffect: VertexEffect | null = null;
|
||||||
private tempColor = new Color();
|
private tempColor = new Color();
|
||||||
private tempColor2 = new Color();
|
private tempColor2 = new Color();
|
||||||
private vertices: NumberArrayLike;
|
private vertices: NumberArrayLike;
|
||||||
private vertexSize = 2 + 2 + 4;
|
private vertexSize = 2 + 2 + 4;
|
||||||
private twoColorTint = false;
|
private twoColorTint = false;
|
||||||
private renderable: Renderable = new Renderable(null, 0, 0);
|
private renderable: Renderable = new Renderable([], 0, 0);
|
||||||
private clipper: SkeletonClipping = new SkeletonClipping();
|
private clipper: SkeletonClipping = new SkeletonClipping();
|
||||||
private temp = new Vector2();
|
private temp = new Vector2();
|
||||||
private temp2 = new Vector2();
|
private temp2 = new Vector2();
|
||||||
@ -65,7 +65,7 @@ export class SkeletonRenderer {
|
|||||||
let clipper = this.clipper;
|
let clipper = this.clipper;
|
||||||
let premultipliedAlpha = this.premultipliedAlpha;
|
let premultipliedAlpha = this.premultipliedAlpha;
|
||||||
let twoColorTint = this.twoColorTint;
|
let twoColorTint = this.twoColorTint;
|
||||||
let blendMode: BlendMode = null;
|
let blendMode: BlendMode | null = null;
|
||||||
|
|
||||||
let tempPos = this.temp;
|
let tempPos = this.temp;
|
||||||
let tempUv = this.temp2;
|
let tempUv = this.temp2;
|
||||||
@ -73,10 +73,10 @@ export class SkeletonRenderer {
|
|||||||
let tempDark = this.temp4;
|
let tempDark = this.temp4;
|
||||||
|
|
||||||
let renderable: Renderable = this.renderable;
|
let renderable: Renderable = this.renderable;
|
||||||
let uvs: NumberArrayLike = null;
|
let uvs: NumberArrayLike;
|
||||||
let triangles: Array<number> = null;
|
let triangles: Array<number>;
|
||||||
let drawOrder = skeleton.drawOrder;
|
let drawOrder = skeleton.drawOrder;
|
||||||
let attachmentColor: Color = null;
|
let attachmentColor: Color;
|
||||||
let skeletonColor = skeleton.color;
|
let skeletonColor = skeleton.color;
|
||||||
let vertexSize = twoColorTint ? 12 : 8;
|
let vertexSize = twoColorTint ? 12 : 8;
|
||||||
let inRange = false;
|
let inRange = false;
|
||||||
@ -103,7 +103,7 @@ export class SkeletonRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let attachment = slot.getAttachment();
|
let attachment = slot.getAttachment();
|
||||||
let texture: GLTexture = null;
|
let texture: GLTexture;
|
||||||
if (attachment instanceof RegionAttachment) {
|
if (attachment instanceof RegionAttachment) {
|
||||||
let region = <RegionAttachment>attachment;
|
let region = <RegionAttachment>attachment;
|
||||||
renderable.vertices = this.vertices;
|
renderable.vertices = this.vertices;
|
||||||
@ -112,7 +112,7 @@ export class SkeletonRenderer {
|
|||||||
region.computeWorldVertices(slot, renderable.vertices, 0, clippedVertexSize);
|
region.computeWorldVertices(slot, renderable.vertices, 0, clippedVertexSize);
|
||||||
triangles = SkeletonRenderer.QUAD_TRIANGLES;
|
triangles = SkeletonRenderer.QUAD_TRIANGLES;
|
||||||
uvs = region.uvs;
|
uvs = region.uvs;
|
||||||
texture = <GLTexture>(<TextureAtlasRegion>region.region.renderObject).page.texture;
|
texture = <GLTexture>(<TextureAtlasRegion>region.region!.renderObject).page.texture;
|
||||||
attachmentColor = region.color;
|
attachmentColor = region.color;
|
||||||
} else if (attachment instanceof MeshAttachment) {
|
} else if (attachment instanceof MeshAttachment) {
|
||||||
let mesh = <MeshAttachment>attachment;
|
let mesh = <MeshAttachment>attachment;
|
||||||
@ -124,7 +124,7 @@ export class SkeletonRenderer {
|
|||||||
}
|
}
|
||||||
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, renderable.vertices, 0, clippedVertexSize);
|
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, renderable.vertices, 0, clippedVertexSize);
|
||||||
triangles = mesh.triangles;
|
triangles = mesh.triangles;
|
||||||
texture = <GLTexture>(<TextureAtlasRegion>mesh.region.renderObject).page.texture;
|
texture = <GLTexture>(<TextureAtlasRegion>mesh.region!.renderObject).page.texture;
|
||||||
uvs = mesh.uvs;
|
uvs = mesh.uvs;
|
||||||
attachmentColor = mesh.color;
|
attachmentColor = mesh.color;
|
||||||
} else if (attachment instanceof ClippingAttachment) {
|
} else if (attachment instanceof ClippingAttachment) {
|
||||||
|
|||||||
@ -77,15 +77,15 @@ export class SpineCanvas {
|
|||||||
|
|
||||||
/** Constructs a new spine canvas, rendering to the provided HTML canvas. */
|
/** Constructs a new spine canvas, rendering to the provided HTML canvas. */
|
||||||
constructor (canvas: HTMLCanvasElement, config: SpineCanvasConfig) {
|
constructor (canvas: HTMLCanvasElement, config: SpineCanvasConfig) {
|
||||||
if (config.pathPrefix === undefined) config.pathPrefix = "";
|
if (!config.pathPrefix) config.pathPrefix = "";
|
||||||
if (config.app === undefined) config.app = {
|
if (!config.app) config.app = {
|
||||||
loadAssets: () => { },
|
loadAssets: () => { },
|
||||||
initialize: () => { },
|
initialize: () => { },
|
||||||
update: () => { },
|
update: () => { },
|
||||||
render: () => { },
|
render: () => { },
|
||||||
error: () => { },
|
error: () => { },
|
||||||
}
|
}
|
||||||
if (config.webglConfig === undefined) config.webglConfig = { alpha: true };
|
if (config.webglConfig) config.webglConfig = { alpha: true };
|
||||||
|
|
||||||
this.htmlCanvas = canvas;
|
this.htmlCanvas = canvas;
|
||||||
this.context = new ManagedWebGLRenderingContext(canvas, config.webglConfig);
|
this.context = new ManagedWebGLRenderingContext(canvas, config.webglConfig);
|
||||||
@ -94,21 +94,21 @@ export class SpineCanvas {
|
|||||||
this.assetManager = new AssetManager(this.context, config.pathPrefix);
|
this.assetManager = new AssetManager(this.context, config.pathPrefix);
|
||||||
this.input = new Input(canvas);
|
this.input = new Input(canvas);
|
||||||
|
|
||||||
config.app.loadAssets(this);
|
if (config.app.loadAssets) config.app.loadAssets(this);
|
||||||
|
|
||||||
let loop = () => {
|
let loop = () => {
|
||||||
requestAnimationFrame(loop);
|
requestAnimationFrame(loop);
|
||||||
this.time.update();
|
this.time.update();
|
||||||
config.app.update(this, this.time.delta);
|
if (config.app.update) config.app.update(this, this.time.delta);
|
||||||
config.app.render(this);
|
if (config.app.render) config.app.render(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
let waitForAssets = () => {
|
let waitForAssets = () => {
|
||||||
if (this.assetManager.isLoadingComplete()) {
|
if (this.assetManager.isLoadingComplete()) {
|
||||||
if (this.assetManager.hasErrors()) {
|
if (this.assetManager.hasErrors()) {
|
||||||
config.app.error(this, this.assetManager.getErrors());
|
if (config.app.error) config.app.error(this, this.assetManager.getErrors());
|
||||||
} else {
|
} else {
|
||||||
config.app.initialize(this);
|
if (config.app.initialize) config.app.initialize(this);
|
||||||
loop();
|
loop();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -34,27 +34,23 @@ export class ManagedWebGLRenderingContext {
|
|||||||
public gl: WebGLRenderingContext;
|
public gl: WebGLRenderingContext;
|
||||||
private restorables = new Array<Restorable>();
|
private restorables = new Array<Restorable>();
|
||||||
|
|
||||||
constructor (canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | EventTarget, contextConfig: any = { alpha: "true" }) {
|
constructor (canvasOrContext: HTMLCanvasElement | WebGLRenderingContext, contextConfig: any = { alpha: "true" }) {
|
||||||
if (!((canvasOrContext instanceof WebGLRenderingContext) || (typeof WebGL2RenderingContext !== 'undefined' && canvasOrContext instanceof WebGL2RenderingContext)))
|
if (!((canvasOrContext instanceof WebGLRenderingContext) || (typeof WebGL2RenderingContext !== 'undefined' && canvasOrContext instanceof WebGL2RenderingContext))) {
|
||||||
this.setupCanvas(canvasOrContext, contextConfig);
|
let canvas: HTMLCanvasElement = canvasOrContext;
|
||||||
else {
|
|
||||||
this.gl = canvasOrContext;
|
|
||||||
this.canvas = this.gl.canvas;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupCanvas (canvas: any, contextConfig: any) {
|
|
||||||
this.gl = <WebGLRenderingContext>(canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
|
this.gl = <WebGLRenderingContext>(canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
canvas.addEventListener("webglcontextlost", (e: any) => {
|
canvas.addEventListener("webglcontextlost", (e: any) => {
|
||||||
let event = <WebGLContextEvent>e;
|
let event = <WebGLContextEvent>e;
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener("webglcontextrestored", (e: any) => {
|
canvas.addEventListener("webglcontextrestored", (e: any) => {
|
||||||
for (let i = 0, n = this.restorables.length; i < n; i++)
|
for (let i = 0, n = this.restorables.length; i < n; i++)
|
||||||
this.restorables[i].restore();
|
this.restorables[i].restore();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
this.gl = canvasOrContext;
|
||||||
|
this.canvas = this.gl.canvas;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addRestorable (restorable: Restorable) {
|
addRestorable (restorable: Restorable) {
|
||||||
|
|||||||
@ -12,8 +12,7 @@
|
|||||||
],
|
],
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"moduleResolution": "node"
|
"moduleResolution": "node",
|
||||||
/*"strictNullChecks": true,
|
"strict": true
|
||||||
"strictPropertyInitialization": true*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +65,12 @@ void SSpineWidget::SetData(USpineWidget *Widget) {
|
|||||||
skeleton->setToSetupPose();
|
skeleton->setToSetupPose();
|
||||||
skeleton->updateWorldTransform();
|
skeleton->updateWorldTransform();
|
||||||
Vector<float> scratchBuffer;
|
Vector<float> scratchBuffer;
|
||||||
skeleton->getBounds(this->boundsMin.X, this->boundsMin.Y, this->boundsSize.X, this->boundsSize.Y, scratchBuffer);
|
float x, y, w, h;
|
||||||
|
skeleton->getBounds(x, y, w, h, scratchBuffer);
|
||||||
|
boundsMin.X = x;
|
||||||
|
boundsMin.Y = y;
|
||||||
|
boundsSize.X = w;
|
||||||
|
boundsSize.Y = h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -154,7 +154,7 @@ namespace Spine.Unity.Editor {
|
|||||||
|
|
||||||
void Clear () {
|
void Clear () {
|
||||||
preview.Clear();
|
preview.Clear();
|
||||||
targetSkeletonDataAsset.Clear();
|
SpineEditorUtilities.ClearSkeletonDataAsset(targetSkeletonDataAsset);
|
||||||
targetSkeletonData = null;
|
targetSkeletonData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,10 @@
|
|||||||
#define NEW_PREFAB_SYSTEM
|
#define NEW_PREFAB_SYSTEM
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if UNITY_2018_2_OR_NEWER
|
||||||
|
#define HAS_CULL_TRANSPARENT_MESH
|
||||||
|
#endif
|
||||||
|
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -469,6 +473,11 @@ namespace Spine.Unity.Editor {
|
|||||||
graphic.additiveMaterial = SkeletonGraphicInspector.DefaultSkeletonGraphicAdditiveMaterial;
|
graphic.additiveMaterial = SkeletonGraphicInspector.DefaultSkeletonGraphicAdditiveMaterial;
|
||||||
graphic.multiplyMaterial = SkeletonGraphicInspector.DefaultSkeletonGraphicMultiplyMaterial;
|
graphic.multiplyMaterial = SkeletonGraphicInspector.DefaultSkeletonGraphicMultiplyMaterial;
|
||||||
graphic.screenMaterial = SkeletonGraphicInspector.DefaultSkeletonGraphicScreenMaterial;
|
graphic.screenMaterial = SkeletonGraphicInspector.DefaultSkeletonGraphicScreenMaterial;
|
||||||
|
|
||||||
|
#if HAS_CULL_TRANSPARENT_MESH
|
||||||
|
var canvasRenderer = go.GetComponent<CanvasRenderer>();
|
||||||
|
canvasRenderer.cullTransparentMesh = false;
|
||||||
|
#endif
|
||||||
return go;
|
return go;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -493,7 +493,7 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Debug.LogFormat("Changes to '{0}' or atlas detected. Clearing SkeletonDataAsset: {1}", skeletonJSONPath, localPath);
|
Debug.LogFormat("Changes to '{0}' or atlas detected. Clearing SkeletonDataAsset: {1}", skeletonJSONPath, localPath);
|
||||||
skeletonDataAsset.Clear();
|
SpineEditorUtilities.ClearSkeletonDataAsset(skeletonDataAsset);
|
||||||
|
|
||||||
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(skeletonDataAsset));
|
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(skeletonDataAsset));
|
||||||
string lastHash = EditorPrefs.GetString(guid + "_hash");
|
string lastHash = EditorPrefs.GetString(guid + "_hash");
|
||||||
@ -978,7 +978,7 @@ namespace Spine.Unity.Editor {
|
|||||||
AssetDatabase.CreateAsset(skeletonDataAsset, filePath);
|
AssetDatabase.CreateAsset(skeletonDataAsset, filePath);
|
||||||
} else {
|
} else {
|
||||||
skeletonDataAsset.atlasAssets = atlasAssets;
|
skeletonDataAsset.atlasAssets = atlasAssets;
|
||||||
skeletonDataAsset.Clear();
|
SpineEditorUtilities.ClearSkeletonDataAsset(skeletonDataAsset);
|
||||||
}
|
}
|
||||||
var skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
var skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
||||||
if (skeletonData != null)
|
if (skeletonData != null)
|
||||||
|
|||||||
@ -96,7 +96,7 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skeletonDataAsset.Clear();
|
SpineEditorUtilities.ClearSkeletonDataAsset(skeletonDataAsset);
|
||||||
skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
||||||
if (anyMaterialsChanged)
|
if (anyMaterialsChanged)
|
||||||
ReloadSceneSkeletons(skeletonDataAsset);
|
ReloadSceneSkeletons(skeletonDataAsset);
|
||||||
@ -164,7 +164,7 @@ namespace Spine.Unity.Editor {
|
|||||||
|
|
||||||
var skinEntries = new List<Skin.SkinEntry>();
|
var skinEntries = new List<Skin.SkinEntry>();
|
||||||
|
|
||||||
skeletonDataAsset.Clear();
|
SpineEditorUtilities.ClearSkeletonDataAsset(skeletonDataAsset);
|
||||||
skeletonDataAsset.isUpgradingBlendModeMaterials = true;
|
skeletonDataAsset.isUpgradingBlendModeMaterials = true;
|
||||||
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
||||||
|
|
||||||
|
|||||||
@ -80,36 +80,24 @@ namespace Spine.Unity.Editor {
|
|||||||
// Here we save the skeletonGraphic.skeletonDataAsset asset path in order
|
// Here we save the skeletonGraphic.skeletonDataAsset asset path in order
|
||||||
// to restore it later.
|
// to restore it later.
|
||||||
var activeSkeletonGraphics = GameObject.FindObjectsOfType<SkeletonGraphic>();
|
var activeSkeletonGraphics = GameObject.FindObjectsOfType<SkeletonGraphic>();
|
||||||
foreach (var sg in activeSkeletonGraphics) {
|
foreach (var skeletonGraphic in activeSkeletonGraphics) {
|
||||||
var skeletonDataAsset = sg.skeletonDataAsset;
|
var skeletonDataAsset = skeletonGraphic.skeletonDataAsset;
|
||||||
if (skeletonDataAsset != null) {
|
if (skeletonDataAsset != null) {
|
||||||
var assetPath = AssetDatabase.GetAssetPath(skeletonDataAsset);
|
var assetPath = AssetDatabase.GetAssetPath(skeletonDataAsset);
|
||||||
var sgID = sg.GetInstanceID();
|
var sgID = skeletonGraphic.GetInstanceID();
|
||||||
savedSkeletonDataAssetAtSKeletonGraphicID[sgID] = assetPath;
|
savedSkeletonDataAssetAtSKeletonGraphicID[sgID] = assetPath;
|
||||||
skeletonDataAssetsToReload.Add(skeletonDataAsset);
|
skeletonDataAssetsToReload.Add(skeletonDataAsset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var sda in skeletonDataAssetsToReload) {
|
foreach (var skeletonDataAsset in skeletonDataAssetsToReload) {
|
||||||
sda.Clear();
|
ReloadSkeletonDataAsset(skeletonDataAsset, false);
|
||||||
sda.GetSkeletonData(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var sr in activeSkeletonRenderers) {
|
foreach (var skeletonRenderer in activeSkeletonRenderers)
|
||||||
var meshRenderer = sr.GetComponent<MeshRenderer>();
|
skeletonRenderer.Initialize(true);
|
||||||
var sharedMaterials = meshRenderer.sharedMaterials;
|
foreach (var skeletonGraphic in activeSkeletonGraphics)
|
||||||
foreach (var m in sharedMaterials) {
|
skeletonGraphic.Initialize(true);
|
||||||
if (m == null) {
|
|
||||||
sr.Initialize(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var sg in activeSkeletonGraphics) {
|
|
||||||
if (sg.mainTexture == null)
|
|
||||||
sg.Initialize(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReloadSceneSkeletonComponents (SkeletonDataAsset skeletonDataAsset) {
|
public static void ReloadSceneSkeletonComponents (SkeletonDataAsset skeletonDataAsset) {
|
||||||
@ -119,24 +107,35 @@ namespace Spine.Unity.Editor {
|
|||||||
if (EditorApplication.isPlayingOrWillChangePlaymode) return;
|
if (EditorApplication.isPlayingOrWillChangePlaymode) return;
|
||||||
|
|
||||||
var activeSkeletonRenderers = GameObject.FindObjectsOfType<SkeletonRenderer>();
|
var activeSkeletonRenderers = GameObject.FindObjectsOfType<SkeletonRenderer>();
|
||||||
foreach (var sr in activeSkeletonRenderers) {
|
foreach (var renderer in activeSkeletonRenderers) {
|
||||||
if (sr.isActiveAndEnabled && sr.skeletonDataAsset == skeletonDataAsset) sr.Initialize(true);
|
if (renderer.isActiveAndEnabled && renderer.skeletonDataAsset == skeletonDataAsset) renderer.Initialize(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeSkeletonGraphics = GameObject.FindObjectsOfType<SkeletonGraphic>();
|
var activeSkeletonGraphics = GameObject.FindObjectsOfType<SkeletonGraphic>();
|
||||||
foreach (var sg in activeSkeletonGraphics) {
|
foreach (var graphic in activeSkeletonGraphics) {
|
||||||
if (sg.isActiveAndEnabled && sg.skeletonDataAsset == skeletonDataAsset) sg.Initialize(true);
|
if (graphic.isActiveAndEnabled && graphic.skeletonDataAsset == skeletonDataAsset)
|
||||||
|
graphic.Initialize(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ClearAnimationReferenceAssets (SkeletonDataAsset skeletonDataAsset) {
|
||||||
|
ForEachAnimationReferenceAsset(skeletonDataAsset, (referenceAsset) => referenceAsset.Clear());
|
||||||
|
}
|
||||||
|
|
||||||
public static void ReloadAnimationReferenceAssets (SkeletonDataAsset skeletonDataAsset) {
|
public static void ReloadAnimationReferenceAssets (SkeletonDataAsset skeletonDataAsset) {
|
||||||
|
ForEachAnimationReferenceAsset(skeletonDataAsset, (referenceAsset) => referenceAsset.Initialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ForEachAnimationReferenceAsset (SkeletonDataAsset skeletonDataAsset,
|
||||||
|
System.Action<AnimationReferenceAsset> func) {
|
||||||
|
|
||||||
string[] guids = UnityEditor.AssetDatabase.FindAssets("t:AnimationReferenceAsset");
|
string[] guids = UnityEditor.AssetDatabase.FindAssets("t:AnimationReferenceAsset");
|
||||||
foreach (string guid in guids) {
|
foreach (string guid in guids) {
|
||||||
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
|
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
|
||||||
if (!string.IsNullOrEmpty(path)) {
|
if (!string.IsNullOrEmpty(path)) {
|
||||||
var referenceAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<AnimationReferenceAsset>(path);
|
var referenceAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<AnimationReferenceAsset>(path);
|
||||||
if (referenceAsset.SkeletonDataAsset == skeletonDataAsset)
|
if (referenceAsset.SkeletonDataAsset == skeletonDataAsset)
|
||||||
referenceAsset.Initialize();
|
func(referenceAsset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -266,14 +266,23 @@ namespace Spine.Unity.Editor {
|
|||||||
ReinitializeComponent(component);
|
ReinitializeComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReloadSkeletonDataAsset (SkeletonDataAsset skeletonDataAsset) {
|
public static void ClearSkeletonDataAsset (SkeletonDataAsset skeletonDataAsset) {
|
||||||
if (skeletonDataAsset != null) {
|
skeletonDataAsset.Clear();
|
||||||
|
DataReloadHandler.ClearAnimationReferenceAssets(skeletonDataAsset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReloadSkeletonDataAsset (SkeletonDataAsset skeletonDataAsset, bool clearAtlasAssets = true) {
|
||||||
|
if (skeletonDataAsset == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (clearAtlasAssets) {
|
||||||
foreach (AtlasAssetBase aa in skeletonDataAsset.atlasAssets) {
|
foreach (AtlasAssetBase aa in skeletonDataAsset.atlasAssets) {
|
||||||
if (aa != null) aa.Clear();
|
if (aa != null) aa.Clear();
|
||||||
}
|
}
|
||||||
skeletonDataAsset.Clear();
|
|
||||||
}
|
}
|
||||||
|
ClearSkeletonDataAsset(skeletonDataAsset);
|
||||||
skeletonDataAsset.GetSkeletonData(true);
|
skeletonDataAsset.GetSkeletonData(true);
|
||||||
|
DataReloadHandler.ReloadAnimationReferenceAssets(skeletonDataAsset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReinitializeComponent (SkeletonRenderer component) {
|
public static void ReinitializeComponent (SkeletonRenderer component) {
|
||||||
|
|||||||
@ -48,11 +48,16 @@ namespace Spine.Unity {
|
|||||||
if (animation == null)
|
if (animation == null)
|
||||||
Initialize();
|
Initialize();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return animation;
|
return animation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Clears the cached animation corresponding to a loaded SkeletonData object.
|
||||||
|
/// Use this to force a reload for the next time Animation is called.</summary>
|
||||||
|
public void Clear () {
|
||||||
|
animation = null;
|
||||||
|
}
|
||||||
|
|
||||||
public void Initialize () {
|
public void Initialize () {
|
||||||
if (skeletonDataAsset == null) return;
|
if (skeletonDataAsset == null) return;
|
||||||
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(AnimationReferenceAsset.QuietSkeletonData);
|
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(AnimationReferenceAsset.QuietSkeletonData);
|
||||||
|
|||||||
@ -169,13 +169,17 @@ namespace Spine.Unity {
|
|||||||
/// <summary>Add a SkeletonGraphic component to a GameObject.</summary>
|
/// <summary>Add a SkeletonGraphic component to a GameObject.</summary>
|
||||||
/// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
|
/// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
|
||||||
public static SkeletonGraphic AddSkeletonGraphicComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset, Material material) {
|
public static SkeletonGraphic AddSkeletonGraphicComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset, Material material) {
|
||||||
var c = gameObject.AddComponent<SkeletonGraphic>();
|
var skeletonGraphic = gameObject.AddComponent<SkeletonGraphic>();
|
||||||
if (skeletonDataAsset != null) {
|
if (skeletonDataAsset != null) {
|
||||||
c.material = material;
|
skeletonGraphic.material = material;
|
||||||
c.skeletonDataAsset = skeletonDataAsset;
|
skeletonGraphic.skeletonDataAsset = skeletonDataAsset;
|
||||||
c.Initialize(false);
|
skeletonGraphic.Initialize(false);
|
||||||
}
|
}
|
||||||
return c;
|
#if HAS_CULL_TRANSPARENT_MESH
|
||||||
|
var canvasRenderer = gameObject.GetComponent<CanvasRenderer>();
|
||||||
|
if (canvasRenderer) canvasRenderer.cullTransparentMesh = false;
|
||||||
|
#endif
|
||||||
|
return skeletonGraphic;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user