@vgai/engine 0.5.13 → 0.5.14

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 (192) hide show
  1. package/dist/adapter/adapter-module.d.ts +447 -0
  2. package/dist/adapter/adapter-module.d.ts.map +1 -0
  3. package/dist/adapter/adapter-module.js +308 -0
  4. package/dist/adapter/authoring.d.ts +357 -10
  5. package/dist/adapter/authoring.d.ts.map +1 -1
  6. package/dist/adapter/authoring.js +87 -1
  7. package/dist/adapter/constraint.d.ts +9 -3
  8. package/dist/adapter/constraint.d.ts.map +1 -1
  9. package/dist/adapter/finders/finder-result.d.ts +28 -0
  10. package/dist/adapter/finders/finder-result.d.ts.map +1 -0
  11. package/dist/adapter/finders/finder-result.js +10 -0
  12. package/dist/adapter/finders/index.d.ts +38 -0
  13. package/dist/adapter/finders/index.d.ts.map +1 -0
  14. package/dist/adapter/finders/index.js +41 -0
  15. package/dist/adapter/finders/prefabs-from-stories.d.ts +64 -0
  16. package/dist/adapter/finders/prefabs-from-stories.d.ts.map +1 -0
  17. package/dist/adapter/finders/prefabs-from-stories.js +92 -0
  18. package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts +67 -0
  19. package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -0
  20. package/dist/adapter/finders/scenes-from-entrypoint-selection.js +226 -0
  21. package/dist/adapter/first-party-systems.d.ts.map +1 -1
  22. package/dist/adapter/first-party-systems.js +29 -0
  23. package/dist/adapter/index.d.ts +7 -3
  24. package/dist/adapter/index.d.ts.map +1 -1
  25. package/dist/adapter/index.js +5 -2
  26. package/dist/adapter/ingest/contract-system-adapters.d.ts +24 -1
  27. package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -1
  28. package/dist/adapter/ingest/contract-system-adapters.js +100 -34
  29. package/dist/adapter/ingest/game-contract.d.ts +188 -2
  30. package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
  31. package/dist/adapter/ingest/game-contract.js +107 -0
  32. package/dist/adapter/ingest/mount-readiness.d.ts +84 -0
  33. package/dist/adapter/ingest/mount-readiness.d.ts.map +1 -0
  34. package/dist/adapter/ingest/mount-readiness.js +81 -0
  35. package/dist/adapter/ingest/scene-capture.d.ts +91 -2
  36. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
  37. package/dist/adapter/ingest/scene-capture.js +110 -18
  38. package/dist/adapter/ingest/structural-ids.d.ts +11 -5
  39. package/dist/adapter/ingest/structural-ids.d.ts.map +1 -1
  40. package/dist/adapter/ingest/structural-ids.js +18 -9
  41. package/dist/adapter/ingest/upstream-pin.d.ts +4 -2
  42. package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -1
  43. package/dist/adapter/ingest/upstream-pin.js +4 -2
  44. package/dist/adapter/ingest/visible-capture-window.d.ts +116 -0
  45. package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -0
  46. package/dist/adapter/ingest/visible-capture-window.js +141 -0
  47. package/dist/adapter/loop-gate-report.d.ts +3 -4
  48. package/dist/adapter/loop-gate-report.d.ts.map +1 -1
  49. package/dist/adapter/loop-gate-report.js +3 -4
  50. package/dist/adapter/root-adapter.d.ts +22 -2
  51. package/dist/adapter/root-adapter.d.ts.map +1 -1
  52. package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -1
  53. package/dist/adapter/setup-three-root-adapter.js +15 -0
  54. package/dist/adapter/system-adapter.d.ts +161 -2
  55. package/dist/adapter/system-adapter.d.ts.map +1 -1
  56. package/dist/adapter/system-adapter.js +18 -1
  57. package/dist/animation/camera-ownership.d.ts +4 -2
  58. package/dist/animation/camera-ownership.d.ts.map +1 -1
  59. package/dist/animation/camera-ownership.js +58 -1
  60. package/dist/animation/runtime-inspection.d.ts +70 -0
  61. package/dist/animation/runtime-inspection.d.ts.map +1 -0
  62. package/dist/animation/runtime-inspection.js +25 -0
  63. package/dist/animation/xstate-animation-binding.d.ts +9 -1
  64. package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
  65. package/dist/animation/xstate-animation-binding.js +48 -1
  66. package/dist/asset-formats/material.d.ts +1 -1
  67. package/dist/asset-formats/mesh.d.ts +1 -1
  68. package/dist/asset-formats/particles.d.ts +13 -0
  69. package/dist/asset-formats/particles.d.ts.map +1 -1
  70. package/dist/asset-formats/particles.js +28 -0
  71. package/dist/behavior/xstate-inspection.d.ts +24 -0
  72. package/dist/behavior/xstate-inspection.d.ts.map +1 -0
  73. package/dist/behavior/xstate-inspection.js +25 -0
  74. package/dist/core/game-loop.d.ts +7 -5
  75. package/dist/core/game-loop.d.ts.map +1 -1
  76. package/dist/core/game-loop.js +39 -18
  77. package/dist/core/types.d.ts +11 -12
  78. package/dist/core/types.d.ts.map +1 -1
  79. package/dist/data/data-asset.js +1 -1
  80. package/dist/dev/instruments.d.ts.map +1 -1
  81. package/dist/dev/instruments.js +5 -1
  82. package/dist/dev/render-debug-adapter.d.ts +19 -2
  83. package/dist/dev/render-debug-adapter.d.ts.map +1 -1
  84. package/dist/dev/render-debug-adapter.js +10 -0
  85. package/dist/ecs/user-data.d.ts +10 -2
  86. package/dist/ecs/user-data.d.ts.map +1 -1
  87. package/dist/ecs/user-data.js +6 -2
  88. package/dist/input/binding-identity.d.ts +10 -0
  89. package/dist/input/binding-identity.d.ts.map +1 -0
  90. package/dist/input/binding-identity.js +36 -0
  91. package/dist/input/input-manager.d.ts +18 -185
  92. package/dist/input/input-manager.d.ts.map +1 -1
  93. package/dist/input/input-manager.js +78 -68
  94. package/dist/manifest/load.d.ts +14 -0
  95. package/dist/manifest/load.d.ts.map +1 -1
  96. package/dist/manifest/load.js +6 -1
  97. package/dist/manifest/schema.d.ts +18 -0
  98. package/dist/manifest/schema.d.ts.map +1 -1
  99. package/dist/manifest/schema.js +64 -7
  100. package/dist/pixi/authoring.d.ts +27 -0
  101. package/dist/pixi/authoring.d.ts.map +1 -1
  102. package/dist/pixi/authoring.js +10 -3
  103. package/dist/pixi/index.d.ts +1 -1
  104. package/dist/pixi/index.d.ts.map +1 -1
  105. package/dist/pixi/index.js +1 -1
  106. package/dist/pixi/ingest.d.ts +8 -0
  107. package/dist/pixi/ingest.d.ts.map +1 -1
  108. package/dist/pixi/ingest.js +10 -1
  109. package/dist/pixi/render-pass-bracket.d.ts +56 -0
  110. package/dist/pixi/render-pass-bracket.d.ts.map +1 -0
  111. package/dist/pixi/render-pass-bracket.js +98 -0
  112. package/dist/pixi/scene-capture.d.ts +32 -1
  113. package/dist/pixi/scene-capture.d.ts.map +1 -1
  114. package/dist/pixi/scene-capture.js +21 -9
  115. package/dist/pixi/system-adapters.d.ts +29 -0
  116. package/dist/pixi/system-adapters.d.ts.map +1 -1
  117. package/dist/pixi/system-adapters.js +27 -0
  118. package/dist/render/particles-factory.d.ts +16 -2
  119. package/dist/render/particles-factory.d.ts.map +1 -1
  120. package/dist/render/particles-factory.js +88 -5
  121. package/dist/render/soft-particle-depth.d.ts +149 -0
  122. package/dist/render/soft-particle-depth.d.ts.map +1 -0
  123. package/dist/render/soft-particle-depth.js +305 -0
  124. package/dist/render/voxel-surface.d.ts +117 -0
  125. package/dist/render/voxel-surface.d.ts.map +1 -0
  126. package/dist/render/voxel-surface.js +263 -0
  127. package/dist/runtime/create-runtime.js +7 -0
  128. package/dist/runtime/debug-bridge.d.ts +15 -15
  129. package/dist/runtime/debug-bridge.d.ts.map +1 -1
  130. package/dist/runtime/debug-bridge.js +27 -26
  131. package/dist/runtime/game.d.ts.map +1 -1
  132. package/dist/runtime/game.js +1 -2
  133. package/dist/setup/setup-particles.d.ts.map +1 -1
  134. package/dist/setup/setup-particles.js +9 -0
  135. package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
  136. package/dist/world3d-react/r3f-adapter.js +42 -3
  137. package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
  138. package/dist/world3d-react/rapier-physics-bridge.js +201 -11
  139. package/dist-config/config.js.map +1 -1
  140. package/package.json +4 -2
  141. package/schemas/engine-capabilities.json +12 -12
  142. package/schemas/vgai-project.schema.json +41 -2
  143. package/src/adapter/adapter-module.ts +607 -0
  144. package/src/adapter/authoring.ts +440 -10
  145. package/src/adapter/constraint.ts +10 -3
  146. package/src/adapter/finders/finder-result.ts +29 -0
  147. package/src/adapter/finders/index.ts +62 -0
  148. package/src/adapter/finders/prefabs-from-stories.ts +142 -0
  149. package/src/adapter/finders/scenes-from-entrypoint-selection.ts +304 -0
  150. package/src/adapter/first-party-systems.ts +30 -0
  151. package/src/adapter/index.ts +32 -1
  152. package/src/adapter/ingest/contract-system-adapters.ts +116 -32
  153. package/src/adapter/ingest/game-contract.ts +265 -2
  154. package/src/adapter/ingest/mount-readiness.ts +140 -0
  155. package/src/adapter/ingest/scene-capture.ts +199 -22
  156. package/src/adapter/ingest/structural-ids.ts +21 -9
  157. package/src/adapter/ingest/upstream-pin.ts +4 -2
  158. package/src/adapter/ingest/visible-capture-window.ts +213 -0
  159. package/src/adapter/loop-gate-report.ts +3 -4
  160. package/src/adapter/root-adapter.ts +22 -2
  161. package/src/adapter/setup-three-root-adapter.ts +16 -0
  162. package/src/adapter/system-adapter.ts +188 -2
  163. package/src/animation/camera-ownership.ts +68 -3
  164. package/src/animation/runtime-inspection.ts +104 -0
  165. package/src/animation/xstate-animation-binding.ts +53 -1
  166. package/src/asset-formats/particles.ts +31 -0
  167. package/src/behavior/xstate-inspection.ts +51 -0
  168. package/src/core/game-loop.ts +39 -17
  169. package/src/core/types.ts +11 -12
  170. package/src/data/data-asset.ts +1 -1
  171. package/src/dev/instruments.ts +5 -1
  172. package/src/dev/render-debug-adapter.ts +22 -2
  173. package/src/ecs/user-data.ts +10 -2
  174. package/src/input/binding-identity.ts +37 -0
  175. package/src/input/input-manager.ts +93 -68
  176. package/src/manifest/load.ts +23 -2
  177. package/src/manifest/schema.ts +76 -7
  178. package/src/pixi/authoring.ts +37 -5
  179. package/src/pixi/index.ts +2 -0
  180. package/src/pixi/ingest.ts +15 -2
  181. package/src/pixi/render-pass-bracket.ts +117 -0
  182. package/src/pixi/scene-capture.ts +63 -13
  183. package/src/pixi/system-adapters.ts +41 -0
  184. package/src/render/particles-factory.ts +89 -6
  185. package/src/render/soft-particle-depth.ts +336 -0
  186. package/src/render/voxel-surface.ts +358 -0
  187. package/src/runtime/create-runtime.ts +7 -0
  188. package/src/runtime/debug-bridge.ts +32 -31
  189. package/src/runtime/game.ts +1 -2
  190. package/src/setup/setup-particles.ts +9 -0
  191. package/src/world3d-react/r3f-adapter.tsx +48 -2
  192. package/src/world3d-react/rapier-physics-bridge.tsx +249 -11
