"use strict"; const ElevatorFrameGeometryGame = (function(){ const ElevatorFrameGeometryGame = function(hollow_bond){ const cache = {}; // Añade una caja completa (6 caras) para elementos aislados o interiores const append_box = (buffers, min_x, max_x, min_y, max_y, min_z, max_z) => { const { positions, normals, uvs, indices } = buffers; let offset = positions.length / 3; const add_quad = (p1, p2, p3, p4, n) => { positions.push(...p1, ...p2, ...p3, ...p4); normals.push(...n, ...n, ...n, ...n); uvs.push(0, 0, 1, 0, 1, 1, 0, 1); indices.push(offset, offset + 2, offset + 1, offset, offset + 3, offset + 2); offset += 4; }; add_quad([min_x, max_y, max_z], [max_x, max_y, max_z], [max_x, max_y, min_z], [min_x, max_y, min_z], [0, 1, 0]); add_quad([min_x, min_y, min_z], [max_x, min_y, min_z], [max_x, min_y, max_z], [min_x, min_y, max_z], [0, -1, 0]); add_quad([max_x, min_y, min_z], [min_x, min_y, min_z], [min_x, max_y, min_z], [max_x, max_y, min_z], [0, 0, -1]); add_quad([min_x, min_y, max_z], [max_x, min_y, max_z], [max_x, max_y, max_z], [min_x, max_y, max_z], [0, 0, 1]); add_quad([min_x, min_y, min_z], [min_x, min_y, max_z], [min_x, max_y, max_z], [min_x, max_y, min_z], [-1, 0, 0]); add_quad([max_x, min_y, max_z], [max_x, min_y, min_z], [max_x, max_y, min_z], [max_x, max_y, max_z], [1, 0, 0]); }; // Añade una moldura apoyada en pared SIN cara trasera (-Z) para evitar Z-fighting const append_relief = (buffers, min_x, max_x, min_y, max_y, z_base, z_front) => { const { positions, normals, uvs, indices } = buffers; let offset = positions.length / 3; const add_quad = (p1, p2, p3, p4, n) => { positions.push(...p1, ...p2, ...p3, ...p4); normals.push(...n, ...n, ...n, ...n); uvs.push(0, 0, 1, 0, 1, 1, 0, 1); indices.push(offset, offset + 2, offset + 1, offset, offset + 3, offset + 2); offset += 4; }; // Frontal (+Z) hacia la sala add_quad([min_x, min_y, z_front], [max_x, min_y, z_front], [max_x, max_y, z_front], [min_x, max_y, z_front], [0, 0, 1]); // Superior (+Y) add_quad([min_x, max_y, z_front], [max_x, max_y, z_front], [max_x, max_y, z_base], [min_x, max_y, z_base], [0, 1, 0]); // Inferior (-Y) add_quad([min_x, min_y, z_base], [max_x, min_y, z_base], [max_x, min_y, z_front], [min_x, min_y, z_front], [0, -1, 0]); // Lateral Izquierdo (-X) add_quad([min_x, min_y, z_base], [min_x, min_y, z_front], [min_x, max_y, z_front], [min_x, max_y, z_base], [-1, 0, 0]); // Lateral Derecho (+X) add_quad([max_x, min_y, z_front], [max_x, min_y, z_base], [max_x, max_y, z_base], [max_x, max_y, z_front], [1, 0, 0]); // NOTA: No generamos la cara -Z (trasera) porque apoya directamente sobre la pared. }; this.get_for_elevator = model => { const door_w = hollow_bond.elevator_door_width || 1.1; const door_h = 2.10; const key = `frame_clean_${door_w}_${door_h}`; if(cache[key]) return cache[key]; const buffers = { positions: [], normals: [], uvs: [], indices: [] }; const hw = door_w * 0.5; // 0.55m const wall_z = (hollow_bond.room_padding !== undefined) ? hollow_bond.room_padding : 0.0; const trim_w = 0.12; // 12 cm de moldura const header_h = 0.22; // 22 cm de dintel const z_front = wall_z + 0.02; // Relieve limpio de 2 cm // --- GRUPO 0: Acero cepillado del marco --- const start_metal = buffers.indices.length; // 1. Forro interior de las jambas (dentro del vano, sin pared de yeso) append_box(buffers, -hw - 0.025, -hw, 0, door_h, -0.04, wall_z); append_box(buffers, hw, hw + 0.025, 0, door_h, -0.04, wall_z); append_box(buffers, -hw, hw, door_h, door_h + 0.025, -0.04, wall_z); // 2. Tapajuntas sobre la pared (SIN penetración y SIN cara trasera) // Moldura izquierda append_relief(buffers, -hw - trim_w, -hw, 0, door_h + header_h, wall_z, z_front); // Moldura derecha append_relief(buffers, hw, hw + trim_w, 0, door_h + header_h, wall_z, z_front); // Dintel superior append_relief(buffers, -hw, hw, door_h, door_h + header_h, wall_z, z_front); // 3. Umbral de suelo append_box(buffers, -hw, hw, 0, 0.012, -0.04, z_front); // 4. Placa base de la botonera exterior (en relieve, sin cara trasera) const btn_x1 = hw + trim_w + 0.03; const btn_x2 = btn_x1 + 0.12; append_relief(buffers, btn_x1, btn_x2, 1.10, 1.38, wall_z, z_front); const count_metal = buffers.indices.length - start_metal; // --- GRUPO 1: Elementos luminosos --- const start_light = buffers.indices.length; // Display de piso append_relief(buffers, -0.18, 0.18, door_h + 0.06, door_h + 0.16, z_front, z_front + 0.006); // Botón de llamada iluminado append_relief(buffers, btn_x1 + 0.035, btn_x2 - 0.035, 1.20, 1.28, z_front, z_front + 0.008); const count_light = buffers.indices.length - start_light; const geometry = new THREE.BufferGeometry(); geometry.setAttribute("position", new THREE.Float32BufferAttribute(buffers.positions, 3)); geometry.setAttribute("normal", new THREE.Float32BufferAttribute(buffers.normals, 3)); geometry.setAttribute("uv", new THREE.Float32BufferAttribute(buffers.uvs, 2)); geometry.setIndex(buffers.indices); geometry.addGroup(start_metal, count_metal, 0); geometry.addGroup(start_light, count_light, 1); cache[key] = geometry; return geometry; }; this.reset = () => { for(const k in cache){ cache[k].dispose(); delete cache[k]; } }; }; return ElevatorFrameGeometryGame; })();