All files / engine/Source/Core TerrainMesh.js

100% Statements 61/61
90% Branches 9/10
100% Functions 7/7
100% Lines 61/61

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362                                                                                                                                    944x                   944x               944x           944x           944x           944x           944x           944x           944x           944x               944x           944x           944x           944x           944x           944x           944x               944x           944x           944x                   1x 90x 12x   78x   78x 59x     19x       59x 59x   59x           59x           59x               59x     1x 1x 1x 1x 1x 1x                       19x 19x   19x           19x           19x                   19x                   19x 19x             19x             19x 19x 19x   19x                       1x 90x                             1x           93x 93x 93x               1x 15x 15x        
import SceneMode from "../Scene/SceneMode.js";
import Cartesian3 from "./Cartesian3.js";
import Cartographic from "./Cartographic.js";
import defined from "./defined.js";
import Ellipsoid from "./Ellipsoid.js";
import Matrix4 from "./Matrix4.js";
import OrientedBoundingBox from "./OrientedBoundingBox.js";
import TerrainPicker from "./TerrainPicker.js";
import Transforms from "./Transforms.js";
import VerticalExaggeration from "./VerticalExaggeration.js";
 
/**
 * A mesh plus related metadata for a single tile of terrain.  Instances of this type are
 * usually created from raw {@link TerrainData}.
 *
 * @alias TerrainMesh
 * @constructor
 *
 * @param {Cartesian3} center The center of the tile.  Vertex positions are specified relative to this center.
 * @param {Float32Array} vertices The vertex data, including positions, texture coordinates, and heights.
 *                       The vertex data is in the order [X, Y, Z, H, U, V], where X, Y, and Z represent
 *                       the Cartesian position of the vertex, H is the height above the ellipsoid, and
 *                       U and V are the texture coordinates.
 * @param {Uint8Array|Uint16Array|Uint32Array} indices The indices describing how the vertices are connected to form triangles.
 * @param {number} indexCountWithoutSkirts The index count of the mesh not including skirts.
 * @param {number} vertexCountWithoutSkirts The vertex count of the mesh not including skirts.
 * @param {number} minimumHeight The lowest height in the tile, in meters above the ellipsoid.
 * @param {number} maximumHeight The highest height in the tile, in meters above the ellipsoid.
 * @param {Rectangle} rectangle The rectangle, in radians, covered by this tile.
 * @param {BoundingSphere} boundingSphere3D A bounding sphere that completely contains the tile.
 * @param {Cartesian3} occludeePointInScaledSpace The occludee point of the tile, represented in ellipsoid-
 *                     scaled space, and used for horizon culling.  If this point is below the horizon,
 *                     the tile is considered to be entirely below the horizon.
 * @param {number} [vertexStride=6] The number of components in each vertex.
 * @param {OrientedBoundingBox} [orientedBoundingBox] A bounding box that completely contains the tile.
 * @param {TerrainEncoding} encoding Information used to decode the mesh.
 * @param {number[]|Uint8Array|Uint16Array|Uint32Array} westIndicesSouthToNorth The indices of the vertices on the Western edge of the tile, ordered from South to North (clockwise).
 * @param {number[]|Uint8Array|Uint16Array|Uint32Array} southIndicesEastToWest The indices of the vertices on the Southern edge of the tile, ordered from East to West (clockwise).
 * @param {number[]|Uint8Array|Uint16Array|Uint32Array} eastIndicesNorthToSouth The indices of the vertices on the Eastern edge of the tile, ordered from North to South (clockwise).
 * @param {number[]|Uint8Array|Uint16Array|Uint32Array} northIndicesWestToEast The indices of the vertices on the Northern edge of the tile, ordered from West to East (clockwise).
 *
 * @private
 */
