TA159

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