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
JavaScript
/* 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