@woosh/meep-engine 3.40.0 → 3.41.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 (203) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/quaternion/swing_twist_clamp.d.ts +24 -0
  3. package/src/core/geom/3d/quaternion/swing_twist_clamp.d.ts.map +1 -0
  4. package/src/core/geom/3d/quaternion/swing_twist_clamp.js +62 -0
  5. package/src/core/geom/3d/quaternion/swing_twist_compose.d.ts +15 -0
  6. package/src/core/geom/3d/quaternion/swing_twist_compose.d.ts.map +1 -0
  7. package/src/core/geom/3d/quaternion/swing_twist_compose.js +36 -0
  8. package/src/core/geom/3d/quaternion/swing_twist_error.d.ts +39 -0
  9. package/src/core/geom/3d/quaternion/swing_twist_error.d.ts.map +1 -0
  10. package/src/core/geom/3d/quaternion/swing_twist_error.js +85 -0
  11. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts +11 -6
  12. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts.map +1 -1
  13. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.js +27 -13
  14. package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.d.ts +25 -0
  15. package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.d.ts.map +1 -0
  16. package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.js +166 -0
  17. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts +5 -3
  18. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts.map +1 -1
  19. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.js +5 -3
  20. package/src/engine/animation/curve/AnimationCurve.d.ts +3 -0
  21. package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
  22. package/src/engine/animation/curve/AnimationCurve.js +6 -1
  23. package/src/engine/ecs/EntityManager.d.ts +9 -1
  24. package/src/engine/ecs/EntityManager.d.ts.map +1 -1
  25. package/src/engine/ecs/EntityManager.js +26 -27
  26. package/src/engine/ecs/gpu/GPUStateAuthority.d.ts +20 -0
  27. package/src/engine/ecs/gpu/GPUStateAuthority.d.ts.map +1 -1
  28. package/src/engine/ecs/gpu/GPUStateAuthority.js +21 -0
  29. package/src/engine/ecs/gpu/GPU_AUTHORITY_EVENT_HOLD_CHANGE.d.ts +13 -0
  30. package/src/engine/ecs/gpu/GPU_AUTHORITY_EVENT_HOLD_CHANGE.d.ts.map +1 -0
  31. package/src/engine/ecs/gpu/GPU_AUTHORITY_EVENT_HOLD_CHANGE.js +12 -0
  32. package/src/engine/ecs/gpu/gpu_authority_grant.d.ts +6 -0
  33. package/src/engine/ecs/gpu/gpu_authority_grant.d.ts.map +1 -1
  34. package/src/engine/ecs/gpu/gpu_authority_grant.js +7 -1
  35. package/src/engine/ecs/gpu/gpu_authority_held.d.ts +14 -0
  36. package/src/engine/ecs/gpu/gpu_authority_held.d.ts.map +1 -0
  37. package/src/engine/ecs/gpu/gpu_authority_held.js +23 -0
  38. package/src/engine/ecs/gpu/gpu_authority_hold.d.ts +23 -0
  39. package/src/engine/ecs/gpu/gpu_authority_hold.d.ts.map +1 -0
  40. package/src/engine/ecs/gpu/gpu_authority_hold.js +41 -0
  41. package/src/engine/ecs/gpu/gpu_authority_unhold.d.ts +14 -0
  42. package/src/engine/ecs/gpu/gpu_authority_unhold.d.ts.map +1 -0
  43. package/src/engine/ecs/gpu/gpu_authority_unhold.js +32 -0
  44. package/src/engine/ecs/ik/IKGoal.d.ts +59 -0
  45. package/src/engine/ecs/ik/IKGoal.d.ts.map +1 -0
  46. package/src/engine/ecs/ik/IKGoal.js +83 -0
  47. package/src/engine/ecs/ik/IKGoalSystem.d.ts +75 -0
  48. package/src/engine/ecs/ik/IKGoalSystem.d.ts.map +1 -0
  49. package/src/engine/ecs/ik/IKGoalSystem.js +1456 -0
  50. package/src/engine/ecs/ik/PLAN.md +179 -0
  51. package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
  52. package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
  53. package/src/engine/ecs/storage/BinaryBufferSerializer.d.ts.map +1 -1
  54. package/src/engine/ecs/storage/BinaryBufferSerializer.js +4 -5
  55. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +7 -3
  56. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  57. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +1 -2
  58. package/src/engine/ecs/system/orderSystemsByDeclaredAccess.d.ts +33 -0
  59. package/src/engine/ecs/system/orderSystemsByDeclaredAccess.d.ts.map +1 -0
  60. package/src/engine/ecs/system/orderSystemsByDeclaredAccess.js +282 -0
  61. package/src/engine/ecs/transform/T64WorldCache.js +139 -139
  62. package/src/engine/ecs/transform/t64_evaluate_local.d.ts +25 -0
  63. package/src/engine/ecs/transform/t64_evaluate_local.d.ts.map +1 -0
  64. package/src/engine/ecs/transform/t64_evaluate_local.js +35 -0
  65. package/src/engine/ecs/transform/t64_evaluate_world.d.ts +5 -3
  66. package/src/engine/ecs/transform/t64_evaluate_world.d.ts.map +1 -1
  67. package/src/engine/ecs/transform/t64_evaluate_world.js +128 -126
  68. package/src/engine/graphics3/AnimationGraphSystem.d.ts +2 -0
  69. package/src/engine/graphics3/AnimationGraphSystem.d.ts.map +1 -1
  70. package/src/engine/graphics3/AnimationGraphSystem.js +337 -329
  71. package/src/engine/graphics3/AnimationSystem.d.ts +2 -0
  72. package/src/engine/graphics3/AnimationSystem.d.ts.map +1 -1
  73. package/src/engine/graphics3/AnimationSystem.js +360 -352
  74. package/src/engine/physics/cloth/MEASUREMENTS.md +130 -0
  75. package/src/engine/physics/cloth/PLAN.md +16 -1
  76. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts +5 -6
  77. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts.map +1 -1
  78. package/src/engine/physics/cloth/ecs/ClothColliderSystem.js +5 -6
  79. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +4 -7
  80. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  81. package/src/engine/physics/cloth/ecs/ClothInstance.js +4 -7
  82. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +4 -3
  83. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  84. package/src/engine/physics/cloth/ecs/ClothSystem.js +4 -3
  85. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  86. package/src/engine/physics/cloth/ecs/ClothWorld.js +10 -0
  87. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +24 -24
  88. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
  89. package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +46 -43
  90. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts +1 -1
  91. package/src/engine/physics/cloth/ecs/cloth_write_back.js +1 -1
  92. package/src/engine/physics/constraint/solve_constraints.d.ts +0 -35
  93. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  94. package/src/engine/physics/constraint/solve_constraints.js +1 -83
  95. package/src/engine/physics/ecs/ContactProbe.d.ts +73 -0
  96. package/src/engine/physics/ecs/ContactProbe.d.ts.map +1 -0
  97. package/src/engine/physics/ecs/ContactProbe.js +103 -0
  98. package/src/engine/physics/ecs/ContactProbeSystem.d.ts +44 -0
  99. package/src/engine/physics/ecs/ContactProbeSystem.d.ts.map +1 -0
  100. package/src/engine/physics/ecs/ContactProbeSystem.js +397 -0
  101. package/src/engine/physics/fluid/ecs/FluidComponent.d.ts +1 -1
  102. package/src/engine/physics/fluid/ecs/FluidComponent.js +1 -1
  103. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  104. package/src/engine/physics/inverse_kinematics/frame_fabrik/FrameFabrikProblem.d.ts +205 -0
  105. package/src/engine/physics/inverse_kinematics/frame_fabrik/FrameFabrikProblem.d.ts.map +1 -0
  106. package/src/engine/physics/inverse_kinematics/frame_fabrik/FrameFabrikProblem.js +245 -0
  107. package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_limit_from_joint.d.ts +21 -0
  108. package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_limit_from_joint.d.ts.map +1 -0
  109. package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_limit_from_joint.js +63 -0
  110. package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.d.ts +33 -0
  111. package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.d.ts.map +1 -0
  112. package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.js +874 -0
  113. package/src/engine/physics/inverse_kinematics/joint_hinge_mid_axis.d.ts +27 -0
  114. package/src/engine/physics/inverse_kinematics/joint_hinge_mid_axis.d.ts.map +1 -0
  115. package/src/engine/physics/inverse_kinematics/joint_hinge_mid_axis.js +77 -0
  116. package/src/engine/physics/inverse_kinematics/two_bone_path_solve.d.ts +36 -0
  117. package/src/engine/physics/inverse_kinematics/two_bone_path_solve.d.ts.map +1 -0
  118. package/src/engine/physics/inverse_kinematics/two_bone_path_solve.js +97 -0
  119. package/src/engine/physics/inverse_kinematics/two_joint_ik.d.ts +33 -11
  120. package/src/engine/physics/inverse_kinematics/two_joint_ik.d.ts.map +1 -1
  121. package/src/engine/physics/inverse_kinematics/two_joint_ik.js +158 -67
  122. package/src/shade/renderer/animation/ANIMATION_TRACK_BINDING_STRUCT.d.ts.map +1 -1
  123. package/src/shade/renderer/animation/ANIMATION_TRACK_BINDING_STRUCT.js +10 -1
  124. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +10 -4
  125. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  126. package/src/shade/renderer/animation/GPUAnimationManager.js +251 -23
  127. package/src/shade/renderer/animation/POSE_ACCUMULATOR_LAYOUT.d.ts +12 -4
  128. package/src/shade/renderer/animation/POSE_ACCUMULATOR_LAYOUT.d.ts.map +1 -1
  129. package/src/shade/renderer/animation/POSE_ACCUMULATOR_LAYOUT.js +12 -4
  130. package/src/shade/renderer/animation/chunk_pose_accumulator.d.ts.map +1 -1
  131. package/src/shade/renderer/animation/chunk_pose_accumulator.js +14 -1
  132. package/src/shade/renderer/animation/pose/PoseChannelIndex.d.ts +78 -0
  133. package/src/shade/renderer/animation/pose/PoseChannelIndex.d.ts.map +1 -0
  134. package/src/shade/renderer/animation/pose/PoseChannelIndex.js +274 -0
  135. package/src/shade/renderer/animation/pose/PosePlayback.d.ts +40 -3
  136. package/src/shade/renderer/animation/pose/PosePlayback.d.ts.map +1 -1
  137. package/src/shade/renderer/animation/pose/PosePlayback.js +77 -3
  138. package/src/shade/renderer/animation/pose/pose_evaluate_local.d.ts +21 -3
  139. package/src/shade/renderer/animation/pose/pose_evaluate_local.d.ts.map +1 -1
  140. package/src/shade/renderer/animation/pose/pose_evaluate_local.js +79 -11
  141. package/src/shade/renderer/animation/shader_animation_apply.js +1 -1
  142. package/src/shade/renderer/animation/shader_animation_pose_resolve.d.ts +3 -2
  143. package/src/shade/renderer/animation/shader_animation_pose_resolve.d.ts.map +1 -1
  144. package/src/shade/renderer/animation/shader_animation_pose_resolve.js +187 -176
  145. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  146. package/src/shade/renderer/object_property/chunk_write_object_property.d.ts +3 -1
  147. package/src/shade/renderer/object_property/chunk_write_object_property.d.ts.map +1 -1
  148. package/src/shade/renderer/object_property/chunk_write_object_property.js +5 -2
  149. package/src/shade/renderer/object_property/chunk_write_object_property_node3d.d.ts +4 -0
  150. package/src/shade/renderer/object_property/chunk_write_object_property_node3d.d.ts.map +1 -1
  151. package/src/shade/renderer/object_property/chunk_write_object_property_node3d.js +6 -1
  152. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  153. package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.d.ts +1 -1
  154. package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.d.ts.map +1 -1
  155. package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.js +135 -73
  156. package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.d.ts +7 -0
  157. package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.d.ts.map +1 -1
  158. package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.js +29 -1
  159. package/src/shade/renderer/scene/optimization/geometry/internal/face_neighbourhood_hash.d.ts +19 -0
  160. package/src/shade/renderer/scene/optimization/geometry/internal/face_neighbourhood_hash.d.ts.map +1 -0
  161. package/src/shade/renderer/scene/optimization/geometry/internal/face_neighbourhood_hash.js +167 -0
  162. package/src/shade/renderer/scene/optimization/geometry/internal/neighbourhood_anchor_index.d.ts +25 -0
  163. package/src/shade/renderer/scene/optimization/geometry/internal/neighbourhood_anchor_index.d.ts.map +1 -0
  164. package/src/shade/renderer/scene/optimization/geometry/internal/neighbourhood_anchor_index.js +124 -0
  165. package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_face_pair.d.ts +19 -0
  166. package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_face_pair.d.ts.map +1 -0
  167. package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_face_pair.js +48 -0
  168. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts +17 -1
  169. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  170. package/src/shade/renderer/scene/rows/GPUSceneRows.js +29 -3
  171. package/src/engine/ecs/ik/IKConstraint.d.ts +0 -52
  172. package/src/engine/ecs/ik/IKConstraint.d.ts.map +0 -1
  173. package/src/engine/ecs/ik/IKConstraint.js +0 -95
  174. package/src/engine/ecs/ik/IKProblem.d.ts +0 -45
  175. package/src/engine/ecs/ik/IKProblem.d.ts.map +0 -1
  176. package/src/engine/ecs/ik/IKProblem.js +0 -58
  177. package/src/engine/ecs/ik/IKSolver.d.ts +0 -8
  178. package/src/engine/ecs/ik/IKSolver.d.ts.map +0 -1
  179. package/src/engine/ecs/ik/IKSolver.js +0 -9
  180. package/src/engine/ecs/ik/InverseKinematics.d.ts +0 -48
  181. package/src/engine/ecs/ik/InverseKinematics.d.ts.map +0 -1
  182. package/src/engine/ecs/ik/InverseKinematics.js +0 -119
  183. package/src/engine/ecs/ik/InverseKinematicsSerializationAdapter.d.ts +0 -19
  184. package/src/engine/ecs/ik/InverseKinematicsSerializationAdapter.d.ts.map +0 -1
  185. package/src/engine/ecs/ik/InverseKinematicsSerializationAdapter.js +0 -72
  186. package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts +0 -54
  187. package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts.map +0 -1
  188. package/src/engine/ecs/ik/InverseKinematicsSystem.js +0 -212
  189. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts +0 -8
  190. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +0 -1
  191. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +0 -153
  192. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.d.ts +0 -5
  193. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.d.ts.map +0 -1
  194. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +0 -120
  195. package/src/engine/ecs/ik/ik_bone_chain.d.ts +0 -64
  196. package/src/engine/ecs/ik/ik_bone_chain.d.ts.map +0 -1
  197. package/src/engine/ecs/ik/ik_bone_chain.js +0 -142
  198. package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts +0 -42
  199. package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts.map +0 -1
  200. package/src/engine/ecs/ik/ik_probe_terrain_contact.js +0 -123
  201. package/src/engine/ecs/ik/ik_write_local_rotation.d.ts +0 -18
  202. package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +0 -1
  203. package/src/engine/ecs/ik/ik_write_local_rotation.js +0 -32
