Skip to content

Commit e5f7ffb

Browse files
committed
[ts][pixi-v7][pixi-v8] Slot objects follow bone using local matrix making its transform equivalent to the one of the slot. See #3015.
1 parent 6133cab commit e5f7ffb

File tree

4 files changed

+44
-45
lines changed

4 files changed

+44
-45
lines changed

spine-ts/spine-pixi-v7/example/slot-objects-scale-rotation.html

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,25 +55,34 @@
5555
const bone = spineGO.skeleton.findBone("replaceMe");
5656

5757
const myObject = {
58-
parentScaleX: 1, parentScaleY: 1, parentRotation: 0,
59-
parent2ScaleX: 1, parent2ScaleY: 1, parent2Rotation: 0,
58+
parentScaleX: 1, parentScaleY: 1, parentRotation: 0, parentShearX: 0, parentShearY: 0,
59+
parent2ScaleX: 1, parent2ScaleY: 1, parent2Rotation: 0, parent2ShearX: 0, parent2ShearY: 0,
6060
scaleX: 1, scaleY: 1, rotation: 0,
61+
shearX: 0, shearY: 0,
62+
spriteAlpha: 1,
6163
};
6264

6365
const gui = new lil.GUI({});
6466
gui.add(myObject, 'parentScaleX').min(-3).max(3).step(0.1).name('parentScaleX').onChange(value => parentBone.pose.scaleX = value);
6567
gui.add(myObject, 'parentScaleY').min(-3).max(3).step(0.1).name('parentScaleY').onChange(value => parentBone.pose.scaleY = value);
6668
gui.add(myObject, 'parentRotation').min(-180).max(180).step(1).name('parentRotation').onChange(value => parentBone.pose.rotation = value);
69+
gui.add(myObject, 'parentShearX').min(-180).max(180).step(1).name('parentShearX').onChange(value => parentBone.pose.shearX = value);
70+
gui.add(myObject, 'parentShearY').min(-180).max(180).step(1).name('parentShearY').onChange(value => parentBone.pose.shearY = value);
6771
gui.add(myObject, 'parent2ScaleX').min(-3).max(3).step(0.1).name('parent2ScaleX').onChange(value => parent2Bone.pose.scaleX = value);
6872
gui.add(myObject, 'parent2ScaleY').min(-3).max(3).step(0.1).name('parent2ScaleY').onChange(value => parent2Bone.pose.scaleY = value);
6973
gui.add(myObject, 'parent2Rotation').min(-180).max(180).step(1).name('parent2Rotation').onChange(value => parent2Bone.pose.rotation = value);
74+
gui.add(myObject, 'parent2ShearX').min(-180).max(180).step(1).name('parent2ShearX').onChange(value => parent2Bone.pose.shearX = value);
75+
gui.add(myObject, 'parent2ShearY').min(-180).max(180).step(1).name('parent2ShearY').onChange(value => parent2Bone.pose.shearY = value);
7076
gui.add(myObject, 'scaleX').min(-3).max(3).step(0.1).name('scaleX').onChange(value => bone.pose.scaleX = value);
7177
gui.add(myObject, 'scaleY').min(-3).max(3).step(0.1).name('scaleY').onChange(value => bone.pose.scaleY = value);
7278
gui.add(myObject, 'rotation').min(-180).max(180).step(1).name('rotation').onChange(value => bone.pose.rotation = value);
79+
gui.add(myObject, 'shearX').min(-180).max(180).step(1).name('shearX').onChange(value => bone.pose.shearX = value);
80+
gui.add(myObject, 'shearY').min(-180).max(180).step(1).name('shearY').onChange(value => bone.pose.shearY = value);
81+
gui.add(myObject, 'spriteAlpha').min(0).max(1).step(0.01).name('spriteAlpha').onChange(value => sprite.alpha = value);
7382

7483
// add instructions
7584
const basicText = new PIXI.Text(
76-
"This example shows that slot objects follow scale and rotation from ancestors too, but if a rotation on a bone occurs scale won't work anymore on ancestors.",
85+
"This example shows that slot objects can follow scale, shear and rotation from ancestors too.",
7786
{ fontSize: 20, fill: "white", wordWrap: true, wordWrapWidth: 300 }
7887
);
7988
basicText.position.set(10, 10);

