Add spirit model preview functionality with Three.js
This commit is contained in:
@@ -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 = `<div style="color:#aaa;padding:16px 0;text-align:center;">Modell konnte nicht geladen werden :(</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Maus-Events für Drehung
|
||||
const canvas = spiritPreviewRenderer.domElement;
|
||||
canvas.style.cursor = "grab";
|
||||
canvas.onpointerdown = (e) => {
|
||||
spiritPreviewDragging = true;
|
||||
spiritPreviewLastX = e.clientX;
|
||||
canvas.setPointerCapture(e.pointerId);
|
||||
canvas.style.cursor = "grabbing";
|
||||
};
|
||||
canvas.onpointermove = (e) => {
|
||||
if (spiritPreviewDragging && spiritPreviewObj) {
|
||||
let delta = (e.clientX - spiritPreviewLastX) / width * Math.PI;
|
||||
spiritPreviewRotationY += delta;
|
||||
spiritPreviewObj.rotation.y = spiritPreviewRotationY;
|
||||
spiritPreviewLastX = e.clientX;
|
||||
}
|
||||
};
|
||||
canvas.onpointerup = (e) => {
|
||||
spiritPreviewDragging = false;
|
||||
canvas.releasePointerCapture(e.pointerId);
|
||||
canvas.style.cursor = "grab";
|
||||
};
|
||||
|
||||
// Vertikales Drag: Optional für X-Rotation
|
||||
let lastY = 0;
|
||||
canvas.onpointerdown = (e) => {
|
||||
spiritPreviewDragging = true;
|
||||
spiritPreviewLastX = e.clientX;
|
||||
lastY = e.clientY;
|
||||
canvas.setPointerCapture(e.pointerId);
|
||||
canvas.style.cursor = "grabbing";
|
||||
};
|
||||
canvas.onpointermove = (e) => {
|
||||
if (spiritPreviewDragging && spiritPreviewObj) {
|
||||
let deltaX = (e.clientX - spiritPreviewLastX) / width * Math.PI;
|
||||
let deltaY = (e.clientY - lastY) / height * Math.PI;
|
||||
spiritPreviewRotationY += deltaX;
|
||||
spiritPreviewRotationX += deltaY;
|
||||
spiritPreviewRotationX = Math.max(-Math.PI / 2.3, Math.min(Math.PI / 2.3, spiritPreviewRotationX)); // Limitiere X
|
||||
spiritPreviewObj.rotation.y = spiritPreviewRotationY;
|
||||
spiritPreviewObj.rotation.x = spiritPreviewRotationX;
|
||||
spiritPreviewLastX = e.clientX;
|
||||
lastY = e.clientY;
|
||||
}
|
||||
};
|
||||
canvas.onpointerup = (e) => {
|
||||
spiritPreviewDragging = false;
|
||||
canvas.releasePointerCapture(e.pointerId);
|
||||
canvas.style.cursor = "grab";
|
||||
};
|
||||
|
||||
// Animation/Rendering starten
|
||||
previewRenderLoop();
|
||||
}
|
||||
|
||||
function previewRenderLoop() {
|
||||
if (!spiritPreviewRenderer || !spiritPreviewScene || !spiritPreviewCamera) return;
|
||||
spiritPreviewRenderer.render(spiritPreviewScene, spiritPreviewCamera);
|
||||
// Nur weiter machen, wenn das Overlay sichtbar ist!
|
||||
let el = document.getElementById('spirit-info');
|
||||
if (el && el.style.display !== 'none') {
|
||||
requestAnimationFrame(previewRenderLoop);
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse-Picking (zentral)
|
||||
const raycaster = new THREE.Raycaster();
|
||||
const mouse = new THREE.Vector2();
|
||||
|
||||
Reference in New Issue
Block a user