reze-engine 0.15.0 → 0.16.0

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 (93) hide show
  1. package/README.md +3 -3
  2. package/dist/camera.d.ts +2 -0
  3. package/dist/camera.d.ts.map +1 -1
  4. package/dist/camera.js +8 -3
  5. package/dist/engine.d.ts +19 -4
  6. package/dist/engine.d.ts.map +1 -1
  7. package/dist/engine.js +294 -27
  8. package/dist/gpu-profile.d.ts +19 -0
  9. package/dist/gpu-profile.d.ts.map +1 -0
  10. package/dist/gpu-profile.js +120 -0
  11. package/dist/ik-solver.d.ts.map +1 -1
  12. package/dist/ik-solver.js +13 -7
  13. package/dist/math.d.ts +2 -0
  14. package/dist/math.d.ts.map +1 -1
  15. package/dist/math.js +65 -46
  16. package/dist/model.d.ts +28 -1
  17. package/dist/model.d.ts.map +1 -1
  18. package/dist/model.js +259 -75
  19. package/dist/physics/body.d.ts +2 -0
  20. package/dist/physics/body.d.ts.map +1 -1
  21. package/dist/physics/body.js +30 -0
  22. package/dist/physics/constraint.d.ts +13 -0
  23. package/dist/physics/constraint.d.ts.map +1 -1
  24. package/dist/physics/constraint.js +13 -0
  25. package/dist/physics/contact.d.ts +28 -0
  26. package/dist/physics/contact.d.ts.map +1 -1
  27. package/dist/physics/contact.js +27 -32
  28. package/dist/physics/physics.d.ts +3 -0
  29. package/dist/physics/physics.d.ts.map +1 -1
  30. package/dist/physics/physics.js +57 -8
  31. package/dist/physics/profile.d.ts +18 -0
  32. package/dist/physics/profile.d.ts.map +1 -0
  33. package/dist/physics/profile.js +44 -0
  34. package/dist/physics/solver.d.ts.map +1 -1
  35. package/dist/physics/solver.js +436 -293
  36. package/dist/pmx-loader.d.ts.map +1 -1
  37. package/dist/pmx-loader.js +2 -30
  38. package/dist/shaders/materials/body.d.ts +1 -1
  39. package/dist/shaders/materials/body.d.ts.map +1 -1
  40. package/dist/shaders/materials/body.js +3 -1
  41. package/dist/shaders/materials/cloth_rough.d.ts +1 -1
  42. package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
  43. package/dist/shaders/materials/cloth_rough.js +3 -1
  44. package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
  45. package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
  46. package/dist/shaders/materials/common.d.ts +2 -2
  47. package/dist/shaders/materials/common.d.ts.map +1 -1
  48. package/dist/shaders/materials/common.js +8 -0
  49. package/dist/shaders/materials/default.d.ts +1 -1
  50. package/dist/shaders/materials/default.d.ts.map +1 -1
  51. package/dist/shaders/materials/eye.d.ts +1 -1
  52. package/dist/shaders/materials/eye.d.ts.map +1 -1
  53. package/dist/shaders/materials/face.d.ts +1 -1
  54. package/dist/shaders/materials/face.d.ts.map +1 -1
  55. package/dist/shaders/materials/face.js +4 -1
  56. package/dist/shaders/materials/hair.d.ts +1 -1
  57. package/dist/shaders/materials/hair.d.ts.map +1 -1
  58. package/dist/shaders/materials/metal.d.ts +1 -1
  59. package/dist/shaders/materials/metal.d.ts.map +1 -1
  60. package/dist/shaders/materials/stockings.d.ts +1 -1
  61. package/dist/shaders/materials/stockings.d.ts.map +1 -1
  62. package/dist/shaders/materials/stockings.js +13 -9
  63. package/dist/shaders/passes/composite.d.ts +1 -1
  64. package/dist/shaders/passes/composite.d.ts.map +1 -1
  65. package/dist/shaders/passes/composite.js +18 -12
  66. package/dist/shaders/passes/morph.d.ts +2 -0
  67. package/dist/shaders/passes/morph.d.ts.map +1 -0
  68. package/dist/shaders/passes/morph.js +49 -0
  69. package/package.json +1 -1
  70. package/src/camera.ts +9 -3
  71. package/src/engine.ts +336 -29
  72. package/src/ik-solver.ts +14 -7
  73. package/src/math.ts +41 -51
  74. package/src/model.ts +288 -91
  75. package/src/physics/body.ts +29 -0
  76. package/src/physics/constraint.ts +30 -0
  77. package/src/physics/contact.ts +43 -30
  78. package/src/physics/physics.ts +58 -17
  79. package/src/physics/solver.ts +431 -284
  80. package/src/pmx-loader.ts +2 -35
  81. package/src/shaders/materials/body.ts +3 -1
  82. package/src/shaders/materials/cloth_rough.ts +3 -1
  83. package/src/shaders/materials/common.ts +8 -0
  84. package/src/shaders/materials/face.ts +4 -1
  85. package/src/shaders/materials/stockings.ts +13 -9
  86. package/src/shaders/passes/composite.ts +18 -12
  87. package/src/shaders/passes/morph.ts +50 -0
  88. package/dist/physics-debug.d.ts +0 -30
  89. package/dist/physics-debug.d.ts.map +0 -1
  90. package/dist/physics-debug.js +0 -526
  91. package/dist/shaders/passes/physics-debug.d.ts +0 -2
  92. package/dist/shaders/passes/physics-debug.d.ts.map +0 -1
  93. package/dist/shaders/passes/physics-debug.js +0 -69
