Add Spirit class and refactor scene initialization

This commit is contained in:
2025-05-28 02:58:22 +02:00
parent 83c007b9d0
commit 47ff134bb5

View File

@@ -186,6 +186,89 @@ const rotatingLights = [], counterRotatingLights = [];
const LIGHT_RADIUS = 1; const LIGHT_RADIUS = 1;
const clock = new THREE.Clock(); const clock = new THREE.Clock();
// ---- Spirit-Klasse ----
class Spirit {
constructor(scene, gltfScene, info) {
this.scene = scene;
this.grp = new THREE.Group();
this.gltf = gltfScene;
this.info = info || {};
this.clock = new THREE.Clock();
this.isFading = true;
this.lifeTime = 15; // Sekunden
this.spiritMeshes = [];
this.grp.add(this.gltf);
this.gltf.position.set(0, 0, -0.6);
this.gltf.traverse((mesh) => {
if (mesh.isMesh) {
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.userData.originalMaterial = mesh.material.clone();
mesh.material = mesh.material.clone();
mesh.material.color.set(0xffffcc);
mesh.material.opacity = 0.0;
mesh.material.transparent = true;
mesh.material.emissive?.set(0xffffcc);
mesh.material.emissiveIntensity = 2.0;
this.spiritMeshes.push(mesh);
}
});
this.scene.add(this.grp);
}
update(dt) {
const t = this.clock.getElapsedTime();
if (this.spiritMeshes && this.isFading) {
for (const mesh of this.spiritMeshes) {
if (t < 0.5) {
mesh.material.opacity = 1;
mesh.material.color.lerp(mesh.userData.originalMaterial.color, t / 0.5);
if (mesh.material.emissive)
mesh.material.emissive.lerp(
mesh.userData.originalMaterial.emissive || new THREE.Color(0x000000),
t / 0.5
);
mesh.material.emissiveIntensity =
2.0 * (1 - t / 0.5) +
(mesh.userData.originalMaterial.emissiveIntensity || 1.0) * (t / 0.5);
} else {
mesh.material.opacity = mesh.userData.originalMaterial.opacity ?? 1.0;
mesh.material.color.copy(mesh.userData.originalMaterial.color);
if (mesh.material.emissive)
mesh.material.emissive.copy(
mesh.userData.originalMaterial.emissive || new THREE.Color(0x000000)
);
mesh.material.emissiveIntensity =
mesh.userData.originalMaterial.emissiveIntensity ?? 1.0;
this.isFading = false;
}
}
}
// Nach Lebenszeit entfernen
if (t > this.lifeTime) {
this.dispose();
return false;
}
return true;
}
dispose() {
this.scene.remove(this.grp);
this.gltf.traverse((mesh) => {
if (mesh.isMesh) {
mesh.geometry.dispose();
if (Array.isArray(mesh.material)) {
mesh.material.forEach((m) => m.dispose());
} else {
mesh.material.dispose();
}
}
});
}
}
// ---- Start der Szene ----
(async()=>{ (async()=>{
landscape = await loadGLB( landscape = await loadGLB(
'assets/models/landscape.glb', 'assets/models/landscape.glb',
@@ -208,8 +291,6 @@ const clock = new THREE.Clock();
[0.0,0.0,0.0], [90,0,0], [0.0,0.0,0.0], [90,0,0],
{receiveShadow:false, castShadow:true, shadowOnly: true} {receiveShadow:false, castShadow:true, shadowOnly: true}
); );
// Rotierende Lichter initialisieren
for(let i=0;i<3;i++){ for(let i=0;i<3;i++){
const L = new THREE.PointLight(0xFFA230, 5, 30); const L = new THREE.PointLight(0xFFA230, 5, 30);
L.castShadow = true; L.castShadow = true;
@@ -221,11 +302,13 @@ const clock = new THREE.Clock();
counterRotatingLights.push(L2); counterRotatingLights.push(L2);
scene.add(L2); scene.add(L2);
} }
animate(); animate();
})(); })();
// ---- Render-Loop ---- // ---- Render-Loop ----
let currentSpirit = null;
window.currentSpirit = null;
function animate(){ function animate(){
const dt = clock.getDelta(), t = clock.getElapsedTime(); const dt = clock.getDelta(), t = clock.getElapsedTime();
// Spinner Animation // Spinner Animation
@@ -256,17 +339,20 @@ function animate(){
lightZ lightZ
); );
} }
// Spirit animieren/entfernen
if (currentSpirit && currentSpirit.update) {
if (!currentSpirit.update(dt)) {
currentSpirit = null;
window.currentSpirit = null;
}
}
composer.render(scene, camera); composer.render(scene, camera);
requestAnimationFrame(animate); requestAnimationFrame(animate);
} }
// ---- WebSocket-basierter Spirit Spawn ---- // ---- WebSocket-basierter Spirit Spawn ----
let currentSpiritGroup = null;
// WebSocket verbinden
const ws = new WebSocket(`ws://${location.host}`); const ws = new WebSocket(`ws://${location.host}`);
// Spirit per Server-Anweisung anzeigen
ws.addEventListener('message', async (event) => { ws.addEventListener('message', async (event) => {
const msg = JSON.parse(event.data); const msg = JSON.parse(event.data);
if (msg.type === 'spirit') { if (msg.type === 'spirit') {
@@ -275,29 +361,15 @@ ws.addEventListener('message', async (event) => {
}); });
async function showSpirit(spirit) { async function showSpirit(spirit) {
// Vorherigen Spirit entfernen if (currentSpirit) {
if (currentSpiritGroup) { currentSpirit.dispose();
scene.remove(currentSpiritGroup); currentSpirit = null;
// Optional: Materialien etc. dispose() aufrufen window.currentSpirit = null;
currentSpiritGroup = null;
} }
console.log("Lade Spirit", spirit.modelUrl); console.log("Lade Spirit", spirit.modelUrl);
// Modell laden
const { scene: spiritObj } = await gltfLoader.loadAsync(spirit.modelUrl); const { scene: spiritObj } = await gltfLoader.loadAsync(spirit.modelUrl);
spiritObj.traverse(mesh => { currentSpirit = new Spirit(scene, spiritObj, spirit);
if (mesh.isMesh) { window.currentSpirit = currentSpirit;
mesh.castShadow = true;
mesh.receiveShadow = true;
// weitere Material-Anpassungen...
}
});
console.log("Spirit wurde der Szene hinzugefügt!", spiritObj);
// Positionierung, z.B. am Boden leicht nach hinten
spiritObj.position.set(0, 0, 0);
scene.add(spiritObj);
currentSpiritGroup = spiritObj;
// Overlay mit Spirit-Name/Description
updateSpiritOverlay(spirit); updateSpiritOverlay(spirit);
} }