[pixi] Clean-up and improvements.

This commit is contained in:
Mario Zechner 2023-06-27 15:10:53 +02:00
parent 7698f5fc92
commit 8fb691c054
7 changed files with 55 additions and 92 deletions

View File

@ -341,6 +341,11 @@ cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-ts/spine-player/example/
cp -f ../spineboy/export/spineboy-pma.atlas "$ROOT/spine-ts/spine-player/example/assets/"
cp -f ../spineboy/export/spineboy-pma.png "$ROOT/spine-ts/spine-player/example/assets/"
cp -f ../spineboy/export/spineboy-pro.json "$ROOT/spine-ts/spine-pixi/example/assets/"
cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-ts/spine-pixi/example/assets/"
cp -f ../spineboy/export/spineboy.atlas "$ROOT/spine-ts/spine-pixi/example/assets/"
cp -f ../spineboy/export/spineboy.png "$ROOT/spine-ts/spine-pixi/example/assets/"
rm "$ROOT/spine-ts/spine-phaser/example/assets/"*
cp -f ../raptor/export/raptor-pro.json "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../raptor/export/raptor-pma.atlas "$ROOT/spine-ts/spine-phaser/example/assets/"

View File

@ -54,7 +54,7 @@
]);
// Create the spine display object
const spineBoy = spine.Spine.from("spineboySkeletonJson", "spineboyAtlasPolypack", { scale: 0.5 });
const spineBoy = spine.Spine.from("spineboySkeletonJson", "spineboyAtlas", { scale: 0.5 });
// .from(...) is a shortcut + cache for creating the skeleton data at a certain scale
// Here would be the "long way" of doing it (without cache):

View File

