@safe-engine/pixi 8.4.7 → 8.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +14 -23
  2. package/dist/box2d-wasm/ContactListener.d.ts +1 -1
  3. package/dist/box2d-wasm/ContactListener.d.ts.map +1 -1
  4. package/dist/box2d-wasm/ContactListener.js +50 -18
  5. package/dist/box2d-wasm/PhysicsComponent.d.ts +27 -30
  6. package/dist/box2d-wasm/PhysicsComponent.d.ts.map +1 -1
  7. package/dist/box2d-wasm/PhysicsComponent.js +44 -11
  8. package/dist/box2d-wasm/PhysicsSprite.d.ts +3 -0
  9. package/dist/box2d-wasm/PhysicsSprite.d.ts.map +1 -1
  10. package/dist/box2d-wasm/PhysicsSprite.js +24 -3
  11. package/dist/box2d-wasm/PhysicsSystem.d.ts +4 -2
  12. package/dist/box2d-wasm/PhysicsSystem.d.ts.map +1 -1
  13. package/dist/box2d-wasm/PhysicsSystem.js +134 -44
  14. package/dist/collider/CollideComponent.d.ts +27 -10
  15. package/dist/collider/CollideComponent.d.ts.map +1 -1
  16. package/dist/collider/CollideComponent.js +10 -12
  17. package/dist/collider/helper/Intersection.d.ts +6 -6
  18. package/dist/collider/helper/Intersection.d.ts.map +1 -1
  19. package/dist/collider/helper/Intersection.js +4 -4
  20. package/dist/helper/utils.d.ts +0 -6
  21. package/dist/helper/utils.d.ts.map +1 -1
  22. package/dist/helper/utils.js +0 -12
  23. package/package.json +6 -2
  24. package/.github/workflows/npm-publish.yml +0 -35
  25. package/dist/@types/safex.d.ts +0 -15
  26. package/src/@types/index.d.ts +0 -2
  27. package/src/app.ts +0 -91
  28. package/src/base/EnhancedComponent.ts +0 -38
  29. package/src/base/gworld.ts +0 -20
  30. package/src/base/index.ts +0 -14
  31. package/src/base/utils.ts +0 -23
  32. package/src/box2d-wasm/ContactListener.ts +0 -66
  33. package/src/box2d-wasm/PhysicsComponent.ts +0 -83
  34. package/src/box2d-wasm/PhysicsSprite.ts +0 -42
  35. package/src/box2d-wasm/PhysicsSystem.ts +0 -142
  36. package/src/box2d-wasm/debugDraw.ts +0 -249
  37. package/src/box2d-wasm/index.ts +0 -19
  38. package/src/collider/CollideComponent.ts +0 -257
  39. package/src/collider/CollideSystem.ts +0 -166
  40. package/src/collider/helper/Intersection.ts +0 -139
  41. package/src/collider/helper/utils.ts +0 -37
  42. package/src/collider/index.ts +0 -16
  43. package/src/components/BaseComponent.ts +0 -17
  44. package/src/components/NodeComp.ts +0 -442
  45. package/src/components/Scene.ts +0 -17
  46. package/src/core/Color.ts +0 -7
  47. package/src/core/LoadingBar.ts +0 -63
  48. package/src/core/NodePool.ts +0 -28
  49. package/src/core/Size.ts +0 -21
  50. package/src/core/Vec2.ts +0 -108
  51. package/src/core/director.ts +0 -11
  52. package/src/core/index.ts +0 -7
  53. package/src/core/loader.ts +0 -14
  54. package/src/core/math.ts +0 -22
  55. package/src/dragonbones/DragonBonesComponent.ts +0 -32
  56. package/src/dragonbones/DragonBonesSystem.ts +0 -35
  57. package/src/dragonbones/index.ts +0 -11
  58. package/src/gui/GUIComponent.ts +0 -155
  59. package/src/gui/GUISystem.ts +0 -125
  60. package/src/helper/utils.ts +0 -50
  61. package/src/index.ts +0 -21
  62. package/src/norender/NoRenderComponent.ts +0 -60
  63. package/src/norender/NoRenderSystem.ts +0 -67
  64. package/src/norender/Touch.ts +0 -37
  65. package/src/planck/PhysicsComponent.ts +0 -83
  66. package/src/planck/PhysicsSprite.ts +0 -43
  67. package/src/planck/PhysicsSystem.ts +0 -201
  68. package/src/planck/index.ts +0 -3
  69. package/src/render/RenderComponent.ts +0 -132
  70. package/src/render/RenderSystem.ts +0 -64
  71. package/src/richtext/RichTextComp.ts +0 -50
  72. package/src/richtext/RichTextSystem.ts +0 -26
  73. package/src/richtext/html-text-parser.ts +0 -87
  74. package/src/richtext/index.ts +0 -8
  75. package/src/spine/SpineComponent.ts +0 -18
  76. package/src/spine/SpineSystem.ts +0 -30
  77. package/src/spine/index.ts +0 -11
  78. package/src/spine/lib/BatchableSpineSlot.ts +0 -138
  79. package/src/spine/lib/Spine.ts +0 -894
  80. package/src/spine/lib/SpineDebugRenderer.ts +0 -615
  81. package/src/spine/lib/SpinePipe.ts +0 -203
  82. package/src/spine/lib/SpineTexture.ts +0 -143
  83. package/src/spine/lib/assets/atlasLoader.ts +0 -158
  84. package/src/spine/lib/assets/skeletonLoader.ts +0 -81
  85. package/src/spine/lib/darktint/DarkTintBatchGeometry.ts +0 -92
  86. package/src/spine/lib/darktint/DarkTintBatcher.ts +0 -186
  87. package/src/spine/lib/darktint/DarkTintShader.ts +0 -74
  88. package/src/spine/lib/darktint/darkTintBit.ts +0 -77
  89. package/src/spine/lib/index.ts +0 -43
  90. package/src/spine/lib/require-shim.ts +0 -43
  91. package/tsconfig.json +0 -18
  92. /package/{dist/@types → @types}/index.d.ts +0 -0
