Skip to content
This repository has been archived by the owner on Nov 20, 2024. It is now read-only.

Commit

Permalink
- correct depth sorting on spine slots
Browse files Browse the repository at this point in the history
- `attachToSlot` as opposed to `attachToBone`
- use `Skeleton.yDown` to simplify the maths
  • Loading branch information
GoodBoyDigital committed Jun 5, 2024
1 parent 34a2e3b commit 6f98fd1
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/BatchableClippedSpineSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class BatchableClippedSpineSlot implements BatchableObject

// position
float32View[index++] = clippedVertices[localIndex];
float32View[index++] = clippedVertices[localIndex + 1] * -1;
float32View[index++] = clippedVertices[localIndex + 1];

// uv
float32View[index++] = clippedVertices[localIndex + 6];
Expand Down
2 changes: 1 addition & 1 deletion src/BatchableSpineSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export class BatchableSpineSlot implements BatchableObject
// index++;
// float32View[index++] *= -1;
const x = float32View[index];
const y = -float32View[index + 1];
const y = float32View[index + 1];

float32View[index++] = (a * x) + (c * y) + tx;
float32View[index++] = (b * x) + (d * y) + ty;
Expand Down
70 changes: 40 additions & 30 deletions src/Spine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/

import { Assets, Bounds, Cache, Container, ContainerOptions, DEG_TO_RAD, DestroyOptions, PointData, Ticker, View } from 'pixi.js';
import { Assets, Bounds, Cache, Container, ContainerOptions, DEG_TO_RAD, DestroyOptions, InstructionSet, PointData, Ticker, View } from 'pixi.js';

Check warning on line 30 in src/Spine.ts

View workflow job for this annotation

GitHub Actions / build

This line has a length of 146. Maximum allowed is 125

Check warning on line 30 in src/Spine.ts

View workflow job for this annotation

GitHub Actions / build

'InstructionSet' is defined but never used

Check failure on line 30 in src/Spine.ts

View workflow job for this annotation

GitHub Actions / build

'InstructionSet' is declared but its value is never read.

Check warning on line 30 in src/Spine.ts

View workflow job for this annotation

GitHub Actions / build

This line has a length of 146. Maximum allowed is 125

Check warning on line 30 in src/Spine.ts

View workflow job for this annotation

GitHub Actions / build

'InstructionSet' is defined but never used

Check failure on line 30 in src/Spine.ts

View workflow job for this annotation

GitHub Actions / build