@@ -61,12 +61,18 @@
61
61
 
62
62
  import { type RapierRigidBody, useRapier } from '@react-three/rapier';
63
63
  import { useEffect } from 'react';
64
- import type * as THREE from 'three';
64
+ import * as THREE from 'three';
65
65
  import {
66
66
  createRapierBodyEditing,
67
67
  type RapierEditableBody,
68
68
  } from '../adapter/rapier-physics-adapter';
69
- import type { PhysicsAdapter } from '../adapter/system-adapter';
69
+ import type {
70
+ PhysicsAdapter,
71
+ PhysicsColliderShape,
72
+ PhysicsColliderSnapshot,
73
+ PhysicsJointSnapshot,
74
+ PhysicsJointType,
75
+ } from '../adapter/system-adapter';
70
76
  import { useGameContext } from './engine-bridge';
71
77
 
72
78
  export interface RapierPhysicsBridgeProps {
@@ -79,6 +85,38 @@ export interface RapierPhysicsBridgeProps {
79
85
  readonly onDebugChange: (debug: boolean) => void;
80
86
  }
81
87
 
88
+ interface RapierBodyStateLike {
89
+ readonly object: THREE.Object3D;
90
+ readonly rigidBody: RapierRigidBody;
91
+ }
92
+
93
+ function lateEntityObject(
94
+ states: Iterable<RapierBodyStateLike>,
95
+ nodeId: string,
96
+ ): THREE.Object3D | undefined {
97
+ let found: THREE.Object3D | undefined;
98
+ for (const state of states) {
99
+ state.object.traverse((candidate) => {
100
+ if (!found && candidate.userData['entityId'] === nodeId) found = candidate;
101
+ });
102
+ if (found) return found;
103
+ }
104
+ return undefined;
105
+ }
106
+
107
+ function owningBody(
108
+ object: THREE.Object3D,
109
+ states: Iterable<RapierBodyStateLike>,
110
+ ): RapierRigidBody | undefined {
111
+ const byObject = new Map<THREE.Object3D, RapierRigidBody>();
112
+ for (const state of states) byObject.set(state.object, state.rigidBody);
113
+ for (let node: THREE.Object3D | null = object; node; node = node.parent) {
114
+ const body = byObject.get(node);
115
+ if (body) return body;
116
+ }
117
+ return undefined;
118
+ }
119
+
82
120
  /**
83
121
  * Register this R3F world's `@react-three/rapier` physics as the game's
84
122
  * `SystemAdapters.physics`. Mount it once, INSIDE `<Physics>` (it reads
@@ -86,7 +124,7 @@ export interface RapierPhysicsBridgeProps {
86
124
  */
87
125
  export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps): null {
88
126
  const ctx = useGameContext();
89
- const { rigidBodyStates } = useRapier();
127
+ const { colliderStates, rapier, rigidBodyStates, world } = useRapier();
90
128
  const registerSystemAdapter = ctx.registerSystemAdapter;
91
129
 
92
130
  useEffect(() => {
@@ -114,24 +152,224 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
114
152
  * `setSceneCamera`), so a destructured copy would be permanently absent.
115
153
  */
116
154
  const bodyFor = (nodeId: string): RapierEditableBody | undefined => {
117
- const object = ctx.sceneIndex?.byEntityId(nodeId);
155
+ const indexed = ctx.sceneIndex?.byEntityId(nodeId);
156
+ const object = indexed ?? lateEntityObject(rigidBodyStates.values(), nodeId);
157
+ if (!indexed && object) {
158
+ // The editor assigns its stable authoring entity ids AFTER Fiber's
159
+ // first commit, while the root's SceneIndex is constructed during
160
+ // that commit. Resolve the one late id inside the library-owned body
161
+ // subtrees and immediately reindex it; subsequent gestures take the
162
+ // normal indexed path. This is the SceneIndex's documented escape
163
+ // hatch for a userData id written after construction, not a parallel
164
+ // scene mirror.
165
+ ctx.sceneIndex?.reindex(object);
166
+ }
118
167
  if (!object) return undefined;
119
- const byObject = new Map<THREE.Object3D, RapierRigidBody>();
120
- for (const state of rigidBodyStates.values()) byObject.set(state.object, state.rigidBody);
121
- for (let node: THREE.Object3D | null = object; node; node = node.parent) {
122
- const body = byObject.get(node);
123
- if (body) return body;
168
+ return owningBody(object, rigidBodyStates.values());
169
+ };
170
+
171
+ const colliderShape = (collider: {
172
+ shapeType(): number;
173
+ halfExtents(): { x: number; y: number; z: number };
174
+ radius(): number;
175
+ halfHeight(): number;
176
+ }): PhysicsColliderShape | null => {
177
+ switch (collider.shapeType()) {
178
+ case rapier.ShapeType.Cuboid: {
179
+ const halfExtents = collider.halfExtents();
180
+ return { type: 'cuboid', halfExtents: [halfExtents.x, halfExtents.y, halfExtents.z] };
181
+ }
182
+ case rapier.ShapeType.Ball:
183
+ return { type: 'ball', radius: collider.radius() };
184
+ case rapier.ShapeType.Capsule:
185
+ return {
186
+ type: 'capsule',
187
+ halfHeight: collider.halfHeight(),
188
+ radius: collider.radius(),
189
+ };
190
+ default:
191
+ return null;
192
+ }
193
+ };
194
+
195
+ const colliders = (nodeId: string): PhysicsColliderSnapshot[] => {
196
+ const body = bodyFor(nodeId) as RapierRigidBody | undefined;
197
+ if (!body) return [];
198
+ const result: PhysicsColliderSnapshot[] = [];
199
+ for (const state of colliderStates.values()) {
200
+ if (state.collider.parent()?.handle !== body.handle) continue;
201
+ const shape = colliderShape(state.collider);
202
+ if (!shape) continue;
203
+ state.object.updateWorldMatrix(true, false);
204
+ const position = state.collider.translation();
205
+ const rotation = state.collider.rotation();
206
+ const scale = state.object.getWorldScale(new THREE.Vector3());
207
+ result.push({
208
+ id: String(state.collider.handle),
209
+ shape,
210
+ position: [position.x, position.y, position.z],
211
+ rotation: [rotation.x, rotation.y, rotation.z, rotation.w],
212
+ scale: [Math.abs(scale.x), Math.abs(scale.y), Math.abs(scale.z)],
213
+ sensor: state.collider.isSensor(),
214
+ });
215
+ }
216
+ return result.sort((a, b) => Number(a.id) - Number(b.id));
217
+ };
218
+
219
+ const previewCollider = (colliderId: string, shape: PhysicsColliderShape): void => {
220
+ const collider = [...colliderStates.values()].find(
221
+ (state) => String(state.collider.handle) === colliderId,
222
+ )?.collider;
223
+ if (!collider || colliderShape(collider)?.type !== shape.type) return;
224
+ switch (shape.type) {
225
+ case 'cuboid':
226
+ collider.setHalfExtents({
227
+ x: shape.halfExtents[0],
228
+ y: shape.halfExtents[1],
229
+ z: shape.halfExtents[2],
230
+ });
231
+ break;
232
+ case 'ball':
233
+ collider.setRadius(shape.radius);
234
+ break;
235
+ case 'capsule':
236
+ collider.setHalfHeight(shape.halfHeight);
237
+ collider.setRadius(shape.radius);
238
+ break;
124
239
  }
125
- return undefined;
240
+ };
241
+
242
+ const jointType = (type: number): PhysicsJointType => {
243
+ switch (type) {
244
+ case rapier.JointType.Revolute:
245
+ return 'revolute';
246
+ case rapier.JointType.Fixed:
247
+ return 'fixed';
248
+ case rapier.JointType.Prismatic:
249
+ return 'prismatic';
250
+ case rapier.JointType.Rope:
251
+ return 'rope';
252
+ case rapier.JointType.Spring:
253
+ return 'spring';
254
+ case rapier.JointType.Spherical:
255
+ return 'spherical';
256
+ default:
257
+ return 'generic';
258
+ }
259
+ };
260
+
261
+ const tuple3 = (value: { x: number; y: number; z: number }): [number, number, number] => [
262
+ value.x,
263
+ value.y,
264
+ value.z,
265
+ ];
266
+ const tuple4 = (value: {
267
+ x: number;
268
+ y: number;
269
+ z: number;
270
+ w: number;
271
+ }): [number, number, number, number] => [value.x, value.y, value.z, value.w];
272
+ const worldAnchor = (
273
+ body: RapierRigidBody,
274
+ anchor: { x: number; y: number; z: number },
275
+ ): [number, number, number] => {
276
+ const point = new THREE.Vector3(anchor.x, anchor.y, anchor.z)
277
+ .applyQuaternion(new THREE.Quaternion(...tuple4(body.rotation())))
278
+ .add(new THREE.Vector3(...tuple3(body.translation())));
279
+ return tuple3(point);
280
+ };
281
+
282
+ type NativeImpulseJoint = ReturnType<typeof world.impulseJoints.getAll>[number];
283
+ const jointLimits = (
284
+ joint: NativeImpulseJoint,
285
+ type: PhysicsJointType,
286
+ ): Pick<PhysicsJointSnapshot, 'limits'> => {
287
+ if (type !== 'revolute' && type !== 'prismatic') return {};
288
+ const unit = joint as unknown as {
289
+ limitsEnabled(): boolean;
290
+ limitsMin(): number;
291
+ limitsMax(): number;
292
+ };
293
+ return unit.limitsEnabled()
294
+ ? { limits: { min: unit.limitsMin(), max: unit.limitsMax() } }
295
+ : {};
296
+ };
297
+
298
+ const jointAxis = (
299
+ joint: NativeImpulseJoint,
300
+ type: PhysicsJointType,
301
+ body: RapierRigidBody,
302
+ ): Pick<PhysicsJointSnapshot, 'axis' | 'worldAxis'> => {
303
+ if (type !== 'revolute' && type !== 'prismatic') return {};
304
+ const frame = joint.frameX1();
305
+ const axis = new THREE.Vector3(1, 0, 0).applyQuaternion(
306
+ new THREE.Quaternion(frame.x, frame.y, frame.z, frame.w),
307
+ );
308
+ const worldAxis = axis
309
+ .clone()
310
+ .applyQuaternion(new THREE.Quaternion(...tuple4(body.rotation())));
311
+ return { axis: tuple3(axis), worldAxis: tuple3(worldAxis) };
312
+ };
313
+
314
+ const jointSnapshot = (joint: NativeImpulseJoint): PhysicsJointSnapshot => {
315
+ const body1 = joint.body1();
316
+ const body2 = joint.body2();
317
+ const anchor1 = joint.anchor1();
318
+ const anchor2 = joint.anchor2();
319
+ const type = jointType(joint.type());
320
+ return {
321
+ id: String(joint.handle),
322
+ type,
323
+ body1: String(body1.handle),
324
+ body2: String(body2.handle),
325
+ anchor1: tuple3(anchor1),
326
+ anchor2: tuple3(anchor2),
327
+ worldAnchor1: worldAnchor(body1, anchor1),
328
+ worldAnchor2: worldAnchor(body2, anchor2),
329
+ body1Position: tuple3(body1.translation()),
330
+ body2Position: tuple3(body2.translation()),
331
+ body1Rotation: tuple4(body1.rotation()),
332
+ body2Rotation: tuple4(body2.rotation()),
333
+ ...jointAxis(joint, type, body1),
334
+ ...jointLimits(joint, type),
335
+ contactsEnabled: joint.contactsEnabled(),
336
+ };
337
+ };
338
+
339
+ const joints = (nodeId: string): PhysicsJointSnapshot[] => {
340
+ const body = bodyFor(nodeId) as RapierRigidBody | undefined;
341
+ if (!body) return [];
342
+ const result: PhysicsJointSnapshot[] = [];
343
+ world.impulseJoints.forEachJointHandleAttachedToRigidBody(body.handle, (handle) => {
344
+ const joint = world.impulseJoints.get(handle);
345
+ if (joint) result.push(jointSnapshot(joint));
346
+ });
347
+ return result.sort((a, b) => Number(a.id) - Number(b.id));
348
+ };
349
+
350
+ const previewJointAnchor = (
351
+ jointId: string,
352
+ endpoint: 0 | 1,
353
+ anchor: readonly [number, number, number],
354
+ ): void => {
355
+ const joint = world.impulseJoints.get(Number(jointId));
356
+ if (!joint) return;
357
+ const value = { x: anchor[0], y: anchor[1], z: anchor[2] };
358
+ if (endpoint === 0) joint.setAnchor1(value);
359
+ else joint.setAnchor2(value);
126
360
  };
127
361
 
128
362
  const adapter: PhysicsAdapter = {
129
363
  ...createRapierBodyEditing(bodyFor),
130
364
  setDebugDrawEnabled: (enabled) => onDebugChange(enabled),
365
+ colliders,
366
+ previewCollider,
367
+ joints,
368
+ previewJointAnchor,
131
369
  };
132
370
  registerSystemAdapter('physics', adapter);
133
371
  return () => registerSystemAdapter('physics', undefined);
134
- }, [ctx, registerSystemAdapter, rigidBodyStates, onDebugChange]);
372
+ }, [colliderStates, ctx, onDebugChange, rapier, registerSystemAdapter, rigidBodyStates, world]);
135
373
 
136
374
  return null;
137
375
  }