auto-git:

[add] src/geometry/model-instance-collider-generation.ts
 [add] src/runtime-three/rapier-collision-world.ts
 [change] AGENTS.md
 [change] CHAT_CONTEXT.md
 [change] architecture.md
 [change] package.json
 [change] prompts-lite.txt
 [change] prompts.txt
 [change] roadmap.md
 [change] src/assets/model-instances.ts
 [change] src/document/migrate-scene-document.ts
 [change] src/document/scene-document-validation.ts
 [change] src/document/scene-document.ts
 [change] src/runtime-three/first-person-navigation-controller.ts
 [change] src/runtime-three/navigation-controller.ts
 [change] src/runtime-three/runtime-host.ts
 [change] src/runtime-three/runtime-scene-build.ts
 [change] src/runtime-three/runtime-scene-validation.ts
 [change] src/viewport-three/viewport-host.ts
 [change] testing.md
This commit is contained in:
2026-04-04 07:51:38 +02:00
parent d82e37861e
commit 3d1dd3fe63
20 changed files with 1553 additions and 31 deletions

View File

@@ -40,6 +40,8 @@ import {
import { FirstPersonNavigationController } from "./first-person-navigation-controller";
import type { FirstPersonTelemetry, NavigationController, RuntimeControllerContext } from "./navigation-controller";
import { FIRST_PERSON_PLAYER_SHAPE } from "./player-collision";
import { RapierCollisionWorld } from "./rapier-collision-world";
import { RuntimeInteractionSystem, type RuntimeInteractionDispatcher, type RuntimeInteractionPrompt } from "./runtime-interaction-system";
import { RuntimeAudioSystem } from "./runtime-audio-system";
import { OrbitVisitorNavigationController } from "./orbit-visitor-navigation-controller";
@@ -85,6 +87,8 @@ export class RuntimeHost {
private readonly controllerContext: RuntimeControllerContext;
private readonly renderer: WebGLRenderer | null;
private runtimeScene: RuntimeSceneDefinition | null = null;
private collisionWorld: RapierCollisionWorld | null = null;
private collisionWorldRequestId = 0;
private currentWorld: RuntimeSceneDefinition["world"] | null = null;
private currentAdvancedRenderingSettings: AdvancedRenderingSettings | null = null;
private advancedRenderingComposer: EffectComposer | null = null;
@@ -131,6 +135,7 @@ export class RuntimeHost {
return this.runtimeScene;
},
resolveFirstPersonMotion: (feetPosition, motion, shape) => this.collisionWorld?.resolveFirstPersonMotion(feetPosition, motion, shape) ?? null,
setRuntimeMessage: (message) => {
if (message === this.currentRuntimeMessage) {
return;
@@ -171,6 +176,7 @@ export class RuntimeHost {
this.rebuildLocalLights(runtimeScene.localLights);
this.rebuildBrushMeshes(runtimeScene.brushes);
this.rebuildModelInstances(runtimeScene.modelInstances);
void this.rebuildCollisionWorld(runtimeScene.colliders);
this.audioSystem.loadScene(runtimeScene);
}
@@ -244,6 +250,8 @@ export class RuntimeHost {
this.clearLocalLights();
this.clearBrushMeshes();
this.clearModelInstances();
this.collisionWorldRequestId += 1;
this.clearCollisionWorld();
this.audioSystem.dispose();
this.advancedRenderingComposer?.dispose();
this.advancedRenderingComposer = null;
@@ -306,6 +314,36 @@ export class RuntimeHost {
this.applyShadowState();
}
private async rebuildCollisionWorld(colliders: RuntimeSceneDefinition["colliders"]) {
const requestId = ++this.collisionWorldRequestId;
this.clearCollisionWorld();
try {
const nextCollisionWorld = await RapierCollisionWorld.create(colliders, FIRST_PERSON_PLAYER_SHAPE);
if (requestId !== this.collisionWorldRequestId) {
nextCollisionWorld.dispose();
return;
}
this.collisionWorld = nextCollisionWorld;
} catch (error) {
if (requestId !== this.collisionWorldRequestId) {
return;
}
const message = error instanceof Error ? error.message : "Runner collision initialization failed.";
this.currentRuntimeMessage = `Runner collision initialization failed: ${message}`;
this.runtimeMessageHandler?.(this.currentRuntimeMessage);
}
}
private clearCollisionWorld() {
this.collisionWorld?.dispose();
this.collisionWorld = null;
}
private syncAdvancedRenderingComposer(settings: AdvancedRenderingSettings) {
if (this.renderer === null) {
return;