| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 | import AttributeCompression from "../Core/AttributeCompression.js";import BoundingSphere from "../Core/BoundingSphere.js";import Cartesian2 from "../Core/Cartesian2.js";import Cartesian3 from "../Core/Cartesian3.js";import Cartographic from "../Core/Cartographic.js";import defined from "../Core/defined.js";import Ellipsoid from "../Core/Ellipsoid.js";import EllipsoidalOccluder from "../Core/EllipsoidalOccluder.js";import IndexDatatype from "../Core/IndexDatatype.js";import Intersections2D from "../Core/Intersections2D.js";import CesiumMath from "../Core/Math.js";import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";import Rectangle from "../Core/Rectangle.js";import TerrainEncoding from "../Core/TerrainEncoding.js";import createTaskProcessorWorker from "./createTaskProcessorWorker.js";const maxShort = 32767;const halfMaxShort = (maxShort / 2) | 0;const clipScratch = [];const clipScratch2 = [];const verticesScratch = [];const cartographicScratch = new Cartographic();let cartesian3Scratch = new Cartesian3();const uScratch = [];const vScratch = [];const heightScratch = [];const indicesScratch = [];const normalsScratch = [];const horizonOcclusionPointScratch = new Cartesian3();const boundingSphereScratch = new BoundingSphere();const orientedBoundingBoxScratch = new OrientedBoundingBox();const decodeTexCoordsScratch = new Cartesian2();const octEncodedNormalScratch = new Cartesian3();function upsampleQuantizedTerrainMesh(parameters, transferableObjects) {  const isEastChild = parameters.isEastChild;  const isNorthChild = parameters.isNorthChild;  const minU = isEastChild ? halfMaxShort : 0;  const maxU = isEastChild ? maxShort : halfMaxShort;  const minV = isNorthChild ? halfMaxShort : 0;  const maxV = isNorthChild ? maxShort : halfMaxShort;  const uBuffer = uScratch;  const vBuffer = vScratch;  const heightBuffer = heightScratch;  const normalBuffer = normalsScratch;  uBuffer.length = 0;  vBuffer.length = 0;  heightBuffer.length = 0;  normalBuffer.length = 0;  const indices = indicesScratch;  indices.length = 0;  const vertexMap = {};  const parentVertices = parameters.vertices;  let parentIndices = parameters.indices;  parentIndices = parentIndices.subarray(0, parameters.indexCountWithoutSkirts);  const encoding = TerrainEncoding.clone(parameters.encoding);  const hasVertexNormals = encoding.hasVertexNormals;  let vertexCount = 0;  const quantizedVertexCount = parameters.vertexCountWithoutSkirts;  const parentMinimumHeight = parameters.minimumHeight;  const parentMaximumHeight = parameters.maximumHeight;  const parentUBuffer = new Array(quantizedVertexCount);  const parentVBuffer = new Array(quantizedVertexCount);  const parentHeightBuffer = new Array(quantizedVertexCount);  const parentNormalBuffer = hasVertexNormals    ? new Array(quantizedVertexCount * 2)    : undefined;  const threshold = 20;  let height;  let i, n;  let u, v;  for (i = 0, n = 0; i < quantizedVertexCount; ++i, n += 2) {    const texCoords = encoding.decodeTextureCoordinates(      parentVertices,      i,      decodeTexCoordsScratch    );    height = encoding.decodeHeight(parentVertices, i);    u = CesiumMath.clamp((texCoords.x * maxShort) | 0, 0, maxShort);    v = CesiumMath.clamp((texCoords.y * maxShort) | 0, 0, maxShort);    parentHeightBuffer[i] = CesiumMath.clamp(      (((height - parentMinimumHeight) /        (parentMaximumHeight - parentMinimumHeight)) *        maxShort) |        0,      0,      maxShort    );    if (u < threshold) {      u = 0;    }    if (v < threshold) {      v = 0;    }    if (maxShort - u < threshold) {      u = maxShort;    }    if (maxShort - v < threshold) {      v = maxShort;    }    parentUBuffer[i] = u;    parentVBuffer[i] = v;    if (hasVertexNormals) {      const encodedNormal = encoding.getOctEncodedNormal(        parentVertices,        i,        octEncodedNormalScratch      );      parentNormalBuffer[n] = encodedNormal.x;      parentNormalBuffer[n + 1] = encodedNormal.y;    }    if (      ((isEastChild && u >= halfMaxShort) ||        (!isEastChild && u <= halfMaxShort)) &&      ((isNorthChild && v >= halfMaxShort) ||        (!isNorthChild && v <= halfMaxShort))    ) {      vertexMap[i] = vertexCount;      uBuffer.push(u);      vBuffer.push(v);      heightBuffer.push(parentHeightBuffer[i]);      if (hasVertexNormals) {        normalBuffer.push(parentNormalBuffer[n]);        normalBuffer.push(parentNormalBuffer[n + 1]);      }      ++vertexCount;    }  }  const triangleVertices = [];  triangleVertices.push(new Vertex());  triangleVertices.push(new Vertex());  triangleVertices.push(new Vertex());  const clippedTriangleVertices = [];  clippedTriangleVertices.push(new Vertex());  clippedTriangleVertices.push(new Vertex());  clippedTriangleVertices.push(new Vertex());  let clippedIndex;  let clipped2;  for (i = 0; i < parentIndices.length; i += 3) {    const i0 = parentIndices[i];    const i1 = parentIndices[i + 1];    const i2 = parentIndices[i + 2];    const u0 = parentUBuffer[i0];    const u1 = parentUBuffer[i1];    const u2 = parentUBuffer[i2];    triangleVertices[0].initializeIndexed(      parentUBuffer,      parentVBuffer,      parentHeightBuffer,      parentNormalBuffer,      i0    );    triangleVertices[1].initializeIndexed(      parentUBuffer,      parentVBuffer,      parentHeightBuffer,      parentNormalBuffer,      i1    );    triangleVertices[2].initializeIndexed(      parentUBuffer,      parentVBuffer,      parentHeightBuffer,      parentNormalBuffer,      i2    );    // Clip triangle on the east-west boundary.    const clipped = Intersections2D.clipTriangleAtAxisAlignedThreshold(      halfMaxShort,      isEastChild,      u0,      u1,      u2,      clipScratch    );    // Get the first clipped triangle, if any.    clippedIndex = 0;    if (clippedIndex >= clipped.length) {      continue;    }    clippedIndex = clippedTriangleVertices[0].initializeFromClipResult(      clipped,      clippedIndex,      triangleVertices    );    if (clippedIndex >= clipped.length) {      continue;    }    clippedIndex = clippedTriangleVertices[1].initializeFromClipResult(      clipped,      clippedIndex,      triangleVertices    );    if (clippedIndex >= clipped.length) {      continue;    }    clippedIndex = clippedTriangleVertices[2].initializeFromClipResult(      clipped,      clippedIndex,      triangleVertices    );    // Clip the triangle against the North-south boundary.    clipped2 = Intersections2D.clipTriangleAtAxisAlignedThreshold(      halfMaxShort,      isNorthChild,      clippedTriangleVertices[0].getV(),      clippedTriangleVertices[1].getV(),      clippedTriangleVertices[2].getV(),      clipScratch2    );    addClippedPolygon(      uBuffer,      vBuffer,      heightBuffer,      normalBuffer,      indices,      vertexMap,      clipped2,      clippedTriangleVertices,      hasVertexNormals    );    // If there's another vertex in the original clipped result,    // it forms a second triangle.  Clip it as well.    if (clippedIndex < clipped.length) {      clippedTriangleVertices[2].clone(clippedTriangleVertices[1]);      clippedTriangleVertices[2].initializeFromClipResult(        clipped,        clippedIndex,        triangleVertices      );      clipped2 = Intersections2D.clipTriangleAtAxisAlignedThreshold(        halfMaxShort,        isNorthChild,        clippedTriangleVertices[0].getV(),        clippedTriangleVertices[1].getV(),        clippedTriangleVertices[2].getV(),        clipScratch2      );      addClippedPolygon(        uBuffer,        vBuffer,        heightBuffer,        normalBuffer,        indices,        vertexMap,        clipped2,        clippedTriangleVertices,        hasVertexNormals      );    }  }  const uOffset = isEastChild ? -maxShort : 0;  const vOffset = isNorthChild ? -maxShort : 0;  const westIndices = [];  const southIndices = [];  const eastIndices = [];  const northIndices = [];  let minimumHeight = Number.MAX_VALUE;  let maximumHeight = -minimumHeight;  const cartesianVertices = verticesScratch;  cartesianVertices.length = 0;  const ellipsoid = Ellipsoid.clone(parameters.ellipsoid);  const rectangle = Rectangle.clone(parameters.childRectangle);  const north = rectangle.north;  const south = rectangle.south;  let east = rectangle.east;  const west = rectangle.west;  if (east < west) {    east += CesiumMath.TWO_PI;  }  for (i = 0; i < uBuffer.length; ++i) {    u = Math.round(uBuffer[i]);    if (u <= minU) {      westIndices.push(i);      u = 0;    } else if (u >= maxU) {      eastIndices.push(i);      u = maxShort;    } else {      u = u * 2 + uOffset;    }    uBuffer[i] = u;    v = Math.round(vBuffer[i]);    if (v <= minV) {      southIndices.push(i);      v = 0;    } else if (v >= maxV) {      northIndices.push(i);      v = maxShort;    } else {      v = v * 2 + vOffset;    }    vBuffer[i] = v;    height = CesiumMath.lerp(      parentMinimumHeight,      parentMaximumHeight,      heightBuffer[i] / maxShort    );    if (height < minimumHeight) {      minimumHeight = height;    }    if (height > maximumHeight) {      maximumHeight = height;    }    heightBuffer[i] = height;    cartographicScratch.longitude = CesiumMath.lerp(west, east, u / maxShort);    cartographicScratch.latitude = CesiumMath.lerp(south, north, v / maxShort);    cartographicScratch.height = height;    ellipsoid.cartographicToCartesian(cartographicScratch, cartesian3Scratch);    cartesianVertices.push(cartesian3Scratch.x);    cartesianVertices.push(cartesian3Scratch.y);    cartesianVertices.push(cartesian3Scratch.z);  }  const boundingSphere = BoundingSphere.fromVertices(    cartesianVertices,    Cartesian3.ZERO,    3,    boundingSphereScratch  );  const orientedBoundingBox = OrientedBoundingBox.fromRectangle(    rectangle,    minimumHeight,    maximumHeight,    ellipsoid,    orientedBoundingBoxScratch  );  const occluder = new EllipsoidalOccluder(ellipsoid);  const horizonOcclusionPoint = occluder.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid(    boundingSphere.center,    cartesianVertices,    3,    boundingSphere.center,    minimumHeight,    horizonOcclusionPointScratch  );  const heightRange = maximumHeight - minimumHeight;  const vertices = new Uint16Array(    uBuffer.length + vBuffer.length + heightBuffer.length  );  for (i = 0; i < uBuffer.length; ++i) {    vertices[i] = uBuffer[i];  }  let start = uBuffer.length;  for (i = 0; i < vBuffer.length; ++i) {    vertices[start + i] = vBuffer[i];  }  start += vBuffer.length;  for (i = 0; i < heightBuffer.length; ++i) {    vertices[start + i] =      (maxShort * (heightBuffer[i] - minimumHeight)) / heightRange;  }  const indicesTypedArray = IndexDatatype.createTypedArray(    uBuffer.length,    indices  );  let encodedNormals;  if (hasVertexNormals) {    const normalArray = new Uint8Array(normalBuffer);    transferableObjects.push(      vertices.buffer,      indicesTypedArray.buffer,      normalArray.buffer    );    encodedNormals = normalArray.buffer;  } else {    transferableObjects.push(vertices.buffer, indicesTypedArray.buffer);  }  return {    vertices: vertices.buffer,    encodedNormals: encodedNormals,    indices: indicesTypedArray.buffer,    minimumHeight: minimumHeight,    maximumHeight: maximumHeight,    westIndices: westIndices,    southIndices: southIndices,    eastIndices: eastIndices,    northIndices: northIndices,    boundingSphere: boundingSphere,    orientedBoundingBox: orientedBoundingBox,    horizonOcclusionPoint: horizonOcclusionPoint,  };}function Vertex() {  this.vertexBuffer = undefined;  this.index = undefined;  this.first = undefined;  this.second = undefined;  this.ratio = undefined;}Vertex.prototype.clone = function (result) {  if (!defined(result)) {    result = new Vertex();  }  result.uBuffer = this.uBuffer;  result.vBuffer = this.vBuffer;  result.heightBuffer = this.heightBuffer;  result.normalBuffer = this.normalBuffer;  result.index = this.index;  result.first = this.first;  result.second = this.second;  result.ratio = this.ratio;  return result;};Vertex.prototype.initializeIndexed = function (  uBuffer,  vBuffer,  heightBuffer,  normalBuffer,  index) {  this.uBuffer = uBuffer;  this.vBuffer = vBuffer;  this.heightBuffer = heightBuffer;  this.normalBuffer = normalBuffer;  this.index = index;  this.first = undefined;  this.second = undefined;  this.ratio = undefined;};Vertex.prototype.initializeFromClipResult = function (  clipResult,  index,  vertices) {  let nextIndex = index + 1;  if (clipResult[index] !== -1) {    vertices[clipResult[index]].clone(this);  } else {    this.vertexBuffer = undefined;    this.index = undefined;    this.first = vertices[clipResult[nextIndex]];    ++nextIndex;    this.second = vertices[clipResult[nextIndex]];    ++nextIndex;    this.ratio = clipResult[nextIndex];    ++nextIndex;  }  return nextIndex;};Vertex.prototype.getKey = function () {  if (this.isIndexed()) {    return this.index;  }  return JSON.stringify({    first: this.first.getKey(),    second: this.second.getKey(),    ratio: this.ratio,  });};Vertex.prototype.isIndexed = function () {  return defined(this.index);};Vertex.prototype.getH = function () {  if (defined(this.index)) {    return this.heightBuffer[this.index];  }  return CesiumMath.lerp(this.first.getH(), this.second.getH(), this.ratio);};Vertex.prototype.getU = function () {  if (defined(this.index)) {    return this.uBuffer[this.index];  }  return CesiumMath.lerp(this.first.getU(), this.second.getU(), this.ratio);};Vertex.prototype.getV = function () {  if (defined(this.index)) {    return this.vBuffer[this.index];  }  return CesiumMath.lerp(this.first.getV(), this.second.getV(), this.ratio);};let encodedScratch = new Cartesian2();// An upsampled triangle may be clipped twice before it is assigned an index// In this case, we need a buffer to handle the recursion of getNormalX() and getNormalY().let depth = -1;const cartesianScratch1 = [new Cartesian3(), new Cartesian3()];const cartesianScratch2 = [new Cartesian3(), new Cartesian3()];function lerpOctEncodedNormal(vertex, result) {  ++depth;  let first = cartesianScratch1[depth];  let second = cartesianScratch2[depth];  first = AttributeCompression.octDecode(    vertex.first.getNormalX(),    vertex.first.getNormalY(),    first  );  second = AttributeCompression.octDecode(    vertex.second.getNormalX(),    vertex.second.getNormalY(),    second  );  cartesian3Scratch = Cartesian3.lerp(    first,    second,    vertex.ratio,    cartesian3Scratch  );  Cartesian3.normalize(cartesian3Scratch, cartesian3Scratch);  AttributeCompression.octEncode(cartesian3Scratch, result);  --depth;  return result;}Vertex.prototype.getNormalX = function () {  if (defined(this.index)) {    return this.normalBuffer[this.index * 2];  }  encodedScratch = lerpOctEncodedNormal(this, encodedScratch);  return encodedScratch.x;};Vertex.prototype.getNormalY = function () {  if (defined(this.index)) {    return this.normalBuffer[this.index * 2 + 1];  }  encodedScratch = lerpOctEncodedNormal(this, encodedScratch);  return encodedScratch.y;};const polygonVertices = [];polygonVertices.push(new Vertex());polygonVertices.push(new Vertex());polygonVertices.push(new Vertex());polygonVertices.push(new Vertex());function addClippedPolygon(  uBuffer,  vBuffer,  heightBuffer,  normalBuffer,  indices,  vertexMap,  clipped,  triangleVertices,  hasVertexNormals) {  if (clipped.length === 0) {    return;  }  let numVertices = 0;  let clippedIndex = 0;  while (clippedIndex < clipped.length) {    clippedIndex = polygonVertices[numVertices++].initializeFromClipResult(      clipped,      clippedIndex,      triangleVertices    );  }  for (let i = 0; i < numVertices; ++i) {    const polygonVertex = polygonVertices[i];    if (!polygonVertex.isIndexed()) {      const key = polygonVertex.getKey();      if (defined(vertexMap[key])) {        polygonVertex.newIndex = vertexMap[key];      } else {        const newIndex = uBuffer.length;        uBuffer.push(polygonVertex.getU());        vBuffer.push(polygonVertex.getV());        heightBuffer.push(polygonVertex.getH());        if (hasVertexNormals) {          normalBuffer.push(polygonVertex.getNormalX());          normalBuffer.push(polygonVertex.getNormalY());        }        polygonVertex.newIndex = newIndex;        vertexMap[key] = newIndex;      }    } else {      polygonVertex.newIndex = vertexMap[polygonVertex.index];      polygonVertex.uBuffer = uBuffer;      polygonVertex.vBuffer = vBuffer;      polygonVertex.heightBuffer = heightBuffer;      if (hasVertexNormals) {        polygonVertex.normalBuffer = normalBuffer;      }    }  }  if (numVertices === 3) {    // A triangle.    indices.push(polygonVertices[0].newIndex);    indices.push(polygonVertices[1].newIndex);    indices.push(polygonVertices[2].newIndex);  } else if (numVertices === 4) {    // A quad - two triangles.    indices.push(polygonVertices[0].newIndex);    indices.push(polygonVertices[1].newIndex);    indices.push(polygonVertices[2].newIndex);    indices.push(polygonVertices[0].newIndex);    indices.push(polygonVertices[2].newIndex);    indices.push(polygonVertices[3].newIndex);  }}export default createTaskProcessorWorker(upsampleQuantizedTerrainMesh);
 |