mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 07:14:55 +08:00
[ts][canvas] Canvas renderer now supports shearing and non-uniform scale. Closes #851
This commit is contained in:
parent
30d52282af
commit
e8df948d9d
@ -20,7 +20,11 @@ The Spine Runtimes are developed with the intent to be used with data exported f
|
||||
|
||||
spine-ts works with data exported from Spine 3.5.xx.
|
||||
|
||||
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. The spine-ts THREE.JS backend does not support color tinting and blend modes. The THREE.JS backend provides `SkeletonMesh.zOffset` to avoid z-fighting. Adjust to your near/far plane settings.
|
||||
spine-ts WebGL & Widget backends supports all Spine features.
|
||||
|
||||
spine-ts Canvas does not support color tinting and mesh attachments. Experimental support for mesh attachments can be enabled by setting `spine.canvas.SkeletonRenderer.useTriangleRendering` to true. Note that this method is slow and may lead to artifacts on some browsers.
|
||||
|
||||
spine-ts THREE.JS does not support color tinting and blend modes. The THREE.JS backend provides `SkeletonMesh.zOffset` to avoid z-fighting. Adjust to your near/far plane settings.
|
||||
|
||||
spine-ts does not yet support loading the binary format.
|
||||
|
||||
|
||||
@ -231,10 +231,8 @@ var spine;
|
||||
var attachment = slot.getAttachment();
|
||||
var region = null;
|
||||
var image = null;
|
||||
var vertices = null;
|
||||
if (attachment instanceof spine.RegionAttachment) {
|
||||
var regionAttachment = attachment;
|
||||
vertices = regionAttachment.updateWorldVertices(slot, false);
|
||||
region = regionAttachment.region;
|
||||
image = (region).texture.getImage();
|
||||
}
|
||||
@ -242,28 +240,22 @@ var spine;
|
||||
continue;
|
||||
var att = attachment;
|
||||
var bone = slot.bone;
|
||||
var x = vertices[0];
|
||||
var y = vertices[1];
|
||||
var rotation = (bone.getWorldRotationX() - att.rotation) * Math.PI / 180;
|
||||
var xx = vertices[24] - vertices[0];
|
||||
var xy = vertices[25] - vertices[1];
|
||||
var yx = vertices[8] - vertices[0];
|
||||
var yy = vertices[9] - vertices[1];
|
||||
var 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);
|
||||
}
|
||||
var w = region.width;
|
||||
var h = region.height;
|
||||
var offsetX = attachment.offset[0];
|
||||
var offsetY = attachment.offset[1];
|
||||
ctx.save();
|
||||
ctx.transform(bone.a, bone.c, bone.b, bone.d, bone.worldX, bone.worldY);
|
||||
ctx.translate(offsetX, offsetY);
|
||||
ctx.rotate(attachment.rotation * Math.PI / 180);
|
||||
ctx.scale(attachment.scaleX, attachment.scaleY);
|
||||
ctx.translate(region.width / 2, region.height / 2);
|
||||
ctx.scale(1, -1);
|
||||
ctx.translate(-region.width / 2, -region.height / 2);
|
||||
ctx.drawImage(image, region.x, region.y, region.width, region.height, 0, 0, w, h);
|
||||
if (this.debugRendering)
|
||||
ctx.strokeRect(0, 0, w, h);
|
||||
ctx.rotate(-rotation);
|
||||
ctx.translate(-x, -y);
|
||||
ctx.restore();
|
||||
}
|
||||
};
|
||||
SkeletonRenderer.prototype.drawTriangles = function (skeleton) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -231,10 +231,8 @@ var spine;
|
||||
var attachment = slot.getAttachment();
|
||||
var region = null;
|
||||
var image = null;
|
||||
var vertices = null;
|
||||
if (attachment instanceof spine.RegionAttachment) {
|
||||
var regionAttachment = attachment;
|
||||
vertices = regionAttachment.updateWorldVertices(slot, false);
|
||||
region = regionAttachment.region;
|
||||
image = (region).texture.getImage();
|
||||
}
|
||||
@ -242,28 +240,22 @@ var spine;
|
||||
continue;
|
||||
var att = attachment;
|
||||
var bone = slot.bone;
|
||||
var x = vertices[0];
|
||||
var y = vertices[1];
|
||||
var rotation = (bone.getWorldRotationX() - att.rotation) * Math.PI / 180;
|
||||
var xx = vertices[24] - vertices[0];
|
||||
var xy = vertices[25] - vertices[1];
|
||||
var yx = vertices[8] - vertices[0];
|
||||
var yy = vertices[9] - vertices[1];
|
||||
var 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);
|
||||
}
|
||||
var w = region.width;
|
||||
var h = region.height;
|
||||
var offsetX = attachment.offset[0];
|
||||
var offsetY = attachment.offset[1];
|
||||
ctx.save();
|
||||
ctx.transform(bone.a, bone.c, bone.b, bone.d, bone.worldX, bone.worldY);
|
||||
ctx.translate(offsetX, offsetY);
|
||||
ctx.rotate(attachment.rotation * Math.PI / 180);
|
||||
ctx.scale(attachment.scaleX, attachment.scaleY);
|
||||
ctx.translate(region.width / 2, region.height / 2);
|
||||
ctx.scale(1, -1);
|
||||
ctx.translate(-region.width / 2, -region.height / 2);
|
||||
ctx.drawImage(image, region.x, region.y, region.width, region.height, 0, 0, w, h);
|
||||
if (this.debugRendering)
|
||||
ctx.strokeRect(0, 0, w, h);
|
||||
ctx.rotate(-rotation);
|
||||
ctx.translate(-x, -y);
|
||||
ctx.restore();
|
||||
}
|
||||
};
|
||||
SkeletonRenderer.prototype.drawTriangles = function (skeleton) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -5,141 +5,120 @@ format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
eye_indifferent
|
||||
rotate: true
|
||||
xy: 648, 629
|
||||
rotate: false
|
||||
xy: 275, 349
|
||||
size: 93, 89
|
||||
orig: 93, 89
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eye_surprised
|
||||
rotate: true
|
||||
xy: 233, 179
|
||||
rotate: false
|
||||
xy: 214, 125
|
||||
size: 93, 89
|
||||
orig: 93, 89
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_bracer
|
||||
rotate: false
|
||||
xy: 245, 2
|
||||
xy: 678, 774
|
||||
size: 58, 80
|
||||
orig: 58, 80
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_fist_closed
|
||||
rotate: false
|
||||
xy: 168, 45
|
||||
xy: 944, 940
|
||||
size: 75, 82
|
||||
orig: 75, 82
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_fist_open
|
||||
rotate: false
|
||||
xy: 844, 646
|
||||
xy: 132, 28
|
||||
size: 86, 87
|
||||
orig: 86, 87
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_foot
|
||||
rotate: true
|
||||
xy: 310, 326
|
||||
rotate: false
|
||||
xy: 550, 785
|
||||
size: 126, 69
|
||||
orig: 126, 69
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_foot_bend1
|
||||
rotate: true
|
||||
xy: 951, 894
|
||||
rotate: false
|
||||
xy: 2, 45
|
||||
size: 128, 70
|
||||
orig: 128, 70
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_foot_bend2
|
||||
rotate: false
|
||||
xy: 2, 33
|
||||
xy: 729, 929
|
||||
size: 108, 93
|
||||
orig: 108, 93
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_shin
|
||||
rotate: true
|
||||
xy: 739, 735
|
||||
rotate: false
|
||||
xy: 466, 670
|
||||
size: 82, 184
|
||||
orig: 82, 184
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_thigh
|
||||
rotate: false
|
||||
xy: 381, 340
|
||||
xy: 281, 235
|
||||
size: 48, 112
|
||||
orig: 48, 112
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_upper_arm
|
||||
rotate: false
|
||||
xy: 112, 29
|
||||
xy: 220, 26
|
||||
size: 54, 97
|
||||
orig: 54, 97
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goggles
|
||||
rotate: false
|
||||
xy: 156, 454
|
||||
xy: 466, 856
|
||||
size: 261, 166
|
||||
orig: 261, 166
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun
|
||||
rotate: false
|
||||
xy: 739, 819
|
||||
xy: 2, 117
|
||||
size: 210, 203
|
||||
orig: 210, 203
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
head
|
||||
rotate: false
|
||||
xy: 466, 724
|
||||
xy: 2, 322
|
||||
size: 271, 298
|
||||
orig: 271, 298
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
hoverboard_board
|
||||
rotate: true
|
||||
xy: 2, 128
|
||||
size: 492, 152
|
||||
orig: 492, 152
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
hoverboard_thruster
|
||||
rotate: false
|
||||
xy: 602, 558
|
||||
size: 60, 64
|
||||
orig: 60, 64
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
hoverglow_small
|
||||
rotate: true
|
||||
xy: 156, 178
|
||||
size: 274, 75
|
||||
orig: 274, 75
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_grind
|
||||
rotate: true
|
||||
xy: 951, 799
|
||||
rotate: false
|
||||
xy: 844, 878
|
||||
size: 93, 59
|
||||
orig: 93, 59
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_oooo
|
||||
rotate: true
|
||||
xy: 245, 84
|
||||
rotate: false
|
||||
xy: 550, 656
|
||||
size: 93, 59
|
||||
orig: 93, 59
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_smile
|
||||
rotate: false
|
||||
xy: 925, 738
|
||||
xy: 738, 806
|
||||
size: 93, 59
|
||||
orig: 93, 59
|
||||
offset: 0, 0
|
||||
@ -153,63 +132,63 @@ muzzle
|
||||
index: -1
|
||||
neck
|
||||
rotate: false
|
||||
xy: 168, 2
|
||||
xy: 2, 2
|
||||
size: 36, 41
|
||||
orig: 36, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_bracer
|
||||
rotate: false
|
||||
xy: 932, 664
|
||||
xy: 276, 51
|
||||
size: 56, 72
|
||||
orig: 56, 72
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_foot
|
||||
rotate: false
|
||||
xy: 487, 562
|
||||
xy: 729, 867
|
||||
size: 113, 60
|
||||
orig: 113, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_foot_bend1
|
||||
rotate: true
|
||||
xy: 419, 503
|
||||
rotate: false
|
||||
xy: 550, 717
|
||||
size: 117, 66
|
||||
orig: 117, 66
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_foot_bend2
|
||||
rotate: false
|
||||
xy: 739, 650
|
||||
xy: 839, 939
|
||||
size: 103, 83
|
||||
orig: 103, 83
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_shin
|
||||
rotate: false
|
||||
xy: 233, 274
|
||||
xy: 375, 442
|
||||
size: 75, 178
|
||||
orig: 75, 178
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_thigh
|
||||
rotate: true
|
||||
xy: 487, 495
|
||||
rotate: false
|
||||
xy: 214, 216
|
||||
size: 65, 104
|
||||
orig: 65, 104
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
rear_upper_arm
|
||||
rotate: true
|
||||
xy: 156, 129
|
||||
rotate: false
|
||||
xy: 331, 260
|
||||
size: 47, 87
|
||||
orig: 47, 87
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
torso
|
||||
rotate: true
|
||||
xy: 466, 624
|
||||
rotate: false
|
||||
xy: 275, 440
|
||||
size: 98, 180
|
||||
orig: 98, 180
|
||||
offset: 0, 0
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 678 KiB After Width: | Height: | Size: 595 KiB |
@ -17,6 +17,9 @@ var assetManager;
|
||||
var skeleton, state, bounds;
|
||||
var skeletonRenderer;
|
||||
|
||||
var skelName = "spineboy";
|
||||
var animName = "idle";
|
||||
|
||||
function init () {
|
||||
canvas = document.getElementById("canvas");
|
||||
canvas.width = window.innerWidth;
|
||||
@ -29,21 +32,21 @@ function init () {
|
||||
// enable the triangle renderer, supports meshes, but may produce artifacts in some browsers
|
||||
skeletonRenderer.triangleRendering = false;
|
||||
|
||||
assetManager = new spine.canvas.AssetManager();
|
||||
assetManager = new spine.canvas.AssetManager();
|
||||
|
||||
assetManager.loadText("assets/spineboy.json");
|
||||
assetManager.loadText("assets/spineboy.atlas");
|
||||
assetManager.loadTexture("assets/spineboy.png");
|
||||
assetManager.loadText("assets/" + skelName + ".json");
|
||||
assetManager.loadText("assets/" + skelName + ".atlas");
|
||||
assetManager.loadTexture("assets/" + skelName + ".png");
|
||||
|
||||
requestAnimationFrame(load);
|
||||
}
|
||||
|
||||
function load () {
|
||||
if (assetManager.isLoadingComplete()) {
|
||||
var data = loadSkeleton("spineboy", "walk", "default");
|
||||
var data = loadSkeleton(skelName, animName, "default");
|
||||
skeleton = data.skeleton;
|
||||
state = data.state;
|
||||
bounds = data.bounds;
|
||||
bounds = data.bounds;
|
||||
requestAnimationFrame(render);
|
||||
} else {
|
||||
requestAnimationFrame(load);
|
||||
@ -56,7 +59,7 @@ function loadSkeleton (name, initialAnimation, skin) {
|
||||
// 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 + ".atlas"), function(path) {
|
||||
return assetManager.get("assets/" + path);
|
||||
return assetManager.get("assets/" + path);
|
||||
});
|
||||
|
||||
// Create a AtlasAttachmentLoader, which is specific to the WebGL backend.
|
||||
@ -64,12 +67,12 @@ function loadSkeleton (name, initialAnimation, skin) {
|
||||
|
||||
// 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.
|
||||
|
||||
// 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.flipY = true;
|
||||
var bounds = calculateBounds(skeleton);
|
||||
skeleton.flipY = true;
|
||||
var bounds = calculateBounds(skeleton);
|
||||
skeleton.setSkinByName(skin);
|
||||
|
||||
// Create an AnimationState, and set the initial animation in looping mode.
|
||||
@ -100,7 +103,7 @@ function calculateBounds(skeleton) {
|
||||
skeleton.updateWorldTransform();
|
||||
var offset = new spine.Vector2();
|
||||
var size = new spine.Vector2();
|
||||
skeleton.getBounds(offset, size);
|
||||
skeleton.getBounds(offset, size);
|
||||
return { offset: offset, size: size };
|
||||
}
|
||||
|
||||
@ -108,7 +111,7 @@ function render () {
|
||||
var now = Date.now() / 1000;
|
||||
var delta = now - lastFrameTime;
|
||||
lastFrameTime = now;
|
||||
|
||||
|
||||
resize();
|
||||
|
||||
context.save();
|
||||
@ -119,15 +122,23 @@ function render () {
|
||||
|
||||
state.update(delta);
|
||||
state.apply(skeleton);
|
||||
skeleton.updateWorldTransform();
|
||||
skeletonRenderer.draw(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;
|
||||
var h = canvas.clientHeight;
|
||||
if (canvas.width != w || canvas.height != h) {
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
@ -140,12 +151,12 @@ function resize () {
|
||||
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;
|
||||
|
||||
var width = canvas.width;
|
||||
var height = canvas.height;
|
||||
|
||||
context.resetTransform();
|
||||
context.scale(1 / scale, 1 / scale);
|
||||
context.translate(-centerX, -centerY);
|
||||
context.translate(-centerX, -centerY);
|
||||
context.translate(width / 2, height / 2);
|
||||
}
|
||||
|
||||
|
||||
@ -57,37 +57,29 @@ module spine.canvas {
|
||||
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);
|
||||
}
|
||||
let w = region.width;
|
||||
let h = region.height;
|
||||
let offsetX = attachment.offset[0];
|
||||
let offsetY = attachment.offset[1];
|
||||
ctx.save();
|
||||
ctx.transform(bone.a, bone.c, bone.b, bone.d, bone.worldX, bone.worldY);
|
||||
ctx.translate(offsetX, offsetY);
|
||||
ctx.rotate(attachment.rotation * Math.PI / 180);
|
||||
ctx.scale(attachment.scaleX, attachment.scaleY);
|
||||
ctx.translate(region.width / 2, region.height / 2);
|
||||
ctx.scale(1, -1);
|
||||
ctx.translate(-region.width / 2, -region.height / 2);
|
||||
ctx.drawImage(image, region.x, region.y, region.width, region.height, 0, 0, w, h);
|
||||
if (this.debugRendering) ctx.strokeRect(0, 0, w, h);
|
||||
ctx.rotate(-rotation);
|
||||
ctx.translate(-x, -y);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user