package/dist/model.js CHANGED
@@ -6,6 +6,13 @@ import { VMDLoader } from "./vmd-loader";
6
6
  import { VMDWriter } from "./vmd-writer";
7
7
  import { AnimationState, interpolateControlPoints, rawInterpolationToBoneInterpolation, } from "./animation";
8
8
  const VERTEX_STRIDE = 8;
9
+ // Animation-sampling scratch (applyPoseFromClip → convertVMDTranslationToLocal). These
10
+ // run sequentially and are not reentrant with the world-matrix scratch in math.ts, so
11
+ // plain module singletons are safe and let the per-bone sample path allocate nothing.
12
+ const _animSlerp = new Quat(0, 0, 0, 1);
13
+ const _animInterpT = new Vec3(0, 0, 0);
14
+ const _convOut = new Vec3(0, 0, 0);
15
+ const _convMat = new Float32Array(16);
9
16
  export class Model {
10
17
  get name() {
11
18
  return this._name;
@@ -37,6 +44,15 @@ export class Model {
37
44
  }
38
45
  constructor(vertexData, indexData, textures, materials, skeleton, skinning, morphing, rigidbodies = [], joints = []) {
39
46
  this._name = "";
47
+ this.morphPrevMinVert = -1; // vertices touched by last applyMorphs (reset to base this pass)
48
+ this.morphPrevMaxVert = -1;
49
+ this.morphPendingMinVert = -1; // accumulated range awaiting a GPU upload
50
+ this.morphPendingMaxVert = -1;
51
+ this.morphUploadFull = true; // first upload after load pushes the whole buffer once
52
+ // GPU morph path: when enabled (engine set up the compute buffers), applyMorphs only
53
+ // resolves effective weights and flags them dirty — the compute pass does the vertex work.
54
+ this.gpuMorphEnabled = false;
55
+ this.morphWeightsDirty = false;
40
56
  this.textures = [];
41
57
  this.materials = [];
42
58
  // Physics data from PMX
@@ -141,6 +157,58 @@ export class Model {
141
157
  }
142
158
  this.runtimeSkeleton.ikChainInfo = ikChainInfo;
143
159
  this.runtimeSkeleton.ikSolvers = ikSolvers;
160
+ this.buildDeformOrder();
161
+ }
162
+ // Precompute the bone order that computeWorldMatrices iterates every frame: every
163
+ // bone appears after its parent. This is the exact finishing order the previous
164
+ // recursive computeWorld() produced (walk up the not-yet-emitted ancestor chain,
165
+ // then emit it top-down; ties broken by ascending index) — so behavior is identical,
166
+ // minus the per-frame closure + visited-array allocation and the recursion overhead.
167
+ buildDeformOrder() {
168
+ const bones = this.skeleton.bones;
169
+ const n = bones.length;
170
+ const order = new Int32Array(n);
171
+ const done = new Uint8Array(n);
172
+ const stack = [];
173
+ let k = 0;
174
+ for (let i = 0; i < n; i++) {
175
+ if (done[i])
176
+ continue;
177
+ stack.length = 0;
178
+ let cur = i;
179
+ // Collect the chain of not-yet-emitted ancestors up to the root (or a done one).
180
+ while (cur >= 0 && cur < n && !done[cur]) {
181
+ stack.push(cur);
182
+ cur = bones[cur].parentIndex;
183
+ }
184
+ // Emit from the topmost ancestor down so parents precede children.
185
+ for (let s = stack.length - 1; s >= 0; s--) {
186
+ const b = stack[s];
187
+ done[b] = 1;
188
+ order[k++] = b;
189
+ }
190
+ }
191
+ this.deformOrder = order;
192
+ // Accumulate bind-pose world positions in the same parent-before-child order and
193
+ // with the same add order as the old recursive computeBindPoseWorldPosition, so the
194
+ // downstream arithmetic stays bit-identical.
195
+ const bindWorld = new Float32Array(n * 3);
196
+ for (let idx = 0; idx < n; idx++) {
197
+ const i = order[idx];
198
+ const bt = bones[i].bindTranslation;
199
+ const p = bones[i].parentIndex;
200
+ if (p >= 0 && p < n) {
201
+ bindWorld[i * 3 + 0] = bindWorld[p * 3 + 0] + bt[0];
202
+ bindWorld[i * 3 + 1] = bindWorld[p * 3 + 1] + bt[1];
203
+ bindWorld[i * 3 + 2] = bindWorld[p * 3 + 2] + bt[2];
204
+ }
205
+ else {
206
+ bindWorld[i * 3 + 0] = bt[0];
207
+ bindWorld[i * 3 + 1] = bt[1];
208
+ bindWorld[i * 3 + 2] = bt[2];
209
+ }
210
+ }
211
+ this.bindWorldPos = bindWorld;
144
212
  }
145
213
  initializeTweenBuffers() {
146
214
  const boneCount = this.skeleton.bones.length;
@@ -411,51 +479,50 @@ export class Model {
411
479
  }
412
480
  }
413
481
  // VMD translation (world delta from bind pose) → bone local space; optional rotation for animation vs IK
482
+ // Returns a REUSED scratch Vec3 (_convOut) — callers must copy immediately (they do:
483
+ // .set() / destructure). Zero allocation; result is bit-identical to the previous
484
+ // recursive implementation (verified numerically).
414
485
  convertVMDTranslationToLocal(boneIdx, vmdRelativeTranslation, rotation) {
415
- const skeleton = this.skeleton;
416
- const bones = skeleton.bones;
417
- const localRot = this.runtimeSkeleton.localRotations;
418
- // Compute bind pose world positions for all bones
419
- const computeBindPoseWorldPosition = (idx) => {
420
- const bone = bones[idx];
421
- const bindPos = new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2]);
422
- if (bone.parentIndex >= 0 && bone.parentIndex < bones.length) {
423
- const parentWorldPos = computeBindPoseWorldPosition(bone.parentIndex);
424
- return parentWorldPos.add(bindPos);
425
- }
426
- else {
427
- return bindPos;
428
- }
429
- };
430
- const bone = bones[boneIdx];
431
- // VMD translation is relative to bind pose world position
432
- // targetWorldPos = bindPoseWorldPos + vmdRelativeTranslation
433
- const bindPoseWorldPos = computeBindPoseWorldPosition(boneIdx);
434
- const targetWorldPos = bindPoseWorldPos.add(vmdRelativeTranslation);
435
- // Convert target world position to local translation
436
- // We need parent's bind pose world position to transform to parent space
437
- let parentBindPoseWorldPos;
438
- if (bone.parentIndex >= 0) {
439
- parentBindPoseWorldPos = computeBindPoseWorldPosition(bone.parentIndex);
486
+ const bone = this.skeleton.bones[boneIdx];
487
+ const bindWorld = this.bindWorldPos;
488
+ const bt = bone.bindTranslation;
489
+ const p = bone.parentIndex;
490
+ // afterBindTranslation = (bindWorld[bone] + vmd − bindWorld[parent]) bindTranslation.
491
+ // (Algebraically this reduces to vmd, since bindWorld[bone] = bindWorld[parent] +
492
+ // bindTranslation, but the explicit form keeps the result bit-identical to before.)
493
+ const bi3 = boneIdx * 3;
494
+ const targetX = bindWorld[bi3 + 0] + vmdRelativeTranslation.x;
495
+ const targetY = bindWorld[bi3 + 1] + vmdRelativeTranslation.y;
496
+ const targetZ = bindWorld[bi3 + 2] + vmdRelativeTranslation.z;
497
+ const px = p >= 0 ? bindWorld[p * 3 + 0] : 0;
498
+ const py = p >= 0 ? bindWorld[p * 3 + 1] : 0;
499
+ const pz = p >= 0 ? bindWorld[p * 3 + 2] : 0;
500
+ const abx = targetX - px - bt[0];
501
+ const aby = targetY - py - bt[1];
502
+ const abz = targetZ - pz - bt[2];
503
+ // Inverse rotation = conjugate + normalize (matches localRotation.clone().conjugate()
504
+ // .normalize()), applied via a rotation matrix (matches Mat4.fromQuat). Uses animation
505
+ // rotation when provided so IK-modified localRot doesn't perturb the conversion.
506
+ const q = rotation ?? this.runtimeSkeleton.localRotations[boneIdx];
507
+ const qlen = Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
508
+ let ix, iy, iz, iw;
509
+ if (qlen === 0) {
510
+ ix = 0;
511
+ iy = 0;
512
+ iz = 0;
513
+ iw = 1;
440
514
  }
441
515
  else {
442
- parentBindPoseWorldPos = Vec3.zeros();
516
+ const inv = 1 / qlen;
517
+ ix = -q.x * inv;
518
+ iy = -q.y * inv;
519
+ iz = -q.z * inv;
520
+ iw = q.w * inv;
443
521
  }
444
- // Transform target world position to parent's local space
445
- // In bind pose, parent's world matrix is just a translation
446
- const parentSpacePos = targetWorldPos.subtract(parentBindPoseWorldPos);
447
- // Subtract bindTranslation to get position after bind translation
448
- const afterBindTranslation = parentSpacePos.subtract(new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2]));
449
- // Apply inverse rotation to get local translation
450
- // Use provided rotation (animation rotation) or fall back to current localRotation
451
- // Using animation rotation prevents conflicts when IK modifies the rotation
452
- const localRotation = rotation ?? localRot[boneIdx];
453
- // Clone to avoid mutating, then conjugate and normalize
454
- const invRotation = localRotation.clone().conjugate().normalize();
455
- const rotationMat = Mat4.fromQuat(invRotation.x, invRotation.y, invRotation.z, invRotation.w);
456
- const rm = rotationMat.values;
457
- const localTranslation = new Vec3(rm[0] * afterBindTranslation.x + rm[4] * afterBindTranslation.y + rm[8] * afterBindTranslation.z, rm[1] * afterBindTranslation.x + rm[5] * afterBindTranslation.y + rm[9] * afterBindTranslation.z, rm[2] * afterBindTranslation.x + rm[6] * afterBindTranslation.y + rm[10] * afterBindTranslation.z);
458
- return localTranslation;
522
+ Mat4.fromQuatInto(ix, iy, iz, iw, _convMat, 0);
523
+ const rm = _convMat;
524
+ _convOut.setXYZ(rm[0] * abx + rm[4] * aby + rm[8] * abz, rm[1] * abx + rm[5] * aby + rm[9] * abz, rm[2] * abx + rm[6] * aby + rm[10] * abz);
525
+ return _convOut;
459
526
  }