@ -8,10 +8,10 @@ export class DarkSlotMesh extends DarkTintMesh implements ISlotMesh {
private static auxColor = [0, 0, 0, 0];
constructor() {
constructor () {
super();
}
public updateFromSpineData(
public updateFromSpineData (
slotTexture: SpineTexture,
slotBlendMode: BlendMode,
slotName: string,

View File

@ -10,7 +10,7 @@ export class SlotMesh extends Mesh implements ISlotMesh {
private static readonly auxColor = [0, 0, 0, 0];
private warnedTwoTint: boolean = false;
constructor() {
constructor () {
const geometry = new MeshGeometry();
geometry.getBuffer("aVertexPosition").static = false;
@ -19,7 +19,7 @@ export class SlotMesh extends Mesh implements ISlotMesh {
const meshMaterial = new MeshMaterial(Texture.EMPTY);
super(geometry, meshMaterial);
}
public updateFromSpineData(
public updateFromSpineData (
slotTexture: SpineTexture,
slotBlendMode: BlendMode,
slotName: string,

View File

@ -24,7 +24,6 @@ import type { IDestroyOptions, DisplayObject } from "@pixi/display";
import { Container } from "@pixi/display";
export interface ISpineOptions {
removeUnusedSlots?: boolean;
autoUpdate?: boolean;
slotMeshFactory?: () => ISlotMesh;
}
@ -43,10 +42,10 @@ export class Spine extends Container {
public state: AnimationState;
private _debug?: ISpineDebugRenderer | undefined = undefined;
public get debug(): ISpineDebugRenderer | undefined {
public get debug (): ISpineDebugRenderer | undefined {
return this._debug;
}
public set debug(value: ISpineDebugRenderer | undefined) {
public set debug (value: ISpineDebugRenderer | undefined) {
if (this._debug) {
this._debug.unregisterSpine(this);
}
@ -56,16 +55,14 @@ export class Spine extends Container {
this._debug = value;
}
// Each slot is a pixi mesh, by default we just visible=false the ones we don't need. This forces a removeChild and addChild every time we need to show a slot.
public removeUnusedSlots: boolean;
protected slotMeshFactory: () => ISlotMesh;
private autoUpdateWarned: boolean = false;
private _autoUpdate: boolean = true;
public get autoUpdate(): boolean {
public get autoUpdate (): boolean {
return this._autoUpdate;
}
public set autoUpdate(value: boolean) {
public set autoUpdate (value: boolean) {
if (value) {
Ticker.shared.add(this.internalUpdate, this);
this.autoUpdateWarned = false;
@ -88,13 +85,12 @@ export class Spine extends Container {
private darkColor = new Color();
constructor(skeletonData: SkeletonData, options?: ISpineOptions) {
constructor (skeletonData: SkeletonData, options?: ISpineOptions) {
super();
this.skeleton = new Skeleton(skeletonData);
const animData = new AnimationStateData(skeletonData);
this.state = new AnimationState(animData);
this.removeUnusedSlots = options?.removeUnusedSlots ?? false;
this.autoUpdate = options?.autoUpdate ?? true;
this.slotMeshFactory = options?.slotMeshFactory ?? ((): ISlotMesh => new SlotMesh());
@ -116,7 +112,7 @@ export class Spine extends Container {
*/
}
public update(deltaSeconds: number): void {
public update (deltaSeconds: number): void {
if (this.autoUpdate && !this.autoUpdateWarned) {
console.warn("You are calling update on a Spine instance that has autoUpdate set to true. This is probably not what you want.");
this.autoUpdateWarned = true;
@ -124,18 +120,18 @@ export class Spine extends Container {
this.internalUpdate(0, deltaSeconds);
}
protected internalUpdate(_deltaFrame: number, deltaSeconds?: number): void {
protected internalUpdate (_deltaFrame: number, deltaSeconds?: number): void {
// Because reasons, pixi uses deltaFrames at 60fps. We ignore the default deltaFrames and use the deltaSeconds from pixi ticker.
this.state.update(deltaSeconds ?? Ticker.shared.deltaMS / 1000);
}
public override updateTransform(): void {
public override updateTransform (): void {
this.updateSpineTransform();
this.debug?.renderDebug(this);
super.updateTransform();
}
protected updateSpineTransform(): void {
protected updateSpineTransform (): void {
// if I ever create the linked spines, this will be useful.
this.state.apply(this.skeleton);
@ -144,7 +140,7 @@ export class Spine extends Container {
this.sortChildren();
}
public override destroy(options?: boolean | IDestroyOptions | undefined): void {
public override destroy (options?: boolean | IDestroyOptions | undefined): void {
for (const [, mesh] of this.meshesCache) {
mesh?.destroy();
}
@ -154,11 +150,8 @@ export class Spine extends Container {
super.destroy(options);
}
private recycleMeshes(): void {
private resetMeshes (): void {
for (const [, mesh] of this.meshesCache) {
if (this.removeUnusedSlots) {
mesh.parent?.removeChild(mesh);
}
mesh.zIndex = -1;
mesh.visible = false;
}
@ -167,7 +160,7 @@ export class Spine extends Container {
/**
* If you want to manually handle which meshes go on which slot and how you cache, overwrite this method.
*/
protected getMeshForSlot(slot: Slot): ISlotMesh {
protected getMeshForSlot (slot: Slot): ISlotMesh {
if (!this.meshesCache.has(slot)) {
let mesh = this.slotMeshFactory();
this.addChild(mesh);
@ -175,10 +168,6 @@ export class Spine extends Container {
return mesh;
} else {
let mesh = this.meshesCache.get(slot)!;
if (this.removeUnusedSlots) {
this.addChild(mesh);
}
mesh.visible = true;
return mesh;
}
@ -186,8 +175,8 @@ export class Spine extends Container {
private verticesCache: NumberArrayLike = Utils.newFloatArray(1024);
private updateGeometry(): void {
this.recycleMeshes();
private updateGeometry (): void {
this.resetMeshes();
let triangles: Array<number> | null = null;
let uvs: NumberArrayLike | null = null;
@ -300,46 +289,30 @@ export class Spine extends Container {
Spine.clipper.clipEnd();
}
public setBonePosition(bone: string | Bone, position: IPointData): void {
public setBonePosition (bone: string | Bone, position: IPointData): void {
const boneAux = bone;
if (typeof bone === "string") {
bone = this.skeleton.findBone(bone)!;
this.skeleton.findBone;
this.skeleton.findIkConstraint;
this.skeleton.findPathConstraint;
this.skeleton.findSlot;
this.skeleton.findTransformConstraint;
}
if (!bone) {
console.error(`Cant set bone position! Bone ${String(boneAux)} not found`);
return;
}
if (!bone) throw Error(`Cant set bone position, bone ${String(boneAux)} not found`);
Spine.vectorAux.set(position.x, position.y);
if (bone.parent)
{
if (bone.parent) {
const aux = bone.parent.worldToLocal(Spine.vectorAux);
bone.x = aux.x;
bone.y = aux.y;
}
else
{
else {
bone.x = Spine.vectorAux.x;
bone.y = Spine.vectorAux.y;
}
}
public getBonePosition(bone: string | Bone, outPos?: IPointData): IPointData | undefined {
public getBonePosition (bone: string | Bone, outPos?: IPointData): IPointData | undefined {
const boneAux = bone;
if (typeof bone === "string") {
bone = this.skeleton.findBone(bone)!;
this.skeleton.findBone;
this.skeleton.findIkConstraint;
this.skeleton.findPathConstraint;
this.skeleton.findSlot;
this.skeleton.findTransformConstraint;
}
if (!bone) {
@ -358,34 +331,19 @@ export class Spine extends Container {
public static readonly skeletonCache: Record<string, SkeletonData> = Object.create(null);
public static from(skeletonAssetName: string, atlasAssetName: string, options?: ISpineOptions & { scale?: number }): Spine {
public static from (skeletonAssetName: string, atlasAssetName: string, options?: ISpineOptions & { scale?: number }): Spine {
const cacheKey = `${skeletonAssetName}-${atlasAssetName}-${options?.scale ?? 1}`;
let skeletonData = Spine.skeletonCache[cacheKey];
if (skeletonData) {
return new Spine(skeletonData, options);
}
const skeletonAsset = Assets.get<any | Uint8Array>(skeletonAssetName);
const atlasAsset = Assets.get<TextureAtlas>(atlasAssetName);
// If you want a custom attachment laoder, you don't use .from(...)
const attachmentLoader = new AtlasAttachmentLoader(atlasAsset);
// What parser do we need?
let parser: SkeletonBinary | SkeletonJson;
if (skeletonAsset instanceof Uint8Array) {
parser = new SkeletonBinary(attachmentLoader);
} else {
parser = new SkeletonJson(attachmentLoader);
}
let parser = skeletonAsset instanceof Uint8Array ? new SkeletonBinary(attachmentLoader) : new SkeletonJson(attachmentLoader);
parser.scale = options?.scale ?? 1;
skeletonData = parser.readSkeletonData(skeletonAsset);
Spine.skeletonCache[cacheKey] = skeletonData;
return new this(skeletonData, options);
}
}
@ -394,7 +352,7 @@ Skeleton.yDown = true;
export interface ISlotMesh extends DisplayObject {
name: string;
updateFromSpineData(
updateFromSpineData (
slotTexture: SpineTexture,
slotBlendMode: BlendMode,
slotName: string,

View File

@ -13,17 +13,17 @@ export interface ISpineDebugRenderer {
/**
* This will be called every frame, after the spine has been updated.
*/
renderDebug(spine: Spine): void;
renderDebug (spine: Spine): void;
/**
* This is called when the `spine.debug` object is set to null or when the spine is destroyed.
*/
unregisterSpine(spine: Spine): void;
unregisterSpine (spine: Spine): void;
/**
* This is called when the `spine.debug` object is set to a new instance of a debug renderer.
*/
registerSpine(spine: Spine): void;
registerSpine (spine: Spine): void;
}
type DebugDisplayObjects = {
@ -77,7 +77,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
/**
* The debug is attached by force to each spine object. So we need to create it inside the spine when we get the first update
*/
public registerSpine(spine: Spine): void {
public registerSpine (spine: Spine): void {
if (this.registeredSpines.has(spine)) {
console.warn("SpineDebugRenderer.registerSpine() - this spine is already registered!", spine);
return;
@ -128,11 +128,11 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.eventText);
debugDisplayObjects.parentDebugContainer.zIndex = 9999999;
// Disable screen reader and mouse input on debug objects.
(debugDisplayObjects.parentDebugContainer as any).accessibleChildren = false;
(debugDisplayObjects.parentDebugContainer as any).eventMode = "none";
(debugDisplayObjects.parentDebugContainer as any ).interactiveChildren = false;
(debugDisplayObjects.parentDebugContainer as any).interactiveChildren = false;
spine.addChild(debugDisplayObjects.parentDebugContainer);
@ -140,7 +140,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
this.registeredSpines.set(spine, debugDisplayObjects);
}
public renderDebug(spine: Spine): void {
public renderDebug (spine: Spine): void {
if (!this.registeredSpines.has(spine)) {
// This should never happen. Spines are registered when you assign spine.debug
this.registerSpine(spine);
@ -203,7 +203,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
}
}
private drawBonesFunc(spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number, scale: number): void {
private drawBonesFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number, scale: number): void {
const skeleton = spine.skeleton;
const skeletonX = skeleton.x;
const skeletonY = skeleton.y;
@ -300,7 +300,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
debugDisplayObjects.skeletonXY.lineTo(skeletonX - startDotSize, skeletonY + startDotSize);
}
private drawRegionAttachmentsFunc(spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
private drawRegionAttachmentsFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
const skeleton = spine.skeleton;
const slots = skeleton.slots;
@ -323,7 +323,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
}
}
private drawMeshHullAndMeshTriangles(spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
private drawMeshHullAndMeshTriangles (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
const skeleton = spine.skeleton;
const slots = skeleton.slots;
@ -381,7 +381,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
}
}
private drawClippingFunc(spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
private drawClippingFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
const skeleton = spine.skeleton;
const slots = skeleton.slots;
@ -408,7 +408,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
}
}
private drawBoundingBoxesFunc(spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
private drawBoundingBoxesFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
// draw the total outline of the bounding box
debugDisplayObjects.boundingBoxesRect.lineStyle(lineWidth, this.boundingBoxesRectColor, 5);
@ -453,7 +453,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
}
}
private drawPathsFunc(spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
private drawPathsFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
const skeleton = spine.skeleton;
const slots = skeleton.slots;
@ -525,7 +525,7 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
}
}
public unregisterSpine(spine: Spine): void {
public unregisterSpine (spine: Spine): void {
if (!this.registeredSpines.has(spine)) {
console.warn("SpineDebugRenderer.unregisterSpine() - spine is not registered, can't unregister!", spine);
}

View File

@ -5,7 +5,7 @@ import { Texture as PixiTexture, SCALE_MODES, MIPMAP_MODES, WRAP_MODES, BLEND_MO
export class SpineTexture extends Texture {
private static textureMap: Map<PixiBaseTexture, SpineTexture> = new Map<PixiBaseTexture, SpineTexture>();
public static from(texture: PixiBaseTexture): SpineTexture {
public static from (texture: PixiBaseTexture): SpineTexture {
if (SpineTexture.textureMap.has(texture)) {
return SpineTexture.textureMap.get(texture)!;
}
@ -14,31 +14,31 @@ export class SpineTexture extends Texture {
public readonly texture: PixiTexture;
private constructor(image: PixiBaseTexture) {
private constructor (image: PixiBaseTexture) {
// Todo: maybe add error handling if you feed a video texture to spine?
super((image.resource as BaseImageResource).source as any);
this.texture = PixiTexture.from(image);
}
public setFilters(minFilter: TextureFilter, _magFilter: TextureFilter): void {
public setFilters (minFilter: TextureFilter, _magFilter: TextureFilter): void {
this.texture.baseTexture.scaleMode = SpineTexture.toPixiTextureFilter(minFilter);
this.texture.baseTexture.mipmap = SpineTexture.toPixiMipMap(minFilter);
// pixi only has one filter for both min and mag, too bad
}
public setWraps(uWrap: TextureWrap, _vWrap: TextureWrap): void {
public setWraps (uWrap: TextureWrap, _vWrap: TextureWrap): void {
this.texture.baseTexture.wrapMode = SpineTexture.toPixiTextureWrap(uWrap);
// Pixi only has one setting
}
public dispose(): void {
public dispose (): void {
// I am not entirely sure about this...
this.texture.destroy();
}
private static toPixiTextureFilter(filter: TextureFilter): SCALE_MODES {
private static toPixiTextureFilter (filter: TextureFilter): SCALE_MODES {
switch (filter) {
case TextureFilter.Nearest:
case TextureFilter.MipMapNearestLinear:
@ -55,7 +55,7 @@ export class SpineTexture extends Texture {
}
}
private static toPixiMipMap(filter: TextureFilter): MIPMAP_MODES {
private static toPixiMipMap (filter: TextureFilter): MIPMAP_MODES {
switch (filter) {
case TextureFilter.Nearest:
case TextureFilter.Linear:
@ -72,7 +72,7 @@ export class SpineTexture extends Texture {
}
}
private static toPixiTextureWrap(wrap: TextureWrap): WRAP_MODES {
private static toPixiTextureWrap (wrap: TextureWrap): WRAP_MODES {
switch (wrap) {
case TextureWrap.ClampToEdge:
return WRAP_MODES.CLAMP;
@ -88,7 +88,7 @@ export class SpineTexture extends Texture {
}
}
public static toPixiBlending(blend: BlendMode): BLEND_MODES {
public static toPixiBlending (blend: BlendMode): BLEND_MODES {
switch (blend) {
case BlendMode.Normal:
return BLEND_MODES.NORMAL;