@@ -0,0 +1,274 @@
1
+ import { UINT32_MAP_ABSENT, Uint32Map } from "../../../../core/collection/Uint32Map.js";
2
+
3
+ /**
4
+ * @typedef {import("../ShadeAnimationChannel.js").ShadeAnimationChannel} ShadeAnimationChannel
5
+ * @typedef {import("../ShadeAnimationClip.js").ShadeAnimationClip} ShadeAnimationClip
6
+ */
7
+
8
+ /**
9
+ * What {@link PoseChannelIndex#group_of} answers for an entity the clip has no track for.
10
+ *
11
+ * @type {number}
12
+ */
13
+ export const POSE_CHANNEL_GROUP_NONE = UINT32_MAP_ABSENT;
14
+
15
+ /**
16
+ * The largest entity {@link Uint32Map} can hold as a key; it stores a key as `key + 1`.
17
+ *
18
+ * @type {number}
19
+ */
20
+ const LARGEST_ENTITY = 0xFFFFFFFE;
21
+
22
+ /**
23
+ * Each clip's index, built the first time a playback takes the clip and shared by every playback
24
+ * that takes it after — a released joint's seed takes the same clip once per joint.
25
+ *
26
+ * @type {WeakMap<ShadeAnimationClip, PoseChannelIndex>}
27
+ */
28
+ const INDICES = new WeakMap();
29
+
30
+ /**
31
+ * Per-group cursors for {@link PoseChannelIndex#holds}, grown to the largest clip seen.
32
+ *
33
+ * @type {Uint32Array}
34
+ */
35
+ let CURSORS = new Uint32Array(64);
36
+
37
+ /**
38
+ * Whether a channel names an entity — the only thing the renderer binds a channel to, so a channel
39
+ * naming anything else plays on nothing there either.
40
+ *
41
+ * @param {*} target
42
+ * @returns {boolean}
43
+ */
44
+ function is_entity(target) {
45
+ return Number.isInteger(target) && target >= 0 && target <= LARGEST_ENTITY;
46
+ }
47
+
48
+ /**
49
+ * A clip's channels grouped by the entity each names, for the CPU pose twin to look one entity up
50
+ * in, rather than scan the whole clip for it once per level of every query.
51
+ *
52
+ * The groups are laid end to end in one array, each in the clip's own order, with a table of where
53
+ * each starts and a {@link Uint32Map} from entity to group: on a 70-joint clip of 210 channels it
54
+ * holds a few KiB, against the ~25 KiB of the clip itself, and nothing in it is handed out to be
55
+ * written. Read it with {@link group_of}, {@link first}, {@link end} and {@link channel}.
56
+ *
57
+ * Built once per clip ({@link of}) and checked, not rebuilt, each time a playback takes the clip
58
+ * again.
59
+ *
60
+ * @author Alex Goldring
61
+ * @copyright Company Named Limited (c) 2026
62
+ */
63
+ export class PoseChannelIndex {
64
+ /**
65
+ * Entity → group.
66
+ *
67
+ * @type {Uint32Map}
68
+ */
69
+ #groups;
70
+
71
+ /**
72
+ * Group `g` is `#channels[#starts[g] .. #starts[g + 1])`.
73
+ *
74
+ * @type {Uint32Array}
75
+ */
76
+ #starts;
77
+
78
+ /**
79
+ * The clip's channels that name an entity, grouped by it, each group in the clip's order.
80
+ *
81
+ * @type {ShadeAnimationChannel[]}
82
+ */
83
+ #channels;
84
+
85
+ /**
86
+ * How many channels the clip held when this was built, those naming no entity included.
87
+ *
88
+ * @type {number}
89
+ */
90
+ #source_count;
91
+
92
+ /**
93
+ * @param {ShadeAnimationChannel[]} channels a clip's channels
94
+ */
95
+ constructor(channels) {
96
+ const count = channels.length;
97
+ const groups = new Uint32Map(8);
98
+ const sizes = [];
99
+
100
+ let indexed = 0;
101
+
102
+ for (let i = 0; i < count; i++) {
103
+ const target = channels[i].target;
104
+
105
+ if (!is_entity(target)) {
106
+ continue;
107
+ }
108
+
109
+ let group = groups.get(target);
110
+
111
+ if (group === UINT32_MAP_ABSENT) {
112
+ group = sizes.length;
113
+
114
+ groups.set(target, group);
115
+ sizes.push(0);
116
+ }
117
+
118
+ sizes[group]++;
119
+ indexed++;
120
+ }
121
+
122
+ const group_count = sizes.length;
123
+ const starts = new Uint32Array(group_count + 1);
124
+
125
+ for (let g = 0; g < group_count; g++) {
126
+ starts[g + 1] = starts[g] + sizes[g];
127
+ }
128
+
129
+ // a counting sort: each channel's slot, walking the clip in order so each group keeps it
130
+ const slots = new Uint32Array(indexed);
131
+ const cursors = starts.slice(0, group_count);
132
+
133
+ for (let i = 0; i < count; i++) {
134
+ const target = channels[i].target;
135
+
136
+ if (is_entity(target)) {
137
+ slots[cursors[groups.get(target)]++] = i;
138
+ }
139
+ }
140
+
141
+ const grouped = [];
142
+
143
+ for (let s = 0; s < indexed; s++) {
144
+ grouped.push(channels[slots[s]]);
145
+ }
146
+
147
+ this.#groups = groups;
148
+ this.#starts = starts;
149
+ this.#channels = grouped;
150
+ this.#source_count = count;
151
+ }
152
+
153
+ /**
154
+ * The group of channels naming `entity`, or {@link POSE_CHANNEL_GROUP_NONE} where the clip has no
155
+ * track for it — which is most entities for most clips.
156
+ *
157
+ * @param {number} entity
158
+ * @returns {number}
159
+ */
160
+ group_of(entity) {
161
+ return this.#groups.get(entity);
162
+ }
163
+
164
+ /**
165
+ * @param {number} group
166
+ * @returns {number} the position of the group's first channel
167
+ */
168
+ first(group) {
169
+ return this.#starts[group];
170
+ }
171
+
172
+ /**
173
+ * @param {number} group
174
+ * @returns {number} one past the position of the group's last channel
175
+ */
176
+ end(group) {
177
+ return this.#starts[group + 1];
178
+ }
179
+
180
+ /**
181
+ * @param {number} position from {@link first} up to {@link end}
182
+ * @returns {ShadeAnimationChannel}
183
+ */
184
+ channel(position) {
185
+ return this.#channels[position];
186
+ }
187
+
188
+ /**
189
+ * Whether this still describes `channels`: every channel naming an entity is where this put it
190
+ * — the same channel, naming the same entity, in the same order among that entity's — and there
191
+ * are as many as there were. A walk of the clip without allocation.
192
+ *
193
+ * @param {ShadeAnimationChannel[]} channels
194
+ * @returns {boolean}
195
+ */
196
+ holds(channels) {
197
+ const count = channels.length;
198
+
199
+ if (count !== this.#source_count) {
200
+ return false;
201
+ }
202
+
203
+ const starts = this.#starts;
204
+ const indexed = this.#channels;
205
+ const groups = this.#groups;
206
+ const group_count = starts.length - 1;
207
+
208
+ if (CURSORS.length < group_count) {
209
+ CURSORS = new Uint32Array(group_count);
210
+ }
211
+
212
+ const cursors = CURSORS;
213
+
214
+ cursors.fill(0, 0, group_count);
215
+
216
+ let matched = 0;
217
+
218
+ for (let i = 0; i < count; i++) {
219
+ const channel = channels[i];
220
+ const target = channel.target;
221
+
222
+ if (!is_entity(target)) {
223
+ continue;
224
+ }
225
+
226
+ const group = groups.get(target);
227
+
228
+ if (group === UINT32_MAP_ABSENT) {
229
+ return false;
230
+ }
231
+
232
+ const slot = starts[group] + cursors[group];
233
+
234
+ if (slot >= starts[group + 1] || indexed[slot] !== channel) {
235
+ return false;
236
+ }
237
+
238
+ cursors[group]++;
239
+ matched++;
240
+ }
241
+
242
+ return matched === indexed.length;
243
+ }
244
+
245
+ /**
246
+ * The clip's index, as the clip holds its channels now: the one built before when it still
247
+ * holds, a new one otherwise.
248
+ *
249
+ * @param {ShadeAnimationClip} clip
250
+ * @returns {PoseChannelIndex}
251
+ */
252
+ static of(clip) {
253
+ const channels = clip.channels;
254
+ const cached = INDICES.get(clip);
255
+
256
+ if (cached !== undefined && cached.holds(channels)) {
257
+ return cached;
258
+ }
259
+
260
+ const index = new PoseChannelIndex(channels);
261
+
262
+ INDICES.set(clip, index);
263
+
264
+ return index;
265
+ }
266
+ }
267
+
268
+ /**
269
+ * What a playback with no clip holds: no groups at all.
270
+ *
271
+ * @readonly
272
+ * @type {PoseChannelIndex}
273
+ */
274
+ PoseChannelIndex.EMPTY = new PoseChannelIndex([]);
@@ -17,11 +17,46 @@ export class PosePlayback {
17
17
  */
18
18
  static from(clip: ShadeAnimationClip, time: number, weight: number): PosePlayback;
19
19
  /**
20
- * @type {ShadeAnimationClip}
20
+ * Assigning a clip reads its {@link start_time} once, here: `ShadeAnimationClip.start_time`
21
+ * walks every channel and every curve, and the evaluator runs per node of every query — reading
22
+ * it there tripled the cost of a whole-skeleton pass. Assigning the clip already held reads
23
+ * nothing, so a producer that refreshes its playbacks per query pays for it once per clip.
24
+ *
25
+ * It reads the clip's channels here too, grouped by the entity each names
26
+ * ({@link channel_index}), for the same reason: the evaluator asks once per level of every
27
+ * query, and scanning the whole clip for the few channels of one joint made its cost grow with
28
+ * every clip a model has bound. The grouping is built once per clip and shared, and taking the
29
+ * clip again only checks that it still holds.
30
+ *
31
+ * A clip's channels are taken as settled once it plays, as the renderer takes them when it
32
+ * registers the clip.
33
+ *
34
+ * @param {ShadeAnimationClip|null} clip
35
+ */
36
+ set clip(clip: any);
37
+ /**
38
+ * @returns {ShadeAnimationClip|null}
39
+ */
40
+ get clip(): any;
41
+ /**
42
+ * The clip's channels grouped by the entity each names, as they were when the clip was assigned;
43
+ * {@link PoseChannelIndex.EMPTY} while there is no clip.
44
+ *
45
+ * @returns {PoseChannelIndex}
46
+ */
47
+ get channel_index(): PoseChannelIndex;
48
+ /**
49
+ * Where {@link time} 0 sits on the clip's curves: its earliest key. The renderer's clip record
50
+ * takes the same origin at registration (`time_start`), which is what has the two sample one
51
+ * instant — a glTF clip keeps its file's key times, and its first key need not be at zero.
52
+ *
53
+ * @returns {number}
21
54
  */
22
- clip: ShadeAnimationClip;
55
+ get start_time(): number;
23
56
  /**
24
- * Seconds into the clip. Not wrapped for you — a caller that loops decides what looping means.
57
+ * Seconds into the clip, from its {@link start_time} — the renderer's clip record measures its
58
+ * `time` the same way, and both players run their clocks over `[0, duration]`. Not wrapped for
59
+ * you — a caller that loops decides what looping means.
25
60
  *
26
61
  * @type {number}
27
62
  */
@@ -36,5 +71,7 @@ export class PosePlayback {
36
71
  * @type {number}
37
72
  */
38
73
  weight: number;
74
+ #private;
39
75
  }
76
+ import { PoseChannelIndex } from "./PoseChannelIndex.js";
40
77
  //# sourceMappingURL=PosePlayback.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PosePlayback.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/animation/pose/PosePlayback.js"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH;IAwBI;;;;;OAKG;IACH,4CAJW,MAAM,UACN,MAAM,GACJ,YAAY,CAcxB;IAzCD;;OAEG;IACH,yBAAY;IAEZ;;;;OAIG;IACH,MAFU,MAAM,CAEP;IAET;;;;;;;;OAQG;IACH,QAFU,MAAM,CAEL;CAqBd"}
1
+ {"version":3,"file":"PosePlayback.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/animation/pose/PosePlayback.js"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH;IAiGI;;;;;OAKG;IACH,4CAJW,MAAM,UACN,MAAM,GACJ,YAAY,CAcxB;IA5FD;;;;;;;;;;;;;;;;OAgBG;IACH,oBAcC;IAtCD;;OAEG;IACH,gBAEC;IAmCD;;;;;OAKG;IACH,sCAEC;IAED;;;;;;OAMG;IACH,yBAEC;IAED;;;;;;OAMG;IACH,MAFU,MAAM,CAEP;IAET;;;;;;;;OAQG;IACH,QAFU,MAAM,CAEL;;CAqBd;iCAhIgC,uBAAuB"}
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
+ import { PoseChannelIndex } from "./PoseChannelIndex.js";
2
3
 
3
4
  /**
4
5
  * One clip contributing to a pose: which clip, where in it, and how much of it.
@@ -12,12 +13,85 @@ import { assert } from "../../../../core/assert.js";
12
13
  */
13
14
  export class PosePlayback {
14
15
  /**
15
- * @type {ShadeAnimationClip}
16
+ * @type {ShadeAnimationClip|null}
16
17
  */
17
- clip = null;
18
+ #clip = null;
18
19
 
19
20
  /**
20
- * Seconds into the clip. Not wrapped for you — a caller that loops decides what looping means.
21
+ * @type {number}
22
+ */
23
+ #start_time = 0;
24
+
25
+ /**
26
+ * @type {PoseChannelIndex}
27
+ */
28
+ #channel_index = PoseChannelIndex.EMPTY;
29
+
30
+ /**
31
+ * @returns {ShadeAnimationClip|null}
32
+ */
33
+ get clip() {
34
+ return this.#clip;
35
+ }
36
+
37
+ /**
38
+ * Assigning a clip reads its {@link start_time} once, here: `ShadeAnimationClip.start_time`
39
+ * walks every channel and every curve, and the evaluator runs per node of every query — reading
40
+ * it there tripled the cost of a whole-skeleton pass. Assigning the clip already held reads
41
+ * nothing, so a producer that refreshes its playbacks per query pays for it once per clip.
42
+ *
43
+ * It reads the clip's channels here too, grouped by the entity each names
44
+ * ({@link channel_index}), for the same reason: the evaluator asks once per level of every
45
+ * query, and scanning the whole clip for the few channels of one joint made its cost grow with
46
+ * every clip a model has bound. The grouping is built once per clip and shared, and taking the
47
+ * clip again only checks that it still holds.
48
+ *
49
+ * A clip's channels are taken as settled once it plays, as the renderer takes them when it
50
+ * registers the clip.
51
+ *
52
+ * @param {ShadeAnimationClip|null} clip
53
+ */
54
+ set clip(clip) {
55
+ if (clip === this.#clip) {
56
+ return;
57
+ }
58
+
59
+ this.#clip = clip;
60
+
61
+ if (clip === null) {
62
+ this.#start_time = 0;
63
+ this.#channel_index = PoseChannelIndex.EMPTY;
64
+ } else {
65
+ this.#start_time = clip.start_time;
66
+ this.#channel_index = PoseChannelIndex.of(clip);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * The clip's channels grouped by the entity each names, as they were when the clip was assigned;
72
+ * {@link PoseChannelIndex.EMPTY} while there is no clip.
73
+ *
74
+ * @returns {PoseChannelIndex}
75
+ */
76
+ get channel_index() {
77
+ return this.#channel_index;
78
+ }
79
+
80
+ /**
81
+ * Where {@link time} 0 sits on the clip's curves: its earliest key. The renderer's clip record
82
+ * takes the same origin at registration (`time_start`), which is what has the two sample one
83
+ * instant — a glTF clip keeps its file's key times, and its first key need not be at zero.
84
+ *
85
+ * @returns {number}
86
+ */
87
+ get start_time() {
88
+ return this.#start_time;
89
+ }
90
+
91
+ /**
92
+ * Seconds into the clip, from its {@link start_time} — the renderer's clip record measures its
93
+ * `time` the same way, and both players run their clocks over `[0, duration]`. Not wrapped for
94
+ * you — a caller that loops decides what looping means.
21
95
  *
22
96
  * @type {number}
23
97
  */
@@ -6,26 +6,44 @@
6
6
  * into a per-row slot (`POSE_ACCUMULATOR_LAYOUT`) and then drains it
7
7
  * (`shader_animation_pose_resolve`); the rules reproduced here are that drain's, exactly:
8
8
  *
9
+ * - **A clip is sampled at its start plus its time** — {@link PosePlayback.start_time}, its earliest
10
+ * key, is the apply pass's `clip.time_start`.
9
11
  * - **A channel that contributed replaces its axis; one that did not leaves the rest pose alone.**
10
12
  * Translation and scale track this per axis, because a channel may animate only one of them.
11
13
  * - **Rotation is a weighted sum, normalised once at the end** — nlerp, not slerp, and normalising
12
14
  * is what turns the sum back into a rotation. It is tracked with a single dirty bit because all
13
15
  * four components always move together.
14
16
  * - **Weights are not normalised.** See {@link PosePlayback.weight}.
17
+ * - **A clip at weight zero still writes**: the accumulator takes a bound clip's contribution
18
+ * whatever its weight, so the axes it keys resolve to the sum of what was added. It adds its value
19
+ * times zero, which leaves any finite sum where it was, so here its curves are not sampled — only
20
+ * its axes are marked. The two part only where a silent clip's curve would sample to something
21
+ * that is not a finite number — an infinite key, or keys whose interpolation overflows — which
22
+ * the GPU multiplies by zero for no number at all, and which adds nothing here.
15
23
  *
16
24
  * Where the two disagree, the GPU is right by definition — it is what is on screen — and this is
17
25
  * the thing to fix. `pose_evaluator_lockstep.spec.js` fails when the WGSL moves, so that the pair
18
26
  * cannot drift silently.
19
27
  *
28
+ * A call costs a lookup per playback and the curves of the channels that name `target` in the
29
+ * audible ones — not a scan of every clip: each playback holds its clip's channels grouped by
30
+ * entity ({@link PosePlayback#channel_index}), read once when it took the clip. A model with many
31
+ * clips bound and few playing pays little more than one with only the few.
32
+ *
20
33
  * `rest` supplies the rest pose: the node's or the entity's authored local offset. That is sound
21
34
  * because the CPU never writes a GPU-authoritative row, so what the authored offset holds is what
22
- * the row started from, and undirty axes on the GPU hold the same value for the same reason.
35
+ * the row started from, and undirty axes on the GPU hold the same value for the same reason. A CPU
36
+ * writer's hold is how the offset may move under a bound clip, and its end hands the offset the
37
+ * holder left to the row (`GPUSceneRows#mark_handover`) and to every binding as its rest
38
+ * (`GPUAnimationManager#unmute`), so the same holds after it. Anything else writing a driven
39
+ * joint's offset breaks this, and the twin then answers a rest the GPU does not have.
23
40
  *
24
41
  * @param {Transform64} result written with the evaluated local TRS; its matrix is left stale
25
- * @param {Node3D|number} target what the channels name — a node, or an entity
42
+ * @param {number} target the entity the channels name
26
43
  * @param {Transform64} rest the target's authored local pose
27
44
  * @param {PosePlayback[]} playbacks
28
45
  * @returns {Transform64} `result`
29
46
  */
30
- export function pose_evaluate_local(result: Transform64, target: Node3D | number, rest: Transform64, playbacks: PosePlayback[]): Transform64;
47
+ export function pose_evaluate_local(result: Transform64, target: number, rest: Transform64, playbacks: PosePlayback[]): Transform64;
48
+ export type ShadeAnimationChannel = import("../ShadeAnimationChannel.js").ShadeAnimationChannel;
31
49
  //# sourceMappingURL=pose_evaluate_local.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pose_evaluate_local.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/animation/pose/pose_evaluate_local.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,iEALW,SAAO,MAAM,gCAEb,cAAc,eAqIxB"}
1
+ {"version":3,"file":"pose_evaluate_local.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/animation/pose/pose_evaluate_local.js"],"names":[],"mappings":"AAqDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,iEALW,MAAM,gCAEN,cAAc,eAsJxB;oCAjPY,OAAO,6BAA6B,EAAE,qBAAqB"}
@@ -1,5 +1,10 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
2
  import { Node3DProperty } from "../../object_property/Node3DProperty.js";
3
+ import { POSE_CHANNEL_GROUP_NONE } from "./PoseChannelIndex.js";
4
+
5
+ /**
6
+ * @typedef {import("../ShadeAnimationChannel.js").ShadeAnimationChannel} ShadeAnimationChannel
7
+ */
3
8
 
4
9
  /**
5
10
  * Per-axis accumulators, reused across calls. The evaluator is a query on a gameplay path and can
@@ -17,6 +22,35 @@ const DIRTY_SX = 16;
17
22
  const DIRTY_SY = 32;
18
23
  const DIRTY_SZ = 64;
19
24
 
25
+ /**
26
+ * What a channel writes, as dirty bits: each translation or scale axis it has a curve for, and the
27
+ * rotation whole — what the accumulator marks for it at any weight.
28
+ *
29
+ * @param {ShadeAnimationChannel} channel
30
+ * @returns {number}
31
+ */
32
+ function channel_dirty_bits(channel) {
33
+ const curves = channel.curves;
34
+
35
+ switch (channel.property) {
36
+ case Node3DProperty.Translation:
37
+ return (curves.x !== undefined ? DIRTY_TX : 0)
38
+ | (curves.y !== undefined ? DIRTY_TY : 0)
39
+ | (curves.z !== undefined ? DIRTY_TZ : 0);
40
+
41
+ case Node3DProperty.Rotation:
42
+ return DIRTY_R;
43
+
44
+ case Node3DProperty.Scale:
45
+ return (curves.x !== undefined ? DIRTY_SX : 0)
46
+ | (curves.y !== undefined ? DIRTY_SY : 0)
47
+ | (curves.z !== undefined ? DIRTY_SZ : 0);
48
+
49
+ default:
50
+ return 0;
51
+ }
52
+ }
53
+
20
54
  /**
21
55
  * Evaluate one node's local pose from a set of playing clips, into `result`.
22
56
  *
@@ -25,23 +59,40 @@ const DIRTY_SZ = 64;
25
59
  * into a per-row slot (`POSE_ACCUMULATOR_LAYOUT`) and then drains it
26
60
  * (`shader_animation_pose_resolve`); the rules reproduced here are that drain's, exactly:
27
61
  *
62
+ * - **A clip is sampled at its start plus its time** — {@link PosePlayback.start_time}, its earliest
63
+ * key, is the apply pass's `clip.time_start`.
28
64
  * - **A channel that contributed replaces its axis; one that did not leaves the rest pose alone.**
29
65
  * Translation and scale track this per axis, because a channel may animate only one of them.
30
66
  * - **Rotation is a weighted sum, normalised once at the end** — nlerp, not slerp, and normalising
31
67
  * is what turns the sum back into a rotation. It is tracked with a single dirty bit because all
32
68
  * four components always move together.
33
69
  * - **Weights are not normalised.** See {@link PosePlayback.weight}.
70
+ * - **A clip at weight zero still writes**: the accumulator takes a bound clip's contribution
71
+ * whatever its weight, so the axes it keys resolve to the sum of what was added. It adds its value
72
+ * times zero, which leaves any finite sum where it was, so here its curves are not sampled — only
73
+ * its axes are marked. The two part only where a silent clip's curve would sample to something
74
+ * that is not a finite number — an infinite key, or keys whose interpolation overflows — which
75
+ * the GPU multiplies by zero for no number at all, and which adds nothing here.
34
76
  *
35
77
  * Where the two disagree, the GPU is right by definition — it is what is on screen — and this is
36
78
  * the thing to fix. `pose_evaluator_lockstep.spec.js` fails when the WGSL moves, so that the pair
37
79
  * cannot drift silently.
38
80
  *
81
+ * A call costs a lookup per playback and the curves of the channels that name `target` in the
82
+ * audible ones — not a scan of every clip: each playback holds its clip's channels grouped by
83
+ * entity ({@link PosePlayback#channel_index}), read once when it took the clip. A model with many
84
+ * clips bound and few playing pays little more than one with only the few.
85
+ *
39
86
  * `rest` supplies the rest pose: the node's or the entity's authored local offset. That is sound
40
87
  * because the CPU never writes a GPU-authoritative row, so what the authored offset holds is what
41
- * the row started from, and undirty axes on the GPU hold the same value for the same reason.
88
+ * the row started from, and undirty axes on the GPU hold the same value for the same reason. A CPU
89
+ * writer's hold is how the offset may move under a bound clip, and its end hands the offset the
90
+ * holder left to the row (`GPUSceneRows#mark_handover`) and to every binding as its rest
91
+ * (`GPUAnimationManager#unmute`), so the same holds after it. Anything else writing a driven
92
+ * joint's offset breaks this, and the twin then answers a rest the GPU does not have.
42
93
  *
43
94
  * @param {Transform64} result written with the evaluated local TRS; its matrix is left stale
44
- * @param {Node3D|number} target what the channels name — a node, or an entity
95
+ * @param {number} target the entity the channels name
45
96
  * @param {Transform64} rest the target's authored local pose
46
97
  * @param {PosePlayback[]} playbacks
47
98
  * @returns {Transform64} `result`
@@ -69,17 +120,33 @@ export function pose_evaluate_local(result, target, rest, playbacks) {
69
120
 
70
121
  for (let p = 0; p < playbacks.length; p++) {
71
122
  const playback = playbacks[p];
72
- const weight = playback.weight;
73
- const time = playback.time;
74
- const channels = playback.clip.channels;
123
+ const index = playback.channel_index;
124
+ // only the channels that name this target, which most clips have none of
125
+ const group = index.group_of(target);
126
+
127
+ if (group === POSE_CHANNEL_GROUP_NONE) {
128
+ continue;
129
+ }
75
130
 
76
- for (let c = 0; c < channels.length; c++) {
77
- const channel = channels[c];
131
+ const first = index.first(group);
132
+ const end = index.end(group);
78
133
 
79
- if (channel.target !== target) {
80
- continue;
134
+ const weight = playback.weight;
135
+
136
+ if (weight === 0) {
137
+ // it writes, and adds nothing to what it writes: mark its axes, sample nothing
138
+ for (let c = first; c < end; c++) {
139
+ dirty |= channel_dirty_bits(index.channel(c));
81
140
  }
82
141
 
142
+ continue;
143
+ }
144
+
145
+ // the apply pass's `clip.time + clip.time_start`
146
+ const time = playback.start_time + playback.time;
147
+
148
+ for (let c = first; c < end; c++) {
149
+ const channel = index.channel(c);
83
150
  const curves = channel.curves;
84
151
 
85
152
  switch (channel.property) {
@@ -160,8 +227,9 @@ export function pose_evaluate_local(result, target, rest, playbacks) {
160
227
  // A quaternion sum of length zero has no orientation in it to recover. The ordinary way in
161
228
  // is a joint whose rotation only clips at weight zero write — the accumulator takes a bound
162
229
  // clip's contribution whatever its weight — and the other is contributions that cancelled
163
- // exactly. The GPU leaves the row alone there, which is the rest pose because nothing has
164
- // written a rotation to it; this says the same thing from the node.
230
+ // exactly. The answer is the rest pose: the GPU writes the rest rotation every
231
+ // contribution carried from its binding, which was taken from this same rest when the clip
232
+ // bound the target.
165
233
  const length = Math.sqrt(x * x + y * y + z * z + w * w);
166
234
 
167
235
  if (length > 0) {
@@ -86,7 +86,7 @@ fn main(
86
86
 
87
87
  let value = animation_track_evaluate(effective_time, track, &animation_database) * weight;
88
88
 
89
- write_object_property(binding.instance, binding.property, track.mask, value, weight);
89
+ write_object_property(binding.instance, binding.property, track.mask, value, weight, binding.rest_rotation);
90
90
  }
91
91
  `, [
92
92
  BINDING_TABLE.chunk_iterate(WORKGROUP_SIZE),
@@ -11,8 +11,9 @@ export const WORKGROUP_SIZE: 64;
11
11
  * thread per slot. For each occupied row whose accumulator slot has
12
12
  * any dirty bits set, reads the contributed components (atomic
13
13
  * loads, bitcast back to f32), writes them through the per-field
14
- * scene helpers, normalises rotation, and zeros the slot for next
15
- * frame.
14
+ * scene helpers, normalises rotation — or, for a rotation summing to
15
+ * nothing, writes the rest rotation the contributions carried — and
16
+ * zeros the sums and the dirty mask for next frame.
16
17
  *
17
18
  * The accumulator buffer must be sized to at least
18
19
  * `transforms.dispatch_page_count * elements_per_page` rows —
@@ -1 +1 @@
1
- {"version":3,"file":"shader_animation_pose_resolve.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/animation/shader_animation_pose_resolve.js"],"names":[],"mappings":"AAWA;;;GAGG;AACH,gCAAiC;AAyIjC;;;;;;;;;;;;;;;;;;GAkBG;AACH,0DAIG;8BA1K2B,4BAA4B"}
1
+ {"version":3,"file":"shader_animation_pose_resolve.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/animation/shader_animation_pose_resolve.js"],"names":[],"mappings":"AAWA;;;GAGG;AACH,gCAAiC;AAmJjC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,0DAIG;8BArL2B,4BAA4B"}