[phaser] WebGL and Canvas rendering, fix mixins

# Conflicts:
#	spine-ts/package-lock.json
#	spine-ts/package.json
This commit is contained in:
Mario Zechner 2022-12-13 15:15:43 +01:00
parent 15bbf5701e
commit 7ae9c490d5
10 changed files with 161 additions and 68 deletions

View File

@ -17,7 +17,7 @@
"build:player": "npx copyfiles -f spine-player/css/spine-player.css spine-player/dist/ && npx npx esbuild --bundle spine-player/src/index.ts --tsconfig=spine-player/tsconfig.json --sourcemap --outfile=spine-player/dist/iife/spine-player.js --format=iife --global-name=spine",
"build:phaser": "npx esbuild --bundle spine-phaser/src/index.ts --tsconfig=spine-phaser/tsconfig.json --sourcemap --outfile=spine-phaser/dist/iife/spine-phaser.js --external:Phaser --alias:phaser=Phaser --format=iife --global-name=spine",
"build:threejs": "npx esbuild --bundle spine-threejs/src/index.ts --tsconfig=spine-threejs/tsconfig.json --sourcemap --outfile=spine-threejs/dist/iife/spine-threejs.js --external:three --format=iife --global-name=spine",
"minify": "npx esbuild --minify spine-core/dist/iife/spine-core.js --outfile=spine-core/dist/iife/spine-core.min.js && npx esbuild --minify spine-canvas/dist/iife/spine-canvas.js --outfile=spine-canvas/dist/iife/spine-canvas.min.js && npx esbuild --minify spine-player/dist/iife/spine-player.js --outfile=spine-player/dist/iife/spine-player.min.js && npx esbuild --minify spine-webgl/dist/iife/spine-webgl.js --outfile=spine-webgl/dist/iife/spine-webgl.min.js && npx esbuild --minify spine-threejs/dist/iife/spine-threejs.js --outfile=spine-threejs/dist/iife/spine-threejs.min.js",
"minify": "npx esbuild --minify spine-core/dist/iife/spine-core.js --outfile=spine-core/dist/iife/spine-core.min.js && npx esbuild --minify spine-canvas/dist/iife/spine-canvas.js --outfile=spine-canvas/dist/iife/spine-canvas.min.js && npx esbuild --minify spine-player/dist/iife/spine-player.js --outfile=spine-player/dist/iife/spine-player.min.js && npx esbuild --minify spine-phaser/dist/iife/spine-phaser.js --outfile=spine-phaser/dist/iife/spine-phaser.min.js && npx esbuild --minify spine-webgl/dist/iife/spine-webgl.js --outfile=spine-webgl/dist/iife/spine-webgl.min.js && npx esbuild --minify spine-threejs/dist/iife/spine-threejs.js --outfile=spine-threejs/dist/iife/spine-threejs.min.js",
"dev": "concurrently \"npx live-server --no-browser\" \"npm run dev:canvas\" \"npm run dev:webgl\" \"npm run dev:phaser\" \"npm run dev:player\" \"npm run dev:threejs\"",
"dev:modules": "npm run build:modules -- --watch",
"dev:canvas": "npm run build:canvas -- --watch",
@ -54,13 +54,12 @@
"spine-webgl"
],
"devDependencies": {
"concurrently": "^6.2.1",
"@types/offscreencanvas": "^2019.6.4",
"concurrently": "^7.6.0",
"copyfiles": "^2.4.1",
"esbuild": "^0.16.3",
"live-server": "^1.2.1",
"npx": "^10.2.2",
"esbuild": "^0.16.4",
"live-server": "^1.2.2",
"rimraf": "^3.0.2",
"typescript": "^4.3.5",
"@types/offscreencanvas": "^2019.6.4"
"typescript": "^4.9.4"
}
}

View File

