[ts][webgl] Slot range rendering in SkeletonRenderer and SceneRenderer. Closes #1083

This commit is contained in:
badlogic 2018-02-16 13:47:03 +01:00
parent db504d7bc6
commit 4d5010680d
15 changed files with 209 additions and 27 deletions

View File

@ -242,6 +242,7 @@
* Added two color tinting support, enabled by default. You can disable it via the constructors of `SceneRenderer`, `SkeletonRenderer`and `PolygonBatcher`. Note that you will need to use a shader created via `Shader.newTwoColoredTexturedShader` shader with `SkeletonRenderer` and `PolygonBatcher` if two color tinting is enabled.
* Added clipping support
* Added `VertexEffect` interface, instances of which can be set on `SkeletonRenderer`. Allows to modify vertices before submitting them to GPU. See `SwirlEffect`, `JitterEffect`, and the example which allows to set effects.
* Added `slotRangeStart` and `slotRangeEnd` parameters to `SkeletonRenderer#draw` and `SceneRenderer#drawSkeleton`. This allows you to render only a range of slots in the draw order. See `spine-ts/webgl/tests/test-slot-range.html` for an example.
### Canvas backend
* Fixed renderer to work for 3.6 changes. Sadly, we can't support two color tinting via the Canvas API.

View File

