TA159

Notas, resueltos y trabajos practicos de la materia Sistemas Gráficos
Index Commits Files Refs Submodules README LICENSE
tp/src/trees.js (6516B)
   1 import * as THREE from 'three';
   2 
   3 let treesForbiddenMapData, treesForbiddenMap, elevationMap, elevationMapData;
   4 
   5 import elevationMapUrl     from './assets/elevation_map_wider_river.png'
   6 import treeForbiddenMapUrl from './assets/tree_forbidden_zone_map_wider_path.png'
   7 
   8 const textures = {
   9     elevationMap:     { url: elevationMapUrl, object: null },
  10     treeForbiddenMap: { url: treeForbiddenMapUrl, object: null }
  11 };
  12 
  13 const widthSegments   = 100;
  14 const heightSegments  = 100;
  15 const amplitude       = 10;
  16 const amplitudeBottom = -1.00;
  17 const imgWidth  = 512;
  18 const imgHeight = 512;
  19 
  20 function getPixel(imgData, index) {
  21   let i = index*4, d = imgData.data
  22   return [d[i],d[i+1],d[i+2],d[i+3]] // Returns array [R,G,B,A]
  23 }
  24 
  25 function getPixelXY(imgData, x, y) {
  26   return getPixel(imgData, y*imgData.width+x)
  27 }
  28 
  29 // position: Vector3
  30 function isForbbidenPosition(position) {
  31     const x = Math.floor(position.x);
  32     const y = position.y;
  33     const z = Math.floor(position.z);
  34 
  35     // TODO: estos valores deberian depender de la posicion del terreno
  36     if((y > 6.8) || (y < 3.25)) {
  37         // console.log("(" + position.x + ", " + position.y + ", " + position.z + ") is not valid ");
  38         return true;
  39     }
  40     
  41     let pixelArray = getPixelXY(treesForbiddenMap, x, z);
  42     const R = pixelArray[0]; // Red
  43     const G = pixelArray[1]; // Green
  44     const B = pixelArray[2]; // Blue
  45     const A = pixelArray[3]; // Alpha
  46     // const pixel = new THREE.Vector4(R, G, B, A);
  47 
  48     if(((R <= 10) && (G >= 250) && (B <= 10))
  49         || (R <= 80) && (G <= 80) && (B <= 80)
  50         || (R >= 200) && (G >= 200) && (B >= 200)) {
  51         // console.log("(" + position.x + ", " + position.y + ", " + position.z + ") is not valid ");
  52         return true;
  53     }
  54 
  55     // console.log("(" + position.x + ", " + position.y + ") is valid ");
  56     return false;
  57 }
  58 
  59 // obtiene una posicion aleatoria en el terreno, para obtener la altura del
  60 // terreno utiliza el mapa de elevacion.
  61 // `padding` permite definir un borde del cual no se toman puntos
  62 function getRandomPositionInTerrain(padding = 0) {
  63     const x = Math.floor(Math.random() * (widthSegments-(padding*2)));
  64     const z = Math.floor(Math.random() * (heightSegments-(padding*2)));
  65 
  66     const pixelArray = getPixelXY(elevationMap, x, z); // array [R,G,B,A]
  67     const y = (pixelArray[0]/255)*amplitude;
  68 
  69     const position = new THREE.Vector3(x+padding, y, z+padding);
  70     return position;
  71 }
  72 
  73 // devuelve un arreglo de 2 `instancedMesh` con los troncos y copas de los arboles
  74 export function createInstancedTrees(count) {
  75     console.log('Generating `' + count + '` instances of tree');
  76 
  77     let logHeight = 3.0;
  78     const treeLogGeometry   = new THREE.CylinderGeometry(
  79         0.10, 0.25, logHeight, 40, 40);
  80     treeLogGeometry.translate(0, logHeight/2.0, 0);
  81     const instancedTreeLogGeometry = new THREE.InstancedBufferGeometry();
  82     instancedTreeLogGeometry.copy(treeLogGeometry);
  83     const treeLogMaterial   = new THREE.MeshPhongMaterial({color: 0x7c3f00, side: THREE.FrontSide});
  84     const instancedTreeLogs = new THREE.InstancedMesh(
  85         instancedTreeLogGeometry,
  86         treeLogMaterial,
  87         count);
  88 
  89     const treeLeavesRadius = 1.25;
  90     const treeLeavesGeometry = new THREE.SphereGeometry(treeLeavesRadius,40,40);
  91     const instancedTreeLeavesGeometry = new THREE.InstancedBufferGeometry();
  92     instancedTreeLeavesGeometry.copy(treeLeavesGeometry);
  93     const treeLeavesMaterial  = new THREE.MeshPhongMaterial({color: 0x365829});
  94     const instancedTreeLeaves = new THREE.InstancedMesh(
  95         instancedTreeLeavesGeometry,
  96         treeLeavesMaterial,
  97         count);
  98 
  99     const rotMatrix         = new THREE.Matrix4();
 100     const translationMatrix = new THREE.Matrix4();
 101     const treeLogMatrix     = new THREE.Matrix4();
 102     const treeLeavesMatrix  = new THREE.Matrix4();
 103 
 104     const treesBorderPadding = 3.0;
 105     for (let i = 0; i < count; i++) {
 106         let position = getRandomPositionInTerrain(treesBorderPadding);
 107         for(let j = 0; isForbbidenPosition(position); ++j) {
 108             position = getRandomPositionInTerrain(treesBorderPadding);
 109             if(j++ == 1000) { // maximo de iteraciones
 110                 break;
 111             }
 112         }
 113 
 114         if(isForbbidenPosition(position)) {
 115             continue;
 116         }
 117 
 118         const treeOffset = -1.50;
 119 
 120         // 1.50 numbero magico para posicionar correctamente los arboles con
 121         // respecto al terreno
 122         position.x -= (widthSegments+treesBorderPadding+1.50)/2;
 123         position.y += (amplitudeBottom + treeOffset);
 124         position.z -= (heightSegments+treesBorderPadding)/2;
 125         translationMatrix.makeTranslation(position);
 126         treeLogMatrix.identity();
 127         treeLeavesMatrix.identity();
 128 
 129         let scale = 0.6 + (Math.random()*(logHeight/3));
 130         treeLogMatrix.makeScale(1, scale, 1);
 131         treeLogMatrix.premultiply(translationMatrix);
 132 
 133         position.y += scale*logHeight;
 134 
 135         translationMatrix.makeTranslation(position);
 136         treeLeavesMatrix.premultiply(translationMatrix);
 137 
 138         instancedTreeLogs.setMatrixAt(i, treeLogMatrix);
 139         instancedTreeLeaves.setMatrixAt(i, treeLeavesMatrix);
 140     }
 141 
 142     console.log('Done generating `' + count + '` instances of tree');
 143     return [instancedTreeLogs, instancedTreeLeaves];
 144 }
 145 
 146 function loadMapsData() {
 147     console.log("Loading maps data");
 148 
 149     // Creamos un canvas para poder leer los valores de los píxeles de la textura
 150     let canvas = document.createElement('canvas');
 151     let ctx = canvas.getContext('2d');
 152 
 153     let treesForbiddenMapImage = textures.treeForbiddenMap.object.image;
 154     let elevationMapImage = textures.elevationMap.object.image;
 155 
 156     // ambos mapas deben tener el mismo tamaño
 157     const imgWidth  = widthSegments;
 158     const imgHeight = heightSegments;
 159 
 160     canvas.width  = imgWidth;
 161     canvas.height = imgHeight;
 162 
 163     ctx.drawImage(treesForbiddenMapImage, 0, 0, imgWidth, imgHeight);
 164     treesForbiddenMap = ctx.getImageData(0, 0, imgWidth, imgHeight);
 165     treesForbiddenMapData = treesForbiddenMap.data;
 166 
 167     ctx.drawImage(elevationMapImage, 0, 0, imgWidth, imgHeight);
 168     elevationMap = ctx.getImageData(0, 0, imgWidth, imgHeight);
 169     elevationMapData = elevationMap.data
 170 
 171     console.log("All maps data loaded succesfully");
 172 }
 173 
 174 function onTextureLoaded(key, texture) {
 175     texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
 176     textures[key].object = texture;
 177     console.log('Texture `' + key + '` loaded');
 178 }
 179 
 180 function loadTextures(callback) {
 181     const loadingManager = new THREE.LoadingManager();
 182 
 183     loadingManager.onLoad = () => {
 184         console.log('All textures loaded');
 185         callback();
 186     };
 187 
 188     for (const key in textures) {
 189         console.log("Loading textures");
 190         const loader = new THREE.TextureLoader(loadingManager);
 191         const texture = textures[key];
 192         texture.object = loader.load(
 193             texture.url,
 194             onTextureLoaded.bind(this, key),
 195             null,
 196             (error) => {
 197                 console.error(error);
 198             }
 199         );
 200     }
 201 }
 202 
 203 loadTextures(loadMapsData);