460
527
  getWorldMatrices() {
461
528
  return this.runtimeSkeleton.worldMatrices;
@@ -552,13 +619,15 @@ export class Model {
552
619
  this.applyMorphs();
553
620
  }
554
621
  applyMorphs() {
555
- // Reset vertex data to base positions
556
- this.vertexData.set(this.baseVertexData);
557
622
  const vertexCount = this.vertexCount;
558
623
  const morphCount = this.morphing.morphs.length;
559
624
  const weights = this.runtimeMorph.weights;
560
- // First pass: Compute effective weights for all morphs (handling group morphs)
561
- const effectiveWeights = new Float32Array(morphCount);
625
+ // Effective weights (group-morph resolution + clamp). Both paths need these: the GPU
626
+ // path uploads them for the compute pass; the CPU path applies them below. Reused buffer.
627
+ if (!this.morphEffectiveWeights || this.morphEffectiveWeights.length !== morphCount) {
628
+ this.morphEffectiveWeights = new Float32Array(morphCount);
629
+ }
630
+ const effectiveWeights = this.morphEffectiveWeights;
562
631
  effectiveWeights.set(weights); // Start with direct weights
563
632
  // Apply group morphs: group morph weight * ratio affects referenced morphs
564
633
  for (let morphIdx = 0; morphIdx < morphCount; morphIdx++) {
@@ -568,18 +637,30 @@ export class Model {
568
637
  if (groupWeight > 0.0001) {
569
638
  for (const ref of morph.groupReferences) {
570
639
  if (ref.morphIndex >= 0 && ref.morphIndex < morphCount) {
571
- // Add group morph's contribution to the referenced morph
572
640
  effectiveWeights[ref.morphIndex] += groupWeight * ref.ratio;
573
641
  }
574
642
  }
575
643
  }
576
644
  }
577
645
  }
578
- // Clamp effective weights to [0, 1]
579
646
  for (let i = 0; i < morphCount; i++) {
580
647
  effectiveWeights[i] = Math.max(0, Math.min(1, effectiveWeights[i]));
581
648
  }
582
- // Second pass: Apply vertex morphs with their effective weights
649
+ // GPU path: the compute pass applies the vertex offsets from these weights.
650
+ if (this.gpuMorphEnabled) {
651
+ this.morphWeightsDirty = true;
652
+ return;
653
+ }
654
+ // ── CPU path ── Reset only the vertices morphed by the previous pass back to base
655
+ // (targeted reset; vertexData never diverges from base outside that range).
656
+ if (this.morphPrevMaxVert >= 0) {
657
+ const s = this.morphPrevMinVert * VERTEX_STRIDE;
658
+ const e = (this.morphPrevMaxVert + 1) * VERTEX_STRIDE;
659
+ this.vertexData.set(this.baseVertexData.subarray(s, e), s);
660
+ }
661
+ // Apply vertex morphs, tracking the touched vertex-index range for partial upload.
662
+ let curMinVert = -1;
663
+ let curMaxVert = -1;
583
664
  for (let morphIdx = 0; morphIdx < morphCount; morphIdx++) {
584
665
  const effectiveWeight = effectiveWeights[morphIdx];
585
666
  if (effectiveWeight === 0 || effectiveWeight < 0.0001)
@@ -587,26 +668,136 @@ export class Model {
587
668
  const morph = this.morphing.morphs[morphIdx];
588
669
  if (morph.type !== 1)
589
670
  continue; // Only process vertex morphs
590
- // For vertex morphs, iterate through vertices that have offsets
591
671
  for (const vertexOffset of morph.vertexOffsets) {
592
672
  const vIdx = vertexOffset.vertexIndex;
593
673
  if (vIdx < 0 || vIdx >= vertexCount)
594
674
  continue;
595
- // Get morph offset for this vertex
596
675
  const offsetX = vertexOffset.positionOffset[0];
597
676
  const offsetY = vertexOffset.positionOffset[1];
598
677
  const offsetZ = vertexOffset.positionOffset[2];
599
- // Skip if offset is zero
600
678
  if (Math.abs(offsetX) < 0.0001 && Math.abs(offsetY) < 0.0001 && Math.abs(offsetZ) < 0.0001) {
601
679
  continue;
602
680
  }
603
- // Apply weighted offset to vertex position (positions are at stride 0, 8, 16, ...)
604
681
  const vertexIdx = vIdx * VERTEX_STRIDE;
605
682
  this.vertexData[vertexIdx] += offsetX * effectiveWeight;
606
683
  this.vertexData[vertexIdx + 1] += offsetY * effectiveWeight;
607
684
  this.vertexData[vertexIdx + 2] += offsetZ * effectiveWeight;
685
+ if (curMinVert < 0 || vIdx < curMinVert)
686
+ curMinVert = vIdx;
687
+ if (vIdx > curMaxVert)
688
+ curMaxVert = vIdx;
689
+ }
690
+ }
691
+ let dirtyMin = curMinVert;
692
+ let dirtyMax = curMaxVert;
693
+ if (this.morphPrevMaxVert >= 0) {
694
+ if (dirtyMin < 0 || this.morphPrevMinVert < dirtyMin)
695
+ dirtyMin = this.morphPrevMinVert;
696
+ if (this.morphPrevMaxVert > dirtyMax)
697
+ dirtyMax = this.morphPrevMaxVert;
698
+ }
699
+ if (dirtyMin >= 0 && dirtyMax >= 0) {
700
+ this.morphPendingMinVert =
701
+ this.morphPendingMinVert < 0 ? dirtyMin : Math.min(this.morphPendingMinVert, dirtyMin);
702
+ this.morphPendingMaxVert =
703
+ this.morphPendingMaxVert < 0 ? dirtyMax : Math.max(this.morphPendingMaxVert, dirtyMax);
704
+ }
705
+ this.morphPrevMinVert = curMinVert;
706
+ this.morphPrevMaxVert = curMaxVert;
707
+ }
708
+ // ── GPU morph path support ──
709
+ // Called by the engine once it has created the compute buffers for this model; switches
710
+ // applyMorphs to the weights-only branch.
711
+ enableGpuMorphs() {
712
+ this.gpuMorphEnabled = true;
713
+ }
714
+ // True (once) when morph weights changed since the last check — the engine then uploads
715
+ // getEffectiveMorphWeights() and dispatches the compute pass.
716
+ consumeMorphWeightsDirty() {
717
+ const d = this.morphWeightsDirty;
718
+ this.morphWeightsDirty = false;
719
+ return d;
720
+ }
721
+ // Effective (group-resolved, clamped) morph weights for GPU upload. Ensures they're
722
+ // computed at least once even before the first weight change.
723
+ getEffectiveMorphWeights() {
724
+ if (!this.morphEffectiveWeights)
725
+ this.applyMorphs();
726
+ return this.morphEffectiveWeights ?? new Float32Array(0);
727
+ }
728
+ // Build the CSR inversion of vertex-morph offsets for the GPU compute pass. Returns null
729
+ // when the model has no vertex-morph offsets (no GPU path needed). Entries for each vertex
730
+ // are emitted in ascending morph-index order, matching the CPU accumulation order.
731
+ buildMorphComputeData() {
732
+ const V = this.vertexCount;
733
+ const M = this.morphing.morphs.length;
734
+ const morphs = this.morphing.morphs;
735
+ const EPS = 0.0001;
736
+ const isLive = (o) => o.vertexIndex >= 0 &&
737
+ o.vertexIndex < V &&
738
+ (Math.abs(o.positionOffset[0]) >= EPS ||
739
+ Math.abs(o.positionOffset[1]) >= EPS ||
740
+ Math.abs(o.positionOffset[2]) >= EPS);
741
+ const counts = new Uint32Array(V);
742
+ for (let m = 0; m < M; m++) {
743
+ const morph = morphs[m];
744
+ if (morph.type !== 1)
745
+ continue;
746
+ for (const o of morph.vertexOffsets)
747
+ if (isLive(o))
748
+ counts[o.vertexIndex]++;
749
+ }
750
+ const rowStart = new Uint32Array(V + 1);
751
+ let acc = 0;
752
+ for (let v = 0; v < V; v++) {
753
+ rowStart[v] = acc;
754
+ acc += counts[v];
755
+ }
756
+ rowStart[V] = acc;
757
+ const E = acc;
758
+ if (E === 0)
759
+ return null;
760
+ const colMorph = new Uint32Array(E);
761
+ const colOffset = new Float32Array(E * 3);
762
+ const fill = new Uint32Array(V);
763
+ for (let m = 0; m < M; m++) {
764
+ const morph = morphs[m];
765
+ if (morph.type !== 1)
766
+ continue;
767
+ for (const o of morph.vertexOffsets) {
768
+ if (!isLive(o))
769
+ continue;
770
+ const v = o.vertexIndex;
771
+ const p = rowStart[v] + fill[v]++;
772
+ colMorph[p] = m;
773
+ colOffset[p * 3] = o.positionOffset[0];
774
+ colOffset[p * 3 + 1] = o.positionOffset[1];
775
+ colOffset[p * 3 + 2] = o.positionOffset[2];
608
776
  }
609
777
  }
778
+ const basePositions = new Float32Array(V * 3);
779
+ for (let v = 0; v < V; v++) {
780
+ const vi = v * VERTEX_STRIDE;
781
+ basePositions[v * 3] = this.baseVertexData[vi];
782
+ basePositions[v * 3 + 1] = this.baseVertexData[vi + 1];
783
+ basePositions[v * 3 + 2] = this.baseVertexData[vi + 2];
784
+ }
785
+ return { basePositions, rowStart, colMorph, colOffset, morphCount: M, vertexCount: V, entryCount: E };
786
+ }
787
+ // Consume the pending morph vertex-upload range for the engine. Returns null when a
788
+ // full upload is needed (first time after load, or nothing tracked), else the inclusive
789
+ // [minVert, maxVert] slice that changed. Resets pending state.
790
+ consumeVertexUploadRange() {
791
+ if (this.morphUploadFull || this.morphPendingMaxVert < 0) {
792
+ this.morphUploadFull = false;
793
+ this.morphPendingMinVert = -1;
794
+ this.morphPendingMaxVert = -1;
795
+ return null;
796
+ }
797
+ const range = { minVert: this.morphPendingMinVert, maxVert: this.morphPendingMaxVert };
798
+ this.morphPendingMinVert = -1;
799
+ this.morphPendingMaxVert = -1;
800
+ return range;
610
801
  }
611
802
  buildClipFromVmdKeyFrames(vmdKeyFrames) {
612
803
  const boneTracksByBone = {};
@@ -835,11 +1026,11 @@ export class Model {
835
1026
  const gradient = frameDelta > 0 ? (clampedFrame - frameA.frame) / frameDelta : 0;
836
1027
  const interp = frameB.interpolation;
837
1028
  const rotT = interpolateControlPoints(interp.rotation, gradient);
838
- const rotation = Quat.slerp(frameA.rotation, frameB.rotation, rotT);
1029
+ const rotation = Quat.slerpInto(frameA.rotation, frameB.rotation, rotT, _animSlerp);
839
1030
  const txWeight = interpolateControlPoints(interp.translationX, gradient);
840
1031
  const tyWeight = interpolateControlPoints(interp.translationY, gradient);
841
1032
  const tzWeight = interpolateControlPoints(interp.translationZ, gradient);
842
- const interpolatedVMDTranslation = new Vec3(frameA.translation.x + (frameB.translation.x - frameA.translation.x) * txWeight, frameA.translation.y + (frameB.translation.y - frameA.translation.y) * tyWeight, frameA.translation.z + (frameB.translation.z - frameA.translation.z) * tzWeight);
1033
+ const interpolatedVMDTranslation = _animInterpT.setXYZ(frameA.translation.x + (frameB.translation.x - frameA.translation.x) * txWeight, frameA.translation.y + (frameB.translation.y - frameA.translation.y) * tyWeight, frameA.translation.z + (frameB.translation.z - frameA.translation.z) * tzWeight);
843
1034
  const localTranslation = this.convertVMDTranslationToLocal(boneIdx, interpolatedVMDTranslation, rotation);
844
1035
  localRot.set(rotation);
845
1036
  localTrans.set(localTranslation);
@@ -906,10 +1097,14 @@ export class Model {
906
1097
  if (!ikChainInfo)
907
1098
  return;
908
1099
  // Solve each IK solver sequentially, ensuring consistent state between solvers
1100
+ let firstSolver = true;
909
1101
  for (const solver of ikSolvers) {
910
- // Recompute ALL world matrices before each solver starts
911
- // This ensures each solver sees the effects of previous solvers on localRotations
912
- this.computeWorldMatrices();
1102
+ // Each solver must see the effects of previous solvers on localRotations, so
1103
+ // recompute world matrices between solvers. The first solver is skipped: the
1104
+ // caller (update) already computed them and nothing has changed localRotations yet.
1105
+ if (!firstSolver)
1106
+ this.computeWorldMatrices();
1107
+ firstSolver = false;
913
1108
  // Clear computed set for this solver's pass
914
1109
  this.ikComputedSet.clear();
915
1110
  // Solve this IK chain
@@ -1033,20 +1228,13 @@ export class Model {
1033
1228
  const boneCount = bones.length;
1034
1229
  if (boneCount === 0)
1035
1230
  return;
1036
- // Local computed array (avoids instance field overhead)
1037
- const computed = new Array(boneCount).fill(false);
1038
- const computeWorld = (i) => {
1039
- if (computed[i])
1040
- return;
1231
+ // Flat traversal in precomputed order: every bone's parent is already done, so no
1232
+ // per-bone visited check, no recursion, and no per-call allocation. Same per-bone
1233
+ // math as before. Scratch slots are safe to reuse since there's no reentrancy now.
1234
+ const order = this.deformOrder;
1235
+ for (let k = 0; k < boneCount; k++) {
1236
+ const i = order[k];
1041
1237
  const b = bones[i];
1042
- if (b.parentIndex >= boneCount) {
1043
- console.warn(`[RZM] bone ${i} parent out of range: ${b.parentIndex}`);
1044
- }
1045
- // Ensure parent is computed FIRST, before we touch any scratch buffers.
1046
- // Recursion may itself use scratchMat4Values[0] / scratchQuat; doing it up
1047
- // front keeps the current frame's scratch slots untouched when we use them below.
1048
- if (b.parentIndex >= 0 && !computed[b.parentIndex])
1049
- computeWorld(b.parentIndex);
1050
1238
  const boneRot = localRot[i];
1051
1239
  let fx = boneRot.x, fy = boneRot.y, fz = boneRot.z, fw = boneRot.w;
1052
1240
  let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
@@ -1103,10 +1291,6 @@ export class Model {
1103
1291
  else {
1104
1292
  worldMat.values.set(localMVals);
1105
1293
  }
1106
- computed[i] = true;
1107
- };
1108
- // Process all bones (recursion handles dependencies automatically)
1109
- for (let i = 0; i < boneCount; i++)
1110
- computeWorld(i);
1294
+ }
1111
1295
  }
1112
1296
  }
@@ -22,9 +22,11 @@ export declare class RigidBodyStore {
22
22
  readonly bodyOffsetMatrix: Float32Array;
23
23
  readonly bodyOffsetInverse: Float32Array;
24
24
  private boneOffsetsReady;
25
+ private collisionPairs;
25
26
  constructor(rigidbodies: Rigidbody[]);
26
27
  updateAabbs(margin?: number): void;
27
28
  computeBoneOffsets(boneInverseBindMatrices: Float32Array): void;
28
29
  isBoneOffsetsReady(): boolean;
30
+ getCollisionPairs(): Uint16Array;
29
31
  }
30
32
  //# sourceMappingURL=body.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../../src/physics/body.ts"],"names":[],"mappings":"AACA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AAIvE,qBAAa,cAAc;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IAEtB,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAA;IAChC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAA;IACnC,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAA;IACvC,QAAQ,CAAC,iBAAiB,EAAE,YAAY,CAAA;IAExC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;IAI9B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAA;IACjC,QAAQ,CAAC,aAAa,EAAE,YAAY,CAAA;IACpC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAA;IAIlC,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAA;IACpC,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAA;IAErC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAE3B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;IAC9B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;IAI9B,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAA;IACvC,QAAQ,CAAC,iBAAiB,EAAE,YAAY,CAAA;IACxC,OAAO,CAAC,gBAAgB,CAAQ;gBAEpB,WAAW,EAAE,SAAS,EAAE;IA4DpC,WAAW,CAAC,MAAM,SAAM,GAAG,IAAI;IA2F/B,kBAAkB,CAAC,uBAAuB,EAAE,YAAY,GAAG,IAAI;IAqD/D,kBAAkB,IAAI,OAAO;CAG9B"}
1
+ {"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../../src/physics/body.ts"],"names":[],"mappings":"AACA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AAIvE,qBAAa,cAAc;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IAEtB,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAA;IAChC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAA;IACnC,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAA;IACvC,QAAQ,CAAC,iBAAiB,EAAE,YAAY,CAAA;IAExC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;IAI9B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAA;IACjC,QAAQ,CAAC,aAAa,EAAE,YAAY,CAAA;IACpC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAA;IAIlC,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAA;IACpC,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAA;IAErC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAE3B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;IAC9B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;IAI9B,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAA;IACvC,QAAQ,CAAC,iBAAiB,EAAE,YAAY,CAAA;IACxC,OAAO,CAAC,gBAAgB,CAAQ;IAMhC,OAAO,CAAC,cAAc,CAA2B;gBAErC,WAAW,EAAE,SAAS,EAAE;IA4DpC,WAAW,CAAC,MAAM,SAAM,GAAG,IAAI;IA2F/B,kBAAkB,CAAC,uBAAuB,EAAE,YAAY,GAAG,IAAI;IAqD/D,kBAAkB,IAAI,OAAO;IAM7B,iBAAiB,IAAI,WAAW;CAoBjC"}
@@ -5,6 +5,11 @@ import { RigidbodyType, RigidbodyShape } from "./types";
5
5
  export class RigidBodyStore {
6
6
  constructor(rigidbodies) {
7
7
  this.boneOffsetsReady = false;
8
+ // Flat list of (i, j) pairs that survive the static-static + group/mask
9
+ // filter. None of those inputs change after construction, so building this
10
+ // once collapses 60k pair tests/step (349 bodies) down to a few thousand.
11
+ // Built lazily on first access.
12
+ this.collisionPairs = null;
8
13
  const N = rigidbodies.length;
9
14
  this.count = N;
10
15
  this.positions = new Float32Array(N * 3);
@@ -159,6 +164,31 @@ export class RigidBodyStore {
159
164
  isBoneOffsetsReady() {
160
165
  return this.boneOffsetsReady;
161
166
  }
167
+ // Pair-filter inputs (invMass, group, mask) are immutable post-construction,
168
+ // so build the candidate-pair list once and reuse every step.
169
+ getCollisionPairs() {
170
+ if (this.collisionPairs !== null)
171
+ return this.collisionPairs;
172
+ const N = this.count;
173
+ const invMass = this.invMass;
174
+ const group = this.collisionGroup;
175
+ const mask = this.willCollideMask;
176
+ const buf = [];
177
+ for (let i = 0; i < N; i++) {
178
+ const gi = group[i];
179
+ const mi = mask[i];
180
+ const dynA = invMass[i] > 0;
181
+ for (let j = i + 1; j < N; j++) {
182
+ if (!dynA && invMass[j] === 0)
183
+ continue;
184
+ if ((mi & group[j]) === 0 || (mask[j] & gi) === 0)
185
+ continue;
186
+ buf.push(i, j);
187
+ }
188
+ }
189
+ this.collisionPairs = new Uint16Array(buf);
190
+ return this.collisionPairs;
191
+ }
162
192
  }
163
193
  const _scratchA = new Float32Array(16);
164
194
  const _scratchB = new Float32Array(16);
@@ -11,6 +11,19 @@ export interface SixDofSpringConstraint {
11
11
  springEnabled: Uint8Array;
12
12
  springStiffness: Float32Array;
13
13
  equilibriumPoint: Float32Array;
14
+ cacheSkip: boolean;
15
+ cacheLeverA: Float32Array;
16
+ cacheLeverB: Float32Array;
17
+ cacheLinAxes: Float32Array;
18
+ cacheLinCrossA: Float32Array;
19
+ cacheLinCrossB: Float32Array;
20
+ cacheLinJacInv: Float32Array;
21
+ cacheLinTargetVel: Float32Array;
22
+ cacheLinActive: Uint8Array;
23
+ cacheAngAxes: Float32Array;
24
+ cacheAngTargetVel: Float32Array;
25
+ cacheAngActive: Uint8Array;
26
+ cacheAngJacInv: number;
14
27
  }
15
28
  export declare const STOP_ERP = 0.475;
16
29
  export declare function buildConstraints(rigidbodies: Rigidbody[], joints: Joint[]): SixDofSpringConstraint[];
@@ -1 +1 @@
1
- {"version":3,"file":"constraint.d.ts","sourceRoot":"","sources":["../../src/physics/constraint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAW/C,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IAEb,MAAM,EAAE,YAAY,CAAA;IACpB,MAAM,EAAE,YAAY,CAAA;IAGpB,SAAS,EAAE,YAAY,CAAA;IACvB,SAAS,EAAE,YAAY,CAAA;IACvB,UAAU,EAAE,YAAY,CAAA;IACxB,UAAU,EAAE,YAAY,CAAA;IAExB,aAAa,EAAE,UAAU,CAAA;IACzB,eAAe,EAAE,YAAY,CAAA;IAC7B,gBAAgB,EAAE,YAAY,CAAA;CAC/B;AAED,eAAO,MAAM,QAAQ,QAAQ,CAAA;AAM7B,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,SAAS,EAAE,EACxB,MAAM,EAAE,KAAK,EAAE,GACd,sBAAsB,EAAE,CAqE1B"}
1
+ {"version":3,"file":"constraint.d.ts","sourceRoot":"","sources":["../../src/physics/constraint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAW/C,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IAEb,MAAM,EAAE,YAAY,CAAA;IACpB,MAAM,EAAE,YAAY,CAAA;IAGpB,SAAS,EAAE,YAAY,CAAA;IACvB,SAAS,EAAE,YAAY,CAAA;IACvB,UAAU,EAAE,YAAY,CAAA;IACxB,UAAU,EAAE,YAAY,CAAA;IAExB,aAAa,EAAE,UAAU,CAAA;IACzB,eAAe,EAAE,YAAY,CAAA;IAC7B,gBAAgB,EAAE,YAAY,CAAA;IAK9B,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,YAAY,CAAA;IACzB,WAAW,EAAE,YAAY,CAAA;IACzB,YAAY,EAAE,YAAY,CAAA;IAC1B,cAAc,EAAE,YAAY,CAAA;IAC5B,cAAc,EAAE,YAAY,CAAA;IAC5B,cAAc,EAAE,YAAY,CAAA;IAC5B,iBAAiB,EAAE,YAAY,CAAA;IAC/B,cAAc,EAAE,UAAU,CAAA;IAC1B,YAAY,EAAE,YAAY,CAAA;IAC1B,iBAAiB,EAAE,YAAY,CAAA;IAC/B,cAAc,EAAE,UAAU,CAAA;IAC1B,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,QAAQ,QAAQ,CAAA;AAM7B,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,SAAS,EAAE,EACxB,MAAM,EAAE,KAAK,EAAE,GACd,sBAAsB,EAAE,CAkF1B"}
@@ -64,6 +64,19 @@ export function buildConstraints(rigidbodies, joints) {
64
64
  springEnabled,
65
65
  springStiffness,
66
66
  equilibriumPoint: new Float32Array(6),
67
+ cacheSkip: false,
68
+ cacheLeverA: new Float32Array(3),
69
+ cacheLeverB: new Float32Array(3),
70
+ cacheLinAxes: new Float32Array(9),
71
+ cacheLinCrossA: new Float32Array(9),
72
+ cacheLinCrossB: new Float32Array(9),
73
+ cacheLinJacInv: new Float32Array(3),
74
+ cacheLinTargetVel: new Float32Array(3),
75
+ cacheLinActive: new Uint8Array(3),
76
+ cacheAngAxes: new Float32Array(9),
77
+ cacheAngTargetVel: new Float32Array(3),
78
+ cacheAngActive: new Uint8Array(3),
79
+ cacheAngJacInv: 0,
67
80
  });
68
81
  }
69
82
  return out;
@@ -18,6 +18,34 @@ export interface Contact {
18
18
  appliedNormalImpulse: number;
19
19
  appliedFrictionImpulse1: number;
20
20
  appliedFrictionImpulse2: number;
21
+ cAxN: number;
22
+ cAyN: number;
23
+ cAzN: number;
24
+ cBxN: number;
25
+ cByN: number;
26
+ cBzN: number;
27
+ jacInvN: number;
28
+ bounceVel: number;
29
+ t1x: number;
30
+ t1y: number;
31
+ t1z: number;
32
+ cAxT1: number;
33
+ cAyT1: number;
34
+ cAzT1: number;
35
+ cBxT1: number;
36
+ cByT1: number;
37
+ cBzT1: number;
38
+ jacInvT1: number;
39
+ t2x: number;
40
+ t2y: number;
41
+ t2z: number;
42
+ cAxT2: number;
43
+ cAyT2: number;
44
+ cAzT2: number;
45
+ cBxT2: number;
46
+ cByT2: number;
47
+ cBzT2: number;
48
+ jacInvT2: number;
21
49
  }
22
50
  export declare class ContactPool {
23
51
  private pool;
@@ -1 +1 @@
1
- {"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../../src/physics/contact.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAO5C,eAAO,MAAM,cAAc,OAAO,CAAA;AAElC,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IAEb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IAEX,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IAEnB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uBAAuB,EAAE,MAAM,CAAA;CAChC;AAyBD,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAgB;IAC5B,KAAK,SAAI;IAET,OAAO,IAAI,OAAO;IAelB,KAAK,IAAI,IAAI;IAGb,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;CAGxB;AASD,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAahF;AAkxBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAuCrG;AA4BD,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAkB3E"}
1
+ {"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../../src/physics/contact.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAO5C,eAAO,MAAM,cAAc,OAAO,CAAA;AAElC,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IAEb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IAEX,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IAEnB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uBAAuB,EAAE,MAAM,CAAA;IAI/B,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;IACxC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;IACxC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IAEjB,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;IACrC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAEhB,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;IACrC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;CACjB;AA8BD,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAgB;IAC5B,KAAK,SAAI;IAET,OAAO,IAAI,OAAO;IAelB,KAAK,IAAI,IAAI;IAGb,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;CAGxB;AASD,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAahF;AAkxBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAuCrG;AA4BD,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAS3E"}