spine-ts/spine-pixi-v7/src/Spine.ts

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -540,25 +540,16 @@ export class Spine extends Container {
540540
slotObject.visible = this.skeleton.drawOrder.includes(slot) && followAttachmentValue;
541541

542542
if (slotObject.visible) {
543-
const applied = slot.bone.applied;
544-
slotObject.position.set(applied.worldX, applied.worldY);
545-
slotObject.angle = applied.getWorldRotationX();
546-
547-
let bone: Bone | null = slot.bone;
548-
let cumulativeScaleX = 1;
549-
let cumulativeScaleY = 1;
550-
while (bone) {
551-
cumulativeScaleX *= bone.applied.scaleX;
552-
cumulativeScaleY *= bone.applied.scaleY;
553-
bone = bone.parent;
554-
};
555-
556-
if (cumulativeScaleX < 0) slotObject.angle -= 180;
557-
558-
slotObject.scale.set(
559-
applied.getWorldScaleX() * Math.sign(cumulativeScaleX),
560-
applied.getWorldScaleY() * Math.sign(cumulativeScaleY),
561-
);
543+
let applied = slot.bone.applied;
544+
545+
const matrix = slotObject.localTransform;
546+
matrix.a = applied.a;
547+
matrix.b = applied.c;
548+
matrix.c = -applied.b;
549+
matrix.d = -applied.d;
550+
matrix.tx = applied.worldX;
551+
matrix.ty = applied.worldY;
552+
slotObject.transform.setFromMatrix(matrix);
562553

563554
slotObject.zIndex = zIndex + 1;
564555
slotObject.alpha = this.skeleton.color.a * pose.color.a;

spine-ts/spine-pixi-v8/example/slot-objects-scale-rotation.html

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,34 @@
5454
const bone = spineGO.skeleton.findBone("replaceMe");
5555

5656
const myObject = {
57-
parentScaleX: 1, parentScaleY: 1, parentRotation: 0,
58-
parent2ScaleX: 1, parent2ScaleY: 1, parent2Rotation: 0,
57+
parentScaleX: 1, parentScaleY: 1, parentRotation: 0, parentShearX: 0, parentShearY: 0,
58+
parent2ScaleX: 1, parent2ScaleY: 1, parent2Rotation: 0, parent2ShearX: 0, parent2ShearY: 0,
5959
scaleX: 1, scaleY: 1, rotation: 0,
60+
shearX: 0, shearY: 0,
61+
spriteAlpha: 1,
6062
};
6163

6264
const gui = new lil.GUI({});
6365
gui.add(myObject, 'parentScaleX').min(-3).max(3).step(0.1).name('parentScaleX').onChange(value => parentBone.pose.scaleX = value);
6466
gui.add(myObject, 'parentScaleY').min(-3).max(3).step(0.1).name('parentScaleY').onChange(value => parentBone.pose.scaleY = value);
6567
gui.add(myObject, 'parentRotation').min(-180).max(180).step(1).name('parentRotation').onChange(value => parentBone.pose.rotation = value);
68+
gui.add(myObject, 'parentShearX').min(-180).max(180).step(1).name('parentShearX').onChange(value => parentBone.pose.shearX = value);
69+
gui.add(myObject, 'parentShearY').min(-180).max(180).step(1).name('parentShearY').onChange(value => parentBone.pose.shearY = value);
6670
gui.add(myObject, 'parent2ScaleX').min(-3).max(3).step(0.1).name('parent2ScaleX').onChange(value => parent2Bone.pose.scaleX = value);
6771
gui.add(myObject, 'parent2ScaleY').min(-3).max(3).step(0.1).name('parent2ScaleY').onChange(value => parent2Bone.pose.scaleY = value);
6872
gui.add(myObject, 'parent2Rotation').min(-180).max(180).step(1).name('parent2Rotation').onChange(value => parent2Bone.pose.rotation = value);
73+
gui.add(myObject, 'parent2ShearX').min(-180).max(180).step(1).name('parent2ShearX').onChange(value => parent2Bone.pose.shearX = value);
74+
gui.add(myObject, 'parent2ShearY').min(-180).max(180).step(1).name('parent2ShearY').onChange(value => parent2Bone.pose.shearY = value);
6975
gui.add(myObject, 'scaleX').min(-3).max(3).step(0.1).name('scaleX').onChange(value => bone.pose.scaleX = value);
7076
gui.add(myObject, 'scaleY').min(-3).max(3).step(0.1).name('scaleY').onChange(value => bone.pose.scaleY = value);
7177
gui.add(myObject, 'rotation').min(-180).max(180).step(1).name('rotation').onChange(value => bone.pose.rotation = value);
78+
gui.add(myObject, 'shearX').min(-180).max(180).step(1).name('shearX').onChange(value => bone.pose.shearX = value);
79+
gui.add(myObject, 'shearY').min(-180).max(180).step(1).name('shearY').onChange(value => bone.pose.shearY = value);
80+
gui.add(myObject, 'spriteAlpha').min(0).max(1).step(0.01).name('spriteAlpha').onChange(value => sprite.alpha = value);
7281

7382
// add instructions
7483
const basicText = new PIXI.Text({
75-
text: "This example shows that slot objects follow scale and rotation from ancestors too, but if a rotation on a bone occurs scale won't work anymore on ancestors.",
84+
text: "This example shows that slot objects can follow scale, shear and rotation from ancestors too.",
7685
style: { fontSize: 20, fill: "white", wordWrap: true, wordWrapWidth: 300 }
7786
});
7887
basicText.position.set(10, 10);

spine-ts/spine-pixi-v8/src/Spine.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -785,26 +785,16 @@ export class Spine extends ViewContainer {
785785
container.visible = this.skeleton.drawOrder.includes(slot) && followAttachmentValue;
786786

787787
if (container.visible) {
788-
let bone: Bone | null = slot.bone;
789-
790-
const applied = bone.applied;
791-
container.position.set(applied.worldX, applied.worldY);
792-
container.angle = applied.getWorldRotationX();
793-
794-
let cumulativeScaleX = 1;
795-
let cumulativeScaleY = 1;
796-
while (bone) {
797-
cumulativeScaleX *= bone.applied.scaleX;
798-
cumulativeScaleY *= bone.applied.scaleY;
799-
bone = bone.parent;
800-
};
801-
802-
if (cumulativeScaleX < 0) container.angle -= 180;
803-
804-
container.scale.set(
805-
applied.getWorldScaleX() * Math.sign(cumulativeScaleX),
806-
applied.getWorldScaleY() * Math.sign(cumulativeScaleY),
807-
);
788+
let applied = slot.bone.applied;
789+
790+
const matrix = container.localTransform;
791+
matrix.a = applied.a;
792+
matrix.b = applied.c;
793+
matrix.c = -applied.b;
794+
matrix.d = -applied.d;
795+
matrix.tx = applied.worldX;
796+
matrix.ty = applied.worldY;
797+
container.setFromMatrix(matrix);
808798

809799
container.alpha = this.skeleton.color.a * pose.color.a;
810800
}

0 commit comments

Comments
 (0)