@ -1490,7 +1490,7 @@ declare module spine.webgl {
private WHITE;
constructor(canvas: HTMLCanvasElement, context: ManagedWebGLRenderingContext | WebGLRenderingContext, twoColorTint?: boolean);
begin(): void;
drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean): void;
drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean, slotRangeStart?: number, slotRangeEnd?: number): void;
drawSkeletonDebug(skeleton: Skeleton, premultipliedAlpha?: boolean, ignoredBones?: Array<string>): void;
drawTexture(texture: GLTexture, x: number, y: number, width: number, height: number, color?: Color): void;
drawTextureUV(texture: GLTexture, x: number, y: number, width: number, height: number, u: number, v: number, u2: number, v2: number, color?: Color): void;
@ -1649,7 +1649,7 @@ declare module spine.webgl {
private temp3;
private temp4;
constructor(context: ManagedWebGLRenderingContext, twoColorTint?: boolean);
draw(batcher: PolygonBatcher, skeleton: Skeleton): void;
draw(batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart?: number, slotRangeEnd?: number): void;
}
}
declare module spine.webgl {

View File

@ -7874,11 +7874,13 @@ var spine;
this.camera.update();
this.enableRenderer(this.batcher);
};
SceneRenderer.prototype.drawSkeleton = function (skeleton, premultipliedAlpha) {
SceneRenderer.prototype.drawSkeleton = function (skeleton, premultipliedAlpha, slotRangeStart, slotRangeEnd) {
if (premultipliedAlpha === void 0) { premultipliedAlpha = false; }
if (slotRangeStart === void 0) { slotRangeStart = -1; }
if (slotRangeEnd === void 0) { slotRangeEnd = -1; }
this.enableRenderer(this.batcher);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
this.skeletonRenderer.draw(this.batcher, skeleton);
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd);
};
SceneRenderer.prototype.drawSkeletonDebug = function (skeleton, premultipliedAlpha, ignoredBones) {
if (premultipliedAlpha === void 0) { premultipliedAlpha = false; }
@ -9027,7 +9029,9 @@ var spine;
this.vertexSize += 4;
this.vertices = spine.Utils.newFloatArray(this.vertexSize * 1024);
}
SkeletonRenderer.prototype.draw = function (batcher, skeleton) {
SkeletonRenderer.prototype.draw = function (batcher, skeleton, slotRangeStart, slotRangeEnd) {
if (slotRangeStart === void 0) { slotRangeStart = -1; }
if (slotRangeEnd === void 0) { slotRangeEnd = -1; }
var clipper = this.clipper;
var premultipliedAlpha = this.premultipliedAlpha;
var twoColorTint = this.twoColorTint;
@ -9043,9 +9047,22 @@ var spine;
var attachmentColor = null;
var skeletonColor = skeleton.color;
var vertexSize = twoColorTint ? 12 : 8;
var inRange = false;
if (slotRangeStart == -1)
inRange = true;
for (var i = 0, n = drawOrder.length; i < n; i++) {
var clippedVertexSize = clipper.isClipping() ? 2 : vertexSize;
var slot = drawOrder[i];
if (slotRangeStart >= 0 && slotRangeStart == slot.data.index) {
inRange = true;
}
if (!inRange) {
clipper.clipEndWithSlot(slot);
continue;
}
if (slotRangeEnd >= 0 && slotRangeEnd == slot.data.index) {
inRange = false;
}
var attachment = slot.getAttachment();
var texture = null;
if (attachment instanceof spine.RegionAttachment) {

File diff suppressed because one or more lines are too long

View File

@ -1459,7 +1459,7 @@ declare module spine.webgl {
private WHITE;
constructor(canvas: HTMLCanvasElement, context: ManagedWebGLRenderingContext | WebGLRenderingContext, twoColorTint?: boolean);
begin(): void;
drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean): void;
drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean, slotRangeStart?: number, slotRangeEnd?: number): void;
drawSkeletonDebug(skeleton: Skeleton, premultipliedAlpha?: boolean, ignoredBones?: Array<string>): void;
drawTexture(texture: GLTexture, x: number, y: number, width: number, height: number, color?: Color): void;
drawTextureUV(texture: GLTexture, x: number, y: number, width: number, height: number, u: number, v: number, u2: number, v2: number, color?: Color): void;
@ -1618,7 +1618,7 @@ declare module spine.webgl {
private temp3;
private temp4;
constructor(context: ManagedWebGLRenderingContext, twoColorTint?: boolean);
draw(batcher: PolygonBatcher, skeleton: Skeleton): void;
draw(batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart?: number, slotRangeEnd?: number): void;
}
}
declare module spine.webgl {

View File

@ -7619,11 +7619,13 @@ var spine;
this.camera.update();
this.enableRenderer(this.batcher);
};
SceneRenderer.prototype.drawSkeleton = function (skeleton, premultipliedAlpha) {
SceneRenderer.prototype.drawSkeleton = function (skeleton, premultipliedAlpha, slotRangeStart, slotRangeEnd) {
if (premultipliedAlpha === void 0) { premultipliedAlpha = false; }
if (slotRangeStart === void 0) { slotRangeStart = -1; }
if (slotRangeEnd === void 0) { slotRangeEnd = -1; }
this.enableRenderer(this.batcher);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
this.skeletonRenderer.draw(this.batcher, skeleton);
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd);
};
SceneRenderer.prototype.drawSkeletonDebug = function (skeleton, premultipliedAlpha, ignoredBones) {
if (premultipliedAlpha === void 0) { premultipliedAlpha = false; }
@ -8772,7 +8774,9 @@ var spine;
this.vertexSize += 4;
this.vertices = spine.Utils.newFloatArray(this.vertexSize * 1024);
}
SkeletonRenderer.prototype.draw = function (batcher, skeleton) {
SkeletonRenderer.prototype.draw = function (batcher, skeleton, slotRangeStart, slotRangeEnd) {
if (slotRangeStart === void 0) { slotRangeStart = -1; }
if (slotRangeEnd === void 0) { slotRangeEnd = -1; }
var clipper = this.clipper;
var premultipliedAlpha = this.premultipliedAlpha;
var twoColorTint = this.twoColorTint;
@ -8788,9 +8792,22 @@ var spine;
var attachmentColor = null;
var skeletonColor = skeleton.color;
var vertexSize = twoColorTint ? 12 : 8;
var inRange = false;
if (slotRangeStart == -1)
inRange = true;
for (var i = 0, n = drawOrder.length; i < n; i++) {
var clippedVertexSize = clipper.isClipping() ? 2 : vertexSize;
var slot = drawOrder[i];
if (slotRangeStart >= 0 && slotRangeStart == slot.data.index) {
inRange = true;
}
if (!inRange) {
clipper.clipEndWithSlot(slot);
continue;
}
if (slotRangeEnd >= 0 && slotRangeEnd == slot.data.index) {
inRange = false;
}
var attachment = slot.getAttachment();
var texture = null;
if (attachment instanceof spine.RegionAttachment) {

File diff suppressed because one or more lines are too long

View File

@ -1459,7 +1459,7 @@ declare module spine.webgl {
private WHITE;
constructor(canvas: HTMLCanvasElement, context: ManagedWebGLRenderingContext | WebGLRenderingContext, twoColorTint?: boolean);
begin(): void;
drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean): void;
drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean, slotRangeStart?: number, slotRangeEnd?: number): void;
drawSkeletonDebug(skeleton: Skeleton, premultipliedAlpha?: boolean, ignoredBones?: Array<string>): void;
drawTexture(texture: GLTexture, x: number, y: number, width: number, height: number, color?: Color): void;
drawTextureUV(texture: GLTexture, x: number, y: number, width: number, height: number, u: number, v: number, u2: number, v2: number, color?: Color): void;
@ -1618,7 +1618,7 @@ declare module spine.webgl {
private temp3;
private temp4;
constructor(context: ManagedWebGLRenderingContext, twoColorTint?: boolean);
draw(batcher: PolygonBatcher, skeleton: Skeleton): void;
draw(batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart?: number, slotRangeEnd?: number): void;
}
}
declare module spine.webgl {

View File

@ -7619,11 +7619,13 @@ var spine;
this.camera.update();
this.enableRenderer(this.batcher);
};
SceneRenderer.prototype.drawSkeleton = function (skeleton, premultipliedAlpha) {
SceneRenderer.prototype.drawSkeleton = function (skeleton, premultipliedAlpha, slotRangeStart, slotRangeEnd) {
if (premultipliedAlpha === void 0) { premultipliedAlpha = false; }
if (slotRangeStart === void 0) { slotRangeStart = -1; }
if (slotRangeEnd === void 0) { slotRangeEnd = -1; }
this.enableRenderer(this.batcher);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
this.skeletonRenderer.draw(this.batcher, skeleton);
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd);
};
SceneRenderer.prototype.drawSkeletonDebug = function (skeleton, premultipliedAlpha, ignoredBones) {
if (premultipliedAlpha === void 0) { premultipliedAlpha = false; }
@ -8772,7 +8774,9 @@ var spine;
this.vertexSize += 4;
this.vertices = spine.Utils.newFloatArray(this.vertexSize * 1024);
}
SkeletonRenderer.prototype.draw = function (batcher, skeleton) {
SkeletonRenderer.prototype.draw = function (batcher, skeleton, slotRangeStart, slotRangeEnd) {
if (slotRangeStart === void 0) { slotRangeStart = -1; }
if (slotRangeEnd === void 0) { slotRangeEnd = -1; }
var clipper = this.clipper;
var premultipliedAlpha = this.premultipliedAlpha;
var twoColorTint = this.twoColorTint;
@ -8788,9 +8792,22 @@ var spine;
var attachmentColor = null;
var skeletonColor = skeleton.color;
var vertexSize = twoColorTint ? 12 : 8;
var inRange = false;
if (slotRangeStart == -1)
inRange = true;
for (var i = 0, n = drawOrder.length; i < n; i++) {
var clippedVertexSize = clipper.isClipping() ? 2 : vertexSize;
var slot = drawOrder[i];
if (slotRangeStart >= 0 && slotRangeStart == slot.data.index) {
inRange = true;
}
if (!inRange) {
clipper.clipEndWithSlot(slot);
continue;
}
if (slotRangeEnd >= 0 && slotRangeEnd == slot.data.index) {
inRange = false;
}
var attachment = slot.getAttachment();
var texture = null;
if (attachment instanceof spine.RegionAttachment) {

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
spineboy.png
spineboy-pma.png
size: 1024,512
format: RGBA8888
filter: Linear,Linear

View File

@ -68,10 +68,10 @@ module spine.webgl {
this.enableRenderer(this.batcher);
}
drawSkeleton (skeleton: Skeleton, premultipliedAlpha = false) {
drawSkeleton (skeleton: Skeleton, premultipliedAlpha = false, slotRangeStart = -1, slotRangeEnd = -1) {
this.enableRenderer(this.batcher);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
this.skeletonRenderer.draw(this.batcher, skeleton);
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd);
}
drawSkeletonDebug(skeleton: Skeleton, premultipliedAlpha = false, ignoredBones: Array<string> = null) {

View File

@ -57,7 +57,7 @@ module spine.webgl {
this.vertices = Utils.newFloatArray(this.vertexSize * 1024);
}
draw (batcher: PolygonBatcher, skeleton: Skeleton) {
draw (batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart: number = -1, slotRangeEnd: number = -1) {
let clipper = this.clipper;
let premultipliedAlpha = this.premultipliedAlpha;
let twoColorTint = this.twoColorTint;
@ -75,9 +75,25 @@ module spine.webgl {
let attachmentColor: Color = null;
let skeletonColor = skeleton.color;
let vertexSize = twoColorTint ? 12 : 8;
let inRange = false;
if (slotRangeStart == -1) inRange = true;
for (let i = 0, n = drawOrder.length; i < n; i++) {
let clippedVertexSize = clipper.isClipping() ? 2 : vertexSize;
let slot = drawOrder[i];
if (slotRangeStart >= 0 && slotRangeStart == slot.data.index) {
inRange = true;
}
if (!inRange) {
clipper.clipEndWithSlot(slot);
continue;
}
if (slotRangeEnd >= 0 && slotRangeEnd == slot.data.index) {
inRange = false;
}
let attachment = slot.getAttachment();
let texture: GLTexture = null;
if (attachment instanceof RegionAttachment) {

View File

@ -7,7 +7,7 @@
var canvas = document.getElementsByTagName("canvas")[0];
canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight;
var context = new spine.webgl.ManagedWebGLRenderingContext(canvas);
var assetManager = new spine.webgl.AssetManager(context, "https://esotericsoftware.com/sketch/files/4772/");
var assetManager = new spine.webgl.AssetManager(context, "../example/assets/");
var renderer = new spine.webgl.SceneRenderer(canvas, context);
var time = new spine.TimeKeeper();
var loadingScreen = new spine.webgl.LoadingScreen(new spine.webgl.SceneRenderer(canvas, context));
@ -15,16 +15,16 @@ var loadingScreen = new spine.webgl.LoadingScreen(new spine.webgl.SceneRenderer(
var skeleton = null;
var animationState = null;
assetManager.loadText("Spineboy/spineboy-pro.json");
assetManager.loadTextureAtlas("Spineboy/spineboy-pro.atlas");
assetManager.loadText("spineboy-ess.json");
assetManager.loadTextureAtlas("spineboy.atlas");
requestAnimationFrame(load);
function load () {
if (assetManager.isLoadingComplete()) {
var atlas = assetManager.get("Spineboy/spineboy-pro.atlas");
var atlas = assetManager.get("spineboy.atlas");
var skeletonJson = new spine.SkeletonJson(new spine.AtlasAttachmentLoader(atlas));
var skeletonData = skeletonJson.readSkeletonData(assetManager.get("Spineboy/spineboy-pro.json"));
var skeletonData = skeletonJson.readSkeletonData(assetManager.get("spineboy-ess.json"));
var animationStateData = new spine.AnimationStateData(skeletonData);
skeleton = new spine.Skeleton(skeletonData);

View File

@ -0,0 +1,114 @@
<html>
<body>
<script src="../../build/spine-webgl.js"></script>
<canvas width="640" height="480"></canvas>
</body>
<script>
/*
This test illustrates how to render one skeleton in the
middle of rendering another skeleton. This technique can
be used to e.g. render different weapons on a character that
are themselves animated.
*/
var canvas = document.getElementsByTagName("canvas")[0];
canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight;
var context = new spine.webgl.ManagedWebGLRenderingContext(canvas);
var assetManager = new spine.webgl.AssetManager(context, "../example/assets/");
var renderer = new spine.webgl.SceneRenderer(canvas, context);
var time = new spine.TimeKeeper();
var loadingScreen = new spine.webgl.LoadingScreen(new spine.webgl.SceneRenderer(canvas, context));
var spineBoy;
var vine;
assetManager.loadText("spineboy-ess.json");
assetManager.loadTextureAtlas("spineboy.atlas");
assetManager.loadText("vine-pro.json");
assetManager.loadTextureAtlas("vine.atlas");
requestAnimationFrame(load);
function loadSkeleton(json, atlas, scale) {
var atlas = assetManager.get(atlas);
var skeletonJson = new spine.SkeletonJson(new spine.AtlasAttachmentLoader(atlas));
skeletonJson.scale = scale;
var skeletonData = skeletonJson.readSkeletonData(assetManager.get(json));
var animationStateData = new spine.AnimationStateData(skeletonData);
skeleton = new spine.Skeleton(skeletonData);
animationState = new spine.AnimationState(animationStateData);
return { skeleton: skeleton, animationState: animationState };
}
function load () {
if (assetManager.isLoadingComplete()) {
spineBoy = loadSkeleton("spineboy-ess.json", "spineboy.atlas", 1);
vine = loadSkeleton("vine-pro.json", "vine.atlas", 0.3);
var offset = new spine.Vector2();
var size = new spine.Vector2();
spineBoy.skeleton.updateWorldTransform();
spineBoy.skeleton.getBounds(offset, size, []);
renderer.camera.position.x = offset.x + size.x / 2;
renderer.camera.position.y = offset.y + size.y / 2;
renderer.camera.zoom = size.x > size.y ? size.x / canvas.width : size.y / canvas.height;
spineBoy.animationState.setAnimation(0, "walk", true);
vine.animationState.setAnimation(0, "grow", true);
requestAnimationFrame(render);
} else {
loadingScreen.draw(false);
requestAnimationFrame(load);
}
}
function render () {
context.gl.clearColor(0.9, 0.9, 0.9, 1);
context.gl.clear(context.gl.COLOR_BUFFER_BIT);
time.update();
// Update Spineboy first
spineBoy.animationState.update(time.delta);
spineBoy.animationState.apply(spineBoy.skeleton);
spineBoy.skeleton.updateWorldTransform();
// Next figure out the world location of the bone
// we attach the vine to, and apply it to the vine's
// root bone. Note that the root bone must not be
// keyed! We also ignore scaling and shearing.
var gun = spineBoy.skeleton.findBone("gun");
var vineRoot = vine.skeleton.getRootBone();
vineRoot.x = gun.worldX;
vineRoot.y = gun.worldY;
vineRoot.rotation = gun.getWorldRotationX();
vine.animationState.update(time.delta);
vine.animationState.apply(vine.skeleton);
vine.skeleton.updateWorldTransform();
// Find the two slots inbetween we want to render the vine
var gunSlot = spineBoy.skeleton.findSlot("gun");
var rearFoot = spineBoy.skeleton.findSlot("rear-foot");
renderer.begin();
// render from the back most slot (-1) to the gun slot (inclusive)
renderer.drawSkeleton(spineBoy.skeleton, true, -1, gunSlot.data.index);
// now render the vine
renderer.drawSkeleton(vine.skeleton, false);
// finally render the remaining spineboy slots starting at the rear foot
// (the next slot after the gun)
renderer.drawSkeleton(spineBoy.skeleton, true, rearFoot.data.index, -1);
renderer.end();
loadingScreen.draw(true);
requestAnimationFrame(render);
}
</script>
</html>