mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-07 11:16:53 +08:00
[phaser] Closes #2348, SpineGameObject.willRender() didn't reset state correctly in case an object is inside a container.
This commit is contained in:
parent
c56c9b5cef
commit
e404d0bede
@ -29,90 +29,138 @@
|
|||||||
|
|
||||||
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, AlphaMixin, OriginMixin } from "./mixins";
|
import {
|
||||||
import { AnimationState, AnimationStateData, Bone, MathUtils, Skeleton, Skin, Vector2 } from "@esotericsoftware/spine-core";
|
ComputedSizeMixin,
|
||||||
|
DepthMixin,
|
||||||
|
FlipMixin,
|
||||||
|
ScrollFactorMixin,
|
||||||
|
TransformMixin,
|
||||||
|
VisibleMixin,
|
||||||
|
AlphaMixin,
|
||||||
|
OriginMixin,
|
||||||
|
} from "./mixins";
|
||||||
|
import {
|
||||||
|
AnimationState,
|
||||||
|
AnimationStateData,
|
||||||
|
Bone,
|
||||||
|
MathUtils,
|
||||||
|
Skeleton,
|
||||||
|
Skin,
|
||||||
|
Vector2,
|
||||||
|
} from "@esotericsoftware/spine-core";
|
||||||
|
|
||||||
class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
|
class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
|
||||||
constructor (scene: Phaser.Scene, type: string) {
|
constructor(scene: Phaser.Scene, type: string) {
|
||||||
super(scene, type);
|
super(scene, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A bounds provider calculates the bounding box for a skeleton, which is then assigned as the size of the SpineGameObject. */
|
/** A bounds provider calculates the bounding box for a skeleton, which is then assigned as the size of the SpineGameObject. */
|
||||||
export interface SpineGameObjectBoundsProvider {
|
export interface SpineGameObjectBoundsProvider {
|
||||||
// Returns the bounding box for the skeleton, in skeleton space.
|
// Returns the bounding box for the skeleton, in skeleton space.
|
||||||
calculateBounds (gameObject: SpineGameObject): { x: number, y: number, width: number, height: number };
|
calculateBounds(gameObject: SpineGameObject): {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A bounds provider that calculates the bounding box from the setup pose. */
|
/** A bounds provider that calculates the bounding box from the setup pose. */
|
||||||
export class SetupPoseBoundsProvider implements SpineGameObjectBoundsProvider {
|
export class SetupPoseBoundsProvider implements SpineGameObjectBoundsProvider {
|
||||||
calculateBounds (gameObject: SpineGameObject) {
|
calculateBounds(gameObject: SpineGameObject) {
|
||||||
if (!gameObject.skeleton) return { x: 0, y: 0, width: 0, height: 0 };
|
if (!gameObject.skeleton) return { x: 0, y: 0, width: 0, height: 0 };
|
||||||
// Make a copy of animation state and skeleton as this might be called while
|
// Make a copy of animation state and skeleton as this might be called while
|
||||||
// the skeleton in the GameObject has already been heavily modified. We can not
|
// the skeleton in the GameObject has already been heavily modified. We can not
|
||||||
// reconstruct that state.
|
// reconstruct that state.
|
||||||
const skeleton = new Skeleton(gameObject.skeleton.data);
|
const skeleton = new Skeleton(gameObject.skeleton.data);
|
||||||
skeleton.setToSetupPose();
|
skeleton.setToSetupPose();
|
||||||
skeleton.updateWorldTransform();
|
skeleton.updateWorldTransform();
|
||||||
const bounds = skeleton.getBoundsRect();
|
const bounds = skeleton.getBoundsRect();
|
||||||
return bounds.width == Number.NEGATIVE_INFINITY ? { x: 0, y: 0, width: 0, height: 0 } : bounds;
|
return bounds.width == Number.NEGATIVE_INFINITY
|
||||||
}
|
? { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
: bounds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A bounds provider that calculates the bounding box by taking the maximumg bounding box for a combination of skins and specific animation. */
|
/** A bounds provider that calculates the bounding box by taking the maximumg bounding box for a combination of skins and specific animation. */
|
||||||
export class SkinsAndAnimationBoundsProvider implements SpineGameObjectBoundsProvider {
|
export class SkinsAndAnimationBoundsProvider
|
||||||
/**
|
implements SpineGameObjectBoundsProvider
|
||||||
* @param animation The animation to use for calculating the bounds. If null, the setup pose is used.
|
{
|
||||||
* @param skins The skins to use for calculating the bounds. If empty, the default skin is used.
|
/**
|
||||||
* @param timeStep The time step to use for calculating the bounds. A smaller time step means more precision, but slower calculation.
|
* @param animation The animation to use for calculating the bounds. If null, the setup pose is used.
|
||||||
*/
|
* @param skins The skins to use for calculating the bounds. If empty, the default skin is used.
|
||||||
constructor (private animation: string | null, private skins: string[] = [], private timeStep: number = 0.05) {
|
* @param timeStep The time step to use for calculating the bounds. A smaller time step means more precision, but slower calculation.
|
||||||
}
|
*/
|
||||||
|
constructor(
|
||||||
|
private animation: string | null,
|
||||||
|
private skins: string[] = [],
|
||||||
|
private timeStep: number = 0.05
|
||||||
|
) {}
|
||||||
|
|
||||||
calculateBounds (gameObject: SpineGameObject): { x: number; y: number; width: number; height: number; } {
|
calculateBounds(gameObject: SpineGameObject): {
|
||||||
if (!gameObject.skeleton || !gameObject.animationState) return { x: 0, y: 0, width: 0, height: 0 };
|
x: number;
|
||||||
// Make a copy of animation state and skeleton as this might be called while
|
y: number;
|
||||||
// the skeleton in the GameObject has already been heavily modified. We can not
|
width: number;
|
||||||
// reconstruct that state.
|
height: number;
|
||||||
const animationState = new AnimationState(gameObject.animationState.data);
|
} {
|
||||||
const skeleton = new Skeleton(gameObject.skeleton.data);
|
if (!gameObject.skeleton || !gameObject.animationState)
|
||||||
const data = skeleton.data;
|
return { x: 0, y: 0, width: 0, height: 0 };
|
||||||
if (this.skins.length > 0) {
|
// Make a copy of animation state and skeleton as this might be called while
|
||||||
let customSkin = new Skin("custom-skin");
|
// the skeleton in the GameObject has already been heavily modified. We can not
|
||||||
for (const skinName of this.skins) {
|
// reconstruct that state.
|
||||||
const skin = data.findSkin(skinName);
|
const animationState = new AnimationState(gameObject.animationState.data);
|
||||||
if (skin == null) continue;
|
const skeleton = new Skeleton(gameObject.skeleton.data);
|
||||||
customSkin.addSkin(skin);
|
const data = skeleton.data;
|
||||||
}
|
if (this.skins.length > 0) {
|
||||||
skeleton.setSkin(customSkin);
|
let customSkin = new Skin("custom-skin");
|
||||||
}
|
for (const skinName of this.skins) {
|
||||||
skeleton.setToSetupPose();
|
const skin = data.findSkin(skinName);
|
||||||
|
if (skin == null) continue;
|
||||||
|
customSkin.addSkin(skin);
|
||||||
|
}
|
||||||
|
skeleton.setSkin(customSkin);
|
||||||
|
}
|
||||||
|
skeleton.setToSetupPose();
|
||||||
|
|
||||||
const animation = this.animation != null ? data.findAnimation(this.animation!) : null;
|
const animation =
|
||||||
if (animation == null) {
|
this.animation != null ? data.findAnimation(this.animation!) : null;
|
||||||
skeleton.updateWorldTransform();
|
if (animation == null) {
|
||||||
const bounds = skeleton.getBoundsRect();
|
skeleton.updateWorldTransform();
|
||||||
return bounds.width == Number.NEGATIVE_INFINITY ? { x: 0, y: 0, width: 0, height: 0 } : bounds;
|
const bounds = skeleton.getBoundsRect();
|
||||||
} else {
|
return bounds.width == Number.NEGATIVE_INFINITY
|
||||||
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
|
? { x: 0, y: 0, width: 0, height: 0 }
|
||||||
animationState.clearTracks();
|
: bounds;
|
||||||
animationState.setAnimationWith(0, animation, false);
|
} else {
|
||||||
const steps = Math.max(animation.duration / this.timeStep, 1.0);
|
let minX = Number.POSITIVE_INFINITY,
|
||||||
for (let i = 0; i < steps; i++) {
|
minY = Number.POSITIVE_INFINITY,
|
||||||
animationState.update(i > 0 ? this.timeStep : 0);
|
maxX = Number.NEGATIVE_INFINITY,
|
||||||
animationState.apply(skeleton);
|
maxY = Number.NEGATIVE_INFINITY;
|
||||||
skeleton.updateWorldTransform();
|
animationState.clearTracks();
|
||||||
|
animationState.setAnimationWith(0, animation, false);
|
||||||
|
const steps = Math.max(animation.duration / this.timeStep, 1.0);
|
||||||
|
for (let i = 0; i < steps; i++) {
|
||||||
|
animationState.update(i > 0 ? this.timeStep : 0);
|
||||||
|
animationState.apply(skeleton);
|
||||||
|
skeleton.updateWorldTransform();
|
||||||
|
|
||||||
const bounds = skeleton.getBoundsRect();
|
const bounds = skeleton.getBoundsRect();
|
||||||
minX = Math.min(minX, bounds.x);
|
minX = Math.min(minX, bounds.x);
|
||||||
minY = Math.min(minY, bounds.y);
|
minY = Math.min(minY, bounds.y);
|
||||||
maxX = Math.max(maxX, minX + bounds.width);
|
maxX = Math.max(maxX, minX + bounds.width);
|
||||||
maxY = Math.max(maxY, minY + bounds.height);
|
maxY = Math.max(maxY, minY + bounds.height);
|
||||||
}
|
}
|
||||||
const bounds = { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
const bounds = {
|
||||||
return bounds.width == Number.NEGATIVE_INFINITY ? { x: 0, y: 0, width: 0, height: 0 } : bounds;
|
x: minX,
|
||||||
}
|
y: minY,
|
||||||
}
|
width: maxX - minX,
|
||||||
|
height: maxY - minY,
|
||||||
|
};
|
||||||
|
return bounds.width == Number.NEGATIVE_INFINITY
|
||||||
|
? { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
: bounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,147 +184,215 @@ export class SkinsAndAnimationBoundsProvider implements SpineGameObjectBoundsPro
|
|||||||
*
|
*
|
||||||
* See {@link skeletonToPhaserWorldCoordinates}, {@link phaserWorldCoordinatesToSkeleton}, and {@link phaserWorldCoordinatesToBoneLocal.}
|
* See {@link skeletonToPhaserWorldCoordinates}, {@link phaserWorldCoordinatesToSkeleton}, and {@link phaserWorldCoordinatesToBoneLocal.}
|
||||||
*/
|
*/
|
||||||
export class SpineGameObject extends DepthMixin(OriginMixin(ComputedSizeMixin(FlipMixin(ScrollFactorMixin(TransformMixin(VisibleMixin(AlphaMixin(BaseSpineGameObject)))))))) {
|
export class SpineGameObject extends DepthMixin(
|
||||||
blendMode = -1;
|
OriginMixin(
|
||||||
skeleton: Skeleton;
|
ComputedSizeMixin(
|
||||||
animationStateData: AnimationStateData;
|
FlipMixin(
|
||||||
animationState: AnimationState;
|
ScrollFactorMixin(
|
||||||
beforeUpdateWorldTransforms: (object: SpineGameObject) => void = () => { };
|
TransformMixin(VisibleMixin(AlphaMixin(BaseSpineGameObject)))
|
||||||
afterUpdateWorldTransforms: (object: SpineGameObject) => void = () => { };
|
)
|
||||||
private premultipliedAlpha = false;
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
blendMode = -1;
|
||||||
|
skeleton: Skeleton;
|
||||||
|
animationStateData: AnimationStateData;
|
||||||
|
animationState: AnimationState;
|
||||||
|
beforeUpdateWorldTransforms: (object: SpineGameObject) => void = () => {};
|
||||||
|
afterUpdateWorldTransforms: (object: SpineGameObject) => void = () => {};
|
||||||
|
private premultipliedAlpha = false;
|
||||||
|
|
||||||
constructor (scene: Phaser.Scene, private plugin: SpinePlugin, x: number, y: number, dataKey: string, atlasKey: string, public boundsProvider: SpineGameObjectBoundsProvider = new SetupPoseBoundsProvider()) {
|
constructor(
|
||||||
super(scene, SPINE_GAME_OBJECT_TYPE);
|
scene: Phaser.Scene,
|
||||||
this.setPosition(x, y);
|
private plugin: SpinePlugin,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
dataKey: string,
|
||||||
|
atlasKey: string,
|
||||||
|
public boundsProvider: SpineGameObjectBoundsProvider = new SetupPoseBoundsProvider()
|
||||||
|
) {
|
||||||
|
super(scene, SPINE_GAME_OBJECT_TYPE);
|
||||||
|
this.setPosition(x, y);
|
||||||
|
|
||||||
this.premultipliedAlpha = this.plugin.isAtlasPremultiplied(atlasKey);
|
this.premultipliedAlpha = this.plugin.isAtlasPremultiplied(atlasKey);
|
||||||
this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
|
this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
|
||||||
this.animationStateData = new AnimationStateData(this.skeleton.data);
|
this.animationStateData = new AnimationStateData(this.skeleton.data);
|
||||||
this.animationState = new AnimationState(this.animationStateData);
|
this.animationState = new AnimationState(this.animationStateData);
|
||||||
this.skeleton.updateWorldTransform();
|
this.skeleton.updateWorldTransform();
|
||||||
this.updateSize();
|
this.updateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSize () {
|
updateSize() {
|
||||||
if (!this.skeleton) return;
|
if (!this.skeleton) return;
|
||||||
let bounds = this.boundsProvider.calculateBounds(this);
|
let bounds = this.boundsProvider.calculateBounds(this);
|
||||||
// For some reason the TS compiler and the ComputedSize mixin don't work well together and we have
|
// For some reason the TS compiler and the ComputedSize mixin don't work well together and we have
|
||||||
// to cast to any.
|
// to cast to any.
|
||||||
let self = this as any;
|
let self = this as any;
|
||||||
self.width = bounds.width;
|
self.width = bounds.width;
|
||||||
self.height = bounds.height;
|
self.height = bounds.height;
|
||||||
this.displayOriginX = -bounds.x;
|
this.displayOriginX = -bounds.x;
|
||||||
this.displayOriginY = -bounds.y;
|
this.displayOriginY = -bounds.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Converts a point from the skeleton coordinate system to the Phaser world coordinate system. */
|
/** Converts a point from the skeleton coordinate system to the Phaser world coordinate system. */
|
||||||
skeletonToPhaserWorldCoordinates (point: { x: number, y: number }) {
|
skeletonToPhaserWorldCoordinates(point: { x: number; y: number }) {
|
||||||
let transform = this.getWorldTransformMatrix();
|
let transform = this.getWorldTransformMatrix();
|
||||||
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
|
let a = transform.a,
|
||||||
let x = point.x
|
b = transform.b,
|
||||||
let y = point.y
|
c = transform.c,
|
||||||
point.x = x * a + y * c + tx;
|
d = transform.d,
|
||||||
point.y = x * b + y * d + ty;
|
tx = transform.tx,
|
||||||
}
|
ty = transform.ty;
|
||||||
|
let x = point.x;
|
||||||
|
let y = point.y;
|
||||||
|
point.x = x * a + y * c + tx;
|
||||||
|
point.y = x * b + y * d + ty;
|
||||||
|
}
|
||||||
|
|
||||||
/** Converts a point from the Phaser world coordinate system to the skeleton coordinate system. */
|
/** Converts a point from the Phaser world coordinate system to the skeleton coordinate system. */
|
||||||
phaserWorldCoordinatesToSkeleton (point: { x: number, y: number }) {
|
phaserWorldCoordinatesToSkeleton(point: { x: number; y: number }) {
|
||||||
let transform = this.getWorldTransformMatrix();
|
let transform = this.getWorldTransformMatrix();
|
||||||
transform = transform.invert();
|
transform = transform.invert();
|
||||||
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
|
let a = transform.a,
|
||||||
let x = point.x
|
b = transform.b,
|
||||||
let y = point.y
|
c = transform.c,
|
||||||
point.x = x * a + y * c + tx;
|
d = transform.d,
|
||||||
point.y = x * b + y * d + ty;
|
tx = transform.tx,
|
||||||
}
|
ty = transform.ty;
|
||||||
|
let x = point.x;
|
||||||
|
let y = point.y;
|
||||||
|
point.x = x * a + y * c + tx;
|
||||||
|
point.y = x * b + y * d + ty;
|
||||||
|
}
|
||||||
|
|
||||||
/** Converts a point from the Phaser world coordinate system to the bone's local coordinate system. */
|
/** Converts a point from the Phaser world coordinate system to the bone's local coordinate system. */
|
||||||
phaserWorldCoordinatesToBone (point: { x: number, y: number }, bone: Bone) {
|
phaserWorldCoordinatesToBone(point: { x: number; y: number }, bone: Bone) {
|
||||||
this.phaserWorldCoordinatesToSkeleton(point);
|
this.phaserWorldCoordinatesToSkeleton(point);
|
||||||
if (bone.parent) {
|
if (bone.parent) {
|
||||||
bone.parent.worldToLocal(point as Vector2);
|
bone.parent.worldToLocal(point as Vector2);
|
||||||
} else {
|
} else {
|
||||||
bone.worldToLocal(point as Vector2);
|
bone.worldToLocal(point as Vector2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the {@link AnimationState}, applies it to the {@link Skeleton}, then updates the world transforms of all bones.
|
* Updates the {@link AnimationState}, applies it to the {@link Skeleton}, then updates the world transforms of all bones.
|
||||||
* @param delta The time delta in milliseconds
|
* @param delta The time delta in milliseconds
|
||||||
*/
|
*/
|
||||||
updatePose (delta: number) {
|
updatePose(delta: number) {
|
||||||
this.animationState.update(delta / 1000);
|
this.animationState.update(delta / 1000);
|
||||||
this.animationState.apply(this.skeleton);
|
this.animationState.apply(this.skeleton);
|
||||||
this.beforeUpdateWorldTransforms(this);
|
this.beforeUpdateWorldTransforms(this);
|
||||||
this.skeleton.updateWorldTransform();
|
this.skeleton.updateWorldTransform();
|
||||||
this.afterUpdateWorldTransforms(this);
|
this.afterUpdateWorldTransforms(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
preUpdate (time: number, delta: number) {
|
preUpdate(time: number, delta: number) {
|
||||||
if (!this.skeleton || !this.animationState) return;
|
if (!this.skeleton || !this.animationState) return;
|
||||||
this.updatePose(delta);
|
this.updatePose(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
preDestroy () {
|
preDestroy() {
|
||||||
// FIXME tear down any event emitters
|
// FIXME tear down any event emitters
|
||||||
}
|
}
|
||||||
|
|
||||||
willRender (camera: Phaser.Cameras.Scene2D.Camera) {
|
willRender(camera: Phaser.Cameras.Scene2D.Camera) {
|
||||||
if (!this.visible) return false;
|
if (!this.visible) return false;
|
||||||
|
|
||||||
var GameObjectRenderMask = 0xf;
|
var GameObjectRenderMask = 0xf;
|
||||||
var result = (!this.skeleton || !(GameObjectRenderMask !== this.renderFlags || (this.cameraFilter !== 0 && (this.cameraFilter & camera.id))));
|
var result = !this.skeleton || !(GameObjectRenderMask !== this.renderFlags || (this.cameraFilter !== 0 && this.cameraFilter & camera.id));
|
||||||
|
|
||||||
return result;
|
if (!result && this.parentContainer && this.plugin.webGLRenderer) {
|
||||||
}
|
var sceneRenderer = this.plugin.webGLRenderer;
|
||||||
|
|
||||||
renderWebGL (renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
|
if (this.plugin.gl && this.plugin.phaserRenderer instanceof Phaser.Renderer.WebGL.WebGLRenderer && sceneRenderer.batcher.isDrawing) {
|
||||||
if (!this.skeleton || !this.animationState || !this.plugin.webGLRenderer) return;
|
sceneRenderer.end();
|
||||||
|
this.plugin.phaserRenderer.pipelines.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let sceneRenderer = this.plugin.webGLRenderer;
|
return result;
|
||||||
if (renderer.newType) {
|
}
|
||||||
renderer.pipelines.clear();
|
|
||||||
sceneRenderer.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
camera.addToRenderList(src);
|
renderWebGL(
|
||||||
let transform = Phaser.GameObjects.GetCalcMatrix(src, camera, parentMatrix).calc;
|
renderer: Phaser.Renderer.WebGL.WebGLRenderer,
|
||||||
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
|
src: SpineGameObject,
|
||||||
sceneRenderer.drawSkeleton(this.skeleton, this.premultipliedAlpha, -1, -1, (vertices, numVertices, stride) => {
|
camera: Phaser.Cameras.Scene2D.Camera,
|
||||||
for (let i = 0; i < numVertices; i += stride) {
|
parentMatrix: Phaser.GameObjects.Components.TransformMatrix
|
||||||
let vx = vertices[i];
|
) {
|
||||||
let vy = vertices[i + 1];
|
if (!this.skeleton || !this.animationState || !this.plugin.webGLRenderer)
|
||||||
vertices[i] = vx * a + vy * c + tx;
|
return;
|
||||||
vertices[i + 1] = vx * b + vy * d + ty;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!renderer.nextTypeMatch) {
|
let sceneRenderer = this.plugin.webGLRenderer;
|
||||||
sceneRenderer.end();
|
if (renderer.newType) {
|
||||||
renderer.pipelines.rebind();
|
renderer.pipelines.clear();
|
||||||
}
|
sceneRenderer.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCanvas (renderer: Phaser.Renderer.Canvas.CanvasRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
|
camera.addToRenderList(src);
|
||||||
if (!this.skeleton || !this.animationState || !this.plugin.canvasRenderer) return;
|
let transform = Phaser.GameObjects.GetCalcMatrix(
|
||||||
|
src,
|
||||||
|
camera,
|
||||||
|
parentMatrix
|
||||||
|
).calc;
|
||||||
|
let a = transform.a,
|
||||||
|
b = transform.b,
|
||||||
|
c = transform.c,
|
||||||
|
d = transform.d,
|
||||||
|
tx = transform.tx,
|
||||||
|
ty = transform.ty;
|
||||||
|
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];
|
||||||
|
vertices[i] = vx * a + vy * c + tx;
|
||||||
|
vertices[i + 1] = vx * b + vy * d + ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let context = renderer.currentContext;
|
if (!renderer.nextTypeMatch) {
|
||||||
let skeletonRenderer = this.plugin.canvasRenderer;
|
sceneRenderer.end();
|
||||||
(skeletonRenderer as any).ctx = context;
|
renderer.pipelines.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
camera.addToRenderList(src);
|
renderCanvas(
|
||||||
let transform = Phaser.GameObjects.GetCalcMatrix(src, camera, parentMatrix).calc;
|
renderer: Phaser.Renderer.Canvas.CanvasRenderer,
|
||||||
let skeleton = this.skeleton;
|
src: SpineGameObject,
|
||||||
skeleton.x = transform.tx;
|
camera: Phaser.Cameras.Scene2D.Camera,
|
||||||
skeleton.y = transform.ty;
|
parentMatrix: Phaser.GameObjects.Components.TransformMatrix
|
||||||
skeleton.scaleX = transform.scaleX;
|
) {
|
||||||
skeleton.scaleY = transform.scaleY;
|
if (!this.skeleton || !this.animationState || !this.plugin.canvasRenderer)
|
||||||
let root = skeleton.getRootBone()!;
|
return;
|
||||||
root.rotation = -MathUtils.radiansToDegrees * transform.rotationNormalized;
|
|
||||||
this.skeleton.updateWorldTransform();
|
|
||||||
|
|
||||||
context.save();
|
let context = renderer.currentContext;
|
||||||
skeletonRenderer.draw(skeleton);
|
let skeletonRenderer = this.plugin.canvasRenderer;
|
||||||
context.restore();
|
(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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,9 +74,10 @@ export interface SpineGameObjectConfig extends Phaser.Types.GameObjects.GameObje
|
|||||||
export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
|
export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
|
||||||
game: Phaser.Game;
|
game: Phaser.Game;
|
||||||
private isWebGL: boolean;
|
private isWebGL: boolean;
|
||||||
private gl: WebGLRenderingContext | null;
|
gl: WebGLRenderingContext | null;
|
||||||
webGLRenderer: SceneRenderer | null;
|
webGLRenderer: SceneRenderer | null;
|
||||||
canvasRenderer: SkeletonRenderer | null;
|
canvasRenderer: SkeletonRenderer | null;
|
||||||
|
phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer;
|
||||||
private skeletonDataCache: Phaser.Cache.BaseCache;
|
private skeletonDataCache: Phaser.Cache.BaseCache;
|
||||||
private atlasCache: Phaser.Cache.BaseCache;
|
private atlasCache: Phaser.Cache.BaseCache;
|
||||||
|
|
||||||
@ -85,6 +86,7 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
|
|||||||
this.game = pluginManager.game;
|
this.game = pluginManager.game;
|
||||||
this.isWebGL = this.game.config.renderType === 2;
|
this.isWebGL = this.game.config.renderType === 2;
|
||||||
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.phaserRenderer = this.game.renderer;
|
||||||
this.webGLRenderer = null;
|
this.webGLRenderer = null;
|
||||||
this.canvasRenderer = 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);
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export class PolygonBatcher implements Disposable {
|
|||||||
private context: ManagedWebGLRenderingContext;
|
private context: ManagedWebGLRenderingContext;
|
||||||
private drawCalls = 0;
|
private drawCalls = 0;
|
||||||
private static globalDrawCalls = 0;
|
private static globalDrawCalls = 0;
|
||||||
private isDrawing = false;
|
isDrawing = false;
|
||||||
private mesh: Mesh;
|
private mesh: Mesh;
|
||||||
private shader: Shader | null = null;
|
private shader: Shader | null = null;
|
||||||
private lastTexture: GLTexture | null = null;
|
private lastTexture: GLTexture | null = null;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user