mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[ts] Added spine-ts runtime for TypeScript and JavaScript
This commit is contained in:
parent
b7c9a1cc20
commit
698c4a1661
28
LICENSE
28
LICENSE
@ -1,17 +1,17 @@
|
||||
Spine Runtimes Software License
|
||||
Version 2.4
|
||||
Version 2.5
|
||||
|
||||
Copyright (c) 2013-2016, Esoteric Software
|
||||
All rights reserved.
|
||||
|
||||
You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
non-transferable license to use, install, execute and perform the Spine
|
||||
Runtimes Software (the "Software") and derivative works solely for personal
|
||||
or internal use. Without the written permission of Esoteric Software (see
|
||||
Section 2 of the Spine Software License Agreement), you may not (a) modify,
|
||||
translate, adapt or otherwise create derivative works, improvements of
|
||||
the Software or develop new applications using the Software or (b) remove,
|
||||
delete, alter or obscure any trademarks or any copyright, trademark, patent
|
||||
You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
non-transferable license to use, install, execute, and perform the Spine
|
||||
Runtimes software and derivative works solely for personal or internal
|
||||
use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
or other intellectual property or proprietary rights notices on or in the
|
||||
Software, including any copy thereof. Redistributions in binary or source
|
||||
form must include this license and terms.
|
||||
@ -21,8 +21,8 @@ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
@ -3,7 +3,7 @@
|
||||
<AS3LibraryFolder>lib</AS3LibraryFolder>
|
||||
<AS3Classpath generateProblems="true" sdkBased="true" type="lib" useAsSharedCode="false">frameworks/libs/player/{playerVersion}/playerglobal.swc</AS3Classpath>
|
||||
<AS3Classpath generateProblems="true" sdkBased="false" type="source" useAsSharedCode="false">src</AS3Classpath>
|
||||
<AS3Classpath generateProblems="true" sdkBased="false" type="lib" useAsSharedCode="false">lib/starling-2.0.1.swc</AS3Classpath>
|
||||
<AS3Classpath generateProblems="true" sdkBased="false" type="lib" useAsSharedCode="false">lib/spine-as3.swc</AS3Classpath>
|
||||
<AS3Classpath generateProblems="true" sdkBased="false" type="lib" useAsSharedCode="false">lib/spine-starling.swc</AS3Classpath>
|
||||
<AS3Classpath generateProblems="true" sdkBased="false" type="lib" useAsSharedCode="false">lib/starling-2.0.1.swc</AS3Classpath>
|
||||
</AS3Classpath>
|
||||
|
||||
69
spine-ts/README.md
Normal file
69
spine-ts/README.md
Normal file
@ -0,0 +1,69 @@
|
||||
# spine-ts
|
||||
|
||||
The spine-ts runtime provides functionality to load and manipulate [Spine](http://esotericsoftware.com) skeletal animation data using TypeScript and JavaScript. spine-ts is split
|
||||
up into multiple modules:
|
||||
|
||||
1. **Core**: `core/`, the core classes to load and process Spine models
|
||||
1. **WebGL**: `webgl/`, a self-contained WebGL backend, build on the core classes
|
||||
1. **Widget**: `widget/`, a self-contained widget to easily display Spine animations on your website, build on core classes & WebGL backend.
|
||||
|
||||
While the source code for the core library and backends is written in TypeScript, all code is compiled to easily consumable JavaScript.
|
||||
|
||||
## Licensing
|
||||
This Spine Runtime may only be used for personal or internal use, typically to evaluate Spine before purchasing. If you would like to incorporate a Spine Runtime into your applications, distribute software containing a Spine Runtime, or modify a Spine Runtime, then you will need a valid [Spine license](https://esotericsoftware.com/spine-purchase). Please see the [Spine Runtimes Software License](https://github.com/EsotericSoftware/spine-runtimes/blob/master/LICENSE) for detailed information.
|
||||
|
||||
The Spine Runtimes are developed with the intent to be used with data exported from Spine. By purchasing Spine, `Section 2` of the [Spine Software License](https://esotericsoftware.com/files/license.txt) grants the right to create and distribute derivative works of the Spine Runtimes.
|
||||
|
||||
## Spine version
|
||||
|
||||
spine-ts works with data exported from the latest Spine version.
|
||||
|
||||
spine-ts supports all Spine features.
|
||||
|
||||
spine-ts does not yet support loading the binary format.
|
||||
|
||||
## Usage
|
||||
1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip).
|
||||
2. To use only the core library without rendering support, include the `build/spine-core.js` file in your project.
|
||||
3. To use the WebGL backend, include the `spine-webgl.js` file in your project.
|
||||
4. To use the Widget, include `spine-widget.js` file in your project.
|
||||
|
||||
All `*.js` files are self-contained and include both the core and respective backend classes.
|
||||
|
||||
If you write your app with TypeScript, additionally copy the corresponding `build/spine-*.d.ts` file to your project.
|
||||
|
||||
## Example
|
||||
To run the examples, spawn a light-weight web server in the root of spine-ts, then navigate to the respective
|
||||
`index.html` file. E.g.:
|
||||
|
||||
```
|
||||
cd spine-ts
|
||||
python -m SimpleHTTPServer
|
||||
````
|
||||
|
||||
Then open `http://localhost:8000/webgl/example` or `http://localhost:8000/widget/example` in your browser.
|
||||
|
||||
## Development Setup
|
||||
The spine-ts runtime and the various backends are implemented in TypeScript for greater maintainability and better tooling support. To
|
||||
setup a development environment, follow these steps.
|
||||
|
||||
1. Install [NPM](https://nodejs.org/en/download/) and make sure it's available on the command line
|
||||
2. On the command line, Install the TypeScript compiler via `npm install -g typescript`
|
||||
3. Install [Visual Studio Code](https://code.visualstudio.com/)
|
||||
4. On the command line, change into the `spine-ts` directory
|
||||
5. Start the TypeScript compiler in watcher mode for the backend you want to work on:
|
||||
1. **Core**: `tsc -w -p tsconfig.core.json`, builds `core/src`, outputs `build/spine-core.js|d.ts|js.map`
|
||||
2. **WebGL**: `tsc -w -p tsconfig.webgl.json`, builds `core/src` and `webgl/src`, outputs `build/spine-webgl.js|d.ts|js.map`
|
||||
3. **Widget**: `tsc -w -p tsconfig.widget.json`, builds `core/src` and `widget/src`, outputs `build/spine-widget.js|d.ts|js.map`
|
||||
6. Open the `spine-ts` folder in Visual Studio Code. VS Code will use the `tsconfig.json` file all source files from core and all
|
||||
backends for your development pleasure. The actual JavaScript output is still created by the command line TypeScript compiler process from the previous step.
|
||||
|
||||
Each backend contains an `example/` folder with an `index.html` file that demonstrates the respective backend. For development, we
|
||||
suggest to run a HTTP server in the root of `spine-ts`, e.g.
|
||||
|
||||
```
|
||||
cd spine-ts
|
||||
python -m SimpleHTTPServer
|
||||
```
|
||||
|
||||
Then navigate to `http://localhost:8000/webgl/example` or `http://localhost:8000/widget/example`
|
||||
2502
spine-ts/build/spine-all.d.ts
vendored
Normal file
2502
spine-ts/build/spine-all.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6877
spine-ts/build/spine-all.js
Normal file
6877
spine-ts/build/spine-all.js
Normal file
File diff suppressed because it is too large
Load Diff
1
spine-ts/build/spine-all.js.map
Normal file
1
spine-ts/build/spine-all.js.map
Normal file
File diff suppressed because one or more lines are too long
2502
spine-ts/build/spine-core.d.ts
vendored
Normal file
2502
spine-ts/build/spine-core.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6877
spine-ts/build/spine-core.js
Normal file
6877
spine-ts/build/spine-core.js
Normal file
File diff suppressed because it is too large
Load Diff
1
spine-ts/build/spine-core.js.map
Normal file
1
spine-ts/build/spine-core.js.map
Normal file
File diff suppressed because one or more lines are too long
2502
spine-ts/build/spine-webgl.d.ts
vendored
Normal file
2502
spine-ts/build/spine-webgl.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6877
spine-ts/build/spine-webgl.js
Normal file
6877
spine-ts/build/spine-webgl.js
Normal file
File diff suppressed because it is too large
Load Diff
1
spine-ts/build/spine-webgl.js.map
Normal file
1
spine-ts/build/spine-webgl.js.map
Normal file
File diff suppressed because one or more lines are too long
2502
spine-ts/build/spine-widget.d.ts
vendored
Normal file
2502
spine-ts/build/spine-widget.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6877
spine-ts/build/spine-widget.js
Normal file
6877
spine-ts/build/spine-widget.js
Normal file
File diff suppressed because it is too large
Load Diff
1
spine-ts/build/spine-widget.js.map
Normal file
1
spine-ts/build/spine-widget.js.map
Normal file
File diff suppressed because one or more lines are too long
816
spine-ts/core/src/Animation.ts
Normal file
816
spine-ts/core/src/Animation.ts
Normal file
@ -0,0 +1,816 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class Animation {
|
||||
name: string;
|
||||
timelines: Array<Timeline>;
|
||||
duration: number;
|
||||
|
||||
constructor (name: string, timelines: Array<Timeline>, duration: number) {
|
||||
if (name == null) throw new Error("name cannot be null.");
|
||||
if (timelines == null) throw new Error("timelines cannot be null.");
|
||||
this.name = name;
|
||||
this.timelines = timelines;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>) {
|
||||
if (skeleton == null) throw new Error("skeleton cannot be null.");
|
||||
|
||||
if (loop && this.duration != 0) {
|
||||
time %= this.duration;
|
||||
if (lastTime > 0) lastTime %= this.duration;
|
||||
}
|
||||
|
||||
let timelines = this.timelines;
|
||||
for (let i = 0, n = timelines.length; i < n; i++)
|
||||
timelines[i].apply(skeleton, lastTime, time, events, 1);
|
||||
}
|
||||
|
||||
mix (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number) {
|
||||
if (skeleton == null) throw new Error("skeleton cannot be null.");
|
||||
|
||||
if (loop && this.duration != 0) {
|
||||
time %= this.duration;
|
||||
if (lastTime > 0) lastTime %= this.duration;
|
||||
}
|
||||
|
||||
let timelines = this.timelines;
|
||||
for (let i = 0, n = timelines.length; i < n; i++)
|
||||
timelines[i].apply(skeleton, lastTime, time, events, alpha);
|
||||
}
|
||||
|
||||
static binarySearch (values: ArrayLike<number>, target: number, step: number = 1) {
|
||||
let low = 0;
|
||||
let high = values.length / step - 2;
|
||||
if (high == 0) return step;
|
||||
let current = high >>> 1;
|
||||
while (true) {
|
||||
if (values[(current + 1) * step] <= target)
|
||||
low = current + 1;
|
||||
else
|
||||
high = current;
|
||||
if (low == high) return (low + 1) * step;
|
||||
current = (low + high) >>> 1;
|
||||
}
|
||||
}
|
||||
|
||||
static linearSearch (values: ArrayLike<number>, target: number, step: number) {
|
||||
for (let i = 0, last = values.length - step; i <= last; i += step)
|
||||
if (values[i] > target) return i;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Timeline {
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number): void;
|
||||
}
|
||||
|
||||
export abstract class CurveTimeline implements Timeline {
|
||||
static LINEAR = 0; static STEPPED = 1; static BEZIER = 2;
|
||||
static BEZIER_SIZE = 10 * 2 - 1;
|
||||
|
||||
private curves: ArrayLike<number>; // type, x, y, ...
|
||||
|
||||
constructor (frameCount: number) {
|
||||
if (frameCount <= 0) throw new Error("frameCount must be > 0: " + frameCount);
|
||||
this.curves = Utils.newFloatArray((frameCount - 1) * CurveTimeline.BEZIER_SIZE);
|
||||
}
|
||||
|
||||
getFrameCount () {
|
||||
return this.curves.length / CurveTimeline.BEZIER_SIZE + 1;
|
||||
}
|
||||
|
||||
setLinear (frameIndex: number) {
|
||||
this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.LINEAR;
|
||||
}
|
||||
|
||||
setStepped (frameIndex: number) {
|
||||
this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.STEPPED;
|
||||
}
|
||||
|
||||
getCurveType (frameIndex: number): number {
|
||||
let index = frameIndex * CurveTimeline.BEZIER_SIZE;
|
||||
if (index == this.curves.length) return CurveTimeline.LINEAR;
|
||||
let type = this.curves[index];
|
||||
if (type == CurveTimeline.LINEAR) return CurveTimeline.LINEAR;
|
||||
if (type == CurveTimeline.STEPPED) return CurveTimeline.STEPPED;
|
||||
return CurveTimeline.BEZIER;
|
||||
}
|
||||
|
||||
/** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
|
||||
* cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
|
||||
* the difference between the keyframe's values. */
|
||||
setCurve (frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) {
|
||||
let tmpx = (-cx1 * 2 + cx2) * 0.03, tmpy = (-cy1 * 2 + cy2) * 0.03;
|
||||
let dddfx = ((cx1 - cx2) * 3 + 1) * 0.006, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006;
|
||||
let ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
|
||||
let dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667, dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667;
|
||||
|
||||
let i = frameIndex * CurveTimeline.BEZIER_SIZE;
|
||||
let curves = this.curves;
|
||||
curves[i++] = CurveTimeline.BEZIER;
|
||||
|
||||
let x = dfx, y = dfy;
|
||||
for (let n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) {
|
||||
curves[i] = x;
|
||||
curves[i + 1] = y;
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
ddfx += dddfx;
|
||||
ddfy += dddfy;
|
||||
x += dfx;
|
||||
y += dfy;
|
||||
}
|
||||
}
|
||||
|
||||
getCurvePercent (frameIndex: number, percent: number) {
|
||||
percent = MathUtils.clamp(percent, 0, 1);
|
||||
let curves = this.curves;
|
||||
let i = frameIndex * CurveTimeline.BEZIER_SIZE;
|
||||
let type = curves[i];
|
||||
if (type == CurveTimeline.LINEAR) return percent;
|
||||
if (type == CurveTimeline.STEPPED) return 0;
|
||||
i++;
|
||||
let x = 0;
|
||||
for (let start = i, n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) {
|
||||
x = curves[i];
|
||||
if (x >= percent) {
|
||||
let prevX: number, prevY: number;
|
||||
if (i == start) {
|
||||
prevX = 0;
|
||||
prevY = 0;
|
||||
} else {
|
||||
prevX = curves[i - 2];
|
||||
prevY = curves[i - 1];
|
||||
}
|
||||
return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX);
|
||||
}
|
||||
}
|
||||
let y = curves[i - 1];
|
||||
return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
|
||||
}
|
||||
|
||||
abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number): void;
|
||||
}
|
||||
|
||||
export class RotateTimeline extends CurveTimeline {
|
||||
static ENTRIES = 2;
|
||||
static PREV_TIME = -2; static PREV_ROTATION = -1;
|
||||
static ROTATION = 1;
|
||||
|
||||
boneIndex: number;
|
||||
frames: ArrayLike<number>; // time, degrees, ...
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
this.frames = Utils.newFloatArray(frameCount << 1);
|
||||
}
|
||||
|
||||
/** Sets the time and angle of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, degrees: number) {
|
||||
frameIndex <<= 1;
|
||||
this.frames[frameIndex] = time;
|
||||
this.frames[frameIndex + RotateTimeline.ROTATION] = degrees;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let bone = skeleton.bones[this.boneIndex];
|
||||
|
||||
if (time >= frames[frames.length - RotateTimeline.ENTRIES]) { // Time is after last frame.
|
||||
let amount = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION] - bone.rotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
amount += 360;
|
||||
bone.rotation += amount * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES);
|
||||
let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent((frame >> 1) - 1,
|
||||
1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
let amount = frames[frame + RotateTimeline.ROTATION] - prevRotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
amount += 360;
|
||||
amount = bone.data.rotation + (prevRotation + amount * percent) - bone.rotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
amount += 360;
|
||||
bone.rotation += amount * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
export class TranslateTimeline extends CurveTimeline {
|
||||
static ENTRIES = 3;
|
||||
static PREV_TIME = -3; static PREV_X = -2; static PREV_Y = -1;
|
||||
static X = 1; static Y = 2;
|
||||
|
||||
boneIndex: number;
|
||||
frames: ArrayLike<number>; // time, x, y, ...
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
this.frames = Utils.newFloatArray(frameCount * TranslateTimeline.ENTRIES);
|
||||
}
|
||||
|
||||
/** Sets the time and value of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, x: number, y: number) {
|
||||
frameIndex *= TranslateTimeline.ENTRIES;
|
||||
this.frames[frameIndex] = time;
|
||||
this.frames[frameIndex + TranslateTimeline.X] = x;
|
||||
this.frames[frameIndex + TranslateTimeline.Y] = y;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let bone = skeleton.bones[this.boneIndex];
|
||||
|
||||
if (time >= frames[frames.length - TranslateTimeline.ENTRIES]) { // Time is after last frame.
|
||||
bone.x += (bone.data.x + frames[frames.length + TranslateTimeline.PREV_X] - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + frames[frames.length + TranslateTimeline.PREV_Y] - bone.y) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, TranslateTimeline.ENTRIES);
|
||||
let prevX = frames[frame + TranslateTimeline.PREV_X];
|
||||
let prevY = frames[frame + TranslateTimeline.PREV_Y];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / TranslateTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + TranslateTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
bone.x += (bone.data.x + prevX + (frames[frame + TranslateTimeline.X] - prevX) * percent - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + prevY + (frames[frame + TranslateTimeline.Y] - prevY) * percent - bone.y) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
export class ScaleTimeline extends TranslateTimeline {
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let bone = skeleton.bones[this.boneIndex];
|
||||
if (time >= frames[frames.length - ScaleTimeline.ENTRIES]) { // Time is after last frame.
|
||||
bone.scaleX += (bone.data.scaleX * frames[frames.length + ScaleTimeline.PREV_X] - bone.scaleX) * alpha;
|
||||
bone.scaleY += (bone.data.scaleY * frames[frames.length + ScaleTimeline.PREV_Y] - bone.scaleY) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, ScaleTimeline.ENTRIES);
|
||||
let prevX = frames[frame + ScaleTimeline.PREV_X];
|
||||
let prevY = frames[frame + ScaleTimeline.PREV_Y];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / ScaleTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + ScaleTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
bone.scaleX += (bone.data.scaleX * (prevX + (frames[frame + ScaleTimeline.X] - prevX) * percent) - bone.scaleX) * alpha;
|
||||
bone.scaleY += (bone.data.scaleY * (prevY + (frames[frame + ScaleTimeline.Y] - prevY) * percent) - bone.scaleY) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
export class ShearTimeline extends TranslateTimeline {
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let bone = skeleton.bones[this.boneIndex];
|
||||
if (time >= frames[frames.length - ShearTimeline.ENTRIES]) { // Time is after last frame.
|
||||
bone.shearX += (bone.data.shearX + frames[frames.length + ShearTimeline.PREV_X] - bone.shearX) * alpha;
|
||||
bone.shearY += (bone.data.shearY + frames[frames.length + ShearTimeline.PREV_Y] - bone.shearY) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, ShearTimeline.ENTRIES);
|
||||
let prevX = frames[frame + ShearTimeline.PREV_X];
|
||||
let prevY = frames[frame + ShearTimeline.PREV_Y];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / ShearTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + ShearTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
bone.shearX += (bone.data.shearX + (prevX + (frames[frame + ShearTimeline.X] - prevX) * percent) - bone.shearX) * alpha;
|
||||
bone.shearY += (bone.data.shearY + (prevY + (frames[frame + ShearTimeline.Y] - prevY) * percent) - bone.shearY) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
export class ColorTimeline extends CurveTimeline {
|
||||
static ENTRIES = 5;
|
||||
static PREV_TIME = -5; static PREV_R = -4; static PREV_G = -3; static PREV_B = -2; static PREV_A = -1;
|
||||
static R = 1; static G = 2; static B = 3; static A = 4;
|
||||
|
||||
slotIndex: number;
|
||||
frames: ArrayLike<number>; // time, r, g, b, a, ...
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
this.frames = Utils.newFloatArray(frameCount * ColorTimeline.ENTRIES);
|
||||
}
|
||||
|
||||
/** Sets the time and value of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, r: number, g: number, b: number, a: number) {
|
||||
frameIndex *= ColorTimeline.ENTRIES;
|
||||
this.frames[frameIndex] = time;
|
||||
this.frames[frameIndex + ColorTimeline.R] = r;
|
||||
this.frames[frameIndex + ColorTimeline.G] = g;
|
||||
this.frames[frameIndex + ColorTimeline.B] = b;
|
||||
this.frames[frameIndex + ColorTimeline.A] = a;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let r = 0, g = 0, b = 0, a = 0;
|
||||
if (time >= frames[frames.length - ColorTimeline.ENTRIES]) { // Time is after last frame.
|
||||
let i = frames.length;
|
||||
r = frames[i + ColorTimeline.PREV_R];
|
||||
g = frames[i + ColorTimeline.PREV_G];
|
||||
b = frames[i + ColorTimeline.PREV_B];
|
||||
a = frames[i + ColorTimeline.PREV_A];
|
||||
} else {
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, ColorTimeline.ENTRIES);
|
||||
r = frames[frame + ColorTimeline.PREV_R];
|
||||
g = frames[frame + ColorTimeline.PREV_G];
|
||||
b = frames[frame + ColorTimeline.PREV_B];
|
||||
a = frames[frame + ColorTimeline.PREV_A];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / ColorTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + ColorTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
r += (frames[frame + ColorTimeline.R] - r) * percent;
|
||||
g += (frames[frame + ColorTimeline.G] - g) * percent;
|
||||
b += (frames[frame + ColorTimeline.B] - b) * percent;
|
||||
a += (frames[frame + ColorTimeline.A] - a) * percent;
|
||||
}
|
||||
let color: Color = skeleton.slots[this.slotIndex].color;
|
||||
if (alpha < 1)
|
||||
color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
|
||||
else
|
||||
color.set(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
export class AttachmentTimeline implements Timeline {
|
||||
slotIndex: number;
|
||||
frames: ArrayLike<number> // time, ...
|
||||
attachmentNames: Array<string>;
|
||||
|
||||
constructor (frameCount: number) {
|
||||
this.frames = Utils.newFloatArray(frameCount);
|
||||
this.attachmentNames = new Array<string>(frameCount);
|
||||
}
|
||||
|
||||
getFrameCount () {
|
||||
return this.frames.length;
|
||||
}
|
||||
|
||||
/** Sets the time and value of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, attachmentName: string) {
|
||||
this.frames[frameIndex] = time;
|
||||
this.attachmentNames[frameIndex] = attachmentName;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let frameIndex = 0;
|
||||
if (time >= frames[frames.length - 1]) // Time is after last frame.
|
||||
frameIndex = frames.length - 1;
|
||||
else
|
||||
frameIndex = Animation.binarySearch(frames, time, 1) - 1;
|
||||
|
||||
let attachmentName = this.attachmentNames[frameIndex];
|
||||
skeleton.slots[this.slotIndex]
|
||||
.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
|
||||
}
|
||||
}
|
||||
|
||||
export class EventTimeline implements Timeline {
|
||||
frames: ArrayLike<number>; // time, ...
|
||||
events: Array<Event>;
|
||||
|
||||
constructor (frameCount: number) {
|
||||
this.frames = Utils.newFloatArray(frameCount);
|
||||
this.events = new Array<Event>(frameCount);
|
||||
}
|
||||
|
||||
getFrameCount () {
|
||||
return this.frames.length;
|
||||
}
|
||||
|
||||
/** Sets the time of the specified keyframe. */
|
||||
setFrame (frameIndex: number, event: Event) {
|
||||
this.frames[frameIndex] = event.time;
|
||||
this.events[frameIndex] = event;
|
||||
}
|
||||
|
||||
/** Fires events for frames > lastTime and <= time. */
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number) {
|
||||
if (firedEvents == null) return;
|
||||
let frames = this.frames;
|
||||
let frameCount = this.frames.length;
|
||||
|
||||
if (lastTime > time) { // Fire events after last time for looped animations.
|
||||
this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha);
|
||||
lastTime = -1;
|
||||
} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
|
||||
return;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let frame = 0;
|
||||
if (lastTime < frames[0])
|
||||
frame = 0;
|
||||
else {
|
||||
frame = Animation.binarySearch(frames, lastTime);
|
||||
let frameTime = frames[frame];
|
||||
while (frame > 0) { // Fire multiple events with the same frame.
|
||||
if (frames[frame - 1] != frameTime) break;
|
||||
frame--;
|
||||
}
|
||||
}
|
||||
for (; frame < frameCount && time >= frames[frame]; frame++)
|
||||
firedEvents.push(this.events[frame]);
|
||||
}
|
||||
}
|
||||
|
||||
export class DrawOrderTimeline implements Timeline {
|
||||
frames: ArrayLike<number>; // time, ...
|
||||
drawOrders: Array<Array<number>>;
|
||||
|
||||
constructor (frameCount: number) {
|
||||
this.frames = Utils.newFloatArray(frameCount);
|
||||
this.drawOrders = new Array<Array<number>>(frameCount);
|
||||
}
|
||||
|
||||
getFrameCount () {
|
||||
return this.frames.length;
|
||||
}
|
||||
|
||||
/** Sets the time of the specified keyframe.
|
||||
* @param drawOrder May be null to use bind pose draw order. */
|
||||
setFrame (frameIndex: number, time: number, drawOrder: Array<number>) {
|
||||
this.frames[frameIndex] = time;
|
||||
this.drawOrders[frameIndex] = drawOrder;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let frame = 0;
|
||||
if (time >= frames[frames.length - 1]) // Time is after last frame.
|
||||
frame = frames.length - 1;
|
||||
else
|
||||
frame = Animation.binarySearch(frames, time) - 1;
|
||||
|
||||
let drawOrder: Array<Slot> = skeleton.drawOrder;
|
||||
let slots: Array<Slot> = skeleton.slots;
|
||||
let drawOrderToSetupIndex = this.drawOrders[frame];
|
||||
if (drawOrderToSetupIndex == null)
|
||||
Utils.arrayCopy(slots, 0, drawOrder, 0, slots.length);
|
||||
else {
|
||||
for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
|
||||
drawOrder[i] = slots[drawOrderToSetupIndex[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DeformTimeline extends CurveTimeline {
|
||||
frames: ArrayLike<number>; // time, ...
|
||||
frameVertices: Array<ArrayLike<number>>;
|
||||
slotIndex: number;
|
||||
attachment: VertexAttachment;
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
this.frames = Utils.newFloatArray(frameCount);
|
||||
this.frameVertices = new Array<ArrayLike<number>>(frameCount);
|
||||
}
|
||||
|
||||
/** Sets the time of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, vertices: ArrayLike<number>) {
|
||||
this.frames[frameIndex] = time;
|
||||
this.frameVertices[frameIndex] = vertices;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number) {
|
||||
let slot: Slot = skeleton.slots[this.slotIndex];
|
||||
let slotAttachment: Attachment = slot.getAttachment();
|
||||
if (!(slotAttachment instanceof VertexAttachment) || !(<VertexAttachment>slotAttachment).applyDeform(this.attachment)) return;
|
||||
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let frameVertices = this.frameVertices;
|
||||
let vertexCount = frameVertices[0].length;
|
||||
|
||||
let verticesArray: Array<number> = slot.attachmentVertices;
|
||||
if (verticesArray.length != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
|
||||
let vertices: Array<number> = Utils.setArraySize(verticesArray, vertexCount);
|
||||
|
||||
if (time >= frames[frames.length - 1]) { // Time is after last frame.
|
||||
let lastVertices = frameVertices[frames.length - 1];
|
||||
if (alpha < 1) {
|
||||
for (let i = 0; i < vertexCount; i++)
|
||||
vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
|
||||
} else
|
||||
Utils.arrayCopy(lastVertices, 0, vertices, 0, vertexCount);
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time);
|
||||
let prevVertices = frameVertices[frame - 1];
|
||||
let nextVertices = frameVertices[frame];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
|
||||
|
||||
if (alpha < 1) {
|
||||
for (let i = 0; i < vertexCount; i++) {
|
||||
let prev = prevVertices[i];
|
||||
vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha;
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < vertexCount; i++) {
|
||||
let prev = prevVertices[i];
|
||||
vertices[i] = prev + (nextVertices[i] - prev) * percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class IkConstraintTimeline extends CurveTimeline {
|
||||
static ENTRIES = 3;
|
||||
static PREV_TIME = -3; static PREV_MIX = -2; static PREV_BEND_DIRECTION = -1;
|
||||
static MIX = 1; static BEND_DIRECTION = 2;
|
||||
|
||||
ikConstraintIndex: number;
|
||||
frames: ArrayLike<number>; // time, mix, bendDirection, ...
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
this.frames = Utils.newFloatArray(frameCount * IkConstraintTimeline.ENTRIES);
|
||||
}
|
||||
|
||||
/** Sets the time, mix and bend direction of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, mix: number, bendDirection: number) {
|
||||
frameIndex *= IkConstraintTimeline.ENTRIES;
|
||||
this.frames[frameIndex] = time;
|
||||
this.frames[frameIndex + IkConstraintTimeline.MIX] = mix;
|
||||
this.frames[frameIndex + IkConstraintTimeline.BEND_DIRECTION] = bendDirection;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex];
|
||||
|
||||
if (time >= frames[frames.length - IkConstraintTimeline.ENTRIES]) { // Time is after last frame.
|
||||
constraint.mix += (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.mix) * alpha;
|
||||
constraint.bendDirection = Math.floor(frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, IkConstraintTimeline.ENTRIES);
|
||||
let mix = frames[frame + IkConstraintTimeline.PREV_MIX];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / IkConstraintTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + IkConstraintTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
constraint.mix += (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.mix) * alpha;
|
||||
constraint.bendDirection = Math.floor(frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION]);
|
||||
}
|
||||
}
|
||||
|
||||
export class TransformConstraintTimeline extends CurveTimeline {
|
||||
static ENTRIES = 5;
|
||||
static PREV_TIME = -5; static PREV_ROTATE = -4; static PREV_TRANSLATE = -3; static PREV_SCALE = -2; static PREV_SHEAR = -1;
|
||||
static ROTATE = 1; static TRANSLATE = 2; static SCALE = 3; static SHEAR = 4;
|
||||
|
||||
transformConstraintIndex: number;
|
||||
frames: ArrayLike<number>; // time, rotate mix, translate mix, scale mix, shear mix, ...
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
this.frames = Utils.newFloatArray(frameCount * TransformConstraintTimeline.ENTRIES);
|
||||
}
|
||||
|
||||
/** Sets the time and mixes of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number) {
|
||||
frameIndex *= TransformConstraintTimeline.ENTRIES;
|
||||
this.frames[frameIndex] = time;
|
||||
this.frames[frameIndex + TransformConstraintTimeline.ROTATE] = rotateMix;
|
||||
this.frames[frameIndex + TransformConstraintTimeline.TRANSLATE] = translateMix;
|
||||
this.frames[frameIndex + TransformConstraintTimeline.SCALE] = scaleMix;
|
||||
this.frames[frameIndex + TransformConstraintTimeline.SHEAR] = shearMix;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex];
|
||||
|
||||
if (time >= frames[frames.length - TransformConstraintTimeline.ENTRIES]) { // Time is after last frame.
|
||||
let i = frames.length;
|
||||
constraint.rotateMix += (frames[i + TransformConstraintTimeline.PREV_ROTATE] - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (frames[i + TransformConstraintTimeline.PREV_TRANSLATE] - constraint.translateMix) * alpha;
|
||||
constraint.scaleMix += (frames[i + TransformConstraintTimeline.PREV_SCALE] - constraint.scaleMix) * alpha;
|
||||
constraint.shearMix += (frames[i + TransformConstraintTimeline.PREV_SHEAR] - constraint.shearMix) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, TransformConstraintTimeline.ENTRIES);
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / TransformConstraintTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + TransformConstraintTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
let rotate = frames[frame + TransformConstraintTimeline.PREV_ROTATE];
|
||||
let translate = frames[frame + TransformConstraintTimeline.PREV_TRANSLATE];
|
||||
let scale = frames[frame + TransformConstraintTimeline.PREV_SCALE];
|
||||
let shear = frames[frame + TransformConstraintTimeline.PREV_SHEAR];
|
||||
constraint.rotateMix += (rotate + (frames[frame + TransformConstraintTimeline.ROTATE] - rotate) * percent - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (translate + (frames[frame + TransformConstraintTimeline.TRANSLATE] - translate) * percent - constraint.translateMix)
|
||||
* alpha;
|
||||
constraint.scaleMix += (scale + (frames[frame + TransformConstraintTimeline.SCALE] - scale) * percent - constraint.scaleMix) * alpha;
|
||||
constraint.shearMix += (shear + (frames[frame + TransformConstraintTimeline.SHEAR] - shear) * percent - constraint.shearMix) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
export class PathConstraintPositionTimeline extends CurveTimeline {
|
||||
static ENTRIES = 2;
|
||||
static PREV_TIME = -2; static PREV_VALUE = -1;
|
||||
static VALUE = 1;
|
||||
|
||||
pathConstraintIndex: number;
|
||||
|
||||
frames: ArrayLike<number>; // time, position, ...
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
this.frames = Utils.newFloatArray(frameCount * PathConstraintPositionTimeline.ENTRIES);
|
||||
}
|
||||
|
||||
/** Sets the time and value of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, value: number) {
|
||||
frameIndex *= PathConstraintPositionTimeline.ENTRIES;
|
||||
this.frames[frameIndex] = time;
|
||||
this.frames[frameIndex + PathConstraintPositionTimeline.VALUE] = value;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
|
||||
|
||||
if (time >= frames[frames.length - PathConstraintPositionTimeline.ENTRIES]) { // Time is after last frame.
|
||||
let i = frames.length;
|
||||
constraint.position += (frames[i + PathConstraintPositionTimeline.PREV_VALUE] - constraint.position) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, PathConstraintPositionTimeline.ENTRIES);
|
||||
let position = frames[frame + PathConstraintPositionTimeline.PREV_VALUE];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / PathConstraintPositionTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + PathConstraintPositionTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
constraint.position += (position + (frames[frame + PathConstraintPositionTimeline.VALUE] - position) * percent - constraint.position) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
export class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline {
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
|
||||
|
||||
if (time >= frames[frames.length - PathConstraintSpacingTimeline.ENTRIES]) { // Time is after last frame.
|
||||
let i = frames.length;
|
||||
constraint.spacing += (frames[i + PathConstraintSpacingTimeline.PREV_VALUE] - constraint.spacing) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, PathConstraintSpacingTimeline.ENTRIES);
|
||||
let spacing = frames[frame + PathConstraintSpacingTimeline.PREV_VALUE];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / PathConstraintSpacingTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + PathConstraintSpacingTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
constraint.spacing += (spacing + (frames[frame + PathConstraintSpacingTimeline.VALUE] - spacing) * percent - constraint.spacing) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
export class PathConstraintMixTimeline extends CurveTimeline {
|
||||
static ENTRIES = 3;
|
||||
static PREV_TIME = -3; static PREV_ROTATE = -2; static PREV_TRANSLATE = -1;
|
||||
static ROTATE = 1; static TRANSLATE = 2;
|
||||
|
||||
pathConstraintIndex: number;
|
||||
|
||||
frames: ArrayLike<number>; // time, rotate mix, translate mix, ...
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount);
|
||||
this.frames = Utils.newFloatArray(frameCount * PathConstraintMixTimeline.ENTRIES);
|
||||
}
|
||||
|
||||
/** Sets the time and mixes of the specified keyframe. */
|
||||
setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number) {
|
||||
frameIndex *= PathConstraintMixTimeline.ENTRIES;
|
||||
this.frames[frameIndex] = time;
|
||||
this.frames[frameIndex + PathConstraintMixTimeline.ROTATE] = rotateMix;
|
||||
this.frames[frameIndex + PathConstraintMixTimeline.TRANSLATE] = translateMix;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number) {
|
||||
let frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
|
||||
|
||||
if (time >= frames[frames.length - PathConstraintMixTimeline.ENTRIES]) { // Time is after last frame.
|
||||
let i = frames.length;
|
||||
constraint.rotateMix += (frames[i + PathConstraintMixTimeline.PREV_ROTATE] - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (frames[i + PathConstraintMixTimeline.PREV_TRANSLATE] - constraint.translateMix) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
let frame = Animation.binarySearch(frames, time, PathConstraintMixTimeline.ENTRIES);
|
||||
let rotate = frames[frame + PathConstraintMixTimeline.PREV_ROTATE];
|
||||
let translate = frames[frame + PathConstraintMixTimeline.PREV_TRANSLATE];
|
||||
let frameTime = frames[frame];
|
||||
let percent = this.getCurvePercent(frame / PathConstraintMixTimeline.ENTRIES - 1,
|
||||
1 - (time - frameTime) / (frames[frame + PathConstraintMixTimeline.PREV_TIME] - frameTime));
|
||||
|
||||
constraint.rotateMix += (rotate + (frames[frame + PathConstraintMixTimeline.ROTATE] - rotate) * percent - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (translate + (frames[frame + PathConstraintMixTimeline.TRANSLATE] - translate) * percent - constraint.translateMix)
|
||||
* alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
321
spine-ts/core/src/AnimationState.ts
Normal file
321
spine-ts/core/src/AnimationState.ts
Normal file
@ -0,0 +1,321 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class AnimationState {
|
||||
data: AnimationStateData;
|
||||
tracks = new Array<TrackEntry>();
|
||||
events = new Array<Event>();
|
||||
listeners = new Array<AnimationStateListener>();
|
||||
timeScale = 1;
|
||||
|
||||
constructor (data: AnimationStateData = null) {
|
||||
if (data == null) throw new Error("data cannot be null.");
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
update (delta: number) {
|
||||
delta *= this.timeScale;
|
||||
for (let i = 0; i < this.tracks.length; i++) {
|
||||
let current = this.tracks[i];
|
||||
if (current == null) continue;
|
||||
|
||||
let next = current.next;
|
||||
if (next != null) {
|
||||
let nextTime = current.lastTime - next.delay;
|
||||
if (nextTime >= 0) {
|
||||
let nextDelta = delta * next.timeScale;
|
||||
next.time = nextTime + nextDelta; // For start event to see correct time.
|
||||
current.time += delta * current.timeScale; // For end event to see correct time.
|
||||
this.setCurrent(i, next);
|
||||
next.time -= nextDelta; // Prevent increasing time twice, below.
|
||||
current = next;
|
||||
}
|
||||
} else if (!current.loop && current.lastTime >= current.endTime) {
|
||||
// End non-looping animation when it reaches its end time and there is no next entry.
|
||||
this.clearTrack(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
current.time += delta * current.timeScale;
|
||||
if (current.previous != null) {
|
||||
let previousDelta = delta * current.previous.timeScale;
|
||||
current.previous.time += previousDelta;
|
||||
current.mixTime += previousDelta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton) {
|
||||
let events = this.events;
|
||||
let listenerCount = this.listeners.length;
|
||||
|
||||
for (let i = 0; i < this.tracks.length; i++) {
|
||||
let current = this.tracks[i];
|
||||
if (current == null) continue;
|
||||
|
||||
events.length = 0;
|
||||
|
||||
let time = current.time;
|
||||
let lastTime = current.lastTime;
|
||||
let endTime = current.endTime;
|
||||
let loop = current.loop;
|
||||
if (!loop && time > endTime) time = endTime;
|
||||
|
||||
let previous = current.previous;
|
||||
if (previous == null)
|
||||
current.animation.mix(skeleton, lastTime, time, loop, events, current.mix);
|
||||
else {
|
||||
let previousTime = previous.time;
|
||||
if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
|
||||
previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null);
|
||||
|
||||
let alpha = current.mixTime / current.mixDuration * current.mix;
|
||||
if (alpha >= 1) {
|
||||
alpha = 1;
|
||||
current.previous = null;
|
||||
}
|
||||
current.animation.mix(skeleton, lastTime, time, loop, events, alpha);
|
||||
}
|
||||
|
||||
for (let ii = 0, nn = events.length; ii < nn; ii++) {
|
||||
let event = events[ii];
|
||||
if (current.listener != null) current.listener.event(i, event);
|
||||
for (let iii = 0; iii < listenerCount; iii++)
|
||||
this.listeners[iii].event(i, event);
|
||||
}
|
||||
|
||||
// Check if completed the animation or a loop iteration.
|
||||
if (loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime)) {
|
||||
let count = MathUtils.toInt(time / endTime);
|
||||
if (current.listener != null) current.listener.complete(i, count);
|
||||
for (let ii = 0, nn = this.listeners.length; ii < nn; ii++)
|
||||
this.listeners[ii].complete(i, count);
|
||||
}
|
||||
|
||||
current.lastTime = current.time;
|
||||
}
|
||||
}
|
||||
|
||||
clearTracks () {
|
||||
for (let i = 0, n = this.tracks.length; i < n; i++)
|
||||
this.clearTrack(i);
|
||||
this.tracks.length = 0;
|
||||
}
|
||||
|
||||
clearTrack (trackIndex: number) {
|
||||
if (trackIndex >= this.tracks.length) return;
|
||||
let current = this.tracks[trackIndex];
|
||||
if (current == null) return;
|
||||
|
||||
if (current.listener != null) current.listener.end(trackIndex);
|
||||
for (let i = 0, n = this.listeners.length; i < n; i++)
|
||||
this.listeners[i].end(trackIndex);
|
||||
|
||||
this.tracks[trackIndex] = null;
|
||||
|
||||
this.freeAll(current);
|
||||
}
|
||||
|
||||
freeAll (entry: TrackEntry) {
|
||||
while (entry != null) {
|
||||
let next = entry.next;
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
||||
expandToIndex (index: number) {
|
||||
if (index < this.tracks.length) return this.tracks[index];
|
||||
Utils.setArraySize(this.tracks, index - this.tracks.length + 1, null);
|
||||
this.tracks.length = index + 1;
|
||||
return null;
|
||||
}
|
||||
|
||||
setCurrent (index: number, entry: TrackEntry) {
|
||||
let current = this.expandToIndex(index);
|
||||
if (current != null) {
|
||||
let previous = current.previous;
|
||||
current.previous = null;
|
||||
|
||||
if (current.listener != null) current.listener.end(index);
|
||||
for (let i = 0, n = this.listeners.length; i < n; i++)
|
||||
this.listeners[i].end(index);
|
||||
|
||||
entry.mixDuration = this.data.getMix(current.animation, entry.animation);
|
||||
if (entry.mixDuration > 0) {
|
||||
entry.mixTime = 0;
|
||||
// If a mix is in progress, mix from the closest animation.
|
||||
if (previous != null && current.mixTime / current.mixDuration < 0.5) {
|
||||
entry.previous = previous;
|
||||
previous = current;
|
||||
} else
|
||||
entry.previous = current;
|
||||
}
|
||||
}
|
||||
|
||||
this.tracks[index] = entry;
|
||||
|
||||
if (entry.listener != null) entry.listener.start(index);
|
||||
for (let i = 0, n = this.listeners.length; i < n; i++)
|
||||
this.listeners[i].start(index);
|
||||
}
|
||||
|
||||
/** @see #setAnimation(int, Animation, boolean) */
|
||||
setAnimation (trackIndex: number, animationName: string, loop: boolean) {
|
||||
let animation = this.data.skeletonData.findAnimation(animationName);
|
||||
if (animation == null) throw new Error("Animation not found: " + animationName);
|
||||
return this.setAnimationWith(trackIndex, animation, loop);
|
||||
}
|
||||
|
||||
/** Set the current animation. Any queued animations are cleared. */
|
||||
setAnimationWith (trackIndex: number, animation: Animation, loop: boolean) {
|
||||
let current = this.expandToIndex(trackIndex);
|
||||
if (current != null) this.freeAll(current.next);
|
||||
|
||||
let entry = new TrackEntry();
|
||||
entry.animation = animation;
|
||||
entry.loop = loop;
|
||||
entry.endTime = animation.duration;
|
||||
this.setCurrent(trackIndex, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/** {@link #addAnimation(int, Animation, boolean, float)} */
|
||||
addAnimation (trackIndex: number, animationName: string, loop: boolean, delay: number) {
|
||||
let animation = this.data.skeletonData.findAnimation(animationName);
|
||||
if (animation == null) throw new Error("Animation not found: " + animationName);
|
||||
return this.addAnimationWith(trackIndex, animation, loop, delay);
|
||||
}
|
||||
|
||||
/** Adds an animation to be played delay seconds after the current or last queued animation.
|
||||
* @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */
|
||||
addAnimationWith (trackIndex: number, animation: Animation, loop: boolean, delay: number) {
|
||||
let entry = new TrackEntry();
|
||||
entry.animation = animation;
|
||||
entry.loop = loop;
|
||||
entry.endTime = animation.duration;
|
||||
|
||||
let last = this.expandToIndex(trackIndex);
|
||||
if (last != null) {
|
||||
while (last.next != null)
|
||||
last = last.next;
|
||||
last.next = entry;
|
||||
} else
|
||||
this.tracks[trackIndex] = entry;
|
||||
|
||||
if (delay <= 0) {
|
||||
if (last != null)
|
||||
delay += last.endTime - this.data.getMix(last.animation, animation);
|
||||
else
|
||||
delay = 0;
|
||||
}
|
||||
entry.delay = delay;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
getCurrent (trackIndex: number) {
|
||||
if (trackIndex >= this.tracks.length) return null;
|
||||
return this.tracks[trackIndex];
|
||||
}
|
||||
|
||||
/** Adds a listener to receive events for all animations. */
|
||||
addListener (listener: AnimationStateListener) {
|
||||
if (listener == null) throw new Error("listener cannot be null.");
|
||||
this.listeners.push(listener);
|
||||
}
|
||||
|
||||
/** Removes the listener added with {@link #addListener(AnimationStateListener)}. */
|
||||
removeListener (listener: AnimationStateListener) {
|
||||
let index = this.listeners.indexOf(listener);
|
||||
if (index >= 0) this.listeners.splice(index, 1);
|
||||
}
|
||||
|
||||
clearListeners () {
|
||||
this.listeners.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class TrackEntry {
|
||||
next: TrackEntry; previous: TrackEntry;
|
||||
animation: Animation;
|
||||
loop = false;
|
||||
delay = 0; time = 0; lastTime = -1; endTime = 0; timeScale = 1;
|
||||
mixTime = 0; mixDuration = 0;
|
||||
listener: AnimationStateListener;
|
||||
mix = 1;
|
||||
|
||||
reset () {
|
||||
this.next = null;
|
||||
this.previous = null;
|
||||
this.animation = null;
|
||||
this.listener = null;
|
||||
this.timeScale = 1;
|
||||
this.lastTime = -1; // Trigger events on frame zero.
|
||||
this.time = 0;
|
||||
}
|
||||
|
||||
/** Returns true if the current time is greater than the end time, regardless of looping. */
|
||||
isComplete () : boolean {
|
||||
return this.time >= this.endTime;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class AnimationStateAdapter implements AnimationStateListener {
|
||||
event (trackIndex: number, event: Event) {
|
||||
}
|
||||
|
||||
complete (trackIndex: number, loopCount: number) {
|
||||
}
|
||||
|
||||
start (trackIndex: number) {
|
||||
}
|
||||
|
||||
end (trackIndex: number) {
|
||||
}
|
||||
}
|
||||
|
||||
export interface AnimationStateListener {
|
||||
/** Invoked when the current animation triggers an event. */
|
||||
event (trackIndex: number, event: Event): void;
|
||||
|
||||
/** Invoked when the current animation has completed.
|
||||
* @param loopCount The number of times the animation reached the end. */
|
||||
complete (trackIndex: number, loopCount: number): void;
|
||||
|
||||
/** Invoked just after the current animation is set. */
|
||||
start (trackIndex: number): void;
|
||||
|
||||
/** Invoked just before the current animation is replaced. */
|
||||
end (trackIndex: number): void;
|
||||
}
|
||||
}
|
||||
64
spine-ts/core/src/AnimationStateData.ts
Normal file
64
spine-ts/core/src/AnimationStateData.ts
Normal file
@ -0,0 +1,64 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class AnimationStateData {
|
||||
skeletonData: SkeletonData;
|
||||
animationToMixTime: Map<number> = { };
|
||||
defaultMix = 0;
|
||||
|
||||
constructor (skeletonData: SkeletonData) {
|
||||
if (skeletonData == null) throw new Error("skeletonData cannot be null.");
|
||||
this.skeletonData = skeletonData;
|
||||
}
|
||||
|
||||
setMix (fromName: string, toName: string, duration: number) {
|
||||
let from = this.skeletonData.findAnimation(fromName);
|
||||
if (from == null) throw new Error("Animation not found: " + fromName);
|
||||
let to = this.skeletonData.findAnimation(toName);
|
||||
if (to == null) throw new Error("Animation not found: " + toName);
|
||||
this.setMixWith(from, to, duration);
|
||||
}
|
||||
|
||||
setMixWith (from: Animation, to: Animation, duration: number) {
|
||||
if (from == null) throw new Error("from cannot be null.");
|
||||
if (to == null) throw new Error("to cannot be null.");
|
||||
let key = from.name + to.name;
|
||||
this.animationToMixTime[key] = duration;
|
||||
}
|
||||
|
||||
getMix (from: Animation, to: Animation) {
|
||||
let key = from.name + to.name;
|
||||
let value = this.animationToMixTime[key];
|
||||
return value === undefined ? this.defaultMix : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
spine-ts/core/src/BlendMode.ts
Normal file
39
spine-ts/core/src/BlendMode.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export enum BlendMode {
|
||||
Normal,
|
||||
Additive,
|
||||
Multiply,
|
||||
Screen
|
||||
}
|
||||
}
|
||||
293
spine-ts/core/src/Bone.ts
Normal file
293
spine-ts/core/src/Bone.ts
Normal file
@ -0,0 +1,293 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class Bone implements Updatable {
|
||||
data: BoneData;
|
||||
skeleton: Skeleton;
|
||||
parent: Bone;
|
||||
children = new Array<Bone>();
|
||||
x = 0; y = 0; rotation = 0; scaleX = 0; scaleY = 0; shearX = 0; shearY = 0;
|
||||
appliedRotation = 0;
|
||||
|
||||
a = 0; b = 0; worldX = 0;
|
||||
c = 0; d = 0; worldY = 0;
|
||||
worldSignX = 0; worldSignY = 0;
|
||||
|
||||
sorted = false;
|
||||
|
||||
/** @param parent May be null. */
|
||||
constructor (data: BoneData, skeleton: Skeleton, parent: Bone) {
|
||||
if (data == null) throw new Error("data cannot be null.");
|
||||
if (skeleton == null) throw new Error("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.skeleton = skeleton;
|
||||
this.parent = parent;
|
||||
this.setToSetupPose();
|
||||
}
|
||||
|
||||
/** Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}. */
|
||||
update () {
|
||||
this.updateWorldTransformWith(this.x, this.y, this.rotation, this.scaleX, this.scaleY, this.shearX, this.shearY);
|
||||
}
|
||||
|
||||
/** Computes the world transform using the parent bone and this bone's local transform. */
|
||||
updateWorldTransform () {
|
||||
this.updateWorldTransformWith(this.x, this.y, this.rotation, this.scaleX, this.scaleY, this.shearX, this.shearY);
|
||||
}
|
||||
|
||||
/** Computes the world transform using the parent bone and the specified local transform. */
|
||||
updateWorldTransformWith (x: number, y: number, rotation: number, scaleX: number, scaleY: number, shearX: number, shearY: number) {
|
||||
this.appliedRotation = rotation;
|
||||
|
||||
let rotationY = rotation + 90 + shearY;
|
||||
let la = MathUtils.cosDeg(rotation + shearX) * scaleX, lb = MathUtils.cosDeg(rotationY) * scaleY;
|
||||
let lc = MathUtils.sinDeg(rotation + shearX) * scaleX, ld = MathUtils.sinDeg(rotationY) * scaleY;
|
||||
|
||||
let parent = this.parent;
|
||||
if (parent == null) { // Root bone.
|
||||
let skeleton = this.skeleton;
|
||||
if (skeleton.flipX) {
|
||||
x = -x;
|
||||
la = -la;
|
||||
lb = -lb;
|
||||
}
|
||||
if (skeleton.flipY) {
|
||||
y = -y;
|
||||
lc = -lc;
|
||||
ld = -ld;
|
||||
}
|
||||
this.a = la;
|
||||
this.b = lb;
|
||||
this.c = lc;
|
||||
this.d = ld;
|
||||
this.worldX = x;
|
||||
this.worldY = y;
|
||||
this.worldSignX = MathUtils.signum(scaleX);
|
||||
this.worldSignY = MathUtils.signum(scaleY);
|
||||
return;
|
||||
}
|
||||
|
||||
let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
this.worldX = pa * x + pb * y + parent.worldX;
|
||||
this.worldY = pc * x + pd * y + parent.worldY;
|
||||
this.worldSignX = parent.worldSignX * MathUtils.signum(scaleX);
|
||||
this.worldSignY = parent.worldSignY * MathUtils.signum(scaleY);
|
||||
|
||||
if (this.data.inheritRotation && this.data.inheritScale) {
|
||||
this.a = pa * la + pb * lc;
|
||||
this.b = pa * lb + pb * ld;
|
||||
this.c = pc * la + pd * lc;
|
||||
this.d = pc * lb + pd * ld;
|
||||
} else {
|
||||
if (this.data.inheritRotation) { // No scale inheritance.
|
||||
pa = 1;
|
||||
pb = 0;
|
||||
pc = 0;
|
||||
pd = 1;
|
||||
do {
|
||||
let cos = MathUtils.cosDeg(parent.appliedRotation), sin = MathUtils.sinDeg(parent.appliedRotation);
|
||||
let temp = pa * cos + pb * sin;
|
||||
pb = pb * cos - pa * sin;
|
||||
pa = temp;
|
||||
temp = pc * cos + pd * sin;
|
||||
pd = pd * cos - pc * sin;
|
||||
pc = temp;
|
||||
|
||||
if (!parent.data.inheritRotation) break;
|
||||
parent = parent.parent;
|
||||
} while (parent != null);
|
||||
this.a = pa * la + pb * lc;
|
||||
this.b = pa * lb + pb * ld;
|
||||
this.c = pc * la + pd * lc;
|
||||
this.d = pc * lb + pd * ld;
|
||||
} else if (this.data.inheritScale) { // No rotation inheritance.
|
||||
pa = 1;
|
||||
pb = 0;
|
||||
pc = 0;
|
||||
pd = 1;
|
||||
do {
|
||||
let cos = MathUtils.cosDeg(parent.appliedRotation), sin = MathUtils.sinDeg(parent.appliedRotation);
|
||||
let psx = parent.scaleX, psy = parent.scaleY;
|
||||
let za = cos * psx, zb = sin * psy, zc = sin * psx, zd = cos * psy;
|
||||
let temp = pa * za + pb * zc;
|
||||
pb = pb * zd - pa * zb;
|
||||
pa = temp;
|
||||
temp = pc * za + pd * zc;
|
||||
pd = pd * zd - pc * zb;
|
||||
pc = temp;
|
||||
|
||||
if (psx >= 0) sin = -sin;
|
||||
temp = pa * cos + pb * sin;
|
||||
pb = pb * cos - pa * sin;
|
||||
pa = temp;
|
||||
temp = pc * cos + pd * sin;
|
||||
pd = pd * cos - pc * sin;
|
||||
pc = temp;
|
||||
|
||||
if (!parent.data.inheritScale) break;
|
||||
parent = parent.parent;
|
||||
} while (parent != null);
|
||||
this.a = pa * la + pb * lc;
|
||||
this.b = pa * lb + pb * ld;
|
||||
this.c = pc * la + pd * lc;
|
||||
this.d = pc * lb + pd * ld;
|
||||
} else {
|
||||
this.a = la;
|
||||
this.b = lb;
|
||||
this.c = lc;
|
||||
this.d = ld;
|
||||
}
|
||||
if (this.skeleton.flipX) {
|
||||
this.a = -this.a;
|
||||
this.b = -this.b;
|
||||
}
|
||||
if (this.skeleton.flipY) {
|
||||
this.c = -this.c;
|
||||
this.d = -this.d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setToSetupPose () {
|
||||
let data = this.data;
|
||||
this.x = data.x;
|
||||
this.y = data.y;
|
||||
this.rotation = data.rotation;
|
||||
this.scaleX = data.scaleX;
|
||||
this.scaleY = data.scaleY;
|
||||
this.shearX = data.shearX;
|
||||
this.shearY = data.shearY;
|
||||
}
|
||||
|
||||
getWorldRotationX () {
|
||||
return Math.atan2(this.c, this.a) * MathUtils.radDeg;
|
||||
}
|
||||
|
||||
getWorldRotationY () {
|
||||
return Math.atan2(this.d, this.b) * MathUtils.radDeg;
|
||||
}
|
||||
|
||||
getWorldScaleX () {
|
||||
return Math.sqrt(this.a * this.a + this.b * this.b) * this.worldSignX;
|
||||
}
|
||||
|
||||
getWorldScaleY () {
|
||||
return Math.sqrt(this.c * this.c + this.d * this.d) * this.worldSignY;
|
||||
}
|
||||
|
||||
worldToLocalRotationX () {
|
||||
let parent = this.parent;
|
||||
if (parent == null) return this.rotation;
|
||||
let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c;
|
||||
return Math.atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.radDeg;
|
||||
}
|
||||
|
||||
worldToLocalRotationY () {
|
||||
let parent = this.parent;
|
||||
if (parent == null) return this.rotation;
|
||||
let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d;
|
||||
return Math.atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.radDeg;
|
||||
}
|
||||
|
||||
rotateWorld (degrees: number) {
|
||||
let a = this.a, b = this.b, c = this.c, d = this.d;
|
||||
let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees);
|
||||
this.a = cos * a - sin * c;
|
||||
this.b = cos * b - sin * d;
|
||||
this.c = sin * a + cos * c;
|
||||
this.d = sin * b + cos * d;
|
||||
}
|
||||
|
||||
/** Computes the local transform from the world transform. This can be useful to perform processing on the local transform
|
||||
* after the world transform has been modified directly (eg, by a constraint).
|
||||
* <p>
|
||||
* Some redundant information is lost by the world transform, such as -1,-1 scale versus 180 rotation. The computed local
|
||||
* transform values may differ from the original values but are functionally the same. */
|
||||
updateLocalTransform () {
|
||||
let parent = this.parent;
|
||||
if (parent == null) {
|
||||
this.x = this.worldX;
|
||||
this.y = this.worldY;
|
||||
this.rotation = Math.atan2(this.c, this.a) * MathUtils.radDeg;
|
||||
this.scaleX = Math.sqrt(this.a * this.a + this.c * this.c);
|
||||
this.scaleY = Math.sqrt(this.b * this.b + this.d * this.d);
|
||||
let det = this.a * this.d - this.b * this.c;
|
||||
this.shearX = 0;
|
||||
this.shearY = Math.atan2(this.a * this.b + this.c * this.d, det) * MathUtils.radDeg;
|
||||
return;
|
||||
}
|
||||
let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
let pid = 1 / (pa * pd - pb * pc);
|
||||
let dx = this.worldX - parent.worldX, dy = this.worldY - parent.worldY;
|
||||
this.x = (dx * pd * pid - dy * pb * pid);
|
||||
this.y = (dy * pa * pid - dx * pc * pid);
|
||||
let ia = pid * pd;
|
||||
let id = pid * pa;
|
||||
let ib = pid * pb;
|
||||
let ic = pid * pc;
|
||||
let ra = ia * this.a - ib * this.c;
|
||||
let rb = ia * this.b - ib * this.d;
|
||||
let rc = id * this.c - ic * this.a;
|
||||
let rd = id * this.d - ic * this.b;
|
||||
this.shearX = 0;
|
||||
this.scaleX = Math.sqrt(ra * ra + rc * rc);
|
||||
if (this.scaleX > 0.0001) {
|
||||
let det = ra * rd - rb * rc;
|
||||
this.scaleY = det / this.scaleX;
|
||||
this.shearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg;
|
||||
this.rotation = Math.atan2(rc, ra) * MathUtils.radDeg;
|
||||
} else {
|
||||
this.scaleX = 0;
|
||||
this.scaleY = Math.sqrt(rb * rb + rd * rd);
|
||||
this.shearY = 0;
|
||||
this.rotation = 90 - Math.atan2(rd, rb) * MathUtils.radDeg;
|
||||
}
|
||||
this.appliedRotation = this.rotation;
|
||||
}
|
||||
|
||||
worldToLocal (world: Vector2) {
|
||||
let a = this.a, b = this.b, c = this.c, d = this.d;
|
||||
let invDet = 1 / (a * d - b * c);
|
||||
let x = world.x - this.worldX, y = world.y - this.worldY;
|
||||
world.x = (x * d * invDet - y * b * invDet);
|
||||
world.y = (y * a * invDet - x * c * invDet);
|
||||
return world;
|
||||
}
|
||||
|
||||
localToWorld (local: Vector2) {
|
||||
let x = local.x, y = local.y;
|
||||
local.x = x * this.a + y * this.b + this.worldX;
|
||||
local.y = x * this.c + y * this.d + this.worldY;
|
||||
return local;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
spine-ts/core/src/BoneData.ts
Normal file
49
spine-ts/core/src/BoneData.ts
Normal file
@ -0,0 +1,49 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class BoneData {
|
||||
index: number;
|
||||
name: string;
|
||||
parent: BoneData;
|
||||
length: number;
|
||||
x = 0; y = 0; rotation = 0; scaleX = 1; scaleY = 1; shearX = 0; shearY = 0;
|
||||
inheritRotation = true; inheritScale = true;
|
||||
|
||||
constructor (index: number, name: string, parent: BoneData) {
|
||||
if (index < 0) throw new Error("index must be >= 0.");
|
||||
if (name == null) throw new Error("name cannot be null.");
|
||||
this.index = index;
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
spine-ts/core/src/Event.ts
Normal file
46
spine-ts/core/src/Event.ts
Normal file
@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class Event {
|
||||
data: EventData;
|
||||
intValue: number;
|
||||
floatValue: number;
|
||||
stringValue: string;
|
||||
time: number;
|
||||
|
||||
constructor (time: number, data: EventData) {
|
||||
if (data == null) throw new Error("data cannot be null.");
|
||||
this.time = time;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
spine-ts/core/src/EventData.ts
Normal file
43
spine-ts/core/src/EventData.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class EventData {
|
||||
name: string;
|
||||
intValue: number;
|
||||
floatValue: number;
|
||||
stringValue: string;
|
||||
|
||||
constructor (name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
223
spine-ts/core/src/IkConstraint.ts
Normal file
223
spine-ts/core/src/IkConstraint.ts
Normal file
@ -0,0 +1,223 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class IkConstraint implements Updatable {
|
||||
data: IkConstraintData;
|
||||
bones: Array<Bone>;
|
||||
target: Bone;
|
||||
mix = 1;
|
||||
bendDirection = 0;
|
||||
|
||||
level = 0;
|
||||
|
||||
constructor (data: IkConstraintData, skeleton: Skeleton) {
|
||||
if (data == null) throw new Error("data cannot be null.");
|
||||
if (skeleton == null) throw new Error("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.mix = data.mix;
|
||||
this.bendDirection = data.bendDirection;
|
||||
|
||||
this.bones = new Array<Bone>();
|
||||
for (let i = 0; i < data.bones.length; i++)
|
||||
this.bones.push(skeleton.findBone(data.bones[i].name));
|
||||
this.target = skeleton.findBone(data.target.name);
|
||||
}
|
||||
|
||||
apply () {
|
||||
this.update();
|
||||
}
|
||||
|
||||
update () {
|
||||
let target = this.target;
|
||||
let bones = this.bones;
|
||||
switch (bones.length) {
|
||||
case 1:
|
||||
this.apply1(bones[0], target.worldX, target.worldY, this.mix);
|
||||
break;
|
||||
case 2:
|
||||
this.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.mix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world
|
||||
* coordinate system. */
|
||||
apply1 (bone: Bone, targetX: number, targetY: number, alpha: number) {
|
||||
let pp = bone.parent;
|
||||
let id = 1 / (pp.a * pp.d - pp.b * pp.c);
|
||||
let x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||
let tx = (x * pp.d - y * pp.b) * id - bone.x, ty = (y * pp.a - x * pp.c) * id - bone.y;
|
||||
let rotationIK = Math.atan2(ty, tx) * MathUtils.radDeg - bone.shearX - bone.rotation;
|
||||
if (bone.scaleX < 0) rotationIK += 180;
|
||||
if (rotationIK > 180)
|
||||
rotationIK -= 360;
|
||||
else if (rotationIK < -180) rotationIK += 360;
|
||||
bone.updateWorldTransformWith(bone.x, bone.y, bone.rotation + rotationIK * alpha, bone.scaleX, bone.scaleY, bone.shearX,
|
||||
bone.shearY);
|
||||
}
|
||||
|
||||
/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The
|
||||
* target is specified in the world coordinate system.
|
||||
* @param child A direct descendant of the parent bone. */
|
||||
apply2 (parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, alpha: number) {
|
||||
if (alpha == 0) {
|
||||
child.updateWorldTransform();
|
||||
return;
|
||||
}
|
||||
let px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
|
||||
let os1 = 0, os2 = 0, s2 = 0;
|
||||
if (psx < 0) {
|
||||
psx = -psx;
|
||||
os1 = 180;
|
||||
s2 = -1;
|
||||
} else {
|
||||
os1 = 0;
|
||||
s2 = 1;
|
||||
}
|
||||
if (psy < 0) {
|
||||
psy = -psy;
|
||||
s2 = -s2;
|
||||
}
|
||||
if (csx < 0) {
|
||||
csx = -csx;
|
||||
os2 = 180;
|
||||
} else
|
||||
os2 = 0;
|
||||
let cx = child.x, cy = 0, cwx = 0, cwy = 0, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
|
||||
let u = Math.abs(psx - psy) <= 0.0001;
|
||||
if (!u) {
|
||||
cy = 0;
|
||||
cwx = a * cx + parent.worldX;
|
||||
cwy = c * cx + parent.worldY;
|
||||
} else {
|
||||
cy = child.y;
|
||||
cwx = a * cx + b * cy + parent.worldX;
|
||||
cwy = c * cx + d * cy + parent.worldY;
|
||||
}
|
||||
let pp = parent.parent;
|
||||
a = pp.a;
|
||||
b = pp.b;
|
||||
c = pp.c;
|
||||
d = pp.d;
|
||||
let id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||
let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
|
||||
x = cwx - pp.worldX;
|
||||
y = cwy - pp.worldY;
|
||||
let dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
|
||||
let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1 = 0, a2 = 0;
|
||||
outer:
|
||||
if (u) {
|
||||
l2 *= psx;
|
||||
let cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
|
||||
if (cos < -1)
|
||||
cos = -1;
|
||||
else if (cos > 1) cos = 1;
|
||||
a2 = Math.acos(cos) * bendDir;
|
||||
a = l1 + l2 * cos;
|
||||
b = l2 * Math.sin(a2);
|
||||
a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b);
|
||||
} else {
|
||||
a = psx * l2;
|
||||
b = psy * l2;
|
||||
let aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = Math.atan2(ty, tx);
|
||||
c = bb * l1 * l1 + aa * dd - aa * bb;
|
||||
let c1 = -2 * bb * l1, c2 = bb - aa;
|
||||
d = c1 * c1 - 4 * c2 * c;
|
||||
if (d >= 0) {
|
||||
let q = Math.sqrt(d);
|
||||
if (c1 < 0) q = -q;
|
||||
q = -(c1 + q) / 2;
|
||||
let r0 = q / c2, r1 = c / q;
|
||||
let r = Math.abs(r0) < Math.abs(r1) ? r0 : r1;
|
||||
if (r * r <= dd) {
|
||||
y = Math.sqrt(dd - r * r) * bendDir;
|
||||
a1 = ta - Math.atan2(y, r);
|
||||
a2 = Math.atan2(y / psy, (r - l1) / psx);
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
let minAngle = 0, minDist = Number.MAX_VALUE, minX = 0, minY = 0;
|
||||
let maxAngle = 0, maxDist = 0, maxX = 0, maxY = 0;
|
||||
x = l1 + a;
|
||||
d = x * x;
|
||||
if (d > maxDist) {
|
||||
maxAngle = 0;
|
||||
maxDist = d;
|
||||
maxX = x;
|
||||
}
|
||||
x = l1 - a;
|
||||
d = x * x;
|
||||
if (d < minDist) {
|
||||
minAngle = MathUtils.PI;
|
||||
minDist = d;
|
||||
minX = x;
|
||||
}
|
||||
let angle = Math.acos(-a * l1 / (aa - bb));
|
||||
x = a * Math.cos(angle) + l1;
|
||||
y = b * Math.sin(angle);
|
||||
d = x * x + y * y;
|
||||
if (d < minDist) {
|
||||
minAngle = angle;
|
||||
minDist = d;
|
||||
minX = x;
|
||||
minY = y;
|
||||
}
|
||||
if (d > maxDist) {
|
||||
maxAngle = angle;
|
||||
maxDist = d;
|
||||
maxX = x;
|
||||
maxY = y;
|
||||
}
|
||||
if (dd <= (minDist + maxDist) / 2) {
|
||||
a1 = ta - Math.atan2(minY * bendDir, minX);
|
||||
a2 = minAngle * bendDir;
|
||||
} else {
|
||||
a1 = ta - Math.atan2(maxY * bendDir, maxX);
|
||||
a2 = maxAngle * bendDir;
|
||||
}
|
||||
}
|
||||
let os = Math.atan2(cy, cx) * s2;
|
||||
let rotation = parent.rotation;
|
||||
a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation;
|
||||
if (a1 > 180)
|
||||
a1 -= 360;
|
||||
else if (a1 < -180) a1 += 360;
|
||||
parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, parent.scaleX, parent.scaleY, 0, 0);
|
||||
rotation = child.rotation;
|
||||
a2 = ((a2 + os) * MathUtils.radDeg - child.shearX) * s2 + os2 - rotation;
|
||||
if (a2 > 180)
|
||||
a2 -= 360;
|
||||
else if (a2 < -180) a2 += 360;
|
||||
child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.scaleX, child.scaleY, child.shearX, child.shearY);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
spine-ts/core/src/IkConstraintData.ts
Normal file
44
spine-ts/core/src/IkConstraintData.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class IkConstraintData {
|
||||
name: string;
|
||||
bones = new Array<BoneData>();
|
||||
target: BoneData;
|
||||
bendDirection = 1;
|
||||
mix = 1;
|
||||
|
||||
constructor (name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
388
spine-ts/core/src/PathConstraint.ts
Normal file
388
spine-ts/core/src/PathConstraint.ts
Normal file
@ -0,0 +1,388 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class PathConstraint implements Updatable {
|
||||
static NONE = -1; static BEFORE = -2; static AFTER = -3;
|
||||
|
||||
data: PathConstraintData;
|
||||
bones: Array<Bone>;
|
||||
target: Slot;
|
||||
position = 0; spacing = 0; rotateMix = 0; translateMix = 0;
|
||||
|
||||
spaces = new Array<number>(); positions = new Array<number>();
|
||||
world = new Array<number>(); curves = new Array<number>(); lengths = new Array<number>();
|
||||
segments = new Array<number>();
|
||||
|
||||
constructor (data: PathConstraintData, skeleton: Skeleton) {
|
||||
if (data == null) throw new Error("data cannot be null.");
|
||||
if (skeleton == null) throw new Error("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.bones = new Array<Bone>();
|
||||
for (let i = 0, n = data.bones.length; i < n; i++)
|
||||
this.bones.push(skeleton.findBone(data.bones[i].name));
|
||||
this.target = skeleton.findSlot(data.target.name);
|
||||
this.position = data.position;
|
||||
this.spacing = data.spacing;
|
||||
this.rotateMix = data.rotateMix;
|
||||
this.translateMix = data.translateMix;
|
||||
}
|
||||
|
||||
apply () {
|
||||
this.update();
|
||||
}
|
||||
|
||||
update () {
|
||||
let attachment = this.target.getAttachment();
|
||||
if (!(attachment instanceof PathAttachment)) return;
|
||||
|
||||
let rotateMix = this.rotateMix, translateMix = this.translateMix;
|
||||
let translate = translateMix > 0, rotate = rotateMix > 0;
|
||||
if (!translate && !rotate) return;
|
||||
|
||||
let data = this.data;
|
||||
let spacingMode = data.spacingMode;
|
||||
let lengthSpacing = spacingMode == SpacingMode.Length;
|
||||
let rotateMode = data.rotateMode;
|
||||
let tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale;
|
||||
let boneCount = this.bones.length, spacesCount = tangents ? boneCount : boneCount + 1;
|
||||
let bones = this.bones;
|
||||
let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array<number> = null;
|
||||
let spacing = this.spacing;
|
||||
if (scale || lengthSpacing) {
|
||||
if (scale) lengths = Utils.setArraySize(this.lengths, boneCount);
|
||||
for (let i = 0, n = spacesCount - 1; i < n;) {
|
||||
let bone = bones[i];
|
||||
let length = bone.data.length, x = length * bone.a, y = length * bone.c;
|
||||
length = Math.sqrt(x * x + y * y);
|
||||
if (scale) lengths[i] = length;
|
||||
spaces[++i] = lengthSpacing ? Math.max(0, length + spacing) : spacing;
|
||||
}
|
||||
} else {
|
||||
for (let i = 1; i < spacesCount; i++)
|
||||
spaces[i] = spacing;
|
||||
}
|
||||
|
||||
let positions = this.computeWorldPositions(<PathAttachment>attachment, spacesCount, tangents,
|
||||
data.positionMode == PositionMode.Percent, spacingMode == SpacingMode.Percent);
|
||||
let skeleton = this.target.bone.skeleton;
|
||||
let skeletonX = skeleton.x, skeletonY = skeleton.y;
|
||||
let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
|
||||
let tip = rotateMode == RotateMode.Chain && offsetRotation == 0;
|
||||
for (let i = 0, p = 3; i < boneCount; i++, p += 3) {
|
||||
let bone = bones[i];
|
||||
bone.worldX += (boneX - skeletonX - bone.worldX) * translateMix;
|
||||
bone.worldY += (boneY - skeletonY - bone.worldY) * translateMix;
|
||||
let x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
|
||||
if (scale) {
|
||||
let length = lengths[i];
|
||||
if (length != 0) {
|
||||
let s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
}
|
||||
}
|
||||
boneX = x;
|
||||
boneY = y;
|
||||
if (rotate) {
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d, r = 0, cos = 0, sin = 0;
|
||||
if (tangents)
|
||||
r = positions[p - 1];
|
||||
else if (spaces[i + 1] == 0)
|
||||
r = positions[p + 2];
|
||||
else
|
||||
r = Math.atan2(dy, dx);
|
||||
r -= Math.atan2(c, a) - offsetRotation * MathUtils.degRad;
|
||||
if (tip) {
|
||||
cos = Math.cos(r);
|
||||
sin = Math.sin(r);
|
||||
let length = bone.data.length;
|
||||
boneX += (length * (cos * a - sin * c) - dx) * rotateMix;
|
||||
boneY += (length * (sin * a + cos * c) - dy) * rotateMix;
|
||||
}
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) //
|
||||
r += MathUtils.PI2;
|
||||
r *= rotateMix;
|
||||
cos = Math.cos(r);
|
||||
sin = Math.sin(r);
|
||||
bone.a = cos * a - sin * c;
|
||||
bone.b = cos * b - sin * d;
|
||||
bone.c = sin * a + cos * c;
|
||||
bone.d = sin * b + cos * d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean,
|
||||
percentSpacing: boolean) {
|
||||
let target = this.target;
|
||||
let position = this.position;
|
||||
let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array<number> = null;
|
||||
let closed = path.closed;
|
||||
let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE;
|
||||
|
||||
if (!path.constantSpeed) {
|
||||
let lengths = path.lengths;
|
||||
curveCount -= closed ? 1 : 2;
|
||||
let pathLength = lengths[curveCount];
|
||||
if (percentPosition) position *= pathLength;
|
||||
if (percentSpacing) {
|
||||
for (let i = 0; i < spacesCount; i++)
|
||||
spaces[i] *= pathLength;
|
||||
}
|
||||
world = Utils.setArraySize(this.world, 8);
|
||||
for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
|
||||
let space = spaces[i];
|
||||
position += space;
|
||||
let p = position;
|
||||
|
||||
if (closed) {
|
||||
p %= pathLength;
|
||||
if (p < 0) p += pathLength;
|
||||
curve = 0;
|
||||
} else if (p < 0) {
|
||||
if (prevCurve != PathConstraint.BEFORE) {
|
||||
prevCurve = PathConstraint.BEFORE;
|
||||
path.computeWorldVerticesWith(target, 2, 4, world, 0);
|
||||
}
|
||||
this.addBeforePosition(p, world, 0, out, o);
|
||||
continue;
|
||||
} else if (p > pathLength) {
|
||||
if (prevCurve != PathConstraint.AFTER) {
|
||||
prevCurve = PathConstraint.AFTER;
|
||||
path.computeWorldVerticesWith(target, verticesLength - 6, 4, world, 0);
|
||||
}
|
||||
this.addAfterPosition(p - pathLength, world, 0, out, o);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine curve containing position.
|
||||
for (;; curve++) {
|
||||
let length = lengths[curve];
|
||||
if (p > length) continue;
|
||||
if (curve == 0)
|
||||
p /= length;
|
||||
else {
|
||||
let prev = lengths[curve - 1];
|
||||
p = (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (curve != prevCurve) {
|
||||
prevCurve = curve;
|
||||
if (closed && curve == curveCount) {
|
||||
path.computeWorldVerticesWith(target, verticesLength - 4, 4, world, 0);
|
||||
path.computeWorldVerticesWith(target, 0, 4, world, 4);
|
||||
} else
|
||||
path.computeWorldVerticesWith(target, curve * 6 + 2, 8, world, 0);
|
||||
}
|
||||
this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o,
|
||||
tangents || (i > 0 && space == 0));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// World vertices.
|
||||
if (closed) {
|
||||
verticesLength += 2;
|
||||
world = Utils.setArraySize(this.world, verticesLength);
|
||||
path.computeWorldVerticesWith(target, 2, verticesLength - 4, world, 0);
|
||||
path.computeWorldVerticesWith(target, 0, 2, world, verticesLength - 4);
|
||||
world[verticesLength - 2] = world[0];
|
||||
world[verticesLength - 1] = world[1];
|
||||
} else {
|
||||
curveCount--;
|
||||
verticesLength -= 4;
|
||||
world = Utils.setArraySize(this.world, verticesLength);
|
||||
path.computeWorldVerticesWith(target, 2, verticesLength, world, 0);
|
||||
}
|
||||
|
||||
// Curve lengths.
|
||||
let curves = Utils.setArraySize(this.curves, curveCount);
|
||||
let pathLength = 0;
|
||||
let x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
|
||||
let tmpx = 0, tmpy = 0, dddfx = 0, dddfy = 0, ddfx = 0, ddfy = 0, dfx = 0, dfy = 0;
|
||||
for (let i = 0, w = 2; i < curveCount; i++, w += 6) {
|
||||
cx1 = world[w];
|
||||
cy1 = world[w + 1];
|
||||
cx2 = world[w + 2];
|
||||
cy2 = world[w + 3];
|
||||
x2 = world[w + 4];
|
||||
y2 = world[w + 5];
|
||||
tmpx = (x1 - cx1 * 2 + cx2) * 0.1875;
|
||||
tmpy = (y1 - cy1 * 2 + cy2) * 0.1875;
|
||||
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375;
|
||||
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375;
|
||||
ddfx = tmpx * 2 + dddfx;
|
||||
ddfy = tmpy * 2 + dddfy;
|
||||
dfx = (cx1 - x1) * 0.75 + tmpx + dddfx * 0.16666667;
|
||||
dfy = (cy1 - y1) * 0.75 + tmpy + dddfy * 0.16666667;
|
||||
pathLength += Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
ddfx += dddfx;
|
||||
ddfy += dddfy;
|
||||
pathLength += Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
pathLength += Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
dfx += ddfx + dddfx;
|
||||
dfy += ddfy + dddfy;
|
||||
pathLength += Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
curves[i] = pathLength;
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
}
|
||||
if (percentPosition) position *= pathLength;
|
||||
if (percentSpacing) {
|
||||
for (let i = 0; i < spacesCount; i++)
|
||||
spaces[i] *= pathLength;
|
||||
}
|
||||
|
||||
let segments = this.segments;
|
||||
let curveLength = 0;
|
||||
for (let i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
|
||||
let space = spaces[i];
|
||||
position += space;
|
||||
let p = position;
|
||||
|
||||
if (closed) {
|
||||
p %= pathLength;
|
||||
if (p < 0) p += pathLength;
|
||||
curve = 0;
|
||||
} else if (p < 0) {
|
||||
this.addBeforePosition(p, world, 0, out, o);
|
||||
continue;
|
||||
} else if (p > pathLength) {
|
||||
this.addAfterPosition(p - pathLength, world, verticesLength - 4, out, o);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine curve containing position.
|
||||
for (;; curve++) {
|
||||
let length = curves[curve];
|
||||
if (p > length) continue;
|
||||
if (curve == 0)
|
||||
p /= length;
|
||||
else {
|
||||
let prev = curves[curve - 1];
|
||||
p = (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Curve segment lengths.
|
||||
if (curve != prevCurve) {
|
||||
prevCurve = curve;
|
||||
let ii = curve * 6;
|
||||
x1 = world[ii];
|
||||
y1 = world[ii + 1];
|
||||
cx1 = world[ii + 2];
|
||||
cy1 = world[ii + 3];
|
||||
cx2 = world[ii + 4];
|
||||
cy2 = world[ii + 5];
|
||||
x2 = world[ii + 6];
|
||||
y2 = world[ii + 7];
|
||||
tmpx = (x1 - cx1 * 2 + cx2) * 0.03;
|
||||
tmpy = (y1 - cy1 * 2 + cy2) * 0.03;
|
||||
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006;
|
||||
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006;
|
||||
ddfx = tmpx * 2 + dddfx;
|
||||
ddfy = tmpy * 2 + dddfy;
|
||||
dfx = (cx1 - x1) * 0.3 + tmpx + dddfx * 0.16666667;
|
||||
dfy = (cy1 - y1) * 0.3 + tmpy + dddfy * 0.16666667;
|
||||
curveLength = Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
segments[0] = curveLength;
|
||||
for (ii = 1; ii < 8; ii++) {
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
ddfx += dddfx;
|
||||
ddfy += dddfy;
|
||||
curveLength += Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
segments[ii] = curveLength;
|
||||
}
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
curveLength += Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
segments[8] = curveLength;
|
||||
dfx += ddfx + dddfx;
|
||||
dfy += ddfy + dddfy;
|
||||
curveLength += Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
segments[9] = curveLength;
|
||||
segment = 0;
|
||||
}
|
||||
|
||||
// Weight by segment length.
|
||||
p *= curveLength;
|
||||
for (;; segment++) {
|
||||
let length = segments[segment];
|
||||
if (p > length) continue;
|
||||
if (segment == 0)
|
||||
p /= length;
|
||||
else {
|
||||
let prev = segments[segment - 1];
|
||||
p = segment + (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
addBeforePosition (p: number, temp: Array<number>, i: number, out: Array<number>, o: number) {
|
||||
let x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx);
|
||||
out[o] = x1 + p * Math.cos(r);
|
||||
out[o + 1] = y1 + p * Math.sin(r);
|
||||
out[o + 2] = r;
|
||||
}
|
||||
|
||||
addAfterPosition (p: number, temp: Array<number>, i: number, out: Array<number>, o: number) {
|
||||
let x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx);
|
||||
out[o] = x1 + p * Math.cos(r);
|
||||
out[o + 1] = y1 + p * Math.sin(r);
|
||||
out[o + 2] = r;
|
||||
}
|
||||
|
||||
addCurvePosition (p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number,
|
||||
out: Array<number>, o: number, tangents: boolean) {
|
||||
if (p == 0) p = 0.0001;
|
||||
let tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
|
||||
let ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
|
||||
let x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
|
||||
out[o] = x;
|
||||
out[o + 1] = y;
|
||||
if (tangents) out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
|
||||
}
|
||||
}
|
||||
}
|
||||
59
spine-ts/core/src/PathConstraintData.ts
Normal file
59
spine-ts/core/src/PathConstraintData.ts
Normal file
@ -0,0 +1,59 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class PathConstraintData {
|
||||
name: string;
|
||||
bones = new Array<BoneData>();
|
||||
target: SlotData;
|
||||
positionMode: PositionMode;
|
||||
spacingMode: SpacingMode;
|
||||
rotateMode: RotateMode;
|
||||
offsetRotation: number;
|
||||
position: number; spacing: number; rotateMix: number; translateMix: number;
|
||||
|
||||
constructor (name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
export enum PositionMode {
|
||||
Fixed, Percent
|
||||
}
|
||||
|
||||
export enum SpacingMode {
|
||||
Length, Fixed, Percent
|
||||
}
|
||||
|
||||
export enum RotateMode {
|
||||
Tangent, Chain, ChainScale
|
||||
}
|
||||
}
|
||||
469
spine-ts/core/src/Skeleton.ts
Normal file
469
spine-ts/core/src/Skeleton.ts
Normal file
@ -0,0 +1,469 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class Skeleton {
|
||||
data: SkeletonData;
|
||||
bones: Array<Bone>;
|
||||
slots: Array<Slot>;
|
||||
drawOrder: Array<Slot>;
|
||||
ikConstraints: Array<IkConstraint>; ikConstraintsSorted: Array<IkConstraint>;
|
||||
transformConstraints: Array<TransformConstraint>;
|
||||
pathConstraints: Array<PathConstraint>;
|
||||
_updateCache = new Array<Updatable>();
|
||||
skin: Skin;
|
||||
color: Color;
|
||||
time = 0;
|
||||
flipX = false; flipY = false;
|
||||
x = 0; y = 0;
|
||||
|
||||
constructor (data: SkeletonData) {
|
||||
if (data == null) throw new Error("data cannot be null.");
|
||||
this.data = data;
|
||||
|
||||
this.bones = new Array<Bone>();
|
||||
for (let i = 0; i < data.bones.length; i++) {
|
||||
let boneData = data.bones[i];
|
||||
let bone: Bone;
|
||||
if (boneData.parent == null)
|
||||
bone = new Bone(boneData, this, null);
|
||||
else {
|
||||
let parent = this.bones[boneData.parent.index];
|
||||
bone = new Bone(boneData, this, parent);
|
||||
parent.children.push(bone);
|
||||
}
|
||||
this.bones.push(bone);
|
||||
}
|
||||
|
||||
this.slots = new Array<Slot>();
|
||||
this.drawOrder = new Array<Slot>();
|
||||
for (let i = 0; i < data.slots.length; i++) {
|
||||
let slotData = data.slots[i];
|
||||
let bone = this.bones[slotData.boneData.index];
|
||||
let slot = new Slot(slotData, bone);
|
||||
this.slots.push(slot);
|
||||
this.drawOrder.push(slot);
|
||||
}
|
||||
|
||||
this.ikConstraints = new Array<IkConstraint>();
|
||||
this.ikConstraintsSorted = new Array<IkConstraint>();
|
||||
for (let i = 0; i < data.ikConstraints.length; i++) {
|
||||
let ikConstraintData = data.ikConstraints[i];
|
||||
this.ikConstraints.push(new IkConstraint(ikConstraintData, this));
|
||||
}
|
||||
|
||||
this.transformConstraints = new Array<TransformConstraint>();
|
||||
for (let i = 0; i < data.transformConstraints.length; i++) {
|
||||
let transformConstraintData = data.transformConstraints[i];
|
||||
this.transformConstraints.push(new TransformConstraint(transformConstraintData, this));
|
||||
}
|
||||
|
||||
this.pathConstraints = new Array<PathConstraint>();
|
||||
for (let i = 0; i < data.pathConstraints.length; i++) {
|
||||
let pathConstraintData = data.pathConstraints[i];
|
||||
this.pathConstraints.push(new PathConstraint(pathConstraintData, this));
|
||||
}
|
||||
|
||||
this.color = new Color(1, 1, 1, 1);
|
||||
this.updateCache();
|
||||
}
|
||||
|
||||
updateCache () {
|
||||
let updateCache = this._updateCache;
|
||||
updateCache.length = 0;
|
||||
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++)
|
||||
bones[i].sorted = false;
|
||||
|
||||
// IK first, lowest hierarchy depth first.
|
||||
let ikConstraints = this.ikConstraintsSorted;
|
||||
ikConstraints.length = 0;
|
||||
for (let i = 0; i < this.ikConstraints.length; i++)
|
||||
ikConstraints.push(this.ikConstraints[i]);
|
||||
let ikCount = ikConstraints.length;
|
||||
for (let i = 0, level = 0, n = ikCount; i < n; i++) {
|
||||
let ik = ikConstraints[i];
|
||||
let bone = ik.bones[0].parent;
|
||||
for (level = 0; bone != null; level++)
|
||||
bone = bone.parent;
|
||||
ik.level = level;
|
||||
}
|
||||
for (let i = 1, ii = 0; i < ikCount; i++) {
|
||||
let ik = ikConstraints[i];
|
||||
let level = ik.level;
|
||||
for (ii = i - 1; ii >= 0; ii--) {
|
||||
let other = ikConstraints[ii];
|
||||
if (other.level < level) break;
|
||||
ikConstraints[ii + 1] = other;
|
||||
}
|
||||
ikConstraints[ii + 1] = ik;
|
||||
}
|
||||
for (let i = 0, n = ikConstraints.length; i < n; i++) {
|
||||
let constraint = ikConstraints[i];
|
||||
let target = constraint.target;
|
||||
this.sortBone(target);
|
||||
|
||||
let constrained = constraint.bones;
|
||||
let parent = constrained[0];
|
||||
this.sortBone(parent);
|
||||
|
||||
updateCache.push(constraint);
|
||||
|
||||
this.sortReset(parent.children);
|
||||
constrained[constrained.length - 1].sorted = true;
|
||||
}
|
||||
|
||||
let pathConstraints = this.pathConstraints;
|
||||
for (let i = 0, n = pathConstraints.length; i < n; i++) {
|
||||
let constraint = pathConstraints[i];
|
||||
|
||||
let slot = constraint.target;
|
||||
let slotIndex = slot.data.index;
|
||||
let slotBone = slot.bone;
|
||||
if (this.skin != null) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone);
|
||||
if (this.data.defaultSkin != null && this.data.defaultSkin != this.skin)
|
||||
this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone);
|
||||
for (let ii = 0, nn = this.data.skins.length; ii < nn; ii++)
|
||||
this.sortPathConstraintAttachment(this.data.skins[ii], slotIndex, slotBone);
|
||||
|
||||
let attachment = slot.getAttachment();
|
||||
if (attachment instanceof PathAttachment) this.sortPathConstraintAttachmentWith(attachment, slotBone);
|
||||
|
||||
let constrained = constraint.bones;
|
||||
let boneCount = constrained.length;
|
||||
for (let ii = 0; ii < boneCount; ii++)
|
||||
this.sortBone(constrained[ii]);
|
||||
|
||||
updateCache.push(constraint);
|
||||
|
||||
for (let ii = 0; ii < boneCount; ii++)
|
||||
this.sortReset(constrained[ii].children);
|
||||
for (let ii = 0; ii < boneCount; ii++)
|
||||
constrained[ii].sorted = true;
|
||||
}
|
||||
|
||||
let transformConstraints = this.transformConstraints;
|
||||
for (let i = 0, n = transformConstraints.length; i < n; i++) {
|
||||
let constraint = transformConstraints[i];
|
||||
|
||||
this.sortBone(constraint.target);
|
||||
|
||||
let constrained = constraint.bones;
|
||||
let boneCount = constrained.length;
|
||||
for (let ii = 0; ii < boneCount; ii++)
|
||||
this.sortBone(constrained[ii]);
|
||||
|
||||
updateCache.push(constraint);
|
||||
|
||||
for (let ii = 0; ii < boneCount; ii++)
|
||||
this.sortReset(constrained[ii].children);
|
||||
for (let ii = 0; ii < boneCount; ii++)
|
||||
constrained[ii].sorted = true;
|
||||
}
|
||||
|
||||
for (let i = 0, n = bones.length; i < n; i++)
|
||||
this.sortBone(bones[i]);
|
||||
}
|
||||
|
||||
sortPathConstraintAttachment (skin: Skin, slotIndex: number, slotBone: Bone) {
|
||||
let attachments = skin.attachments[slotIndex];
|
||||
if (!attachments) return;
|
||||
for (let key in attachments) {
|
||||
this.sortPathConstraintAttachmentWith(attachments[key], slotBone);
|
||||
}
|
||||
}
|
||||
|
||||
sortPathConstraintAttachmentWith (attachment: Attachment, slotBone: Bone) {
|
||||
if (!(attachment instanceof PathAttachment)) return;
|
||||
let pathBones = (<PathAttachment>attachment).bones;
|
||||
if (pathBones == null)
|
||||
this.sortBone(slotBone);
|
||||
else {
|
||||
let bones = this.bones;
|
||||
for (let i = 0; i < pathBones.length; i++) {
|
||||
let boneIndex = pathBones[i];
|
||||
this.sortBone(bones[boneIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sortBone (bone: Bone) {
|
||||
if (bone.sorted) return;
|
||||
let parent = bone.parent;
|
||||
if (parent != null) this.sortBone(parent);
|
||||
bone.sorted = true;
|
||||
this._updateCache.push(bone);
|
||||
}
|
||||
|
||||
sortReset (bones: Array<Bone>) {
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
if (bone.sorted) this.sortReset(bone.children);
|
||||
bone.sorted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the world transform for each bone and applies constraints. */
|
||||
updateWorldTransform () {
|
||||
let updateCache = this._updateCache;
|
||||
for (let i = 0, n = updateCache.length; i < n; i++)
|
||||
updateCache[i].update();
|
||||
}
|
||||
|
||||
/** Sets the bones, constraints, and slots to their setup pose values. */
|
||||
setToSetupPose () {
|
||||
this.setBonesToSetupPose();
|
||||
this.setSlotsToSetupPose();
|
||||
}
|
||||
|
||||
/** Sets the bones and constraints to their setup pose values. */
|
||||
setBonesToSetupPose () {
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++)
|
||||
bones[i].setToSetupPose();
|
||||
|
||||
let ikConstraints = this.ikConstraints;
|
||||
for (let i = 0, n = ikConstraints.length; i < n; i++) {
|
||||
let constraint = ikConstraints[i];
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.mix = constraint.data.mix;
|
||||
}
|
||||
|
||||
let transformConstraints = this.transformConstraints;
|
||||
for (let i = 0, n = transformConstraints.length; i < n; i++) {
|
||||
let constraint = transformConstraints[i];
|
||||
let data = constraint.data;
|
||||
constraint.rotateMix = data.rotateMix;
|
||||
constraint.translateMix = data.translateMix;
|
||||
constraint.scaleMix = data.scaleMix;
|
||||
constraint.shearMix = data.shearMix;
|
||||
}
|
||||
|
||||
let pathConstraints = this.pathConstraints;
|
||||
for (let i = 0, n = pathConstraints.length; i < n; i++) {
|
||||
let constraint = pathConstraints[i];
|
||||
let data = constraint.data;
|
||||
constraint.position = data.position;
|
||||
constraint.spacing = data.spacing;
|
||||
constraint.rotateMix = data.rotateMix;
|
||||
constraint.translateMix = data.translateMix;
|
||||
}
|
||||
}
|
||||
|
||||
setSlotsToSetupPose () {
|
||||
let slots = this.slots;
|
||||
Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length);
|
||||
for (let i = 0, n = slots.length; i < n; i++)
|
||||
slots[i].setToSetupPose();
|
||||
}
|
||||
|
||||
/** @return May return null. */
|
||||
getRootBone () {
|
||||
if (this.bones.length == 0) return null;
|
||||
return this.bones[0];
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
findBone (boneName: string) {
|
||||
if (boneName == null) throw new Error("boneName cannot be null.");
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
if (bone.data.name == boneName) return bone;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return -1 if the bone was not found. */
|
||||
findBoneIndex (boneName: string) {
|
||||
if (boneName == null) throw new Error("boneName cannot be null.");
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++)
|
||||
if (bones[i].data.name == boneName) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
findSlot (slotName: string) {
|
||||
if (slotName == null) throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++) {
|
||||
let slot = slots[i];
|
||||
if (slot.data.name == slotName) return slot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return -1 if the bone was not found. */
|
||||
findSlotIndex (slotName: string) {
|
||||
if (slotName == null) throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++)
|
||||
if (slots[i].data.name == slotName) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Sets a skin by name.
|
||||
* @see #setSkin(Skin) */
|
||||
setSkinByName (skinName: string) {
|
||||
let skin = this.data.findSkin(skinName);
|
||||
if (skin == null) throw new Error("Skin not found: " + skinName);
|
||||
this.setSkin(skin);
|
||||
}
|
||||
|
||||
/** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}.
|
||||
* Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was no
|
||||
* old skin, each slot's setup mode attachment is attached from the new skin.
|
||||
* @param newSkin May be null. */
|
||||
setSkin (newSkin: Skin) {
|
||||
if (newSkin != null) {
|
||||
if (this.skin != null)
|
||||
newSkin.attachAll(this, this.skin);
|
||||
else {
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++) {
|
||||
let slot = slots[i];
|
||||
let name = slot.data.attachmentName;
|
||||
if (name != null) {
|
||||
let attachment: Attachment = newSkin.getAttachment(i, name);
|
||||
if (attachment != null) slot.setAttachment(attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.skin = newSkin;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
getAttachmentByName (slotName: string, attachmentName: string): Attachment {
|
||||
return this.getAttachment(this.data.findSlotIndex(slotName), attachmentName);
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
getAttachment (slotIndex: number, attachmentName: string): Attachment {
|
||||
if (attachmentName == null) throw new Error("attachmentName cannot be null.");
|
||||
if (this.skin != null) {
|
||||
let attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName);
|
||||
if (attachment != null) return attachment;
|
||||
}
|
||||
if (this.data.defaultSkin != null) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName);
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @param attachmentName May be null. */
|
||||
setAttachment (slotName: string, attachmentName: string) {
|
||||
if (slotName == null) throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++) {
|
||||
let slot = slots[i];
|
||||
if (slot.data.name == slotName) {
|
||||
let attachment: Attachment = null;
|
||||
if (attachmentName != null) {
|
||||
attachment = this.getAttachment(i, attachmentName);
|
||||
if (attachment == null)
|
||||
throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName);
|
||||
}
|
||||
slot.setAttachment(attachment);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new Error("Slot not found: " + slotName);
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
findIkConstraint (constraintName: string) {
|
||||
if (constraintName == null) throw new Error("constraintName cannot be null.");
|
||||
let ikConstraints = this.ikConstraints;
|
||||
for (let i = 0, n = ikConstraints.length; i < n; i++) {
|
||||
let ikConstraint = ikConstraints[i];
|
||||
if (ikConstraint.data.name == constraintName) return ikConstraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
findTransformConstraint (constraintName: string) {
|
||||
if (constraintName == null) throw new Error("constraintName cannot be null.");
|
||||
let transformConstraints = this.transformConstraints;
|
||||
for (let i = 0, n = transformConstraints.length; i < n; i++) {
|
||||
let constraint = transformConstraints[i];
|
||||
if (constraint.data.name == constraintName) return constraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
findPathConstraint (constraintName: string) {
|
||||
if (constraintName == null) throw new Error("constraintName cannot be null.");
|
||||
let pathConstraints = this.pathConstraints;
|
||||
for (let i = 0, n = pathConstraints.length; i < n; i++) {
|
||||
let constraint = pathConstraints[i];
|
||||
if (constraint.data.name == constraintName) return constraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.
|
||||
* @param offset The distance from the skeleton origin to the bottom left corner of the AABB.
|
||||
* @param size The width and height of the AABB. */
|
||||
getBounds (offset: Vector2, size: Vector2) {
|
||||
if (offset == null) throw new Error("offset cannot be null.");
|
||||
if (size == null) throw new Error("size cannot be null.");
|
||||
let drawOrder = this.drawOrder;
|
||||
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
let slot = drawOrder[i];
|
||||
let vertices: ArrayLike<number> = null;
|
||||
let attachment = slot.getAttachment();
|
||||
if (attachment instanceof RegionAttachment)
|
||||
vertices = (<RegionAttachment>attachment).updateWorldVertices(slot, false);
|
||||
else if (attachment instanceof MeshAttachment) //
|
||||
vertices = (<MeshAttachment>attachment).updateWorldVertices(slot, true);
|
||||
if (vertices != null) {
|
||||
for (let ii = 0, nn = vertices.length; ii < nn; ii += 5) {
|
||||
let x = vertices[ii], y = vertices[ii + 1];
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x);
|
||||
maxY = Math.max(maxY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
offset.set(minX, minY);
|
||||
size.set(maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
update (delta: number) {
|
||||
this.time += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
193
spine-ts/core/src/SkeletonBounds.ts
Normal file
193
spine-ts/core/src/SkeletonBounds.ts
Normal file
@ -0,0 +1,193 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class SkeletonBounds {
|
||||
minX = 0; minY = 0; maxX = 0; maxY = 0;
|
||||
boundingBoxes = new Array<BoundingBoxAttachment>();
|
||||
polygons = new Array<ArrayLike<number>>();
|
||||
private _polygonPool = new Pool<ArrayLike<number>>(() => {
|
||||
return Utils.newFloatArray(16);
|
||||
});
|
||||
|
||||
update (skeleton: Skeleton, updateAabb: boolean) {
|
||||
if (skeleton == null) throw new Error("skeleton cannot be null.");
|
||||
let boundingBoxes = this.boundingBoxes;
|
||||
let polygons = this.polygons;
|
||||
let polygonPool = this._polygonPool;
|
||||
let slots = skeleton.slots;
|
||||
let slotCount = slots.length;
|
||||
|
||||
boundingBoxes.length = 0;
|
||||
polygonPool.freeAll(polygons);
|
||||
polygons.length = 0;
|
||||
|
||||
for (let i = 0; i < slotCount; i++) {
|
||||
let slot = slots[i];
|
||||
let attachment = slot.getAttachment();
|
||||
if (attachment instanceof BoundingBoxAttachment) {
|
||||
let boundingBox = attachment as BoundingBoxAttachment;
|
||||
boundingBoxes.push(boundingBox);
|
||||
|
||||
let polygon = polygonPool.obtain();
|
||||
if (polygon.length != boundingBox.worldVerticesLength) {
|
||||
polygon = Utils.newFloatArray(boundingBox.worldVerticesLength);
|
||||
}
|
||||
polygons.push(polygon);
|
||||
boundingBox.computeWorldVertices(slot, polygon);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateAabb) this.aabbCompute();
|
||||
}
|
||||
|
||||
aabbCompute () {
|
||||
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
|
||||
let polygons = this.polygons;
|
||||
for (let i = 0, n = polygons.length; i < n; i++) {
|
||||
let polygon = polygons[i];
|
||||
let vertices = polygon;
|
||||
for (let ii = 0, nn = polygon.length; ii < nn; ii += 2) {
|
||||
let x = vertices[ii];
|
||||
let y = vertices[ii + 1];
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x);
|
||||
maxY = Math.max(maxY, y);
|
||||
}
|
||||
}
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
}
|
||||
|
||||
/** Returns true if the axis aligned bounding box contains the point. */
|
||||
aabbContainsPoint (x: number, y: number) {
|
||||
return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY;
|
||||
}
|
||||
|
||||
/** Returns true if the axis aligned bounding box intersects the line segment. */
|
||||
aabbIntersectsSegment (x1: number, y1: number, x2: number, y2: number) {
|
||||
let minX = this.minX;
|
||||
let minY = this.minY;
|
||||
let maxX = this.maxX;
|
||||
let maxY = this.maxY;
|
||||
if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
|
||||
return false;
|
||||
let m = (y2 - y1) / (x2 - x1);
|
||||
let y = m * (minX - x1) + y1;
|
||||
if (y > minY && y < maxY) return true;
|
||||
y = m * (maxX - x1) + y1;
|
||||
if (y > minY && y < maxY) return true;
|
||||
let x = (minY - y1) / m + x1;
|
||||
if (x > minX && x < maxX) return true;
|
||||
x = (maxY - y1) / m + x1;
|
||||
if (x > minX && x < maxX) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
|
||||
aabbIntersectsSkeleton (bounds: SkeletonBounds) {
|
||||
return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY;
|
||||
}
|
||||
|
||||
/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
|
||||
* efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */
|
||||
containsPoint (x: number, y: number): BoundingBoxAttachment {
|
||||
let polygons = this.polygons;
|
||||
for (let i = 0, n = polygons.length; i < n; i++)
|
||||
if (this.containsPointPolygon(polygons[i], x, y)) return this.boundingBoxes[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns true if the polygon contains the point. */
|
||||
containsPointPolygon (polygon: ArrayLike<number>, x: number, y: number) {
|
||||
let vertices = polygon;
|
||||
let nn = polygon.length;
|
||||
|
||||
let prevIndex = nn - 2;
|
||||
let inside = false;
|
||||
for (let ii = 0; ii < nn; ii += 2) {
|
||||
let vertexY = vertices[ii + 1];
|
||||
let prevY = vertices[prevIndex + 1];
|
||||
if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
|
||||
let vertexX = vertices[ii];
|
||||
if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside;
|
||||
}
|
||||
prevIndex = ii;
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
|
||||
/** Returns the first bounding box attachment that contains any part of the line segment, or null. When doing many checks, it
|
||||
* is usually more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns
|
||||
* true. */
|
||||
intersectsSegment (x1: number, y1: number, x2: number, y2: number) {
|
||||
let polygons = this.polygons;
|
||||
for (let i = 0, n = polygons.length; i < n; i++)
|
||||
if (this.intersectsSegmentPolygon(polygons[i], x1, y1, x2, y2)) return this.boundingBoxes[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns true if the polygon contains any part of the line segment. */
|
||||
intersectsSegmentPolygon (polygon: ArrayLike<number>, x1: number, y1: number, x2: number, y2: number) {
|
||||
let vertices = polygon;
|
||||
let nn = polygon.length;
|
||||
|
||||
let width12 = x1 - x2, height12 = y1 - y2;
|
||||
let det1 = x1 * y2 - y1 * x2;
|
||||
let x3 = vertices[nn - 2], y3 = vertices[nn - 1];
|
||||
for (let ii = 0; ii < nn; ii += 2) {
|
||||
let x4 = vertices[ii], y4 = vertices[ii + 1];
|
||||
let det2 = x3 * y4 - y3 * x4;
|
||||
let width34 = x3 - x4, height34 = y3 - y4;
|
||||
let det3 = width12 * height34 - height12 * width34;
|
||||
let x = (det1 * width34 - width12 * det2) / det3;
|
||||
if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
|
||||
let y = (det1 * height34 - height12 * det2) / det3;
|
||||
if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
|
||||
}
|
||||
x3 = x4;
|
||||
y3 = y4;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the polygon for the specified bounding box, or null. */
|
||||
getPolygon (boundingBox: BoundingBoxAttachment) {
|
||||
if (boundingBox == null) throw new Error("boundingBox cannot be null.");
|
||||
let index = this.boundingBoxes.indexOf(boundingBox);
|
||||
return index == -1 ? null : this.polygons[index];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
151
spine-ts/core/src/SkeletonData.ts
Normal file
151
spine-ts/core/src/SkeletonData.ts
Normal file
@ -0,0 +1,151 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class SkeletonData {
|
||||
name: string;
|
||||
bones = new Array<BoneData>(); // Ordered parents first.
|
||||
slots = new Array<SlotData>(); // Setup pose draw order.
|
||||
skins = new Array<Skin>();
|
||||
defaultSkin: Skin;
|
||||
events = new Array<EventData>();
|
||||
animations = new Array<Animation>();
|
||||
ikConstraints = new Array<IkConstraintData>();
|
||||
transformConstraints = new Array<TransformConstraintData>();
|
||||
pathConstraints = new Array<PathConstraintData>();
|
||||
width: number; height: number;
|
||||
version: string; hash: string; imagesPath: string;
|
||||
|
||||
findBone (boneName: string) {
|
||||
if (boneName == null) throw new Error("boneName cannot be null.");
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
if (bone.name == boneName) return bone;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findBoneIndex (boneName: string) {
|
||||
if (boneName == null) throw new Error("boneName cannot be null.");
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++)
|
||||
if (bones[i].name == boneName) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
findSlot (slotName: string) {
|
||||
if (slotName == null) throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++) {
|
||||
let slot = slots[i];
|
||||
if (slot.name == slotName) return slot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findSlotIndex (slotName: string) {
|
||||
if (slotName == null) throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++)
|
||||
if (slots[i].name == slotName) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
findSkin (skinName: string) {
|
||||
if (skinName == null) throw new Error("skinName cannot be null.");
|
||||
let skins = this.skins;
|
||||
for (let i = 0, n = skins.length; i < n; i++) {
|
||||
let skin = skins[i];
|
||||
if (skin.name == skinName) return skin;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findEvent (eventDataName: string) {
|
||||
if (eventDataName == null) throw new Error("eventDataName cannot be null.");
|
||||
let events = this.events;
|
||||
for (let i = 0, n = events.length; i < n; i++) {
|
||||
let event = events[i];
|
||||
if (event.name == eventDataName) return event;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findAnimation (animationName: string) {
|
||||
if (animationName == null) throw new Error("animationName cannot be null.");
|
||||
let animations = this.animations;
|
||||
for (let i = 0, n = animations.length; i < n; i++) {
|
||||
let animation = animations[i];
|
||||
if (animation.name == animationName) return animation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findIkConstraint (constraintName: string) {
|
||||
if (constraintName == null) throw new Error("constraintName cannot be null.");
|
||||
let ikConstraints = this.ikConstraints;
|
||||
for (let i = 0, n = ikConstraints.length; i < n; i++) {
|
||||
let constraint = ikConstraints[i];
|
||||
if (constraint.name == constraintName) return constraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findTransformConstraint (constraintName: string) {
|
||||
if (constraintName == null) throw new Error("constraintName cannot be null.");
|
||||
let transformConstraints = this.transformConstraints;
|
||||
for (let i = 0, n = transformConstraints.length; i < n; i++) {
|
||||
let constraint = transformConstraints[i];
|
||||
if (constraint.name == constraintName) return constraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findPathConstraint (constraintName: string) {
|
||||
if (constraintName == null) throw new Error("constraintName cannot be null.");
|
||||
let pathConstraints = this.pathConstraints;
|
||||
for (let i = 0, n = pathConstraints.length; i < n; i++) {
|
||||
let constraint = pathConstraints[i];
|
||||
if (constraint.name == constraintName) return constraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findPathConstraintIndex (pathConstraintName: string) {
|
||||
if (pathConstraintName == null) throw new Error("pathConstraintName cannot be null.");
|
||||
let pathConstraints = this.pathConstraints;
|
||||
for (let i = 0, n = pathConstraints.length; i < n; i++)
|
||||
if (pathConstraints[i].name == pathConstraintName) return i;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
715
spine-ts/core/src/SkeletonJson.ts
Normal file
715
spine-ts/core/src/SkeletonJson.ts
Normal file
@ -0,0 +1,715 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class SkeletonJson {
|
||||
attachmentLoader: AttachmentLoader;
|
||||
scale = 1;
|
||||
private linkedMeshes = new Array<LinkedMesh>();
|
||||
|
||||
constructor (attachmentLoader: AttachmentLoader) {
|
||||
this.attachmentLoader = attachmentLoader;
|
||||
}
|
||||
|
||||
readSkeletonData (json: string): SkeletonData {
|
||||
let scale = this.scale;
|
||||
let skeletonData = new SkeletonData();
|
||||
let root = JSON.parse(json);
|
||||
|
||||
// Skeleton
|
||||
let skeletonMap = root.skeleton;
|
||||
if (skeletonMap != null) {
|
||||
skeletonData.hash = skeletonMap.hash;
|
||||
skeletonData.version = skeletonMap.spine;
|
||||
skeletonData.width = skeletonMap.width;
|
||||
skeletonData.height = skeletonMap.height;
|
||||
skeletonData.imagesPath = skeletonMap.images;
|
||||
}
|
||||
|
||||
// Bones
|
||||
if (root.bones) {
|
||||
for (let i = 0; i < root.bones.length; i++) {
|
||||
let boneMap = root.bones[i];
|
||||
|
||||
let parent: BoneData = null;
|
||||
let parentName: string = this.getValue(boneMap, "parent", null);
|
||||
if (parentName != null) {
|
||||
parent = skeletonData.findBone(parentName);
|
||||
if (parent == null) throw new Error("Parent bone not found: " + parentName);
|
||||
}
|
||||
let data = new BoneData(skeletonData.bones.length, boneMap.name, parent);
|
||||
data.length = this.getValue(boneMap, "length", 0) * scale;
|
||||
data.x = this.getValue(boneMap, "x", 0) * scale;
|
||||
data.y = this.getValue(boneMap, "y", 0) * scale;
|
||||
data.rotation = this.getValue(boneMap, "rotation", 0);
|
||||
data.scaleX = this.getValue(boneMap, "scaleX", 1);
|
||||
data.scaleY = this.getValue(boneMap, "scaleY", 1);
|
||||
data.shearX = this.getValue(boneMap, "shearX", 0);
|
||||
data.shearY = this.getValue(boneMap, "shearY", 0);
|
||||
data.inheritRotation = this.getValue(boneMap, "inheritRotation", true);
|
||||
data.inheritScale = this.getValue(boneMap, "inheritScale", true);
|
||||
|
||||
skeletonData.bones.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Slots.
|
||||
if (root.slots) {
|
||||
for (let i = 0; i < root.slots.length; i++) {
|
||||
let slotMap = root.slots[i];
|
||||
let slotName: string = slotMap.name;
|
||||
let boneName: string = slotMap.bone;
|
||||
let boneData = skeletonData.findBone(boneName);
|
||||
if (boneData == null) throw new Error("Slot bone not found: " + boneName);
|
||||
let data = new SlotData(skeletonData.slots.length, slotName, boneData);
|
||||
|
||||
let color: string = slotMap.color ? slotMap.color : null;
|
||||
if (color != null) data.color.setFromString(color);
|
||||
|
||||
data.attachmentName = this.getValue(slotMap, "attachment", null);
|
||||
data.blendMode = SkeletonJson.blendModeFromString(this.getValue(slotMap, "blend", "normal"));
|
||||
skeletonData.slots.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
// IK constraints
|
||||
if (root.ik) {
|
||||
for (let i = 0; i < root.ik.length; i++) {
|
||||
let constraintMap = root.ik[i];
|
||||
let data = new IkConstraintData(constraintMap.name);
|
||||
|
||||
for (let j = 0; j < constraintMap.bones.length; j++) {
|
||||
let boneName = constraintMap.bones[j];
|
||||
let bone = skeletonData.findBone(boneName);
|
||||
if (bone == null) throw new Error("IK bone not found: " + boneName);
|
||||
data.bones.push(bone);
|
||||
}
|
||||
|
||||
let targetName: string = constraintMap.target;
|
||||
data.target = skeletonData.findBone(targetName);
|
||||
if (data.target == null) throw new Error("IK target bone not found: " + targetName);
|
||||
|
||||
data.bendDirection = this.getValue(constraintMap, "bendPositive", true) ? 1 : -1;
|
||||
data.mix = constraintMap.mix ? constraintMap.mix : 1;
|
||||
|
||||
skeletonData.ikConstraints.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Transform constraints.
|
||||
if (root.transform) {
|
||||
for (let i = 0; i < root.transform.length; i++) {
|
||||
let constraintMap = root.transform[i];
|
||||
let data = new TransformConstraintData(constraintMap.name);
|
||||
|
||||
for (let j = 0; j < constraintMap.bones.length; j++) {
|
||||
let boneName = constraintMap.bones[j];
|
||||
let bone = skeletonData.findBone(boneName);
|
||||
if (bone == null) throw new Error("Transform constraint bone not found: " + boneName);
|
||||
data.bones.push(bone);
|
||||
}
|
||||
|
||||
let targetName: string = constraintMap.target;
|
||||
data.target = skeletonData.findBone(targetName);
|
||||
if (data.target == null) throw new Error("Transform constraint target bone not found: " + targetName);
|
||||
|
||||
data.offsetRotation = this.getValue(constraintMap, "rotation", 0);
|
||||
data.offsetX = this.getValue(constraintMap, "x", 0) * scale;
|
||||
data.offsetY = this.getValue(constraintMap, "y", 0) * scale;
|
||||
data.offsetScaleX = this.getValue(constraintMap, "scaleX", 0);
|
||||
data.offsetScaleY = this.getValue(constraintMap, "scaleY", 0);
|
||||
data.offsetShearY = this.getValue(constraintMap, "shearY", 0);
|
||||
|
||||
data.rotateMix = this.getValue(constraintMap, "rotateMix", 1);
|
||||
data.translateMix = this.getValue(constraintMap, "translateMix", 1);
|
||||
data.scaleMix = this.getValue(constraintMap, "scaleMix", 1);
|
||||
data.shearMix = this.getValue(constraintMap, "shearMix", 1);
|
||||
|
||||
skeletonData.transformConstraints.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Path constraints.
|
||||
if (root.path) {
|
||||
for (let i = 0; i < root.path.length; i++) {
|
||||
let constraintMap = root.path[i];
|
||||
let data = new PathConstraintData(constraintMap.name);
|
||||
|
||||
for (let j = 0; j < constraintMap.bones.length; j++) {
|
||||
let boneName = constraintMap.bones[j];
|
||||
let bone = skeletonData.findBone(boneName);
|
||||
if (bone == null) throw new Error("Transform constraint bone not found: " + boneName);
|
||||
data.bones.push(bone);
|
||||
}
|
||||
|
||||
let targetName: string = constraintMap.target;
|
||||
data.target = skeletonData.findSlot(targetName);
|
||||
if (data.target == null) throw new Error("Path target slot not found: " + targetName);
|
||||
|
||||
data.positionMode = SkeletonJson.positionModeFromString(this.getValue(constraintMap, "positionMode", "percent"));
|
||||
data.spacingMode = SkeletonJson.spacingModeFromString(this.getValue(constraintMap, "spacingMode", "length"));
|
||||
data.rotateMode = SkeletonJson.rotateModeFromString(this.getValue(constraintMap, "rotateMode", "tangent"));
|
||||
data.offsetRotation = this.getValue(constraintMap, "rotation", 0);
|
||||
data.position = this.getValue(constraintMap, "position", 0);
|
||||
if (data.positionMode == PositionMode.Fixed) data.position *= scale;
|
||||
data.spacing = this.getValue(constraintMap, "spacing", 0);
|
||||
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
|
||||
data.rotateMix = this.getValue(constraintMap, "rotateMix", 1);
|
||||
data.translateMix = this.getValue(constraintMap, "translateMix", 1);
|
||||
|
||||
skeletonData.pathConstraints.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Skins.
|
||||
if (root.skins) {
|
||||
for (let skinName in root.skins) {
|
||||
let skinMap = root.skins[skinName]
|
||||
let skin = new Skin(skinName);
|
||||
for (let slotName in skinMap) {
|
||||
let slotIndex = skeletonData.findSlotIndex(slotName);
|
||||
if (slotIndex == -1) throw new Error("Slot not found: " + slotName);
|
||||
let slotMap = skinMap[slotName];
|
||||
for (let entryName in slotMap) {
|
||||
let attachment = this.readAttachment(slotMap[entryName], skin, slotIndex, entryName);
|
||||
if (attachment != null) skin.addAttachment(slotIndex, entryName, attachment);
|
||||
}
|
||||
}
|
||||
skeletonData.skins.push(skin);
|
||||
if (skin.name == "default") skeletonData.defaultSkin = skin;
|
||||
}
|
||||
}
|
||||
|
||||
// Linked meshes.
|
||||
for (let i = 0, n = this.linkedMeshes.length; i < n; i++) {
|
||||
let linkedMesh = this.linkedMeshes[i];
|
||||
let skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
|
||||
if (skin == null) throw new Error("Skin not found: " + linkedMesh.skin);
|
||||
let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
||||
if (parent == null) throw new Error("Parent mesh not found: " + linkedMesh.parent);
|
||||
linkedMesh.mesh.setParentMesh(<MeshAttachment> parent);
|
||||
linkedMesh.mesh.updateUVs();
|
||||
}
|
||||
this.linkedMeshes.length = 0;
|
||||
|
||||
// Events.
|
||||
if (root.events) {
|
||||
for (let eventName in root.events) {
|
||||
let eventMap = root.events[eventName];
|
||||
let data = new EventData(eventName);
|
||||
data.intValue = this.getValue(eventMap, "int", 0);
|
||||
data.floatValue = this.getValue(eventMap, "float", 0);
|
||||
data.stringValue = this.getValue(eventMap, "string", null);
|
||||
skeletonData.events.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Animations.
|
||||
if (root.animations) {
|
||||
for (let animationName in root.animations) {
|
||||
let animationMap = root.animations[animationName];
|
||||
this.readAnimation(animationMap, animationName, skeletonData);
|
||||
}
|
||||
}
|
||||
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
readAttachment (map: any, skin: Skin, slotIndex: number, name: string): Attachment {
|
||||
let scale = this.scale;
|
||||
name = this.getValue(map, "name", name);
|
||||
|
||||
let type = this.getValue(map, "type", "region");
|
||||
|
||||
switch (type) {
|
||||
case "region": {
|
||||
let path = this.getValue(map, "path", name);
|
||||
let region = this.attachmentLoader.newRegionAttachment(skin, name, path);
|
||||
if (region == null) return null;
|
||||
region.path = path;
|
||||
region.x = this.getValue(map, "x", 0) * scale;
|
||||
region.y = this.getValue(map, "y", 0) * scale;
|
||||
region.scaleX = this.getValue(map, "scaleX", 1);
|
||||
region.scaleY = this.getValue(map, "scaleY", 1);
|
||||
region.rotation = this.getValue(map, "rotation", 0);
|
||||
region.width = map.width * scale;
|
||||
region.height = map.height * scale;
|
||||
|
||||
let color: string = this.getValue(map, "color", null);
|
||||
if (color != null) region.color.setFromString(color);
|
||||
|
||||
region.updateOffset();
|
||||
return region;
|
||||
}
|
||||
case "boundingbox": {
|
||||
let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name);
|
||||
if (box == null) return null;
|
||||
this.readVertices(map, box, map.vertexCount << 1);
|
||||
return box;
|
||||
}
|
||||
case "mesh":
|
||||
case "linkedmesh": {
|
||||
let path = this.getValue(map, "path", name);
|
||||
let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path);
|
||||
if (mesh == null) return null;
|
||||
mesh.path = path;
|
||||
|
||||
let color = this.getValue(map, "color", null);
|
||||
if (color != null) mesh.color.setFromString(color);
|
||||
|
||||
let parent: string = this.getValue(map, "parent", null);
|
||||
if (parent != null) {
|
||||
mesh.inheritDeform = this.getValue(map, "deform", true);
|
||||
this.linkedMeshes.push(new LinkedMesh(mesh, <string> this.getValue(map, "skin", null), slotIndex, parent));
|
||||
return mesh;
|
||||
}
|
||||
|
||||
let uvs: Array<number> = map.uvs;
|
||||
this.readVertices(map, mesh, uvs.length);
|
||||
mesh.triangles = map.triangles;
|
||||
mesh.regionUVs = uvs;
|
||||
mesh.updateUVs();
|
||||
|
||||
mesh.hullLength = this.getValue(map, "hull", 0) * 2;
|
||||
return mesh;
|
||||
}
|
||||
case "path": {
|
||||
let path = this.attachmentLoader.newPathAttachment(skin, name);
|
||||
if (path == null) return null;
|
||||
path.closed = this.getValue(map, "closed", false);
|
||||
path.constantSpeed = this.getValue(map, "constantSpeed", true);
|
||||
|
||||
let vertexCount = map.vertexCount;
|
||||
this.readVertices(map, path, vertexCount << 1);
|
||||
|
||||
let lengths: Array<number> = Utils.newArray(vertexCount / 3, 0);
|
||||
for (let i = 0; i < map.lengths.length; i++)
|
||||
lengths[i++] = map.lengths[i] * scale;
|
||||
path.lengths = lengths;
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
readVertices (map: any, attachment: VertexAttachment, verticesLength: number) {
|
||||
let scale = this.scale;
|
||||
attachment.worldVerticesLength = verticesLength;
|
||||
let vertices: Array<number> = map.vertices;
|
||||
if (verticesLength == vertices.length) {
|
||||
if (scale != 1) {
|
||||
for (let i = 0, n = vertices.length; i < n; i++)
|
||||
vertices[i] *= scale;
|
||||
}
|
||||
attachment.vertices = Utils.toFloatArray(vertices);
|
||||
return;
|
||||
}
|
||||
let weights = new Array<number>();
|
||||
let bones = new Array<number>();
|
||||
for (let i = 0, n = vertices.length; i < n;) {
|
||||
let boneCount = vertices[i++];
|
||||
bones.push(boneCount);
|
||||
for (let nn = i + boneCount * 4; i < nn; i += 4) {
|
||||
bones.push(vertices[i]);
|
||||
weights.push(vertices[i + 1] * scale);
|
||||
weights.push(vertices[i + 2] * scale);
|
||||
weights.push(vertices[i + 3]);
|
||||
}
|
||||
}
|
||||
attachment.bones = bones;
|
||||
attachment.vertices = Utils.toFloatArray(weights);
|
||||
}
|
||||
|
||||
readAnimation (map: any, name: string, skeletonData: SkeletonData) {
|
||||
let scale = this.scale;
|
||||
let timelines = new Array<Timeline>();
|
||||
let duration = 0;
|
||||
|
||||
// Slot timelines.
|
||||
if (map.slots) {
|
||||
for (let slotName in map.slots) {
|
||||
let slotMap = map.slots[slotName];
|
||||
let slotIndex = skeletonData.findSlotIndex(slotName);
|
||||
if (slotIndex == -1) throw new Error("Slot not found: " + slotName);
|
||||
for (let timelineName in slotMap) {
|
||||
let timelineMap = slotMap[timelineName];
|
||||
if (timelineName == "color") {
|
||||
let timeline = new ColorTimeline(timelineMap.length);
|
||||
timeline.slotIndex = slotIndex;
|
||||
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < timelineMap.length; i++) {
|
||||
let valueMap = timelineMap[i];
|
||||
let color = new Color();
|
||||
color.setFromString(valueMap.color);
|
||||
timeline.setFrame(frameIndex, valueMap.time, color.r, color.g, color.b, color.a);
|
||||
this.readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * ColorTimeline.ENTRIES]);
|
||||
|
||||
} else if (timelineName = "attachment") {
|
||||
let timeline = new AttachmentTimeline(timelineMap.length);
|
||||
timeline.slotIndex = slotIndex;
|
||||
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < timelineMap.length; i++) {
|
||||
let valueMap = timelineMap[i];
|
||||
timeline.setFrame(frameIndex++, valueMap.time, valueMap.name);
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
|
||||
} else
|
||||
throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bone timelines.
|
||||
if (map.bones) {
|
||||
for (let boneName in map.bones) {
|
||||
let boneMap = map.bones[boneName];
|
||||
let boneIndex = skeletonData.findBoneIndex(boneName);
|
||||
if (boneIndex == -1) throw new Error("Bone not found: " + boneName);
|
||||
for (let timelineName in boneMap) {
|
||||
let timelineMap = boneMap[timelineName];
|
||||
if (timelineName === "rotate") {
|
||||
let timeline = new RotateTimeline(timelineMap.length);
|
||||
timeline.boneIndex = boneIndex;
|
||||
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < timelineMap.length; i++) {
|
||||
let valueMap = timelineMap[i];
|
||||
timeline.setFrame(frameIndex, valueMap.time, valueMap.angle);
|
||||
this.readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * RotateTimeline.ENTRIES]);
|
||||
|
||||
} else if (timelineName === "translate" || timelineName === "scale" || timelineName === "shear") {
|
||||
let timeline: TranslateTimeline = null;
|
||||
let timelineScale = 1;
|
||||
if (timelineName === "scale")
|
||||
timeline = new ScaleTimeline(timelineMap.length);
|
||||
else if (timelineName === "shear")
|
||||
timeline = new ShearTimeline(timelineMap.length);
|
||||
else {
|
||||
timeline = new TranslateTimeline(timelineMap.length);
|
||||
timelineScale = scale;
|
||||
}
|
||||
timeline.boneIndex = boneIndex;
|
||||
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < timelineMap.length; i++) {
|
||||
let valueMap = timelineMap[i];
|
||||
let x = this.getValue(valueMap, "x", 0), y = this.getValue(valueMap, "y", 0);
|
||||
timeline.setFrame(frameIndex, valueMap.time, x * timelineScale, y * timelineScale);
|
||||
this.readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TranslateTimeline.ENTRIES]);
|
||||
|
||||
} else
|
||||
throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IK constraint timelines.
|
||||
if (map.ik) {
|
||||
for (let constraintName in map.ik) {
|
||||
let constraintMap = map.ik[constraintName];
|
||||
let constraint = skeletonData.findIkConstraint(constraintName);
|
||||
let timeline = new IkConstraintTimeline(constraintMap.length);
|
||||
timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(constraint);
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < constraintMap.length; i++) {
|
||||
let valueMap = constraintMap[i];
|
||||
timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "mix", 1),
|
||||
this.getValue(valueMap, "bendPositive", true) ? 1 : -1);
|
||||
this.readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * IkConstraintTimeline.ENTRIES]);
|
||||
}
|
||||
}
|
||||
|
||||
// Transform constraint timelines.
|
||||
if (map.transform) {
|
||||
for (let constraintName in map.transform) {
|
||||
let constraintMap = map.transform[constraintName];
|
||||
let constraint = skeletonData.findTransformConstraint(constraintName);
|
||||
let timeline = new TransformConstraintTimeline(constraintMap.length);
|
||||
timeline.transformConstraintIndex = skeletonData.transformConstraints.indexOf(constraint);
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < constraintMap.length; i++) {
|
||||
let valueMap = constraintMap[i];
|
||||
timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "rotateMix", 1),
|
||||
this.getValue(valueMap, "translateMix", 1), this.getValue(valueMap, "scaleMix", 1), this.getValue(valueMap, "shearMix", 1));
|
||||
this.readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration,
|
||||
timeline.frames[(timeline.getFrameCount() - 1) * TransformConstraintTimeline.ENTRIES]);
|
||||
}
|
||||
}
|
||||
|
||||
// Path constraint timelines.
|
||||
if (map.paths) {
|
||||
for (let constraintName in map.paths) {
|
||||
let constraintMap = map.paths[constraintName];
|
||||
let index = skeletonData.findPathConstraintIndex(constraintName);
|
||||
if (index == -1) throw new Error("Path constraint not found: " + constraintName);
|
||||
let data = skeletonData.pathConstraints[index];
|
||||
for (let timelineName in constraintMap) {
|
||||
let timelineMap = constraintMap[timelineName];
|
||||
if (timelineName === "position" || timelineName === "spacing") {
|
||||
let timeline: PathConstraintPositionTimeline = null;
|
||||
let timelineScale = 1;
|
||||
if (timelineName === "spacing") {
|
||||
timeline = new PathConstraintSpacingTimeline(timelineMap.length);
|
||||
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale;
|
||||
} else {
|
||||
timeline = new PathConstraintPositionTimeline(timelineMap.length);
|
||||
if (data.positionMode == PositionMode.Fixed) timelineScale = scale;
|
||||
}
|
||||
timeline.pathConstraintIndex = index;
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < timelineMap.length; i++) {
|
||||
let valueMap = timelineMap[i];
|
||||
timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, timelineName, 0) * timelineScale);
|
||||
this.readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration,
|
||||
timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintPositionTimeline.ENTRIES]);
|
||||
} else if (timelineName === "mix") {
|
||||
let timeline = new PathConstraintMixTimeline(timelineMap.length);
|
||||
timeline.pathConstraintIndex = index;
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < timelineMap.length; i++) {
|
||||
let valueMap = timelineMap[i];
|
||||
timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "rotateMix", 1),
|
||||
this.getValue(valueMap, "translateMix", 1));
|
||||
this.readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration,
|
||||
timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintMixTimeline.ENTRIES]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deform timelines.
|
||||
if (map.deform) {
|
||||
for (let deformName in map.deform) {
|
||||
let deformMap = map.deform[deformName];
|
||||
let skin = skeletonData.findSkin(deformName);
|
||||
if (skin == null) throw new Error("Skin not found: " + deformName);
|
||||
for (let slotName in deformMap) {
|
||||
let slotMap = deformMap[slotName];
|
||||
let slotIndex = skeletonData.findSlotIndex(slotName);
|
||||
if (slotIndex == -1) throw new Error("Slot not found: " + slotMap.name);
|
||||
for (let timelineName in slotMap) {
|
||||
let timelineMap = slotMap[timelineName];
|
||||
let attachment = <VertexAttachment>skin.getAttachment(slotIndex, timelineName);
|
||||
if (attachment == null) throw new Error("Deform attachment not found: " + timelineMap.name);
|
||||
let weighted = attachment.bones != null;
|
||||
let vertices = attachment.vertices;
|
||||
let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
|
||||
|
||||
let timeline = new DeformTimeline(timelineMap.length);
|
||||
timeline.slotIndex = slotIndex;
|
||||
timeline.attachment = attachment;
|
||||
|
||||
let frameIndex = 0;
|
||||
for (let j = 0; j < timelineMap.length; j++) {
|
||||
let valueMap = timelineMap[j];
|
||||
let deform: ArrayLike<number>;
|
||||
let verticesValue: Array<Number> = this.getValue(valueMap, "vertices", null);
|
||||
if (verticesValue == null)
|
||||
deform = weighted ? Utils.newFloatArray(deformLength) : vertices;
|
||||
else {
|
||||
deform = Utils.newFloatArray(deformLength);
|
||||
let start = <number>this.getValue(valueMap, "offset", 0);
|
||||
Utils.arrayCopy(verticesValue, 0, deform, start, verticesValue.length);
|
||||
if (scale != 1) {
|
||||
for (let i = start, n = i + verticesValue.length; i < n; i++)
|
||||
deform[i] *= scale;
|
||||
}
|
||||
if (!weighted) {
|
||||
for (let i = 0; i < deformLength; i++)
|
||||
deform[i] += vertices[i];
|
||||
}
|
||||
}
|
||||
|
||||
timeline.setFrame(frameIndex, valueMap.time, deform);
|
||||
this.readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw order timeline.
|
||||
let drawOrderNode = map.drawOrder;
|
||||
if (drawOrderNode == null) drawOrderNode = map.draworder;
|
||||
if (drawOrderNode != null) {
|
||||
let timeline = new DrawOrderTimeline(drawOrderNode.length);
|
||||
let slotCount = skeletonData.slots.length;
|
||||
let frameIndex = 0;
|
||||
for (let j = 0; j < drawOrderNode.length; j++) {
|
||||
let drawOrderMap = drawOrderNode[j];
|
||||
let drawOrder: Array<number> = null;
|
||||
let offsets = this.getValue(drawOrderMap, "offsets", null);
|
||||
if (offsets != null) {
|
||||
drawOrder = Utils.newArray<number>(slotCount, -1);
|
||||
let unchanged = Utils.newArray<number>(slotCount - offsets.length, 0);
|
||||
let originalIndex = 0, unchangedIndex = 0;
|
||||
for (let i = 0; i < offsets.length; i++) {
|
||||
let offsetMap = offsets[i];
|
||||
let slotIndex = skeletonData.findSlotIndex(offsetMap.slot);
|
||||
if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap.slot);
|
||||
// Collect unchanged items.
|
||||
while (originalIndex != slotIndex)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Set changed items.
|
||||
drawOrder[originalIndex + offsetMap.offset] = originalIndex++;
|
||||
}
|
||||
// Collect remaining unchanged items.
|
||||
while (originalIndex < slotCount)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Fill in unchanged items.
|
||||
for (let i = slotCount - 1; i >= 0; i--)
|
||||
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
|
||||
}
|
||||
timeline.setFrame(frameIndex++, drawOrderMap.time, drawOrder);
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
|
||||
}
|
||||
|
||||
// Event timeline.
|
||||
if (map.events) {
|
||||
let timeline = new EventTimeline(map.events.length);
|
||||
let frameIndex = 0;
|
||||
for (let i = 0; i < map.events.length; i++) {
|
||||
let eventMap = map.events[i];
|
||||
let eventData = skeletonData.findEvent(eventMap.name);
|
||||
if (eventData == null) throw new Error("Event not found: " + eventMap.name);
|
||||
let event = new Event(eventMap.time, eventData);
|
||||
event.intValue = this.getValue(eventMap, "int", eventData.intValue);
|
||||
event.floatValue = this.getValue(eventMap, "float", eventData.floatValue);
|
||||
event.stringValue = this.getValue(eventMap, "string", eventData.stringValue);
|
||||
timeline.setFrame(frameIndex++, event);
|
||||
}
|
||||
timelines.push(timeline);
|
||||
duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
|
||||
}
|
||||
|
||||
if (isNaN(duration)) {
|
||||
throw new Error("Error while parsing animation, duration is NaN");
|
||||
}
|
||||
|
||||
skeletonData.animations.push(new Animation(name, timelines, duration));
|
||||
}
|
||||
|
||||
readCurve (map: any, timeline: CurveTimeline, frameIndex: number) {
|
||||
if (!map.curve) return;
|
||||
if (map.curve === "stepped")
|
||||
timeline.setStepped(frameIndex);
|
||||
else if (Object.prototype.toString.call(map.curve) === '[object Array]') {
|
||||
let curve: Array<number> = map.curve;
|
||||
timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]);
|
||||
}
|
||||
}
|
||||
|
||||
getValue (map: any, prop: string, defaultValue: any) {
|
||||
return map[prop] !== undefined ? map[prop] : defaultValue;
|
||||
}
|
||||
|
||||
static blendModeFromString (str: string) {
|
||||
str = str.toLowerCase();
|
||||
if (str == "normal") return BlendMode.Normal;
|
||||
if (str == "additive") return BlendMode.Additive;
|
||||
if (str == "multiply") return BlendMode.Multiply;
|
||||
if (str == "screen") return BlendMode.Screen;
|
||||
throw new Error(`Unknown blend mode: ${str}`);
|
||||
}
|
||||
|
||||
static positionModeFromString (str: string) {
|
||||
str = str.toLowerCase();
|
||||
if (str == "fixed") return PositionMode.Fixed;
|
||||
if (str == "percent") return PositionMode.Percent;
|
||||
throw new Error(`Unknown position mode: ${str}`);
|
||||
}
|
||||
|
||||
static spacingModeFromString (str: string) {
|
||||
str = str.toLowerCase();
|
||||
if (str == "length") return SpacingMode.Length;
|
||||
if (str == "fixed") return SpacingMode.Fixed;
|
||||
if (str == "percent") return SpacingMode.Percent;
|
||||
throw new Error(`Unknown position mode: ${str}`);
|
||||
}
|
||||
|
||||
static rotateModeFromString (str: string) {
|
||||
str = str.toLowerCase();
|
||||
if (str == "tangent") return RotateMode.Tangent;
|
||||
if (str == "chain") return RotateMode.Chain;
|
||||
if (str == "chainscale") return RotateMode.ChainScale;
|
||||
throw new Error(`Unknown rotate mode: ${str}`);
|
||||
}
|
||||
}
|
||||
|
||||
class LinkedMesh {
|
||||
parent: string; skin: string;
|
||||
slotIndex: number;
|
||||
mesh: MeshAttachment;
|
||||
|
||||
constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string) {
|
||||
this.mesh = mesh;
|
||||
this.skin = skin;
|
||||
this.slotIndex = slotIndex;
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
spine-ts/core/src/Skin.ts
Normal file
77
spine-ts/core/src/Skin.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class Skin {
|
||||
name: string;
|
||||
attachments = new Array<Map<Attachment>>();
|
||||
|
||||
constructor (name: string) {
|
||||
if (name == null) throw new Error("name cannot be null.");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
addAttachment (slotIndex: number, name: string, attachment: Attachment) {
|
||||
if (attachment == null) throw new Error("attachment cannot be null.");
|
||||
let attachments = this.attachments;
|
||||
if (slotIndex >= attachments.length) attachments.length = slotIndex + 1;
|
||||
if (!attachments[slotIndex]) attachments[slotIndex] = { };
|
||||
attachments[slotIndex][name] = attachment;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
getAttachment (slotIndex: number, name: string): Attachment {
|
||||
let dictionary = this.attachments[slotIndex];
|
||||
return dictionary ? dictionary[name] : null;
|
||||
}
|
||||
|
||||
/** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */
|
||||
attachAll (skeleton: Skeleton, oldSkin: Skin) {
|
||||
let slotIndex = 0;
|
||||
for (let i = 0; i < skeleton.slots.length; i++) {
|
||||
let slot = skeleton.slots[i];
|
||||
let slotAttachment = slot.getAttachment();
|
||||
if (slotAttachment && slotIndex < oldSkin.attachments.length) {
|
||||
let dictionary = oldSkin.attachments[slotIndex];
|
||||
for (let key in dictionary) {
|
||||
let skinAttachment:Attachment = dictionary[key];
|
||||
if (slotAttachment == skinAttachment) {
|
||||
let attachment = this.getAttachment(slotIndex, name);
|
||||
if (attachment != null) slot.setAttachment(attachment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
slotIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
83
spine-ts/core/src/Slot.ts
Normal file
83
spine-ts/core/src/Slot.ts
Normal file
@ -0,0 +1,83 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class Slot {
|
||||
data: SlotData;
|
||||
bone: Bone;
|
||||
color: Color;
|
||||
private attachment: Attachment;
|
||||
private attachmentTime: number;
|
||||
attachmentVertices = new Array<number>();
|
||||
|
||||
constructor (data: SlotData, bone: Bone) {
|
||||
if (data == null) throw new Error("data cannot be null.");
|
||||
if (bone == null) throw new Error("bone cannot be null.");
|
||||
this.data = data;
|
||||
this.bone = bone;
|
||||
this.color = new Color();
|
||||
this.setToSetupPose();
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
getAttachment (): Attachment {
|
||||
return this.attachment;
|
||||
}
|
||||
|
||||
/** Sets the attachment and if it changed, resets {@link #getAttachmentTime()} and clears {@link #getAttachmentVertices()}.
|
||||
* @param attachment May be null. */
|
||||
setAttachment (attachment: Attachment) {
|
||||
if (this.attachment == attachment) return;
|
||||
this.attachment = attachment;
|
||||
this.attachmentTime = this.bone.skeleton.time;
|
||||
this.attachmentVertices.length = 0;
|
||||
}
|
||||
|
||||
setAttachmentTime (time: number) {
|
||||
this.attachmentTime = this.bone.skeleton.time - time;
|
||||
}
|
||||
|
||||
/** Returns the time since the attachment was set. */
|
||||
getAttachmentTime (): number {
|
||||
return this.bone.skeleton.time - this.attachmentTime;
|
||||
}
|
||||
|
||||
setToSetupPose () {
|
||||
this.color.setFromColor(this.data.color);
|
||||
if (this.data.attachmentName == null)
|
||||
this.attachment = null;
|
||||
else {
|
||||
this.attachment = null;
|
||||
this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
spine-ts/core/src/SlotData.ts
Normal file
50
spine-ts/core/src/SlotData.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class SlotData {
|
||||
index: number;
|
||||
name: string;
|
||||
boneData: BoneData;
|
||||
color = new Color(1, 1, 1, 1);
|
||||
attachmentName: string;
|
||||
blendMode: BlendMode;
|
||||
|
||||
constructor (index: number, name: string, boneData: BoneData) {
|
||||
if (index < 0) throw new Error("index must be >= 0.");
|
||||
if (name == null) throw new Error("name cannot be null.");
|
||||
if (boneData == null) throw new Error("boneData cannot be null.");
|
||||
this.index = index;
|
||||
this.name = name;
|
||||
this.boneData = boneData;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
spine-ts/core/src/TransformConstraint.ts
Normal file
117
spine-ts/core/src/TransformConstraint.ts
Normal file
@ -0,0 +1,117 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class TransformConstraint implements Updatable {
|
||||
data: TransformConstraintData;
|
||||
bones: Array<Bone>;
|
||||
target: Bone;
|
||||
rotateMix = 0; translateMix = 0; scaleMix = 0; shearMix = 0;
|
||||
temp = new Vector2();
|
||||
|
||||
constructor (data: TransformConstraintData, skeleton: Skeleton) {
|
||||
if (data == null) throw new Error("data cannot be null.");
|
||||
if (skeleton == null) throw new Error("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.rotateMix = data.rotateMix;
|
||||
this.translateMix = data.translateMix;
|
||||
this.scaleMix = data.scaleMix;
|
||||
this.shearMix = data.shearMix;
|
||||
this.bones = new Array<Bone>();
|
||||
for (let i = 0; i < data.bones.length; i++)
|
||||
this.bones.push(skeleton.findBone(data.bones[i].name));
|
||||
this.target = skeleton.findBone(data.target.name);
|
||||
}
|
||||
|
||||
apply () {
|
||||
this.update();
|
||||
}
|
||||
|
||||
update () {
|
||||
let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
|
||||
let target = this.target;
|
||||
let ta = target.a, tb = target.b, tc = target.c, td = target.d;
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
|
||||
if (rotateMix > 0) {
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
let r = Math.atan2(tc, ta) - Math.atan2(c, a) + this.data.offsetRotation * MathUtils.degRad;
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI)
|
||||
r += MathUtils.PI2;
|
||||
r *= rotateMix;
|
||||
let cos = Math.cos(r), sin = Math.sin(r);
|
||||
bone.a = cos * a - sin * c;
|
||||
bone.b = cos * b - sin * d;
|
||||
bone.c = sin * a + cos * c;
|
||||
bone.d = sin * b + cos * d;
|
||||
}
|
||||
|
||||
if (translateMix > 0) {
|
||||
let temp = this.temp;
|
||||
target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
|
||||
bone.worldX += (temp.x - bone.worldX) * translateMix;
|
||||
bone.worldY += (temp.y - bone.worldY) * translateMix;
|
||||
}
|
||||
|
||||
if (scaleMix > 0) {
|
||||
let bs = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
|
||||
let ts = Math.sqrt(ta * ta + tc * tc);
|
||||
let s = bs > 0.00001 ? (bs + (ts - bs + this.data.offsetScaleX) * scaleMix) / bs : 0;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
bs = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
|
||||
ts = Math.sqrt(tb * tb + td * td);
|
||||
s = bs > 0.00001 ? (bs + (ts - bs + this.data.offsetScaleY) * scaleMix) / bs : 0;
|
||||
bone.b *= s;
|
||||
bone.d *= s;
|
||||
}
|
||||
|
||||
if (shearMix > 0) {
|
||||
let b = bone.b, d = bone.d;
|
||||
let by = Math.atan2(d, b);
|
||||
let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(bone.c, bone.a));
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI)
|
||||
r += MathUtils.PI2;
|
||||
r = by + (r + this.data.offsetShearY * MathUtils.degRad) * shearMix;
|
||||
let s = Math.sqrt(b * b + d * d);
|
||||
bone.b = Math.cos(r) * s;
|
||||
bone.d = Math.sin(r) * s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
spine-ts/core/src/TransformConstraintData.ts
Normal file
45
spine-ts/core/src/TransformConstraintData.ts
Normal file
@ -0,0 +1,45 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class TransformConstraintData {
|
||||
name: string;
|
||||
bones = new Array<BoneData>();
|
||||
target: BoneData;
|
||||
rotateMix = 0; translateMix = 0; scaleMix = 0; shearMix = 0;
|
||||
offsetRotation = 0; offsetX = 0; offsetY = 0; offsetScaleX = 0; offsetScaleY = 0; offsetShearY = 0;
|
||||
|
||||
constructor (name: string) {
|
||||
if (name == null) throw new Error("name cannot be null.");
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
spine-ts/core/src/Updatable.ts
Normal file
36
spine-ts/core/src/Updatable.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export interface Updatable {
|
||||
update(): void;
|
||||
}
|
||||
}
|
||||
198
spine-ts/core/src/Utils.ts
Normal file
198
spine-ts/core/src/Utils.ts
Normal file
@ -0,0 +1,198 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export interface Map<T> {
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
export interface Disposable {
|
||||
dispose (): void;
|
||||
}
|
||||
|
||||
export class Color {
|
||||
constructor (public r: number = 0, public g: number = 0, public b: number = 0, public a: number = 0) {
|
||||
}
|
||||
|
||||
set (r: number, g: number, b: number, a: number) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
this.clamp();
|
||||
}
|
||||
|
||||
setFromColor (c: Color) {
|
||||
this.r = c.r;
|
||||
this.g = c.g;
|
||||
this.b = c.b;
|
||||
this.a = c.a;
|
||||
}
|
||||
|
||||
setFromString (hex: string) {
|
||||
hex = hex.charAt(0) == '#' ? hex.substr(1) : hex;
|
||||
this.r = parseInt(hex.substr(0, 2), 16) / 255.0;
|
||||
this.g = parseInt(hex.substr(2, 2), 16) / 255.0;
|
||||
this.b = parseInt(hex.substr(4, 2), 16) / 255.0;
|
||||
this.a = (hex.length != 8 ? 255 : parseInt(hex.substr(6, 2), 16)) / 255.0;
|
||||
}
|
||||
|
||||
add (r: number, g: number, b: number, a: number) {
|
||||
this.r += r;
|
||||
this.g += g;
|
||||
this.b += b;
|
||||
this.a += a;
|
||||
this.clamp();
|
||||
}
|
||||
|
||||
clamp () {
|
||||
if (this.r < 0) this.r = 0;
|
||||
else if (this.r > 1) this.r = 1;
|
||||
|
||||
if (this.g < 0) this.g = 0;
|
||||
else if (this.g > 1) this.g = 1;
|
||||
|
||||
if (this.b < 0) this.b = 0;
|
||||
else if (this.b > 1) this.b = 1;
|
||||
|
||||
if (this.a < 0) this.a = 0;
|
||||
else if (this.a > 1) this.a = 1;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class MathUtils {
|
||||
static PI = 3.1415927;
|
||||
static PI2 = MathUtils.PI * 2;
|
||||
static radiansToDegrees = 180 / MathUtils.PI;
|
||||
static radDeg = MathUtils.radiansToDegrees;
|
||||
static degreesToRadians = MathUtils.PI / 180;
|
||||
static degRad = MathUtils.degreesToRadians;
|
||||
|
||||
static clamp (value: number, min: number, max: number) {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
static cosDeg (degrees: number) {
|
||||
return Math.cos(degrees * MathUtils.degRad);
|
||||
}
|
||||
|
||||
static sinDeg (degrees: number) {
|
||||
return Math.sin(degrees * MathUtils.degRad);
|
||||
}
|
||||
|
||||
static signum (value: number): number {
|
||||
return value >= 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
static toInt (x: number) {
|
||||
return x > 0 ? Math.floor(x) : Math.ceil(x);
|
||||
}
|
||||
}
|
||||
|
||||
export class Utils {
|
||||
static SUPPORTS_TYPED_ARRAYS = typeof(Float32Array) !== "undefined";
|
||||
|
||||
static arrayCopy<T> (source: ArrayLike<T>, sourceStart: number, dest: ArrayLike<T>, destStart: number, numElements: number) {
|
||||
for (let i = sourceStart, j = destStart; i < sourceStart + numElements; i++, j++) {
|
||||
dest[j] = source[i];
|
||||
}
|
||||
}
|
||||
|
||||
static setArraySize<T> (array: Array<T>, size: number, value: any = 0): Array<T> {
|
||||
let oldSize = array.length;
|
||||
if (oldSize == size) return array;
|
||||
array.length = size;
|
||||
if (oldSize < size) {
|
||||
for (let i = oldSize; i < size; i++) array[i] = value;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
static newArray<T> (size: number, defaultValue: T): Array<T> {
|
||||
let array = new Array<T>(size);
|
||||
for (let i = 0; i < size; i++) array[i] = defaultValue;
|
||||
return array;
|
||||
}
|
||||
|
||||
static newFloatArray (size: number): ArrayLike<number> {
|
||||
if (Utils.SUPPORTS_TYPED_ARRAYS) {
|
||||
return new Float32Array(size)
|
||||
} else {
|
||||
let array = new Array<number>(size);
|
||||
for (let i = 0; i < array.length; i++) array[i] = 0;
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
static toFloatArray (array: Array<number>) {
|
||||
return Utils.SUPPORTS_TYPED_ARRAYS ? new Float32Array(array) : array;
|
||||
}
|
||||
}
|
||||
|
||||
export class Pool<T> {
|
||||
private _items = new Array<T>(16);
|
||||
private _instantiator: () => T;
|
||||
|
||||
constructor (instantiator: () => T) {
|
||||
this._instantiator = instantiator;
|
||||
}
|
||||
|
||||
obtain () {
|
||||
return this._items.length > 0 ? this._items.pop() : this._instantiator();
|
||||
}
|
||||
|
||||
free (item: T) {
|
||||
this._items.push(item);
|
||||
}
|
||||
|
||||
freeAll (items: ArrayLike<T>) {
|
||||
for (let i = 0; i < items.length; i++) this._items[i] = items[i];
|
||||
}
|
||||
|
||||
clear () {
|
||||
this._items.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class Vector2 {
|
||||
constructor (public x = 0, public y = 0) {
|
||||
}
|
||||
|
||||
set (x: number, y: number): Vector2 {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
124
spine-ts/core/src/attachments/Attachment.ts
Normal file
124
spine-ts/core/src/attachments/Attachment.ts
Normal file
@ -0,0 +1,124 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export abstract class Attachment {
|
||||
name: string;
|
||||
|
||||
constructor (name: string) {
|
||||
if (name == null) throw new Error("name cannot be null.");
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class VertexAttachment extends Attachment {
|
||||
bones: Array<number>;
|
||||
vertices: ArrayLike<number>;
|
||||
worldVerticesLength = 0;
|
||||
|
||||
constructor (name: string) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
computeWorldVertices (slot: Slot, worldVertices: ArrayLike<number>) {
|
||||
this.computeWorldVerticesWith(slot, 0, this.worldVerticesLength, worldVertices, 0);
|
||||
}
|
||||
|
||||
/** Transforms local vertices to world coordinates.
|
||||
* @param start The index of the first local vertex value to transform. Each vertex has 2 values, x and y.
|
||||
* @param count The number of world vertex values to output. Must be <= {@link #getWorldVerticesLength()} - start.
|
||||
* @param worldVertices The output world vertices. Must have a length >= offset + count.
|
||||
* @param offset The worldVertices index to begin writing values. */
|
||||
computeWorldVerticesWith (slot: Slot, start: number, count: number, worldVertices: ArrayLike<number>, offset: number) {
|
||||
count += offset;
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let x = skeleton.x, y = skeleton.y;
|
||||
let deformArray = slot.attachmentVertices;
|
||||
let vertices = this.vertices;
|
||||
let bones = this.bones;
|
||||
if (bones == null) {
|
||||
if (deformArray.length > 0) vertices = deformArray;
|
||||
let bone = slot.bone;
|
||||
x += bone.worldX;
|
||||
y += bone.worldY;
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
for (let v = start, w = offset; w < count; v += 2, w += 2) {
|
||||
let vx = vertices[v], vy = vertices[v + 1];
|
||||
worldVertices[w] = vx * a + vy * b + x;
|
||||
worldVertices[w + 1] = vx * c + vy * d + y;
|
||||
}
|
||||
return;
|
||||
}
|
||||
let v = 0, skip = 0;
|
||||
for (let i = 0; i < start; i += 2) {
|
||||
let n = bones[v];
|
||||
v += n + 1;
|
||||
skip += n;
|
||||
}
|
||||
let skeletonBones = skeleton.bones;
|
||||
if (deformArray.length == 0) {
|
||||
for (let w = offset, b = skip * 3; w < count; w += 2) {
|
||||
let wx = x, wy = y;
|
||||
let n = bones[v++];
|
||||
n += v;
|
||||
for (; v < n; v++, b += 3) {
|
||||
let bone = skeletonBones[bones[v]];
|
||||
let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
|
||||
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
||||
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
||||
}
|
||||
worldVertices[w] = wx;
|
||||
worldVertices[w + 1] = wy;
|
||||
}
|
||||
} else {
|
||||
let deform = deformArray;
|
||||
for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) {
|
||||
let wx = x, wy = y;
|
||||
let n = bones[v++];
|
||||
n += v;
|
||||
for (; v < n; v++, b += 3, f += 2) {
|
||||
let bone = skeletonBones[bones[v]];
|
||||
let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
|
||||
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
||||
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
||||
}
|
||||
worldVertices[w] = wx;
|
||||
worldVertices[w + 1] = wy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if a deform originally applied to the specified attachment should be applied to this attachment. */
|
||||
applyDeform (sourceAttachment: VertexAttachment) {
|
||||
return this == sourceAttachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
spine-ts/core/src/attachments/AttachmentLoader.ts
Normal file
46
spine-ts/core/src/attachments/AttachmentLoader.ts
Normal file
@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export interface AttachmentLoader {
|
||||
/** @return May be null to not load an attachment. */
|
||||
newRegionAttachment (skin: Skin, name: string, path: string): RegionAttachment;
|
||||
|
||||
/** @return May be null to not load an attachment. */
|
||||
newMeshAttachment (skin: Skin, name: string, path: string) : MeshAttachment;
|
||||
|
||||
/** @return May be null to not load an attachment. */
|
||||
newBoundingBoxAttachment (skin: Skin, name: string) : BoundingBoxAttachment;
|
||||
|
||||
/** @return May be null to not load an attachment */
|
||||
newPathAttachment(skin: Skin, name: string): PathAttachment;
|
||||
}
|
||||
}
|
||||
36
spine-ts/core/src/attachments/AttachmentType.ts
Normal file
36
spine-ts/core/src/attachments/AttachmentType.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export enum AttachmentType {
|
||||
Region, BoundingBox, Mesh, LinkedMesh, Path
|
||||
}
|
||||
}
|
||||
38
spine-ts/core/src/attachments/BoundingBoxAttachment.ts
Normal file
38
spine-ts/core/src/attachments/BoundingBoxAttachment.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class BoundingBoxAttachment extends VertexAttachment {
|
||||
constructor (name: string) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
173
spine-ts/core/src/attachments/MeshAttachment.ts
Normal file
173
spine-ts/core/src/attachments/MeshAttachment.ts
Normal file
@ -0,0 +1,173 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class MeshAttachment extends VertexAttachment {
|
||||
region: TextureRegion;
|
||||
path: string;
|
||||
regionUVs: ArrayLike<number>; worldVertices: ArrayLike<number>;
|
||||
triangles: Array<number>;
|
||||
color = new Color(1, 1, 1, 1);
|
||||
hullLength: number;
|
||||
private _parentMesh: MeshAttachment;
|
||||
inheritDeform = false;
|
||||
tempColor = new Color(0, 0, 0, 0);
|
||||
|
||||
constructor (name: string) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
updateUVs () {
|
||||
let regionUVs = this.regionUVs;
|
||||
let verticesLength = regionUVs.length;
|
||||
let worldVerticesLength = (verticesLength >> 1) * 8;
|
||||
if (this.worldVertices == null || this.worldVertices.length != worldVerticesLength)
|
||||
this.worldVertices = Utils.newFloatArray(worldVerticesLength);
|
||||
|
||||
let u = 0, v = 0, width = 0, height = 0;
|
||||
if (this.region == null) {
|
||||
u = v = 0;
|
||||
width = height = 1;
|
||||
} else {
|
||||
u = this.region.u;
|
||||
v = this.region.v;
|
||||
width = this.region.u2 - u;
|
||||
height = this.region.v2 - v;
|
||||
}
|
||||
if (this.region.rotate) {
|
||||
for (let i = 0, w = 6; i < verticesLength; i += 2, w += 8) {
|
||||
this.worldVertices[w] = u + regionUVs[i + 1] * width;
|
||||
this.worldVertices[w + 1] = v + height - regionUVs[i] * height;
|
||||
}
|
||||
} else {
|
||||
for (let i = 0, w = 6; i < verticesLength; i += 2, w += 8) {
|
||||
this.worldVertices[w] = u + regionUVs[i] * width;
|
||||
this.worldVertices[w + 1] = v + regionUVs[i + 1] * height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return The updated world vertices. */
|
||||
updateWorldVertices (slot: Slot, premultipliedAlpha: boolean) {
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let skeletonColor = skeleton.color, slotColor = slot.color, meshColor = this.color;
|
||||
let alpha = skeletonColor.a * slotColor.a * meshColor.a;
|
||||
let multiplier = premultipliedAlpha ? alpha : 1;
|
||||
let color = this.tempColor;
|
||||
color.set(skeletonColor.r * slotColor.r * meshColor.r * multiplier,
|
||||
skeletonColor.g * slotColor.g * meshColor.g * multiplier,
|
||||
skeletonColor.b * slotColor.b * meshColor.b * multiplier,
|
||||
alpha);
|
||||
|
||||
let x = skeleton.x, y = skeleton.y;
|
||||
let deformArray = slot.attachmentVertices;
|
||||
let vertices = this.vertices, worldVertices = this.worldVertices;
|
||||
let bones = this.bones;
|
||||
if (bones == null) {
|
||||
let verticesLength = vertices.length;
|
||||
if (deformArray.length > 0) vertices = deformArray;
|
||||
let bone = slot.bone;
|
||||
x += bone.worldX;
|
||||
y += bone.worldY;
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
for (let v = 0, w = 0; v < verticesLength; v += 2, w += 8) {
|
||||
let vx = vertices[v], vy = vertices[v + 1];
|
||||
worldVertices[w] = vx * a + vy * b + x;
|
||||
worldVertices[w + 1] = vx * c + vy * d + y;
|
||||
worldVertices[w + 2] = color.r;
|
||||
worldVertices[w + 3] = color.g;
|
||||
worldVertices[w + 4] = color.b;
|
||||
worldVertices[w + 5] = color.a;
|
||||
}
|
||||
return worldVertices;
|
||||
}
|
||||
let skeletonBones = skeleton.bones;
|
||||
if (deformArray.length == 0) {
|
||||
for (let w = 0, v = 0, b = 0, n = bones.length; v < n; w += 8) {
|
||||
let wx = x, wy = y;
|
||||
let nn = bones[v++] + v;
|
||||
for (; v < nn; v++, b += 3) {
|
||||
let bone = skeletonBones[bones[v]];
|
||||
let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
|
||||
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
||||
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
||||
}
|
||||
worldVertices[w] = wx;
|
||||
worldVertices[w + 1] = wy;
|
||||
worldVertices[w + 2] = color.r;
|
||||
worldVertices[w + 3] = color.g;
|
||||
worldVertices[w + 4] = color.b;
|
||||
worldVertices[w + 5] = color.a;
|
||||
}
|
||||
} else {
|
||||
let deform = deformArray;
|
||||
for (let w = 0, v = 0, b = 0, f = 0, n = bones.length; v < n; w += 8) {
|
||||
let wx = x, wy = y;
|
||||
let nn = bones[v++] + v;
|
||||
for (; v < nn; v++, b += 3, f += 2) {
|
||||
let bone = skeletonBones[bones[v]];
|
||||
let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
|
||||
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
||||
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
||||
}
|
||||
worldVertices[w] = wx;
|
||||
worldVertices[w + 1] = wy;
|
||||
worldVertices[w + 2] = color.r;
|
||||
worldVertices[w + 3] = color.g;
|
||||
worldVertices[w + 4] = color.b;
|
||||
worldVertices[w + 5] = color.a;
|
||||
}
|
||||
}
|
||||
return worldVertices;
|
||||
}
|
||||
|
||||
applyDeform (sourceAttachment: VertexAttachment): boolean {
|
||||
return this == sourceAttachment || (this.inheritDeform && this._parentMesh == sourceAttachment);
|
||||
}
|
||||
|
||||
getParentMesh () {
|
||||
return this._parentMesh;
|
||||
}
|
||||
|
||||
/** @param parentMesh May be null. */
|
||||
setParentMesh (parentMesh: MeshAttachment) {
|
||||
this._parentMesh = parentMesh;
|
||||
if (parentMesh != null) {
|
||||
this.bones = parentMesh.bones;
|
||||
this.vertices = parentMesh.vertices;
|
||||
this.regionUVs = parentMesh.regionUVs;
|
||||
this.triangles = parentMesh.triangles;
|
||||
this.hullLength = parentMesh.hullLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
41
spine-ts/core/src/attachments/PathAttachment.ts
Normal file
41
spine-ts/core/src/attachments/PathAttachment.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class PathAttachment extends VertexAttachment {
|
||||
lengths: Array<number>;
|
||||
closed = false; constantSpeed = false;
|
||||
|
||||
constructor (name: string) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
206
spine-ts/core/src/attachments/RegionAttachment.ts
Normal file
206
spine-ts/core/src/attachments/RegionAttachment.ts
Normal file
@ -0,0 +1,206 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class RegionAttachment extends Attachment {
|
||||
static OX1 = 0;
|
||||
static OY1 = 1;
|
||||
static OX2 = 2;
|
||||
static OY2 = 3;
|
||||
static OX3 = 4;
|
||||
static OY3 = 5;
|
||||
static OX4 = 6;
|
||||
static OY4 = 7;
|
||||
|
||||
static X1 = 0;
|
||||
static Y1 = 1;
|
||||
static C1R = 2;
|
||||
static C1G = 3;
|
||||
static C1B = 4;
|
||||
static C1A = 5;
|
||||
static U1 = 6;
|
||||
static V1 = 7;
|
||||
|
||||
static X2 = 8;
|
||||
static Y2 = 9;
|
||||
static C2R = 10;
|
||||
static C2G = 11;
|
||||
static C2B = 12;
|
||||
static C2A = 13;
|
||||
static U2 = 14;
|
||||
static V2 = 15;
|
||||
|
||||
static X3 = 16;
|
||||
static Y3 = 17;
|
||||
static C3R = 18;
|
||||
static C3G = 19;
|
||||
static C3B = 20;
|
||||
static C3A = 21;
|
||||
static U3 = 22;
|
||||
static V3 = 23;
|
||||
|
||||
static X4 = 24;
|
||||
static Y4 = 25;
|
||||
static C4R = 26;
|
||||
static C4G = 27;
|
||||
static C4B = 28;
|
||||
static C4A = 29;
|
||||
static U4 = 30;
|
||||
static V4 = 31;
|
||||
|
||||
x = 0; y = 0; scaleX = 1; scaleY = 1; rotation = 0; width = 0; height = 0;
|
||||
color = new Color(1, 1, 1, 1);
|
||||
|
||||
path: string;
|
||||
rendererObject: any;
|
||||
region: TextureRegion;
|
||||
|
||||
offset = Utils.newFloatArray(8);
|
||||
vertices = Utils.newFloatArray(8 * 4);
|
||||
|
||||
tempColor = new Color(1, 1, 1, 1);
|
||||
|
||||
constructor (name:string) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
setRegion (region: TextureRegion) : void {
|
||||
let vertices = this.vertices;
|
||||
if (region.rotate) {
|
||||
vertices[RegionAttachment.U2] = region.u;
|
||||
vertices[RegionAttachment.V2] = region.v2;
|
||||
vertices[RegionAttachment.U3] = region.u;
|
||||
vertices[RegionAttachment.V3] = region.v;
|
||||
vertices[RegionAttachment.U4] = region.u2;
|
||||
vertices[RegionAttachment.V4] = region.v;
|
||||
vertices[RegionAttachment.U1] = region.u2;
|
||||
vertices[RegionAttachment.V1] = region.v2;
|
||||
} else {
|
||||
vertices[RegionAttachment.U1] = region.u;
|
||||
vertices[RegionAttachment.V1] = region.v2;
|
||||
vertices[RegionAttachment.U2] = region.u;
|
||||
vertices[RegionAttachment.V2] = region.v;
|
||||
vertices[RegionAttachment.U3] = region.u2;
|
||||
vertices[RegionAttachment.V3] = region.v;
|
||||
vertices[RegionAttachment.U4] = region.u2;
|
||||
vertices[RegionAttachment.V4] = region.v2;
|
||||
}
|
||||
}
|
||||
|
||||
updateOffset () : void {
|
||||
let regionScaleX = this.width / this.region.originalWidth * this.scaleX;
|
||||
let regionScaleY = this.height / this.region.originalHeight * this.scaleY;
|
||||
let localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX;
|
||||
let localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY;
|
||||
let localX2 = localX + this.region.width * regionScaleX;
|
||||
let localY2 = localY + this.region.height * regionScaleY;
|
||||
let radians = this.rotation * Math.PI / 180;
|
||||
let cos = Math.cos(radians);
|
||||
let sin = Math.sin(radians);
|
||||
let localXCos = localX * cos + this.x;
|
||||
let localXSin = localX * sin;
|
||||
let localYCos = localY * cos + this.y;
|
||||
let localYSin = localY * sin;
|
||||
let localX2Cos = localX2 * cos + this.x;
|
||||
let localX2Sin = localX2 * sin;
|
||||
let localY2Cos = localY2 * cos + this.y;
|
||||
let localY2Sin = localY2 * sin;
|
||||
let offset = this.offset;
|
||||
offset[RegionAttachment.OX1] = localXCos - localYSin;
|
||||
offset[RegionAttachment.OY1] = localYCos + localXSin;
|
||||
offset[RegionAttachment.OX2] = localXCos - localY2Sin;
|
||||
offset[RegionAttachment.OY2] = localY2Cos + localXSin;
|
||||
offset[RegionAttachment.OX3] = localX2Cos - localY2Sin;
|
||||
offset[RegionAttachment.OY3] = localY2Cos + localX2Sin;
|
||||
offset[RegionAttachment.OX4] = localX2Cos - localYSin;
|
||||
offset[RegionAttachment.OY4] = localYCos + localX2Sin;
|
||||
}
|
||||
|
||||
updateWorldVertices (slot: Slot, premultipliedAlpha: boolean) {
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let skeletonColor = skeleton.color;
|
||||
let slotColor = slot.color;
|
||||
let regionColor = this.color;
|
||||
let alpha = skeletonColor.a * slotColor.a * regionColor.a;
|
||||
let multiplier = premultipliedAlpha ? alpha : 1;
|
||||
let color = this.tempColor;
|
||||
color.set(skeletonColor.r * slotColor.r * regionColor.r * multiplier,
|
||||
skeletonColor.g * slotColor.g * regionColor.g * multiplier,
|
||||
skeletonColor.b * slotColor.b * regionColor.b * multiplier,
|
||||
alpha);
|
||||
|
||||
let vertices = this.vertices;
|
||||
let offset = this.offset;
|
||||
let bone = slot.bone;
|
||||
let x = skeleton.x + bone.worldX, y = skeleton.y + bone.worldY;
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
let offsetX = 0, offsetY = 0;
|
||||
|
||||
offsetX = offset[RegionAttachment.OX1];
|
||||
offsetY = offset[RegionAttachment.OY1];
|
||||
vertices[RegionAttachment.X1] = offsetX * a + offsetY * b + x; // br
|
||||
vertices[RegionAttachment.Y1] = offsetX * c + offsetY * d + y;
|
||||
vertices[RegionAttachment.C1R] = color.r;
|
||||
vertices[RegionAttachment.C1G] = color.g;
|
||||
vertices[RegionAttachment.C1B] = color.b;
|
||||
vertices[RegionAttachment.C1A] = color.a;
|
||||
|
||||
offsetX = offset[RegionAttachment.OX2];
|
||||
offsetY = offset[RegionAttachment.OY2];
|
||||
vertices[RegionAttachment.X2] = offsetX * a + offsetY * b + x; // bl
|
||||
vertices[RegionAttachment.Y2] = offsetX * c + offsetY * d + y;
|
||||
vertices[RegionAttachment.C2R] = color.r;
|
||||
vertices[RegionAttachment.C2G] = color.g;
|
||||
vertices[RegionAttachment.C2B] = color.b;
|
||||
vertices[RegionAttachment.C2A] = color.a;
|
||||
|
||||
offsetX = offset[RegionAttachment.OX3];
|
||||
offsetY = offset[RegionAttachment.OY3];
|
||||
vertices[RegionAttachment.X3] = offsetX * a + offsetY * b + x; // ul
|
||||
vertices[RegionAttachment.Y3] = offsetX * c + offsetY * d + y;
|
||||
vertices[RegionAttachment.C3R] = color.r;
|
||||
vertices[RegionAttachment.C3G] = color.g;
|
||||
vertices[RegionAttachment.C3B] = color.b;
|
||||
vertices[RegionAttachment.C3A] = color.a;
|
||||
|
||||
offsetX = offset[RegionAttachment.OX4];
|
||||
offsetY = offset[RegionAttachment.OY4];
|
||||
vertices[RegionAttachment.X4] = offsetX * a + offsetY * b + x; // ur
|
||||
vertices[RegionAttachment.Y4] = offsetX * c + offsetY * d + y;
|
||||
vertices[RegionAttachment.C4R] = color.r;
|
||||
vertices[RegionAttachment.C4G] = color.g;
|
||||
vertices[RegionAttachment.C4B] = color.b;
|
||||
vertices[RegionAttachment.C4A] = color.a;
|
||||
|
||||
return vertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
spine-ts/core/src/attachments/TextureRegion.ts
Normal file
42
spine-ts/core/src/attachments/TextureRegion.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class TextureRegion {
|
||||
renderObject: any;
|
||||
u = 0; v = 0;
|
||||
u2 = 0; v2 = 0;
|
||||
width = 0; height = 0;
|
||||
rotate = false;
|
||||
offsetX = 0; offsetY = 0;
|
||||
originalWidth = 0; originalHeight = 0;
|
||||
}
|
||||
}
|
||||
19
spine-ts/tsconfig.core.json
Normal file
19
spine-ts/tsconfig.core.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "none",
|
||||
"noImplicitAny": true,
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
"outFile": "build/spine-core.js",
|
||||
"sourceMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": [
|
||||
"core/src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"webgl/src/*.ts",
|
||||
"build",
|
||||
"build/*.d.ts"
|
||||
]
|
||||
}
|
||||
22
spine-ts/tsconfig.json
Normal file
22
spine-ts/tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "none",
|
||||
"noImplicitAny": true,
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
"outFile": "build/spine-all.js",
|
||||
"sourceMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": [
|
||||
"core/src/**/*",
|
||||
"webgl/src/**/*",
|
||||
"widget/src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"build",
|
||||
"build/*.js",
|
||||
"build/*.d.ts",
|
||||
"build/*.js.map"
|
||||
]
|
||||
}
|
||||
21
spine-ts/tsconfig.webgl.json
Normal file
21
spine-ts/tsconfig.webgl.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "none",
|
||||
"noImplicitAny": true,
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
"outFile": "build/spine-webgl.js",
|
||||
"sourceMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": [
|
||||
"core/src/**/*",
|
||||
"webgl/src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"build",
|
||||
"build/*.d.ts",
|
||||
"build/*.js",
|
||||
"build/*.js.map"
|
||||
]
|
||||
}
|
||||
22
spine-ts/tsconfig.widget.json
Normal file
22
spine-ts/tsconfig.widget.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "none",
|
||||
"noImplicitAny": true,
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
"outFile": "build/spine-widget.js",
|
||||
"sourceMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": [
|
||||
"core/src/**/*",
|
||||
"webgl/src/**/*",
|
||||
"widget/src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"build",
|
||||
"build/*.d.ts",
|
||||
"build/*.js",
|
||||
"build/*.js.map"
|
||||
]
|
||||
}
|
||||
293
spine-ts/webgl/example/assets/goblins-mesh.atlas
Normal file
293
spine-ts/webgl/example/assets/goblins-mesh.atlas
Normal file
@ -0,0 +1,293 @@
|
||||
|
||||
goblins-mesh.png
|
||||
size: 1024,128
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
dagger
|
||||
rotate: true
|
||||
xy: 372, 100
|
||||
size: 26, 108
|
||||
orig: 26, 108
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/eyes-closed
|
||||
rotate: false
|
||||
xy: 2, 7
|
||||
size: 34, 12
|
||||
orig: 34, 12
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/head
|
||||
rotate: false
|
||||
xy: 107, 36
|
||||
size: 103, 66
|
||||
orig: 103, 66
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-arm
|
||||
rotate: false
|
||||
xy: 901, 56
|
||||
size: 37, 35
|
||||
orig: 37, 35
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-foot
|
||||
rotate: false
|
||||
xy: 929, 95
|
||||
size: 65, 31
|
||||
orig: 65, 31
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-hand
|
||||
rotate: false
|
||||
xy: 452, 2
|
||||
size: 36, 41
|
||||
orig: 36, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-lower-leg
|
||||
rotate: true
|
||||
xy: 713, 93
|
||||
size: 33, 70
|
||||
orig: 33, 70
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-shoulder
|
||||
rotate: false
|
||||
xy: 610, 44
|
||||
size: 29, 44
|
||||
orig: 29, 44
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-upper-leg
|
||||
rotate: true
|
||||
xy: 638, 93
|
||||
size: 33, 73
|
||||
orig: 33, 73
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/neck
|
||||
rotate: false
|
||||
xy: 490, 2
|
||||
size: 36, 41
|
||||
orig: 36, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/pelvis
|
||||
rotate: false
|
||||
xy: 482, 45
|
||||
size: 62, 43
|
||||
orig: 62, 43
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-arm
|
||||
rotate: true
|
||||
xy: 690, 2
|
||||
size: 23, 50
|
||||
orig: 23, 50
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-foot
|
||||
rotate: false
|
||||
xy: 771, 58
|
||||
size: 63, 33
|
||||
orig: 63, 33
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-hand
|
||||
rotate: false
|
||||
xy: 940, 56
|
||||
size: 36, 37
|
||||
orig: 36, 37
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-lower-leg
|
||||
rotate: true
|
||||
xy: 482, 90
|
||||
size: 36, 76
|
||||
orig: 36, 76
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-shoulder
|
||||
rotate: true
|
||||
xy: 602, 3
|
||||
size: 39, 45
|
||||
orig: 39, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-upper-leg
|
||||
rotate: true
|
||||
xy: 641, 57
|
||||
size: 34, 63
|
||||
orig: 34, 63
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/torso
|
||||
rotate: true
|
||||
xy: 212, 34
|
||||
size: 68, 96
|
||||
orig: 68, 96
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/undie-straps
|
||||
rotate: false
|
||||
xy: 380, 5
|
||||
size: 55, 19
|
||||
orig: 55, 19
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/undies
|
||||
rotate: false
|
||||
xy: 174, 5
|
||||
size: 36, 29
|
||||
orig: 36, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/eyes-closed
|
||||
rotate: false
|
||||
xy: 269, 11
|
||||
size: 37, 21
|
||||
orig: 37, 21
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/head
|
||||
rotate: false
|
||||
xy: 2, 21
|
||||
size: 103, 81
|
||||
orig: 103, 81
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-arm
|
||||
rotate: true
|
||||
xy: 978, 56
|
||||
size: 37, 35
|
||||
orig: 37, 35
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-foot
|
||||
rotate: false
|
||||
xy: 107, 3
|
||||
size: 65, 31
|
||||
orig: 65, 31
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-hand
|
||||
rotate: false
|
||||
xy: 565, 2
|
||||
size: 35, 40
|
||||
orig: 35, 40
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-lower-leg
|
||||
rotate: true
|
||||
xy: 785, 93
|
||||
size: 33, 70
|
||||
orig: 33, 70
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-shoulder
|
||||
rotate: true
|
||||
xy: 690, 27
|
||||
size: 28, 46
|
||||
orig: 28, 46
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-upper-leg
|
||||
rotate: true
|
||||
xy: 857, 93
|
||||
size: 33, 70
|
||||
orig: 33, 70
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/neck
|
||||
rotate: false
|
||||
xy: 528, 2
|
||||
size: 35, 41
|
||||
orig: 35, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/pelvis
|
||||
rotate: false
|
||||
xy: 546, 45
|
||||
size: 62, 43
|
||||
orig: 62, 43
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-arm
|
||||
rotate: false
|
||||
xy: 452, 48
|
||||
size: 28, 50
|
||||
orig: 28, 50
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-foot
|
||||
rotate: false
|
||||
xy: 836, 58
|
||||
size: 63, 33
|
||||
orig: 63, 33
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-hand
|
||||
rotate: true
|
||||
xy: 771, 20
|
||||
size: 36, 37
|
||||
orig: 36, 37
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-lower-leg
|
||||
rotate: true
|
||||
xy: 560, 90
|
||||
size: 36, 76
|
||||
orig: 36, 76
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-shoulder
|
||||
rotate: false
|
||||
xy: 649, 10
|
||||
size: 39, 45
|
||||
orig: 39, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-upper-leg
|
||||
rotate: true
|
||||
xy: 706, 57
|
||||
size: 34, 63
|
||||
orig: 34, 63
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/torso
|
||||
rotate: false
|
||||
xy: 310, 2
|
||||
size: 68, 96
|
||||
orig: 68, 96
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/undie-straps
|
||||
rotate: false
|
||||
xy: 212, 13
|
||||
size: 55, 19
|
||||
orig: 55, 19
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/undies
|
||||
rotate: false
|
||||
xy: 810, 27
|
||||
size: 36, 29
|
||||
orig: 36, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
shield
|
||||
rotate: false
|
||||
xy: 380, 26
|
||||
size: 70, 72
|
||||
orig: 70, 72
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
spear
|
||||
rotate: true
|
||||
xy: 2, 104
|
||||
size: 22, 368
|
||||
orig: 22, 368
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
1082
spine-ts/webgl/example/assets/goblins-mesh.json
Normal file
1082
spine-ts/webgl/example/assets/goblins-mesh.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
spine-ts/webgl/example/assets/goblins-mesh.png
Normal file
BIN
spine-ts/webgl/example/assets/goblins-mesh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 157 KiB |
279
spine-ts/webgl/example/assets/raptor.atlas
Normal file
279
spine-ts/webgl/example/assets/raptor.atlas
Normal file
@ -0,0 +1,279 @@
|
||||
|
||||
raptor.png
|
||||
size: 1024,1024
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
back_arm
|
||||
rotate: true
|
||||
xy: 140, 191
|
||||
size: 46, 29
|
||||
orig: 46, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_bracer
|
||||
rotate: true
|
||||
xy: 167, 317
|
||||
size: 39, 28
|
||||
orig: 39, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_hand
|
||||
rotate: false
|
||||
xy: 167, 358
|
||||
size: 36, 34
|
||||
orig: 36, 34
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_knee
|
||||
rotate: false
|
||||
xy: 299, 478
|
||||
size: 49, 67
|
||||
orig: 49, 67
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_thigh
|
||||
rotate: true
|
||||
xy: 167, 437
|
||||
size: 39, 24
|
||||
orig: 39, 24
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eyes_closed
|
||||
rotate: true
|
||||
xy: 2, 2
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eyes_open
|
||||
rotate: true
|
||||
xy: 49, 2
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eyes_surprised
|
||||
rotate: true
|
||||
xy: 96, 2
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_arm
|
||||
rotate: false
|
||||
xy: 419, 544
|
||||
size: 48, 30
|
||||
orig: 48, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_bracer
|
||||
rotate: false
|
||||
xy: 880, 695
|
||||
size: 41, 29
|
||||
orig: 41, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_hand
|
||||
rotate: true
|
||||
xy: 167, 394
|
||||
size: 41, 38
|
||||
orig: 41, 38
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_open_hand
|
||||
rotate: false
|
||||
xy: 880, 726
|
||||
size: 43, 44
|
||||
orig: 43, 44
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_thigh
|
||||
rotate: false
|
||||
xy: 360, 545
|
||||
size: 57, 29
|
||||
orig: 57, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun
|
||||
rotate: false
|
||||
xy: 785, 774
|
||||
size: 107, 103
|
||||
orig: 107, 103
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun_nohand
|
||||
rotate: false
|
||||
xy: 614, 703
|
||||
size: 105, 102
|
||||
orig: 105, 102
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
head
|
||||
rotate: false
|
||||
xy: 2, 137
|
||||
size: 136, 149
|
||||
orig: 136, 149
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
lower_leg
|
||||
rotate: true
|
||||
xy: 780, 699
|
||||
size: 73, 98
|
||||
orig: 73, 98
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_grind
|
||||
rotate: false
|
||||
xy: 469, 544
|
||||
size: 47, 30
|
||||
orig: 47, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_oooo
|
||||
rotate: true
|
||||
xy: 894, 772
|
||||
size: 105, 30
|
||||
orig: 105, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_smile
|
||||
rotate: true
|
||||
xy: 140, 239
|
||||
size: 47, 30
|
||||
orig: 47, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
neck
|
||||
rotate: true
|
||||
xy: 538, 577
|
||||
size: 18, 21
|
||||
orig: 18, 21
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_arm_back
|
||||
rotate: false
|
||||
xy: 940, 936
|
||||
size: 82, 86
|
||||
orig: 82, 86
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_body
|
||||
rotate: false
|
||||
xy: 2, 737
|
||||
size: 610, 285
|
||||
orig: 610, 285
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_front_arm
|
||||
rotate: true
|
||||
xy: 195, 464
|
||||
size: 81, 102
|
||||
orig: 81, 102
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_front_leg
|
||||
rotate: false
|
||||
xy: 2, 478
|
||||
size: 191, 257
|
||||
orig: 191, 257
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_hindleg_back
|
||||
rotate: false
|
||||
xy: 614, 807
|
||||
size: 169, 215
|
||||
orig: 169, 215
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_horn
|
||||
rotate: false
|
||||
xy: 360, 655
|
||||
size: 182, 80
|
||||
orig: 182, 80
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_horn_back
|
||||
rotate: false
|
||||
xy: 360, 576
|
||||
size: 176, 77
|
||||
orig: 176, 77
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_jaw
|
||||
rotate: false
|
||||
xy: 785, 879
|
||||
size: 153, 143
|
||||
orig: 153, 143
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_noshadow
|
||||
rotate: false
|
||||
xy: 2, 288
|
||||
size: 163, 188
|
||||
orig: 163, 188
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_strap_front
|
||||
rotate: false
|
||||
xy: 721, 710
|
||||
size: 57, 95
|
||||
orig: 57, 95
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_strap_rear
|
||||
rotate: true
|
||||
xy: 940, 880
|
||||
size: 54, 74
|
||||
orig: 54, 74
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_w_shadow
|
||||
rotate: false
|
||||
xy: 195, 547
|
||||
size: 163, 188
|
||||
orig: 163, 188
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_tongue
|
||||
rotate: true
|
||||
xy: 544, 649
|
||||
size: 86, 64
|
||||
orig: 86, 64
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_back
|
||||
rotate: true
|
||||
xy: 140, 145
|
||||
size: 44, 35
|
||||
orig: 44, 35
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_front
|
||||
rotate: false
|
||||
xy: 538, 597
|
||||
size: 45, 50
|
||||
orig: 45, 50
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_strap
|
||||
rotate: false
|
||||
xy: 350, 497
|
||||
size: 49, 46
|
||||
orig: 49, 46
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
torso
|
||||
rotate: true
|
||||
xy: 610, 647
|
||||
size: 54, 91
|
||||
orig: 54, 91
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
visor
|
||||
rotate: false
|
||||
xy: 2, 51
|
||||
size: 131, 84
|
||||
orig: 131, 84
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
2621
spine-ts/webgl/example/assets/raptor.json
Normal file
2621
spine-ts/webgl/example/assets/raptor.json
Normal file
File diff suppressed because one or more lines are too long
BIN
spine-ts/webgl/example/assets/raptor.png
Normal file
BIN
spine-ts/webgl/example/assets/raptor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 495 KiB |
195
spine-ts/webgl/example/assets/spineboy.atlas
Normal file
195
spine-ts/webgl/example/assets/spineboy.atlas
Normal file
@ -0,0 +1,195 @@
|
||||
|
||||
spineboy.png
|
||||
size: 1024,1024
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
eye_indifferent
|
||||
rotate: false
|
||||
xy: 550, 694
|
||||
size: 93, 89
|
||||
orig: 93, 89
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eye_surprised
|
||||
rotate: false
|
||||
xy: 834, 856
|
||||
size: 93, 89
|
||||
orig: 93, 89
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_bracer
|
||||
rotate: false
|
||||
xy: 678, 774
|
||||
size: 58, 80
|
||||
orig: 58, 80
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_fist_closed
|
||||
rotate: true
|
||||
xy: 466, 593
|
||||
size: 75, 82
|
||||
orig: 75, 82
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_fist_open
|
||||
rotate: false
|
||||
xy: 550, 605
|
||||
size: 86, 87
|
||||
orig: 86, 87
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_foot
|
||||
rotate: false
|
||||
xy: 550, 785
|
||||
size: 126, 69
|
||||
orig: 126, 69
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_foot_bend1
|
||||
rotate: true
|
||||
xy: 375, 492
|
||||
size: 128, 70
|
||||
orig: 128, 70
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_foot_bend2
|
||||
rotate: true
|
||||
xy: 275, 330
|
||||
size: 108, 93
|
||||
orig: 108, 93
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_shin
|
||||
rotate: false
|
||||
xy: 466, 670
|
||||
size: 82, 184
|
||||
orig: 82, 184
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_thigh
|
||||
rotate: false
|
||||
xy: 214, 208
|
||||
size: 48, 112
|
||||
orig: 48, 112
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_upper_arm
|
||||
rotate: false
|
||||
xy: 214, 109
|
||||
size: 54, 97
|
||||
orig: 54, 97
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goggles
|
||||
rotate: false
|
||||
xy: 466, 856
|
||||
size: 261, 166
|
||||
orig: 261, 166
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun
|
||||
rotate: false
|
||||
xy: 2, 117
|
||||
size: 210, 203
|
||||
orig: 210, 203
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
head
|
||||
rotate: false
|
||||
xy: 2, 322
|
||||
size: 271, 298
|
||||
orig: 271, 298
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_grind
|
||||
rotate: false
|
||||
xy: 929, 896
|
||||
size: 93, 59
|
||||
orig: 93, 59
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_oooo
|
||||
rotate: false
|
||||
xy: 929, 835
|
||||
size: 93, 59
|
||||
orig: 93, 59
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_smile
|
||||
rotate: false
|
||||
xy: 447, 532
|
||||
size: 93, 59
|
||||
orig: 93, 59
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
muzzle
|
||||
rotate: false
|
||||
xy: 2, 622
|
||||
size: 462, 400
|
||||
orig: 462, 400
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
neck
|
||||
rotate: false
|
||||
xy: 796, 819
|
||||
size: 36, 41
|
||||
orig: 36, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_bracer
|
||||
rotate: false
|
||||
xy: 738, 788
|
||||
size: 56, 72
|
||||
orig: 56, 72
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_foot
|
||||
rotate: true
|
||||
xy: 2, 2
|
||||
size: 113, 60
|
||||
orig: 113, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_foot_bend1
|
||||
rotate: false
|
||||
xy: 64, 49
|
||||
size: 117, 66
|
||||
orig: 117, 66
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_foot_bend2
|
||||
rotate: false
|
||||
xy: 729, 862
|
||||
size: 103, 83
|
||||
orig: 103, 83
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_shin
|
||||
rotate: true
|
||||
xy: 729, 947
|
||||
size: 75, 178
|
||||
orig: 75, 178
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_thigh
|
||||
rotate: true
|
||||
xy: 909, 957
|
||||
size: 65, 104
|
||||
orig: 65, 104
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_upper_arm
|
||||
rotate: true
|
||||
xy: 447, 483
|
||||
size: 47, 87
|
||||
orig: 47, 87
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
torso
|
||||
rotate: false
|
||||
xy: 275, 440
|
||||
size: 98, 180
|
||||
orig: 98, 180
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
2412
spine-ts/webgl/example/assets/spineboy.json
Normal file
2412
spine-ts/webgl/example/assets/spineboy.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
spine-ts/webgl/example/assets/spineboy.png
Normal file
BIN
spine-ts/webgl/example/assets/spineboy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 593 KiB |
125
spine-ts/webgl/example/assets/tank.atlas
Normal file
125
spine-ts/webgl/example/assets/tank.atlas
Normal file
@ -0,0 +1,125 @@
|
||||
|
||||
tank.png
|
||||
size: 2048,1024
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
images/antenna
|
||||
rotate: true
|
||||
xy: 1295, 683
|
||||
size: 22, 303
|
||||
orig: 22, 303
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/cannon
|
||||
rotate: false
|
||||
xy: 2, 93
|
||||
size: 931, 58
|
||||
orig: 931, 58
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/cannonConnector
|
||||
rotate: true
|
||||
xy: 1676, 455
|
||||
size: 112, 135
|
||||
orig: 112, 135
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/guntower
|
||||
rotate: false
|
||||
xy: 1295, 707
|
||||
size: 730, 289
|
||||
orig: 730, 289
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/machinegun
|
||||
rotate: false
|
||||
xy: 2, 34
|
||||
size: 331, 57
|
||||
orig: 331, 57
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/machinegun-mount
|
||||
rotate: false
|
||||
xy: 1952, 609
|
||||
size: 72, 96
|
||||
orig: 72, 96
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/rock
|
||||
rotate: false
|
||||
xy: 935, 96
|
||||
size: 252, 55
|
||||
orig: 252, 55
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/tankBottom
|
||||
rotate: false
|
||||
xy: 2, 377
|
||||
size: 1285, 276
|
||||
orig: 1285, 276
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/tankBottom-shadow
|
||||
rotate: false
|
||||
xy: 2, 655
|
||||
size: 1291, 341
|
||||
orig: 1291, 341
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/tankTop
|
||||
rotate: false
|
||||
xy: 2, 153
|
||||
size: 1407, 222
|
||||
orig: 1407, 222
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/tread
|
||||
rotate: false
|
||||
xy: 2, 2
|
||||
size: 96, 30
|
||||
orig: 96, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/tread-inside
|
||||
rotate: false
|
||||
xy: 335, 63
|
||||
size: 25, 28
|
||||
orig: 25, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/wheel-big
|
||||
rotate: false
|
||||
xy: 1295, 490
|
||||
size: 191, 191
|
||||
orig: 191, 191
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/wheel-big-overlay
|
||||
rotate: false
|
||||
xy: 1488, 495
|
||||
size: 186, 186
|
||||
orig: 186, 186
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/wheel-mid
|
||||
rotate: false
|
||||
xy: 1676, 569
|
||||
size: 136, 136
|
||||
orig: 136, 136
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/wheel-mid-overlay
|
||||
rotate: false
|
||||
xy: 1814, 569
|
||||
size: 136, 136
|
||||
orig: 136, 136
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
images/wheel-small
|
||||
rotate: false
|
||||
xy: 1813, 496
|
||||
size: 71, 71
|
||||
orig: 71, 71
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
1899
spine-ts/webgl/example/assets/tank.json
Normal file
1899
spine-ts/webgl/example/assets/tank.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
spine-ts/webgl/example/assets/tank.png
Normal file
BIN
spine-ts/webgl/example/assets/tank.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
13
spine-ts/webgl/example/assets/vine.atlas
Normal file
13
spine-ts/webgl/example/assets/vine.atlas
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
vine.png
|
||||
size: 128,1024
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
images/vine
|
||||
rotate: false
|
||||
xy: 2, 2
|
||||
size: 68, 962
|
||||
orig: 68, 962
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
300
spine-ts/webgl/example/assets/vine.json
Normal file
300
spine-ts/webgl/example/assets/vine.json
Normal file
File diff suppressed because one or more lines are too long
BIN
spine-ts/webgl/example/assets/vine.png
Normal file
BIN
spine-ts/webgl/example/assets/vine.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 156 KiB |
219
spine-ts/webgl/example/index.html
Normal file
219
spine-ts/webgl/example/index.html
Normal file
@ -0,0 +1,219 @@
|
||||
<html>
|
||||
<script src="../../build/spine-webgl.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<body>
|
||||
<center>
|
||||
<canvas id="canvas" style="width: 640; height: 480"></canvas></br>
|
||||
<span>Skeleton:</span><select id="skeletonList"></select>
|
||||
<span>Animation:</span><select id="animationList"></select>
|
||||
<span>Skin:</span><select id="skinList"></select>
|
||||
</center>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
var lastFrameTime = Date.now() / 1000;
|
||||
var canvas;
|
||||
var shader;
|
||||
var batcher;
|
||||
var gl;
|
||||
var mvp = new spine.webgl.Matrix4();
|
||||
var assetManager;
|
||||
var skeletonRenderer;
|
||||
var skeletons = {};
|
||||
var activeSkeleton = "raptor";
|
||||
|
||||
function init () {
|
||||
// Setup canvas and WebGL context. We pass alpha: false to canvas.getContext() so we don't use premultiplied alpha when
|
||||
// loading textures. That is handled separately by PolygonBatcher.
|
||||
canvas = document.getElementById("canvas");
|
||||
canvas.width = 640;
|
||||
canvas.height = 480;
|
||||
var config = { alpha: false };
|
||||
gl = canvas.getContext("webgl", config) || canvas.getContext("experimental-webgl", config);
|
||||
|
||||
// Create a simple shader, mesh, model-view-projection matrix and SkeletonRenderer.
|
||||
shader = spine.webgl.Shader.newColoredTextured(gl);
|
||||
batcher = new spine.webgl.PolygonBatcher(gl);
|
||||
mvp.ortho2d(0, 0, 639, 479);
|
||||
skeletonRenderer = new spine.webgl.SkeletonRenderer(gl);
|
||||
assetManager = new spine.webgl.AssetManager(gl);
|
||||
|
||||
// Tell AssetManager to load the resources for each model, including the exported .json file, the .atlas file and the .png
|
||||
// file for the atlas. We then wait until all resources are loaded in the load() method.
|
||||
assetManager.loadText("assets/spineboy.json");
|
||||
assetManager.loadText("assets/spineboy.atlas");
|
||||
assetManager.loadTexture("assets/spineboy.png");
|
||||
assetManager.loadText("assets/raptor.json");
|
||||
assetManager.loadText("assets/raptor.atlas");
|
||||
assetManager.loadTexture("assets/raptor.png");
|
||||
assetManager.loadText("assets/tank.json");
|
||||
assetManager.loadText("assets/tank.atlas");
|
||||
assetManager.loadTexture("assets/tank.png");
|
||||
assetManager.loadText("assets/goblins-mesh.json");
|
||||
assetManager.loadText("assets/goblins-mesh.atlas");
|
||||
assetManager.loadTexture("assets/goblins-mesh.png");
|
||||
assetManager.loadText("assets/vine.json");
|
||||
assetManager.loadText("assets/vine.atlas");
|
||||
assetManager.loadTexture("assets/vine.png");
|
||||
requestAnimationFrame(load);
|
||||
}
|
||||
|
||||
function load () {
|
||||
// Wait until the AssetManager has loaded all resources, then load the skeletons.
|
||||
if (assetManager.isLoadingComplete()) {
|
||||
skeletons["spineboy"] = loadSkeleton("spineboy", 0.5, "test", 320, 20, false);
|
||||
skeletons["raptor"] = loadSkeleton("raptor", 0.4, "walk", 320, 20, false);
|
||||
skeletons["tank"] = loadSkeleton("tank", 0.3, "drive", 400, 20, false);
|
||||
skeletons["goblins"] = loadSkeleton("goblins-mesh", 1, "walk", 320, 20, true, "goblin");
|
||||
skeletons["vine"] = loadSkeleton("vine", 0.5, "animation", 320, 20, false);
|
||||
setupUI();
|
||||
requestAnimationFrame(render);
|
||||
} else {
|
||||
requestAnimationFrame(load);
|
||||
}
|
||||
}
|
||||
|
||||
function loadSkeleton (name, scale, initialAnimation, positionX, positionY, premultipliedAlpha, skin) {
|
||||
if (skin === undefined) skin = "default";
|
||||
|
||||
// Load the texture atlas using name.atlas and name.png from the AssetManager.
|
||||
// The function passed to TextureAtlas is used to resolve relative paths.
|
||||
atlas = new spine.webgl.TextureAtlas(assetManager.get("assets/" + name + ".atlas"), function(path) {
|
||||
return assetManager.get("assets/" + path);
|
||||
});
|
||||
|
||||
// Create a TextureAtlasAttachmentLoader, which is specific to the WebGL backend.
|
||||
atlasLoader = new spine.webgl.TextureAtlasAttachmentLoader(atlas);
|
||||
|
||||
// Create a SkeletonJson instance for parsing the .json file.
|
||||
var skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||
|
||||
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
||||
skeletonJson.scale = scale;
|
||||
var skeletonData = skeletonJson.readSkeletonData(assetManager.get("assets/" + name + ".json"));
|
||||
var skeleton = new spine.Skeleton(skeletonData);
|
||||
skeleton.x = positionX;
|
||||
skeleton.y = positionY;
|
||||
skeleton.setSkinByName(skin);
|
||||
|
||||
// Create an AnimationState, and set the initial animation in looping mode.
|
||||
var animationState = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
|
||||
animationState.setAnimation(0, initialAnimation, true);
|
||||
animationState.addListener({
|
||||
event: function(trackIndex, event) {
|
||||
// console.log("Event on track " + trackIndex + ": " + JSON.stringify(event));
|
||||
},
|
||||
complete: function(trackIndex, loopCount) {
|
||||
// console.log("Animation on track " + trackIndex + " completed, loop count: " + loopCount);
|
||||
},
|
||||
start: function(trackIndex) {
|
||||
// console.log("Animation on track " + trackIndex + " started");
|
||||
},
|
||||
end: function(trackIndex) {
|
||||
// console.log("Animation on track " + trackIndex + " ended");
|
||||
}
|
||||
})
|
||||
|
||||
// Pack everything up and return to caller.
|
||||
return { skeleton: skeleton, state: animationState, premultipliedAlpha: premultipliedAlpha };
|
||||
}
|
||||
|
||||
function setupUI () {
|
||||
var skeletonList = $("#skeletonList");
|
||||
for (var skeletonName in skeletons) {
|
||||
var option = $("<option></option>");
|
||||
option.attr("value", skeletonName).text(skeletonName);
|
||||
if (skeletonName === activeSkeleton) option.attr("selected", "selected");
|
||||
skeletonList.append(option);
|
||||
}
|
||||
var setupAnimationUI = function() {
|
||||
var animationList = $("#animationList");
|
||||
animationList.empty();
|
||||
var skeleton = skeletons[activeSkeleton].skeleton;
|
||||
var state = skeletons[activeSkeleton].state;
|
||||
var activeAnimation = state.tracks[0].animation.name;
|
||||
for (var i = 0; i < skeleton.data.animations.length; i++) {
|
||||
var name = skeleton.data.animations[i].name;
|
||||
var option = $("<option></option>");
|
||||
option.attr("value", name).text(name);
|
||||
if (name === activeAnimation) option.attr("selected", "selected");
|
||||
animationList.append(option);
|
||||
}
|
||||
|
||||
animationList.change(function() {
|
||||
var state = skeletons[activeSkeleton].state;
|
||||
var skeleton = skeletons[activeSkeleton].skeleton;
|
||||
var animationName = $("#animationList option:selected").text();
|
||||
skeleton.setToSetupPose();
|
||||
state.setAnimation(0, animationName, true);
|
||||
})
|
||||
}
|
||||
|
||||
var setupSkinUI = function() {
|
||||
var skinList = $("#skinList");
|
||||
skinList.empty();
|
||||
var skeleton = skeletons[activeSkeleton].skeleton;
|
||||
var activeSkin = skeleton.skin == null ? "default" : skeleton.skin.name;
|
||||
for (var i = 0; i < skeleton.data.skins.length; i++) {
|
||||
var name = skeleton.data.skins[i].name;
|
||||
var option = $("<option></option>");
|
||||
option.attr("value", name).text(name);
|
||||
if (name === activeSkin) option.attr("selected", "selected");
|
||||
skinList.append(option);
|
||||
}
|
||||
|
||||
skinList.change(function() {
|
||||
var skeleton = skeletons[activeSkeleton].skeleton;
|
||||
var skinName = $("#skinList option:selected").text();
|
||||
skeleton.setSkinByName(skinName);
|
||||
skeleton.setSlotsToSetupPose();
|
||||
})
|
||||
}
|
||||
|
||||
skeletonList.change(function() {
|
||||
activeSkeleton = $("#skeletonList option:selected").text();
|
||||
setupAnimationUI();
|
||||
setupSkinUI();
|
||||
})
|
||||
setupAnimationUI();
|
||||
setupSkinUI();
|
||||
}
|
||||
|
||||
function render () {
|
||||
var now = Date.now() / 1000;
|
||||
var delta = now - lastFrameTime;
|
||||
lastFrameTime = now;
|
||||
|
||||
gl.clearColor(0.3, 0.3, 0.3, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
// Apply the animation state based on the delta time.
|
||||
var state = skeletons[activeSkeleton].state;
|
||||
var skeleton = skeletons[activeSkeleton].skeleton;
|
||||
var premultipliedAlpha = skeletons[activeSkeleton].premultipliedAlpha;
|
||||
state.update(delta);
|
||||
state.apply(skeleton);
|
||||
skeleton.updateWorldTransform();
|
||||
|
||||
// Bind the shader and set the texture and model-view-projection matrix.
|
||||
shader.bind();
|
||||
shader.setUniformi(spine.webgl.Shader.SAMPLER, 0);
|
||||
shader.setUniform4x4f(spine.webgl.Shader.MVP_MATRIX, mvp.values);
|
||||
|
||||
// Start the batch and tell the SkeletonRenderer to render the active skeleton.
|
||||
batcher.begin(shader);
|
||||
skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
|
||||
skeletonRenderer.draw(batcher, skeleton);
|
||||
batcher.end();
|
||||
|
||||
shader.unbind();
|
||||
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
(function() {
|
||||
init();
|
||||
})();
|
||||
|
||||
</script>
|
||||
</html>
|
||||
133
spine-ts/webgl/src/AssetManager.ts
Normal file
133
spine-ts/webgl/src/AssetManager.ts
Normal file
@ -0,0 +1,133 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class AssetManager implements Disposable {
|
||||
private _gl: WebGLRenderingContext;
|
||||
private _assets: Map<string | Texture> = {};
|
||||
private _errors: Map<string> = {};
|
||||
private _toLoad = 0;
|
||||
private _loaded = 0;
|
||||
|
||||
constructor (gl: WebGLRenderingContext) {
|
||||
this._gl = gl;
|
||||
}
|
||||
|
||||
loadText(path: string,
|
||||
success: (path: string, text: string) => void = null,
|
||||
error: (path: string, error: string) => void = null
|
||||
) {
|
||||
this._toLoad++;
|
||||
let request = new XMLHttpRequest();
|
||||
request.onreadystatechange = () => {
|
||||
if (request.readyState == XMLHttpRequest.DONE) {
|
||||
if (request.status >= 200 && request.status < 300) {
|
||||
if (success) success(path, request.responseText);
|
||||
this._assets[path] = request.responseText;
|
||||
} else {
|
||||
if (error) error(path, `Couldn't load text ${path}: status ${request.status}, ${request.responseText}`);
|
||||
this._errors[path] = `Couldn't load text ${path}: status ${request.status}, ${request.responseText}`;
|
||||
}
|
||||
this._toLoad--;
|
||||
this._loaded++;
|
||||
}
|
||||
};
|
||||
request.open("GET", path, true);
|
||||
request.send();
|
||||
}
|
||||
|
||||
loadTexture (path: string,
|
||||
success: (path: string, image: HTMLImageElement) => void = null,
|
||||
error: (path: string, error: string) => void = null
|
||||
) {
|
||||
this._toLoad++;
|
||||
let img = new Image();
|
||||
img.src = path;
|
||||
img.onload = (ev) => {
|
||||
if (success) success(path, img);
|
||||
let texture = new Texture(this._gl, img);
|
||||
this._assets[path] = texture;
|
||||
this._toLoad--;
|
||||
this._loaded++;
|
||||
}
|
||||
img.onerror = (ev) => {
|
||||
if (error) error(path, `Couldn't load image ${path}`);
|
||||
this._errors[path] = `Couldn't load image ${path}`;
|
||||
this._toLoad--;
|
||||
this._loaded++;
|
||||
}
|
||||
}
|
||||
|
||||
get (path: string) {
|
||||
return this._assets[path];
|
||||
}
|
||||
|
||||
remove (path: string) {
|
||||
let asset = this._assets[path];
|
||||
if (asset instanceof Texture) {
|
||||
asset.dispose();
|
||||
}
|
||||
this._assets[path] = null;
|
||||
}
|
||||
|
||||
removeAll () {
|
||||
for (let key in this._assets) {
|
||||
let asset = this._assets[key];
|
||||
if (asset instanceof Texture) asset.dispose();
|
||||
}
|
||||
this._assets = {};
|
||||
}
|
||||
|
||||
isLoadingComplete (): boolean {
|
||||
return this._toLoad == 0;
|
||||
}
|
||||
|
||||
toLoad (): number {
|
||||
return this._toLoad;
|
||||
}
|
||||
|
||||
loaded (): number {
|
||||
return this._loaded;
|
||||
}
|
||||
|
||||
dispose () {
|
||||
this.removeAll();
|
||||
}
|
||||
|
||||
hasErrors() {
|
||||
return Object.keys(this._errors).length > 0;
|
||||
}
|
||||
|
||||
errors() {
|
||||
return this._errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
303
spine-ts/webgl/src/Matrix4.ts
Normal file
303
spine-ts/webgl/src/Matrix4.ts
Normal file
@ -0,0 +1,303 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export const M00 = 0;
|
||||
export const M01 = 4;
|
||||
export const M02 = 8;
|
||||
export const M03 = 12;
|
||||
export const M10 = 1;
|
||||
export const M11 = 5;
|
||||
export const M12 = 9;
|
||||
export const M13 = 13;
|
||||
export const M20 = 2;
|
||||
export const M21 = 6;
|
||||
export const M22 = 10;
|
||||
export const M23 = 14;
|
||||
export const M30 = 3;
|
||||
export const M31 = 7;
|
||||
export const M32 = 11;
|
||||
export const M33 = 15;
|
||||
|
||||
export class Matrix4 {
|
||||
temp: Float32Array = new Float32Array(16);
|
||||
values: Float32Array = new Float32Array(16);
|
||||
|
||||
constructor () {
|
||||
let v = this.values;
|
||||
v[M00] = 1;
|
||||
v[M11] = 1;
|
||||
v[M22] = 1;
|
||||
v[M33] = 1;
|
||||
}
|
||||
|
||||
set (values: ArrayLike<number>): Matrix4 {
|
||||
this.values.set(values);
|
||||
return this;
|
||||
}
|
||||
|
||||
transpose (): Matrix4 {
|
||||
let t = this.temp;
|
||||
let v = this.values;
|
||||
t[M00] = v[M00];
|
||||
t[M01] = v[M10];
|
||||
t[M02] = v[M20];
|
||||
t[M03] = v[M30];
|
||||
t[M10] = v[M01];
|
||||
t[M11] = v[M11];
|
||||
t[M12] = v[M21];
|
||||
t[M13] = v[M31];
|
||||
t[M20] = v[M02];
|
||||
t[M21] = v[M12];
|
||||
t[M22] = v[M22];
|
||||
t[M23] = v[M32];
|
||||
t[M30] = v[M03];
|
||||
t[M31] = v[M13];
|
||||
t[M32] = v[M23];
|
||||
t[M33] = v[M33];
|
||||
return this.set(t);
|
||||
}
|
||||
|
||||
identity (): Matrix4 {
|
||||
let v = this.values;
|
||||
v[M00] = 1;
|
||||
v[M01] = 0;
|
||||
v[M02] = 0;
|
||||
v[M03] = 0;
|
||||
v[M10] = 0;
|
||||
v[M11] = 1;
|
||||
v[M12] = 0;
|
||||
v[M13] = 0;
|
||||
v[M20] = 0;
|
||||
v[M21] = 0;
|
||||
v[M22] = 1;
|
||||
v[M23] = 0;
|
||||
v[M30] = 0;
|
||||
v[M31] = 0;
|
||||
v[M32] = 0;
|
||||
v[M33] = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
invert (): Matrix4 {
|
||||
let v = this.values;
|
||||
let t = this.temp;
|
||||
let l_det = v[M30] * v[M21] * v[M12] * v[M03] - v[M20] * v[M31] * v[M12] * v[M03] - v[M30] * v[M11] * v[M22] * v[M03]
|
||||
+ v[M10] * v[M31] * v[M22] * v[M03] + v[M20] * v[M11] * v[M32] * v[M03] - v[M10] * v[M21] * v[M32] * v[M03]
|
||||
- v[M30] * v[M21] * v[M02] * v[M13] + v[M20] * v[M31] * v[M02] * v[M13] + v[M30] * v[M01] * v[M22] * v[M13]
|
||||
- v[M00] * v[M31] * v[M22] * v[M13] - v[M20] * v[M01] * v[M32] * v[M13] + v[M00] * v[M21] * v[M32] * v[M13]
|
||||
+ v[M30] * v[M11] * v[M02] * v[M23] - v[M10] * v[M31] * v[M02] * v[M23] - v[M30] * v[M01] * v[M12] * v[M23]
|
||||
+ v[M00] * v[M31] * v[M12] * v[M23] + v[M10] * v[M01] * v[M32] * v[M23] - v[M00] * v[M11] * v[M32] * v[M23]
|
||||
- v[M20] * v[M11] * v[M02] * v[M33] + v[M10] * v[M21] * v[M02] * v[M33] + v[M20] * v[M01] * v[M12] * v[M33]
|
||||
- v[M00] * v[M21] * v[M12] * v[M33] - v[M10] * v[M01] * v[M22] * v[M33] + v[M00] * v[M11] * v[M22] * v[M33];
|
||||
if (l_det == 0) throw new Error("non-invertible matrix");
|
||||
let inv_det = 1.0 / l_det;
|
||||
t[M00] = v[M12] * v[M23] * v[M31] - v[M13] * v[M22] * v[M31] + v[M13] * v[M21] * v[M32]
|
||||
- v[M11] * v[M23] * v[M32] - v[M12] * v[M21] * v[M33] + v[M11] * v[M22] * v[M33];
|
||||
t[M01] = v[M03] * v[M22] * v[M31] - v[M02] * v[M23] * v[M31] - v[M03] * v[M21] * v[M32]
|
||||
+ v[M01] * v[M23] * v[M32] + v[M02] * v[M21] * v[M33] - v[M01] * v[M22] * v[M33];
|
||||
t[M02] = v[M02] * v[M13] * v[M31] - v[M03] * v[M12] * v[M31] + v[M03] * v[M11] * v[M32]
|
||||
- v[M01] * v[M13] * v[M32] - v[M02] * v[M11] * v[M33] + v[M01] * v[M12] * v[M33];
|
||||
t[M03] = v[M03] * v[M12] * v[M21] - v[M02] * v[M13] * v[M21] - v[M03] * v[M11] * v[M22]
|
||||
+ v[M01] * v[M13] * v[M22] + v[M02] * v[M11] * v[M23] - v[M01] * v[M12] * v[M23];
|
||||
t[M10] = v[M13] * v[M22] * v[M30] - v[M12] * v[M23] * v[M30] - v[M13] * v[M20] * v[M32]
|
||||
+ v[M10] * v[M23] * v[M32] + v[M12] * v[M20] * v[M33] - v[M10] * v[M22] * v[M33];
|
||||
t[M11] = v[M02] * v[M23] * v[M30] - v[M03] * v[M22] * v[M30] + v[M03] * v[M20] * v[M32]
|
||||
- v[M00] * v[M23] * v[M32] - v[M02] * v[M20] * v[M33] + v[M00] * v[M22] * v[M33];
|
||||
t[M12] = v[M03] * v[M12] * v[M30] - v[M02] * v[M13] * v[M30] - v[M03] * v[M10] * v[M32]
|
||||
+ v[M00] * v[M13] * v[M32] + v[M02] * v[M10] * v[M33] - v[M00] * v[M12] * v[M33];
|
||||
t[M13] = v[M02] * v[M13] * v[M20] - v[M03] * v[M12] * v[M20] + v[M03] * v[M10] * v[M22]
|
||||
- v[M00] * v[M13] * v[M22] - v[M02] * v[M10] * v[M23] + v[M00] * v[M12] * v[M23];
|
||||
t[M20] = v[M11] * v[M23] * v[M30] - v[M13] * v[M21] * v[M30] + v[M13] * v[M20] * v[M31]
|
||||
- v[M10] * v[M23] * v[M31] - v[M11] * v[M20] * v[M33] + v[M10] * v[M21] * v[M33];
|
||||
t[M21] = v[M03] * v[M21] * v[M30] - v[M01] * v[M23] * v[M30] - v[M03] * v[M20] * v[M31]
|
||||
+ v[M00] * v[M23] * v[M31] + v[M01] * v[M20] * v[M33] - v[M00] * v[M21] * v[M33];
|
||||
t[M22] = v[M01] * v[M13] * v[M30] - v[M03] * v[M11] * v[M30] + v[M03] * v[M10] * v[M31]
|
||||
- v[M00] * v[M13] * v[M31] - v[M01] * v[M10] * v[M33] + v[M00] * v[M11] * v[M33];
|
||||
t[M23] = v[M03] * v[M11] * v[M20] - v[M01] * v[M13] * v[M20] - v[M03] * v[M10] * v[M21]
|
||||
+ v[M00] * v[M13] * v[M21] + v[M01] * v[M10] * v[M23] - v[M00] * v[M11] * v[M23];
|
||||
t[M30] = v[M12] * v[M21] * v[M30] - v[M11] * v[M22] * v[M30] - v[M12] * v[M20] * v[M31]
|
||||
+ v[M10] * v[M22] * v[M31] + v[M11] * v[M20] * v[M32] - v[M10] * v[M21] * v[M32];
|
||||
t[M31] = v[M01] * v[M22] * v[M30] - v[M02] * v[M21] * v[M30] + v[M02] * v[M20] * v[M31]
|
||||
- v[M00] * v[M22] * v[M31] - v[M01] * v[M20] * v[M32] + v[M00] * v[M21] * v[M32];
|
||||
t[M32] = v[M02] * v[M11] * v[M30] - v[M01] * v[M12] * v[M30] - v[M02] * v[M10] * v[M31]
|
||||
+ v[M00] * v[M12] * v[M31] + v[M01] * v[M10] * v[M32] - v[M00] * v[M11] * v[M32];
|
||||
t[M33] = v[M01] * v[M12] * v[M20] - v[M02] * v[M11] * v[M20] + v[M02] * v[M10] * v[M21]
|
||||
- v[M00] * v[M12] * v[M21] - v[M01] * v[M10] * v[M22] + v[M00] * v[M11] * v[M22];
|
||||
v[M00] = t[M00] * inv_det;
|
||||
v[M01] = t[M01] * inv_det;
|
||||
v[M02] = t[M02] * inv_det;
|
||||
v[M03] = t[M03] * inv_det;
|
||||
v[M10] = t[M10] * inv_det;
|
||||
v[M11] = t[M11] * inv_det;
|
||||
v[M12] = t[M12] * inv_det;
|
||||
v[M13] = t[M13] * inv_det;
|
||||
v[M20] = t[M20] * inv_det;
|
||||
v[M21] = t[M21] * inv_det;
|
||||
v[M22] = t[M22] * inv_det;
|
||||
v[M23] = t[M23] * inv_det;
|
||||
v[M30] = t[M30] * inv_det;
|
||||
v[M31] = t[M31] * inv_det;
|
||||
v[M32] = t[M32] * inv_det;
|
||||
v[M33] = t[M33] * inv_det;
|
||||
return this;
|
||||
}
|
||||
|
||||
determinant (): number {
|
||||
let v = this.values;
|
||||
return v[M30] * v[M21] * v[M12] * v[M03] - v[M20] * v[M31] * v[M12] * v[M03] - v[M30] * v[M11] * v[M22] * v[M03]
|
||||
+ v[M10] * v[M31] * v[M22] * v[M03] + v[M20] * v[M11] * v[M32] * v[M03] - v[M10] * v[M21] * v[M32] * v[M03]
|
||||
- v[M30] * v[M21] * v[M02] * v[M13] + v[M20] * v[M31] * v[M02] * v[M13] + v[M30] * v[M01] * v[M22] * v[M13]
|
||||
- v[M00] * v[M31] * v[M22] * v[M13] - v[M20] * v[M01] * v[M32] * v[M13] + v[M00] * v[M21] * v[M32] * v[M13]
|
||||
+ v[M30] * v[M11] * v[M02] * v[M23] - v[M10] * v[M31] * v[M02] * v[M23] - v[M30] * v[M01] * v[M12] * v[M23]
|
||||
+ v[M00] * v[M31] * v[M12] * v[M23] + v[M10] * v[M01] * v[M32] * v[M23] - v[M00] * v[M11] * v[M32] * v[M23]
|
||||
- v[M20] * v[M11] * v[M02] * v[M33] + v[M10] * v[M21] * v[M02] * v[M33] + v[M20] * v[M01] * v[M12] * v[M33]
|
||||
- v[M00] * v[M21] * v[M12] * v[M33] - v[M10] * v[M01] * v[M22] * v[M33] + v[M00] * v[M11] * v[M22] * v[M33];
|
||||
}
|
||||
|
||||
translate (x: number, y: number, z: number): Matrix4 {
|
||||
let v = this.values;
|
||||
v[M03] += x;
|
||||
v[M13] += y;
|
||||
v[M23] += z;
|
||||
return this;
|
||||
}
|
||||
|
||||
copy (): Matrix4 {
|
||||
return new Matrix4().set(this.values);
|
||||
}
|
||||
|
||||
projection (near: number, far: number, fovy: number, aspectRatio: number): Matrix4 {
|
||||
this.identity();
|
||||
let l_fd = (1.0 / Math.tan((fovy * (Math.PI / 180)) / 2.0));
|
||||
let l_a1 = (far + near) / (near - far);
|
||||
let l_a2 = (2 * far * near) / (near - far);
|
||||
let v = this.values;
|
||||
v[M00] = l_fd / aspectRatio;
|
||||
v[M10] = 0;
|
||||
v[M20] = 0;
|
||||
v[M30] = 0;
|
||||
v[M01] = 0;
|
||||
v[M11] = l_fd;
|
||||
v[M21] = 0;
|
||||
v[M31] = 0;
|
||||
v[M02] = 0;
|
||||
v[M12] = 0;
|
||||
v[M22] = l_a1;
|
||||
v[M32] = -1;
|
||||
v[M03] = 0;
|
||||
v[M13] = 0;
|
||||
v[M23] = l_a2;
|
||||
v[M33] = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
ortho2d (x: number, y: number, width: number, height: number): Matrix4 {
|
||||
return this.ortho(x, x + width, y, y + height, 0, 1);
|
||||
}
|
||||
|
||||
ortho (left: number, right: number, bottom: number, top: number, near: number, far: number): Matrix4 {
|
||||
this.identity();
|
||||
let x_orth = 2 / (right - left);
|
||||
let y_orth = 2 / (top - bottom);
|
||||
let z_orth = -2 / (far - near);
|
||||
|
||||
let tx = -(right + left) / (right - left);
|
||||
let ty = -(top + bottom) / (top - bottom);
|
||||
let tz = -(far + near) / (far - near);
|
||||
|
||||
let v = this.values;
|
||||
v[M00] = x_orth;
|
||||
v[M10] = 0;
|
||||
v[M20] = 0;
|
||||
v[M30] = 0;
|
||||
v[M01] = 0;
|
||||
v[M11] = y_orth;
|
||||
v[M21] = 0;
|
||||
v[M31] = 0;
|
||||
v[M02] = 0;
|
||||
v[M12] = 0;
|
||||
v[M22] = z_orth;
|
||||
v[M32] = 0;
|
||||
v[M03] = tx;
|
||||
v[M13] = ty;
|
||||
v[M23] = tz;
|
||||
v[M33] = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
multiply (matrix: Matrix4): Matrix4 {
|
||||
let t = this.temp;
|
||||
let v = this.values;
|
||||
let m = matrix.values;
|
||||
t[M00] = v[M00] * m[M00] + v[M01] * m[M10] + v[M02] * m[M20] + v[M03] * m[M30];
|
||||
t[M01] = v[M00] * m[M01] + v[M01] * m[M11] + v[M02] * m[M21] + v[M03] * m[M31];
|
||||
t[M02] = v[M00] * m[M02] + v[M01] * m[M12] + v[M02] * m[M22] + v[M03] * m[M32];
|
||||
t[M03] = v[M00] * m[M03] + v[M01] * m[M13] + v[M02] * m[M23] + v[M03] * m[M33];
|
||||
t[M10] = v[M10] * m[M00] + v[M11] * m[M10] + v[M12] * m[M20] + v[M13] * m[M30];
|
||||
t[M11] = v[M10] * m[M01] + v[M11] * m[M11] + v[M12] * m[M21] + v[M13] * m[M31];
|
||||
t[M12] = v[M10] * m[M02] + v[M11] * m[M12] + v[M12] * m[M22] + v[M13] * m[M32];
|
||||
t[M13] = v[M10] * m[M03] + v[M11] * m[M13] + v[M12] * m[M23] + v[M13] * m[M33];
|
||||
t[M20] = v[M20] * m[M00] + v[M21] * m[M10] + v[M22] * m[M20] + v[M23] * m[M30];
|
||||
t[M21] = v[M20] * m[M01] + v[M21] * m[M11] + v[M22] * m[M21] + v[M23] * m[M31];
|
||||
t[M22] = v[M20] * m[M02] + v[M21] * m[M12] + v[M22] * m[M22] + v[M23] * m[M32];
|
||||
t[M23] = v[M20] * m[M03] + v[M21] * m[M13] + v[M22] * m[M23] + v[M23] * m[M33];
|
||||
t[M30] = v[M30] * m[M00] + v[M31] * m[M10] + v[M32] * m[M20] + v[M33] * m[M30];
|
||||
t[M31] = v[M30] * m[M01] + v[M31] * m[M11] + v[M32] * m[M21] + v[M33] * m[M31];
|
||||
t[M32] = v[M30] * m[M02] + v[M31] * m[M12] + v[M32] * m[M22] + v[M33] * m[M32];
|
||||
t[M33] = v[M30] * m[M03] + v[M31] * m[M13] + v[M32] * m[M23] + v[M33] * m[M33];
|
||||
return this.set(this.temp);
|
||||
}
|
||||
|
||||
multiplyLeft (matrix: Matrix4): Matrix4 {
|
||||
let t = this.temp;
|
||||
let v = this.values;
|
||||
let m = matrix.values;
|
||||
t[M00] = m[M00] * v[M00] + m[M01] * v[M10] + m[M02] * v[M20] + m[M03] * v[M30];
|
||||
t[M01] = m[M00] * v[M01] + m[M01] * v[M11] + m[M02] * v[M21] + m[M03] * v[M31];
|
||||
t[M02] = m[M00] * v[M02] + m[M01] * v[M12] + m[M02] * v[M22] + m[M03] * v[M32];
|
||||
t[M03] = m[M00] * v[M03] + m[M01] * v[M13] + m[M02] * v[M23] + m[M03] * v[M33];
|
||||
t[M10] = m[M10] * v[M00] + m[M11] * v[M10] + m[M12] * v[M20] + m[M13] * v[M30];
|
||||
t[M11] = m[M10] * v[M01] + m[M11] * v[M11] + m[M12] * v[M21] + m[M13] * v[M31];
|
||||
t[M12] = m[M10] * v[M02] + m[M11] * v[M12] + m[M12] * v[M22] + m[M13] * v[M32];
|
||||
t[M13] = m[M10] * v[M03] + m[M11] * v[M13] + m[M12] * v[M23] + m[M13] * v[M33];
|
||||
t[M20] = m[M20] * v[M00] + m[M21] * v[M10] + m[M22] * v[M20] + m[M23] * v[M30];
|
||||
t[M21] = m[M20] * v[M01] + m[M21] * v[M11] + m[M22] * v[M21] + m[M23] * v[M31];
|
||||
t[M22] = m[M20] * v[M02] + m[M21] * v[M12] + m[M22] * v[M22] + m[M23] * v[M32];
|
||||
t[M23] = m[M20] * v[M03] + m[M21] * v[M13] + m[M22] * v[M23] + m[M23] * v[M33];
|
||||
t[M30] = m[M30] * v[M00] + m[M31] * v[M10] + m[M32] * v[M20] + m[M33] * v[M30];
|
||||
t[M31] = m[M30] * v[M01] + m[M31] * v[M11] + m[M32] * v[M21] + m[M33] * v[M31];
|
||||
t[M32] = m[M30] * v[M02] + m[M31] * v[M12] + m[M32] * v[M22] + m[M33] * v[M32];
|
||||
t[M33] = m[M30] * v[M03] + m[M31] * v[M13] + m[M32] * v[M23] + m[M33] * v[M33];
|
||||
return this.set(this.temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
184
spine-ts/webgl/src/Mesh.ts
Normal file
184
spine-ts/webgl/src/Mesh.ts
Normal file
@ -0,0 +1,184 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class Mesh implements Disposable {
|
||||
private _gl: WebGLRenderingContext;
|
||||
private _vertices:Float32Array;
|
||||
private _verticesBuffer: WebGLBuffer;
|
||||
private _verticesLength = 0;
|
||||
private _dirtyVertices = false;
|
||||
private _indices:Uint16Array;
|
||||
private _indicesBuffer: WebGLBuffer;
|
||||
private _indicesLength = 0;
|
||||
private _dirtyIndices = false;
|
||||
private _elementsPerVertex = 0;
|
||||
|
||||
attributes (): VertexAttribute[] { return this._attributes; }
|
||||
|
||||
maxVertices (): number { return this._vertices.length / this._elementsPerVertex; }
|
||||
numVertices (): number { return this._verticesLength / this._elementsPerVertex; }
|
||||
setVerticesLength (length: number) {
|
||||
this._dirtyVertices = true;
|
||||
this._verticesLength = length;
|
||||
}
|
||||
vertices (): Float32Array { return this._vertices; }
|
||||
|
||||
maxIndices (): number { return this._indices.length; }
|
||||
numIndices (): number { return this._indicesLength; }
|
||||
setIndicesLength (length: number) {
|
||||
this._dirtyIndices = true;
|
||||
this._indicesLength = length;
|
||||
}
|
||||
indices (): Uint16Array { return this._indices };
|
||||
|
||||
constructor (gl: WebGLRenderingContext, private _attributes: VertexAttribute[], maxVertices: number, maxIndices: number) {
|
||||
this._gl = gl;
|
||||
this._elementsPerVertex = 0;
|
||||
for (let i = 0; i < _attributes.length; i++) {
|
||||
this._elementsPerVertex += _attributes[i].numElements;
|
||||
}
|
||||
this._vertices = new Float32Array(maxVertices * this._elementsPerVertex);
|
||||
this._indices = new Uint16Array(maxIndices);
|
||||
}
|
||||
|
||||
setVertices (vertices: Array<number>) {
|
||||
this._dirtyVertices = true;
|
||||
if (vertices.length > this._vertices.length) throw Error("Mesh can't store more than " + this.maxVertices() + " vertices");
|
||||
this._vertices.set(vertices, 0);
|
||||
this._verticesLength = vertices.length;
|
||||
}
|
||||
|
||||
setIndices (indices: Array<number>) {
|
||||
this._dirtyIndices = true;
|
||||
if (indices.length > this._indices.length) throw Error("Mesh can't store more than " + this.maxIndices() + " indices");
|
||||
this._indices.set(indices, 0);
|
||||
this._indicesLength = indices.length;
|
||||
}
|
||||
|
||||
draw (shader: Shader, primitiveType: number) {
|
||||
this.drawWithOffset(shader, primitiveType, 0, this._indicesLength > 0? this._indicesLength: this._verticesLength);
|
||||
}
|
||||
|
||||
drawWithOffset (shader: Shader, primitiveType: number, offset: number, count: number) {
|
||||
let gl = this._gl;
|
||||
if (this._dirtyVertices || this._dirtyIndices) this.update();
|
||||
this.bind(shader);
|
||||
if (this._indicesLength > 0) gl.drawElements(primitiveType, count, gl.UNSIGNED_SHORT, offset * 2);
|
||||
else gl.drawArrays(primitiveType, offset, count);
|
||||
this.unbind(shader);
|
||||
}
|
||||
|
||||
bind (shader: Shader) {
|
||||
let gl = this._gl;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < this._attributes.length; i++) {
|
||||
let attrib = this._attributes[i];
|
||||
let location = shader.getAttributeLocation(attrib.name);
|
||||
gl.enableVertexAttribArray(location);
|
||||
gl.vertexAttribPointer(location, attrib.numElements, gl.FLOAT, false, this._elementsPerVertex * 4, offset * 4);
|
||||
offset += attrib.numElements;
|
||||
}
|
||||
if (this._indicesLength > 0) gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indicesBuffer);
|
||||
}
|
||||
|
||||
unbind (shader: Shader) {
|
||||
let gl = this._gl;
|
||||
for (let i = 0; i < this._attributes.length; i++) {
|
||||
let attrib = this._attributes[i];
|
||||
let location = shader.getAttributeLocation(attrib.name);
|
||||
gl.disableVertexAttribArray(location);
|
||||
}
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
if (this._indicesLength > 0) gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
||||
}
|
||||
|
||||
private update () {
|
||||
let gl = this._gl;
|
||||
if (this._dirtyVertices) {
|
||||
if (!this._verticesBuffer) {
|
||||
this._verticesBuffer = gl.createBuffer();
|
||||
}
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, this._vertices.subarray(0, this._verticesLength), gl.STATIC_DRAW);
|
||||
this._dirtyVertices = false;
|
||||
}
|
||||
|
||||
if (this._dirtyIndices) {
|
||||
if (!this._indicesBuffer) {
|
||||
this._indicesBuffer = gl.createBuffer();
|
||||
}
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indicesBuffer);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices.subarray(0, this._indicesLength), gl.STATIC_DRAW);
|
||||
this._dirtyIndices = false;
|
||||
}
|
||||
}
|
||||
|
||||
dispose () {
|
||||
let gl = this._gl;
|
||||
gl.deleteBuffer(this._verticesBuffer);
|
||||
gl.deleteBuffer(this._indicesBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
export class VertexAttribute {
|
||||
constructor (public name: string, public type: VertexAttributeType, public numElements: number) { }
|
||||
}
|
||||
|
||||
export class Position2Attribute extends VertexAttribute {
|
||||
constructor () {
|
||||
super(Shader.POSITION, VertexAttributeType.Float, 2);
|
||||
}
|
||||
}
|
||||
|
||||
export class Position3Attribute extends VertexAttribute {
|
||||
constructor () {
|
||||
super(Shader.POSITION, VertexAttributeType.Float, 3);
|
||||
}
|
||||
}
|
||||
|
||||
export class TexCoordAttribute extends VertexAttribute {
|
||||
constructor (unit: number = 0) {
|
||||
super(Shader.TEXCOORDS + (unit == 0? "": unit), VertexAttributeType.Float, 2);
|
||||
}
|
||||
}
|
||||
|
||||
export class ColorAttribute extends VertexAttribute {
|
||||
constructor () {
|
||||
super(Shader.COLOR, VertexAttributeType.Float, 4);
|
||||
}
|
||||
}
|
||||
|
||||
export enum VertexAttributeType {
|
||||
Float
|
||||
}
|
||||
}
|
||||
121
spine-ts/webgl/src/PolygonBatcher.ts
Normal file
121
spine-ts/webgl/src/PolygonBatcher.ts
Normal file
@ -0,0 +1,121 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class PolygonBatcher {
|
||||
private _gl: WebGLRenderingContext;
|
||||
private _drawCalls: number;
|
||||
private _drawing = false;
|
||||
private _mesh: Mesh;
|
||||
private _shader: Shader = null;
|
||||
private _lastTexture: Texture = null;
|
||||
private _verticesLength = 0;
|
||||
private _indicesLength = 0;
|
||||
private _srcBlend: number = WebGLRenderingContext.SRC_ALPHA;
|
||||
private _dstBlend: number = WebGLRenderingContext.ONE_MINUS_SRC_ALPHA;
|
||||
|
||||
constructor (gl: WebGLRenderingContext, maxVertices: number = 10920) {
|
||||
if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
|
||||
this._gl = gl;
|
||||
this._mesh = new Mesh(gl, [new Position2Attribute(), new ColorAttribute(), new TexCoordAttribute()], maxVertices, maxVertices * 3);
|
||||
}
|
||||
|
||||
begin (shader: Shader) {
|
||||
let gl = this._gl;
|
||||
if (this._drawing) throw new Error("PolygonBatch is already drawing. Call PolygonBatch.end() before calling PolygonBatch.begin()");
|
||||
this._drawCalls = 0;
|
||||
this._shader = shader;
|
||||
this._lastTexture = null;
|
||||
this._drawing = true;
|
||||
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(this._srcBlend, this._dstBlend);
|
||||
}
|
||||
|
||||
setBlendMode (srcBlend: number, dstBlend: number) {
|
||||
let gl = this._gl;
|
||||
this._srcBlend = srcBlend;
|
||||
this._dstBlend = dstBlend;
|
||||
if (this._drawing) {
|
||||
this.flush();
|
||||
gl.blendFunc(this._srcBlend, this._dstBlend);
|
||||
}
|
||||
}
|
||||
|
||||
draw (texture: Texture, vertices: ArrayLike<number>, indices: Array<number>) {
|
||||
if (texture != this._lastTexture) {
|
||||
this.flush();
|
||||
this._lastTexture = texture;
|
||||
texture.bind();
|
||||
} else if (this._verticesLength + vertices.length > this._mesh.vertices().length ||
|
||||
this._indicesLength + indices.length > this._mesh.indices().length) {
|
||||
this.flush();
|
||||
}
|
||||
|
||||
let indexStart = this._mesh.numVertices();
|
||||
this._mesh.vertices().set(vertices, this._verticesLength);
|
||||
this._verticesLength += vertices.length;
|
||||
this._mesh.setVerticesLength(this._verticesLength)
|
||||
|
||||
let indicesArray = this._mesh.indices();
|
||||
for (let i = this._indicesLength, j = 0; j < indices.length; i++, j++)
|
||||
indicesArray[i] = indices[j] + indexStart;
|
||||
this._indicesLength += indices.length;
|
||||
this._mesh.setIndicesLength(this._indicesLength);
|
||||
}
|
||||
|
||||
private flush () {
|
||||
let gl = this._gl;
|
||||
if (this._verticesLength == 0) return;
|
||||
|
||||
this._mesh.draw(this._shader, gl.TRIANGLES);
|
||||
|
||||
this._verticesLength = 0;
|
||||
this._indicesLength = 0;
|
||||
this._mesh.setVerticesLength(0);
|
||||
this._mesh.setIndicesLength(0);
|
||||
this._drawCalls++;
|
||||
}
|
||||
|
||||
end () {
|
||||
let gl = this._gl;
|
||||
if (!this._drawing) throw new Error("PolygonBatch is not drawing. Call PolygonBatch.begin() before calling PolygonBatch.end()");
|
||||
if (this._verticesLength > 0 || this._indicesLength > 0) this.flush();
|
||||
this._shader = null;
|
||||
this._lastTexture = null;
|
||||
this._drawing = false;
|
||||
|
||||
gl.disable(gl.BLEND);
|
||||
}
|
||||
|
||||
drawCalls () { return this._drawCalls; }
|
||||
}
|
||||
}
|
||||
240
spine-ts/webgl/src/Shader.ts
Normal file
240
spine-ts/webgl/src/Shader.ts
Normal file
@ -0,0 +1,240 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class Shader implements Disposable {
|
||||
public static MVP_MATRIX = "u_projTrans";
|
||||
public static POSITION = "a_position";
|
||||
public static COLOR = "a_color";
|
||||
public static TEXCOORDS = "a_texCoords";
|
||||
public static SAMPLER = "u_texture";
|
||||
|
||||
private _gl: WebGLRenderingContext;
|
||||
private _vs: WebGLShader = null;
|
||||
private _fs: WebGLShader = null;
|
||||
private _program: WebGLProgram = null;
|
||||
private _tmp2x2: Float32Array = new Float32Array(2 * 2);
|
||||
private _tmp3x3: Float32Array = new Float32Array(3 * 3);
|
||||
private _tmp4x4: Float32Array = new Float32Array(4 * 4);
|
||||
|
||||
public program () { return this._program; }
|
||||
public vertexShader () { return this._vertexShader; }
|
||||
public fragmentShader () { return this._fragmentShader; }
|
||||
|
||||
constructor (gl: WebGLRenderingContext, private _vertexShader: string, private _fragmentShader: string) {
|
||||
this._gl = gl;
|
||||
this.compile();
|
||||
}
|
||||
|
||||
private compile () {
|
||||
let gl = this._gl;
|
||||
try {
|
||||
this._vs = this.compileShader(gl.VERTEX_SHADER, this._vertexShader);
|
||||
this._fs = this.compileShader(gl.FRAGMENT_SHADER, this._fragmentShader);
|
||||
this._program = this.compileProgram(this._vs, this._fs);
|
||||
} catch (e) {
|
||||
this.dispose();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private compileShader (type: number, source: string) {
|
||||
let gl = this._gl;
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let error = "Couldn't compile shader: " + gl.getShaderInfoLog(shader);
|
||||
gl.deleteShader(shader);
|
||||
throw new Error(error);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
private compileProgram (vs: WebGLShader, fs: WebGLShader) {
|
||||
let gl = this._gl;
|
||||
let program = gl.createProgram();
|
||||
gl.attachShader(program, vs);
|
||||
gl.attachShader(program, fs);
|
||||
gl.linkProgram(program);
|
||||
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
||||
let error = "Couldn't compile shader program: " + gl.getProgramInfoLog(program);
|
||||
gl.deleteProgram(program);
|
||||
throw new Error(error);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
public bind () {
|
||||
this._gl.useProgram(this._program);
|
||||
}
|
||||
|
||||
public unbind () {
|
||||
this._gl.useProgram(null);
|
||||
}
|
||||
|
||||
public setUniformi (uniform: string, value: number) {
|
||||
this._gl.uniform1i(this.getUniformLocation(uniform), value);
|
||||
}
|
||||
|
||||
public setUniformf (uniform: string, value: number) {
|
||||
this._gl.uniform1f(this.getUniformLocation(uniform), value);
|
||||
}
|
||||
|
||||
public setUniform2f (uniform: string, value: number, value2: number) {
|
||||
this._gl.uniform2f(this.getUniformLocation(uniform), value, value2);
|
||||
}
|
||||
|
||||
public setUniform3f (uniform: string, value: number, value2: number, value3: number) {
|
||||
this._gl.uniform3f(this.getUniformLocation(uniform), value, value2, value3);
|
||||
}
|
||||
|
||||
public setUniform4f (uniform: string, value: number, value2: number, value3: number, value4: number) {
|
||||
this._gl.uniform4f(this.getUniformLocation(uniform), value, value2, value3, value4);
|
||||
}
|
||||
|
||||
public setUniform2x2f (uniform: string, value: ArrayLike<number>) {
|
||||
let gl = this._gl;
|
||||
this._tmp2x2.set(value);
|
||||
gl.uniformMatrix2fv(this.getUniformLocation(uniform), false, this._tmp2x2);
|
||||
}
|
||||
|
||||
public setUniform3x3f (uniform: string, value: ArrayLike<number>) {
|
||||
let gl = this._gl;
|
||||
this._tmp3x3.set(value);
|
||||
gl.uniformMatrix3fv(this.getUniformLocation(uniform), false, this._tmp3x3);
|
||||
}
|
||||
|
||||
public setUniform4x4f (uniform: string, value: ArrayLike<number>) {
|
||||
let gl = this._gl;
|
||||
this._tmp4x4.set(value);
|
||||
gl.uniformMatrix4fv(this.getUniformLocation(uniform), false, this._tmp4x4);
|
||||
}
|
||||
|
||||
public getUniformLocation (uniform: string): WebGLUniformLocation {
|
||||
let gl = this._gl;
|
||||
let location = gl.getUniformLocation(this._program, uniform);
|
||||
if (!location) throw new Error(`Couldn't find location for uniform ${uniform}`);
|
||||
return location;
|
||||
}
|
||||
|
||||
public getAttributeLocation (attribute: string): number {
|
||||
let gl = this._gl;
|
||||
let location = gl.getAttribLocation(this._program, attribute);
|
||||
if (location == -1) throw new Error(`Couldn't find location for attribute ${attribute}`);
|
||||
return location;
|
||||
}
|
||||
|
||||
public dispose () {
|
||||
let gl = this._gl;
|
||||
if (this._vs) {
|
||||
gl.deleteShader(this._vs);
|
||||
this._vs = null;
|
||||
}
|
||||
|
||||
if (this._fs) {
|
||||
gl.deleteShader(this._fs);
|
||||
this._fs = null;
|
||||
}
|
||||
|
||||
if (this._program) {
|
||||
gl.deleteProgram(this._program);
|
||||
this._program = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static newColoredTextured (gl: WebGLRenderingContext): Shader {
|
||||
let vs = `
|
||||
attribute vec4 ${Shader.POSITION};
|
||||
attribute vec4 ${Shader.COLOR};
|
||||
attribute vec2 ${Shader.TEXCOORDS};
|
||||
uniform mat4 ${Shader.MVP_MATRIX};
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_texCoords;
|
||||
|
||||
void main () {
|
||||
v_color = ${Shader.COLOR};
|
||||
v_texCoords = ${Shader.TEXCOORDS};
|
||||
gl_Position = ${Shader.MVP_MATRIX} * ${Shader.POSITION};
|
||||
}
|
||||
`;
|
||||
|
||||
let fs = `
|
||||
#ifdef GL_ES
|
||||
#define LOWP lowp
|
||||
precision mediump float;
|
||||
#else
|
||||
#define LOWP
|
||||
#endif
|
||||
varying LOWP vec4 v_color;
|
||||
varying vec2 v_texCoords;
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
void main () {
|
||||
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
|
||||
}
|
||||
`;
|
||||
|
||||
return new Shader(gl, vs, fs);
|
||||
}
|
||||
|
||||
public static newColored (gl: WebGLRenderingContext): Shader {
|
||||
let vs = `
|
||||
attribute vec4 ${Shader.POSITION};
|
||||
attribute vec4 ${Shader.COLOR};
|
||||
uniform mat4 ${Shader.MVP_MATRIX};
|
||||
varying vec4 v_color;
|
||||
|
||||
void main () {
|
||||
v_color = ${Shader.COLOR};
|
||||
gl_Position = ${Shader.MVP_MATRIX} * ${Shader.POSITION};
|
||||
}
|
||||
`;
|
||||
|
||||
let fs = `
|
||||
#ifdef GL_ES
|
||||
#define LOWP lowp
|
||||
precision mediump float;
|
||||
#else
|
||||
#define LOWP
|
||||
#endif
|
||||
varying LOWP vec4 v_color;
|
||||
|
||||
void main () {
|
||||
gl_FragColor = v_color;
|
||||
}
|
||||
`;
|
||||
|
||||
return new Shader(gl, vs, fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
spine-ts/webgl/src/SkeletonRenderer.ts
Normal file
78
spine-ts/webgl/src/SkeletonRenderer.ts
Normal file
@ -0,0 +1,78 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class SkeletonRenderer {
|
||||
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
||||
|
||||
premultipliedAlpha = false;
|
||||
private _gl: WebGLRenderingContext;
|
||||
|
||||
constructor (gl: WebGLRenderingContext) {
|
||||
this._gl = gl;
|
||||
}
|
||||
|
||||
draw (batcher: PolygonBatcher, skeleton: Skeleton) {
|
||||
let premultipliedAlpha = this.premultipliedAlpha;
|
||||
let blendMode: BlendMode = null;
|
||||
|
||||
let vertices: ArrayLike<number> = null;
|
||||
let triangles: Array<number> = null;
|
||||
let drawOrder = skeleton.drawOrder;
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
let slot = drawOrder[i];
|
||||
let attachment = slot.getAttachment();
|
||||
let texture: Texture = null;
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
let region = <RegionAttachment>attachment;
|
||||
vertices = region.updateWorldVertices(slot, premultipliedAlpha);
|
||||
triangles = SkeletonRenderer.QUAD_TRIANGLES;
|
||||
texture = (<TextureAtlasRegion>region.region.renderObject).texture;
|
||||
|
||||
} else if (attachment instanceof MeshAttachment) {
|
||||
let mesh = <MeshAttachment>attachment;
|
||||
vertices = mesh.updateWorldVertices(slot, premultipliedAlpha);
|
||||
triangles = mesh.triangles;
|
||||
texture = (<TextureAtlasRegion>mesh.region.renderObject).texture;
|
||||
}
|
||||
|
||||
if (texture != null) {
|
||||
let slotBlendMode = slot.data.blendMode;
|
||||
if (slotBlendMode != blendMode) {
|
||||
blendMode = slotBlendMode;
|
||||
batcher.setBlendMode(getSourceGLBlendMode(this._gl, blendMode, premultipliedAlpha), getDestGLBlendMode(this._gl, blendMode));
|
||||
}
|
||||
batcher.draw(texture, vertices, triangles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
131
spine-ts/webgl/src/Texture.ts
Normal file
131
spine-ts/webgl/src/Texture.ts
Normal file
@ -0,0 +1,131 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class Texture implements Disposable {
|
||||
private _gl: WebGLRenderingContext;
|
||||
private _texture: WebGLTexture;
|
||||
private _image: HTMLImageElement;
|
||||
private _boundUnit = 0;
|
||||
|
||||
constructor (gl: WebGLRenderingContext, image: HTMLImageElement, useMipMaps: boolean = false) {
|
||||
this._gl = gl;
|
||||
this._texture = gl.createTexture();
|
||||
this._image = image;
|
||||
this.update(useMipMaps);
|
||||
}
|
||||
|
||||
getImage (): HTMLImageElement {
|
||||
return this._image;
|
||||
}
|
||||
|
||||
setFilters (minFilter: TextureFilter, magFilter: TextureFilter) {
|
||||
let gl = this._gl;
|
||||
this.bind();
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter);
|
||||
}
|
||||
|
||||
setWraps (uWrap: TextureWrap, vWrap: TextureWrap) {
|
||||
let gl = this._gl;
|
||||
this.bind();
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, uWrap);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, vWrap);
|
||||
}
|
||||
|
||||
update (useMipMaps: boolean) {
|
||||
let gl = this._gl;
|
||||
this.bind();
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._image);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, useMipMaps? gl.LINEAR_MIPMAP_LINEAR: gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
if (useMipMaps) gl.generateMipmap(gl.TEXTURE_2D);
|
||||
}
|
||||
|
||||
bind (unit: number = 0) {
|
||||
let gl = this._gl;
|
||||
this._boundUnit = unit;
|
||||
gl.activeTexture(gl.TEXTURE0 + unit);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._texture);
|
||||
}
|
||||
|
||||
unbind () {
|
||||
let gl = this._gl;
|
||||
gl.activeTexture(gl.TEXTURE0 + this._boundUnit);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
}
|
||||
|
||||
dispose () {
|
||||
let gl = this._gl;
|
||||
gl.deleteTexture(this._texture);
|
||||
}
|
||||
|
||||
public static filterFromString (text: string): TextureFilter {
|
||||
switch (text.toLowerCase()) {
|
||||
case "nearest": return TextureFilter.Nearest;
|
||||
case "linear": return TextureFilter.Linear;
|
||||
case "mipmap": return TextureFilter.MipMap;
|
||||
case "mipmapnearestnearest": return TextureFilter.MipMapNearestNearest;
|
||||
case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest;
|
||||
case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear;
|
||||
case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear;
|
||||
default: throw new Error(`Unknown texture filter ${text}`);
|
||||
}
|
||||
}
|
||||
|
||||
public static wrapFromString (text: string): TextureWrap {
|
||||
switch (text.toLowerCase()) {
|
||||
case "mirroredtepeat": return TextureWrap.MirroredRepeat;
|
||||
case "clamptoedge": return TextureWrap.ClampToEdge;
|
||||
case "repeat": return TextureWrap.Repeat;
|
||||
default: throw new Error(`Unknown texture wrap ${text}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum TextureFilter {
|
||||
Nearest = WebGLRenderingContext.NEAREST,
|
||||
Linear = WebGLRenderingContext.LINEAR,
|
||||
MipMap = WebGLRenderingContext.LINEAR_MIPMAP_LINEAR,
|
||||
MipMapNearestNearest = WebGLRenderingContext.NEAREST_MIPMAP_NEAREST,
|
||||
MipMapLinearNearest = WebGLRenderingContext.LINEAR_MIPMAP_NEAREST,
|
||||
MipMapNearestLinear = WebGLRenderingContext.NEAREST_MIPMAP_LINEAR,
|
||||
MipMapLinearLinear = WebGLRenderingContext.LINEAR_MIPMAP_LINEAR
|
||||
}
|
||||
|
||||
export enum TextureWrap {
|
||||
MirroredRepeat = WebGLRenderingContext.MIRRORED_REPEAT,
|
||||
ClampToEdge = WebGLRenderingContext.CLAMP_TO_EDGE,
|
||||
Repeat = WebGLRenderingContext.REPEAT
|
||||
}
|
||||
}
|
||||
213
spine-ts/webgl/src/TextureAtlas.ts
Normal file
213
spine-ts/webgl/src/TextureAtlas.ts
Normal file
@ -0,0 +1,213 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class TextureAtlas implements Disposable {
|
||||
pages = new Array<TextureAtlasPage>();
|
||||
regions = new Array<TextureAtlasRegion>();
|
||||
|
||||
constructor (atlasText: string, textureLoader: (path: string) => Texture) {
|
||||
this.load(atlasText, textureLoader);
|
||||
}
|
||||
|
||||
private load (atlasText: string, textureLoader: (path: string) => Texture) {
|
||||
if (textureLoader == null)
|
||||
throw new Error("textureLoader cannot be null.");
|
||||
|
||||
let reader = new TextureAtlasReader(atlasText);
|
||||
let tuple = new Array<string>(4);
|
||||
let page:TextureAtlasPage = null;
|
||||
while (true) {
|
||||
let line = reader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
line = line.trim();
|
||||
if (line.length == 0)
|
||||
page = null;
|
||||
else if (!page) {
|
||||
page = new TextureAtlasPage();
|
||||
page.name = line;
|
||||
|
||||
if (reader.readTuple(tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker.
|
||||
page.width = parseInt(tuple[0]);
|
||||
page.height = parseInt(tuple[1]);
|
||||
reader.readTuple(tuple);
|
||||
}
|
||||
// page.format = Format[tuple[0]]; we don't need format in WebGL
|
||||
|
||||
reader.readTuple(tuple);
|
||||
page.minFilter = Texture.filterFromString(tuple[0]);
|
||||
page.magFilter = Texture.filterFromString(tuple[1]);
|
||||
|
||||
let direction= reader.readValue();
|
||||
page.uWrap = TextureWrap.ClampToEdge;
|
||||
page.vWrap = TextureWrap.ClampToEdge;
|
||||
if (direction == "x")
|
||||
page.uWrap = TextureWrap.Repeat;
|
||||
else if (direction == "y")
|
||||
page.vWrap = TextureWrap.Repeat;
|
||||
else if (direction == "xy")
|
||||
page.uWrap = page.vWrap = TextureWrap.Repeat;
|
||||
|
||||
page.texture = textureLoader(line);
|
||||
page.texture.setFilters(page.minFilter, page.magFilter);
|
||||
page.texture.setWraps(page.uWrap, page.vWrap);
|
||||
page.width = page.texture.getImage().width;
|
||||
page.height = page.texture.getImage().height;
|
||||
this.pages.push(page);
|
||||
} else {
|
||||
let region:TextureAtlasRegion = new TextureAtlasRegion();
|
||||
region.name = line;
|
||||
region.page = page;
|
||||
|
||||
region.rotate = reader.readValue() == "true";
|
||||
|
||||
reader.readTuple(tuple);
|
||||
let x = parseInt(tuple[0]);
|
||||
let y = parseInt(tuple[1]);
|
||||
|
||||
reader.readTuple(tuple);
|
||||
let width = parseInt(tuple[0]);
|
||||
let height = parseInt(tuple[1]);
|
||||
|
||||
region.u = x / page.width;
|
||||
region.v = y / page.height;
|
||||
if (region.rotate) {
|
||||
region.u2 = (x + height) / page.width;
|
||||
region.v2 = (y + width) / page.height;
|
||||
} else {
|
||||
region.u2 = (x + width) / page.width;
|
||||
region.v2 = (y + height) / page.height;
|
||||
}
|
||||
region.x = x;
|
||||
region.y = y;
|
||||
region.width = Math.abs(width);
|
||||
region.height = Math.abs(height);
|
||||
|
||||
if (reader.readTuple(tuple) == 4) { // split is optional
|
||||
// region.splits = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
|
||||
if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits
|
||||
//region.pads = Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
|
||||
reader.readTuple(tuple);
|
||||
}
|
||||
}
|
||||
|
||||
region.originalWidth = parseInt(tuple[0]);
|
||||
region.originalHeight = parseInt(tuple[1]);
|
||||
|
||||
reader.readTuple(tuple);
|
||||
region.offsetX = parseInt(tuple[0]);
|
||||
region.offsetY = parseInt(tuple[1]);
|
||||
|
||||
region.index = parseInt(reader.readValue());
|
||||
|
||||
region.texture = page.texture;
|
||||
this.regions.push(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findRegion (name: string): TextureAtlasRegion {
|
||||
for (let i = 0; i < this.regions.length; i++) {
|
||||
if (this.regions[i].name == name) {
|
||||
return this.regions[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
dispose () {
|
||||
for (let i = 0; i < this.pages.length; i++) {
|
||||
this.pages[i].texture.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TextureAtlasReader {
|
||||
lines: Array<string>;
|
||||
index: number = 0;
|
||||
|
||||
constructor (text: string) {
|
||||
this.lines = text.split(/\r\n|\r|\n/);
|
||||
}
|
||||
|
||||
readLine (): string {
|
||||
if (this.index >= this.lines.length)
|
||||
return null;
|
||||
return this.lines[this.index++];
|
||||
}
|
||||
|
||||
readValue (): string {
|
||||
let line = this.readLine();
|
||||
let colon= line.indexOf(":");
|
||||
if (colon == -1)
|
||||
throw new Error("Invalid line: " + line);
|
||||
return line.substring(colon + 1).trim();
|
||||
}
|
||||
|
||||
readTuple (tuple: Array<string>): number {
|
||||
let line = this.readLine();
|
||||
let colon = line.indexOf(":");
|
||||
if (colon == -1)
|
||||
throw new Error("Invalid line: " + line);
|
||||
let i = 0, lastMatch = colon + 1;
|
||||
for (; i < 3; i++) {
|
||||
let comma = line.indexOf(",", lastMatch);
|
||||
if (comma == -1) break;
|
||||
tuple[i] = line.substr(lastMatch, comma - lastMatch).trim();
|
||||
lastMatch = comma + 1;
|
||||
}
|
||||
tuple[i] = line.substring(lastMatch).trim();
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
export class TextureAtlasPage {
|
||||
name: string;
|
||||
minFilter: TextureFilter;
|
||||
magFilter: TextureFilter;
|
||||
uWrap: TextureWrap;
|
||||
vWrap: TextureWrap;
|
||||
texture: Texture;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export class TextureAtlasRegion extends TextureRegion {
|
||||
page: TextureAtlasPage;
|
||||
name: string;
|
||||
x: number;
|
||||
y: number;
|
||||
index: number;
|
||||
rotate: boolean;
|
||||
texture: Texture;
|
||||
}
|
||||
}
|
||||
71
spine-ts/webgl/src/TextureAtlasAttachmentLoader.ts
Normal file
71
spine-ts/webgl/src/TextureAtlasAttachmentLoader.ts
Normal file
@ -0,0 +1,71 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class TextureAtlasAttachmentLoader implements AttachmentLoader {
|
||||
atlas: TextureAtlas;
|
||||
|
||||
constructor (atlas: TextureAtlas) {
|
||||
this.atlas = atlas;
|
||||
}
|
||||
|
||||
/** @return May be null to not load an attachment. */
|
||||
newRegionAttachment (skin: Skin, name: string, path: string): RegionAttachment {
|
||||
let region = this.atlas.findRegion(path);
|
||||
region.renderObject = region;
|
||||
if (region == null) throw new Error("Region not found in atlas: " + path + " (region attachment: " + name + ")");
|
||||
let attachment = new RegionAttachment(name);
|
||||
attachment.setRegion(region);
|
||||
attachment.region = region;
|
||||
return attachment;
|
||||
}
|
||||
|
||||
/** @return May be null to not load an attachment. */
|
||||
newMeshAttachment (skin: Skin, name: string, path: string) : MeshAttachment {
|
||||
let region = this.atlas.findRegion(path);
|
||||
region.renderObject = region;
|
||||
if (region == null) throw new Error("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
|
||||
let attachment = new MeshAttachment(name);
|
||||
attachment.region = region;
|
||||
return attachment;
|
||||
}
|
||||
|
||||
/** @return May be null to not load an attachment. */
|
||||
newBoundingBoxAttachment (skin: Skin, name: string) : BoundingBoxAttachment {
|
||||
return new BoundingBoxAttachment(name);
|
||||
}
|
||||
|
||||
/** @return May be null to not load an attachment */
|
||||
newPathAttachment (skin: Skin, name: string): PathAttachment {
|
||||
return new PathAttachment(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
110
spine-ts/webgl/src/Vector3.ts
Normal file
110
spine-ts/webgl/src/Vector3.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export class Vector3 {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
|
||||
set (x: number, y: number, z: number): Vector3 {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
add (v: Vector3): Vector3 {
|
||||
this.x += v.x;
|
||||
this.y += v.y;
|
||||
this.z += v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
sub (v: Vector3): Vector3 {
|
||||
this.x -= v.x;
|
||||
this.y -= v.y;
|
||||
this.z -= v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
scale (s: number): Vector3 {
|
||||
this.x *= s;
|
||||
this.y *= s;
|
||||
this.z *= s;
|
||||
return this;
|
||||
}
|
||||
|
||||
normalize (): Vector3 {
|
||||
let len = this.length();
|
||||
if (len == 0) return this;
|
||||
len = 1 / len;
|
||||
this.x *= len;
|
||||
this.y *= len;
|
||||
this.z *= len;
|
||||
return this;
|
||||
}
|
||||
|
||||
cross (v: Vector3): Vector3 {
|
||||
return this.set(this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x)
|
||||
}
|
||||
|
||||
multiply (matrix: Matrix4): Vector3 {
|
||||
let l_mat = matrix.values;
|
||||
return this.set(this.x * l_mat[M00] + this.y * l_mat[M01] + this.z * l_mat[M02] + l_mat[M03],
|
||||
this.x * l_mat[M10] + this.y * l_mat[M11] + this.z * l_mat[M12] + l_mat[M13],
|
||||
this.x * l_mat[M20] + this.y * l_mat[M21] + this.z * l_mat[M22] + l_mat[M23]);
|
||||
}
|
||||
|
||||
project (matrix: Matrix4): Vector3 {
|
||||
let l_mat = matrix.values;
|
||||
let l_w = 1 / (this.x * l_mat[M30] + this.y * l_mat[M31] + this.z * l_mat[M32] + l_mat[M33]);
|
||||
return this.set((this.x * l_mat[M00] + this.y * l_mat[M01] + this.z * l_mat[M02] + l_mat[M03]) * l_w,
|
||||
(this.x * l_mat[M10] + this.y * l_mat[M11] + this.z * l_mat[M12] + l_mat[M13]) * l_w,
|
||||
(this.x * l_mat[M20] + this.y * l_mat[M21] + this.z * l_mat[M22] + l_mat[M23]) * l_w);
|
||||
}
|
||||
|
||||
dot (v: Vector3): number {
|
||||
return this.x * v.x + this.y * v.y + this.z * v.z;
|
||||
}
|
||||
|
||||
length (): number {
|
||||
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
||||
}
|
||||
|
||||
distance (v: Vector3): number {
|
||||
let a = v.x - this.x;
|
||||
let b = v.y - this.y;
|
||||
let c = v.z - this.z;
|
||||
return Math.sqrt(a * a + b * b + c * c);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
spine-ts/webgl/src/WebGL.ts
Normal file
52
spine-ts/webgl/src/WebGL.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.webgl {
|
||||
export function getSourceGLBlendMode (gl: WebGLRenderingContext, blendMode: BlendMode, premultipliedAlpha: boolean = false) {
|
||||
switch(blendMode) {
|
||||
case BlendMode.Normal: return premultipliedAlpha? gl.ONE : gl.SRC_ALPHA;
|
||||
case BlendMode.Additive: return premultipliedAlpha? gl.ONE : gl.SRC_ALPHA;
|
||||
case BlendMode.Multiply: return gl.DST_COLOR;
|
||||
case BlendMode.Screen: return gl.ONE;
|
||||
default: throw new Error("Unknown blend mode: " + blendMode);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDestGLBlendMode (gl: WebGLRenderingContext, blendMode: BlendMode) {
|
||||
switch(blendMode) {
|
||||
case BlendMode.Normal: return gl.ONE_MINUS_SRC_ALPHA;
|
||||
case BlendMode.Additive: return gl.ONE;
|
||||
case BlendMode.Multiply: return gl.ONE_MINUS_SRC_ALPHA;
|
||||
case BlendMode.Screen: return gl.ONE_MINUS_SRC_ALPHA;
|
||||
default: throw new Error("Unknown blend mode: " + blendMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
279
spine-ts/widget/example/assets/raptor.atlas
Normal file
279
spine-ts/widget/example/assets/raptor.atlas
Normal file
@ -0,0 +1,279 @@
|
||||
|
||||
raptor.png
|
||||
size: 1024,1024
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
back_arm
|
||||
rotate: true
|
||||
xy: 140, 191
|
||||
size: 46, 29
|
||||
orig: 46, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_bracer
|
||||
rotate: true
|
||||
xy: 167, 317
|
||||
size: 39, 28
|
||||
orig: 39, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_hand
|
||||
rotate: false
|
||||
xy: 167, 358
|
||||
size: 36, 34
|
||||
orig: 36, 34
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_knee
|
||||
rotate: false
|
||||
xy: 299, 478
|
||||
size: 49, 67
|
||||
orig: 49, 67
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_thigh
|
||||
rotate: true
|
||||
xy: 167, 437
|
||||
size: 39, 24
|
||||
orig: 39, 24
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eyes_closed
|
||||
rotate: true
|
||||
xy: 2, 2
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eyes_open
|
||||
rotate: true
|
||||
xy: 49, 2
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eyes_surprised
|
||||
rotate: true
|
||||
xy: 96, 2
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_arm
|
||||
rotate: false
|
||||
xy: 419, 544
|
||||
size: 48, 30
|
||||
orig: 48, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_bracer
|
||||
rotate: false
|
||||
xy: 880, 695
|
||||
size: 41, 29
|
||||
orig: 41, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_hand
|
||||
rotate: true
|
||||
xy: 167, 394
|
||||
size: 41, 38
|
||||
orig: 41, 38
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_open_hand
|
||||
rotate: false
|
||||
xy: 880, 726
|
||||
size: 43, 44
|
||||
orig: 43, 44
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_thigh
|
||||
rotate: false
|
||||
xy: 360, 545
|
||||
size: 57, 29
|
||||
orig: 57, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun
|
||||
rotate: false
|
||||
xy: 785, 774
|
||||
size: 107, 103
|
||||
orig: 107, 103
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun_nohand
|
||||
rotate: false
|
||||
xy: 614, 703
|
||||
size: 105, 102
|
||||
orig: 105, 102
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
head
|
||||
rotate: false
|
||||
xy: 2, 137
|
||||
size: 136, 149
|
||||
orig: 136, 149
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
lower_leg
|
||||
rotate: true
|
||||
xy: 780, 699
|
||||
size: 73, 98
|
||||
orig: 73, 98
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_grind
|
||||
rotate: false
|
||||
xy: 469, 544
|
||||
size: 47, 30
|
||||
orig: 47, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_oooo
|
||||
rotate: true
|
||||
xy: 894, 772
|
||||
size: 105, 30
|
||||
orig: 105, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_smile
|
||||
rotate: true
|
||||
xy: 140, 239
|
||||
size: 47, 30
|
||||
orig: 47, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
neck
|
||||
rotate: true
|
||||
xy: 538, 577
|
||||
size: 18, 21
|
||||
orig: 18, 21
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_arm_back
|
||||
rotate: false
|
||||
xy: 940, 936
|
||||
size: 82, 86
|
||||
orig: 82, 86
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_body
|
||||
rotate: false
|
||||
xy: 2, 737
|
||||
size: 610, 285
|
||||
orig: 610, 285
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_front_arm
|
||||
rotate: true
|
||||
xy: 195, 464
|
||||
size: 81, 102
|
||||
orig: 81, 102
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_front_leg
|
||||
rotate: false
|
||||
xy: 2, 478
|
||||
size: 191, 257
|
||||
orig: 191, 257
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_hindleg_back
|
||||
rotate: false
|
||||
xy: 614, 807
|
||||
size: 169, 215
|
||||
orig: 169, 215
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_horn
|
||||
rotate: false
|
||||
xy: 360, 655
|
||||
size: 182, 80
|
||||
orig: 182, 80
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_horn_back
|
||||
rotate: false
|
||||
xy: 360, 576
|
||||
size: 176, 77
|
||||
orig: 176, 77
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_jaw
|
||||
rotate: false
|
||||
xy: 785, 879
|
||||
size: 153, 143
|
||||
orig: 153, 143
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_noshadow
|
||||
rotate: false
|
||||
xy: 2, 288
|
||||
size: 163, 188
|
||||
orig: 163, 188
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_strap_front
|
||||
rotate: false
|
||||
xy: 721, 710
|
||||
size: 57, 95
|
||||
orig: 57, 95
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_strap_rear
|
||||
rotate: true
|
||||
xy: 940, 880
|
||||
size: 54, 74
|
||||
orig: 54, 74
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_w_shadow
|
||||
rotate: false
|
||||
xy: 195, 547
|
||||
size: 163, 188
|
||||
orig: 163, 188
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_tongue
|
||||
rotate: true
|
||||
xy: 544, 649
|
||||
size: 86, 64
|
||||
orig: 86, 64
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_back
|
||||
rotate: true
|
||||
xy: 140, 145
|
||||
size: 44, 35
|
||||
orig: 44, 35
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_front
|
||||
rotate: false
|
||||
xy: 538, 597
|
||||
size: 45, 50
|
||||
orig: 45, 50
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_strap
|
||||
rotate: false
|
||||
xy: 350, 497
|
||||
size: 49, 46
|
||||
orig: 49, 46
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
torso
|
||||
rotate: true
|
||||
xy: 610, 647
|
||||
size: 54, 91
|
||||
orig: 54, 91
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
visor
|
||||
rotate: false
|
||||
xy: 2, 51
|
||||
size: 131, 84
|
||||
orig: 131, 84
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
2621
spine-ts/widget/example/assets/raptor.json
Normal file
2621
spine-ts/widget/example/assets/raptor.json
Normal file
File diff suppressed because one or more lines are too long
BIN
spine-ts/widget/example/assets/raptor.png
Normal file
BIN
spine-ts/widget/example/assets/raptor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 495 KiB |
30
spine-ts/widget/example/index.html
Normal file
30
spine-ts/widget/example/index.html
Normal file
@ -0,0 +1,30 @@
|
||||
<html>
|
||||
<script src="../../build/spine-widget.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<body>
|
||||
<center>
|
||||
<div id="spine-widget" style="margin-bottom: 20px"></div>
|
||||
<div style="margin-bottom: 20px" class="spine-widget" data-json="assets/raptor.json" data-atlas="assets/raptor.atlas" data-animation="Jump" data-scale="0.25" data-x="40"></div>
|
||||
<div style="margin-bottom: 20px" class="spine-widget" data-json="assets/raptor.json" data-atlas="assets/raptor.atlas" data-animation="walk" data-scale="0.2" data-width="320" data-height="240" data-background-color="#cc0000"></div>
|
||||
</center>
|
||||
</body>
|
||||
<script>
|
||||
new spine.SpineWidget("spine-widget", {
|
||||
json: "assets/raptor.json",
|
||||
atlas: "assets/raptor.atlas",
|
||||
animation: "walk",
|
||||
scale: 0.4,
|
||||
backgroundColor: "#000000",
|
||||
success: function (widget) {
|
||||
var animIndex = 0;
|
||||
widget.canvas.onclick = function () {
|
||||
animIndex++;
|
||||
let animations = widget.skeleton.data.animations;
|
||||
if (animIndex >= animations.length) animIndex = 0;
|
||||
widget.setAnimation(animations[animIndex].name);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
261
spine-ts/widget/src/Widget.ts
Normal file
261
spine-ts/widget/src/Widget.ts
Normal file
@ -0,0 +1,261 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.5
|
||||
*
|
||||
* Copyright (c) 2013-2016, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||
* non-transferable license to use, install, execute, and perform the Spine
|
||||
* Runtimes software and derivative works solely for personal or internal
|
||||
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||
* or other intellectual property or proprietary rights notices on or in the
|
||||
* Software, including any copy thereof. Redistributions in binary or source
|
||||
* form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine {
|
||||
export class SpineWidget {
|
||||
skeleton: Skeleton;
|
||||
state: AnimationState;
|
||||
gl: WebGLRenderingContext;
|
||||
canvas: HTMLCanvasElement;
|
||||
|
||||
private _config: SpineWidgetConfig;
|
||||
private _assetManager: spine.webgl.AssetManager;
|
||||
private _shader: spine.webgl.Shader;
|
||||
private _batcher: spine.webgl.PolygonBatcher;
|
||||
private _mvp = new spine.webgl.Matrix4();
|
||||
private _skeletonRenderer: spine.webgl.SkeletonRenderer;
|
||||
private _paused = false;
|
||||
private _lastFrameTime = Date.now() / 1000.0;
|
||||
private _backgroundColor = new Color();
|
||||
private _loaded = false;
|
||||
|
||||
constructor (element: Element | string, config: SpineWidgetConfig) {
|
||||
if (!element) throw new Error("Please provide a DOM element, e.g. document.getElementById('myelement')");
|
||||
if (!config) throw new Error("Please provide a configuration, specifying at least the json file, atlas file and animation name");
|
||||
|
||||
let elementId = element as string;
|
||||
if (typeof(element) === "string") element = document.getElementById(element as string);
|
||||
if (element == null) throw new Error(`Element ${elementId} does not exist`);
|
||||
|
||||
this.validateConfig(config);
|
||||
|
||||
let canvas = this.canvas = document.createElement("canvas");
|
||||
(<Element> element).appendChild(canvas);
|
||||
canvas.width = config.width;
|
||||
canvas.height = config.height;
|
||||
var webglConfig = { alpha: false };
|
||||
let gl = this.gl = <WebGLRenderingContext> (canvas.getContext("webgl", webglConfig) || canvas.getContext("experimental-webgl", webglConfig));
|
||||
|
||||
this._shader = spine.webgl.Shader.newColoredTextured(gl);
|
||||
this._batcher = new spine.webgl.PolygonBatcher(gl);
|
||||
this._mvp.ortho2d(0, 0, 639, 479);
|
||||
this._skeletonRenderer = new spine.webgl.SkeletonRenderer(gl);
|
||||
|
||||
let assets = this._assetManager = new spine.webgl.AssetManager(gl);
|
||||
assets.loadText(config.atlas);
|
||||
assets.loadText(config.json);
|
||||
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
|
||||
requestAnimationFrame(() => { this.load(); });
|
||||
}
|
||||
|
||||
private validateConfig (config: SpineWidgetConfig) {
|
||||
if (!config.atlas) throw new Error("Please specify config.atlas");
|
||||
if (!config.json) throw new Error("Please specify config.json");
|
||||
if (!config.animation) throw new Error("Please specify config.animationName");
|
||||
|
||||
if (!config.scale) config.scale = 1.0;
|
||||
if (!config.skin) config.skin = "default";
|
||||
if (config.loop === undefined) config.loop = true;
|
||||
if (!config.y) config.y = 20;
|
||||
if (!config.width) config.width = 640;
|
||||
if (!config.height) config.height = 480;
|
||||
if (!config.x) config.x = config.width / 2;
|
||||
if (!config.backgroundColor) config.backgroundColor = "#555555";
|
||||
if (!config.imagesPath) {
|
||||
let index = config.atlas.lastIndexOf("/");
|
||||
if (index != -1) {
|
||||
config.imagesPath = config.atlas.substr(0, index) + "/";
|
||||
} else {
|
||||
config.imagesPath = "";
|
||||
}
|
||||
}
|
||||
if (!config.premultipliedAlpha === undefined) config.premultipliedAlpha = false;
|
||||
this._backgroundColor.setFromString(config.backgroundColor);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private load () {
|
||||
let assetManager = this._assetManager;
|
||||
let imagesPath = this._config.imagesPath;
|
||||
let config = this._config;
|
||||
if (assetManager.isLoadingComplete()) {
|
||||
if (assetManager.hasErrors()) {
|
||||
if (config.error) config.error(this, "Failed to load assets: " + JSON.stringify(assetManager.errors));
|
||||
else throw new Error("Failed to load assets: " + JSON.stringify(assetManager.errors));
|
||||
}
|
||||
|
||||
let atlas = new spine.webgl.TextureAtlas(this._assetManager.get(this._config.atlas) as string, (path) => {
|
||||
return assetManager.get(imagesPath + path) as spine.webgl.Texture;
|
||||
});
|
||||
|
||||
let atlasLoader = new spine.webgl.TextureAtlasAttachmentLoader(atlas);
|
||||
var skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||
|
||||
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
||||
skeletonJson.scale = config.scale;
|
||||
var skeletonData = skeletonJson.readSkeletonData(assetManager.get(config.json) as string);
|
||||
var skeleton = this.skeleton = new spine.Skeleton(skeletonData);
|
||||
skeleton.x = config.x;
|
||||
skeleton.y = config.y;
|
||||
skeleton.setSkinByName(config.skin);
|
||||
|
||||
var animationState = this.state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
|
||||
animationState.setAnimation(0, config.animation, true);
|
||||
if (config.success) config.success(this);
|
||||
this._loaded = true;
|
||||
requestAnimationFrame(() => { this.render(); });
|
||||
} else
|
||||
requestAnimationFrame(() => { this.load(); });
|
||||
}
|
||||
|
||||
private render () {
|
||||
var now = Date.now() / 1000;
|
||||
var delta = now - this._lastFrameTime;
|
||||
if (delta > 0.1) delta = 0;
|
||||
this._lastFrameTime = now;
|
||||
|
||||
let gl = this.gl;
|
||||
let color = this._backgroundColor;
|
||||
gl.clearColor(color.r, color.g, color.b, color.a);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
// Apply the animation state based on the delta time.
|
||||
var state = this.state;
|
||||
var skeleton = this.skeleton;
|
||||
var premultipliedAlpha = this._config.premultipliedAlpha;
|
||||
state.update(delta);
|
||||
state.apply(skeleton);
|
||||
skeleton.updateWorldTransform();
|
||||
|
||||
// Bind the shader and set the texture and model-view-projection matrix.
|
||||
let shader = this._shader;
|
||||
shader.bind();
|
||||
shader.setUniformi(spine.webgl.Shader.SAMPLER, 0);
|
||||
shader.setUniform4x4f(spine.webgl.Shader.MVP_MATRIX, this._mvp.values);
|
||||
|
||||
// Start the batch and tell the SkeletonRenderer to render the active skeleton.
|
||||
let batcher = this._batcher;
|
||||
let skeletonRenderer = this._skeletonRenderer;
|
||||
batcher.begin(shader);
|
||||
skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
|
||||
skeletonRenderer.draw(batcher, skeleton);
|
||||
batcher.end();
|
||||
|
||||
shader.unbind();
|
||||
|
||||
if (!this._paused) requestAnimationFrame(() => { this.render(); });
|
||||
}
|
||||
|
||||
pause () {
|
||||
this._paused = true;
|
||||
}
|
||||
|
||||
play () {
|
||||
this._paused = false;
|
||||
requestAnimationFrame(() => { this.render(); });
|
||||
}
|
||||
|
||||
isPlaying () {
|
||||
return !this._paused;
|
||||
}
|
||||
|
||||
setAnimation (animationName: string) {
|
||||
if (!this._loaded) throw new Error("Widget isn't loaded yet");
|
||||
this.skeleton.setToSetupPose();
|
||||
this.state.setAnimation(0, animationName, this._config.loop);
|
||||
}
|
||||
|
||||
|
||||
static loadWidgets() {
|
||||
let widgets = document.getElementsByClassName("spine-widget");
|
||||
for (var i = 0; i < widgets.length; i++) {
|
||||
SpineWidget.loadWidget(widgets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static loadWidget(widget: Element) {
|
||||
let config = new SpineWidgetConfig();
|
||||
config.atlas = widget.getAttribute("data-atlas");
|
||||
config.json = widget.getAttribute("data-json");
|
||||
config.animation = widget.getAttribute("data-animation");
|
||||
if (widget.getAttribute("data-images-path")) config.imagesPath = widget.getAttribute("data-images-path");
|
||||
if (widget.getAttribute("data-skin")) config.skin = widget.getAttribute("data-skin");
|
||||
if (widget.getAttribute("data-loop")) config.loop = widget.getAttribute("data-loop") === "true";
|
||||
if (widget.getAttribute("data-scale")) config.scale = parseFloat(widget.getAttribute("data-scale"));
|
||||
if (widget.getAttribute("data-x")) config.x = parseFloat(widget.getAttribute("data-x"));
|
||||
if (widget.getAttribute("data-y")) config.x = parseFloat(widget.getAttribute("data-y"));
|
||||
if (widget.getAttribute("data-width")) config.width = parseInt(widget.getAttribute("data-width"));
|
||||
if (widget.getAttribute("data-height")) config.height = parseInt(widget.getAttribute("data-height"));
|
||||
if (widget.getAttribute("data-background-color")) config.backgroundColor = widget.getAttribute("data-background-color");
|
||||
if (widget.getAttribute("data-premultiplied-alpha")) config.premultipliedAlpha = widget.getAttribute("data-premultiplied-alpha") === "true";
|
||||
|
||||
new spine.SpineWidget(widget, config);
|
||||
}
|
||||
|
||||
static pageLoaded = false;
|
||||
private static ready () {
|
||||
if (SpineWidget.pageLoaded) return;
|
||||
SpineWidget.pageLoaded = true;
|
||||
SpineWidget.loadWidgets();
|
||||
}
|
||||
|
||||
static setupDOMListener() {
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener("DOMContentLoaded", SpineWidget.ready, false);
|
||||
window.addEventListener("load", SpineWidget.ready, false);
|
||||
} else {
|
||||
(<any>document).attachEvent("onreadystatechange", function readyStateChange() {
|
||||
if (document.readyState === "complete" ) SpineWidget.ready();
|
||||
});
|
||||
(<any>window).attachEvent("onload", SpineWidget.ready);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SpineWidgetConfig {
|
||||
json: string;
|
||||
atlas: string;
|
||||
animation: string;
|
||||
imagesPath: string;
|
||||
skin = "default";
|
||||
loop = true;
|
||||
scale = 1.0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
width = 640;
|
||||
height = 480;
|
||||
backgroundColor = "#555555";
|
||||
premultipliedAlpha = false;
|
||||
success: (widget: SpineWidget) => void;
|
||||
error: (widget: SpineWidget, msg: string) => void;
|
||||
}
|
||||
}
|
||||
spine.SpineWidget.setupDOMListener();
|
||||
Loading…
x
Reference in New Issue
Block a user