From e90cfe36647b1ca14ae8a6469f03fb374ebf16ea Mon Sep 17 00:00:00 2001 From: marti Date: Tue, 30 Dec 2025 23:40:39 +0100 Subject: [PATCH] =?UTF-8?q?Versi=C3=B3n=20inicial:=20Motor=20funcional=20c?= =?UTF-8?q?on=20visualizaci=C3=B3n=20de=20texturas=20corregida?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DEVLOG.md | 6 +- src/view/GameRenderer.js | 140 +++++++++++++++++++-------------------- 2 files changed, 72 insertions(+), 74 deletions(-) diff --git a/DEVLOG.md b/DEVLOG.md index 4de50ff..8ea56b4 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -39,12 +39,14 @@ En esta sesión se ha establecido la base completa del motor de juego para **War * Zoom y Panoramización. * **Assets**: Integración de texturas (`.png`) para baldosas, movidas a la carpeta `public/assets` para su correcta carga en el navegador. +### Estado Actual +### Estado Actual ### Estado Actual * El generador crea mazmorras lógicas válidas siguiendo las reglas. * El visualizador pinta la estructura en 3D. -* Se han añadido las texturas, aunque persisten problemas de caché/visualización en el navegador del usuario que requieren un reinicio limpio. +* **Texturas operativas**: Se ha corregido un bug crítico en la rotación (NaN) que impedía la visualización. Ahora las texturas se cargan y posicionan, aunque persisten problemas de alineación visual en las juntas. ### Próximos Pasos -* Validar la alineación visual fina de las texturas (especialmente en uniones T y L). +* Corregir la alineación fina de las baldosas (especialmente T y L) para eliminar huecos visuales. * Implementar la interfaz de usuario (UI) para mostrar cartas y estado del juego. * Añadir modelos 3D para héroes y monstruos. diff --git a/src/view/GameRenderer.js b/src/view/GameRenderer.js index c83de0c..efa8159 100644 --- a/src/view/GameRenderer.js +++ b/src/view/GameRenderer.js @@ -56,20 +56,31 @@ export class GameRenderer { } } - getTexture(path) { + getTexture(path, onLoad) { if (!this.textureCache.has(path)) { - // NOTE: Using absolute paths from public dir requires leading slash if served from root - // But verify if we need to prepend anything else. - // Assuming served at root /. - const tex = this.textureLoader.load(path, - (t) => console.log(`Texture loaded: ${path}`), + console.log(`[TextureLoader] Starting load for: ${path}`); + const tex = this.textureLoader.load( + path, + (texture) => { + console.log(`[TextureLoader] ✓ Successfully loaded: ${path}`); + texture.needsUpdate = true; + if (onLoad) onLoad(texture); + }, undefined, - (err) => console.error(`Texture failed: ${path}`, err) + (err) => { + console.error(`[TextureLoader] ✗ Failed to load: ${path}`, err); + } ); tex.magFilter = THREE.NearestFilter; tex.minFilter = THREE.NearestFilter; tex.colorSpace = THREE.SRGBColorSpace; this.textureCache.set(path, tex); + } else { + // Already cached, call onLoad immediately if texture is ready + const cachedTex = this.textureCache.get(path); + if (onLoad && cachedTex.image) { + onLoad(cachedTex); + } } return this.textureCache.get(path); } @@ -79,78 +90,63 @@ export class GameRenderer { // tileDef: The definition object (has textures, dimensions) // tileInstance: The instance object (has x, y, rotation, id) - console.log(`Rendering Tile [${type}] with ${cells.length} cells.`); + console.log(`[GameRenderer] Rendering Tile [${type}] ID: ${tileDef?.id} at (${tileInstance?.x}, ${tileInstance?.y}) Rot: ${tileInstance?.rotation}`); - const isRoom = type === 'room' || type === 'room_objective' || type === 'room_dungeon'; - - // 1. Draw individual Cells (The Grill) - cells.forEach(cell => { - const geometry = new THREE.BoxGeometry(1, 0.5, 1); - const material = new THREE.MeshStandardMaterial({ - color: isRoom ? 0x4444ff : 0xaaaaaa, - roughness: 0.8, - metalness: 0.1, - transparent: true, - opacity: 0.5 - }); - const mesh = new THREE.Mesh(geometry, material); - mesh.position.set(cell.x, 0, -cell.y); - - const edges = new THREE.EdgesGeometry(geometry); - const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x000000 })); - mesh.add(line); - - this.scene.add(mesh); - }); - - // 2. Draw Texture Plane (The Image) + // Draw Texture Plane (The Image) - WAIT FOR TEXTURE TO LOAD if (tileDef && tileInstance && tileDef.textures && tileDef.textures.length > 0) { const texturePath = tileDef.textures[0]; - console.log(`[GameRenderer] Loading texture ${texturePath} for tile`, tileDef.id); - const texture = this.getTexture(texturePath); - const w = tileDef.width; - const l = tileDef.length; + // Load texture with callback + this.getTexture(texturePath, (texture) => { + const w = tileDef.width; + const l = tileDef.length; - // Create Plane - const geometry = new THREE.PlaneGeometry(w, l); - const material = new THREE.MeshBasicMaterial({ - map: texture, - transparent: true, - side: THREE.DoubleSide, - alphaTest: 0.1 + // Create Plane + const geometry = new THREE.PlaneGeometry(w, l); + // Use MeshStandardMaterial for reaction to light if needed + const material = new THREE.MeshStandardMaterial({ + map: texture, + transparent: true, + side: THREE.FrontSide, // Only visible from top + alphaTest: 0.1, + roughness: 0.8, + metalness: 0.2 + }); + const plane = new THREE.Mesh(geometry, material); + + // Initial Rotation: Plane X-Y to X-Z + plane.rotation.x = -Math.PI / 2; + + // Handle Rotation safely (Support both 0-3 and N-W) + const rotMap = { 'N': 0, '0': 0, 0: 0, 'E': 1, '1': 1, 1: 1, 'S': 2, '2': 2, 2: 2, 'W': 3, '3': 3, 3: 3 }; + const r = rotMap[tileInstance.rotation] !== undefined ? rotMap[tileInstance.rotation] : 0; + + // Apply Tile Rotation + plane.rotation.z = -r * (Math.PI / 2); + + // Calculate Center Offset for Positioning + const midX = (tileDef.width - 1) / 2; + const midY = (tileDef.length - 1) / 2; + + // Rotate the offset vector based on tile rotation + let dx, dy; + + if (r === 0) { dx = midX; dy = midY; } + else if (r === 1) { dx = midY; dy = -midX; } + else if (r === 2) { dx = -midX; dy = -midY; } + else if (r === 3) { dx = -midY; dy = midX; } + + const centerX = tileInstance.x + dx; + const centerY = tileInstance.y + dy; + + // Set at almost 0 height to avoid z-fighting with grid helper, but effectively on floor + plane.position.set(centerX, 0.01, -centerY); + plane.receiveShadow = true; + + this.scene.add(plane); + console.log(`[GameRenderer] ✓ Tile plane added at (${centerX}, 0.01, ${-centerY}) for ${tileDef.id}`); }); - const plane = new THREE.Mesh(geometry, material); - - // Initial Rotation: Plane X-Y to X-Z - plane.rotation.x = -Math.PI / 2; - - // Apply Tile Rotation (N=0, E=1, S=2, W=3 in Y axis) - // We rotate around 0,0 of the plane geometry - // Note: rotation.z is local Z, which after rotX(-90) is Global Y (Vertical) - plane.rotation.z = -tileInstance.rotation * (Math.PI / 2); - - // Calculate Center Offset for Positioning - // Visual Center needs to be offset from Tile Origin (x,y) - const midX = (tileDef.width - 1) / 2; - const midY = (tileDef.length - 1) / 2; - - // Rotate the offset vector based on tile rotation - let dx, dy; - const r = tileInstance.rotation; - - if (r === 0) { dx = midX; dy = midY; } - else if (r === 1) { dx = midY; dy = -midX; } - else if (r === 2) { dx = -midX; dy = -midY; } - else if (r === 3) { dx = -midY; dy = midX; } - - const centerX = tileInstance.x + dx; - const centerY = tileInstance.y + dy; - - plane.position.set(centerX, 0.55, -centerY); - - this.scene.add(plane); } else { console.warn(`[GameRenderer] details missing for texture render. def: ${!!tileDef}, inst: ${!!tileInstance}, tex: ${tileDef?.textures?.length}`); }