feat: use custom player module
This commit is contained in:
1557
apps/module/spine-ts/canvas/example/assets/spineboy-ess.json
Executable file
1557
apps/module/spine-ts/canvas/example/assets/spineboy-ess.json
Executable file
File diff suppressed because it is too large
Load Diff
286
apps/module/spine-ts/canvas/example/assets/spineboy.atlas
Executable file
286
apps/module/spine-ts/canvas/example/assets/spineboy.atlas
Executable file
@@ -0,0 +1,286 @@
|
||||
|
||||
spineboy.png
|
||||
size: 1024,256
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
crosshair
|
||||
rotate: false
|
||||
xy: 813, 160
|
||||
size: 45, 45
|
||||
orig: 45, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eye-indifferent
|
||||
rotate: false
|
||||
xy: 569, 2
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eye-surprised
|
||||
rotate: true
|
||||
xy: 643, 7
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front-bracer
|
||||
rotate: false
|
||||
xy: 811, 51
|
||||
size: 29, 40
|
||||
orig: 29, 40
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front-fist-closed
|
||||
rotate: false
|
||||
xy: 807, 93
|
||||
size: 38, 41
|
||||
orig: 38, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front-fist-open
|
||||
rotate: false
|
||||
xy: 815, 210
|
||||
size: 43, 44
|
||||
orig: 43, 44
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front-foot
|
||||
rotate: true
|
||||
xy: 706, 64
|
||||
size: 63, 35
|
||||
orig: 63, 35
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front-shin
|
||||
rotate: false
|
||||
xy: 80, 11
|
||||
size: 41, 92
|
||||
orig: 41, 92
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front-thigh
|
||||
rotate: false
|
||||
xy: 754, 12
|
||||
size: 23, 56
|
||||
orig: 23, 56
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front-upper-arm
|
||||
rotate: false
|
||||
xy: 618, 5
|
||||
size: 23, 49
|
||||
orig: 23, 49
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goggles
|
||||
rotate: false
|
||||
xy: 214, 20
|
||||
size: 131, 83
|
||||
orig: 131, 83
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun
|
||||
rotate: true
|
||||
xy: 347, 14
|
||||
size: 105, 102
|
||||
orig: 105, 102
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
head
|
||||
rotate: false
|
||||
xy: 80, 105
|
||||
size: 136, 149
|
||||
orig: 136, 149
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
hoverboard-board
|
||||
rotate: true
|
||||
xy: 2, 8
|
||||
size: 246, 76
|
||||
orig: 246, 76
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
hoverboard-thruster
|
||||
rotate: false
|
||||
xy: 478, 2
|
||||
size: 30, 32
|
||||
orig: 30, 32
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
hoverglow-small
|
||||
rotate: true
|
||||
xy: 218, 117
|
||||
size: 137, 38
|
||||
orig: 137, 38
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth-grind
|
||||
rotate: true
|
||||
xy: 775, 80
|
||||
size: 47, 30
|
||||
orig: 47, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth-oooo
|
||||
rotate: true
|
||||
xy: 779, 31
|
||||
size: 47, 30
|
||||
orig: 47, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth-smile
|
||||
rotate: true
|
||||
xy: 783, 207
|
||||
size: 47, 30
|
||||
orig: 47, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
muzzle-glow
|
||||
rotate: false
|
||||
xy: 779, 4
|
||||
size: 25, 25
|
||||
orig: 25, 25
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
muzzle-ring
|
||||
rotate: false
|
||||
xy: 451, 14
|
||||
size: 25, 105
|
||||
orig: 25, 105
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
muzzle01
|
||||
rotate: true
|
||||
xy: 664, 60
|
||||
size: 67, 40
|
||||
orig: 67, 40
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
muzzle02
|
||||
rotate: true
|
||||
xy: 580, 56
|
||||
size: 68, 42
|
||||
orig: 68, 42
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
muzzle03
|
||||
rotate: true
|
||||
xy: 478, 36
|
||||
size: 83, 53
|
||||
orig: 83, 53
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
muzzle04
|
||||
rotate: true
|
||||
xy: 533, 49
|
||||
size: 75, 45
|
||||
orig: 75, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
muzzle05
|
||||
rotate: true
|
||||
xy: 624, 56
|
||||
size: 68, 38
|
||||
orig: 68, 38
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
neck
|
||||
rotate: false
|
||||
xy: 806, 8
|
||||
size: 18, 21
|
||||
orig: 18, 21
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
portal-bg
|
||||
rotate: false
|
||||
xy: 258, 121
|
||||
size: 133, 133
|
||||
orig: 133, 133
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
portal-flare1
|
||||
rotate: true
|
||||
xy: 690, 2
|
||||
size: 56, 30
|
||||
orig: 56, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
portal-flare2
|
||||
rotate: false
|
||||
xy: 510, 3
|
||||
size: 57, 31
|
||||
orig: 57, 31
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
portal-flare3
|
||||
rotate: true
|
||||
xy: 722, 4
|
||||
size: 58, 30
|
||||
orig: 58, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
portal-shade
|
||||
rotate: false
|
||||
xy: 393, 121
|
||||
size: 133, 133
|
||||
orig: 133, 133
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
portal-streaks1
|
||||
rotate: false
|
||||
xy: 528, 126
|
||||
size: 126, 128
|
||||
orig: 126, 128
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
portal-streaks2
|
||||
rotate: false
|
||||
xy: 656, 129
|
||||
size: 125, 125
|
||||
orig: 125, 125
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear-bracer
|
||||
rotate: false
|
||||
xy: 826, 13
|
||||
size: 28, 36
|
||||
orig: 28, 36
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear-foot
|
||||
rotate: true
|
||||
xy: 743, 70
|
||||
size: 57, 30
|
||||
orig: 57, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear-shin
|
||||
rotate: false
|
||||
xy: 174, 14
|
||||
size: 38, 89
|
||||
orig: 38, 89
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear-thigh
|
||||
rotate: false
|
||||
xy: 783, 158
|
||||
size: 28, 47
|
||||
orig: 28, 47
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear-upper-arm
|
||||
rotate: true
|
||||
xy: 783, 136
|
||||
size: 20, 44
|
||||
orig: 20, 44
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
torso
|
||||
rotate: false
|
||||
xy: 123, 13
|
||||
size: 49, 90
|
||||
orig: 49, 90
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
BIN
apps/module/spine-ts/canvas/example/assets/spineboy.png
Executable file
BIN
apps/module/spine-ts/canvas/example/assets/spineboy.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 261 KiB |
168
apps/module/spine-ts/canvas/example/index.html
Executable file
168
apps/module/spine-ts/canvas/example/index.html
Executable file
@@ -0,0 +1,168 @@
|
||||
<html>
|
||||
<script src="../../build/spine-canvas.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; }
|
||||
body, html { height: 100% }
|
||||
canvas { position: absolute; width: 100% ;height: 100%; }
|
||||
</style>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
var lastFrameTime = Date.now() / 1000;
|
||||
var canvas, context;
|
||||
var assetManager;
|
||||
var skeleton, state, bounds;
|
||||
var skeletonRenderer;
|
||||
|
||||
var skelName = "spineboy-ess";
|
||||
var animName = "walk";
|
||||
|
||||
function init () {
|
||||
canvas = document.getElementById("canvas");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
context = canvas.getContext("2d");
|
||||
|
||||
skeletonRenderer = new spine.canvas.SkeletonRenderer(context);
|
||||
// enable debug rendering
|
||||
skeletonRenderer.debugRendering = true;
|
||||
// enable the triangle renderer, supports meshes, but may produce artifacts in some browsers
|
||||
skeletonRenderer.triangleRendering = false;
|
||||
|
||||
assetManager = new spine.canvas.AssetManager();
|
||||
|
||||
assetManager.loadText("assets/" + skelName + ".json");
|
||||
assetManager.loadText("assets/" + skelName.replace("-pro", "").replace("-ess", "") + ".atlas");
|
||||
assetManager.loadTexture("assets/" + skelName.replace("-pro", "").replace("-ess", "") + ".png");
|
||||
|
||||
requestAnimationFrame(load);
|
||||
}
|
||||
|
||||
function load () {
|
||||
if (assetManager.isLoadingComplete()) {
|
||||
var data = loadSkeleton(skelName, animName, "default");
|
||||
skeleton = data.skeleton;
|
||||
state = data.state;
|
||||
bounds = data.bounds;
|
||||
requestAnimationFrame(render);
|
||||
} else {
|
||||
requestAnimationFrame(load);
|
||||
}
|
||||
}
|
||||
|
||||
function loadSkeleton (name, initialAnimation, 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.TextureAtlas(assetManager.get("assets/" + name.replace("-pro", "").replace("-ess", "") + ".atlas"), function(path) {
|
||||
return assetManager.get("assets/" + path);
|
||||
});
|
||||
|
||||
// Create a AtlasAttachmentLoader, which is specific to the WebGL backend.
|
||||
atlasLoader = new spine.AtlasAttachmentLoader(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.
|
||||
var skeletonData = skeletonJson.readSkeletonData(assetManager.get("assets/" + name + ".json"));
|
||||
var skeleton = new spine.Skeleton(skeletonData);
|
||||
skeleton.scaleY = -1;
|
||||
var bounds = calculateBounds(skeleton);
|
||||
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, bounds: bounds };
|
||||
}
|
||||
|
||||
function calculateBounds(skeleton) {
|
||||
var data = skeleton.data;
|
||||
skeleton.setToSetupPose();
|
||||
skeleton.updateWorldTransform();
|
||||
var offset = new spine.Vector2();
|
||||
var size = new spine.Vector2();
|
||||
skeleton.getBounds(offset, size, []);
|
||||
return { offset: offset, size: size };
|
||||
}
|
||||
|
||||
function render () {
|
||||
var now = Date.now() / 1000;
|
||||
var delta = now - lastFrameTime;
|
||||
lastFrameTime = now;
|
||||
|
||||
resize();
|
||||
|
||||
context.save();
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
context.fillStyle = "#cccccc";
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
context.restore();
|
||||
|
||||
state.update(delta);
|
||||
state.apply(skeleton);
|
||||
skeleton.updateWorldTransform();
|
||||
skeletonRenderer.draw(skeleton);
|
||||
|
||||
context.strokeStyle = "green";
|
||||
context.beginPath();
|
||||
context.moveTo(-1000, 0);
|
||||
context.lineTo(1000, 0);
|
||||
context.moveTo(0, -1000);
|
||||
context.lineTo(0, 1000);
|
||||
context.stroke();
|
||||
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
function resize () {
|
||||
var w = canvas.clientWidth;
|
||||
var h = canvas.clientHeight;
|
||||
if (canvas.width != w || canvas.height != h) {
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
}
|
||||
|
||||
// magic
|
||||
var centerX = bounds.offset.x + bounds.size.x / 2;
|
||||
var centerY = bounds.offset.y + bounds.size.y / 2;
|
||||
var scaleX = bounds.size.x / canvas.width;
|
||||
var scaleY = bounds.size.y / canvas.height;
|
||||
var scale = Math.max(scaleX, scaleY) * 1.2;
|
||||
if (scale < 1) scale = 1;
|
||||
var width = canvas.width * scale;
|
||||
var height = canvas.height * scale;
|
||||
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
context.scale(1 / scale, 1 / scale);
|
||||
context.translate(-centerX, -centerY);
|
||||
context.translate(width / 2, height / 2);
|
||||
}
|
||||
|
||||
(function() {
|
||||
init();
|
||||
}());
|
||||
|
||||
</script>
|
||||
</html>
|
||||
38
apps/module/spine-ts/canvas/src/AssetManager.ts
Executable file
38
apps/module/spine-ts/canvas/src/AssetManager.ts
Executable file
@@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, Esoteric Software LLC
|
||||
*
|
||||
* Integration of the Spine Runtimes into software or otherwise creating
|
||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||
* http://esotericsoftware.com/spine-editor-license
|
||||
*
|
||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
* "Products"), provided that each user of the Products must obtain their own
|
||||
* Spine Editor license and redistribution of the Products in any form must
|
||||
* include this license and copyright notice.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/// <reference path="../../core/src/AssetManager.ts"/>
|
||||
|
||||
module spine.canvas {
|
||||
export class AssetManager extends spine.AssetManager {
|
||||
constructor (pathPrefix: string = "") {
|
||||
super((image: HTMLImageElement) => { return new spine.canvas.CanvasTexture(image); }, pathPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
apps/module/spine-ts/canvas/src/CanvasTexture.ts
Executable file
42
apps/module/spine-ts/canvas/src/CanvasTexture.ts
Executable file
@@ -0,0 +1,42 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, Esoteric Software LLC
|
||||
*
|
||||
* Integration of the Spine Runtimes into software or otherwise creating
|
||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||
* http://esotericsoftware.com/spine-editor-license
|
||||
*
|
||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
* "Products"), provided that each user of the Products must obtain their own
|
||||
* Spine Editor license and redistribution of the Products in any form must
|
||||
* include this license and copyright notice.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/// <reference path="../../core/src/Texture.ts"/>
|
||||
|
||||
module spine.canvas {
|
||||
export class CanvasTexture extends Texture {
|
||||
constructor (image: HTMLImageElement) {
|
||||
super(image);
|
||||
}
|
||||
|
||||
setFilters (minFilter: TextureFilter, magFilter: TextureFilter) { }
|
||||
setWraps (uWrap: TextureWrap, vWrap: TextureWrap) { }
|
||||
dispose () { }
|
||||
}
|
||||
}
|
||||
323
apps/module/spine-ts/canvas/src/SkeletonRenderer.ts
Executable file
323
apps/module/spine-ts/canvas/src/SkeletonRenderer.ts
Executable file
@@ -0,0 +1,323 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, Esoteric Software LLC
|
||||
*
|
||||
* Integration of the Spine Runtimes into software or otherwise creating
|
||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||
* http://esotericsoftware.com/spine-editor-license
|
||||
*
|
||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
* "Products"), provided that each user of the Products must obtain their own
|
||||
* Spine Editor license and redistribution of the Products in any form must
|
||||
* include this license and copyright notice.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
module spine.canvas {
|
||||
export class SkeletonRenderer {
|
||||
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
||||
static VERTEX_SIZE = 2 + 2 + 4;
|
||||
|
||||
private ctx: CanvasRenderingContext2D;
|
||||
|
||||
public triangleRendering = false;
|
||||
public debugRendering = false;
|
||||
private vertices = Utils.newFloatArray(8 * 1024);
|
||||
private tempColor = new Color();
|
||||
|
||||
constructor (context: CanvasRenderingContext2D) {
|
||||
this.ctx = context;
|
||||
}
|
||||
|
||||
draw (skeleton: Skeleton) {
|
||||
if (this.triangleRendering) this.drawTriangles(skeleton);
|
||||
else this.drawImages(skeleton);
|
||||
}
|
||||
|
||||
private drawImages (skeleton: Skeleton) {
|
||||
let ctx = this.ctx;
|
||||
let drawOrder = skeleton.drawOrder;
|
||||
|
||||
if (this.debugRendering) ctx.strokeStyle = "green";
|
||||
|
||||
ctx.save();
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
let slot = drawOrder[i];
|
||||
if (!slot.bone.active) continue;
|
||||
let attachment = slot.getAttachment();
|
||||
let regionAttachment: RegionAttachment = null;
|
||||
let region: TextureAtlasRegion = null;
|
||||
let image: HTMLImageElement = null;
|
||||
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
regionAttachment = <RegionAttachment>attachment;
|
||||
region = <TextureAtlasRegion>regionAttachment.region;
|
||||
image = (<CanvasTexture>region.texture).getImage() as HTMLImageElement;
|
||||
} else continue;
|
||||
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let skeletonColor = skeleton.color;
|
||||
let slotColor = slot.color;
|
||||
let regionColor = regionAttachment.color;
|
||||
let alpha = skeletonColor.a * slotColor.a * regionColor.a;
|
||||
let color = this.tempColor;
|
||||
color.set(skeletonColor.r * slotColor.r * regionColor.r,
|
||||
skeletonColor.g * slotColor.g * regionColor.g,
|
||||
skeletonColor.b * slotColor.b * regionColor.b,
|
||||
alpha);
|
||||
|
||||
let att = <RegionAttachment>attachment;
|
||||
let bone = slot.bone;
|
||||
let w = region.width;
|
||||
let h = region.height;
|
||||
ctx.save();
|
||||
ctx.transform(bone.a, bone.c, bone.b, bone.d, bone.worldX, bone.worldY);
|
||||
ctx.translate(attachment.offset[0], attachment.offset[1]);
|
||||
ctx.rotate(attachment.rotation * Math.PI / 180);
|
||||
let atlasScale = att.width / w;
|
||||
ctx.scale(atlasScale * attachment.scaleX, atlasScale * attachment.scaleY);
|
||||
ctx.translate(w / 2, h / 2);
|
||||
if (attachment.region.rotate) {
|
||||
let t = w;
|
||||
w = h;
|
||||
h = t;
|
||||
ctx.rotate(-Math.PI / 2);
|
||||
}
|
||||
ctx.scale(1, -1);
|
||||
ctx.translate(-w / 2, -h / 2);
|
||||
ctx.globalAlpha = color.a;
|
||||
// experimental tinting via compositing, doesn't work
|
||||
// ctx.globalCompositeOperation = "source-atop";
|
||||
// ctx.fillStyle = "rgba(" + (color.r * 255 | 0) + ", " + (color.g * 255 | 0) + ", " + (color.b * 255 | 0) + ", " + color.a + ")";
|
||||
// ctx.fillRect(0, 0, w, h);
|
||||
ctx.drawImage(image, region.x, region.y, w, h, 0, 0, w, h);
|
||||
if (this.debugRendering) ctx.strokeRect(0, 0, w, h);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
private drawTriangles (skeleton: Skeleton) {
|
||||
let blendMode: BlendMode = null;
|
||||
|
||||
let vertices: ArrayLike<number> = this.vertices;
|
||||
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: HTMLImageElement = null;
|
||||
let region: TextureAtlasRegion = null;
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
let regionAttachment = <RegionAttachment>attachment;
|
||||
vertices = this.computeRegionVertices(slot, regionAttachment, false);
|
||||
triangles = SkeletonRenderer.QUAD_TRIANGLES;
|
||||
region = <TextureAtlasRegion>regionAttachment.region;
|
||||
texture = (<CanvasTexture>region.texture).getImage() as HTMLImageElement;
|
||||
|
||||
} else if (attachment instanceof MeshAttachment) {
|
||||
let mesh = <MeshAttachment>attachment;
|
||||
vertices = this.computeMeshVertices(slot, mesh, false);
|
||||
triangles = mesh.triangles;
|
||||
texture = (<TextureAtlasRegion>mesh.region.renderObject).texture.getImage() as HTMLImageElement;
|
||||
} else continue;
|
||||
|
||||
if (texture != null) {
|
||||
let slotBlendMode = slot.data.blendMode;
|
||||
if (slotBlendMode != blendMode) {
|
||||
blendMode = slotBlendMode;
|
||||
}
|
||||
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let skeletonColor = skeleton.color;
|
||||
let slotColor = slot.color;
|
||||
let attachmentColor = attachment.color;
|
||||
let alpha = skeletonColor.a * slotColor.a * attachmentColor.a;
|
||||
let color = this.tempColor;
|
||||
color.set(skeletonColor.r * slotColor.r * attachmentColor.r,
|
||||
skeletonColor.g * slotColor.g * attachmentColor.g,
|
||||
skeletonColor.b * slotColor.b * attachmentColor.b,
|
||||
alpha);
|
||||
|
||||
let ctx = this.ctx;
|
||||
|
||||
ctx.globalAlpha = color.a;
|
||||
// experimental tinting via compositing, doesn't work
|
||||
// ctx.globalCompositeOperation = "source-atop";
|
||||
// ctx.fillStyle = "rgba(" + (color.r * 255 | 0) + ", " + (color.g * 255 | 0) + ", " + (color.b * 255 | 0) + ", " + color.a + ")";
|
||||
// ctx.fillRect(0, 0, w, h);
|
||||
|
||||
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.debugRendering) {
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x0, y0);
|
||||
ctx.lineTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.lineTo(x0, y0);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
// Adapted from http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
|
||||
// Apache 2 licensed
|
||||
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;
|
||||
|
||||
u0 *= img.width;
|
||||
v0 *= img.height;
|
||||
u1 *= img.width;
|
||||
v1 *= img.height;
|
||||
u2 *= img.width;
|
||||
v2 *= img.height;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x0, y0);
|
||||
ctx.lineTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.closePath();
|
||||
|
||||
x1 -= x0;
|
||||
y1 -= y0;
|
||||
x2 -= x0;
|
||||
y2 -= y0;
|
||||
|
||||
u1 -= u0;
|
||||
v1 -= v0;
|
||||
u2 -= u0;
|
||||
v2 -= v0;
|
||||
|
||||
var det = 1 / (u1*v2 - u2*v1),
|
||||
|
||||
// linear transformation
|
||||
a = (v2*x1 - v1*x2) * det,
|
||||
b = (v2*y1 - v1*y2) * det,
|
||||
c = (u1*x2 - u2*x1) * det,
|
||||
d = (u1*y2 - u2*y1) * det,
|
||||
|
||||
// translation
|
||||
e = x0 - a*u0 - c*v0,
|
||||
f = y0 - b*u0 - d*v0;
|
||||
|
||||
ctx.save();
|
||||
ctx.transform(a, b, c, d, e, f);
|
||||
ctx.clip();
|
||||
ctx.drawImage(img, 0, 0);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
private computeRegionVertices(slot: Slot, region: RegionAttachment, pma: boolean) {
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let skeletonColor = skeleton.color;
|
||||
let slotColor = slot.color;
|
||||
let regionColor = region.color;
|
||||
let alpha = skeletonColor.a * slotColor.a * regionColor.a;
|
||||
let multiplier = pma ? 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);
|
||||
|
||||
region.computeWorldVertices(slot.bone, this.vertices, 0, SkeletonRenderer.VERTEX_SIZE);
|
||||
|
||||
let vertices = this.vertices;
|
||||
let uvs = region.uvs;
|
||||
|
||||
vertices[RegionAttachment.C1R] = color.r;
|
||||
vertices[RegionAttachment.C1G] = color.g;
|
||||
vertices[RegionAttachment.C1B] = color.b;
|
||||
vertices[RegionAttachment.C1A] = color.a;
|
||||
vertices[RegionAttachment.U1] = uvs[0];
|
||||
vertices[RegionAttachment.V1] = uvs[1];
|
||||
|
||||
vertices[RegionAttachment.C2R] = color.r;
|
||||
vertices[RegionAttachment.C2G] = color.g;
|
||||
vertices[RegionAttachment.C2B] = color.b;
|
||||
vertices[RegionAttachment.C2A] = color.a;
|
||||
vertices[RegionAttachment.U2] = uvs[2];
|
||||
vertices[RegionAttachment.V2] = uvs[3];
|
||||
|
||||
vertices[RegionAttachment.C3R] = color.r;
|
||||
vertices[RegionAttachment.C3G] = color.g;
|
||||
vertices[RegionAttachment.C3B] = color.b;
|
||||
vertices[RegionAttachment.C3A] = color.a;
|
||||
vertices[RegionAttachment.U3] = uvs[4];
|
||||
vertices[RegionAttachment.V3] = uvs[5];
|
||||
|
||||
vertices[RegionAttachment.C4R] = color.r;
|
||||
vertices[RegionAttachment.C4G] = color.g;
|
||||
vertices[RegionAttachment.C4B] = color.b;
|
||||
vertices[RegionAttachment.C4A] = color.a;
|
||||
vertices[RegionAttachment.U4] = uvs[6];
|
||||
vertices[RegionAttachment.V4] = uvs[7];
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
private computeMeshVertices(slot: Slot, mesh: MeshAttachment, pma: boolean) {
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let skeletonColor = skeleton.color;
|
||||
let slotColor = slot.color;
|
||||
let regionColor = mesh.color;
|
||||
let alpha = skeletonColor.a * slotColor.a * regionColor.a;
|
||||
let multiplier = pma ? 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 numVertices = mesh.worldVerticesLength / 2;
|
||||
if (this.vertices.length < mesh.worldVerticesLength) {
|
||||
this.vertices = Utils.newFloatArray(mesh.worldVerticesLength);
|
||||
}
|
||||
let vertices = this.vertices;
|
||||
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, vertices, 0, SkeletonRenderer.VERTEX_SIZE);
|
||||
|
||||
let uvs = mesh.uvs;
|
||||
for (let i = 0, n = numVertices, u = 0, v = 2; i < n; i++) {
|
||||
vertices[v++] = color.r;
|
||||
vertices[v++] = color.g;
|
||||
vertices[v++] = color.b;
|
||||
vertices[v++] = color.a;
|
||||
vertices[v++] = uvs[u++];
|
||||
vertices[v++] = uvs[u++];
|
||||
v += 2;
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user