'InstructionSet' is declared but its value is never read.
import { getSkeletonBounds } from './getSkeletonBounds';
import { ISpineDebugRenderer } from './SpineDebugRenderer';
import {
Expand All @@ -40,6 +40,7 @@ import {
SkeletonBounds,
SkeletonData,
SkeletonJson,
Slot,
type TextureAtlas,
TrackEntry,
Vector2
Expand All @@ -53,6 +54,8 @@ export type SpineFromOptions = {

const vectorAux = new Vector2();

Skeleton.yDown = true;

export interface SpineOptions extends ContainerOptions
{
skeletonData: SkeletonData;
Expand Down Expand Up @@ -86,7 +89,7 @@ export class Spine extends Container implements View
public skeletonBounds: SkeletonBounds;
private _debug?: ISpineDebugRenderer | undefined = undefined;

private readonly _mappings:{bone:Bone, container:Container}[] = [];
readonly _slotAttachments:{slot:Slot, container:Container}[] = [];

public get debug(): ISpineDebugRenderer | undefined
{
Expand Down Expand Up @@ -192,7 +195,7 @@ export class Spine extends Container implements View
else
{
bone.x = vectorAux.x;
bone.y = -vectorAux.y;
bone.y = vectorAux.y;
}
}

Expand All @@ -218,7 +221,7 @@ export class Spine extends Container implements View
}

outPos.x = bone.worldX;
outPos.y = -bone.worldY;
outPos.y = bone.worldY;

return outPos;
}
Expand All @@ -228,25 +231,28 @@ export class Spine extends Container implements View
this.state.update(dt);
this._boundsDirty = true;

// update the mappings..

this._mappings.forEach((mapping) =>
for (let i = 0; i < this._slotAttachments.length; i++)
{
const { bone, container } = mapping;
const slotAttachment = this._slotAttachments[i];

const { slot, container } = slotAttachment;

const bone = slot.bone;

container.position.set(bone.worldX, -bone.worldY);
container.position.set(bone.worldX, bone.worldY);

container.scale.x = bone.getWorldScaleX();
container.scale.y = bone.getWorldScaleY();

const rotationX = bone.getWorldRotationX() * DEG_TO_RAD;
const rotationY = bone.getWorldRotationY() * DEG_TO_RAD;

container.rotation = -Math.atan2(
container.rotation = Math.atan2(
Math.sin(rotationX) + Math.sin(rotationY),
Math.cos(rotationX) + Math.cos(rotationY)
);
});
}

this.onViewUpdate();
}

Expand Down Expand Up @@ -279,26 +285,28 @@ export class Spine extends Container implements View
* @param container - The container to attach to the bone
* @param bone - The bone id or bone to attach to
*/
attachToBone(container:Container, bone:string | Bone)
attachToSlot(container:Container, slot:string | Slot)
{
this.detachFromBone(container, bone);
this.detachFromSlot(container, slot);

if (typeof bone === 'string')
container.includeInBuild = false;

if (typeof slot === 'string')
{
bone = this.skeleton.findBone(bone) as Bone;
slot = this.skeleton.findSlot(slot) as Slot;
}

if (!bone)
if (!slot)
{
throw new Error(`Bone ${bone} not found`);
throw new Error(`Slot ${slot} not found`);
}

// TODO only add once??
this.addChild(container);

// TODO search for copies... - one container - to one bone!
this._mappings.push({
bone,
this._slotAttachments.push({
slot,
container
});
}
Expand All @@ -307,29 +315,31 @@ export class Spine extends Container implements View
* Removes a PixiJS container from the bone it is attached to.
*
* @param container - The container to detach from the bone
* @param bone - The bone id or bone to detach from
* @param slot - The bone id or bone to detach from
*/
detachFromBone(container:Container, bone:string | Bone)
detachFromSlot(container:Container, slot:string | Slot)
{
if (typeof bone === 'string')
container.includeInBuild = true;

if (typeof slot === 'string')
{
bone = this.skeleton.findBone(bone) as Bone;
slot = this.skeleton.findSlot(slot) as Slot;
}

if (!bone)
if (!slot)
{
throw new Error(`Bone ${bone} not found`);
throw new Error(`Bone ${slot} not found`);
}

this.removeChild(container);

for (let i = 0; i < this._mappings.length; i++)
for (let i = 0; i < this._slotAttachments.length; i++)
{
const mapping = this._mappings[i];
const mapping = this._slotAttachments[i];

if (mapping.bone === bone && mapping.container === container)
if (mapping.slot === slot && mapping.container === container)
{
this._mappings.splice(i, 1);
this._slotAttachments.splice(i, 1);
break;
}
}
Expand Down Expand Up @@ -396,7 +406,7 @@ export class Spine extends Container implements View
this.debug = undefined;
this.skeleton = null as any;
this.state = null as any;
(this._mappings as any) = null;
(this._slotAttachments as any) = null;
}

/** Whether or not to round the x/y position of the sprite. */
Expand Down
19 changes: 15 additions & 4 deletions src/SpinePipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/

import { BigPool, extensions, ExtensionType, type Renderer, type RenderPipe, Texture } from 'pixi.js';
import { BigPool, collectAllRenderables, extensions, ExtensionType, type Renderer, type RenderPipe, Texture } from 'pixi.js';
import { BatchableClippedSpineSlot } from './BatchableClippedSpineSlot';
import { BatchableSpineSlot } from './BatchableSpineSlot';
import { Spine } from './Spine';
Expand All @@ -40,6 +40,8 @@ const QUAD_VERTS = new Float32Array(8);
const lightColor = new Color();
const darkColor = new Color();

const clipper = new SkeletonClipping();

// eslint-disable-next-line max-len
export class SpinePipe implements RenderPipe<Spine>
{
Expand Down Expand Up @@ -79,7 +81,7 @@ export class SpinePipe implements RenderPipe<Spine>
this._returnActiveBatches();
}

addRenderable(spine: Spine)
addRenderable(spine: Spine, instructionSet)

Check failure on line 84 in src/SpinePipe.ts

View workflow job for this annotation

GitHub Actions / build

Parameter 'instructionSet' implicitly has an 'any' type.

Check failure on line 84 in src/SpinePipe.ts

View workflow job for this annotation

GitHub Actions / build

Parameter 'instructionSet' implicitly has an 'any' type.
{
const batcher = this.renderer.renderPipes.batch;

Expand All @@ -98,8 +100,6 @@ export class SpinePipe implements RenderPipe<Spine>

const activeBatchableSpineSlot = this.activeBatchableSpineSlots;

const clipper = new SkeletonClipping();

for (let i = 0, n = drawOrder.length; i < n; i++)
{
const slot = drawOrder[i];
Expand Down Expand Up @@ -168,6 +168,17 @@ export class SpinePipe implements RenderPipe<Spine>
{
clipper.clipEndWithSlot(slot);
}

const containerAttachment = spine._slotAttachments.find((mapping) => mapping.slot === slot);

if (containerAttachment)
{
const container = containerAttachment.container;

container.includeInBuild = true;
collectAllRenderables(container, instructionSet, this.renderer.renderPipes);
container.includeInBuild = false;
}
}

clipper.clipEnd();
Expand Down
8 changes: 0 additions & 8 deletions src/getSkeletonBounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,6 @@ export function getSkeletonBounds(skeleton:Skeleton, out:Bounds)
{
out.clear();

const rootBone = skeleton.getRootBone() as Bone;

rootBone.x = 0;
rootBone.y = 0;
rootBone.scaleX = 1;
rootBone.scaleY = -1;
rootBone.rotation = 0;

skeleton.updateWorldTransform();

const drawOrder = skeleton.drawOrder;
Expand Down

0 comments on commit 6f98fd1

Please sign in to comment.