@vgai/engine 0.5.12 → 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.
- package/dist/adapter/adapter-module.d.ts +447 -0
- package/dist/adapter/adapter-module.d.ts.map +1 -0
- package/dist/adapter/adapter-module.js +308 -0
- package/dist/adapter/authoring.d.ts +357 -10
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/authoring.js +87 -1
- package/dist/adapter/constraint.d.ts +9 -3
- package/dist/adapter/constraint.d.ts.map +1 -1
- package/dist/adapter/finders/finder-result.d.ts +28 -0
- package/dist/adapter/finders/finder-result.d.ts.map +1 -0
- package/dist/adapter/finders/finder-result.js +10 -0
- package/dist/adapter/finders/index.d.ts +38 -0
- package/dist/adapter/finders/index.d.ts.map +1 -0
- package/dist/adapter/finders/index.js +41 -0
- package/dist/adapter/finders/prefabs-from-stories.d.ts +64 -0
- package/dist/adapter/finders/prefabs-from-stories.d.ts.map +1 -0
- package/dist/adapter/finders/prefabs-from-stories.js +92 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts +67 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +226 -0
- package/dist/adapter/first-party-systems.d.ts.map +1 -1
- package/dist/adapter/first-party-systems.js +29 -0
- package/dist/adapter/index.d.ts +7 -3
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +5 -2
- package/dist/adapter/ingest/contract-system-adapters.d.ts +24 -1
- package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -1
- package/dist/adapter/ingest/contract-system-adapters.js +100 -34
- package/dist/adapter/ingest/game-contract.d.ts +188 -2
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +107 -0
- package/dist/adapter/ingest/mount-readiness.d.ts +84 -0
- package/dist/adapter/ingest/mount-readiness.d.ts.map +1 -0
- package/dist/adapter/ingest/mount-readiness.js +81 -0
- package/dist/adapter/ingest/scene-capture.d.ts +91 -2
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +110 -18
- package/dist/adapter/ingest/structural-ids.d.ts +11 -5
- package/dist/adapter/ingest/structural-ids.d.ts.map +1 -1
- package/dist/adapter/ingest/structural-ids.js +18 -9
- package/dist/adapter/ingest/upstream-pin.d.ts +4 -2
- package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -1
- package/dist/adapter/ingest/upstream-pin.js +4 -2
- package/dist/adapter/ingest/visible-capture-window.d.ts +116 -0
- package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -0
- package/dist/adapter/ingest/visible-capture-window.js +141 -0
- package/dist/adapter/loop-gate-report.d.ts +3 -4
- package/dist/adapter/loop-gate-report.d.ts.map +1 -1
- package/dist/adapter/loop-gate-report.js +3 -4
- package/dist/adapter/root-adapter.d.ts +22 -2
- package/dist/adapter/root-adapter.d.ts.map +1 -1
- package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -1
- package/dist/adapter/setup-three-root-adapter.js +15 -0
- package/dist/adapter/system-adapter.d.ts +161 -2
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/adapter/system-adapter.js +18 -1
- package/dist/animation/camera-ownership.d.ts +4 -2
- package/dist/animation/camera-ownership.d.ts.map +1 -1
- package/dist/animation/camera-ownership.js +58 -1
- package/dist/animation/runtime-inspection.d.ts +70 -0
- package/dist/animation/runtime-inspection.d.ts.map +1 -0
- package/dist/animation/runtime-inspection.js +25 -0
- package/dist/animation/xstate-animation-binding.d.ts +9 -1
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
- package/dist/animation/xstate-animation-binding.js +48 -1
- package/dist/asset-formats/material.d.ts +1 -1
- package/dist/asset-formats/mesh.d.ts +1 -1
- package/dist/asset-formats/particles.d.ts +13 -0
- package/dist/asset-formats/particles.d.ts.map +1 -1
- package/dist/asset-formats/particles.js +28 -0
- package/dist/behavior/xstate-inspection.d.ts +24 -0
- package/dist/behavior/xstate-inspection.d.ts.map +1 -0
- package/dist/behavior/xstate-inspection.js +25 -0
- package/dist/core/game-loop.d.ts +7 -5
- package/dist/core/game-loop.d.ts.map +1 -1
- package/dist/core/game-loop.js +39 -18
- package/dist/core/types.d.ts +11 -12
- package/dist/core/types.d.ts.map +1 -1
- package/dist/data/data-asset.js +1 -1
- package/dist/dev/instruments.d.ts.map +1 -1
- package/dist/dev/instruments.js +5 -1
- package/dist/dev/render-debug-adapter.d.ts +19 -2
- package/dist/dev/render-debug-adapter.d.ts.map +1 -1
- package/dist/dev/render-debug-adapter.js +10 -0
- package/dist/ecs/user-data.d.ts +10 -2
- package/dist/ecs/user-data.d.ts.map +1 -1
- package/dist/ecs/user-data.js +6 -2
- package/dist/input/binding-identity.d.ts +10 -0
- package/dist/input/binding-identity.d.ts.map +1 -0
- package/dist/input/binding-identity.js +36 -0
- package/dist/input/input-manager.d.ts +18 -185
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +78 -68
- package/dist/manifest/load.d.ts +14 -0
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +6 -1
- package/dist/manifest/schema.d.ts +18 -0
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +64 -7
- package/dist/pixi/authoring.d.ts +27 -0
- package/dist/pixi/authoring.d.ts.map +1 -1
- package/dist/pixi/authoring.js +10 -3
- package/dist/pixi/index.d.ts +1 -1
- package/dist/pixi/index.d.ts.map +1 -1
- package/dist/pixi/index.js +1 -1
- package/dist/pixi/ingest.d.ts +8 -0
- package/dist/pixi/ingest.d.ts.map +1 -1
- package/dist/pixi/ingest.js +10 -1
- package/dist/pixi/render-pass-bracket.d.ts +56 -0
- package/dist/pixi/render-pass-bracket.d.ts.map +1 -0
- package/dist/pixi/render-pass-bracket.js +98 -0
- package/dist/pixi/scene-capture.d.ts +32 -1
- package/dist/pixi/scene-capture.d.ts.map +1 -1
- package/dist/pixi/scene-capture.js +21 -9
- package/dist/pixi/system-adapters.d.ts +29 -0
- package/dist/pixi/system-adapters.d.ts.map +1 -1
- package/dist/pixi/system-adapters.js +27 -0
- package/dist/render/particles-factory.d.ts +16 -2
- package/dist/render/particles-factory.d.ts.map +1 -1
- package/dist/render/particles-factory.js +88 -5
- package/dist/render/soft-particle-depth.d.ts +149 -0
- package/dist/render/soft-particle-depth.d.ts.map +1 -0
- package/dist/render/soft-particle-depth.js +305 -0
- package/dist/render/voxel-surface.d.ts +117 -0
- package/dist/render/voxel-surface.d.ts.map +1 -0
- package/dist/render/voxel-surface.js +263 -0
- package/dist/runtime/create-runtime.js +7 -0
- package/dist/runtime/debug-bridge.d.ts +15 -15
- package/dist/runtime/debug-bridge.d.ts.map +1 -1
- package/dist/runtime/debug-bridge.js +27 -26
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +1 -2
- package/dist/setup/setup-particles.d.ts.map +1 -1
- package/dist/setup/setup-particles.js +9 -0
- package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
- package/dist/world3d-react/r3f-adapter.js +42 -3
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +201 -11
- package/dist-config/config.js.map +1 -1
- package/package.json +4 -2
- package/schemas/engine-capabilities.json +12 -12
- package/schemas/vgai-project.schema.json +41 -2
- package/src/adapter/adapter-module.ts +607 -0
- package/src/adapter/authoring.ts +440 -10
- package/src/adapter/constraint.ts +10 -3
- package/src/adapter/finders/finder-result.ts +29 -0
- package/src/adapter/finders/index.ts +62 -0
- package/src/adapter/finders/prefabs-from-stories.ts +142 -0
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +304 -0
- package/src/adapter/first-party-systems.ts +30 -0
- package/src/adapter/index.ts +32 -1
- package/src/adapter/ingest/contract-system-adapters.ts +116 -32
- package/src/adapter/ingest/game-contract.ts +265 -2
- package/src/adapter/ingest/mount-readiness.ts +140 -0
- package/src/adapter/ingest/scene-capture.ts +199 -22
- package/src/adapter/ingest/structural-ids.ts +21 -9
- package/src/adapter/ingest/upstream-pin.ts +4 -2
- package/src/adapter/ingest/visible-capture-window.ts +213 -0
- package/src/adapter/loop-gate-report.ts +3 -4
- package/src/adapter/root-adapter.ts +22 -2
- package/src/adapter/setup-three-root-adapter.ts +16 -0
- package/src/adapter/system-adapter.ts +188 -2
- package/src/animation/camera-ownership.ts +68 -3
- package/src/animation/runtime-inspection.ts +104 -0
- package/src/animation/xstate-animation-binding.ts +53 -1
- package/src/asset-formats/particles.ts +31 -0
- package/src/behavior/xstate-inspection.ts +51 -0
- package/src/core/game-loop.ts +39 -17
- package/src/core/types.ts +11 -12
- package/src/data/data-asset.ts +1 -1
- package/src/dev/instruments.ts +5 -1
- package/src/dev/render-debug-adapter.ts +22 -2
- package/src/ecs/user-data.ts +10 -2
- package/src/input/binding-identity.ts +37 -0
- package/src/input/input-manager.ts +93 -68
- package/src/manifest/load.ts +23 -2
- package/src/manifest/schema.ts +76 -7
- package/src/pixi/authoring.ts +37 -5
- package/src/pixi/index.ts +2 -0
- package/src/pixi/ingest.ts +15 -2
- package/src/pixi/render-pass-bracket.ts +117 -0
- package/src/pixi/scene-capture.ts +63 -13
- package/src/pixi/system-adapters.ts +41 -0
- package/src/render/particles-factory.ts +89 -6
- package/src/render/soft-particle-depth.ts +336 -0
- package/src/render/voxel-surface.ts +358 -0
- package/src/runtime/create-runtime.ts +7 -0
- package/src/runtime/debug-bridge.ts +32 -31
- package/src/runtime/game.ts +1 -2
- package/src/setup/setup-particles.ts +9 -0
- package/src/world3d-react/r3f-adapter.tsx +48 -2
- package/src/world3d-react/rapier-physics-bridge.tsx +249 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-particles.d.ts","sourceRoot":"","sources":["../../src/setup/setup-particles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"setup-particles.d.ts","sourceRoot":"","sources":["../../src/setup/setup-particles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,eAAe,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,gBAAgB,CAanE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BatchedRenderer } from 'three.quarks';
|
|
2
|
+
import { farDepthTexture } from '../render/soft-particle-depth';
|
|
2
3
|
/**
|
|
3
4
|
* Creates the three.quarks BatchedRenderer for GPU particle systems.
|
|
4
5
|
*
|
|
@@ -11,6 +12,14 @@ import { BatchedRenderer } from 'three.quarks';
|
|
|
11
12
|
*/
|
|
12
13
|
export function setupParticles(scene) {
|
|
13
14
|
const batchedRenderer = new BatchedRenderer();
|
|
15
|
+
// Seed the soft-particle depth sampler BEFORE any batch exists, so every
|
|
16
|
+
// batch this renderer later builds inherits it (`BatchedRenderer.addSystem`
|
|
17
|
+
// applies `this.depthTexture` to each new batch). A `softParticles` batch
|
|
18
|
+
// whose `depthTexture` uniform is still `null` samples black and multiplies
|
|
19
|
+
// every particle away; the far-depth default makes that same batch render
|
|
20
|
+
// exactly as an unfaded one until a real depth pass supplies the scene's own.
|
|
21
|
+
// See `render/soft-particle-depth.ts`.
|
|
22
|
+
batchedRenderer.setDepthTexture(farDepthTexture());
|
|
14
23
|
scene.add(batchedRenderer);
|
|
15
24
|
return { batchedRenderer };
|
|
16
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r3f-adapter.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-adapter.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"r3f-adapter.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-adapter.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAUH,OAAO,EAA2B,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAoB,WAAW,EAAoB,MAAM,YAAY,CAAC;AAclF,OAAO,EAA4B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA2CvF,sEAAsE;AACtE,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;uBAEmB;IACnB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5B;;;;0CAIsC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD;;;iDAG6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;CACrD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,WAAW,CA6e9E"}
|
|
@@ -18,18 +18,49 @@
|
|
|
18
18
|
* project guarantees via its Vite `resolve.dedupe: ['three', 'react',
|
|
19
19
|
* 'react-dom']` (see `examples/r3f-first-party/vite.config.ts`).
|
|
20
20
|
*/
|
|
21
|
-
import { advance, createRoot, extend, events as pointerEvents, } from '@react-three/fiber';
|
|
22
|
-
import { createElement } from 'react';
|
|
21
|
+
import { advance, createRoot, extend, events as pointerEvents, useFrame, } from '@react-three/fiber';
|
|
22
|
+
import { createElement, Fragment, useEffect, useMemo } from 'react';
|
|
23
23
|
import { registerRenderVitals } from '../dev/register-render-vitals';
|
|
24
24
|
import { createRenderDebugAdapter, frameCaptureContextFor, } from '../dev/render-debug-adapter';
|
|
25
25
|
import { collectRenderMemory } from '../dev/render-memory';
|
|
26
26
|
import { RENDER_SUBMIT_PHASE } from '../dev/render-vitals';
|
|
27
27
|
import { createWebGLFrameCapture } from '../dev/webgl-frame-capture';
|
|
28
28
|
import { WorldProvider } from '../react/world-state';
|
|
29
|
+
import { createSoftParticleDepthPass } from '../render/soft-particle-depth';
|
|
29
30
|
import { devLayersEnabled } from '../runtime/dev-layers';
|
|
30
31
|
import { EngineBridge } from './engine-bridge';
|
|
31
32
|
import { applyWorldRendererConfig } from './renderer-config';
|
|
32
33
|
import { createR3FRootContext } from './world-context';
|
|
34
|
+
/**
|
|
35
|
+
* Scene depth for any soft-particle system this world mounted, drawn between
|
|
36
|
+
* gameplay and the draw.
|
|
37
|
+
*
|
|
38
|
+
* `useFrame`'s priority is the whole reason this is a component rather than an
|
|
39
|
+
* engine phase: `systems.run(dt)` below finishes BEFORE `advance()`, and
|
|
40
|
+
* `advance()` is where every `useFrame` gameplay callback moves the frame's
|
|
41
|
+
* transforms — depth taken from an engine phase would be one frame stale.
|
|
42
|
+
* Priority `0` puts it after every negative-priority callback (fiber sorts
|
|
43
|
+
* subscribers by priority) and before both a positive-priority composer and
|
|
44
|
+
* fiber's own `state.gl.render` — and, unlike a positive priority, it does NOT
|
|
45
|
+
* flip fiber into manual-render mode (`internal.priority` only counts
|
|
46
|
+
* subscribers with `priority > 0`), so mounting it can never stop an ordinary
|
|
47
|
+
* world from drawing itself.
|
|
48
|
+
*
|
|
49
|
+
* Rendered as the LAST child of the adapter's tree so its subscription lands
|
|
50
|
+
* after the world's own (React completes children before siblings, and fiber's
|
|
51
|
+
* sort is stable) — the tie-break among priority-0 callbacks is insertion
|
|
52
|
+
* order.
|
|
53
|
+
*
|
|
54
|
+
* Costs nothing while no soft-particle system is armed — see
|
|
55
|
+
* `../render/soft-particle-depth.ts` for what "armed" means and what the pass
|
|
56
|
+
* spends when it is.
|
|
57
|
+
*/
|
|
58
|
+
function SoftParticleDepth() {
|
|
59
|
+
const pass = useMemo(() => createSoftParticleDepthPass(), []);
|
|
60
|
+
useEffect(() => () => pass.dispose(), [pass]);
|
|
61
|
+
useFrame(({ gl, scene, camera }) => pass.render(gl, scene, camera), 0);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
33
64
|
/**
|
|
34
65
|
* Build a `RootAdapter` that mounts `options.content` through
|
|
35
66
|
* react-three-fiber, gated entirely by the host's own loop and rendering
|
|
@@ -214,7 +245,10 @@ export function createR3FAdapter(options) {
|
|
|
214
245
|
// hosts, so the provider is conditional and those hooks stay the inert
|
|
215
246
|
// no-ops they already document themselves to be.
|
|
216
247
|
const game = runtime.ctx.game;
|
|
217
|
-
|
|
248
|
+
// `SoftParticleDepth` trails the world's own tree deliberately — see its
|
|
249
|
+
// docblock for why the subscription must land last.
|
|
250
|
+
const world = createElement(Fragment, null, content, createElement(SoftParticleDepth, { key: 'vgai-soft-particle-depth' }));
|
|
251
|
+
root.render(createElement(EngineBridge.Provider, { value: bridge }, game ? createElement(WorldProvider, { game }, world) : world));
|
|
218
252
|
// A reconcile-time crash (e.g. a missing `extend` catalogue entry)
|
|
219
253
|
// surfaces as an uncaught window error and `onCreated` never fires —
|
|
220
254
|
// without this guard, `mount()` would await `statePromise` FOREVER and
|
|
@@ -401,6 +435,11 @@ export function createR3FAdapter(options) {
|
|
|
401
435
|
return live().camera;
|
|
402
436
|
},
|
|
403
437
|
drivesOwnLoop: false,
|
|
438
|
+
// REPORTED for a host that draws this scene with a renderer other than
|
|
439
|
+
// the one it mounted us against — see `MountedThreeRoot.rendererConfig`.
|
|
440
|
+
// It is the same object already applied to `host.renderer` above; the
|
|
441
|
+
// report never applies anything itself.
|
|
442
|
+
rendererConfig: rendererConfig,
|
|
404
443
|
// Adapter surface: `debug` (the shared game registry) and, when a real
|
|
405
444
|
// WebGL2 context exists, `renderDebug` are engine-seeded. Game code
|
|
406
445
|
// adds its own capabilities via `ctx.registerSystemAdapter` exactly
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rapier-physics-bridge.d.ts","sourceRoot":"","sources":["../../src/world3d-react/rapier-physics-bridge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;
|
|
1
|
+
{"version":3,"file":"rapier-physics-bridge.d.ts","sourceRoot":"","sources":["../../src/world3d-react/rapier-physics-bridge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAkBH,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAClD;AAkCD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,aAAa,EAAE,EAAE,wBAAwB,GAAG,IAAI,CA0PrF"}
|
|
@@ -60,8 +60,32 @@
|
|
|
60
60
|
*/
|
|
61
61
|
import { useRapier } from '@react-three/rapier';
|
|
62
62
|
import { useEffect } from 'react';
|
|
63
|
+
import * as THREE from 'three';
|
|
63
64
|
import { createRapierBodyEditing, } from '../adapter/rapier-physics-adapter';
|
|
64
65
|
import { useGameContext } from './engine-bridge';
|
|
66
|
+
function lateEntityObject(states, nodeId) {
|
|
67
|
+
let found;
|
|
68
|
+
for (const state of states) {
|
|
69
|
+
state.object.traverse((candidate) => {
|
|
70
|
+
if (!found && candidate.userData['entityId'] === nodeId)
|
|
71
|
+
found = candidate;
|
|
72
|
+
});
|
|
73
|
+
if (found)
|
|
74
|
+
return found;
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
function owningBody(object, states) {
|
|
79
|
+
const byObject = new Map();
|
|
80
|
+
for (const state of states)
|
|
81
|
+
byObject.set(state.object, state.rigidBody);
|
|
82
|
+
for (let node = object; node; node = node.parent) {
|
|
83
|
+
const body = byObject.get(node);
|
|
84
|
+
if (body)
|
|
85
|
+
return body;
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
65
89
|
/**
|
|
66
90
|
* Register this R3F world's `@react-three/rapier` physics as the game's
|
|
67
91
|
* `SystemAdapters.physics`. Mount it once, INSIDE `<Physics>` (it reads
|
|
@@ -69,7 +93,7 @@ import { useGameContext } from './engine-bridge';
|
|
|
69
93
|
*/
|
|
70
94
|
export function RapierPhysicsBridge({ onDebugChange }) {
|
|
71
95
|
const ctx = useGameContext();
|
|
72
|
-
const { rigidBodyStates } = useRapier();
|
|
96
|
+
const { colliderStates, rapier, rigidBodyStates, world } = useRapier();
|
|
73
97
|
const registerSystemAdapter = ctx.registerSystemAdapter;
|
|
74
98
|
useEffect(() => {
|
|
75
99
|
if (!registerSystemAdapter)
|
|
@@ -96,25 +120,191 @@ export function RapierPhysicsBridge({ onDebugChange }) {
|
|
|
96
120
|
* `setSceneCamera`), so a destructured copy would be permanently absent.
|
|
97
121
|
*/
|
|
98
122
|
const bodyFor = (nodeId) => {
|
|
99
|
-
const
|
|
123
|
+
const indexed = ctx.sceneIndex?.byEntityId(nodeId);
|
|
124
|
+
const object = indexed ?? lateEntityObject(rigidBodyStates.values(), nodeId);
|
|
125
|
+
if (!indexed && object) {
|
|
126
|
+
// The editor assigns its stable authoring entity ids AFTER Fiber's
|
|
127
|
+
// first commit, while the root's SceneIndex is constructed during
|
|
128
|
+
// that commit. Resolve the one late id inside the library-owned body
|
|
129
|
+
// subtrees and immediately reindex it; subsequent gestures take the
|
|
130
|
+
// normal indexed path. This is the SceneIndex's documented escape
|
|
131
|
+
// hatch for a userData id written after construction, not a parallel
|
|
132
|
+
// scene mirror.
|
|
133
|
+
ctx.sceneIndex?.reindex(object);
|
|
134
|
+
}
|
|
100
135
|
if (!object)
|
|
101
136
|
return undefined;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return
|
|
137
|
+
return owningBody(object, rigidBodyStates.values());
|
|
138
|
+
};
|
|
139
|
+
const colliderShape = (collider) => {
|
|
140
|
+
switch (collider.shapeType()) {
|
|
141
|
+
case rapier.ShapeType.Cuboid: {
|
|
142
|
+
const halfExtents = collider.halfExtents();
|
|
143
|
+
return { type: 'cuboid', halfExtents: [halfExtents.x, halfExtents.y, halfExtents.z] };
|
|
144
|
+
}
|
|
145
|
+
case rapier.ShapeType.Ball:
|
|
146
|
+
return { type: 'ball', radius: collider.radius() };
|
|
147
|
+
case rapier.ShapeType.Capsule:
|
|
148
|
+
return {
|
|
149
|
+
type: 'capsule',
|
|
150
|
+
halfHeight: collider.halfHeight(),
|
|
151
|
+
radius: collider.radius(),
|
|
152
|
+
};
|
|
153
|
+
default:
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const colliders = (nodeId) => {
|
|
158
|
+
const body = bodyFor(nodeId);
|
|
159
|
+
if (!body)
|
|
160
|
+
return [];
|
|
161
|
+
const result = [];
|
|
162
|
+
for (const state of colliderStates.values()) {
|
|
163
|
+
if (state.collider.parent()?.handle !== body.handle)
|
|
164
|
+
continue;
|
|
165
|
+
const shape = colliderShape(state.collider);
|
|
166
|
+
if (!shape)
|
|
167
|
+
continue;
|
|
168
|
+
state.object.updateWorldMatrix(true, false);
|
|
169
|
+
const position = state.collider.translation();
|
|
170
|
+
const rotation = state.collider.rotation();
|
|
171
|
+
const scale = state.object.getWorldScale(new THREE.Vector3());
|
|
172
|
+
result.push({
|
|
173
|
+
id: String(state.collider.handle),
|
|
174
|
+
shape,
|
|
175
|
+
position: [position.x, position.y, position.z],
|
|
176
|
+
rotation: [rotation.x, rotation.y, rotation.z, rotation.w],
|
|
177
|
+
scale: [Math.abs(scale.x), Math.abs(scale.y), Math.abs(scale.z)],
|
|
178
|
+
sensor: state.collider.isSensor(),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return result.sort((a, b) => Number(a.id) - Number(b.id));
|
|
182
|
+
};
|
|
183
|
+
const previewCollider = (colliderId, shape) => {
|
|
184
|
+
const collider = [...colliderStates.values()].find((state) => String(state.collider.handle) === colliderId)?.collider;
|
|
185
|
+
if (!collider || colliderShape(collider)?.type !== shape.type)
|
|
186
|
+
return;
|
|
187
|
+
switch (shape.type) {
|
|
188
|
+
case 'cuboid':
|
|
189
|
+
collider.setHalfExtents({
|
|
190
|
+
x: shape.halfExtents[0],
|
|
191
|
+
y: shape.halfExtents[1],
|
|
192
|
+
z: shape.halfExtents[2],
|
|
193
|
+
});
|
|
194
|
+
break;
|
|
195
|
+
case 'ball':
|
|
196
|
+
collider.setRadius(shape.radius);
|
|
197
|
+
break;
|
|
198
|
+
case 'capsule':
|
|
199
|
+
collider.setHalfHeight(shape.halfHeight);
|
|
200
|
+
collider.setRadius(shape.radius);
|
|
201
|
+
break;
|
|
109
202
|
}
|
|
110
|
-
|
|
203
|
+
};
|
|
204
|
+
const jointType = (type) => {
|
|
205
|
+
switch (type) {
|
|
206
|
+
case rapier.JointType.Revolute:
|
|
207
|
+
return 'revolute';
|
|
208
|
+
case rapier.JointType.Fixed:
|
|
209
|
+
return 'fixed';
|
|
210
|
+
case rapier.JointType.Prismatic:
|
|
211
|
+
return 'prismatic';
|
|
212
|
+
case rapier.JointType.Rope:
|
|
213
|
+
return 'rope';
|
|
214
|
+
case rapier.JointType.Spring:
|
|
215
|
+
return 'spring';
|
|
216
|
+
case rapier.JointType.Spherical:
|
|
217
|
+
return 'spherical';
|
|
218
|
+
default:
|
|
219
|
+
return 'generic';
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const tuple3 = (value) => [
|
|
223
|
+
value.x,
|
|
224
|
+
value.y,
|
|
225
|
+
value.z,
|
|
226
|
+
];
|
|
227
|
+
const tuple4 = (value) => [value.x, value.y, value.z, value.w];
|
|
228
|
+
const worldAnchor = (body, anchor) => {
|
|
229
|
+
const point = new THREE.Vector3(anchor.x, anchor.y, anchor.z)
|
|
230
|
+
.applyQuaternion(new THREE.Quaternion(...tuple4(body.rotation())))
|
|
231
|
+
.add(new THREE.Vector3(...tuple3(body.translation())));
|
|
232
|
+
return tuple3(point);
|
|
233
|
+
};
|
|
234
|
+
const jointLimits = (joint, type) => {
|
|
235
|
+
if (type !== 'revolute' && type !== 'prismatic')
|
|
236
|
+
return {};
|
|
237
|
+
const unit = joint;
|
|
238
|
+
return unit.limitsEnabled()
|
|
239
|
+
? { limits: { min: unit.limitsMin(), max: unit.limitsMax() } }
|
|
240
|
+
: {};
|
|
241
|
+
};
|
|
242
|
+
const jointAxis = (joint, type, body) => {
|
|
243
|
+
if (type !== 'revolute' && type !== 'prismatic')
|
|
244
|
+
return {};
|
|
245
|
+
const frame = joint.frameX1();
|
|
246
|
+
const axis = new THREE.Vector3(1, 0, 0).applyQuaternion(new THREE.Quaternion(frame.x, frame.y, frame.z, frame.w));
|
|
247
|
+
const worldAxis = axis
|
|
248
|
+
.clone()
|
|
249
|
+
.applyQuaternion(new THREE.Quaternion(...tuple4(body.rotation())));
|
|
250
|
+
return { axis: tuple3(axis), worldAxis: tuple3(worldAxis) };
|
|
251
|
+
};
|
|
252
|
+
const jointSnapshot = (joint) => {
|
|
253
|
+
const body1 = joint.body1();
|
|
254
|
+
const body2 = joint.body2();
|
|
255
|
+
const anchor1 = joint.anchor1();
|
|
256
|
+
const anchor2 = joint.anchor2();
|
|
257
|
+
const type = jointType(joint.type());
|
|
258
|
+
return {
|
|
259
|
+
id: String(joint.handle),
|
|
260
|
+
type,
|
|
261
|
+
body1: String(body1.handle),
|
|
262
|
+
body2: String(body2.handle),
|
|
263
|
+
anchor1: tuple3(anchor1),
|
|
264
|
+
anchor2: tuple3(anchor2),
|
|
265
|
+
worldAnchor1: worldAnchor(body1, anchor1),
|
|
266
|
+
worldAnchor2: worldAnchor(body2, anchor2),
|
|
267
|
+
body1Position: tuple3(body1.translation()),
|
|
268
|
+
body2Position: tuple3(body2.translation()),
|
|
269
|
+
body1Rotation: tuple4(body1.rotation()),
|
|
270
|
+
body2Rotation: tuple4(body2.rotation()),
|
|
271
|
+
...jointAxis(joint, type, body1),
|
|
272
|
+
...jointLimits(joint, type),
|
|
273
|
+
contactsEnabled: joint.contactsEnabled(),
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
const joints = (nodeId) => {
|
|
277
|
+
const body = bodyFor(nodeId);
|
|
278
|
+
if (!body)
|
|
279
|
+
return [];
|
|
280
|
+
const result = [];
|
|
281
|
+
world.impulseJoints.forEachJointHandleAttachedToRigidBody(body.handle, (handle) => {
|
|
282
|
+
const joint = world.impulseJoints.get(handle);
|
|
283
|
+
if (joint)
|
|
284
|
+
result.push(jointSnapshot(joint));
|
|
285
|
+
});
|
|
286
|
+
return result.sort((a, b) => Number(a.id) - Number(b.id));
|
|
287
|
+
};
|
|
288
|
+
const previewJointAnchor = (jointId, endpoint, anchor) => {
|
|
289
|
+
const joint = world.impulseJoints.get(Number(jointId));
|
|
290
|
+
if (!joint)
|
|
291
|
+
return;
|
|
292
|
+
const value = { x: anchor[0], y: anchor[1], z: anchor[2] };
|
|
293
|
+
if (endpoint === 0)
|
|
294
|
+
joint.setAnchor1(value);
|
|
295
|
+
else
|
|
296
|
+
joint.setAnchor2(value);
|
|
111
297
|
};
|
|
112
298
|
const adapter = {
|
|
113
299
|
...createRapierBodyEditing(bodyFor),
|
|
114
300
|
setDebugDrawEnabled: (enabled) => onDebugChange(enabled),
|
|
301
|
+
colliders,
|
|
302
|
+
previewCollider,
|
|
303
|
+
joints,
|
|
304
|
+
previewJointAnchor,
|
|
115
305
|
};
|
|
116
306
|
registerSystemAdapter('physics', adapter);
|
|
117
307
|
return () => registerSystemAdapter('physics', undefined);
|
|
118
|
-
}, [ctx, registerSystemAdapter, rigidBodyStates,
|
|
308
|
+
}, [colliderStates, ctx, onDebugChange, rapier, registerSystemAdapter, rigidBodyStates, world]);
|
|
119
309
|
return null;
|
|
120
310
|
}
|