function TerrainMesh(
  center,
  vertices,
  indices,
  indexCountWithoutSkirts,
  vertexCountWithoutSkirts,
  minimumHeight,
  maximumHeight,
  rectangle,
  boundingSphere3D,
  occludeePointInScaledSpace,
  vertexStride,
  orientedBoundingBox,
  encoding,
  westIndicesSouthToNorth,
  southIndicesEastToWest,
  eastIndicesNorthToSouth,
  northIndicesWestToEast,
) {
  /**
   * The center of the tile.  Vertex positions are specified relative to this center.
   * @type {Cartesian3}
   */
  this.center = center;
 
  /**
   * The vertex data, including positions, texture coordinates, and heights.
   * The vertex data is in the order [X, Y, Z, H, U, V], where X, Y, and Z represent
   * the Cartesian position of the vertex, H is the height above the ellipsoid, and
   * U and V are the texture coordinates.  The vertex data may have additional attributes after those
   * mentioned above when the {@link TerrainMesh#stride} is greater than 6.
   * @type {Float32Array}
   */
  this.vertices = vertices;
 
  /**
   * The number of components in each vertex.  Typically this is 6 for the 6 components
   * [X, Y, Z, H, U, V], but if each vertex has additional data (such as a vertex normal), this value
   * may be higher.
   * @type {number}
   */
  this.stride = vertexStride ?? 6;
 
  /**
   * The indices describing how the vertices are connected to form triangles.
   * @type {Uint8Array|Uint16Array|Uint32Array}
   */
  this.indices = indices;
 
  /**
   * The index count of the mesh not including skirts.
   * @type {number}
   */
  this.indexCountWithoutSkirts = indexCountWithoutSkirts;
 
  /**
   * The vertex count of the mesh not including skirts.
   * @type {number}
   */
  this.vertexCountWithoutSkirts = vertexCountWithoutSkirts;
 
  /**
   * The lowest height in the tile, in meters above the ellipsoid.
   * @type {number}
   */
  this.minimumHeight = minimumHeight;
 
  /**
   * The highest height in the tile, in meters above the ellipsoid.
   * @type {number}
   */
  this.maximumHeight = maximumHeight;
 
  /**
   * The rectangle, in radians, covered by this tile.
   * @type {Rectangle}
   */
  this.rectangle = rectangle;
 
  /**
   * A bounding sphere that completely contains the tile.
   * @type {BoundingSphere}
   */
  this.boundingSphere3D = boundingSphere3D;
 
  /**
   * The occludee point of the tile, represented in ellipsoid-
   * scaled space, and used for horizon culling.  If this point is below the horizon,
   * the tile is considered to be entirely below the horizon.
   * @type {Cartesian3}
   */
  this.occludeePointInScaledSpace = occludeePointInScaledSpace;
 
  /**
   * A bounding box that completely contains the tile.
   * @type {OrientedBoundingBox}
   */
  this.orientedBoundingBox = orientedBoundingBox;
 
  /**
   * Information for decoding the mesh vertices.
   * @type {TerrainEncoding}
   */
  this.encoding = encoding;
 
  /**
   * The indices of the vertices on the Western edge of the tile, ordered from South to North (clockwise).
   * @type {number[]|Uint8Array|Uint16Array|Uint32Array}
   */
  this.westIndicesSouthToNorth = westIndicesSouthToNorth;
 
  /**
   * The indices of the vertices on the Southern edge of the tile, ordered from East to West (clockwise).
   * @type {number[]|Uint8Array|Uint16Array|Uint32Array}
   */
  this.southIndicesEastToWest = southIndicesEastToWest;
 
  /**
   * The indices of the vertices on the Eastern edge of the tile, ordered from North to South (clockwise).
   * @type {number[]|Uint8Array|Uint16Array|Uint32Array}
   */
  this.eastIndicesNorthToSouth = eastIndicesNorthToSouth;
 
  /**
   * The indices of the vertices on the Northern edge of the tile, ordered from West to East (clockwise).
   * @type {number[]|Uint8Array|Uint16Array|Uint32Array}
   */
  this.northIndicesWestToEast = northIndicesWestToEast;
 
  /**
   * The transform from model to world coordinates based on the terrain mesh's oriented bounding box.
   * In 3D mode, this is computed from the oriented bounding box.  In 2D and Columbus View modes,
   * this is computed from the tile's rectangle's projected coordinates.
   * @type {Matrix4}
   */
  this._transform = new Matrix4();
 
  /**
   * True if the transform needs to be recomputed (due to changes in exaggeration or scene mode).
   * @type {boolean}
   */
  this._recomputeTransform = true;
 
  /**
   * The terrain picker for this mesh, used for ray intersection tests.
   * @type {TerrainPicker}
   */
  this._terrainPicker = new TerrainPicker(vertices, indices, encoding);
}
 
/**
 * Get the terrain tile's model-to-world transform matrix for the given scene mode and projection.
 * @param {SceneMode} mode The scene mode (3D, 2D, or Columbus View).
 * @param {MapProjection} projection The map projection.
 * @returns {Matrix4} The transform matrix.
 * @private
 */
TerrainMesh.prototype.getTransform = function (mode, projection) {
  if (!this._recomputeTransform) {
    return this._transform;
  }
  this._recomputeTransform = false;
 
  if (!defined(mode) || mode === SceneMode.SCENE3D) {
    return computeTransform(this, this._transform);
  }
 
  return computeTransform2D(this, projection, this._transform);
};
 
