[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: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: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", "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": "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:modules": "npm run build:modules -- --watch",
"dev:canvas": "npm run build:canvas -- --watch", "dev:canvas": "npm run build:canvas -- --watch",
@ -54,13 +54,12 @@
"spine-webgl" "spine-webgl"
], ],
"devDependencies": { "devDependencies": {
"concurrently": "^6.2.1", "@types/offscreencanvas": "^2019.6.4",
"concurrently": "^7.6.0",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"esbuild": "^0.16.3", "esbuild": "^0.16.4",
"live-server": "^1.2.1", "live-server": "^1.2.2",
"npx": "^10.2.2",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^4.3.5", "typescript": "^4.9.4"
"@types/offscreencanvas": "^2019.6.4"
} }
} }

View File

@ -8,10 +8,11 @@ var config = {
type: Phaser.AUTO, type: Phaser.AUTO,
width: 800, width: 800,
height: 600, height: 600,
type: Phaser.CANVAS, type: Phaser.WEBGL,
scene: { scene: {
preload: preload, preload: preload,
create: create, create: create,
update: update,
}, },
plugins: { plugins: {
scene: [ scene: [
@ -20,18 +21,35 @@ var config = {
} }
}; };
var game = new Phaser.Game(config); let game = new Phaser.Game(config);
let debug;
function preload () { function preload () {
this.load.spineJson("raptor-data", "assets/raptor-pro.json"); this.load.spineJson("raptor-data", "assets/raptor-pro.json");
this.load.spineAtlas("raptor-atlas", "assets/raptor-pma.atlas"); this.load.spineAtlas("raptor-atlas", "assets/raptor-pma.atlas");
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel"); this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas"); this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
this.load.image("nyan", "nyan.png");
let canvas = document.querySelector("#game-canvas");
} }
function create () { function create () {
let plugin = this.spine; let plugin = this.spine;
var raptor = this.add.spine(400, 600, 'raptor-data', "raptor-atlas"); let x = 25;
var spineboy = this.add.spine(400, 600, 'spineboy-data', "spineboy-atlas"); let y = 60;
this.add.text(10, 10, "Spine", { font: '16px Courier', fill: '#00ff00' }); 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 { SPINE_GAME_OBJECT_TYPE } from "./keys";
import { SpinePlugin } from "./SpinePlugin"; import { SpinePlugin } from "./SpinePlugin";
import { ComputedSizeMixin, DepthMixin, FlipMixin, ScrollFactorMixin, TransformMixin, VisibleMixin } from "./mixins"; 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 { class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
constructor (scene: Phaser.Scene, type: string) { constructor (scene: Phaser.Scene, type: string) {
@ -17,6 +18,7 @@ export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(Scro
blendMode = -1; blendMode = -1;
skeleton: Skeleton | null = null; skeleton: Skeleton | null = null;
animationState: AnimationState | null = null; animationState: AnimationState | null = null;
private premultipliedAlpha = false;
constructor (scene: Phaser.Scene, private plugin: SpinePlugin, x: number, y: number, dataKey: string, atlasKey: string) { constructor (scene: Phaser.Scene, private plugin: SpinePlugin, x: number, y: number, dataKey: string, atlasKey: string) {
super(scene, SPINE_GAME_OBJECT_TYPE); super(scene, SPINE_GAME_OBJECT_TYPE);
@ -26,21 +28,94 @@ export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(Scro
setSkeleton (dataKey: string, atlasKey: string) { setSkeleton (dataKey: string, atlasKey: string) {
if (dataKey && atlasKey) { if (dataKey && atlasKey) {
this.premultipliedAlpha = this.plugin.isAtlasPremultiplied(atlasKey);
this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey); this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
this.animationState = new AnimationState(new AnimationStateData(this.skeleton.data)); this.animationState = new AnimationState(new AnimationStateData(this.skeleton.data));
} else { } else {
this.skeleton = null; this.skeleton = null;
this.animationState = null;
} }
} }
preUpdate (time: number, delta: number) { 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) { 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 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 { 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 { SpineGameObject } from "./SpineGameObject";
import { CanvasTexture } from "@esotericsoftware/spine-canvas"; import { CanvasTexture, SkeletonRenderer } from "@esotericsoftware/spine-canvas";
export class SpinePlugin extends Phaser.Plugins.ScenePlugin { export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
game: Phaser.Game; game: Phaser.Game;
@ -39,7 +39,8 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
gl: WebGLRenderingContext | null; gl: WebGLRenderingContext | null;
textureManager: Phaser.Textures.TextureManager; textureManager: Phaser.Textures.TextureManager;
phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null; phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null;
sceneRenderer: SceneRenderer | null; webGLRenderer: SceneRenderer | null;
canvasRenderer: SkeletonRenderer | null;
skeletonDataCache: Phaser.Cache.BaseCache; skeletonDataCache: Phaser.Cache.BaseCache;
atlasCache: 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.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null;
this.textureManager = this.game.textures; this.textureManager = this.game.textures;
this.phaserRenderer = this.game.renderer; 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.skeletonDataCache = this.game.cache.addCustom(SPINE_SKELETON_DATA_CACHE_KEY);
this.atlasCache = this.game.cache.addCustom(SPINE_ATLAS_CACHE_KEY); this.atlasCache = this.game.cache.addCustom(SPINE_ATLAS_CACHE_KEY);
@ -116,28 +118,16 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
} }
boot () { boot () {
Skeleton.yDown = true;
if (this.isWebGL) { if (this.isWebGL) {
// Monkeypatch the Spine setBlendMode functions, or batching is destroyed! if (!this.webGLRenderer) {
let setBlendMode = function (this: any, srcBlend: any, dstBlend: any) { this.webGLRenderer = new SceneRenderer((this.phaserRenderer! as Phaser.Renderer.WebGL.WebGLRenderer).canvas, this.gl!, true);
if (srcBlend !== this.srcBlend || dstBlend !== this.dstBlend) { }
let gl = this.context.gl; this.game.scale.on(Phaser.Scale.Events.RESIZE, this.onResize, this);
this.srcBlend = srcBlend; } else {
this.dstBlend = dstBlend; if (!this.canvasRenderer) {
if (this.isDrawing) { this.canvasRenderer = new SkeletonRenderer(this.scene.sys.context);
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;
} }
this.sceneRenderer = sceneRenderer;
} }
var eventEmitter = this.systems.events; var eventEmitter = this.systems.events;
@ -148,13 +138,15 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
onResize () { onResize () {
var phaserRenderer = this.phaserRenderer; var phaserRenderer = this.phaserRenderer;
var sceneRenderer = this.sceneRenderer; var sceneRenderer = this.webGLRenderer;
if (phaserRenderer && sceneRenderer) { if (phaserRenderer && sceneRenderer) {
var viewportWidth = phaserRenderer.width; var viewportWidth = phaserRenderer.width;
var viewportHeight = phaserRenderer.height; var viewportHeight = phaserRenderer.height;
sceneRenderer.camera.position.x = viewportWidth / 2; sceneRenderer.camera.position.x = viewportWidth / 2;
sceneRenderer.camera.position.y = viewportHeight / 2; sceneRenderer.camera.position.y = viewportHeight / 2;
sceneRenderer.camera.up.y = -1;
sceneRenderer.camera.direction.z = 1;
sceneRenderer.camera.setViewport(viewportWidth, viewportHeight); sceneRenderer.camera.setViewport(viewportWidth, viewportHeight);
} }
} }
@ -173,7 +165,13 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
gameDestroy () { gameDestroy () {
this.pluginManager.removeGameObject(SPINE_GAME_OBJECT_TYPE, true, true); this.pluginManager.removeGameObject(SPINE_GAME_OBJECT_TYPE, true, true);
this.pluginManager.removeGameObject(SPINE_CONTAINER_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) { createSkeleton (dataKey: string, atlasKey: string) {
@ -181,8 +179,8 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
if (this.atlasCache.exists(atlasKey)) { if (this.atlasCache.exists(atlasKey)) {
atlas = this.atlasCache.get(atlasKey); atlas = this.atlasCache.get(atlasKey);
} else { } else {
let atlasFile = this.game.cache.text.get(atlasKey) as string; let atlasFile = this.game.cache.text.get(atlasKey) as { data: string, premultipliedAlpha: boolean };
atlas = new TextureAtlas(atlasFile); atlas = new TextureAtlas(atlasFile.data);
if (this.isWebGL) { if (this.isWebGL) {
let gl = this.gl!; let gl = this.gl!;
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
@ -207,8 +205,8 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
skeletonData = json.readSkeletonData(jsonFile); skeletonData = json.readSkeletonData(jsonFile);
} else { } else {
let binaryFile = this.game.cache.binary.get(dataKey) as ArrayBuffer; let binaryFile = this.game.cache.binary.get(dataKey) as ArrayBuffer;
let binary = new SkeletonJson(new AtlasAttachmentLoader(atlas)); let binary = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
skeletonData = binary.readSkeletonData(binaryFile); skeletonData = binary.readSkeletonData(new Uint8Array(binaryFile));
} }
this.skeletonDataCache.add(dataKey, skeletonData); this.skeletonDataCache.add(dataKey, skeletonData);
} }
@ -254,7 +252,7 @@ export class SpineSkeletonDataFile extends Phaser.Loader.MultiFile {
} }
export class SpineAtlasFile 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, [ super(loader, SPINE_ATLAS_FILE_TYPE, key, [
new Phaser.Loader.FileTypes.TextFile(loader, { new Phaser.Loader.FileTypes.TextFile(loader, {
key: key, key: key,
@ -305,6 +303,10 @@ export class SpineAtlasFile extends Phaser.Loader.MultiFile {
textureManager.addImage(file.key, file.data); textureManager.addImage(file.key, file.data);
} }
} else { } else {
file.data = {
data: file.data,
premultipliedAlpha: this.premultipliedAlpha || file.data.indexOf("pma: true") >= 0
};
file.addToCache(); file.addToCache();
} }
} }

View File

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

View File

@ -26,7 +26,7 @@ SOFTWARE.
let components = (Phaser.GameObjects.Components as any); let components = (Phaser.GameObjects.Components as any);
export const ComputedSize = components.ComputedSize; export const ComputedSize = components.ComputedSize;
export const Depth = components.ComputedSize; export const Depth = components.Depth;
export const Flip = components.Flip; export const Flip = components.Flip;
export const ScrollFactor = components.ScrollFactor; export const ScrollFactor = components.ScrollFactor;
export const Transform = components.Transform; export const Transform = components.Transform;
@ -52,24 +52,11 @@ export function createMixin<
...component: GameObjectComponent[] ...component: GameObjectComponent[]
): Mixin<GameObjectComponent, GameObjectConstraint> { ): Mixin<GameObjectComponent, GameObjectConstraint> {
return (BaseGameObject) => { return (BaseGameObject) => {
applyMixins(BaseGameObject, component); (Phaser as any).Class.mixin(BaseGameObject, component);
return BaseGameObject as any; 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>; type ComputedSizeMixin = Mixin<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>;
export const ComputedSizeMixin: ComputedSizeMixin = createMixin<Phaser.GameObjects.Components.ComputedSize>(ComputedSize); 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); export const DepthMixin: DepthMixin = createMixin<Phaser.GameObjects.Components.Depth>(Depth);
type FlipMixin = Mixin<Phaser.GameObjects.Components.Flip, Phaser.GameObjects.GameObject>; 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>; 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>; type TransformMixin = Mixin<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>;
export const TransformMixin: TransformMixin = createMixin<Phaser.GameObjects.Components.Transform>(Transform); export const TransformMixin: TransformMixin = createMixin<Phaser.GameObjects.Components.Transform>(Transform);
type VisibleMixin = Mixin<Phaser.GameObjects.Components.Visible, Phaser.GameObjects.GameObject>; 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 { export class PolygonBatcher implements Disposable {
private context: ManagedWebGLRenderingContext; private context: ManagedWebGLRenderingContext;
private drawCalls = 0; private drawCalls = 0;
private static globalDrawCalls = 0;
private isDrawing = false; private isDrawing = false;
private mesh: Mesh; private mesh: Mesh;
private shader: Shader | null = null; private shader: Shader | null = null;
@ -120,6 +121,7 @@ export class PolygonBatcher implements Disposable {
this.mesh.setVerticesLength(0); this.mesh.setVerticesLength(0);
this.mesh.setIndicesLength(0); this.mesh.setIndicesLength(0);
this.drawCalls++; this.drawCalls++;
PolygonBatcher.globalDrawCalls++;
} }
end () { end () {
@ -138,6 +140,12 @@ export class PolygonBatcher implements Disposable {
return this.drawCalls; return this.drawCalls;
} }
static getAndResetGlobalDrawCalls () {
let result = PolygonBatcher.globalDrawCalls;
PolygonBatcher.globalDrawCalls = 0;
return result;
}
dispose () { dispose () {
this.mesh.dispose(); this.mesh.dispose();
} }

View File

@ -34,7 +34,7 @@ import { PolygonBatcher } from "./PolygonBatcher";
import { Shader } from "./Shader"; import { Shader } from "./Shader";
import { ShapeRenderer } from "./ShapeRenderer"; import { ShapeRenderer } from "./ShapeRenderer";
import { SkeletonDebugRenderer } from "./SkeletonDebugRenderer"; import { SkeletonDebugRenderer } from "./SkeletonDebugRenderer";
import { SkeletonRenderer } from "./SkeletonRenderer"; import { SkeletonRenderer, VertexTransformer } from "./SkeletonRenderer";
import { ManagedWebGLRenderingContext } from "./WebGL"; import { ManagedWebGLRenderingContext } from "./WebGL";
; ;
@ -86,10 +86,10 @@ export class SceneRenderer implements Disposable {
this.enableRenderer(this.batcher); 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.enableRenderer(this.batcher);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha; 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>) { 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) { } constructor (public vertices: NumberArrayLike, public numVertices: number, public numFloats: number) { }
}; };
export type VertexTransformer = (vertices: NumberArrayLike, numVertices: number, stride: number) => void;
export class SkeletonRenderer { export class SkeletonRenderer {
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0]; static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
@ -60,7 +62,7 @@ export class SkeletonRenderer {
this.vertices = Utils.newFloatArray(this.vertexSize * 1024); 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 clipper = this.clipper;
let premultipliedAlpha = this.premultipliedAlpha; let premultipliedAlpha = this.premultipliedAlpha;
let twoColorTint = this.twoColorTint; let twoColorTint = this.twoColorTint;
@ -174,6 +176,7 @@ export class SkeletonRenderer {
clipper.clipTriangles(renderable.vertices, renderable.numFloats, triangles, triangles.length, uvs, finalColor, darkColor, twoColorTint); clipper.clipTriangles(renderable.vertices, renderable.numFloats, triangles, triangles.length, uvs, finalColor, darkColor, twoColorTint);
let clippedVertices = new Float32Array(clipper.clippedVertices); let clippedVertices = new Float32Array(clipper.clippedVertices);
let clippedTriangles = clipper.clippedTriangles; let clippedTriangles = clipper.clippedTriangles;
if (transformer) transformer(renderable.vertices, renderable.numFloats, vertexSize);
batcher.draw(texture, clippedVertices, clippedTriangles); batcher.draw(texture, clippedVertices, clippedTriangles);
} else { } else {
let verts = renderable.vertices; let verts = renderable.vertices;
@ -201,6 +204,7 @@ export class SkeletonRenderer {
} }
} }
let view = (renderable.vertices as Float32Array).subarray(0, renderable.numFloats); let view = (renderable.vertices as Float32Array).subarray(0, renderable.numFloats);
if (transformer) transformer(renderable.vertices, renderable.numFloats, vertexSize);
batcher.draw(texture, view, triangles); batcher.draw(texture, view, triangles);
} }
} }