123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- import AxisAlignedBoundingBox from '../Core/AxisAlignedBoundingBox.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 defaultValue from '../Core/defaultValue.js';
- import defined from '../Core/defined.js';
- import Ellipsoid from '../Core/Ellipsoid.js';
- import EllipsoidalOccluder from '../Core/EllipsoidalOccluder.js';
- import CesiumMath from '../Core/Math.js';
- import Matrix4 from '../Core/Matrix4.js';
- import OrientedBoundingBox from '../Core/OrientedBoundingBox.js';
- import Rectangle from '../Core/Rectangle.js';
- import RuntimeError from '../Core/RuntimeError.js';
- import TerrainEncoding from '../Core/TerrainEncoding.js';
- import Transforms from '../Core/Transforms.js';
- import WebMercatorProjection from '../Core/WebMercatorProjection.js';
- import createTaskProcessorWorker from './createTaskProcessorWorker.js';
- var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT;
- var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT;
- var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
- var sizeOfFloat = Float32Array.BYTES_PER_ELEMENT;
- var sizeOfDouble = Float64Array.BYTES_PER_ELEMENT;
- function indexOfEpsilon(arr, elem, elemType) {
- elemType = defaultValue(elemType, CesiumMath);
- var count = arr.length;
- for (var i = 0; i < count; ++i) {
- if (elemType.equalsEpsilon(arr[i], elem, CesiumMath.EPSILON12)) {
- return i;
- }
- }
- return -1;
- }
- function createVerticesFromGoogleEarthEnterpriseBuffer(parameters, transferableObjects) {
- parameters.ellipsoid = Ellipsoid.clone(parameters.ellipsoid);
- parameters.rectangle = Rectangle.clone(parameters.rectangle);
- var statistics = processBuffer(parameters.buffer, parameters.relativeToCenter, parameters.ellipsoid,
- parameters.rectangle, parameters.nativeRectangle, parameters.exaggeration, parameters.skirtHeight,
- parameters.includeWebMercatorT, parameters.negativeAltitudeExponentBias, parameters.negativeElevationThreshold);
- var vertices = statistics.vertices;
- transferableObjects.push(vertices.buffer);
- var indices = statistics.indices;
- transferableObjects.push(indices.buffer);
- return {
- vertices : vertices.buffer,
- indices : indices.buffer,
- numberOfAttributes : statistics.encoding.getStride(),
- minimumHeight : statistics.minimumHeight,
- maximumHeight : statistics.maximumHeight,
- boundingSphere3D : statistics.boundingSphere3D,
- orientedBoundingBox : statistics.orientedBoundingBox,
- occludeePointInScaledSpace : statistics.occludeePointInScaledSpace,
- encoding : statistics.encoding,
- vertexCountWithoutSkirts : statistics.vertexCountWithoutSkirts,
- skirtIndex : statistics.skirtIndex,
- westIndicesSouthToNorth : statistics.westIndicesSouthToNorth,
- southIndicesEastToWest : statistics.southIndicesEastToWest,
- eastIndicesNorthToSouth : statistics.eastIndicesNorthToSouth,
- northIndicesWestToEast : statistics.northIndicesWestToEast
- };
- }
- var scratchCartographic = new Cartographic();
- var scratchCartesian = new Cartesian3();
- var minimumScratch = new Cartesian3();
- var maximumScratch = new Cartesian3();
- var matrix4Scratch = new Matrix4();
- function processBuffer(buffer, relativeToCenter, ellipsoid, rectangle, nativeRectangle, exaggeration, skirtHeight, includeWebMercatorT, negativeAltitudeExponentBias, negativeElevationThreshold) {
- var geographicWest;
- var geographicSouth;
- var geographicEast;
- var geographicNorth;
- var rectangleWidth, rectangleHeight;
- if (!defined(rectangle)) {
- geographicWest = CesiumMath.toRadians(nativeRectangle.west);
- geographicSouth = CesiumMath.toRadians(nativeRectangle.south);
- geographicEast = CesiumMath.toRadians(nativeRectangle.east);
- geographicNorth = CesiumMath.toRadians(nativeRectangle.north);
- rectangleWidth = CesiumMath.toRadians(rectangle.width);
- rectangleHeight = CesiumMath.toRadians(rectangle.height);
- } else {
- geographicWest = rectangle.west;
- geographicSouth = rectangle.south;
- geographicEast = rectangle.east;
- geographicNorth = rectangle.north;
- rectangleWidth = rectangle.width;
- rectangleHeight = rectangle.height;
- }
- // Keep track of quad borders so we can remove duplicates around the borders
- var quadBorderLatitudes = [geographicSouth, geographicNorth];
- var quadBorderLongitudes = [geographicWest, geographicEast];
- var fromENU = Transforms.eastNorthUpToFixedFrame(relativeToCenter, ellipsoid);
- var toENU = Matrix4.inverseTransformation(fromENU, matrix4Scratch);
- var southMercatorY;
- var oneOverMercatorHeight;
- if (includeWebMercatorT) {
- southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(geographicSouth);
- oneOverMercatorHeight = 1.0 / (WebMercatorProjection.geodeticLatitudeToMercatorAngle(geographicNorth) - southMercatorY);
- }
- var dv = new DataView(buffer);
- var minHeight = Number.POSITIVE_INFINITY;
- var maxHeight = Number.NEGATIVE_INFINITY;
- var minimum = minimumScratch;
- minimum.x = Number.POSITIVE_INFINITY;
- minimum.y = Number.POSITIVE_INFINITY;
- minimum.z = Number.POSITIVE_INFINITY;
- var maximum = maximumScratch;
- maximum.x = Number.NEGATIVE_INFINITY;
- maximum.y = Number.NEGATIVE_INFINITY;
- maximum.z = Number.NEGATIVE_INFINITY;
- // Compute sizes
- var offset = 0;
- var size = 0;
- var indicesSize = 0;
- var quadSize;
- var quad;
- for (quad = 0; quad < 4; ++quad) {
- var o = offset;
- quadSize = dv.getUint32(o, true);
- o += sizeOfUint32;
- var x = CesiumMath.toRadians(dv.getFloat64(o, true) * 180.0);
- o += sizeOfDouble;
- if (indexOfEpsilon(quadBorderLongitudes, x) === -1) {
- quadBorderLongitudes.push(x);
- }
- var y = CesiumMath.toRadians(dv.getFloat64(o, true) * 180.0);
- o += sizeOfDouble;
- if (indexOfEpsilon(quadBorderLatitudes, y) === -1) {
- quadBorderLatitudes.push(y);
- }
- o += 2 * sizeOfDouble; // stepX + stepY
- var c = dv.getInt32(o, true); // Read point count
- o += sizeOfInt32;
- size += c;
- c = dv.getInt32(o, true); // Read index count
- indicesSize += c * 3;
- offset += quadSize + sizeOfUint32; // Jump to next quad
- }
- // Quad Border points to remove duplicates
- var quadBorderPoints = [];
- var quadBorderIndices = [];
- // Create arrays
- var positions = new Array(size);
- var uvs = new Array(size);
- var heights = new Array(size);
- var webMercatorTs = includeWebMercatorT ? new Array(size) : [];
- var indices = new Array(indicesSize);
- // Points are laid out in rows starting at SW, so storing border points as we
- // come across them all points will be adjacent.
- var westBorder = [];
- var southBorder = [];
- var eastBorder = [];
- var northBorder = [];
- // Each tile is split into 4 parts
- var pointOffset = 0;
- var indicesOffset = 0;
- offset = 0;
- for (quad = 0; quad < 4; ++quad) {
- quadSize = dv.getUint32(offset, true);
- offset += sizeOfUint32;
- var startQuad = offset;
- var originX = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0);
- offset += sizeOfDouble;
- var originY = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0);
- offset += sizeOfDouble;
- var stepX = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0);
- var halfStepX = stepX * 0.5;
- offset += sizeOfDouble;
- var stepY = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0);
- var halfStepY = stepY * 0.5;
- offset += sizeOfDouble;
- var numPoints = dv.getInt32(offset, true);
- offset += sizeOfInt32;
- var numFaces = dv.getInt32(offset, true);
- offset += sizeOfInt32;
- //var level = dv.getInt32(offset, true);
- offset += sizeOfInt32;
- // Keep track of quad indices to overall tile indices
- var indicesMapping = new Array(numPoints);
- for (var i = 0; i < numPoints; ++i) {
- var longitude = originX + dv.getUint8(offset++) * stepX;
- scratchCartographic.longitude = longitude;
- var latitude = originY + dv.getUint8(offset++) * stepY;
- scratchCartographic.latitude = latitude;
- var height = dv.getFloat32(offset, true);
- offset += sizeOfFloat;
- // In order to support old clients, negative altitude values are stored as
- // height/-2^32. Old clients see the value as really close to 0 but new clients multiply
- // by -2^32 to get the real negative altitude value.
- if (height !== 0 && height < negativeElevationThreshold) {
- height *= -Math.pow(2, negativeAltitudeExponentBias);
- }
- // Height is stored in units of (1/EarthRadius) or (1/6371010.0)
- height *= 6371010.0 * exaggeration;
- scratchCartographic.height = height;
- // Is it along a quad border - if so check if already exists and use that index
- if (indexOfEpsilon(quadBorderLongitudes, longitude) !== -1 ||
- indexOfEpsilon(quadBorderLatitudes, latitude) !== -1) {
- var index = indexOfEpsilon(quadBorderPoints, scratchCartographic, Cartographic);
- if (index === -1) {
- quadBorderPoints.push(Cartographic.clone(scratchCartographic));
- quadBorderIndices.push(pointOffset);
- } else {
- indicesMapping[i] = quadBorderIndices[index];
- continue;
- }
- }
- indicesMapping[i] = pointOffset;
- if (Math.abs(longitude - geographicWest) < halfStepX) {
- westBorder.push({
- index : pointOffset,
- cartographic : Cartographic.clone(scratchCartographic)
- });
- } else if (Math.abs(longitude - geographicEast) < halfStepX) {
- eastBorder.push({
- index : pointOffset,
- cartographic : Cartographic.clone(scratchCartographic)
- });
- } else if (Math.abs(latitude - geographicSouth) < halfStepY) {
- southBorder.push({
- index : pointOffset,
- cartographic : Cartographic.clone(scratchCartographic)
- });
- } else if (Math.abs(latitude - geographicNorth) < halfStepY) {
- northBorder.push({
- index : pointOffset,
- cartographic : Cartographic.clone(scratchCartographic)
- });
- }
- minHeight = Math.min(height, minHeight);
- maxHeight = Math.max(height, maxHeight);
- heights[pointOffset] = height;
- var pos = ellipsoid.cartographicToCartesian(scratchCartographic);
- positions[pointOffset] = pos;
- if (includeWebMercatorT) {
- webMercatorTs[pointOffset] = (WebMercatorProjection.geodeticLatitudeToMercatorAngle(latitude) - southMercatorY) * oneOverMercatorHeight;
- }
- Matrix4.multiplyByPoint(toENU, pos, scratchCartesian);
- Cartesian3.minimumByComponent(scratchCartesian, minimum, minimum);
- Cartesian3.maximumByComponent(scratchCartesian, maximum, maximum);
- var u = (longitude - geographicWest) / (geographicEast - geographicWest);
- u = CesiumMath.clamp(u, 0.0, 1.0);
- var v = (latitude - geographicSouth) / (geographicNorth - geographicSouth);
- v = CesiumMath.clamp(v, 0.0, 1.0);
- uvs[pointOffset] = new Cartesian2(u, v);
- ++pointOffset;
- }
- var facesElementCount = numFaces * 3;
- for (var j = 0; j < facesElementCount; ++j, ++indicesOffset) {
- indices[indicesOffset] = indicesMapping[dv.getUint16(offset, true)];
- offset += sizeOfUint16;
- }
- if (quadSize !== (offset - startQuad)) {
- throw new RuntimeError('Invalid terrain tile.');
- }
- }
- positions.length = pointOffset;
- uvs.length = pointOffset;
- heights.length = pointOffset;
- if (includeWebMercatorT) {
- webMercatorTs.length = pointOffset;
- }
- var vertexCountWithoutSkirts = pointOffset;
- var skirtIndex = indicesOffset;
- // Add skirt points
- var skirtOptions = {
- hMin : minHeight,
- lastBorderPoint : undefined,
- skirtHeight : skirtHeight,
- toENU : toENU,
- ellipsoid : ellipsoid,
- minimum : minimum,
- maximum : maximum
- };
- // Sort counter clockwise from NW corner
- // Corner points are in the east/west arrays
- westBorder.sort(function(a, b) {
- return b.cartographic.latitude - a.cartographic.latitude;
- });
- southBorder.sort(function(a, b) {
- return a.cartographic.longitude - b.cartographic.longitude;
- });
- eastBorder.sort(function(a, b) {
- return a.cartographic.latitude - b.cartographic.latitude;
- });
- northBorder.sort(function(a, b) {
- return b.cartographic.longitude - a.cartographic.longitude;
- });
- var percentage = 0.00001;
- addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions,
- westBorder, -percentage * rectangleWidth, true, -percentage * rectangleHeight);
- addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions,
- southBorder, -percentage * rectangleHeight, false);
- addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions,
- eastBorder, percentage * rectangleWidth, true, percentage * rectangleHeight);
- addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions,
- northBorder, percentage * rectangleHeight, false);
- // Since the corner between the north and west sides is in the west array, generate the last
- // two triangles between the last north vertex and the first west vertex
- if (westBorder.length > 0 && northBorder.length > 0) {
- var firstBorderIndex = westBorder[0].index;
- var firstSkirtIndex = vertexCountWithoutSkirts;
- var lastBorderIndex = northBorder[northBorder.length - 1].index;
- var lastSkirtIndex = positions.length - 1;
- indices.push(lastBorderIndex, lastSkirtIndex, firstSkirtIndex, firstSkirtIndex, firstBorderIndex, lastBorderIndex);
- }
- size = positions.length; // Get new size with skirt vertices
- var boundingSphere3D = BoundingSphere.fromPoints(positions);
- var orientedBoundingBox;
- if (defined(rectangle) && rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
- // Here, rectangle.width < pi/2, and rectangle.height < pi
- // (though it would still work with rectangle.width up to pi)
- orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minHeight, maxHeight, ellipsoid);
- }
- var occluder = new EllipsoidalOccluder(ellipsoid);
- var occludeePointInScaledSpace = occluder.computeHorizonCullingPoint(relativeToCenter, positions);
- var aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter);
- var encoding = new TerrainEncoding(aaBox, skirtOptions.hMin, maxHeight, fromENU, false, includeWebMercatorT);
- var vertices = new Float32Array(size * encoding.getStride());
- var bufferIndex = 0;
- for (var k = 0; k < size; ++k) {
- bufferIndex = encoding.encode(vertices, bufferIndex, positions[k], uvs[k], heights[k], undefined, webMercatorTs[k]);
- }
- var westIndicesSouthToNorth = westBorder.map(function(vertex) { return vertex.index; }).reverse();
- var southIndicesEastToWest = southBorder.map(function(vertex) { return vertex.index; }).reverse();
- var eastIndicesNorthToSouth = eastBorder.map(function(vertex) { return vertex.index; }).reverse();
- var northIndicesWestToEast = northBorder.map(function(vertex) { return vertex.index; }).reverse();
- southIndicesEastToWest.unshift(eastIndicesNorthToSouth[eastIndicesNorthToSouth.length - 1]);
- southIndicesEastToWest.push(westIndicesSouthToNorth[0]);
- northIndicesWestToEast.unshift(westIndicesSouthToNorth[westIndicesSouthToNorth.length - 1]);
- northIndicesWestToEast.push(eastIndicesNorthToSouth[0]);
- return {
- vertices : vertices,
- indices : new Uint16Array(indices),
- maximumHeight : maxHeight,
- minimumHeight : minHeight,
- encoding : encoding,
- boundingSphere3D : boundingSphere3D,
- orientedBoundingBox : orientedBoundingBox,
- occludeePointInScaledSpace : occludeePointInScaledSpace,
- vertexCountWithoutSkirts : vertexCountWithoutSkirts,
- skirtIndex : skirtIndex,
- westIndicesSouthToNorth : westIndicesSouthToNorth,
- southIndicesEastToWest : southIndicesEastToWest,
- eastIndicesNorthToSouth : eastIndicesNorthToSouth,
- northIndicesWestToEast : northIndicesWestToEast
- };
- }
- function addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions,
- borderPoints, fudgeFactor, eastOrWest, cornerFudge) {
- var count = borderPoints.length;
- for (var j = 0; j < count; ++j) {
- var borderPoint = borderPoints[j];
- var borderCartographic = borderPoint.cartographic;
- var borderIndex = borderPoint.index;
- var currentIndex = positions.length;
- var longitude = borderCartographic.longitude;
- var latitude = borderCartographic.latitude;
- latitude = CesiumMath.clamp(latitude, -CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO); // Don't go over the poles
- var height = borderCartographic.height - skirtOptions.skirtHeight;
- skirtOptions.hMin = Math.min(skirtOptions.hMin, height);
- Cartographic.fromRadians(longitude, latitude, height, scratchCartographic);
- // Adjust sides to angle out
- if (eastOrWest) {
- scratchCartographic.longitude += fudgeFactor;
- }
- // Adjust top or bottom to angle out
- // Since corners are in the east/west arrays angle the first and last points as well
- if (!eastOrWest) {
- scratchCartographic.latitude += fudgeFactor;
- } else if (j === (count - 1)) {
- scratchCartographic.latitude += cornerFudge;
- } else if (j === 0) {
- scratchCartographic.latitude -= cornerFudge;
- }
- var pos = skirtOptions.ellipsoid.cartographicToCartesian(scratchCartographic);
- positions.push(pos);
- heights.push(height);
- uvs.push(Cartesian2.clone(uvs[borderIndex])); // Copy UVs from border point
- if (webMercatorTs.length > 0) {
- webMercatorTs.push(webMercatorTs[borderIndex]);
- }
- Matrix4.multiplyByPoint(skirtOptions.toENU, pos, scratchCartesian);
- var minimum = skirtOptions.minimum;
- var maximum = skirtOptions.maximum;
- Cartesian3.minimumByComponent(scratchCartesian, minimum, minimum);
- Cartesian3.maximumByComponent(scratchCartesian, maximum, maximum);
- var lastBorderPoint = skirtOptions.lastBorderPoint;
- if (defined(lastBorderPoint)) {
- var lastBorderIndex = lastBorderPoint.index;
- indices.push(lastBorderIndex, currentIndex - 1, currentIndex, currentIndex, borderIndex, lastBorderIndex);
- }
- skirtOptions.lastBorderPoint = borderPoint;
- }
- }
- export default createTaskProcessorWorker(createVerticesFromGoogleEarthEnterpriseBuffer);
|