UNPKG

cesium

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

1,105 lines (975 loc) 135 kB
/* This file is automatically rebuilt by the Cesium build process. */ define(['exports', './when-e6e3e713', './Check-1df6b9a0', './Math-c5f6c994', './Cartesian2-1d7364fa', './Transforms-943e8463', './ComponentDatatype-2b8834a4', './GeometryAttribute-3a303898', './AttributeCompression-d68d64ef', './EncodedCartesian3-d723731d', './IndexDatatype-e2961542', './IntersectionTests-c05f88ce', './Plane-2e419ea5'], function (exports, when, Check, _Math, Cartesian2, Transforms, ComponentDatatype, GeometryAttribute, AttributeCompression, EncodedCartesian3, IndexDatatype, IntersectionTests, Plane) { 'use strict'; var scratchCartesian1 = new Cartesian2.Cartesian3(); var scratchCartesian2 = new Cartesian2.Cartesian3(); var scratchCartesian3 = new Cartesian2.Cartesian3(); /** * Computes the barycentric coordinates for a point with respect to a triangle. * * @exports barycentricCoordinates * * @param {Cartesian2|Cartesian3} point The point to test. * @param {Cartesian2|Cartesian3} p0 The first point of the triangle, corresponding to the barycentric x-axis. * @param {Cartesian2|Cartesian3} p1 The second point of the triangle, corresponding to the barycentric y-axis. * @param {Cartesian2|Cartesian3} p2 The third point of the triangle, corresponding to the barycentric z-axis. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. * * @example * // Returns Cartesian3.UNIT_X * var p = new Cesium.Cartesian3(-1.0, 0.0, 0.0); * var b = Cesium.barycentricCoordinates(p, * new Cesium.Cartesian3(-1.0, 0.0, 0.0), * new Cesium.Cartesian3( 1.0, 0.0, 0.0), * new Cesium.Cartesian3( 0.0, 1.0, 1.0)); */ function barycentricCoordinates(point, p0, p1, p2, result) { //>>includeStart('debug', pragmas.debug); Check.Check.defined('point', point); Check.Check.defined('p0', p0); Check.Check.defined('p1', p1); Check.Check.defined('p2', p2); //>>includeEnd('debug'); if (!when.defined(result)) { result = new Cartesian2.Cartesian3(); } // Implementation based on http://www.blackpawn.com/texts/pointinpoly/default.html. var v0; var v1; var v2; var dot00; var dot01; var dot02; var dot11; var dot12; if(!when.defined(p0.z)) { if (Cartesian2.Cartesian2.equalsEpsilon(point, p0, _Math.CesiumMath.EPSILON14)) { return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_X, result); } if (Cartesian2.Cartesian2.equalsEpsilon(point, p1, _Math.CesiumMath.EPSILON14)) { return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_Y, result); } if (Cartesian2.Cartesian2.equalsEpsilon(point, p2, _Math.CesiumMath.EPSILON14)) { return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_Z, result); } v0 = Cartesian2.Cartesian2.subtract(p1, p0, scratchCartesian1); v1 = Cartesian2.Cartesian2.subtract(p2, p0, scratchCartesian2); v2 = Cartesian2.Cartesian2.subtract(point, p0, scratchCartesian3); dot00 = Cartesian2.Cartesian2.dot(v0, v0); dot01 = Cartesian2.Cartesian2.dot(v0, v1); dot02 = Cartesian2.Cartesian2.dot(v0, v2); dot11 = Cartesian2.Cartesian2.dot(v1, v1); dot12 = Cartesian2.Cartesian2.dot(v1, v2); } else { if (Cartesian2.Cartesian3.equalsEpsilon(point, p0, _Math.CesiumMath.EPSILON14)) { return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_X, result); } if (Cartesian2.Cartesian3.equalsEpsilon(point, p1, _Math.CesiumMath.EPSILON14)) { return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_Y, result); } if (Cartesian2.Cartesian3.equalsEpsilon(point, p2, _Math.CesiumMath.EPSILON14)) { return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_Z, result); } v0 = Cartesian2.Cartesian3.subtract(p1, p0, scratchCartesian1); v1 = Cartesian2.Cartesian3.subtract(p2, p0, scratchCartesian2); v2 = Cartesian2.Cartesian3.subtract(point, p0, scratchCartesian3); dot00 = Cartesian2.Cartesian3.dot(v0, v0); dot01 = Cartesian2.Cartesian3.dot(v0, v1); dot02 = Cartesian2.Cartesian3.dot(v0, v2); dot11 = Cartesian2.Cartesian3.dot(v1, v1); dot12 = Cartesian2.Cartesian3.dot(v1, v2); } result.y = (dot11 * dot02 - dot01 * dot12); result.z = (dot00 * dot12 - dot01 * dot02); var q = dot00 * dot11 - dot01 * dot01; // This is done to avoid dividing by infinity causing a NaN if (result.y !== 0) { result.y /= q; } if (result.z !== 0) { result.z /= q; } result.x = 1.0 - result.y - result.z; return result; } /** * Encapsulates an algorithm to optimize triangles for the post * vertex-shader cache. This is based on the 2007 SIGGRAPH paper * 'Fast Triangle Reordering for Vertex Locality and Reduced Overdraw.' * The runtime is linear but several passes are made. * * @exports Tipsify * * @see <a href='http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf'> * Fast Triangle Reordering for Vertex Locality and Reduced Overdraw</a> * by Sander, Nehab, and Barczak * * @private */ var Tipsify = {}; /** * Calculates the average cache miss ratio (ACMR) for a given set of indices. * * @param {Object} options Object with the following properties: * @param {Number[]} options.indices Lists triads of numbers corresponding to the indices of the vertices * in the vertex buffer that define the geometry's triangles. * @param {Number} [options.maximumIndex] The maximum value of the elements in <code>args.indices</code>. * If not supplied, this value will be computed. * @param {Number} [options.cacheSize=24] The number of vertices that can be stored in the cache at any one time. * @returns {Number} The average cache miss ratio (ACMR). * * @exception {DeveloperError} indices length must be a multiple of three. * @exception {DeveloperError} cacheSize must be greater than two. * * @example * var indices = [0, 1, 2, 3, 4, 5]; * var maxIndex = 5; * var cacheSize = 3; * var acmr = Cesium.Tipsify.calculateACMR({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize}); */ Tipsify.calculateACMR = function(options) { options = when.defaultValue(options, when.defaultValue.EMPTY_OBJECT); var indices = options.indices; var maximumIndex = options.maximumIndex; var cacheSize = when.defaultValue(options.cacheSize, 24); //>>includeStart('debug', pragmas.debug); if (!when.defined(indices)) { throw new Check.DeveloperError('indices is required.'); } //>>includeEnd('debug'); var numIndices = indices.length; //>>includeStart('debug', pragmas.debug); if (numIndices < 3 || numIndices % 3 !== 0) { throw new Check.DeveloperError('indices length must be a multiple of three.'); } if (maximumIndex <= 0) { throw new Check.DeveloperError('maximumIndex must be greater than zero.'); } if (cacheSize < 3) { throw new Check.DeveloperError('cacheSize must be greater than two.'); } //>>includeEnd('debug'); // Compute the maximumIndex if not given if (!when.defined(maximumIndex)) { maximumIndex = 0; var currentIndex = 0; var intoIndices = indices[currentIndex]; while (currentIndex < numIndices) { if (intoIndices > maximumIndex) { maximumIndex = intoIndices; } ++currentIndex; intoIndices = indices[currentIndex]; } } // Vertex time stamps var vertexTimeStamps = []; for ( var i = 0; i < maximumIndex + 1; i++) { vertexTimeStamps[i] = 0; } // Cache processing var s = cacheSize + 1; for ( var j = 0; j < numIndices; ++j) { if ((s - vertexTimeStamps[indices[j]]) > cacheSize) { vertexTimeStamps[indices[j]] = s; ++s; } } return (s - cacheSize + 1) / (numIndices / 3); }; /** * Optimizes triangles for the post-vertex shader cache. * * @param {Object} options Object with the following properties: * @param {Number[]} options.indices Lists triads of numbers corresponding to the indices of the vertices * in the vertex buffer that define the geometry's triangles. * @param {Number} [options.maximumIndex] The maximum value of the elements in <code>args.indices</code>. * If not supplied, this value will be computed. * @param {Number} [options.cacheSize=24] The number of vertices that can be stored in the cache at any one time. * @returns {Number[]} A list of the input indices in an optimized order. * * @exception {DeveloperError} indices length must be a multiple of three. * @exception {DeveloperError} cacheSize must be greater than two. * * @example * var indices = [0, 1, 2, 3, 4, 5]; * var maxIndex = 5; * var cacheSize = 3; * var reorderedIndices = Cesium.Tipsify.tipsify({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize}); */ Tipsify.tipsify = function(options) { options = when.defaultValue(options, when.defaultValue.EMPTY_OBJECT); var indices = options.indices; var maximumIndex = options.maximumIndex; var cacheSize = when.defaultValue(options.cacheSize, 24); var cursor; function skipDeadEnd(vertices, deadEnd, indices, maximumIndexPlusOne) { while (deadEnd.length >= 1) { // while the stack is not empty var d = deadEnd[deadEnd.length - 1]; // top of the stack deadEnd.splice(deadEnd.length - 1, 1); // pop the stack if (vertices[d].numLiveTriangles > 0) { return d; } } while (cursor < maximumIndexPlusOne) { if (vertices[cursor].numLiveTriangles > 0) { ++cursor; return cursor - 1; } ++cursor; } return -1; } function getNextVertex(indices, cacheSize, oneRing, vertices, s, deadEnd, maximumIndexPlusOne) { var n = -1; var p; var m = -1; var itOneRing = 0; while (itOneRing < oneRing.length) { var index = oneRing[itOneRing]; if (vertices[index].numLiveTriangles) { p = 0; if ((s - vertices[index].timeStamp + (2 * vertices[index].numLiveTriangles)) <= cacheSize) { p = s - vertices[index].timeStamp; } if ((p > m) || (m === -1)) { m = p; n = index; } } ++itOneRing; } if (n === -1) { return skipDeadEnd(vertices, deadEnd, indices, maximumIndexPlusOne); } return n; } //>>includeStart('debug', pragmas.debug); if (!when.defined(indices)) { throw new Check.DeveloperError('indices is required.'); } //>>includeEnd('debug'); var numIndices = indices.length; //>>includeStart('debug', pragmas.debug); if (numIndices < 3 || numIndices % 3 !== 0) { throw new Check.DeveloperError('indices length must be a multiple of three.'); } if (maximumIndex <= 0) { throw new Check.DeveloperError('maximumIndex must be greater than zero.'); } if (cacheSize < 3) { throw new Check.DeveloperError('cacheSize must be greater than two.'); } //>>includeEnd('debug'); // Determine maximum index var maximumIndexPlusOne = 0; var currentIndex = 0; var intoIndices = indices[currentIndex]; var endIndex = numIndices; if (when.defined(maximumIndex)) { maximumIndexPlusOne = maximumIndex + 1; } else { while (currentIndex < endIndex) { if (intoIndices > maximumIndexPlusOne) { maximumIndexPlusOne = intoIndices; } ++currentIndex; intoIndices = indices[currentIndex]; } if (maximumIndexPlusOne === -1) { return 0; } ++maximumIndexPlusOne; } // Vertices var vertices = []; var i; for (i = 0; i < maximumIndexPlusOne; i++) { vertices[i] = { numLiveTriangles : 0, timeStamp : 0, vertexTriangles : [] }; } currentIndex = 0; var triangle = 0; while (currentIndex < endIndex) { vertices[indices[currentIndex]].vertexTriangles.push(triangle); ++(vertices[indices[currentIndex]]).numLiveTriangles; vertices[indices[currentIndex + 1]].vertexTriangles.push(triangle); ++(vertices[indices[currentIndex + 1]]).numLiveTriangles; vertices[indices[currentIndex + 2]].vertexTriangles.push(triangle); ++(vertices[indices[currentIndex + 2]]).numLiveTriangles; ++triangle; currentIndex += 3; } // Starting index var f = 0; // Time Stamp var s = cacheSize + 1; cursor = 1; // Process var oneRing = []; var deadEnd = []; //Stack var vertex; var intoVertices; var currentOutputIndex = 0; var outputIndices = []; var numTriangles = numIndices / 3; var triangleEmitted = []; for (i = 0; i < numTriangles; i++) { triangleEmitted[i] = false; } var index; var limit; while (f !== -1) { oneRing = []; intoVertices = vertices[f]; limit = intoVertices.vertexTriangles.length; for ( var k = 0; k < limit; ++k) { triangle = intoVertices.vertexTriangles[k]; if (!triangleEmitted[triangle]) { triangleEmitted[triangle] = true; currentIndex = triangle + triangle + triangle; for ( var j = 0; j < 3; ++j) { // Set this index as a possible next index index = indices[currentIndex]; oneRing.push(index); deadEnd.push(index); // Output index outputIndices[currentOutputIndex] = index; ++currentOutputIndex; // Cache processing vertex = vertices[index]; --vertex.numLiveTriangles; if ((s - vertex.timeStamp) > cacheSize) { vertex.timeStamp = s; ++s; } ++currentIndex; } } } f = getNextVertex(indices, cacheSize, oneRing, vertices, s, deadEnd, maximumIndexPlusOne); } return outputIndices; }; /** * Content pipeline functions for geometries. * * @exports GeometryPipeline * * @see Geometry */ var GeometryPipeline = {}; function addTriangle(lines, index, i0, i1, i2) { lines[index++] = i0; lines[index++] = i1; lines[index++] = i1; lines[index++] = i2; lines[index++] = i2; lines[index] = i0; } function trianglesToLines(triangles) { var count = triangles.length; var size = (count / 3) * 6; var lines = IndexDatatype.IndexDatatype.createTypedArray(count, size); var index = 0; for ( var i = 0; i < count; i += 3, index += 6) { addTriangle(lines, index, triangles[i], triangles[i + 1], triangles[i + 2]); } return lines; } function triangleStripToLines(triangles) { var count = triangles.length; if (count >= 3) { var size = (count - 2) * 6; var lines = IndexDatatype.IndexDatatype.createTypedArray(count, size); addTriangle(lines, 0, triangles[0], triangles[1], triangles[2]); var index = 6; for ( var i = 3; i < count; ++i, index += 6) { addTriangle(lines, index, triangles[i - 1], triangles[i], triangles[i - 2]); } return lines; } return new Uint16Array(); } function triangleFanToLines(triangles) { if (triangles.length > 0) { var count = triangles.length - 1; var size = (count - 1) * 6; var lines = IndexDatatype.IndexDatatype.createTypedArray(count, size); var base = triangles[0]; var index = 0; for ( var i = 1; i < count; ++i, index += 6) { addTriangle(lines, index, base, triangles[i], triangles[i + 1]); } return lines; } return new Uint16Array(); } /** * Converts a geometry's triangle indices to line indices. If the geometry has an <code>indices</code> * and its <code>primitiveType</code> is <code>TRIANGLES</code>, <code>TRIANGLE_STRIP</code>, * <code>TRIANGLE_FAN</code>, it is converted to <code>LINES</code>; otherwise, the geometry is not changed. * <p> * This is commonly used to create a wireframe geometry for visual debugging. * </p> * * @param {Geometry} geometry The geometry to modify. * @returns {Geometry} The modified <code>geometry</code> argument, with its triangle indices converted to lines. * * @exception {DeveloperError} geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN. * * @example * geometry = Cesium.GeometryPipeline.toWireframe(geometry); */ GeometryPipeline.toWireframe = function(geometry) { //>>includeStart('debug', pragmas.debug); if (!when.defined(geometry)) { throw new Check.DeveloperError('geometry is required.'); } //>>includeEnd('debug'); var indices = geometry.indices; if (when.defined(indices)) { switch (geometry.primitiveType) { case GeometryAttribute.PrimitiveType.TRIANGLES: geometry.indices = trianglesToLines(indices); break; case GeometryAttribute.PrimitiveType.TRIANGLE_STRIP: geometry.indices = triangleStripToLines(indices); break; case GeometryAttribute.PrimitiveType.TRIANGLE_FAN: geometry.indices = triangleFanToLines(indices); break; //>>includeStart('debug', pragmas.debug); default: throw new Check.DeveloperError('geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN.'); //>>includeEnd('debug'); } geometry.primitiveType = GeometryAttribute.PrimitiveType.LINES; } return geometry; }; /** * Creates a new {@link Geometry} with <code>LINES</code> representing the provided * attribute (<code>attributeName</code>) for the provided geometry. This is used to * visualize vector attributes like normals, tangents, and bitangents. * * @param {Geometry} geometry The <code>Geometry</code> instance with the attribute. * @param {String} [attributeName='normal'] The name of the attribute. * @param {Number} [length=10000.0] The length of each line segment in meters. This can be negative to point the vector in the opposite direction. * @returns {Geometry} A new <code>Geometry</code> instance with line segments for the vector. * * @exception {DeveloperError} geometry.attributes must have an attribute with the same name as the attributeName parameter. * * @example * var geometry = Cesium.GeometryPipeline.createLineSegmentsForVectors(instance.geometry, 'bitangent', 100000.0); */ GeometryPipeline.createLineSegmentsForVectors = function(geometry, attributeName, length) { attributeName = when.defaultValue(attributeName, 'normal'); //>>includeStart('debug', pragmas.debug); if (!when.defined(geometry)) { throw new Check.DeveloperError('geometry is required.'); } if (!when.defined(geometry.attributes.position)) { throw new Check.DeveloperError('geometry.attributes.position is required.'); } if (!when.defined(geometry.attributes[attributeName])) { throw new Check.DeveloperError('geometry.attributes must have an attribute with the same name as the attributeName parameter, ' + attributeName + '.'); } //>>includeEnd('debug'); length = when.defaultValue(length, 10000.0); var positions = geometry.attributes.position.values; var vectors = geometry.attributes[attributeName].values; var positionsLength = positions.length; var newPositions = new Float64Array(2 * positionsLength); var j = 0; for (var i = 0; i < positionsLength; i += 3) { newPositions[j++] = positions[i]; newPositions[j++] = positions[i + 1]; newPositions[j++] = positions[i + 2]; newPositions[j++] = positions[i] + (vectors[i] * length); newPositions[j++] = positions[i + 1] + (vectors[i + 1] * length); newPositions[j++] = positions[i + 2] + (vectors[i + 2] * length); } var newBoundingSphere; var bs = geometry.boundingSphere; if (when.defined(bs)) { newBoundingSphere = new Transforms.BoundingSphere(bs.center, bs.radius + length); } return new GeometryAttribute.Geometry({ attributes : { position : new GeometryAttribute.GeometryAttribute({ componentDatatype : ComponentDatatype.ComponentDatatype.DOUBLE, componentsPerAttribute : 3, values : newPositions }) }, primitiveType : GeometryAttribute.PrimitiveType.LINES, boundingSphere : newBoundingSphere }); }; /** * Creates an object that maps attribute names to unique locations (indices) * for matching vertex attributes and shader programs. * * @param {Geometry} geometry The geometry, which is not modified, to create the object for. * @returns {Object} An object with attribute name / index pairs. * * @example * var attributeLocations = Cesium.GeometryPipeline.createAttributeLocations(geometry); * // Example output * // { * // 'position' : 0, * // 'normal' : 1 * // } */ GeometryPipeline.createAttributeLocations = function(geometry) { //>>includeStart('debug', pragmas.debug); if (!when.defined(geometry)) { throw new Check.DeveloperError('geometry is required.'); } //>>includeEnd('debug') // There can be a WebGL performance hit when attribute 0 is disabled, so // assign attribute locations to well-known attributes. var semantics = [ 'position', 'positionHigh', 'positionLow', // From VertexFormat.position - after 2D projection and high-precision encoding 'position3DHigh', 'position3DLow', 'position2DHigh', 'position2DLow', // From Primitive 'pickColor', // From VertexFormat 'normal', 'st', 'tangent', 'bitangent', // For shadow volumes 'extrudeDirection', // From compressing texture coordinates and normals 'compressedAttributes' ]; var attributes = geometry.attributes; var indices = {}; var j = 0; var i; var len = semantics.length; // Attribute locations for well-known attributes for (i = 0; i < len; ++i) { var semantic = semantics[i]; if (when.defined(attributes[semantic])) { indices[semantic] = j++; } } // Locations for custom attributes for (var name in attributes) { if (attributes.hasOwnProperty(name) && (!when.defined(indices[name]))) { indices[name] = j++; } } return indices; }; /** * Reorders a geometry's attributes and <code>indices</code> to achieve better performance from the GPU's pre-vertex-shader cache. * * @param {Geometry} geometry The geometry to modify. * @returns {Geometry} The modified <code>geometry</code> argument, with its attributes and indices reordered for the GPU's pre-vertex-shader cache. * * @exception {DeveloperError} Each attribute array in geometry.attributes must have the same number of attributes. * * * @example * geometry = Cesium.GeometryPipeline.reorderForPreVertexCache(geometry); * * @see GeometryPipeline.reorderForPostVertexCache */ GeometryPipeline.reorderForPreVertexCache = function(geometry) { //>>includeStart('debug', pragmas.debug); if (!when.defined(geometry)) { throw new Check.DeveloperError('geometry is required.'); } //>>includeEnd('debug'); var numVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry); var indices = geometry.indices; if (when.defined(indices)) { var indexCrossReferenceOldToNew = new Int32Array(numVertices); for ( var i = 0; i < numVertices; i++) { indexCrossReferenceOldToNew[i] = -1; } // Construct cross reference and reorder indices var indicesIn = indices; var numIndices = indicesIn.length; var indicesOut = IndexDatatype.IndexDatatype.createTypedArray(numVertices, numIndices); var intoIndicesIn = 0; var intoIndicesOut = 0; var nextIndex = 0; var tempIndex; while (intoIndicesIn < numIndices) { tempIndex = indexCrossReferenceOldToNew[indicesIn[intoIndicesIn]]; if (tempIndex !== -1) { indicesOut[intoIndicesOut] = tempIndex; } else { tempIndex = indicesIn[intoIndicesIn]; indexCrossReferenceOldToNew[tempIndex] = nextIndex; indicesOut[intoIndicesOut] = nextIndex; ++nextIndex; } ++intoIndicesIn; ++intoIndicesOut; } geometry.indices = indicesOut; // Reorder attributes var attributes = geometry.attributes; for ( var property in attributes) { if (attributes.hasOwnProperty(property) && when.defined(attributes[property]) && when.defined(attributes[property].values)) { var attribute = attributes[property]; var elementsIn = attribute.values; var intoElementsIn = 0; var numComponents = attribute.componentsPerAttribute; var elementsOut = ComponentDatatype.ComponentDatatype.createTypedArray(attribute.componentDatatype, nextIndex * numComponents); while (intoElementsIn < numVertices) { var temp = indexCrossReferenceOldToNew[intoElementsIn]; if (temp !== -1) { for (var j = 0; j < numComponents; j++) { elementsOut[numComponents * temp + j] = elementsIn[numComponents * intoElementsIn + j]; } } ++intoElementsIn; } attribute.values = elementsOut; } } } return geometry; }; /** * Reorders a geometry's <code>indices</code> to achieve better performance from the GPU's * post vertex-shader cache by using the Tipsify algorithm. If the geometry <code>primitiveType</code> * is not <code>TRIANGLES</code> or the geometry does not have an <code>indices</code>, this function has no effect. * * @param {Geometry} geometry The geometry to modify. * @param {Number} [cacheCapacity=24] The number of vertices that can be held in the GPU's vertex cache. * @returns {Geometry} The modified <code>geometry</code> argument, with its indices reordered for the post-vertex-shader cache. * * @exception {DeveloperError} cacheCapacity must be greater than two. * * * @example * geometry = Cesium.GeometryPipeline.reorderForPostVertexCache(geometry); * * @see GeometryPipeline.reorderForPreVertexCache * @see {@link http://gfx.cs.princ0eton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf|Fast Triangle Reordering for Vertex Locality and Reduced Overdraw} * by Sander, Nehab, and Barczak */ GeometryPipeline.reorderForPostVertexCache = function(geometry, cacheCapacity) { //>>includeStart('debug', pragmas.debug); if (!when.defined(geometry)) { throw new Check.DeveloperError('geometry is required.'); } //>>includeEnd('debug'); var indices = geometry.indices; if ((geometry.primitiveType === GeometryAttribute.PrimitiveType.TRIANGLES) && (when.defined(indices))) { var numIndices = indices.length; var maximumIndex = 0; for ( var j = 0; j < numIndices; j++) { if (indices[j] > maximumIndex) { maximumIndex = indices[j]; } } geometry.indices = Tipsify.tipsify({ indices : indices, maximumIndex : maximumIndex, cacheSize : cacheCapacity }); } return geometry; }; function copyAttributesDescriptions(attributes) { var newAttributes = {}; for ( var attribute in attributes) { if (attributes.hasOwnProperty(attribute) && when.defined(attributes[attribute]) && when.defined(attributes[attribute].values)) { var attr = attributes[attribute]; newAttributes[attribute] = new GeometryAttribute.GeometryAttribute({ componentDatatype : attr.componentDatatype, componentsPerAttribute : attr.componentsPerAttribute, normalize : attr.normalize, values : [] }); } } return newAttributes; } function copyVertex(destinationAttributes, sourceAttributes, index) { for ( var attribute in sourceAttributes) { if (sourceAttributes.hasOwnProperty(attribute) && when.defined(sourceAttributes[attribute]) && when.defined(sourceAttributes[attribute].values)) { var attr = sourceAttributes[attribute]; for ( var k = 0; k < attr.componentsPerAttribute; ++k) { destinationAttributes[attribute].values.push(attr.values[(index * attr.componentsPerAttribute) + k]); } } } } /** * Splits a geometry into multiple geometries, if necessary, to ensure that indices in the * <code>indices</code> fit into unsigned shorts. This is used to meet the WebGL requirements * when unsigned int indices are not supported. * <p> * If the geometry does not have any <code>indices</code>, this function has no effect. * </p> * * @param {Geometry} geometry The geometry to be split into multiple geometries. * @returns {Geometry[]} An array of geometries, each with indices that fit into unsigned shorts. * * @exception {DeveloperError} geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS * @exception {DeveloperError} All geometry attribute lists must have the same number of attributes. * * @example * var geometries = Cesium.GeometryPipeline.fitToUnsignedShortIndices(geometry); */ GeometryPipeline.fitToUnsignedShortIndices = function(geometry) { //>>includeStart('debug', pragmas.debug); if (!when.defined(geometry)) { throw new Check.DeveloperError('geometry is required.'); } if ((when.defined(geometry.indices)) && ((geometry.primitiveType !== GeometryAttribute.PrimitiveType.TRIANGLES) && (geometry.primitiveType !== GeometryAttribute.PrimitiveType.LINES) && (geometry.primitiveType !== GeometryAttribute.PrimitiveType.POINTS))) { throw new Check.DeveloperError('geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS.'); } //>>includeEnd('debug'); var geometries = []; // If there's an index list and more than 64K attributes, it is possible that // some indices are outside the range of unsigned short [0, 64K - 1] var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry); if (when.defined(geometry.indices) && (numberOfVertices >= _Math.CesiumMath.SIXTY_FOUR_KILOBYTES)) { var oldToNewIndex = []; var newIndices = []; var currentIndex = 0; var newAttributes = copyAttributesDescriptions(geometry.attributes); var originalIndices = geometry.indices; var numberOfIndices = originalIndices.length; var indicesPerPrimitive; if (geometry.primitiveType === GeometryAttribute.PrimitiveType.TRIANGLES) { indicesPerPrimitive = 3; } else if (geometry.primitiveType === GeometryAttribute.PrimitiveType.LINES) { indicesPerPrimitive = 2; } else if (geometry.primitiveType === GeometryAttribute.PrimitiveType.POINTS) { indicesPerPrimitive = 1; } for ( var j = 0; j < numberOfIndices; j += indicesPerPrimitive) { for (var k = 0; k < indicesPerPrimitive; ++k) { var x = originalIndices[j + k]; var i = oldToNewIndex[x]; if (!when.defined(i)) { i = currentIndex++; oldToNewIndex[x] = i; copyVertex(newAttributes, geometry.attributes, x); } newIndices.push(i); } if (currentIndex + indicesPerPrimitive >= _Math.CesiumMath.SIXTY_FOUR_KILOBYTES) { geometries.push(new GeometryAttribute.Geometry({ attributes : newAttributes, indices : newIndices, primitiveType : geometry.primitiveType, boundingSphere : geometry.boundingSphere, boundingSphereCV : geometry.boundingSphereCV })); // Reset for next vertex-array oldToNewIndex = []; newIndices = []; currentIndex = 0; newAttributes = copyAttributesDescriptions(geometry.attributes); } } if (newIndices.length !== 0) { geometries.push(new GeometryAttribute.Geometry({ attributes : newAttributes, indices : newIndices, primitiveType : geometry.primitiveType, boundingSphere : geometry.boundingSphere, boundingSphereCV : geometry.boundingSphereCV })); } } else { // No need to split into multiple geometries geometries.push(geometry); } return geometries; }; var scratchProjectTo2DCartesian3 = new Cartesian2.Cartesian3(); var scratchProjectTo2DCartographic = new Cartesian2.Cartographic(); /** * Projects a geometry's 3D <code>position</code> attribute to 2D, replacing the <code>position</code> * attribute with separate <code>position3D</code> and <code>position2D</code> attributes. * <p> * If the geometry does not have a <code>position</code>, this function has no effect. * </p> * * @param {Geometry} geometry The geometry to modify. * @param {String} attributeName The name of the attribute. * @param {String} attributeName3D The name of the attribute in 3D. * @param {String} attributeName2D The name of the attribute in 2D. * @param {Object} [projection=new GeographicProjection()] The projection to use. * @returns {Geometry} The modified <code>geometry</code> argument with <code>position3D</code> and <code>position2D</code> attributes. * * @exception {DeveloperError} geometry must have attribute matching the attributeName argument. * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE. * @exception {DeveloperError} Could not project a point to 2D. * * @example * geometry = Cesium.GeometryPipeline.projectTo2D(geometry, 'position', 'position3D', 'position2D'); */ GeometryPipeline.projectTo2D = function(geometry, attributeName, attributeName3D, attributeName2D, projection) { //>>includeStart('debug', pragmas.debug); if (!when.defined(geometry)) { throw new Check.DeveloperError('geometry is required.'); } if (!when.defined(attributeName)) { throw new Check.DeveloperError('attributeName is required.'); } if (!when.defined(attributeName3D)) { throw new Check.DeveloperError('attributeName3D is required.'); } if (!when.defined(attributeName2D)) { throw new Check.DeveloperError('attributeName2D is required.'); } if (!when.defined(geometry.attributes[attributeName])) { throw new Check.DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.'); } if (geometry.attributes[attributeName].componentDatatype !== ComponentDatatype.ComponentDatatype.DOUBLE) { throw new Check.DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.'); } //>>includeEnd('debug'); var attribute = geometry.attributes[attributeName]; projection = (when.defined(projection)) ? projection : new Transforms.GeographicProjection(); var ellipsoid = projection.ellipsoid; // Project original values to 2D. var values3D = attribute.values; var projectedValues = new Float64Array(values3D.length); var index = 0; for ( var i = 0; i < values3D.length; i += 3) { var value = Cartesian2.Cartesian3.fromArray(values3D, i, scratchProjectTo2DCartesian3); var lonLat = ellipsoid.cartesianToCartographic(value, scratchProjectTo2DCartographic); //>>includeStart('debug', pragmas.debug); if (!when.defined(lonLat)) { throw new Check.DeveloperError('Could not project point (' + value.x + ', ' + value.y + ', ' + value.z + ') to 2D.'); } //>>includeEnd('debug'); var projectedLonLat = projection.project(lonLat, scratchProjectTo2DCartesian3); projectedValues[index++] = projectedLonLat.x; projectedValues[index++] = projectedLonLat.y; projectedValues[index++] = projectedLonLat.z; } // Rename original cartesians to WGS84 cartesians. geometry.attributes[attributeName3D] = attribute; // Replace original cartesians with 2D projected cartesians geometry.attributes[attributeName2D] = new GeometryAttribute.GeometryAttribute({ componentDatatype : ComponentDatatype.ComponentDatatype.DOUBLE, componentsPerAttribute : 3, values : projectedValues }); delete geometry.attributes[attributeName]; return geometry; }; var encodedResult = { high : 0.0, low : 0.0 }; /** * Encodes floating-point geometry attribute values as two separate attributes to improve * rendering precision. * <p> * This is commonly used to create high-precision position vertex attributes. * </p> * * @param {Geometry} geometry The geometry to modify. * @param {String} attributeName The name of the attribute. * @param {String} attributeHighName The name of the attribute for the encoded high bits. * @param {String} attributeLowName The name of the attribute for the encoded low bits. * @returns {Geometry} The modified <code>geometry</code> argument, with its encoded attribute. * * @exception {DeveloperError} geometry must have attribute matching the attributeName argument. * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE. * * @example * geometry = Cesium.GeometryPipeline.encodeAttribute(geometry, 'position3D', 'position3DHigh', 'position3DLow'); */ GeometryPipeline.encodeAttribute = function(geometry, attributeName, attributeHighName, attributeLowName) { //>>includeStart('debug', pragmas.debug); if (!when.defined(geometry)) { throw new Check.DeveloperError('geometry is required.'); } if (!when.defined(attributeName)) { throw new Check.DeveloperError('attributeName is required.'); } if (!when.defined(attributeHighName)) { throw new Check.DeveloperError('attributeHighName is required.'); } if (!when.defined(attributeLowName)) { throw new Check.DeveloperError('attributeLowName is required.'); } if (!when.defined(geometry.attributes[attributeName])) { throw new Check.DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.'); } if (geometry.attributes[attributeName].componentDatatype !== ComponentDatatype.ComponentDatatype.DOUBLE) { throw new Check.DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.'); } //>>includeEnd('debug'); var attribute = geometry.attributes[attributeName]; var values = attribute.values; var length = values.length; var highValues = new Float32Array(length); var lowValues = new Float32Array(length); for (var i = 0; i < length; ++i) { EncodedCartesian3.EncodedCartesian3.encode(values[i], encodedResult); highValues[i] = encodedResult.high; lowValues[i] = encodedResult.low; } var componentsPerAttribute = attribute.componentsPerAttribute; geometry.attributes[attributeHighName] = new GeometryAttribute.GeometryAttribute({ componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT, componentsPerAttribute : componentsPerAttribute, values : highValues }); geometry.attributes[attributeLowName] = new GeometryAttribute.GeometryAttribute({ componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT, componentsPerAttribute : componentsPerAttribute, values : lowValues }); delete geometry.attributes[attributeName]; return geometry; }; var scratchCartesian3$1 = new Cartesian2.Cartesian3(); function transformPoint(matrix, attribute) { if (when.defined(attribute)) { var values = attribute.values; var length = values.length; for (var i = 0; i < length; i += 3) { Cartesian2.Cartesian3.unpack(values, i, scratchCartesian3$1); Transforms.Matrix4.multiplyByPoint(matrix, scratchCartesian3$1, scratchCartesian3$1); Cartesian2.Cartesian3.pack(scratchCartesian3$1, values, i); } } } function transformVector(matrix, attribute) { if (when.defined(attribute)) { var values = attribute.values; var length = values.length; for (var i = 0; i < length; i += 3) { Cartesian2.Cartesian3.unpack(values, i, scratchCartesian3$1); Transforms.Matrix3.multiplyByVector(matrix, scratchCartesian3$1, scratchCartesian3$1); scratchCartesian3$1 = Cartesian2.Cartesian3.normalize(scr