TA159

Notas, resueltos y trabajos practicos de la materia Sistemas Gráficos
Index Commits Files Refs Submodules README LICENSE
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);