87 lines
2.3 KiB
JavaScript
87 lines
2.3 KiB
JavaScript
|
|
import * as THREE from 'three';
|
||
|
|
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Loads GLB objects from a config list into the scene and returns a registry
|
||
|
|
* keyed by name. Positions are returned live via getPosition() so the AI
|
||
|
|
* always sees the current world location even if the object is moved later.
|
||
|
|
*/
|
||
|
|
|
||
|
|
const loader = new GLTFLoader();
|
||
|
|
|
||
|
|
async function loadOne(entry) {
|
||
|
|
const gltf = await loader.loadAsync(entry.url);
|
||
|
|
const object3d = gltf.scene;
|
||
|
|
|
||
|
|
const [px, py, pz] = entry.position ?? [0, 0, 0];
|
||
|
|
const [rx, ry, rz] = entry.rotation ?? [0, 0, 0];
|
||
|
|
const scale = entry.scale ?? [1, 1, 1];
|
||
|
|
const [sx, sy, sz] = Array.isArray(scale) ? scale : [scale, scale, scale];
|
||
|
|
|
||
|
|
object3d.position.set(px, py, pz);
|
||
|
|
object3d.rotation.set(rx, ry, rz);
|
||
|
|
object3d.scale.set(sx, sy, sz);
|
||
|
|
object3d.name = entry.name;
|
||
|
|
|
||
|
|
object3d.traverse((child) => {
|
||
|
|
if (child.isMesh) {
|
||
|
|
child.castShadow = true;
|
||
|
|
child.receiveShadow = true;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
return object3d;
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function loadSceneObjects(scene, config) {
|
||
|
|
const registry = new Map();
|
||
|
|
|
||
|
|
for (const entry of config) {
|
||
|
|
try {
|
||
|
|
const object3d = await loadOne(entry);
|
||
|
|
scene.add(object3d);
|
||
|
|
|
||
|
|
registry.set(entry.name, {
|
||
|
|
name: entry.name,
|
||
|
|
url: entry.url,
|
||
|
|
object3d,
|
||
|
|
getPosition() {
|
||
|
|
const v = new THREE.Vector3();
|
||
|
|
object3d.getWorldPosition(v);
|
||
|
|
return { x: v.x, y: v.y, z: v.z };
|
||
|
|
},
|
||
|
|
getRotation() {
|
||
|
|
return {
|
||
|
|
x: object3d.rotation.x,
|
||
|
|
y: object3d.rotation.y,
|
||
|
|
z: object3d.rotation.z,
|
||
|
|
};
|
||
|
|
},
|
||
|
|
getScale() {
|
||
|
|
return {
|
||
|
|
x: object3d.scale.x,
|
||
|
|
y: object3d.scale.y,
|
||
|
|
z: object3d.scale.z,
|
||
|
|
};
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const p = registry.get(entry.name).getPosition();
|
||
|
|
console.log(`📦 Loaded "${entry.name}" at (${p.x.toFixed(2)}, ${p.y.toFixed(2)}, ${p.z.toFixed(2)})`);
|
||
|
|
} catch (err) {
|
||
|
|
console.error(`Failed to load scene object "${entry.name}" from ${entry.url}:`, err);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return registry;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Convenience: dump all object positions as plain JSON (for AI context). */
|
||
|
|
export function describeObjects(registry) {
|
||
|
|
const out = {};
|
||
|
|
for (const [name, obj] of registry) {
|
||
|
|
out[name] = { position: obj.getPosition() };
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
}
|