diff --git a/src/BatchableSpineSlot.ts b/src/BatchableSpineSlot.ts index ad2a6d4..fdd0c1f 100644 --- a/src/BatchableSpineSlot.ts +++ b/src/BatchableSpineSlot.ts @@ -109,7 +109,6 @@ export class BatchableSpineSlot implements DefaultBatchableMeshElement setData( renderable:Spine, data:AttachmentCacheData, - texture:Texture, blendMode:BLEND_MODES, roundPixels: 0 | 1) { @@ -136,7 +135,7 @@ export class BatchableSpineSlot implements DefaultBatchableMeshElement this.uvs = data.uvs; } - this.texture = texture; + this.texture = data.texture; this.roundPixels = roundPixels; this.blendMode = blendMode; diff --git a/src/Spine.ts b/src/Spine.ts index c8498e6..82cfa1a 100644 --- a/src/Spine.ts +++ b/src/Spine.ts @@ -36,6 +36,7 @@ import { DEG_TO_RAD, DestroyOptions, PointData, + Texture, Ticker, ViewContainer, } from 'pixi.js'; @@ -104,6 +105,7 @@ export interface AttachmentCacheData darkColor: Color | null; darkTint: boolean; skipRender: boolean; + texture: Texture; clippedData?: { vertices: Float32Array; uvs: Float32Array; @@ -145,10 +147,12 @@ export class Spine extends ViewContainer return slot; } - public spineAttachmentsDirty: boolean; + public spineAttachmentsDirty = true; + public spineTexturesDirty = true; + private _lastAttachments: Attachment[]; - private _stateChanged: boolean; + private _stateChanged = true; private attachmentCacheData: Record[] = []; public get debug(): ISpineDebugRenderer | undefined @@ -443,6 +447,14 @@ export class Spine extends ViewContainer cacheData.skipRender = cacheData.clipped = false; + const texture = attachment.region?.texture.texture || Texture.EMPTY; + + if (cacheData.texture !== texture) + { + cacheData.texture = texture; + this.spineTexturesDirty = true; + } + if (clipper.isClipping()) { this.updateClippingData(cacheData); @@ -595,6 +607,7 @@ export class Spine extends ViewContainer darkColor: new Color(0, 0, 0, 0), darkTint: false, skipRender: false, + texture: attachment.region?.texture.texture, }; } else @@ -611,6 +624,7 @@ export class Spine extends ViewContainer darkColor: new Color(0, 0, 0, 0), darkTint: false, skipRender: false, + texture: attachment.region?.texture.texture, }; } diff --git a/src/SpinePipe.ts b/src/SpinePipe.ts index a65f83c..3121fde 100644 --- a/src/SpinePipe.ts +++ b/src/SpinePipe.ts @@ -33,7 +33,6 @@ import { InstructionSet, type Renderer, type RenderPipe, - Texture } from 'pixi.js'; import { BatchableSpineSlot } from './BatchableSpineSlot'; import { Spine } from './Spine'; @@ -73,9 +72,43 @@ export class SpinePipe implements RenderPipe validateRenderable(spine: Spine): boolean { spine._applyState(); - // loop through and see if the mesh lengths have changed.. - return spine.spineAttachmentsDirty; + // if pine attachments have changed, we need to rebuild the batch! + if (spine.spineAttachmentsDirty) + { + return true; + } + // if the textures have changed, we need to rebuild the batch, but only if the texture is not already in the batch + else if (spine.spineTexturesDirty) + { + // loop through and see if the textures have changed.. + const drawOrder = spine.skeleton.drawOrder; + const gpuSpine = this.gpuSpineData[spine.uid]; + + for (let i = 0, n = drawOrder.length; i < n; i++) + { + const slot = drawOrder[i]; + const attachment = slot.getAttachment(); + + if (attachment instanceof RegionAttachment || attachment instanceof MeshAttachment) + { + const cacheData = spine._getCachedData(slot, attachment); + const batchableSpineSlot = gpuSpine.slotBatches[cacheData.id]; + + const texture = cacheData.texture; + + if (texture !== batchableSpineSlot.texture) + { + if (!batchableSpineSlot._batcher.checkAndUpdateTexture(batchableSpineSlot, texture)) + { + return true; + } + } + } + } + } + + return false; } addRenderable(spine: Spine, instructionSet:InstructionSet) @@ -104,7 +137,6 @@ export class SpinePipe implements RenderPipe batchableSpineSlot.setData( spine, cacheData, - (attachment.region?.texture.texture) || Texture.EMPTY, blendMode, roundPixels );