diff --git a/node/server/public/app.js b/node/server/public/app.js index e8deaa8..681cb3f 100644 --- a/node/server/public/app.js +++ b/node/server/public/app.js @@ -479,6 +479,155 @@ function showSpiritOverlay(spirit) { } +let spiritPreviewRenderer = null, spiritPreviewScene = null, spiritPreviewCamera = null, spiritPreviewObj = null; +let spiritPreviewDragging = false, spiritPreviewLastX = 0, spiritPreviewRotationY = 0, spiritPreviewRotationX = 0; + +function destroySpiritModelPreview() { + // Cleanup: Renderer und Scene entsorgen + if (spiritPreviewRenderer) { + spiritPreviewRenderer.dispose(); + spiritPreviewRenderer.domElement.remove(); + spiritPreviewRenderer = null; + } + spiritPreviewScene = null; + spiritPreviewCamera = null; + spiritPreviewObj = null; + spiritPreviewDragging = false; + spiritPreviewLastX = 0; + spiritPreviewRotationY = 0; + spiritPreviewRotationX = 0; +} + +async function setupSpiritModelPreview(spirit) { + destroySpiritModelPreview(); + + // Canvas erzeugen + const container = document.getElementById('spirit-model-preview'); + if (!container) return; + const width = container.clientWidth; + const height = container.clientHeight; + + // Three.js Preview Renderer + spiritPreviewRenderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); + spiritPreviewRenderer.setClearColor(0x000000, 0); // transparent + spiritPreviewRenderer.setSize(width, height, false); + container.appendChild(spiritPreviewRenderer.domElement); + + // Preview Szene und Kamera + spiritPreviewScene = new THREE.Scene(); + spiritPreviewCamera = new THREE.PerspectiveCamera(27, width / height, 0.1, 100); + spiritPreviewCamera.position.set(0, 1, 5); + + // Licht + const keyLight = new THREE.DirectionalLight(0xffffff, 1.2); + keyLight.position.set(3, 4, 8); + spiritPreviewScene.add(keyLight); + const fillLight = new THREE.AmbientLight(0xffffff, 0.85); + spiritPreviewScene.add(fillLight); + + // Model laden + const modelUrl = spirit['Model URL'] || spirit.modelUrl; + try { + const { scene: modelObj } = await gltfLoader.loadAsync(modelUrl); + // Färbe/Resette ggf. Materialien (optional, nicht zwingend nötig) + modelObj.traverse(mesh => { + if (mesh.isMesh) { + mesh.castShadow = false; + mesh.receiveShadow = false; + mesh.material = mesh.material.clone(); + mesh.material.opacity = 1.0; + mesh.material.transparent = false; + mesh.material.emissiveIntensity = 1.0; + } + }); + // Zentriere das Modell (Bounding Box) + const bbox = new THREE.Box3().setFromObject(modelObj); + const center = bbox.getCenter(new THREE.Vector3()); + modelObj.position.sub(center); + // Größe skalieren + const size = bbox.getSize(new THREE.Vector3()); + const maxDim = Math.max(size.x, size.y, size.z); + const scale = 2.1 / maxDim; + modelObj.scale.setScalar(scale); + + // Initiale Drehung: leicht nach rechts & von oben + spiritPreviewRotationY = Math.PI / 8; + spiritPreviewRotationX = -Math.PI / 18; + modelObj.rotation.y = spiritPreviewRotationY; + modelObj.rotation.x = spiritPreviewRotationX; + + spiritPreviewObj = modelObj; + spiritPreviewScene.add(modelObj); + } catch (err) { + container.innerHTML = `