Versión inicial: Motor funcional con visualización de texturas corregida

This commit is contained in:
2025-12-30 23:40:39 +01:00
parent 7dbc77e75a
commit e90cfe3664
2 changed files with 72 additions and 74 deletions

View File

@@ -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.

View File

@@ -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}`);
}