feat(world): Orte-Liste mit Lage, Radien und Kollisionsmaterialien
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6c291c4106
commit
62dbbe41d2
2 files changed
+207
No files matched your search
@@ -0,0 +1,99 @@
|
||||
// Orte: fest platzierte Modelle aus dem Editor three-d-stage, getrennt vom Chunk-Streaming
|
||||
// (docs/superpowers/specs/2026-09-14-hood-yard-design.md). Aufbereitet werden sie mit
|
||||
// tools/assets/places.mjs; hier stehen Lage, Laden und Kollision.
|
||||
import { worldToThree } from "./coords.js";
|
||||
|
||||
// Chunks laden bei 400 m und bauen bei 600 m ab, gemessen zu ihrem Mittelpunkt (ChunkStreamer.update).
|
||||
// Der fernste Chunk-Mittelpunkt unter dem Hood Yard liegt 148 m von dessen Mitte entfernt: erschiene
|
||||
// der Hof schon bei 400 m, stünde er vom Startpunkt aus über dem Meer. Mit 250 m und 450 m ist sein
|
||||
// Boden geladen, solange er in der Szene ist (tests/placesSite.test.js prüft das für jeden Ort).
|
||||
// Heruntergeladen wird früher, damit das Einsetzen nicht auf die Datei wartet.
|
||||
export const PLACE_PRELOAD_RADIUS = 600;
|
||||
export const PLACE_SHOW_RADIUS = 250;
|
||||
export const PLACE_HIDE_RADIUS = 450;
|
||||
|
||||
/**
|
||||
* at: Welt-x und Welt-y in Metern, wie auf der Minimap — nie three-z. rotation: Grad gegen den
|
||||
* Uhrzeigersinn, von oben mit Norden oben (wie add_building in corrections.toml). lift: Meter auf die
|
||||
* Modellwurzel. bounds: Außenmaße in Modellkoordinaten (x, z); das Spiel braucht sie nicht, die Tests
|
||||
* prüfen damit Datei (placesAsset.test.js) und Lage (placesSite.test.js) gegeneinander.
|
||||
*/
|
||||
export const PLACES = [
|
||||
{
|
||||
id: "hood-yard",
|
||||
url: "./models/places/hood-yard.glb",
|
||||
// Übergang Container -> Schuppen an derselben Stelle wie im Satellitenbild des Nutzers.
|
||||
at: [1489, 1067.5],
|
||||
rotation: 0,
|
||||
// Die 34 cm dicke Kiesplatte liegt mit ihrer Unterseite auf 0; so ragt sie noch 6 cm heraus.
|
||||
lift: -0.28,
|
||||
bounds: { minX: -17.15, maxX: 17, minZ: -13.4, maxZ: 8.6 },
|
||||
},
|
||||
];
|
||||
|
||||
/** Materialien, die kollidieren. Alles andere — Gras, Blätter, Büsche, Kleinteile — läuft man durch. */
|
||||
export const SOLID_MATERIALS = [
|
||||
// Boden
|
||||
"gravel", "asphalt", "curbAsphalt", "padAsphalt", "puddleAsphalt", "cobble", "grass", "concrete",
|
||||
// Container
|
||||
"cont_a_side", "cont_a_end", "cont_b_side", "cont_b_end", "cont_c_side", "cont_c_end", "contFrame",
|
||||
// Schuppen und Tore
|
||||
"ironPlain", "ironTeal", "ironBlue", "galv", "rust", "darkTrim", "osb", "osbB",
|
||||
"rollershutter_window_01", "rollershutter_window_01_graffiti",
|
||||
// Zaun
|
||||
"chainlink_post", "chainlink_wire",
|
||||
// Masten, Fass, Kiste
|
||||
"modular_electricity_poles", "barrel_03", "plastic_container",
|
||||
// Bäume
|
||||
"quiver_tree_01_trunk", "quiver_tree_02",
|
||||
];
|
||||
const SOLID = new Set(SOLID_MATERIALS);
|
||||
|
||||
export function isSolidMaterial(name) {
|
||||
return SOLID.has(name);
|
||||
}
|
||||
|
||||
/** Lage eines Orts in three-Koordinaten. Positives rotationY dreht +x nach −z, also nach Norden. */
|
||||
export function placeTransform(place) {
|
||||
const { x, z } = worldToThree(place.at[0], place.at[1]);
|
||||
return { position: { x, y: place.lift, z }, rotationY: (place.rotation * Math.PI) / 180 };
|
||||
}
|
||||
|
||||
/** Abstand in der Ebene zwischen einer three-Position und der Mitte eines Orts. */
|
||||
export function placeDistance(position, place) {
|
||||
const { x, z } = worldToThree(place.at[0], place.at[1]);
|
||||
return Math.hypot(x - position.x, z - position.z);
|
||||
}
|
||||
|
||||
/** Wie wantedChunks: add innerhalb von show, remove erst jenseits von hide. */
|
||||
export function wantedPlaces(position, places, { show, hide, active }) {
|
||||
const add = [];
|
||||
const remove = [];
|
||||
for (const place of places) {
|
||||
const distance = placeDistance(position, place);
|
||||
if (distance <= show && !active.has(place.id)) add.push(place.id);
|
||||
if (distance > hide && active.has(place.id)) remove.push(place.id);
|
||||
}
|
||||
return { add, remove };
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt Lage und Schatten und nimmt nicht feste Meshes aus der Kollision (colliderBuild.ts im
|
||||
* three-player-controller überspringt userData.excludeFromCollider). Gibt zurück, ob ein Teil fest ist.
|
||||
*/
|
||||
export function prepareModel(root, place) {
|
||||
const { position, rotationY } = placeTransform(place);
|
||||
root.position.set(position.x, position.y, position.z);
|
||||
root.rotation.set(0, rotationY, 0);
|
||||
let solid = false;
|
||||
root.traverse((object) => {
|
||||
if (!object.isMesh) return;
|
||||
object.castShadow = true;
|
||||
object.receiveShadow = true;
|
||||
const materials = Array.isArray(object.material) ? object.material : [object.material];
|
||||
if (materials.some((material) => isSolidMaterial(material?.name))) solid = true;
|
||||
else object.userData.excludeFromCollider = true;
|
||||
});
|
||||
root.updateMatrixWorld(true);
|
||||
return solid;
|
||||
}
|
||||
Reference in new issue
Block a user