mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-16 03:51:37 +08:00
[ts] Fixed Canvas backend, updated README.md for Canvas backend
This commit is contained in:
parent
066412b67a
commit
5ad388fe0c
@ -5,6 +5,7 @@ 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. **Canvas**: `canvas/`, a self-contained Canvas 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.
|
||||
@ -18,7 +19,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f
|
||||
|
||||
spine-ts works with data exported from the latest Spine version.
|
||||
|
||||
spine-ts supports all Spine features.
|
||||
spine-ts WebGL & Widget backends supports all Spine features. The spine-ts Canvas backend does not support color tinting, mesh attachments or shearing. Mesh attachments are supported by setting `spine.canvas.SkeletonRenderer.useTriangleRendering` to true. Note that this method is slow and may lead to artifacts on some browsers.
|
||||
|
||||
spine-ts does not yet support loading the binary format.
|
||||
|
||||
@ -26,6 +27,7 @@ spine-ts does not yet support loading the binary format.
|
||||
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.
|
||||
3. To use the Canvas backend, include the `spine-canvas.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.
|
||||
@ -41,7 +43,7 @@ cd spine-ts
|
||||
python -m SimpleHTTPServer
|
||||
````
|
||||
|
||||
Then open `http://localhost:8000/webgl/example` or `http://localhost:8000/widget/example` in your browser.
|
||||
Then open `http://localhost:8000/webgl/example`, `http://localhost:8000/canvas/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
|
||||
@ -52,11 +54,12 @@ setup a development environment, follow these steps.
|
||||
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`
|
||||
* **Core**: `tsc -w -p tsconfig.core.json`, builds `core/src`, outputs `build/spine-core.js|d.ts|js.map`
|
||||
* **WebGL**: `tsc -w -p tsconfig.webgl.json`, builds `core/src` and `webgl/src`, outputs `build/spine-webgl.js|d.ts|js.map`
|
||||
* **WebGL**: `tsc -w -p tsconfig.canvas.json`, builds `core/src` and `canvas/src`, outputs `build/spine-canvas.js|d.ts|js.map`
|
||||
* **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.
|
||||
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.
|
||||
@ -66,4 +69,4 @@ cd spine-ts
|
||||
python -m SimpleHTTPServer
|
||||
```
|
||||
|
||||
Then navigate to `http://localhost:8000/webgl/example` or `http://localhost:8000/widget/example`
|
||||
Then navigate to `http://localhost:8000/webgl/example`, `http://localhost:8000/canvas/example` or `http://localhost:8000/widget/example`
|
||||
1625
spine-ts/build/spine-all.d.ts
vendored
1625
spine-ts/build/spine-all.d.ts
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1163
spine-ts/build/spine-core.d.ts
vendored
1163
spine-ts/build/spine-core.d.ts
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1382
spine-ts/build/spine-widget.d.ts
vendored
1382
spine-ts/build/spine-widget.d.ts
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,15 +1,17 @@
|
||||
{
|
||||
"skeleton": { "hash": "lDI/ipVg9yXAdln1ZwTDAuXPGng", "spine": "3.4.02", "width": 650.27, "height": 650.27, "images": "./images/" },
|
||||
"skeleton": { "hash": "IupPHodxMJRnAG/MhbCuGMvI6B0", "spine": "3.4.02", "width": 62.32, "height": 62.32, "images": "./images/" },
|
||||
"bones": [
|
||||
{ "name": "root" }
|
||||
{ "name": "root" },
|
||||
{ "name": "bone", "parent": "root", "y": 142.84 }
|
||||
],
|
||||
"slots": [
|
||||
{ "name": "badlogic", "bone": "root", "attachment": "badlogic" }
|
||||
{ "name": "badlogic2", "bone": "bone", "attachment": "badlogic" },
|
||||
{ "name": "badlogic", "bone": "root" }
|
||||
],
|
||||
"skins": {
|
||||
"default": {
|
||||
"badlogic": {
|
||||
"badlogic": { "rotation": 43.36, "width": 460, "height": 460 }
|
||||
"badlogic2": {
|
||||
"badlogic": { "x": 30.33, "y": -69.62, "scaleX": 0.1, "scaleY": 0.1, "rotation": 61.65, "width": 460, "height": 460 }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -23,6 +23,10 @@ function init () {
|
||||
context = canvas.getContext("2d");
|
||||
|
||||
skeletonRenderer = new spine.canvas.SkeletonRenderer(context);
|
||||
// enable debug rendering
|
||||
skeletonRenderer.useDebugRendering = true;
|
||||
// enable the triangle renderer, supports meshes, but may produce artifacts in some browsers
|
||||
skeletonRenderer.useTriangleRendering = true;
|
||||
|
||||
assetManager = new spine.AssetManager(function(image) {
|
||||
return new spine.canvas.CanvasTexture(image);
|
||||
@ -43,7 +47,7 @@ function init () {
|
||||
|
||||
function load () {
|
||||
if (assetManager.isLoadingComplete()) {
|
||||
var data = loadSkeleton("raptor", 0.3, "walk", 320, 20, "default");
|
||||
var data = loadSkeleton("spineboy", 0.7, "walk", 320, 460, "default");
|
||||
skeleton = data.skeleton;
|
||||
state = data.state;
|
||||
requestAnimationFrame(render);
|
||||
@ -73,6 +77,7 @@ function loadSkeleton (name, scale, initialAnimation, positionX, positionY, skin
|
||||
var skeleton = new spine.Skeleton(skeletonData);
|
||||
skeleton.x = positionX;
|
||||
skeleton.y = positionY;
|
||||
skeleton.flipY = true;
|
||||
skeleton.setSkinByName(skin);
|
||||
|
||||
// Create an AnimationState, and set the initial animation in looping mode.
|
||||
|
||||
@ -34,14 +34,66 @@ module spine.canvas {
|
||||
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
||||
|
||||
private _ctx: CanvasRenderingContext2D;
|
||||
|
||||
public useTriangleRendering = false;
|
||||
public useDebugRendering = false;
|
||||
|
||||
constructor (context: CanvasRenderingContext2D) {
|
||||
this._ctx = context;
|
||||
}
|
||||
|
||||
draw (skeleton: Skeleton) {
|
||||
let blendMode: BlendMode = null;
|
||||
draw (skeleton: Skeleton) {
|
||||
if (this.useTriangleRendering) this.drawTriangles(skeleton);
|
||||
else this.drawImages(skeleton);
|
||||
}
|
||||
|
||||
private drawImages (skeleton: Skeleton) {
|
||||
let ctx = this._ctx;
|
||||
let drawOrder = skeleton.drawOrder;
|
||||
|
||||
if (this.useDebugRendering) ctx.strokeStyle = "green";
|
||||
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
let slot = drawOrder[i];
|
||||
let attachment = slot.getAttachment();
|
||||
let region: TextureAtlasRegion = null;
|
||||
let image: HTMLImageElement = null;
|
||||
let vertices: ArrayLike<number> = null;
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
let regionAttachment = <RegionAttachment>attachment;
|
||||
vertices = regionAttachment.updateWorldVertices(slot, false);
|
||||
region = <TextureAtlasRegion>regionAttachment.region;
|
||||
image = (<CanvasTexture>(region).texture).getImage();
|
||||
|
||||
} else continue;
|
||||
|
||||
let att = <RegionAttachment>attachment;
|
||||
let bone = slot.bone;
|
||||
let x = vertices[0];
|
||||
let y = vertices[1];
|
||||
let rotation = (bone.getWorldRotationX() - att.rotation) * Math.PI / 180;
|
||||
let xx = vertices[24] - vertices[0];
|
||||
let xy = vertices[25] - vertices[1];
|
||||
let yx = vertices[8] - vertices[0];
|
||||
let yy = vertices[9] - vertices[1];
|
||||
let w = Math.sqrt(xx * xx + xy * xy), h = -Math.sqrt(yx * yx + yy * yy);
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(rotation);
|
||||
if (region.rotate) {
|
||||
ctx.rotate(Math.PI / 2);
|
||||
ctx.drawImage(image, region.x, region.y, region.height, region.width, 0, 0, h, -w);
|
||||
ctx.rotate(-Math.PI / 2);
|
||||
} else {
|
||||
ctx.drawImage(image, region.x, region.y, region.width, region.height, 0, 0, w, h);
|
||||
}
|
||||
if (this.useDebugRendering) ctx.strokeRect(0, 0, w, h);
|
||||
ctx.rotate(-rotation);
|
||||
ctx.translate(-x, -y);
|
||||
}
|
||||
}
|
||||
|
||||
private drawTriangles (skeleton: Skeleton) {
|
||||
let blendMode: BlendMode = null;
|
||||
|
||||
let vertices: ArrayLike<number> = null;
|
||||
let triangles: Array<number> = null;
|
||||
@ -64,7 +116,7 @@ module spine.canvas {
|
||||
vertices = mesh.updateWorldVertices(slot, false);
|
||||
triangles = mesh.triangles;
|
||||
texture = (<TextureAtlasRegion>mesh.region.renderObject).texture.getImage();
|
||||
} else continue;
|
||||
} else continue;
|
||||
|
||||
if (texture != null) {
|
||||
let slotBlendMode = slot.data.blendMode;
|
||||
@ -72,30 +124,34 @@ module spine.canvas {
|
||||
blendMode = slotBlendMode;
|
||||
}
|
||||
|
||||
this.drawTriangles(texture, vertices, triangles);
|
||||
let ctx = this._ctx;
|
||||
|
||||
// ctx.drawImage(texture, 0, 0);
|
||||
}
|
||||
for (var j = 0; j < triangles.length; j+=3) {
|
||||
let t1 = triangles[j] * 8, t2 = triangles[j+1] * 8, t3 = triangles[j+2] * 8;
|
||||
|
||||
let x0 = vertices[t1], y0 = vertices[t1 + 1], u0 = vertices[t1 + 6], v0 = vertices[t1 + 7];
|
||||
let x1 = vertices[t2], y1 = vertices[t2 + 1], u1 = vertices[t2 + 6], v1 = vertices[t2 + 7];
|
||||
let x2 = vertices[t3], y2 = vertices[t3 + 1], u2 = vertices[t3 + 6], v2 = vertices[t3 + 7];
|
||||
|
||||
this.drawTriangle(texture, x0, y0, u0, v0, x1, y1, u1, v1, x2, y2, u2, v2);
|
||||
|
||||
if (this.useDebugRendering) {
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x0, y0);
|
||||
ctx.lineTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.lineTo(x0, y0);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawTriangles(texture: HTMLImageElement, vertices: ArrayLike<number>, triangles: ArrayLike<number>) {
|
||||
let ctx = this._ctx;
|
||||
|
||||
for (var i = 0; i < triangles.length; i+=3) {
|
||||
let t1 = triangles[i] * 8, t2 = triangles[i+1] * 8, t3 = triangles[i+2] * 8;
|
||||
|
||||
let x0 = vertices[t1], y0 = vertices[t1 + 1], u0 = vertices[t1 + 6], v0 = vertices[t1 + 7];
|
||||
let x1 = vertices[t2], y1 = vertices[t2 + 1], u1 = vertices[t2 + 6], v1 = vertices[t2 + 7];
|
||||
let x2 = vertices[t3], y2 = vertices[t3 + 1], u2 = vertices[t3 + 6], v2 = vertices[t3 + 7];
|
||||
|
||||
this.drawTriangle(texture, x0, y0, u0, v0, x1, y1, u1, v1, x2, y2, u2, v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adapted from http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
|
||||
// Apache 2 licensed
|
||||
drawTriangle(img: HTMLImageElement, x0: number, y0: number, u0: number, v0: number,
|
||||
private drawTriangle(img: HTMLImageElement, x0: number, y0: number, u0: number, v0: number,
|
||||
x1: number, y1: number, u1: number, v1: number,
|
||||
x2: number, y2: number, u2: number, v2: number) {
|
||||
let ctx = this._ctx;
|
||||
@ -139,7 +195,7 @@ module spine.canvas {
|
||||
ctx.transform(a, b, c, d, e, f);
|
||||
ctx.clip();
|
||||
ctx.drawImage(img, 0, 0);
|
||||
ctx.restore();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,14 +114,12 @@ module spine {
|
||||
else throw new Error("Failed to load assets: " + JSON.stringify(assetManager.errors));
|
||||
}
|
||||
|
||||
let atlas = new spine.TextureAtlas(this._assetManager.get(this._config.atlas) as string, (path: string, minFilter: TextureFilter, magFilter: TextureFilter, uWrap: TextureWrap, vWrap: TextureWrap) => {
|
||||
let atlas = new spine.TextureAtlas(this._assetManager.get(this._config.atlas) as string, (path: string) => {
|
||||
let texture = assetManager.get(imagesPath + path) as spine.webgl.GLTexture;
|
||||
texture.setFilters(minFilter, magFilter);
|
||||
texture.setWraps(uWrap, vWrap);
|
||||
return texture;
|
||||
});
|
||||
|
||||
let atlasLoader = new spine.webgl.TextureAtlasAttachmentLoader(atlas);
|
||||
let atlasLoader = new spine.TextureAtlasAttachmentLoader(atlas);
|
||||
var skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||
|
||||
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user