@@ -1,894 +0,0 @@
1
- /** ****************************************************************************
2
- * Spine Runtimes License Agreement
3
- * Last updated July 28, 2023. Replaces all prior versions.
4
- *
5
- * Copyright (c) 2013-2023, Esoteric Software LLC
6
- *
7
- * Integration of the Spine Runtimes into software or otherwise creating
8
- * derivative works of the Spine Runtimes is permitted under the terms and
9
- * conditions of Section 2 of the Spine Editor License Agreement:
10
- * http://esotericsoftware.com/spine-editor-license
11
- *
12
- * Otherwise, it is permitted to integrate the Spine Runtimes into software or
13
- * otherwise create derivative works of the Spine Runtimes (collectively,
14
- * "Products"), provided that each user of the Products must obtain their own
15
- * Spine Editor license and redistribution of the Products in any form must
16
- * include this license and copyright notice.
17
- *
18
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
19
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
- * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
22
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
24
- * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
25
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
27
- * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
- *****************************************************************************/
29
-
30
- import {
31
- AnimationState,
32
- AnimationStateData,
33
- AtlasAttachmentLoader,
34
- Attachment,
35
- Bone,
36
- ClippingAttachment,
37
- Color,
38
- MeshAttachment,
39
- Physics,
40
- Pool,
41
- RegionAttachment,
42
- Skeleton,
43
- SkeletonBinary,
44
- SkeletonBounds,
45
- SkeletonClipping,
46
- SkeletonData,
47
- SkeletonJson,
48
- Slot,
49
- type TextureAtlas,
50
- TrackEntry,
51
- Vector2,
52
- } from '@esotericsoftware/spine-core'
53
- import {
54
- Assets,
55
- Bounds,
56
- Cache,
57
- Container,
58
- ContainerChild,
59
- ContainerOptions,
60
- DEG_TO_RAD,
61
- DestroyOptions,
62
- fastCopy,
63
- Graphics,
64
- PointData,
65
- Texture,
66
- Ticker,
67
- ViewContainer,
68
- } from 'pixi.js'
69
- import { ISpineDebugRenderer } from './SpineDebugRenderer.js'
70
-
71
- /**
72
- * Options to create a {@link Spine} using {@link Spine.from}.
73
- */
74
- export interface SpineFromOptions {
75
- /** the asset name for the skeleton `.skel` or `.json` file previously loaded into the Assets */
76
- skeleton: string
77
-
78
- /** the asset name for the atlas file previously loaded into the Assets */
79
- atlas: string
80
-
81
- /** The value passed to the skeleton reader. If omitted, 1 is passed. See {@link SkeletonBinary.scale} for details. */
82
- scale?: number
83
-
84
- /** Set the {@link Spine.autoUpdate} value. If omitted, it is set to `true`. */
85
- autoUpdate?: boolean
86
-
87
- /**
88
- * If `true`, use the dark tint renderer to render the skeleton
89
- * If `false`, use the default pixi renderer to render the skeleton
90
- * If `undefined`, use the dark tint renderer if at least one slot has tint black
91
- */
92
- darkTint?: boolean
93
- }
94
-
95
- const vectorAux = new Vector2()
96
-
97
- Skeleton.yDown = true
98
-
99
- const clipper = new SkeletonClipping()
100
-
101
- export interface SpineOptions extends ContainerOptions {
102
- /** the {@link SkeletonData} used to instantiate the skeleton */
103
- skeletonData: SkeletonData
104
-
105
- /** See {@link SpineFromOptions.autoUpdate}. */
106
- autoUpdate?: boolean
107
-
108
- /** See {@link SpineFromOptions.darkTint}. */
109
- darkTint?: boolean
110
- }
111
-
112
- /**
113
- * AnimationStateListener {@link https://en.esotericsoftware.com/spine-api-reference#AnimationStateListener events} exposed for Pixi.
114
- */
115
- export interface SpineEvents {
116
- complete: [trackEntry: TrackEntry]
117
- dispose: [trackEntry: TrackEntry]
118
- end: [trackEntry: TrackEntry]
119
- event: [trackEntry: TrackEntry, event: Event]
120
- interrupt: [trackEntry: TrackEntry]
121
- start: [trackEntry: TrackEntry]
122
- }
123
-
124
- export interface AttachmentCacheData {
125
- id: string
126
- clipped: boolean
127
- vertices: Float32Array
128
- uvs: Float32Array
129
- indices: number[]
130
- color: Color
131
- darkColor: Color
132
- darkTint: boolean
133
- skipRender: boolean
134
- texture: Texture
135
- clippedData?: {
136
- vertices: Float32Array
137
- uvs: Float32Array
138
- indices: Uint16Array
139
- vertexCount: number
140
- indicesCount: number
141
- }
142
- }
143
-
144
- interface SlotsToClipping {
145
- slot: Slot
146
- mask?: Graphics
147
- maskComputed?: boolean
148
- vertices: Array<number>
149
- }
150
-
151
- const maskPool = new Pool<Graphics>(() => new Graphics())
152
-
153
- /**
154
- * The class to instantiate a {@link Spine} game object in Pixi.
155
- * The static method {@link Spine.from} should be used to instantiate a Spine game object.
156
- */
157
- export class Spine extends ViewContainer {
158
- // Pixi properties
159
- public batched = true
160
- public buildId = 0
161
- public override readonly renderPipeId = 'spine'
162
- public _didSpineUpdate = false
163
-
164
- public beforeUpdateWorldTransforms: (object: Spine) => void = () => {
165
- /** */
166
- }
167
- public afterUpdateWorldTransforms: (object: Spine) => void = () => {
168
- /** */
169
- }
170
-
171
- // Spine properties
172
- /** The skeleton for this Spine game object. */
173
- public skeleton: Skeleton
174
- /** The animation state for this Spine game object. */
175
- public state: AnimationState
176
- public skeletonBounds?: SkeletonBounds
177
-
178
- private darkTint = false
179
- private _debug?: ISpineDebugRenderer | undefined = undefined
180
-
181
- readonly _slotsObject: Record<string, { slot: Slot; container: Container } | null> = Object.create(null)
182
- private clippingSlotToPixiMasks: Record<string, SlotsToClipping> = Object.create(null)
183
-
184
- private getSlotFromRef(slotRef: number | string | Slot): Slot {
185
- let slot: Slot | null
186
-
187
- if (typeof slotRef === 'number') slot = this.skeleton.slots[slotRef]
188
- else if (typeof slotRef === 'string') slot = this.skeleton.findSlot(slotRef)
189
- else slot = slotRef
190
-
191
- if (!slot) throw new Error(`No slot found with the given slot reference: ${slotRef}`)
192
-
193
- return slot
194
- }
195
-
196
- public spineAttachmentsDirty = true
197
- public spineTexturesDirty = true
198
-
199
- private _lastAttachments: Attachment[] = []
200
-
201
- private _stateChanged = true
202
- private attachmentCacheData: Record<string, AttachmentCacheData>[] = []
203
-
204
- public get debug(): ISpineDebugRenderer | undefined {
205
- return this._debug
206
- }
207
-
208
- /** Pass a {@link SpineDebugRenderer} or create your own {@link ISpineDebugRenderer} to render bones, meshes, ...
209
- * @example spineGO.debug = new SpineDebugRenderer();
210
- */
211
- public set debug(value: ISpineDebugRenderer | undefined) {
212
- if (this._debug) {
213
- this._debug.unregisterSpine(this)
214
- }
215
- if (value) {
216
- value.registerSpine(this)
217
- }
218
- this._debug = value
219
- }
220
-
221
- private _autoUpdate = true
222
-
223
- public get autoUpdate(): boolean {
224
- return this._autoUpdate
225
- }
226
- /** When `true`, the Spine AnimationState and the Skeleton will be automatically updated using the {@link Ticker.shared} instance. */
227
- public set autoUpdate(value: boolean) {
228
- if (value) {
229
- Ticker.shared.add(this.internalUpdate, this)
230
- } else {
231
- Ticker.shared.remove(this.internalUpdate, this)
232
- }
233
-
234
- this._autoUpdate = value
235
- }
236
-
237
- private hasNeverUpdated = true
238
- constructor(options: SpineOptions | SkeletonData) {
239
- if (options instanceof SkeletonData) {
240
- options = {
241
- skeletonData: options,
242
- }
243
- }
244
-
245
- super(options)
246
-
247
- const skeletonData = options instanceof SkeletonData ? options : options.skeletonData
248
-
249
- this.skeleton = new Skeleton(skeletonData)
250
- this.state = new AnimationState(new AnimationStateData(skeletonData))
251
- this.autoUpdate = options?.autoUpdate ?? true
252
-
253
- // dark tint can be enabled by options, otherwise is enable if at least one slot has tint black
254
- this.darkTint = options?.darkTint === undefined ? this.skeleton.slots.some((slot) => !!slot.data.darkColor) : options?.darkTint
255
-
256
- const slots = this.skeleton.slots
257
-
258
- for (let i = 0; i < slots.length; i++) {
259
- this.attachmentCacheData[i] = Object.create(null)
260
- }
261
- }
262
-
263
- /** If {@link Spine.autoUpdate} is `false`, this method allows to update the AnimationState and the Skeleton with the given delta. */
264
- public update(dt: number): void {
265
- this.internalUpdate(0, dt)
266
- }
267
-
268
- protected internalUpdate(_deltaFrame: any, deltaSeconds?: number): void {
269
- // Because reasons, pixi uses deltaFrames at 60fps.
270
- // We ignore the default deltaFrames and use the deltaSeconds from pixi ticker.
271
- this._updateAndApplyState(deltaSeconds ?? Ticker.shared.deltaMS / 1000)
272
- }
273
-
274
- override get bounds() {
275
- if (this._boundsDirty) {
276
- this.updateBounds()
277
- }
278
-
279
- return this._bounds
280
- }
281
-
282
- /**
283
- * Set the position of the bone given in input through a {@link IPointData}.
284
- * @param bone: the bone name or the bone instance to set the position
285
- * @param outPos: the new position of the bone.
286
- * @throws {Error}: if the given bone is not found in the skeleton, an error is thrown
287
- */
288
- public setBonePosition(bone: string | Bone, position: PointData): void {
289
- const boneAux = bone
290
-
291
- if (typeof bone === 'string') {
292
- bone = this.skeleton.findBone(bone) as Bone
293
- }
294
-
295
- if (!bone) throw Error(`Cant set bone position, bone ${String(boneAux)} not found`)
296
- vectorAux.set(position.x, position.y)
297
-
298
- if (bone.parent) {
299
- const aux = bone.parent.worldToLocal(vectorAux)
300
-
301
- bone.x = aux.x
302
- bone.y = -aux.y
303
- } else {
304
- bone.x = vectorAux.x
305
- bone.y = vectorAux.y
306
- }
307
- }
308
-
309
- /**
310
- * Return the position of the bone given in input into an {@link IPointData}.
311
- * @param bone: the bone name or the bone instance to get the position from
312
- * @param outPos: an optional {@link IPointData} to use to return the bone position, rathern than instantiating a new object.
313
- * @returns {IPointData | undefined}: the position of the bone, or undefined if no matching bone is found in the skeleton
314
- */
315
- public getBonePosition(bone: string | Bone, outPos?: PointData): PointData | undefined {
316
- const boneAux = bone
317
-
318
- if (typeof bone === 'string') {
319
- bone = this.skeleton.findBone(bone) as Bone
320
- }
321
-
322
- if (!bone) {
323
- console.error(`Cant set bone position! Bone ${String(boneAux)} not found`)
324
-
325
- return outPos
326
- }
327
-
328
- if (!outPos) {
329
- outPos = { x: 0, y: 0 }
330
- }
331
-
332
- outPos.x = bone.worldX
333
- outPos.y = bone.worldY
334
-
335
- return outPos
336
- }
337
-
338
- /**
339
- * Advance the state and skeleton by the given time, then update slot objects too.
340
- * The container transform is not updated.
341
- *
342
- * @param time the time at which to set the state
343
- */
344
- private _updateAndApplyState(time: number) {
345
- this.hasNeverUpdated = false
346
-
347
- this.state.update(time)
348
- this.skeleton.update(time)
349
-
350
- const { skeleton } = this
351
-
352
- this.state.apply(skeleton)
353
-
354
- this.beforeUpdateWorldTransforms(this)
355
- skeleton.updateWorldTransform(Physics.update)
356
- this.afterUpdateWorldTransforms(this)
357
-
358
- this.updateSlotObjects()
359
-
360
- this._stateChanged = true
361
-
362
- this._boundsDirty = true
363
-
364
- this.onViewUpdate()
365
- }
366
-
367
- /**
368
- * - validates the attachments - to flag if the attachments have changed this state
369
- * - transforms the attachments - to update the vertices of the attachments based on the new positions
370
- * @internal
371
- */
372
- _validateAndTransformAttachments() {
373
- if (!this._stateChanged) return
374
- this._stateChanged = false
375
-
376
- this.validateAttachments()
377
-
378
- this.transformAttachments()
379
- }
380
-
381
- private validateAttachments() {
382
- const currentDrawOrder = this.skeleton.drawOrder
383
-
384
- const lastAttachments = this._lastAttachments
385
-
386
- let index = 0
387
-
388
- let spineAttachmentsDirty = false
389
-
390
- for (let i = 0; i < currentDrawOrder.length; i++) {
391
- const slot = currentDrawOrder[i]
392
- const attachment = slot.getAttachment()
393
-
394
- if (attachment) {
395
- if (attachment !== lastAttachments[index]) {
396
- spineAttachmentsDirty = true
397
- lastAttachments[index] = attachment
398
- }
399
-
400
- index++
401
- }
402
- }
403
-
404
- if (index !== lastAttachments.length) {
405
- spineAttachmentsDirty = true
406
- lastAttachments.length = index
407
- }
408
-
409
- this.spineAttachmentsDirty ||= spineAttachmentsDirty
410
- }
411
-
412
- private updateAndSetPixiMask(slot: Slot, last: boolean) {
413
- // assign/create the currentClippingSlot
414
- const attachment = slot.attachment
415
- if (attachment && attachment instanceof ClippingAttachment) {
416
- const clip = (this.clippingSlotToPixiMasks[slot.data.name] ||= { slot, vertices: new Array<number>() })
417
- clip.maskComputed = false
418
- this.currentClippingSlot = this.clippingSlotToPixiMasks[slot.data.name]
419
- return
420
- }
421
-
422
- // assign the currentClippingSlot mask to the slot object
423
- const currentClippingSlot = this.currentClippingSlot
424
- const slotObject = this._slotsObject[slot.data.name]
425
- if (currentClippingSlot && slotObject) {
426
- const slotClipping = currentClippingSlot.slot
427
- const clippingAttachment = slotClipping.attachment as ClippingAttachment
428
-
429
- // create the pixi mask, only the first time and if the clipped slot is the first one clipped by this currentClippingSlot
430
- let mask = currentClippingSlot.mask as Graphics
431
- if (!mask) {
432
- mask = maskPool.obtain()
433
- currentClippingSlot.mask = mask
434
- this.addChild(mask)
435
- }
436
-
437
- // compute the pixi mask polygon, if the clipped slot is the first one clipped by this currentClippingSlot
438
- if (!currentClippingSlot.maskComputed) {
439
- currentClippingSlot.maskComputed = true
440
- const worldVerticesLength = clippingAttachment.worldVerticesLength
441
- const vertices = currentClippingSlot.vertices
442
- clippingAttachment.computeWorldVertices(slotClipping, 0, worldVerticesLength, vertices, 0, 2)
443
- mask.clear().poly(vertices).stroke({ width: 0 }).fill({ alpha: 0.25 })
444
- }
445
- slotObject.container.mask = mask
446
- } else if (slotObject?.container.mask) {
447
- // remove the mask, if slot object has a mask, but currentClippingSlot is undefined
448
- slotObject.container.mask = null
449
- }
450
-
451
- // if current slot is the ending one of the currentClippingSlot mask, set currentClippingSlot to undefined
452
- if (currentClippingSlot && (currentClippingSlot.slot.attachment as ClippingAttachment).endSlot == slot.data) {
453
- this.currentClippingSlot = undefined
454
- }
455
-
456
- // clean up unused masks
457
- if (last) {
458
- for (const key in this.clippingSlotToPixiMasks) {
459
- const clippingSlotToPixiMask = this.clippingSlotToPixiMasks[key]
460
- if (
461
- (!(clippingSlotToPixiMask.slot.attachment instanceof ClippingAttachment) || !clippingSlotToPixiMask.maskComputed) &&
462
- clippingSlotToPixiMask.mask
463
- ) {
464
- this.removeChild(clippingSlotToPixiMask.mask)
465
- maskPool.free(clippingSlotToPixiMask.mask)
466
- clippingSlotToPixiMask.mask = undefined
467
- }
468
- }
469
- }
470
- }
471
-
472
- private currentClippingSlot: SlotsToClipping | undefined
473
- private transformAttachments() {
474
- const currentDrawOrder = this.skeleton.drawOrder
475
-
476
- for (let i = 0; i < currentDrawOrder.length; i++) {
477
- const slot = currentDrawOrder[i]
478
-
479
- this.updateAndSetPixiMask(slot, i === currentDrawOrder.length - 1)
480
-
481
- const attachment = slot.getAttachment()
482
-
483
- if (attachment) {
484
- if (attachment instanceof MeshAttachment || attachment instanceof RegionAttachment) {
485
- const cacheData = this._getCachedData(slot, attachment)
486
-
487
- if (attachment instanceof RegionAttachment) {
488
- attachment.computeWorldVertices(slot, cacheData.vertices, 0, 2)
489
- } else {
490
- attachment.computeWorldVertices(slot, 0, attachment.worldVerticesLength, cacheData.vertices, 0, 2)
491
- }
492
-
493
- // sequences uvs are known only after computeWorldVertices is invoked
494
- if (cacheData.uvs.length < attachment.uvs.length) {
495
- cacheData.uvs = new Float32Array(attachment.uvs.length)
496
- }
497
-
498
- // need to copy because attachments uvs are shared among skeletons using the same atlas
499
- fastCopy((attachment.uvs as Float32Array).buffer, cacheData.uvs.buffer)
500
-
501
- const skeleton = slot.bone.skeleton
502
- const skeletonColor = skeleton.color
503
- const slotColor = slot.color
504
-
505
- const attachmentColor = attachment.color
506
-
507
- cacheData.color.set(
508
- skeletonColor.r * slotColor.r * attachmentColor.r,
509
- skeletonColor.g * slotColor.g * attachmentColor.g,
510
- skeletonColor.b * slotColor.b * attachmentColor.b,
511
- skeletonColor.a * slotColor.a * attachmentColor.a,
512
- )
513
-
514
- if (slot.darkColor) {
515
- cacheData.darkColor.setFromColor(slot.darkColor)
516
- }
517
-
518
- cacheData.skipRender = cacheData.clipped = false
519
-
520
- const texture = attachment.region?.texture.texture || Texture.EMPTY
521
-
522
- if (cacheData.texture !== texture) {
523
- cacheData.texture = texture
524
- this.spineTexturesDirty = true
525
- }
526
-
527
- if (clipper.isClipping()) {
528
- this.updateClippingData(cacheData)
529
- }
530
- } else if (attachment instanceof ClippingAttachment) {
531
- clipper.clipStart(slot, attachment)
532
- continue
533
- }
534
- }
535
- clipper.clipEndWithSlot(slot)
536
- }
537
- clipper.clipEnd()
538
- }
539
-
540
- private updateClippingData(cacheData: AttachmentCacheData) {
541
- cacheData.clipped = true
542
-
543
- clipper.clipTrianglesUnpacked(cacheData.vertices, cacheData.indices, cacheData.indices.length, cacheData.uvs)
544
-
545
- const { clippedVertices, clippedUVs, clippedTriangles } = clipper
546
-
547
- const verticesCount = clippedVertices.length / 2
548
- const indicesCount = clippedTriangles.length
549
-
550
- if (!cacheData.clippedData) {
551
- cacheData.clippedData = {
552
- vertices: new Float32Array(verticesCount * 2),
553
- uvs: new Float32Array(verticesCount * 2),
554
- vertexCount: verticesCount,
555
- indices: new Uint16Array(indicesCount),
556
- indicesCount,
557
- }
558
-
559
- this.spineAttachmentsDirty = true
560
- }
561
-
562
- const clippedData = cacheData.clippedData
563
-
564
- const sizeChange = clippedData.vertexCount !== verticesCount || indicesCount !== clippedData.indicesCount
565
-
566
- cacheData.skipRender = verticesCount === 0
567
-
568
- if (sizeChange) {
569
- this.spineAttachmentsDirty = true
570
-
571
- if (clippedData.vertexCount < verticesCount) {
572
- // buffer reuse!
573
- clippedData.vertices = new Float32Array(verticesCount * 2)
574
- clippedData.uvs = new Float32Array(verticesCount * 2)
575
- }
576
-
577
- if (clippedData.indices.length < indicesCount) {
578
- clippedData.indices = new Uint16Array(indicesCount)
579
- }
580
- }
581
-
582
- const { vertices, uvs, indices } = clippedData
583
-
584
- for (let i = 0; i < verticesCount; i++) {
585
- vertices[i * 2] = clippedVertices[i * 2]
586
- vertices[i * 2 + 1] = clippedVertices[i * 2 + 1]
587
-
588
- uvs[i * 2] = clippedUVs[i * 2]
589
- uvs[i * 2 + 1] = clippedUVs[i * 2 + 1]
590
- }
591
-
592
- clippedData.vertexCount = verticesCount
593
-
594
- for (let i = 0; i < indicesCount; i++) {
595
- if (indices[i] !== clippedTriangles[i]) {
596
- this.spineAttachmentsDirty = true
597
- indices[i] = clippedTriangles[i]
598
- }
599
- }
600
-
601
- clippedData.indicesCount = indicesCount
602
- }
603
-
604
- /**
605
- * ensure that attached containers map correctly to their slots
606
- * along with their position, rotation, scale, and visibility.
607
- */
608
- private updateSlotObjects() {
609
- for (const i in this._slotsObject) {
610
- const slotAttachment = this._slotsObject[i]
611
-
612
- if (!slotAttachment) continue
613
-
614
- this.updateSlotObject(slotAttachment)
615
- }
616
- }
617
-
618
- private updateSlotObject(slotAttachment: { slot: Slot; container: Container }) {
619
- const { slot, container } = slotAttachment
620
-
621
- container.visible = this.skeleton.drawOrder.includes(slot)
622
-
623
- if (container.visible) {
624
- const bone = slot.bone
625
-
626
- container.position.set(bone.worldX, bone.worldY)
627
-
628
- container.scale.x = bone.getWorldScaleX()
629
- container.scale.y = bone.getWorldScaleY()
630
-
631
- container.rotation = bone.getWorldRotationX() * DEG_TO_RAD
632
-
633
- container.alpha = this.skeleton.color.a * slot.color.a
634
- }
635
- }
636
-
637
- /** @internal */
638
- _getCachedData(slot: Slot, attachment: RegionAttachment | MeshAttachment): AttachmentCacheData {
639
- return this.attachmentCacheData[slot.data.index][attachment.name] || this.initCachedData(slot, attachment)
640
- }
641
-
642
- private initCachedData(slot: Slot, attachment: RegionAttachment | MeshAttachment): AttachmentCacheData {
643
- let vertices: Float32Array
644
-
645
- if (attachment instanceof RegionAttachment) {
646
- vertices = new Float32Array(8)
647
-
648
- this.attachmentCacheData[slot.data.index][attachment.name] = {
649
- id: `${slot.data.index}-${attachment.name}`,
650
- vertices,
651
- clipped: false,
652
- indices: [0, 1, 2, 0, 2, 3],
653
- uvs: new Float32Array(attachment.uvs.length),
654
- color: new Color(1, 1, 1, 1),
655
- darkColor: new Color(0, 0, 0, 0),
656
- darkTint: this.darkTint,
657
- skipRender: false,
658
- texture: attachment.region?.texture.texture,
659
- }
660
- } else {
661
- vertices = new Float32Array(attachment.worldVerticesLength)
662
-
663
- this.attachmentCacheData[slot.data.index][attachment.name] = {
664
- id: `${slot.data.index}-${attachment.name}`,
665
- vertices,
666
- clipped: false,
667
- indices: attachment.triangles,
668
- uvs: new Float32Array(attachment.uvs.length),
669
- color: new Color(1, 1, 1, 1),
670
- darkColor: new Color(0, 0, 0, 0),
671
- darkTint: this.darkTint,
672
- skipRender: false,
673
- texture: attachment.region?.texture.texture,
674
- }
675
- }
676
-
677
- return this.attachmentCacheData[slot.data.index][attachment.name]
678
- }
679
-
680
- protected onViewUpdate() {
681
- // increment from the 12th bit!
682
- this._didViewChangeTick++
683
- this._boundsDirty = true
684
-
685
- if (this.didViewUpdate) return
686
- this.didViewUpdate = true
687
-
688
- const renderGroup = this.renderGroup || this.parentRenderGroup
689
-
690
- if (renderGroup) {
691
- renderGroup.onChildViewUpdate(this)
692
- }
693
-
694
- this.debug?.renderDebug(this)
695
- }
696
-
697
- /**
698
- * Attaches a PixiJS container to a specified slot. This will map the world transform of the slots bone
699
- * to the attached container. A container can only be attached to one slot at a time.
700
- *
701
- * @param container - The container to attach to the slot
702
- * @param slotRef - The slot id or slot to attach to
703
- */
704
- public addSlotObject(slot: number | string | Slot, container: Container) {
705
- slot = this.getSlotFromRef(slot)
706
-
707
- // need to check in on the container too...
708
- for (const i in this._slotsObject) {
709
- if (this._slotsObject[i]?.container === container) {
710
- this.removeSlotObject(this._slotsObject[i].slot)
711
- }
712
- }
713
-
714
- this.removeSlotObject(slot)
715
-
716
- container.includeInBuild = false
717
-
718
- // TODO only add once??
719
- this.addChild(container)
720
-
721
- const slotObject = { container, slot }
722
- this._slotsObject[slot.data.name] = slotObject
723
-
724
- this.updateSlotObject(slotObject)
725
- }
726
-
727
- /**
728
- * Removes a PixiJS container from the slot it is attached to.
729
- *
730
- * @param container - The container to detach from the slot
731
- * @param slotOrContainer - The container, slot id or slot to detach from
732
- */
733
- public removeSlotObject(slotOrContainer: number | string | Slot | Container) {
734
- let containerToRemove: Container | undefined
735
-
736
- if (slotOrContainer instanceof Container) {
737
- for (const i in this._slotsObject) {
738
- if (this._slotsObject[i]?.container === slotOrContainer) {
739
- this._slotsObject[i] = null
740
-
741
- containerToRemove = slotOrContainer
742
- break
743
- }
744
- }
745
- } else {
746
- const slot = this.getSlotFromRef(slotOrContainer)
747
-
748
- containerToRemove = this._slotsObject[slot.data.name]?.container
749
- this._slotsObject[slot.data.name] = null
750
- }
751
-
752
- if (containerToRemove) {
753
- this.removeChild(containerToRemove)
754
-
755
- containerToRemove.includeInBuild = true
756
- }
757
- }
758
-
759
- /**
760
- * Returns a container attached to a slot, or undefined if no container is attached.
761
- *
762
- * @param slotRef - The slot id or slot to get the attachment from
763
- * @returns - The container attached to the slot
764
- */
765
- public getSlotObject(slot: number | string | Slot): Container<ContainerChild> {
766
- slot = this.getSlotFromRef(slot)
767
-
768
- return this._slotsObject[slot.data.name]?.container
769
- }
770
-
771
- protected updateBounds() {
772
- this._boundsDirty = false
773
-
774
- this.skeletonBounds ||= new SkeletonBounds()
775
-
776
- const skeletonBounds = this.skeletonBounds
777
-
778
- skeletonBounds.update(this.skeleton, true)
779
-
780
- if (skeletonBounds.minX === Infinity) {
781
- if (this.hasNeverUpdated) {
782
- this._updateAndApplyState(0)
783
- this._boundsDirty = false
784
- }
785
- this._validateAndTransformAttachments()
786
-
787
- const drawOrder = this.skeleton.drawOrder
788
- const bounds = this._bounds
789
-
790
- bounds.clear()
791
-
792
- for (let i = 0; i < drawOrder.length; i++) {
793
- const slot = drawOrder[i]
794
-
795
- const attachment = slot.getAttachment()
796
-
797
- if (attachment && (attachment instanceof RegionAttachment || attachment instanceof MeshAttachment)) {
798
- const cacheData = this._getCachedData(slot, attachment)
799
-
800
- bounds.addVertexData(cacheData.vertices, 0, cacheData.vertices.length)
801
- }
802
- }
803
- } else {
804
- this._bounds.minX = skeletonBounds.minX
805
- this._bounds.minY = skeletonBounds.minY
806
- this._bounds.maxX = skeletonBounds.maxX
807
- this._bounds.maxY = skeletonBounds.maxY
808
- }
809
- }
810
-
811
- /** @internal */
812
- addBounds(bounds: Bounds) {
813
- bounds.addBounds(this.bounds)
814
- }
815
-
816
- /**
817
- * Destroys this sprite renderable and optionally its texture.
818
- * @param options - Options parameter. A boolean will act as if all options
819
- * have been set to that value
820
- * @param {boolean} [options.texture=false] - Should it destroy the current texture of the renderable as well
821
- * @param {boolean} [options.textureSource=false] - Should it destroy the textureSource of the renderable as well
822
- */
823
- public override destroy(options: DestroyOptions = false) {
824
- super.destroy(options)
825
-
826
- Ticker.shared.remove(this.internalUpdate, this)
827
- this.state.clearListeners()
828
- this.debug = undefined
829
- this.skeleton = null as any
830
- this.state = null as any
831
- ;(this._slotsObject as any) = null
832
- this._lastAttachments.length = 0
833
- this.attachmentCacheData = null as any
834
- }
835
-
836
- /** Converts a point from the skeleton coordinate system to the Pixi world coordinate system. */
837
- public skeletonToPixiWorldCoordinates(point: { x: number; y: number }) {
838
- this.worldTransform.apply(point, point)
839
- }
840
-
841
- /** Converts a point from the Pixi world coordinate system to the skeleton coordinate system. */
842
- public pixiWorldCoordinatesToSkeleton(point: { x: number; y: number }) {
843
- this.worldTransform.applyInverse(point, point)
844
- }
845
-
846
- /** Converts a point from the Pixi world coordinate system to the bone's local coordinate system. */
847
- public pixiWorldCoordinatesToBone(point: { x: number; y: number }, bone: Bone) {
848
- this.pixiWorldCoordinatesToSkeleton(point)
849
- if (bone.parent) {
850
- bone.parent.worldToLocal(point as Vector2)
851
- } else {
852
- bone.worldToLocal(point as Vector2)
853
- }
854
- }
855
-
856
- /**
857
- * Use this method to instantiate a Spine game object.
858
- * Before instantiating a Spine game object, the skeleton (`.skel` or `.json`) and the atlas text files must be loaded into the Assets. For example:
859
- * ```
860
- * PIXI.Assets.add("sackData", "./assets/sack-pro.skel");
861
- * PIXI.Assets.add("sackAtlas", "./assets/sack-pma.atlas");
862
- * await PIXI.Assets.load(["sackData", "sackAtlas"]);
863
- * ```
864
- * Once a Spine game object is created, its skeleton data is cached into {@link Cache} using the key:
865
- * `${skeletonAssetName}-${atlasAssetName}-${options?.scale ?? 1}`
866
- *
867
- * @param options - Options to configure the Spine game object. See {@link SpineFromOptions}
868
- * @returns {Spine} The Spine game object instantiated
869
- */
870
- static from({ skeleton, atlas, scale = 1, darkTint, autoUpdate = true }: SpineFromOptions) {
871
- const cacheKey = `${skeleton}-${atlas}-${scale}`
872
-
873
- if (Cache.has(cacheKey)) {
874
- return new Spine(Cache.get<SkeletonData>(cacheKey))
875
- }
876
-
877
- const skeletonAsset = Assets.get<any | Uint8Array>(skeleton)
878
-
879
- const atlasAsset = Assets.get<TextureAtlas>(atlas)
880
- const attachmentLoader = new AtlasAttachmentLoader(atlasAsset)
881
- const parser = skeletonAsset instanceof Uint8Array ? new SkeletonBinary(attachmentLoader) : new SkeletonJson(attachmentLoader)
882
-
883
- parser.scale = scale
884
- const skeletonData = parser.readSkeletonData(skeletonAsset)
885
-
886
- Cache.set(cacheKey, skeletonData)
887
-
888
- return new Spine({
889
- skeletonData,
890
- darkTint,
891
- autoUpdate,
892
- })
893
- }
894
- }