Add paths functionality to scene document and related components

This commit is contained in:
2026-04-13 21:19:30 +02:00
parent 94dfc1be1e
commit 0eac096de5
12 changed files with 753 additions and 28 deletions

View File

@@ -397,17 +397,17 @@ describe("buildRuntimeSceneFromDocument", () => {
},
max: {
x: 4,
y: 0,
y: 1.8,
z: 4
},
center: {
x: 0,
y: -0.5,
y: 0.4,
z: 0
},
size: {
x: 8,
y: 1,
y: 2.8,
z: 8
}
});
@@ -470,7 +470,7 @@ describe("buildRuntimeSceneFromDocument", () => {
modelAssetId: modelAsset.id,
collider: {
mode: "capsule",
radius: 0.35,
radius: 0.3,
height: 1.8,
eyeHeight: 1.6
}
@@ -659,20 +659,20 @@ describe("buildRuntimeSceneFromDocument", () => {
},
shape: {
mode: "capsule",
radius: 0.35,
radius: 0.3,
height: 1.8,
eyeHeight: 1.6
},
worldBounds: {
min: {
x: -1.35,
x: -1.3,
y: 0,
z: -2.35
z: -2.3
},
max: {
x: -0.65,
x: -0.7,
y: 1.8,
z: -1.65
z: -1.7
}
}
});

View File

@@ -4,7 +4,10 @@ import { BoxGeometry, PlaneGeometry } from "three";
import { createModelInstance } from "../../src/assets/model-instances";
import { createBoxBrush } from "../../src/document/brushes";
import { createEmptySceneDocument } from "../../src/document/scene-document";
import { createPlayerStartEntity } from "../../src/entities/entity-instances";
import {
createNpcEntity,
createPlayerStartEntity
} from "../../src/entities/entity-instances";
import { RapierCollisionWorld } from "../../src/runtime-three/rapier-collision-world";
import { buildRuntimeSceneFromDocument } from "../../src/runtime-three/runtime-scene-build";
import { createFixtureLoadedModelAssetFromGeometry } from "../helpers/model-collider-fixtures";
@@ -99,6 +102,75 @@ describe("RapierCollisionWorld", () => {
}
});
it("blocks first-person motion against authored NPC colliders", async () => {
const floorBrush = createBoxBrush({
id: "brush-floor-npc-collision",
center: {
x: 0,
y: -0.5,
z: 0
},
size: {
x: 10,
y: 1,
z: 10
}
});
const npc = createNpcEntity({
id: "entity-npc-guard",
actorId: "actor-gate-guard",
position: {
x: 2,
y: 0,
z: 0
},
collider: {
mode: "box",
eyeHeight: 1.6,
boxSize: {
x: 1,
y: 1.8,
z: 1
}
}
});
const runtimeScene = buildRuntimeSceneFromDocument({
...createEmptySceneDocument({ name: "NPC Collision Scene" }),
brushes: {
[floorBrush.id]: floorBrush
},
entities: {
[npc.id]: npc
}
});
const collisionWorld = await RapierCollisionWorld.create(
runtimeScene.colliders,
runtimeScene.playerCollider
);
try {
const blocked = collisionWorld.resolveFirstPersonMotion(
{
x: 0,
y: 0,
z: 0
},
{
x: 3,
y: 0,
z: 0
},
runtimeScene.playerCollider
);
expect(blocked.feetPosition.x).toBeLessThan(1.21);
expect(blocked.feetPosition.y).toBeLessThan(0.02);
expect(blocked.collidedAxes.x).toBe(true);
} finally {
collisionWorld.dispose();
}
});
it("initializes and resolves first-person motion against terrain heightfield colliders", async () => {
const terrainGeometry = new PlaneGeometry(8, 8, 4, 4);
terrainGeometry.rotateX(-Math.PI / 2);

View File

@@ -444,7 +444,7 @@ describe("validateSceneDocument", () => {
actorId: "actor-town-guard",
collider: {
mode: "box",
eyeHeight: 2,
eyeHeight: 1.2,
boxSize: {
x: 0.7,
y: 1.2,
@@ -452,6 +452,7 @@ describe("validateSceneDocument", () => {
}
}
});
invalidColliderNpc.collider.eyeHeight = 2;
const validation = validateSceneDocument({
...createEmptySceneDocument(),

View File

@@ -1662,6 +1662,7 @@ describe("scene document JSON", () => {
const migratedDocument = migrateSceneDocument({
version: NPC_ENTITY_FOUNDATION_SCENE_DOCUMENT_VERSION,
name: "NPC Collider Migration",
time: createDefaultProjectTimeSettings(),
world: createEmptySceneDocument().world,
materials: createEmptySceneDocument().materials,
textures: {},