@ -8,10 +8,11 @@ var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.CANVAS,
type: Phaser.WEBGL,
scene: {
preload: preload,
create: create,
update: update,
},
plugins: {
scene: [
@ -20,18 +21,35 @@ var config = {
}
};
var game = new Phaser.Game(config);
let game = new Phaser.Game(config);
let debug;
function preload () {
this.load.spineJson("raptor-data", "assets/raptor-pro.json");
this.load.spineAtlas("raptor-atlas", "assets/raptor-pma.atlas");
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
this.load.image("nyan", "nyan.png");
let canvas = document.querySelector("#game-canvas");
}
function create () {
let plugin = this.spine;
var raptor = this.add.spine(400, 600, 'raptor-data', "raptor-atlas");
var spineboy = this.add.spine(400, 600, 'spineboy-data', "spineboy-atlas");
this.add.text(10, 10, "Spine", { font: '16px Courier', fill: '#00ff00' });
let x = 25;
let y = 60;
for (let j = 0; j < 10; j++, y+= 600 / 10) {
for (let i = 0; i < 20; i++, x += 800 / 20) {
let obj = Math.random() > 0.5
? this.add.spine(x, y, 'spineboy-data', "spineboy-atlas")
: this.add.spine(x, y, 'raptor-data', "raptor-atlas");
obj.animationState.setAnimation(0, "walk", true);
obj.scale = 0.1;
}
x = 25;
}
debug = this.add.text(0, 600 - 40, "FPS: ");
}
function update () {
debug.setText("draw calls: " + spine.PolygonBatcher.getAndResetGlobalDrawCalls() + "\ndelta: " + game.loop.delta);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

View File

@ -1,7 +1,8 @@
import { SPINE_GAME_OBJECT_TYPE } from "./keys";
import { SpinePlugin } from "./SpinePlugin";
import { ComputedSizeMixin, DepthMixin, FlipMixin, ScrollFactorMixin, TransformMixin, VisibleMixin } from "./mixins";
import { AnimationState, AnimationStateData, Skeleton } from "@esotericsoftware/spine-core";
import { AnimationState, AnimationStateData, MathUtils, Skeleton } from "@esotericsoftware/spine-core";
import { Matrix4, Vector3 } from "@esotericsoftware/spine-webgl";
class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
constructor (scene: Phaser.Scene, type: string) {
@ -17,6 +18,7 @@ export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(Scro
blendMode = -1;
skeleton: Skeleton | null = null;
animationState: AnimationState | null = null;
private premultipliedAlpha = false;
constructor (scene: Phaser.Scene, private plugin: SpinePlugin, x: number, y: number, dataKey: string, atlasKey: string) {
super(scene, SPINE_GAME_OBJECT_TYPE);
@ -26,21 +28,94 @@ export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(Scro
setSkeleton (dataKey: string, atlasKey: string) {
if (dataKey && atlasKey) {
this.premultipliedAlpha = this.plugin.isAtlasPremultiplied(atlasKey);
this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
this.animationState = new AnimationState(new AnimationStateData(this.skeleton.data));
} else {
this.skeleton = null;
this.animationState = null;
}
}
preUpdate (time: number, delta: number) {
if (!this.skeleton || !this.animationState) return;
this.animationState.update(delta / 1000);
this.animationState.apply(this.skeleton);
this.skeleton.updateWorldTransform();
}
renderWebGL (renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix, container: SpineContainer) {
preDestroy () {
this.skeleton = null;
this.animationState = null;
// FIXME tear down any event emitters
}
willRender (camera: Phaser.Cameras.Scene2D.Camera) {
// FIXME
return true;
}
renderWebGL (renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
if (!this.skeleton || !this.animationState || !this.plugin.webGLRenderer) return;
let sceneRenderer = this.plugin.webGLRenderer;
if (renderer.newType) {
renderer.pipelines.clear();
sceneRenderer.begin();
}
camera.addToRenderList(src);
let transform = Phaser.GameObjects.GetCalcMatrix(src, camera, parentMatrix).calc;
let x = transform.tx;
let y = transform.ty;
let scaleX = transform.scaleX;
let scaleY = transform.scaleY;
let rotation = transform.rotationNormalized;
let cosRotation = Math.cos(rotation);
let sinRotation = Math.sin(rotation);
sceneRenderer.drawSkeleton(this.skeleton, this.premultipliedAlpha, -1, -1, (vertices, numVertices, stride) => {
for (let i = 0; i < numVertices; i += stride) {
let vx = vertices[i];
let vy = vertices[i + 1];
let vxOld = vx * scaleX, vyOld = vy * scaleY;
vx = vxOld * cosRotation - vyOld * sinRotation;
vy = vxOld * sinRotation + vyOld * cosRotation;
vx += x;
vy += y;
vertices[i] = vx;
vertices[i + 1] = vy;
}
});
if (!renderer.nextTypeMatch) {
sceneRenderer.end();
renderer.pipelines.rebind();
console.log("Draw calls: " + sceneRenderer.batcher.getDrawCalls());
}
}
renderCanvas (renderer: Phaser.Renderer.Canvas.CanvasRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
if (!this.skeleton || !this.animationState || !this.plugin.canvasRenderer) return;
let context = renderer.currentContext;
let skeletonRenderer = this.plugin.canvasRenderer;
(skeletonRenderer as any).ctx = context;
camera.addToRenderList(src);
let transform = Phaser.GameObjects.GetCalcMatrix(src, camera, parentMatrix).calc;
let skeleton = this.skeleton;
skeleton.x = transform.tx;
skeleton.y = transform.ty;
skeleton.scaleX = transform.scaleX;
skeleton.scaleY = transform.scaleY;
let root = skeleton.getRootBone()!;
root.rotation = -MathUtils.radiansToDegrees * transform.rotationNormalized;
this.skeleton.updateWorldTransform();
context.save();
skeletonRenderer.draw(skeleton);
context.restore();
}
}

View File

@ -29,9 +29,9 @@
import Phaser from "phaser";
import { SPINE_ATLAS_CACHE_KEY, SPINE_CONTAINER_TYPE, SPINE_GAME_OBJECT_TYPE, SPINE_ATLAS_TEXTURE_CACHE_KEY, SPINE_SKELETON_DATA_FILE_TYPE, SPINE_ATLAS_FILE_TYPE, SPINE_SKELETON_FILE_CACHE_KEY as SPINE_SKELETON_DATA_CACHE_KEY } from "./keys";
import { AtlasAttachmentLoader, GLTexture, SceneRenderer, Skeleton, SkeletonData, SkeletonDebugRenderer, SkeletonJson, SkeletonRenderer, TextureAtlas } from "@esotericsoftware/spine-webgl"
import { AtlasAttachmentLoader, Bone, GLTexture, SceneRenderer, Skeleton, SkeletonBinary, SkeletonData, SkeletonJson, TextureAtlas } from "@esotericsoftware/spine-webgl"
import { SpineGameObject } from "./SpineGameObject";
import { CanvasTexture } from "@esotericsoftware/spine-canvas";
import { CanvasTexture, SkeletonRenderer } from "@esotericsoftware/spine-canvas";
export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
game: Phaser.Game;
@ -39,7 +39,8 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
gl: WebGLRenderingContext | null;
textureManager: Phaser.Textures.TextureManager;
phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null;
sceneRenderer: SceneRenderer | null;
webGLRenderer: SceneRenderer | null;
canvasRenderer: SkeletonRenderer | null;
skeletonDataCache: Phaser.Cache.BaseCache;
atlasCache: Phaser.Cache.BaseCache;
@ -50,7 +51,8 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null;
this.textureManager = this.game.textures;
this.phaserRenderer = this.game.renderer;
this.sceneRenderer = null;
this.webGLRenderer = null;
this.canvasRenderer = null;
this.skeletonDataCache = this.game.cache.addCustom(SPINE_SKELETON_DATA_CACHE_KEY);
this.atlasCache = this.game.cache.addCustom(SPINE_ATLAS_CACHE_KEY);
@ -116,28 +118,16 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
}
boot () {
Skeleton.yDown = true;
if (this.isWebGL) {
// Monkeypatch the Spine setBlendMode functions, or batching is destroyed!
let setBlendMode = function (this: any, srcBlend: any, dstBlend: any) {
if (srcBlend !== this.srcBlend || dstBlend !== this.dstBlend) {
let gl = this.context.gl;
this.srcBlend = srcBlend;
this.dstBlend = dstBlend;
if (this.isDrawing) {
this.flush();
gl.blendFunc(this.srcBlend, this.dstBlend);
}
}
};
var sceneRenderer = this.sceneRenderer;
if (!sceneRenderer) {
sceneRenderer = new SceneRenderer((this.phaserRenderer! as Phaser.Renderer.WebGL.WebGLRenderer).canvas, this.gl!, true);
sceneRenderer.batcher.setBlendMode = setBlendMode;
(sceneRenderer as any).shapes.setBlendMode = setBlendMode;
if (!this.webGLRenderer) {
this.webGLRenderer = new SceneRenderer((this.phaserRenderer! as Phaser.Renderer.WebGL.WebGLRenderer).canvas, this.gl!, true);
}
this.game.scale.on(Phaser.Scale.Events.RESIZE, this.onResize, this);
} else {
if (!this.canvasRenderer) {
this.canvasRenderer = new SkeletonRenderer(this.scene.sys.context);
}
this.sceneRenderer = sceneRenderer;
}
var eventEmitter = this.systems.events;
@ -148,13 +138,15 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
onResize () {
var phaserRenderer = this.phaserRenderer;
var sceneRenderer = this.sceneRenderer;
var sceneRenderer = this.webGLRenderer;
if (phaserRenderer && sceneRenderer) {
var viewportWidth = phaserRenderer.width;
var viewportHeight = phaserRenderer.height;
sceneRenderer.camera.position.x = viewportWidth / 2;
sceneRenderer.camera.position.y = viewportHeight / 2;
sceneRenderer.camera.up.y = -1;
sceneRenderer.camera.direction.z = 1;
sceneRenderer.camera.setViewport(viewportWidth, viewportHeight);
}
}
@ -173,7 +165,13 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
gameDestroy () {
this.pluginManager.removeGameObject(SPINE_GAME_OBJECT_TYPE, true, true);
this.pluginManager.removeGameObject(SPINE_CONTAINER_TYPE, true, true);
if (this.sceneRenderer) this.sceneRenderer.dispose();
if (this.webGLRenderer) this.webGLRenderer.dispose();
}
isAtlasPremultiplied(atlasKey: string) {
let atlasFile = this.game.cache.text.get(atlasKey);
if (!atlasFile) return false;
return atlasFile.premultipliedAlpha;
}
createSkeleton (dataKey: string, atlasKey: string) {
@ -181,8 +179,8 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
if (this.atlasCache.exists(atlasKey)) {
atlas = this.atlasCache.get(atlasKey);
} else {
let atlasFile = this.game.cache.text.get(atlasKey) as string;
atlas = new TextureAtlas(atlasFile);
let atlasFile = this.game.cache.text.get(atlasKey) as { data: string, premultipliedAlpha: boolean };
atlas = new TextureAtlas(atlasFile.data);
if (this.isWebGL) {
let gl = this.gl!;
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
@ -207,8 +205,8 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
skeletonData = json.readSkeletonData(jsonFile);
} else {
let binaryFile = this.game.cache.binary.get(dataKey) as ArrayBuffer;
let binary = new SkeletonJson(new AtlasAttachmentLoader(atlas));
skeletonData = binary.readSkeletonData(binaryFile);
let binary = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
skeletonData = binary.readSkeletonData(new Uint8Array(binaryFile));
}
this.skeletonDataCache.add(dataKey, skeletonData);
}
@ -254,7 +252,7 @@ export class SpineSkeletonDataFile extends Phaser.Loader.MultiFile {
}
export class SpineAtlasFile extends Phaser.Loader.MultiFile {
constructor (loader: Phaser.Loader.LoaderPlugin, key: string, url: string, public premultipliedAlpha: boolean, xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
constructor (loader: Phaser.Loader.LoaderPlugin, key: string, url: string, public premultipliedAlpha: boolean = true, xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
super(loader, SPINE_ATLAS_FILE_TYPE, key, [
new Phaser.Loader.FileTypes.TextFile(loader, {
key: key,
@ -305,6 +303,10 @@ export class SpineAtlasFile extends Phaser.Loader.MultiFile {
textureManager.addImage(file.key, file.data);
}
} else {
file.data = {
data: file.data,
premultipliedAlpha: this.premultipliedAlpha || file.data.indexOf("pma: true") >= 0
};
file.addToCache();
}
}

View File

@ -2,6 +2,6 @@ export * from "./require-shim"
export * from "./SpinePlugin"
export * from "./mixins"
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-canvas";
export * from "@esotericsoftware/spine-webgl";
import { SpinePlugin } from "./SpinePlugin";
(window as any).spine = { SpinePlugin: SpinePlugin };

View File

@ -26,7 +26,7 @@ SOFTWARE.
let components = (Phaser.GameObjects.Components as any);
export const ComputedSize = components.ComputedSize;
export const Depth = components.ComputedSize;
export const Depth = components.Depth;
export const Flip = components.Flip;
export const ScrollFactor = components.ScrollFactor;
export const Transform = components.Transform;
@ -52,24 +52,11 @@ export function createMixin<
...component: GameObjectComponent[]
): Mixin<GameObjectComponent, GameObjectConstraint> {
return (BaseGameObject) => {
applyMixins(BaseGameObject, component);
(Phaser as any).Class.mixin(BaseGameObject, component);
return BaseGameObject as any;
};
}
function applyMixins (derivedCtor: any, constructors: any[]) {
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype || baseCtor).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype || baseCtor, name) ||
Object.create(null)
);
});
});
}
type ComputedSizeMixin = Mixin<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>;
export const ComputedSizeMixin: ComputedSizeMixin = createMixin<Phaser.GameObjects.Components.ComputedSize>(ComputedSize);
@ -77,14 +64,14 @@ type DepthMixin = Mixin<Phaser.GameObjects.Components.Depth, Phaser.GameObjects.
export const DepthMixin: DepthMixin = createMixin<Phaser.GameObjects.Components.Depth>(Depth);
type FlipMixin = Mixin<Phaser.GameObjects.Components.Flip, Phaser.GameObjects.GameObject>;
export const FlipMixin: FlipMixin = createMixin<Phaser.GameObjects.Components.Flip>(Depth);
export const FlipMixin: FlipMixin = createMixin<Phaser.GameObjects.Components.Flip>(Flip);
type ScrollFactorMixin = Mixin<Phaser.GameObjects.Components.ScrollFactor, Phaser.GameObjects.GameObject>;
export const ScrollFactorMixin: ScrollFactorMixin = createMixin<Phaser.GameObjects.Components.ScrollFactor>(Depth);
export const ScrollFactorMixin: ScrollFactorMixin = createMixin<Phaser.GameObjects.Components.ScrollFactor>(ScrollFactor);
type TransformMixin = Mixin<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>;
export const TransformMixin: TransformMixin = createMixin<Phaser.GameObjects.Components.Transform>(Transform);
type VisibleMixin = Mixin<Phaser.GameObjects.Components.Visible, Phaser.GameObjects.GameObject>;
export const VisibleMixin: VisibleMixin = createMixin<Phaser.GameObjects.Components.Visible>(Depth);
export const VisibleMixin: VisibleMixin = createMixin<Phaser.GameObjects.Components.Visible>(Visible);

View File

@ -36,6 +36,7 @@ import { ManagedWebGLRenderingContext } from "./WebGL";
export class PolygonBatcher implements Disposable {
private context: ManagedWebGLRenderingContext;
private drawCalls = 0;
private static globalDrawCalls = 0;
private isDrawing = false;
private mesh: Mesh;
private shader: Shader | null = null;
@ -120,6 +121,7 @@ export class PolygonBatcher implements Disposable {
this.mesh.setVerticesLength(0);
this.mesh.setIndicesLength(0);
this.drawCalls++;
PolygonBatcher.globalDrawCalls++;
}
end () {
@ -138,6 +140,12 @@ export class PolygonBatcher implements Disposable {
return this.drawCalls;
}
static getAndResetGlobalDrawCalls () {
let result = PolygonBatcher.globalDrawCalls;
PolygonBatcher.globalDrawCalls = 0;
return result;
}
dispose () {
this.mesh.dispose();
}

View File

@ -34,7 +34,7 @@ import { PolygonBatcher } from "./PolygonBatcher";
import { Shader } from "./Shader";
import { ShapeRenderer } from "./ShapeRenderer";
import { SkeletonDebugRenderer } from "./SkeletonDebugRenderer";
import { SkeletonRenderer } from "./SkeletonRenderer";
import { SkeletonRenderer, VertexTransformer } from "./SkeletonRenderer";
import { ManagedWebGLRenderingContext } from "./WebGL";
;
@ -86,10 +86,10 @@ export class SceneRenderer implements Disposable {
this.enableRenderer(this.batcher);
}
drawSkeleton (skeleton: Skeleton, premultipliedAlpha = false, slotRangeStart = -1, slotRangeEnd = -1) {
drawSkeleton (skeleton: Skeleton, premultipliedAlpha = false, slotRangeStart = -1, slotRangeEnd = -1, transform: VertexTransformer | null = null) {
this.enableRenderer(this.batcher);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd);
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd, transform);
}
drawSkeletonDebug (skeleton: Skeleton, premultipliedAlpha = false, ignoredBones?: Array<string>) {

View File

@ -37,6 +37,8 @@ class Renderable {
constructor (public vertices: NumberArrayLike, public numVertices: number, public numFloats: number) { }
};
export type VertexTransformer = (vertices: NumberArrayLike, numVertices: number, stride: number) => void;
export class SkeletonRenderer {
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
@ -60,7 +62,7 @@ export class SkeletonRenderer {
this.vertices = Utils.newFloatArray(this.vertexSize * 1024);
}
draw (batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart: number = -1, slotRangeEnd: number = -1) {
draw (batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart: number = -1, slotRangeEnd: number = -1, transformer: VertexTransformer | null = null) {
let clipper = this.clipper;
let premultipliedAlpha = this.premultipliedAlpha;
let twoColorTint = this.twoColorTint;
@ -174,6 +176,7 @@ export class SkeletonRenderer {
clipper.clipTriangles(renderable.vertices, renderable.numFloats, triangles, triangles.length, uvs, finalColor, darkColor, twoColorTint);
let clippedVertices = new Float32Array(clipper.clippedVertices);
let clippedTriangles = clipper.clippedTriangles;
if (transformer) transformer(renderable.vertices, renderable.numFloats, vertexSize);
batcher.draw(texture, clippedVertices, clippedTriangles);
} else {
let verts = renderable.vertices;
@ -201,6 +204,7 @@ export class SkeletonRenderer {
}
}
let view = (renderable.vertices as Float32Array).subarray(0, renderable.numFloats);
if (transformer) transformer(renderable.vertices, renderable.numFloats, vertexSize);
batcher.draw(texture, view, triangles);
}
}