function computeTransform(mesh, result) {
  const exaggeration = mesh.encoding.exaggeration;
  const exaggerationRelativeHeight = mesh.encoding.exaggerationRelativeHeight;
 
  const exaggeratedMinHeight = VerticalExaggeration.getHeight(
    mesh.minimumHeight,
    exaggeration,
    exaggerationRelativeHeight,
  );
 
  const exaggeratedMaxHeight = VerticalExaggeration.getHeight(
    mesh.maximumHeight,
    exaggeration,
    exaggerationRelativeHeight,
  );
 
  const obb = OrientedBoundingBox.fromRectangle(
    mesh.rectangle,
    exaggeratedMinHeight,
    exaggeratedMaxHeight,
    Ellipsoid.default,
    mesh.orientedBoundingBox,
  );
 
  return OrientedBoundingBox.computeTransformation(obb, result);
}
 
const scratchSWCartesian = new Cartesian3();
const scratchNECartesian = new Cartesian3();
const scratchSWCartographic = new Cartographic();
const scratchNECartographic = new Cartographic();
const scratchScale2D = new Cartesian3();
const scratchCenter2D = new Cartesian3();
 
/**
 * Get the terrain tile's model-to-world transform matrix for 2D or Columbus View modes.
 * Assumes tiles in 2D are axis-aligned and still rectangular. (This is true for Web Mercator and Geographic projections.)
 * @param {TerrainMesh} mesh The terrain mesh.
 * @param {MapProjection} projection The map projection.
 * @param {Matrix4} result The object in which to store the result.
 * @returns {Matrix4} The transform matrix.
 * @private
 */
function computeTransform2D(mesh, projection, result) {
  const exaggeration = mesh.encoding.exaggeration;
  const exaggerationRelativeHeight = mesh.encoding.exaggerationRelativeHeight;
 
  const exaggeratedMinHeight = VerticalExaggeration.getHeight(
    mesh.minimumHeight,
    exaggeration,
    exaggerationRelativeHeight,
  );
 
  const exaggeratedMaxHeight = VerticalExaggeration.getHeight(
    mesh.maximumHeight,
    exaggeration,
    exaggerationRelativeHeight,
  );
 
  const southwest = projection.project(
    Cartographic.fromRadians(
      mesh.rectangle.west,
      mesh.rectangle.south,
      0,
      scratchSWCartographic,
    ),
    scratchSWCartesian,
  );
 
  const northeast = projection.project(
    Cartographic.fromRadians(
      mesh.rectangle.east,
      mesh.rectangle.north,
      0,
      scratchNECartographic,
    ),
    scratchNECartesian,
  );
 
  const heightRange = exaggeratedMaxHeight - exaggeratedMinHeight;
  const scale = Cartesian3.fromElements(
    northeast.x - southwest.x,
    northeast.y - southwest.y,
    heightRange > 0 ? heightRange : 1.0, // Avoid zero scale
    scratchScale2D,
  );
 
  const center = Cartesian3.fromElements(
    southwest.x + scale.x * 0.5,
    southwest.y + scale.y * 0.5,
    exaggeratedMinHeight + scale.z * 0.5,
    scratchCenter2D,
  );
 
  Matrix4.fromTranslation(center, result);
  Matrix4.setScale(result, scale, result);
  Matrix4.multiply(Transforms.SWIZZLE_3D_TO_2D_MATRIX, result, result);
 
  return result;
}
 
/**
 * Gives the point on this terrain tile where the given ray intersects
 * @param {Ray} ray The ray to test for intersection.
 * @param {boolean} cullBackFaces Whether to consider back-facing triangles as intersections.
 * @param {SceneMode} mode The scene mode (3D, 2D, or Columbus View).
 * @param {MapProjection} projection The map projection.
 * @returns {Cartesian3} The point on the mesh where the ray intersects, or undefined if there is no intersection.
 * @private
 */
TerrainMesh.prototype.pick = function (ray, cullBackFaces, mode, projection) {
  return this._terrainPicker.rayIntersect(
    ray,
    this.getTransform(mode, projection),
    cullBackFaces,
    mode,
    projection,
  );
};
 
/**
 * Updates the terrain mesh to account for changes in vertical exaggeration.
 * @param {Number} exaggeration A scalar used to exaggerate terrain.
 * @param {Number} exaggerationRelativeHeight The relative height from which terrain is exaggerated.
 * @private
 */
TerrainMesh.prototype.updateExaggeration = function (
  exaggeration,
  exaggerationRelativeHeight,
) {
  // The encoding stored on the TerrainMesh references the updated exaggeration values already. This is just used
  // to trigger a rebuild on the terrain picker.
  this._terrainPicker._vertices = this.vertices;
  this._terrainPicker.needsRebuild = true;
  this._recomputeTransform = true;
};
 
/**
 * Updates the terrain mesh to account for changes in scene mode.
 * @param {SceneMode} mode The scene mode (3D, 2D, or Columbus View).
 * @private
 */
TerrainMesh.prototype.updateSceneMode = function (mode) {
  this._terrainPicker.needsRebuild = true;
  this._recomputeTransform = true;
};
 
export default TerrainMesh;