ej/parametric_surface/src/main.js (14227B)
1 import * as THREE from 'three'; 2 import * as dat from 'dat.gui'; 3 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; 4 5 import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js'; 6 import { ParametricGeometries } from 'three/examples/jsm/geometries/ParametricGeometries.js'; 7 import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js'; 8 9 let scene, camera, renderer, container, terrainMaterial, instancedTrees; 10 let spherePath; 11 let railsPath; 12 let railsFoundationShape; 13 14 let controls; 15 let shapeBase, shapeNarrow; 16 17 import tierraUrl from '/src/assets/tierra.jpg' 18 import rocaUrl from '/src/assets/roca.jpg' 19 import pastoUrl from '/src/assets/pasto.jpg' 20 import durmientesUrl from '/src/assets/durmientes.jpg' 21 22 const textures = { 23 tierra: { url: tierraUrl, object: null }, 24 roca: { url: rocaUrl, object: null }, 25 pasto: { url: pastoUrl, object: null }, 26 durmientes: { url: durmientesUrl, object: null }, 27 }; 28 29 function onResize() { 30 camera.aspect = container.offsetWidth / container.offsetHeight; 31 const rotMatrix = new THREE.Matrix4(); 32 renderer.setSize(container.offsetWidth, container.offsetHeight); 33 } 34 35 function setupThreeJs() { 36 scene = new THREE.Scene(); 37 container = document.getElementById('mainContainer'); 38 39 renderer = new THREE.WebGLRenderer(); 40 renderer.setClearColor(0x606060); 41 container.appendChild(renderer.domElement); 42 43 camera = new THREE.PerspectiveCamera( 44 35, window.innerWidth / window.innerHeight, 0.1, 1000); 45 46 camera.position.set(10, 20, 10); 47 camera.lookAt(0, 0, 0); 48 49 controls = new OrbitControls(camera, renderer.domElement); 50 controls.update(); 51 52 const ambientLight = new THREE.AmbientLight(0xffffff); 53 scene.add(ambientLight); 54 55 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x000000, 0.25); 56 scene.add(hemisphereLight); 57 58 const directionalLight = new THREE.DirectionalLight(0xffffff, 1); 59 directionalLight.position.set(100, 100, 100); 60 scene.add(directionalLight); 61 62 const gridHelper = new THREE.GridHelper(50, 50); 63 scene.add(gridHelper); 64 65 const axesHelper = new THREE.AxesHelper(1); 66 scene.add(axesHelper); 67 68 window.addEventListener('resize', onResize); 69 onResize(); 70 } 71 72 function onTextureLoaded(key, texture) { 73 texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 74 textures[key].object = texture; 75 console.log('Texture `' + key + '` loaded'); 76 } 77 78 function loadTextures(callback) { 79 const loadingManager = new THREE.LoadingManager(); 80 81 loadingManager.onLoad = () => { 82 console.log('All textures loaded'); 83 callback(); 84 }; 85 86 for (const key in textures) { 87 console.log("Loading textures"); 88 const loader = new THREE.TextureLoader(loadingManager); 89 const texture = textures[key]; 90 texture.object = loader.load( 91 texture.url, 92 onTextureLoaded.bind(this, key), 93 null, 94 (error) => { 95 console.error(error); 96 } 97 ); 98 } 99 } 100 101 function parametricRailsFoundationFunction(u, v, target) { 102 const rotMatrix = new THREE.Matrix4(); 103 const translationMatrix = new THREE.Matrix4(); 104 const levelMatrix = new THREE.Matrix4(); 105 106 let railsPathPos = railsPath.getPointAt(v); 107 let railsFoundationShapePos = railsFoundationShape.getPointAt(u); 108 // TODO: make `railsFoundationShape` smaller and remove this multiplication 109 railsFoundationShapePos.multiplyScalar(0.5); 110 111 let tangente = new THREE.Vector3(); 112 let binormal = new THREE.Vector3(); 113 let normal = new THREE.Vector3(); 114 115 tangente = railsPath.getTangent(v); 116 117 tangente.normalize(); 118 binormal = new THREE.Vector3(0, 1, 0); 119 normal.crossVectors(tangente, binormal); 120 121 translationMatrix.makeTranslation(railsPathPos); 122 123 rotMatrix.identity(); 124 levelMatrix.identity(); 125 126 levelMatrix.makeTranslation(railsPathPos); 127 rotMatrix.makeBasis(normal, tangente, binormal); 128 levelMatrix.multiply(rotMatrix); 129 railsFoundationShapePos.applyMatrix4(levelMatrix); 130 131 const x = railsFoundationShapePos.x; 132 const y = railsFoundationShapePos.y; 133 const z = railsFoundationShapePos.z; 134 target.set(x, y, z); 135 } 136 137 138 export function buildRailsFoundation() { 139 railsFoundationShape = new THREE.CatmullRomCurve3([ 140 new THREE.Vector3( -2.00, 0.00, 0.00), 141 new THREE.Vector3( -1.00, 0.00, 0.50), 142 new THREE.Vector3( 0.00, 0.00, 0.55), 143 new THREE.Vector3( 1.00, 0.00, 0.50), 144 new THREE.Vector3( 2.00, 0.00, 0.00), 145 ], false); 146 147 // show rails foundation shape 148 const points = railsFoundationShape.getPoints(50); 149 const geometry = new THREE.BufferGeometry().setFromPoints(points); 150 const lineMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 }); 151 const curveObject = new THREE.Line(geometry, lineMaterial); 152 scene.add(curveObject); 153 154 const pGeometry = new ParametricGeometry( 155 parametricRailsFoundationFunction, 4, 50); // paso de discretizacion u y v 156 157 textures.durmientes.object.wrapS = THREE.RepeatWrapping; 158 textures.durmientes.object.wrapT = THREE.RepeatWrapping; 159 textures.durmientes.object.repeat.set(1, 60); 160 textures.durmientes.object.anisotropy = 16; 161 162 // load into `map` the example texture 163 const map = new THREE.TextureLoader().load('https://threejs.org/examples/textures/uv_grid_opengl.jpg'); 164 map.wrapS = map.wrapT = THREE.RepeatWrapping; 165 map.repeat.set(1, 30); 166 map.anisotropy = 16; 167 // map.rotation = Math.PI/2; 168 169 const pMaterial = new THREE.MeshPhongMaterial({ 170 side: THREE.DoubleSide, 171 transparent: false, 172 opacity: 1.0, 173 shininess: 10, 174 // map: textures.durmientes.object 175 map: map 176 }); 177 const pMesh = new THREE.Mesh(pGeometry, pMaterial); 178 pMesh.receiveShadow = true; 179 pMesh.castShadow = true; 180 scene.add(pMesh); 181 } 182 183 // `position` es de tipo `THREE.Vector3` y representa la translacion de la 184 // forma del rail con respecto al origen del sist. de coordenadas de modelado 185 function getParametricRailsFunction(radius, position) { 186 return function parametricRails(u, v, target) { 187 const rotMatrix = new THREE.Matrix4(); 188 const translationMatrix = new THREE.Matrix4(); 189 const levelMatrix = new THREE.Matrix4(); 190 191 let railsShape = new THREE.Vector3(); 192 193 let railsPathPos = railsPath.getPointAt(v); 194 195 // railsShapePos es un cilindro 196 let railsShapePos = new THREE.Vector3( 197 Math.cos(u*6.28) + position.x, 198 position.y, 199 Math.sin(u*6.28) + position.z); 200 201 railsShapePos.multiplyScalar(0.1*railsRadius); 202 203 let tangente = new THREE.Vector3(); 204 let binormal = new THREE.Vector3(); 205 let normal = new THREE.Vector3(); 206 207 // https://threejs.org/docs/index.html?q=curve#api/en/extras/core/Curve.getTangent 208 tangente = railsPath.getTangentAt(v); 209 binormal = new THREE.Vector3(0, 1, 0); 210 normal.crossVectors(tangente, binormal); 211 212 translationMatrix.makeTranslation(railsPathPos); 213 214 rotMatrix.identity(); 215 levelMatrix.identity(); 216 217 levelMatrix.makeTranslation(railsPathPos); 218 rotMatrix.makeBasis(normal, tangente, binormal); 219 220 levelMatrix.multiply(rotMatrix); 221 railsShapePos.applyMatrix4(levelMatrix); 222 223 const x = railsShapePos.x; 224 const y = railsShapePos.y; 225 const z = railsShapePos.z; 226 target.set(x, y, z); 227 } 228 } 229 230 const railsRadius = 0.35; 231 function buildRails() { 232 let railsGeometries = []; 233 234 const leftRailGeometryFunction = getParametricRailsFunction(railsRadius, 235 new THREE.Vector3( 6, 0, railsRadius+8)); 236 237 const rightRailGeometryFunction = getParametricRailsFunction(railsRadius, 238 new THREE.Vector3(-6, 0, railsRadius+8)); 239 240 const leftRailGeometry = new ParametricGeometry(leftRailGeometryFunction, 100, 500); 241 const rightRailGeometry = new ParametricGeometry(rightRailGeometryFunction, 100, 500); 242 243 railsGeometries.push(leftRailGeometry); 244 railsGeometries.push(rightRailGeometry); 245 246 const railsMaterial = new THREE.MeshPhongMaterial({ 247 side: THREE.DoubleSide, 248 transparent: false, 249 opacity: 1.0, 250 shininess: 10, 251 color: 0xFFFFFF 252 }); 253 254 const railsGeometry = mergeGeometries(railsGeometries); 255 const rails = new THREE.Mesh(railsGeometry, railsMaterial); 256 rails.castShadow = true; 257 scene.add(rails); 258 } 259 260 function buildRailsPath() { 261 railsPath = new THREE.CatmullRomCurve3([ 262 new THREE.Vector3(-10, 0, 10), 263 new THREE.Vector3( 10, 0, 10), 264 new THREE.Vector3( 10, 0, -10), 265 new THREE.Vector3(-10, 0, -10), 266 ], true, 'catmullrom', 1.0); 267 268 // muestra la curva utilizada para el camino de `rails` 269 const railsPathPoints = railsPath.getPoints(50); 270 const railsPathGeometry = new THREE.BufferGeometry().setFromPoints(railsPathPoints); 271 const railsPathMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 }); 272 const railsPathMesh = new THREE.Line(railsPathGeometry, railsPathMaterial); 273 scene.add(railsPathMesh); 274 } 275 276 function shapeExample() { 277 const arcRadius = 1; 278 279 shapeBase = new THREE.Shape() 280 .moveTo(-1, -1.5) 281 .lineTo(1, -1.5) 282 .lineTo(1, -1) 283 .arc(0, arcRadius, arcRadius, -Math.PI/2, Math.PI/2, false) 284 .lineTo(1, 1.5) 285 .lineTo(-1, 1.5) 286 .lineTo(-1, 1) 287 .arc(0, -arcRadius, arcRadius, Math.PI/2, -Math.PI/2, false) 288 .lineTo(-1, -1.5); 289 290 shapeNarrow = new THREE.Shape() 291 .moveTo(-1.0, -1.0) 292 .lineTo(-1.0, -0.5) 293 .lineTo( 1.0, -0.5) 294 .lineTo( 1.0, -1.0) 295 .arc(0, arcRadius, arcRadius, -Math.PI/2, Math.PI/2, false) 296 .lineTo( 1.0, 0.5) 297 .lineTo(-1.0, 0.5) 298 .lineTo(-1.0, 1.0) 299 .arc(0, -arcRadius, arcRadius, Math.PI/2, -Math.PI/2, false); 300 301 const pointsBase = shapeBase.getPoints(); 302 const geometryBase = new THREE.BufferGeometry().setFromPoints(pointsBase); 303 const materialBase = new THREE.LineBasicMaterial({ color: 0xff0000 }); 304 const lineBase = new THREE.Line(geometryBase, materialBase); 305 // lineBase.rotation.x = Math.PI / 2; // de XY a XZ 306 307 const pointsNarrow = shapeNarrow.getPoints(); 308 const geometryNarrow = new THREE.BufferGeometry().setFromPoints(pointsNarrow); 309 const materialNarrow = new THREE.LineBasicMaterial({ color: 0x0000FF }); 310 const lineNarrow = new THREE.Line(geometryNarrow, materialNarrow); 311 // lineNarrow.rotation.x = Math.PI / 2; // de XY a XZ 312 313 // scene.add(lineBase); 314 // scene.add(lineNarrow); 315 } 316 317 function shapeExtrude(shape, color) { 318 const extrudeSettings = { 319 steps: 10, 320 depth: 5, 321 bevelEnabled: false, 322 }; 323 324 const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings ); 325 const material = new THREE.MeshPhongMaterial( { color: color } ); 326 const mesh = new THREE.Mesh( geometry, material ) ; 327 scene.add(mesh); 328 mesh.rotation.x =-Math.PI/2; 329 } 330 331 /* 332 if (v <= 0.2 || v >= 0.8) { 333 point = pointNarrow; 334 } else if (v <= 0.3) { 335 const interpolationFactor = (v - 0.3)/0.1; 336 337 point.x = THREE.MathUtils.lerp(pointNarrow.x, pointBase.x, interpolationFactor); 338 point.y = THREE.MathUtils.lerp(pointNarrow.y, pointBase.y, interpolationFactor); 339 } else if (v >= 0.7 && v <= 0.8) { 340 const interpolationFactor = (v - 0.7)/0.1; 341 342 point.x = THREE.MathUtils.lerp(pointBase.x, pointNarrow.x, interpolationFactor); 343 point.y = THREE.MathUtils.lerp(pointBase.y, pointNarrow.y, interpolationFactor); 344 } else { 345 point = pointBase; 346 } 347 */ 348 function parametricFunction(u, v, target) { 349 let point = new THREE.Vector2(0, 0); 350 const pointBase = shapeBase.getPointAt(u); 351 const pointNarrow = shapeNarrow.getPointAt(u); 352 // const pointNarrow = shapeBase.getPointAt(u); 353 354 // if (v <= 0.40) { 355 // point = pointNarrow; 356 // } else if (v >= 0.60) { 357 // point = pointBase; 358 // } else { // v en [0.45 - 0.55] 359 // const interpolationFactor = (v - 0.40)/0.2; 360 // console.log(interpolationFactor); 361 // point.lerpVectors(pointNarrow, pointBase, interpolationFactor); 362 // } 363 364 point = new THREE.Vector3(pointBase.x, pointBase.y, 10*v); 365 // point.applyAxisAngle(new THREE.Vector3(0, 0, 1), 2*Math.PI*v); 366 367 const rotMatrix = new THREE.Matrix4(); 368 rotMatrix.makeRotationAxis(new THREE.Vector3(0, 0, 1), Math.PI*v); 369 // rotMatrix.makeBasis(normal, tangente, binormal); 370 371 point.applyMatrix4(rotMatrix); 372 target.set(point.x, point.y, point.z); 373 } 374 375 function generateTunnelGeometry( 376 tunnelHeight = 20, tunnelWidth = 14, 377 tunnelWallThickness = 0.5, tunnelLen = 26) { 378 379 const path = new THREE.Path(); 380 path.moveTo(-tunnelWidth/2, 0); 381 path.lineTo(-tunnelWidth/2, tunnelHeight*1/3); 382 path.moveTo(-tunnelWidth/2, tunnelHeight*1/3); 383 path.quadraticCurveTo(0, tunnelHeight, tunnelWidth/2, tunnelHeight*1/3); 384 path.moveTo(tunnelWidth/2, 0); 385 path.lineTo(tunnelWidth/2, 0); 386 387 388 // cerramos la curva con otra de la misma forma con una diferencia de 389 // `tunnelWallThickness` 390 path.lineTo(tunnelWidth/2-tunnelWallThickness, 0); 391 path.moveTo(tunnelWidth/2-tunnelWallThickness, 0); 392 393 path.lineTo(tunnelWidth/2-tunnelWallThickness, tunnelHeight*1/3); 394 path.moveTo(tunnelWidth/2-tunnelWallThickness, tunnelHeight*1/3); 395 396 path.quadraticCurveTo( 397 0, tunnelHeight-(tunnelWallThickness*2), 398 -tunnelWidth/2+tunnelWallThickness, tunnelHeight*1/3); 399 400 path.lineTo(-tunnelWidth/2+tunnelWallThickness, 0); 401 path.moveTo(-tunnelWidth/2+tunnelWallThickness, 0); 402 403 path.lineTo(-tunnelWidth/2, 0); 404 path.moveTo(-tunnelWidth/2, 0); 405 406 const pathPoints = path.getPoints(50); 407 const pathGeometry = new THREE.BufferGeometry().setFromPoints(pathPoints); 408 const pathMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 }); 409 const pathObject = new THREE.Line(pathGeometry, pathMaterial); 410 scene.add(pathObject); 411 return; 412 413 // const points = path.getPoints(); 414 // const shape = new THREE.Shape(points); 415 const extrudeSettings = { 416 curveSegments: 24, 417 steps: 50, 418 depth: tunnelLen, 419 }; 420 421 const geometry = new THREE.ExtrudeGeometry(path, extrudeSettings); 422 423 // el `1` en `x` es porque por algun motivo queda descentrado con respecto 424 // a la vía del tren 425 geometry.translate(1, 0, -tunnelLen/2); 426 return geometry; 427 } 428 429 function generateTunnel() { 430 const geometry = generateTunnelGeometry(); 431 const material = new THREE.MeshPhongMaterial({ color: 0xFFFF00, side: THREE.DoubleSide }); 432 const mesh = new THREE.Mesh( geometry, material ); 433 scene.add(mesh); 434 } 435 436 function shapeSweep(shape) { 437 const geometry = new ParametricGeometry(parametricFunction, 100, 100); 438 const material = new THREE.MeshPhongMaterial({ color: 0xFFFF00, side: THREE.DoubleSide }); 439 const mesh = new THREE.Mesh( geometry, material ); 440 mesh.rotation.x = -Math.PI/2; 441 scene.add(mesh); 442 } 443 444 function mainLoop() { 445 requestAnimationFrame(mainLoop); 446 renderer.render(scene, camera); 447 } 448 449 function main() { 450 shapeExample(); 451 // shapeExtrude(shapeBase, 0xCF4040); 452 // shapeExtrude(shapeNarrow, 0x5050FF); 453 shapeSweep(); 454 // generateTunnel(); 455 456 // buildRailsPath(); 457 // buildRailsFoundation(); 458 // buildRails(); 459 460 mainLoop(); 461 } 462 463 setupThreeJs(); 464 loadTextures(main);
