tp/src/standalone/bridge.js (10001B)
1 import * as THREE from 'three'; 2 import * as dat from 'dat.gui'; 3 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; 4 import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js'; 5 6 import tierraSecaUrl from '../assets/tierra_seca.jpg' 7 import ladrillosUrl from '../assets/pared_de_ladrillo.jpg' 8 9 const textures = { 10 tierra: { url: tierraSecaUrl, object: null }, 11 ladrillos: { url: ladrillosUrl, object: null }, 12 }; 13 14 let scene, camera, renderer, container; 15 16 function onResize() { 17 camera.aspect = container.offsetWidth / container.offsetHeight; 18 camera.updateProjectionMatrix(); 19 renderer.setSize(container.offsetWidth, container.offsetHeight); 20 } 21 22 function setupThreeJs() { 23 scene = new THREE.Scene(); 24 container = document.getElementById('mainContainer'); 25 26 renderer = new THREE.WebGLRenderer(); 27 renderer.setClearColor(0x606060); 28 // renderer.setClearColor(0xFFFFFF); 29 container.appendChild(renderer.domElement); 30 31 camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 1000); 32 camera.position.set(25, 25, 25); 33 camera.lookAt(10, 10, 10); 34 35 const controls = new OrbitControls(camera, renderer.domElement); 36 37 const ambientLight = new THREE.AmbientLight(0xaaaaaa); 38 scene.add(ambientLight); 39 40 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x000000, 0.25); 41 scene.add(hemisphereLight); 42 43 const directionalLight = new THREE.DirectionalLight(0xffffff, 1); 44 directionalLight.position.set(100, 100, 100); 45 scene.add(directionalLight); 46 47 const gridHelper = new THREE.GridHelper(50, 20); 48 scene.add(gridHelper); 49 50 const axesHelper = new THREE.AxesHelper(5); 51 scene.add(axesHelper); 52 53 window.addEventListener('resize', onResize); 54 onResize(); 55 } 56 57 function onTextureLoaded(key, texture) { 58 texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 59 textures[key].object = texture; 60 console.log('Texture `' + key + '` loaded'); 61 } 62 63 function loadTextures(callback) { 64 const loadingManager = new THREE.LoadingManager(); 65 66 loadingManager.onLoad = () => { 67 console.log('All textures loaded'); 68 callback(); 69 }; 70 71 for (const key in textures) { 72 console.log("Loading textures"); 73 const loader = new THREE.TextureLoader(loadingManager); 74 const texture = textures[key]; 75 texture.object = loader.load( 76 texture.url, 77 onTextureLoaded.bind(this, key), 78 null, 79 (error) => { 80 console.error(error); 81 } 82 ); 83 } 84 } 85 86 const arcWidth = 5; 87 const arcCount = 4; 88 const arcRadius = arcWidth/2; 89 const columnHeight = 5; 90 const columnWidth = 1.50; 91 const topPadding = 0.50; 92 const startPadding = 10; 93 const endPadding = startPadding; 94 const bridgeWallThickness = 2.5; 95 const bridgeLen = arcCount*(columnWidth+arcWidth)+columnWidth+startPadding+endPadding; 96 const bridgeHeight = columnHeight+arcRadius+topPadding; 97 98 function generateBridgeWall() { 99 const path = new THREE.Path(); 100 101 // generate the arcs 102 for(let i = 1; i <= arcCount; ++i) { 103 path.lineTo(startPadding+i*columnWidth+((i-1)*arcWidth), 0); 104 path.moveTo(startPadding+i*columnWidth+((i-1)*arcWidth), 0); 105 path.lineTo(startPadding+i*columnWidth+((i-1)*arcWidth), columnHeight); 106 path.arc(arcRadius, 0, arcRadius, Math.PI, 0, true) 107 path.moveTo(startPadding+i*(columnWidth+arcWidth), 0); 108 path.lineTo(startPadding+i*(columnWidth+arcWidth), 0); 109 } 110 111 // no we close the curve 112 path.lineTo(bridgeLen, 0); 113 path.lineTo(bridgeLen, bridgeHeight); 114 115 path.lineTo(0, bridgeHeight); 116 path.lineTo(0, 0); 117 118 /* 119 // muestra la curva utilizada para la extrusión 120 const geometry = new THREE.BufferGeometry().setFromPoints(points); 121 const lineMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 }); 122 const curveObject = new THREE.Line(geometry, lineMaterial); 123 scene.add(curveObject); 124 */ 125 126 const points = path.getPoints(); 127 const shape = new THREE.Shape(points); 128 129 const extrudeSettings = { 130 curveSegments: 24, 131 steps: 50, 132 depth: bridgeWallThickness, 133 bevelEnabled: false 134 }; 135 136 const bridgeWallGeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); 137 bridgeWallGeometry.translate(-bridgeLen/2, 0, -bridgeWallThickness/2); 138 return bridgeWallGeometry; 139 } 140 141 const squareTubeRadius = 0.15; 142 function generateBridgeCage(squaresCount = 3) { 143 const squaresSideLen = 10; 144 const bridgeCageLen = squaresCount * squaresSideLen; 145 146 let geometries = [] 147 148 let cylinderBase, cylinderCorner, cylinderCrossbar; 149 for(let square = 0; square < squaresCount; ++square) { 150 // 0 -> 00 151 // 1 -> 01 152 // 2 -> 10 153 // 3 -> 11 154 for(let i = 0; i < 4; ++i) { 155 cylinderBase = new THREE.CylinderGeometry( 156 squareTubeRadius, squareTubeRadius, squaresSideLen); 157 158 cylinderCorner = cylinderBase.clone(); 159 160 const squareHypotenuse = Math.sqrt(2*squaresSideLen*squaresSideLen); 161 cylinderCrossbar = new THREE.CylinderGeometry( 162 squareTubeRadius, squareTubeRadius, squareHypotenuse); 163 164 if((i % 2) == 0) { 165 cylinderBase.rotateZ(Math.PI/2); 166 cylinderBase.translate( 167 0, 168 square*(squaresSideLen), 169 ((-1)**(i>>1))*squaresSideLen/2); 170 171 cylinderCrossbar.rotateZ((-1)**((i>>1))*Math.PI/4); 172 cylinderCrossbar.translate( 173 0, 174 square*(squaresSideLen)+(squaresSideLen/2), 175 ((-1)**(i>>1))*squaresSideLen/2); 176 177 cylinderCorner.translate( 178 ((-1)**(i>>1))*squaresSideLen/2, 179 square*(squaresSideLen)+(squaresSideLen/2), 180 ((-1)**(i&1))*squaresSideLen/2); 181 } else { 182 cylinderBase.rotateX(Math.PI/2); 183 cylinderBase.translate( 184 ((-1)**(i>>1))*squaresSideLen/2, 185 square*(squaresSideLen), 186 0); 187 188 cylinderCrossbar.rotateX((-1)**((i>>1))*Math.PI/4); 189 cylinderCrossbar.translate( 190 ((-1)**(i>>1))*squaresSideLen/2, 191 square*(squaresSideLen)+(squaresSideLen/2), 192 0); 193 194 cylinderCorner.translate( 195 ((-1)**(i>>1))*squaresSideLen/2, 196 square*(squaresSideLen)+(squaresSideLen/2), 197 ((-1)**(i&1))*squaresSideLen/2); 198 } 199 geometries.push(cylinderBase); 200 geometries.push(cylinderCrossbar); 201 geometries.push(cylinderCorner); 202 } 203 204 // agregamos un cuadrado mas para 'cerrar' la 'jaula' 205 if((square + 1) == squaresCount) { 206 for(let i = 0; i < 4; ++i) { 207 cylinderBase = new THREE.CylinderGeometry( 208 squareTubeRadius, squareTubeRadius, squaresSideLen); 209 210 if((i % 2) == 0) { 211 cylinderBase.rotateZ(Math.PI/2); 212 cylinderBase.translate( 213 0, 214 (square+1)*(squaresSideLen), 215 ((-1)**(i>>1))*squaresSideLen/2); 216 } else { 217 cylinderBase.rotateX(Math.PI/2); 218 cylinderBase.translate( 219 ((-1)**(i>>1))*squaresSideLen/2, 220 (square+1)*(squaresSideLen), 0); 221 } 222 geometries.push(cylinderBase); 223 } 224 } 225 } 226 227 const bridgeCage = mergeGeometries(geometries); 228 bridgeCage.rotateZ(Math.PI/2); 229 bridgeCage.translate(bridgeCageLen/2, squaresSideLen/2, 0); 230 return bridgeCage; 231 } 232 233 function generateBridge() { 234 const bridgeWidth = 10; 235 const roadwayHeight = 2; 236 237 const leftWallGeometry = generateBridgeWall(); 238 leftWallGeometry.translate(0, 0, -bridgeWidth/2); 239 240 const rightWallGeometry = generateBridgeWall(); 241 rightWallGeometry.translate(0, 0, bridgeWidth/2) 242 243 const bridgeColumnsGeometry = mergeGeometries([leftWallGeometry, rightWallGeometry]); 244 const bridgeRoadwayGeometry = new THREE.BoxGeometry( 245 bridgeLen, roadwayHeight, bridgeWidth+bridgeWallThickness, 246 ); 247 248 bridgeRoadwayGeometry.translate(0, bridgeHeight+roadwayHeight/2, 0); 249 250 textures.ladrillos.object.wrapS = THREE.RepeatWrapping; 251 textures.ladrillos.object.wrapT = THREE.RepeatWrapping; 252 textures.ladrillos.object.repeat.set(0.75*0.15, 0.75*0.35); 253 textures.ladrillos.object.anisotropy = 16; 254 255 const bridgeMaterial = new THREE.MeshPhongMaterial({ 256 side: THREE.DoubleSide, 257 transparent: false, 258 opacity: 1.0, 259 shininess: 10, 260 map: textures.ladrillos.object 261 }); 262 263 /* 264 textures.ladrillos2.object.wrapS = THREE.RepeatWrapping; 265 textures.ladrillos2.object.wrapT = THREE.RepeatWrapping; 266 textures.ladrillos2.object.repeat.set(0.75*5, 0.75*0.75); 267 textures.ladrillos2.object.anisotropy = 16; 268 269 const roadwayMaterial = new THREE.MeshPhongMaterial({ 270 side: THREE.DoubleSide, 271 transparent: false, 272 opacity: 1.0, 273 shininess: 10, 274 map: textures.ladrillos2.object 275 // color: 0xFF0000 276 }); 277 278 const bridgeRoadway = new THREE.Mesh(bridgeRoadwayGeometry, roadwayMaterial); 279 scene.add(bridgeRoadway); 280 */ 281 282 const bridgeColumns = new THREE.Mesh(bridgeColumnsGeometry, bridgeMaterial); 283 scene.add(bridgeColumns); 284 285 // para reutilizar la textura de ladrillos usada en los arcos se escalan las 286 // coordenadas uv de la geometria de la parte superior 287 let uvs = bridgeRoadwayGeometry.attributes.uv.array; 288 for (let i = 0, len = uvs.length; i < len; i++) { 289 uvs[i] = (i % 2) ? uvs[i]*2.50 : uvs[i]*30.0; 290 } 291 292 const bridgeRoadway = new THREE.Mesh(bridgeRoadwayGeometry, bridgeMaterial); 293 scene.add(bridgeRoadway); 294 295 const cageGeometry = generateBridgeCage() 296 cageGeometry.translate(0, bridgeHeight+roadwayHeight-squareTubeRadius*2, 0); 297 298 const cageMaterial = new THREE.MeshPhongMaterial({ 299 side: THREE.DoubleSide, 300 transparent: false, 301 opacity: 1.0, 302 shininess: 10, 303 color: 0xFFFFFF 304 }); 305 306 const bridgeCage = new THREE.Mesh(cageGeometry, cageMaterial); 307 scene.add(bridgeCage); 308 309 const roadwayFloorGeometry = new THREE.BoxGeometry( 310 bridgeWidth+bridgeWallThickness, 311 bridgeLen, 0.5); 312 313 roadwayFloorGeometry.rotateZ(Math.PI/2) 314 roadwayFloorGeometry.rotateX(Math.PI/2) 315 roadwayFloorGeometry.translate(0, bridgeHeight+roadwayHeight, 0) 316 317 textures.tierra.object.wrapS = THREE.MirroredRepeatWrapping; 318 textures.tierra.object.wrapT = THREE.MirroredRepeatWrapping; 319 textures.tierra.object.repeat.set(1, 5); 320 textures.tierra.object.anisotropy = 16; 321 322 const roadwayFloorMaterial = new THREE.MeshPhongMaterial({ 323 side: THREE.DoubleSide, 324 transparent: false, 325 opacity: 1.0, 326 shininess: 10, 327 map: textures.tierra.object 328 }); 329 330 const roadwayFloor = new THREE.Mesh(roadwayFloorGeometry, roadwayFloorMaterial); 331 scene.add(roadwayFloor) 332 } 333 334 function mainLoop() { 335 requestAnimationFrame(mainLoop); 336 renderer.render(scene, camera); 337 } 338 339 function main() { 340 generateBridge(); 341 mainLoop(); 342 } 343 344 setupThreeJs(); 345 loadTextures(main);