diff --git a/Apps/Sandcastle/gallery/Animations.html b/Apps/Sandcastle/gallery/Animations.html index b40c6109f0a6..b6fbabfcf6af 100644 --- a/Apps/Sandcastle/gallery/Animations.html +++ b/Apps/Sandcastle/gallery/Animations.html @@ -34,7 +34,7 @@ { "use strict"; - var polygon; + var extent; var rectangularSensor; function addAlphaAnimation(primitive, scene) { @@ -49,22 +49,14 @@ }); } - function addHeightAnimation(primitive, scene) { - Sandcastle.declare(addHeightAnimation); // For highlighting in Sandcastle. - scene.getAnimations().addProperty(primitive, 'height', 5000000.0, 0.0, { - duration : 1000 - }); - } - function addStripeAnimation(primitive, scene) { Sandcastle.declare(addStripeAnimation); // For highlighting in Sandcastle. scene.getAnimations().addOffsetIncrement(primitive.material); } - function resetPolygonPropeties(polygon) { - polygon.height = 0.0; - polygon.material.uniforms.time = 1.0; - polygon.material.uniforms.color = new Cesium.Color(1.0, 0.0, 0.0, 0.5); + function resetExtentPropeties(extent) { + extent.material.uniforms.time = 1.0; + extent.material.uniforms.color = new Cesium.Color(1.0, 0.0, 0.0, 0.5); } function createPrimitives(widget) { @@ -72,14 +64,15 @@ var scene = widget.scene; var primitives = scene.getPrimitives(); - polygon = new Cesium.Polygon(); - polygon.configureExtent(new Cesium.Extent( - Cesium.Math.toRadians(-120.0), - Cesium.Math.toRadians(20.0), - Cesium.Math.toRadians(-80.0), - Cesium.Math.toRadians(50.0))); - polygon.material = Cesium.Material.fromType(scene.getContext(), 'Erosion'); - primitives.add(polygon); + extent = new Cesium.ExtentPrimitive({ + extent : new Cesium.Extent( + Cesium.Math.toRadians(-120.0), + Cesium.Math.toRadians(20.0), + Cesium.Math.toRadians(-80.0), + Cesium.Math.toRadians(50.0)), + material : Cesium.Material.fromType(scene.getContext(), 'Erosion') + }); + primitives.add(extent); var modelMatrix = Cesium.Transforms.northEastDownToFixedFrame( ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-45.0, 45.0))); @@ -105,8 +98,8 @@ label : 'Alpha Animation', onClick : function() { scene.getAnimations().removeAll(); - resetPolygonPropeties(polygon); - addAlphaAnimation(polygon, scene); + resetExtentPropeties(extent); + addAlphaAnimation(extent, scene); Sandcastle.highlight(addAlphaAnimation); } }).placeAt('toolbar'); @@ -115,22 +108,12 @@ label : 'Erosion Animation', onClick : function() { scene.getAnimations().removeAll(); - resetPolygonPropeties(polygon); - addErosionAnimation(polygon, scene); + resetExtentPropeties(extent); + addErosionAnimation(extent, scene); Sandcastle.highlight(addErosionAnimation); } }).placeAt('toolbar'); - new Button({ - label : 'Height Animation', - onClick : function() { - scene.getAnimations().removeAll(); - resetPolygonPropeties(polygon); - addHeightAnimation(polygon, scene); - Sandcastle.highlight(addHeightAnimation); - } - }).placeAt('toolbar'); - new Button({ label : 'Stripe Animation', onClick : function() { diff --git a/Apps/Sandcastle/gallery/Custom Rendering.html b/Apps/Sandcastle/gallery/Custom Rendering.html deleted file mode 100644 index dcde55549600..000000000000 --- a/Apps/Sandcastle/gallery/Custom Rendering.html +++ /dev/null @@ -1,386 +0,0 @@ - - -
- - - - -true
if they are equal, false
otherwise.
@@ -840,6 +983,18 @@ define([
return BoundingSphere.getPlaneDistances(this, position, direction, result);
};
+ /**
+ * Creates a bounding sphere in 2D from this bounding sphere. This bounding sphere must be in 3D world coordinates.
+ * @memberof BoundingSphere
+ *
+ * @param {Object} [projection=GeographicProjection] The projection to 2D.
+ * @param {BoundingSphere} [result] The object onto which to store the result.
+ * @return {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided.
+ */
+ BoundingSphere.prototype.projectTo2D = function(projection, result) {
+ return BoundingSphere.projectTo2D(this, projection, result);
+ };
+
/**
* Compares this BoundingSphere against the provided BoundingSphere componentwise and returns
* true
if they are equal, false
otherwise.
diff --git a/Source/Core/BoxGeometry.js b/Source/Core/BoxGeometry.js
new file mode 100644
index 000000000000..669b4f75adeb
--- /dev/null
+++ b/Source/Core/BoxGeometry.js
@@ -0,0 +1,727 @@
+/*global define*/
+define([
+ './DeveloperError',
+ './Cartesian3',
+ './ComponentDatatype',
+ './PrimitiveType',
+ './defaultValue',
+ './BoundingSphere',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './VertexFormat'
+ ], function(
+ DeveloperError,
+ Cartesian3,
+ ComponentDatatype,
+ PrimitiveType,
+ defaultValue,
+ BoundingSphere,
+ GeometryAttribute,
+ GeometryAttributes,
+ VertexFormat) {
+ "use strict";
+
+ var diffScratch = new Cartesian3();
+
+ /**
+ * A {@link Geometry} that represents vertices and indices for a cube centered at the origin.
+ *
+ * @alias BoxGeometry
+ * @constructor
+ *
+ * @param {Cartesian3} options.minimumCorner The minimum x, y, and z coordinates of the box.
+ * @param {Cartesian3} options.maximumCorner The maximum x, y, and z coordinates of the box.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ *
+ * @exception {DeveloperError} options.minimumCorner is required.
+ * @exception {DeveloperError} options.maximumCorner is required.
+ *
+ * @example
+ * var box = new BoxGeometry({
+ * vertexFormat : VertexFormat.POSITION_ONLY,
+ * maximumCorner : new Cartesian3(250000.0, 250000.0, 250000.0),
+ * minimumCorner : new Cartesian3(-250000.0, -250000.0, -250000.0)
+ * });
+ */
+ var BoxGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var min = options.minimumCorner;
+ var max = options.maximumCorner;
+
+ if (typeof min === 'undefined') {
+ throw new DeveloperError('options.minimumCorner is required.');
+ }
+
+ if (typeof max === 'undefined') {
+ throw new DeveloperError('options.maximumCorner is required');
+ }
+
+ var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
+
+ var attributes = new GeometryAttributes();
+ var indices;
+ var positions;
+
+ if (vertexFormat !== VertexFormat.POSITION_ONLY) {
+ if (vertexFormat.position) {
+ // 8 corner points. Duplicated 3 times each for each incident edge/face.
+ positions = new Float64Array(6 * 4 * 3);
+
+ // +z face
+ positions[0] = min.x;
+ positions[1] = min.y;
+ positions[2] = max.z;
+ positions[3] = max.x;
+ positions[4] = min.y;
+ positions[5] = max.z;
+ positions[6] = max.x;
+ positions[7] = max.y;
+ positions[8] = max.z;
+ positions[9] = min.x;
+ positions[10] = max.y;
+ positions[11] = max.z;
+
+ // -z face
+ positions[12] = min.x;
+ positions[13] = min.y;
+ positions[14] = min.z;
+ positions[15] = max.x;
+ positions[16] = min.y;
+ positions[17] = min.z;
+ positions[18] = max.x;
+ positions[19] = max.y;
+ positions[20] = min.z;
+ positions[21] = min.x;
+ positions[22] = max.y;
+ positions[23] = min.z;
+
+ // +x face
+ positions[24] = max.x;
+ positions[25] = min.y;
+ positions[26] = min.z;
+ positions[27] = max.x;
+ positions[28] = max.y;
+ positions[29] = min.z;
+ positions[30] = max.x;
+ positions[31] = max.y;
+ positions[32] = max.z;
+ positions[33] = max.x;
+ positions[34] = min.y;
+ positions[35] = max.z;
+
+ // -x face
+ positions[36] = min.x;
+ positions[37] = min.y;
+ positions[38] = min.z;
+ positions[39] = min.x;
+ positions[40] = max.y;
+ positions[41] = min.z;
+ positions[42] = min.x;
+ positions[43] = max.y;
+ positions[44] = max.z;
+ positions[45] = min.x;
+ positions[46] = min.y;
+ positions[47] = max.z;
+
+ // +y face
+ positions[48] = min.x;
+ positions[49] = max.y;
+ positions[50] = min.z;
+ positions[51] = max.x;
+ positions[52] = max.y;
+ positions[53] = min.z;
+ positions[54] = max.x;
+ positions[55] = max.y;
+ positions[56] = max.z;
+ positions[57] = min.x;
+ positions[58] = max.y;
+ positions[59] = max.z;
+
+ // -y face
+ positions[60] = min.x;
+ positions[61] = min.y;
+ positions[62] = min.z;
+ positions[63] = max.x;
+ positions[64] = min.y;
+ positions[65] = min.z;
+ positions[66] = max.x;
+ positions[67] = min.y;
+ positions[68] = max.z;
+ positions[69] = min.x;
+ positions[70] = min.y;
+ positions[71] = max.z;
+
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : positions
+ });
+ }
+
+ if (vertexFormat.normal) {
+ var normals = new Float32Array(6 * 4 * 3);
+
+ // +z face
+ normals[0] = 0.0;
+ normals[1] = 0.0;
+ normals[2] = 1.0;
+ normals[3] = 0.0;
+ normals[4] = 0.0;
+ normals[5] = 1.0;
+ normals[6] = 0.0;
+ normals[7] = 0.0;
+ normals[8] = 1.0;
+ normals[9] = 0.0;
+ normals[10] = 0.0;
+ normals[11] = 1.0;
+
+ // -z face
+ normals[12] = 0.0;
+ normals[13] = 0.0;
+ normals[14] = -1.0;
+ normals[15] = 0.0;
+ normals[16] = 0.0;
+ normals[17] = -1.0;
+ normals[18] = 0.0;
+ normals[19] = 0.0;
+ normals[20] = -1.0;
+ normals[21] = 0.0;
+ normals[22] = 0.0;
+ normals[23] = -1.0;
+
+ // +x face
+ normals[24] = 1.0;
+ normals[25] = 0.0;
+ normals[26] = 0.0;
+ normals[27] = 1.0;
+ normals[28] = 0.0;
+ normals[29] = 0.0;
+ normals[30] = 1.0;
+ normals[31] = 0.0;
+ normals[32] = 0.0;
+ normals[33] = 1.0;
+ normals[34] = 0.0;
+ normals[35] = 0.0;
+
+ // -x face
+ normals[36] = -1.0;
+ normals[37] = 0.0;
+ normals[38] = 0.0;
+ normals[39] = -1.0;
+ normals[40] = 0.0;
+ normals[41] = 0.0;
+ normals[42] = -1.0;
+ normals[43] = 0.0;
+ normals[44] = 0.0;
+ normals[45] = -1.0;
+ normals[46] = 0.0;
+ normals[47] = 0.0;
+
+ // +y face
+ normals[48] = 0.0;
+ normals[49] = 1.0;
+ normals[50] = 0.0;
+ normals[51] = 0.0;
+ normals[52] = 1.0;
+ normals[53] = 0.0;
+ normals[54] = 0.0;
+ normals[55] = 1.0;
+ normals[56] = 0.0;
+ normals[57] = 0.0;
+ normals[58] = 1.0;
+ normals[59] = 0.0;
+
+ // -y face
+ normals[60] = 0.0;
+ normals[61] = -1.0;
+ normals[62] = 0.0;
+ normals[63] = 0.0;
+ normals[64] = -1.0;
+ normals[65] = 0.0;
+ normals[66] = 0.0;
+ normals[67] = -1.0;
+ normals[68] = 0.0;
+ normals[69] = 0.0;
+ normals[70] = -1.0;
+ normals[71] = 0.0;
+
+ attributes.normal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : normals
+ });
+ }
+
+ if (vertexFormat.st) {
+ var texCoords = new Float32Array(6 * 4 * 2);
+
+ // +z face
+ texCoords[0] = 0.0;
+ texCoords[1] = 0.0;
+ texCoords[2] = 1.0;
+ texCoords[3] = 0.0;
+ texCoords[4] = 1.0;
+ texCoords[5] = 1.0;
+ texCoords[6] = 0.0;
+ texCoords[7] = 1.0;
+
+ // -z face
+ texCoords[8] = 1.0;
+ texCoords[9] = 0.0;
+ texCoords[10] = 0.0;
+ texCoords[11] = 0.0;
+ texCoords[12] = 0.0;
+ texCoords[13] = 1.0;
+ texCoords[14] = 1.0;
+ texCoords[15] = 1.0;
+
+ //+x face
+ texCoords[16] = 0.0;
+ texCoords[17] = 0.0;
+ texCoords[18] = 1.0;
+ texCoords[19] = 0.0;
+ texCoords[20] = 1.0;
+ texCoords[21] = 1.0;
+ texCoords[22] = 0.0;
+ texCoords[23] = 1.0;
+
+ // -x face
+ texCoords[24] = 1.0;
+ texCoords[25] = 0.0;
+ texCoords[26] = 0.0;
+ texCoords[27] = 0.0;
+ texCoords[28] = 0.0;
+ texCoords[29] = 1.0;
+ texCoords[30] = 1.0;
+ texCoords[31] = 1.0;
+
+ // +y face
+ texCoords[32] = 1.0;
+ texCoords[33] = 0.0;
+ texCoords[34] = 0.0;
+ texCoords[35] = 0.0;
+ texCoords[36] = 0.0;
+ texCoords[37] = 1.0;
+ texCoords[38] = 1.0;
+ texCoords[39] = 1.0;
+
+ // -y face
+ texCoords[40] = 0.0;
+ texCoords[41] = 0.0;
+ texCoords[42] = 1.0;
+ texCoords[43] = 0.0;
+ texCoords[44] = 1.0;
+ texCoords[45] = 1.0;
+ texCoords[46] = 0.0;
+ texCoords[47] = 1.0;
+
+ attributes.st = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : texCoords
+ });
+ }
+
+ if (vertexFormat.tangent) {
+ var tangents = new Float32Array(6 * 4 * 3);
+
+ // +z face
+ tangents[0] = 1.0;
+ tangents[1] = 0.0;
+ tangents[2] = 0.0;
+ tangents[3] = 1.0;
+ tangents[4] = 0.0;
+ tangents[5] = 0.0;
+ tangents[6] = 1.0;
+ tangents[7] = 0.0;
+ tangents[8] = 0.0;
+ tangents[9] = 1.0;
+ tangents[10] = 0.0;
+ tangents[11] = 0.0;
+
+ // -z face
+ tangents[12] = -1.0;
+ tangents[13] = 0.0;
+ tangents[14] = 0.0;
+ tangents[15] = -1.0;
+ tangents[16] = 0.0;
+ tangents[17] = 0.0;
+ tangents[18] = -1.0;
+ tangents[19] = 0.0;
+ tangents[20] = 0.0;
+ tangents[21] = -1.0;
+ tangents[22] = 0.0;
+ tangents[23] = 0.0;
+
+ // +x face
+ tangents[24] = 0.0;
+ tangents[25] = 1.0;
+ tangents[26] = 0.0;
+ tangents[27] = 0.0;
+ tangents[28] = 1.0;
+ tangents[29] = 0.0;
+ tangents[30] = 0.0;
+ tangents[31] = 1.0;
+ tangents[32] = 0.0;
+ tangents[33] = 0.0;
+ tangents[34] = 1.0;
+ tangents[35] = 0.0;
+
+ // -x face
+ tangents[36] = 0.0;
+ tangents[37] = -1.0;
+ tangents[38] = 0.0;
+ tangents[39] = 0.0;
+ tangents[40] = -1.0;
+ tangents[41] = 0.0;
+ tangents[42] = 0.0;
+ tangents[43] = -1.0;
+ tangents[44] = 0.0;
+ tangents[45] = 0.0;
+ tangents[46] = -1.0;
+ tangents[47] = 0.0;
+
+ // +y face
+ tangents[48] = -1.0;
+ tangents[49] = 0.0;
+ tangents[50] = 0.0;
+ tangents[51] = -1.0;
+ tangents[52] = 0.0;
+ tangents[53] = 0.0;
+ tangents[54] = -1.0;
+ tangents[55] = 0.0;
+ tangents[56] = 0.0;
+ tangents[57] = -1.0;
+ tangents[58] = 0.0;
+ tangents[59] = 0.0;
+
+ // -y face
+ tangents[60] = 1.0;
+ tangents[61] = 0.0;
+ tangents[62] = 0.0;
+ tangents[63] = 1.0;
+ tangents[64] = 0.0;
+ tangents[65] = 0.0;
+ tangents[66] = 1.0;
+ tangents[67] = 0.0;
+ tangents[68] = 0.0;
+ tangents[69] = 1.0;
+ tangents[70] = 0.0;
+ tangents[71] = 0.0;
+
+ attributes.tangent = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : tangents
+ });
+ }
+
+ if (vertexFormat.binormal) {
+ var binormals = new Float32Array(6 * 4 * 3);
+
+ // +z face
+ binormals[0] = 0.0;
+ binormals[1] = 1.0;
+ binormals[2] = 0.0;
+ binormals[3] = 0.0;
+ binormals[4] = 1.0;
+ binormals[5] = 0.0;
+ binormals[6] = 0.0;
+ binormals[7] = 1.0;
+ binormals[8] = 0.0;
+ binormals[9] = 0.0;
+ binormals[10] = 1.0;
+ binormals[11] = 0.0;
+
+ // -z face
+ binormals[12] = 0.0;
+ binormals[13] = 1.0;
+ binormals[14] = 0.0;
+ binormals[15] = 0.0;
+ binormals[16] = 1.0;
+ binormals[17] = 0.0;
+ binormals[18] = 0.0;
+ binormals[19] = 1.0;
+ binormals[20] = 0.0;
+ binormals[21] = 0.0;
+ binormals[22] = 1.0;
+ binormals[23] = 0.0;
+
+ // +x face
+ binormals[24] = 0.0;
+ binormals[25] = 0.0;
+ binormals[26] = 1.0;
+ binormals[27] = 0.0;
+ binormals[28] = 0.0;
+ binormals[29] = 1.0;
+ binormals[30] = 0.0;
+ binormals[31] = 0.0;
+ binormals[32] = 1.0;
+ binormals[33] = 0.0;
+ binormals[34] = 0.0;
+ binormals[35] = 1.0;
+
+ // -x face
+ binormals[36] = 0.0;
+ binormals[37] = 0.0;
+ binormals[38] = 1.0;
+ binormals[39] = 0.0;
+ binormals[40] = 0.0;
+ binormals[41] = 1.0;
+ binormals[42] = 0.0;
+ binormals[43] = 0.0;
+ binormals[44] = 1.0;
+ binormals[45] = 0.0;
+ binormals[46] = 0.0;
+ binormals[47] = 1.0;
+
+ // +y face
+ binormals[48] = 0.0;
+ binormals[49] = 0.0;
+ binormals[50] = 1.0;
+ binormals[51] = 0.0;
+ binormals[52] = 0.0;
+ binormals[53] = 1.0;
+ binormals[54] = 0.0;
+ binormals[55] = 0.0;
+ binormals[56] = 1.0;
+ binormals[57] = 0.0;
+ binormals[58] = 0.0;
+ binormals[59] = 1.0;
+
+ // -y face
+ binormals[60] = 0.0;
+ binormals[61] = 0.0;
+ binormals[62] = 1.0;
+ binormals[63] = 0.0;
+ binormals[64] = 0.0;
+ binormals[65] = 1.0;
+ binormals[66] = 0.0;
+ binormals[67] = 0.0;
+ binormals[68] = 1.0;
+ binormals[69] = 0.0;
+ binormals[70] = 0.0;
+ binormals[71] = 1.0;
+
+ attributes.binormal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : binormals
+ });
+ }
+
+ // 12 triangles: 6 faces, 2 triangles each.
+ indices = new Uint16Array(6 * 2 * 3);
+
+ // +z face
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+ indices[3] = 0;
+ indices[4] = 2;
+ indices[5] = 3;
+
+ // -z face
+ indices[6] = 4 + 2;
+ indices[7] = 4 + 1;
+ indices[8] = 4 + 0;
+ indices[9] = 4 + 3;
+ indices[10] = 4 + 2;
+ indices[11] = 4 + 0;
+
+ // +x face
+ indices[12] = 8 + 0;
+ indices[13] = 8 + 1;
+ indices[14] = 8 + 2;
+ indices[15] = 8 + 0;
+ indices[16] = 8 + 2;
+ indices[17] = 8 + 3;
+
+ // -x face
+ indices[18] = 12 + 2;
+ indices[19] = 12 + 1;
+ indices[20] = 12 + 0;
+ indices[21] = 12 + 3;
+ indices[22] = 12 + 2;
+ indices[23] = 12 + 0;
+
+ // +y face
+ indices[24] = 16 + 2;
+ indices[25] = 16 + 1;
+ indices[26] = 16 + 0;
+ indices[27] = 16 + 3;
+ indices[28] = 16 + 2;
+ indices[29] = 16 + 0;
+
+ // -y face
+ indices[30] = 20 + 0;
+ indices[31] = 20 + 1;
+ indices[32] = 20 + 2;
+ indices[33] = 20 + 0;
+ indices[34] = 20 + 2;
+ indices[35] = 20 + 3;
+ } else {
+ // Positions only - no need to duplicate corner points
+ positions = new Float64Array(8 * 3);
+
+ positions[0] = min.x;
+ positions[1] = min.y;
+ positions[2] = min.z;
+ positions[3] = max.x;
+ positions[4] = min.y;
+ positions[5] = min.z;
+ positions[6] = max.x;
+ positions[7] = max.y;
+ positions[8] = min.z;
+ positions[9] = min.x;
+ positions[10] = max.y;
+ positions[11] = min.z;
+ positions[12] = min.x;
+ positions[13] = min.y;
+ positions[14] = max.z;
+ positions[15] = max.x;
+ positions[16] = min.y;
+ positions[17] = max.z;
+ positions[18] = max.x;
+ positions[19] = max.y;
+ positions[20] = max.z;
+ positions[21] = min.x;
+ positions[22] = max.y;
+ positions[23] = max.z;
+
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : positions
+ });
+
+ // 12 triangles: 6 faces, 2 triangles each.
+ indices = new Uint16Array(6 * 2 * 3);
+
+ // plane z = corner.Z
+ indices[0] = 4;
+ indices[1] = 5;
+ indices[2] = 6;
+ indices[3] = 4;
+ indices[4] = 6;
+ indices[5] = 7;
+
+ // plane z = -corner.Z
+ indices[6] = 1;
+ indices[7] = 0;
+ indices[8] = 3;
+ indices[9] = 1;
+ indices[10] = 3;
+ indices[11] = 2;
+
+ // plane x = corner.X
+ indices[12] = 1;
+ indices[13] = 6;
+ indices[14] = 5;
+ indices[15] = 1;
+ indices[16] = 2;
+ indices[17] = 6;
+
+ // plane y = corner.Y
+ indices[18] = 2;
+ indices[19] = 3;
+ indices[20] = 7;
+ indices[21] = 2;
+ indices[22] = 7;
+ indices[23] = 6;
+
+ // plane x = -corner.X
+ indices[24] = 3;
+ indices[25] = 0;
+ indices[26] = 4;
+ indices[27] = 3;
+ indices[28] = 4;
+ indices[29] = 7;
+
+ // plane y = -corner.Y
+ indices[30] = 0;
+ indices[31] = 1;
+ indices[32] = 5;
+ indices[33] = 0;
+ indices[34] = 5;
+ indices[35] = 4;
+ }
+
+ var diff = Cartesian3.subtract(max, min, diffScratch);
+ var radius = diff.magnitude() * 0.5;
+
+ /**
+ * An object containing {@link GeometryAttribute} properties named after each of the
+ * true
values of the {@link VertexFormat} option.
+ *
+ * @type GeometryAttributes
+ *
+ * @see Geometry#attributes
+ */
+ this.attributes = attributes;
+
+ /**
+ * Index data that, along with {@link Geometry#primitiveType}, determines the primitives in the geometry.
+ *
+ * @type Array
+ */
+ this.indices = indices;
+
+ /**
+ * The type of primitives in the geometry. For this geometry, it is {@link PrimitiveType.TRIANGLES}.
+ *
+ * @type PrimitiveType
+ */
+ this.primitiveType = PrimitiveType.TRIANGLES;
+
+ /**
+ * A tight-fitting bounding sphere that encloses the vertices of the geometry.
+ *
+ * @type BoundingSphere
+ */
+ this.boundingSphere = new BoundingSphere(Cartesian3.ZERO, radius);
+ };
+
+ /**
+ * Creates vertices and indices for a cube centered at the origin given its dimensions.
+ * @memberof BoxGeometry
+ *
+ * @param {Cartesian3} options.dimensions The width, depth, and height of the box stored in the x, y, and z coordinates of the Cartesian3
, respectively.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ *
+ * @exception {DeveloperError} options.dimensions is required.
+ * @exception {DeveloperError} All dimensions components must be greater than or equal to zero.
+ *
+ * @example
+ * var box = BoxGeometry.fromDimensions({
+ * vertexFormat : VertexFormat.POSITION_ONLY,
+ * dimensions : new Cartesian3(500000.0, 500000.0, 500000.0)
+ * });
+ */
+ BoxGeometry.fromDimensions = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var dimensions = options.dimensions;
+ if (typeof dimensions === 'undefined') {
+ throw new DeveloperError('options.dimensions is required.');
+ }
+
+ if (dimensions.x < 0 || dimensions.y < 0 || dimensions.z < 0) {
+ throw new DeveloperError('All dimensions components must be greater than or equal to zero.');
+ }
+
+ var corner = dimensions.multiplyByScalar(0.5);
+ var min = corner.negate();
+ var max = corner;
+
+ var newOptions = {
+ minimumCorner : min,
+ maximumCorner : max,
+ vertexFormat : options.vertexFormat
+ };
+ return new BoxGeometry(newOptions);
+ };
+
+ return BoxGeometry;
+});
\ No newline at end of file
diff --git a/Source/Core/BoxTessellator.js b/Source/Core/BoxTessellator.js
deleted file mode 100644
index 3e96251b0457..000000000000
--- a/Source/Core/BoxTessellator.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/*global define*/
-define([
- './DeveloperError',
- './Cartesian3',
- './ComponentDatatype',
- './PrimitiveType',
- './defaultValue'
- ], function(
- DeveloperError,
- Cartesian3,
- ComponentDatatype,
- PrimitiveType,
- defaultValue) {
- "use strict";
-
- /**
- * DOC_TBA
- *
- * @alias BoxTessellator
- * @exports BoxTessellator
- *
- * @see CubeMapEllipsoidTessellator
- * @see PlaneTessellator
- */
- var BoxTessellator = {
- /**
- * DOC_TBA
- *
- * @exception {DeveloperError} All dimensions' components must be greater than or equal to zero.
- */
- compute : function(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
-
- var minimumCorner;
- var maximumCorner;
-
- if (typeof options.minimumCorner !== 'undefined' && typeof options.maximumCorner !== 'undefined') {
- minimumCorner = options.minimumCorner;
- maximumCorner = options.maximumCorner;
- } else {
- var dimensions = typeof options.dimensions !== 'undefined' ? options.dimensions : new Cartesian3(1.0, 1.0, 1.0);
-
- if (dimensions.x < 0 || dimensions.y < 0 || dimensions.z < 0) {
- throw new DeveloperError('All dimensions components must be greater than or equal to zero.');
- }
-
- var corner = dimensions.multiplyByScalar(0.5);
- minimumCorner = corner.negate();
- maximumCorner = corner;
- }
-
- var mesh = {};
- mesh.attributes = {};
- mesh.indexLists = [];
-
- // 8 corner points.
- mesh.attributes.position = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 3,
- values : [
- minimumCorner.x, minimumCorner.y, minimumCorner.z,
- maximumCorner.x, minimumCorner.y, minimumCorner.z,
- maximumCorner.x, maximumCorner.y, minimumCorner.z,
- minimumCorner.x, maximumCorner.y, minimumCorner.z,
- minimumCorner.x, minimumCorner.y, maximumCorner.z,
- maximumCorner.x, minimumCorner.y, maximumCorner.z,
- maximumCorner.x, maximumCorner.y, maximumCorner.z,
- minimumCorner.x, maximumCorner.y, maximumCorner.z
- ]
- };
-
- // 12 triangles: 6 faces, 2 triangles each.
- mesh.indexLists.push({
- primitiveType : PrimitiveType.TRIANGLES,
- values : [
- 4, 5, 6, // Top: plane z = corner.Z
- 4, 6, 7,
- 1, 0, 3, // Bottom: plane z = -corner.Z
- 1, 3, 2,
- 1, 6, 5, // Side: plane x = corner.X
- 1, 2, 6,
- 2, 3, 7, // Side: plane y = corner.Y
- 2, 7, 6,
- 3, 0, 4, // Side: plane x = -corner.X
- 3, 4, 7,
- 0, 1, 5, // Side: plane y = -corner.Y
- 0, 5, 4
- ]
- });
-
- return mesh;
- }
- };
-
- return BoxTessellator;
-});
\ No newline at end of file
diff --git a/Source/Core/CircleGeometry.js b/Source/Core/CircleGeometry.js
new file mode 100644
index 000000000000..53a139a17d32
--- /dev/null
+++ b/Source/Core/CircleGeometry.js
@@ -0,0 +1,97 @@
+/*global define*/
+define([
+ './clone',
+ './defaultValue',
+ './DeveloperError',
+ './EllipseGeometry'
+ ], function(
+ clone,
+ defaultValue,
+ DeveloperError,
+ EllipseGeometry) {
+ "use strict";
+
+ /**
+ * A {@link Geometry} that represents vertices and indices for a circle on the ellipsoid.
+ *
+ * @alias CircleGeometry
+ * @constructor
+ *
+ * @param {Cartesian3} options.center The circle's center point in the fixed frame.
+ * @param {Number} options.radius The radius in meters.
+ * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid the circle will be on.
+ * @param {Number} [options.height=0.0] The height above the ellipsoid.
+ * @param {Number} [options.granularity=0.02] The angular distance between points on the circle in radians.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ *
+ * @exception {DeveloperError} center is required.
+ * @exception {DeveloperError} radius is required.
+ * @exception {DeveloperError} radius must be greater than zero.
+ * @exception {DeveloperError} granularity must be greater than zero.
+ *
+ * @example
+ * // Create a circle.
+ * var ellipsoid = Ellipsoid.WGS84;
+ * var circle = new CircleGeometry({
+ * ellipsoid : ellipsoid,
+ * center : ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883)),
+ * radius : 100000.0
+ * });
+ */
+ var CircleGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+ var radius = options.radius;
+
+ if (typeof radius === 'undefined') {
+ throw new DeveloperError('radius is required.');
+ }
+
+ if (radius <= 0.0) {
+ throw new DeveloperError('radius must be greater than zero.');
+ }
+
+ var ellipseGeometryOptions = {
+ center : options.center,
+ semiMajorAxis : radius,
+ semiMinorAxis : radius,
+ ellipsoid : options.ellipsoid,
+ height : options.height,
+ granularity : options.granularity,
+ vertexFormat : options.vertexFormat
+ };
+ var ellipseGeometry = new EllipseGeometry(ellipseGeometryOptions);
+
+ /**
+ * An object containing {@link GeometryAttribute} properties named after each of the
+ * true
values of the {@link VertexFormat} option.
+ *
+ * @type GeometryAttributes
+ *
+ * @see Geometry#attributes
+ */
+ this.attributes = ellipseGeometry.attributes;
+
+ /**
+ * Index data that, along with {@link Geometry#primitiveType}, determines the primitives in the geometry.
+ *
+ * @type Array
+ */
+ this.indices = ellipseGeometry.indices;
+
+ /**
+ * The type of primitives in the geometry. For this geometry, it is {@link PrimitiveType.TRIANGLES}.
+ *
+ * @type PrimitiveType
+ */
+ this.primitiveType = ellipseGeometry.primitiveType;
+
+ /**
+ * A tight-fitting bounding sphere that encloses the vertices of the geometry.
+ *
+ * @type BoundingSphere
+ */
+ this.boundingSphere = ellipseGeometry.boundingSphere;
+ };
+
+ return CircleGeometry;
+});
\ No newline at end of file
diff --git a/Source/Core/ColorGeometryInstanceAttribute.js b/Source/Core/ColorGeometryInstanceAttribute.js
new file mode 100644
index 000000000000..76b68dff32e9
--- /dev/null
+++ b/Source/Core/ColorGeometryInstanceAttribute.js
@@ -0,0 +1,145 @@
+/*global define*/
+define([
+ './defaultValue',
+ './Color',
+ './ComponentDatatype',
+ './DeveloperError'
+ ], function(
+ defaultValue,
+ Color,
+ ComponentDatatype,
+ DeveloperError) {
+ "use strict";
+
+ /**
+ * Value and type information for per-instance geometry color.
+ *
+ * @alias ColorGeometryInstanceAttribute
+ * @constructor
+ *
+ * @param {Number} [red=1.0] The red component.
+ * @param {Number} [green=1.0] The green component.
+ * @param {Number} [blue=1.0] The blue component.
+ * @param {Number} [alpha=1.0] The alpha component.
+ *
+ * @example
+ * var instance = new GeometryInstance({
+ * geometry : new BoxGeometry({
+ * dimensions : new Cartesian3(1000000.0, 1000000.0, 500000.0)
+ * }),
+ * modelMatrix : Matrix4.multiplyByTranslation(Transforms.eastNorthUpToFixedFrame(
+ * ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-0.0, 0.0))), new Cartesian3(0.0, 0.0, 1000000.0)),
+ * id : 'box',
+ * attributes : {
+ * color : new ColorGeometryInstanceAttribute(red, green, blue, alpha)
+ * }
+ * });
+ *
+ * @see GeometryInstance
+ * @see GeometryInstanceAttribute
+ */
+ var ColorGeometryInstanceAttribute = function(red, green, blue, alpha) {
+ red = defaultValue(red, 1.0);
+ green = defaultValue(green, 1.0);
+ blue = defaultValue(blue, 1.0);
+ alpha = defaultValue(alpha, 1.0);
+
+ /**
+ * The datatype of each component in the attribute, e.g., individual elements in
+ * {@link ColorGeometryInstanceAttribute#value}.
+ *
+ * @type ComponentDatatype
+ *
+ * @default {@link ComponentDatatype.UNSIGNED_BYTE}
+ *
+ * @readonly
+ */
+ this.componentDatatype = ComponentDatatype.UNSIGNED_BYTE;
+
+ /**
+ * The number of components in the attributes, i.e., {@link ColorGeometryInstanceAttribute#value}.
+ *
+ * @type Number
+ *
+ * @default 4
+ *
+ * @readonly
+ */
+ this.componentsPerAttribute = 4;
+
+ /**
+ * When true
and componentDatatype
is an integer format,
+ * indicate that the components should be mapped to the range [0, 1] (unsigned)
+ * or [-1, 1] (signed) when they are accessed as floating-point for rendering.
+ *
+ * @type Boolean
+ *
+ * @default true
+ *
+ * @readonly
+ */
+ this.normalize = true;
+
+ /**
+ * The values for the attributes stored in a typed array.
+ *
+ * @type Uint8Array
+ *
+ * @default [255, 255, 255, 255]
+ */
+ this.value = new Uint8Array([
+ Color.floatToByte(red),
+ Color.floatToByte(green),
+ Color.floatToByte(blue),
+ Color.floatToByte(alpha)
+ ]);
+ };
+
+ /**
+ * Creates a new {@link ColorGeometryInstanceAttribute} instance given the provided {@link Color}.
+ *
+ * @param {Color} color The color.
+ *
+ * @returns {ColorGeometryInstanceAttribute} The new {@link ColorGeometryInstanceAttribute} instance.
+ *
+ * @exception {DeveloperError} color is required.
+ *
+ * @example
+ * var instance = new GeometryInstance({
+ * geometry : // ...
+ * attributes : {
+ * color : ColorGeometryInstanceAttribute.fromColor(Color.CORNFLOWERBLUE),
+ * }
+ * });
+ */
+ ColorGeometryInstanceAttribute.fromColor = function(color) {
+ if (typeof color === 'undefined') {
+ throw new DeveloperError('color is required.');
+ }
+
+ return new ColorGeometryInstanceAttribute(color.red, color.green, color.blue, color.alpha);
+ };
+
+ /**
+ * Converts a color to a typed array that can be used to assign a color attribute.
+ *
+ * @param {Color} color The color.
+ *
+ * @returns {Uint8Array} The typed array in the attribute's format.
+ *
+ * @exception {DeveloperError} color is required.
+ *
+ * @example
+ * var attributes = primitive.getGeometryInstanceAttributes('an id');
+ * attributes.color = ColorGeometryInstanceAttribute.toValue(Color.AQUA);
+ */
+ ColorGeometryInstanceAttribute.toValue = function(color) {
+ if (typeof color === 'undefined') {
+ throw new DeveloperError('color is required.');
+ }
+
+ return new Uint8Array(color.toBytes());
+ };
+
+ return ColorGeometryInstanceAttribute;
+});
diff --git a/Source/Core/ComponentDatatype.js b/Source/Core/ComponentDatatype.js
index 772249d76cfa..841b56431f12 100644
--- a/Source/Core/ComponentDatatype.js
+++ b/Source/Core/ComponentDatatype.js
@@ -12,7 +12,8 @@ define(['./Enumeration'], function(Enumeration) {
}
/**
- * DOC_TBA
+ * Enumerations for WebGL component datatypes. Components are intrinsics,
+ * which form attributes, which form vertices.
*
* @alias ComponentDatatype
* @enumeration
@@ -20,17 +21,19 @@ define(['./Enumeration'], function(Enumeration) {
var ComponentDatatype = {};
/**
- * DOC_TBA
+ * 8-bit signed byte enumeration corresponding to gl.BYTE
and the type
+ * of an element in Int8Array
.
+ *
+ * @memberOf ComponentDatatype
*
* @type {Enumeration}
* @constant
* @default 0x1400
- * @memberOf ComponentDatatype
*/
ComponentDatatype.BYTE = new Enumeration(0x1400, 'BYTE');
ComponentDatatype.BYTE.sizeInBytes = Int8Array.BYTES_PER_ELEMENT;
- ComponentDatatype.BYTE.toTypedArray = function(values) {
- return new Int8Array(values);
+ ComponentDatatype.BYTE.createTypedArray = function(valuesOrLength) {
+ return new Int8Array(valuesOrLength);
};
ComponentDatatype.BYTE.createArrayBufferView = function(buffer, byteOffset) {
@@ -38,17 +41,19 @@ define(['./Enumeration'], function(Enumeration) {
};
/**
- * DOC_TBA
+ * 8-bit unsigned byte enumeration corresponding to UNSIGNED_BYTE
and the type
+ * of an element in Uint8Array
.
+ *
+ * @memberOf ComponentDatatype
*
* @type {Enumeration}
* @constant
* @default 0x1401
- * @memberOf ComponentDatatype
*/
ComponentDatatype.UNSIGNED_BYTE = new Enumeration(0x1401, 'UNSIGNED_BYTE');
ComponentDatatype.UNSIGNED_BYTE.sizeInBytes = Uint8Array.BYTES_PER_ELEMENT;
- ComponentDatatype.UNSIGNED_BYTE.toTypedArray = function(values) {
- return new Uint8Array(values);
+ ComponentDatatype.UNSIGNED_BYTE.createTypedArray = function(valuesOrLength) {
+ return new Uint8Array(valuesOrLength);
};
ComponentDatatype.UNSIGNED_BYTE.createArrayBufferView = function(buffer, byteOffset) {
@@ -56,17 +61,19 @@ define(['./Enumeration'], function(Enumeration) {
};
/**
- * DOC_TBA
+ * 16-bit signed short enumeration corresponding to SHORT
and the type
+ * of an element in Int16Array
.
+ *
+ * @memberOf ComponentDatatype
*
* @type {Enumeration}
* @constant
* @default 0x1402
- * @memberOf ComponentDatatype
*/
ComponentDatatype.SHORT = new Enumeration(0x1402, 'SHORT');
ComponentDatatype.SHORT.sizeInBytes = Int16Array.BYTES_PER_ELEMENT;
- ComponentDatatype.SHORT.toTypedArray = function(values) {
- return new Int16Array(values);
+ ComponentDatatype.SHORT.createTypedArray = function(valuesOrLength) {
+ return new Int16Array(valuesOrLength);
};
ComponentDatatype.SHORT.createArrayBufferView = function(buffer, byteOffset) {
@@ -74,17 +81,19 @@ define(['./Enumeration'], function(Enumeration) {
};
/**
- * DOC_TBA
+ * 16-bit unsigned short enumeration corresponding to UNSIGNED_SHORT
and the type
+ * of an element in Uint16Array
.
+ *
+ * @memberOf ComponentDatatype
*
* @type {Enumeration}
* @constant
* @default 0x1403
- * @memberOf ComponentDatatype
*/
ComponentDatatype.UNSIGNED_SHORT = new Enumeration(0x1403, 'UNSIGNED_SHORT');
ComponentDatatype.UNSIGNED_SHORT.sizeInBytes = Uint16Array.BYTES_PER_ELEMENT;
- ComponentDatatype.UNSIGNED_SHORT.toTypedArray = function(values) {
- return new Uint16Array(values);
+ ComponentDatatype.UNSIGNED_SHORT.createTypedArray = function(valuesOrLength) {
+ return new Uint16Array(valuesOrLength);
};
ComponentDatatype.UNSIGNED_SHORT.createArrayBufferView = function(buffer, byteOffset) {
@@ -92,17 +101,19 @@ define(['./Enumeration'], function(Enumeration) {
};
/**
- * DOC_TBA
+ * 32-bit floating-point enumeration corresponding to FLOAT
and the type
+ * of an element in Float32Array
.
+ *
+ * @memberOf ComponentDatatype
*
* @type {Enumeration}
* @constant
* @default 0x1406
- * @memberOf ComponentDatatype
*/
ComponentDatatype.FLOAT = new Enumeration(0x1406, 'FLOAT');
ComponentDatatype.FLOAT.sizeInBytes = Float32Array.BYTES_PER_ELEMENT;
- ComponentDatatype.FLOAT.toTypedArray = function(values) {
- return new Float32Array(values);
+ ComponentDatatype.FLOAT.createTypedArray = function(valuesOrLength) {
+ return new Float32Array(valuesOrLength);
};
ComponentDatatype.FLOAT.createArrayBufferView = function(buffer, byteOffset) {
@@ -110,18 +121,45 @@ define(['./Enumeration'], function(Enumeration) {
};
/**
- * DOC_TBA
+ * 64-bit floating-point enumeration corresponding to gl.DOUBLE
(in Desktop OpenGL;
+ * this is not supported in WebGL, and is emulated in Cesium via {@link GeometryPipeline.encodeAttribute})
+ * and the type of an element in Float64Array
.
+ *
+ * @memberOf ComponentDatatype
+ *
+ * @type {Enumeration}
+ * @constant
+ * @default 0x140A
+ */
+ ComponentDatatype.DOUBLE = new Enumeration(0x140A, 'DOUBLE');
+ ComponentDatatype.DOUBLE.sizeInBytes = Float64Array.BYTES_PER_ELEMENT;
+ ComponentDatatype.DOUBLE.createTypedArray = function(valuesOrLength) {
+ return new Float64Array(valuesOrLength);
+ };
+
+ ComponentDatatype.DOUBLE.createArrayBufferView = function(buffer, byteOffset) {
+ return new Float64Array(buffer, byteOffset);
+ };
+
+ /**
+ * Validates that the provided component datatype is a valid {@link ComponentDatatype}
+ *
+ * @param {ComponentDatatype} componentDatatype The component datatype to validate.
*
- * @param {ComponentDataType} componentDatatype
+ * @return {Boolean} true
if the provided component datatype is a valid enumeration value; otherwise, false
.
*
- * @returns {Boolean}
+ * @example
+ * if (!ComponentDatatype.validate(componentDatatype)) {
+ * throw new DeveloperError('componentDatatype must be a valid enumeration value.');
+ * }
*/
ComponentDatatype.validate = function(componentDatatype) {
return ((componentDatatype === ComponentDatatype.BYTE) ||
(componentDatatype === ComponentDatatype.UNSIGNED_BYTE) ||
(componentDatatype === ComponentDatatype.SHORT) ||
(componentDatatype === ComponentDatatype.UNSIGNED_SHORT) ||
- (componentDatatype === ComponentDatatype.FLOAT));
+ (componentDatatype === ComponentDatatype.FLOAT) ||
+ (componentDatatype === ComponentDatatype.DOUBLE));
};
return ComponentDatatype;
diff --git a/Source/Core/CubeMapEllipsoidTessellator.js b/Source/Core/CubeMapEllipsoidTessellator.js
deleted file mode 100644
index 56d5d7a5d3a4..000000000000
--- a/Source/Core/CubeMapEllipsoidTessellator.js
+++ /dev/null
@@ -1,200 +0,0 @@
-/*global define*/
-define([
- './defaultValue',
- './DeveloperError',
- './Cartesian3',
- './ComponentDatatype',
- './PrimitiveType'
- ], function(
- defaultValue,
- DeveloperError,
- Cartesian3,
- ComponentDatatype,
- PrimitiveType) {
- "use strict";
-
- /**
- * DOC_TBA
- *
- * @exports CubeMapEllipsoidTessellator
- *
- * @see BoxTessellator
- */
- var CubeMapEllipsoidTessellator = {};
-
- /**
- * DOC_TBA
- *
- * @param {Ellipsoid} ellipsoid DOC_TBA.
- * @param {Number} numberOfPartitions DOC_TBA.
- * @param {String} attributeName DOC_TBA.
- *
- * @exception {DeveloperError} numberOfPartitions must be greater than zero.
- */
- CubeMapEllipsoidTessellator.compute = function(ellipsoid, numberOfPartitions, attributeName) {
- if (numberOfPartitions <= 0) {
- throw new DeveloperError('numberOfPartitions must be greater than zero.');
- }
-
- attributeName = defaultValue(attributeName, 'position');
-
- var positions = [];
- var indices = [];
-
- function addEdgePositions(i0, i1) {
- var indices = [];
- indices[0] = i0;
- indices[2 + (numberOfPartitions - 1) - 1] = i1;
-
- var origin = positions[i0];
- var direction = positions[i1].subtract(positions[i0]);
-
- for ( var i = 1; i < numberOfPartitions; ++i) {
- var delta = i / numberOfPartitions;
-
- indices[i] = positions.length;
- positions.push(origin.add(direction.multiplyByScalar(delta)));
- }
-
- return indices;
- }
-
- function addFaceTriangles(leftBottomToTop, bottomLeftToRight, rightBottomToTop, topLeftToRight) {
- var origin = positions[bottomLeftToRight[0]];
- var x = positions[bottomLeftToRight[bottomLeftToRight.length - 1]].subtract(origin);
- var y = positions[topLeftToRight[0]].subtract(origin);
-
- var bottomIndicesBuffer = [];
- var topIndicesBuffer = [];
-
- var bottomIndices = bottomLeftToRight;
- var topIndices = topIndicesBuffer;
-
- for ( var j = 1; j <= numberOfPartitions; ++j) {
- if (j !== numberOfPartitions) {
- if (j !== 1) {
- //
- // This copy could be avoided by ping ponging buffers.
- //
- bottomIndicesBuffer = topIndicesBuffer.slice(0);
- bottomIndices = bottomIndicesBuffer;
- }
-
- topIndicesBuffer[0] = leftBottomToTop[j];
- topIndicesBuffer[numberOfPartitions] = rightBottomToTop[j];
-
- var deltaY = j / numberOfPartitions;
- var offsetY = y.multiplyByScalar(deltaY);
-
- for ( var i = 1; i < numberOfPartitions; ++i) {
- var deltaX = i / numberOfPartitions;
- var offsetX = x.multiplyByScalar(deltaX);
-
- topIndicesBuffer[i] = positions.length;
- positions.push(origin.add(offsetX).add(offsetY));
- }
- } else {
- if (j !== 1) {
- bottomIndices = topIndicesBuffer;
- }
- topIndices = topLeftToRight;
- }
-
- for ( var k = 0; k < numberOfPartitions; ++k) {
- indices.push(bottomIndices[k]);
- indices.push(bottomIndices[k + 1]);
- indices.push(topIndices[k + 1]);
-
- indices.push(bottomIndices[k]);
- indices.push(topIndices[k + 1]);
- indices.push(topIndices[k]);
- }
- }
- }
-
- //
- // Initial cube. In the plane, z = -1:
- //
- // +y
- // |
- // Q2 * p3 Q1
- // / | \
- // p0 *--+--* p2 +x
- // \ | /
- // Q3 * p1 Q4
- // |
- //
- // Similarly, p4 to p7 are in the plane z = 1.
- //
- positions.push(new Cartesian3(-1, 0, -1));
- positions.push(new Cartesian3(0, -1, -1));
- positions.push(new Cartesian3(1, 0, -1));
- positions.push(new Cartesian3(0, 1, -1));
- positions.push(new Cartesian3(-1, 0, 1));
- positions.push(new Cartesian3(0, -1, 1));
- positions.push(new Cartesian3(1, 0, 1));
- positions.push(new Cartesian3(0, 1, 1));
-
- //
- // Edges
- //
- // 0 -> 1, 1 -> 2, 2 -> 3, 3 -> 0. Plane z = -1
- // 4 -> 5, 5 -> 6, 6 -> 7, 7 -> 4. Plane z = 1
- // 0 -> 4, 1 -> 5, 2 -> 6, 3 -> 7. From plane z = -1 to plane z - 1
- //
- var edge0to1 = addEdgePositions(0, 1);
- var edge1to2 = addEdgePositions(1, 2);
- var edge2to3 = addEdgePositions(2, 3);
- var edge3to0 = addEdgePositions(3, 0);
-
- var edge4to5 = addEdgePositions(4, 5);
- var edge5to6 = addEdgePositions(5, 6);
- var edge6to7 = addEdgePositions(6, 7);
- var edge7to4 = addEdgePositions(7, 4);
-
- var edge0to4 = addEdgePositions(0, 4);
- var edge1to5 = addEdgePositions(1, 5);
- var edge2to6 = addEdgePositions(2, 6);
- var edge3to7 = addEdgePositions(3, 7);
-
- addFaceTriangles(edge0to4, edge0to1, edge1to5, edge4to5); // Q3 Face
- addFaceTriangles(edge1to5, edge1to2, edge2to6, edge5to6); // Q4 Face
- addFaceTriangles(edge2to6, edge2to3, edge3to7, edge6to7); // Q1 Face
- addFaceTriangles(edge3to7, edge3to0, edge0to4, edge7to4); // Q2 Face
- addFaceTriangles(edge7to4.slice(0).reverse(), edge4to5, edge5to6, edge6to7.slice(0).reverse()); // Plane z = 1
- addFaceTriangles(edge1to2, edge0to1.slice(0).reverse(), edge3to0.slice(0).reverse(), edge2to3); // Plane z = -1
-
- // Expand cube into ellipsoid and flatten values
- var radii = ellipsoid.getRadii();
- var length = positions.length;
- var q = 0;
- var flattenedPositions = new Array(length * 3);
- for ( var i = 0; i < length; ++i) {
- var item = positions[i];
- Cartesian3.normalize(item, item);
- Cartesian3.multiplyComponents(item, radii, item);
- flattenedPositions[q++] = item.x;
- flattenedPositions[q++] = item.y;
- flattenedPositions[q++] = item.z;
- }
-
- var mesh = {};
- mesh.attributes = {};
- mesh.indexLists = [];
-
- mesh.attributes[attributeName] = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 3,
- values : flattenedPositions
- };
-
- mesh.indexLists.push({
- primitiveType : PrimitiveType.TRIANGLES,
- values : indices
- });
-
- return mesh;
- };
-
- return CubeMapEllipsoidTessellator;
-});
\ No newline at end of file
diff --git a/Source/Core/EllipseGeometry.js b/Source/Core/EllipseGeometry.js
new file mode 100644
index 000000000000..153ae6b9df45
--- /dev/null
+++ b/Source/Core/EllipseGeometry.js
@@ -0,0 +1,478 @@
+/*global define*/
+define([
+ './defaultValue',
+ './BoundingSphere',
+ './Cartesian3',
+ './Cartographic',
+ './ComponentDatatype',
+ './IndexDatatype',
+ './DeveloperError',
+ './Ellipsoid',
+ './GeographicProjection',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './Math',
+ './Matrix3',
+ './PrimitiveType',
+ './Quaternion',
+ './VertexFormat'
+ ], function(
+ defaultValue,
+ BoundingSphere,
+ Cartesian3,
+ Cartographic,
+ ComponentDatatype,
+ IndexDatatype,
+ DeveloperError,
+ Ellipsoid,
+ GeographicProjection,
+ GeometryAttribute,
+ GeometryAttributes,
+ CesiumMath,
+ Matrix3,
+ PrimitiveType,
+ Quaternion,
+ VertexFormat) {
+ "use strict";
+
+ var rotAxis = new Cartesian3();
+ var tempVec = new Cartesian3();
+ var unitQuat = new Quaternion();
+ var rotMtx = new Matrix3();
+
+ function pointOnEllipsoid(theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, result) {
+ var azimuth = theta + rotation;
+
+ Cartesian3.multiplyByScalar(eastVec, Math.cos(azimuth), rotAxis);
+ Cartesian3.multiplyByScalar(northVec, Math.sin(azimuth), tempVec);
+ Cartesian3.add(rotAxis, tempVec, rotAxis);
+
+ var cosThetaSquared = Math.cos(theta);
+ cosThetaSquared = cosThetaSquared * cosThetaSquared;
+
+ var sinThetaSquared = Math.sin(theta);
+ sinThetaSquared = sinThetaSquared * sinThetaSquared;
+
+ var radius = ab / Math.sqrt(bSqr * cosThetaSquared + aSqr * sinThetaSquared);
+ var angle = radius / mag;
+
+ // Create the quaternion to rotate the position vector to the boundary of the ellipse.
+ Quaternion.fromAxisAngle(rotAxis, angle, unitQuat);
+ Matrix3.fromQuaternion(unitQuat, rotMtx);
+
+ Matrix3.multiplyByVector(rotMtx, unitPos, result);
+ Cartesian3.normalize(result, result);
+ Cartesian3.multiplyByScalar(result, mag, result);
+ return result;
+ }
+
+ var scratchCartesian1 = new Cartesian3();
+ var scratchCartesian2 = new Cartesian3();
+ var scratchCartesian3 = new Cartesian3();
+ var scratchCartesian4 = new Cartesian3();
+ var unitPosScratch = new Cartesian3();
+ var eastVecScratch = new Cartesian3();
+ var northVecScratch = new Cartesian3();
+ var scratchCartographic = new Cartographic();
+ var projectedCenterScratch = new Cartesian3();
+
+ /**
+ * A {@link Geometry} that represents vertices and indices for an ellipse on the ellipsoid.
+ *
+ * @alias EllipseGeometry
+ * @constructor
+ *
+ * @param {Cartesian3} options.center The ellipse's center point in the fixed frame.
+ * @param {Number} options.semiMajorAxis The length of the ellipse's semi-major axis in meters.
+ * @param {Number} options.semiMinorAxis The length of the ellipse's semi-minor axis in meters.
+ * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid the ellipse will be on.
+ * @param {Number} [options.height=0.0] The height above the ellipsoid.
+ * @param {Number} [options.rotation=0.0] The angle from north (clockwise) in radians. The default is zero.
+ * @param {Number} [options.granularity=0.02] The angular distance between points on the ellipse in radians.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ *
+ * @exception {DeveloperError} center is required.
+ * @exception {DeveloperError} semiMajorAxis is required.
+ * @exception {DeveloperError} semiMinorAxis is required.
+ * @exception {DeveloperError} semiMajorAxis and semiMinorAxis must be greater than zero.
+ * @exception {DeveloperError} semiMajorAxis must be larger than the semiMajorAxis.
+ * @exception {DeveloperError} granularity must be greater than zero.
+ *
+ * @example
+ * // Create an ellipse.
+ * var ellipsoid = Ellipsoid.WGS84;
+ * var ellipse = new EllipseGeometry({
+ * ellipsoid : ellipsoid,
+ * center : ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883)),
+ * semiMajorAxis : 500000.0,
+ * semiMinorAxis : 300000.0,
+ * rotation : CesiumMath.toRadians(60.0)
+ * });
+ */
+ var EllipseGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+ var center = options.center;
+ var semiMajorAxis = options.semiMajorAxis;
+ var semiMinorAxis = options.semiMinorAxis;
+
+ var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
+ var rotation = defaultValue(options.rotation, 0.0);
+ var height = defaultValue(options.height, 0.0);
+ var granularity = defaultValue(options.granularity, 0.02);
+
+ if (typeof center === 'undefined') {
+ throw new DeveloperError('center is required.');
+ }
+
+ if (typeof semiMajorAxis === 'undefined') {
+ throw new DeveloperError('semiMajorAxis is required.');
+ }
+
+ if (typeof semiMinorAxis === 'undefined') {
+ throw new DeveloperError('semiMinorAxis is required.');
+ }
+
+ if (semiMajorAxis <= 0.0 || semiMinorAxis <= 0.0) {
+ throw new DeveloperError('Semi-major and semi-minor axes must be greater than zero.');
+ }
+
+ if (granularity <= 0.0) {
+ throw new DeveloperError('granularity must be greater than zero.');
+ }
+
+ if (semiMajorAxis < semiMinorAxis) {
+ throw new DeveloperError('semiMajorAxis must be larger than the semiMajorAxis.');
+ }
+
+ var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
+
+ var MAX_ANOMALY_LIMIT = 2.31;
+
+ var aSqr = semiMinorAxis * semiMinorAxis;
+ var bSqr = semiMajorAxis * semiMajorAxis;
+ var ab = semiMajorAxis * semiMinorAxis;
+
+ var mag = center.magnitude();
+
+ var unitPos = Cartesian3.normalize(center, unitPosScratch);
+ var eastVec = Cartesian3.cross(Cartesian3.UNIT_Z, center, eastVecScratch);
+ Cartesian3.normalize(eastVec, eastVec);
+ var northVec = Cartesian3.cross(unitPos, eastVec, northVecScratch);
+
+ // The number of points in the first quadrant
+ var numPts = 1 + Math.ceil(CesiumMath.PI_OVER_TWO / granularity);
+ var deltaTheta = MAX_ANOMALY_LIMIT / (numPts - 1);
+
+ // If the number of points were three, the ellipse
+ // would be tessellated like below:
+ //
+ // *---*
+ // / | \ | \
+ // *---*---*---*
+ // / | \ | \ | \ | \
+ // *---*---*---*---*---*
+ // | \ | \ | \ | \ | \ |
+ // *---*---*---*---*---*
+ // \ | \ | \ | \ | /
+ // *---*---*---*
+ // \ | \ | /
+ // *---*
+ // Notice each vertical column contains an even number of positions.
+ // The sum of the first n even numbers is n * (n + 1). Double it for the number of points
+ // for the whole ellipse. Note: this is just an estimate and may actually be less depending
+ // on the number of iterations before the angle reaches pi/2.
+ var size = 2 * numPts * (numPts + 1);
+ var positions = new Array(size * 3);
+ var positionIndex = 0;
+ var position = scratchCartesian1;
+ var reflectedPosition = scratchCartesian2;
+
+ var i;
+ var j;
+ var numInterior;
+ var t;
+ var interiorPosition;
+
+ // Compute points in the 'northern' half of the ellipse
+ var theta = CesiumMath.PI_OVER_TWO;
+ for (i = 0; i < numPts && theta > 0; ++i) {
+ pointOnEllipsoid(theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position);
+ pointOnEllipsoid(Math.PI - theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, reflectedPosition);
+
+ positions[positionIndex++] = position.x;
+ positions[positionIndex++] = position.y;
+ positions[positionIndex++] = position.z;
+
+ numInterior = 2 * i + 2;
+ for (j = 1; j < numInterior - 1; ++j) {
+ t = j / (numInterior - 1);
+ interiorPosition = Cartesian3.lerp(position, reflectedPosition, t, scratchCartesian3);
+ positions[positionIndex++] = interiorPosition.x;
+ positions[positionIndex++] = interiorPosition.y;
+ positions[positionIndex++] = interiorPosition.z;
+ }
+
+ positions[positionIndex++] = reflectedPosition.x;
+ positions[positionIndex++] = reflectedPosition.y;
+ positions[positionIndex++] = reflectedPosition.z;
+
+ theta = CesiumMath.PI_OVER_TWO - (i + 1) * deltaTheta;
+ }
+
+ // Set numPts if theta reached zero
+ numPts = i;
+
+ // Compute points in the 'northern' half of the ellipse
+ for (i = numPts; i > 0; --i) {
+ theta = CesiumMath.PI_OVER_TWO - (i - 1) * deltaTheta;
+
+ pointOnEllipsoid(-theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position);
+ pointOnEllipsoid( theta + Math.PI, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, reflectedPosition);
+
+ positions[positionIndex++] = position.x;
+ positions[positionIndex++] = position.y;
+ positions[positionIndex++] = position.z;
+
+ numInterior = 2 * (i - 1) + 2;
+ for (j = 1; j < numInterior - 1; ++j) {
+ t = j / (numInterior - 1);
+ interiorPosition = Cartesian3.lerp(position, reflectedPosition, t, scratchCartesian3);
+ positions[positionIndex++] = interiorPosition.x;
+ positions[positionIndex++] = interiorPosition.y;
+ positions[positionIndex++] = interiorPosition.z;
+ }
+
+ positions[positionIndex++] = reflectedPosition.x;
+ positions[positionIndex++] = reflectedPosition.y;
+ positions[positionIndex++] = reflectedPosition.z;
+ }
+
+ // The original length may have been an over-estimate
+ if (positions.length !== positionIndex) {
+ size = positionIndex / 3;
+ positions.length = positionIndex;
+ }
+
+ positions = new Float64Array(positions);
+
+ var textureCoordinates = (vertexFormat.st) ? new Float32Array(size * 2) : undefined;
+ var normals = (vertexFormat.normal) ? new Float32Array(size * 3) : undefined;
+ var tangents = (vertexFormat.tangent) ? new Float32Array(size * 3) : undefined;
+ var binormals = (vertexFormat.binormal) ? new Float32Array(size * 3) : undefined;
+
+ var textureCoordIndex = 0;
+
+ // Raise positions to a height above the ellipsoid and compute the
+ // texture coordinates, normals, tangents, and binormals.
+ var normal;
+ var tangent;
+ var binormal;
+
+ var projection = new GeographicProjection(ellipsoid);
+ var projectedCenter = projection.project(ellipsoid.cartesianToCartographic(center, scratchCartographic), projectedCenterScratch);
+
+ var length = positions.length;
+ for (i = 0; i < length; i += 3) {
+ position = Cartesian3.fromArray(positions, i, scratchCartesian2);
+
+ if (vertexFormat.st) {
+ var projectedPoint = projection.project(ellipsoid.cartesianToCartographic(position, scratchCartographic), scratchCartesian3);
+ var relativeToCenter = Cartesian3.subtract(projectedPoint, projectedCenter, projectedPoint);
+ textureCoordinates[textureCoordIndex++] = (relativeToCenter.x + semiMajorAxis) / (2.0 * semiMajorAxis);
+ textureCoordinates[textureCoordIndex++] = (relativeToCenter.y + semiMinorAxis) / (2.0 * semiMinorAxis);
+ }
+
+ ellipsoid.scaleToGeodeticSurface(position, position);
+ Cartesian3.add(position, Cartesian3.multiplyByScalar(ellipsoid.geodeticSurfaceNormal(position), height), position);
+
+ if (vertexFormat.position) {
+ positions[i] = position.x;
+ positions[i + 1] = position.y;
+ positions[i + 2] = position.z;
+ }
+
+ if (vertexFormat.normal) {
+ normal = ellipsoid.geodeticSurfaceNormal(position, scratchCartesian3);
+
+ normals[i] = normal.x;
+ normals[i + 1] = normal.y;
+ normals[i + 2] = normal.z;
+ }
+
+ if (vertexFormat.tangent) {
+ normal = ellipsoid.geodeticSurfaceNormal(position, scratchCartesian3);
+ tangent = Cartesian3.cross(Cartesian3.UNIT_Z, normal, scratchCartesian3);
+
+ tangents[i] = tangent.x;
+ tangents[i + 1] = tangent.y;
+ tangents[i + 2] = tangent.z;
+ }
+
+ if (vertexFormat.binormal) {
+ normal = ellipsoid.geodeticSurfaceNormal(position, scratchCartesian3);
+ tangent = Cartesian3.cross(Cartesian3.UNIT_Z, normal, scratchCartesian4);
+ binormal = Cartesian3.cross(normal, tangent, scratchCartesian3);
+
+ binormals[i] = binormal.x;
+ binormals[i + 1] = binormal.y;
+ binormals[i + 2] = binormal.z;
+ }
+ }
+
+ var attributes = new GeometryAttributes();
+
+ if (vertexFormat.position) {
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : positions
+ });
+ }
+
+ if (vertexFormat.st) {
+ attributes.st = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : textureCoordinates
+ });
+ }
+
+ if (vertexFormat.normal) {
+ attributes.normal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : normals
+ });
+ }
+
+ if (vertexFormat.tangent) {
+ attributes.tangent = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : tangents
+ });
+ }
+
+ if (vertexFormat.binormal) {
+ attributes.binormal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : binormals
+ });
+ }
+
+ // The number of triangles in the ellipse on the positive x half-space and for
+ // the column of triangles in the middle is:
+ //
+ // numTriangles = 4 + 8 + 12 + ... = 4 + (4 + 4) + (4 + 4 + 4) + ... = 4 * (1 + 2 + 3 + ...)
+ // = 4 * ((n * ( n + 1)) / 2)
+ // numColumnTriangles = 2 * 2 * n
+ // total = 2 * numTrangles + numcolumnTriangles
+ //
+ // Substitute (numPts - 1.0) for n above
+ var indicesSize = 2 * numPts * (numPts + 1);
+ var indices = new Array(indicesSize);
+ var indicesIndex = 0;
+ var prevIndex;
+
+ // Indices triangles to the 'left' of the north vector
+ for (i = 1; i < numPts; ++i) {
+ positionIndex = i * (i + 1);
+ prevIndex = (i - 1) * i;
+
+ indices[indicesIndex++] = positionIndex++;
+ indices[indicesIndex++] = prevIndex;
+ indices[indicesIndex++] = positionIndex;
+
+ numInterior = 2 * i;
+ for (j = 0; j < numInterior - 1; ++j) {
+
+ indices[indicesIndex++] = positionIndex;
+ indices[indicesIndex++] = prevIndex++;
+ indices[indicesIndex++] = prevIndex;
+
+ indices[indicesIndex++] = positionIndex++;
+ indices[indicesIndex++] = prevIndex;
+ indices[indicesIndex++] = positionIndex;
+ }
+
+ indices[indicesIndex++] = positionIndex++;
+ indices[indicesIndex++] = prevIndex;
+ indices[indicesIndex++] = positionIndex;
+ }
+
+ // Indices for central column of triangles
+ numInterior = numPts * 2;
+ ++positionIndex;
+ ++prevIndex;
+ for (i = 0; i < numInterior - 1; ++i) {
+ indices[indicesIndex++] = positionIndex;
+ indices[indicesIndex++] = prevIndex++;
+ indices[indicesIndex++] = prevIndex;
+
+ indices[indicesIndex++] = positionIndex++;
+ indices[indicesIndex++] = prevIndex;
+ indices[indicesIndex++] = positionIndex;
+ }
+
+ // Reverse the process creating indices to the 'right' of the north vector
+ ++prevIndex;
+ ++positionIndex;
+ for (i = numPts - 1; i > 0; --i) {
+ indices[indicesIndex++] = prevIndex++;
+ indices[indicesIndex++] = prevIndex;
+ indices[indicesIndex++] = positionIndex;
+
+ numInterior = 2 * i;
+ for (j = 0; j < numInterior - 1; ++j) {
+ indices[indicesIndex++] = positionIndex;
+ indices[indicesIndex++] = prevIndex++;
+ indices[indicesIndex++] = prevIndex;
+
+ indices[indicesIndex++] = positionIndex++;
+ indices[indicesIndex++] = prevIndex;
+ indices[indicesIndex++] = positionIndex;
+ }
+
+ indices[indicesIndex++] = prevIndex++;
+ indices[indicesIndex++] = prevIndex++;
+ indices[indicesIndex++] = positionIndex++;
+ }
+
+ var boundingSphereCenter = Cartesian3.multiplyByScalar(ellipsoid.geodeticSurfaceNormal(center), height);
+ Cartesian3.add(center, boundingSphereCenter, boundingSphereCenter);
+
+ /**
+ * An object containing {@link GeometryAttribute} properties named after each of the
+ * true
values of the {@link VertexFormat} option.
+ *
+ * @type GeometryAttributes
+ *
+ * @see Geometry#attributes
+ */
+ this.attributes = attributes;
+
+ /**
+ * Index data that, along with {@link Geometry#primitiveType}, determines the primitives in the geometry.
+ *
+ * @type Array
+ */
+ this.indices = IndexDatatype.createTypedArray(positions.length / 3, indices);
+
+ /**
+ * The type of primitives in the geometry. For this geometry, it is {@link PrimitiveType.TRIANGLES}.
+ *
+ * @type PrimitiveType
+ */
+ this.primitiveType = PrimitiveType.TRIANGLES;
+
+ /**
+ * A tight-fitting bounding sphere that encloses the vertices of the geometry.
+ *
+ * @type BoundingSphere
+ */
+ this.boundingSphere = new BoundingSphere(boundingSphereCenter, semiMajorAxis);
+ };
+
+ return EllipseGeometry;
+});
\ No newline at end of file
diff --git a/Source/Core/EllipsoidGeometry.js b/Source/Core/EllipsoidGeometry.js
new file mode 100644
index 000000000000..861cd3ad1c70
--- /dev/null
+++ b/Source/Core/EllipsoidGeometry.js
@@ -0,0 +1,344 @@
+/*global define*/
+define([
+ './defaultValue',
+ './DeveloperError',
+ './Cartesian3',
+ './Math',
+ './Ellipsoid',
+ './ComponentDatatype',
+ './IndexDatatype',
+ './PrimitiveType',
+ './BoundingSphere',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './VertexFormat'
+ ], function(
+ defaultValue,
+ DeveloperError,
+ Cartesian3,
+ CesiumMath,
+ Ellipsoid,
+ ComponentDatatype,
+ IndexDatatype,
+ PrimitiveType,
+ BoundingSphere,
+ GeometryAttribute,
+ GeometryAttributes,
+ VertexFormat) {
+ "use strict";
+
+ var scratchDirection = new Cartesian3();
+
+ function addEdgePositions(i0, i1, numberOfPartitions, positions) {
+ var indices = new Array(2 + numberOfPartitions - 1);
+ indices[0] = i0;
+
+ var origin = positions[i0];
+ var direction = Cartesian3.subtract(positions[i1], positions[i0], scratchDirection);
+
+ for ( var i = 1; i < numberOfPartitions; ++i) {
+ var delta = i / numberOfPartitions;
+ var position = Cartesian3.multiplyByScalar(direction, delta);
+ Cartesian3.add(origin, position, position);
+
+ indices[i] = positions.length;
+ positions.push(position);
+ }
+
+ indices[2 + (numberOfPartitions - 1) - 1] = i1;
+
+ return indices;
+ }
+
+ var scratchX = new Cartesian3();
+ var scratchY = new Cartesian3();
+ var scratchOffsetX = new Cartesian3();
+ var scratchOffsetY = new Cartesian3();
+
+ function addFaceTriangles(leftBottomToTop, bottomLeftToRight, rightBottomToTop, topLeftToRight, numberOfPartitions, positions, indices) {
+ var origin = positions[bottomLeftToRight[0]];
+ var x = Cartesian3.subtract(positions[bottomLeftToRight[bottomLeftToRight.length - 1]], origin, scratchX);
+ var y = Cartesian3.subtract(positions[topLeftToRight[0]], origin, scratchY);
+
+ var bottomIndicesBuffer = [];
+ var topIndicesBuffer = [];
+
+ var bottomIndices = bottomLeftToRight;
+ var topIndices = topIndicesBuffer;
+
+ for ( var j = 1; j <= numberOfPartitions; ++j) {
+ if (j !== numberOfPartitions) {
+ if (j !== 1) {
+ //
+ // This copy could be avoided by ping ponging buffers.
+ //
+ bottomIndicesBuffer = topIndicesBuffer.slice(0);
+ bottomIndices = bottomIndicesBuffer;
+ }
+
+ topIndicesBuffer[0] = leftBottomToTop[j];
+ topIndicesBuffer[numberOfPartitions] = rightBottomToTop[j];
+
+ var deltaY = j / numberOfPartitions;
+ var offsetY = Cartesian3.multiplyByScalar(y, deltaY, scratchOffsetY);
+
+ for ( var i = 1; i < numberOfPartitions; ++i) {
+ var deltaX = i / numberOfPartitions;
+ var offsetX = Cartesian3.multiplyByScalar(x, deltaX, scratchOffsetX);
+ var position = Cartesian3.add(origin, offsetX);
+ Cartesian3.add(position, offsetY, position);
+
+ topIndicesBuffer[i] = positions.length;
+ positions.push(position);
+ }
+ } else {
+ if (j !== 1) {
+ bottomIndices = topIndicesBuffer;
+ }
+ topIndices = topLeftToRight;
+ }
+
+ for ( var k = 0; k < numberOfPartitions; ++k) {
+ indices.push(bottomIndices[k]);
+ indices.push(bottomIndices[k + 1]);
+ indices.push(topIndices[k + 1]);
+
+ indices.push(bottomIndices[k]);
+ indices.push(topIndices[k + 1]);
+ indices.push(topIndices[k]);
+ }
+ }
+ }
+
+ var sphericalNormal = new Cartesian3();
+ var normal = new Cartesian3();
+ var tangent = new Cartesian3();
+ var binormal = new Cartesian3();
+ var defaultRadii = new Cartesian3(1.0, 1.0, 1.0);
+
+ /**
+ * A {@link Geometry} that represents vertices and indices for an ellipsoid centered at the origin.
+ *
+ * @alias EllipsoidGeometry
+ * @constructor
+ *
+ * @param {Cartesian3} [options.radii=Cartesian3(1.0, 1.0, 1.0)] The radii of the ellipsoid in the x, y, and z directions.
+ * @param {Number} [options.numberOfPartitions=32] The number of times to partition the ellipsoid in a plane formed by two radii in a single quadrant.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ *
+ * @exception {DeveloperError} options.numberOfPartitions must be greater than zero.
+ *
+ * @example
+ * var ellipsoid = new EllipsoidGeometry({
+ * vertexFormat : VertexFormat.POSITION_ONLY,
+ * radii : new Cartesian3(1000000.0, 500000.0, 500000.0)
+ * });
+ */
+ var EllipsoidGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var radii = defaultValue(options.radii, defaultRadii);
+ var ellipsoid = Ellipsoid.fromCartesian3(radii);
+ var numberOfPartitions = defaultValue(options.numberOfPartitions, 32);
+
+ var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
+
+ if (numberOfPartitions <= 0) {
+ throw new DeveloperError('options.numberOfPartitions must be greater than zero.');
+ }
+
+ var positions = [];
+ var indices = [];
+
+ //
+ // Initial cube. In the plane, z = -1:
+ //
+ // +y
+ // |
+ // Q2 * p3 Q1
+ // / | \
+ // p0 *--+--* p2 +x
+ // \ | /
+ // Q3 * p1 Q4
+ // |
+ //
+ // Similarly, p4 to p7 are in the plane z = 1.
+ //
+ positions.push(new Cartesian3(-1, 0, -1));
+ positions.push(new Cartesian3(0, -1, -1));
+ positions.push(new Cartesian3(1, 0, -1));
+ positions.push(new Cartesian3(0, 1, -1));
+ positions.push(new Cartesian3(-1, 0, 1));
+ positions.push(new Cartesian3(0, -1, 1));
+ positions.push(new Cartesian3(1, 0, 1));
+ positions.push(new Cartesian3(0, 1, 1));
+
+ //
+ // Edges
+ //
+ // 0 -> 1, 1 -> 2, 2 -> 3, 3 -> 0. Plane z = -1
+ // 4 -> 5, 5 -> 6, 6 -> 7, 7 -> 4. Plane z = 1
+ // 0 -> 4, 1 -> 5, 2 -> 6, 3 -> 7. From plane z = -1 to plane z - 1
+ //
+ var edge0to1 = addEdgePositions(0, 1, numberOfPartitions, positions);
+ var edge1to2 = addEdgePositions(1, 2, numberOfPartitions, positions);
+ var edge2to3 = addEdgePositions(2, 3, numberOfPartitions, positions);
+ var edge3to0 = addEdgePositions(3, 0, numberOfPartitions, positions);
+
+ var edge4to5 = addEdgePositions(4, 5, numberOfPartitions, positions);
+ var edge5to6 = addEdgePositions(5, 6, numberOfPartitions, positions);
+ var edge6to7 = addEdgePositions(6, 7, numberOfPartitions, positions);
+ var edge7to4 = addEdgePositions(7, 4, numberOfPartitions, positions);
+
+ var edge0to4 = addEdgePositions(0, 4, numberOfPartitions, positions);
+ var edge1to5 = addEdgePositions(1, 5, numberOfPartitions, positions);
+ var edge2to6 = addEdgePositions(2, 6, numberOfPartitions, positions);
+ var edge3to7 = addEdgePositions(3, 7, numberOfPartitions, positions);
+
+ // Q3 Face
+ addFaceTriangles(edge0to4, edge0to1, edge1to5, edge4to5, numberOfPartitions, positions, indices);
+ // Q4 Face
+ addFaceTriangles(edge1to5, edge1to2, edge2to6, edge5to6, numberOfPartitions, positions, indices);
+ // Q1 Face
+ addFaceTriangles(edge2to6, edge2to3, edge3to7, edge6to7, numberOfPartitions, positions, indices);
+ // Q2 Face
+ addFaceTriangles(edge3to7, edge3to0, edge0to4, edge7to4, numberOfPartitions, positions, indices);
+ // Plane z = 1
+ addFaceTriangles(edge7to4.slice(0).reverse(), edge4to5, edge5to6, edge6to7.slice(0).reverse(), numberOfPartitions, positions, indices);
+ // Plane z = -1
+ addFaceTriangles(edge1to2, edge0to1.slice(0).reverse(), edge3to0.slice(0).reverse(), edge2to3, numberOfPartitions, positions, indices);
+
+ var attributes = new GeometryAttributes();
+
+ var length = positions.length;
+ var i;
+ var j;
+
+ if (vertexFormat.position) {
+ // Expand cube into ellipsoid and flatten values
+ var flattenedPositions = new Float64Array(length * 3);
+
+ for (i = j = 0; i < length; ++i) {
+ var item = positions[i];
+ Cartesian3.normalize(item, item);
+ Cartesian3.multiplyComponents(item, radii, item);
+
+ flattenedPositions[j++] = item.x;
+ flattenedPositions[j++] = item.y;
+ flattenedPositions[j++] = item.z;
+ }
+
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : flattenedPositions
+ });
+ }
+
+ if (vertexFormat.st) {
+ var texCoords = new Float32Array(length * 2);
+ var oneOverRadii = ellipsoid.getOneOverRadii();
+
+ for (i = j = 0; i < length; ++i) {
+ Cartesian3.multiplyComponents(positions[i], oneOverRadii, sphericalNormal);
+ Cartesian3.normalize(sphericalNormal, sphericalNormal);
+
+ texCoords[j++] = Math.atan2(sphericalNormal.y, sphericalNormal.x) * CesiumMath.ONE_OVER_TWO_PI + 0.5;
+ texCoords[j++] = Math.asin(sphericalNormal.z) * CesiumMath.ONE_OVER_PI + 0.5;
+ }
+
+ attributes.st = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : texCoords
+ });
+ }
+
+ if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.binormal) {
+ var normals = (vertexFormat.normal) ? new Float32Array(length * 3) : undefined;
+ var tangents = (vertexFormat.tangent) ? new Float32Array(length * 3) : undefined;
+ var binormals = (vertexFormat.binormal) ? new Float32Array(length * 3) : undefined;
+
+ for (i = j = 0; i < length; ++i, j += 3) {
+ ellipsoid.geodeticSurfaceNormal(positions[i], normal);
+ Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent).normalize(tangent);
+ Cartesian3.cross(normal, tangent, binormal).normalize(binormal);
+
+ if (vertexFormat.normal) {
+ normals[j] = normal.x;
+ normals[j + 1] = normal.y;
+ normals[j + 2] = normal.z;
+ }
+
+ if (vertexFormat.tangent) {
+ tangents[j] = tangent.x;
+ tangents[j + 1] = tangent.y;
+ tangents[j + 2] = tangent.z;
+ }
+
+ if (vertexFormat.binormal) {
+ binormals[j] = binormal.x;
+ binormals[j + 1] = binormal.y;
+ binormals[j + 2] = binormal.z;
+ }
+ }
+
+ if (vertexFormat.normal) {
+ attributes.normal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : normals
+ });
+ }
+
+ if (vertexFormat.tangent) {
+ attributes.tangent = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : tangents
+ });
+ }
+
+ if (vertexFormat.binormal) {
+ attributes.binormal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : binormals
+ });
+ }
+ }
+
+ /**
+ * An object containing {@link GeometryAttribute} properties named after each of the
+ * true
values of the {@link VertexFormat} option.
+ *
+ * @type Object
+ *
+ * @see Geometry#attributes
+ */
+ this.attributes = attributes;
+
+ /**
+ * Index data that, along with {@link Geometry#primitiveType}, determines the primitives in the geometry.
+ *
+ * @type Array
+ */
+ this.indices = IndexDatatype.createTypedArray(length, indices);
+
+ /**
+ * The type of primitives in the geometry. For this geometry, it is {@link PrimitiveType.TRIANGLES}.
+ *
+ * @type PrimitiveType
+ */
+ this.primitiveType = PrimitiveType.TRIANGLES;
+
+ /**
+ * A tight-fitting bounding sphere that encloses the vertices of the geometry.
+ *
+ * @type BoundingSphere
+ */
+ this.boundingSphere = BoundingSphere.fromEllipsoid(ellipsoid);
+ };
+
+ return EllipsoidGeometry;
+});
\ No newline at end of file
diff --git a/Source/Core/EllipsoidalOccluder.js b/Source/Core/EllipsoidalOccluder.js
index 7a4fb62b3459..283f8f295e0f 100644
--- a/Source/Core/EllipsoidalOccluder.js
+++ b/Source/Core/EllipsoidalOccluder.js
@@ -253,7 +253,7 @@ define([
throw new DeveloperError('extent is required.');
}
- var positions = extent.subsample(ellipsoid, subsampleScratch);
+ var positions = extent.subsample(ellipsoid, 0.0, subsampleScratch);
var bs = BoundingSphere.fromPoints(positions);
// If the bounding sphere center is too close to the center of the occluder, it doesn't make
diff --git a/Source/Core/Extent.js b/Source/Core/Extent.js
index 022ede46c830..a7c2288c34bf 100644
--- a/Source/Core/Extent.js
+++ b/Source/Core/Extent.js
@@ -17,44 +17,83 @@ define([
/**
* A two dimensional region specified as longitude and latitude coordinates.
+ *
* @alias Extent
* @constructor
*
- * @param {Number} [west=0.0] The westernmost longitude in the range [-Pi, Pi].
- * @param {Number} [south=0.0] The southernmost latitude in the range [-Pi/2, Pi/2].
- * @param {Number} [east=0.0] The easternmost longitude in the range [-Pi, Pi].
- * @param {Number} [north=0.0] The northernmost latitude in the range [-Pi/2, Pi/2].
+ * @param {Number} [west=0.0] The westernmost longitude, in radians, in the range [-Pi, Pi].
+ * @param {Number} [south=0.0] The southernmost latitude, in radians, in the range [-Pi/2, Pi/2].
+ * @param {Number} [east=0.0] The easternmost longitude, in radians, in the range [-Pi, Pi].
+ * @param {Number} [north=0.0] The northernmost latitude, in radians, in the range [-Pi/2, Pi/2].
*/
var Extent = function(west, south, east, north) {
/**
- * The westernmost longitude in the range [-Pi, Pi].
+ * The westernmost longitude in radians in the range [-Pi, Pi].
+ *
* @type {Number}
* @default 0.0
*/
this.west = defaultValue(west, 0.0);
/**
- * The southernmost latitude in the range [-Pi/2, Pi/2].
+ * The southernmost latitude in radians in the range [-Pi/2, Pi/2].
+ *
* @type {Number}
* @default 0.0
*/
this.south = defaultValue(south, 0.0);
/**
- * The easternmost longitude in the range [-Pi, Pi].
+ * The easternmost longitude in radians in the range [-Pi, Pi].
+ *
* @type {Number}
* @default 0.0
*/
this.east = defaultValue(east, 0.0);
/**
- * The northernmost latitude in the range [-Pi/2, Pi/2].
+ * The northernmost latitude in radians in the range [-Pi/2, Pi/2].
+ *
* @type {Number}
* @default 0.0
*/
this.north = defaultValue(north, 0.0);
};
+ /**
+ * Creates an extent given the boundary longitude and latitude in degrees.
+ *
+ * @memberof Extent
+ *
+ * @param {Number} [west=0.0] The westernmost longitude in degrees in the range [-180.0, 180.0].
+ * @param {Number} [south=0.0] The southernmost latitude in degrees in the range [-90.0, 90.0].
+ * @param {Number} [east=0.0] The easternmost longitude in degrees in the range [-180.0, 180.0].
+ * @param {Number} [north=0.0] The northernmost latitude in degrees in the range [-90.0, 90.0].
+ * @param {Extent} [result] The object onto which to store the result, or undefined if a new instance should be created.
+ *
+ * @return {Extent} The modified result parameter or a new Extent instance if none was provided.
+ *
+ * @example
+ * var extent = Extent.fromDegrees(0.0, 20.0, 10.0, 30.0);
+ */
+ Extent.fromDegrees = function(west, south, east, north, result) {
+ west = CesiumMath.toRadians(defaultValue(west, 0.0));
+ south = CesiumMath.toRadians(defaultValue(south, 0.0));
+ east = CesiumMath.toRadians(defaultValue(east, 0.0));
+ north = CesiumMath.toRadians(defaultValue(north, 0.0));
+
+ if (typeof result === 'undefined') {
+ return new Extent(west, south, east, north);
+ }
+
+ result.west = west;
+ result.south = south;
+ result.east = east;
+ result.north = north;
+
+ return result;
+ };
+
/**
* Creates the smallest possible Extent that encloses all positions in the provided array.
* @memberof Extent
@@ -105,9 +144,11 @@ define([
if (typeof extent === 'undefined') {
return undefined;
}
+
if (typeof result === 'undefined') {
return new Extent(extent.west, extent.south, extent.east, extent.north);
}
+
result.west = extent.west;
result.south = extent.south;
result.east = extent.east;
@@ -136,11 +177,28 @@ define([
* @return {Boolean} true
if the Extents are equal, false
otherwise.
*/
Extent.prototype.equals = function(other) {
- return typeof other !== 'undefined' &&
- this.west === other.west &&
- this.south === other.south &&
- this.east === other.east &&
- this.north === other.north;
+ return Extent.equals(this, other);
+ };
+
+ /**
+ * Compares the provided extents and returns true
if they are equal,
+ * false
otherwise.
+ *
+ * @memberof Extent
+ *
+ * @param {Extent} [left] The first Extent.
+ * @param {Extent} [right] The second Extent.
+ *
+ * @return {Boolean} true
if left and right are equal; otherwise false
.
+ */
+ Extent.equals = function(left, right) {
+ return (left === right) ||
+ ((typeof left !== 'undefined') &&
+ (typeof right !== 'undefined') &&
+ (left.west === right.west) &&
+ (left.south === right.south) &&
+ (left.east === right.east) &&
+ (left.north === right.north));
};
/**
@@ -363,16 +421,18 @@ define([
var subsampleLlaScratch = new Cartographic();
/**
- * Samples this Extent so that it includes a list of Cartesian points suitable for passing to
+ * Samples this extent so that it includes a list of Cartesian points suitable for passing to
* {@link BoundingSphere#fromPoints}. Sampling is necessary to account
* for extents that cover the poles or cross the equator.
*
* @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use.
+ * @param {Number} [surfaceHeight=0.0] The height of the extent above the ellipsoid.
* @param {Array} [result] The array of Cartesians onto which to store the result.
* @return {Array} The modified result parameter or a new Array of Cartesians instances if none was provided.
*/
- Extent.prototype.subsample = function(ellipsoid, result) {
+ Extent.prototype.subsample = function(ellipsoid, surfaceHeight, result) {
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
+ surfaceHeight = defaultValue(surfaceHeight, 0.0);
if (typeof result === 'undefined') {
result = [];
@@ -385,6 +445,8 @@ define([
var west = this.west;
var lla = subsampleLlaScratch;
+ lla.height = surfaceHeight;
+
lla.longitude = west;
lla.latitude = north;
result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
diff --git a/Source/Core/ExtentGeometry.js b/Source/Core/ExtentGeometry.js
new file mode 100644
index 000000000000..77718ed61960
--- /dev/null
+++ b/Source/Core/ExtentGeometry.js
@@ -0,0 +1,329 @@
+/*global define*/
+define([
+ './clone',
+ './defaultValue',
+ './BoundingSphere',
+ './Cartesian3',
+ './Cartographic',
+ './ComponentDatatype',
+ './IndexDatatype',
+ './DeveloperError',
+ './Ellipsoid',
+ './Extent',
+ './GeographicProjection',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './Math',
+ './Matrix2',
+ './PrimitiveType',
+ './VertexFormat'
+ ], function(
+ clone,
+ defaultValue,
+ BoundingSphere,
+ Cartesian3,
+ Cartographic,
+ ComponentDatatype,
+ IndexDatatype,
+ DeveloperError,
+ Ellipsoid,
+ Extent,
+ GeographicProjection,
+ GeometryAttribute,
+ GeometryAttributes,
+ CesiumMath,
+ Matrix2,
+ PrimitiveType,
+ VertexFormat) {
+ "use strict";
+
+ function isValidLatLon(latitude, longitude) {
+ if (latitude < -CesiumMath.PI_OVER_TWO || latitude > CesiumMath.PI_OVER_TWO) {
+ return false;
+ }
+ if (longitude > CesiumMath.PI || longitude < -CesiumMath.PI) {
+ return false;
+ }
+ return true;
+ }
+
+ var nw = new Cartesian3();
+ var nwCartographic = new Cartographic();
+ var centerCartographic = new Cartographic();
+ var center = new Cartesian3();
+ var rotationMatrix = new Matrix2();
+ var proj = new GeographicProjection();
+ var position = new Cartesian3();
+ var normal = new Cartesian3();
+ var tangent = new Cartesian3();
+ var binormal = new Cartesian3();
+
+ /**
+ * A {@link Geometry} that represents geometry for a cartographic extent on an ellipsoid centered at the origin.
+ *
+ * @alias ExtentGeometry
+ * @constructor
+ *
+ * @param {Extent} options.extent A cartographic extent with north, south, east and west properties in radians.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the extent lies.
+ * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
+ * @param {Number} [options.height=0.0] The height from the surface of the ellipsoid.
+ * @param {Number} [options.rotation=0.0] The rotation of the extent in radians. A positive rotation is counter-clockwise.
+ *
+ * @exception {DeveloperError} options.extent
is required and must have north, south, east and west attributes.
+ * @exception {DeveloperError} options.extent.north
must be in the interval [-Pi/2
, Pi/2
].
+ * @exception {DeveloperError} options.extent.south
must be in the interval [-Pi/2
, Pi/2
].
+ * @exception {DeveloperError} options.extent.east
must be in the interval [-Pi
, Pi
].
+ * @exception {DeveloperError} options.extent.west
must be in the interval [-Pi
, Pi
].
+ * @exception {DeveloperError} options.extent.north
must be greater than extent.south
.
+ * @exception {DeveloperError} options.extent.east
must be greater than extent.west
.
+ * @exception {DeveloperError} Rotated extent is invalid.
+ *
+ * @example
+ * var extent = new ExtentGeometry({
+ * ellipsoid : Ellipsoid.WGS84,
+ * extent : Extent.fromDegrees(-80.0, 39.0, -74.0, 42.0),
+ * height : 10000.0
+ * });
+ */
+ var ExtentGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var extent = options.extent;
+ if (typeof extent === 'undefined') {
+ throw new DeveloperError('extent is required.');
+ }
+
+ extent.validate();
+
+ var granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
+ var width = Math.ceil((extent.east - extent.west) / granularity) + 1;
+ var height = Math.ceil((extent.north - extent.south) / granularity) + 1;
+ var granularityX = (extent.east - extent.west) / (width - 1);
+ var granularityY = (extent.north - extent.south) / (height - 1);
+
+ var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
+ var radiiSquared = ellipsoid.getRadiiSquared();
+ var radiiSquaredX = radiiSquared.x;
+ var radiiSquaredY = radiiSquared.y;
+ var radiiSquaredZ = radiiSquared.z;
+
+ var surfaceHeight = defaultValue(options.height, 0.0);
+ var rotation = defaultValue(options.rotation, 0.0);
+
+ var cos = Math.cos;
+ var sin = Math.sin;
+ var sqrt = Math.sqrt;
+
+ // for computing texture coordinates
+ var lonScalar = 1.0 / (extent.east - extent.west);
+ var latScalar = 1.0 / (extent.north - extent.south);
+
+ extent.getNorthwest(nwCartographic);
+ extent.getCenter(centerCartographic);
+ var latitude, longitude;
+
+ var granYCos = granularityY * cos(rotation);
+ var granYSin = granularityY * sin(rotation);
+ var granXCos = granularityX * cos(rotation);
+ var granXSin = granularityX * sin(rotation);
+
+ if (rotation !== 0) {
+ proj.project(nwCartographic, nw);
+ proj.project(centerCartographic, center);
+ nw.subtract(center, nw);
+ Matrix2.fromRotation(rotation, rotationMatrix);
+ rotationMatrix.multiplyByVector(nw, nw);
+ nw.add(center, nw);
+ proj.unproject(nw, nwCartographic);
+ latitude = nwCartographic.latitude;
+ longitude = nwCartographic.longitude;
+
+ if (!isValidLatLon(latitude, longitude) ||
+ !isValidLatLon(latitude + (width-1)*granXSin, longitude + (width-1)*granXCos) ||
+ !isValidLatLon(latitude - granYCos*(height-1), longitude + (height-1)*granYSin) ||
+ !isValidLatLon(latitude - granYCos*(height-1) + (width-1)*granXSin, longitude + (height-1)*granYSin + (width-1)*granXCos)) {
+ throw new DeveloperError('Rotated extent is invalid.');
+ }
+ }
+
+ var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
+ var attributes = new GeometryAttributes();
+
+ var positionIndex = 0;
+ var stIndex = 0;
+ var normalIndex = 0;
+ var tangentIndex = 0;
+ var binormalIndex = 0;
+
+ var size = width * height;
+ var positions = (vertexFormat.position) ? new Float64Array(size * 3) : undefined;
+ var textureCoordinates = (vertexFormat.st) ? new Float32Array(size * 2) : undefined;
+ var normals = (vertexFormat.normal) ? new Float32Array(size * 3) : undefined;
+ var tangents = (vertexFormat.tangent) ? new Float32Array(size * 3) : undefined;
+ var binormals = (vertexFormat.binormal) ? new Float32Array(size * 3) : undefined;
+
+ for ( var row = 0; row < height; ++row) {
+ for ( var col = 0; col < width; ++col) {
+ latitude = nwCartographic.latitude - granYCos*row + col*granXSin;
+ var cosLatitude = cos(latitude);
+ var nZ = sin(latitude);
+ var kZ = radiiSquaredZ * nZ;
+
+ longitude = nwCartographic.longitude + row*granYSin + col*granXCos;
+
+ var nX = cosLatitude * cos(longitude);
+ var nY = cosLatitude * sin(longitude);
+
+ var kX = radiiSquaredX * nX;
+ var kY = radiiSquaredY * nY;
+
+ var gamma = sqrt((kX * nX) + (kY * nY) + (kZ * nZ));
+
+ var rSurfaceX = kX / gamma;
+ var rSurfaceY = kY / gamma;
+ var rSurfaceZ = kZ / gamma;
+
+ position.x = rSurfaceX + nX * surfaceHeight;
+ position.y = rSurfaceY + nY * surfaceHeight;
+ position.z = rSurfaceZ + nZ * surfaceHeight;
+
+ if (vertexFormat.position) {
+ positions[positionIndex++] = position.x;
+ positions[positionIndex++] = position.y;
+ positions[positionIndex++] = position.z;
+ }
+
+ if (vertexFormat.st) {
+ textureCoordinates[stIndex++] = (longitude - extent.west) * lonScalar;
+ textureCoordinates[stIndex++] = (latitude - extent.south) * latScalar;
+ }
+
+ if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.binormal) {
+ ellipsoid.geodeticSurfaceNormal(position, normal);
+
+ if (vertexFormat.normal) {
+ normals[normalIndex++] = normal.x;
+ normals[normalIndex++] = normal.y;
+ normals[normalIndex++] = normal.z;
+ }
+
+ if (vertexFormat.tangent) {
+ Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent);
+
+ tangents[tangentIndex++] = tangent.x;
+ tangents[tangentIndex++] = tangent.y;
+ tangents[tangentIndex++] = tangent.z;
+ }
+
+ if (vertexFormat.binormal) {
+ Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent);
+ Cartesian3.cross(normal, tangent, binormal);
+
+ binormals[binormalIndex++] = binormal.x;
+ binormals[binormalIndex++] = binormal.y;
+ binormals[binormalIndex++] = binormal.z;
+ }
+ }
+ }
+ }
+
+ var indicesSize = 6 * (width - 1) * (height - 1);
+ var indices = IndexDatatype.createTypedArray(size, indicesSize);
+
+ var index = 0;
+ var indicesIndex = 0;
+ for ( var i = 0; i < height - 1; ++i) {
+ for ( var j = 0; j < width - 1; ++j) {
+ var upperLeft = index;
+ var lowerLeft = upperLeft + width;
+ var lowerRight = lowerLeft + 1;
+ var upperRight = upperLeft + 1;
+
+ indices[indicesIndex++] = upperLeft;
+ indices[indicesIndex++] = lowerLeft;
+ indices[indicesIndex++] = upperRight;
+ indices[indicesIndex++] = upperRight;
+ indices[indicesIndex++] = lowerLeft;
+ indices[indicesIndex++] = lowerRight;
+
+ ++index;
+ }
+ ++index;
+ }
+
+ if (vertexFormat.position) {
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : positions
+ });
+ }
+
+ if (vertexFormat.st) {
+ attributes.st = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : textureCoordinates
+ });
+ }
+
+ if (vertexFormat.normal) {
+ attributes.normal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : normals
+ });
+ }
+
+ if (vertexFormat.tangent) {
+ attributes.tangent = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : tangents
+ });
+ }
+
+ if (vertexFormat.binormal) {
+ attributes.binormal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : binormals
+ });
+ }
+
+ /**
+ * An object containing {@link GeometryAttribute} properties named after each of the
+ * true
values of the {@link VertexFormat} option.
+ *
+ * @type GeometryAttributes
+ *
+ * @see Geometry#attributes
+ */
+ this.attributes = attributes;
+
+ /**
+ * Index data that, along with {@link Geometry#primitiveType}, determines the primitives in the geometry.
+ *
+ * @type Array
+ */
+ this.indices = indices;
+
+ /**
+ * The type of primitives in the geometry. For this geometry, it is {@link PrimitiveType.TRIANGLES}.
+ *
+ * @type PrimitiveType
+ */
+ this.primitiveType = PrimitiveType.TRIANGLES;
+
+ /**
+ * A tight-fitting bounding sphere that encloses the vertices of the geometry.
+ *
+ * @type BoundingSphere
+ */
+ this.boundingSphere = BoundingSphere.fromExtent3D(extent, ellipsoid, surfaceHeight);
+ };
+
+ return ExtentGeometry;
+});
diff --git a/Source/Core/ExtentTessellator.js b/Source/Core/ExtentTessellator.js
deleted file mode 100644
index d944142792d6..000000000000
--- a/Source/Core/ExtentTessellator.js
+++ /dev/null
@@ -1,446 +0,0 @@
-/*global define*/
-define([
- './clone',
- './defaultValue',
- './Math',
- './Ellipsoid',
- './Cartesian3',
- './Cartographic',
- './Matrix2',
- './GeographicProjection',
- './ComponentDatatype',
- './PrimitiveType'
- ], function(
- clone,
- defaultValue,
- CesiumMath,
- Ellipsoid,
- Cartesian3,
- Cartographic,
- Matrix2,
- GeographicProjection,
- ComponentDatatype,
- PrimitiveType) {
- "use strict";
-
- /**
- * Contains class functions to create a mesh or vertex array from a cartographic extent.
- *
- * @exports ExtentTessellator
- *
- * @see HeightmapTessellator
- * @see CubeMapEllipsoidTessellator
- * @see BoxTessellator
- * @see PlaneTessellator
- */
- var ExtentTessellator = {};
-
- function isValidLatLon(latitude, longitude) {
- if (latitude < -CesiumMath.PI_OVER_TWO || latitude > CesiumMath.PI_OVER_TWO) {
- return false;
- }
- if (longitude > CesiumMath.PI || longitude < -CesiumMath.PI) {
- return false;
- }
- return true;
- }
- /**
- * Compute vertices from a cartographic extent. This function is different from
- * {@link ExtentTessellator#compute} and {@link ExtentTessellator#computeBuffers}
- * in that it assumes that you have already allocated output arrays of the correct size.
- *
- * @param {Extent} description.extent A cartographic extent with north, south, east and west properties in radians.
- * @param {Number} description.rotation The rotation of the extent in radians.
- * @param {Number} description.width The number of vertices in the longitude direction.
- * @param {Number} description.height The number of vertices in the latitude direction.
- * @param {Number} description.surfaceHeight The height from the surface of the ellipsoid.
- * @param {Boolean} description.generateTextureCoordinates Whether to generate texture coordinates.
- * @param {Boolean} description.interleaveTextureCoordinates Whether to interleave the texture coordinates into the vertex array.
- * @param {Cartesian3} description.relativetoCenter The positions will be computed as worldPosition.subtract(relativeToCenter)
.
- * @param {Cartesian3} description.radiiSquared The radii squared of the ellipsoid to use.
- * @param {Array|Float32Array} description.vertices The array to use to store computed vertices.
- * @param {Array|Float32Array} description.textureCoordinates The array to use to store computed texture coordinates, unless interleaved.
- * @param {Array|Float32Array} [description.indices] The array to use to store computed indices. If undefined, indices will be not computed.
- */
- var nw = new Cartesian3();
- var nwCartographic = new Cartographic();
- var centerCartographic = new Cartographic();
- var center = new Cartesian3();
- var rotationMatrix = new Matrix2();
- var proj = new GeographicProjection();
- ExtentTessellator.computeVertices = function(description) {
- description = defaultValue(description, defaultValue.EMPTY_OBJECT);
- var extent = description.extent;
- extent.validate();
- var rotation = description.rotation;
- var surfaceHeight = description.surfaceHeight;
- var width = description.width;
- var height = description.height;
-
- var granularityX = (extent.east - extent.west) / (width - 1);
- var granularityY = (extent.north - extent.south) / (height - 1);
- var generateTextureCoordinates = description.generateTextureCoordinates;
- var interleaveTextureCoordinates = description.interleaveTextureCoordinates;
- var relativeToCenter = description.relativeToCenter;
-
- var vertices = description.vertices;
- var textureCoordinates = description.textureCoordinates;
- var indices = description.indices;
-
- var radiiSquared = description.radiiSquared;
- var radiiSquaredX = radiiSquared.x;
- var radiiSquaredY = radiiSquared.y;
- var radiiSquaredZ = radiiSquared.z;
-
- var cos = Math.cos;
- var sin = Math.sin;
- var sqrt = Math.sqrt;
-
- // for computing texture coordinates
- var lonScalar = 1.0 / (extent.east - extent.west);
- var latScalar = 1.0 / (extent.north - extent.south);
-
- var vertexArrayIndex = 0;
- var textureCoordinatesIndex = 0;
-
- extent.getNorthwest(nwCartographic);
- extent.getCenter(centerCartographic);
- var latitude, longitude;
-
- if (typeof rotation === 'undefined') {
- rotation = 0;
- }
-
- var granYCos = granularityY * cos(rotation);
- var granYSin = granularityY * sin(rotation);
- var granXCos = granularityX * cos(rotation);
- var granXSin = granularityX * sin(rotation);
-
- if (rotation !== 0) {
- proj.project(nwCartographic, nw);
- proj.project(centerCartographic, center);
- nw.subtract(center, nw);
- Matrix2.fromRotation(rotation, rotationMatrix);
- rotationMatrix.multiplyByVector(nw, nw);
- nw.add(center, nw);
- proj.unproject(nw, nwCartographic);
- latitude = nwCartographic.latitude;
- longitude = nwCartographic.longitude;
- if (!isValidLatLon(latitude, longitude)) { //NW corner
- return;
- }
- if (!isValidLatLon(latitude + (width-1)*granXSin, longitude + (width-1)*granXCos)) { //NE corner
- return;
- }
- if (!isValidLatLon(latitude - granYCos*(height-1), longitude + (height-1)*granYSin)) { //SW corner
- return;
- }
- if (!isValidLatLon(latitude - granYCos*(height-1) + (width-1)*granXSin, longitude + (height-1)*granYSin + (width-1)*granXCos)) { //SE corner
- return;
- }
- }
-
- for ( var row = 0; row < height; ++row) {
- for ( var col = 0; col < width; ++col) {
- latitude = nwCartographic.latitude - granYCos*row + col*granXSin;
- var cosLatitude = cos(latitude);
- var nZ = sin(latitude);
- var kZ = radiiSquaredZ * nZ;
-
- longitude = nwCartographic.longitude + row*granYSin + col*granXCos;
-
- var nX = cosLatitude * cos(longitude);
- var nY = cosLatitude * sin(longitude);
-
- var kX = radiiSquaredX * nX;
- var kY = radiiSquaredY * nY;
-
- var gamma = sqrt((kX * nX) + (kY * nY) + (kZ * nZ));
-
- var rSurfaceX = kX / gamma;
- var rSurfaceY = kY / gamma;
- var rSurfaceZ = kZ / gamma;
-
- vertices[vertexArrayIndex++] = rSurfaceX + nX * surfaceHeight - relativeToCenter.x;
- vertices[vertexArrayIndex++] = rSurfaceY + nY * surfaceHeight - relativeToCenter.y;
- vertices[vertexArrayIndex++] = rSurfaceZ + nZ * surfaceHeight - relativeToCenter.z;
-
- if (generateTextureCoordinates) {
- var u = (longitude - extent.west) * lonScalar;
- var v = (latitude - extent.south) * latScalar;
-
- if (interleaveTextureCoordinates) {
- vertices[vertexArrayIndex++] = u;
- vertices[vertexArrayIndex++] = v;
- } else {
- textureCoordinates[textureCoordinatesIndex++] = u;
- textureCoordinates[textureCoordinatesIndex++] = v;
- }
- }
- }
- }
-
- if (typeof indices !== 'undefined') {
- var index = 0;
- var indicesIndex = 0;
- for ( var i = 0; i < height - 1; ++i) {
- for ( var j = 0; j < width - 1; ++j) {
- var upperLeft = index;
- var lowerLeft = upperLeft + width;
- var lowerRight = lowerLeft + 1;
- var upperRight = upperLeft + 1;
-
- indices[indicesIndex++] = upperLeft;
- indices[indicesIndex++] = lowerLeft;
- indices[indicesIndex++] = upperRight;
- indices[indicesIndex++] = upperRight;
- indices[indicesIndex++] = lowerLeft;
- indices[indicesIndex++] = lowerRight;
-
- ++index;
- }
- ++index;
- }
- }
- };
-
- /**
- * Creates a mesh from a cartographic extent.
- *
- * @param {Extent} description.extent A cartographic extent with north, south, east and west properties in radians.
- * @param {Ellipsoid} [description.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the extent lies.
- * @param {Number} [description.granularity=0.1] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
- * @param {Number} [description.surfaceHeight=0.0] The height from the surface of the ellipsoid.
- * @param {Cartesian3} [description.relativetoCenter=Cartesian3.ZERO] The positions will be computed as worldPosition.subtract(relativeToCenter)
.
- * @param {Boolean} [description.generateTextureCoordinates=false] Whether to generate texture coordinates.
- *
- * @exception {DeveloperError} description.extent
is required and must have north, south, east and west attributes.
- * @exception {DeveloperError} description.extent.north
must be in the interval [-Pi/2
, Pi/2
].
- * @exception {DeveloperError} description.extent.south
must be in the interval [-Pi/2
, Pi/2
].
- * @exception {DeveloperError} description.extent.east
must be in the interval [-Pi
, Pi
].
- * @exception {DeveloperError} description.extent.west
must be in the interval [-Pi
, Pi
].
- * @exception {DeveloperError} description.extent.north
must be greater than extent.south
.
- * @exception {DeveloperError} description.extent.east
must be greater than extent.west
.
- * @exception {DeveloperError} description.context
is required.
- *
- * @return {Object} A mesh containing attributes for positions, possibly texture coordinates and indices
- * from the extent for creating a vertex array. (returns undefined if no indices are found)
- *
- * @see Context#createVertexArrayFromMesh
- * @see MeshFilters.createAttributeIndices
- * @see MeshFilters.toWireframeInPlace
- * @see Extent
- *
- * @example
- * // Create a vertex array for rendering a wireframe extent.
- * var mesh = ExtentTessellator.compute({
- * ellipsoid : Ellipsoid.WGS84,
- * extent : new Extent(
- * CesiumMath.toRadians(-80.0),
- * CesiumMath.toRadians(39.0),
- * CesiumMath.toRadians(-74.0),
- * CesiumMath.toRadians(42.0)
- * ),
- * granularity : 0.01,
- * surfaceHeight : 10000.0
- * });
- * mesh = MeshFilters.toWireframeInPlace(mesh);
- * var va = context.createVertexArrayFromMesh({
- * mesh : mesh,
- * attributeIndices : MeshFilters.createAttributeIndices(mesh)
- * });
- */
- ExtentTessellator.compute = function(description) {
- description = defaultValue(description, defaultValue.EMPTY_OBJECT);
-
- // make a copy of description to allow us to change values before passing to computeVertices
- var computeVerticesDescription = clone(description);
-
- var extent = description.extent;
- extent.validate();
-
- var ellipsoid = defaultValue(description.ellipsoid, Ellipsoid.WGS84);
- computeVerticesDescription.radiiSquared = ellipsoid.getRadiiSquared();
- computeVerticesDescription.relativeToCenter = defaultValue(description.relativeToCenter, Cartesian3.ZERO);
-
- var granularity = defaultValue(description.granularity, 0.1);
- computeVerticesDescription.surfaceHeight = defaultValue(description.surfaceHeight, 0.0);
-
- computeVerticesDescription.width = Math.ceil((extent.east - extent.west) / granularity) + 1;
- computeVerticesDescription.height = Math.ceil((extent.north - extent.south) / granularity) + 1;
-
- var vertices = [];
- var indices = [];
- var textureCoordinates = [];
-
- computeVerticesDescription.generateTextureCoordinates = defaultValue(computeVerticesDescription.generateTextureCoordinates, false);
- computeVerticesDescription.interleaveTextureCoordinates = false;
- computeVerticesDescription.vertices = vertices;
- computeVerticesDescription.textureCoordinates = textureCoordinates;
- computeVerticesDescription.indices = indices;
-
- ExtentTessellator.computeVertices(computeVerticesDescription);
-
- if (indices.length === 0) {
- return undefined;
- }
-
- var mesh = {
- attributes : {},
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : indices
- }]
- };
-
- var positionName = defaultValue(description.positionName, 'position');
- mesh.attributes[positionName] = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 3,
- values : vertices
- };
-
- if (description.generateTextureCoordinates) {
- var textureCoordinatesName = defaultValue(description.textureCoordinatesName, 'textureCoordinates');
- mesh.attributes[textureCoordinatesName] = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 2,
- values : textureCoordinates
- };
- }
-
- return mesh;
- };
-
- /**
- * Creates arrays of vertex attributes and indices from a cartographic extent.
- *
- * @param {Extent} description.extent A cartographic extent with north, south, east and west properties in radians.
- * @param {Ellipsoid} [description.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the extent lies.
- * @param {Number} [description.granularity=0.1] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
- * @param {Number} [description.surfaceHeight=0.0] The height from the surface of the ellipsoid.
- * @param {Cartesian3} [description.relativetoCenter=Cartesian3.ZERO] The positions will be computed as worldPosition.subtract(relativeToCenter)
.
- * @param {Boolean} [description.generateTextureCoordinates=false] Whether to generate texture coordinates.
- * @param {Boolean} [description.interleaveTextureCoordinates=false] If texture coordinates are generated, whether to interleave the positions and texture coordinates in a single buffer.
- *
- * @exception {DeveloperError} description.extent
is required and must have north, south, east and west attributes.
- * @exception {DeveloperError} description.extent.north
must be in the interval [-Pi/2
, Pi/2
].
- * @exception {DeveloperError} description.extent.south
must be in the interval [-Pi/2
, Pi/2
].
- * @exception {DeveloperError} description.extent.east
must be in the interval [-Pi
, Pi
].
- * @exception {DeveloperError} description.extent.west
must be in the interval [-Pi
, Pi
].
- * @exception {DeveloperError} description.extent.north
must be greater than extent.south
. *
- * @exception {DeveloperError} description.extent.east
must be greater than extent.west
.
- *
- * @return {Object} An object with flattened arrays for vertex attributes and indices.
- *
- * @example
- * // Example 1:
- * // Create a vertex array for a solid extent, with separate positions and texture coordinates.
- * var buffers = ExtentTessellator.computeBuffers({
- * ellipsoid : ellipsoid,
- * extent : extent,
- * generateTextureCoordinates : true
- * });
- *
- * var datatype = ComponentDatatype.FLOAT;
- * var usage = BufferUsage.STATIC_DRAW;
- * var positionBuffer = context.createVertexBuffer(datatype.toTypedArray(buffers.positions), usage);
- * var textureCoordinateBuffer = context.createVertexBuffer(datatype.toTypedArray(buffers.textureCoordinates), usage);
- * attributes = [{
- * index : attributeIndices.position,
- * vertexBuffer : positionBuffer,
- * componentDatatype : datatype,
- * componentsPerAttribute : 3
- * }, {
- * index : attributeIndices.textureCoordinates,
- * vertexBuffer : textureCoordinateBuffer,
- * componentDatatype : datatype,
- * componentsPerAttribute : 2
- * }];
- * var indexBuffer = context.createIndexBuffer(new Uint16Array(buffers.indices), usage, IndexDatatype.UNSIGNED_SHORT);
- * var va = context.createVertexArray(attributes, indexBuffer);
- *
- * @example
- * // Example 2:
- * // Create a vertex array for a solid extent, with interleaved positions and texture coordinates.
- * var buffers = ExtentTessellator.computeBuffers({
- * ellipsoid : ellipsoid,
- * extent : extent,
- * generateTextureCoordinates : true,
- * interleaveTextureCoordinates : true
- * });
- *
- * var datatype = ComponentDatatype.FLOAT;
- * var usage = BufferUsage.STATIC_DRAW;
- * var typedArray = datatype.toTypedArray(buffers.vertices);
- * var buffer = context.createVertexBuffer(typedArray, usage);
- * var stride = 5 * datatype.sizeInBytes;
- * var attributes = [{
- * index : attributeIndices.position3D,
- * vertexBuffer : buffer,
- * componentDatatype : datatype,
- * componentsPerAttribute : 3,
- * normalize : false,
- * offsetInBytes : 0,
- * strideInBytes : stride
- * }, {
- * index : attributeIndices.textureCoordinates,
- * vertexBuffer : buffer,
- * componentDatatype : datatype,
- * componentsPerAttribute : 2,
- * normalize : false,
- * offsetInBytes : 3 * datatype.sizeInBytes,
- * strideInBytes : stride
- * }];
- * var indexBuffer = context.createIndexBuffer(new Uint16Array(buffers.indices), usage, IndexDatatype.UNSIGNED_SHORT);
- * var vacontext.createVertexArray(attributes, indexBuffer);
- */
- ExtentTessellator.computeBuffers = function(description) {
- description = defaultValue(description, defaultValue.EMPTY_OBJECT);
-
- // make a copy of description to allow us to change values before passing to computeVertices
- var computeVerticesDescription = clone(description);
-
- var extent = description.extent;
- extent.validate();
-
- var ellipsoid = defaultValue(description.ellipsoid, Ellipsoid.WGS84);
- computeVerticesDescription.radiiSquared = ellipsoid.getRadiiSquared();
- computeVerticesDescription.relativeToCenter = defaultValue(description.relativeToCenter, Cartesian3.ZERO);
-
- var granularity = defaultValue(description.granularity, 0.1);
- computeVerticesDescription.surfaceHeight = defaultValue(description.surfaceHeight, 0.0);
-
- computeVerticesDescription.width = Math.ceil((extent.east - extent.west) / granularity) + 1;
- computeVerticesDescription.height = Math.ceil((extent.north - extent.south) / granularity) + 1;
-
- var vertices = [];
- var indices = [];
- var textureCoordinates = [];
-
- computeVerticesDescription.generateTextureCoordinates = defaultValue(description.generateTextureCoordinates, false);
- computeVerticesDescription.interleaveTextureCoordinates = defaultValue(description.interleaveTextureCoordinates, false);
- computeVerticesDescription.vertices = vertices;
- computeVerticesDescription.textureCoordinates = textureCoordinates;
- computeVerticesDescription.indices = indices;
-
- ExtentTessellator.computeVertices(computeVerticesDescription);
-
- var result = {
- indices : indices
- };
-
- if (description.interleaveTextureCoordinates) {
- result.vertices = vertices;
- } else {
- result.positions = vertices;
- if (description.generateTextureCoordinates) {
- result.textureCoordinates = textureCoordinates;
- }
- }
-
- return result;
- };
-
- return ExtentTessellator;
-});
diff --git a/Source/Core/Geometry.js b/Source/Core/Geometry.js
new file mode 100644
index 000000000000..424e08319f5c
--- /dev/null
+++ b/Source/Core/Geometry.js
@@ -0,0 +1,195 @@
+/*global define*/
+define([
+ './defaultValue',
+ './DeveloperError',
+ './BoundingSphere'
+ ], function(
+ defaultValue,
+ DeveloperError,
+ BoundingSphere) {
+ "use strict";
+
+ /**
+ * A geometry representation with attributes forming vertices and optional index data
+ * defining primitives. Geometries and an {@link Appearance}, which describes the shading,
+ * can be assigned to a {@link Primitive} for visualization. A Primitive
can
+ * be created from many heterogeneous - in many cases - geometries for performance.
+ * + * In low-level rendering code, a vertex array can be created from a geometry using + * {@link Context#createVertexArrayFromGeometry}. + *
+ *+ * Geometries can be transformed and optimized using functions in {@link GeometryPipeline}. + *
+ * + * @alias Geometry + * @constructor + * + * @param {GeometryAttributes} options.attributes Attributes, which make up the geometry's vertices. + * @param {PrimitiveType} options.primitiveType The type of primitives in the geometry. + * @param {Array} [options.indices] Optional index data that determines the primitives in the geometry. + * @param {BoundingSphere} [options.boundingSphere] An optional bounding sphere that fully enclosed the geometry. + * + * @example + * // Create geometry with a position attribute and indexed lines. + * var positions = new Float64Array([ + * 0.0, 0.0, 0.0, + * 7500000.0, 0.0, 0.0, + * 0.0, 7500000.0, 0.0 + * ]); + * + * var geometry = new Geometry({ + * attributes : { + * position : new GeometryAttribute({ + * componentDatatype : ComponentDatatype.DOUBLE, + * componentsPerAttribute : 3, + * values : positions + * }) + * }, + * indices : new Uint16Array([0, 1, 1, 2, 2, 0]), + * primitiveType : PrimitiveType.LINES, + * boundingSphere : BoundingSphere.fromVertices(positions) + * }); + * + * @demo Geometry and Appearances Demo + * + * @see PolygonGeometry + * @see ExtentGeometry + * @see EllipseGeometry + * @see CircleGeometry + * @see WallGeometry + * @see SimplePolylineGeometry + * @see BoxGeometry + * @see EllipsoidGeometry + */ + var Geometry = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + if (typeof options.attributes === 'undefined') { + throw new DeveloperError('options.attributes is required.'); + } + + if (typeof options.primitiveType === 'undefined') { + throw new DeveloperError('options.primitiveType is required.'); + } + + /** + * Attributes, which make up the geometry's vertices. Each property in this object corresponds to a + * {@link GeometryAttribute} containing the attribute's data. + *+ * Attributes are always stored non-interleaved in a Geometry. When geometry is prepared for rendering + * with {@link Context#createVertexArrayFromGeometry}, attributes are generally written interleaved + * into the vertex buffer for better rendering performance. + *
+ *+ * There are reserved attribute names with well-known semantics. The following attributes + * are created by a Geometry (depending on the provided {@link VertexFormat}. + *
position
- 3D vertex position. 64-bit floating-point (for precision). 3 components per attribute. See {@link VertexFormat#position}.normal
- Normal (normalized), commonly used for lighting. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#normal}.st
- 2D texture coordinate. 32-bit floating-point. 2 components per attribute. See {@link VertexFormat#st}.binormal
- Binormal (normalized), used for tangent-space effects like bump mapping. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#binormal}.tangent
- Tangent (normalized), used for tangent-space effects like bump mapping. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#tangent}.+ * The following attribute names are generally not created by a Geometry, but are added + * to a Geometry by a {@link Primitive} or {@link GeometryPipeline} functions to prepare + * the geometry for rendering. + *
position3DHigh
- High 32 bits for encoded 64-bit position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.position3DLow
- Low 32 bits for encoded 64-bit position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.position3DHigh
- High 32 bits for encoded 64-bit 2D (Columbus view) position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.position2DLow
- Low 32 bits for encoded 64-bit 2D (Columbus view) position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.color
- RGBA color (normalized) usually from {@link GeometryInstance#color}. 32-bit floating-point. 4 components per attribute.pickColor
- RGBA color used for picking, created from {@link Context#createPickId}. 32-bit floating-point. 4 components per attribute.true
and componentDatatype
is an integer format, indicate that the components should be mapped to the range [0, 1] (unsigned) or [-1, 1] (signed) when they are accessed as floating-point for rendering.
+ * @param {Array} [options.values=undefined] The values for the attributes stored in a typed array.
+ *
+ * @exception {DeveloperError} options.componentDatatype is required.
+ * @exception {DeveloperError} options.componentsPerAttribute is required.
+ * @exception {DeveloperError} options.componentsPerAttribute must be between 1 and 4.
+ * @exception {DeveloperError} options.values is required.
+ *
+ * @example
+ * var geometry = new Geometry({
+ * attributes : {
+ * position : new GeometryAttribute({
+ * componentDatatype : ComponentDatatype.FLOAT,
+ * componentsPerAttribute : 3,
+ * values : [
+ * 0.0, 0.0, 0.0,
+ * 7500000.0, 0.0, 0.0,
+ * 0.0, 7500000.0, 0.0
+ * ]
+ * })
+ * },
+ * primitiveType : PrimitiveType.LINE_LOOP
+ * });
+ *
+ * @see Geometry
+ */
+ var GeometryAttribute = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ if (typeof options.componentDatatype === 'undefined') {
+ throw new DeveloperError('options.componentDatatype is required.');
+ }
+
+ if (typeof options.componentsPerAttribute === 'undefined') {
+ throw new DeveloperError('options.componentsPerAttribute is required.');
+ }
+
+ if (options.componentsPerAttribute < 1 || options.componentsPerAttribute > 4) {
+ throw new DeveloperError('options.componentsPerAttribute must be between 1 and 4.');
+ }
+
+ if (typeof options.values === 'undefined') {
+ throw new DeveloperError('options.values is required.');
+ }
+
+ /**
+ * The datatype of each component in the attribute, e.g., individual elements in
+ * {@see GeometryAttribute#values}.
+ *
+ * @type ComponentDatatype
+ *
+ * @default undefined
+ */
+ this.componentDatatype = options.componentDatatype;
+
+ /**
+ * A number between 1 and 4 that defines the number of components in an attributes.
+ * For example, a position attribute with x, y, and z components would have 3 as
+ * shown in the code example.
+ *
+ * @type Number
+ *
+ * @default undefined
+ *
+ * @example
+ * attribute.componentDatatype : ComponentDatatype.FLOAT,
+ * attribute.componentsPerAttribute : 3,
+ * attribute.values = new Float32Array([
+ * 0.0, 0.0, 0.0,
+ * 7500000.0, 0.0, 0.0,
+ * 0.0, 7500000.0, 0.0
+ * ]);
+ */
+ this.componentsPerAttribute = options.componentsPerAttribute;
+
+ /**
+ * When true
and componentDatatype
is an integer format,
+ * indicate that the components should be mapped to the range [0, 1] (unsigned)
+ * or [-1, 1] (signed) when they are accessed as floating-point for rendering.
+ * + * This is commonly used when storing colors using {@ ComponentDatatype.UNSIGNED_BYTE}. + *
+ * + * @type Boolean + * + * @default false + * + * @example + * attribute.componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + * attribute.componentsPerAttribute : 4, + * attribute.normalize = true; + * attribute.values = new Uint8Array([ + * Color.floatToByte(color.red) + * Color.floatToByte(color.green) + * Color.floatToByte(color.blue) + * Color.floatToByte(color.alpha) + * ]); + */ + this.normalize = defaultValue(options.normalize, false); + + /** + * The values for the attributes stored in a typed array. In the code example, + * every three elements invalues
defines one attributes since
+ * componentsPerAttribute
is 3.
+ *
+ * @type Array
+ *
+ * @default undefined
+ *
+ * @example
+ * attribute.componentDatatype : ComponentDatatype.FLOAT,
+ * attribute.componentsPerAttribute : 3,
+ * attribute.values = new Float32Array([
+ * 0.0, 0.0, 0.0,
+ * 7500000.0, 0.0, 0.0,
+ * 0.0, 7500000.0, 0.0
+ * ]);
+ */
+ this.values = options.values;
+ };
+
+ return GeometryAttribute;
+});
diff --git a/Source/Core/GeometryAttributes.js b/Source/Core/GeometryAttributes.js
new file mode 100644
index 000000000000..a2670915c22d
--- /dev/null
+++ b/Source/Core/GeometryAttributes.js
@@ -0,0 +1,82 @@
+/*global define*/
+define(['./defaultValue'], function(defaultValue) {
+ "use strict";
+
+ /**
+ * Attributes, which make up a geometry's vertices. Each property in this object corresponds to a
+ * {@link GeometryAttribute} containing the attribute's data.
+ * + * Attributes are always stored non-interleaved in a Geometry. When geometry is prepared for rendering + * with {@link Context#createVertexArrayFromGeometry}, attributes are generally written interleaved + * into the vertex buffer for better rendering performance. + *
+ * + * @alias VertexFormat + * @constructor + */ + var GeometryAttributes = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + /** + * The 3D position attribute. + *+ * 64-bit floating-point (for precision). 3 components per attribute. + *
+ * + * @type GeometryAttribute + * + * @default undefined + */ + this.position = options.position; + + /** + * The normal attribute (normalized), which is commonly used for lighting. + *+ * 32-bit floating-point. 3 components per attribute. + *
+ * + * @type GeometryAttribute + * + * @default undefined + */ + this.normal = options.normal; + + /** + * The 2D texture coordinate attribute. + *+ * 32-bit floating-point. 2 components per attribute + *
+ * + * @type GeometryAttribute + * + * @default undefined + */ + this.st = options.st; + + /** + * The binormal attribute (normalized), which is used for tangent-space effects like bump mapping. + *+ * 32-bit floating-point. 3 components per attribute. + *
+ * + * @type GeometryAttribute + * + * @default undefined + */ + this.binormal = options.binormal; + + /** + * The tangent attribute (normalized), which is used for tangent-space effects like bump mapping. + *+ * 32-bit floating-point. 3 components per attribute. + *
+ * + * @type GeometryAttribute + * + * @default undefined + */ + this.tangent = options.tangent; + }; + + return GeometryAttributes; +}); \ No newline at end of file diff --git a/Source/Core/GeometryInstance.js b/Source/Core/GeometryInstance.js new file mode 100644 index 000000000000..fc19edca2b63 --- /dev/null +++ b/Source/Core/GeometryInstance.js @@ -0,0 +1,113 @@ +/*global define*/ +define([ + './defaultValue', + './DeveloperError', + './Matrix4', + './Geometry', + './GeometryInstanceAttribute' + ], function( + defaultValue, + DeveloperError, + Matrix4, + Geometry, + GeometryInstanceAttribute) { + "use strict"; + + /** + * Geometry instancing allows one {@link Geometry} object to be positions in several + * different locations and colored uniquely. For example, one {@link BoxGeometry} can + * be instanced several times, each with a differentmodelMatrix
to change
+ * its position, rotation, and scale.
+ *
+ * @alias GeometryInstance
+ * @constructor
+ *
+ * @param {Geometry} options.geometry The geometry to instance.
+ * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The model matrix that transforms to transform the geometry from model to world coordinates.
+ * @param {Object} [options.id=undefined] A user-defined object to return when the instance is picked with {@link Context#pick} or get/set per-instance attributes with {@link Primitive#getGeometryInstanceAttributes}.
+ * @param {Object} [options.attributes] Per-instance attributes like a show or color attribute shown in the example below.
+ *
+ * @exception {DeveloperError} options.geometry is required.
+ *
+ * @example
+ * // Create geometry for a box, and two instances that refer to it.
+ * // One instance positions the box on the bottom and colored aqua.
+ * // The other instance positions the box on the top and color white.
+ * var geometry = new BoxGeometry({
+ * vertexFormat : VertexFormat.POSITION_AND_NORMAL,
+ * dimensions : new Cartesian3(1000000.0, 1000000.0, 500000.0)
+ * }),
+ * var instanceBottom = new GeometryInstance({
+ * geometry : geometry,
+ * modelMatrix : Matrix4.multiplyByTranslation(Transforms.eastNorthUpToFixedFrame(
+ * ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883))), new Cartesian3(0.0, 0.0, 1000000.0)),
+ * attributes : {
+ * color : new ColorGeometryInstanceAttribute(Color.AQUA)
+ * }
+ * id : 'bottom'
+ * });
+ * var instanceTop = new GeometryInstance({
+ * geometry : geometry,
+ * modelMatrix : Matrix4.multiplyByTranslation(Transforms.eastNorthUpToFixedFrame(
+ * ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883))), new Cartesian3(0.0, 0.0, 3000000.0)),
+ * attributes : {
+ * color : new ColorGeometryInstanceAttribute(Color.AQUA)
+ * }
+ * id : 'top'
+ * });
+ *
+ * @see Geometry
+ */
+ var GeometryInstance = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ if (typeof options.geometry === 'undefined') {
+ throw new DeveloperError('options.geometry is required.');
+ }
+
+ /**
+ * The geometry being instanced.
+ *
+ * @type Geometry
+ *
+ * @default undefined
+ */
+ this.geometry = options.geometry;
+
+ /**
+ * The 4x4 transformation matrix that transforms the geometry from model to world coordinates.
+ * When this is the identity matrix, the geometry is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
+ * Local reference frames can be used by providing a different transformation matrix, like that returned
+ * by {@link Transforms.eastNorthUpToFixedFrame}.
+ *
+ * @type Matrix4
+ *
+ * @default Matrix4.IDENTITY
+ */
+ this.modelMatrix = Matrix4.clone(defaultValue(options.modelMatrix, Matrix4.IDENTITY));
+
+ /**
+ * User-defined object returned when the instance is picked or used to get/set per-instance attributes.
+ *
+ * @type Object
+ *
+ * @default undefined
+ *
+ * @see Context#pick
+ * @see Primitive#getGeometryInstanceAttributes
+ */
+ this.id = options.id;
+
+ /**
+ * Per-instance attributes like {@link ColorGeometryInstanceAttribute} or {@link ShowGeometryInstanceAttribute}.
+ * {@link Geometry} attributes varying per vertex; these attributes are constant for the entire instance.
+ *
+ * @type Object
+ *
+ * @default undefined
+ */
+ this.attributes = defaultValue(options.attributes, {});
+ };
+
+ return GeometryInstance;
+});
diff --git a/Source/Core/GeometryInstanceAttribute.js b/Source/Core/GeometryInstanceAttribute.js
new file mode 100644
index 000000000000..3958dab12a44
--- /dev/null
+++ b/Source/Core/GeometryInstanceAttribute.js
@@ -0,0 +1,139 @@
+/*global define*/
+define([
+ './defaultValue',
+ './DeveloperError'
+ ], function(
+ defaultValue,
+ DeveloperError) {
+ "use strict";
+
+ /**
+ * Values and type information for per-instance geometry attributes.
+ *
+ * @alias GeometryInstanceAttribute
+ * @constructor
+ *
+ * @param {ComponentDatatype} [options.componentDatatype=undefined] The datatype of each component in the attribute, e.g., individual elements in values.
+ * @param {Number} [options.componentsPerAttribute=undefined] A number between 1 and 4 that defines the number of components in an attributes.
+ * @param {Boolean} [options.normalize=false] When true
and componentDatatype
is an integer format, indicate that the components should be mapped to the range [0, 1] (unsigned) or [-1, 1] (signed) when they are accessed as floating-point for rendering.
+ * @param {Array} [options.value=undefined] The value for the attribute.
+ *
+ * @exception {DeveloperError} options.componentDatatype is required.
+ * @exception {DeveloperError} options.componentsPerAttribute is required.
+ * @exception {DeveloperError} options.componentsPerAttribute must be between 1 and 4.
+ * @exception {DeveloperError} options.value is required.
+ *
+ * @example
+ * var instance = new GeometryInstance({
+ * geometry : new BoxGeometry({
+ * dimensions : new Cartesian3(1000000.0, 1000000.0, 500000.0)
+ * }),
+ * modelMatrix : Matrix4.multiplyByTranslation(Transforms.eastNorthUpToFixedFrame(
+ * ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-0.0, 0.0))), new Cartesian3(0.0, 0.0, 1000000.0)),
+ * id : 'box',
+ * attributes : {
+ * color : new GeometryInstanceAttribute({
+ * componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ * componentsPerAttribute : 4,
+ * normalize : true,
+ * value : [255, 255, 0 255]
+ * }
+ * }
+ * });
+ *
+ * @see ColorGeometryInstanceAttribute
+ * @see ShowGeometryInstanceAttribute
+ */
+ var GeometryInstanceAttribute = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ if (typeof options.componentDatatype === 'undefined') {
+ throw new DeveloperError('options.componentDatatype is required.');
+ }
+
+ if (typeof options.componentsPerAttribute === 'undefined') {
+ throw new DeveloperError('options.componentsPerAttribute is required.');
+ }
+
+ if (options.componentsPerAttribute < 1 || options.componentsPerAttribute > 4) {
+ throw new DeveloperError('options.componentsPerAttribute must be between 1 and 4.');
+ }
+
+ if (typeof options.value === 'undefined') {
+ throw new DeveloperError('options.value is required.');
+ }
+
+ /**
+ * The datatype of each component in the attribute, e.g., individual elements in
+ * {@see GeometryInstanceAttribute#value}.
+ *
+ * @type ComponentDatatype
+ *
+ * @default undefined
+ */
+ this.componentDatatype = options.componentDatatype;
+
+ /**
+ * A number between 1 and 4 that defines the number of components in an attributes.
+ * For example, a position attribute with x, y, and z components would have 3 as
+ * shown in the code example.
+ *
+ * @type Number
+ *
+ * @default undefined
+ *
+ * @example
+ * show : new GeometryInstanceAttribute({
+ * componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ * componentsPerAttribute : 1,
+ * normalize : true,
+ * value : 1.0
+ * }
+ */
+ this.componentsPerAttribute = options.componentsPerAttribute;
+
+ /**
+ * When true
and componentDatatype
is an integer format,
+ * indicate that the components should be mapped to the range [0, 1] (unsigned)
+ * or [-1, 1] (signed) when they are accessed as floating-point for rendering.
+ * + * This is commonly used when storing colors using {@ ComponentDatatype.UNSIGNED_BYTE}. + *
+ * + * @type Boolean + * + * @default false + * + * @example + * attribute.componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + * attribute.componentsPerAttribute : 4, + * attribute.normalize = true; + * attribute.value = [ + * Color.floatToByte(color.red) + * Color.floatToByte(color.green) + * Color.floatToByte(color.blue) + * Color.floatToByte(color.alpha) + * ]; + */ + this.normalize = defaultValue(options.normalize, false); + + /** + * The values for the attributes stored in a typed array. In the code example, + * every three elements invalues
defines one attributes since
+ * componentsPerAttribute
is 3.
+ *
+ * @default undefined
+ *
+ * @example
+ * show : new GeometryInstanceAttribute({
+ * componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ * componentsPerAttribute : 1,
+ * normalize : true,
+ * value : [1.0]
+ * }
+ */
+ this.value = options.value;
+ };
+
+ return GeometryInstanceAttribute;
+});
diff --git a/Source/Core/GeometryPipeline.js b/Source/Core/GeometryPipeline.js
new file mode 100644
index 000000000000..88c9b9d5c8f2
--- /dev/null
+++ b/Source/Core/GeometryPipeline.js
@@ -0,0 +1,1886 @@
+/*global define*/
+define([
+ './barycentricCoordinates',
+ './defaultValue',
+ './DeveloperError',
+ './Cartesian2',
+ './Cartesian3',
+ './Cartesian4',
+ './Cartographic',
+ './EncodedCartesian3',
+ './Intersect',
+ './IntersectionTests',
+ './Math',
+ './Matrix3',
+ './Matrix4',
+ './Plane',
+ './GeographicProjection',
+ './ComponentDatatype',
+ './IndexDatatype',
+ './PrimitiveType',
+ './Tipsify',
+ './BoundingSphere',
+ './Geometry',
+ './GeometryAttribute'
+ ], function(
+ barycentricCoordinates,
+ defaultValue,
+ DeveloperError,
+ Cartesian2,
+ Cartesian3,
+ Cartesian4,
+ Cartographic,
+ EncodedCartesian3,
+ Intersect,
+ IntersectionTests,
+ CesiumMath,
+ Matrix3,
+ Matrix4,
+ Plane,
+ GeographicProjection,
+ ComponentDatatype,
+ IndexDatatype,
+ PrimitiveType,
+ Tipsify,
+ BoundingSphere,
+ Geometry,
+ GeometryAttribute) {
+ "use strict";
+
+ /**
+ * Content pipeline functions for geometries.
+ *
+ * @exports GeometryPipeline
+ *
+ * @see Geometry
+ * @see Context#createVertexArrayFromGeometry
+ */
+ 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.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.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.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 indices
+ * and its primitiveType
is TRIANGLES
, TRIANGLE_STRIP
,
+ * TRIANGLE_FAN
, it is converted to LINES
; otherwise, the geometry is not changed.
+ * + * This is commonly used to create a wireframe geometry for visual debugging. + *
+ * + * @param {Geometry} geometry The geometry to modify. + * + * @returns {Geometry} The modifiedgeometry
argument, with its triangle indices converted to lines.
+ *
+ * @exception {DeveloperError} geometry is required.
+ * @exception {DeveloperError} geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN.
+ *
+ * @example
+ * geometry = GeometryPipeline.toWireframe(geometry);
+ */
+ GeometryPipeline.toWireframe = function(geometry) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ var indices = geometry.indices;
+ if (typeof indices !== 'undefined') {
+ switch (geometry.primitiveType) {
+ case PrimitiveType.TRIANGLES:
+ geometry.indices = trianglesToLines(indices);
+ break;
+ case PrimitiveType.TRIANGLE_STRIP:
+ geometry.indices = triangleStripToLines(indices);
+ break;
+ case PrimitiveType.TRIANGLE_FAN:
+ geometry.indices = triangleFanToLines(indices);
+ break;
+ default:
+ throw new DeveloperError('geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN.');
+ }
+
+ geometry.primitiveType = PrimitiveType.LINES;
+ }
+
+ return geometry;
+ };
+
+ /**
+ * Creates a new {@link Geometry} with LINES
representing the provided
+ * attribute (attributeName
) for the provided geometry. This is used to
+ * visualize vector attributes like normals, binormals, and tangents.
+ *
+ * @param {Geometry} geometry The Geometry
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 Geometry instance with line segments for the vector.
+ *
+ * @exception {DeveloperError} geometry is required.
+ * @exception {DeveloperError} geometry.attributes.position is required.
+ * @exception {DeveloperError} geometry.attributes must have an attribute with the same name as the attributeName parameter.
+ *
+ * @example
+ * var geometry = GeometryPipeline.createLineSegmentsForVectors(instance.geometry, 'binormal', 100000.0),
+ */
+ GeometryPipeline.createLineSegmentsForVectors = function(geometry, attributeName, length) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ if (typeof geometry.attributes.position === 'undefined') {
+ throw new DeveloperError('geometry.attributes.position is required.');
+ }
+
+ attributeName = defaultValue(attributeName, 'normal');
+
+ if (typeof geometry.attributes[attributeName] === 'undefined') {
+ throw new DeveloperError('geometry.attributes must have an attribute with the same name as the attributeName parameter, ' + attributeName + '.');
+ }
+
+ length = 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 (typeof bs !== 'undefined') {
+ newBoundingSphere = new BoundingSphere(bs.center, bs.radius + length);
+ }
+
+ return new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : newPositions
+ })
+ },
+ primitiveType : PrimitiveType.LINES,
+ boundingSphere : newBoundingSphere
+ });
+ };
+
+ /**
+ * Creates an object that maps attribute names to unique 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.
+ *
+ * @exception {DeveloperError} geometry is required.
+ *
+ * @example
+ * var attributeIndices = GeometryPipeline.createAttributeIndices(geometry);
+ * // Example output
+ * // {
+ * // 'position' : 0,
+ * // 'normal' : 1
+ * // }
+ *
+ * @see Context#createVertexArrayFromGeometry
+ * @see ShaderCache
+ */
+ GeometryPipeline.createAttributeIndices = function(geometry) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ // 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',
+ 'binormal',
+ 'tangent'
+ ];
+
+ 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 (typeof attributes[semantic] !== 'undefined') {
+ indices[semantic] = j++;
+ }
+ }
+
+ // Locations for custom attributes
+ for (var name in attributes) {
+ if (attributes.hasOwnProperty(name) && (typeof indices[name] === 'undefined')) {
+ indices[name] = j++;
+ }
+ }
+
+ return indices;
+ };
+
+ /**
+ * Reorders a geometry's attributes and indices
to achieve better performance from the GPU's pre-vertex-shader cache.
+ *
+ * @param {Geometry} geometry The geometry to modify.
+ *
+ * @returns {Geometry} The modified geometry
argument, with its attributes and indices reordered for the GPU's pre-vertex-shader cache.
+ *
+ * @exception {DeveloperError} geometry is required.
+ * @exception {DeveloperError} Each attribute array in geometry.attributes must have the same number of attributes.
+ *
+ * @example
+ * geometry = GeometryPipeline.reorderForPreVertexCache(geometry);
+ *
+ * @see GeometryPipeline.reorderForPostVertexCache
+ */
+ GeometryPipeline.reorderForPreVertexCache = function(geometry) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ var numVertices = Geometry.computeNumberOfVertices(geometry);
+
+ var indices = geometry.indices;
+ if (typeof indices !== 'undefined') {
+ 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.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) &&
+ typeof attributes[property] !== 'undefined' &&
+ typeof attributes[property].values !== 'undefined') {
+
+ var attribute = attributes[property];
+ var elementsIn = attribute.values;
+ var intoElementsIn = 0;
+ var numComponents = attribute.componentsPerAttribute;
+ var elementsOut = attribute.componentDatatype.createTypedArray(elementsIn.length);
+ while (intoElementsIn < numVertices) {
+ var temp = indexCrossReferenceOldToNew[intoElementsIn];
+ for (i = 0; i < numComponents; i++) {
+ elementsOut[numComponents * temp + i] = elementsIn[numComponents * intoElementsIn + i];
+ }
+ ++intoElementsIn;
+ }
+ attribute.values = elementsOut;
+ }
+ }
+ }
+
+ return geometry;
+ };
+
+ /**
+ * Reorders a geometry's indices
to achieve better performance from the GPU's
+ * post vertex-shader cache by using the Tipsify algorithm. If the geometry primitiveType
+ * is not TRIANGLES
or the geometry does not have an indices
, 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 geometry
argument, with its indices reordered for the post-vertex-shader cache.
+ *
+ * @exception {DeveloperError} geometry is required.
+ * @exception {DeveloperError} cacheCapacity must be greater than two.
+ *
+ * @example
+ * geometry = GeometryPipeline.reorderForPostVertexCache(geometry);
+ *
+ * @see GeometryPipeline.reorderForPreVertexCache
+ * @see
+ * Fast Triangle Reordering for Vertex Locality and Reduced Overdraw
+ * by Sander, Nehab, and Barczak
+ */
+ GeometryPipeline.reorderForPostVertexCache = function(geometry, cacheCapacity) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ var indices = geometry.indices;
+ if ((geometry.primitiveType === PrimitiveType.TRIANGLES) && (typeof indices !== 'undefined')) {
+ 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) &&
+ typeof attributes[attribute] !== 'undefined' &&
+ typeof attributes[attribute].values !== 'undefined') {
+
+ var attr = attributes[attribute];
+ newAttributes[attribute] = new 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) &&
+ typeof sourceAttributes[attribute] !== 'undefined' &&
+ typeof sourceAttributes[attribute].values !== 'undefined') {
+
+ 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
+ * indices
fit into unsigned shorts. This is used to meet the WebGL requirements
+ * when {@link Context#getElementIndexUint} is false
.
+ *
+ * If the geometry does not have any indices
, this function has no effect.
+ *
+ *
+ * @param {Geometry} geometry The geometry to be split into multiple geometries.
+ *
+ * @returns {Array} An array of geometries, each with indices that fit into unsigned shorts.
+ *
+ * @exception {DeveloperError} geometry is required.
+ * @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 = GeometryPipeline.fitToUnsignedShortIndices(geometry);
+ */
+ GeometryPipeline.fitToUnsignedShortIndices = function(geometry) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ if ((typeof geometry.indices !== 'undefined') &&
+ ((geometry.primitiveType !== PrimitiveType.TRIANGLES) &&
+ (geometry.primitiveType !== PrimitiveType.LINES) &&
+ (geometry.primitiveType !== PrimitiveType.POINTS))) {
+ throw new DeveloperError('geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS.');
+ }
+
+ 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 = Geometry.computeNumberOfVertices(geometry);
+ if (typeof geometry.indices !== 'undefined' && (numberOfVertices > 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 === PrimitiveType.TRIANGLES) {
+ indicesPerPrimitive = 3;
+ } else if (geometry.primitiveType === PrimitiveType.LINES) {
+ indicesPerPrimitive = 2;
+ } else if (geometry.primitiveType === 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 (typeof i === 'undefined') {
+ i = currentIndex++;
+ oldToNewIndex[x] = i;
+ copyVertex(newAttributes, geometry.attributes, x);
+ }
+ newIndices.push(i);
+ }
+
+ if (currentIndex + indicesPerPrimitive > CesiumMath.SIXTY_FOUR_KILOBYTES) {
+ geometries.push(new Geometry({
+ attributes : newAttributes,
+ indices : newIndices,
+ primitiveType : geometry.primitiveType,
+ boundingSphere : geometry.boundingSphere
+ }));
+
+ // Reset for next vertex-array
+ oldToNewIndex = [];
+ newIndices = [];
+ currentIndex = 0;
+ newAttributes = copyAttributesDescriptions(geometry.attributes);
+ }
+ }
+
+ if (newIndices.length !== 0) {
+ geometries.push(new Geometry({
+ attributes : newAttributes,
+ indices : newIndices,
+ primitiveType : geometry.primitiveType,
+ boundingSphere : geometry.boundingSphere
+ }));
+ }
+ } else {
+ // No need to split into multiple geometries
+ geometries.push(geometry);
+ }
+
+ return geometries;
+ };
+
+ var scratchProjectTo2DCartesian3 = new Cartesian3();
+ var scratchProjectTo2DCartographic = new Cartographic();
+
+ /**
+ * Projects a geometry's 3D position
attribute to 2D, replacing the position
+ * attribute with separate position3D
and position2D
attributes.
+ *
+ * If the geometry does not have a position
, this function has no effect.
+ *
+ *
+ * @param {Geometry} geometry The geometry to modify.
+ * @param {Object} [projection=new GeographicProjection()] The projection to use.
+ *
+ * @returns {Geometry} The modified geometry
argument with position3D
and position2D
attributes.
+ *
+ * @exception {DeveloperError} geometry is required.
+ *
+ * @example
+ * geometry = GeometryPipeline.projectTo2D(geometry);
+ */
+ GeometryPipeline.projectTo2D = function(geometry, projection) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ if (typeof geometry.attributes.position !== 'undefined') {
+ projection = (typeof projection !== 'undefined') ? projection : new GeographicProjection();
+ var ellipsoid = projection.getEllipsoid();
+
+ // Project original positions to 2D.
+ var positions3D = geometry.attributes.position.values;
+ var projectedPositions = new Float64Array(positions3D.length);
+ var index = 0;
+
+ for ( var i = 0; i < positions3D.length; i += 3) {
+ var position = Cartesian3.fromArray(positions3D, i, scratchProjectTo2DCartesian3);
+ var lonLat = ellipsoid.cartesianToCartographic(position, scratchProjectTo2DCartographic);
+ var projectedLonLat = projection.project(lonLat, scratchProjectTo2DCartesian3);
+
+ projectedPositions[index++] = projectedLonLat.x;
+ projectedPositions[index++] = projectedLonLat.y;
+ projectedPositions[index++] = projectedLonLat.z;
+ }
+
+ // Rename original positions to WGS84 Positions.
+ geometry.attributes.position3D = geometry.attributes.position;
+
+ // Replace original positions with 2D projected positions
+ geometry.attributes.position2D = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : projectedPositions
+ });
+ delete geometry.attributes.position;
+ }
+
+ return geometry;
+ };
+
+ var encodedResult = {
+ high : 0.0,
+ low : 0.0
+ };
+
+ /**
+ * Encodes floating-point geometry attribute values as two separate attributes to improve
+ * rendering precision using the same encoding as {@link EncodedCartesian3}.
+ *
+ * This is commonly used to create high-precision position vertex attributes.
+ *
+ *
+ * @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 geometry
argument, with its encoded attribute.
+ *
+ * @exception {DeveloperError} geometry is required.
+ * @exception {DeveloperError} attributeName is required.
+ * @exception {DeveloperError} attributeHighName is required.
+ * @exception {DeveloperError} attributeLowName is required.
+ * @exception {DeveloperError} geometry must have attribute matching the attributeName argument.
+ * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE.
+ *
+ * @example
+ * geometry = GeometryPipeline.encodeAttribute(geometry, 'position3D', 'position3DHigh', 'position3DLow');
+ *
+ * @see EncodedCartesian3
+ */
+ GeometryPipeline.encodeAttribute = function(geometry, attributeName, attributeHighName, attributeLowName) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ if (typeof attributeName === 'undefined') {
+ throw new DeveloperError('attributeName is required.');
+ }
+
+ if (typeof attributeHighName === 'undefined') {
+ throw new DeveloperError('attributeHighName is required.');
+ }
+
+ if (typeof attributeLowName === 'undefined') {
+ throw new DeveloperError('attributeLowName is required.');
+ }
+
+ var attribute = geometry.attributes[attributeName];
+
+ if (typeof attribute === 'undefined') {
+ throw new DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.');
+ }
+
+ if (attribute.componentDatatype !== ComponentDatatype.DOUBLE) {
+ throw new DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.');
+ }
+
+ 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.encode(values[i], encodedResult);
+ highValues[i] = encodedResult.high;
+ lowValues[i] = encodedResult.low;
+ }
+
+ var componentsPerAttribute = attribute.componentsPerAttribute;
+
+ geometry.attributes[attributeHighName] = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : componentsPerAttribute,
+ values : highValues
+ });
+ geometry.attributes[attributeLowName] = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : componentsPerAttribute,
+ values : lowValues
+ });
+ delete geometry.attributes[attributeName];
+
+ return geometry;
+ };
+
+ var scratch = new Cartesian3();
+
+ function transformPoint(matrix, attribute) {
+ if (typeof attribute !== 'undefined') {
+ var values = attribute.values;
+ var length = values.length;
+ for (var i = 0; i < length; i += 3) {
+ Cartesian3.fromArray(values, i, scratch);
+ Matrix4.multiplyByPoint(matrix, scratch, scratch);
+ values[i] = scratch.x;
+ values[i + 1] = scratch.y;
+ values[i + 2] = scratch.z;
+ }
+ }
+ }
+
+ function transformVector(matrix, attribute) {
+ if (typeof attribute !== 'undefined') {
+ var values = attribute.values;
+ var length = values.length;
+ for (var i = 0; i < length; i += 3) {
+ Cartesian3.fromArray(values, i, scratch);
+ Matrix3.multiplyByVector(matrix, scratch, scratch);
+ values[i] = scratch.x;
+ values[i + 1] = scratch.y;
+ values[i + 2] = scratch.z;
+ }
+ }
+ }
+
+ var inverseTranspose = new Matrix4();
+ var normalMatrix = new Matrix3();
+
+ /**
+ * Transforms a geometry instance to world coordinates. This is used as a prerequisite
+ * to batch together several instances with {@link GeometryPipeline.combine}. This changes
+ * the instance's modelMatrix
to {@see Matrix4.IDENTITY} and transforms the
+ * following attributes if they are present: position
, normal
,
+ * binormal
, and tangent
.
+ *
+ * @param {GeometryInstance} instance The geometry instance to modify.
+ *
+ * @returns {GeometryInstance} The modified instance
argument, with its attributes transforms to world coordinates.
+ *
+ * @exception {DeveloperError} instance is required.
+ *
+ * @example
+ * for (var i = 0; i < instances.length; ++i) {
+ * GeometryPipeline.transformToWorldCoordinates(instances[i]);
+ * }
+ * var geometry = GeometryPipeline.combine(instances);
+ *
+ * @see GeometryPipeline.combine
+ */
+ GeometryPipeline.transformToWorldCoordinates = function(instance) {
+ if (typeof instance === 'undefined') {
+ throw new DeveloperError('instance is required.');
+ }
+
+ var modelMatrix = instance.modelMatrix;
+
+ if (modelMatrix.equals(Matrix4.IDENTITY)) {
+ // Already in world coordinates
+ return instance;
+ }
+
+ var attributes = instance.geometry.attributes;
+
+ // Transform attributes in known vertex formats
+ transformPoint(modelMatrix, attributes.position);
+
+ if ((typeof attributes.normal !== 'undefined') ||
+ (typeof attributes.binormal !== 'undefined') ||
+ (typeof attributes.tangent !== 'undefined')) {
+
+ Matrix4.inverse(modelMatrix, inverseTranspose);
+ Matrix4.transpose(inverseTranspose, inverseTranspose);
+ Matrix4.getRotation(inverseTranspose, normalMatrix);
+
+ transformVector(normalMatrix, attributes.normal);
+ transformVector(normalMatrix, attributes.binormal);
+ transformVector(normalMatrix, attributes.tangent);
+ }
+
+ var boundingSphere = instance.geometry.boundingSphere;
+
+ if (typeof boundingSphere !== 'undefined') {
+ Matrix4.multiplyByPoint(modelMatrix, boundingSphere.center, boundingSphere.center);
+ boundingSphere.center = Cartesian3.fromCartesian4(boundingSphere.center);
+ }
+
+ instance.modelMatrix = Matrix4.IDENTITY.clone();
+
+ return instance;
+ };
+
+ function findAttributesInAllGeometries(instances) {
+ var length = instances.length;
+
+ var attributesInAllGeometries = {};
+
+ var attributes0 = instances[0].geometry.attributes;
+ var name;
+
+ for (name in attributes0) {
+ if (attributes0.hasOwnProperty(name) &&
+ typeof attributes0[name] !== 'undefined' &&
+ typeof attributes0[name].values !== 'undefined') {
+
+ var attribute = attributes0[name];
+ var numberOfComponents = attribute.values.length;
+ var inAllGeometries = true;
+
+ // Does this same attribute exist in all geometries?
+ for (var i = 1; i < length; ++i) {
+ var otherAttribute = instances[i].geometry.attributes[name];
+
+ if ((typeof otherAttribute === 'undefined') ||
+ (attribute.componentDatatype !== otherAttribute.componentDatatype) ||
+ (attribute.componentsPerAttribute !== otherAttribute.componentsPerAttribute) ||
+ (attribute.normalize !== otherAttribute.normalize)) {
+
+ inAllGeometries = false;
+ break;
+ }
+
+ numberOfComponents += otherAttribute.values.length;
+ }
+
+ if (inAllGeometries) {
+ attributesInAllGeometries[name] = new GeometryAttribute({
+ componentDatatype : attribute.componentDatatype,
+ componentsPerAttribute : attribute.componentsPerAttribute,
+ normalize : attribute.normalize,
+ values : attribute.componentDatatype.createTypedArray(numberOfComponents)
+ });
+ }
+ }
+ }
+
+ return attributesInAllGeometries;
+ }
+
+ /**
+ * Combines geometry from several {@link GeometryInstance} objects into one geometry.
+ * This concatenates the attributes, concatenates and adjusts the indices, and creates
+ * a bounding sphere encompassing all instances.
+ *
+ * If the instances do not have the same attributes, a subset of attributes common
+ * to all instances is used, and the others are ignored.
+ *
+ *
+ * This is used by {@link Primitive} to efficiently render a large amount of static data.
+ *
+ *
+ * @param {Array} [instances] The array of {@link GeometryInstance} objects whose geometry will be combined.
+ *
+ * @returns {Geometry} A single geometry created from the provided geometry instances.
+ *
+ * @exception {DeveloperError} instances is required and must have length greater than zero.
+ * @exception {DeveloperError} All instances must have the same modelMatrix.
+ * @exception {DeveloperError} All instance geometries must have an indices or not have one.
+ * @exception {DeveloperError} All instance geometries must have the same primitiveType.
+ *
+ * @example
+ * for (var i = 0; i < instances.length; ++i) {
+ * GeometryPipeline.transformToWorldCoordinates(instances[i]);
+ * }
+ * var geometry = GeometryPipeline.combine(instances);
+ *
+ * @see GeometryPipeline.transformToWorldCoordinates
+ */
+ GeometryPipeline.combine = function(instances) {
+ if ((typeof instances === 'undefined') || (instances.length < 1)) {
+ throw new DeveloperError('instances is required and must have length greater than zero.');
+ }
+
+ var length = instances.length;
+
+ var name;
+ var i;
+ var j;
+ var k;
+
+ var m = instances[0].modelMatrix;
+ var haveIndices = (typeof instances[0].geometry.indices !== 'undefined');
+ var primitiveType = instances[0].geometry.primitiveType;
+
+ for (i = 1; i < length; ++i) {
+ if (!Matrix4.equals(instances[i].modelMatrix, m)) {
+ throw new DeveloperError('All instances must have the same modelMatrix.');
+ }
+
+ if ((typeof instances[i].geometry.indices !== 'undefined') !== haveIndices) {
+ throw new DeveloperError('All instance geometries must have an indices or not have one.');
+ }
+
+ if (instances[i].geometry.primitiveType !== primitiveType) {
+ throw new DeveloperError('All instance geometries must have the same primitiveType.');
+ }
+ }
+
+ // Find subset of attributes in all geometries
+ var attributes = findAttributesInAllGeometries(instances);
+ var values;
+ var sourceValues;
+ var sourceValuesLength;
+
+ // Combine attributes from each geometry into a single typed array
+ for (name in attributes) {
+ if (attributes.hasOwnProperty(name)) {
+ values = attributes[name].values;
+
+ k = 0;
+ for (i = 0; i < length; ++i) {
+ sourceValues = instances[i].geometry.attributes[name].values;
+ sourceValuesLength = sourceValues.length;
+
+ for (j = 0; j < sourceValuesLength; ++j) {
+ values[k++] = sourceValues[j];
+ }
+ }
+ }
+ }
+
+ // Combine index lists
+ var indices;
+
+ if (haveIndices) {
+ var numberOfIndices = 0;
+ for (i = 0; i < length; ++i) {
+ numberOfIndices += instances[i].geometry.indices.length;
+ }
+
+ var numberOfVertices = Geometry.computeNumberOfVertices(new Geometry({
+ attributes : attributes,
+ primitiveType : PrimitiveType.POINTS
+ }));
+ var destIndices = IndexDatatype.createTypedArray(numberOfVertices, numberOfIndices);
+
+ var destOffset = 0;
+ var offset = 0;
+
+ for (i = 0; i < length; ++i) {
+ var sourceIndices = instances[i].geometry.indices;
+ var sourceIndicesLen = sourceIndices.length;
+
+ for (k = 0; k < sourceIndicesLen; ++k) {
+ destIndices[destOffset++] = offset + sourceIndices[k];
+ }
+
+ offset += Geometry.computeNumberOfVertices(instances[i].geometry);
+ }
+
+ indices = destIndices;
+ }
+
+ // Create bounding sphere that includes all instances
+ var boundingSphere;
+
+ for (i = 0; i < length; ++i) {
+ var bs = instances[i].geometry.boundingSphere;
+ if (typeof bs === 'undefined') {
+ // If any geometries have an undefined bounding sphere, then so does the combined geometry
+ boundingSphere = undefined;
+ break;
+ }
+
+ if (typeof boundingSphere === 'undefined') {
+ boundingSphere = bs.clone();
+ } else {
+ BoundingSphere.union(boundingSphere, bs, boundingSphere);
+ }
+ }
+
+ return new Geometry({
+ attributes : attributes,
+ indices : indices,
+ primitiveType : primitiveType,
+ boundingSphere : boundingSphere
+ });
+ };
+
+ var normal = new Cartesian3();
+ var v0 = new Cartesian3();
+ var v1 = new Cartesian3();
+ var v2 = new Cartesian3();
+
+ /**
+ * Computes per-vertex normals for a geometry containing TRIANGLES
by averaging the normals of
+ * all triangles incident to the vertex. The result is a new normal
attribute added to the geometry.
+ * This assumes a counter-clockwise winding order.
+ *
+ * @param {Geometry} geometry The geometry to modify.
+ *
+ * @returns {Geometry} The modified geometry
argument with the computed normal
attribute.
+ *
+ * @exception {DeveloperError} geometry is required.
+ * @exception {DeveloperError} geometry.attributes.position.values is required.
+ * @exception {DeveloperError} geometry.indices is required.
+ * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3.
+ * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}.
+ *
+ * @example
+ * GeometryPipeline.computeNormal(geometry);
+ */
+ GeometryPipeline.computeNormal = function(geometry) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ var attributes = geometry.attributes;
+ var indices = geometry.indices;
+
+ if (typeof attributes.position === 'undefined' || typeof attributes.position.values === 'undefined') {
+ throw new DeveloperError('geometry.attributes.position.values is required.');
+ }
+
+ if (typeof indices === 'undefined') {
+ throw new DeveloperError('geometry.indices is required.');
+ }
+
+ if (indices.length < 2 || indices.length % 3 !== 0) {
+ throw new DeveloperError('geometry.indices length must be greater than 0 and be a multiple of 3.');
+ }
+
+ if (geometry.primitiveType !== PrimitiveType.TRIANGLES) {
+ throw new DeveloperError('geometry.primitiveType must be PrimitiveType.TRIANGLES.');
+ }
+
+ var vertices = geometry.attributes.position.values;
+ var numVertices = geometry.attributes.position.values.length / 3;
+ var numIndices = indices.length;
+ var normalsPerVertex = new Array(numVertices);
+ var normalsPerTriangle = new Array(numIndices / 3);
+ var normalIndices = new Array(numIndices);
+
+ for ( var i = 0; i < numVertices; i++) {
+ normalsPerVertex[i] = {
+ indexOffset : 0,
+ count : 0,
+ currentCount : 0
+ };
+ }
+
+ var j = 0;
+ for (i = 0; i < numIndices; i += 3) {
+ var i0 = indices[i];
+ var i1 = indices[i + 1];
+ var i2 = indices[i + 2];
+ var i03 = i0 * 3;
+ var i13 = i1 * 3;
+ var i23 = i2 * 3;
+
+ v0.x = vertices[i03];
+ v0.y = vertices[i03 + 1];
+ v0.z = vertices[i03 + 2];
+ v1.x = vertices[i13];
+ v1.y = vertices[i13 + 1];
+ v1.z = vertices[i13 + 2];
+ v2.x = vertices[i23];
+ v2.y = vertices[i23 + 1];
+ v2.z = vertices[i23 + 2];
+
+ normalsPerVertex[i0].count++;
+ normalsPerVertex[i1].count++;
+ normalsPerVertex[i2].count++;
+
+ v1.subtract(v0, v1);
+ v2.subtract(v0, v2);
+ normalsPerTriangle[j] = v1.cross(v2);
+ j++;
+ }
+
+ var indexOffset = 0;
+ for (i = 0; i < numVertices; i++) {
+ normalsPerVertex[i].indexOffset += indexOffset;
+ indexOffset += normalsPerVertex[i].count;
+ }
+
+ j = 0;
+ var vertexNormalData;
+ for (i = 0; i < numIndices; i += 3) {
+ vertexNormalData = normalsPerVertex[indices[i]];
+ var index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
+ normalIndices[index] = j;
+ vertexNormalData.currentCount++;
+
+ vertexNormalData = normalsPerVertex[indices[i + 1]];
+ index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
+ normalIndices[index] = j;
+ vertexNormalData.currentCount++;
+
+ vertexNormalData = normalsPerVertex[indices[i + 2]];
+ index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
+ normalIndices[index] = j;
+ vertexNormalData.currentCount++;
+
+ j++;
+ }
+
+ var normalValues = new Float32Array(numVertices * 3);
+ for (i = 0; i < numVertices; i++) {
+ var i3 = i * 3;
+ vertexNormalData = normalsPerVertex[i];
+ if (vertexNormalData.count > 0) {
+ Cartesian3.ZERO.clone(normal);
+ for (j = 0; j < vertexNormalData.count; j++) {
+ normal.add(normalsPerTriangle[normalIndices[vertexNormalData.indexOffset + j]], normal);
+ }
+ normal.normalize(normal);
+ normalValues[i3] = normal.x;
+ normalValues[i3 + 1] = normal.y;
+ normalValues[i3 + 2] = normal.z;
+ } else {
+ normalValues[i3] = 0.0;
+ normalValues[i3 + 1] = 0.0;
+ normalValues[i3 + 2] = 1.0;
+ }
+ }
+
+ geometry.attributes.normal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : normalValues
+ });
+
+ return geometry;
+ };
+
+ var normalScratch = new Cartesian3();
+ var normalScale = new Cartesian3();
+ var tScratch = new Cartesian3();
+
+ /**
+ * Computes per-vertex binormals and tangents for a geometry containing TRIANGLES
.
+ * The result is new binormal
and tangent
attributes added to the geometry.
+ * This assumes a counter-clockwise winding order.
+ *
+ * Based on Computing Tangent Space Basis Vectors
+ * for an Arbitrary Mesh by Eric Lengyel.
+ *
+ *
+ * @param {Geometry} geometry The geometry to modify.
+ *
+ * @returns {Geometry} The modified geometry
argument with the computed binormal
and tangent
attributes.
+ *
+ * @exception {DeveloperError} geometry is required.
+ * @exception {DeveloperError} geometry.attributes.position.values is required.
+ * @exception {DeveloperError} geometry.attributes.normal.values is required.
+ * @exception {DeveloperError} geometry.attributes.st.values is required.
+ * @exception {DeveloperError} geometry.indices is required.
+ * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3.
+ * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}.
+ *
+ * @example
+ * GeometryPipeline.computeBinormalAndTangent(geometry);
+ */
+ GeometryPipeline.computeBinormalAndTangent = function(geometry) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ var attributes = geometry.attributes;
+ var indices = geometry.indices;
+
+ if (typeof attributes.position === 'undefined' || typeof attributes.position.values === 'undefined') {
+ throw new DeveloperError('geometry.attributes.position.values is required.');
+ }
+
+ if (typeof attributes.normal === 'undefined' || typeof attributes.normal.values === 'undefined') {
+ throw new DeveloperError('geometry.attributes.normal.values is required.');
+ }
+
+ if (typeof attributes.st === 'undefined' || typeof attributes.st.values === 'undefined') {
+ throw new DeveloperError('geometry.attributes.st.values is required.');
+ }
+
+ if (typeof indices === 'undefined') {
+ throw new DeveloperError('geometry.indices is required.');
+ }
+
+ if (indices.length < 2 || indices.length % 3 !== 0) {
+ throw new DeveloperError('geometry.indices length must be greater than 0 and be a multiple of 3.');
+ }
+
+ if (geometry.primitiveType !== PrimitiveType.TRIANGLES) {
+ throw new DeveloperError('geometry.primitiveType must be PrimitiveType.TRIANGLES.');
+ }
+
+ var vertices = geometry.attributes.position.values;
+ var normals = geometry.attributes.normal.values;
+ var st = geometry.attributes.st.values;
+
+ var numVertices = geometry.attributes.position.values.length / 3;
+ var numIndices = indices.length;
+ var tan1 = new Array(numVertices * 3);
+
+ for ( var i = 0; i < tan1.length; i++) {
+ tan1[i] = 0;
+ }
+
+ var i03;
+ var i13;
+ var i23;
+ for (i = 0; i < numIndices; i += 3) {
+ var i0 = indices[i];
+ var i1 = indices[i + 1];
+ var i2 = indices[i + 2];
+ i03 = i0 * 3;
+ i13 = i1 * 3;
+ i23 = i2 * 3;
+ var i02 = i0 * 2;
+ var i12 = i1 * 2;
+ var i22 = i2 * 2;
+
+ var ux = vertices[i03];
+ var uy = vertices[i03 + 1];
+ var uz = vertices[i03 + 2];
+
+ var wx = st[i02];
+ var wy = st[i02 + 1];
+ var t1 = st[i12 + 1] - wy;
+ var t2 = st[i22 + 1] - wy;
+
+ var r = 1.0 / ((st[i12] - wx) * t2 - (st[i22] - wx) * t1);
+ var sdirx = (t2 * (vertices[i13] - ux) - t1 * (vertices[i23] - ux)) * r;
+ var sdiry = (t2 * (vertices[i13 + 1] - uy) - t1 * (vertices[i23 + 1] - uy)) * r;
+ var sdirz = (t2 * (vertices[i13 + 2] - uz) - t1 * (vertices[i23 + 2] - uz)) * r;
+
+ tan1[i03] += sdirx;
+ tan1[i03 + 1] += sdiry;
+ tan1[i03 + 2] += sdirz;
+
+ tan1[i13] += sdirx;
+ tan1[i13 + 1] += sdiry;
+ tan1[i13 + 2] += sdirz;
+
+ tan1[i23] += sdirx;
+ tan1[i23 + 1] += sdiry;
+ tan1[i23 + 2] += sdirz;
+ }
+
+ var binormalValues = new Float32Array(numVertices * 3);
+ var tangentValues = new Float32Array(numVertices * 3);
+
+ for (i = 0; i < numVertices; i++) {
+ i03 = i * 3;
+ i13 = i03 + 1;
+ i23 = i03 + 2;
+
+ var n = Cartesian3.fromArray(normals, i03, normalScratch);
+ var t = Cartesian3.fromArray(tan1, i03, tScratch);
+ var scalar = n.dot(t);
+ n.multiplyByScalar(scalar, normalScale);
+ t.subtract(normalScale, t).normalize(t);
+
+ tangentValues[i03] = t.x;
+ tangentValues[i13] = t.y;
+ tangentValues[i23] = t.z;
+
+ n.cross(t, t).normalize(t);
+
+ binormalValues[i03] = t.x;
+ binormalValues[i13] = t.y;
+ binormalValues[i23] = t.z;
+ }
+
+ geometry.attributes.tangent = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : tangentValues
+ });
+
+ geometry.attributes.binormal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : binormalValues
+ });
+
+ return geometry;
+ };
+
+ function indexTriangles(geometry) {
+ if (typeof geometry.indices !== 'undefined') {
+ return geometry;
+ }
+
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+ if (numberOfVertices < 3) {
+ throw new DeveloperError('The number of vertices must be at least three.');
+ }
+
+ if (numberOfVertices % 3 !== 0) {
+ throw new DeveloperError('The number of vertices must be a multiple of three.');
+ }
+
+ var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices);
+ for (var i = 0; i < numberOfVertices; ++i) {
+ indices[i] = i;
+ }
+
+ geometry.indices = indices;
+ return geometry;
+ }
+
+ function indexTriangleFan(geometry) {
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+ if (numberOfVertices < 3) {
+ throw new DeveloperError('The number of vertices must be at least three.');
+ }
+
+ var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 2) * 3);
+ indices[0] = 1;
+ indices[1] = 0;
+ indices[2] = 2;
+
+ var indicesIndex = 3;
+ for (var i = 3; i < numberOfVertices; ++i) {
+ indices[indicesIndex++] = i - 1;
+ indices[indicesIndex++] = 0;
+ indices[indicesIndex++] = i;
+ }
+
+ geometry.indices = indices;
+ geometry.primitiveType = PrimitiveType.TRIANGLES;
+ return geometry;
+ }
+
+ function indexTriangleStrip(geometry) {
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+ if (numberOfVertices < 3) {
+ throw new DeveloperError('The number of vertices must be at least 3.');
+ }
+
+ var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 2) * 3);
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+
+ if (numberOfVertices > 3) {
+ indices[3] = 0;
+ indices[4] = 2;
+ indices[5] = 3;
+ }
+
+ var indicesIndex = 6;
+ for (var i = 3; i < numberOfVertices - 1; i += 2) {
+ indices[indicesIndex++] = i;
+ indices[indicesIndex++] = i - 1;
+ indices[indicesIndex++] = i + 1;
+
+ if (i + 2 < numberOfVertices) {
+ indices[indicesIndex++] = i;
+ indices[indicesIndex++] = i + 1;
+ indices[indicesIndex++] = i + 2;
+ }
+ }
+
+ geometry.indices = indices;
+ geometry.primitiveType = PrimitiveType.TRIANGLES;
+ return geometry;
+ }
+
+ function indexLines(geometry) {
+ if (typeof geometry.indices !== 'undefined') {
+ return geometry;
+ }
+
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+ if (numberOfVertices < 2) {
+ throw new DeveloperError('The number of vertices must be at least two.');
+ }
+
+ if (numberOfVertices % 2 !== 0) {
+ throw new DeveloperError('The number of vertices must be a multiple of 2.');
+ }
+
+ var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices);
+ for (var i = 0; i < numberOfVertices; ++i) {
+ indices[i] = i;
+ }
+
+ geometry.indices = indices;
+ return geometry;
+ }
+
+ function indexLineStrip(geometry) {
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+ if (numberOfVertices < 2) {
+ throw new DeveloperError('The number of vertices must be at least two.');
+ }
+ var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 1) * 2);
+
+ indices[0] = 0;
+ indices[1] = 1;
+
+ var indicesIndex = 2;
+ for (var i = 2; i < numberOfVertices; ++i) {
+ indices[indicesIndex++] = i - 1;
+ indices[indicesIndex++] = i;
+ }
+
+ geometry.indices = indices;
+ geometry.primitiveType = PrimitiveType.LINES;
+ return geometry;
+ }
+
+ function indexLineLoop(geometry) {
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+ if (numberOfVertices < 2) {
+ throw new DeveloperError('The number of vertices must be at least two.');
+ }
+
+ var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices * 2);
+
+ indices[0] = 0;
+ indices[1] = 1;
+
+ var indicesIndex = 2;
+ for (var i = 2; i < numberOfVertices; ++i) {
+ indices[indicesIndex++] = i - 1;
+ indices[indicesIndex++] = i;
+ }
+
+ indices[indicesIndex++] = numberOfVertices - 1;
+ indices[indicesIndex] = 0;
+
+ geometry.indices = indices;
+ geometry.primitiveType = PrimitiveType.LINES;
+ return geometry;
+ }
+
+ function indexPrimitive(geometry) {
+ switch (geometry.primitiveType) {
+ case PrimitiveType.TRIANGLE_FAN:
+ return indexTriangleFan(geometry);
+ case PrimitiveType.TRIANGLE_STRIP:
+ return indexTriangleStrip(geometry);
+ case PrimitiveType.TRIANGLES:
+ return indexTriangles(geometry);
+ case PrimitiveType.LINE_STRIP:
+ return indexLineStrip(geometry);
+ case PrimitiveType.LINE_LOOP:
+ return indexLineLoop(geometry);
+ case PrimitiveType.LINES:
+ return indexLines(geometry);
+ }
+
+ return geometry;
+ }
+
+ function offsetPointFromXZPlane(p, isBehind) {
+ if (Math.abs(p.y) < CesiumMath.EPSILON11){
+ if (isBehind) {
+ p.y = -CesiumMath.EPSILON11;
+ } else {
+ p.y = CesiumMath.EPSILON11;
+ }
+ }
+ }
+
+ var c3 = new Cartesian3();
+ function getXZIntersectionOffsetPoints(p, p1, u1, v1) {
+ p.add(p1.subtract(p, c3).multiplyByScalar(p.y/(p.y-p1.y), c3), u1);
+ Cartesian3.clone(u1, v1);
+ offsetPointFromXZPlane(u1, true);
+ offsetPointFromXZPlane(v1, false);
+ }
+
+ var u1 = new Cartesian3();
+ var u2 = new Cartesian3();
+ var q1 = new Cartesian3();
+ var q2 = new Cartesian3();
+
+ var splitTriangleResult = {
+ positions : new Array(7),
+ indices : new Array(3 * 3)
+ };
+
+ function splitTriangle(p0, p1, p2) {
+ // In WGS84 coordinates, for a triangle approximately on the
+ // ellipsoid to cross the IDL, first it needs to be on the
+ // negative side of the plane x = 0.
+ if ((p0.x >= 0.0) || (p1.x >= 0.0) || (p2.x >= 0.0)) {
+ return undefined;
+ }
+
+ var p0Behind = p0.y < 0.0;
+ var p1Behind = p1.y < 0.0;
+ var p2Behind = p2.y < 0.0;
+
+ offsetPointFromXZPlane(p0, p0Behind);
+ offsetPointFromXZPlane(p1, p1Behind);
+ offsetPointFromXZPlane(p2, p2Behind);
+
+ var numBehind = 0;
+ numBehind += p0Behind ? 1 : 0;
+ numBehind += p1Behind ? 1 : 0;
+ numBehind += p2Behind ? 1 : 0;
+
+ var indices = splitTriangleResult.indices;
+
+ if (numBehind === 1) {
+ indices[1] = 3;
+ indices[2] = 4;
+ indices[5] = 6;
+ indices[7] = 6;
+ indices[8] = 5;
+
+ if (p0Behind) {
+ getXZIntersectionOffsetPoints(p0, p1, u1, q1);
+ getXZIntersectionOffsetPoints(p0, p2, u2, q2);
+
+ indices[0] = 0;
+ indices[3] = 1;
+ indices[4] = 2;
+ indices[6] = 1;
+ } else if (p1Behind) {
+ getXZIntersectionOffsetPoints(p1, p0, u1, q1);
+ getXZIntersectionOffsetPoints(p1, p2, u2, q2);
+
+ indices[0] = 1;
+ indices[3] = 2;
+ indices[4] = 0;
+ indices[6] = 2;
+ } else if (p2Behind) {
+ getXZIntersectionOffsetPoints(p2, p0, u1, q1);
+ getXZIntersectionOffsetPoints(p2, p1, u2, q2);
+
+ indices[0] = 2;
+ indices[3] = 0;
+ indices[4] = 1;
+ indices[6] = 0;
+ }
+ } else if (numBehind === 2) {
+ indices[2] = 4;
+ indices[4] = 4;
+ indices[5] = 3;
+ indices[7] = 5;
+ indices[8] = 6;
+
+ if (!p0Behind) {
+ getXZIntersectionOffsetPoints(p0, p1, u1, q1);
+ getXZIntersectionOffsetPoints(p0, p2, u2, q2);
+
+ indices[0] = 1;
+ indices[1] = 2;
+ indices[3] = 1;
+ indices[6] = 0;
+ } else if (!p1Behind) {
+ getXZIntersectionOffsetPoints(p1, p2, u1, q1);
+ getXZIntersectionOffsetPoints(p1, p0, u2, q2);
+
+ indices[0] = 2;
+ indices[1] = 0;
+ indices[3] = 2;
+ indices[6] = 1;
+ } else if (!p2Behind) {
+ getXZIntersectionOffsetPoints(p2, p0, u1, q1);
+ getXZIntersectionOffsetPoints(p2, p1, u2, q2);
+
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[3] = 0;
+ indices[6] = 2;
+ }
+ }
+
+ var positions = splitTriangleResult.positions;
+ positions[0] = p0;
+ positions[1] = p1;
+ positions[2] = p2;
+ splitTriangleResult.length = 3;
+
+ if (numBehind === 1 || numBehind === 2) {
+ positions[3] = u1;
+ positions[4] = u2;
+ positions[5] = q1;
+ positions[6] = q2;
+ splitTriangleResult.length = 7;
+ }
+
+ return splitTriangleResult;
+ }
+
+ function computeTriangleAttributes(i0, i1, i2, dividedTriangle, normals, binormals, tangents, texCoords) {
+ if (typeof normals === 'undefined' && typeof binormals === 'undefined' &&
+ typeof tangents === 'undefined' && typeof texCoords === 'undefined') {
+ return;
+ }
+
+ var positions = dividedTriangle.positions;
+ var p0 = positions[0];
+ var p1 = positions[1];
+ var p2 = positions[2];
+
+ var n0, n1, n2;
+ var b0, b1, b2;
+ var t0, t1, t2;
+ var s0, s1, s2;
+ var v0, v1, v2;
+ var u0, u1, u2;
+
+ if (typeof normals !== 'undefined') {
+ n0 = Cartesian3.fromArray(normals, i0 * 3);
+ n1 = Cartesian3.fromArray(normals, i1 * 3);
+ n2 = Cartesian3.fromArray(normals, i2 * 3);
+ }
+
+ if (typeof binormals !== 'undefined') {
+ b0 = Cartesian3.fromArray(binormals, i0 * 3);
+ b1 = Cartesian3.fromArray(binormals, i1 * 3);
+ b2 = Cartesian3.fromArray(binormals, i2 * 3);
+ }
+
+ if (typeof tangents !== 'undefined') {
+ t0 = Cartesian3.fromArray(tangents, i0 * 3);
+ t1 = Cartesian3.fromArray(tangents, i1 * 3);
+ t2 = Cartesian3.fromArray(tangents, i2 * 3);
+ }
+
+ if (typeof texCoords !== 'undefined') {
+ s0 = Cartesian2.fromArray(texCoords, i0 * 2);
+ s1 = Cartesian2.fromArray(texCoords, i1 * 2);
+ s2 = Cartesian2.fromArray(texCoords, i2 * 2);
+ }
+
+ for (var i = 3; i < positions.length; ++i) {
+ var point = positions[i];
+ var coords = barycentricCoordinates(point, p0, p1, p2);
+
+ if (typeof normals !== 'undefined') {
+ v0 = Cartesian3.multiplyByScalar(n0, coords.x, v0);
+ v1 = Cartesian3.multiplyByScalar(n1, coords.y, v1);
+ v2 = Cartesian3.multiplyByScalar(n2, coords.z, v2);
+
+ var normal = Cartesian3.add(v0, v1);
+ Cartesian3.add(normal, v2, normal);
+ Cartesian3.normalize(normal, normal);
+
+ normals.push(normal.x, normal.y, normal.z);
+ }
+
+ if (typeof binormals !== 'undefined') {
+ v0 = Cartesian3.multiplyByScalar(b0, coords.x, v0);
+ v1 = Cartesian3.multiplyByScalar(b1, coords.y, v1);
+ v2 = Cartesian3.multiplyByScalar(b2, coords.z, v2);
+
+ var binormal = Cartesian3.add(v0, v1);
+ Cartesian3.add(binormal, v2, binormal);
+ Cartesian3.normalize(binormal, binormal);
+
+ binormals.push(binormal.x, binormal.y, binormal.z);
+ }
+
+ if (typeof tangents !== 'undefined') {
+ v0 = Cartesian3.multiplyByScalar(t0, coords.x, v0);
+ v1 = Cartesian3.multiplyByScalar(t1, coords.y, v1);
+ v2 = Cartesian3.multiplyByScalar(t2, coords.z, v2);
+
+ var tangent = Cartesian3.add(v0, v1);
+ Cartesian3.add(tangent, v2, tangent);
+ Cartesian3.normalize(tangent, tangent);
+
+ tangents.push(tangent.x, tangent.y, tangent.z);
+ }
+
+ if (typeof texCoords !== 'undefined') {
+ u0 = Cartesian2.multiplyByScalar(s0, coords.x, u0);
+ u1 = Cartesian2.multiplyByScalar(s1, coords.y, u1);
+ u2 = Cartesian2.multiplyByScalar(s2, coords.z, u2);
+
+ var texCoord = Cartesian2.add(u0, u1);
+ Cartesian2.add(texCoord, u2, texCoord);
+
+ texCoords.push(texCoord.x, texCoord.y);
+ }
+ }
+ }
+
+ function wrapLongitudeTriangles(geometry) {
+ var attributes = geometry.attributes;
+ var positions = attributes.position.values;
+ var normals = (typeof attributes.normal !== 'undefined') ? attributes.normal.values : undefined;
+ var binormals = (typeof attributes.binormal !== 'undefined') ? attributes.binormal.values : undefined;
+ var tangents = (typeof attributes.tangent !== 'undefined') ? attributes.tangent.values : undefined;
+ var texCoords = (typeof attributes.st !== 'undefined') ? attributes.st.values : undefined;
+ var indices = geometry.indices;
+
+ var newPositions = Array.prototype.slice.call(positions, 0);
+ var newNormals = (typeof normals !== 'undefined') ? Array.prototype.slice.call(normals, 0) : undefined;
+ var newBinormals = (typeof binormals !== 'undefined') ? Array.prototype.slice.call(binormals, 0) : undefined;
+ var newTangents = (typeof tangents !== 'undefined') ? Array.prototype.slice.call(tangents, 0) : undefined;
+ var newTexCoords = (typeof texCoords !== 'undefined') ? Array.prototype.slice.call(texCoords, 0) : undefined;
+ var newIndices = [];
+
+ var len = indices.length;
+ for (var i = 0; i < len; i += 3) {
+ var i0 = indices[i];
+ var i1 = indices[i + 1];
+ var i2 = indices[i + 2];
+
+ var p0 = Cartesian3.fromArray(positions, i0 * 3);
+ var p1 = Cartesian3.fromArray(positions, i1 * 3);
+ var p2 = Cartesian3.fromArray(positions, i2 * 3);
+
+ var result = splitTriangle(p0, p1, p2);
+ if (typeof result !== 'undefined') {
+ newPositions[i0 * 3 + 1] = result.positions[0].y;
+ newPositions[i1 * 3 + 1] = result.positions[1].y;
+ newPositions[i2 * 3 + 1] = result.positions[2].y;
+
+ if (result.length > 3) {
+ var positionsLength = newPositions.length / 3;
+ for(var j = 0; j < result.indices.length; ++j) {
+ var index = result.indices[j];
+ if (index < 3) {
+ newIndices.push(indices[i + index]);
+ } else {
+ newIndices.push(index - 3 + positionsLength);
+ }
+ }
+
+ for (var k = 3; k < result.positions.length; ++k) {
+ var position = result.positions[k];
+ newPositions.push(position.x, position.y, position.z);
+ }
+ computeTriangleAttributes(i0, i1, i2, result, newNormals, newBinormals, newTangents, newTexCoords);
+ } else {
+ newIndices.push(i0, i1, i2);
+ }
+ } else {
+ newIndices.push(i0, i1, i2);
+ }
+ }
+
+ geometry.attributes.position.values = new Float64Array(newPositions);
+
+ if (typeof newNormals !== 'undefined') {
+ attributes.normal.values = attributes.normal.componentDatatype.createTypedArray(newNormals);
+ }
+
+ if (typeof newBinormals !== 'undefined') {
+ attributes.binormal.values = attributes.binormal.componentDatatype.createTypedArray(newBinormals);
+ }
+
+ if (typeof newTangents !== 'undefined') {
+ attributes.tangent.values = attributes.tangent.componentDatatype.createTypedArray(newTangents);
+ }
+
+ if (typeof newTexCoords !== 'undefined') {
+ attributes.st.values = attributes.st.componentDatatype.createTypedArray(newTexCoords);
+ }
+
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+ geometry.indices = IndexDatatype.createTypedArray(numberOfVertices, newIndices);
+ }
+
+ function wrapLongitudeLines(geometry) {
+ var attributes = geometry.attributes;
+ var positions = attributes.position.values;
+ var indices = geometry.indices;
+
+ var newPositions = Array.prototype.slice.call(positions, 0);
+ var newIndices = [];
+
+ var xzPlane = Plane.fromPointNormal(Cartesian3.ZERO, Cartesian3.UNIT_Y);
+
+ var length = indices.length;
+ for ( var i = 0; i < length; i += 2) {
+ var i0 = indices[i];
+ var i1 = indices[i + 1];
+
+ var prev = Cartesian3.fromArray(positions, i0 * 3);
+ var cur = Cartesian3.fromArray(positions, i1 * 3);
+
+ if (Math.abs(prev.y) < CesiumMath.EPSILON6){
+ if (prev.y < 0.0) {
+ prev.y = -CesiumMath.EPSILON6;
+ } else {
+ prev.y = CesiumMath.EPSILON6;
+ }
+
+ newPositions[i0 * 3 + 1] = prev.y;
+ }
+
+ if (Math.abs(cur.y) < CesiumMath.EPSILON6){
+ if (cur.y < 0.0) {
+ cur.y = -CesiumMath.EPSILON6;
+ } else {
+ cur.y = CesiumMath.EPSILON6;
+ }
+
+ newPositions[i1 * 3 + 1] = cur.y;
+ }
+
+ newIndices.push(i0);
+
+ // intersects the IDL if either endpoint is on the negative side of the yz-plane
+ if (prev.x < 0.0 || cur.x < 0.0) {
+ // and intersects the xz-plane
+ var intersection = IntersectionTests.lineSegmentPlane(prev, cur, xzPlane);
+ if (typeof intersection !== 'undefined') {
+ // move point on the xz-plane slightly away from the plane
+ var offset = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Y, 5.0 * CesiumMath.EPSILON9);
+ if (prev.y < 0.0) {
+ Cartesian3.negate(offset, offset);
+ }
+
+ var index = newPositions.length / 3;
+ newIndices.push(index, index + 1);
+
+ var offsetPoint = Cartesian3.add(intersection, offset);
+ newPositions.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
+
+ Cartesian3.negate(offset, offset);
+ Cartesian3.add(intersection, offset, offsetPoint);
+ newPositions.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
+ }
+ }
+
+ newIndices.push(i1);
+ }
+
+ geometry.attributes.position.values = new Float64Array(newPositions);
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+ geometry.indices = IndexDatatype.createTypedArray(numberOfVertices, newIndices);
+ }
+
+ /**
+ * Splits the geometry's primitives, by introducing new vertices and indices,that
+ * intersect the International Date Line so that no primitives cross longitude
+ * -180/180 degrees. This is not required for 3D drawing, but is required for
+ * correcting drawing in 2D and Columbus view.
+ *
+ * @param {Geometry} geometry The geometry to modify.
+ *
+ * @returns {Geometry} The modified geometry
argument, with it's primitives split at the International Date Line.
+ *
+ * @exception {DeveloperError} geometry is required.
+ *
+ * @example
+ * geometry = GeometryPipeline.wrapLongitude(geometry);
+ */
+ GeometryPipeline.wrapLongitude = function(geometry) {
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('geometry is required.');
+ }
+
+ var boundingSphere = geometry.boundingSphere;
+ if (typeof boundingSphere !== 'undefined') {
+ var minX = boundingSphere.center.x - boundingSphere.radius;
+ if (minX > 0 || BoundingSphere.intersect(boundingSphere, Cartesian4.UNIT_Y) !== Intersect.INTERSECTING) {
+ return geometry;
+ }
+ }
+
+ indexPrimitive(geometry);
+ if (geometry.primitiveType === PrimitiveType.TRIANGLES) {
+ wrapLongitudeTriangles(geometry);
+ } else if (geometry.primitiveType === PrimitiveType.LINES) {
+ wrapLongitudeLines(geometry);
+ }
+
+ return geometry;
+ };
+
+ return GeometryPipeline;
+});
diff --git a/Source/Core/HeightmapTessellator.js b/Source/Core/HeightmapTessellator.js
index 71fb70041ce0..7963fbcab876 100644
--- a/Source/Core/HeightmapTessellator.js
+++ b/Source/Core/HeightmapTessellator.js
@@ -19,11 +19,6 @@ define([
* Contains functions to create a mesh from a heightmap image.
*
* @exports HeightmapTessellator
- *
- * @see ExtentTessellator
- * @see CubeMapEllipsoidTessellator
- * @see BoxTessellator
- * @see PlaneTessellator
*/
var HeightmapTessellator = {};
diff --git a/Source/Core/IndexDatatype.js b/Source/Core/IndexDatatype.js
index 31143e2b4654..b32c8c7f5678 100644
--- a/Source/Core/IndexDatatype.js
+++ b/Source/Core/IndexDatatype.js
@@ -1,29 +1,100 @@
/*global define*/
-define(['./Enumeration'], function(Enumeration) {
+define([
+ './Enumeration',
+ './DeveloperError',
+ './Math'
+ ], function(
+ Enumeration,
+ DeveloperError,
+ CesiumMath) {
"use strict";
/**
- * DOC_TBA
+ * Enumerations for WebGL index datatypes. These corresponds to the
+ * type
parameter of drawElements.
*
- * @exports IndexDatatype
+ * @alias IndexDatatype
+ * @enumeration
*/
var IndexDatatype = {
/**
- * DOC_TBA
+ * 8-bit unsigned byte enumeration corresponding to UNSIGNED_BYTE
and the type
+ * of an element in Uint8Array
.
*
* @type {Enumeration}
* @constant
* @default 0x1401
*/
UNSIGNED_BYTE : new Enumeration(0x1401, 'UNSIGNED_BYTE'),
+
/**
- * DOC_TBA
+ * 16-bit unsigned short enumeration corresponding to UNSIGNED_SHORT
and the type
+ * of an element in Uint16Array
.
*
* @type {Enumeration}
* @constant
* @default 0x1403
*/
- UNSIGNED_SHORT : new Enumeration(0x1403, 'UNSIGNED_SHORT')
+ UNSIGNED_SHORT : new Enumeration(0x1403, 'UNSIGNED_SHORT'),
+
+ /**
+ * 32-bit unsigned int enumeration corresponding to UNSIGNED_INT
and the type
+ * of an element in Uint32Array
.
+ *
+ * @memberOf ComponentDatatype
+ *
+ * @constant
+ * @type {Enumeration}
+ */
+ UNSIGNED_INT : new Enumeration(0x1405, 'UNSIGNED_INT')
+ };
+
+ IndexDatatype.UNSIGNED_BYTE.sizeInBytes = Uint8Array.BYTES_PER_ELEMENT;
+ IndexDatatype.UNSIGNED_SHORT.sizeInBytes = Uint16Array.BYTES_PER_ELEMENT;
+ IndexDatatype.UNSIGNED_INT.sizeInBytes = Uint32Array.BYTES_PER_ELEMENT;
+
+ /**
+ * Validates that the provided index datatype is a valid {@link IndexDatatype}
+ *
+ * @param {IndexDatatype} indexDatatype The index datatype to validate.
+ *
+ * @return {Boolean} true
if the provided index datatype is a valid enumeration value; otherwise, false
.
+ *
+ * @example
+ * if (!IndexDatatype.validate(indexDatatype)) {
+ * throw new DeveloperError('indexDatatype must be a valid enumeration value.');
+ * }
+ */
+ IndexDatatype.validate = function(indexDatatype) {
+ return ((indexDatatype === IndexDatatype.UNSIGNED_BYTE) ||
+ (indexDatatype === IndexDatatype.UNSIGNED_SHORT) ||
+ (indexDatatype === IndexDatatype.UNSIGNED_INT));
+ };
+
+ /**
+ * Creates a typed array that will store indices, using either
+ * or Uint32Array
depending on the number of vertices.
+ *
+ * @param {Number} numberOfVertices Number of vertices that the indices will reference.
+ * @param {Any} indicesLengthOrArray Passed through to the typed array constructor.
+ *
+ * @return {Array} A Uint16Array
or Uint32Array
constructed with indicesLengthOrArray
.
+ *
+ * @exception {DeveloperError} center is required.
+ *
+ * @example
+ * this.indices = IndexDatatype.createTypedArray(positions.length / 3, numberOfIndices);
+ */
+ IndexDatatype.createTypedArray = function(numberOfVertices, indicesLengthOrArray) {
+ if (typeof numberOfVertices === 'undefined') {
+ throw new DeveloperError('numberOfVertices is required.');
+ }
+
+ if (numberOfVertices > CesiumMath.SIXTY_FOUR_KILOBYTES) {
+ return new Uint32Array(indicesLengthOrArray);
+ }
+
+ return new Uint16Array(indicesLengthOrArray);
};
return IndexDatatype;
diff --git a/Source/Core/Math.js b/Source/Core/Math.js
index d5939acfedf5..3e304ad8fbfa 100644
--- a/Source/Core/Math.js
+++ b/Source/Core/Math.js
@@ -164,10 +164,16 @@ define([
* Radius of the sun in meters: 6.995e8
* @type {Number}
* @constant
- * @default
*/
CesiumMath.SOLAR_RADIUS = 6.995e8;
+ /**
+ * 64 * 1024
+ * @type {Number}
+ * @constant
+ */
+ CesiumMath.SIXTY_FOUR_KILOBYTES = 64 * 1024;
+
/**
* Returns the sign of the value; 1 if the value is positive, -1 if the value is
* negative, or 0 if the value is 0.
diff --git a/Source/Core/Matrix4.js b/Source/Core/Matrix4.js
index 16530291b2ec..f2087331b199 100644
--- a/Source/Core/Matrix4.js
+++ b/Source/Core/Matrix4.js
@@ -87,32 +87,32 @@ define([
* @param {Matrix4} [result] The object onto which to store the result.
* @return {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. (Returns undefined if matrix is undefined)
*/
- Matrix4.clone = function(values, result) {
- if (typeof values === 'undefined') {
+ Matrix4.clone = function(matrix, result) {
+ if (typeof matrix === 'undefined') {
return undefined;
}
if (typeof result === 'undefined') {
- return new Matrix4(values[0], values[4], values[8], values[12],
- values[1], values[5], values[9], values[13],
- values[2], values[6], values[10], values[14],
- values[3], values[7], values[11], values[15]);
+ return new Matrix4(matrix[0], matrix[4], matrix[8], matrix[12],
+ matrix[1], matrix[5], matrix[9], matrix[13],
+ matrix[2], matrix[6], matrix[10], matrix[14],
+ matrix[3], matrix[7], matrix[11], matrix[15]);
}
- result[0] = values[0];
- result[1] = values[1];
- result[2] = values[2];
- result[3] = values[3];
- result[4] = values[4];
- result[5] = values[5];
- result[6] = values[6];
- result[7] = values[7];
- result[8] = values[8];
- result[9] = values[9];
- result[10] = values[10];
- result[11] = values[11];
- result[12] = values[12];
- result[13] = values[13];
- result[14] = values[14];
- result[15] = values[15];
+ result[0] = matrix[0];
+ result[1] = matrix[1];
+ result[2] = matrix[2];
+ result[3] = matrix[3];
+ result[4] = matrix[4];
+ result[5] = matrix[5];
+ result[6] = matrix[6];
+ result[7] = matrix[7];
+ result[8] = matrix[8];
+ result[9] = matrix[9];
+ result[10] = matrix[10];
+ result[11] = matrix[11];
+ result[12] = matrix[12];
+ result[13] = matrix[13];
+ result[14] = matrix[14];
+ result[15] = matrix[15];
return result;
};
diff --git a/Source/Core/MeshFilters.js b/Source/Core/MeshFilters.js
deleted file mode 100644
index 4040b55d7946..000000000000
--- a/Source/Core/MeshFilters.js
+++ /dev/null
@@ -1,568 +0,0 @@
-/*global define*/
-define([
- './defaultValue',
- './DeveloperError',
- './Cartesian3',
- './EncodedCartesian3',
- './GeographicProjection',
- './ComponentDatatype',
- './PrimitiveType',
- './Tipsify'
- ], function(
- defaultValue,
- DeveloperError,
- Cartesian3,
- EncodedCartesian3,
- GeographicProjection,
- ComponentDatatype,
- PrimitiveType,
- Tipsify) {
- "use strict";
-
- /**
- * DOC_TBA
- *
- * @exports MeshFilters
- *
- * @see Context#createVertexArrayFromMesh
- */
- var MeshFilters = {};
-
- /**
- * Converts a mesh's triangle indices to line indices. Each list of indices in the mesh's indexList
with
- * a primitive type of triangles
, triangleStrip
, or trangleFan
is converted to a
- * list of indices with a primitive type of lines
. Lists of indices with other primitive types remain unchanged.
- *
- * The mesh
argument should use the standard layout like the mesh returned by {@link BoxTessellator}.
- *
- * This filter is commonly used to create a wireframe mesh for visual debugging.
- *
- * @param {Object} mesh The mesh to filter, which is modified in place.
- *
- * @returns The modified mesh
argument, with its triangle indices converted to lines.
- *
- * @see BoxTessellator
- *
- * @example
- * var mesh = BoxTessellator.compute();
- * mesh = MeshFilters.toWireframeInPlace(mesh);
- */
- MeshFilters.toWireframeInPlace = function(mesh) {
- function addTriangle(lines, i0, i1, i2) {
- lines.push(i0);
- lines.push(i1);
-
- lines.push(i1);
- lines.push(i2);
-
- lines.push(i2);
- lines.push(i0);
- }
-
- function trianglesToLines(triangles) {
- var lines = [];
- var count = triangles.length;
- for ( var i = 0; i < count; i += 3) {
- addTriangle(lines, triangles[i], triangles[i + 1], triangles[i + 2]);
- }
-
- return lines;
- }
-
- function triangleStripToLines(triangles) {
- var lines = [];
- var count = triangles.length;
-
- if (count >= 3) {
- addTriangle(lines, triangles[0], triangles[1], triangles[2]);
-
- for ( var i = 3; i < count; ++i) {
- addTriangle(lines, triangles[i - 1], triangles[i], triangles[i - 2]);
- }
- }
-
- return lines;
- }
-
- function triangleFanToLines(triangles) {
- var lines = [];
-
- if (triangles.length > 0) {
- var base = triangles[0];
- var count = triangles.length - 1;
- for ( var i = 1; i < count; ++i) {
- addTriangle(lines, base, triangles[i], triangles[i + 1]);
- }
- }
-
- return lines;
- }
-
- if (typeof mesh !== 'undefined') {
- var indexLists = mesh.indexLists;
- if (typeof indexLists !== 'undefined') {
- var count = indexLists.length;
- for ( var i = 0; i < count; ++i) {
- var indices = indexLists[i];
-
- switch (indices.primitiveType) {
- case PrimitiveType.TRIANGLES:
- indices.primitiveType = PrimitiveType.LINES;
- indices.values = trianglesToLines(indices.values);
- break;
- case PrimitiveType.TRIANGLE_STRIP:
- indices.primitiveType = PrimitiveType.LINES;
- indices.values = triangleStripToLines(indices.values);
- break;
- case PrimitiveType.TRIANGLE_FAN:
- indices.primitiveType = PrimitiveType.LINES;
- indices.values = triangleFanToLines(indices.values);
- break;
- }
- }
- }
- }
-
- return mesh;
- };
-
- /**
- * DOC_TBA
- */
- MeshFilters.createAttributeIndices = function(mesh) {
- var indices = {};
-
- if (typeof mesh !== 'undefined') {
- var attributes = mesh.attributes;
- var j = 0;
-
- for ( var name in attributes) {
- if (attributes.hasOwnProperty(name)) {
- indices[name] = j++;
- }
- }
- }
-
- return indices;
- };
-
- /**
- * DOC_TBA
- */
- MeshFilters.mapAttributeIndices = function(indices, map) {
- var mappedIndices = {};
-
- if (typeof indices !== 'undefined' && typeof map !== 'undefined') {
- for ( var name in map) {
- if (map.hasOwnProperty(name)) {
- mappedIndices[map[name]] = indices[name];
- }
- }
- }
-
- return mappedIndices;
- };
-
- MeshFilters._computeNumberOfAttributes = function(mesh) {
- var numberOfVertices = -1;
- for ( var property in mesh.attributes) {
- if (mesh.attributes.hasOwnProperty(property) && mesh.attributes[property].values) {
- var attribute = mesh.attributes[property];
- var num = attribute.values.length / attribute.componentsPerAttribute;
- if ((numberOfVertices !== num) && (numberOfVertices !== -1)) {
- throw new DeveloperError('All mesh attribute lists must have the same number of attributes.');
- }
- numberOfVertices = num;
- }
- }
-
- return numberOfVertices;
- };
-
- /**
- * Reorders a mesh's indices to achieve better performance from the GPU's pre-vertex-shader cache.
- * Each list of indices in the mesh's indexList
is reordered to keep the same index-vertex correspondence.
- *
- * The mesh
argument should use the standard layout like the mesh returned by {@link BoxTessellator}.
- *
-
- * @param {Object} mesh The mesh to filter, which is modified in place.
- *
- * @exception {DeveloperError} All mesh attribute lists must have the same number of attributes.
- *
- * @returns The modified mesh
argument, with its vertices and indices reordered for the GPU's pre-vertex-shader cache.
- *
- * @see MeshFilters.reorderForPostVertexCache
- *
- * @example
- * var mesh = CubeMapEllipsoidTessellator.compute(...);
- * mesh = MeshFilters.reorderForPreVertexCache(mesh);
- */
- MeshFilters.reorderForPreVertexCache = function(mesh) {
- if (typeof mesh !== 'undefined') {
- var numVertices = MeshFilters._computeNumberOfAttributes(mesh);
-
- var indexCrossReferenceOldToNew = [];
- for ( var i = 0; i < numVertices; i++) {
- indexCrossReferenceOldToNew[i] = -1;
- }
-
- //Construct cross reference and reorder indices
- var indexLists = mesh.indexLists;
- if (typeof indexLists !== 'undefined') {
- var count = indexLists.length;
- for ( var j = 0; j < count; ++j) {
- var indicesIn = indexLists[j].values;
- var numIndices = indicesIn.length;
- var indicesOut = [];
- 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];
- if (tempIndex >= numVertices) {
- throw new DeveloperError('Input indices contains a value greater than or equal to the number of vertices');
- }
- indexCrossReferenceOldToNew[tempIndex] = nextIndex;
-
- indicesOut[intoIndicesOut] = nextIndex;
- ++nextIndex;
- }
- ++intoIndicesIn;
- ++intoIndicesOut;
- }
- indexLists[j].values = indicesOut;
- }
- }
-
- //Reorder Vertices
- var attributes = mesh.attributes;
- if (typeof attributes !== 'undefined') {
- for ( var property in attributes) {
- if (attributes.hasOwnProperty(property) && attributes[property].values) {
- var elementsIn = attributes[property].values;
- var intoElementsIn = 0;
- var numComponents = attributes[property].componentsPerAttribute;
- var elementsOut = [];
- while (intoElementsIn < numVertices) {
- var temp = indexCrossReferenceOldToNew[intoElementsIn];
- for (i = 0; i < numComponents; i++) {
- elementsOut[numComponents * temp + i] = elementsIn[numComponents * intoElementsIn + i];
- }
- ++intoElementsIn;
- }
- attributes[property].values = elementsOut;
- }
- }
- }
- }
- return mesh;
- };
-
- /**
- * Reorders a mesh's indices to achieve better performance from the GPU's post vertex-shader cache by using the Tipsify algorithm.
- * Each list of indices in the mesh's indexList
is optimally reordered.
- *
- * The mesh
argument should use the standard layout like the mesh returned by {@link BoxTessellator}.
- *
-
- * @param {Object} mesh The mesh to filter, which is modified in place.
- * @param {Number} [cacheCapacity=24] The number of vertices that can be held in the GPU's vertex cache.
- *
- * @exception {DeveloperError} Mesh's index list must be defined.
- * @exception {DeveloperError} Mesh's index lists' lengths must each be a multiple of three.
- * @exception {DeveloperError} Mesh's index list's maximum index value must be greater than zero.
- * @exception {DeveloperError} cacheCapacity must be greater than two.
- *
- * @returns The modified mesh
argument, with its indices optimally reordered for the post-vertex-shader cache.
- *
- * @see MeshFilters.reorderForPreVertexCache
- * @see Tipsify
- * @see
- * Fast Triangle Reordering for Vertex Locality and Reduced Overdraw
- * by Sander, Nehab, and Barczak
- *
- * @example
- * var mesh = CubeMapEllipsoidTessellator.compute(...);
- * mesh = MeshFilters.reorderForPostVertexCache(mesh);
- */
- MeshFilters.reorderForPostVertexCache = function(mesh, cacheCapacity) {
- if (typeof mesh !== 'undefined') {
- var indexLists = mesh.indexLists;
- if (typeof indexLists !== 'undefined') {
- var count = indexLists.length;
- for ( var i = 0; i < count; i++) {
- var indices = indexLists[i].values;
- var numIndices = indices.length;
- var maximumIndex = 0;
- for ( var j = 0; j < numIndices; j++) {
- if (indices[j] > maximumIndex) {
- maximumIndex = indices[j];
- }
- }
- indexLists[i].values = Tipsify.tipsify({indices : indices,
- maximumIndex : maximumIndex,
- cacheSize : cacheCapacity});
- }
- }
- }
- return mesh;
- };
-
- MeshFilters._verifyTrianglesPrimitiveType = function(indexLists) {
- var length = indexLists.length;
- for ( var i = 0; i < length; ++i) {
- if (indexLists[i].primitiveType !== PrimitiveType.TRIANGLES) {
- throw new DeveloperError('indexLists must have PrimitiveType equal to PrimitiveType.TRIANGLES.');
- }
- }
- };
-
- MeshFilters._copyAttributesDescriptions = function(attributes) {
- var newAttributes = {};
-
- for ( var attribute in attributes) {
- if (attributes.hasOwnProperty(attribute) && attributes[attribute].values) {
- var attr = attributes[attribute];
- newAttributes[attribute] = {
- componentDatatype : attr.componentDatatype,
- componentsPerAttribute : attr.componentsPerAttribute,
- values : []
- };
- }
- }
-
- return newAttributes;
- };
-
- function copyVertex(destinationAttributes, sourceAttributes, index) {
- for ( var attribute in sourceAttributes) {
- if (sourceAttributes.hasOwnProperty(attribute) && 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]);
- }
- }
- }
- }
-
- /**
- * DOC_TBA. Old mesh is not guaranteed to be copied.
- *
- * @exception {DeveloperError} The mesh's index-lists must have PrimitiveType equal to PrimitiveType.TRIANGLES.
- * @exception {DeveloperError} All mesh attribute lists must have the same number of attributes.
- */
- MeshFilters.fitToUnsignedShortIndices = function(mesh) {
- function createMesh(attributes, primitiveType, indices) {
- return {
- attributes : attributes,
- indexLists : [{
- primitiveType : primitiveType,
- values : indices
- }]
- };
- }
-
- var meshes = [];
-
- if (typeof mesh !== 'undefined') {
- MeshFilters._verifyTrianglesPrimitiveType(mesh.indexLists);
-
- var numberOfVertices = MeshFilters._computeNumberOfAttributes(mesh);
-
- // 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 sixtyFourK = 64 * 1024;
- var indexLists = mesh.indexLists;
- if (typeof indexLists !== 'undefined' && (numberOfVertices > sixtyFourK)) {
- // PERFORMANCE_IDEA: If an input mesh has more than one index-list. This creates
- // at least one vertex-array per index-list. A more sophisticated implementation
- // may create less vertex-arrays.
- var length = indexLists.length;
- for ( var i = 0; i < length; ++i) {
- var oldToNewIndex = [];
- var newIndices = [];
- var currentIndex = 0;
- var newAttributes = MeshFilters._copyAttributesDescriptions(mesh.attributes);
-
- var originalIndices = indexLists[i].values;
- var numberOfIndices = originalIndices.length;
-
- for ( var j = 0; j < numberOfIndices; j += 3) {
- // It would be easy to extend this inter-loop to support all primitive-types.
-
- var x0 = originalIndices[j];
- var x1 = originalIndices[j + 1];
- var x2 = originalIndices[j + 2];
-
- var i0 = oldToNewIndex[x0];
- if (typeof i0 === 'undefined') {
- i0 = currentIndex++;
- oldToNewIndex[x0] = i0;
-
- copyVertex(newAttributes, mesh.attributes, x0);
- }
-
- var i1 = oldToNewIndex[x1];
- if (typeof i1 === 'undefined') {
- i1 = currentIndex++;
- oldToNewIndex[x1] = i1;
-
- copyVertex(newAttributes, mesh.attributes, x1);
- }
-
- var i2 = oldToNewIndex[x2];
- if (typeof i2 === 'undefined') {
- i2 = currentIndex++;
- oldToNewIndex[x2] = i2;
-
- copyVertex(newAttributes, mesh.attributes, x2);
- }
-
- newIndices.push(i0);
- newIndices.push(i1);
- newIndices.push(i2);
-
- if (currentIndex + 3 > sixtyFourK) {
- meshes.push(createMesh(newAttributes, indexLists[i].primitiveType, newIndices));
-
- // Reset for next vertex-array
- oldToNewIndex = [];
- newIndices = [];
- currentIndex = 0;
- newAttributes = MeshFilters._copyAttributesDescriptions(mesh.attributes);
- }
- }
-
- if (newIndices.length !== 0) {
- meshes.push(createMesh(newAttributes, indexLists[i].primitiveType, newIndices));
- }
- }
- } else {
- // No need to split into multiple meshes
- meshes.push(mesh);
- }
- }
-
- return meshes;
- };
-
- /**
- * DOC_TBA
- */
- MeshFilters.projectTo2D = function(mesh, projection) {
- if (typeof mesh !== 'undefined' && typeof mesh.attributes !== 'undefined' && typeof mesh.attributes.position !== 'undefined') {
- projection = typeof projection !== 'undefined' ? projection : new GeographicProjection();
- var ellipsoid = projection.getEllipsoid();
-
- // Project original positions to 2D.
- var wgs84Positions = mesh.attributes.position.values;
- var projectedPositions = [];
-
- for ( var i = 0; i < wgs84Positions.length; i += 3) {
- var lonLat = ellipsoid.cartesianToCartographic(new Cartesian3(wgs84Positions[i], wgs84Positions[i + 1], wgs84Positions[i + 2]));
- var projectedLonLat = projection.project(lonLat);
- projectedPositions.push(projectedLonLat.x, projectedLonLat.y);
- }
-
- // Rename original positions to WGS84 Positions.
- mesh.attributes.position3D = mesh.attributes.position;
-
- // Replace original positions with 2D projected positions
- mesh.attributes.position2D = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 2,
- values : projectedPositions
- };
- delete mesh.attributes.position;
- }
-
- return mesh;
- };
-
- var encodedResult = {
- high : 0.0,
- low : 0.0
- };
-
- /**
- * Encodes floating-point mesh attribute values as two separate attributes to improve
- * rendering precision using the same encoding as {@link EncodedCartesian3}.
- *
- * This is commonly used to create high-precision position vertex attributes.
- *
- *
- * @param {Object} mesh The mesh to filter, which is modified in place.
- * @param {String} [attributeName='position'] The name of the attribute.
- * @param {String} [attributeHighName='positionHigh'] The name of the attribute for the encoded high bits.
- * @param {String} [attributeLowName='positionLow'] The name of the attribute for the encoded low bits.
- *
- * @returns The modified mesh
argument, with its encoded attribute.
- *
- * @exception {DeveloperError} mesh is required.
- * @exception {DeveloperError} mesh must have an attributes property.
- * @exception {DeveloperError} mesh must have attribute matching the attributeName argument.
- * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.FLOAT.
- *
- * @example
- * mesh = MeshFilters.encodeAttribute(mesh, 'position3D', 'position3DHigh', 'position3DLow');
- *
- * @see EncodedCartesian3
- */
- MeshFilters.encodeAttribute = function(mesh, attributeName, attributeHighName, attributeLowName) {
- attributeName = defaultValue(attributeName, 'position');
- attributeHighName = defaultValue(attributeHighName, 'positionHigh');
- attributeLowName = defaultValue(attributeLowName, 'positionLow');
-
- if (typeof mesh === 'undefined') {
- throw new DeveloperError('mesh is required.');
- }
-
- if (typeof mesh.attributes === 'undefined') {
- throw new DeveloperError('mesh must have an attributes property.');
- }
-
- var attribute = mesh.attributes[attributeName];
-
- if (typeof attribute === 'undefined') {
- throw new DeveloperError('mesh must have attribute matching the attributeName argument: ' + attributeName + '.');
- }
-
- if (attribute.componentDatatype !== ComponentDatatype.FLOAT) {
- throw new DeveloperError('The attribute componentDatatype must be ComponentDatatype.FLOAT.');
- }
-
- var values = attribute.values;
- var length = values.length;
- var highValues = new Array(length);
- var lowValues = new Array(length);
-
- for (var i = 0; i < length; ++i) {
- EncodedCartesian3.encode(values[i], encodedResult);
- highValues[i] = encodedResult.high;
- lowValues[i] = encodedResult.low;
- }
-
- mesh.attributes[attributeHighName] = {
- componentDatatype : attribute.componentDatatype,
- componentsPerAttribute : attribute.componentsPerAttribute,
- values : highValues
- };
- mesh.attributes[attributeLowName] = {
- componentDatatype : attribute.componentDatatype,
- componentsPerAttribute : attribute.componentsPerAttribute,
- values : lowValues
- };
- delete mesh.attributes[attributeName];
-
- return mesh;
- };
-
- return MeshFilters;
-});
diff --git a/Source/Core/Occluder.js b/Source/Core/Occluder.js
index 5564fbb6bb78..a234fc63a725 100644
--- a/Source/Core/Occluder.js
+++ b/Source/Core/Occluder.js
@@ -374,7 +374,7 @@ define([
}
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
- var positions = extent.subsample(ellipsoid, computeOccludeePointFromExtentScratch);
+ var positions = extent.subsample(ellipsoid, 0.0, computeOccludeePointFromExtentScratch);
var bs = BoundingSphere.fromPoints(positions);
// TODO: get correct ellipsoid center
diff --git a/Source/Core/PlaneTessellator.js b/Source/Core/PlaneTessellator.js
deleted file mode 100644
index 4e1544134ec6..000000000000
--- a/Source/Core/PlaneTessellator.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*global define*/
-define([
- './DeveloperError',
- './Cartesian2',
- './PrimitiveType',
- './defaultValue'
- ], function(
- DeveloperError,
- Cartesian2,
- PrimitiveType,
- defaultValue) {
- "use strict";
-
- /**
- * DOC_TBA
- *
- * @exports PlaneTessellator
- *
- * @see CubeMapEllipsoidTessellator
- * @see BoxTessellator
- */
- var PlaneTessellator = {
- /**
- * DOC_TBA
- *
- * @exception {DeveloperError} Resolution must be greater than one in both the x and y directions.
- */
- compute : function(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- var resolution = typeof options.resolution !== "undefined" ? options.resolution : new Cartesian2(2, 2);
- var onInterpolation = options.onInterpolation; // Can be undefined
-
- if (resolution.x <= 1 || resolution.y <= 1) {
- throw new DeveloperError('Resolution must be greater than one in both the x and y directions.');
- }
-
- var i;
- var j;
-
- // To allow computing custom attributes, e.g., texture coordinates, etc.
- if (onInterpolation) {
- for (j = 0; j < resolution.y; ++j) {
- var yTime = j / (resolution.y - 1);
-
- for (i = 0; i < resolution.x; ++i) {
- var xTime = i / (resolution.x - 1);
- onInterpolation(new Cartesian2(xTime, yTime));
- }
- }
- }
-
- var indices = [];
-
- // Counterclockwise winding order
- for (j = 0; j < resolution.y - 1; ++j) {
- var row = j * resolution.x;
- var aboveRow = (j + 1) * resolution.x;
-
- for (i = 0; i < resolution.x - 1; ++i) {
- indices.push(row + i, row + i + 1, aboveRow + i + 1);
- indices.push(row + i, aboveRow + i + 1, aboveRow + i);
- }
- }
-
- return {
- attributes : {},
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : indices
- }]
- };
- }
- };
-
- return PlaneTessellator;
-});
\ No newline at end of file
diff --git a/Source/Core/PolygonGeometry.js b/Source/Core/PolygonGeometry.js
new file mode 100644
index 000000000000..661e34e0f61b
--- /dev/null
+++ b/Source/Core/PolygonGeometry.js
@@ -0,0 +1,477 @@
+/*global define*/
+define([
+ './defaultValue',
+ './BoundingRectangle',
+ './BoundingSphere',
+ './Cartesian2',
+ './Cartesian3',
+ './Cartesian4',
+ './ComponentDatatype',
+ './DeveloperError',
+ './Ellipsoid',
+ './EllipsoidTangentPlane',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './GeometryInstance',
+ './GeometryPipeline',
+ './IndexDatatype',
+ './Intersect',
+ './Math',
+ './Matrix3',
+ './PolygonPipeline',
+ './Quaternion',
+ './Queue',
+ './VertexFormat',
+ './WindingOrder'
+ ], function(
+ defaultValue,
+ BoundingRectangle,
+ BoundingSphere,
+ Cartesian2,
+ Cartesian3,
+ Cartesian4,
+ ComponentDatatype,
+ DeveloperError,
+ Ellipsoid,
+ EllipsoidTangentPlane,
+ GeometryAttribute,
+ GeometryAttributes,
+ GeometryInstance,
+ GeometryPipeline,
+ IndexDatatype,
+ Intersect,
+ CesiumMath,
+ Matrix3,
+ PolygonPipeline,
+ Quaternion,
+ Queue,
+ VertexFormat,
+ WindingOrder) {
+ "use strict";
+
+ var computeBoundingRectangleCartesian2 = new Cartesian2();
+ var computeBoundingRectangleCartesian3 = new Cartesian3();
+ var computeBoundingRectangleQuaternion = new Quaternion();
+ var computeBoundingRectangleMatrix3 = new Matrix3();
+
+ function computeBoundingRectangle(tangentPlane, positions, angle, result) {
+ var rotation = Quaternion.fromAxisAngle(tangentPlane._plane.normal, angle, computeBoundingRectangleQuaternion);
+ var textureMatrix = Matrix3.fromQuaternion(rotation, computeBoundingRectangleMatrix3);
+
+ var minX = Number.POSITIVE_INFINITY;
+ var maxX = Number.NEGATIVE_INFINITY;
+ var minY = Number.POSITIVE_INFINITY;
+ var maxY = Number.NEGATIVE_INFINITY;
+
+ var length = positions.length;
+ for ( var i = 0; i < length; ++i) {
+ var p = Cartesian3.clone(positions[i], computeBoundingRectangleCartesian3);
+ Matrix3.multiplyByVector(textureMatrix, p, p);
+ var st = tangentPlane.projectPointOntoPlane(p, computeBoundingRectangleCartesian2);
+
+ if (typeof st !== 'undefined') {
+ minX = Math.min(minX, st.x);
+ maxX = Math.max(maxX, st.x);
+
+ minY = Math.min(minY, st.y);
+ maxY = Math.max(maxY, st.y);
+ }
+ }
+
+ result.x = minX;
+ result.y = minY;
+ result.width = maxX - minX;
+ result.height = maxY - minY;
+ return result;
+ }
+
+ var createGeometryFromPositionsPositions = [];
+
+ function createGeometryFromPositions(ellipsoid, positions, granularity) {
+ var cleanedPositions = PolygonPipeline.removeDuplicates(positions);
+ if (cleanedPositions.length < 3) {
+ throw new DeveloperError('Duplicate positions result in not enough positions to form a polygon.');
+ }
+
+ var tangentPlane = EllipsoidTangentPlane.fromPoints(cleanedPositions, ellipsoid);
+ var positions2D = tangentPlane.projectPointsOntoPlane(cleanedPositions, createGeometryFromPositionsPositions);
+
+ var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D);
+ if (originalWindingOrder === WindingOrder.CLOCKWISE) {
+ positions2D.reverse();
+ cleanedPositions.reverse();
+ }
+
+ var indices = PolygonPipeline.earClip2D(positions2D);
+ return new GeometryInstance({
+ geometry : PolygonPipeline.computeSubdivision(cleanedPositions, indices, granularity)
+ });
+ }
+
+ var scratchBoundingRectangle = new BoundingRectangle();
+ var scratchPosition = new Cartesian3();
+ var scratchNormal = new Cartesian3();
+ var scratchTangent = new Cartesian3();
+ var scratchBinormal = new Cartesian3();
+
+ var appendTextureCoordinatesOrigin = new Cartesian2();
+ var appendTextureCoordinatesCartesian2 = new Cartesian2();
+ var appendTextureCoordinatesCartesian3 = new Cartesian3();
+ var appendTextureCoordinatesQuaternion = new Quaternion();
+ var appendTextureCoordinatesMatrix3 = new Matrix3();
+
+ /**
+ * A {@link Geometry} that represents vertices and indices for a polygon on the ellipsoid. The polygon is either defined
+ * by an array of Cartesian points, or a polygon hierarchy.
+ *
+ * @alias PolygonGeometry
+ * @constructor
+ *
+ * @param {Object} options.polygonHierarchy A polygon hierarchy that can include holes.
+ * @param {Number} [options.height=0.0] The height of the polygon.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ * @param {Number} [options.stRotation=0.0] The rotation of the texture coordiantes, in radians. A positive rotation is counter-clockwise.
+ * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference.
+ * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
+ *
+ * @exception {DeveloperError} polygonHierarchy is required.
+ * @exception {DeveloperError} At least three positions are required.
+ * @exception {DeveloperError} Duplicate positions result in not enough positions to form a polygon.
+ *
+ * @example
+ * // create a polygon from points
+ * var geometry = new PolygonGeometry({
+ * polygonHierarchy : {
+ * positions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(-72.0, 40.0),
+ * Cartographic.fromDegrees(-70.0, 35.0),
+ * Cartographic.fromDegrees(-75.0, 30.0),
+ * Cartographic.fromDegrees(-70.0, 30.0),
+ * Cartographic.fromDegrees(-68.0, 40.0)
+ * ])
+ * }
+ * });
+ *
+ * // create a nested polygon with holes
+ * var geometryWithHole = new PolygonGeometry({
+ * polygonHierarchy : {
+ * positions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(-109.0, 30.0),
+ * Cartographic.fromDegrees(-95.0, 30.0),
+ * Cartographic.fromDegrees(-95.0, 40.0),
+ * Cartographic.fromDegrees(-109.0, 40.0)
+ * ]),
+ * holes : [{
+ * positions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(-107.0, 31.0),
+ * Cartographic.fromDegrees(-107.0, 39.0),
+ * Cartographic.fromDegrees(-97.0, 39.0),
+ * Cartographic.fromDegrees(-97.0, 31.0)
+ * ]),
+ * holes : [{
+ * positions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(-105.0, 33.0),
+ * Cartographic.fromDegrees(-99.0, 33.0),
+ * Cartographic.fromDegrees(-99.0, 37.0),
+ * Cartographic.fromDegrees(-105.0, 37.0)
+ * ]),
+ * holes : [{
+ * positions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(-103.0, 34.0),
+ * Cartographic.fromDegrees(-101.0, 34.0),
+ * Cartographic.fromDegrees(-101.0, 36.0),
+ * Cartographic.fromDegrees(-103.0, 36.0)
+ * ])
+ * }]
+ * }]
+ * }]
+ * }
+ * });
+ */
+ var PolygonGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
+ var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
+ var granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
+ var stRotation = defaultValue(options.stRotation, 0.0);
+ var height = defaultValue(options.height, 0.0);
+
+ var polygonHierarchy = options.polygonHierarchy;
+ if (typeof polygonHierarchy === 'undefined') {
+ throw new DeveloperError('options.polygonHierarchy is required.');
+ }
+
+ // create from a polygon hierarchy
+ // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
+ var polygons = [];
+ var queue = new Queue();
+ queue.enqueue(polygonHierarchy);
+
+ var i;
+
+ while (queue.length !== 0) {
+ var outerNode = queue.dequeue();
+ var outerRing = outerNode.positions;
+
+ if (outerRing.length < 3) {
+ throw new DeveloperError('At least three positions are required.');
+ }
+
+ var numChildren = outerNode.holes ? outerNode.holes.length : 0;
+ if (numChildren === 0) {
+ // The outer polygon is a simple polygon with no nested inner polygon.
+ polygons.push(outerNode.positions);
+ } else {
+ // The outer polygon contains inner polygons
+ var holes = [];
+ for (i = 0; i < numChildren; i++) {
+ var hole = outerNode.holes[i];
+ holes.push(hole.positions);
+
+ var numGrandchildren = 0;
+ if (typeof hole.holes !== 'undefined') {
+ numGrandchildren = hole.holes.length;
+ }
+
+ for (var j = 0; j < numGrandchildren; j++) {
+ queue.enqueue(hole.holes[j]);
+ }
+ }
+ var combinedPolygon = PolygonPipeline.eliminateHoles(outerRing, holes);
+ polygons.push(combinedPolygon);
+ }
+ }
+
+ var outerPositions = polygons[0];
+ // The bounding volume is just around the boundary points, so there could be cases for
+ // contrived polygons on contrived ellipsoids - very oblate ones - where the bounding
+ // volume doesn't cover the polygon.
+ var boundingSphere = BoundingSphere.fromPoints(outerPositions);
+
+ var geometry;
+ var geometries = [];
+ for (var k = 0; k < polygons.length; k++) {
+ geometry = createGeometryFromPositions(ellipsoid, polygons[k], granularity);
+ if (typeof geometry !== 'undefined') {
+ geometries.push(geometry);
+ }
+ }
+
+ geometry = GeometryPipeline.combine(geometries);
+ geometry = PolygonPipeline.scaleToGeodeticHeight(geometry, height, ellipsoid);
+
+ var center = boundingSphere.center;
+ var mag = center.magnitude();
+ ellipsoid.geodeticSurfaceNormal(center, center);
+ Cartesian3.multiplyByScalar(center, mag + height, center);
+
+ var attributes = new GeometryAttributes();
+
+ if (vertexFormat.position) {
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array(geometry.attributes.position.values)
+ });
+ }
+
+ if (vertexFormat.st || vertexFormat.normal || vertexFormat.tangent || vertexFormat.binormal) {
+ // PERFORMANCE_IDEA: Compute before subdivision, then just interpolate during subdivision.
+ // PERFORMANCE_IDEA: Compute with createGeometryFromPositions() for fast path when there's no holes.
+ var cleanedPositions = PolygonPipeline.removeDuplicates(outerPositions);
+ var tangentPlane = EllipsoidTangentPlane.fromPoints(cleanedPositions, ellipsoid);
+ var boundingRectangle = computeBoundingRectangle(tangentPlane, outerPositions, stRotation, scratchBoundingRectangle);
+
+ var origin = appendTextureCoordinatesOrigin;
+ origin.x = boundingRectangle.x;
+ origin.y = boundingRectangle.y;
+
+ var flatPositions = geometry.attributes.position.values;
+ var length = flatPositions.length;
+
+ var textureCoordinates = vertexFormat.st ? new Float32Array(2 * (length / 3)) : undefined;
+ var normals = vertexFormat.normal ? new Float32Array(length) : undefined;
+ var tangents = vertexFormat.tangent ? new Float32Array(length) : undefined;
+ var binormals = vertexFormat.binormal ? new Float32Array(length) : undefined;
+
+ var textureCoordIndex = 0;
+ var normalIndex = 0;
+ var tangentIndex = 0;
+ var binormalIndex = 0;
+
+ var normal = scratchNormal;
+ var tangent = scratchTangent;
+
+ var rotation = Quaternion.fromAxisAngle(tangentPlane._plane.normal, stRotation, appendTextureCoordinatesQuaternion);
+ var textureMatrix = Matrix3.fromQuaternion(rotation, appendTextureCoordinatesMatrix3);
+
+ for (i = 0; i < length; i += 3) {
+ var position = Cartesian3.fromArray(flatPositions, i, appendTextureCoordinatesCartesian3);
+
+ if (vertexFormat.st) {
+ var p = Matrix3.multiplyByVector(textureMatrix, position, scratchPosition);
+ var st = tangentPlane.projectPointOntoPlane(p, appendTextureCoordinatesCartesian2);
+ Cartesian2.subtract(st, origin, st);
+
+ textureCoordinates[textureCoordIndex++] = st.x / boundingRectangle.width;
+ textureCoordinates[textureCoordIndex++] = st.y / boundingRectangle.height;
+ }
+
+ if (vertexFormat.normal) {
+ normal = ellipsoid.geodeticSurfaceNormal(position, normal);
+
+ normals[normalIndex++] = normal.x;
+ normals[normalIndex++] = normal.y;
+ normals[normalIndex++] = normal.z;
+ }
+
+ if (vertexFormat.tangent) {
+ if (!vertexFormat.normal) {
+ ellipsoid.geodeticSurfaceNormal(position, normal);
+ }
+
+ Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent);
+ Matrix3.multiplyByVector(textureMatrix, tangent, tangent);
+ Cartesian3.normalize(tangent, tangent);
+
+ tangents[tangentIndex++] = tangent.x;
+ tangents[tangentIndex++] = tangent.y;
+ tangents[tangentIndex++] = tangent.z;
+ }
+
+ if (vertexFormat.binormal) {
+ if (!vertexFormat.normal) {
+ ellipsoid.geodeticSurfaceNormal(position, normal);
+ }
+
+ if (!vertexFormat.tangent) {
+ Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent);
+ Matrix3.multiplyByVector(textureMatrix, tangent, tangent);
+ }
+
+ var binormal = Cartesian3.cross(tangent, normal, scratchBinormal);
+ Cartesian3.normalize(binormal, binormal);
+
+ binormals[binormalIndex++] = binormal.x;
+ binormals[binormalIndex++] = binormal.y;
+ binormals[binormalIndex++] = binormal.z;
+ }
+ }
+
+ if (vertexFormat.st) {
+ attributes.st = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : textureCoordinates
+ });
+ }
+
+ if (vertexFormat.normal) {
+ attributes.normal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : normals
+ });
+ }
+
+ if (vertexFormat.tangent) {
+ attributes.tangent = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : tangents
+ });
+ }
+
+ if (vertexFormat.binormal) {
+ attributes.binormal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : binormals
+ });
+ }
+ }
+
+ /**
+ * An object containing {@link GeometryAttribute} properties named after each of the
+ * true
values of the {@link VertexFormat} option.
+ *
+ * @type GeometryAttributes
+ *
+ * @see Geometry#attributes
+ */
+ this.attributes = attributes;
+
+ /**
+ * Index data that, along with {@link Geometry#primitiveType}, determines the primitives in the geometry.
+ *
+ * @type Array
+ */
+ this.indices = IndexDatatype.createTypedArray(geometry.attributes.position.values / 3, geometry.indices);
+
+ /**
+ * The type of primitives in the geometry. For this geometry, it is {@link PrimitiveType.TRIANGLES}.
+ *
+ * @type PrimitiveType
+ */
+ this.primitiveType = geometry.primitiveType;
+
+ /**
+ * A tight-fitting bounding sphere that encloses the vertices of the geometry.
+ *
+ * @type BoundingSphere
+ */
+ this.boundingSphere = boundingSphere;
+ };
+
+ /**
+ * Creates a polygon from an array of positions.
+ *
+ * @memberof PolygonGeometry
+ *
+ * @param {Array} options.positions An array of positions that defined the corner points of the polygon.
+ * @param {Number} [options.height=0.0] The height of the polygon.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ * @param {Number} [options.stRotation=0.0] The rotation of the texture coordiantes, in radians. A positive rotation is counter-clockwise.
+ * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference.
+ * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
+ *
+ * @exception {DeveloperError} options.positions is required.
+ * @exception {DeveloperError} At least three positions are required.
+ * @exception {DeveloperError} Duplicate positions result in not enough positions to form a polygon.
+ *
+ * @example
+ * // create a polygon from points
+ * var geometry = new PolygonGeometry({
+ * positions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(-72.0, 40.0),
+ * Cartographic.fromDegrees(-70.0, 35.0),
+ * Cartographic.fromDegrees(-75.0, 30.0),
+ * Cartographic.fromDegrees(-70.0, 30.0),
+ * Cartographic.fromDegrees(-68.0, 40.0)
+ * ])
+ * });
+ */
+ PolygonGeometry.fromPositions = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ if (typeof options.positions === 'undefined') {
+ throw new DeveloperError('options.positions is required.');
+ }
+
+ var newOptions = {
+ polygonHierarchy : {
+ positions : options.positions
+ },
+ height : options.height,
+ vertexFormat : options.vertexFormat,
+ stRotation : options.stRotation,
+ ellipsoid : options.ellipsoid,
+ granularity : options.granularity
+ };
+ return new PolygonGeometry(newOptions);
+ };
+
+ return PolygonGeometry;
+});
+
diff --git a/Source/Core/PolygonPipeline.js b/Source/Core/PolygonPipeline.js
index ef99c8f45110..441177800c51 100644
--- a/Source/Core/PolygonPipeline.js
+++ b/Source/Core/PolygonPipeline.js
@@ -4,10 +4,12 @@ define([
'./Math',
'./Cartesian2',
'./Cartesian3',
+ './Geometry',
+ './GeometryAttribute',
'./Ellipsoid',
'./EllipsoidTangentPlane',
'./defaultValue',
- './pointInsideTriangle2D',
+ './pointInsideTriangle',
'./ComponentDatatype',
'./PrimitiveType',
'./Queue',
@@ -17,10 +19,12 @@ define([
CesiumMath,
Cartesian2,
Cartesian3,
+ Geometry,
+ GeometryAttribute,
Ellipsoid,
EllipsoidTangentPlane,
defaultValue,
- pointInsideTriangle2D,
+ pointInsideTriangle,
ComponentDatatype,
PrimitiveType,
Queue,
@@ -153,7 +157,7 @@ define([
* Returns true if the given point is contained in the list of positions.
*
* @param {Array} positions A list of Cartesian elements defining a polygon.
- * @param {Cartesian} point The point to check.
+ * @param {Cartesian2} point The point to check.
* @returns {Boolean} true>
if point
is found in polygon
, false
otherwise.
*
* @private
@@ -170,11 +174,11 @@ define([
/**
* Given a point inside a polygon, find the nearest point directly to the right that lies on one of the polygon's edges.
*
- * @param {Cartesian} point A point inside the polygon defined by ring
.
+ * @param {Cartesian2} point A point inside the polygon defined by ring
.
* @param {Array} ring A list of Cartesian points defining a polygon.
* @param {Array} [edgeIndices] An array containing the indices two endpoints of the edge containing the intersection.
*
- * @returns {Cartesian} The intersection point.
+ * @returns {Cartesian2} The intersection point.
* @private
*/
function intersectPointWithRing(point, ring, edgeIndices) {
@@ -182,7 +186,7 @@ define([
var minDistance = Number.MAX_VALUE;
var rightmostVertexIndex = getRightmostPositionIndex(ring);
- var intersection = new Cartesian3(ring[rightmostVertexIndex].x, point.y, 0.0);
+ var intersection = new Cartesian2(ring[rightmostVertexIndex].x, point.y);
edgeIndices.push(rightmostVertexIndex);
edgeIndices.push((rightmostVertexIndex + 1) % ring.length);
@@ -263,7 +267,7 @@ define([
var pointsInside = [];
for ( var i = 0; i < reflexVertices.length; i++) {
var vertex = reflexVertices[i];
- if (pointInsideTriangle2D(vertex, innerRingVertex, intersection, p)) {
+ if (pointInsideTriangle(vertex, innerRingVertex, intersection, p)) {
pointsInside.push(vertex);
}
}
@@ -299,7 +303,7 @@ define([
* @param {Array} outerRing An array of Cartesian points defining the outer boundary of the polygon.
* @param {Array} innerRings An array of arrays of Cartesian points, where each array represents a hole in the polygon.
*
- * @return A single list of Cartesian points defining the polygon, including the eliminated inner ring.
+ * @return {Array} A single list of Cartesian points defining the polygon, including the eliminated inner ring.
*
* @private
*/
@@ -370,24 +374,6 @@ define([
return newPolygonVertices;
}
- var c3 = new Cartesian3();
- function getXZIntersectionOffsetPoints(p, p1, u1, v1) {
- p.add(p1.subtract(p, c3).multiplyByScalar(p.y/(p.y-p1.y), c3), u1);
- Cartesian3.clone(u1, v1);
- offsetPointFromXZPlane(u1, true);
- offsetPointFromXZPlane(v1, false);
- }
-
- function offsetPointFromXZPlane(p, isBehind) {
- if (Math.abs(p.y) < CesiumMath.EPSILON11){
- if (isBehind) {
- p.y = -CesiumMath.EPSILON11;
- } else {
- p.y = CesiumMath.EPSILON11;
- }
- }
- }
-
var scaleToGeodeticHeightN = new Cartesian3();
var scaleToGeodeticHeightP = new Cartesian3();
@@ -406,7 +392,7 @@ define([
* @exception {DeveloperError} positions is required.
* @exception {DeveloperError} At least three positions are required.
*/
- cleanUp : function(positions) {
+ removeDuplicates : function(positions) {
if (typeof positions === 'undefined') {
throw new DeveloperError('positions is required.');
}
@@ -518,7 +504,7 @@ define([
var isEar = true;
for ( var n = (nextNode.next ? nextNode.next : remainingPositions.head); n !== previousNode; n = (n.next ? n.next : remainingPositions.head)) {
- if (pointInsideTriangle2D(n.item.position, p0, p1, p2)) {
+ if (pointInsideTriangle(n.item.position, p0, p1, p2)) {
isEar = false;
break;
}
@@ -556,122 +542,6 @@ define([
return indices;
},
- /**
- * Subdivides a {@link Polygon} such that no triangles cross the ±180 degree meridian of an ellipsoid.
- * @memberof PolygonPipeline
- *
- * @param {Array} positions The Cartesian positions of triangles that make up a polygon.
- * @param {Array} indices The indices of positions in the positions array that make up triangles
- *
- * @returns {Object} The full set of indices, including those for positions added for newly created triangles
- *
- * @exception {DeveloperError} positions and indices are required
- * @exception {DeveloperError} At least three indices are required.
- * @exception {DeveloperError} The number of indices must be divisable by three.
- *
- * @see Polygon
- *
- * @example
- * var positions = [new Cartesian3(-1, -1, 0), new Cartesian3(-1, 1, 2), new Cartesian3(-1, 2, 2)];
- * var indices = [0, 1, 2];
- * indices = PolygonPipeline.wrapLongitude(positions, indices);
- */
-
- wrapLongitude : function(positions, indices) {
- if ((typeof positions === 'undefined') ||
- (typeof indices === 'undefined')) {
- throw new DeveloperError('positions and indices are required.');
- }
-
- if (indices.length < 3) {
- throw new DeveloperError('At least three indices are required.');
- }
-
- if (indices.length % 3 !== 0) {
- throw new DeveloperError('The number of indices must be divisable by three.');
- }
-
- var newIndices = [];
-
- var len = indices.length;
- for (var i = 0; i < len; i += 3) {
- var i0 = indices[i];
- var i1 = indices[i + 1];
- var i2 = indices[i + 2];
- var p0 = positions[i0];
- var p1 = positions[i1];
- var p2 = positions[i2];
-
- // In WGS84 coordinates, for a triangle approximately on the
- // ellipsoid to cross the IDL, first it needs to be on the
- // negative side of the plane x = 0.
- if ((p0.x < 0.0) && (p1.x < 0.0) && (p2.x < 0.0)) {
- var p0Behind = p0.y < 0.0;
- var p1Behind = p1.y < 0.0;
- var p2Behind = p2.y < 0.0;
-
- offsetPointFromXZPlane(p0, p0Behind);
- offsetPointFromXZPlane(p1, p1Behind);
- offsetPointFromXZPlane(p2, p2Behind);
-
- var numBehind = 0;
- numBehind += p0Behind ? 1 : 0;
- numBehind += p1Behind ? 1 : 0;
- numBehind += p2Behind ? 1 : 0;
-
- var u1, u2, v1, v2;
-
- if (numBehind === 1 || numBehind === 2) {
- u1 = new Cartesian3();
- u2 = new Cartesian3();
- v1 = new Cartesian3();
- v2 = new Cartesian3();
- }
- var iu1 = positions.length;
- if (numBehind === 1) {
- if (p0Behind) {
- getXZIntersectionOffsetPoints(p0, p1, u1, v1);
- getXZIntersectionOffsetPoints(p0, p2, u2, v2);
- positions.push(u1, u2, v1, v2);
- newIndices.push(i0, iu1, iu1+1, i1, i2, iu1+3, i1, iu1+3, iu1+2);
- } else if (p1Behind) {
- getXZIntersectionOffsetPoints(p1, p0, u1, v1);
- getXZIntersectionOffsetPoints(p1, p2, u2, v2);
- positions.push(u1, u2, v1, v2);
- newIndices.push(i1, iu1, iu1+1, i2, i0, iu1+3, i2, iu1+3, iu1+2);
- } else if (p2Behind) {
- getXZIntersectionOffsetPoints(p2, p0, u1, v1);
- getXZIntersectionOffsetPoints(p2, p1, u2, v2);
- positions.push(u1, u2, v1, v2);
- newIndices.push(i2, iu1, iu1+1, i0, i1, iu1+3, i0, iu1+3, iu1+2);
- }
- } else if (numBehind === 2) {
- if (!p0Behind) {
- getXZIntersectionOffsetPoints(p0, p1, u1, v1);
- getXZIntersectionOffsetPoints(p0, p2, u2, v2);
- positions.push(u1, u2, v1, v2);
- newIndices.push(i1, i2, iu1+1, i1, iu1+1, iu1, i0, iu1+2, iu1+3);
- } else if (!p1Behind) {
- getXZIntersectionOffsetPoints(p1, p2, u1, v1);
- getXZIntersectionOffsetPoints(p1, p0, u2, v2);
- positions.push(u1, u2, v1, v2);
- newIndices.push(i2, i0, iu1+1, i2, iu1+1, iu1, i1, iu1+2, iu1+3);
- } else if (!p2Behind) {
- getXZIntersectionOffsetPoints(p2, p0, u1, v1);
- getXZIntersectionOffsetPoints(p2, p1, u2, v2);
- positions.push(u1, u2, v1, v2);
- newIndices.push(i0, i1, iu1+1, i0, iu1+1, iu1, i2, iu1+2, iu1+3);
- }
- } else {
- newIndices.push(i0, i1, i2);
- }
- } else {
- newIndices.push(i0, i1, i2);
- }
- }
- return newIndices;
- },
-
/**
* DOC_TBA
*
@@ -702,7 +572,7 @@ define([
throw new DeveloperError('The number of indices must be divisable by three.');
}
- granularity = defaultValue(granularity, CesiumMath.toRadians(1.0));
+ granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE);
if (granularity <= 0.0) {
throw new DeveloperError('granularity must be greater than zero.');
}
@@ -823,20 +693,17 @@ define([
flattenedPositions[q++] = item.z;
}
- return {
+ return new Geometry({
attributes : {
- position : {
- componentDatatype : ComponentDatatype.FLOAT,
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
componentsPerAttribute : 3,
values : flattenedPositions
- }
+ })
},
-
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : subdividedIndices
- }]
- };
+ indices : subdividedIndices,
+ primitiveType : PrimitiveType.TRIANGLES
+ });
},
/**
@@ -844,7 +711,7 @@ define([
*
* @exception {DeveloperError} ellipsoid is required.
*/
- scaleToGeodeticHeight : function(mesh, height, ellipsoid) {
+ scaleToGeodeticHeight : function(geometry, height, ellipsoid) {
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
var n = scaleToGeodeticHeightN;
@@ -852,8 +719,8 @@ define([
height = defaultValue(height, 0.0);
- if (typeof mesh !== 'undefined' && typeof mesh.attributes !== 'undefined' && typeof mesh.attributes.position !== 'undefined') {
- var positions = mesh.attributes.position.values;
+ if (typeof geometry !== 'undefined' && typeof geometry.attributes !== 'undefined' && typeof geometry.attributes.position !== 'undefined') {
+ var positions = geometry.attributes.position.values;
var length = positions.length;
for ( var i = 0; i < length; i += 3) {
@@ -872,7 +739,7 @@ define([
}
}
- return mesh;
+ return geometry;
},
/**
diff --git a/Source/Core/PolylinePipeline.js b/Source/Core/PolylinePipeline.js
index 51e1d4750a07..147a6414f1a8 100644
--- a/Source/Core/PolylinePipeline.js
+++ b/Source/Core/PolylinePipeline.js
@@ -1,6 +1,7 @@
/*global define*/
define([
'./defaultValue',
+ './DeveloperError',
'./Cartesian3',
'./Cartesian4',
'./IntersectionTests',
@@ -8,6 +9,7 @@ define([
'./Plane'
], function(
defaultValue,
+ DeveloperError,
Cartesian3,
Cartesian4,
IntersectionTests,
@@ -112,5 +114,49 @@ define([
};
};
+ /**
+ * Removes adjacent duplicate positions in an array of positions.
+ *
+ * @memberof PolylinePipeline
+ *
+ * @param {Array} positions The array of positions. Each element is usually a {@see Cartesian3}, but all that is required is that the object have an equals
function.
+ *
+ * @returns {Array} A new array of positions with no adjacent duplicate positions. Positions are shallow copied.
+ *
+ * @exception {DeveloperError} positions is required.
+ *
+ * @example
+ * // Returns [(1.0, 1.0, 1.0), (2.0, 2.0, 2.0)]
+ * var positions = [
+ * new Cartesian3(1.0, 1.0, 1.0),
+ * new Cartesian3(1.0, 1.0, 1.0),
+ * new Cartesian3(2.0, 2.0, 2.0)];
+ * var nonDuplicatePositions = PolylinePipeline.removeDuplicates(positions);
+ */
+ PolylinePipeline.removeDuplicates = function(positions) {
+ if (typeof positions === 'undefined') {
+ throw new DeveloperError('positions is required.');
+ }
+
+ var length = positions.length;
+ if (length < 2) {
+ return positions.slice(0);
+ }
+
+ var cleanedPositions = [];
+ cleanedPositions.push(positions[0]);
+
+ for (var i = 1; i < length; ++i) {
+ var v0 = positions[i - 1];
+ var v1 = positions[i];
+
+ if (!v0.equals(v1)) {
+ cleanedPositions.push(v1); // Shallow copy!
+ }
+ }
+
+ return cleanedPositions;
+ };
+
return PolylinePipeline;
});
diff --git a/Source/Core/Shapes.js b/Source/Core/Shapes.js
index 296b1c052f7e..fa62956ca3bb 100644
--- a/Source/Core/Shapes.js
+++ b/Source/Core/Shapes.js
@@ -110,7 +110,7 @@ define([
throw new DeveloperError('radius must be greater than zero.');
}
- granularity = defaultValue(granularity, CesiumMath.toRadians(1.0));
+ granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE);
if (granularity <= 0.0) {
throw new DeveloperError('granularity must be greater than zero.');
}
@@ -162,7 +162,7 @@ define([
}
bearing = bearing || 0.0;
- granularity = defaultValue(granularity, CesiumMath.toRadians(1.0));
+ granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE);
if (granularity <= 0.0) {
throw new DeveloperError('granularity must be greater than zero.');
diff --git a/Source/Core/ShowGeometryInstanceAttribute.js b/Source/Core/ShowGeometryInstanceAttribute.js
new file mode 100644
index 000000000000..c82a823ed5b8
--- /dev/null
+++ b/Source/Core/ShowGeometryInstanceAttribute.js
@@ -0,0 +1,108 @@
+/*global define*/
+define([
+ './defaultValue',
+ './ComponentDatatype',
+ './DeveloperError'
+ ], function(
+ defaultValue,
+ ComponentDatatype,
+ DeveloperError) {
+ "use strict";
+
+ /**
+ * Value and type information for per-instance geometry attribute that determines if the geometry instance will be shown.
+ *
+ * @alias ShowGeometryInstanceAttribute
+ * @constructor
+ *
+ * @param {Boolean} [show=true] Determines if the geometry instance will be shown.
+ *
+ * @example
+ * var instance = new GeometryInstance({
+ * geometry : new BoxGeometry({
+ * vertexFormat : VertexFormat.POSITION_AND_NORMAL,
+ * dimensions : new Cartesian3(1000000.0, 1000000.0, 500000.0)
+ * }),
+ * modelMatrix : Matrix4.multiplyByTranslation(Transforms.eastNorthUpToFixedFrame(
+ * ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883))), new Cartesian3(0.0, 0.0, 1000000.0)),
+ * id : 'box',
+ * attributes : {
+ * show : new ShowGeometryInstanceAttribute(false)
+ * }
+ * });
+ *
+ * @see GeometryInstance
+ * @see GeometryInstanceAttribute
+ */
+ var ShowGeometryInstanceAttribute = function(show) {
+ show = defaultValue(show, true);
+
+ /**
+ * The datatype of each component in the attribute, e.g., individual elements in
+ * {@link ShowGeometryInstanceAttribute#value}.
+ *
+ * @type ComponentDatatype
+ *
+ * @default {@link ComponentDatatype.UNSIGNED_BYTE}
+ *
+ * @readonly
+ */
+ this.componentDatatype = ComponentDatatype.UNSIGNED_BYTE;
+
+ /**
+ * The number of components in the attributes, i.e., {@link ShowGeometryInstanceAttribute#value}.
+ *
+ * @type Number
+ *
+ * @default 1
+ *
+ * @readonly
+ */
+ this.componentsPerAttribute = 1;
+
+ /**
+ * When true
and componentDatatype
is an integer format,
+ * indicate that the components should be mapped to the range [0, 1] (unsigned)
+ * or [-1, 1] (signed) when they are accessed as floating-point for rendering.
+ *
+ * @type Boolean
+ *
+ * @default true
+ *
+ * @readonly
+ */
+ this.normalize = true;
+
+ /**
+ * The values for the attributes stored in a typed array.
+ *
+ * @type Uint8Array
+ *
+ * @default [1.0]
+ */
+ this.value = ShowGeometryInstanceAttribute.toValue(show);
+ };
+
+ /**
+ * Converts a boolean show to a typed array that can be used to assign a show attribute.
+ *
+ * @param {Boolean} show The show value.
+ *
+ * @returns {Uint8Array} The typed array in the attribute's format.
+ *
+ * @exception {DeveloperError} show is required.
+ *
+ * @example
+ * var attributes = primitive.getGeometryInstanceAttributes('an id');
+ * attributes.show = ShowGeometryInstanceAttribute.toValue(true);
+ */
+ ShowGeometryInstanceAttribute.toValue = function(show) {
+ if (typeof show === 'undefined') {
+ throw new DeveloperError('show is required.');
+ }
+
+ return new Uint8Array([show]);
+ };
+
+ return ShowGeometryInstanceAttribute;
+});
diff --git a/Source/Core/SimplePolylineGeometry.js b/Source/Core/SimplePolylineGeometry.js
new file mode 100644
index 000000000000..fa7d8718a07f
--- /dev/null
+++ b/Source/Core/SimplePolylineGeometry.js
@@ -0,0 +1,114 @@
+/*global define*/
+define([
+ './DeveloperError',
+ './ComponentDatatype',
+ './IndexDatatype',
+ './PrimitiveType',
+ './defaultValue',
+ './BoundingSphere',
+ './GeometryAttribute',
+ './GeometryAttributes'
+ ], function(
+ DeveloperError,
+ ComponentDatatype,
+ IndexDatatype,
+ PrimitiveType,
+ defaultValue,
+ BoundingSphere,
+ GeometryAttribute,
+ GeometryAttributes) {
+ "use strict";
+
+ /**
+ * A {@link Geometry} that represents a polyline modeled as a line strip; the first two positions define a line segment,
+ * and each additional position defines a line segment from the previous position.
+ *
+ * @alias SimplePolylineGeometry
+ * @constructor
+ *
+ * @param {Array} [options.positions] An array of {@link Cartesian3} defining the positions in the polyline as a line strip.
+ *
+ * @exception {DeveloperError} At least two positions are required.
+ *
+ * @example
+ * // A polyline with two connected line segments
+ * var geometry = new SimplePolylineGeometry({
+ * positions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(0.0, 0.0),
+ * Cartographic.fromDegrees(5.0, 0.0),
+ * Cartographic.fromDegrees(5.0, 5.0)
+ * ])
+ * });
+ */
+ var SimplePolylineGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+ var positions = options.positions;
+
+ if ((typeof positions === 'undefined') || (positions.length < 2)) {
+ throw new DeveloperError('At least two positions are required.');
+ }
+
+ var i;
+ var j = 0;
+ var numberOfPositions = positions.length;
+ var positionValues = new Float64Array(numberOfPositions * 3);
+
+ for (i = 0; i < numberOfPositions; ++i) {
+ var p = positions[i];
+
+ positionValues[j++] = p.x;
+ positionValues[j++] = p.y;
+ positionValues[j++] = p.z;
+ }
+
+ var attributes = new GeometryAttributes();
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : positionValues
+ });
+
+ // From line strip to lines
+ var numberOfIndices = 2 * (numberOfPositions - 1);
+ var indices = IndexDatatype.createTypedArray(numberOfPositions, numberOfIndices);
+
+ j = 0;
+ for (i = 0; i < numberOfPositions - 1; ++i) {
+ indices[j++] = i;
+ indices[j++] = i + 1;
+ }
+
+ /**
+ * An object containing {@link GeometryAttribute} properties named after each of the
+ * true
values of the {@link VertexFormat} option.
+ *
+ * @type Object
+ *
+ * @see Geometry#attributes
+ */
+ this.attributes = attributes;
+
+ /**
+ * Index data that, along with {@link Geometry#primitiveType}, determines the primitives in the geometry.
+ *
+ * @type Array
+ */
+ this.indices = indices;
+
+ /**
+ * The type of primitives in the geometry. For this geometry, it is {@link PrimitiveType.LINES}.
+ *
+ * @type PrimitiveType
+ */
+ this.primitiveType = PrimitiveType.LINES;
+
+ /**
+ * A tight-fitting bounding sphere that encloses the vertices of the geometry.
+ *
+ * @type BoundingSphere
+ */
+ this.boundingSphere = BoundingSphere.fromPoints(positions);
+ };
+
+ return SimplePolylineGeometry;
+});
\ No newline at end of file
diff --git a/Source/Core/Tipsify.js b/Source/Core/Tipsify.js
index c62884a5dc00..2adde8cd21aa 100644
--- a/Source/Core/Tipsify.js
+++ b/Source/Core/Tipsify.js
@@ -25,7 +25,7 @@ define([
* Calculates the average cache miss ratio (ACMR) for a given set of indices.
*
* @param {Array} description.indices Lists triads of numbers corresponding to the indices of the vertices
- * in the vertex buffer that define the mesh's triangles.
+ * in the vertex buffer that define the geometry's triangles.
* @param {Number} [description.maximumIndex] The maximum value of the elements in args.indices
.
* If not supplied, this value will be computed.
* @param {Number} [description.cacheSize=24] The number of vertices that can be stored in the cache at any one time.
@@ -100,7 +100,7 @@ define([
* Optimizes triangles for the post-vertex shader cache.
*
* @param {Array} description.indices Lists triads of numbers corresponding to the indices of the vertices
- * in the vertex buffer that define the mesh's triangles.
+ * in the vertex buffer that define the geometry's triangles.
* @param {Number} [description.maximumIndex] The maximum value of the elements in args.indices
.
* If not supplied, this value will be computed.
* @param {Number} [description.cacheSize=24] The number of vertices that can be stored in the cache at any one time.
diff --git a/Source/Core/VertexFormat.js b/Source/Core/VertexFormat.js
new file mode 100644
index 000000000000..6cf0197673ac
--- /dev/null
+++ b/Source/Core/VertexFormat.js
@@ -0,0 +1,182 @@
+/*global define*/
+define([
+ './defaultValue',
+ './freezeObject'
+ ], function(
+ defaultValue,
+ freezeObject) {
+ "use strict";
+
+ /**
+ * A vertex format defines what attributes make up a vertex. A VertexFormat can be provided
+ * to a {@link Geometry} to request that certain properties be computed, e.g., just position,
+ * position and normal, etc.
+ *
+ * @param {Object} [options=undefined] An object with boolean properties corresponding to VertexFormat properties as shown in the code example.
+ *
+ * @alias VertexFormat
+ * @constructor
+ *
+ * @example
+ * // Create a vertex format with position and 2D texture coordinate attributes.
+ * var format = new VertexFormat({
+ * position : true,
+ * st : true
+ * });
+ *
+ * @see Geometry#attributes
+ */
+ var VertexFormat = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ /**
+ * When true
, the vertex has a 3D position attribute.
+ *
+ * 64-bit floating-point (for precision). 3 components per attribute.
+ *
+ *
+ * @type Boolean
+ *
+ * @default false
+ */
+ this.position = defaultValue(options.position, false);
+
+ /**
+ * When true
, the vertex has a normal attribute (normalized), which is commonly used for lighting.
+ *
+ * 32-bit floating-point. 3 components per attribute.
+ *
+ *
+ * @type Boolean
+ *
+ * @default false
+ */
+ this.normal = defaultValue(options.normal, false);
+
+ /**
+ * When true
, the vertex has a 2D texture coordinate attribute.
+ *
+ * 32-bit floating-point. 2 components per attribute
+ *
+ *
+ * @type Boolean
+ *
+ * @default false
+ */
+ this.st = defaultValue(options.st, false);
+
+ /**
+ * When true
, the vertex has a binormal attribute (normalized), which is used for tangent-space effects like bump mapping.
+ *
+ * 32-bit floating-point. 3 components per attribute.
+ *
+ *
+ * @type Boolean
+ *
+ * @default false
+ */
+ this.binormal = defaultValue(options.binormal, false);
+
+ /**
+ * When true
, the vertex has a tangent attribute (normalized), which is used for tangent-space effects like bump mapping.
+ *
+ * 32-bit floating-point. 3 components per attribute.
+ *
+ *
+ * @type Boolean
+ *
+ * @default false
+ */
+ this.tangent = defaultValue(options.tangent, false);
+ };
+
+ /**
+ * An immutable vertex format with only a position attribute.
+ *
+ * @memberof VertexFormat
+ *
+ * @see VertexFormat#position
+ */
+ VertexFormat.POSITION_ONLY = freezeObject(new VertexFormat({
+ position : true
+ }));
+
+ /**
+ * An immutable vertex format with position and normal attributes.
+ * This is compatible with per-instance color appearances like {@link PerInstanceColorAppearance}.
+ *
+ * @memberof VertexFormat
+ *
+ * @see VertexFormat#position
+ * @see VertexFormat#normal
+ */
+ VertexFormat.POSITION_AND_NORMAL = freezeObject(new VertexFormat({
+ position : true,
+ normal : true
+ }));
+
+ /**
+ * An immutable vertex format with position, normal, and st attributes.
+ * This is compatible with {@link MaterialAppearance} when {@link MaterialAppearance#materialSupport}
+ * is TEXTURED/code>.
+ *
+ * @memberof VertexFormat
+ *
+ * @see VertexFormat#position
+ * @see VertexFormat#normal
+ * @see VertexFormat#st
+ */
+ VertexFormat.POSITION_NORMAL_AND_ST = freezeObject(new VertexFormat({
+ position : true,
+ normal : true,
+ st : true
+ }));
+
+ /**
+ * An immutable vertex format with position and st attributes.
+ * This is compatible with {@link EllipsoidSurfaceAppearance}.
+ *
+ * @memberof VertexFormat
+ *
+ * @see VertexFormat#position
+ * @see VertexFormat#st
+ */
+ VertexFormat.POSITION_AND_ST = freezeObject(new VertexFormat({
+ position : true,
+ st : true
+ }));
+
+ /**
+ * An immutable vertex format with all well-known attributes: position, normal, st, binormal, and tangent.
+ *
+ * @memberof VertexFormat
+ *
+ * @see VertexFormat#position
+ * @see VertexFormat#normal
+ * @see VertexFormat#st
+ * @see VertexFormat#binormal
+ * @see VertexFormat#tangent
+ */
+ VertexFormat.ALL = freezeObject(new VertexFormat({
+ position : true,
+ normal : true,
+ st : true,
+ binormal : true,
+ tangent : true
+ }));
+
+ /**
+ * An immutable vertex format with position, normal, and st attributes.
+ * This is compatible with most appearances and materials; however
+ * normal and st attributes are not always required. When this is
+ * known in advance, another VertexFormat
should be used.
+ *
+ * @memberof VertexFormat
+ *
+ * @see VertexFormat#position
+ * @see VertexFormat#normal
+ */
+ VertexFormat.DEFAULT = VertexFormat.POSITION_NORMAL_AND_ST;
+
+ return VertexFormat;
+});
\ No newline at end of file
diff --git a/Source/Core/WallGeometry.js b/Source/Core/WallGeometry.js
new file mode 100644
index 000000000000..aa6d0b65907c
--- /dev/null
+++ b/Source/Core/WallGeometry.js
@@ -0,0 +1,467 @@
+/*global define*/
+define([
+ './defaultValue',
+ './BoundingSphere',
+ './Cartesian3',
+ './Cartographic',
+ './ComponentDatatype',
+ './IndexDatatype',
+ './DeveloperError',
+ './Ellipsoid',
+ './EllipsoidTangentPlane',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './Math',
+ './PolylinePipeline',
+ './PolygonPipeline',
+ './PrimitiveType',
+ './VertexFormat',
+ './WindingOrder'
+ ], function(
+ defaultValue,
+ BoundingSphere,
+ Cartesian3,
+ Cartographic,
+ ComponentDatatype,
+ IndexDatatype,
+ DeveloperError,
+ Ellipsoid,
+ EllipsoidTangentPlane,
+ GeometryAttribute,
+ GeometryAttributes,
+ CesiumMath,
+ PolylinePipeline,
+ PolygonPipeline,
+ PrimitiveType,
+ VertexFormat,
+ WindingOrder) {
+ "use strict";
+
+ function removeDuplicates(positions, topHeights, bottomHeights, cleanedPositions, cleanedTopHeights, cleanedBottomHeights) {
+ var length = positions.length;
+
+ cleanedPositions.push(positions[0]);
+
+ if (typeof topHeights !== 'undefined') {
+ cleanedTopHeights.push(topHeights[0]);
+ }
+
+ if (typeof bottomHeights !== 'undefined') {
+ cleanedBottomHeights.push(bottomHeights[0]);
+ }
+
+ for (var i = 1; i < length; ++i) {
+ var v0 = positions[i - 1];
+ var v1 = positions[i];
+
+ if (!Cartesian3.equals(v0, v1)) {
+ cleanedPositions.push(v1); // Shallow copy!
+
+ if (typeof topHeights !== 'undefined') {
+ cleanedTopHeights.push(topHeights[i]);
+ }
+
+ if (typeof bottomHeights !== 'undefined') {
+ cleanedBottomHeights.push(bottomHeights[i]);
+ }
+ }
+ }
+ }
+
+ var scratchCartographic = new Cartographic();
+ var scratchCartesian3Position1 = new Cartesian3();
+ var scratchCartesian3Position2 = new Cartesian3();
+ var scratchCartesian3Position3 = new Cartesian3();
+ var scratchBinormal = new Cartesian3();
+ var scratchTangent = new Cartesian3();
+ var scratchNormal = new Cartesian3();
+
+ /**
+ * A {@link Geometry} that represents a wall, which is similar to a KML line string. A wall is defined by a series of points,
+ * which extrude down to the ground. Optionally, they can extrude downwards to a specified height.
+ *
+ * @alias WallGeometry
+ * @constructor
+ *
+ * @param {Array} positions An array of Cartesian objects, which are the points of the wall.
+ * @param {Array} [maximumHeights] An array parallel to positions
that give the maximum height of the
+ * wall at positions
. If undefined, the height of each position in used.
+ * @param {Array} [minimumHeights] An array parallel to positions
that give the minimum height of the
+ * wall at positions
. If undefined, the height at each position is 0.0.
+ * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid for coordinate manipulation
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ *
+ * @exception {DeveloperError} positions is required.
+ * @exception {DeveloperError} positions and maximumHeights must have the same length.
+ * @exception {DeveloperError} positions and minimumHeights must have the same length.
+ *
+ * @example
+ * var positions = [
+ * Cartographic.fromDegrees(19.0, 47.0, 10000.0),
+ * Cartographic.fromDegrees(19.0, 48.0, 10000.0),
+ * Cartographic.fromDegrees(20.0, 48.0, 10000.0),
+ * Cartographic.fromDegrees(20.0, 47.0, 10000.0),
+ * Cartographic.fromDegrees(19.0, 47.0, 10000.0)
+ * ];
+ *
+ * // create a wall that spans from ground level to 10000 meters
+ * var wall = new WallGeometry({
+ * positions : ellipsoid.cartographicArrayToCartesianArray(positions)
+ * });
+ */
+ var WallGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var wallPositions = options.positions;
+ var maximumHeights = options.maximumHeights;
+ var minimumHeights = options.minimumHeights;
+ var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
+ var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
+
+ if (typeof wallPositions === 'undefined') {
+ throw new DeveloperError('positions is required.');
+ }
+
+ if (typeof maximumHeights !== 'undefined' && maximumHeights.length !== wallPositions.length) {
+ throw new DeveloperError('positions and maximumHeights must have the same length.');
+ }
+
+ if (typeof minimumHeights !== 'undefined' && minimumHeights.length !== wallPositions.length) {
+ throw new DeveloperError('positions and minimumHeights must have the same length.');
+ }
+
+ var cleanedPositions = [];
+ var cleanedMaximumHeights = (typeof maximumHeights !== 'undefined') ? [] : undefined;
+ var cleanedMinimumHeights = (typeof minimumHeights !== 'undefined') ? [] : undefined;
+ removeDuplicates(wallPositions, maximumHeights, minimumHeights, cleanedPositions, cleanedMaximumHeights, cleanedMinimumHeights);
+
+ wallPositions = cleanedPositions;
+ maximumHeights = cleanedMaximumHeights;
+ minimumHeights = cleanedMinimumHeights;
+
+ if (wallPositions.length >= 3) {
+ // Order positions counter-clockwise
+ var tangentPlane = EllipsoidTangentPlane.fromPoints(wallPositions, ellipsoid);
+ var positions2D = tangentPlane.projectPointsOntoPlane(wallPositions);
+
+ if (PolygonPipeline.computeWindingOrder2D(positions2D) === WindingOrder.CLOCKWISE) {
+ wallPositions.reverse();
+
+ if (typeof maximumHeights !== 'undefined') {
+ maximumHeights.reverse();
+ }
+
+ if (typeof minimumHeights !== 'undefined') {
+ minimumHeights.reverse();
+ }
+ }
+ }
+
+ var i;
+ var length = wallPositions.length;
+ var size = 2 * (length - 1) * 2 * 3;
+
+ var positions = vertexFormat.position ? new Float64Array(size) : undefined;
+ var normals = vertexFormat.normal ? new Float32Array(size) : undefined;
+ var tangents = vertexFormat.tangent ? new Float32Array(size) : undefined;
+ var binormals = vertexFormat.binormal ? new Float32Array(size) : undefined;
+ var textureCoordinates = vertexFormat.st ? new Float32Array(size / 3 * 2) : undefined;
+
+ var positionIndex = 0;
+ var normalIndex = 0;
+ var tangentIndex = 0;
+ var binormalIndex = 0;
+ var textureCoordIndex = 0;
+
+ // add lower and upper points one after the other, lower
+ // points being even and upper points being odd
+ var normal = scratchNormal;
+ var tangent = scratchTangent;
+ var binormal = scratchBinormal;
+ var recomputeNormal = true;
+ for (i = 0; i < length - 1; ++i) {
+ for (var j = 0; j < 2; ++j) {
+ var index = i + j;
+
+ var pos = wallPositions[index];
+ var c = ellipsoid.cartesianToCartographic(pos, scratchCartographic);
+
+ if (typeof maximumHeights !== 'undefined') {
+ c.height = maximumHeights[index];
+ }
+ var topPosition = ellipsoid.cartographicToCartesian(c, scratchCartesian3Position1);
+
+ c.height = 0.0;
+ if (typeof minimumHeights !== 'undefined') {
+ c.height += minimumHeights[index];
+ } else {
+ c.height = 0.0;
+ }
+
+ var bottomPosition = ellipsoid.cartographicToCartesian(c, scratchCartesian3Position2);
+
+ if (vertexFormat.position) {
+ // insert the lower point
+ positions[positionIndex++] = bottomPosition.x;
+ positions[positionIndex++] = bottomPosition.y;
+ positions[positionIndex++] = bottomPosition.z;
+
+ // insert the upper point
+ positions[positionIndex++] = topPosition.x;
+ positions[positionIndex++] = topPosition.y;
+ positions[positionIndex++] = topPosition.z;
+ }
+
+ if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.binormal) {
+ var nextPosition;
+ if (index + 1 < length) {
+ nextPosition = Cartesian3.clone(wallPositions[index + 1], scratchCartesian3Position3);
+ }
+
+ if (recomputeNormal) {
+ nextPosition = nextPosition.subtract(topPosition, nextPosition);
+ bottomPosition = bottomPosition.subtract(topPosition, bottomPosition);
+ normal = Cartesian3.cross(bottomPosition, nextPosition, normal).normalize(normal);
+ if (vertexFormat.tangent) {
+ tangent = nextPosition.normalize(tangent);
+ }
+ if (vertexFormat.binormal) {
+ binormal = Cartesian3.cross(normal, tangent, binormal).normalize(binormal);
+ }
+ recomputeNormal = false;
+ }
+
+ if (j === 1) {
+ recomputeNormal = true;
+ }
+
+ if (vertexFormat.normal) {
+ normals[normalIndex++] = normal.x;
+ normals[normalIndex++] = normal.y;
+ normals[normalIndex++] = normal.z;
+
+ normals[normalIndex++] = normal.x;
+ normals[normalIndex++] = normal.y;
+ normals[normalIndex++] = normal.z;
+ }
+
+ if (vertexFormat.tangent) {
+ tangents[tangentIndex++] = tangent.x;
+ tangents[tangentIndex++] = tangent.y;
+ tangents[tangentIndex++] = tangent.z;
+
+ tangents[tangentIndex++] = tangent.x;
+ tangents[tangentIndex++] = tangent.y;
+ tangents[tangentIndex++] = tangent.z;
+ }
+
+ if (vertexFormat.binormal) {
+ binormals[binormalIndex++] = binormal.x;
+ binormals[binormalIndex++] = binormal.y;
+ binormals[binormalIndex++] = binormal.z;
+
+ binormals[binormalIndex++] = binormal.x;
+ binormals[binormalIndex++] = binormal.y;
+ binormals[binormalIndex++] = binormal.z;
+ }
+ }
+
+ if (vertexFormat.st) {
+ var s = index / (length - 1);
+
+ textureCoordinates[textureCoordIndex++] = s;
+ textureCoordinates[textureCoordIndex++] = 0.0;
+
+ textureCoordinates[textureCoordIndex++] = s;
+ textureCoordinates[textureCoordIndex++] = 1.0;
+ }
+ }
+ }
+
+ var attributes = new GeometryAttributes();
+
+ if (vertexFormat.position) {
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : positions
+ });
+ }
+
+ if (vertexFormat.normal) {
+ attributes.normal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : normals
+ });
+ }
+
+ if (vertexFormat.tangent) {
+ attributes.tangent = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : tangents
+ });
+ }
+
+ if (vertexFormat.binormal) {
+ attributes.binormal = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : binormals
+ });
+ }
+
+ if (vertexFormat.st) {
+ attributes.st = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : textureCoordinates
+ });
+ }
+
+
+ // prepare the side walls, two triangles for each wall
+ //
+ // A (i+1) B (i+3) E
+ // +--------+-------+
+ // | / | /| triangles: A C B
+ // | / | / | B C D
+ // | / | / |
+ // | / | / |
+ // | / | / |
+ // | / | / |
+ // +--------+-------+
+ // C (i) D (i+2) F
+ //
+
+ var numVertices = size / 3;
+ size -= 6;
+ var indices = IndexDatatype.createTypedArray(numVertices, size);
+
+ var edgeIndex = 0;
+ for (i = 0; i < numVertices - 2; i += 2) {
+ var LL = i;
+ var LR = i + 2;
+ var pl = Cartesian3.fromArray(positions, LL * 3, scratchCartesian3Position1);
+ var pr = Cartesian3.fromArray(positions, LR * 3, scratchCartesian3Position2);
+ if (Cartesian3.equalsEpsilon(pl, pr, CesiumMath.EPSILON6)) {
+ continue;
+ }
+ var UL = i + 1;
+ var UR = i + 3;
+
+ indices[edgeIndex++] = UL;
+ indices[edgeIndex++] = LL;
+ indices[edgeIndex++] = UR;
+ indices[edgeIndex++] = UR;
+ indices[edgeIndex++] = LL;
+ indices[edgeIndex++] = LR;
+ }
+
+ /**
+ * An object containing {@link GeometryAttribute} properties named after each of the
+ * true
values of the {@link VertexFormat} option.
+ *
+ * @type GeometryAttributes
+ *
+ * @see Geometry#attributes
+ */
+ this.attributes = attributes;
+
+ /**
+ * Index data that, along with {@link Geometry#primitiveType}, determines the primitives in the geometry.
+ *
+ * @type Array
+ */
+ this.indices = indices;
+
+ /**
+ * The type of primitives in the geometry. For this geometry, it is {@link PrimitiveType.TRIANGLES}.
+ *
+ * @type PrimitiveType
+ */
+ this.primitiveType = PrimitiveType.TRIANGLES;
+
+ /**
+ * A tight-fitting bounding sphere that encloses the vertices of the geometry.
+ *
+ * @type BoundingSphere
+ */
+ this.boundingSphere = new BoundingSphere.fromVertices(positions);
+ };
+
+ /**
+ * A {@link Geometry} that represents a wall, which is similar to a KML line string. A wall is defined by a series of points,
+ * which extrude down to the ground. Optionally, they can extrude downwards to a specified height.
+ *
+ * @memberof WallGeometry
+ *
+ * @param {Array} positions An array of Cartesian objects, which are the points of the wall.
+ * @param {Number} [maximumHeight] A constant that defines the maximum height of the
+ * wall at positions
. If undefined, the height of each position in used.
+ * @param {Number} [minimumHeight] A constant that defines the minimum height of the
+ * wall at positions
. If undefined, the height at each position is 0.0.
+ * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid for coordinate manipulation
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ *
+ * @exception {DeveloperError} positions is required.
+ *
+ * @example
+ * var positions = [
+ * Cartographic.fromDegrees(19.0, 47.0, 10000.0),
+ * Cartographic.fromDegrees(19.0, 48.0, 10000.0),
+ * Cartographic.fromDegrees(20.0, 48.0, 10000.0),
+ * Cartographic.fromDegrees(20.0, 47.0, 10000.0),
+ * Cartographic.fromDegrees(19.0, 47.0, 10000.0)
+ * ];
+ *
+ * // create a wall that spans from 10000 meters to 20000 meters
+ * var wall = new WallGeometry({
+ * positions : ellipsoid.cartographicArrayToCartesianArray(positions),
+ * topHeight : 20000.0,
+ * bottomHeight : 10000.0
+ * });
+ */
+ WallGeometry.fromConstantHeights = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var positions = options.positions;
+ if (typeof positions === 'undefined') {
+ throw new DeveloperError('options.positions is required.');
+ }
+
+ var minHeights;
+ var maxHeights;
+
+ var min = options.minimumHeight;
+ var max = options.maximumHeight;
+ if (typeof min !== 'undefined' || typeof max !== 'undefined') {
+ var length = positions.length;
+ minHeights = (typeof min !== 'undefined') ? new Array(length) : undefined;
+ maxHeights = (typeof max !== 'undefined') ? new Array(length) : undefined;
+
+ for (var i = 0; i < length; ++i) {
+ if (typeof min !== 'undefined') {
+ minHeights[i] = min;
+ }
+
+ if (typeof max !== 'undefined') {
+ maxHeights[i] = max;
+ }
+ }
+ }
+
+ var newOptions = {
+ positions : positions,
+ maximumHeights : maxHeights,
+ minimumHeights : minHeights,
+ ellipsoid : options.ellipsoid,
+ vertexFormat : options.vertexFormat
+ };
+ return new WallGeometry(newOptions);
+ };
+
+ return WallGeometry;
+});
+
diff --git a/Source/Core/barycentricCoordinates.js b/Source/Core/barycentricCoordinates.js
new file mode 100644
index 000000000000..f01baa85e342
--- /dev/null
+++ b/Source/Core/barycentricCoordinates.js
@@ -0,0 +1,65 @@
+/*global define*/
+define([
+ './Cartesian3',
+ './DeveloperError'
+ ], function(
+ Cartesian3,
+ DeveloperError) {
+ "use strict";
+
+ var scratchCartesian1 = new Cartesian3();
+ var scratchCartesian2 = new Cartesian3();
+ var scratchCartesian3 = new Cartesian3();
+
+ /**
+ * Computes the barycentric coordinates for a point with respect to a triangle.
+ *
+ * @exports pointInsideTriangle
+ *
+ * @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.
+ *
+ * @return {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
+ *
+ * @exception {DeveloperError} point, p0, p1, and p2 are required.
+ *
+ * @example
+ * // Returns Cartesian3.UNIT_X
+ * var p = new Cartesian3(-1.0, 0.0, 0.0);
+ * var b = barycentricCoordinates(p,
+ * new Cartesian3(-1.0, 0.0, 0.0),
+ * new Cartesian3( 1.0, 0.0, 0.0),
+ * new Cartesian3( 0.0, 1.0, 1.0));
+ */
+ var barycentricCoordinates = function(point, p0, p1, p2, result) {
+ if (typeof point === 'undefined' || typeof p0 === 'undefined' || typeof p1 === 'undefined' || typeof p2 === 'undefined') {
+ throw new DeveloperError('point, p0, p1, and p2 are required.');
+ }
+
+ if (typeof result === 'undefined') {
+ result = new Cartesian3();
+ }
+
+ // Implementation based on http://www.blackpawn.com/texts/pointinpoly/default.html.
+ var v0 = p1.subtract(p0, scratchCartesian1);
+ var v1 = p2.subtract(p0, scratchCartesian2);
+ var v2 = point.subtract(p0, scratchCartesian3);
+
+ var dot00 = v0.dot(v0);
+ var dot01 = v0.dot(v1);
+ var dot02 = v0.dot(v2);
+ var dot11 = v1.dot(v1);
+ var dot12 = v1.dot(v2);
+
+ var q = 1.0 / (dot00 * dot11 - dot01 * dot01);
+ result.y = (dot11 * dot02 - dot01 * dot12) * q;
+ result.z = (dot00 * dot12 - dot01 * dot02) * q;
+ result.x = 1.0 - result.y - result.z;
+ return result;
+ };
+
+ return barycentricCoordinates;
+});
\ No newline at end of file
diff --git a/Source/Core/pointInsideTriangle.js b/Source/Core/pointInsideTriangle.js
new file mode 100644
index 000000000000..9f2cfcca3d6b
--- /dev/null
+++ b/Source/Core/pointInsideTriangle.js
@@ -0,0 +1,42 @@
+/*global define*/
+define([
+ './barycentricCoordinates',
+ './Cartesian3',
+ './DeveloperError'
+ ], function(
+ barycentricCoordinates,
+ Cartesian3,
+ DeveloperError) {
+ "use strict";
+
+ var coords = new Cartesian3();
+
+ /**
+ * Determines if a point is inside a triangle.
+ *
+ * @exports pointInsideTriangle
+ *
+ * @param {Cartesian2|Cartesian3} point The point to test.
+ * @param {Cartesian2|Cartesian3} p0 The first point of the triangle.
+ * @param {Cartesian2|Cartesian3} p1 The second point of the triangle.
+ * @param {Cartesian2|Cartesian3} p2 The third point of the triangle.
+ *
+ * @return {Boolean} true
if the point is inside the triangle; otherwise, false
.
+ *
+ * @exception {DeveloperError} point, p0, p1, and p2 are required.
+ *
+ * @example
+ * // Returns true
+ * var p = new Cartesian2(0.25, 0.25);
+ * var b = pointInsideTriangle(p,
+ * new Cartesian2(0.0, 0.0),
+ * new Cartesian2(1.0, 0.0),
+ * new Cartesian2(0.0, 1.0));
+ */
+ var pointInsideTriangle = function(point, p0, p1, p2) {
+ barycentricCoordinates(point, p0, p1, p2, coords);
+ return (coords.x > 0.0) && (coords.y > 0.0) && (coords.z > 0);
+ };
+
+ return pointInsideTriangle;
+});
diff --git a/Source/Core/pointInsideTriangle2D.js b/Source/Core/pointInsideTriangle2D.js
deleted file mode 100644
index e03c54f5144b..000000000000
--- a/Source/Core/pointInsideTriangle2D.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*global define*/
-define(['./DeveloperError'], function(DeveloperError) {
- "use strict";
-
- /**
- * DOC_TBA
- *
- * @param point
- * @param p0
- * @param p1
- * @param p2
- *
- * @exports pointInsideTriangle2D
- *
- * @exception {DeveloperError} point, p0, p1, and p2 are required.
- */
- var pointInsideTriangle2D = function(point, p0, p1, p2) {
- if (typeof point === 'undefined' || typeof p0 === 'undefined' || typeof p1 === 'undefined' || typeof p2 === 'undefined') {
- throw new DeveloperError('point, p0, p1, and p2 are required.');
- }
-
- // Implementation based on http://www.blackpawn.com/texts/pointinpoly/default.html.
- var v0 = p1.subtract(p0);
- var v1 = p2.subtract(p0);
- var v2 = point.subtract(p0);
-
- var dot00 = v0.dot(v0);
- var dot01 = v0.dot(v1);
- var dot02 = v0.dot(v2);
- var dot11 = v1.dot(v1);
- var dot12 = v1.dot(v2);
-
- var q = 1.0 / (dot00 * dot11 - dot01 * dot01);
- var u = (dot11 * dot02 - dot01 * dot12) * q;
- var v = (dot00 * dot12 - dot01 * dot02) * q;
-
- return (u > 0) && (v > 0) && (u + v < 1);
- };
-
- return pointInsideTriangle2D;
-});
diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js
index 3b6cde2e9b37..9fb8864ba3b9 100644
--- a/Source/Renderer/Context.js
+++ b/Source/Renderer/Context.js
@@ -4,11 +4,14 @@ define([
'../Core/DeveloperError',
'../Core/destroyObject',
'../Core/Color',
+ '../Core/ComponentDatatype',
'../Core/IndexDatatype',
'../Core/RuntimeError',
'../Core/PrimitiveType',
+ '../Core/Geometry',
'../Core/createGuid',
'../Core/Matrix4',
+ '../Core/Math',
'./Buffer',
'./BufferUsage',
'./CubeMap',
@@ -36,11 +39,14 @@ define([
DeveloperError,
destroyObject,
Color,
+ ComponentDatatype,
IndexDatatype,
RuntimeError,
PrimitiveType,
+ Geometry,
createGuid,
Matrix4,
+ CesiumMath,
Buffer,
BufferUsage,
CubeMap,
@@ -219,6 +225,7 @@ define([
// Query and initialize extensions
this._standardDerivatives = gl.getExtension('OES_standard_derivatives');
+ this._elementIndexUint = gl.getExtension('OES_element_index_uint');
this._depthTexture = gl.getExtension('WEBKIT_WEBGL_depth_texture') || gl.getExtension('MOZ_WEBGL_depth_texture');
this._textureFloat = gl.getExtension('OES_texture_float');
var textureFilterAnisotropic = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || gl.getExtension('MOZ_EXT_texture_filter_anisotropic');
@@ -697,6 +704,21 @@ define([
return !!this._standardDerivatives;
};
+ /**
+ * Returns true
if the OES_element_index_uint extension is supported. This
+ * extension allows the use of unsigned int indices, which can improve performance by
+ * eliminating batch breaking caused by unsigned short indices.
+ *
+ * @memberof Context
+ *
+ * @returns {Boolean} true
if OES_element_index_uint is supported; otherwise, false
.
+ *
+ * @see OES_element_index_uint
+ */
+ Context.prototype.getElementIndexUint = function() {
+ return !!this._elementIndexUint;
+ };
+
/**
* Returns true
if WEBGL_depth_texture is supported. This extension provides
* access to depth textures that, for example, can be attached to framebuffers for shadow mapping.
@@ -1062,6 +1084,7 @@ define([
*
* @return {IndexBuffer} The index buffer, ready to be attached to a vertex array.
*
+ * @exception {RuntimeError} IndexDatatype.UNSIGNED_INT requires OES_element_index_uint, which is not supported on this system.
* @exception {DeveloperError} The size in bytes must be greater than zero.
* @exception {DeveloperError} Invalid usage
.
* @exception {DeveloperError} Invalid indexDatatype
.
@@ -1087,16 +1110,16 @@ define([
* BufferUsage.STATIC_DRAW, IndexDatatype.UNSIGNED_SHORT)
*/
Context.prototype.createIndexBuffer = function(typedArrayOrSizeInBytes, usage, indexDatatype) {
- var bytesPerIndex;
-
- if (indexDatatype === IndexDatatype.UNSIGNED_BYTE) {
- bytesPerIndex = Uint8Array.BYTES_PER_ELEMENT;
- } else if (indexDatatype === IndexDatatype.UNSIGNED_SHORT) {
- bytesPerIndex = Uint16Array.BYTES_PER_ELEMENT;
- } else {
+ if (!IndexDatatype.validate(indexDatatype)) {
throw new DeveloperError('Invalid indexDatatype.');
}
+ if ((indexDatatype === IndexDatatype.UNSIGNED_INT) && !this.getElementIndexUint()) {
+ throw new RuntimeError('IndexDatatype.UNSIGNED_INT requires OES_element_index_uint, which is not supported on this system.');
+ }
+
+ var bytesPerIndex = indexDatatype.sizeInBytes;
+
var gl = this._gl;
var buffer = createBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, typedArrayOrSizeInBytes, usage);
var numberOfIndices = buffer.getSizeInBytes() / bytesPerIndex;
@@ -1133,7 +1156,7 @@ define([
* @exception {DeveloperError} Attribute must have a strideInBytes
less than or equal to 255 or not specify it.
* @exception {DeveloperError} Index n is used by more than one attribute.
*
- * @see Context#createVertexArrayFromMesh
+ * @see Context#createVertexArrayFromGeometry
* @see Context#createVertexBuffer
* @see Context#createIndexBuffer
* @see Context#draw
@@ -2180,8 +2203,15 @@ define([
var names = [];
for (name in attributes) {
// Attribute needs to have per-vertex values; not a constant value for all vertices.
- if (attributes.hasOwnProperty(name) && attributes[name].values) {
+ if (attributes.hasOwnProperty(name) &&
+ typeof attributes[name] !== 'undefined' &&
+ typeof attributes[name].values !== 'undefined') {
names.push(name);
+
+ if (attributes[name].componentDatatype === ComponentDatatype.DOUBLE) {
+ attributes[name].componentDatatype = ComponentDatatype.FLOAT;
+ attributes[name].values = ComponentDatatype.FLOAT.createTypedArray(attributes[name].values);
+ }
}
}
@@ -2244,7 +2274,7 @@ define([
var sizeInBytes = attributes[name].componentDatatype.sizeInBytes;
views[name] = {
- pointer : attributes[name].componentDatatype.toTypedArray(buffer),
+ pointer : attributes[name].componentDatatype.createTypedArray(buffer),
index : offsetsInBytes[name] / sizeInBytes, // Offset in ComponentType
strideInComponentType : vertexSizeInBytes / sizeInBytes
};
@@ -2281,54 +2311,53 @@ define([
}
/**
- * Creates a vertex array from a mesh. A mesh contains vertex attributes and optional index data
+ * Creates a vertex array from a geometry. A geometry contains vertex attributes and optional index data
* in system memory, whereas a vertex array contains vertex buffers and an optional index buffer in WebGL
* memory for use with rendering.
*
- * The mesh
argument should use the standard layout like the mesh returned by {@link BoxTessellator}.
+ * The geometry
argument should use the standard layout like the geometry returned by {@link BoxGeometry}.
*
* creationArguments
can have four properties:
*
- * mesh
: The source mesh containing data used to create the vertex array.
- * attributeIndices
: An object that maps mesh attribute names to vertex shader attribute indices.
+ * geometry
: The source geometry containing data used to create the vertex array.
+ * attributeIndices
: An object that maps geometry attribute names to vertex shader attribute indices.
* bufferUsage
: The expected usage pattern of the vertex array's buffers. On some WebGL implementations, this can significantly affect performance. See {@link BufferUsage}. Default: BufferUsage.DYNAMIC_DRAW
.
* vertexLayout
: Determines if all attributes are interleaved in a single vertex buffer or if each attribute is stored in a separate vertex buffer. Default: VertexLayout.SEPARATE
.
*
*
- * If creationArguments
is not specified or the mesh
contains no data, the returned vertex array is empty.
+ * If creationArguments
is not specified or the geometry
contains no data, the returned vertex array is empty.
*
* @memberof Context
*
- * @param {Object} [creationArguments=undefined] An object defining the mesh, attribute indices, buffer usage, and vertex layout used to create the vertex array.
+ * @param {Object} [creationArguments=undefined] An object defining the geometry, attribute indices, buffer usage, and vertex layout used to create the vertex array.
*
* @exception {RuntimeError} Each attribute list must have the same number of vertices.
- * @exception {DeveloperError} The mesh must have zero or one index lists.
+ * @exception {DeveloperError} The geometry must have zero or one index lists.
* @exception {DeveloperError} Index n is used by more than one attribute.
*
* @see Context#createVertexArray
* @see Context#createVertexBuffer
* @see Context#createIndexBuffer
- * @see MeshFilters.createAttributeIndices
+ * @see GeometryPipeline.createAttributeIndices
* @see ShaderProgram
- * @see BoxTessellator
*
* @example
* // Example 1. Creates a vertex array for rendering a box. The default dynamic draw
* // usage is used for the created vertex and index buffer. The attributes are not
* // interleaved by default.
- * var mesh = BoxTessellator.compute();
- * var va = context.createVertexArrayFromMesh({
- * mesh : mesh,
- * attributeIndices : MeshFilters.createAttributeIndices(mesh),
+ * var geometry = new BoxGeometry();
+ * var va = context.createVertexArrayFromGeometry({
+ * geometry : geometry,
+ * attributeIndices : GeometryPipeline.createAttributeIndices(geometry),
* });
*
* ////////////////////////////////////////////////////////////////////////////////
*
* // Example 2. Creates a vertex array with interleaved attributes in a
* // single vertex buffer. The vertex and index buffer have static draw usage.
- * var va = context.createVertexArrayFromMesh({
- * mesh : mesh,
- * attributeIndices : MeshFilters.createAttributeIndices(mesh),
+ * var va = context.createVertexArrayFromGeometry({
+ * geometry : geometry,
+ * attributeIndices : GeometryPipeline.createAttributeIndices(geometry),
* bufferUsage : BufferUsage.STATIC_DRAW,
* vertexLayout : VertexLayout.INTERLEAVED
* });
@@ -2339,41 +2368,35 @@ define([
* // attached vertex buffer(s) and index buffer.
* va = va.destroy();
*/
- Context.prototype.createVertexArrayFromMesh = function(creationArguments) {
+ Context.prototype.createVertexArrayFromGeometry = function(creationArguments) {
var ca = defaultValue(creationArguments, defaultValue.EMPTY_OBJECT);
- var mesh = defaultValue(ca.mesh, defaultValue.EMPTY_OBJECT);
+ var geometry = defaultValue(ca.geometry, defaultValue.EMPTY_OBJECT);
var bufferUsage = defaultValue(ca.bufferUsage, BufferUsage.DYNAMIC_DRAW);
- var indexLists;
-
- if (mesh.indexLists) {
- indexLists = mesh.indexLists;
- if (indexLists.length !== 1) {
- throw new DeveloperError('The mesh must have zero or one index lists. This mesh has ' + indexLists.length.toString() + ' index lists.');
- }
- }
var attributeIndices = defaultValue(ca.attributeIndices, defaultValue.EMPTY_OBJECT);
- var interleave = ca.vertexLayout && (ca.vertexLayout === VertexLayout.INTERLEAVED);
+ var interleave = (typeof ca.vertexLayout !== 'undefined') && (ca.vertexLayout === VertexLayout.INTERLEAVED);
+ var createdVAAttributes = ca.vertexArrayAttributes;
var name;
var attribute;
- var vaAttributes = [];
- var attributes = mesh.attributes;
+ var vertexBuffer;
+ var vaAttributes = (typeof createdVAAttributes !== 'undefined') ? createdVAAttributes : [];
+ var attributes = geometry.attributes;
if (interleave) {
// Use a single vertex buffer with interleaved vertices.
var interleavedAttributes = interleaveAttributes(attributes);
- if (interleavedAttributes) {
- var vertexBuffer = this.createVertexBuffer(interleavedAttributes.buffer, bufferUsage);
+ if (typeof interleavedAttributes !== 'undefined') {
+ vertexBuffer = this.createVertexBuffer(interleavedAttributes.buffer, bufferUsage);
var offsetsInBytes = interleavedAttributes.offsetsInBytes;
var strideInBytes = interleavedAttributes.vertexSizeInBytes;
for (name in attributes) {
- if (attributes.hasOwnProperty(name)) {
+ if (attributes.hasOwnProperty(name) && typeof attributes[name] !== 'undefined') {
attribute = attributes[name];
- if (attribute.values) {
+ if (typeof attribute.values !== 'undefined') {
// Common case: per-vertex attributes
vaAttributes.push({
index : attributeIndices[name],
@@ -2399,13 +2422,24 @@ define([
} else {
// One vertex buffer per attribute.
for (name in attributes) {
- if (attributes.hasOwnProperty(name)) {
+ if (attributes.hasOwnProperty(name) && typeof attributes[name] !== 'undefined') {
attribute = attributes[name];
+
+ var componentDatatype = attribute.componentDatatype;
+ if (componentDatatype === ComponentDatatype.DOUBLE) {
+ componentDatatype = ComponentDatatype.FLOAT;
+ }
+
+ vertexBuffer = undefined;
+ if (typeof attribute.values !== 'undefined') {
+ vertexBuffer = this.createVertexBuffer(componentDatatype.createTypedArray(attribute.values), bufferUsage);
+ }
+
vaAttributes.push({
index : attributeIndices[name],
- vertexBuffer : attribute.values ? this.createVertexBuffer(attribute.componentDatatype.toTypedArray(attribute.values), bufferUsage) : undefined,
- value : attribute.value ? attribute.value : undefined,
- componentDatatype : attribute.componentDatatype,
+ vertexBuffer : vertexBuffer,
+ value : attribute.value,
+ componentDatatype : componentDatatype,
componentsPerAttribute : attribute.componentsPerAttribute,
normalize : attribute.normalize
});
@@ -2414,8 +2448,13 @@ define([
}
var indexBuffer;
- if (typeof indexLists !== 'undefined') {
- indexBuffer = this.createIndexBuffer(new Uint16Array(indexLists[0].values), bufferUsage, IndexDatatype.UNSIGNED_SHORT);
+ var indices = geometry.indices;
+ if (typeof indices !== 'undefined') {
+ if ((Geometry.computeNumberOfVertices(geometry) > CesiumMath.SIXTY_FOUR_KILOBYTES) && this.getElementIndexUint()) {
+ indexBuffer = this.createIndexBuffer(new Uint32Array(indices), bufferUsage, IndexDatatype.UNSIGNED_INT);
+ } else{
+ indexBuffer = this.createIndexBuffer(new Uint16Array(indices), bufferUsage, IndexDatatype.UNSIGNED_SHORT);
+ }
}
return this.createVertexArray(vaAttributes, indexBuffer);
diff --git a/Source/Renderer/ShaderCache.js b/Source/Renderer/ShaderCache.js
index 2aed6c6533e6..d83519e2f8c4 100644
--- a/Source/Renderer/ShaderCache.js
+++ b/Source/Renderer/ShaderCache.js
@@ -61,8 +61,7 @@ define([
* @see ShaderCache#replaceShaderProgram
*/
ShaderCache.prototype.getShaderProgram = function(vertexShaderSource, fragmentShaderSource, attributeLocations) {
- // TODO: compare attributeLocations!
- var keyword = vertexShaderSource + fragmentShaderSource;
+ var keyword = vertexShaderSource + fragmentShaderSource + JSON.stringify(attributeLocations);
var cachedShader;
if (this._shaders[keyword]) {
diff --git a/Source/Renderer/ShaderProgram.js b/Source/Renderer/ShaderProgram.js
index 9034d1a86394..0d7e401e0769 100644
--- a/Source/Renderer/ShaderProgram.js
+++ b/Source/Renderer/ShaderProgram.js
@@ -759,8 +759,8 @@ define([
*
* void main()
* {
- * vec3 p = czm_translateRelativeToEye(positionHigh, positionLow);
- * gl_Position = czm_projection * (czm_modelViewRelativeToEye * vec4(p, 1.0));
+ * vec4 p = czm_translateRelativeToEye(positionHigh, positionLow);
+ * gl_Position = czm_projection * (czm_modelViewRelativeToEye * p);
* }
*
* @see czm_modelViewProjectionRelativeToEye
@@ -963,8 +963,8 @@ define([
*
* void main()
* {
- * vec3 p = czm_translateRelativeToEye(positionHigh, positionLow);
- * gl_Position = czm_modelViewProjectionRelativeToEye * vec4(p, 1.0);
+ * vec4 p = czm_translateRelativeToEye(positionHigh, positionLow);
+ * gl_Position = czm_modelViewProjectionRelativeToEye * p;
* }
*
* @see czm_modelViewRelativeToEye
@@ -2596,7 +2596,7 @@ define([
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
- if (attributeLocations) {
+ if (typeof attributeLocations !== 'undefined') {
for ( var attribute in attributeLocations) {
if (attributeLocations.hasOwnProperty(attribute)) {
gl.bindAttribLocation(program, attributeLocations[attribute], attribute);
diff --git a/Source/Renderer/VertexArray.js b/Source/Renderer/VertexArray.js
index 7527067af2fe..d7834a8a2fff 100644
--- a/Source/Renderer/VertexArray.js
+++ b/Source/Renderer/VertexArray.js
@@ -122,7 +122,7 @@ define([
* @internalConstructor
*
* @see {@link Context#createVertexArray}
- * @see {@link Context#createVertexArrayFromMesh}
+ * @see {@link Context#createVertexArrayFromGeometry}
*/
var VertexArray = function(gl, vertexArrayObject, attributes, indexBuffer) {
var vaAttributes = [];
diff --git a/Source/Renderer/VertexArrayFacade.js b/Source/Renderer/VertexArrayFacade.js
index 00d587a4fb7f..223b06e6d0bd 100644
--- a/Source/Renderer/VertexArrayFacade.js
+++ b/Source/Renderer/VertexArrayFacade.js
@@ -4,12 +4,14 @@ define([
'../Core/defaultValue',
'../Core/destroyObject',
'../Core/DeveloperError',
+ '../Core/Math',
'./BufferUsage'
], function(
ComponentDatatype,
defaultValue,
destroyObject,
DeveloperError,
+ CesiumMath,
BufferUsage) {
"use strict";
@@ -371,9 +373,6 @@ define([
}
};
- // Using unsigned short indices, 64K vertices can be indexed by one index buffer
- var sixtyFourK = 64 * 1024;
-
/**
* DOC_TBA
*
@@ -403,7 +402,7 @@ define([
var buffersByUsage = buffersByPurposeAndUsage[purpose];
var va = [];
- var numberOfVertexArrays = Math.ceil(this._size / sixtyFourK);
+ var numberOfVertexArrays = Math.ceil(this._size / CesiumMath.SIXTY_FOUR_KILOBYTES);
for ( var k = 0; k < numberOfVertexArrays; ++k) {
var attributes = [];
@@ -413,7 +412,7 @@ define([
for (var allPurposeUsage in allPurposeBuffersByUsage) {
if (allPurposeBuffersByUsage.hasOwnProperty(allPurposeUsage)) {
var allPurposeBuffer = allPurposeBuffersByUsage[allPurposeUsage];
- VertexArrayFacade._appendAttributes(attributes, allPurposeBuffer, k * (allPurposeBuffer.vertexSizeInBytes * sixtyFourK));
+ VertexArrayFacade._appendAttributes(attributes, allPurposeBuffer, k * (allPurposeBuffer.vertexSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES));
}
}
}
@@ -422,7 +421,7 @@ define([
for (var usage in buffersByUsage) {
if (buffersByUsage.hasOwnProperty(usage)) {
buffer = buffersByUsage[usage];
- VertexArrayFacade._appendAttributes(attributes, buffer, k * (buffer.vertexSizeInBytes * sixtyFourK));
+ VertexArrayFacade._appendAttributes(attributes, buffer, k * (buffer.vertexSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES));
}
}
@@ -430,7 +429,7 @@ define([
va.push({
va : this._context.createVertexArray(attributes, indexBuffer),
- indicesCount : 1.5 * ((k !== (numberOfVertexArrays - 1)) ? sixtyFourK : (this._size % sixtyFourK))
+ indicesCount : 1.5 * ((k !== (numberOfVertexArrays - 1)) ? CesiumMath.SIXTY_FOUR_KILOBYTES : (this._size % CesiumMath.SIXTY_FOUR_KILOBYTES))
// TODO: not hardcode 1.5
});
}
diff --git a/Source/Renderer/VertexLayout.js b/Source/Renderer/VertexLayout.js
index 7d7ad77617e8..cf425b99004c 100644
--- a/Source/Renderer/VertexLayout.js
+++ b/Source/Renderer/VertexLayout.js
@@ -7,7 +7,7 @@ define(['../Core/Enumeration'], function(Enumeration) {
*
* @exports VertexLayout
*
- * @see Context#createVertexArrayFromMesh
+ * @see Context#createVertexArrayFromGeometry
*/
var VertexLayout = {
/**
diff --git a/Source/Scene/Appearance.js b/Source/Scene/Appearance.js
new file mode 100644
index 000000000000..f473d8d3d1e3
--- /dev/null
+++ b/Source/Scene/Appearance.js
@@ -0,0 +1,119 @@
+/*global define*/
+define([
+ '../Core/defaultValue',
+ '../Renderer/BlendingState',
+ '../Renderer/CullFace'
+ ], function(
+ defaultValue,
+ BlendingState,
+ CullFace) {
+ "use strict";
+
+ /**
+ * An appearance defines the full GLSL vertex and fragment shaders and the
+ * render state used to draw a {@link Primitive}. All appearances implement
+ * this base Appearance
interface.
+ *
+ * @alias Appearance
+ * @constructor
+ *
+ * @see MaterialAppearance
+ * @see EllipsoidSurfaceAppearance
+ * @see PerInstanceColorAppearance
+ * @see DebugAppearance
+ */
+ var Appearance = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ /**
+ * The material used to determine the fragment color. Unlike other {@link Appearance}
+ * properties, this is not read-only, so an appearance's material can change on the fly.
+ *
+ * @type Material
+ *
+ * @see Fabric
+ */
+ this.material = options.material;
+
+ /**
+ * The GLSL source code for the vertex shader.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.vertexShaderSource = options.vertexShaderSource;
+
+ /**
+ * The GLSL source code for the fragment shader. The full fragment shader
+ * source is built procedurally taking into account the {@link Appearance#material}.
+ * Use {@link Appearance#getFragmentShaderSource} to get the full source.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.fragmentShaderSource = options.fragmentShaderSource;
+
+ /**
+ * The render state. This is not the final {@link RenderState} instance; instead,
+ * it can contain a subset of render state properties identical to renderState
+ * passed to {@link Context#createRenderState}.
+ *
+ * @type Object
+ *
+ * @readonly
+ */
+ this.renderState = options.renderState;
+ };
+
+ /**
+ * Procedurally creates the full GLSL fragment shader source for this appearance
+ * taking into account {@link Appearance#fragmentShaderSource} and {@link Appearance#material}.
+ *
+ * @memberof Appearance
+ *
+ * @return String The full GLSL fragment shader source.
+ */
+ Appearance.prototype.getFragmentShaderSource = function() {
+ var flat = this.flat ? '#define FLAT 1\n#line 0 \n' : '#line 0 \n';
+ var faceForward = this.faceForward ? '#define FACE_FORWARD 1\n#line 0 \n' : '#line 0 \n';
+
+ if (typeof this.material !== 'undefined') {
+ return '#line 0\n' +
+ this.material.shaderSource +
+ flat +
+ faceForward +
+ this.fragmentShaderSource;
+ }
+
+ return flat + faceForward + this.fragmentShaderSource;
+ };
+
+ /**
+ * @private
+ */
+ Appearance.getDefaultRenderState = function(translucent, closed) {
+ var rs = {
+ depthTest : {
+ enabled : true
+ }
+ };
+
+ if (translucent) {
+ rs.depthMask = false;
+ rs.blending = BlendingState.ALPHA_BLEND;
+ }
+
+ if (closed) {
+ rs.cull = {
+ enabled : true,
+ face : CullFace.BACK
+ };
+ }
+
+ return rs;
+ };
+
+ return Appearance;
+});
\ No newline at end of file
diff --git a/Source/Scene/CameraController.js b/Source/Scene/CameraController.js
index 3735a4437aec..e212a597387d 100644
--- a/Source/Scene/CameraController.js
+++ b/Source/Scene/CameraController.js
@@ -890,8 +890,8 @@ define([
if (!positionOnly) {
var direction = Cartesian3.clone(Cartesian3.UNIT_Z, camera.direction);
Cartesian3.negate(direction, direction);
- var right = Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
- Cartesian3.cross(right, direction, camera.up);
+ Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
+ Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
}
return result;
@@ -947,8 +947,10 @@ define([
frustum.top = top;
frustum.bottom = -top;
- var cameraRight = Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
- Cartesian3.cross(cameraRight, camera.direction, camera.up);
+ var direction = Cartesian3.clone(Cartesian3.UNIT_Z, camera.direction);
+ Cartesian3.negate(direction, direction);
+ Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
+ Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
}
return result;
diff --git a/Source/Scene/CentralBody.js b/Source/Scene/CentralBody.js
index f24a80696479..49bba4bbc346 100644
--- a/Source/Scene/CentralBody.js
+++ b/Source/Scene/CentralBody.js
@@ -14,6 +14,8 @@ define([
'../Core/Ellipsoid',
'../Core/Extent',
'../Core/GeographicProjection',
+ '../Core/Geometry',
+ '../Core/GeometryAttribute',
'../Core/Intersect',
'../Core/Math',
'../Core/Matrix4',
@@ -55,6 +57,8 @@ define([
Ellipsoid,
Extent,
GeographicProjection,
+ Geometry,
+ GeometryAttribute,
Intersect,
CesiumMath,
Matrix4,
@@ -337,7 +341,7 @@ define([
var occludeePoint;
var occluded;
var datatype;
- var mesh;
+ var geometry;
var rect;
var positions;
var occluder = centralBody._occluder;
@@ -367,17 +371,17 @@ define([
if (typeof centralBody._northPoleCommand.vertexArray === 'undefined') {
centralBody._northPoleCommand.boundingVolume = BoundingSphere.fromExtent3D(extent, centralBody._ellipsoid);
- mesh = {
+ geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 2,
values : positions
- }
+ })
}
- };
- centralBody._northPoleCommand.vertexArray = context.createVertexArrayFromMesh({
- mesh : mesh,
+ });
+ centralBody._northPoleCommand.vertexArray = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : {
position : 0
},
@@ -385,7 +389,7 @@ define([
});
} else {
datatype = ComponentDatatype.FLOAT;
- centralBody._northPoleCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(datatype.toTypedArray(positions));
+ centralBody._northPoleCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(datatype.createTypedArray(positions));
}
}
}
@@ -415,17 +419,17 @@ define([
if (typeof centralBody._southPoleCommand.vertexArray === 'undefined') {
centralBody._southPoleCommand.boundingVolume = BoundingSphere.fromExtent3D(extent, centralBody._ellipsoid);
- mesh = {
+ geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 2,
values : positions
- }
+ })
}
- };
- centralBody._southPoleCommand.vertexArray = context.createVertexArrayFromMesh({
- mesh : mesh,
+ });
+ centralBody._southPoleCommand.vertexArray = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : {
position : 0
},
@@ -433,7 +437,7 @@ define([
});
} else {
datatype = ComponentDatatype.FLOAT;
- centralBody._southPoleCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(datatype.toTypedArray(positions));
+ centralBody._southPoleCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(datatype.createTypedArray(positions));
}
}
}
@@ -491,7 +495,7 @@ define([
if (this._mode !== mode || typeof this._rsColor === 'undefined') {
modeChanged = true;
- if (mode === SceneMode.SCENE3D || mode === SceneMode.COLUMBUS_VIEW) {
+ if (mode === SceneMode.SCENE3D || (mode === SceneMode.COLUMBUS_VIEW && !(this.terrainProvider instanceof EllipsoidTerrainProvider))) {
this._rsColor = context.createRenderState({ // Write color and depth
cull : {
enabled : true
@@ -547,21 +551,19 @@ define([
// depth plane
if (!this._depthCommand.vertexArray) {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : depthQuad
- }
+ })
},
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : [0, 1, 2, 2, 1, 3]
- }]
- };
- this._depthCommand.vertexArray = context.createVertexArrayFromMesh({
- mesh : mesh,
+ indices : [0, 1, 2, 2, 1, 3],
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ this._depthCommand.vertexArray = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : {
position : 0
},
@@ -569,7 +571,7 @@ define([
});
} else {
var datatype = ComponentDatatype.FLOAT;
- this._depthCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(datatype.toTypedArray(depthQuad));
+ this._depthCommand.vertexArray.getAttribute(0).vertexBuffer.copyFromArrayView(datatype.createTypedArray(depthQuad));
}
var shaderCache = context.getShaderCache();
diff --git a/Source/Scene/CentralBodySurface.js b/Source/Scene/CentralBodySurface.js
index b5e5248107d1..dc47cb7de0f3 100644
--- a/Source/Scene/CentralBodySurface.js
+++ b/Source/Scene/CentralBodySurface.js
@@ -6,13 +6,13 @@ define([
'../Core/Cartesian2',
'../Core/Cartesian3',
'../Core/Cartesian4',
- '../Core/CubeMapEllipsoidTessellator',
+ '../Core/EllipsoidGeometry',
'../Core/DeveloperError',
'../Core/Ellipsoid',
'../Core/EllipsoidalOccluder',
'../Core/Intersect',
'../Core/Matrix4',
- '../Core/MeshFilters',
+ '../Core/GeometryPipeline',
'../Core/PrimitiveType',
'../Core/Queue',
'../Core/WebMercatorProjection',
@@ -30,13 +30,13 @@ define([
Cartesian2,
Cartesian3,
Cartesian4,
- CubeMapEllipsoidTessellator,
+ EllipsoidGeometry,
DeveloperError,
Ellipsoid,
EllipsoidalOccluder,
Intersect,
Matrix4,
- MeshFilters,
+ GeometryPipeline,
PrimitiveType,
Queue,
WebMercatorProjection,
@@ -708,11 +708,14 @@ define([
if (typeof surface._debug !== 'undefined' && typeof surface._debug.boundingSphereTile !== 'undefined') {
if (!surface._debug.boundingSphereVA) {
var radius = surface._debug.boundingSphereTile.boundingSphere3D.radius;
- var sphere = CubeMapEllipsoidTessellator.compute(new Ellipsoid(radius, radius, radius), 10);
- MeshFilters.toWireframeInPlace(sphere);
- surface._debug.boundingSphereVA = context.createVertexArrayFromMesh({
- mesh : sphere,
- attributeIndices : MeshFilters.createAttributeIndices(sphere)
+ var sphere = new EllipsoidGeometry({
+ radii : new Cartesian3(radius, radius, radius),
+ numberOfPartitions : 10
+ });
+ GeometryPipeline.toWireframe(sphere);
+ surface._debug.boundingSphereVA = context.createVertexArrayFromGeometry({
+ geometry : sphere,
+ attributeIndices : GeometryPipeline.createAttributeIndices(sphere)
});
}
diff --git a/Source/Scene/CompositePrimitive.js b/Source/Scene/CompositePrimitive.js
index f7b978dc4af8..09d4a5633af2 100644
--- a/Source/Scene/CompositePrimitive.js
+++ b/Source/Scene/CompositePrimitive.js
@@ -400,8 +400,7 @@ define([
var primitives = this._primitives;
var length = primitives.length;
for (var i = 0; i < length; ++i) {
- var primitive = primitives[i];
- primitive.update(context, frameState, commandList);
+ primitives[i].update(context, frameState, commandList);
}
};
diff --git a/Source/Scene/CustomSensorVolume.js b/Source/Scene/CustomSensorVolume.js
index a671a8345c51..ea0dd587b079 100644
--- a/Source/Scene/CustomSensorVolume.js
+++ b/Source/Scene/CustomSensorVolume.js
@@ -80,9 +80,10 @@ define([
this.show = defaultValue(options.show, true);
/**
- * When true
, a polyline is shown where the sensor outline intersections the central body. The default is true
.
+ * When true
, a polyline is shown where the sensor outline intersections the central body.
*
* @type {Boolean}
+ *
* @default true
*
* @see CustomSensorVolume#intersectionColor
@@ -94,9 +95,6 @@ define([
* Determines if a sensor intersecting the ellipsoid is drawn through the ellipsoid and potentially out
* to the other side, or if the part of the sensor intersecting the ellipsoid stops at the ellipsoid.
*
- *
- * The default is false
, meaning the sensor will not go through the ellipsoid.
- *
*
* @type {Boolean}
* @default false
diff --git a/Source/Scene/DebugAppearance.js b/Source/Scene/DebugAppearance.js
new file mode 100644
index 000000000000..e7174f5c363d
--- /dev/null
+++ b/Source/Scene/DebugAppearance.js
@@ -0,0 +1,170 @@
+/*global define*/
+define([
+ '../Core/defaultValue',
+ '../Core/DeveloperError',
+ './Appearance'
+ ], function(
+ defaultValue,
+ DeveloperError,
+ Appearance) {
+ "use strict";
+
+ /**
+ * Visualizes a vertex attribute by displaying it as a color for debugging.
+ *
+ * Components for well-known unit-length vectors, i.e., normal
,
+ * binormal
, and tangent
, are scaled and biased
+ * from [-1.0, 1.0] to (-1.0, 1.0).
+ *
+ *
+ * @alias DebugAppearance
+ * @constructor
+ *
+ * @param {String} options.attributeName The name of the attribute to visualize.
+ * @param {String} [options.glslDatatype='vec3'] The GLSL datatype of the attribute. Supported datatypes are float
, vec2
, vec3
, and vec4
.
+ * @param {String} [options.vertexShaderSource=undefined] Optional GLSL vertex shader source to override the default vertex shader.
+ * @param {String} [options.fragmentShaderSource=undefined] Optional GLSL fragment shader source to override the default fragment shader.
+ * @param {RenderState} [options.renderState=undefined] Optional render state to override the default render state.
+ *
+ * @exception {DeveloperError} options.attributeName is required.
+ * @exception {DeveloperError} options.glslDatatype must be float, vec2, vec3, or vec4.
+ *
+ * @example
+ * var primitive = new Primitive({
+ * geometryInstances : // ...
+ * appearance : new DebugAppearance({
+ * attributeName : 'normal'
+ * })
+ * });
+ */
+ var DebugAppearance = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var attributeName = options.attributeName;
+
+ if (typeof attributeName === 'undefined') {
+ throw new DeveloperError('options.attributeName is required.');
+ }
+
+ var glslDatatype = defaultValue(options.glslDatatype, 'vec3');
+ var varyingName = 'v_' + attributeName;
+ var getColor;
+
+ // Well-known normalized vector attributes in VertexFormat
+ if ((attributeName === 'normal') || (attributeName === 'binormal') | (attributeName === 'tangent')) {
+ getColor = 'vec4 getColor() { return vec4((' + varyingName + ' + vec3(1.0)) * 0.5, 1.0); }\n';
+ } else {
+ // All other attributes, both well-known and custom
+ if (attributeName === 'st') {
+ glslDatatype = 'vec2';
+ }
+
+ switch(glslDatatype) {
+ case 'float':
+ getColor = 'vec4 getColor() { return vec4(vec3(' + varyingName + '), 1.0); }\n';
+ break;
+ case 'vec2':
+ getColor = 'vec4 getColor() { return vec4(' + varyingName + ', 0.0, 1.0); }\n';
+ break;
+ case 'vec3':
+ getColor = 'vec4 getColor() { return vec4(' + varyingName + ', 1.0); }\n';
+ break;
+ case 'vec4':
+ getColor = 'vec4 getColor() { return ' + varyingName + '; }\n';
+ break;
+ default:
+ throw new DeveloperError('options.glslDatatype must be float, vec2, vec3, or vec4.');
+ }
+ }
+
+ var vs =
+ 'attribute vec3 position3DHigh;\n' +
+ 'attribute vec3 position3DLow;\n' +
+ 'attribute ' + glslDatatype + ' ' + attributeName + ';\n' +
+ 'varying ' + glslDatatype + ' ' + varyingName + ';\n' +
+ 'void main()\n' +
+ '{\n' +
+ 'vec4 p = czm_translateRelativeToEye(position3DHigh, position3DLow);\n' +
+ varyingName + ' = ' + attributeName + ';\n' +
+ 'gl_Position = czm_modelViewProjectionRelativeToEye * p;\n' +
+ '}';
+ var fs =
+ 'varying ' + glslDatatype + ' ' + varyingName + ';\n' +
+ getColor + '\n' +
+ 'void main()\n' +
+ '{\n' +
+ 'gl_FragColor = getColor();\n' +
+ '}';
+
+ /**
+ * This property is part of the {@link Appearance} interface, but is not
+ * used by {@link DebugAppearance} since a fully custom fragment shader is used.
+ *
+ * @type Material
+ *
+ * @default undefined
+ */
+ this.material = undefined;
+
+ /**
+ * The GLSL source code for the vertex shader.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.vertexShaderSource = defaultValue(options.vertexShaderSource, vs);
+
+ /**
+ * The GLSL source code for the fragment shader.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.fragmentShaderSource = defaultValue(options.fragmentShaderSource, fs);
+
+ /**
+ * The render state. This is not the final {@link RenderState} instance; instead,
+ * it can contain a subset of render state properties identical to renderState
+ * passed to {@link Context#createRenderState}.
+ *
+ * @type Object
+ *
+ * @readonly
+ */
+ this.renderState = defaultValue(options.renderState, Appearance.getDefaultRenderState(false, false));
+
+ // Non-derived members
+
+ /**
+ * The name of the attribute being visualized.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.attributeName = attributeName;
+
+ /**
+ * The GLSL datatype of the attribute being visualized.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.glslDatatype = glslDatatype;
+ };
+
+ /**
+ * Returns the full GLSL fragment shader source, which for {@link DebugAppearance} is just
+ * {@link DebugAppearance#fragmentShaderSource}.
+ *
+ * @memberof DebugAppearance
+ *
+ * @return String The full GLSL fragment shader source.
+ */
+ DebugAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource;
+
+ return DebugAppearance;
+});
\ No newline at end of file
diff --git a/Source/Scene/EllipsoidPrimitive.js b/Source/Scene/EllipsoidPrimitive.js
index 6c91e314244d..a4644e5c275a 100644
--- a/Source/Scene/EllipsoidPrimitive.js
+++ b/Source/Scene/EllipsoidPrimitive.js
@@ -1,7 +1,8 @@
/*global define*/
define([
- '../Core/BoxTessellator',
+ '../Core/BoxGeometry',
'../Core/Cartesian3',
+ '../Core/Cartesian4',
'../Core/combine',
'../Core/DeveloperError',
'../Core/destroyObject',
@@ -19,8 +20,9 @@ define([
'../Shaders/EllipsoidVS',
'../Shaders/EllipsoidFS'
], function(
- BoxTessellator,
+ BoxGeometry,
Cartesian3,
+ Cartesian4,
combine,
DeveloperError,
destroyObject,
@@ -113,13 +115,12 @@ define([
* Local reference frames can be used by providing a different transformation matrix, like that returned
* by {@link Transforms.eastNorthUpToFixedFrame}. This matrix is available to GLSL vertex and fragment
* shaders via {@link czm_model} and derived uniforms.
- *
- * The default is {@link Matrix4.IDENTITY}.
- *
*
* @type {Matrix4}
* @default {@link Matrix4.IDENTITY}
*
+ * @default Matrix4.IDENTITY
+ *
* @example
* var origin = ellipsoid.cartographicToCartesian(
* Cartographic.fromDegrees(-95.0, 40.0, 200000.0));
@@ -133,9 +134,6 @@ define([
/**
* Determines if the ellipsoid primitive will be shown.
- *
- * The default is true
.
- *
*
* @type {Boolean}
* @default true
@@ -199,12 +197,12 @@ define([
return vertexArray;
}
- var mesh = BoxTessellator.compute({
+ var geometry = BoxGeometry.fromDimensions({
dimensions : new Cartesian3(2.0, 2.0, 2.0)
});
- vertexArray = context.createVertexArrayFromMesh({
- mesh: mesh,
+ vertexArray = context.createVertexArrayFromGeometry({
+ geometry: geometry,
attributeIndices: attributeIndices,
bufferUsage: BufferUsage.STATIC_DRAW
});
diff --git a/Source/Scene/EllipsoidSurfaceAppearance.js b/Source/Scene/EllipsoidSurfaceAppearance.js
new file mode 100644
index 000000000000..37a207f96748
--- /dev/null
+++ b/Source/Scene/EllipsoidSurfaceAppearance.js
@@ -0,0 +1,189 @@
+/*global define*/
+define([
+ '../Core/defaultValue',
+ '../Core/VertexFormat',
+ './Material',
+ './Appearance',
+ './MaterialAppearance',
+ '../Shaders/Appearances/EllipsoidSurfaceAppearanceVS',
+ '../Shaders/Appearances/EllipsoidSurfaceAppearanceFS'
+ ], function(
+ defaultValue,
+ VertexFormat,
+ Material,
+ Appearance,
+ MaterialAppearance,
+ EllipsoidSurfaceAppearanceVS,
+ EllipsoidSurfaceAppearanceFS) {
+ "use strict";
+
+ /**
+ * An appearance for geometry on the surface of the ellipsoid like {@link PolygonGeometry}
+ * and {@link ExtentGeometry}, which supports all materials like {@link MaterialAppearance}
+ * with {@link MaterialAppearance.MaterialSupport.ALL}. However, this appearance requires
+ * fewer vertex attributes since the fragment shader can procedurally compute normal
,
+ * binormal
, and tangent
.
+ *
+ * @alias EllipsoidSurfaceAppearance
+ * @constructor
+ *
+ * @param {Boolean} [options.flat=false] When true
, flat shading is used in the fragment shader, which means lighting is not taking into account.
+ * @param {Boolean} [options.faceForward=false] When true
, the fragment shader flips the surface normal as needed to ensure that the normal faces the viewer to avoid dark spots. This is useful when both sides of a geometry should be shaded like {@link WallGeometry}.
+ * @param {Boolean} [options.translucent=true] When true
, the geometry is expected to appear translucent so {@link EllipsoidSurfaceAppearance#renderState} has alpha blending enabled.
+ * @param {Boolean} [options.aboveGround=false] When true
, the geometry is expected to be on the ellipsoid's surface - not at a constant height above it - so {@link EllipsoidSurfaceAppearance#renderState} has backface culling enabled.
+ * @param {Material} [options.material=Material.ColorType] The material used to determine the fragment color.
+ * @param {String} [options.vertexShaderSource=undefined] Optional GLSL vertex shader source to override the default vertex shader.
+ * @param {String} [options.fragmentShaderSource=undefined] Optional GLSL fragment shader source to override the default fragment shader.
+ * @param {RenderState} [options.renderState=undefined] Optional render state to override the default render state.
+ *
+ * @example
+ * var primitive = new Primitive({
+ * geometryInstances : new GeometryInstance({
+ * geometry : new PolygonGeometry({
+ * vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT,
+ * // ...
+ * })
+ * }),
+ * appearance : new EllipsoidSurfaceAppearance({
+ * material : Material.fromType(scene.getContext(), 'Stripe')
+ * })
+ * });
+ *
+ * @see Fabric
+ */
+ var EllipsoidSurfaceAppearance = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var translucent = defaultValue(options.translucent, true);
+ var aboveGround = defaultValue(options.aboveGround, false);
+
+ /**
+ * The material used to determine the fragment color. Unlike other {@link EllipsoidSurfaceAppearance}
+ * properties, this is not read-only, so an appearance's material can change on the fly.
+ *
+ * @type Material
+ *
+ * @default Material.ColorType
+ *
+ * @see Fabric
+ */
+ this.material = (typeof options.material !== 'undefined') ? options.material : Material.fromType(undefined, Material.ColorType);
+
+ /**
+ * The GLSL source code for the vertex shader.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.vertexShaderSource = defaultValue(options.vertexShaderSource, EllipsoidSurfaceAppearanceVS);
+
+ /**
+ * The GLSL source code for the fragment shader. The full fragment shader
+ * source is built procedurally taking into account {@link EllipsoidSurfaceAppearance#material},
+ * {@link EllipsoidSurfaceAppearance#flat}, and {@link EllipsoidSurfaceAppearance#faceForward}.
+ * Use {@link EllipsoidSurfaceAppearance#getFragmentShaderSource} to get the full source.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.fragmentShaderSource = defaultValue(options.fragmentShaderSource, EllipsoidSurfaceAppearanceFS);
+
+ /**
+ * The render state. This is not the final {@link RenderState} instance; instead,
+ * it can contain a subset of render state properties identical to renderState
+ * passed to {@link Context#createRenderState}.
+ *
+ * The render state can be explicitly defined when constructing a {@link EllipsoidSurfaceAppearance}
+ * instance, or it is set implicitly via {@link EllipsoidSurfaceAppearance#translucent}
+ * and {@link EllipsoidSurfaceAppearance#aboveGround}.
+ *
+ *
+ * @type Object
+ *
+ * @readonly
+ */
+ this.renderState = defaultValue(options.renderState, Appearance.getDefaultRenderState(translucent, !aboveGround));
+
+ // Non-derived members
+
+ /**
+ * The {@link VertexFormat} that this appearance instance is compatible with.
+ * A geometry can have more vertex attributes and still be compatible - at a
+ * potential performance cost - but it can't have less.
+ *
+ * @type VertexFormat
+ *
+ * @readonly
+ */
+ this.vertexFormat = EllipsoidSurfaceAppearance.VERTEX_FORMAT;
+
+ /**
+ * When true
, flat shading is used in the fragment shader,
+ * which means lighting is not taking into account.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.flat = defaultValue(options.flat, false);
+
+ /**
+ * When true
, the fragment shader flips the surface normal
+ * as needed to ensure that the normal faces the viewer to avoid
+ * dark spots. This is useful when both sides of a geometry should be
+ * shaded like {@link WallGeometry}.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.faceForward = defaultValue(options.faceForward, false);
+
+ /**
+ * When true
, the geometry is expected to appear translucent so
+ * {@link EllipsoidSurfaceAppearance#renderState} has alpha blending enabled.
+ *
+ * @readonly
+ *
+ * @default true
+ */
+ this.translucent = translucent;
+
+ /**
+ * When true
, the geometry is expected to be on the ellipsoid's
+ * surface - not at a constant height above it - so {@link EllipsoidSurfaceAppearance#renderState}
+ * has backface culling enabled.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.aboveGround = aboveGround;
+ };
+
+ /**
+ * The {@link VertexFormat} that all {@link EllipsoidSurfaceAppearance} instances
+ * are compatible with, which requires only position
and st
+ * attributes. Other attributes are procedurally computed in the fragment shader.
+ *
+ * @type VertexFormat
+ *
+ * @constant
+ */
+ EllipsoidSurfaceAppearance.VERTEX_FORMAT = VertexFormat.POSITION_AND_ST;
+
+ /**
+ * Procedurally creates the full GLSL fragment shader source. For {@link PerInstanceColorAppearance},
+ * this is derived from {@link PerInstanceColorAppearance#fragmentShaderSource}, {@link PerInstanceColorAppearance#flat},
+ * and {@link PerInstanceColorAppearance#faceForward}.
+ *
+ * @memberof EllipsoidSurfaceAppearance
+ *
+ * @return String The full GLSL fragment shader source.
+ */
+ EllipsoidSurfaceAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource;
+
+ return EllipsoidSurfaceAppearance;
+});
\ No newline at end of file
diff --git a/Source/Scene/ExtentPrimitive.js b/Source/Scene/ExtentPrimitive.js
new file mode 100644
index 000000000000..ce1032c3132a
--- /dev/null
+++ b/Source/Scene/ExtentPrimitive.js
@@ -0,0 +1,242 @@
+/*global define*/
+define([
+ '../Core/DeveloperError',
+ '../Core/defaultValue',
+ '../Core/Color',
+ '../Core/destroyObject',
+ '../Core/Math',
+ '../Core/Extent',
+ '../Core/Ellipsoid',
+ '../Core/GeometryInstance',
+ '../Core/ExtentGeometry',
+ './EllipsoidSurfaceAppearance',
+ './Primitive',
+ './Material'
+ ], function(
+ DeveloperError,
+ defaultValue,
+ Color,
+ destroyObject,
+ CesiumMath,
+ Extent,
+ Ellipsoid,
+ GeometryInstance,
+ ExtentGeometry,
+ EllipsoidSurfaceAppearance,
+ Primitive,
+ Material) {
+ "use strict";
+
+ /**
+ * A renderable rectangular extent.
+ *
+ * @alias ExtentPrimitive
+ * @constructor
+ *
+ * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid that the extent is drawn on.
+ * @param {Extent} [extent=undefined] The extent, which defines the rectangular region to draw.
+ * @param {Number} [granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude in the underlying geometry.
+ * @param {Number} [height=0.0] The height, in meters, that the extent is raised above the {@link ExtentPrimitive#ellipsoid}.
+ * @param {Number} [rotation=0.0] The angle, in radians, relative to north that the extent is rotated. Positive angles rotate counter-clockwise.
+ * @param {Boolean} [show=true] Determines if this primitive will be shown.
+ * @param {Material} [material=undefined] The surface appearance of the primitive.
+ *
+ * @example
+ * var extentPrimitive = new ExtentPrimitive({
+ * extent : Extent.fromDegrees(0.0, 20.0, 10.0, 30.0)
+ * });
+ * primitives.add(extentPrimitive);
+ */
+ var ExtentPrimitive = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ /**
+ * The ellipsoid that the extent is drawn on.
+ *
+ * @type Ellipsoid
+ *
+ * @default Ellipsoid.WGS84
+ */
+ this.ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
+ this._ellipsoid = undefined;
+
+ /**
+ * The extent, which defines the rectangular region to draw.
+ *
+ * @type Extent
+ *
+ * @default undefined
+ */
+ this.extent = Extent.clone(options.extent);
+ this._extent = undefined;
+
+ /**
+ * The distance, in radians, between each latitude and longitude in the underlying geometry.
+ * A lower granularity fits the curvature of the {@link ExtentPrimitive#ellipsoid} better,
+ * but uses more triangles.
+ *
+ * @type Number
+ *
+ * @default CesiumMath.RADIANS_PER_DEGREE
+ */
+ this.granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
+ this._granularity = undefined;
+
+ /**
+ * The height, in meters, that the extent is raised above the {@link ExtentPrimitive#ellipsoid}.
+ *
+ * @type Number
+ *
+ * @default 0.0
+ */
+ this.height = defaultValue(options.height, 0.0);
+ this._height = undefined;
+
+ /**
+ * The angle, in radians, relative to north that the extent is rotated.
+ * Positive angles rotate counter-clockwise.
+ *
+ * @type Number
+ *
+ * @default 0.0
+ */
+ this.rotation = defaultValue(options.rotation, 0.0);
+ this._rotation = undefined;
+
+ /**
+ * Determines if this primitive will be shown.
+ *
+ * @type Boolean
+ *
+ * @default true
+ */
+ this.show = defaultValue(options.show, true);
+
+ var material = Material.fromType(undefined, Material.ColorType);
+ material.uniforms.color = new Color(1.0, 1.0, 0.0, 0.5);
+
+ /**
+ * The surface appearance of the primitive. This can be one of several built-in {@link Material} objects or a custom material, scripted with
+ * Fabric.
+ *
+ * The default material is Material.ColorType
.
+ *
+ *
+ * @type Material
+ *
+ * @example
+ * // 1. Change the color of the default material to yellow
+ * extent.material.uniforms.color = new Color(1.0, 1.0, 0.0, 1.0);
+ *
+ * // 2. Change material to horizontal stripes
+ * extent.material = Material.fromType(scene.getContext(), Material.StripeType);
+ *
+ * @see Fabric
+ */
+ this.material = defaultValue(options.material, material);
+
+ this._primitive = undefined;
+ };
+
+ /**
+ * @private
+ */
+ ExtentPrimitive.prototype.update = function(context, frameState, commandList) {
+ if (typeof this.ellipsoid === 'undefined') {
+ throw new DeveloperError('this.ellipsoid must be defined.');
+ }
+
+ if (typeof this.material === 'undefined') {
+ throw new DeveloperError('this.material must be defined.');
+ }
+
+ if (this.granularity < 0.0) {
+ throw new DeveloperError('this.granularity and scene2D/scene3D overrides must be greater than zero.');
+ }
+
+ if (!this.show || (typeof this.extent === 'undefined')) {
+ return;
+ }
+
+ if (!Extent.equals(this._extent, this.extent) ||
+ (this._ellipsoid !== this.ellipsoid) ||
+ (this._granularity !== this.granularity) ||
+ (this._height !== this.height) ||
+ (this._rotation !== this.rotation)) {
+
+ this._extent = Extent.clone(this.extent, this._extent);
+ this._ellipsoid = this.ellipsoid;
+ this._granularity = this.granularity;
+ this._height = this.height;
+ this._rotation = this.rotation;
+
+ var instance = new GeometryInstance({
+ geometry : new ExtentGeometry({
+ extent : this.extent,
+ vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT,
+ ellipsoid : this.ellipsoid,
+ granularity : this.granularity,
+ height : this.height,
+ rotation : this.rotation
+ }),
+ id : this
+ });
+
+ if (typeof this._primitive !== 'undefined') {
+ this._primitive.destroy();
+ }
+
+ this._primitive = new Primitive({
+ geometryInstances : instance,
+ appearance : new EllipsoidSurfaceAppearance({
+ aboveGround : (this.height > 0.0)
+ })
+ });
+ }
+
+ this._primitive.appearance.material = this.material;
+ this._primitive.update(context, frameState, commandList);
+ };
+
+ /**
+ * Returns true if this object was destroyed; otherwise, false.
+ *
+ * If this object was destroyed, it should not be used; calling any function other than
+ * isDestroyed
will result in a {@link DeveloperError} exception.
+ *
+ * @memberof Extent
+ *
+ * @return {Boolean} true
if this object was destroyed; otherwise, false
.
+ *
+ * @see Extent#destroy
+ */
+ ExtentPrimitive.prototype.isDestroyed = function() {
+ return false;
+ };
+
+ /**
+ * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
+ * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
+ *
+ * Once an object is destroyed, it should not be used; calling any function other than
+ * isDestroyed
will result in a {@link DeveloperError} exception. Therefore,
+ * assign the return value (undefined
) to the object as done in the example.
+ *
+ * @memberof Extent
+ *
+ * @return {undefined}
+ *
+ * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
+ *
+ * @see Extent#isDestroyed
+ *
+ * @example
+ * extent = extent && extent.destroy();
+ */
+ ExtentPrimitive.prototype.destroy = function() {
+ this._primitive = this._primitive && this._primitive.destroy();
+ return destroyObject(this);
+ };
+
+ return ExtentPrimitive;
+});
diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js
index 05909e335b9e..c02cdba68f95 100644
--- a/Source/Scene/ImageryLayer.js
+++ b/Source/Scene/ImageryLayer.js
@@ -10,6 +10,8 @@ define([
'../Core/Extent',
'../Core/Math',
'../Core/PrimitiveType',
+ '../Core/Geometry',
+ '../Core/GeometryAttribute',
'../Renderer/BufferUsage',
'../Renderer/MipmapHint',
'../Renderer/TextureMagnificationFilter',
@@ -37,6 +39,8 @@ define([
Extent,
CesiumMath,
PrimitiveType,
+ Geometry,
+ GeometryAttribute,
BufferUsage,
MipmapHint,
TextureMagnificationFilter,
@@ -814,26 +818,24 @@ define([
}
}
- var reprojectMesh = {
+ var reprojectGeometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 2,
values : positions
- }
+ })
},
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : TerrainProvider.getRegularGridIndices(256, 256)
- }]
- };
+ indices : TerrainProvider.getRegularGridIndices(256, 256),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
var reprojectAttribInds = {
position : 0
};
- reproject.vertexArray = context.createVertexArrayFromMesh({
- mesh : reprojectMesh,
+ reproject.vertexArray = context.createVertexArrayFromGeometry({
+ geometry : reprojectGeometry,
attributeIndices : reprojectAttribInds,
bufferUsage : BufferUsage.STATIC_DRAW
});
diff --git a/Source/Scene/MaterialAppearance.js b/Source/Scene/MaterialAppearance.js
new file mode 100644
index 000000000000..9126016e5039
--- /dev/null
+++ b/Source/Scene/MaterialAppearance.js
@@ -0,0 +1,244 @@
+/*global define*/
+define([
+ '../Core/defaultValue',
+ '../Core/freezeObject',
+ '../Core/VertexFormat',
+ './Material',
+ './Appearance',
+ '../Shaders/Appearances/BasicMaterialAppearanceVS',
+ '../Shaders/Appearances/BasicMaterialAppearanceFS',
+ '../Shaders/Appearances/TexturedMaterialAppearanceVS',
+ '../Shaders/Appearances/TexturedMaterialAppearanceFS',
+ '../Shaders/Appearances/AllMaterialAppearanceVS',
+ '../Shaders/Appearances/AllMaterialAppearanceFS'
+ ], function(
+ defaultValue,
+ freezeObject,
+ VertexFormat,
+ Material,
+ Appearance,
+ BasicMaterialAppearanceVS,
+ BasicMaterialAppearanceFS,
+ TexturedMaterialAppearanceVS,
+ TexturedMaterialAppearanceFS,
+ AllMaterialAppearanceVS,
+ AllMaterialAppearanceFS) {
+ "use strict";
+
+ /**
+ * An appearance for arbitrary geometry (as opposed to {@link EllipsoidSurfaceAppearance}, for example)
+ * that supports shading with materials.
+ *
+ * @alias MaterialAppearance
+ * @constructor
+ *
+ * @param {Boolean} [options.flat=false] When true
, flat shading is used in the fragment shader, which means lighting is not taking into account.
+ * @param {Boolean} [options.faceForward=false] When true
, the fragment shader flips the surface normal as needed to ensure that the normal faces the viewer to avoid dark spots. This is useful when both sides of a geometry should be shaded like {@link WallGeometry}.
+ * @param {Boolean} [options.translucent=true] When true
, the geometry is expected to appear translucent so {@link MaterialAppearance#renderState} has alpha blending enabled.
+ * @param {Boolean} [options.closed=false] When true
, the geometry is expected to be closed so {@link MaterialAppearance#renderState} has backface culling enabled.
+ * @param {MaterialAppearance.MaterialSupport} [options.materialSupport=MaterialAppearance.MaterialSupport.TEXTURED] The type of materials that will be supported.
+ * @param {Material} [options.material=Material.ColorType] The material used to determine the fragment color.
+ * @param {String} [options.vertexShaderSource=undefined] Optional GLSL vertex shader source to override the default vertex shader.
+ * @param {String} [options.fragmentShaderSource=undefined] Optional GLSL fragment shader source to override the default fragment shader.
+ * @param {RenderState} [options.renderState=undefined] Optional render state to override the default render state.
+ *
+ * @example
+ * var primitive = new Primitive({
+ * geometryInstances : new GeometryInstance({
+ * geometry : new WallGeometry({
+ materialSupport : MaterialAppearance.MaterialSupport.BASIC.vertexFormat,
+ * // ...
+ * })
+ * }),
+ * appearance : new MaterialAppearance({
+ * material : Material.fromType(scene.getContext(), 'Color'),
+ * faceForward : true
+ * })
+ * });
+ *
+ * @see Fabric
+ */
+ var MaterialAppearance = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var translucent = defaultValue(options.translucent, true);
+ var closed = defaultValue(options.closed, false);
+ var materialSupport = defaultValue(options.materialSupport, MaterialAppearance.MaterialSupport.TEXTURED);
+
+ /**
+ * The material used to determine the fragment color. Unlike other {@link MaterialAppearance}
+ * properties, this is not read-only, so an appearance's material can change on the fly.
+ *
+ * @type Material
+ *
+ * @default Material.ColorType
+ *
+ * @see Fabric
+ */
+ this.material = (typeof options.material !== 'undefined') ? options.material : Material.fromType(undefined, Material.ColorType);
+
+ /**
+ * The GLSL source code for the vertex shader.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.vertexShaderSource = defaultValue(options.vertexShaderSource, materialSupport.vertexShaderSource);
+
+ /**
+ * The GLSL source code for the fragment shader. The full fragment shader
+ * source is built procedurally taking into account {@link MaterialAppearance#material},
+ * {@link MaterialAppearance#flat}, and {@link MaterialAppearance#faceForward}.
+ * Use {@link MaterialAppearance#getFragmentShaderSource} to get the full source.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.fragmentShaderSource = defaultValue(options.fragmentShaderSource, materialSupport.fragmentShaderSource);
+
+ /**
+ * The render state. This is not the final {@link RenderState} instance; instead,
+ * it can contain a subset of render state properties identical to renderState
+ * passed to {@link Context#createRenderState}.
+ *
+ * The render state can be explicitly defined when constructing a {@link MaterialAppearance}
+ * instance, or it is set implicitly via {@link MaterialAppearance#translucent}
+ * and {@link MaterialAppearance#closed}.
+ *
+ *
+ * @type Object
+ *
+ * @readonly
+ */
+ this.renderState = defaultValue(options.renderState, Appearance.getDefaultRenderState(translucent, closed));
+
+ // Non-derived members
+
+ /**
+ * The type of materials supported by this instance. This impacts the required
+ * {@link VertexFormat} and the complexity of the vertex and fragment shaders.
+ *
+ * @type MaterialAppearance.MaterialSupport
+ *
+ * @readonly
+ */
+ this.materialSupport = materialSupport;
+
+ /**
+ * The {@link VertexFormat} that this appearance instance is compatible with.
+ * A geometry can have more vertex attributes and still be compatible - at a
+ * potential performance cost - but it can't have less.
+ *
+ * @type VertexFormat
+ *
+ * @readonly
+ */
+ this.vertexFormat = materialSupport.vertexFormat;
+
+ /**
+ * When true
, flat shading is used in the fragment shader,
+ * which means lighting is not taking into account.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.flat = defaultValue(options.flat, false);
+
+ /**
+ * When true
, the fragment shader flips the surface normal
+ * as needed to ensure that the normal faces the viewer to avoid
+ * dark spots. This is useful when both sides of a geometry should be
+ * shaded like {@link WallGeometry}.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.faceForward = defaultValue(options.faceForward, false);
+
+ /**
+ * When true
, the geometry is expected to appear translucent so
+ * {@link MaterialAppearance#renderState} has alpha blending enabled.
+ *
+ * @readonly
+ *
+ * @default true
+ */
+ this.translucent = translucent;
+
+ /**
+ * When true
, the geometry is expected to be closed so
+ * {@link MaterialAppearance#renderState} has backface culling enabled.
+ * If the viewer enters the geometry, it will not be visible.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.closed = closed;
+ };
+
+ /**
+ * Procedurally creates the full GLSL fragment shader source. For {@link MaterialAppearance},
+ * this is derived from {@link MaterialAppearance#fragmentShaderSource}, {@link MaterialAppearance#material},
+ * {@link MaterialAppearance#flat}, and {@link MaterialAppearance#faceForward}.
+ *
+ * @memberof MaterialAppearance
+ *
+ * @return String The full GLSL fragment shader source.
+ */
+ MaterialAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource;
+
+ /**
+ * Determines the type of {@link Material} that is supported by a
+ * {@link MaterialAppearance} instance. This is a trade-off between
+ * flexibility (a wide array of materials) and memory/performance
+ * (required vertex format and GLSL shader complexity.
+ *
+ * @memberof MaterialAppearance
+ *
+ * @enumeration
+ */
+ MaterialAppearance.MaterialSupport = {
+ /**
+ * Only basic materials, which require just position
and
+ * normal
vertex attributes, are supported.
+ *
+ * @readonly
+ */
+ BASIC : freezeObject({
+ vertexFormat : VertexFormat.POSITION_AND_NORMAL,
+ vertexShaderSource : BasicMaterialAppearanceVS,
+ fragmentShaderSource : BasicMaterialAppearanceFS
+ }),
+ /**
+ * Materials with textures, which require position
,
+ * normal
, and st
vertex attributes,
+ * are supported. The vast majority of materials fall into this category.
+ *
+ * @readonly
+ */
+ TEXTURED : freezeObject({
+ vertexFormat : VertexFormat.POSITION_NORMAL_AND_ST,
+ vertexShaderSource : TexturedMaterialAppearanceVS,
+ fragmentShaderSource : TexturedMaterialAppearanceFS
+ }),
+ /**
+ * All materials, including those that work in tangent space, are supported.
+ * This requires position
, normal
, st
,
+ * binormal
, and tangent
vertex attributes.
+ *
+ * @readonly
+ */
+ ALL : freezeObject({
+ vertexFormat : VertexFormat.ALL,
+ vertexShaderSource : AllMaterialAppearanceVS,
+ fragmentShaderSource : AllMaterialAppearanceFS
+ })
+ };
+
+ return MaterialAppearance;
+});
\ No newline at end of file
diff --git a/Source/Scene/PerInstanceColorAppearance.js b/Source/Scene/PerInstanceColorAppearance.js
new file mode 100644
index 000000000000..aac5e3bf0fdc
--- /dev/null
+++ b/Source/Scene/PerInstanceColorAppearance.js
@@ -0,0 +1,222 @@
+/*global define*/
+define([
+ '../Core/defaultValue',
+ '../Core/VertexFormat',
+ './Appearance',
+ '../Shaders/Appearances/PerInstanceColorAppearanceVS',
+ '../Shaders/Appearances/PerInstanceColorAppearanceFS',
+ '../Shaders/Appearances/PerInstanceFlatColorAppearanceVS',
+ '../Shaders/Appearances/PerInstanceFlatColorAppearanceFS'
+ ], function(
+ defaultValue,
+ VertexFormat,
+ Appearance,
+ PerInstanceColorAppearanceVS,
+ PerInstanceColorAppearanceFS,
+ PerInstanceFlatColorAppearanceVS,
+ PerInstanceFlatColorAppearanceFS) {
+ "use strict";
+
+ /**
+ * An appearance for {@link GeometryInstance} instances with color attributes.
+ * This allows several geometry instances, each with a different color, to
+ * be drawn with the same {@link Primitive} as shown in the second example below.
+ *
+ * @alias PerInstanceColorAppearance
+ * @constructor
+ *
+ * @param {Boolean} [options.flat=false] When true
, flat shading is used in the fragment shader, which means lighting is not taking into account.
+ * @param {Boolean} [options.faceForward=false] When true
, the fragment shader flips the surface normal as needed to ensure that the normal faces the viewer to avoid dark spots. This is useful when both sides of a geometry should be shaded like {@link WallGeometry}.
+ * @param {Boolean} [options.translucent=true] When true
, the geometry is expected to appear translucent so {@link PerInstanceColorAppearance#renderState} has alpha blending enabled.
+ * @param {Boolean} [options.closed=false] When true
, the geometry is expected to be closed so {@link PerInstanceColorAppearance#renderState} has backface culling enabled.
+ * @param {String} [options.vertexShaderSource=undefined] Optional GLSL vertex shader source to override the default vertex shader.
+ * @param {String} [options.fragmentShaderSource=undefined] Optional GLSL fragment shader source to override the default fragment shader.
+ * @param {RenderState} [options.renderState=undefined] Optional render state to override the default render state.
+ *
+ * @example
+ * // A solid white line segment
+ * var primitive = new Primitive({
+ * geometryInstances : new GeometryInstance({
+ * geometry : new SimplePolylineGeometry({
+ * positions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(0.0, 0.0),
+ * Cartographic.fromDegrees(5.0, 0.0)
+ * ])
+ * }),
+ * color : new Color(1.0, 1.0, 1.0, 1.0)
+ * }),
+ * appearance : new PerInstanceColorAppearance({
+ * flat : true,
+ * translucent : false
+ * })
+ * }));
+ *
+ * // Two extents in a primitive, each with a different color
+ * var instance = new GeometryInstance({
+ * geometry : new ExtentGeometry({
+ * extent : Extent.fromDegrees(0.0, 20.0, 10.0, 30.0)
+ * }),
+ * color : new Color(1.0, 0.0, 0.0, 0.5)
+ * });
+ *
+ * var anotherInstance = new GeometryInstance({
+ * geometry : new ExtentGeometry({
+ * extent : Extent.fromDegrees(0.0, 40.0, 10.0, 50.0)
+ * }),
+ * color : new Color(0.0, 0.0, 1.0, 0.5)
+ * });
+ *
+ * var extentPrimitive = new Primitive({
+ * geometryInstances : [instance, anotherInstance],
+ * appearance : new PerInstanceColorAppearance()
+ * });
+ */
+ var PerInstanceColorAppearance = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var translucent = defaultValue(options.translucent, true);
+ var closed = defaultValue(options.closed, false);
+ var flat = defaultValue(options.flat, false);
+ var vs = flat ? PerInstanceFlatColorAppearanceVS : PerInstanceColorAppearanceVS;
+ var fs = flat ? PerInstanceFlatColorAppearanceFS : PerInstanceColorAppearanceFS;
+ var vertexFormat = flat ? PerInstanceColorAppearance.FLAT_VERTEX_FORMAT : PerInstanceColorAppearance.VERTEX_FORMAT;
+
+ /**
+ * This property is part of the {@link Appearance} interface, but is not
+ * used by {@link PerInstanceColorAppearance} since a fully custom fragment shader is used.
+ *
+ * @type Material
+ *
+ * @default undefined
+ */
+ this.material = undefined;
+
+ /**
+ * The GLSL source code for the vertex shader.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.vertexShaderSource = defaultValue(options.vertexShaderSource, vs);
+
+ /**
+ * The GLSL source code for the fragment shader. The full fragment shader
+ * source is built procedurally taking into account {@link PerInstanceColorAppearance#flat},
+ * and {@link PerInstanceColorAppearance#faceForward}.
+ * Use {@link PerInstanceColorAppearance#getFragmentShaderSource} to get the full source.
+ *
+ * @type String
+ *
+ * @readonly
+ */
+ this.fragmentShaderSource = defaultValue(options.fragmentShaderSource, fs);
+
+ /**
+ * The render state. This is not the final {@link RenderState} instance; instead,
+ * it can contain a subset of render state properties identical to renderState
+ * passed to {@link Context#createRenderState}.
+ *
+ * The render state can be explicitly defined when constructing a {@link PerInstanceColorAppearance}
+ * instance, or it is set implicitly via {@link PerInstanceColorAppearance#translucent}
+ * and {@link PerInstanceColorAppearance#closed}.
+ *
+ *
+ * @type Object
+ *
+ * @readonly
+ */
+ this.renderState = defaultValue(options.renderState, Appearance.getDefaultRenderState(translucent, closed));
+
+ // Non-derived members
+
+ /**
+ * The {@link VertexFormat} that this appearance instance is compatible with.
+ * A geometry can have more vertex attributes and still be compatible - at a
+ * potential performance cost - but it can't have less.
+ *
+ * @type VertexFormat
+ *
+ * @readonly
+ */
+ this.vertexFormat = vertexFormat;
+
+ /**
+ * When true
, flat shading is used in the fragment shader,
+ * which means lighting is not taking into account.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.flat = flat;
+
+ /**
+ * When true
, the fragment shader flips the surface normal
+ * as needed to ensure that the normal faces the viewer to avoid
+ * dark spots. This is useful when both sides of a geometry should be
+ * shaded like {@link WallGeometry}.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.faceForward = defaultValue(options.faceForward, false);
+
+ /**
+ * When true
, the geometry is expected to appear translucent so
+ * {@link PerInstanceColorAppearance#renderState} has alpha blending enabled.
+ *
+ * @readonly
+ *
+ * @default true
+ */
+ this.translucent = translucent;
+
+ /**
+ * When true
, the geometry is expected to be closed so
+ * {@link PerInstanceColorAppearance#renderState} has backface culling enabled.
+ * If the viewer enters the geometry, it will not be visible.
+ *
+ * @readonly
+ *
+ * @default false
+ */
+ this.closed = closed;
+ };
+
+ /**
+ * The {@link VertexFormat} that all {@link PerInstanceColorAppearance} instances
+ * are compatible with. This requires only position
and st
+ * attributes.
+ *
+ * @type VertexFormat
+ *
+ * @constant
+ */
+ PerInstanceColorAppearance.VERTEX_FORMAT = VertexFormat.POSITION_AND_NORMAL;
+
+ /**
+ * The {@link VertexFormat} that all {@link PerInstanceColorAppearance} instances
+ * are compatible with when {@link PerInstanceColorAppearance#flat} is false
.
+ * This requires only a position
attribute.
+ *
+ * @type VertexFormat
+ *
+ * @constant
+ */
+ PerInstanceColorAppearance.FLAT_VERTEX_FORMAT = VertexFormat.POSITION_ONLY;
+
+ /**
+ * Procedurally creates the full GLSL fragment shader source. For {@link PerInstanceColorAppearance},
+ * this is derived from {@link PerInstanceColorAppearance#fragmentShaderSource}, {@link PerInstanceColorAppearance#flat},
+ * and {@link PerInstanceColorAppearance#faceForward}.
+ *
+ * @memberof PerInstanceColorAppearance
+ *
+ * @return String The full GLSL fragment shader source.
+ */
+ PerInstanceColorAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource;
+
+ return PerInstanceColorAppearance;
+});
\ No newline at end of file
diff --git a/Source/Scene/Polygon.js b/Source/Scene/Polygon.js
index ff97853ba7a1..f0da0fa4e74c 100644
--- a/Source/Scene/Polygon.js
+++ b/Source/Scene/Polygon.js
@@ -3,136 +3,34 @@ define([
'../Core/DeveloperError',
'../Core/defaultValue',
'../Core/Color',
- '../Core/combine',
'../Core/destroyObject',
- '../Core/Cartesian2',
'../Core/Math',
'../Core/Ellipsoid',
- '../Core/BoundingRectangle',
- '../Core/BoundingSphere',
- '../Core/Cartesian3',
- '../Core/Cartesian4',
- '../Core/ComponentDatatype',
- '../Core/MeshFilters',
- '../Core/PrimitiveType',
- '../Core/EllipsoidTangentPlane',
+ '../Core/GeometryInstance',
+ '../Core/PolygonGeometry',
'../Core/PolygonPipeline',
- '../Core/WindingOrder',
- '../Core/ExtentTessellator',
- '../Core/Intersect',
'../Core/Queue',
- '../Core/Matrix3',
- '../Core/Quaternion',
- '../Renderer/BlendingState',
- '../Renderer/BufferUsage',
- '../Renderer/CommandLists',
- '../Renderer/CullFace',
- '../Renderer/DrawCommand',
- '../Renderer/VertexLayout',
- '../Renderer/createPickFragmentShaderSource',
- './Material',
- './SceneMode',
- '../Shaders/PolygonVS',
- '../Shaders/PolygonFS'
+ './EllipsoidSurfaceAppearance',
+ './Primitive',
+ './Material'
], function(
DeveloperError,
defaultValue,
Color,
- combine,
destroyObject,
- Cartesian2,
CesiumMath,
Ellipsoid,
- BoundingRectangle,
- BoundingSphere,
- Cartesian3,
- Cartesian4,
- ComponentDatatype,
- MeshFilters,
- PrimitiveType,
- EllipsoidTangentPlane,
+ GeometryInstance,
+ PolygonGeometry,
PolygonPipeline,
- WindingOrder,
- ExtentTessellator,
- Intersect,
Queue,
- Matrix3,
- Quaternion,
- BlendingState,
- BufferUsage,
- CommandLists,
- CullFace,
- DrawCommand,
- VertexLayout,
- createPickFragmentShaderSource,
- Material,
- SceneMode,
- PolygonVS,
- PolygonFS) {
+ EllipsoidSurfaceAppearance,
+ Primitive,
+ Material) {
"use strict";
- var attributeIndices = {
- position3DHigh : 0,
- position3DLow : 1,
- position2DHigh : 2,
- position2DLow : 3,
- textureCoordinates : 4
- };
-
- function PositionVertices() {
- this._va = undefined;
- }
-
- PositionVertices.prototype.getVertexArrays = function() {
- return this._va;
- };
-
- PositionVertices.prototype.update = function(context, meshes, bufferUsage) {
- if (typeof meshes !== 'undefined') {
- // Initially create or recreate vertex array and buffers
- this._destroyVA();
-
- var va = [];
-
- var length = meshes.length;
- for ( var i = 0; i < length; ++i) {
- va.push(context.createVertexArrayFromMesh({
- mesh : meshes[i],
- attributeIndices : attributeIndices,
- bufferUsage : bufferUsage,
- vertexLayout : VertexLayout.INTERLEAVED
- }));
- }
-
- this._va = va;
- } else {
- this._destroyVA();
- }
- };
-
- PositionVertices.prototype._destroyVA = function() {
- var va = this._va;
- if (typeof va !== 'undefined') {
- this._va = undefined;
-
- var length = va.length;
- for ( var i = 0; i < length; ++i) {
- va[i].destroy();
- }
- }
- };
-
- PositionVertices.prototype.isDestroyed = function() {
- return false;
- };
-
- PositionVertices.prototype.destroy = function() {
- this._destroyVA();
- return destroyObject(this);
- };
-
/**
- * DOC_TBA
+ * A renderable polygon or hierarchy of polygons.
*
* @alias Polygon
* @constructor
@@ -153,91 +51,65 @@ define([
*
* @demo Cesium Sandcastle Polygons Demo
*/
- var Polygon = function() {
- this._sp = undefined;
- this._rs = undefined;
-
- this._spPick = undefined;
-
- this._vertices = new PositionVertices();
- this._pickId = undefined;
-
- this._boundingVolume = new BoundingSphere();
- this._boundingVolume2D = new BoundingSphere();
-
- this._commandLists = new CommandLists();
+ var Polygon = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
/**
- * DOC_TBA
+ * The ellipsoid that the polygon is drawn on.
+ *
+ * @type Ellipsoid
+ *
+ * @default Ellipsoid.WGS84
*/
- this.ellipsoid = Ellipsoid.WGS84;
+ this.ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
this._ellipsoid = undefined;
/**
- * DOC_TBA
- */
- this.height = 0.0;
- this._height = undefined;
-
- /**
- * DOC_TBA
+ * The distance, in radians, between each latitude and longitude in the underlying geometry.
+ * A lower granularity fits the curvature of the {@link Polygon#ellipsoid} better,
+ * but uses more triangles.
+ *
+ * @type Number
+ *
+ * @default CesiumMath.RADIANS_PER_DEGREE
*/
- this.granularity = CesiumMath.toRadians(1.0);
+ this.granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
this._granularity = undefined;
/**
- * DOC_TBA
+ * The height, in meters, that the polygon is raised above the {@link Polygon#ellipsoid}.
+ *
+ * @type Number
+ *
+ * @default 0.0
*/
- this.scene2D = {
- /**
- * DOC_TBA
- */
- granularity : CesiumMath.toRadians(30.0)
- };
+ this.height = defaultValue(options.height, 0.0);
+ this._height = undefined;
/**
- * DOC_TBA
- */
- this.scene3D = {
- /**
- * DOC_TBA
+ * The angle, in radians, relative to north that the polygon's texture is rotated.
+ * Positive angles rotate counter-clockwise.
+ *
+ * @type Number
*
- * granularity can override object-level granularity
+ * @default 0.0
*/
- };
-
- this._positions = undefined;
+ this.textureRotationAngle = defaultValue(options.textureRotationAngle, 0.0);
this._textureRotationAngle = undefined;
- this._extent = undefined;
- this._polygonHierarchy = undefined;
- this._createVertexArray = false;
/**
- * Determines if this polygon will be shown.
+ * Determines if this primitive will be shown.
*
* @type {Boolean}
* @default true
*/
- this.show = true;
+ this.show = defaultValue(options.show, true);
- /**
- * The usage hint for the polygon's vertex buffer.
- *
- * @type {BufferUsage}
- * @default {@link BufferUsage.STATIC_DRAW}
- *
- * @performance If bufferUsage
changes, the next time
- * {@link Polygon#update} is called, the polygon's vertex buffer
- * is rewritten - an O(n)
operation that also incurs CPU to GPU overhead.
- * For best performance, it is important to provide the proper usage hint. If the polygon
- * will not change over several frames, use BufferUsage.STATIC_DRAW
.
- * If the polygon will change every frame, use BufferUsage.STREAM_DRAW
.
- */
- this.bufferUsage = BufferUsage.STATIC_DRAW;
- this._bufferUsage = BufferUsage.STATIC_DRAW;
+ var material = Material.fromType(undefined, Material.ColorType);
+ material.uniforms.color = new Color(1.0, 1.0, 0.0, 0.5);
/**
- * The surface appearance of the polygon. This can be one of several built-in {@link Material} objects or a custom material, scripted with
+ * The surface appearance of the primitive. This can be one of several built-in {@link Material} objects or a custom material, scripted with
* Fabric.
*
* The default material is Material.ColorType
.
@@ -255,32 +127,18 @@ define([
*
* @see Fabric
*/
- this.material = Material.fromType(undefined, Material.ColorType);
- this.material.uniforms.color = new Color(1.0, 1.0, 0.0, 0.5);
- this._material = undefined;
-
- this._mode = SceneMode.SCENE3D;
- this._projection = undefined;
+ this.material = defaultValue(options.material, material);
- var that = this;
- this._uniforms = {
- u_height : function() {
- return (that._mode !== SceneMode.SCENE2D) ? that.height : 0.0;
- }
- };
+ this._positions = options.positions;
+ this._polygonHierarchy = options.polygonHierarchy;
+ this._createPrimitive = false;
- this._pickColorUniform = {
- czm_pickColor : function() {
- return that._pickId.color;
- }
- };
-
- this._pickUniforms = undefined;
- this._drawUniforms = undefined;
+ this._primitive = undefined;
};
/**
- * DOC_TBA
+ * Returns the positions that define the boundary of the polygon. If {@link Polygon#configureFromPolygonHierarchy}
+ * was used, this returns undefined
.
*
* @memberof Polygon
*
@@ -293,7 +151,7 @@ define([
};
/**
- * DOC_TBA
+ * Sets positions that define the boundary of the polygon.
*
* @memberof Polygon
*
@@ -303,27 +161,22 @@ define([
* @see Polygon#getPositions
*
* @param {Array} positions The cartesian positions of the polygon.
- * @param {Number} [height=0.0] The height of the polygon.
- * @param {Number} [textureRotationAngle=0.0] The angle, in radians, to rotate the texture. Positive angles are counter-clockwise.
*
* @example
* polygon.setPositions([
* ellipsoid.cartographicToCartesian(new Cartographic(...)),
* ellipsoid.cartographicToCartesian(new Cartographic(...)),
* ellipsoid.cartographicToCartesian(new Cartographic(...))
- * ], 10.0);
+ * ]);
*/
- Polygon.prototype.setPositions = function(positions, height, textureRotationAngle) {
+ Polygon.prototype.setPositions = function(positions) {
// positions can be undefined
if (typeof positions !== 'undefined' && (positions.length < 3)) {
throw new DeveloperError('At least three positions are required.');
}
- this.height = defaultValue(height, 0.0);
- this._textureRotationAngle = defaultValue(textureRotationAngle, 0.0);
- this._extent = undefined;
- this._polygonHierarchy = undefined;
this._positions = positions;
- this._createVertexArray = true;
+ this._polygonHierarchy = undefined;
+ this._createPrimitive = true;
};
/**
@@ -354,312 +207,34 @@ define([
* }
*
*
- * @param {Number} [height=0.0] The height of the polygon.
- * @param {Number} [textureRotationAngle=0.0] The angle to rotate the texture in radians.
*
- * @exception {DeveloperError} At least three positions are required.
+ * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
*
* @example
* // A triangle within a triangle
* var hierarchy = {
- * positions : [new Cartesian3(-634066.5629045101,-4608738.034138676,4348640.761750969),
- * new Cartesian3(-1321523.0597310204,-5108871.981065817,3570395.2500986718),
- * new Cartesian3(46839.74837473363,-5303481.972379478,3530933.5841716)],
- * holes : [{
- * positions :[new Cartesian3(-646079.44483647,-4811233.11175887,4123187.2266941597),
- * new Cartesian3(-1024015.4454943262,-5072141.413164587,3716492.6173834214),
- * new Cartesian3(-234678.22583880965,-5189078.820849883,3688809.059214336)]
- * }]
- * };
+ * positions : [
+ * new Cartesian3(-634066.5629045101, -4608738.034138676, 4348640.761750969),
+ * new Cartesian3(-1321523.0597310204, -5108871.981065817, 3570395.2500986718),
+ * new Cartesian3(46839.74837473363, -5303481.972379478, 3530933.5841716)
+ * ],
+ * holes : [{
+ * positions :[
+ * new Cartesian3(-646079.44483647, -4811233.11175887, 4123187.2266941597),
+ * new Cartesian3(-1024015.4454943262, -5072141.413164587, 3716492.6173834214),
+ * new Cartesian3(-234678.22583880965, -5189078.820849883, 3688809.059214336)
+ * ]
+ * }]
+ * };
*/
- Polygon.prototype.configureFromPolygonHierarchy = function(hierarchy, height, textureRotationAngle) {
- // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
- var polygons = [];
- var queue = new Queue();
- queue.enqueue(hierarchy);
-
- while (queue.length !== 0) {
- var outerNode = queue.dequeue();
- var outerRing = outerNode.positions;
-
- if (outerRing.length < 3) {
- throw new DeveloperError('At least three positions are required.');
- }
-
- var numChildren = outerNode.holes ? outerNode.holes.length : 0;
- if (numChildren === 0) {
- // The outer polygon is a simple polygon with no nested inner polygon.
- polygons.push(outerNode.positions);
- } else {
- // The outer polygon contains inner polygons
- var holes = [];
- for ( var i = 0; i < numChildren; i++) {
- var hole = outerNode.holes[i];
- holes.push(hole.positions);
-
- var numGrandchildren = 0;
- if (hole.holes) {
- numGrandchildren = hole.holes.length;
- }
-
- for ( var j = 0; j < numGrandchildren; j++) {
- queue.enqueue(hole.holes[j]);
- }
- }
- var combinedPolygon = PolygonPipeline.eliminateHoles(outerRing, holes);
- polygons.push(combinedPolygon);
- }
- }
-
- this.height = defaultValue(height, 0.0);
- this._textureRotationAngle = defaultValue(textureRotationAngle, 0.0);
+ Polygon.prototype.configureFromPolygonHierarchy = function(hierarchy) {
this._positions = undefined;
- this._extent = undefined;
- this._polygonHierarchy = polygons;
- this._createVertexArray = true;
+ this._polygonHierarchy = hierarchy;
+ this._createPrimitive = true;
};
/**
- * DOC_TBA
- *
- * @memberof Polygon
- *
- * @param {extent} extent. The cartographic extent of the tile, with north, south, east and
- * west properties in radians.
- *
- * @param {double} [height=0.0]. The height of the cartographic extent.
- * @param {double} [rotation=0.0]. The rotation of the cartographic extent.
- * @example
- * polygon.configureExtent(new Extent(
- * CesiumMath.toRadians(0.0),
- * CesiumMath.toRadians(0.0),
- * CesiumMath.toRadians(10.0),
- * CesiumMath.toRadians(10.0)),
- * 0.0,
- * CesiumMath.toRadians(45.0),
- * );
- */
- Polygon.prototype.configureExtent = function(extent, height, rotation) {
- this._extent = extent;
- this.height = defaultValue(height, 0.0);
- this.rotation = defaultValue(rotation, 0.0);
- this._textureRotationAngle = undefined;
- this._positions = undefined;
- this._polygonHierarchy = undefined;
- this._createVertexArray = true;
- };
-
- var appendTextureCoordinatesCartesian2 = new Cartesian2();
- var appendTextureCoordinatesCartesian3 = new Cartesian3();
- var appendTextureCoordinatesQuaternion = new Quaternion();
- var appendTextureCoordinatesMatrix3 = new Matrix3();
-
- function appendTextureCoordinates(tangentPlane, boundingRectangle, mesh, angle) {
- var origin = new Cartesian2(boundingRectangle.x, boundingRectangle.y);
-
- var positions = mesh.attributes.position.values;
- var length = positions.length;
-
- var textureCoordinates = new Float32Array(2 * (length / 3));
- var j = 0;
-
- var rotation = Quaternion.fromAxisAngle(tangentPlane._plane.normal, angle, appendTextureCoordinatesQuaternion);
- var textureMatrix = Matrix3.fromQuaternion(rotation, appendTextureCoordinatesMatrix3);
-
- // PERFORMANCE_IDEA: Instead of storing texture coordinates per-vertex, we could
- // save memory by computing them in the fragment shader. However, projecting
- // the point onto the plane may have precision issues.
- for ( var i = 0; i < length; i += 3) {
- var p = appendTextureCoordinatesCartesian3;
- p.x = positions[i];
- p.y = positions[i + 1];
- p.z = positions[i + 2];
- Matrix3.multiplyByVector(textureMatrix, p, p);
- var st = tangentPlane.projectPointOntoPlane(p, appendTextureCoordinatesCartesian2);
- st.subtract(origin, st);
-
- textureCoordinates[j++] = st.x / boundingRectangle.width;
- textureCoordinates[j++] = st.y / boundingRectangle.height;
- }
-
- mesh.attributes.textureCoordinates = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 2,
- values : textureCoordinates
- };
-
- return mesh;
- }
-
- var computeBoundingRectangleCartesian2 = new Cartesian2();
- var computeBoundingRectangleCartesian3 = new Cartesian3();
- var computeBoundingRectangleQuaternion = new Quaternion();
- var computeBoundingRectangleMatrix3 = new Matrix3();
-
- function computeBoundingRectangle(tangentPlane, positions, angle, result) {
- var rotation = Quaternion.fromAxisAngle(tangentPlane._plane.normal, angle, computeBoundingRectangleQuaternion);
- var textureMatrix = Matrix3.fromQuaternion(rotation,computeBoundingRectangleMatrix3);
-
- var minX = Number.POSITIVE_INFINITY;
- var maxX = Number.NEGATIVE_INFINITY;
- var minY = Number.POSITIVE_INFINITY;
- var maxY = Number.NEGATIVE_INFINITY;
-
- var length = positions.length;
- for ( var i = 0; i < length; ++i) {
- var p = Cartesian3.clone(positions[i], computeBoundingRectangleCartesian3);
- Matrix3.multiplyByVector(textureMatrix, p, p);
- var st = tangentPlane.projectPointOntoPlane(p, computeBoundingRectangleCartesian2);
-
- if (typeof st !== 'undefined') {
- minX = Math.min(minX, st.x);
- maxX = Math.max(maxX, st.x);
-
- minY = Math.min(minY, st.y);
- maxY = Math.max(maxY, st.y);
- }
- }
-
- if (typeof result === 'undefined') {
- result = new BoundingRectangle();
- }
-
- result.x = minX;
- result.y = minY;
- result.width = maxX - minX;
- result.height = maxY - minY;
- return result;
- }
-
- var createMeshFromPositionsPositions = [];
- var createMeshFromPositionsBoundingRectangle = new BoundingRectangle();
-
- function createMeshFromPositions(polygon, positions, angle, boundingSphere, outerPositions) {
- var cleanedPositions = PolygonPipeline.cleanUp(positions);
- if (cleanedPositions.length < 3) {
- // Duplicate positions result in not enough positions to form a polygon.
- return undefined;
- }
-
- var tangentPlane = EllipsoidTangentPlane.fromPoints(cleanedPositions, polygon.ellipsoid);
- var positions2D = tangentPlane.projectPointsOntoPlane(cleanedPositions, createMeshFromPositionsPositions);
-
- var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D);
- if (originalWindingOrder === WindingOrder.CLOCKWISE) {
- positions2D.reverse();
- cleanedPositions.reverse();
- }
- var indices = PolygonPipeline.earClip2D(positions2D);
- // Checking bounding sphere with plane for quick reject
- var minX = boundingSphere.center.x - boundingSphere.radius;
- if ((minX < 0) && (BoundingSphere.intersect(boundingSphere, Cartesian4.UNIT_Y) === Intersect.INTERSECTING)) {
- indices = PolygonPipeline.wrapLongitude(cleanedPositions, indices);
- }
- var mesh = PolygonPipeline.computeSubdivision(cleanedPositions, indices, polygon._granularity);
- var boundary = outerPositions || cleanedPositions;
- var boundingRectangle = computeBoundingRectangle(tangentPlane, boundary, angle, createMeshFromPositionsBoundingRectangle);
- mesh = appendTextureCoordinates(tangentPlane, boundingRectangle, mesh, angle);
- return mesh;
- }
-
- function createMeshes(polygon) {
- // PERFORMANCE_IDEA: Move this to a web-worker.
- var i;
- var meshes = [];
- var mesh;
-
- if ((typeof polygon._extent !== 'undefined') && !polygon._extent.isEmpty()) {
- mesh = ExtentTessellator.compute({extent: polygon._extent, rotation: polygon.rotation, generateTextureCoordinates:true});
- if (typeof mesh !== 'undefined') {
- meshes.push(mesh);
- }
- polygon._boundingVolume = BoundingSphere.fromExtent3D(polygon._extent, polygon._ellipsoid, polygon._boundingVolume);
- if (polygon._mode !== SceneMode.SCENE3D) {
- polygon._boundingVolume2D = BoundingSphere.fromExtent2D(polygon._extent, polygon._projection, polygon._boundingVolume2D);
- var center2D = polygon._boundingVolume2D.center;
- polygon._boundingVolume2D.center = new Cartesian3(0.0, center2D.x, center2D.y);
- }
- } else if (typeof polygon._positions !== 'undefined') {
- polygon._boundingVolume = BoundingSphere.fromPoints(polygon._positions, polygon._boundingVolume);
- mesh = createMeshFromPositions(polygon, polygon._positions, polygon._textureRotationAngle, polygon._boundingVolume);
- if (typeof mesh !== 'undefined') {
- meshes.push(mesh);
- }
- } else if (typeof polygon._polygonHierarchy !== 'undefined') {
- var outerPositions = polygon._polygonHierarchy[0];
- // The bounding volume is just around the boundary points, so there could be cases for
- // contrived polygons on contrived ellipsoids - very oblate ones - where the bounding
- // volume doesn't cover the polygon.
- polygon._boundingVolume = BoundingSphere.fromPoints(outerPositions, polygon._boundingVolume);
- for (i = 0; i < polygon._polygonHierarchy.length; i++) {
- mesh = createMeshFromPositions(polygon, polygon._polygonHierarchy[i], polygon._textureRotationAngle, polygon._boundingVolume, outerPositions);
- if (typeof mesh !== 'undefined') {
- meshes.push(mesh);
- }
- }
- }
-
- if (meshes.length === 0) {
- return undefined;
- }
-
- var processedMeshes = [];
- for (i = 0; i < meshes.length; i++) {
- mesh = meshes[i];
- mesh = PolygonPipeline.scaleToGeodeticHeight(mesh, polygon.height, polygon.ellipsoid);
- mesh = MeshFilters.reorderForPostVertexCache(mesh);
- mesh = MeshFilters.reorderForPreVertexCache(mesh);
-
- if (polygon._mode === SceneMode.SCENE3D) {
- mesh.attributes.position2DHigh = { // Not actually used in shader
- value : [0.0, 0.0]
- };
- mesh.attributes.position2DLow = { // Not actually used in shader
- value : [0.0, 0.0]
- };
- mesh = MeshFilters.encodeAttribute(mesh, 'position', 'position3DHigh', 'position3DLow');
- } else {
- mesh = MeshFilters.projectTo2D(mesh, polygon._projection);
-
- if ((i === 0) && (polygon._mode !== SceneMode.SCENE3D)) {
- var projectedPositions = mesh.attributes.position2D.values;
- var positions = [];
-
- for (var j = 0; j < projectedPositions.length; j += 2) {
- positions.push(new Cartesian3(projectedPositions[j], projectedPositions[j + 1], 0.0));
- }
-
- polygon._boundingVolume2D = BoundingSphere.fromPoints(positions, polygon._boundingVolume2D);
- var center2DPositions = polygon._boundingVolume2D.center;
- polygon._boundingVolume2D.center = new Cartesian3(0.0, center2DPositions.x, center2DPositions.y);
- }
-
- mesh = MeshFilters.encodeAttribute(mesh, 'position3D', 'position3DHigh', 'position3DLow');
- mesh = MeshFilters.encodeAttribute(mesh, 'position2D', 'position2DHigh', 'position2DLow');
- }
- processedMeshes = processedMeshes.concat(MeshFilters.fitToUnsignedShortIndices(mesh));
- }
-
- return processedMeshes;
- }
-
- function getGranularity(polygon, mode) {
- if (mode === SceneMode.SCENE3D) {
- return polygon.scene3D.granularity || polygon.granularity;
- }
-
- return polygon.scene2D.granularity || polygon.granularity;
- }
-
- /**
- * Commits changes to properties before rendering by updating the object's WebGL resources.
- *
- * @memberof Polygon
- *
- * @exception {DeveloperError} this.ellipsoid must be defined.
- * @exception {DeveloperError} this.material must be defined.
- * @exception {DeveloperError} this.granularity must be greater than zero.
- * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
+ * @private
*/
Polygon.prototype.update = function(context, frameState, commandList) {
if (typeof this.ellipsoid === 'undefined') {
@@ -670,10 +245,7 @@ define([
throw new DeveloperError('this.material must be defined.');
}
- var mode = frameState.mode;
- var granularity = getGranularity(this, mode);
-
- if (granularity < 0.0) {
+ if (this.granularity < 0.0) {
throw new DeveloperError('this.granularity and scene2D/scene3D overrides must be greater than zero.');
}
@@ -681,149 +253,66 @@ define([
return;
}
- if (this._ellipsoid !== this.ellipsoid) {
- this._createVertexArray = true;
- this._ellipsoid = this.ellipsoid;
- }
-
- if (this._height !== this.height) {
- this._createVertexArray = true;
- this._height = this.height;
- }
-
- if (this._granularity !== granularity) {
- this._createVertexArray = true;
- this._granularity = granularity;
- }
-
- if (this._bufferUsage !== this.bufferUsage) {
- this._createVertexArray = true;
- this._bufferUsage = this.bufferUsage;
- }
-
- var projection = frameState.scene2D.projection;
- if (this._projection !== projection) {
- this._createVertexArray = true;
- this._projection = projection;
- }
-
- if (this._mode !== mode) {
- // SCENE2D, COLUMBUS_VIEW, and MORPHING use the same rendering path, so a
- // transition only occurs when switching from/to SCENE3D
- this._createVertexArray = this._mode === SceneMode.SCENE3D || mode === SceneMode.SCENE3D;
- this._mode = mode;
- }
-
- if (this._createVertexArray) {
- this._createVertexArray = false;
- this._vertices.update(context, createMeshes(this), this.bufferUsage);
- }
-
- if (typeof this._vertices.getVertexArrays() === 'undefined') {
+ if (!this._createPrimitive && (typeof this._primitive === 'undefined')) {
+ // No positions/hierarchy to draw
return;
}
- var boundingVolume;
- if (mode === SceneMode.SCENE3D) {
- boundingVolume = this._boundingVolume;
- } else if (mode === SceneMode.COLUMBUS_VIEW || mode === SceneMode.SCENE2D) {
- boundingVolume = this._boundingVolume2D;
- } else {
- boundingVolume = this._boundingVolume.union(this._boundingVolume2D);
- }
-
- var pass = frameState.passes;
- var vas = this._vertices.getVertexArrays();
- var length = vas.length;
- var commands;
- var command;
-
- var materialChanged = this._material !== this.material;
+ if (this._createPrimitive ||
+ (this._ellipsoid !== this.ellipsoid) ||
+ (this._granularity !== this.granularity) ||
+ (this._height !== this.height) ||
+ (this._textureRotationAngle !== this.textureRotationAngle)) {
- this._commandLists.removeAll();
- if (pass.color) {
- if (typeof this._rs === 'undefined') {
- // TODO: Should not need this in 2D/columbus view, but is hiding a triangulation issue.
- this._rs = context.createRenderState({
- cull : {
- enabled : true,
- face : CullFace.BACK
- },
- blending : BlendingState.ALPHA_BLEND
- });
- }
-
- // Recompile shader when material changes
- if (materialChanged) {
- this._material = this.material;
-
- var fsSource =
- '#line 0\n' +
- this.material.shaderSource +
- '#line 0\n' +
- PolygonFS;
-
- this._sp = context.getShaderCache().replaceShaderProgram(this._sp, PolygonVS, fsSource, attributeIndices);
-
- this._drawUniforms = combine([this._uniforms, this.material._uniforms], false, false);
- }
-
- commands = this._commandLists.colorList;
- commands.length = length;
+ this._createPrimitive = false;
+ this._ellipsoid = this.ellipsoid;
+ this._granularity = this.granularity;
+ this._height = this.height;
+ this._textureRotationAngle = this.textureRotationAngle;
- for (var i = 0; i < length; ++i) {
- command = commands[i];
- if (typeof command === 'undefined') {
- command = commands[i] = new DrawCommand();
- }
+ this._primitive = this._primitive && this._primitive.destroy();
- command.boundingVolume = boundingVolume;
- command.primitiveType = PrimitiveType.TRIANGLES;
- command.shaderProgram = this._sp;
- command.uniformMap = this._drawUniforms;
- command.vertexArray = vas[i];
- command.renderState = this._rs;
+ if ((typeof this._positions === 'undefined') && (typeof this._polygonHierarchy === 'undefined')) {
+ return;
}
- }
- if (pass.pick) {
- if (typeof this._pickId === 'undefined') {
- this._pickId = context.createPickId(this);
- }
-
- // Recompile shader when material changes
- if (materialChanged || typeof this._spPick === 'undefined') {
- var pickFS = createPickFragmentShaderSource(
- '#line 0\n' +
- this.material.shaderSource +
- '#line 0\n' +
- PolygonFS, 'uniform');
-
- this._spPick = context.getShaderCache().replaceShaderProgram(this._spPick, PolygonVS, pickFS, attributeIndices);
- this._pickUniforms = combine([this._uniforms, this._pickColorUniform, this.material._uniforms], false, false);
+ var instance;
+ if (typeof this._positions !== 'undefined') {
+ instance = new GeometryInstance({
+ geometry : PolygonGeometry.fromPositions({
+ positions : this._positions,
+ height : this.height,
+ vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT,
+ stRotation : this.textureRotationAngle,
+ ellipsoid : this.ellipsoid,
+ granularity : this.granularity
+ }),
+ id : this
+ });
+ } else {
+ instance = new GeometryInstance({
+ geometry : new PolygonGeometry({
+ polygonHierarchy : this._polygonHierarchy,
+ height : this.height,
+ vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT,
+ stRotation : this.textureRotationAngle,
+ ellipsoid : this.ellipsoid,
+ granularity : this.granularity
+ }),
+ id : this
+ });
}
- commands = this._commandLists.pickList;
- commands.length = length;
-
- for (var j = 0; j < length; ++j) {
- command = commands[j];
- if (typeof command === 'undefined') {
- command = commands[j] = new DrawCommand();
- }
-
- command.boundingVolume = boundingVolume;
- command.primitiveType = PrimitiveType.TRIANGLES;
- command.shaderProgram = this._spPick;
- command.uniformMap = this._pickUniforms;
- command.vertexArray = vas[j];
- command.renderState = this._rs;
- }
+ this._primitive = new Primitive({
+ geometryInstances : instance,
+ appearance : new EllipsoidSurfaceAppearance({
+ aboveGround : (this.height > 0.0)
+ })
+ });
}
- if (!this._commandLists.empty()) {
- commandList.push(this._commandLists);
- }
+ this._primitive.appearance.material = this.material;
+ this._primitive.update(context, frameState, commandList);
};
/**
@@ -862,10 +351,7 @@ define([
* polygon = polygon && polygon.destroy();
*/
Polygon.prototype.destroy = function() {
- this._sp = this._sp && this._sp.release();
- this._spPick = this._spPick && this._spPick.release();
- this._vertices = this._vertices.destroy();
- this._pickId = this._pickId && this._pickId.destroy();
+ this._primitive = this._primitive && this._primitive.destroy();
return destroyObject(this);
};
diff --git a/Source/Scene/PolylineCollection.js b/Source/Scene/PolylineCollection.js
index 08f4a448cbba..d4a2cace2a48 100644
--- a/Source/Scene/PolylineCollection.js
+++ b/Source/Scene/PolylineCollection.js
@@ -8,6 +8,7 @@ define([
'../Core/Cartesian4',
'../Core/EncodedCartesian3',
'../Core/Matrix4',
+ '../Core/Math',
'../Core/ComponentDatatype',
'../Core/IndexDatatype',
'../Core/PrimitiveType',
@@ -32,6 +33,7 @@ define([
Cartesian4,
EncodedCartesian3,
Matrix4,
+ CesiumMath,
ComponentDatatype,
IndexDatatype,
PrimitiveType,
@@ -57,7 +59,6 @@ define([
//When it does, we need to recreate the indicesBuffer.
var POSITION_SIZE_INDEX = Polyline.POSITION_SIZE_INDEX;
var NUMBER_OF_PROPERTIES = Polyline.NUMBER_OF_PROPERTIES;
- var SIXTYFOURK = 64 * 1024;
var attributeIndices = {
position3DHigh : 0,
@@ -744,14 +745,14 @@ define([
vbo += vertexBufferOffset[k];
- var positionHighOffset = 6 * (k * (positionSizeInBytes * SIXTYFOURK) - vbo * positionSizeInBytes);//componentsPerAttribute(3) * componentDatatype(4)
+ var positionHighOffset = 6 * (k * (positionSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES) - vbo * positionSizeInBytes);//componentsPerAttribute(3) * componentDatatype(4)
var positionLowOffset = positionSizeInBytes + positionHighOffset;
var prevPositionHighOffset = positionSizeInBytes + positionLowOffset;
var prevPositionLowOffset = positionSizeInBytes + prevPositionHighOffset;
var nextPositionHighOffset = positionSizeInBytes + prevPositionLowOffset;
var nextPositionLowOffset = positionSizeInBytes + nextPositionHighOffset;
- var vertexPickColorBufferOffset = k * (pickColorSizeInBytes * SIXTYFOURK) - vbo * pickColorSizeInBytes;
- var vertexTexCoordExpandWidthAndShowBufferOffset = k * (texCoordExpandWidthAndShowSizeInBytes * SIXTYFOURK) - vbo * texCoordExpandWidthAndShowSizeInBytes;
+ var vertexPickColorBufferOffset = k * (pickColorSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES) - vbo * pickColorSizeInBytes;
+ var vertexTexCoordExpandWidthAndShowBufferOffset = k * (texCoordExpandWidthAndShowSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES) - vbo * texCoordExpandWidthAndShowSizeInBytes;
var attributes = [{
index : attributeIndices.position3DHigh,
@@ -1247,7 +1248,7 @@ define([
for ( var j = 0; j < numberOfSegments; ++j) {
var segmentLength = segments[j] - 1.0;
for ( var k = 0; k < segmentLength; ++k) {
- if (indicesCount + 4 >= SIXTYFOURK - 1) {
+ if (indicesCount + 4 >= CesiumMath.SIXTY_FOUR_KILOBYTES - 1) {
polyline._locatorBuckets.push({
locator : bucketLocator,
count : segmentIndexCount
@@ -1279,7 +1280,7 @@ define([
count : segmentIndexCount
});
- if (indicesCount + 4 >= SIXTYFOURK - 1) {
+ if (indicesCount + 4 >= CesiumMath.SIXTY_FOUR_KILOBYTES - 1) {
vertexBufferOffset.push(0);
indices = [];
totalIndices.push(indices);
diff --git a/Source/Scene/Primitive.js b/Source/Scene/Primitive.js
new file mode 100644
index 000000000000..4711e8c65a4a
--- /dev/null
+++ b/Source/Scene/Primitive.js
@@ -0,0 +1,1002 @@
+/*global define*/
+define([
+ '../Core/clone',
+ '../Core/defaultValue',
+ '../Core/DeveloperError',
+ '../Core/destroyObject',
+ '../Core/Matrix4',
+ '../Core/Color',
+ '../Core/GeometryPipeline',
+ '../Core/PrimitiveType',
+ '../Core/BoundingSphere',
+ '../Core/Geometry',
+ '../Core/GeometryAttribute',
+ '../Core/GeometryAttributes',
+ '../Core/GeometryInstance',
+ '../Core/GeometryInstanceAttribute',
+ '../Core/ComponentDatatype',
+ '../Core/Cartesian3',
+ '../Renderer/BufferUsage',
+ '../Renderer/VertexLayout',
+ '../Renderer/CommandLists',
+ '../Renderer/DrawCommand',
+ '../Renderer/createPickFragmentShaderSource',
+ './SceneMode'
+ ], function(
+ clone,
+ defaultValue,
+ DeveloperError,
+ destroyObject,
+ Matrix4,
+ Color,
+ GeometryPipeline,
+ PrimitiveType,
+ BoundingSphere,
+ Geometry,
+ GeometryAttribute,
+ GeometryAttributes,
+ GeometryInstance,
+ GeometryInstanceAttribute,
+ ComponentDatatype,
+ Cartesian3,
+ BufferUsage,
+ VertexLayout,
+ CommandLists,
+ DrawCommand,
+ createPickFragmentShaderSource,
+ SceneMode) {
+ "use strict";
+
+ /**
+ * A primitive represents geometry in the {@link Scene}. The geometry can be from a single {@link GeometryInstance}
+ * as shown in example 1 below, or from an array of instances, even if the geometry is from different
+ * geometry types, e.g., an {@link ExtentGeometry} and an {@link EllipsoidGeometry} as shown in Code Example 2.
+ *
+ * A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including
+ * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement,
+ * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix
+ * and match most of them and add a new geometry or appearance independently of each other.
+ *
+ *
+ * Combining multiple instances into one primitive is called batching, and significantly improves performance for static data.
+ * Instances can be individually picked; {@link Context#pick} returns their {@link GeometryInstance#id}. Using
+ * per-instance appearances like {@link PerInstanceColorAppearance}, each instance can also have a unique color.
+ *
+ *
+ * @alias Primitive
+ * @constructor
+ *
+ * @param {Array|GeometryInstance} [options.geometryInstances=undefined] The geometry instances - or a single geometry instance - to render.
+ * @param {Appearance} [options.appearance=undefined] The appearance used to render the primitive.
+ * @param {Boolean} [options.vertexCacheOptimize=true] When true
, geometry vertices are optimized for the pre and post-vertex-shader caches.
+ * @param {Boolean} [options.releaseGeometryInstances=true] When true
, the primitive does not keep a reference to the input geometryInstances
to save memory.
+ * @param {Boolean} [options.allow3DOnly=false] When true
, each geometry instance will only be rendered in 3D.
+ *
+ * @example
+ * // 1. Draw a translucent ellipse on the surface with a checkerboard pattern
+ * var instance = new GeometryInstance({
+ * geometry : new EllipseGeometry({
+ * vertexFormat : VertexFormat.POSITION_AND_ST,
+ * ellipsoid : ellipsoid,
+ * center : ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-100, 20)),
+ * semiMinorAxis : 500000.0,
+ * semiMajorAxis : 1000000.0,
+ * bearing : CesiumMath.PI_OVER_FOUR
+ * }),
+ * id : 'object returned when this instance is picked and to get/set per-instance attributes'
+ * });
+ * var primitive = new Primitive({
+ * geometryInstances : instance,
+ * appearance : new EllipsoidSurfaceAppearance({
+ * material : Material.fromType(scene.getContext(), 'Checkerboard')
+ * })
+ * });
+ * scene.getPrimitives().add(primitive);
+ *
+ * // 2. Draw different instances each with a unique color
+ * var extentInstance = new GeometryInstance({
+ * geometry : new ExtentGeometry({
+ * vertexFormat : VertexFormat.POSITION_AND_NORMAL,
+ * extent : new Extent(
+ * CesiumMath.toRadians(-140.0),
+ * CesiumMath.toRadians(30.0),
+ * CesiumMath.toRadians(-100.0),
+ * CesiumMath.toRadians(40.0))
+ * }),
+ * id : 'extent',
+ * attribute : {
+ * color : new ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5)
+ * }
+ * });
+ * var ellipsoidInstance = new GeometryInstance({
+ * geometry : new EllipsoidGeometry({
+ * vertexFormat : VertexFormat.POSITION_AND_NORMAL,
+ * radii : new Cartesian3(500000.0, 500000.0, 1000000.0)
+ * }),
+ * modelMatrix : Matrix4.multiplyByTranslation(Transforms.eastNorthUpToFixedFrame(
+ * ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-95.59777, 40.03883))), new Cartesian3(0.0, 0.0, 500000.0)),
+ * id : 'ellipsoid',
+ * attribute : {
+ * color : ColorGeometryInstanceAttribute.fromColor(Color.AQUA)
+ * }
+ * });
+ * var primitive = new Primitive({
+ * geometryInstances : [extentInstance, ellipsoidInstance],
+ * appearance : new PerInstanceColorAppearance()
+ * });
+ * scene.getPrimitives().add(primitive);
+ *
+ * @see GeometryInstance
+ * @see Appearance
+ */
+ var Primitive = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ /**
+ * The geometry instances rendered with this primitive. This may
+ * be undefined
if options.releaseGeometryInstances
+ * is true
when the primitive is constructed.
+ *
+ * Changing this property after the primitive is rendered has no effect.
+ *
+ *
+ * @type Array
+ *
+ * @default undefined
+ */
+ this.geometryInstances = options.geometryInstances;
+
+ /**
+ * The {@link Appearance} used to shade this primitive. Each geometry
+ * instance is shaded with the same appearance. Some appearances, like
+ * {@link PerInstanceColorAppearance} allow giving each instance unique
+ * properties.
+ *
+ * @type Appearance
+ *
+ * @default undefined
+ */
+ this.appearance = options.appearance;
+ this._appearance = undefined;
+ this._material = undefined;
+
+ /**
+ * The 4x4 transformation matrix that transforms the primitive (all geometry instances) from model to world coordinates.
+ * When this is the identity matrix, the primitive is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
+ * Local reference frames can be used by providing a different transformation matrix, like that returned
+ * by {@link Transforms.eastNorthUpToFixedFrame}. This matrix is available to GLSL vertex and fragment
+ * shaders via {@link czm_model} and derived uniforms.
+ *
+ * @type Matrix4
+ *
+ * @default Matrix4.IDENTITY
+ *
+ * @example
+ * var origin = ellipsoid.cartographicToCartesian(
+ * Cartographic.fromDegrees(-95.0, 40.0, 200000.0));
+ * p.modelMatrix = Transforms.eastNorthUpToFixedFrame(origin);
+ *
+ * @see czm_model
+ */
+ this.modelMatrix = Matrix4.IDENTITY.clone();
+
+ /**
+ * Determines if the primitive will be shown. This affects all geometry
+ * instances in the primitive.
+ *
+ * @type Boolean
+ *
+ * @default true
+ */
+ this.show = true;
+
+ this._vertexCacheOptimize = defaultValue(options.vertexCacheOptimize, true);
+ this._releaseGeometryInstances = defaultValue(options.releaseGeometryInstances, true);
+ // When true, geometry is transformed to world coordinates even if there is a single
+ // geometry or all geometries are in the same reference frame.
+ this._allow3DOnly = defaultValue(options.allow3DOnly, false);
+ this._boundingSphere = undefined;
+ this._boundingSphere2D = undefined;
+ this._perInstanceAttributes = {};
+ this._lastPerInstanceAttributeIndex = 0;
+
+ this._va = [];
+ this._attributeIndices = undefined;
+
+ this._rs = undefined;
+ this._sp = undefined;
+
+ this._pickSP = undefined;
+ this._pickIds = [];
+
+ this._commandLists = new CommandLists();
+ };
+
+ function cloneAttribute(attribute) {
+ return new GeometryAttribute({
+ componentDatatype : attribute.componentDatatype,
+ componentsPerAttribute : attribute.componentsPerAttribute,
+ normalize : attribute.normalize,
+ values : new attribute.values.constructor(attribute.values)
+ });
+ }
+
+ function cloneGeometry(geometry) {
+ var attributes = geometry.attributes;
+ var newAttributes = new GeometryAttributes();
+ for (var property in attributes) {
+ if (attributes.hasOwnProperty(property) && typeof attributes[property] !== 'undefined') {
+ newAttributes[property] = cloneAttribute(attributes[property]);
+ }
+ }
+
+ var indices;
+ if (typeof geometry.indices !== 'undefined') {
+ var sourceValues = geometry.indices;
+ indices = new sourceValues.constructor(sourceValues);
+ }
+
+ return new Geometry({
+ attributes : newAttributes,
+ indices : indices,
+ primitiveType : geometry.primitiveType,
+ boundingSphere : BoundingSphere.clone(geometry.boundingSphere)
+ });
+ }
+
+ function cloneGeometryInstanceAttribute(attribute) {
+ return new GeometryInstanceAttribute({
+ componentDatatype : attribute.componentDatatype,
+ componentsPerAttribute : attribute.componentsPerAttribute,
+ normalize : attribute.normalize,
+ value : new attribute.value.constructor(attribute.value)
+ });
+ }
+
+ function cloneInstance(instance) {
+ var attributes = instance.attributes;
+ var newAttributes = {};
+ for (var property in attributes) {
+ if (attributes.hasOwnProperty(property)) {
+ newAttributes[property] = cloneGeometryInstanceAttribute(attributes[property]);
+ }
+ }
+
+ return new GeometryInstance({
+ geometry : cloneGeometry(instance.geometry),
+ modelMatrix : Matrix4.clone(instance.modelMatrix),
+ id : instance.id, // Shallow copy
+ attributes : newAttributes
+ });
+ }
+
+ function addPickColorAttribute(primitive, instances, context) {
+ var length = instances.length;
+
+ for (var i = 0; i < length; ++i) {
+ var instance = instances[i];
+ var geometry = instance.geometry;
+ var attributes = geometry.attributes;
+ var positionAttr = attributes.position;
+ var numberOfComponents = 4 * (positionAttr.values.length / positionAttr.componentsPerAttribute);
+
+ attributes.pickColor = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 4,
+ normalize : true,
+ values : new Uint8Array(numberOfComponents)
+ });
+
+ var pickId = context.createPickId(defaultValue(instance.id, primitive));
+ primitive._pickIds.push(pickId);
+
+ var pickColor = pickId.color;
+ var red = Color.floatToByte(pickColor.red);
+ var green = Color.floatToByte(pickColor.green);
+ var blue = Color.floatToByte(pickColor.blue);
+ var alpha = Color.floatToByte(pickColor.alpha);
+ var values = attributes.pickColor.values;
+
+ for (var j = 0; j < numberOfComponents; j += 4) {
+ values[j] = red;
+ values[j + 1] = green;
+ values[j + 2] = blue;
+ values[j + 3] = alpha;
+ }
+ }
+ }
+
+ function transformToWorldCoordinates(primitive, instances) {
+ var toWorld = !primitive._allow3DOnly;
+ var length = instances.length;
+ var i;
+
+ if (!toWorld && (length > 1)) {
+ var modelMatrix = instances[0].modelMatrix;
+
+ for (i = 1; i < length; ++i) {
+ if (!Matrix4.equals(modelMatrix, instances[i].modelMatrix)) {
+ toWorld = true;
+ break;
+ }
+ }
+ }
+
+ if (toWorld) {
+ for (i = 0; i < length; ++i) {
+ GeometryPipeline.transformToWorldCoordinates(instances[i]);
+ }
+ } else {
+ // Leave geometry in local coordinate system; auto update model-matrix.
+ Matrix4.clone(instances[0].modelMatrix, primitive.modelMatrix);
+ }
+ }
+
+ function getCommonPerInstanceAttributeNames(instances) {
+ var length = instances.length;
+
+ var attributesInAllInstances = [];
+ var attributes0 = instances[0].attributes;
+ var name;
+
+ for (name in attributes0) {
+ if (attributes0.hasOwnProperty(name)) {
+ var attribute = attributes0[name];
+ var inAllInstances = true;
+
+ // Does this same attribute exist in all instances?
+ for (var i = 1; i < length; ++i) {
+ var otherAttribute = instances[i].attributes[name];
+
+ if ((typeof otherAttribute === 'undefined') ||
+ (attribute.componentDatatype !== otherAttribute.componentDatatype) ||
+ (attribute.componentsPerAttribute !== otherAttribute.componentsPerAttribute) ||
+ (attribute.normalize !== otherAttribute.normalize)) {
+
+ inAllInstances = false;
+ break;
+ }
+ }
+
+ if (inAllInstances) {
+ attributesInAllInstances.push(name);
+ }
+ }
+ }
+
+ return attributesInAllInstances;
+ }
+
+ function addPerInstanceAttributes(primitive, instances, names) {
+ var length = instances.length;
+ for (var i = 0; i < length; ++i) {
+ var instance = instances[i];
+ var instanceAttributes = instance.attributes;
+ var geometry = instance.geometry;
+ var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
+
+ var namesLength = names.length;
+ for (var j = 0; j < namesLength; ++j) {
+ var name = names[j];
+ var attribute = instanceAttributes[name];
+ var componentDatatype = attribute.componentDatatype;
+ var value = attribute.value;
+ var componentsPerAttribute = value.length;
+
+ var buffer = componentDatatype.createTypedArray(numberOfVertices * componentsPerAttribute);
+ for (var k = 0; k < numberOfVertices; ++k) {
+ buffer.set(value, k * componentsPerAttribute);
+ }
+
+ geometry.attributes[name] = new GeometryAttribute({
+ componentDatatype : componentDatatype,
+ componentsPerAttribute : componentsPerAttribute,
+ normalize : attribute.normalize,
+ values : buffer
+ });
+ }
+ }
+ }
+
+ // PERFORMANCE_IDEA: Move pipeline to a web-worker.
+ function geometryPipeline(primitive, instances, context, projection) {
+ var length = instances.length;
+ var primitiveType = instances[0].geometry.primitiveType;
+ for (var i = 1; i < length; ++i) {
+ if (instances[i].geometry.primitiveType !== primitiveType) {
+ throw new DeveloperError('All instance geometries must have the same primitiveType.');
+ }
+ }
+
+ // Unify to world coordinates before combining.
+ transformToWorldCoordinates(primitive, instances);
+
+ // Clip to IDL
+ if (!primitive._allow3DOnly) {
+ for (i = 0; i < length; ++i) {
+ GeometryPipeline.wrapLongitude(instances[i].geometry);
+ }
+ }
+
+ // Add pickColor attribute for picking individual instances
+ addPickColorAttribute(primitive, instances, context);
+
+ // add attributes to the geometry for each per-instance attribute
+ var perInstanceAttributeNames = getCommonPerInstanceAttributeNames(instances);
+ addPerInstanceAttributes(primitive, instances, perInstanceAttributeNames);
+
+ // Optimize for vertex shader caches
+ if (primitive._vertexCacheOptimize) {
+ for (i = 0; i < length; ++i) {
+ GeometryPipeline.reorderForPostVertexCache(instances[i].geometry);
+ GeometryPipeline.reorderForPreVertexCache(instances[i].geometry);
+ }
+ }
+
+ // Combine into single geometry for better rendering performance.
+ var geometry = GeometryPipeline.combine(instances);
+
+ // Split positions for GPU RTE
+ if (!primitive._allow3DOnly) {
+ // Compute 2D positions
+ GeometryPipeline.projectTo2D(geometry, projection);
+
+ GeometryPipeline.encodeAttribute(geometry, 'position3D', 'position3DHigh', 'position3DLow');
+ GeometryPipeline.encodeAttribute(geometry, 'position2D', 'position2DHigh', 'position2DLow');
+ } else {
+ GeometryPipeline.encodeAttribute(geometry, 'position', 'position3DHigh', 'position3DLow');
+ }
+
+ if (!context.getElementIndexUint()) {
+ // Break into multiple geometries to fit within unsigned short indices if needed
+ return GeometryPipeline.fitToUnsignedShortIndices(geometry);
+ }
+
+ // Unsigned int indices are supported. No need to break into multiple geometries.
+ return [geometry];
+ }
+
+ function createPerInstanceVAAttributes(context, geometry, attributeIndices, names) {
+ var vaAttributes = [];
+
+ var bufferUsage = BufferUsage.DYNAMIC_DRAW;
+ var attributes = geometry.attributes;
+
+ var length = names.length;
+ for (var i = 0; i < length; ++i) {
+ var name = names[i];
+ var attribute = attributes[name];
+
+ var componentDatatype = attribute.componentDatatype;
+ if (componentDatatype === ComponentDatatype.DOUBLE) {
+ componentDatatype = ComponentDatatype.FLOAT;
+ }
+
+ var vertexBuffer = context.createVertexBuffer(componentDatatype.createTypedArray(attribute.values), bufferUsage);
+ vaAttributes.push({
+ index : attributeIndices[name],
+ vertexBuffer : vertexBuffer,
+ componentDatatype : componentDatatype,
+ componentsPerAttribute : attribute.componentsPerAttribute,
+ normalize : attribute.normalize
+ });
+
+ delete attributes[name];
+ }
+
+ return vaAttributes;
+ }
+
+ function computePerInstanceAttributeIndices(instances, vertexArrays, attributeIndices) {
+ var ids = [];
+ var indices = [];
+
+ var names = getCommonPerInstanceAttributeNames(instances);
+ var length = instances.length;
+ var offsets = {};
+ var vaIndices = {};
+
+ for (var i = 0; i < length; ++i) {
+ var instance = instances[i];
+ var numberOfVertices = Geometry.computeNumberOfVertices(instance.geometry);
+
+ var namesLength = names.length;
+ for (var j = 0; j < namesLength; ++j) {
+ var name = names[j];
+ var index = attributeIndices[name];
+
+ var tempVertexCount = numberOfVertices;
+ while (tempVertexCount > 0) {
+ var vaIndex = defaultValue(vaIndices[name], 0);
+ var va = vertexArrays[vaIndex];
+ var vaLength = va.getNumberOfAttributes();
+
+ var attribute;
+ for (var k = 0; k < vaLength; ++k) {
+ attribute = va.getAttribute(k);
+ if (attribute.index === index) {
+ break;
+ }
+ }
+
+ if (typeof ids[i] === 'undefined') {
+ ids[i] = instance.id;
+ }
+
+ if (typeof indices[i] === 'undefined') {
+ indices[i] = {};
+ }
+
+ if (typeof indices[i][name] === 'undefined') {
+ indices[i][name] = {
+ dirty : false,
+ value : instance.attributes[name].value,
+ indices : []
+ };
+ }
+
+ var size = attribute.vertexBuffer.getSizeInBytes() / attribute.componentDatatype.sizeInBytes;
+ size /= attribute.componentsPerAttribute;
+ var offset = defaultValue(offsets[name], 0);
+
+ var count;
+ if (offset + tempVertexCount < size) {
+ count = tempVertexCount;
+ indices[i][name].indices.push({
+ attribute : attribute,
+ offset : offset,
+ count : count
+ });
+ offsets[name] = offset + tempVertexCount;
+ } else {
+ count = size - offset;
+ indices[i][name].indices.push({
+ attribute : attribute,
+ offset : offset,
+ count : count
+ });
+ offsets[name] = 0;
+ vaIndices[name] = vaIndex + 1;
+ }
+
+ tempVertexCount -= count;
+ }
+ }
+ }
+
+ return {
+ ids : ids,
+ indices : indices
+ };
+ }
+
+ function createColumbusViewShader(primitive, vertexShaderSource) {
+ var attributes;
+ if (!primitive._allow3DOnly) {
+ attributes =
+ 'attribute vec3 position2DHigh;\n' +
+ 'attribute vec3 position2DLow;\n';
+ } else {
+ attributes = '';
+ }
+
+ var computePosition =
+ '\nvec4 czm_computePosition()\n' +
+ '{\n';
+ if (!primitive._allow3DOnly) {
+ computePosition +=
+ ' vec4 p;\n' +
+ ' if (czm_morphTime == 1.0)\n' +
+ ' {\n' +
+ ' p = czm_translateRelativeToEye(position3DHigh, position3DLow);\n' +
+ ' }\n' +
+ ' else if (czm_morphTime == 0.0)\n' +
+ ' {\n' +
+ ' p = czm_translateRelativeToEye(position2DHigh.zxy, position2DLow.zxy);\n' +
+ ' }\n' +
+ ' else\n' +
+ ' {\n' +
+ ' p = czm_columbusViewMorph(\n' +
+ ' czm_translateRelativeToEye(position2DHigh.zxy, position2DLow.zxy),\n' +
+ ' czm_translateRelativeToEye(position3DHigh, position3DLow),\n' +
+ ' czm_morphTime);\n' +
+ ' }\n' +
+ ' return p;\n';
+ } else {
+ computePosition += ' return czm_translateRelativeToEye(position3DHigh, position3DLow);\n';
+ }
+ computePosition += '}\n\n';
+
+ return attributes + vertexShaderSource + computePosition;
+ }
+
+ function createPickVertexShaderSource(vertexShaderSource) {
+ var renamedVS = vertexShaderSource.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_old_main()');
+ var pickMain =
+ 'attribute vec4 pickColor; \n' +
+ 'varying vec4 czm_pickColor; \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' czm_old_main(); \n' +
+ ' czm_pickColor = pickColor; \n' +
+ '}';
+
+ return renamedVS + '\n' + pickMain;
+ }
+
+ function appendShow(primitive, vertexShaderSource) {
+ if (typeof primitive._attributeIndices.show === 'undefined') {
+ return vertexShaderSource;
+ }
+
+ var renamedVS = vertexShaderSource.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_non_show_main()');
+ var showMain =
+ 'attribute float show;\n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' czm_non_show_main(); \n' +
+ ' gl_Position *= show; \n' +
+ '}';
+
+ return renamedVS + '\n' + showMain;
+ }
+
+ function validateShaderMatching(shaderProgram, attributeIndices) {
+ // For a VAO and shader program to be compatible, the VAO must have
+ // all active attribute in the shader program. The VAO may have
+ // extra attributes with the only concern being a potential
+ // performance hit due to extra memory bandwidth and cache pollution.
+ // The shader source could have extra attributes that are not used,
+ // but there is no guarantee they will be optimized out.
+ //
+ // Here, we validate that the VAO has all attributes required
+ // to match the shader program.
+ var shaderAttributes = shaderProgram.getVertexAttributes();
+
+ for (var name in shaderAttributes) {
+ if (shaderAttributes.hasOwnProperty(name)) {
+ if (typeof attributeIndices[name] === 'undefined') {
+ throw new DeveloperError('Appearance/Geometry mismatch. The appearance requires vertex shader attribute input \'' + name +
+ '\', which was not computed as part of the Geometry. Use the appearance\'s vertexFormat property when constructing the geometry.');
+ }
+ }
+ }
+
+ }
+
+ /**
+ * @private
+ */
+ Primitive.prototype.update = function(context, frameState, commandList) {
+ if (!this.show ||
+ ((typeof this.geometryInstances === 'undefined') && (this._va.length === 0)) ||
+ (typeof this.appearance === 'undefined') ||
+ (frameState.mode !== SceneMode.SCENE3D && this._allow3DOnly) ||
+ (!frameState.passes.color && !frameState.passes.pick)) {
+ return;
+ }
+
+ var colorCommands = this._commandLists.colorList;
+ var pickCommands = this._commandLists.pickList;
+ var colorCommand;
+ var pickCommand;
+ var length;
+ var i;
+
+ if (this._va.length === 0) {
+ var projection = frameState.scene2D.projection;
+
+ var instances = (Array.isArray(this.geometryInstances)) ? this.geometryInstances : [this.geometryInstances];
+ // Copy instances first since most pipeline operations modify the geometry and instance in-place.
+ length = instances.length;
+ var insts = new Array(length);
+ for (i = 0; i < length; ++i) {
+ insts[i] = cloneInstance(instances[i]);
+ }
+ var geometries = geometryPipeline(this, insts, context, projection);
+
+ this._attributeIndices = GeometryPipeline.createAttributeIndices(geometries[0]);
+
+ this._boundingSphere = geometries[0].boundingSphere;
+ if (!this._allow3DOnly && typeof this._boundingSphere !== 'undefined') {
+ this._boundingSphere2D = BoundingSphere.projectTo2D(this._boundingSphere, projection);
+ }
+
+ var geometry;
+ var perInstanceAttributeNames = getCommonPerInstanceAttributeNames(insts);
+
+ var va = [];
+ length = geometries.length;
+ for (i = 0; i < length; ++i) {
+ geometry = geometries[i];
+ var vaAttributes = createPerInstanceVAAttributes(context, geometry, this._attributeIndices, perInstanceAttributeNames);
+ va.push(context.createVertexArrayFromGeometry({
+ geometry : geometry,
+ attributeIndices : this._attributeIndices,
+ bufferUsage : BufferUsage.STATIC_DRAW,
+ vertexLayout : VertexLayout.INTERLEAVED,
+ vertexArrayAttributes : vaAttributes
+ }));
+ }
+
+ this._va = va;
+ this._perInstanceAttributes = computePerInstanceAttributeIndices(insts, va, this._attributeIndices);
+
+ for (i = 0; i < length; ++i) {
+ geometry = geometries[i];
+
+ // renderState, shaderProgram, and uniformMap for commands are set below.
+
+ colorCommand = new DrawCommand();
+ colorCommand.primitiveType = geometry.primitiveType;
+ colorCommand.vertexArray = this._va[i];
+ colorCommands.push(colorCommand);
+
+ pickCommand = new DrawCommand();
+ pickCommand.primitiveType = geometry.primitiveType;
+ pickCommand.vertexArray = this._va[i];
+ pickCommands.push(pickCommand);
+ }
+
+ if (this._releaseGeometryInstances) {
+ this.geometryInstances = undefined;
+ }
+ }
+
+ // Create or recreate render state and shader program if appearance/material changed
+ var appearance = this.appearance;
+ var material = appearance.material;
+ var createRS = false;
+ var createSP = false;
+
+ if (this._appearance !== appearance) {
+ this._appearance = appearance;
+ this._material = material;
+ createRS = true;
+ createSP = true;
+ } else if (this._material !== material ) {
+ this._material = material;
+ createSP = true;
+ }
+
+ if (createRS) {
+ this._rs = context.createRenderState(appearance.renderState);
+ }
+
+ if (createSP) {
+ var shaderCache = context.getShaderCache();
+ var vs = createColumbusViewShader(this, appearance.vertexShaderSource);
+ vs = appendShow(this, vs);
+ var fs = appearance.getFragmentShaderSource();
+
+ this._sp = shaderCache.replaceShaderProgram(this._sp, vs, fs, this._attributeIndices);
+ this._pickSP = shaderCache.replaceShaderProgram(this._pickSP,
+ createPickVertexShaderSource(vs),
+ createPickFragmentShaderSource(fs, 'varying'),
+ this._attributeIndices);
+
+ validateShaderMatching(this._sp, this._attributeIndices);
+ validateShaderMatching(this._pickSP, this._attributeIndices);
+ }
+
+ if (createRS || createSP) {
+ var uniforms = (typeof material !== 'undefined') ? material._uniforms : undefined;
+
+ length = colorCommands.length;
+ for (i = 0; i < length; ++i) {
+
+ colorCommand = colorCommands[i];
+ colorCommand.renderState = this._rs;
+ colorCommand.shaderProgram = this._sp;
+ colorCommand.uniformMap = uniforms;
+
+ pickCommand = pickCommands[i];
+ pickCommand.renderState = this._rs;
+ pickCommand.shaderProgram = this._pickSP;
+ pickCommand.uniformMap = uniforms;
+ }
+ }
+
+ // Update per-instance attributes
+ if (this._perInstanceAttributes._dirty) {
+ var perInstanceIndices = this._perInstanceAttributes.indices;
+ length = perInstanceIndices.length;
+ for (i = 0; i < length; ++i) {
+ var perInstanceAttributes = perInstanceIndices[i];
+ for (var name in perInstanceAttributes) {
+ if (perInstanceAttributes.hasOwnProperty(name)) {
+ var attribute = perInstanceAttributes[name];
+ if (attribute.dirty) {
+ var value = attribute.value;
+ var indices = attribute.indices;
+ var indicesLength = indices.length;
+ for (var j = 0; j < indicesLength; ++j) {
+ var index = indices[j];
+ var offset = index.offset;
+ var count = index.count;
+
+ var vaAttribute = index.attribute;
+ var componentDatatype = vaAttribute.componentDatatype;
+ var componentsPerAttribute = vaAttribute.componentsPerAttribute;
+
+ var typedArray = componentDatatype.createTypedArray(count * componentsPerAttribute);
+ for (var k = 0; k < count; ++k) {
+ typedArray.set(value, k * componentsPerAttribute);
+ }
+
+ var offsetInBytes = offset * componentsPerAttribute * componentDatatype.sizeInBytes;
+ vaAttribute.vertexBuffer.copyFromArrayView(typedArray, offsetInBytes);
+ }
+
+ attribute.dirty = false;
+ }
+ }
+ }
+ }
+
+ this._perInstanceAttributes._dirty = false;
+ }
+
+ var boundingSphere;
+ if (frameState.mode === SceneMode.SCENE3D) {
+ boundingSphere = this._boundingSphere;
+ } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
+ boundingSphere = this._boundingSphere2D;
+ } else if (frameState.mode === SceneMode.SCENE2D && typeof this._boundingSphere2D !== 'undefined') {
+ boundingSphere = BoundingSphere.clone(this._boundingSphere2D);
+ boundingSphere.center.x = 0.0;
+ } else if (typeof this._boundingSphere !== 'undefined' && typeof this._boundingSphere2D !== 'undefined') {
+ boundingSphere = BoundingSphere.union(this._boundingSphere, this._boundingSphere2D);
+ }
+
+ // modelMatrix can change from frame to frame
+ length = colorCommands.length;
+ for (i = 0; i < length; ++i) {
+ colorCommands[i].modelMatrix = this.modelMatrix;
+ pickCommands[i].modelMatrix = this.modelMatrix;
+
+ colorCommands[i].boundingVolume = boundingSphere;
+ pickCommands[i].boundingVolume = boundingSphere;
+ }
+
+ commandList.push(this._commandLists);
+ };
+
+ function createGetFunction(name, perInstanceAttributes) {
+ return function() {
+ return perInstanceAttributes[name].value;
+ };
+ }
+
+ function createSetFunction(name, perInstanceAttributes, container) {
+ return function (value) {
+ if (typeof value === 'undefined' || typeof value.length === 'undefined' || value.length < 1 || value.length > 4) {
+ throw new DeveloperError('value must be and array with length between 1 and 4.');
+ }
+
+ perInstanceAttributes[name].value = value;
+ perInstanceAttributes[name].dirty = true;
+ container._dirty = true;
+ };
+ }
+
+ /**
+ * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
+ *
+ * @param {Object} id The id of the {@link GeometryInstance}.
+ *
+ * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id.
+ *
+ * @exception {DeveloperError} id is required.
+ * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
+ *
+ * @example
+ * var attributes = primitive.getGeometryInstanceAttributes('an id');
+ * attributes.color = ColorGeometryInstanceAttribute.toValue(Color.AQUA);
+ * attributes.show = ShowGeometryInstanceAttribute.toValue(true);
+ */
+ Primitive.prototype.getGeometryInstanceAttributes = function(id) {
+ if (typeof id === 'undefined') {
+ throw new DeveloperError('id is required');
+ }
+
+ if (typeof this._perInstanceAttributes === 'undefined') {
+ throw new DeveloperError('must call update before calling getGeometryInstanceAttributes');
+ }
+
+ var index = -1;
+ var lastIndex = this._lastPerInstanceAttributeIndex;
+ var ids = this._perInstanceAttributes.ids;
+ var length = ids.length;
+ for (var i = 0; i < length; ++i) {
+ var curIndex = (lastIndex + i) % length;
+ if (id === ids[curIndex]) {
+ index = curIndex;
+ break;
+ }
+ }
+
+ if (index === -1) {
+ return undefined;
+ }
+
+ var perInstanceAttributes = this._perInstanceAttributes.indices[index];
+ var attributes = {};
+
+ for (var name in perInstanceAttributes) {
+ if (perInstanceAttributes.hasOwnProperty(name)) {
+ Object.defineProperty(attributes, name, {
+ get : createGetFunction(name, perInstanceAttributes, this._perInstanceAttributes),
+ set : createSetFunction(name, perInstanceAttributes, this._perInstanceAttributes)
+ });
+ }
+ }
+
+ this._lastPerInstanceAttributeIndex = index;
+
+ return attributes;
+ };
+
+ /**
+ * Returns true if this object was destroyed; otherwise, false.
+ *
+ * If this object was destroyed, it should not be used; calling any function other than
+ * isDestroyed
will result in a {@link DeveloperError} exception.
+ *
+ *
+ * @memberof Primitive
+ *
+ * @return {Boolean} true
if this object was destroyed; otherwise, false
.
+ *
+ * @see Primitive#destroy
+ */
+ Primitive.prototype.isDestroyed = function() {
+ return false;
+ };
+
+ /**
+ * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
+ * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
+ *
+ * Once an object is destroyed, it should not be used; calling any function other than
+ * isDestroyed
will result in a {@link DeveloperError} exception. Therefore,
+ * assign the return value (undefined
) to the object as done in the example.
+ *
+ *
+ * @memberof Primitive
+ *
+ * @return {undefined}
+ *
+ * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
+ *
+ * @see Primitive#isDestroyed
+ *
+ * @example
+ * e = e && e.destroy();
+ */
+ Primitive.prototype.destroy = function() {
+ var length;
+ var i;
+
+ this._sp = this._sp && this._sp.release();
+ this._pickSP = this._pickSP && this._pickSP.release();
+
+ var va = this._va;
+ length = va.length;
+ for (i = 0; i < length; ++i) {
+ va[i].destroy();
+ }
+ this._va = undefined;
+
+ var pickIds = this._pickIds;
+ length = pickIds.length;
+ for (i = 0; i < length; ++i) {
+ pickIds[i].destroy();
+ }
+ this._pickIds = undefined;
+
+ return destroyObject(this);
+ };
+
+ return Primitive;
+});
diff --git a/Source/Scene/Projections.js b/Source/Scene/Projections.js
deleted file mode 100644
index a92c288888b3..000000000000
--- a/Source/Scene/Projections.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/*global define*/
-define([
- '../Core/DeveloperError',
- '../Core/Enumeration'
- ], function(
- DeveloperError,
- Enumeration) {
- "use strict";
-
- /**
- * DOC_TBA
- *
- * @exports Projections
- */
- var Projections = {
- /**
- * DOC_TBA
- *
- * @type {Enumeration}
- * @constant
- * @default 0
- */
- WGS84 : new Enumeration(0, 'WGS84', {
- toWgs84 : function(extent, image) {
- return image;
- }
- }),
-
- /**
- * DOC_TBA
- *
- * @type {Enumeration}
- * @constant
- * @default 1
- */
- MERCATOR : new Enumeration(1, 'MERCATOR', {
- toWgs84 : function(extent, image) {
- if (typeof extent === 'undefined' || typeof extent.north === 'undefined' || typeof extent.south === 'undefined') {
- throw new DeveloperError('extent, extent.north and extent.south are required.');
- }
-
- if (typeof image === 'undefined') {
- throw new DeveloperError('image is required.');
- }
-
- var width = parseInt(image.width, 10);
- var height = parseInt(image.height, 10);
- var wRowBytes = width * 4; // Always 4 bytes per pixel.
-
- // draw image to canvas and get the pixels
- var canvas = document.createElement('canvas');
- canvas.width = width;
- canvas.height = height;
- var context = canvas.getContext('2d');
- context.drawImage(image, 0, 0);
- var fromPixels = context.getImageData(0, 0, width, height).data;
-
- // create array of pixels
- var newImageData = context.createImageData(width, height);
- var toPixels = newImageData.data;
-
- // WGS84 parameters
- var deltaWLat = (extent.north - extent.south) / height;
- var currentWLat = extent.north - (0.5 * deltaWLat);
-
- // mercator parameters
- var sinTheta = Math.sin(extent.south);
- var minMLat = 0.5 * Math.log((1 + sinTheta) / (1 - sinTheta));
- sinTheta = Math.sin(extent.north);
- var maxMLat = 0.5 * Math.log((1 + sinTheta) / (1 - sinTheta));
- var invMLatDim = 1.0 / (maxMLat - minMLat);
-
- // first row
- var heightMinusOne = height - 1;
- var i = 0;
- for (; i < wRowBytes; ++i) {
- toPixels[i] = fromPixels[i];
- }
-
- // interior rows
- var end, mLat, mRow;
- var j = 1;
- for (; j < heightMinusOne; ++j, currentWLat -= deltaWLat) {
- sinTheta = Math.sin(currentWLat);
- mLat = 0.5 * Math.log((1.0 + sinTheta) / (1.0 - sinTheta));
- mRow = Math.floor(heightMinusOne - ((heightMinusOne * (mLat - minMLat) * invMLatDim)));
- end = i + wRowBytes;
- for ( var k = 0; i < end; ++i, ++k) {
- toPixels[i] = fromPixels[mRow * wRowBytes + k];
- }
- }
-
- // last row
- end = i + wRowBytes;
- for (j = 0; i < end; ++i, ++j) {
- toPixels[i] = fromPixels[i];
- }
-
- // paint new image to canvas
- context.putImageData(newImageData, 0, 0);
-
- return canvas;
- }
- })
- };
-
- return Projections;
-});
diff --git a/Source/Scene/RectangularPyramidSensorVolume.js b/Source/Scene/RectangularPyramidSensorVolume.js
index 93be4e7bd79d..0b99be2ff376 100644
--- a/Source/Scene/RectangularPyramidSensorVolume.js
+++ b/Source/Scene/RectangularPyramidSensorVolume.js
@@ -43,9 +43,10 @@ define([
this.show = defaultValue(options.show, true);
/**
- * When true
, a polyline is shown where the sensor outline intersections the central body. The default is true
.
+ * When true
, a polyline is shown where the sensor outline intersections the central body.
*
* @type {Boolean}
+ *
* @default true
*
* @see RectangularPyramidSensorVolume#intersectionColor
@@ -57,9 +58,6 @@ define([
* Determines if a sensor intersecting the ellipsoid is drawn through the ellipsoid and potentially out
* to the other side, or if the part of the sensor intersecting the ellipsoid stops at the ellipsoid.
*
- *
- * The default is false
, meaning the sensor will not go through the ellipsoid.
- *
*
* @type {Boolean}
* @default false
diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js
index ca85e4b42116..daff89476255 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -486,7 +486,7 @@ define([
for (var i = 0; i < numFrustums; ++i) {
clearDepthStencil.execute(context, passState);
- var index = numFrustums - i - 1.0;
+ var index = numFrustums - i - 1;
var frustumCommands = frustumCommandsList[index];
frustum.near = frustumCommands.near;
frustum.far = frustumCommands.far;
diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js
index 834331e11ea5..389beb7f1f9f 100644
--- a/Source/Scene/SkyAtmosphere.js
+++ b/Source/Scene/SkyAtmosphere.js
@@ -1,9 +1,9 @@
/*global define*/
define([
'../Core/defaultValue',
- '../Core/CubeMapEllipsoidTessellator',
+ '../Core/EllipsoidGeometry',
'../Core/destroyObject',
- '../Core/MeshFilters',
+ '../Core/GeometryPipeline',
'../Core/PrimitiveType',
'../Core/Ellipsoid',
'../Renderer/BufferUsage',
@@ -15,9 +15,9 @@ define([
'../Shaders/SkyAtmosphereFS'
], function(
defaultValue,
- CubeMapEllipsoidTessellator,
+ EllipsoidGeometry,
destroyObject,
- MeshFilters,
+ GeometryPipeline,
PrimitiveType,
Ellipsoid,
BufferUsage,
@@ -52,9 +52,6 @@ define([
/**
* Determines if the atmosphere is shown.
- *
- * The default is true
.
- *
*
* @type {Boolean}
* @default true
@@ -134,10 +131,13 @@ define([
var command = this._command;
if (typeof command.vertexArray === 'undefined') {
- var mesh = CubeMapEllipsoidTessellator.compute(Ellipsoid.fromCartesian3(this._ellipsoid.getRadii().multiplyByScalar(1.025)), 60);
- command.vertexArray = context.createVertexArrayFromMesh({
- mesh : mesh,
- attributeIndices : MeshFilters.createAttributeIndices(mesh),
+ var geometry = new EllipsoidGeometry({
+ radii : this._ellipsoid.getRadii().multiplyByScalar(1.025),
+ numberOfPartitions : 60
+ });
+ command.vertexArray = context.createVertexArrayFromGeometry({
+ geometry : geometry,
+ attributeIndices : GeometryPipeline.createAttributeIndices(geometry),
bufferUsage : BufferUsage.STATIC_DRAW
});
command.primitiveType = PrimitiveType.TRIANGLES;
diff --git a/Source/Scene/SkyBox.js b/Source/Scene/SkyBox.js
index ae324ea427fe..7c7e4945cfeb 100644
--- a/Source/Scene/SkyBox.js
+++ b/Source/Scene/SkyBox.js
@@ -1,11 +1,12 @@
/*global define*/
define([
- '../Core/BoxTessellator',
+ '../Core/BoxGeometry',
'../Core/Cartesian3',
'../Core/destroyObject',
'../Core/DeveloperError',
'../Core/Matrix4',
- '../Core/MeshFilters',
+ '../Core/GeometryPipeline',
+ '../Core/VertexFormat',
'../Core/PrimitiveType',
'../Renderer/loadCubeMap',
'../Renderer/BufferUsage',
@@ -15,12 +16,13 @@ define([
'../Shaders/SkyBoxVS',
'../Shaders/SkyBoxFS'
], function(
- BoxTessellator,
+ BoxGeometry,
Cartesian3,
destroyObject,
DeveloperError,
Matrix4,
- MeshFilters,
+ GeometryPipeline,
+ VertexFormat,
PrimitiveType,
loadCubeMap,
BufferUsage,
@@ -83,9 +85,6 @@ define([
/**
* Determines if the sky box will be shown.
- *
- * The default is true
.
- *
*
* @type {Boolean}
* @default true
@@ -149,15 +148,16 @@ define([
}
};
- var mesh = BoxTessellator.compute({
- dimensions : new Cartesian3(2.0, 2.0, 2.0)
+ var geometry = BoxGeometry.fromDimensions({
+ dimensions : new Cartesian3(2.0, 2.0, 2.0),
+ vertexFormat : VertexFormat.POSITION_ONLY
});
- var attributeIndices = MeshFilters.createAttributeIndices(mesh);
+ var attributeIndices = GeometryPipeline.createAttributeIndices(geometry);
command.primitiveType = PrimitiveType.TRIANGLES;
command.modelMatrix = Matrix4.IDENTITY.clone();
- command.vertexArray = context.createVertexArrayFromMesh({
- mesh: mesh,
+ command.vertexArray = context.createVertexArrayFromGeometry({
+ geometry: geometry,
attributeIndices: attributeIndices,
bufferUsage: BufferUsage.STATIC_DRAW
});
diff --git a/Source/Scene/Sun.js b/Source/Scene/Sun.js
index 111e3e32e6db..f604b7ffbc17 100644
--- a/Source/Scene/Sun.js
+++ b/Source/Scene/Sun.js
@@ -50,9 +50,6 @@ define([
/**
* Determines if the sun will be shown.
- *
- * The default is true
.
- *
*
* @type {Boolean}
* @default true
diff --git a/Source/Scene/SunPostProcess.js b/Source/Scene/SunPostProcess.js
index b78d4ae1f906..9af0f6ff7bdb 100644
--- a/Source/Scene/SunPostProcess.js
+++ b/Source/Scene/SunPostProcess.js
@@ -7,6 +7,8 @@ define([
'../Core/ComponentDatatype',
'../Core/defaultValue',
'../Core/destroyObject',
+ '../Core/Geometry',
+ '../Core/GeometryAttribute',
'../Core/Math',
'../Core/Matrix4',
'../Core/PrimitiveType',
@@ -31,6 +33,8 @@ define([
ComponentDatatype,
defaultValue,
destroyObject,
+ Geometry,
+ GeometryAttribute,
CesiumMath,
Matrix4,
PrimitiveType,
@@ -113,9 +117,9 @@ define([
return vertexArray;
}
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 2,
values : [
@@ -124,9 +128,9 @@ define([
1.0, 1.0,
-1.0, 1.0
]
- },
+ }),
- textureCoordinates : {
+ textureCoordinates : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 2,
values : [
@@ -135,12 +139,13 @@ define([
1.0, 1.0,
0.0, 1.0
]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
- vertexArray = context.createVertexArrayFromMesh({
- mesh : mesh,
+ vertexArray = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : attributeIndices,
bufferUsage : BufferUsage.STATIC_DRAW
});
diff --git a/Source/Scene/ViewportQuad.js b/Source/Scene/ViewportQuad.js
index 0bdebb17f747..a72e3d8eeb6b 100644
--- a/Source/Scene/ViewportQuad.js
+++ b/Source/Scene/ViewportQuad.js
@@ -8,6 +8,8 @@ define([
'../Core/BoundingRectangle',
'../Core/ComponentDatatype',
'../Core/PrimitiveType',
+ '../Core/Geometry',
+ '../Core/GeometryAttribute',
'./Material',
'../Renderer/BufferUsage',
'../Renderer/BlendingState',
@@ -24,6 +26,8 @@ define([
BoundingRectangle,
ComponentDatatype,
PrimitiveType,
+ Geometry,
+ GeometryAttribute,
Material,
BufferUsage,
BlendingState,
@@ -56,13 +60,10 @@ define([
/**
* Determines if the viewport quad primitive will be shown.
- *
- * The default is true
.
- *
*
* @type {Boolean}
* @default true
- */
+ */
this.show = true;
if (typeof rectangle === 'undefined') {
@@ -119,9 +120,9 @@ define([
return vertexArray;
}
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 2,
values : [
@@ -130,9 +131,9 @@ define([
1.0, 1.0,
-1.0, 1.0
]
- },
+ }),
- textureCoordinates : {
+ textureCoordinates : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 2,
values : [
@@ -141,12 +142,13 @@ define([
1.0, 1.0,
0.0, 1.0
]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
- vertexArray = context.createVertexArrayFromMesh({
- mesh : mesh,
+ vertexArray = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : attributeIndices,
bufferUsage : BufferUsage.STATIC_DRAW
});
diff --git a/Source/Scene/createTangentSpaceDebugPrimitive.js b/Source/Scene/createTangentSpaceDebugPrimitive.js
new file mode 100644
index 000000000000..635cf8262586
--- /dev/null
+++ b/Source/Scene/createTangentSpaceDebugPrimitive.js
@@ -0,0 +1,100 @@
+/*global define*/
+define([
+ '../Core/defaultValue',
+ '../Core/DeveloperError',
+ '../Core/ColorGeometryInstanceAttribute',
+ '../Core/GeometryInstance',
+ '../Core/GeometryPipeline',
+ '../Core/Matrix4',
+ './Primitive',
+ './PerInstanceColorAppearance'
+ ], function(
+ defaultValue,
+ DeveloperError,
+ ColorGeometryInstanceAttribute,
+ GeometryInstance,
+ GeometryPipeline,
+ Matrix4,
+ Primitive,
+ PerInstanceColorAppearance) {
+ "use strict";
+
+ /**
+ * Creates a {@link Primitive} to visualize well-known vector vertex attributes:
+ * normal
, binormal
, and tangent
. Normal
+ * is red; binormal is green; and tangent is blue. If an attribute is not
+ * present, it is not drawn.
+ *
+ * @exports createTangentSpaceDebugPrimitive
+ *
+ * @param {Geometry} options.geometry The Geometry
instance with the attribute.
+ * @param {Number} [options.length=10000.0] The length of each line segment in meters. This can be negative to point the vector in the opposite direction.
+ * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The model matrix that transforms to transform the geometry from model to world coordinates.
+ *
+ * @returns {Primitive} A new Primitive instance with geometry for the vectors.
+ *
+ * @exception {DeveloperError} options.geometry is required.
+ * @exception {DeveloperError} options.geometry.attributes.position is required.
+ *
+ * @example
+ * scene.getPrimitives().add(createTangentSpaceDebugPrimitive({
+ * geometry : instance.geometry,
+ * length : 100000.0,
+ * modelMatrix : instance.modelMatrix
+ * }));
+ */
+ function createTangentSpaceDebugPrimitive(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var instances = [];
+
+ var geometry = options.geometry;
+ if (typeof geometry === 'undefined') {
+ throw new DeveloperError('options.geometry is required.');
+ }
+
+ var attributes = geometry.attributes;
+ var modelMatrix = Matrix4.clone(defaultValue(options.modelMatrix, Matrix4.IDENTITY));
+ var length = defaultValue(options.length, 10000.0);
+
+ if (typeof attributes.normal !== 'undefined') {
+ instances.push(new GeometryInstance({
+ geometry : GeometryPipeline.createLineSegmentsForVectors(geometry, 'normal', length),
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0)
+ },
+ modelMatrix : modelMatrix
+ }));
+ }
+
+ if (typeof attributes.binormal !== 'undefined') {
+ instances.push(new GeometryInstance({
+ geometry : GeometryPipeline.createLineSegmentsForVectors(geometry, 'binormal', length),
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(0.0, 1.0, 0.0, 1.0)
+ },
+ modelMatrix : modelMatrix
+ }));
+ }
+
+ if (typeof attributes.tangent !== 'undefined') {
+ instances.push(new GeometryInstance({
+ geometry : GeometryPipeline.createLineSegmentsForVectors(geometry, 'tangent', length),
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(0.0, 0.0, 1.0, 1.0)
+ },
+ modelMatrix : modelMatrix
+ }));
+ }
+
+ return new Primitive({
+ geometryInstances : instances,
+ appearance : new PerInstanceColorAppearance({
+ flat : true,
+ translucent : false
+ })
+ });
+ }
+
+ return createTangentSpaceDebugPrimitive;
+});
diff --git a/Source/Shaders/Appearances/AllMaterialAppearanceFS.glsl b/Source/Shaders/Appearances/AllMaterialAppearanceFS.glsl
new file mode 100644
index 000000000000..a8a2c2043e9f
--- /dev/null
+++ b/Source/Shaders/Appearances/AllMaterialAppearanceFS.glsl
@@ -0,0 +1,31 @@
+varying vec3 v_positionEC;
+varying vec3 v_normalEC;
+varying vec3 v_tangentEC;
+varying vec3 v_binormalEC;
+varying vec2 v_st;
+
+void main()
+{
+ vec3 positionToEyeEC = -v_positionEC;
+ mat3 tangentToEyeMatrix = czm_tangentToEyeSpaceMatrix(v_normalEC, v_tangentEC, v_binormalEC);
+
+ vec3 normalEC;
+#ifdef FACE_FORWARD
+ normalEC = normalize(faceforward(v_normalEC, vec3(0.0, 0.0, 1.0), -v_normalEC));
+#else
+ normalEC = normalize(v_normalEC);
+#endif
+
+ czm_materialInput materialInput;
+ materialInput.normalEC = normalEC;
+ materialInput.tangentToEyeMatrix = tangentToEyeMatrix;
+ materialInput.positionToEyeEC = positionToEyeEC;
+ materialInput.st = v_st;
+ czm_material material = czm_getMaterial(materialInput);
+
+#ifdef FLAT
+ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
+#else
+ gl_FragColor = czm_phong(normalize(positionToEyeEC), material);
+#endif
+}
diff --git a/Source/Shaders/Appearances/AllMaterialAppearanceVS.glsl b/Source/Shaders/Appearances/AllMaterialAppearanceVS.glsl
new file mode 100644
index 000000000000..59d74a27b25a
--- /dev/null
+++ b/Source/Shaders/Appearances/AllMaterialAppearanceVS.glsl
@@ -0,0 +1,25 @@
+attribute vec3 position3DHigh;
+attribute vec3 position3DLow;
+attribute vec3 normal;
+attribute vec3 tangent;
+attribute vec3 binormal;
+attribute vec2 st;
+
+varying vec3 v_positionEC;
+varying vec3 v_normalEC;
+varying vec3 v_tangentEC;
+varying vec3 v_binormalEC;
+varying vec2 v_st;
+
+void main()
+{
+ vec4 p = czm_computePosition();
+
+ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates
+ v_normalEC = czm_normal * normal; // normal in eye coordinates
+ v_tangentEC = czm_normal * tangent; // tangent in eye coordinates
+ v_binormalEC = czm_normal * binormal; // binormal in eye coordinates
+ v_st = st;
+
+ gl_Position = czm_modelViewProjectionRelativeToEye * p;
+}
diff --git a/Source/Shaders/Appearances/BasicMaterialAppearanceFS.glsl b/Source/Shaders/Appearances/BasicMaterialAppearanceFS.glsl
new file mode 100644
index 000000000000..23dd4f6ff845
--- /dev/null
+++ b/Source/Shaders/Appearances/BasicMaterialAppearanceFS.glsl
@@ -0,0 +1,25 @@
+varying vec3 v_positionEC;
+varying vec3 v_normalEC;
+
+void main()
+{
+ vec3 positionToEyeEC = -v_positionEC;
+
+ vec3 normalEC;
+#ifdef FACE_FORWARD
+ normalEC = normalize(faceforward(v_normalEC, vec3(0.0, 0.0, 1.0), -v_normalEC));
+#else
+ normalEC = normalize(v_normalEC);
+#endif
+
+ czm_materialInput materialInput;
+ materialInput.normalEC = normalEC;
+ materialInput.positionToEyeEC = positionToEyeEC;
+ czm_material material = czm_getMaterial(materialInput);
+
+#ifdef FLAT
+ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
+#else
+ gl_FragColor = czm_phong(normalize(positionToEyeEC), material);
+#endif
+}
diff --git a/Source/Shaders/Appearances/BasicMaterialAppearanceVS.glsl b/Source/Shaders/Appearances/BasicMaterialAppearanceVS.glsl
new file mode 100644
index 000000000000..6fe9772ba516
--- /dev/null
+++ b/Source/Shaders/Appearances/BasicMaterialAppearanceVS.glsl
@@ -0,0 +1,16 @@
+attribute vec3 position3DHigh;
+attribute vec3 position3DLow;
+attribute vec3 normal;
+
+varying vec3 v_positionEC;
+varying vec3 v_normalEC;
+
+void main()
+{
+ vec4 p = czm_computePosition();
+
+ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates
+ v_normalEC = czm_normal * normal; // normal in eye coordinates
+
+ gl_Position = czm_modelViewProjectionRelativeToEye * p;
+}
diff --git a/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceFS.glsl b/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceFS.glsl
new file mode 100644
index 000000000000..8243fe32fcac
--- /dev/null
+++ b/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceFS.glsl
@@ -0,0 +1,35 @@
+varying vec3 v_positionMC;
+varying vec3 v_positionEC;
+varying vec2 v_st;
+
+void main()
+{
+ czm_materialInput materialInput;
+
+ vec3 normalEC = czm_normal3D * czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0));
+#ifdef FACE_FORWARD
+ normalEC = normalize(faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC));
+#else
+ normalEC = normalize(normalEC);
+#endif
+
+ materialInput.s = v_st.s;
+ materialInput.st = v_st;
+ materialInput.str = vec3(v_st, 0.0);
+
+ // Convert tangent space material normal to eye space
+ materialInput.normalEC = normalEC;
+ materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, materialInput.normalEC);
+
+ // Convert view vector to world space
+ vec3 positionToEyeEC = -v_positionEC;
+ materialInput.positionToEyeEC = positionToEyeEC;
+
+ czm_material material = czm_getMaterial(materialInput);
+
+#ifdef FLAT
+ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
+#else
+ gl_FragColor = czm_phong(normalize(positionToEyeEC), material);
+#endif
+}
diff --git a/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceVS.glsl b/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceVS.glsl
new file mode 100644
index 000000000000..61f12dcb32e2
--- /dev/null
+++ b/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceVS.glsl
@@ -0,0 +1,18 @@
+attribute vec3 position3DHigh;
+attribute vec3 position3DLow;
+attribute vec2 st;
+
+varying vec3 v_positionMC;
+varying vec3 v_positionEC;
+varying vec2 v_st;
+
+void main()
+{
+ vec4 p = czm_computePosition();
+
+ v_positionMC = position3DHigh + position3DLow; // position in model coordinates
+ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates
+ v_st = st;
+
+ gl_Position = czm_modelViewProjectionRelativeToEye * p;
+}
diff --git a/Source/Shaders/Appearances/PerInstanceColorAppearanceFS.glsl b/Source/Shaders/Appearances/PerInstanceColorAppearanceFS.glsl
new file mode 100644
index 000000000000..c015090f8cdd
--- /dev/null
+++ b/Source/Shaders/Appearances/PerInstanceColorAppearanceFS.glsl
@@ -0,0 +1,24 @@
+varying vec3 v_positionEC;
+varying vec3 v_normalEC;
+varying vec4 v_color;
+
+void main()
+{
+ vec3 positionToEyeEC = -v_positionEC;
+
+ vec3 normalEC;
+#ifdef FACE_FORWARD
+ normalEC = normalize(faceforward(v_normalEC, vec3(0.0, 0.0, 1.0), -v_normalEC));
+#else
+ normalEC = normalize(v_normalEC);
+#endif
+
+ czm_materialInput materialInput;
+ materialInput.normalEC = normalEC;
+ materialInput.positionToEyeEC = positionToEyeEC;
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ material.diffuse = v_color.rgb;
+ material.alpha = v_color.a;
+
+ gl_FragColor = czm_phong(normalize(positionToEyeEC), material);
+}
diff --git a/Source/Shaders/Appearances/PerInstanceColorAppearanceVS.glsl b/Source/Shaders/Appearances/PerInstanceColorAppearanceVS.glsl
new file mode 100644
index 000000000000..aa8de58a0ba9
--- /dev/null
+++ b/Source/Shaders/Appearances/PerInstanceColorAppearanceVS.glsl
@@ -0,0 +1,19 @@
+attribute vec3 position3DHigh;
+attribute vec3 position3DLow;
+attribute vec3 normal;
+attribute vec4 color;
+
+varying vec3 v_positionEC;
+varying vec3 v_normalEC;
+varying vec4 v_color;
+
+void main()
+{
+ vec4 p = czm_computePosition();
+
+ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates
+ v_normalEC = czm_normal * normal; // normal in eye coordinates
+ v_color = color;
+
+ gl_Position = czm_modelViewProjectionRelativeToEye * p;
+}
diff --git a/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceFS.glsl b/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceFS.glsl
new file mode 100644
index 000000000000..3649a0883f23
--- /dev/null
+++ b/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceFS.glsl
@@ -0,0 +1,6 @@
+varying vec4 v_color;
+
+void main()
+{
+ gl_FragColor = v_color;
+}
diff --git a/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceVS.glsl b/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceVS.glsl
new file mode 100644
index 000000000000..44c759da268e
--- /dev/null
+++ b/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceVS.glsl
@@ -0,0 +1,14 @@
+attribute vec3 position3DHigh;
+attribute vec3 position3DLow;
+attribute vec4 color;
+
+varying vec4 v_color;
+
+void main()
+{
+ vec4 p = czm_computePosition();
+
+ v_color = color;
+
+ gl_Position = czm_modelViewProjectionRelativeToEye * p;
+}
diff --git a/Source/Shaders/Appearances/TexturedMaterialAppearanceFS.glsl b/Source/Shaders/Appearances/TexturedMaterialAppearanceFS.glsl
new file mode 100644
index 000000000000..4cb7d18d56df
--- /dev/null
+++ b/Source/Shaders/Appearances/TexturedMaterialAppearanceFS.glsl
@@ -0,0 +1,27 @@
+varying vec3 v_positionEC;
+varying vec3 v_normalEC;
+varying vec2 v_st;
+
+void main()
+{
+ vec3 positionToEyeEC = -v_positionEC;
+
+ vec3 normalEC;
+#ifdef FACE_FORWARD
+ normalEC = normalize(faceforward(v_normalEC, vec3(0.0, 0.0, 1.0), -v_normalEC));
+#else
+ normalEC = normalize(v_normalEC);
+#endif
+
+ czm_materialInput materialInput;
+ materialInput.normalEC = normalEC;
+ materialInput.positionToEyeEC = positionToEyeEC;
+ materialInput.st = v_st;
+ czm_material material = czm_getMaterial(materialInput);
+
+#ifdef FLAT
+ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
+#else
+ gl_FragColor = czm_phong(normalize(positionToEyeEC), material);
+#endif
+}
diff --git a/Source/Shaders/Appearances/TexturedMaterialAppearanceVS.glsl b/Source/Shaders/Appearances/TexturedMaterialAppearanceVS.glsl
new file mode 100644
index 000000000000..b606f12a0ef1
--- /dev/null
+++ b/Source/Shaders/Appearances/TexturedMaterialAppearanceVS.glsl
@@ -0,0 +1,19 @@
+attribute vec3 position3DHigh;
+attribute vec3 position3DLow;
+attribute vec3 normal;
+attribute vec2 st;
+
+varying vec3 v_positionEC;
+varying vec3 v_normalEC;
+varying vec2 v_st;
+
+void main()
+{
+ vec4 p = czm_computePosition();
+
+ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates
+ v_normalEC = czm_normal * normal; // normal in eye coordinates
+ v_st = st;
+
+ gl_Position = czm_modelViewProjectionRelativeToEye * p;
+}
diff --git a/Source/Shaders/BillboardCollectionVS.glsl b/Source/Shaders/BillboardCollectionVS.glsl
index 008085b83457..286056989917 100644
--- a/Source/Shaders/BillboardCollectionVS.glsl
+++ b/Source/Shaders/BillboardCollectionVS.glsl
@@ -37,7 +37,7 @@ void main()
///////////////////////////////////////////////////////////////////////////
- vec4 p = vec4(czm_translateRelativeToEye(positionHigh, positionLow), 1.0);
+ vec4 p = czm_translateRelativeToEye(positionHigh, positionLow);
vec4 positionEC = czm_modelViewRelativeToEye * p;
positionEC = czm_eyeOffset(positionEC, eyeOffset);
positionEC.xyz *= show;
diff --git a/Source/Shaders/BuiltinFunctions.glsl b/Source/Shaders/BuiltinFunctions.glsl
index 7f2cd10a3803..0bff0b47c696 100644
--- a/Source/Shaders/BuiltinFunctions.glsl
+++ b/Source/Shaders/BuiltinFunctions.glsl
@@ -261,6 +261,32 @@ vec4 czm_windowToEyeCoordinates(vec4 fragmentCoordinate)
return q;
}
+/**
+ * Creates a matrix that transforms vectors from tangent space to eye space.
+ *
+ * @name czm_tangentToEyeSpaceMatrix
+ * @glslFunction
+ *
+ * @param {vec3} normalEC The normal vector in eye coordinates.
+ * @param {vec3} tangentEC The tangent vector in eye coordinates.
+ * @param {vec3} binormalEC The binormal vector in eye coordinates.
+ *
+ * @returns {mat3} The matrix that transforms from tangent space to eye space.
+ *
+ * @example
+ * mat3 tangentToEye = czm_tangentToEyeSpaceMatrix(normalEC, tangentEC, binormalEC);
+ * vec3 normal = tangentToEye * texture2D(normalMap, st).xyz;
+ */
+mat3 czm_tangentToEyeSpaceMatrix(vec3 normalEC, vec3 tangentEC, vec3 binormalEC)
+{
+ vec3 normal = normalize(normalEC);
+ vec3 tangent = normalize(tangentEC);
+ vec3 binormal = normalize(binormalEC);
+ return mat3(tangent.x, tangent.y, tangent.z,
+ binormal.x, binormal.y, binormal.z,
+ normal.x, normal.y, normal.z);
+}
+
///////////////////////////////////////////////////////////////////////////////
/**
@@ -732,12 +758,35 @@ const int czm_morphing = 3;
* @name czm_columbusViewMorph
* @glslFunction
*/
-vec4 czm_columbusViewMorph(vec3 position2D, vec3 position3D, float time)
+vec4 czm_columbusViewMorph(vec4 position2D, vec4 position3D, float time)
{
// Just linear for now.
- vec3 p = mix(position2D, position3D, time);
+ vec3 p = mix(position2D.xyz, position3D.xyz, time);
return vec4(p, 1.0);
-}
+}
+
+/**
+ * Returns a position in model coordinates relative to eye taking into
+ * account the current scene mode: 3D, 2D, or Columbus view.
+ *
+ * This uses standard position attributes, position3DHigh
,
+ * position3DLow
, position2DHigh
, and position2DLow
,
+ * and should be used when writing a vertex shader for an {@link Appearance}.
+ *
+ *
+ * @name czm_computePosition
+ * @glslFunction
+ *
+ * @returns {vec4} The position relative to eye.
+ *
+ * @example
+ * vec4 p = czm_computePosition();
+ * v_positionEC = (czm_modelViewRelativeToEye * p).xyz;
+ * gl_Position = czm_modelViewProjectionRelativeToEye * p;
+ *
+ * @see czm_translateRelativeToEye
+ */
+vec4 czm_computePosition();
///////////////////////////////////////////////////////////////////////////////
@@ -1046,20 +1095,21 @@ float czm_latitudeToWebMercatorFraction(float latitude, float southMercatorYLow,
*
* void main()
* {
- * vec3 p = czm_translateRelativeToEye(positionHigh, positionLow);
- * gl_Position = czm_modelViewProjectionRelativeToEye * vec4(p, 1.0);
+ * vec4 p = czm_translateRelativeToEye(positionHigh, positionLow);
+ * gl_Position = czm_modelViewProjectionRelativeToEye * p;
* }
*
* @see czm_modelViewRelativeToEye
* @see czm_modelViewProjectionRelativeToEye
+ * @see czm_computePosition
* @see EncodedCartesian3
*/
-vec3 czm_translateRelativeToEye(vec3 high, vec3 low)
+vec4 czm_translateRelativeToEye(vec3 high, vec3 low)
{
vec3 highDifference = high - czm_encodedCameraPositionMCHigh;
vec3 lowDifference = low - czm_encodedCameraPositionMCLow;
- return highDifference + lowDifference;
+ return vec4(highDifference + lowDifference, 1.0);
}
/**
diff --git a/Source/Shaders/CentralBodyVS.glsl b/Source/Shaders/CentralBodyVS.glsl
index c8225ef440cb..5588bf21545d 100644
--- a/Source/Shaders/CentralBodyVS.glsl
+++ b/Source/Shaders/CentralBodyVS.glsl
@@ -74,8 +74,8 @@ vec4 getPositionMorphingMode(vec3 position3DWC)
// We do not do RTC while morphing, so there is potential for jitter.
// This is unlikely to be noticable, though.
float yPositionFraction = get2DYPositionFraction();
- vec3 position2DWC = vec3(0.0, mix(u_tileExtent.st, u_tileExtent.pq, vec2(textureCoordinates.x, yPositionFraction)));
- vec4 morphPosition = czm_columbusViewMorph(position2DWC, position3DWC, czm_morphTime);
+ vec4 position2DWC = vec4(0.0, mix(u_tileExtent.st, u_tileExtent.pq, vec2(textureCoordinates.x, yPositionFraction)), 1.0);
+ vec4 morphPosition = czm_columbusViewMorph(position2DWC, vec4(position3DWC, 1.0), czm_morphTime);
return czm_modelViewProjection * morphPosition;
}
diff --git a/Source/Shaders/EllipsoidVS.glsl b/Source/Shaders/EllipsoidVS.glsl
index 600a4969b2ba..b6821a8649b7 100644
--- a/Source/Shaders/EllipsoidVS.glsl
+++ b/Source/Shaders/EllipsoidVS.glsl
@@ -7,7 +7,7 @@ varying vec3 v_positionEC;
void main()
{
// In the vertex data, the cube goes from (-1.0, -1.0, -1.0) to (1.0, 1.0, 1.0) in model coordinates.
- // Scale to consider the radii. We could also do this once on the CPU when using the BoxTessellator,
+ // Scale to consider the radii. We could also do this once on the CPU when using the BoxGeometry,
// but doing it here allows us to change the radii without rewriting the vertex data, and
// allows all ellipsoids to reuse the same vertex data.
vec4 p = vec4(u_radii * position, 1.0);
diff --git a/Source/Shaders/PolygonFS.glsl b/Source/Shaders/PolygonFS.glsl
deleted file mode 100644
index 9612c99611f3..000000000000
--- a/Source/Shaders/PolygonFS.glsl
+++ /dev/null
@@ -1,24 +0,0 @@
-varying vec3 v_positionMC;
-varying vec3 v_positionEC;
-varying vec2 v_textureCoordinates;
-
-void main()
-{
- czm_materialInput materialInput;
-
- // TODO: Real 1D distance, and better 3D coordinate
- materialInput.st = v_textureCoordinates;
- materialInput.str = vec3(v_textureCoordinates, 0.0);
-
- //Convert tangent space material normal to eye space
- materialInput.normalEC = normalize(czm_normal3D * czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)));
- materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, materialInput.normalEC);
-
- //Convert view vector to world space
- vec3 positionToEyeEC = -v_positionEC;
- materialInput.positionToEyeEC = positionToEyeEC;
-
- czm_material material = czm_getMaterial(materialInput);
-
- gl_FragColor = czm_phong(normalize(positionToEyeEC), material);
-}
diff --git a/Source/Shaders/PolygonVS.glsl b/Source/Shaders/PolygonVS.glsl
deleted file mode 100644
index abf9c436555f..000000000000
--- a/Source/Shaders/PolygonVS.glsl
+++ /dev/null
@@ -1,37 +0,0 @@
-attribute vec3 position3DHigh;
-attribute vec3 position3DLow;
-attribute vec2 position2DHigh;
-attribute vec2 position2DLow;
-attribute vec2 textureCoordinates;
-
-uniform float u_height; // in meters
-
-varying vec3 v_positionMC;
-varying vec3 v_positionEC;
-varying vec2 v_textureCoordinates;
-
-void main()
-{
- vec4 p;
-
- if (czm_morphTime == 1.0)
- {
- p = vec4(czm_translateRelativeToEye(position3DHigh, position3DLow), 1.0);
- }
- else if (czm_morphTime == 0.0)
- {
- p = vec4(czm_translateRelativeToEye(vec3(u_height, position2DHigh), vec3(u_height, position2DLow)), 1.0);
- }
- else
- {
- p = czm_columbusViewMorph(
- czm_translateRelativeToEye(vec3(u_height, position2DHigh), vec3(u_height, position2DLow)),
- czm_translateRelativeToEye(position3DHigh, position3DLow),
- czm_morphTime);
- }
-
- v_positionMC = position3DHigh + position3DLow; // position in model coordinates
- v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates
- v_textureCoordinates = textureCoordinates;
- gl_Position = czm_modelViewProjectionRelativeToEye * p; // position in clip coordinates
-}
diff --git a/Source/Shaders/PolylineVS.glsl b/Source/Shaders/PolylineVS.glsl
index 9e18ac4de13c..3e262cb041d0 100644
--- a/Source/Shaders/PolylineVS.glsl
+++ b/Source/Shaders/PolylineVS.glsl
@@ -68,15 +68,15 @@ void main()
vec4 p, prev, next;
if (czm_morphTime == 1.0)
{
- p = vec4(czm_translateRelativeToEye(position3DHigh.xyz, position3DLow.xyz), 1.0);
- prev = vec4(czm_translateRelativeToEye(prevPosition3DHigh.xyz, prevPosition3DLow.xyz), 1.0);
- next = vec4(czm_translateRelativeToEye(nextPosition3DHigh.xyz, nextPosition3DLow.xyz), 1.0);
+ p = czm_translateRelativeToEye(position3DHigh.xyz, position3DLow.xyz);
+ prev = czm_translateRelativeToEye(prevPosition3DHigh.xyz, prevPosition3DLow.xyz);
+ next = czm_translateRelativeToEye(nextPosition3DHigh.xyz, nextPosition3DLow.xyz);
}
else if (czm_morphTime == 0.0)
{
- p = vec4(czm_translateRelativeToEye(position2DHigh.zxy, position2DLow.zxy), 1.0);
- prev = vec4(czm_translateRelativeToEye(prevPosition2DHigh.zxy, prevPosition2DLow.zxy), 1.0);
- next = vec4(czm_translateRelativeToEye(nextPosition2DHigh.zxy, nextPosition2DLow.zxy), 1.0);
+ p = czm_translateRelativeToEye(position2DHigh.zxy, position2DLow.zxy);
+ prev = czm_translateRelativeToEye(prevPosition2DHigh.zxy, prevPosition2DLow.zxy);
+ next = czm_translateRelativeToEye(nextPosition2DHigh.zxy, nextPosition2DLow.zxy);
}
else
{
diff --git a/Source/Widgets/BaseLayerPicker/BaseLayerPicker.js b/Source/Widgets/BaseLayerPicker/BaseLayerPicker.js
index 4b4675471364..bcd60e2d9d13 100644
--- a/Source/Widgets/BaseLayerPicker/BaseLayerPicker.js
+++ b/Source/Widgets/BaseLayerPicker/BaseLayerPicker.js
@@ -208,4 +208,4 @@ define([
};
return BaseLayerPicker;
-});
+});
\ No newline at end of file
diff --git a/Source/Widgets/BaseLayerPicker/BaseLayerPickerViewModel.js b/Source/Widgets/BaseLayerPicker/BaseLayerPickerViewModel.js
index 0ff338148068..bfa565d5fc5f 100644
--- a/Source/Widgets/BaseLayerPicker/BaseLayerPickerViewModel.js
+++ b/Source/Widgets/BaseLayerPicker/BaseLayerPickerViewModel.js
@@ -156,4 +156,4 @@ define([
});
return BaseLayerPickerViewModel;
-});
+});
\ No newline at end of file
diff --git a/Specs/Core/BoundingSphereSpec.js b/Specs/Core/BoundingSphereSpec.js
index a489fa983268..4db94cf3d270 100644
--- a/Specs/Core/BoundingSphereSpec.js
+++ b/Specs/Core/BoundingSphereSpec.js
@@ -27,17 +27,19 @@ defineSuite([
/*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
var positionsRadius = 1.0;
- var positionsCenter = new Cartesian3(1.0, 0.0, 0.0);
+ var positionsCenter = new Cartesian3(10000001.0, 0.0, 0.0);
+
+ var center = new Cartesian3(10000000.0, 0.0, 0.0);
function getPositions() {
return [
- new Cartesian3(1, 0, 0),
- new Cartesian3(2, 0, 0),
- new Cartesian3(0, 0, 0),
- new Cartesian3(1, 1, 0),
- new Cartesian3(1, -1, 0),
- new Cartesian3(1, 0, 1),
- new Cartesian3(1, 0, -1)
+ center.add(new Cartesian3(1, 0, 0)),
+ center.add(new Cartesian3(2, 0, 0)),
+ center.add(new Cartesian3(0, 0, 0)),
+ center.add(new Cartesian3(1, 1, 0)),
+ center.add(new Cartesian3(1, -1, 0)),
+ center.add(new Cartesian3(1, 0, 1)),
+ center.add(new Cartesian3(1, 0, -1))
];
}
@@ -105,6 +107,10 @@ defineSuite([
expect(sphere.radius).toEqual(expectedRadius);
});
+ it('static clone clones undefined', function() {
+ expect(BoundingSphere.clone(undefined)).toBe(undefined);
+ });
+
it('equals', function() {
var sphere = new BoundingSphere(new Cartesian3(1.0, 2.0, 3.0), 4.0);
expect(sphere.equals(new BoundingSphere(new Cartesian3(1.0, 2.0, 3.0), 4.0))).toEqual(true);
@@ -285,6 +291,15 @@ defineSuite([
expect(BoundingSphere.fromExtent3D(extent, ellipsoid)).toEqual(expected);
});
+ it('fromExtent3D with height', function() {
+ var extent = new Extent(0.1, -0.3, 0.2, -0.4);
+ var height = 100000.0;
+ var ellipsoid = Ellipsoid.WGS84;
+ var points = extent.subsample(ellipsoid, height);
+ var expected = BoundingSphere.fromPoints(points);
+ expect(BoundingSphere.fromExtent3D(extent, ellipsoid, height)).toEqual(expected);
+ });
+
it('fromCornerPoints', function() {
var sphere = BoundingSphere.fromCornerPoints(new Cartesian3(-1.0, -0.0, 0.0), new Cartesian3(1.0, 0.0, 0.0));
expect(sphere).toEqual(new BoundingSphere(Cartesian3.ZERO, 1.0));
@@ -309,6 +324,27 @@ defineSuite([
}).toThrow();
});
+ it('fromEllipsoid', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var sphere = BoundingSphere.fromEllipsoid(ellipsoid);
+ expect(sphere.center).toEqual(Cartesian3.ZERO);
+ expect(sphere.radius).toEqual(ellipsoid.getMaximumRadius());
+ });
+
+ it('fromEllipsoid with a result parameter', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var sphere = new BoundingSphere(new Cartesian3(1.0, 2.0, 3.0), 4.0);
+ var result = BoundingSphere.fromEllipsoid(ellipsoid, sphere);
+ expect(result).toBe(sphere);
+ expect(result).toEqual(new BoundingSphere(Cartesian3.ZERO, ellipsoid.getMaximumRadius()));
+ });
+
+ it('fromEllipsoid throws without ellipsoid', function() {
+ expect(function() {
+ BoundingSphere.fromEllipsoid();
+ }).toThrow();
+ });
+
it('sphere on the positive side of a plane', function() {
var sphere = new BoundingSphere(Cartesian3.ZERO, 0.5);
var normal = Cartesian3.UNIT_X.negate();
@@ -370,6 +406,54 @@ defineSuite([
expect(bs.getPlaneDistances(position, direction)).toEqual(expected);
});
+ it('projectTo2D', function() {
+ var positions = getPositions();
+ var projection = new GeographicProjection();
+
+ var positions2D = [];
+ for (var i = 0; i < positions.length; ++i) {
+ var position = positions[i];
+ var cartographic = projection.getEllipsoid().cartesianToCartographic(position);
+ positions2D.push(projection.project(cartographic));
+ }
+
+ var boundingSphere3D = BoundingSphere.fromPoints(positions);
+ var boundingSphere2D = boundingSphere3D.projectTo2D(projection);
+ var actualSphere = BoundingSphere.fromPoints(positions2D);
+ actualSphere.center = new Cartesian3(actualSphere.center.z, actualSphere.center.x, actualSphere.center.y);
+
+ expect(boundingSphere2D.center).toEqualEpsilon(actualSphere.center, CesiumMath.EPSILON6);
+ expect(boundingSphere2D.radius).toBeGreaterThan(actualSphere.radius);
+ });
+
+ it('projectTo2D with result parameter', function() {
+ var positions = getPositions();
+ var projection = new GeographicProjection();
+ var sphere = new BoundingSphere();
+
+ var positions2D = [];
+ for (var i = 0; i < positions.length; ++i) {
+ var position = positions[i];
+ var cartographic = projection.getEllipsoid().cartesianToCartographic(position);
+ positions2D.push(projection.project(cartographic));
+ }
+
+ var boundingSphere3D = BoundingSphere.fromPoints(positions);
+ var boundingSphere2D = boundingSphere3D.projectTo2D(projection, sphere);
+ var actualSphere = BoundingSphere.fromPoints(positions2D);
+ actualSphere.center = new Cartesian3(actualSphere.center.z, actualSphere.center.x, actualSphere.center.y);
+
+ expect(boundingSphere2D).toBe(sphere);
+ expect(boundingSphere2D.center).toEqualEpsilon(actualSphere.center, CesiumMath.EPSILON6);
+ expect(boundingSphere2D.radius).toBeGreaterThan(actualSphere.radius);
+ });
+
+ it('static projectTo2D throws without sphere', function() {
+ expect(function() {
+ BoundingSphere.projectTo2D();
+ }).toThrow();
+ });
+
it('static clone returns undefined with no parameter', function() {
expect(typeof BoundingSphere.clone()).toEqual('undefined');
});
diff --git a/Specs/Core/BoxGeometrySpec.js b/Specs/Core/BoxGeometrySpec.js
new file mode 100644
index 000000000000..ac371e996373
--- /dev/null
+++ b/Specs/Core/BoxGeometrySpec.js
@@ -0,0 +1,84 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/BoxGeometry',
+ 'Core/VertexFormat',
+ 'Core/Cartesian3'
+ ], function(
+ BoxGeometry,
+ VertexFormat,
+ Cartesian3) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor throws without minimum corner', function() {
+ expect(function() {
+ return new BoxGeometry({
+ maximumCorner : new Cartesian3()
+ });
+ }).toThrow();
+ });
+
+ it('constructor throws without maximum corner', function() {
+ expect(function() {
+ return new BoxGeometry({
+ minimumCorner : new Cartesian3()
+ });
+ }).toThrow();
+ });
+
+ it('constructor creates optimized number of positions for VertexFormat.POSITIONS_ONLY', function() {
+ var m = new BoxGeometry({
+ minimumCorner : new Cartesian3(-1, -2, -3),
+ maximumCorner : new Cartesian3(1, 2, 3),
+ vertexFormat : VertexFormat.POSITION_ONLY
+ });
+
+ expect(m.attributes.position.values.length).toEqual(8 * 3);
+ expect(m.indices.length).toEqual(12 * 3);
+ });
+
+ it('constructor computes all vertex attributes', function() {
+ var minimumCorner = new Cartesian3(0, 0, 0);
+ var maximumCorner = new Cartesian3(1, 1, 1);
+ var m = new BoxGeometry({
+ minimumCorner : minimumCorner,
+ maximumCorner : maximumCorner,
+ vertexFormat : VertexFormat.ALL
+ });
+
+ expect(m.attributes.position.values.length).toEqual(6 * 4 * 3);
+ expect(m.attributes.normal.values.length).toEqual(6 * 4 * 3);
+ expect(m.attributes.tangent.values.length).toEqual(6 * 4 * 3);
+ expect(m.attributes.binormal.values.length).toEqual(6 * 4 * 3);
+ expect(m.attributes.st.values.length).toEqual(6 * 4 * 2);
+
+ expect(m.indices.length).toEqual(12 * 3);
+
+ expect(m.boundingSphere.center).toEqual(Cartesian3.ZERO);
+ expect(m.boundingSphere.radius).toEqual(maximumCorner.magnitude() * 0.5);
+ });
+
+ it('fromDimensions throws without dimensions', function() {
+ expect(function() {
+ return BoxGeometry.fromDimensions();
+ }).toThrow();
+ });
+
+ it('fromDimensions throws with negative dimensions', function() {
+ expect(function() {
+ return BoxGeometry.fromDimensions({
+ dimensions : new Cartesian3(1, 2, -1)
+ });
+ }).toThrow();
+ });
+
+ it('fromDimensions', function() {
+ var m = BoxGeometry.fromDimensions({
+ dimensions : new Cartesian3(1, 2, 3),
+ vertexFormat : VertexFormat.POSITION_ONLY
+ });
+
+ expect(m.attributes.position.values.length).toEqual(8 * 3);
+ expect(m.indices.length).toEqual(12 * 3);
+ });
+});
\ No newline at end of file
diff --git a/Specs/Core/BoxTessellatorSpec.js b/Specs/Core/BoxTessellatorSpec.js
deleted file mode 100644
index 65abd664f2fe..000000000000
--- a/Specs/Core/BoxTessellatorSpec.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/*global defineSuite*/
-defineSuite([
- 'Core/BoxTessellator',
- 'Core/Cartesian3'
- ], function(
- BoxTessellator,
- Cartesian3) {
- "use strict";
- /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
-
- it('compute0', function() {
- expect(function() {
- return BoxTessellator.compute({
- dimensions : new Cartesian3(1, 2, -1)
- });
- }).toThrow();
- });
-
- it('compute1', function() {
- var m = BoxTessellator.compute({
- dimensions : new Cartesian3(1, 2, 3)
- });
-
- expect(m.attributes.position.values.length).toEqual(8 * 3);
- expect(m.indexLists[0].values.length).toEqual(12 * 3);
- });
-
- it('compute2', function() {
- expect(function() {
- return BoxTessellator.compute({
- minimumCorner : new Cartesian3(0, 0, 0),
- maximumCorner : new Cartesian3(1, 1, 1)
- });
- }).not.toThrow();
- });
-});
\ No newline at end of file
diff --git a/Specs/Core/CircleGeometrySpec.js b/Specs/Core/CircleGeometrySpec.js
new file mode 100644
index 000000000000..bf9c9166b7ba
--- /dev/null
+++ b/Specs/Core/CircleGeometrySpec.js
@@ -0,0 +1,84 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/CircleGeometry',
+ 'Core/Cartesian3',
+ 'Core/Cartographic',
+ 'Core/Ellipsoid',
+ 'Core/VertexFormat'
+ ], function(
+ CircleGeometry,
+ Cartesian3,
+ Cartographic,
+ Ellipsoid,
+ VertexFormat) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('throws without a center', function() {
+ expect(function() {
+ return new CircleGeometry({
+ radius : 1.0
+ });
+ }).toThrow();
+ });
+
+ it('throws without a radius', function() {
+ expect(function() {
+ return new CircleGeometry({
+ center : Ellipsoid.WGS84.cartographicToCartesian(new Cartographic())
+ });
+ }).toThrow();
+ });
+
+ it('throws with a negative radius', function() {
+ expect(function() {
+ return new CircleGeometry({
+ center : Ellipsoid.WGS84.cartographicToCartesian(new Cartographic()),
+ radius : -1.0
+ });
+ }).toThrow();
+ });
+
+ it('throws with a negative granularity', function() {
+ expect(function() {
+ return new CircleGeometry({
+ center : Ellipsoid.WGS84.cartographicToCartesian(new Cartographic()),
+ radius : 1.0,
+ granularity : -1.0
+ });
+ }).toThrow();
+ });
+
+ it('computes positions', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = new CircleGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ ellipsoid : ellipsoid,
+ center : ellipsoid.cartographicToCartesian(new Cartographic()),
+ granularity : 0.75,
+ radius : 1.0
+ });
+
+ expect(m.attributes.position.values.length).toEqual(3 * 24);
+ expect(m.indices.length).toEqual(3 * 34);
+ expect(m.boundingSphere.radius).toEqual(1);
+ });
+
+ it('compute all vertex attributes', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = new CircleGeometry({
+ vertexFormat : VertexFormat.ALL,
+ ellipsoid : ellipsoid,
+ center : ellipsoid.cartographicToCartesian(new Cartographic()),
+ granularity : 0.75,
+ radius : 1.0
+ });
+
+ expect(m.attributes.position.values.length).toEqual(3 * 24);
+ expect(m.attributes.st.values.length).toEqual(2 * 24);
+ expect(m.attributes.normal.values.length).toEqual(3 * 24);
+ expect(m.attributes.tangent.values.length).toEqual(3 * 24);
+ expect(m.attributes.binormal.values.length).toEqual(3 * 24);
+ expect(m.indices.length).toEqual(3 * 34);
+ });
+});
diff --git a/Specs/Core/ColorGeometryInstanceAttributeSpec.js b/Specs/Core/ColorGeometryInstanceAttributeSpec.js
new file mode 100644
index 000000000000..ca38ce763e4f
--- /dev/null
+++ b/Specs/Core/ColorGeometryInstanceAttributeSpec.js
@@ -0,0 +1,51 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/ColorGeometryInstanceAttribute',
+ 'Core/Color',
+ 'Core/ComponentDatatype'
+ ], function(
+ ColorGeometryInstanceAttribute,
+ Color,
+ ComponentDatatype) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor', function() {
+ var attribute = new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 0.5);
+ expect(attribute.componentDatatype).toEqual(ComponentDatatype.UNSIGNED_BYTE);
+ expect(attribute.componentsPerAttribute).toEqual(4);
+ expect(attribute.normalize).toEqual(true);
+
+ var value = new Uint8Array(new Color(1.0, 1.0, 0.0, 0.5).toBytes());
+ expect(attribute.value).toEqual(value);
+ });
+
+ it('fromColor', function() {
+ var color = Color.AQUA;
+ var attribute = ColorGeometryInstanceAttribute.fromColor(color);
+ expect(attribute.componentDatatype).toEqual(ComponentDatatype.UNSIGNED_BYTE);
+ expect(attribute.componentsPerAttribute).toEqual(4);
+ expect(attribute.normalize).toEqual(true);
+
+ var value = new Uint8Array(color.toBytes());
+ expect(attribute.value).toEqual(value);
+ });
+
+ it('fromColor throws without color', function() {
+ expect(function() {
+ ColorGeometryInstanceAttribute.fromColor();
+ }).toThrow();
+ });
+
+ it('toValue', function() {
+ var color = Color.AQUA;
+ expect(ColorGeometryInstanceAttribute.toValue(color)).toEqual(new Uint8Array(color.toBytes()));
+ });
+
+ it('toValue throws without a color', function() {
+ expect(function() {
+ ColorGeometryInstanceAttribute.toValue();
+ }).toThrow();
+ });
+
+});
diff --git a/Specs/Core/CubeMapEllipsoidTessellatorSpec.js b/Specs/Core/CubeMapEllipsoidTessellatorSpec.js
deleted file mode 100644
index d0a9cf2161c6..000000000000
--- a/Specs/Core/CubeMapEllipsoidTessellatorSpec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*global defineSuite*/
-defineSuite([
- 'Core/CubeMapEllipsoidTessellator',
- 'Core/Cartesian3',
- 'Core/Ellipsoid',
- 'Core/Math'
- ], function(
- CubeMapEllipsoidTessellator,
- Cartesian3,
- Ellipsoid,
- CesiumMath) {
- "use strict";
- /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
-
- it('compute0', function() {
- expect(function() {
- return CubeMapEllipsoidTessellator.compute(Ellipsoid.UNIT_SPHERE, -1);
- }).toThrow();
- });
-
- it('compute1', function() {
- var m = CubeMapEllipsoidTessellator.compute(Ellipsoid.UNIT_SPHERE, 1);
-
- expect(m.attributes.position.values.length).toEqual(3 * 8);
- expect(m.indexLists[0].values.length).toEqual(12 * 3);
- });
-
- it('compute2', function() {
- var m = CubeMapEllipsoidTessellator.compute(Ellipsoid.UNIT_SPHERE, 2);
-
- expect(m.attributes.position.values.length).toEqual(3 * (8 + 6 + 12));
- expect(m.indexLists[0].values.length).toEqual(2 * 3 * 4 * 6);
- });
-
- it('compute3', function() {
- var m = CubeMapEllipsoidTessellator.compute(Ellipsoid.UNIT_SPHERE, 3);
-
- var position = m.attributes.position.values;
- for ( var i = 0; i < position.length; i += 3) {
- expect(1.0).toEqualEpsilon(new Cartesian3(position[i], position[i + 1], position[i + 2]).magnitude(), CesiumMath.EPSILON10);
- }
- });
-});
\ No newline at end of file
diff --git a/Specs/Core/EllipseGeometrySpec.js b/Specs/Core/EllipseGeometrySpec.js
new file mode 100644
index 000000000000..13ac2d333fd6
--- /dev/null
+++ b/Specs/Core/EllipseGeometrySpec.js
@@ -0,0 +1,109 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/EllipseGeometry',
+ 'Core/Cartesian3',
+ 'Core/Cartographic',
+ 'Core/Ellipsoid',
+ 'Core/VertexFormat'
+ ], function(
+ EllipseGeometry,
+ Cartesian3,
+ Cartographic,
+ Ellipsoid,
+ VertexFormat) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('throws without a center', function() {
+ expect(function() {
+ return new EllipseGeometry({
+ semiMajorAxis : 1.0,
+ semiMinorAxis : 1.0
+ });
+ }).toThrow();
+ });
+
+ it('throws without a semiMajorAxis', function() {
+ expect(function() {
+ return new EllipseGeometry({
+ center : Ellipsoid.WGS84.cartographicToCartesian(new Cartographic()),
+ semiMinorAxis : 1.0
+ });
+ }).toThrow();
+ });
+
+ it('throws without a semiMinorAxis', function() {
+ expect(function() {
+ return new EllipseGeometry({
+ center : Ellipsoid.WGS84.cartographicToCartesian(new Cartographic()),
+ semiMajorAxis : 1.0
+ });
+ }).toThrow();
+ });
+
+ it('throws with a negative axis', function() {
+ expect(function() {
+ return new EllipseGeometry({
+ center : Ellipsoid.WGS84.cartographicToCartesian(new Cartographic()),
+ semiMajorAxis : 1.0,
+ semiMinorAxis : -1.0
+ });
+ }).toThrow();
+ });
+
+ it('throws with a negative granularity', function() {
+ expect(function() {
+ return new EllipseGeometry({
+ center : Ellipsoid.WGS84.cartographicToCartesian(new Cartographic()),
+ semiMajorAxis : 1.0,
+ semiMinorAxis : 1.0,
+ granularity : -1.0
+ });
+ }).toThrow();
+ });
+
+ it('throws when semiMajorAxis is less than the semiMajorAxis', function() {
+ expect(function() {
+ return new EllipseGeometry({
+ center : Ellipsoid.WGS84.cartographicToCartesian(new Cartographic()),
+ semiMajorAxis : 1.0,
+ semiMinorAxis : 2.0
+ });
+ }).toThrow();
+ });
+
+ it('computes positions', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = new EllipseGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ ellipsoid : ellipsoid,
+ center : ellipsoid.cartographicToCartesian(new Cartographic()),
+ granularity : 0.75,
+ semiMajorAxis : 1.0,
+ semiMinorAxis : 1.0
+ });
+
+ expect(m.attributes.position.values.length).toEqual(3 * 24);
+ expect(m.indices.length).toEqual(3 * 34);
+ expect(m.boundingSphere.radius).toEqual(1);
+ });
+
+ it('compute all vertex attributes', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = new EllipseGeometry({
+ vertexFormat : VertexFormat.ALL,
+ ellipsoid : ellipsoid,
+ center : ellipsoid.cartographicToCartesian(new Cartographic()),
+ granularity : 0.75,
+ semiMajorAxis : 1.0,
+ semiMinorAxis : 1.0
+ });
+
+ expect(m.attributes.position.values.length).toEqual(3 * 24);
+ expect(m.attributes.st.values.length).toEqual(2 * 24);
+ expect(m.attributes.normal.values.length).toEqual(3 * 24);
+ expect(m.attributes.tangent.values.length).toEqual(3 * 24);
+ expect(m.attributes.binormal.values.length).toEqual(3 * 24);
+ expect(m.indices.length).toEqual(3 * 34);
+ });
+});
diff --git a/Specs/Core/EllipsoidGeometrySpec.js b/Specs/Core/EllipsoidGeometrySpec.js
new file mode 100644
index 000000000000..40434b69c0d8
--- /dev/null
+++ b/Specs/Core/EllipsoidGeometrySpec.js
@@ -0,0 +1,73 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/EllipsoidGeometry',
+ 'Core/Cartesian3',
+ 'Core/Ellipsoid',
+ 'Core/Math',
+ 'Core/VertexFormat'
+ ], function(
+ EllipsoidGeometry,
+ Cartesian3,
+ Ellipsoid,
+ CesiumMath,
+ VertexFormat) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor throws with invalid numberOfPartitions', function() {
+ expect(function() {
+ return new EllipsoidGeometry({
+ numberOfPartitions : -1
+ });
+ }).toThrow();
+ });
+
+ it('computes positions', function() {
+ var m = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ numberOfPartitions : 1
+ });
+
+ expect(m.attributes.position.values.length).toEqual(3 * 8);
+ expect(m.indices.length).toEqual(12 * 3);
+ expect(m.boundingSphere.radius).toEqual(1);
+ });
+
+ it('compute all vertex attributes', function() {
+ var m = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.ALL,
+ numberOfPartitions : 2
+ });
+
+ expect(m.attributes.position.values.length).toEqual(3 * (8 + 6 + 12));
+ expect(m.attributes.st.values.length).toEqual(2 * (8 + 6 + 12));
+ expect(m.attributes.normal.values.length).toEqual(3 * (8 + 6 + 12));
+ expect(m.attributes.tangent.values.length).toEqual(3 * (8 + 6 + 12));
+ expect(m.attributes.binormal.values.length).toEqual(3 * (8 + 6 + 12));
+ expect(m.indices.length).toEqual(2 * 3 * 4 * 6);
+ });
+
+ it('computes attributes for a unit sphere', function() {
+ var m = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.ALL,
+ numberOfPartitions : 3
+ });
+
+ var positions = m.attributes.position.values;
+ var normals = m.attributes.normal.values;
+ var tangents = m.attributes.tangent.values;
+ var binormals = m.attributes.binormal.values;
+
+ for ( var i = 0; i < positions.length; i += 3) {
+ var position = Cartesian3.fromArray(positions, i);
+ var normal = Cartesian3.fromArray(normals, i);
+ var tangent = Cartesian3.fromArray(tangents, i);
+ var binormal = Cartesian3.fromArray(binormals, i);
+
+ expect(position.magnitude()).toEqualEpsilon(1.0, CesiumMath.EPSILON10);
+ expect(normal).toEqualEpsilon(position.normalize(), CesiumMath.EPSILON7);
+ expect(Cartesian3.dot(Cartesian3.UNIT_Z, tangent)).not.toBeLessThan(0.0);
+ expect(binormal).toEqualEpsilon(Cartesian3.cross(normal, tangent), CesiumMath.EPSILON7);
+ }
+ });
+});
\ No newline at end of file
diff --git a/Specs/Core/ExtentGeometrySpec.js b/Specs/Core/ExtentGeometrySpec.js
new file mode 100644
index 000000000000..86a0abc83495
--- /dev/null
+++ b/Specs/Core/ExtentGeometrySpec.js
@@ -0,0 +1,119 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/ExtentGeometry',
+ 'Core/Cartesian3',
+ 'Core/Ellipsoid',
+ 'Core/Extent',
+ 'Core/GeographicProjection',
+ 'Core/Math',
+ 'Core/Matrix2',
+ 'Core/VertexFormat'
+ ], function(
+ ExtentGeometry,
+ Cartesian3,
+ Ellipsoid,
+ Extent,
+ GeographicProjection,
+ CesiumMath,
+ Matrix2,
+ VertexFormat) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('computes positions', function() {
+ var extent = new Extent(-2.0, -1.0, 0.0, 1.0);
+ var m = new ExtentGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ extent : extent,
+ granularity : 1.0
+ });
+ var positions = m.attributes.position.values;
+ var length = positions.length;
+
+ expect(positions.length).toEqual(9 * 3);
+ expect(m.indices.length).toEqual(8 * 3);
+
+ var expectedNWCorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getNorthwest());
+ var expectedSECorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getSoutheast());
+ expect(new Cartesian3(positions[0], positions[1], positions[2])).toEqual(expectedNWCorner);
+ expect(new Cartesian3(positions[length-3], positions[length-2], positions[length-1])).toEqual(expectedSECorner);
+ });
+
+ it('computes all attributes', function() {
+ var m = new ExtentGeometry({
+ vertexFormat : VertexFormat.ALL,
+ extent : new Extent(-2.0, -1.0, 0.0, 1.0),
+ granularity : 1.0
+ });
+ expect(m.attributes.position.values.length).toEqual(9 * 3);
+ expect(m.attributes.st.values.length).toEqual(9 * 2);
+ expect(m.attributes.normal.values.length).toEqual(9 * 3);
+ expect(m.attributes.tangent.values.length).toEqual(9 * 3);
+ expect(m.attributes.binormal.values.length).toEqual(9 * 3);
+ expect(m.indices.length).toEqual(8 * 3);
+ });
+
+ it('compute positions with rotation', function() {
+ var extent = new Extent(-1, -1, 1, 1);
+ var angle = CesiumMath.PI_OVER_TWO;
+ var m = new ExtentGeometry({
+ vertexFormat : VertexFormat.POSITIONS_ONLY,
+ extent: extent,
+ rotation: angle,
+ granularity : 1.0
+ });
+ var positions = m.attributes.position.values;
+ var length = positions.length;
+
+ expect(length).toEqual(9 * 3);
+ expect(m.indices.length).toEqual(8 * 3);
+
+ var unrotatedSECorner = extent.getSoutheast();
+ var projection = new GeographicProjection();
+ var projectedSECorner = projection.project(unrotatedSECorner);
+ var rotation = Matrix2.fromRotation(angle);
+ var rotatedSECornerCartographic = projection.unproject(rotation.multiplyByVector(projectedSECorner));
+ var rotatedSECorner = Ellipsoid.WGS84.cartographicToCartesian(rotatedSECornerCartographic);
+ var actual = new Cartesian3(positions[length-3], positions[length-2], positions[length-1]);
+ expect(actual).toEqualEpsilon(rotatedSECorner, CesiumMath.EPSILON6);
+ });
+
+ it('compute vertices with PI rotation', function() {
+ var extent = new Extent(-1, -1, 1, 1);
+ var m = new ExtentGeometry({
+ extent: extent,
+ rotation: CesiumMath.PI,
+ granularity : 1.0
+ });
+ var positions = m.attributes.position.values;
+ var length = positions.length;
+
+ expect(length).toEqual(9 * 3);
+ expect(m.indices.length).toEqual(8 * 3);
+
+ var unrotatedNWCorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getNorthwest());
+ var unrotatedSECorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getSoutheast());
+
+ var actual = new Cartesian3(positions[0], positions[1], positions[2]);
+ expect(actual).toEqualEpsilon(unrotatedSECorner, CesiumMath.EPSILON8);
+
+ actual = new Cartesian3(positions[length-3], positions[length-2], positions[length-1]);
+ expect(actual).toEqualEpsilon(unrotatedNWCorner, CesiumMath.EPSILON8);
+ });
+
+ it('throws without extent', function() {
+ expect(function() {
+ return new ExtentGeometry({});
+ }).toThrow();
+ });
+
+ it('throws if rotated extent is invalid', function() {
+ expect(function() {
+ return new ExtentGeometry({
+ extent: new Extent(-CesiumMath.PI_OVER_TWO, 1, CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO),
+ rotation: CesiumMath.PI_OVER_TWO
+ });
+ }).toThrow();
+ });
+
+});
diff --git a/Specs/Core/ExtentSpec.js b/Specs/Core/ExtentSpec.js
index ed6c4fc1430f..8c2d55718420 100644
--- a/Specs/Core/ExtentSpec.js
+++ b/Specs/Core/ExtentSpec.js
@@ -36,6 +36,34 @@ defineSuite([
expect(extent.north).toEqual(north);
});
+ it('fromDegrees produces expected values.', function() {
+ var west = -10.0;
+ var south = -20.0;
+ var east = 10.0;
+ var north = 20.0;
+
+ var extent = Extent.fromDegrees(west, south, east, north);
+ expect(extent.west).toEqual(CesiumMath.toRadians(west));
+ expect(extent.south).toEqual(CesiumMath.toRadians(south));
+ expect(extent.east).toEqual(CesiumMath.toRadians(east));
+ expect(extent.north).toEqual(CesiumMath.toRadians(north));
+ });
+
+ it('fromDegrees works with a result parameter.', function() {
+ var west = -10.0;
+ var south = -20.0;
+ var east = 10.0;
+ var north = 20.0;
+
+ var result = new Extent();
+ var extent = Extent.fromDegrees(west, south, east, north, result);
+ expect(result).toBe(extent);
+ expect(extent.west).toEqual(CesiumMath.toRadians(west));
+ expect(extent.south).toEqual(CesiumMath.toRadians(south));
+ expect(extent.east).toEqual(CesiumMath.toRadians(east));
+ expect(extent.north).toEqual(CesiumMath.toRadians(north));
+ });
+
it('fromCartographicArray produces expected values.', function() {
var minLon = new Cartographic(-0.1, 0.3, 0.0);
var minLat = new Cartographic(0.0, -0.2, 0.0);
@@ -87,6 +115,10 @@ defineSuite([
expect(returnedResult).toBe(extent);
});
+ it('clone works without extent', function() {
+ expect(Extent.clone()).not.toBeDefined();
+ });
+
it('Equals works in all cases', function() {
var extent = new Extent(0.1, 0.2, 0.3, 0.4);
expect(extent.equals(new Extent(0.1, 0.2, 0.3, 0.4))).toEqual(true);
@@ -97,6 +129,16 @@ defineSuite([
expect(extent.equals(undefined)).toEqual(false);
});
+ it('Static equals works in all cases', function() {
+ var extent = new Extent(0.1, 0.2, 0.3, 0.4);
+ expect(Extent.equals(extent, new Extent(0.1, 0.2, 0.3, 0.4))).toEqual(true);
+ expect(Extent.equals(extent, new Extent(0.5, 0.2, 0.3, 0.4))).toEqual(false);
+ expect(Extent.equals(extent, new Extent(0.1, 0.5, 0.3, 0.4))).toEqual(false);
+ expect(Extent.equals(extent, new Extent(0.1, 0.2, 0.5, 0.4))).toEqual(false);
+ expect(Extent.equals(extent, new Extent(0.1, 0.2, 0.3, 0.5))).toEqual(false);
+ expect(Extent.equals(extent, undefined)).toEqual(false);
+ });
+
it('Equals epsilon works in all cases', function() {
var extent = new Extent(0.1, 0.2, 0.3, 0.4);
expect(extent.equalsEpsilon(new Extent(0.1, 0.2, 0.3, 0.4), 0.0)).toEqual(true);
@@ -341,7 +383,7 @@ defineSuite([
var extent = new Extent(west, south, east, north);
var cartesian0 = new Cartesian3();
var results = [cartesian0];
- var returnedResult = extent.subsample(Ellipsoid.WGS84, results);
+ var returnedResult = extent.subsample(Ellipsoid.WGS84, 0.0, results);
expect(results).toBe(returnedResult);
expect(results[0]).toBe(cartesian0);
expect(returnedResult).toEqual([Ellipsoid.WGS84.cartographicToCartesian(extent.getNorthwest()),
@@ -385,6 +427,30 @@ defineSuite([
expect(cartographic5.longitude).toEqualEpsilon(east, CesiumMath.EPSILON16);
});
+ it('subsample works at a height above the ellipsoid', function() {
+ var west = 0.1;
+ var south = -0.3;
+ var east = 0.2;
+ var north = -0.4;
+ var extent = new Extent(west, south, east, north);
+ var height = 100000.0;
+ var returnedResult = extent.subsample(Ellipsoid.WGS84, height);
+
+ var nw = extent.getNorthwest();
+ nw.height = height;
+ var ne = extent.getNortheast();
+ ne.height = height;
+ var se = extent.getSoutheast();
+ se.height = height;
+ var sw = extent.getSouthwest();
+ sw.height = height;
+
+ expect(returnedResult).toEqual([Ellipsoid.WGS84.cartographicToCartesian(nw),
+ Ellipsoid.WGS84.cartographicToCartesian(ne),
+ Ellipsoid.WGS84.cartographicToCartesian(se),
+ Ellipsoid.WGS84.cartographicToCartesian(sw)]);
+ });
+
it('equalsEpsilon throws with no epsilon', function() {
var extent = new Extent(west, south, east, north);
var other = new Extent();
diff --git a/Specs/Core/ExtentTessellatorSpec.js b/Specs/Core/ExtentTessellatorSpec.js
deleted file mode 100644
index 1fe1d40e2c49..000000000000
--- a/Specs/Core/ExtentTessellatorSpec.js
+++ /dev/null
@@ -1,219 +0,0 @@
-/*global defineSuite*/
-defineSuite([
- 'Core/ExtentTessellator',
- 'Core/Extent',
- 'Core/Ellipsoid',
- 'Core/Cartesian3',
- 'Core/Math'
- ], function(
- ExtentTessellator,
- Extent,
- Ellipsoid,
- Cartesian3,
- CesiumMath) {
- "use strict";
- /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
-
- it('compute 0', function() {
- var m = ExtentTessellator.compute({
- extent : new Extent(-2.0, -1.0, 0.0, 1.0),
- granularity : 1.0
- });
- expect(m.attributes.position.values.length).toEqual(9 * 3);
- expect(typeof m.attributes.textureCoordinates === 'undefined').toEqual(true);
- expect(m.indexLists[0].values.length).toEqual(8 * 3);
- });
-
- it('compute 1', function() {
- var m = ExtentTessellator.compute({
- extent : new Extent(-2.0, -1.0, 0.0, 1.0),
- granularity : 1.0,
- generateTextureCoordinates : true
- });
- expect(m.attributes.position.values.length).toEqual(9 * 3);
- expect(m.attributes.textureCoordinates.values.length).toEqual(9 * 2);
- expect(m.indexLists[0].values.length).toEqual(8 * 3);
- });
-
- it('compute returns undefined if rotation makes extent invalid', function() {
- expect(typeof ExtentTessellator.compute({
- extent : new Extent(-CesiumMath.PI, -1.0, 0.0, 1.0),
- rotation: CesiumMath.PI_OVER_TWO,
- granularity : 1.0,
- generateTextureCoordinates : true
- }) === 'undefined').toEqual(true);
- });
-
- it('computeBuffers 0', function() {
- var buffers = ExtentTessellator.computeBuffers({
- extent : new Extent(-2.0, -1.0, 0.0, 1.0),
- granularity : 1.0
- });
-
- expect(buffers.positions.length).toEqual(9 * 3);
- expect(buffers.indices.length).toEqual(8 * 3);
- });
-
- it('computeBuffers 1', function() {
- var buffers = ExtentTessellator.computeBuffers({
- extent : new Extent(-2.0, -1.0, 0.0, 1.0),
- granularity : 1.0,
- generateTextureCoordinates : true
- });
-
- expect(buffers.positions.length).toEqual(9 * 3);
- expect(buffers.textureCoordinates.length).toEqual(9 * 2);
- expect(buffers.indices.length).toEqual(8 * 3);
- });
-
- it('computeBuffers 2', function() {
- var buffers = ExtentTessellator.computeBuffers({
- extent : new Extent(-2.0, -1.0, 0.0, 1.0),
- granularity : 1.0,
- generateTextureCoordinates : true,
- interleaveTextureCoordinates : true
- });
-
- expect(buffers.vertices.length).toEqual(9 * 3 + 9 * 2);
- expect(buffers.indices.length).toEqual(8 * 3);
- });
-
- it('compute vertices', function() {
- var extent = new Extent(-CesiumMath.PI, -CesiumMath.PI_OVER_TWO, CesiumMath.PI, CesiumMath.PI_OVER_TWO);
- var description = {
- extent: extent,
- width: Math.ceil(extent.east - extent.west) + 1,
- height: Math.ceil(extent.north - extent.south) + 1,
- radiiSquared: Ellipsoid.WGS84.getRadiiSquared(),
- relativeToCenter: Cartesian3.ZERO,
- surfaceHeight: 0,
- vertices: [],
- indices: []
- };
- ExtentTessellator.computeVertices(description);
- var length = description.vertices.length;
- var expectedNWCorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getNorthwest());
- var expectedSECorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getSoutheast());
- expect(new Cartesian3(description.vertices[0], description.vertices[1], description.vertices[2])).toEqual(expectedNWCorner);
- expect(new Cartesian3(description.vertices[length-3], description.vertices[length-2], description.vertices[length-1])).toEqual(expectedSECorner);
- });
-
- it('compute vertices with rotation', function() {
- var extent = new Extent(-1, -1, 1, 1);
- var description = {
- extent: extent,
- rotation: CesiumMath.PI_OVER_TWO,
- width: Math.ceil(extent.east - extent.west) + 1,
- height: Math.ceil(extent.north - extent.south) + 1,
- radiiSquared: Ellipsoid.WGS84.getRadiiSquared(),
- relativeToCenter: Cartesian3.ZERO,
- surfaceHeight: 0,
- vertices: [],
- indices: []
- };
- ExtentTessellator.computeVertices(description);
- var length = description.vertices.length;
- expect(length).toEqual(9 * 3);
- expect(description.indices.length).toEqual(8 * 3);
- var unrotatedNWCorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getNorthwest());
- var unrotatedSECorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getSoutheast());
- expect(new Cartesian3(description.vertices[0], description.vertices[1], description.vertices[2])).not.toEqual(unrotatedNWCorner);
- expect(new Cartesian3(description.vertices[length-3], description.vertices[length-2], description.vertices[length-1])).not.toEqual(unrotatedSECorner);
- });
-
- it('compute vertices with PI rotation', function() {
- var extent = new Extent(-1, -1, 1, 1);
- var description = {
- extent: extent,
- rotation: CesiumMath.PI,
- width: Math.ceil(extent.east - extent.west) + 1,
- height: Math.ceil(extent.north - extent.south) + 1,
- radiiSquared: Ellipsoid.WGS84.getRadiiSquared(),
- relativeToCenter: Cartesian3.ZERO,
- surfaceHeight: 0,
- vertices: [],
- indices: []
- };
- ExtentTessellator.computeVertices(description);
- var length = description.vertices.length;
- expect(length).toEqual(9 * 3);
- expect(description.indices.length).toEqual(8 * 3);
- var unrotatedNWCorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getNorthwest());
- var unrotatedSECorner = Ellipsoid.WGS84.cartographicToCartesian(extent.getSoutheast());
- expect(new Cartesian3(description.vertices[0], description.vertices[1], description.vertices[2])).toEqualEpsilon(unrotatedSECorner, CesiumMath.EPSILON8);
- expect(new Cartesian3(description.vertices[length-3], description.vertices[length-2], description.vertices[length-1])).toEqualEpsilon(unrotatedNWCorner, CesiumMath.EPSILON8);
- });
-
- it('compute vertices has empty indices and vertices if rotated extent crosses north pole (NE Corner)', function() {
- var extent = new Extent(-CesiumMath.PI_OVER_TWO, 1, CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO);
- var description = {
- extent: extent,
- rotation: CesiumMath.PI_OVER_TWO,
- width: Math.ceil(extent.east - extent.west) + 1,
- height: Math.ceil(extent.north - extent.south) + 1,
- radiiSquared: Ellipsoid.WGS84.getRadiiSquared(),
- relativeToCenter: Cartesian3.ZERO,
- surfaceHeight: 0,
- vertices: [],
- indices: []
- };
- ExtentTessellator.computeVertices(description);
- expect(description.vertices.length).toEqual(0);
- expect(description.indices.length).toEqual(0);
- });
-
- it('compute vertices has empty indices and vertices if rotated extent crosses south pole (NW Corner)', function() {
- var extent = new Extent(-CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO, -1);
- var description = {
- extent : new Extent(-CesiumMath.PI_OVER_TWO, -CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO, -1),
- rotation: CesiumMath.PI_OVER_TWO,
- width: Math.ceil(extent.east - extent.west) + 1,
- height: Math.ceil(extent.north - extent.south) + 1,
- radiiSquared: Ellipsoid.WGS84.getRadiiSquared(),
- relativeToCenter: Cartesian3.ZERO,
- surfaceHeight: 0,
- vertices: [],
- indices: []
- };
- ExtentTessellator.computeVertices(description);
- expect(description.vertices.length).toEqual(0);
- expect(description.indices.length).toEqual(0);
- });
-
- it('compute vertices has empty indices and vertices if rotated extent crosses IDL (SW Corner)', function() {
- var extent = new Extent(-CesiumMath.PI, 0, -3, 0.3);
- var description = {
- extent: extent,
- rotation: -CesiumMath.PI_OVER_TWO,
- width: Math.ceil(extent.east - extent.west) + 1,
- height: Math.ceil(extent.north - extent.south) + 1,
- radiiSquared: Ellipsoid.WGS84.getRadiiSquared(),
- relativeToCenter: Cartesian3.ZERO,
- surfaceHeight: 0,
- vertices: [],
- indices: []
- };
- ExtentTessellator.computeVertices(description);
- expect(description.vertices.length).toEqual(0);
- expect(description.indices.length).toEqual(0);
- });
-
- it('compute vertices has empty indices and vertices if rotated extent crosses IDL (SE Corner)', function() {
- var extent = new Extent(3, 0, CesiumMath.PI, 0.3);
- var description = {
- extent: extent,
- rotation: 0.1,
- width: Math.ceil(extent.east - extent.west) + 1,
- height: Math.ceil(extent.north - extent.south) + 1,
- radiiSquared: Ellipsoid.WGS84.getRadiiSquared(),
- relativeToCenter: Cartesian3.ZERO,
- surfaceHeight: 0,
- vertices: [],
- indices: []
- };
- ExtentTessellator.computeVertices(description);
- expect(description.vertices.length).toEqual(0);
- expect(description.indices.length).toEqual(0);
- });
-
-});
diff --git a/Specs/Core/GeometryAttributeSpec.js b/Specs/Core/GeometryAttributeSpec.js
new file mode 100644
index 000000000000..f1d8d108da8d
--- /dev/null
+++ b/Specs/Core/GeometryAttributeSpec.js
@@ -0,0 +1,82 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/GeometryAttribute',
+ 'Core/ComponentDatatype'
+ ], function(
+ GeometryAttribute,
+ ComponentDatatype) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor', function() {
+ var color = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 4,
+ normalize : true,
+ values : new Uint8Array([
+ 255, 0, 0, 255,
+ 0, 255, 0, 255,
+ 0, 0, 255, 255
+ ])
+ });
+
+ expect(color.componentDatatype).toEqual(ComponentDatatype.UNSIGNED_BYTE);
+ expect(color.componentsPerAttribute).toEqual(4);
+ expect(color.normalize).toEqual(true);
+ expect(color.values).toEqual([
+ 255, 0, 0, 255,
+ 0, 255, 0, 255,
+ 0, 0, 255, 255
+ ]);
+ });
+
+ it('constructor throws without componentDatatype', function() {
+ expect(function() {
+ return new GeometryAttribute({
+ componentsPerAttribute : 4,
+ values : new Uint8Array([
+ 255, 0, 0, 255,
+ 0, 255, 0, 255,
+ 0, 0, 255, 255
+ ])
+ });
+ }).toThrow();
+ });
+
+ it('constructor throws without componentsPerAttribute', function() {
+ expect(function() {
+ return new GeometryAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ values : new Uint8Array([
+ 255, 0, 0, 255,
+ 0, 255, 0, 255,
+ 0, 0, 255, 255
+ ])
+ });
+ }).toThrow();
+ });
+
+ it('constructor throws when componentsPerAttribute is less than 1 or greater than 4', function() {
+ expect(function() {
+ return new GeometryAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 7,
+ values : new Uint8Array([
+ 255, 0, 0, 255,
+ 0, 255, 0, 255,
+ 0, 0, 255, 255
+ ])
+ });
+ }).toThrow();
+ });
+
+ it('constructor throws without values', function() {
+ expect(function() {
+ return new GeometryAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 4
+ });
+ }).toThrow();
+ });
+
+});
diff --git a/Specs/Core/GeometryInstanceAttributeSpec.js b/Specs/Core/GeometryInstanceAttributeSpec.js
new file mode 100644
index 000000000000..230286618ed1
--- /dev/null
+++ b/Specs/Core/GeometryInstanceAttributeSpec.js
@@ -0,0 +1,62 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/GeometryInstanceAttribute',
+ 'Core/ComponentDatatype'
+ ], function(
+ GeometryInstanceAttribute,
+ ComponentDatatype) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor', function() {
+ var color = new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 4,
+ normalize : true,
+ value : new Uint8Array([255, 255, 0, 255])
+ });
+
+ expect(color.componentDatatype).toEqual(ComponentDatatype.UNSIGNED_BYTE);
+ expect(color.componentsPerAttribute).toEqual(4);
+ expect(color.normalize).toEqual(true);
+ expect(color.value).toEqual([255, 255, 0, 255]);
+ });
+
+ it('constructor throws without componentDatatype', function() {
+ expect(function() {
+ return new GeometryInstanceAttribute({
+ componentsPerAttribute : 4,
+ value : new Uint8Array([255, 255, 0, 255])
+ });
+ }).toThrow();
+ });
+
+ it('constructor throws without componentsPerAttribute', function() {
+ expect(function() {
+ return new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ value : new Uint8Array([255, 255, 0, 255])
+ });
+ }).toThrow();
+ });
+
+ it('constructor throws when componentsPerAttribute is less than 1 or greater than 4', function() {
+ expect(function() {
+ return new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 7,
+ value : new Uint8Array([255, 255, 0, 255])
+ });
+ }).toThrow();
+ });
+
+ it('constructor throws without values', function() {
+ expect(function() {
+ return new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 4
+ });
+ }).toThrow();
+ });
+
+});
diff --git a/Specs/Core/GeometryInstanceSpec.js b/Specs/Core/GeometryInstanceSpec.js
new file mode 100644
index 000000000000..152e5f1e89b1
--- /dev/null
+++ b/Specs/Core/GeometryInstanceSpec.js
@@ -0,0 +1,70 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/GeometryInstance',
+ 'Core/Geometry',
+ 'Core/GeometryAttribute',
+ 'Core/GeometryInstanceAttribute',
+ 'Core/ComponentDatatype',
+ 'Core/BoundingSphere',
+ 'Core/Cartesian3',
+ 'Core/PrimitiveType',
+ 'Core/Matrix4'
+ ], function(
+ GeometryInstance,
+ Geometry,
+ GeometryAttribute,
+ GeometryInstanceAttribute,
+ ComponentDatatype,
+ BoundingSphere,
+ Cartesian3,
+ PrimitiveType,
+ Matrix4) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ ])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0)
+ });
+ var modelMatrix = Matrix4.multiplyByTranslation(Matrix4.IDENTITY, new Cartesian3(0.0, 0.0, 9000000.0));
+ var attributes = {
+ color : new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 4,
+ normalize : true,
+ value : new Uint8Array([255, 255, 0, 255])
+ })
+ };
+ var instance = new GeometryInstance({
+ geometry : geometry,
+ modelMatrix : modelMatrix,
+ id : 'geometry',
+ attributes : attributes
+ });
+
+ expect(instance.geometry).toBe(geometry);
+ expect(instance.modelMatrix).toEqual(modelMatrix);
+ expect(instance.id).toEqual('geometry');
+ expect(attributes).toBe(attributes);
+ });
+
+ it('constructor throws without geometry', function() {
+ expect(function() {
+ return new GeometryInstance();
+ }).toThrow();
+ });
+
+});
diff --git a/Specs/Core/GeometryPipelineSpec.js b/Specs/Core/GeometryPipelineSpec.js
new file mode 100644
index 000000000000..83ae56dfaaf4
--- /dev/null
+++ b/Specs/Core/GeometryPipelineSpec.js
@@ -0,0 +1,2090 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/GeometryPipeline',
+ 'Core/PrimitiveType',
+ 'Core/ComponentDatatype',
+ 'Core/EllipsoidGeometry',
+ 'Core/Ellipsoid',
+ 'Core/Cartesian3',
+ 'Core/EncodedCartesian3',
+ 'Core/Matrix4',
+ 'Core/Tipsify',
+ 'Core/GeographicProjection',
+ 'Core/Geometry',
+ 'Core/GeometryAttribute',
+ 'Core/GeometryInstance',
+ 'Core/VertexFormat',
+ 'Core/Math',
+ 'Core/BoundingSphere'
+ ], function(
+ GeometryPipeline,
+ PrimitiveType,
+ ComponentDatatype,
+ EllipsoidGeometry,
+ Ellipsoid,
+ Cartesian3,
+ EncodedCartesian3,
+ Matrix4,
+ Tipsify,
+ GeographicProjection,
+ Geometry,
+ GeometryAttribute,
+ GeometryInstance,
+ VertexFormat,
+ CesiumMath,
+ BoundingSphere) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('converts triangles to wireframe in place', function() {
+ var geometry = GeometryPipeline.toWireframe(new Geometry({
+ attributes : {},
+ indices : [0, 1, 2, 3, 4, 5],
+ primitiveType : PrimitiveType.TRIANGLES
+ }));
+
+ expect(geometry.primitiveType).toEqual(PrimitiveType.LINES);
+
+ var v = geometry.indices;
+ expect(v.length).toEqual(12);
+
+ expect(v[0]).toEqual(0);
+ expect(v[1]).toEqual(1);
+ expect(v[2]).toEqual(1);
+ expect(v[3]).toEqual(2);
+ expect(v[4]).toEqual(2);
+ expect(v[5]).toEqual(0);
+
+ expect(v[6]).toEqual(3);
+ expect(v[7]).toEqual(4);
+ expect(v[8]).toEqual(4);
+ expect(v[9]).toEqual(5);
+ expect(v[10]).toEqual(5);
+ expect(v[11]).toEqual(3);
+ });
+
+ it('converts a triangle fan to wireframe in place', function() {
+ var geometry = GeometryPipeline.toWireframe(new Geometry({
+ attributes : {},
+ indices : [0, 1, 2, 3],
+ primitiveType : PrimitiveType.TRIANGLE_FAN
+ }));
+
+ expect(geometry.primitiveType).toEqual(PrimitiveType.LINES);
+
+ var v = geometry.indices;
+ expect(v.length).toEqual(12);
+
+ expect(v[0]).toEqual(0);
+ expect(v[1]).toEqual(1);
+ expect(v[2]).toEqual(1);
+ expect(v[3]).toEqual(2);
+ expect(v[4]).toEqual(2);
+ expect(v[5]).toEqual(0);
+
+ expect(v[6]).toEqual(0);
+ expect(v[7]).toEqual(2);
+ expect(v[8]).toEqual(2);
+ expect(v[9]).toEqual(3);
+ expect(v[10]).toEqual(3);
+ expect(v[11]).toEqual(0);
+ });
+
+ it('converts a triangle strip to wireframe in place', function() {
+ var geometry = GeometryPipeline.toWireframe(new Geometry({
+ attributes : {},
+ indices : [0, 1, 2, 3],
+ primitiveType : PrimitiveType.TRIANGLE_STRIP
+ }));
+
+ expect(geometry.primitiveType).toEqual(PrimitiveType.LINES);
+
+ var v = geometry.indices;
+ expect(v.length).toEqual(12);
+
+ expect(v[0]).toEqual(0);
+ expect(v[1]).toEqual(1);
+ expect(v[2]).toEqual(1);
+ expect(v[3]).toEqual(2);
+ expect(v[4]).toEqual(2);
+ expect(v[5]).toEqual(0);
+
+ expect(v[6]).toEqual(2);
+ expect(v[7]).toEqual(3);
+ expect(v[8]).toEqual(3);
+ expect(v[9]).toEqual(1);
+ expect(v[10]).toEqual(1);
+ expect(v[11]).toEqual(2);
+ });
+
+ it('toWireframe throws without a geometry', function() {
+ expect(function() {
+ GeometryPipeline.toWireframe(undefined);
+ }).toThrow();
+ });
+
+ it('toWireframe throws when primitiveType is not a triangle type', function() {
+ expect(function() {
+ GeometryPipeline.toWireframe(new Geometry({
+ attributes : {},
+ indices : [],
+ primitiveType : PrimitiveType.POINTS
+ }));
+ }).toThrow();
+ });
+
+ it('createLineSegmentsForVectors', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
+ }),
+ normal : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0]
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0)
+ });
+ var lines = GeometryPipeline.createLineSegmentsForVectors(geometry, 'normal', 1.0);
+ var linePositions = [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0];
+
+ expect(lines.attributes).toBeDefined();
+ expect(lines.attributes.position).toBeDefined();
+ expect(lines.attributes.position.values).toEqual(linePositions);
+ expect(lines.primitiveType).toEqual(PrimitiveType.LINES);
+ expect(lines.boundingSphere.center).toEqual(geometry.boundingSphere.center);
+ expect(lines.boundingSphere.radius).toEqual(geometry.boundingSphere.radius + 1.0);
+ });
+
+ it('createLineSegmentsForVectors throws without geometry', function() {
+ expect(function() {
+ GeometryPipeline.createLineSegmentsForVectors();
+ }).toThrow();
+ });
+
+ it('createLineSegmentsForVectors throws without geometry.attributes.position', function() {
+ expect(function() {
+ GeometryPipeline.createLineSegmentsForVectors();
+ }).toThrow();
+ });
+
+ it('createLineSegmentsForVectors throws when geometry.attributes does not have an attributeName property', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ expect(function() {
+ GeometryPipeline.createLineSegmentsForVectors(geometry, 'binormal');
+ }).toThrow();
+ });
+
+ it('creates attribute indices', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : []
+ }),
+ normal : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : []
+ }),
+ color : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute : 4,
+ values : []
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ var indices = GeometryPipeline.createAttributeIndices(geometry);
+
+ var validIndices = [0, 1, 2];
+ expect(validIndices).toContain(indices.position);
+ expect(validIndices).toContain(indices.normal);
+ expect(validIndices).toContain(indices.color);
+ expect(indices.position).not.toEqual(indices.normal);
+ expect(indices.position).not.toEqual(indices.color);
+ });
+
+ it('createAttributeIndices throws without a geometry', function() {
+ expect(function() {
+ GeometryPipeline.createAttributeIndices(undefined);
+ }).toThrow();
+ });
+
+ it('reorderForPreVertexCache reorders all indices and attributes for the pre vertex cache', function() {
+ var geometry = new Geometry({
+ attributes : {
+ weight : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ values : [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
+ }),
+ positions : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0]
+ })
+ },
+ indices : [5, 3, 2, 0, 1, 4, 4, 1, 3, 2, 5, 0],
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ GeometryPipeline.reorderForPreVertexCache(geometry);
+
+ expect(geometry.indices[0]).toEqual(0);
+ expect(geometry.indices[1]).toEqual(1);
+ expect(geometry.indices[2]).toEqual(2);
+ expect(geometry.indices[3]).toEqual(3);
+ expect(geometry.indices[4]).toEqual(4);
+ expect(geometry.indices[5]).toEqual(5);
+ expect(geometry.indices[6]).toEqual(5);
+ expect(geometry.indices[7]).toEqual(4);
+ expect(geometry.indices[8]).toEqual(1);
+ expect(geometry.indices[9]).toEqual(2);
+ expect(geometry.indices[10]).toEqual(0);
+ expect(geometry.indices[11]).toEqual(3);
+
+ expect(geometry.attributes.weight.values[0]).toEqual(5.0);
+ expect(geometry.attributes.weight.values[1]).toEqual(3.0);
+ expect(geometry.attributes.weight.values[2]).toEqual(2.0);
+ expect(geometry.attributes.weight.values[3]).toEqual(0.0);
+ expect(geometry.attributes.weight.values[4]).toEqual(1.0);
+ expect(geometry.attributes.weight.values[5]).toEqual(4.0);
+
+ expect(geometry.attributes.positions.values[0]).toEqual(15);
+ expect(geometry.attributes.positions.values[1]).toEqual(16);
+ expect(geometry.attributes.positions.values[2]).toEqual(17);
+ expect(geometry.attributes.positions.values[3]).toEqual(9);
+ expect(geometry.attributes.positions.values[4]).toEqual(10);
+ expect(geometry.attributes.positions.values[5]).toEqual(11);
+ expect(geometry.attributes.positions.values[6]).toEqual(6);
+ expect(geometry.attributes.positions.values[7]).toEqual(7);
+ expect(geometry.attributes.positions.values[8]).toEqual(8);
+ expect(geometry.attributes.positions.values[9]).toEqual(0);
+ expect(geometry.attributes.positions.values[10]).toEqual(1);
+ expect(geometry.attributes.positions.values[11]).toEqual(2);
+ expect(geometry.attributes.positions.values[12]).toEqual(3);
+ expect(geometry.attributes.positions.values[13]).toEqual(4);
+ expect(geometry.attributes.positions.values[14]).toEqual(5);
+ expect(geometry.attributes.positions.values[15]).toEqual(12);
+ expect(geometry.attributes.positions.values[16]).toEqual(13);
+ expect(geometry.attributes.positions.values[17]).toEqual(14);
+ });
+
+ it('reorderForPreVertexCache throws without a geometry', function() {
+ expect(function() {
+ GeometryPipeline.reorderForPreVertexCache(undefined);
+ }).toThrow();
+ });
+
+ it('reorderForPreVertexCache throws when attributes have a different number of attributes', function() {
+ expect(function() {
+ var geometry = new Geometry({
+ attributes : {
+ attribute1 : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ values : [0, 1, 2]
+ }),
+ attribute2 : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0, 1, 2, 3, 4, 5]
+ })
+ }
+ });
+
+ geometry = GeometryPipeline.reorderForPreVertexCache(geometry);
+ }).toThrow();
+ });
+
+ it('reorderForPostVertexCache reorders indices for the post vertex cache', function() {
+ var geometry = new EllipsoidGeometry();
+ var acmrBefore = Tipsify.calculateACMR({
+ indices : geometry.indices,
+ cacheSize : 24
+ });
+ expect(acmrBefore).toBeGreaterThan(1.0);
+ geometry = GeometryPipeline.reorderForPostVertexCache(geometry);
+ var acmrAfter = Tipsify.calculateACMR({
+ indices : geometry.indices,
+ cacheSize : 24
+ });
+ expect(acmrAfter).toBeLessThan(0.7);
+ });
+
+ it('reorderForPostVertexCache throws without a geometry', function() {
+ expect(function() {
+ GeometryPipeline.reorderForPostVertexCache(undefined);
+ }).toThrow();
+ });
+
+ it('fitToUnsignedShortIndices does not change geometry', function() {
+ var geometry = new Geometry({
+ attributes : {
+ time : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ values : [10.0]
+ }),
+ heat : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ values : [1.0]
+ })
+ },
+ indices : [0, 0, 0],
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);
+
+ expect(geometries.length).toEqual(1);
+ expect(geometries[0]).toBe(geometry);
+ });
+
+ it('fitToUnsignedShortIndices creates one geometry', function() {
+ var sixtyFourK = CesiumMath.SIXTY_FOUR_KILOBYTES;
+ var times = [];
+ for ( var i = 0; i < sixtyFourK + 1; ++i) {
+ times.push(i);
+ }
+
+ var geometry = new Geometry({
+ attributes : {
+ time : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ values : times
+ })
+ },
+ indices : [0, 0, 0, sixtyFourK, sixtyFourK, sixtyFourK, 0, sixtyFourK, 0],
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);
+
+ expect(geometries.length).toEqual(1);
+ expect(geometries[0].attributes.time.componentDatatype).toEqual(ComponentDatatype.FLOAT);
+ expect(geometries[0].attributes.time.componentsPerAttribute).toEqual(1);
+ expect(geometries[0].attributes.time.values).toEqual([0, sixtyFourK]);
+
+ expect(geometries[0].primitiveType).toEqual(PrimitiveType.TRIANGLES);
+ expect(geometries[0].indices).toEqual([0, 0, 0, 1, 1, 1, 0, 1, 0]);
+ });
+
+ it('fitToUnsignedShortIndices creates two triangle geometries', function() {
+ var sixtyFourK = CesiumMath.SIXTY_FOUR_KILOBYTES;
+
+ var positions = [];
+ for ( var i = 0; i < sixtyFourK + 1; ++i) {
+ positions.push(i, i, i);
+ }
+
+ var indices = [];
+ for ( var j = sixtyFourK; j > 1; j -= 3) {
+ indices.push(j, j - 1, j - 2);
+ }
+ indices.push(0, 1, 2);
+
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : positions
+ })
+ },
+ indices : indices,
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);
+
+ expect(geometries.length).toEqual(2);
+
+ expect(geometries[0].attributes.position.values.length).toEqual(positions.length - 6); // Two vertices are not copied (0, 1)
+ expect(geometries[0].indices.length).toEqual(indices.length - 3); // One triangle is not copied (0, 1, 2)
+
+ expect(geometries[1].attributes.position.values.length).toEqual(9);
+ expect(geometries[1].indices.length).toEqual(3);
+ });
+
+ it('fitToUnsignedShortIndices creates two line geometries', function() {
+ var sixtyFourK = CesiumMath.SIXTY_FOUR_KILOBYTES;
+
+ var positions = [];
+ for ( var i = 0; i < sixtyFourK + 2; ++i) {
+ positions.push(i, i, i);
+ }
+
+ var indices = [];
+ for ( var j = sixtyFourK; j > 1; j -= 2) {
+ indices.push(j, j - 1);
+ }
+ indices.push(0, 1);
+
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : positions
+ })
+ },
+ indices : indices,
+ primitiveType : PrimitiveType.LINES
+ });
+
+ var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);
+
+ expect(geometries.length).toEqual(2);
+
+ expect(geometries[0].attributes.position.values.length).toEqual(positions.length - 6); // Two vertices are not copied (0, 1)
+ expect(geometries[0].indices.length).toEqual(indices.length - 2); // One line is not copied (0, 1)
+
+ expect(geometries[1].attributes.position.values.length).toEqual(6);
+ expect(geometries[1].indices.length).toEqual(2);
+ });
+
+ it('fitToUnsignedShortIndices creates two point geometries', function() {
+ var sixtyFourK = CesiumMath.SIXTY_FOUR_KILOBYTES;
+
+ var positions = [];
+ var indices = [];
+ for ( var i = 0; i < sixtyFourK + 1; ++i) {
+ positions.push(i, i, i);
+ indices.push(i);
+ }
+
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : positions
+ })
+ },
+ indices : indices,
+ primitiveType : PrimitiveType.POINTS
+ });
+
+ var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);
+
+ expect(geometries.length).toEqual(2);
+
+ expect(geometries[0].attributes.position.values.length).toEqual(positions.length - 3); // One vertex is not copied
+ expect(geometries[0].indices.length).toEqual(indices.length - 1); // One point is not copied
+
+ expect(geometries[1].attributes.position.values.length).toEqual(3);
+ expect(geometries[1].indices.length).toEqual(1);
+ });
+
+ it('fitToUnsignedShortIndices throws without a geometry', function() {
+ expect(function() {
+ GeometryPipeline.fitToUnsignedShortIndices(undefined);
+ }).toThrow();
+ });
+
+ it('fitToUnsignedShortIndices throws without triangles, lines, or points', function() {
+ var geometry = new Geometry({
+ attributes : {
+ time : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ values : [10.0, 11.0, 12.0]
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType : PrimitiveType.TRIANGLE_STRIP
+ });
+
+ expect(function() {
+ return GeometryPipeline.fitToUnsignedShortIndices(geometry);
+ }).toThrow();
+ });
+
+ it('fitToUnsignedShortIndices throws with different numbers of attributes', function() {
+ var geometry = new Geometry({
+ attributes : {
+ time : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ values : [10.0]
+ }),
+ heat : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ values : [1.0, 2.0]
+ })
+ },
+ indices : [0, 0, 0],
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ expect(function() {
+ return GeometryPipeline.fitToUnsignedShortIndices(geometry);
+ }).toThrow();
+ });
+
+ it('projectTo2D', function() {
+ var p1 = new Cartesian3(100000, 200000, 300000);
+ var p2 = new Cartesian3(400000, 500000, 600000);
+
+ var geometry = {};
+ geometry.attributes = {};
+ geometry.attributes.position = {
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [p1.x, p1.y, p1.z, p2.x, p2.y, p2.z]
+ };
+
+ geometry = GeometryPipeline.projectTo2D(geometry);
+
+ var ellipsoid = Ellipsoid.WGS84;
+ var projection = new GeographicProjection();
+ var projectedP1 = projection.project(ellipsoid.cartesianToCartographic(p1));
+ var projectedP2 = projection.project(ellipsoid.cartesianToCartographic(p2));
+
+ expect(geometry.attributes.position2D.values[0]).toEqual(projectedP1.x);
+ expect(geometry.attributes.position2D.values[1]).toEqual(projectedP1.y);
+ expect(geometry.attributes.position2D.values[2]).toEqual(projectedP1.z);
+ expect(geometry.attributes.position2D.values[3]).toEqual(projectedP2.x);
+ expect(geometry.attributes.position2D.values[4]).toEqual(projectedP2.y);
+ expect(geometry.attributes.position2D.values[5]).toEqual(projectedP2.z);
+
+ expect(geometry.attributes.position3D.values[0]).toEqual(p1.x);
+ expect(geometry.attributes.position3D.values[1]).toEqual(p1.y);
+ expect(geometry.attributes.position3D.values[2]).toEqual(p1.z);
+ expect(geometry.attributes.position3D.values[3]).toEqual(p2.x);
+ expect(geometry.attributes.position3D.values[4]).toEqual(p2.y);
+ expect(geometry.attributes.position3D.values[5]).toEqual(p2.z);
+ });
+
+ it('projectTo2D throws without a geometry', function() {
+ expect(function() {
+ GeometryPipeline.projectTo2D(undefined);
+ }).toThrow();
+ });
+
+ it('encodeAttribute encodes positions', function() {
+ var c = new Cartesian3(-10000000.0, 0.0, 10000000.0);
+ var encoded = EncodedCartesian3.fromCartesian(c);
+
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : [c.x, c.y, c.z]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
+ geometry = GeometryPipeline.encodeAttribute(geometry, 'position', 'positionHigh', 'positionLow');
+
+ expect(geometry.attributes.positionHigh).toBeDefined();
+ expect(geometry.attributes.positionHigh.values[0]).toEqual(encoded.high.x);
+ expect(geometry.attributes.positionHigh.values[1]).toEqual(encoded.high.y);
+ expect(geometry.attributes.positionHigh.values[2]).toEqual(encoded.high.z);
+ expect(geometry.attributes.positionLow).toBeDefined();
+ expect(geometry.attributes.positionLow.values[0]).toEqual(encoded.low.x);
+ expect(geometry.attributes.positionLow.values[1]).toEqual(encoded.low.y);
+ expect(geometry.attributes.positionLow.values[2]).toEqual(encoded.low.z);
+ expect(geometry.attributes.position).not.toBeDefined();
+ });
+
+ it('encodeAttribute throws without a geometry', function() {
+ expect(function() {
+ GeometryPipeline.encodeAttribute(undefined);
+ }).toThrow();
+ });
+
+ it('encodeAttribute throws without attributeName', function() {
+ expect(function() {
+ GeometryPipeline.encodeAttribute(new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ }));
+ }).toThrow();
+ });
+
+ it('encodeAttribute throws without attributeHighName', function() {
+ expect(function() {
+ GeometryPipeline.encodeAttribute(new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ }), 'position');
+ }).toThrow();
+ });
+
+ it('encodeAttribute throws without attributeLowName', function() {
+ expect(function() {
+ GeometryPipeline.encodeAttribute(new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ }), 'position', 'positionHigh');
+ }).toThrow();
+ });
+
+ it('encodeAttribute throws without attribute', function() {
+ expect(function() {
+ GeometryPipeline.encodeAttribute(new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ }), 'normal');
+ }).toThrow();
+ });
+
+ it('encodeAttribute throws without ComponentDatatype.DOUBLE', function() {
+ expect(function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.UNSIGNED_SHORT,
+ componentsPerAttribute : 1,
+ values : [0.0]
+ })
+ }
+ });
+ GeometryPipeline.encodeAttribute(geometry);
+ }).toThrow();
+ });
+
+ it('transformToWorldCoordinates', function() {
+ var instance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ ]
+ }),
+ normal : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0
+ ]
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0)
+ }),
+ modelMatrix : new Matrix4(0.0, 0.0, 1.0, 0.0,
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0)
+ });
+
+ var transformed = GeometryPipeline.transformToWorldCoordinates(instance);
+ var transformedPositions = [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0];
+ var transformedNormals = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0];
+
+ expect(transformed.geometry.attributes.position.values).toEqual(transformedPositions);
+ expect(transformed.geometry.attributes.normal.values).toEqual(transformedNormals);
+ expect(transformed.geometry.boundingSphere).toEqual(new BoundingSphere(new Cartesian3(0.0, 0.5, 0.5), 1.0));
+ expect(transformed.modelMatrix).toEqual(Matrix4.IDENTITY);
+ });
+
+ it('transformToWorldCoordinates does nothing when already in world coordinates', function() {
+ var instance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ ]
+ }),
+ normal : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0
+ ]
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0)
+ }),
+ modelMatrix : Matrix4.IDENTITY
+ });
+
+ var transformed = GeometryPipeline.transformToWorldCoordinates(instance);
+ var transformedPositions = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0];
+ var transformedNormals = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0];
+
+ expect(transformed.geometry.attributes.position.values).toEqual(transformedPositions);
+ expect(transformed.geometry.attributes.normal.values).toEqual(transformedNormals);
+ expect(transformed.geometry.boundingSphere).toEqual(new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0));
+ expect(transformed.modelMatrix).toEqual(Matrix4.IDENTITY);
+ });
+
+ it('transformToWorldCoordinates throws without an instance', function() {
+ expect(function() {
+ GeometryPipeline.transformToWorldCoordinates();
+ }).toThrow();
+ });
+
+ it('combine combines one geometry', function() {
+ var instance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : new Float32Array([0.0, 0.0, 0.0])
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ })
+ });
+
+ var combined = GeometryPipeline.combine([instance]);
+ expect(combined).toEqual(instance.geometry);
+ });
+
+ it('combine combines several geometries without indicess', function() {
+ var instance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ })
+ });
+ var anotherInstance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [1.0, 1.0, 1.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ })
+ });
+
+ var combined = GeometryPipeline.combine([instance, anotherInstance]);
+ expect(combined).toEqual(new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : new Float32Array([
+ 0.0, 0.0, 0.0,
+ 1.0, 1.0, 1.0
+ ])
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ }));
+ });
+
+ it('combine combines several geometries with indicess', function() {
+ var instance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 0.0, 0.0, 0.0,
+ 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0
+ ]
+ }),
+ normal : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 0.0, 0.0, 0.0,
+ 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0
+ ]
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType : PrimitiveType.TRIANGLES
+ })
+ });
+ var anotherInstance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 3.0, 3.0, 3.0,
+ 4.0, 4.0, 4.0,
+ 5.0, 5.0, 5.0
+ ]
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType : PrimitiveType.TRIANGLES
+ })
+ });
+
+ var combined = GeometryPipeline.combine([instance, anotherInstance]);
+ expect(combined).toEqual(new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : new Float32Array([
+ 0.0, 0.0, 0.0,
+ 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0,
+ 3.0, 3.0, 3.0,
+ 4.0, 4.0, 4.0,
+ 5.0, 5.0, 5.0
+ ])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2, 3, 4, 5]),
+ primitiveType : PrimitiveType.TRIANGLES
+ }));
+ });
+
+ it('combine combines bounding spheres', function() {
+ var instance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ ]
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0)
+ })
+ });
+ var anotherInstance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [
+ 1.0, 0.0, 0.0,
+ 2.0, 0.0, 0.0,
+ 1.0, 1.0, 0.0
+ ]
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : new BoundingSphere(new Cartesian3(1.5, 0.5, 0.0), 1.0)
+ })
+ });
+
+ var combined = GeometryPipeline.combine([instance, anotherInstance]);
+ var expected = BoundingSphere.union(instance.geometry.boundingSphere, anotherInstance.geometry.boundingSphere);
+ expect(combined.boundingSphere).toEqual(expected);
+ });
+
+ it('combine throws without instances', function() {
+ expect(function() {
+ GeometryPipeline.combine();
+ }).toThrow();
+ });
+
+ it('combine throws when instances.length is zero', function() {
+ expect(function() {
+ GeometryPipeline.combine([]);
+ }).toThrow();
+ });
+
+ it('combine throws when instances.modelMatrix do not match', function() {
+ var instance0 = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ }),
+ modelMatrix : Matrix4.fromScale(new Cartesian3(1.0, 1.0, 1.0))
+ });
+
+ var instance1 = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ }),
+ modelMatrix : Matrix4.fromScale(new Cartesian3(2.0, 2.0, 2.0))
+ });
+
+ expect(function() {
+ GeometryPipeline.combine([instance0, instance1]);
+ }).toThrow();
+ });
+
+ it('combine throws when instance geometries do not all have or not have an indices', function() {
+ var instance0 = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ indices : [0],
+ primitiveType : PrimitiveType.POINTS
+ })
+ });
+
+ var instance1 = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ })
+ });
+
+ expect(function() {
+ GeometryPipeline.combine([instance0, instance1]);
+ }).toThrow();
+ });
+
+ it('combine throws when instance geometries do not all have the same primitive type', function() {
+ var instance0 = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ })
+ });
+
+ var instance1 = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : [0.0, 0.0, 0.0, 1.0, 0.0, 0.0]
+ })
+ },
+ primitiveType : PrimitiveType.LINES
+ })
+ });
+
+ expect(function() {
+ GeometryPipeline.combine([instance0, instance1]);
+ }).toThrow();
+ });
+
+ it('computeNormal throws when geometry is undefined', function() {
+ expect(function() {
+ GeometryPipeline.computeNormal();
+ }).toThrow();
+ });
+
+ it('computeNormal throws when geometry.attributes.position is undefined', function() {
+ var geometry = new Geometry({
+ attributes: {},
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ expect(function() {
+ GeometryPipeline.computeNormal(geometry);
+ }).toThrow();
+ });
+
+ it('computeNormal throws when geometry.indices is undefined', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ expect(function() {
+ GeometryPipeline.computeNormal(geometry);
+ }).toThrow();
+ });
+
+ it('computeNormal throws when geometry.indices.length is not a multiple of 3', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ expect(function() {
+ GeometryPipeline.computeNormal(geometry);
+ }).toThrow();
+ });
+
+ it('computeNormal throws when primitive type is not triangle', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType: PrimitiveType.TRIANGLE_STRIP
+ });
+
+ expect(function() {
+ GeometryPipeline.computeNormal(geometry);
+ }).toThrow();
+ });
+
+
+ it('computeNormal computes normal for one triangle', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType: PrimitiveType.TRIANGLES
+ });
+
+ geometry = GeometryPipeline.computeNormal(geometry);
+
+ expect(geometry.attributes.normal.values.length).toEqual(3*3);
+ expect(geometry.attributes.normal.values).toEqual([0, 0, 1, 0, 0, 1, 0, 0, 1]);
+ });
+
+ it('computeNormal computes normal for two triangles', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 1, 1, 1, 1, 2, 0, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2, 1, 3, 2],
+ primitiveType: PrimitiveType.TRIANGLES
+ });
+
+ geometry = GeometryPipeline.computeNormal(geometry);
+
+ var normals = geometry.attributes.normal.values;
+ expect(normals.length).toEqual(4*3);
+
+ var a = new Cartesian3(-1, 0, 1).normalize();
+
+ expect(Cartesian3.fromArray(normals, 0)).toEqualEpsilon(a, CesiumMath.EPSILON7);
+ expect(Cartesian3.fromArray(normals, 3)).toEqualEpsilon(Cartesian3.UNIT_Z, CesiumMath.EPSILON7);
+ expect(Cartesian3.fromArray(normals, 6)).toEqualEpsilon(Cartesian3.UNIT_Z, CesiumMath.EPSILON7);
+
+ a = new Cartesian3(1, 0, 1).normalize();
+ expect(Cartesian3.fromArray(normals, 9)).toEqualEpsilon(a, CesiumMath.EPSILON7);
+ });
+
+ it('computeNormal computes normal for six triangles', function() {
+ var geometry = new Geometry ({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2, 3, 0, 2, 4, 0, 3, 4, 5, 0, 5, 6, 0, 6, 1, 0],
+ primitiveType: PrimitiveType.TRIANGLES
+ });
+
+ geometry = GeometryPipeline.computeNormal(geometry);
+
+ var normals = geometry.attributes.normal.values;
+ expect(normals.length).toEqual(7*3);
+
+ var a = new Cartesian3(-1, -1, -1).normalize();
+ expect(Cartesian3.fromArray(normals, 0)).toEqualEpsilon(a, CesiumMath.EPSILON7);
+
+ a = new Cartesian3(0, -1, -1).normalize();
+ expect(Cartesian3.fromArray(normals, 3)).toEqualEpsilon(a, CesiumMath.EPSILON7);
+
+ expect(Cartesian3.fromArray(normals, 6)).toEqualEpsilon(Cartesian3.UNIT_Y.negate(), CesiumMath.EPSILON7);
+
+ a = new Cartesian3(-1, -1, 0).normalize();
+ expect(Cartesian3.fromArray(normals, 9)).toEqualEpsilon(a, CesiumMath.EPSILON7);
+
+ expect(Cartesian3.fromArray(normals, 12)).toEqualEpsilon(Cartesian3.UNIT_X.negate(), CesiumMath.EPSILON7);
+
+ a = new Cartesian3(-1, 0, -1).normalize();
+ expect(Cartesian3.fromArray(normals, 15)).toEqualEpsilon(a, CesiumMath.EPSILON7);
+
+ expect(Cartesian3.fromArray(normals, 18)).toEqualEpsilon(Cartesian3.UNIT_Z.negate(), CesiumMath.EPSILON7);
+ });
+
+ it('computeBinormalAndTangent throws when geometry is undefined', function() {
+ expect(function() {
+ GeometryPipeline.computeBinormalAndTangent();
+ }).toThrow();
+ });
+
+ it('computeBinormalAndTangent throws when position is undefined', function() {
+ var geometry = new Geometry({
+ attributes: {
+ normal: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ }),
+ st: new GeometryAttribute({
+ values: [0, 0, 1, 1],
+ componentsPerAttribute: 2,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType: PrimitiveType.TRIANGLE_STRIP
+ });
+
+ expect(function() {
+ GeometryPipeline.computeBinormalAndTangent(geometry);
+ }).toThrow();
+ });
+
+ it('computeBinormalAndTangent throws when normal is undefined', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ }),
+ st: new GeometryAttribute({
+ values: [0, 0, 1, 1],
+ componentsPerAttribute: 2,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType: PrimitiveType.TRIANGLE_STRIP
+ });
+
+ expect(function() {
+ GeometryPipeline.computeBinormalAndTangent(geometry);
+ }).toThrow();
+ });
+
+ it('computeBinormalAndTangent throws when st is undefined', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ }),
+ normal: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType: PrimitiveType.TRIANGLE_STRIP
+ });
+
+ expect(function() {
+ GeometryPipeline.computeBinormalAndTangent(geometry);
+ }).toThrow();
+ });
+
+ it('computeBinormalAndTangent throws when geometry.indices is undefined', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ }),
+ normal: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ }),
+ st: new GeometryAttribute({
+ values: [0, 0, 1, 1],
+ componentsPerAttribute: 2,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
+
+ expect(function() {
+ GeometryPipeline.computeBinormalAndTangent(geometry);
+ }).toThrow();
+ });
+
+ it('computeBinormalAndTangent throws when indices is not a multiple of 3', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ }),
+ normal: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ }),
+ st: new GeometryAttribute({
+ values: [0, 0, 1, 1],
+ componentsPerAttribute: 2,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2, 3, 4],
+ primitiveType: PrimitiveType.TRIANGLES
+ });
+
+ expect(function() {
+ GeometryPipeline.computeBinormalAndTangent(geometry);
+ }).toThrow();
+ });
+
+ it('computeBinormalAndTangent throws when primitive type is not triangle', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ }),
+ normal: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+
+ }),
+ st: new GeometryAttribute({
+ values: [0, 0, 1, 1],
+ componentsPerAttribute: 2,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType: PrimitiveType.TRIANGLE_STRIP
+ });
+
+ expect(function() {
+ GeometryPipeline.computeBinormalAndTangent(geometry);
+ }).toThrow();
+ });
+
+ it('computeBinormalAndTangent computes tangent and binormal for one triangle', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ }),
+ st: new GeometryAttribute({
+ values: [0, 0, 1, 0, 0, 1],
+ componentsPerAttribute: 2,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2],
+ primitiveType: PrimitiveType.TRIANGLES
+ });
+
+ geometry = GeometryPipeline.computeNormal(geometry);
+ geometry = GeometryPipeline.computeBinormalAndTangent(geometry);
+
+ expect(geometry.attributes.tangent.values).toEqual([1, 0, 0, 1, 0, 0, 1, 0, 0]);
+ expect(geometry.attributes.binormal.values).toEqual([0, 1, 0, 0, 1, 0, 0, 1, 0]);
+ });
+
+ it('computeBinormalAndTangent computes tangent and binormal for two triangles', function() {
+ var geometry = new Geometry({
+ attributes: {
+ position: new GeometryAttribute({
+ values: [0, 0, 0, 1, 0, 1, 1, 1, 1, 2, 0, 0],
+ componentsPerAttribute: 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ }),
+ st: new GeometryAttribute({
+ values: [0, 0, 1, 0, 1, 1, 0, 1],
+ componentsPerAttribute: 2,
+ componentDatatype : ComponentDatatype.FLOAT
+ })
+ },
+ indices : [0, 1, 2, 1, 3, 2],
+ primitiveType: PrimitiveType.TRIANGLES
+ });
+
+ geometry = GeometryPipeline.computeNormal(geometry);
+ geometry = GeometryPipeline.computeBinormalAndTangent(geometry);
+ expect(geometry.attributes.tangent.values).toEqualEpsilon([0.7071067811865475, 0, 0.7071067811865475,
+ 0, 1, 0,
+ 0, 1, 0,
+ -0.5773502691896258, 0.5773502691896258, 0.5773502691896258], CesiumMath.EPSILON7);
+ expect(geometry.attributes.binormal.values).toEqualEpsilon([0, 1, 0,
+ -1, 0, 0,
+ -1, 0, 0,
+ -0.4082482904638631, -0.8164965809277261, 0.4082482904638631], CesiumMath.EPSILON7);
+ });
+
+ it ('computeBinormalAndTangent computes tangent and binormal for an EllipsoidGeometry', function() {
+ var numberOfPartitions = 10;
+ var geometry = new EllipsoidGeometry({
+ vertexFormat : new VertexFormat({
+ position : true,
+ normal : true,
+ st : true
+ }),
+ numberOfPartitions : numberOfPartitions
+ });
+ geometry = GeometryPipeline.computeBinormalAndTangent(geometry);
+ var actualTangents = geometry.attributes.tangent.values;
+ var actualBinormals = geometry.attributes.binormal.values;
+
+ var expectedGeometry = new EllipsoidGeometry({
+ vertexFormat: VertexFormat.ALL,
+ numberOfPartitions : numberOfPartitions
+ });
+ var expectedTangents = expectedGeometry.attributes.tangent.values;
+ var expectedBinormals = expectedGeometry.attributes.binormal.values;
+
+ expect(actualTangents.length).toEqual(expectedTangents.length);
+ expect(actualBinormals.length).toEqual(expectedBinormals.length);
+
+ for (var i = 300; i < 500; i += 3) {
+ var actual = Cartesian3.fromArray(actualTangents, i);
+ var expected = Cartesian3.fromArray(expectedTangents, i);
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON1);
+
+ actual = Cartesian3.fromArray(actualBinormals, i);
+ expected = Cartesian3.fromArray(expectedBinormals, i);
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON1);
+ }
+ });
+
+ it('wrapLongitude provides indices for an un-indexed triangle list', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 2, 3, 4, 5]);
+ });
+
+ it('wrapLongitude returns unchanged geometry if indices are already defined for an un-indexed triangle list', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES,
+ indices : new Uint16Array([0, 1, 2, 3, 4, 5])
+ });
+
+ GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 2, 3, 4, 5]);
+ });
+
+ it('wrapLongitude throws when primitive type is TRIANGLES and number of vertices is less than 3', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ expect(function() {
+ GeometryPipeline.wrapLongitude(geometry);
+ }).toThrow();
+ });
+
+ it('wrapLongitude throws when primitive type is TRIANGLES and number of vertices is not a multiple of 3', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ expect(function() {
+ GeometryPipeline.wrapLongitude(geometry);
+ }).toThrow();
+ });
+
+ it('wrapLongitude creates indexed triangles for a triangle fan', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 7.0, 6.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLE_FAN
+ });
+
+ GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.primitiveType).toEqual(PrimitiveType.TRIANGLES);
+ expect(geometry.indices).toEqual([1, 0, 2, 2, 0, 3]);
+ });
+
+ it('wrapLongitude throws when primitive type is TRIANGLE_FAN and number of vertices is less than 3', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLE_FAN
+ });
+
+ expect(function() {
+ GeometryPipeline.wrapLongitude(geometry);
+ }).toThrow();
+ });
+
+ it('wrapLongitude creates indexd triangles for triangle strips', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLE_STRIP
+ });
+
+ GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.primitiveType).toEqual(PrimitiveType.TRIANGLES);
+ expect(geometry.indices).toEqual([0, 1, 2, 0, 2, 3, 3, 2, 4, 3, 4, 5]);
+ });
+
+ it('wrapLongitude throws when the primitive type is TRIANGLE_STRIP and number of vertices is less than 3', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLE_STRIP
+ });
+
+ expect(function() {
+ GeometryPipeline.wrapLongitude(geometry);
+ }).toThrow();
+ });
+
+ it('wrapLongitude creates indexed lines', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 7.0, 6.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINES
+ });
+
+ GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 2, 3]);
+ });
+
+ it('wrapLongitude returns lines unchanged if indices are provided', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 7.0, 6.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINES,
+ indices : new Uint16Array([0, 1, 2, 3])
+ });
+
+ GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 2, 3]);
+ });
+
+ it('wrapLongitude throws when primitive type is LINES and number of vertices is less than 2', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINES
+ });
+
+ expect(function() {
+ GeometryPipeline.wrapLongitude(geometry);
+ }).toThrow();
+ });
+
+ it('wrapLongitude throws when primitive type is LINES and number of vertices is not a multiple 2', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINES
+ });
+
+ expect(function() {
+ GeometryPipeline.wrapLongitude(geometry);
+ }).toThrow();
+ });
+
+ it('wrapLongitude creates indexed lines from line strip', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 7.0, 6.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINE_STRIP
+ });
+
+ GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.primitiveType).toEqual(PrimitiveType.LINES);
+ expect(geometry.indices).toEqual([0, 1, 1, 2, 2, 3]);
+ });
+
+ it('wrapLongitude throws when primitive type is LINE_STRIP and number of vertices is less than 2', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINE_STRIP
+ });
+
+ expect(function() {
+ GeometryPipeline.wrapLongitude(geometry);
+ }).toThrow();
+ });
+
+ it('wrapLongitude creates indexed lines from line loops', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 7.0, 6.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINE_LOOP
+ });
+
+ GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.primitiveType).toEqual(PrimitiveType.LINES);
+ expect(geometry.indices).toEqual([0, 1, 1, 2, 2, 3, 3, 0]);
+ });
+
+ it('wrapLongitude throws when the primitive type is LINE_LOOP and number of vertices is less than 2', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([0.0, 1.0, 2.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINE_LOOP
+ });
+
+ expect(function() {
+ GeometryPipeline.wrapLongitude(geometry);
+ }).toThrow();
+ });
+
+ it('wrapLongitude subdivides triangle crossing the international date line, p0 behind', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, -1.0, 0.0, -1.0, 1.0, 2.0, -1.0, 2.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 3, 4, 1, 2, 6, 1, 6, 5]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions.subarray(0, 3 * 3)).toEqual([-1.0, -1.0, 0.0, -1.0, 1.0, 2.0, -1.0, 2.0, 2.0]);
+ expect(positions.length).toEqual(7 * 3);
+ });
+
+ it('wrapLongitude subdivides triangle crossing the international date line, p1 behind', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, 1.0, 2.0, -1.0, -1.0, 0.0, -1.0, 2.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([1, 3, 4, 2, 0, 6, 2, 6, 5]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions.subarray(0, 3 * 3)).toEqual([-1.0, 1.0, 2.0, -1.0, -1.0, 0.0, -1.0, 2.0, 2.0]);
+ expect(positions.length).toEqual(7 * 3);
+ });
+
+ it('wrapLongitude subdivides triangle crossing the international date line, p2 behind', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, 1.0, 2.0, -1.0, 2.0, 2.0, -1.0, -1.0, 0.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([2, 3, 4, 0, 1, 6, 0, 6, 5]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions.subarray(0, 3 * 3)).toEqual([-1.0, 1.0, 2.0, -1.0, 2.0, 2.0, -1.0, -1.0, 0.0]);
+ expect(positions.length).toEqual(7 * 3);
+ });
+
+ it('wrapLongitude subdivides triangle crossing the international date line, p0 ahead', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, 2.0, 0.0, -1.0, -1.0, 0.0, -1.0, -1.0, 0.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([1, 2, 4, 1, 4, 3, 0, 5, 6]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions.subarray(0, 3 * 3)).toEqual([-1.0, 2.0, 0.0, -1.0, -1.0, 0.0, -1.0, -1.0, 0.0]);
+ expect(positions.length).toEqual(7 * 3);
+ });
+
+ it('wrapLongitude subdivides triangle crossing the international date line, p1 ahead', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, -1.0, 0.0, -1.0, 2.0, 0.0, -1.0, -1.0, 0.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([2, 0, 4, 2, 4, 3, 1, 5, 6]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions.subarray(0, 3 * 3)).toEqual([-1.0, -1.0, 0.0, -1.0, 2.0, 0.0, -1.0, -1.0, 0.0]);
+ expect(positions.length).toEqual(7 * 3);
+ });
+
+ it('wrapLongitude subdivides triangle crossing the international date line, p2 ahead', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, -1.0, 0.0, -1.0, -1.0, 0.0, -1.0, 2.0, 0.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 4, 0, 4, 3, 2, 5, 6]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions.subarray(0, 3 * 3)).toEqual([-1.0, -1.0, 0.0, -1.0, -1.0, 0.0, -1.0, 2.0, 0.0]);
+ expect(positions.length).toEqual(7 * 3);
+ });
+
+ it('wrapLongitude returns offset triangle that touches the international date line', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, 0.0, 1.0, -1.0, CesiumMath.EPSILON14, 2.0, -2.0, 2.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 2]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions).toEqual([-1.0, CesiumMath.EPSILON11, 1.0, -1.0, CesiumMath.EPSILON11, 2.0, -2.0, 2.0, 2.0]);
+ expect(positions.length).toEqual(3 * 3);
+ });
+
+ it('wrapLongitude returns the same points if the triangle doesn\'t cross the international date line, behind', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, -1.0, 1.0, -1.0, -2.0, 1.0, -1.0, -2.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 2]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions).toEqual([-1.0, -1.0, 1.0, -1.0, -2.0, 1.0, -1.0, -2.0, 2.0]);
+ expect(positions.length).toEqual(3 * 3);
+ });
+
+ it('wrapLongitude returns the same points if the triangle doesn\'t cross the international date line, ahead', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, 1.0, 1.0, -1.0, 2.0, 1.0, -1.0, 2.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 2]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions).toEqual([-1.0, 1.0, 1.0, -1.0, 2.0, 1.0, -1.0, 2.0, 2.0]);
+ expect(positions.length).toEqual(3 * 3);
+ });
+
+ it('wrapLongitude returns the same points if the triangle doesn\'t cross the international date line, positive x', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 2.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1, 2]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions).toEqual([1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 2.0, 2.0]);
+ expect(positions.length).toEqual(3 * 3);
+ });
+
+ it('wrapLongitude computes all attributes for a triangle crossing the international date line', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-2.0, -1.0, 0.0, -3.0, 1.0, 0.0, -1.0, 1.0, 0.0])
+ }),
+ normal : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0])
+ }),
+ tangent : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : new Float32Array([-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0])
+ }),
+ binormal : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : new Float32Array([0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0])
+ }),
+ st : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : new Float32Array([0.0, 0.0, 1.0, 0.0, 0.5, 0.5])
+ })
+ },
+ indices : new Uint16Array([1, 2, 0]),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 3, 4, 1, 2, 6, 1, 6, 5]);
+
+ var positions = geometry.attributes.position.values;
+ var normals = geometry.attributes.normal.values;
+ var binormals = geometry.attributes.binormal.values;
+ var tangents = geometry.attributes.tangent.values;
+ var texCoords = geometry.attributes.st.values;
+
+ expect(positions.length).toEqual(7 * 3);
+ expect(normals.length).toEqual(7 * 3);
+ expect(binormals.length).toEqual(7 * 3);
+ expect(tangents.length).toEqual(7 * 3);
+ expect(texCoords.length).toEqual(7 * 2);
+
+ for (var i = 0; i < positions.length; i += 3) {
+ expect(Cartesian3.fromArray(normals, i)).toEqual(Cartesian3.UNIT_Z);
+ expect(Cartesian3.fromArray(binormals, i)).toEqual(Cartesian3.UNIT_Y.negate());
+ expect(Cartesian3.fromArray(tangents, i)).toEqual(Cartesian3.UNIT_X.negate());
+ }
+ });
+
+ it('wrapLongitude subdivides line crossing the international date line', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, -1.0, 0.0, -1.0, 1.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1]),
+ primitiveType : PrimitiveType.LINES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 2, 3, 1]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions.subarray(0, 2 * 3)).toEqual([-1.0, -1.0, 0.0, -1.0, 1.0, 2.0]);
+ expect(positions.length).toEqual(4 * 3);
+ });
+
+ it('wrapLongitude returns offset line that touches the international date line', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([-1.0, 0.0, 0.0, -1.0, 1.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1]),
+ primitiveType : PrimitiveType.LINES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions).toEqual([-1.0, CesiumMath.EPSILON6, 0.0, -1.0, 1.0, 2.0]);
+ expect(positions.length).toEqual(2 * 3);
+ });
+
+ it('wrapLongitude returns the same points if the line doesn\'t cross the international date line', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([1.0, 1.0, 0.0, 1.0, 1.0, 2.0])
+ })
+ },
+ indices : new Uint16Array([0, 1]),
+ primitiveType : PrimitiveType.LINES
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).toEqual([0, 1]);
+
+ var positions = geometry.attributes.position.values;
+ expect(positions).toEqual([1.0, 1.0, 0.0, 1.0, 1.0, 2.0]);
+ expect(positions.length).toEqual(2 * 3);
+ });
+
+ it('wrapLongitude does nothing for points', function() {
+ var geometry = new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([1.0, 1.0, 0.0, 1.0, 1.0, 2.0])
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
+ geometry = GeometryPipeline.wrapLongitude(geometry);
+ expect(geometry.indices).not.toBeDefined();
+
+ var positions = geometry.attributes.position.values;
+ expect(positions).toEqual([1.0, 1.0, 0.0, 1.0, 1.0, 2.0]);
+ expect(positions.length).toEqual(2 * 3);
+ });
+
+ it('wrapLongitude throws when geometry is undefined', function() {
+ expect(function() {
+ return GeometryPipeline.wrapLongitude();
+ }).toThrow();
+ });
+});
diff --git a/Specs/Core/GeometrySpec.js b/Specs/Core/GeometrySpec.js
new file mode 100644
index 000000000000..ce41c563620f
--- /dev/null
+++ b/Specs/Core/GeometrySpec.js
@@ -0,0 +1,138 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/Geometry',
+ 'Core/GeometryAttribute',
+ 'Core/ComponentDatatype',
+ 'Core/BoundingSphere',
+ 'Core/Cartesian3',
+ 'Core/PrimitiveType'
+ ], function(
+ Geometry,
+ GeometryAttribute,
+ ComponentDatatype,
+ BoundingSphere,
+ Cartesian3,
+ PrimitiveType) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor', function() {
+ var attributes = {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ ])
+ })
+ };
+ var indices = new Uint16Array([0, 1, 2]);
+ var boundingSphere = new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0);
+
+ var geometry = new Geometry({
+ attributes : attributes,
+ indices : indices,
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : boundingSphere
+ });
+
+ expect(geometry.attributes).toBe(attributes);
+ expect(geometry.indices).toBe(indices);
+ expect(geometry.primitiveType).toEqual(PrimitiveType.TRIANGLES);
+ expect(geometry.boundingSphere).toBe(boundingSphere);
+ });
+
+ it('constructor thows without primitiveType', function() {
+ expect(function() {
+ return new Geometry({
+ attributes : {}
+ });
+ }).toThrow();
+ });
+
+ it('constructor throws without attributes', function() {
+ expect(function() {
+ return new Geometry({
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ }).toThrow();
+ });
+
+ it('computeNumberOfVertices', function() {
+ var attributes = {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ ])
+ }),
+ st : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : new Float32Array([
+ 0.0, 0.0,
+ 1.0, 0.0,
+ 0.0, 1.0
+ ])
+ })
+ };
+ var indices = new Uint16Array([0, 1, 2]);
+ var boundingSphere = new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0);
+
+ var geometry = new Geometry({
+ attributes : attributes,
+ indices : indices,
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : boundingSphere
+ });
+
+ expect(Geometry.computeNumberOfVertices(geometry)).toEqual(3);
+ });
+
+ it('computeNumberOfVertices throws when attributes have different number of vertices', function() {
+ var attributes = {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ ])
+ }),
+ st : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : new Float32Array([
+ 0.0, 0.0,
+ 1.0, 0.0
+ ])
+ })
+ };
+ var indices = new Uint16Array([0, 1, 2]);
+ var boundingSphere = new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0);
+
+ var geometry = new Geometry({
+ attributes : attributes,
+ indices : indices,
+ primitiveType : PrimitiveType.TRIANGLES,
+ boundingSphere : boundingSphere
+ });
+
+ expect(function() {
+ Geometry.computeNumberOfVertices(geometry);
+ }).toThrow();
+ });
+
+ it('computeNumberOfVertices throws without geometry', function() {
+ expect(function() {
+ Geometry.computeNumberOfVertices();
+ }).toThrow();
+ });
+
+});
diff --git a/Specs/Core/MeshFiltersSpec.js b/Specs/Core/MeshFiltersSpec.js
deleted file mode 100644
index d2b99cebc219..000000000000
--- a/Specs/Core/MeshFiltersSpec.js
+++ /dev/null
@@ -1,495 +0,0 @@
-/*global defineSuite*/
-defineSuite([
- 'Core/MeshFilters',
- 'Core/PrimitiveType',
- 'Core/ComponentDatatype',
- 'Core/CubeMapEllipsoidTessellator',
- 'Core/Ellipsoid',
- 'Core/Cartesian3',
- 'Core/EncodedCartesian3',
- 'Core/Tipsify',
- 'Core/GeographicProjection'
- ], function(
- MeshFilters,
- PrimitiveType,
- ComponentDatatype,
- CubeMapEllipsoidTessellator,
- Ellipsoid,
- Cartesian3,
- EncodedCartesian3,
- Tipsify,
- GeographicProjection) {
- "use strict";
- /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
-
- it('converts triangles to wireframe in place', function() {
- var mesh = MeshFilters.toWireframeInPlace({
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : [0, 1, 2, 3, 4, 5]
- }]
- });
-
- expect(mesh.indexLists[0].primitiveType).toEqual(PrimitiveType.LINES);
-
- var v = mesh.indexLists[0].values;
- expect(v.length).toEqual(12);
-
- expect(v[0]).toEqual(0);
- expect(v[1]).toEqual(1);
- expect(v[2]).toEqual(1);
- expect(v[3]).toEqual(2);
- expect(v[4]).toEqual(2);
- expect(v[5]).toEqual(0);
-
- expect(v[6]).toEqual(3);
- expect(v[7]).toEqual(4);
- expect(v[8]).toEqual(4);
- expect(v[9]).toEqual(5);
- expect(v[10]).toEqual(5);
- expect(v[11]).toEqual(3);
- });
-
- it('converts a triangle fan to wireframe in place', function() {
- var mesh = MeshFilters.toWireframeInPlace({
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLE_FAN,
- values : [0, 1, 2, 3]
- }]
- });
-
- expect(mesh.indexLists[0].primitiveType).toEqual(PrimitiveType.LINES);
-
- var v = mesh.indexLists[0].values;
- expect(v.length).toEqual(12);
-
- expect(v[0]).toEqual(0);
- expect(v[1]).toEqual(1);
- expect(v[2]).toEqual(1);
- expect(v[3]).toEqual(2);
- expect(v[4]).toEqual(2);
- expect(v[5]).toEqual(0);
-
- expect(v[6]).toEqual(0);
- expect(v[7]).toEqual(2);
- expect(v[8]).toEqual(2);
- expect(v[9]).toEqual(3);
- expect(v[10]).toEqual(3);
- expect(v[11]).toEqual(0);
- });
-
- it('converts a triangle strip to wireframe in place', function() {
- var mesh = MeshFilters.toWireframeInPlace({
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLE_STRIP,
- values : [0, 1, 2, 3]
- }]
- });
-
- expect(mesh.indexLists[0].primitiveType).toEqual(PrimitiveType.LINES);
-
- var v = mesh.indexLists[0].values;
- expect(v.length).toEqual(12);
-
- expect(v[0]).toEqual(0);
- expect(v[1]).toEqual(1);
- expect(v[2]).toEqual(1);
- expect(v[3]).toEqual(2);
- expect(v[4]).toEqual(2);
- expect(v[5]).toEqual(0);
-
- expect(v[6]).toEqual(2);
- expect(v[7]).toEqual(3);
- expect(v[8]).toEqual(3);
- expect(v[9]).toEqual(1);
- expect(v[10]).toEqual(1);
- expect(v[11]).toEqual(2);
- });
-
- it('creates attribute indices', function() {
- var mesh = {
- attributes : {
- position : {},
- normal : {},
- color : {}
- }
- };
-
- var indices = MeshFilters.createAttributeIndices(mesh);
-
- var validIndices = [0, 1, 2];
- expect(validIndices).toContain(indices.position);
- expect(validIndices).toContain(indices.normal);
- expect(validIndices).toContain(indices.color);
- expect(indices.position).not.toEqual(indices.normal);
- expect(indices.position).not.toEqual(indices.color);
- });
-
- it('maps attribute indices to different names', function() {
- var indices = {
- positions : 0,
- normals : 1,
- colors : 2
- };
-
- var mappedIndices = MeshFilters.mapAttributeIndices(indices, {
- positions : 'position',
- normals : 'normal',
- colors : 'color'
- });
-
- expect(mappedIndices.position).toEqual(indices.positions);
- expect(mappedIndices.normal).toEqual(indices.normals);
- expect(mappedIndices.color).toEqual(indices.colors);
- });
-
- it('throws an exception when mesh properties have a different number of attributes', function() {
- expect(function() {
- var mesh = {};
- mesh.attributes = {};
-
- mesh.attributes.attribute1 = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 1,
- values : [0, 1, 2]
- };
-
- mesh.attributes.attribute2 = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 3,
- values : [0, 1, 2, 3, 4, 5]
- };
- mesh = MeshFilters.reorderForPreVertexCache(mesh);
- }).toThrow();
- });
-
- it('can reorder all indices and attributes for the pre vertex cahce', function() {
- var mesh = {};
- mesh.attributes = {};
- mesh.indexLists = [];
-
- mesh.indexLists.push({
- primitiveType : PrimitiveType.TRIANGLES,
- values : [5, 3, 2, 0, 1, 4]
- });
-
- mesh.indexLists.push({
- primitiveType : PrimitiveType.TRIANGLES,
- values : [4, 1, 3, 2, 5, 0]
- });
-
- mesh.attributes.vertexNames = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 1,
- values : ['v0', 'v1', 'v2', 'v3', 'v4', 'v5']
- };
-
- mesh.attributes.positions = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 3,
- values : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
- };
- MeshFilters.reorderForPreVertexCache(mesh);
-
- expect(mesh.indexLists[0].values[0]).toEqual(0);
- expect(mesh.indexLists[0].values[1]).toEqual(1);
- expect(mesh.indexLists[0].values[2]).toEqual(2);
- expect(mesh.indexLists[0].values[3]).toEqual(3);
- expect(mesh.indexLists[0].values[4]).toEqual(4);
- expect(mesh.indexLists[0].values[5]).toEqual(5);
-
- expect(mesh.indexLists[1].values[0]).toEqual(5);
- expect(mesh.indexLists[1].values[1]).toEqual(4);
- expect(mesh.indexLists[1].values[2]).toEqual(1);
- expect(mesh.indexLists[1].values[3]).toEqual(2);
- expect(mesh.indexLists[1].values[4]).toEqual(0);
- expect(mesh.indexLists[1].values[5]).toEqual(3);
-
- expect(mesh.attributes.vertexNames.values[0]).toEqual('v5');
- expect(mesh.attributes.vertexNames.values[1]).toEqual('v3');
- expect(mesh.attributes.vertexNames.values[2]).toEqual('v2');
- expect(mesh.attributes.vertexNames.values[3]).toEqual('v0');
- expect(mesh.attributes.vertexNames.values[4]).toEqual('v1');
- expect(mesh.attributes.vertexNames.values[5]).toEqual('v4');
-
- expect(mesh.attributes.positions.values[0]).toEqual(15);
- expect(mesh.attributes.positions.values[1]).toEqual(16);
- expect(mesh.attributes.positions.values[2]).toEqual(17);
- expect(mesh.attributes.positions.values[3]).toEqual(9);
- expect(mesh.attributes.positions.values[4]).toEqual(10);
- expect(mesh.attributes.positions.values[5]).toEqual(11);
- expect(mesh.attributes.positions.values[6]).toEqual(6);
- expect(mesh.attributes.positions.values[7]).toEqual(7);
- expect(mesh.attributes.positions.values[8]).toEqual(8);
- expect(mesh.attributes.positions.values[9]).toEqual(0);
- expect(mesh.attributes.positions.values[10]).toEqual(1);
- expect(mesh.attributes.positions.values[11]).toEqual(2);
- expect(mesh.attributes.positions.values[12]).toEqual(3);
- expect(mesh.attributes.positions.values[13]).toEqual(4);
- expect(mesh.attributes.positions.values[14]).toEqual(5);
- expect(mesh.attributes.positions.values[15]).toEqual(12);
- expect(mesh.attributes.positions.values[16]).toEqual(13);
- expect(mesh.attributes.positions.values[17]).toEqual(14);
- });
-
- it('can reorder indices for the post vertex cache', function() {
- var mesh = CubeMapEllipsoidTessellator.compute(new Ellipsoid(10.0, 10.0, 10.0), 100);
- var indices = mesh.indexLists[0].values;
- var numIndices = indices.length;
- var maximumIndex = 0;
- for ( var i = 0; i < numIndices; i++) {
- if (indices[i] > maximumIndex) {
- maximumIndex = indices[i];
- }
- }
- var ACMRbefore = Tipsify.calculateACMR({indices : indices,
- maximumIndex : maximumIndex,
- cacheSize : 24});
- expect(ACMRbefore).toBeGreaterThan(1.00);
- mesh = MeshFilters.reorderForPostVertexCache(mesh);
- indices = mesh.indexLists[0].values;
- var ACMRafter = Tipsify.calculateACMR({indices : indices,
- maximumIndex : maximumIndex,
- cacheSize : 24});
- expect(ACMRafter).toBeLessThan(0.70);
- });
-
- it('fitToUnsignedShortIndices does not change mesh', function() {
- var mesh = {
- attributes : {
- time : {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 1,
- values : [10.0]
- },
- heat : {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 1,
- values : [1.0]
- }
- },
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : [0, 0, 0]
- }]
- };
-
- var meshes = MeshFilters.fitToUnsignedShortIndices(mesh);
-
- expect(meshes.length).toEqual(1);
- expect(meshes[0]).toBe(mesh);
- });
-
- it('fitToUnsignedShortIndices creates one mesh', function() {
- var sixtyFourK = 64 * 1024;
- var times = [];
- for ( var i = 0; i < sixtyFourK + 1; ++i) {
- times.push(i);
- }
-
- var mesh = {
- attributes : {
- time : {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 1,
- values : times
- }
- },
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : [0, 0, 0, sixtyFourK, sixtyFourK, sixtyFourK, 0, sixtyFourK, 0]
- }]
- };
-
- var meshes = MeshFilters.fitToUnsignedShortIndices(mesh);
-
- expect(meshes.length).toEqual(1);
- expect(meshes[0].attributes.time.componentDatatype).toEqual(ComponentDatatype.FLOAT);
- expect(meshes[0].attributes.time.componentsPerAttribute).toEqual(1);
- expect(meshes[0].attributes.time.values).toEqual([0, sixtyFourK]);
-
- expect(meshes[0].indexLists[0].primitiveType).toEqual(PrimitiveType.TRIANGLES);
- expect(meshes[0].indexLists[0].values).toEqual([0, 0, 0, 1, 1, 1, 0, 1, 0]);
- });
-
- it('fitToUnsignedShortIndices creates two meshes', function() {
- var sixtyFourK = 64 * 1024;
-
- var positions = [];
- for ( var i = 0; i < sixtyFourK + 1; ++i) {
- positions.push(i, i, i);
- }
-
- var indices = [];
- for ( var j = sixtyFourK; j > 1; j -= 3) {
- indices.push(j, j - 1, j - 2);
- }
- indices.push(0, 1, 2);
-
- var mesh = {
- attributes : {
- position : {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 3,
- values : positions
- }
- },
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : indices
- }]
- };
-
- var meshes = MeshFilters.fitToUnsignedShortIndices(mesh);
-
- expect(meshes.length).toEqual(2);
-
- expect(meshes[0].attributes.position.values.length).toEqual(positions.length - 6); // Two vertices are not copied (0, 1)
- expect(meshes[0].indexLists[0].values.length).toEqual(indices.length - 3); // One triangle is not copied (0, 1, 2)
-
- expect(meshes[1].attributes.position.values.length).toEqual(9);
- expect(meshes[1].indexLists[0].values.length).toEqual(3);
- });
-
- it('fitToUnsignedShortIndices throws without triangles', function() {
- var mesh = {
- attributes : {
- time : {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 1,
- values : [10.0]
- }
- },
-
- indexLists : [{
- primitiveType : PrimitiveType.POINTS,
- values : [0]
- }]
- };
-
- expect(function() {
- return MeshFilters.fitToUnsignedShortIndices(mesh);
- }).toThrow();
- });
-
- it('fitToUnsignedShortIndices throws with different numbers of attributes', function() {
- var mesh = {
- attributes : {
- time : {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 1,
- values : [10.0]
- },
-
- heat : {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 1,
- values : [1.0, 2.0]
- }
- },
-
- indexLists : [{
- primitiveType : PrimitiveType.TRIANGLES,
- values : [0, 0, 0]
- }]
- };
-
- expect(function() {
- return MeshFilters.fitToUnsignedShortIndices(mesh);
- }).toThrow();
- });
-
- it('projectTo2D', function() {
- var p1 = new Cartesian3(100000, 200000, 300000);
- var p2 = new Cartesian3(400000, 500000, 600000);
-
- var mesh = {};
- mesh.attributes = {};
- mesh.attributes.position = {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 3,
- values : [p1.x, p1.y, p1.z, p2.x, p2.y, p2.z]
- };
-
- mesh = MeshFilters.projectTo2D(mesh);
-
- var ellipsoid = Ellipsoid.WGS84;
- var projection = new GeographicProjection();
- var projectedP1 = projection.project(ellipsoid.cartesianToCartographic(p1));
- var projectedP2 = projection.project(ellipsoid.cartesianToCartographic(p2));
-
- expect(mesh.attributes.position2D.values[0]).toEqual(projectedP1.x);
- expect(mesh.attributes.position2D.values[1]).toEqual(projectedP1.y);
- expect(mesh.attributes.position2D.values[2]).toEqual(projectedP2.x);
- expect(mesh.attributes.position2D.values[3]).toEqual(projectedP2.y);
-
- expect(mesh.attributes.position3D.values[0]).toEqual(p1.x);
- expect(mesh.attributes.position3D.values[1]).toEqual(p1.y);
- expect(mesh.attributes.position3D.values[2]).toEqual(p1.z);
- expect(mesh.attributes.position3D.values[3]).toEqual(p2.x);
- expect(mesh.attributes.position3D.values[4]).toEqual(p2.y);
- expect(mesh.attributes.position3D.values[5]).toEqual(p2.z);
- });
-
- it('MeshFilters.encodeAttribute encodes positions', function() {
- var c = new Cartesian3(-10000000.0, 0.0, 10000000.0);
- var encoded = EncodedCartesian3.fromCartesian(c);
-
- var mesh = {
- attributes : {
- position : {
- componentDatatype : ComponentDatatype.FLOAT,
- componentsPerAttribute : 3,
- values : [c.x, c.y, c.z]
- }
- }
- };
- mesh = MeshFilters.encodeAttribute(mesh);
-
- expect(mesh.attributes.positionHigh).toBeDefined();
- expect(mesh.attributes.positionHigh.values[0]).toEqual(encoded.high.x);
- expect(mesh.attributes.positionHigh.values[1]).toEqual(encoded.high.y);
- expect(mesh.attributes.positionHigh.values[2]).toEqual(encoded.high.z);
- expect(mesh.attributes.positionLow).toBeDefined();
- expect(mesh.attributes.positionLow.values[0]).toEqual(encoded.low.x);
- expect(mesh.attributes.positionLow.values[1]).toEqual(encoded.low.y);
- expect(mesh.attributes.positionLow.values[2]).toEqual(encoded.low.z);
- expect(mesh.attributes.position).not.toBeDefined();
- });
-
- it('MeshFilters.encodeAttribute throws without a mesh', function() {
- expect(function() {
- MeshFilters.encodeAttribute(undefined);
- }).toThrow();
- });
-
- it('MeshFilters.encodeAttribute throws with mesh without attributes property', function() {
- expect(function() {
- MeshFilters.encodeAttribute({});
- }).toThrow();
- });
-
- it('MeshFilters.encodeAttribute throws without attribute', function() {
- expect(function() {
- var mesh = {
- attributes : {
- }
- };
- MeshFilters.encodeAttribute(mesh);
- }).toThrow();
- });
-
- it('MeshFilters.encodeAttribute throws without ComponentDatatype.FLOAT', function() {
- expect(function() {
- var mesh = {
- attributes : {
- componentDatatype : ComponentDatatype.UNSIGNED_SHORT,
- componentsPerAttribute : 1,
- values : [0.0]
- }
- };
- MeshFilters.encodeAttribute(mesh);
- }).toThrow();
- });
-
-});
\ No newline at end of file
diff --git a/Specs/Core/PlaneTessellatorSpec.js b/Specs/Core/PlaneTessellatorSpec.js
deleted file mode 100644
index 695f66df824f..000000000000
--- a/Specs/Core/PlaneTessellatorSpec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*global defineSuite*/
-defineSuite(['Core/PlaneTessellator'], function(PlaneTessellator) {
- "use strict";
- /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
-
- it('compute with default arguments', function() {
- var m = PlaneTessellator.compute();
-
- expect(m.indexLists[0].values.length).toEqual(2 * 3); // 2 triangles
- });
-
- it('compute with arguments', function() {
- var m = PlaneTessellator.compute({
- resolution : {
- x : 4,
- y : 3
- },
- onInterpolation : function(time) {
- }
- });
-
- expect(m.indexLists[0].values.length).toEqual(12 * 3); // 8 triangles
- });
-
- it('compute with onInterpolation callback', function() {
- var callbacks = [];
-
- PlaneTessellator.compute({
- resolution : {
- x : 2,
- y : 2
- },
- onInterpolation : function(time) {
- callbacks.push(time);
- }
- });
-
- expect(callbacks).toEqual([{
- x : 0.0,
- y : 0.0
- }, {
- x : 1.0,
- y : 0.0
- }, {
- x : 0.0,
- y : 1.0
- }, {
- x : 1.0,
- y : 1.0
- }]);
- });
-
- it('throws if resolution is less than 1', function() {
- expect(function() {
- PlaneTessellator.compute({
- resolution: {
- x : 0.0,
- y : 0.0
- }
- });
- }).toThrow();
- });
-});
\ No newline at end of file
diff --git a/Specs/Core/PolygonGeometrySpec.js b/Specs/Core/PolygonGeometrySpec.js
new file mode 100644
index 000000000000..1700875bf85d
--- /dev/null
+++ b/Specs/Core/PolygonGeometrySpec.js
@@ -0,0 +1,197 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/PolygonGeometry',
+ 'Core/Cartesian3',
+ 'Core/Cartographic',
+ 'Core/Ellipsoid',
+ 'Core/Math',
+ 'Core/VertexFormat'
+ ], function(
+ PolygonGeometry,
+ Cartesian3,
+ Cartographic,
+ Ellipsoid,
+ CesiumMath,
+ VertexFormat) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('throws without hierarchy', function() {
+ expect(function() {
+ return new PolygonGeometry();
+ }).toThrow();
+ });
+
+ it('throws without positions', function() {
+ expect(function() {
+ return PolygonGeometry.fromPositions();
+ }).toThrow();
+ });
+
+ it('throws with less than three positions', function() {
+ expect(function() {
+ return PolygonGeometry.fromPositions({ positions : [new Cartesian3()] });
+ }).toThrow();
+ });
+
+ it('throws with polygon hierarchy with less than three positions', function() {
+ var hierarchy = {
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ new Cartographic()
+ ])
+ };
+
+ expect(function() {
+ return new PolygonGeometry({ polygonHierarchy : hierarchy });
+ }).toThrow();
+ });
+
+ it('throws due to duplicate positions', function() {
+ var ellipsoid = Ellipsoid.UNIT_SPHERE;
+
+ expect(function() {
+ return PolygonGeometry.fromPositions({
+ positions : [
+ ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(0.0, 0.0, 0.0)),
+ ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(0.0, 0.0, 0.0)),
+ ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(0.0, 0.0, 0.0))
+ ],
+ ellipsoid : ellipsoid
+ });
+ }).toThrow();
+ });
+
+ it('throws due to duplicate hierarchy positions', function() {
+ var ellipsoid = Ellipsoid.UNIT_SPHERE;
+ var hierarchy = {
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(1.0, 1.0, 0.0),
+ Cartographic.fromDegrees(1.0, 1.0, 0.0),
+ Cartographic.fromDegrees(1.0, 1.0, 0.0)
+ ]),
+ holes : [{
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(0.0, 0.0, 0.0),
+ Cartographic.fromDegrees(0.0, 0.0, 0.0),
+ Cartographic.fromDegrees(0.0, 0.0, 0.0)
+ ])
+ }]
+ };
+
+ expect(function() {
+ return new PolygonGeometry({
+ polygonHierarchy : hierarchy,
+ ellipsoid : ellipsoid
+ });
+ }).toThrow();
+ });
+
+ it('computes positions', function() {
+ var p = PolygonGeometry.fromPositions({
+ vertexformat : VertexFormat.POSITION_ONLY,
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-50.0, -50.0, 0.0),
+ Cartographic.fromDegrees(50.0, -50.0, 0.0),
+ Cartographic.fromDegrees(50.0, 50.0, 0.0),
+ Cartographic.fromDegrees(-50.0, 50.0, 0.0)
+ ]),
+ granularity : CesiumMath.PI_OVER_THREE
+ });
+
+ expect(p.attributes.position.values.length).toEqual(3 * 11);
+ expect(p.indices.length).toEqual(3 * 14);
+ });
+
+ it('computes all attributes', function() {
+ var p = PolygonGeometry.fromPositions({
+ vertexFormat : VertexFormat.ALL,
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-50.0, -50.0, 0.0),
+ Cartographic.fromDegrees(50.0, -50.0, 0.0),
+ Cartographic.fromDegrees(50.0, 50.0, 0.0),
+ Cartographic.fromDegrees(-50.0, 50.0, 0.0)
+ ]),
+ granularity : CesiumMath.PI_OVER_THREE
+ });
+
+ expect(p.attributes.position.values.length).toEqual(3 * 11);
+ expect(p.attributes.st.values.length).toEqual(2 * 11);
+ expect(p.attributes.normal.values.length).toEqual(3 * 11);
+ expect(p.attributes.tangent.values.length).toEqual(3 * 11);
+ expect(p.attributes.binormal.values.length).toEqual(3 * 11);
+ expect(p.indices.length).toEqual(3 * 14);
+ });
+
+ it('creates a polygon from hierarchy', function() {
+ var hierarchy = {
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-124.0, 35.0, 0.0),
+ Cartographic.fromDegrees(-110.0, 35.0, 0.0),
+ Cartographic.fromDegrees(-110.0, 40.0, 0.0),
+ Cartographic.fromDegrees(-124.0, 40.0, 0.0)
+ ]),
+ holes : [{
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-122.0, 36.0, 0.0),
+ Cartographic.fromDegrees(-122.0, 39.0, 0.0),
+ Cartographic.fromDegrees(-112.0, 39.0, 0.0),
+ Cartographic.fromDegrees(-112.0, 36.0, 0.0)
+ ]),
+ holes : [{
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-120.0, 36.5, 0.0),
+ Cartographic.fromDegrees(-114.0, 36.5, 0.0),
+ Cartographic.fromDegrees(-114.0, 38.5, 0.0),
+ Cartographic.fromDegrees(-120.0, 38.5, 0.0)
+ ])
+ }]
+ }]
+ };
+
+ var p = new PolygonGeometry({
+ vertexformat : VertexFormat.POSITION_ONLY,
+ polygonHierarchy : hierarchy,
+ granularity : CesiumMath.PI_OVER_THREE
+ });
+
+ expect(p.attributes.position.values.length).toEqual(3 * 14);
+ expect(p.indices.length).toEqual(3 * 10);
+ });
+
+ it('creates a polygon from clockwise hierarchy', function() {
+ var hierarchy = {
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-124.0, 35.0, 0.0),
+ Cartographic.fromDegrees(-124.0, 40.0, 0.0),
+ Cartographic.fromDegrees(-110.0, 40.0, 0.0),
+ Cartographic.fromDegrees(-110.0, 35.0, 0.0)
+ ]),
+ holes : [{
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-122.0, 36.0, 0.0),
+ Cartographic.fromDegrees(-112.0, 36.0, 0.0),
+ Cartographic.fromDegrees(-112.0, 39.0, 0.0),
+ Cartographic.fromDegrees(-122.0, 39.0, 0.0)
+ ]),
+ holes : [{
+ positions : Ellipsoid.WGS84.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-120.0, 36.5, 0.0),
+ Cartographic.fromDegrees(-120.0, 38.5, 0.0),
+ Cartographic.fromDegrees(-114.0, 38.5, 0.0),
+ Cartographic.fromDegrees(-114.0, 36.5, 0.0)
+ ])
+ }]
+ }]
+ };
+
+ var p = new PolygonGeometry({
+ vertexformat : VertexFormat.POSITION_ONLY,
+ polygonHierarchy : hierarchy,
+ granularity : CesiumMath.PI_OVER_THREE
+ });
+
+ expect(p.attributes.position.values.length).toEqual(3 * 14);
+ expect(p.indices.length).toEqual(3 * 10);
+ });
+
+}, 'WebGL');
diff --git a/Specs/Core/PolygonPipelineSpec.js b/Specs/Core/PolygonPipelineSpec.js
index a8f975cca16b..c40aafd4bc8d 100644
--- a/Specs/Core/PolygonPipelineSpec.js
+++ b/Specs/Core/PolygonPipelineSpec.js
@@ -18,8 +18,8 @@ defineSuite([
"use strict";
/*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
- it('cleanUp removes duplicate points', function() {
- var positions = PolygonPipeline.cleanUp([
+ it('removeDuplicates removes duplicate points', function() {
+ var positions = PolygonPipeline.removeDuplicates([
new Cartesian3(1.0, 1.0, 1.0),
new Cartesian3(2.0, 2.0, 2.0),
new Cartesian3(2.0, 2.0, 2.0),
@@ -33,8 +33,8 @@ defineSuite([
]);
});
- it('cleanUp removes duplicate first and last points', function() {
- var positions = PolygonPipeline.cleanUp([
+ it('removeDuplicates removes duplicate first and last points', function() {
+ var positions = PolygonPipeline.removeDuplicates([
new Cartesian3(1.0, 1.0, 1.0),
new Cartesian3(2.0, 2.0, 2.0),
new Cartesian3(3.0, 3.0, 3.0),
@@ -48,15 +48,15 @@ defineSuite([
]);
});
- it('cleanUp throws without positions', function() {
+ it('removeDuplicates throws without positions', function() {
expect(function() {
- PolygonPipeline.cleanUp();
+ PolygonPipeline.removeDuplicates();
}).toThrow();
});
- it('cleanUp throws without three positions', function() {
+ it('removeDuplicates throws without three positions', function() {
expect(function() {
- PolygonPipeline.cleanUp([Cartesian3.ZERO, Cartesian3.ZERO]);
+ PolygonPipeline.removeDuplicates([Cartesian3.ZERO, Cartesian3.ZERO]);
}).toThrow();
});
@@ -175,142 +175,6 @@ defineSuite([
///////////////////////////////////////////////////////////////////////
- it('wrapLongitude subdivides triangle it crosses the international date line, p0 behind', function() {
- var positions = [new Cartesian3(-1.0, -1.0, 0.0), new Cartesian3(-1.0, 1.0, 2.0), new Cartesian3(-1.0, 2.0, 2.0)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([0, 3, 4, 1, 2, 6, 1, 6, 5]);
- expect(positions[0].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1.0, 1.0, 2.0))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-1.0, 2.0, 2.0))).toEqual(true);
- expect(positions.length).toEqual(7);
- });
-
- it('wrapLongitude subdivides triangle it crosses the international date line, p1 behind', function() {
- var positions = [new Cartesian3(-1.0, 1.0, 2.0), new Cartesian3(-1.0, -1.0, 0.0), new Cartesian3(-1.0, 2.0, 2.0)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([1, 3, 4, 2, 0, 6, 2, 6, 5]);
- expect(positions[0].equals(new Cartesian3(-1.0, 1.0, 2.0))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-1.0, 2.0, 2.0))).toEqual(true);
- expect(positions.length).toEqual(7);
- });
-
- it('wrapLongitude subdivides triangle it crosses the international date line, p2 behind', function() {
- var positions = [new Cartesian3(-1.0, 1.0, 2.0), new Cartesian3(-1.0, 2.0, 2.0), new Cartesian3(-1.0, -1.0, 0.0)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([2, 3, 4, 0, 1, 6, 0, 6, 5]);
- expect(positions[0].equals(new Cartesian3(-1.0, 1.0, 2.0))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1.0, 2.0, 2.0))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions.length).toEqual(7);
- });
-
- it('wrapLongitude subdivides triangle it crosses the international date line, p0 ahead', function() {
- var positions = [new Cartesian3(-1.0, 2.0, 0.0), new Cartesian3(-1.0, -1.0, 0.0), new Cartesian3(-1.0, -1.0, 0.0)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([1, 2, 4, 1, 4, 3, 0, 5, 6]);
- expect(positions[0].equals(new Cartesian3(-1.0, 2.0, 0.0))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions.length).toEqual(7);
- });
-
- it('wrapLongitude subdivides triangle it crosses the international date line, p1 ahead', function() {
- var positions = [new Cartesian3(-1.0, -1.0, 0.0), new Cartesian3(-1.0, 2.0, 0.0), new Cartesian3(-1.0, -1.0, 0.0)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([2, 0, 4, 2, 4, 3, 1, 5, 6]);
- expect(positions[0].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1.0, 2.0, 0.0))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions.length).toEqual(7);
- });
-
- it('wrapLongitude subdivides triangle it crosses the international date line, p2 ahead', function() {
- var positions = [new Cartesian3(-1.0, -1.0, 0.0), new Cartesian3(-1.0, -1.0, 0.0), new Cartesian3(-1.0, 2.0, 0.0)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([0, 1, 4, 0, 4, 3, 2, 5, 6]);
- expect(positions[0].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1.0, -1.0, 0.0))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-1.0, 2.0, 0.0))).toEqual(true);
- expect(positions.length).toEqual(7);
- });
-
- it('wrapLongitude returns offsets triangle that touches the international date line', function() {
- var positions = [new Cartesian3(-1, 0, 1), new Cartesian3(-1, CesiumMath.EPSILON14, 2), new Cartesian3(-2, 2, 2)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([0, 1, 2]);
- expect(positions[0].equals(new Cartesian3(-1, CesiumMath.EPSILON11, 1))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1, CesiumMath.EPSILON11, 2))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-2, 2, 2))).toEqual(true);
- expect(positions.length).toEqual(3);
- });
-
- it('wrapLongitude returns the same points if the triangle doesn\'t cross the international date line, behind', function() {
- var positions = [new Cartesian3(-1, -1, 1), new Cartesian3(-1, -2, 1), new Cartesian3(-1, -2, 2)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([0, 1, 2]);
- expect(positions[0].equals(new Cartesian3(-1, -1, 1))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1, -2, 1))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-1, -2, 2))).toEqual(true);
- expect(positions.length).toEqual(3);
- });
-
- it('wrapLongitude returns the same points if the triangle doesn\'t cross the international date line, ahead', function() {
- var positions = [new Cartesian3(-1, 1, 1), new Cartesian3(-1, 2, 1), new Cartesian3(-1, 2, 2)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([0, 1, 2]);
- expect(positions[0].equals(new Cartesian3(-1, 1, 1))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(-1, 2, 1))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(-1, 2, 2))).toEqual(true);
- expect(positions.length).toEqual(3);
- });
-
- it('wrapLongitude returns the same points if the triangle doesn\'t cross the international date line, positive x', function() {
- var positions = [new Cartesian3(1, 1, 1), new Cartesian3(1, 2, 1), new Cartesian3(1, 2, 2)];
- var indices = [0, 1, 2];
- indices = PolygonPipeline.wrapLongitude(positions, indices);
- expect(indices).toEqual([0, 1, 2]);
- expect(positions[0].equals(new Cartesian3(1, 1, 1))).toEqual(true);
- expect(positions[1].equals(new Cartesian3(1, 2, 1))).toEqual(true);
- expect(positions[2].equals(new Cartesian3(1, 2, 2))).toEqual(true);
- expect(positions.length).toEqual(3);
- });
-
- it('wrapLongitude throws with count indices not multiple of three', function() {
- expect(function() {
- PolygonPipeline.wrapLongitude([Cartesian3.ZERO, Cartesian3.ZERO], [0, 0, 0, 0]);
- }).toThrow();
- });
-
- it('wrapLongitude throws with < 3 indices', function() {
- expect(function() {
- PolygonPipeline.wrapLongitude([Cartesian3.ZERO, Cartesian3.ZERO], []);
- }).toThrow();
- });
-
- it('wrapLongitude throws without positions', function() {
- expect(function() {
- PolygonPipeline.wrapLongitude();
- }).toThrow();
- });
-
- it('wrapLongitude throws without indices', function() {
- expect(function() {
- PolygonPipeline.wrapLongitude([Cartesian3.ZERO, Cartesian3.ZERO]);
- }).toThrow();
- });
-
- ///////////////////////////////////////////////////////////////////////
-
it('computeSubdivision throws without positions', function() {
expect(function() {
PolygonPipeline.computeSubdivision();
@@ -360,9 +224,9 @@ defineSuite([
expect(subdivision.attributes.position.values[7]).toEqual(0.0);
expect(subdivision.attributes.position.values[8]).toEqual(0.0);
- expect(subdivision.indexLists[0].values[0]).toEqual(0);
- expect(subdivision.indexLists[0].values[1]).toEqual(1);
- expect(subdivision.indexLists[0].values[2]).toEqual(2);
+ expect(subdivision.indices[0]).toEqual(0);
+ expect(subdivision.indices[1]).toEqual(1);
+ expect(subdivision.indices[2]).toEqual(2);
});
it('eliminateHoles throws an exception without an outerRing', function() {
diff --git a/Specs/Core/PolylinePipelineSpec.js b/Specs/Core/PolylinePipelineSpec.js
index 7e45362b702b..73d4d56a212a 100644
--- a/Specs/Core/PolylinePipelineSpec.js
+++ b/Specs/Core/PolylinePipelineSpec.js
@@ -49,4 +49,33 @@ defineSuite([
expect(segments.lengths[0]).toEqual(2);
expect(segments.lengths[1]).toEqual(2);
});
+
+ it('removeDuplicates to return one positions', function() {
+ var positions = [Cartesian3.ZERO];
+ var nonDuplicatePositions = PolylinePipeline.removeDuplicates(positions);
+ expect(nonDuplicatePositions).not.toBe(positions);
+ expect(nonDuplicatePositions).toEqual(positions);
+ });
+
+ it('removeDuplicates to remove duplicates', function() {
+ var positions = [
+ new Cartesian3(1.0, 1.0, 1.0),
+ new Cartesian3(1.0, 1.0, 1.0),
+ new Cartesian3(2.0, 2.0, 2.0),
+ new Cartesian3(3.0, 3.0, 3.0),
+ new Cartesian3(3.0, 3.0, 3.0)];
+ var expectedPositions = [
+ new Cartesian3(1.0, 1.0, 1.0),
+ new Cartesian3(2.0, 2.0, 2.0),
+ new Cartesian3(3.0, 3.0, 3.0)];
+ var nonDuplicatePositions = PolylinePipeline.removeDuplicates(positions);
+ expect(nonDuplicatePositions).not.toBe(expectedPositions);
+ expect(nonDuplicatePositions).toEqual(expectedPositions);
+ });
+
+ it('removeDuplicates throws without positions', function() {
+ expect(function() {
+ PolylinePipeline.removeDuplicates();
+ }).toThrow();
+ });
});
\ No newline at end of file
diff --git a/Specs/Core/ShowGeometryInstanceAttributeSpec.js b/Specs/Core/ShowGeometryInstanceAttributeSpec.js
new file mode 100644
index 000000000000..89f9e813a19a
--- /dev/null
+++ b/Specs/Core/ShowGeometryInstanceAttributeSpec.js
@@ -0,0 +1,30 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/ShowGeometryInstanceAttribute',
+ 'Core/ComponentDatatype'
+ ], function(
+ ShowGeometryInstanceAttribute,
+ ComponentDatatype) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor', function() {
+ var attribute = new ShowGeometryInstanceAttribute(false);
+ expect(attribute.componentDatatype).toEqual(ComponentDatatype.UNSIGNED_BYTE);
+ expect(attribute.componentsPerAttribute).toEqual(1);
+ expect(attribute.normalize).toEqual(true);
+
+ expect(attribute.value).toEqual(new Uint8Array([false]));
+ });
+
+ it('toValue', function() {
+ expect(ShowGeometryInstanceAttribute.toValue(true)).toEqual(new Uint8Array([true]));
+ });
+
+ it('toValue throws without a color', function() {
+ expect(function() {
+ ShowGeometryInstanceAttribute.toValue();
+ }).toThrow();
+ });
+
+});
diff --git a/Specs/Core/SimplePolylineGeometrySpec.js b/Specs/Core/SimplePolylineGeometrySpec.js
new file mode 100644
index 000000000000..a59d84d8a423
--- /dev/null
+++ b/Specs/Core/SimplePolylineGeometrySpec.js
@@ -0,0 +1,40 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/SimplePolylineGeometry',
+ 'Core/Cartesian3',
+ 'Core/BoundingSphere',
+ 'Core/PrimitiveType'
+ ], function(
+ SimplePolylineGeometry,
+ Cartesian3,
+ BoundingSphere,
+ PrimitiveType) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor throws with no positions', function() {
+ expect(function() {
+ return new SimplePolylineGeometry();
+ }).toThrow();
+ });
+
+ it('constructor throws with less than two positions', function() {
+ expect(function() {
+ return new SimplePolylineGeometry({
+ positions : [Cartesian3.ZERO]
+ });
+ }).toThrow();
+ });
+
+ it('constructor computes all vertex attributes', function() {
+ var positions = [new Cartesian3(), new Cartesian3(1.0, 0.0, 0.0), new Cartesian3(2.0, 0.0, 0.0)];
+ var line = new SimplePolylineGeometry({
+ positions : positions
+ });
+
+ expect(line.attributes.position.values).toEqual([0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0]);
+ expect(line.indices).toEqual([0, 1, 1, 2]);
+ expect(line.primitiveType).toEqual(PrimitiveType.LINES);
+ expect(line.boundingSphere).toEqual(BoundingSphere.fromPoints(positions));
+ });
+});
\ No newline at end of file
diff --git a/Specs/Core/WallGeometrySpec.js b/Specs/Core/WallGeometrySpec.js
new file mode 100644
index 000000000000..08b8e16788af
--- /dev/null
+++ b/Specs/Core/WallGeometrySpec.js
@@ -0,0 +1,156 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/WallGeometry',
+ 'Core/Cartesian3',
+ 'Core/Cartographic',
+ 'Core/Ellipsoid',
+ 'Core/Math',
+ 'Core/VertexFormat'
+ ], function(
+ WallGeometry,
+ Cartesian3,
+ Cartographic,
+ Ellipsoid,
+ CesiumMath,
+ VertexFormat) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var ellipsoid = Ellipsoid.WGS84;
+
+ it('throws with no positions', function() {
+ expect(function() {
+ return new WallGeometry();
+ }).toThrow();
+ });
+
+ it('throws when positions and minimumHeights length do not match', function() {
+ expect(function() {
+ return new WallGeometry({
+ positions : new Array(2),
+ minimumHeights : new Array(3)
+ });
+ }).toThrow();
+ });
+
+ it('throws when positions and maximumHeights length do not match', function() {
+ expect(function() {
+ return new WallGeometry({
+ positions : new Array(2),
+ maximumHeights : new Array(3)
+ });
+ }).toThrow();
+ });
+
+ it('creates positions relative to ellipsoid', function() {
+ var coords = [
+ Cartographic.fromDegrees(49.0, 18.0, 1000.0),
+ Cartographic.fromDegrees(50.0, 18.0, 1000.0)
+ ];
+
+ var w = new WallGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ positions : ellipsoid.cartographicArrayToCartesianArray(coords)
+ });
+
+ var positions = w.attributes.position.values;
+ expect(positions.length).toEqual(2 * 2 * 3);
+ expect(w.indices.length).toEqual(2 * 3);
+
+ var cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 0));
+ expect(cartographic.height).toEqualEpsilon(0.0, CesiumMath.EPSILON9);
+
+ cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 3));
+ expect(cartographic.height).toEqualEpsilon(1000.0, CesiumMath.EPSILON9);
+ });
+
+ it('creates positions with minimum and maximum heights', function() {
+ var coords = [
+ Cartographic.fromDegrees(49.0, 18.0, 1000.0),
+ Cartographic.fromDegrees(50.0, 18.0, 1000.0)
+ ];
+
+ var w = new WallGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ positions : ellipsoid.cartographicArrayToCartesianArray(coords),
+ minimumHeights : [1000.0, 2000.0],
+ maximumHeights : [3000.0, 4000.0]
+ });
+
+ var positions = w.attributes.position.values;
+ expect(positions.length).toEqual(2 * 2 * 3);
+ expect(w.indices.length).toEqual(2 * 3);
+
+ var cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 0));
+ expect(cartographic.height).toEqualEpsilon(1000.0, CesiumMath.EPSILON8);
+
+ cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 3));
+ expect(cartographic.height).toEqualEpsilon(3000.0, CesiumMath.EPSILON8);
+
+ cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 6));
+ expect(cartographic.height).toEqualEpsilon(2000.0, CesiumMath.EPSILON8);
+
+ cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 9));
+ expect(cartographic.height).toEqualEpsilon(4000.0, CesiumMath.EPSILON8);
+ });
+
+ it('creates all attributes', function() {
+ var coords = [
+ Cartographic.fromDegrees(49.0, 18.0, 1000.0),
+ Cartographic.fromDegrees(50.0, 18.0, 1000.0),
+ Cartographic.fromDegrees(51.0, 18.0, 1000.0)
+ ];
+
+ var w = new WallGeometry({
+ vertexFormat : VertexFormat.ALL,
+ positions : ellipsoid.cartographicArrayToCartesianArray(coords)
+ });
+
+ expect(w.attributes.position.values.length).toEqual(4 * 2 * 3);
+ expect(w.attributes.normal.values.length).toEqual(4 * 2 * 3);
+ expect(w.attributes.tangent.values.length).toEqual(4 * 2 * 3);
+ expect(w.attributes.binormal.values.length).toEqual(4 * 2 * 3);
+ expect(w.attributes.st.values.length).toEqual(4 * 2 * 2);
+ expect(w.indices.length).toEqual((4 * 2 - 2) * 3);
+ });
+
+ it('fromConstantHeights throws without positions', function() {
+ expect(function() {
+ return WallGeometry.fromConstantHeights();
+ }).toThrow();
+ });
+
+ it('creates positions with constant minimum and maximum heights', function() {
+ var coords = [
+ Cartographic.fromDegrees(49.0, 18.0, 1000.0),
+ Cartographic.fromDegrees(50.0, 18.0, 1000.0)
+ ];
+
+ var min = 1000.0;
+ var max = 2000.0;
+
+ var w = WallGeometry.fromConstantHeights({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ positions : ellipsoid.cartographicArrayToCartesianArray(coords),
+ minimumHeight : min,
+ maximumHeight : max
+ });
+
+ var positions = w.attributes.position.values;
+ expect(positions.length).toEqual(2 * 2 * 3);
+ expect(w.indices.length).toEqual(2 * 3);
+
+ var cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 0));
+ expect(cartographic.height).toEqualEpsilon(min, CesiumMath.EPSILON8);
+
+ cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 3));
+ expect(cartographic.height).toEqualEpsilon(max, CesiumMath.EPSILON8);
+
+ cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 6));
+ expect(cartographic.height).toEqualEpsilon(min, CesiumMath.EPSILON8);
+
+ cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 9));
+ expect(cartographic.height).toEqualEpsilon(max, CesiumMath.EPSILON8);
+ });
+});
+
diff --git a/Specs/Core/barycentricCoordinatesSpec.js b/Specs/Core/barycentricCoordinatesSpec.js
new file mode 100644
index 000000000000..f4977ed6ac07
--- /dev/null
+++ b/Specs/Core/barycentricCoordinatesSpec.js
@@ -0,0 +1,76 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/barycentricCoordinates',
+ 'Core/Cartesian3',
+ 'Core/Math'
+ ], function(
+ barycentricCoordinates,
+ Cartesian3,
+ CesiumMath) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var p0 = new Cartesian3(-1.0, 0.0, 0.0);
+ var p1 = new Cartesian3( 1.0, 0.0, 0.0);
+ var p2 = new Cartesian3( 0.0, 1.0, 1.0);
+
+ it('evaluates to p0', function() {
+ var point = Cartesian3.clone(p0);
+ expect(barycentricCoordinates(point, p0, p1, p2)).toEqual(Cartesian3.UNIT_X);
+ });
+
+ it('evaluates to p1', function() {
+ var point = Cartesian3.clone(p1);
+ expect(barycentricCoordinates(point, p0, p1, p2)).toEqual(Cartesian3.UNIT_Y);
+ });
+
+ it('evaluates to p2', function() {
+ var point = Cartesian3.clone(p2);
+ expect(barycentricCoordinates(point, p0, p1, p2)).toEqual(Cartesian3.UNIT_Z);
+ });
+
+ it('evaluates on the p0-p1 edge', function() {
+ var point = p1.add(p0).multiplyByScalar(0.5);
+ expect(barycentricCoordinates(point, p0, p1, p2)).toEqual(new Cartesian3(0.5, 0.5, 0.0));
+ });
+
+ it('evaluates on the p0-p2 edge', function() {
+ var point = p2.add(p0).multiplyByScalar(0.5);
+ expect(barycentricCoordinates(point, p0, p1, p2)).toEqual(new Cartesian3(0.5, 0.0, 0.5));
+ });
+
+ it('evaluates on the p1-p2 edge', function() {
+ var point = p2.add(p1).multiplyByScalar(0.5);
+ expect(barycentricCoordinates(point, p0, p1, p2)).toEqual(new Cartesian3(0.0, 0.5, 0.5));
+ });
+
+ it('evaluates on the interior', function() {
+ var scalar = 1.0 / 3.0;
+ var point = p0.add(p1).add(p2).multiplyByScalar(scalar);
+ expect(barycentricCoordinates(point, p0, p1, p2)).toEqualEpsilon(new Cartesian3(scalar, scalar, scalar), CesiumMath.EPSILON14);
+ });
+
+ it('throws without point', function() {
+ expect(function() {
+ barycentricCoordinates();
+ }).toThrow();
+ });
+
+ it('throws without p0', function() {
+ expect(function() {
+ barycentricCoordinates(new Cartesian3());
+ }).toThrow();
+ });
+
+ it('throws without p1', function() {
+ expect(function() {
+ barycentricCoordinates(new Cartesian3(), new Cartesian3());
+ }).toThrow();
+ });
+
+ it('throws without p2', function() {
+ expect(function() {
+ barycentricCoordinates(new Cartesian3(), new Cartesian3(), new Cartesian3());
+ }).toThrow();
+ });
+});
\ No newline at end of file
diff --git a/Specs/Core/pointInsideTriangle2DSpec.js b/Specs/Core/pointInsideTriangle2DSpec.js
deleted file mode 100644
index a1ed0823ec4e..000000000000
--- a/Specs/Core/pointInsideTriangle2DSpec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*global defineSuite*/
-defineSuite([
- 'Core/pointInsideTriangle2D',
- 'Core/Cartesian2'
- ], function(
- pointInsideTriangle2D,
- Cartesian2) {
- "use strict";
- /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
-
- it('pointInsideTriangle2D has point inside', function() {
- expect(pointInsideTriangle2D(new Cartesian2(0.25, 0.25), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(true);
- });
-
- it('pointInsideTriangle2D has point outside', function() {
- expect(pointInsideTriangle2D(new Cartesian2(1.0, 1.0), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
- });
-
- it('pointInsideTriangle2D has point outside (2)', function() {
- expect(pointInsideTriangle2D(new Cartesian2(0.5, -0.5), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
- });
-
- it('pointInsideTriangle2D has point outside (3)', function() {
- expect(pointInsideTriangle2D(new Cartesian2(-0.5, 0.5), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
- });
-
- it('pointInsideTriangle2D has point on corner', function() {
- expect(pointInsideTriangle2D(Cartesian2.ZERO, Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
- });
-
- it('pointInsideTriangle2D has point inside on edge', function() {
- expect(pointInsideTriangle2D(new Cartesian2(0.5, 0.0), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
- });
-
- it('throws without point', function() {
- expect(function() {
- pointInsideTriangle2D();
- }).toThrow();
- });
-
- it('throws without p0', function() {
- expect(function() {
- pointInsideTriangle2D(new Cartesian2());
- }).toThrow();
- });
-
- it('throws without p1', function() {
- expect(function() {
- pointInsideTriangle2D(new Cartesian2(), new Cartesian2());
- }).toThrow();
- });
-
- it('throws without p2', function() {
- expect(function() {
- pointInsideTriangle2D(new Cartesian2(), new Cartesian2(), new Cartesian2());
- }).toThrow();
- });
-});
\ No newline at end of file
diff --git a/Specs/Core/pointInsideTriangleSpec.js b/Specs/Core/pointInsideTriangleSpec.js
new file mode 100644
index 000000000000..ec2c97dcc39d
--- /dev/null
+++ b/Specs/Core/pointInsideTriangleSpec.js
@@ -0,0 +1,58 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/pointInsideTriangle',
+ 'Core/Cartesian2'
+ ], function(
+ pointInsideTriangle,
+ Cartesian2) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('pointInsideTriangle has point inside', function() {
+ expect(pointInsideTriangle(new Cartesian2(0.25, 0.25), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(true);
+ });
+
+ it('pointInsideTriangle has point outside', function() {
+ expect(pointInsideTriangle(new Cartesian2(1.0, 1.0), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
+ });
+
+ it('pointInsideTriangle has point outside (2)', function() {
+ expect(pointInsideTriangle(new Cartesian2(0.5, -0.5), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
+ });
+
+ it('pointInsideTriangle has point outside (3)', function() {
+ expect(pointInsideTriangle(new Cartesian2(-0.5, 0.5), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
+ });
+
+ it('pointInsideTriangle has point on corner', function() {
+ expect(pointInsideTriangle(Cartesian2.ZERO, Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
+ });
+
+ it('pointInsideTriangle has point inside on edge', function() {
+ expect(pointInsideTriangle(new Cartesian2(0.5, 0.0), Cartesian2.ZERO, new Cartesian2(1.0, 0.0), new Cartesian2(0.0, 1.0))).toEqual(false);
+ });
+
+ it('throws without point', function() {
+ expect(function() {
+ pointInsideTriangle();
+ }).toThrow();
+ });
+
+ it('throws without p0', function() {
+ expect(function() {
+ pointInsideTriangle(new Cartesian2());
+ }).toThrow();
+ });
+
+ it('throws without p1', function() {
+ expect(function() {
+ pointInsideTriangle(new Cartesian2(), new Cartesian2());
+ }).toThrow();
+ });
+
+ it('throws without p2', function() {
+ expect(function() {
+ pointInsideTriangle(new Cartesian2(), new Cartesian2(), new Cartesian2());
+ }).toThrow();
+ });
+});
\ No newline at end of file
diff --git a/Specs/Renderer/BuiltinFunctionsSpec.js b/Specs/Renderer/BuiltinFunctionsSpec.js
index 8709e55fa46b..54f7ba633ad6 100644
--- a/Specs/Renderer/BuiltinFunctionsSpec.js
+++ b/Specs/Renderer/BuiltinFunctionsSpec.js
@@ -144,6 +144,19 @@ defineSuite([
verifyDraw(fs);
});
+ it('has czm_tangentToEyeSpaceMatrix', function() {
+ var fs =
+ 'void main() { ' +
+ ' vec3 tangent = vec3(1.0, 0.0, 0.0); ' +
+ ' vec3 binormal = vec3(0.0, 1.0, 0.0); ' +
+ ' vec3 normal = vec3(0.0, 0.0, 1.0); ' +
+ ' mat3 expected = mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0); ' +
+ ' mat3 actual = czm_tangentToEyeSpaceMatrix(normal, tangent, binormal); ' +
+ ' gl_FragColor = vec4(actual == expected); ' +
+ '}';
+ verifyDraw(fs);
+ });
+
it('has czm_translateRelativeToEye', function() {
var camera = createCamera(context, new Cartesian3(1.0, 2.0, 3.0));
context.getUniformState().update(createFrameState(camera));
@@ -164,8 +177,8 @@ defineSuite([
'uniform vec3 u_high;' +
'uniform vec3 u_low;' +
'void main() { ' +
- ' vec3 p = czm_translateRelativeToEye(u_high, u_low);' +
- ' gl_FragColor = vec4(p == vec3(5.0, 3.0, 1.0)); ' +
+ ' vec4 p = czm_translateRelativeToEye(u_high, u_low);' +
+ ' gl_FragColor = vec4(p == vec4(5.0, 3.0, 1.0, 1.0)); ' +
'}';
verifyDraw(fs, uniformMap);
diff --git a/Specs/Renderer/ContextSpec.js b/Specs/Renderer/ContextSpec.js
index 953463fb3e0d..ecbebf683ce2 100644
--- a/Specs/Renderer/ContextSpec.js
+++ b/Specs/Renderer/ContextSpec.js
@@ -2,12 +2,16 @@
defineSuite([
'Renderer/Context',
'Core/Color',
+ 'Core/IndexDatatype',
+ 'Renderer/BufferUsage',
'Specs/createContext',
'Specs/destroyContext',
'Specs/renderFragment'
], function(
Context,
Color,
+ IndexDatatype,
+ BufferUsage,
createContext,
destroyContext,
renderFragment) {
@@ -170,6 +174,18 @@ defineSuite([
}
});
+ it('gets the element index uint extension', function() {
+ if (context.getElementIndexUint()) {
+ var buffer = context.createIndexBuffer(6, BufferUsage.STREAM_DRAW, IndexDatatype.UNSIGNED_INT);
+ expect(buffer).toBeDefined();
+ buffer.destroy();
+ } else {
+ expect(function() {
+ context.createIndexBuffer(6, BufferUsage.STREAM_DRAW, IndexDatatype.UNSIGNED_INT);
+ }).toThrow();
+ }
+ });
+
it('gets the depth texture extension', function() {
expect(context.getDepthTexture()).toBeDefined();
});
diff --git a/Specs/Renderer/VertexArrayFactorySpec.js b/Specs/Renderer/VertexArrayFactorySpec.js
index 2f6bc84a293a..c98a6122dbec 100644
--- a/Specs/Renderer/VertexArrayFactorySpec.js
+++ b/Specs/Renderer/VertexArrayFactorySpec.js
@@ -3,7 +3,9 @@ defineSuite([
'Specs/createContext',
'Specs/destroyContext',
'Core/ComponentDatatype',
- 'Core/MeshFilters',
+ 'Core/Geometry',
+ 'Core/GeometryAttribute',
+ 'Core/GeometryPipeline',
'Core/PrimitiveType',
'Core/IndexDatatype',
'Renderer/BufferUsage',
@@ -13,7 +15,9 @@ defineSuite([
createContext,
destroyContext,
ComponentDatatype,
- MeshFilters,
+ Geometry,
+ GeometryAttribute,
+ GeometryPipeline,
PrimitiveType,
IndexDatatype,
BufferUsage,
@@ -40,13 +44,13 @@ defineSuite([
});
it('creates with no arguments', function() {
- va = context.createVertexArrayFromMesh();
+ va = context.createVertexArrayFromGeometry();
expect(va.getNumberOfAttributes()).toEqual(0);
expect(va.getIndexBuffer()).not.toBeDefined();
});
- it('creates with no mesh', function() {
- va = context.createVertexArrayFromMesh({
+ it('creates with no geometry', function() {
+ va = context.createVertexArrayFromGeometry({
vertexLayout : VertexLayout.INTERLEAVED
});
expect(va.getNumberOfAttributes()).toEqual(0);
@@ -54,25 +58,26 @@ defineSuite([
});
it('creates a single-attribute vertex (non-interleaved)', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
- attributeIndices : MeshFilters.createAttributeIndices(mesh)
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
+ attributeIndices : GeometryPipeline.createAttributeIndices(geometry)
});
expect(va.getNumberOfAttributes()).toEqual(1);
expect(va.getIndexBuffer()).not.toBeDefined();
- var position = mesh.attributes.position;
+ var position = geometry.attributes.position;
expect(va.getAttribute(0).index).toEqual(0);
expect(va.getAttribute(0).componentDatatype).toEqual(position.componentDatatype);
expect(va.getAttribute(0).componentsPerAttribute).toEqual(position.componentsPerAttribute);
@@ -83,19 +88,20 @@ defineSuite([
});
it('creates a single-attribute vertex (interleaved)', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
- attributeIndices : MeshFilters.createAttributeIndices(mesh),
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
+ attributeIndices : GeometryPipeline.createAttributeIndices(geometry),
vertexLayout : VertexLayout.INTERLEAVED,
bufferUsage : BufferUsage.STATIC_DRAW
});
@@ -103,7 +109,7 @@ defineSuite([
expect(va.getNumberOfAttributes()).toEqual(1);
expect(va.getIndexBuffer()).not.toBeDefined();
- var position = mesh.attributes.position;
+ var position = geometry.attributes.position;
expect(va.getAttribute(0).index).toEqual(0);
expect(va.getAttribute(0).componentDatatype).toEqual(position.componentDatatype);
expect(va.getAttribute(0).componentsPerAttribute).toEqual(position.componentsPerAttribute);
@@ -114,37 +120,38 @@ defineSuite([
});
it('creates a homogeneous multiple-attribute vertex (non-interleaved)', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ customPosition : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [0.0, 0.0, 0.0, 2.0, 2.0, 2.0]
- },
- normal : {
+ }),
+ customNormal : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [1.0, 1.0, 1.0, 3.0, 3.0, 3.0]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
- attributeIndices : MeshFilters.createAttributeIndices(mesh)
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
+ attributeIndices : GeometryPipeline.createAttributeIndices(geometry)
});
expect(va.getNumberOfAttributes()).toEqual(2);
expect(va.getIndexBuffer()).not.toBeDefined();
- var position = mesh.attributes.position;
+ var position = geometry.attributes.customPosition;
expect(va.getAttribute(0).index).toEqual(0);
expect(va.getAttribute(0).componentDatatype).toEqual(position.componentDatatype);
expect(va.getAttribute(0).componentsPerAttribute).toEqual(position.componentsPerAttribute);
expect(va.getAttribute(0).offsetInBytes).toEqual(0);
expect(va.getAttribute(0).strideInBytes).toEqual(0); // Tightly packed
- var normal = mesh.attributes.position;
+ var normal = geometry.attributes.customNormal;
expect(va.getAttribute(1).index).toEqual(1);
expect(va.getAttribute(1).componentDatatype).toEqual(normal.componentDatatype);
expect(va.getAttribute(1).componentsPerAttribute).toEqual(normal.componentsPerAttribute);
@@ -155,32 +162,33 @@ defineSuite([
});
it('creates a homogeneous multiple-attribute vertex (interleaved)', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ customPosition : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [0.0, 0.0, 0.0, 2.0, 2.0, 2.0]
- },
- normal : {
+ }),
+ customNormal : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [1.0, 1.0, 1.0, 3.0, 3.0, 3.0]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
- attributeIndices : MeshFilters.createAttributeIndices(mesh),
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
+ attributeIndices : GeometryPipeline.createAttributeIndices(geometry),
vertexLayout : VertexLayout.INTERLEAVED
});
expect(va.getNumberOfAttributes()).toEqual(2);
expect(va.getIndexBuffer()).not.toBeDefined();
- var position = mesh.attributes.position;
- var normal = mesh.attributes.position;
+ var position = geometry.attributes.customPosition;
+ var normal = geometry.attributes.customNormal;
var expectedStride = position.componentDatatype.sizeInBytes * position.componentsPerAttribute + normal.componentDatatype.sizeInBytes * normal.componentsPerAttribute;
expect(va.getAttribute(0).index).toEqual(0);
@@ -199,32 +207,33 @@ defineSuite([
});
it('creates a heterogeneous multiple-attribute vertex (interleaved)', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [0.0, 0.0, 0.0, 2.0, 2.0, 2.0]
- },
- colors : {
+ }),
+ colors : new GeometryAttribute({
componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute : 4,
values : [1, 1, 1, 1, 2, 2, 2, 2]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
- attributeIndices : MeshFilters.createAttributeIndices(mesh),
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
+ attributeIndices : GeometryPipeline.createAttributeIndices(geometry),
vertexLayout : VertexLayout.INTERLEAVED
});
expect(va.getNumberOfAttributes()).toEqual(2);
expect(va.getIndexBuffer()).not.toBeDefined();
- var position = mesh.attributes.position;
- var colors = mesh.attributes.colors;
+ var position = geometry.attributes.position;
+ var colors = geometry.attributes.colors;
var expectedStride = position.componentDatatype.sizeInBytes * position.componentsPerAttribute + colors.componentDatatype.sizeInBytes * colors.componentsPerAttribute;
expect(va.getAttribute(0).index).toEqual(0);
@@ -243,29 +252,30 @@ defineSuite([
});
it('sorts interleaved attributes from large to small components', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- bytes : {
+ bytes : new GeometryAttribute({
componentDatatype : ComponentDatatype.BYTE,
componentsPerAttribute : 1,
values : [0]
- },
- shorts : {
+ }),
+ shorts : new GeometryAttribute({
componentDatatype : ComponentDatatype.SHORT,
componentsPerAttribute : 1,
values : [1]
- },
- floats : {
+ }),
+ floats : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 1,
values : [2]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var attributeIndices = MeshFilters.createAttributeIndices(mesh);
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
+ var attributeIndices = GeometryPipeline.createAttributeIndices(geometry);
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : attributeIndices,
vertexLayout : VertexLayout.INTERLEAVED
});
@@ -307,25 +317,26 @@ defineSuite([
});
it('sorts interleaved attributes from large to small components (2)', function() {
- // TODO: Color should be normalized
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- color : {
+ color : new GeometryAttribute({
componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute : 4,
+ normalize : true,
values : [255, 0, 0, 255, 0, 255, 0, 255]
- },
- position : {
+ }),
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var attributeIndices = MeshFilters.createAttributeIndices(mesh);
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
+ var attributeIndices = GeometryPipeline.createAttributeIndices(geometry);
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : attributeIndices,
vertexLayout : VertexLayout.INTERLEAVED
});
@@ -372,34 +383,35 @@ defineSuite([
});
it('sorts interleaved attributes from large to small components (3)', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- unsignedByteAttribute : {
+ unsignedByteAttribute : new GeometryAttribute({
componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute : 2,
values : [1, 2]
- },
- unsignedShortAttribute : {
+ }),
+ unsignedShortAttribute : new GeometryAttribute({
componentDatatype : ComponentDatatype.UNSIGNED_SHORT,
componentsPerAttribute : 1,
values : [3]
- },
- byteAttribute : {
+ }),
+ byteAttribute : new GeometryAttribute({
componentDatatype : ComponentDatatype.BYTE,
componentsPerAttribute : 1,
values : [4]
- },
- shortAttribute : {
+ }),
+ shortAttribute : new GeometryAttribute({
componentDatatype : ComponentDatatype.SHORT,
componentsPerAttribute : 1,
values : [5]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var attributeIndices = MeshFilters.createAttributeIndices(mesh);
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
+ var attributeIndices = GeometryPipeline.createAttributeIndices(geometry);
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : attributeIndices,
vertexLayout : VertexLayout.INTERLEAVED
});
@@ -438,36 +450,36 @@ defineSuite([
});
it('creates a custom interleaved vertex', function() {
- // TODO: Color should be normalized
-
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
- },
- color : {
+ }),
+ color : new GeometryAttribute({
componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute : 3,
+ normalize : true,
values : [255, 0, 0, 0, 255, 0]
- },
- normal : {
+ }),
+ normal : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 3,
values : [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
- },
- temperature : {
+ }),
+ temperature : new GeometryAttribute({
componentDatatype : ComponentDatatype.UNSIGNED_SHORT,
componentsPerAttribute : 1,
values : [75, 100]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
- var attributeIndices = MeshFilters.createAttributeIndices(mesh);
- var va = context.createVertexArrayFromMesh({
- mesh : mesh,
+ var attributeIndices = GeometryPipeline.createAttributeIndices(geometry);
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : attributeIndices,
vertexLayout : VertexLayout.INTERLEAVED
});
@@ -540,85 +552,68 @@ defineSuite([
});
it('creates an index buffer', function() {
- var mesh = {
- indexLists : [{
- primitiveType : PrimitiveType.POINTS,
- values : [0]
- }]
- };
-
- var va = context.createVertexArrayFromMesh({
- mesh : mesh
+ var geometry = new Geometry({
+ attributes : {},
+ indices : [0],
+ primitiveType : PrimitiveType.POINTS
+ });
+
+ var va = context.createVertexArrayFromGeometry({
+ geometry : geometry
});
expect(va.getNumberOfAttributes()).toEqual(0);
expect(va.getIndexBuffer()).toBeDefined();
expect(va.getIndexBuffer().getUsage()).toEqual(BufferUsage.DYNAMIC_DRAW); // Default
expect(va.getIndexBuffer().getIndexDatatype()).toEqual(IndexDatatype.UNSIGNED_SHORT);
- expect(va.getIndexBuffer().getNumberOfIndices()).toEqual(mesh.indexLists[0].values.length);
- });
-
- it('throws with multiple index lists', function() {
- var mesh = {
- indexLists : [{
- primitiveType : PrimitiveType.POINTS,
- values : [0]
- }, {
- primitiveType : PrimitiveType.POINTS,
- values : [1]
- }]
- };
-
- expect(function() {
- return context.createVertexArrayFromMesh({
- mesh : mesh
- });
- }).toThrow();
+ expect(va.getIndexBuffer().getNumberOfIndices()).toEqual(geometry.indices.length);
});
it('throws with different number of interleaved attributes', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 1,
values : [0.0]
- },
- normal : {
+ }),
+ normal : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 1,
values : [1.0, 2.0]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
expect(function() {
- return context.createVertexArrayFromMesh({
- mesh : mesh,
+ return context.createVertexArrayFromGeometry({
+ geometry : geometry,
vertexLayout : VertexLayout.INTERLEAVED
});
}).toThrow();
});
it('throws with duplicate indices', function() {
- var mesh = {
+ var geometry = new Geometry({
attributes : {
- position : {
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 1,
values : [0.0]
- },
- normal : {
+ }),
+ normal : new GeometryAttribute({
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : 1,
values : [1.0]
- }
- }
- };
+ })
+ },
+ primitiveType : PrimitiveType.POINTS
+ });
expect(function() {
- return context.createVertexArrayFromMesh({
- mesh : mesh,
+ return context.createVertexArrayFromGeometry({
+ geometry : geometry,
attributeIndices : {
position : 0,
normal : 0
diff --git a/Specs/Scene/AppearanceSpec.js b/Specs/Scene/AppearanceSpec.js
new file mode 100644
index 000000000000..0e696bedee6e
--- /dev/null
+++ b/Specs/Scene/AppearanceSpec.js
@@ -0,0 +1,91 @@
+/*global defineSuite*/
+defineSuite([
+ 'Scene/Appearance',
+ 'Scene/Material',
+ 'Renderer/BlendingState',
+ 'Renderer/CullFace'
+ ], function(
+ Appearance,
+ Material,
+ BlendingState,
+ CullFace) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('constructor', function() {
+ var material = Material.fromType(undefined, 'Color');
+ var vs =
+ 'attribute vec3 position3DHigh;\n' +
+ 'attribute vec3 position3DLow;\n' +
+ 'attribute vec4 color;\n' +
+ 'varying vec4 v_color;\n' +
+ 'void main() {\n' +
+ ' gl_Position = czm_modelViewProjectionRelativeToEye * czm_computePosition();\n' +
+ ' v_color = color;\n' +
+ '}\n';
+ var fs =
+ 'varying vec4 v_color;\n' +
+ 'void main() {\n' +
+ ' gl_FragColor = v_color;\n' +
+ '}\n';
+ var renderState = {
+ depthTest : {
+ enabled : true
+ }
+ };
+ var appearance = new Appearance({
+ material : material,
+ vertexShaderSource : vs,
+ fragmentShaderSource : fs,
+ renderState : renderState
+ });
+
+ expect(appearance.material).toBe(material);
+ expect(appearance.vertexShaderSource).toBe(vs);
+ expect(appearance.fragmentShaderSource).toBe(fs);
+ expect(appearance.renderState).toBe(renderState);
+ });
+
+ it('getFragmentShaderSource', function() {
+ var fs =
+ 'varying vec4 v_color;\n' +
+ 'void main() {\n' +
+ ' gl_FragColor = v_color;\n' +
+ '}\n';
+ var appearance = new Appearance({
+ fragmentShaderSource : fs
+ });
+
+ expect(appearance.getFragmentShaderSource().indexOf(fs)).toBeGreaterThan(-1);
+ });
+
+ it('getFragmentShaderSource with material', function() {
+ var material = Material.fromType(undefined, 'Color');
+ var fs =
+ 'varying vec4 v_color;\n' +
+ 'void main() {\n' +
+ ' gl_FragColor = v_color;\n' +
+ '}\n';
+ var appearance = new Appearance({
+ material : material,
+ fragmentShaderSource : fs
+ });
+
+ var fragmentSource = appearance.getFragmentShaderSource();
+ expect(fragmentSource.indexOf(material.shaderSource)).toBeGreaterThan(-1);
+ expect(fragmentSource.indexOf(fs)).toBeGreaterThan(-1);
+ });
+
+ it('getDefaultRenderState', function() {
+ var renderState = Appearance.getDefaultRenderState(true, true);
+
+ expect(renderState.depthTest).toBeDefined();
+ expect(renderState.depthTest.enabled).toEqual(true);
+ expect(renderState.depthMask).toEqual(false);
+ expect(renderState.blending).toEqual(BlendingState.ALPHA_BLEND);
+ expect(renderState.cull).toBeDefined();
+ expect(renderState.cull.enabled).toEqual(true);
+ expect(renderState.cull.face).toEqual(CullFace.BACK);
+ });
+
+});
diff --git a/Specs/Scene/DebugAppearanceSpec.js b/Specs/Scene/DebugAppearanceSpec.js
new file mode 100644
index 000000000000..faa968cc28fb
--- /dev/null
+++ b/Specs/Scene/DebugAppearanceSpec.js
@@ -0,0 +1,340 @@
+/*global defineSuite*/
+defineSuite([
+ 'Scene/DebugAppearance',
+ 'Scene/Appearance',
+ 'Scene/Primitive',
+ 'Core/ExtentGeometry',
+ 'Core/Extent',
+ 'Core/GeometryInstance',
+ 'Core/GeometryInstanceAttribute',
+ 'Core/ComponentDatatype',
+ 'Core/VertexFormat',
+ 'Renderer/ClearCommand',
+ 'Specs/render',
+ 'Specs/createCanvas',
+ 'Specs/destroyCanvas',
+ 'Specs/createContext',
+ 'Specs/destroyContext',
+ 'Specs/createFrameState'
+ ], function(
+ DebugAppearance,
+ Appearance,
+ Primitive,
+ ExtentGeometry,
+ Extent,
+ GeometryInstance,
+ GeometryInstanceAttribute,
+ ComponentDatatype,
+ VertexFormat,
+ ClearCommand,
+ render,
+ createCanvas,
+ destroyCanvas,
+ createContext,
+ destroyContext,
+ createFrameState) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var context;
+ var frameState;
+ var extentInstance;
+
+ beforeAll(function() {
+ context = createContext();
+ frameState = createFrameState();
+
+ var extent = Extent.fromDegrees(-80.0, 20.0, -70.0, 40.0);
+ extentInstance = new GeometryInstance({
+ geometry : new ExtentGeometry({
+ vertexFormat : VertexFormat.ALL,
+ extent : extent
+ })
+ });
+
+ frameState.camera.controller.viewExtent(extent);
+ var us = context.getUniformState();
+ us.update(frameState);
+ });
+
+ afterAll(function() {
+ destroyContext(context);
+ });
+
+ it('constructor throws without attributeName', function() {
+ expect(function() {
+ return new DebugAppearance();
+ }).toThrow();
+ });
+
+ it('default construct with normal, binormal, or tangent attribute name', function() {
+ var a = new DebugAppearance({
+ attributeName : 'normal'
+ });
+
+ expect(a.vertexShaderSource).toBeDefined();
+ expect(a.vertexShaderSource.indexOf('normal')).toBeGreaterThan(-1);
+ expect(a.vertexShaderSource.indexOf('v_normal')).toBeGreaterThan(-1);
+
+ expect(a.fragmentShaderSource).toBeDefined();
+ expect(a.fragmentShaderSource.indexOf('v_normal')).toBeGreaterThan(-1);
+
+ expect(a.material).not.toBeDefined();
+ expect(a.attributeName).toEqual('normal');
+ expect(a.glslDatatype).toEqual('vec3');
+ expect(a.renderState).toEqual(Appearance.getDefaultRenderState(false, false));
+ });
+
+ it('default construct with st attribute name', function() {
+ var a = new DebugAppearance({
+ attributeName : 'st'
+ });
+
+ expect(a.vertexShaderSource).toBeDefined();
+ expect(a.vertexShaderSource.indexOf('st')).toBeGreaterThan(-1);
+ expect(a.vertexShaderSource.indexOf('v_st')).toBeGreaterThan(-1);
+
+ expect(a.fragmentShaderSource).toBeDefined();
+ expect(a.fragmentShaderSource.indexOf('v_st')).toBeGreaterThan(-1);
+
+ expect(a.material).not.toBeDefined();
+ expect(a.attributeName).toEqual('st');
+ expect(a.glslDatatype).toEqual('vec2');
+ expect(a.renderState).toEqual(Appearance.getDefaultRenderState(false, false));
+ });
+
+ it('debug appearance with float attribute name', function() {
+ var a = new DebugAppearance({
+ attributeName : 'rotation',
+ glslDatatype : 'float'
+ });
+
+ expect(a.vertexShaderSource).toBeDefined();
+ expect(a.vertexShaderSource.indexOf('rotation')).toBeGreaterThan(-1);
+ expect(a.vertexShaderSource.indexOf('v_rotation')).toBeGreaterThan(-1);
+
+ expect(a.fragmentShaderSource).toBeDefined();
+ expect(a.fragmentShaderSource.indexOf('v_rotation')).toBeGreaterThan(-1);
+
+ expect(a.material).not.toBeDefined();
+ expect(a.attributeName).toEqual('rotation');
+ expect(a.glslDatatype).toEqual('float');
+ expect(a.renderState).toEqual(Appearance.getDefaultRenderState(false, false));
+ });
+
+ it('debug appearance with vec3 attribute name', function() {
+ var a = new DebugAppearance({
+ attributeName : 'str',
+ glslDatatype : 'vec3'
+ });
+
+ expect(a.vertexShaderSource).toBeDefined();
+ expect(a.vertexShaderSource.indexOf('str')).toBeGreaterThan(-1);
+ expect(a.vertexShaderSource.indexOf('v_str')).toBeGreaterThan(-1);
+
+ expect(a.fragmentShaderSource).toBeDefined();
+ expect(a.fragmentShaderSource.indexOf('v_str')).toBeGreaterThan(-1);
+
+ expect(a.material).not.toBeDefined();
+ expect(a.attributeName).toEqual('str');
+ expect(a.glslDatatype).toEqual('vec3');
+ expect(a.renderState).toEqual(Appearance.getDefaultRenderState(false, false));
+ });
+
+ it('debug appearance with vec4 attribute name', function() {
+ var a = new DebugAppearance({
+ attributeName : 'quaternion',
+ glslDatatype : 'vec4'
+ });
+
+ expect(a.vertexShaderSource).toBeDefined();
+ expect(a.vertexShaderSource.indexOf('quaternion')).toBeGreaterThan(-1);
+ expect(a.vertexShaderSource.indexOf('v_quaternion')).toBeGreaterThan(-1);
+
+ expect(a.fragmentShaderSource).toBeDefined();
+ expect(a.fragmentShaderSource.indexOf('v_quaternion')).toBeGreaterThan(-1);
+
+ expect(a.material).not.toBeDefined();
+ expect(a.attributeName).toEqual('quaternion');
+ expect(a.glslDatatype).toEqual('vec4');
+ expect(a.renderState).toEqual(Appearance.getDefaultRenderState(false, false));
+ });
+
+ it('debug appearance throws with invalid glsl datatype', function() {
+ expect(function() {
+ return new DebugAppearance({
+ attributeName : 'invalid_datatype',
+ glslDatatype : 'invalid'
+ });
+ }).toThrow();
+ });
+
+ it('renders normal', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance,
+ appearance : new DebugAppearance({
+ attributeName : 'normal'
+ })
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders binormal', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance,
+ appearance : new DebugAppearance({
+ attributeName : 'binormal'
+ })
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders tangent', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance,
+ appearance : new DebugAppearance({
+ attributeName : 'tangent'
+ })
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders st', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance,
+ appearance : new DebugAppearance({
+ attributeName : 'st'
+ })
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders float', function() {
+ extentInstance.attributes = {
+ debug : new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ value : [1.0]
+ })
+ };
+ var primitive = new Primitive({
+ geometryInstances : extentInstance,
+ appearance : new DebugAppearance({
+ attributeName : 'debug',
+ glslDatatype : 'float'
+ })
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders vec2', function() {
+ extentInstance.attributes = {
+ debug : new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ value : [1.0, 2.0]
+ })
+ };
+ var primitive = new Primitive({
+ geometryInstances : extentInstance,
+ appearance : new DebugAppearance({
+ attributeName : 'debug',
+ glslDatatype : 'vec2'
+ })
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders vec3', function() {
+ extentInstance.attributes = {
+ debug : new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ value : [1.0, 2.0, 3.0]
+ })
+ };
+ var primitive = new Primitive({
+ geometryInstances : extentInstance,
+ appearance : new DebugAppearance({
+ attributeName : 'debug',
+ glslDatatype : 'vec3'
+ })
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders vec4', function() {
+ extentInstance.attributes = {
+ debug : new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ value : [1.0, 2.0, 3.0, 4.0]
+ })
+ };
+ var primitive = new Primitive({
+ geometryInstances : extentInstance,
+ appearance : new DebugAppearance({
+ attributeName : 'debug',
+ glslDatatype : 'vec4'
+ })
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+});
diff --git a/Specs/Scene/EllipsoidSurfaceAppearanceSpec.js b/Specs/Scene/EllipsoidSurfaceAppearanceSpec.js
new file mode 100644
index 000000000000..0fe4ad7fb27d
--- /dev/null
+++ b/Specs/Scene/EllipsoidSurfaceAppearanceSpec.js
@@ -0,0 +1,92 @@
+/*global defineSuite*/
+defineSuite([
+ 'Scene/EllipsoidSurfaceAppearance',
+ 'Scene/Appearance',
+ 'Scene/Material',
+ 'Scene/Primitive',
+ 'Core/ExtentGeometry',
+ 'Core/Extent',
+ 'Core/GeometryInstance',
+ 'Core/ColorGeometryInstanceAttribute',
+ 'Renderer/ClearCommand',
+ 'Specs/render',
+ 'Specs/createCanvas',
+ 'Specs/destroyCanvas',
+ 'Specs/createContext',
+ 'Specs/destroyContext',
+ 'Specs/createFrameState'
+ ], function(
+ EllipsoidSurfaceAppearance,
+ Appearance,
+ Material,
+ Primitive,
+ ExtentGeometry,
+ Extent,
+ GeometryInstance,
+ ColorGeometryInstanceAttribute,
+ ClearCommand,
+ render,
+ createCanvas,
+ destroyCanvas,
+ createContext,
+ destroyContext,
+ createFrameState) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var context;
+ var frameState;
+ var primitive;
+
+ beforeAll(function() {
+ context = createContext();
+ frameState = createFrameState();
+
+ var extent = Extent.fromDegrees(-80.0, 20.0, -70.0, 40.0);
+ primitive = new Primitive({
+ geometryInstances : new GeometryInstance({
+ geometry : new ExtentGeometry({
+ extent : extent
+ }),
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ })
+ });
+
+ frameState.camera.controller.viewExtent(extent);
+ var us = context.getUniformState();
+ us.update(frameState);
+ });
+
+ afterAll(function() {
+ primitive = primitive && primitive.destroy();
+ destroyContext(context);
+ });
+
+ it('constructor', function() {
+ var a = new EllipsoidSurfaceAppearance();
+
+ expect(a.material).toBeDefined();
+ expect(a.material.type).toEqual(Material.ColorType);
+ expect(a.vertexShaderSource).toBeDefined();
+ expect(a.fragmentShaderSource).toBeDefined();
+ expect(a.renderState).toEqual(Appearance.getDefaultRenderState(true, true));
+ expect(a.vertexFormat).toEqual(EllipsoidSurfaceAppearance.VERTEX_FORMAT);
+ expect(a.flat).toEqual(false);
+ expect(a.faceForward).toEqual(false);
+ expect(a.translucent).toEqual(true);
+ expect(a.aboveGround).toEqual(false);
+ });
+
+ it('renders', function() {
+ primitive.appearance = new EllipsoidSurfaceAppearance();
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+ });
+
+});
diff --git a/Specs/Scene/ExtentPrimitiveSpec.js b/Specs/Scene/ExtentPrimitiveSpec.js
new file mode 100644
index 000000000000..f1c8b4a0030c
--- /dev/null
+++ b/Specs/Scene/ExtentPrimitiveSpec.js
@@ -0,0 +1,226 @@
+/*global defineSuite*/
+defineSuite([
+ 'Scene/ExtentPrimitive',
+ 'Specs/createContext',
+ 'Specs/destroyContext',
+ 'Specs/createCamera',
+ 'Specs/createFrameState',
+ 'Specs/frameState',
+ 'Specs/pick',
+ 'Specs/render',
+ 'Core/BoundingSphere',
+ 'Core/Cartesian3',
+ 'Core/Cartographic',
+ 'Core/Ellipsoid',
+ 'Core/Extent',
+ 'Core/Math',
+ 'Renderer/ClearCommand',
+ 'Scene/SceneMode'
+ ], function(
+ ExtentPrimitive,
+ createContext,
+ destroyContext,
+ createCamera,
+ createFrameState,
+ frameState,
+ pick,
+ render,
+ BoundingSphere,
+ Cartesian3,
+ Cartographic,
+ Ellipsoid,
+ Extent,
+ CesiumMath,
+ ClearCommand,
+ SceneMode) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var context;
+ var extent;
+ var us;
+
+ beforeAll(function() {
+ context = createContext();
+ });
+
+ afterAll(function() {
+ destroyContext(context);
+ });
+
+ beforeEach(function() {
+ extent = new ExtentPrimitive();
+
+ us = context.getUniformState();
+ us.update(createFrameState(createCamera(context, new Cartesian3(1.02, 0.0, 0.0), Cartesian3.ZERO, Cartesian3.UNIT_Z)));
+ });
+
+ afterEach(function() {
+ extent = extent && extent.destroy();
+ us = undefined;
+ });
+
+ function createExtent() {
+ var ellipsoid = Ellipsoid.UNIT_SPHERE;
+
+ var e = new ExtentPrimitive({
+ ellipsoid : ellipsoid,
+ granularity : CesiumMath.toRadians(20.0),
+ extent : Extent.fromDegrees(-50.0, -50.0, 50.0, 50.0)
+ });
+
+ return e;
+ }
+
+ it('gets default show', function() {
+ expect(extent.show).toEqual(true);
+ });
+
+ it('gets the default color', function() {
+ expect(extent.material.uniforms.color).toEqual({
+ red : 1.0,
+ green : 1.0,
+ blue : 0.0,
+ alpha : 0.5
+ });
+ });
+
+ it('has a default ellipsoid', function() {
+ expect(extent.ellipsoid).toEqual(Ellipsoid.WGS84);
+ });
+
+ it('gets the default granularity', function() {
+ expect(extent.granularity).toEqual(CesiumMath.RADIANS_PER_DEGREE);
+ });
+
+ it('renders', function() {
+ extent = createExtent();
+ extent.material.uniforms.color = {
+ red : 1.0,
+ green : 0.0,
+ blue : 0.0,
+ alpha : 1.0
+ };
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, extent);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+ });
+
+ it('does not render when show is false', function() {
+ extent = createExtent();
+ extent.material.uniforms.color = {
+ red : 1.0,
+ green : 0.0,
+ blue : 0.0,
+ alpha : 1.0
+ };
+ extent.show = false;
+
+ expect(render(context, frameState, extent)).toEqual(0);
+ });
+
+ it('does not render without extent', function() {
+ extent = new ExtentPrimitive();
+ extent.ellipsoid = Ellipsoid.UNIT_SPHERE;
+ extent.granularity = CesiumMath.toRadians(20.0);
+ expect(render(context, frameState, extent)).toEqual(0);
+ });
+
+ it('is picked', function() {
+ extent = createExtent();
+
+ var pickedObject = pick(context, frameState, extent, 0, 0);
+ expect(pickedObject).toEqual(extent);
+ });
+
+ it('is not picked (show === false)', function() {
+ extent = createExtent();
+ extent.show = false;
+
+ var pickedObject = pick(context, frameState, extent, 0, 0);
+ expect(pickedObject).not.toBeDefined();
+ });
+
+ it('is not picked (alpha === 0.0)', function() {
+ extent = createExtent();
+ extent.material.uniforms.color.alpha = 0.0;
+
+ var pickedObject = pick(context, frameState, extent, 0, 0);
+ expect(pickedObject).not.toBeDefined();
+ });
+
+ it('test 3D bounding sphere', function() {
+ extent = createExtent();
+ var commandList = [];
+ extent.update(context, frameState, commandList);
+ var boundingVolume = commandList[0].colorList[0].boundingVolume;
+ expect(boundingVolume).toEqual(BoundingSphere.fromExtent3D(extent.extent, Ellipsoid.UNIT_SPHERE));
+ });
+
+ it('test Columbus view bounding sphere', function() {
+ extent = createExtent();
+
+ var mode = frameState.mode;
+ frameState.mode = SceneMode.COLUMBUS_VIEW;
+ var commandList = [];
+ extent.update(context, frameState, commandList);
+ var boundingVolume = commandList[0].colorList[0].boundingVolume;
+ frameState.mode = mode;
+
+ var b3D = BoundingSphere.fromExtent3D(extent.extent, Ellipsoid.UNIT_SPHERE);
+ expect(boundingVolume).toEqual(BoundingSphere.projectTo2D(b3D, frameState.scene2D.projection));
+ });
+
+ it('test 2D bounding sphere', function() {
+ extent = createExtent();
+
+ var mode = frameState.mode;
+ frameState.mode = SceneMode.SCENE2D;
+ var commandList = [];
+ extent.update(context, frameState, commandList);
+ var boundingVolume = commandList[0].colorList[0].boundingVolume;
+ frameState.mode = mode;
+
+ var b3D = BoundingSphere.fromExtent3D(extent.extent, Ellipsoid.UNIT_SPHERE);
+ var b2D = BoundingSphere.projectTo2D(b3D, frameState.scene2D.projection);
+ b2D.center.x = 0.0;
+ expect(boundingVolume).toEqual(b2D);
+ });
+
+ it('isDestroyed', function() {
+ var e = new ExtentPrimitive();
+ expect(e.isDestroyed()).toEqual(false);
+ e.destroy();
+ expect(e.isDestroyed()).toEqual(true);
+ });
+
+ it('throws when updated/rendered without a ellipsoid', function() {
+ extent = createExtent();
+ extent.ellipsoid = undefined;
+
+ expect(function() {
+ extent.update(context, frameState);
+ }).toThrow();
+ });
+
+ it('throws when updated/rendered without an invalid granularity', function() {
+ extent = createExtent();
+ extent.granularity = -1.0;
+
+ expect(function() {
+ extent.update(context, frameState);
+ }).toThrow();
+ });
+
+ it('throws when rendered without a material', function() {
+ extent = createExtent();
+ extent.material = undefined;
+
+ expect(function() {
+ render(context, frameState, extent);
+ }).toThrow();
+ });
+}, 'WebGL');
diff --git a/Specs/Scene/GeometryRenderingSpec.js b/Specs/Scene/GeometryRenderingSpec.js
new file mode 100644
index 000000000000..8e55ddfe7610
--- /dev/null
+++ b/Specs/Scene/GeometryRenderingSpec.js
@@ -0,0 +1,784 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/BoxGeometry',
+ 'Core/CircleGeometry',
+ 'Core/EllipseGeometry',
+ 'Core/EllipsoidGeometry',
+ 'Core/ExtentGeometry',
+ 'Core/PolygonGeometry',
+ 'Core/SimplePolylineGeometry',
+ 'Core/WallGeometry',
+ 'Core/defaultValue',
+ 'Core/Geometry',
+ 'Core/GeometryAttribute',
+ 'Core/GeometryInstance',
+ 'Core/ColorGeometryInstanceAttribute',
+ 'Core/GeometryInstanceAttribute',
+ 'Core/ComponentDatatype',
+ 'Core/Cartesian3',
+ 'Core/Matrix4',
+ 'Core/Extent',
+ 'Core/Ellipsoid',
+ 'Core/PrimitiveType',
+ 'Core/Transforms',
+ 'Core/Cartographic',
+ 'Core/BoundingSphere',
+ 'Core/Math',
+ 'Renderer/ClearCommand',
+ 'Scene/PerInstanceColorAppearance',
+ 'Scene/Primitive',
+ 'Scene/SceneMode',
+ 'Scene/OrthographicFrustum',
+ 'Specs/render',
+ 'Specs/pick',
+ 'Specs/createCanvas',
+ 'Specs/destroyCanvas',
+ 'Specs/createContext',
+ 'Specs/destroyContext',
+ 'Specs/createFrameState'
+ ], 'Scene/GeometryRendering', function(
+ BoxGeometry,
+ CircleGeometry,
+ EllipseGeometry,
+ EllipsoidGeometry,
+ ExtentGeometry,
+ PolygonGeometry,
+ SimplePolylineGeometry,
+ WallGeometry,
+ defaultValue,
+ Geometry,
+ GeometryAttribute,
+ GeometryInstance,
+ ColorGeometryInstanceAttribute,
+ GeometryInstanceAttribute,
+ ComponentDatatype,
+ Cartesian3,
+ Matrix4,
+ Extent,
+ Ellipsoid,
+ PrimitiveType,
+ Transforms,
+ Cartographic,
+ BoundingSphere,
+ CesiumMath,
+ ClearCommand,
+ PerInstanceColorAppearance,
+ Primitive,
+ SceneMode,
+ OrthographicFrustum,
+ render,
+ pick,
+ createCanvas,
+ destroyCanvas,
+ createContext,
+ destroyContext,
+ createFrameState) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var context;
+ var ellipsoid;
+
+ beforeAll(function() {
+ context = createContext();
+ ellipsoid = Ellipsoid.WGS84;
+ });
+
+ afterAll(function() {
+ destroyContext(context);
+ });
+
+ function viewSphere3D(camera, sphere, modelMatrix) {
+ sphere = BoundingSphere.transform(sphere, modelMatrix);
+ var center = sphere.center.clone();
+ var radius = sphere.radius;
+
+ var direction = ellipsoid.geodeticSurfaceNormal(center, camera.direction);
+ Cartesian3.negate(direction, direction);
+ Cartesian3.normalize(direction, direction);
+ var right = Cartesian3.cross(direction, Cartesian3.UNIT_Z, camera.right);
+ Cartesian3.normalize(right, right);
+ Cartesian3.cross(right, direction, camera.up);
+
+ var scalar = center.magnitude() + radius;
+ Cartesian3.normalize(center, center);
+ Cartesian3.multiplyByScalar(center, scalar, camera.position);
+ }
+
+ function render3D(instance, afterView, boundingSphere) {
+ var primitive = new Primitive({
+ geometryInstances : instance,
+ appearance : new PerInstanceColorAppearance({
+ flat : true
+ })
+ });
+
+ var frameState = createFrameState();
+
+ var sphere = defaultValue(instance.geometry.boundingSphere, boundingSphere);
+ viewSphere3D(frameState.camera, sphere, instance.modelMatrix);
+
+ if (typeof afterView === 'function') {
+ afterView(frameState);
+ }
+
+ context.getUniformState().update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ }
+
+ function viewSphereCV(camera, sphere, modelMatrix) {
+ sphere = BoundingSphere.transform(sphere, modelMatrix);
+ sphere = BoundingSphere.projectTo2D(sphere);
+ var center = sphere.center.clone();
+ var radius = sphere.radius * 0.5;
+
+ Cartesian3.clone(Cartesian3.UNIT_Z, camera.direction);
+ Cartesian3.negate(camera.direction, camera.direction);
+ Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
+ Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
+
+ camera.position.x = center.y;
+ camera.position.y = center.z;
+ camera.position.z = center.x + radius;
+ }
+
+ function renderCV(instance, afterView, boundingSphere) {
+ var primitive = new Primitive({
+ geometryInstances : instance,
+ appearance : new PerInstanceColorAppearance({
+ flat : true
+ })
+ });
+
+ var frameState = createFrameState();
+ frameState.mode = SceneMode.COLUMBUS_VIEW;
+ frameState.morphTime = frameState.mode.morphTime;
+ frameState.camera.transform = new Matrix4(0.0, 0.0, 1.0, 0.0,
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+ frameState.camera.controller.update(frameState.mode, frameState.scene2D);
+
+ var sphere = defaultValue(instance.geometry.boundingSphere, boundingSphere);
+ viewSphereCV(frameState.camera, sphere, instance.modelMatrix);
+
+ if (typeof afterView === 'function') {
+ afterView(frameState);
+ }
+
+ context.getUniformState().update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ }
+
+ function viewSphere2D(camera, sphere, modelMatrix) {
+ sphere = BoundingSphere.transform(sphere, modelMatrix);
+ sphere = BoundingSphere.projectTo2D(sphere);
+ var center = sphere.center.clone();
+ var radius = sphere.radius;
+
+ Cartesian3.clone(Cartesian3.UNIT_Z, camera.direction);
+ Cartesian3.negate(camera.direction, camera.direction);
+ Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
+ Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
+
+ camera.position.x = center.y;
+ camera.position.y = center.z;
+
+ var frustum = camera.frustum;
+ var ratio = camera.frustum.right / camera.frustum.top;
+ frustum.right = radius * 0.25;
+ frustum.top = frustum.right / ratio;
+ frustum.left = -frustum.right;
+ frustum.bottom = -frustum.top;
+ }
+
+ function render2D(instance, boundingSphere) {
+ var primitive = new Primitive({
+ geometryInstances : instance,
+ appearance : new PerInstanceColorAppearance({
+ flat : true
+ })
+ });
+
+ var frameState = createFrameState();
+ frameState.mode = SceneMode.SCENE2D;
+ frameState.morphTime = frameState.mode.morphTime;
+ frameState.camera.transform = new Matrix4(0.0, 0.0, 1.0, 0.0,
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+ var frustum = new OrthographicFrustum();
+ frustum.right = ellipsoid.getMaximumRadius() * Math.PI;
+ frustum.left = -frustum.right;
+ frustum.top = frustum.right;
+ frustum.bottom = -frustum.top;
+ frameState.camera.frustum = frustum;
+ frameState.camera.controller.update(frameState.mode, frameState.scene2D);
+
+ var sphere = defaultValue(instance.geometry.boundingSphere, boundingSphere);
+ viewSphere2D(frameState.camera, sphere, instance.modelMatrix);
+ context.getUniformState().update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ }
+
+ function pickGeometry(instance, afterView, boundingSphere) {
+ var primitive = new Primitive({
+ geometryInstances : instance,
+ appearance : new PerInstanceColorAppearance({
+ flat : true
+ })
+ });
+
+ var frameState = createFrameState();
+
+ var sphere = defaultValue(instance.geometry.boundingSphere, boundingSphere);
+ viewSphere3D(frameState.camera, sphere, instance.modelMatrix);
+
+ if (typeof afterView === 'function') {
+ afterView(frameState);
+ }
+
+ context.getUniformState().update(frameState);
+
+ expect(pick(context, frameState, primitive)).toEqual(instance.id);
+
+ primitive = primitive && primitive.destroy();
+ }
+
+ describe('BoxGeometry', function() {
+ var instance;
+ beforeAll(function() {
+ instance = new GeometryInstance({
+ geometry : BoxGeometry.fromDimensions({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ dimensions : new Cartesian3(1000000.0, 1000000.0, 2000000.0)
+ }),
+ modelMatrix : Matrix4.multiplyByTranslation(Transforms.eastNorthUpToFixedFrame(
+ ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883))), new Cartesian3(0.0, 0.0, 3000000.0)),
+ id : 'box',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+ }, 'WebGL');
+
+ describe('CircleGeometry', function() {
+ var instance;
+ beforeAll(function() {
+ instance = new GeometryInstance({
+ geometry : new CircleGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ center : ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-100, 20)),
+ radius : 1000000.0
+ }),
+ id : 'circle',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+ }, 'WebGL');
+
+ describe('EllipseGeometry', function() {
+ var instance;
+ beforeAll(function() {
+ instance = new GeometryInstance({
+ geometry : new EllipseGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ center : ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-100, 20)),
+ semiMinorAxis : 1000000.0,
+ semiMajorAxis : 1000000.0
+ }),
+ id : 'ellipse',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+
+ it('rotated', function() {
+ var rotated = new GeometryInstance({
+ geometry : new EllipseGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ center : ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-100, 20)),
+ semiMinorAxis : 1000000.0,
+ semiMajorAxis : 1000000.0,
+ bearing : CesiumMath.PI_OVER_FOUR
+ }),
+ id : 'ellipse',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ render3D(rotated);
+ });
+
+ it('at height', function() {
+ var atHeight = new GeometryInstance({
+ geometry : new EllipseGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ center : ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-100, 20)),
+ semiMinorAxis : 1000000.0,
+ semiMajorAxis : 1000000.0,
+ height : 1000000.0
+ }),
+ id : 'ellipse',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ render3D(atHeight);
+ });
+ }, 'WebGL');
+
+ describe('EllipsoidGeometry', function() {
+ var instance;
+ beforeAll(function() {
+ instance = new GeometryInstance({
+ geometry : new EllipsoidGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ radii : new Cartesian3(1000000.0, 1000000.0, 500000.0)
+ }),
+ modelMatrix : Matrix4.multiplyByTranslation(Transforms.eastNorthUpToFixedFrame(
+ ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-100, 20))), new Cartesian3(0.0, 0.0, 1000000.0)),
+ id : 'ellipsoid',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+ }, 'WebGL');
+
+ describe('ExtentGeometry', function() {
+ var instance;
+ var extent;
+ beforeAll(function() {
+ extent = Extent.fromDegrees(0, 0, 1, 1);
+ instance = new GeometryInstance({
+ geometry : new ExtentGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ extent : extent
+ }),
+ id : 'extent',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+
+ it('rotated', function() {
+ var rotated = new GeometryInstance({
+ geometry : new ExtentGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ extent : extent,
+ rotation : CesiumMath.PI_OVER_FOUR
+ }),
+ id : 'extent',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ render3D(rotated);
+ });
+
+ it('at height', function() {
+ var atHeight = new GeometryInstance({
+ geometry : new ExtentGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ extent : extent,
+ height : 100000.0
+ }),
+ id : 'extent',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ render3D(atHeight);
+ });
+ }, 'WebGL');
+
+ describe('PolygonGeometry', function() {
+ var instance;
+ beforeAll(function() {
+ instance = new GeometryInstance({
+ geometry : PolygonGeometry.fromPositions({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(0.0, 45.0),
+ Cartographic.fromDegrees(10.0, 45.0),
+ Cartographic.fromDegrees(10.0, 55.0),
+ Cartographic.fromDegrees(0.0, 55.0)
+ ])
+ }),
+ id : 'polygon',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+
+ it('at height', function() {
+ var atHeight = new GeometryInstance({
+ geometry : PolygonGeometry.fromPositions({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(0.0, 45.0),
+ Cartographic.fromDegrees(10.0, 45.0),
+ Cartographic.fromDegrees(10.0, 55.0),
+ Cartographic.fromDegrees(0.0, 55.0)
+ ]),
+ height : 3000000.0
+ }),
+ id : 'polygon',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ render3D(atHeight);
+ });
+
+ it('hierarchy', function() {
+ var hierarchy = new GeometryInstance({
+ geometry : new PolygonGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ polygonHierarchy : {
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-109.0, 30.0),
+ Cartographic.fromDegrees(-95.0, 30.0),
+ Cartographic.fromDegrees(-95.0, 40.0),
+ Cartographic.fromDegrees(-109.0, 40.0)
+ ]),
+ holes : [{
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-107.0, 31.0),
+ Cartographic.fromDegrees(-107.0, 39.0),
+ Cartographic.fromDegrees(-97.0, 39.0),
+ Cartographic.fromDegrees(-97.0, 31.0)
+ ]),
+ holes : [{
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(-106.5, 31.5),
+ Cartographic.fromDegrees(-97.5, 31.5),
+ Cartographic.fromDegrees(-97.5, 38.5),
+ Cartographic.fromDegrees(-106.5, 38.5)
+ ])
+ }]
+ }]
+ }
+ }),
+ id : 'polygon',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ render3D(hierarchy);
+ });
+ }, 'WebGL');
+
+ describe('WallGeometry', function() {
+ var instance;
+ var afterViewCV;
+ var afterView3D;
+ beforeAll(function() {
+ var height = 100000.0;
+
+ instance = new GeometryInstance({
+ geometry : new WallGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(0.0, 0.0, height),
+ Cartographic.fromDegrees(0.01, 0.0, height)
+ ])
+ }),
+ id : 'wall',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+
+ afterView3D = function(frameState) {
+ var transform = Transforms.eastNorthUpToFixedFrame(instance.geometry.boundingSphere.center);
+ frameState.camera.controller.rotateDown(-CesiumMath.PI_OVER_TWO, transform);
+ frameState.camera.controller.zoomIn(instance.geometry.boundingSphere.radius * 0.99);
+ };
+
+ afterViewCV = function(frameState) {
+ var translation = frameState.camera.position.clone();
+ translation.z = 0.0;
+ var transform = Matrix4.fromTranslation(translation);
+ frameState.camera.controller.rotateDown(-CesiumMath.PI_OVER_TWO, transform);
+ frameState.camera.controller.zoomIn(instance.geometry.boundingSphere.radius * 1.85);
+ };
+ });
+
+ it('3D', function() {
+ render3D(instance, afterView3D);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance, afterViewCV);
+ });
+
+ // walls do not render in 2D
+
+ it('pick', function() {
+ pickGeometry(instance, afterView3D);
+ });
+ }, 'WebGL');
+
+ describe('SimplePolylineGeometry', function() {
+ var instance;
+ beforeAll(function() {
+ instance = new GeometryInstance({
+ geometry : new SimplePolylineGeometry({
+ positions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(0.0, 0.0),
+ Cartographic.fromDegrees(5.0, 0.0)
+ ])
+ }),
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 1.0, 1.0)
+ },
+ id : 'simple polyline'
+ });
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+ });
+
+ describe('Custom geometry', function() {
+ describe('with indices', function() {
+ var instance;
+ beforeAll(function() {
+ instance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 7000000.0, 0.0, 0.0,
+ 7000000.0, 1000000.0, 0.0,
+ 7000000.0, 0.0, 1000000.0,
+ 7000000.0, 1000000.0, 1000000.0
+ ])
+ })
+ },
+ indices : new Uint16Array([0, 1, 2, 2, 1, 3]),
+ primitiveType : PrimitiveType.TRIANGLES
+ }),
+ id : 'customWithIndices',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 1.0, 1.0)
+ }
+ });
+ instance.geometry.boundingSphere = BoundingSphere.fromVertices(instance.geometry.attributes.position.values);
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+ });
+
+ describe('without indices', function() {
+ var instance;
+ beforeAll(function() {
+ instance = new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : new Float64Array([
+ 7000000.0, 0.0, 0.0,
+ 7000000.0, 1000000.0, 0.0,
+ 7000000.0, 0.0, 1000000.0,
+ 7000000.0, 0.0, 1000000.0,
+ 7000000.0, 1000000.0, 0.0,
+ 7000000.0, 1000000.0, 1000000.0
+ ])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ }),
+ id : 'customWithIndices',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 1.0, 1.0)
+ }
+ });
+ instance.geometry.boundingSphere = BoundingSphere.fromVertices(instance.geometry.attributes.position.values);
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+ });
+ });
+
+});
diff --git a/Specs/Scene/MaterialAppearanceSpec.js b/Specs/Scene/MaterialAppearanceSpec.js
new file mode 100644
index 000000000000..b7781a13bc8d
--- /dev/null
+++ b/Specs/Scene/MaterialAppearanceSpec.js
@@ -0,0 +1,129 @@
+/*global defineSuite*/
+defineSuite([
+ 'Scene/MaterialAppearance',
+ 'Scene/Appearance',
+ 'Scene/Material',
+ 'Scene/Primitive',
+ 'Core/ExtentGeometry',
+ 'Core/Extent',
+ 'Core/GeometryInstance',
+ 'Core/ColorGeometryInstanceAttribute',
+ 'Renderer/ClearCommand',
+ 'Specs/render',
+ 'Specs/createCanvas',
+ 'Specs/destroyCanvas',
+ 'Specs/createContext',
+ 'Specs/destroyContext',
+ 'Specs/createFrameState'
+ ], function(
+ MaterialAppearance,
+ Appearance,
+ Material,
+ Primitive,
+ ExtentGeometry,
+ Extent,
+ GeometryInstance,
+ ColorGeometryInstanceAttribute,
+ ClearCommand,
+ render,
+ createCanvas,
+ destroyCanvas,
+ createContext,
+ destroyContext,
+ createFrameState) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var context;
+ var frameState;
+ var primitive;
+
+ beforeAll(function() {
+ context = createContext();
+ frameState = createFrameState();
+
+ var extent = Extent.fromDegrees(-80.0, 20.0, -70.0, 40.0);
+ primitive = new Primitive({
+ geometryInstances : new GeometryInstance({
+ geometry : new ExtentGeometry({
+ vertexFormat : MaterialAppearance.MaterialSupport.ALL.vertexFormat,
+ extent : extent
+ }),
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ })
+ });
+
+ frameState.camera.controller.viewExtent(extent);
+ var us = context.getUniformState();
+ us.update(frameState);
+ });
+
+ afterAll(function() {
+ primitive = primitive && primitive.destroy();
+ destroyContext(context);
+ });
+
+ it('constructor', function() {
+ var a = new MaterialAppearance();
+
+ expect(a.materialSupport).toEqual(MaterialAppearance.MaterialSupport.TEXTURED);
+ expect(a.material).toBeDefined();
+ expect(a.material.type).toEqual(Material.ColorType);
+ expect(a.vertexShaderSource).toEqual(MaterialAppearance.MaterialSupport.TEXTURED.vertexShaderSource);
+ expect(a.fragmentShaderSource).toEqual(MaterialAppearance.MaterialSupport.TEXTURED.fragmentShaderSource);
+ expect(a.renderState).toEqual(Appearance.getDefaultRenderState(true, false));
+ expect(a.vertexFormat).toEqual(MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat);
+ expect(a.flat).toEqual(false);
+ expect(a.faceForward).toEqual(false);
+ expect(a.translucent).toEqual(true);
+ expect(a.closed).toEqual(false);
+ });
+
+ it('renders basic', function() {
+ primitive.appearance = new MaterialAppearance({
+ materialSupport : MaterialAppearance.MaterialSupport.BASIC,
+ translucent : false,
+ closed : true,
+ material : Material.fromType(context, Material.DotType)
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+ });
+
+ it('renders textured', function() {
+ primitive.appearance = new MaterialAppearance({
+ materialSupport : MaterialAppearance.MaterialSupport.TEXTURED,
+ translucent : false,
+ closed : true,
+ material : Material.fromType(context, Material.ImageType)
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+ });
+
+ it('renders all', function() {
+ primitive.appearance = new MaterialAppearance({
+ materialSupport : MaterialAppearance.MaterialSupport.ALL,
+ translucent : false,
+ closed : true,
+ material : Material.fromType(context, Material.NormalMapType)
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+ });
+
+});
diff --git a/Specs/Scene/MultifrustumSpec.js b/Specs/Scene/MultifrustumSpec.js
index 4e3419eba98a..767608946599 100644
--- a/Specs/Scene/MultifrustumSpec.js
+++ b/Specs/Scene/MultifrustumSpec.js
@@ -4,14 +4,14 @@ defineSuite([
'Specs/destroyScene',
'Core/destroyObject',
'Core/BoundingSphere',
- 'Core/BoxTessellator',
+ 'Core/BoxGeometry',
'Core/Cartesian2',
'Core/Cartesian3',
'Core/Color',
'Core/defaultValue',
'Core/Math',
'Core/Matrix4',
- 'Core/MeshFilters',
+ 'Core/GeometryPipeline',
'Core/PrimitiveType',
'Renderer/BlendingState',
'Renderer/BufferUsage',
@@ -25,14 +25,14 @@ defineSuite([
destroyScene,
destroyObject,
BoundingSphere,
- BoxTessellator,
+ BoxGeometry,
Cartesian2,
Cartesian3,
Color,
defaultValue,
CesiumMath,
Matrix4,
- MeshFilters,
+ GeometryPipeline,
PrimitiveType,
BlendingState,
BufferUsage,
@@ -222,13 +222,13 @@ defineSuite([
var dimensions = new Cartesian3(500000.0, 500000.0, 500000.0);
var maximumCorner = dimensions.multiplyByScalar(0.5);
var minimumCorner = maximumCorner.negate();
- var mesh = BoxTessellator.compute({
+ var geometry = new BoxGeometry({
minimumCorner: minimumCorner,
maximumCorner: maximumCorner
});
- var attributeIndices = MeshFilters.createAttributeIndices(mesh);
- this._va = context.createVertexArrayFromMesh({
- mesh: mesh,
+ var attributeIndices = GeometryPipeline.createAttributeIndices(geometry);
+ this._va = context.createVertexArrayFromGeometry({
+ geometry: geometry,
attributeIndices: attributeIndices,
bufferUsage: BufferUsage.STATIC_DRAW
});
diff --git a/Specs/Scene/PerInstanceColorAppearanceSpec.js b/Specs/Scene/PerInstanceColorAppearanceSpec.js
new file mode 100644
index 000000000000..71e1050a0e66
--- /dev/null
+++ b/Specs/Scene/PerInstanceColorAppearanceSpec.js
@@ -0,0 +1,106 @@
+/*global defineSuite*/
+defineSuite([
+ 'Scene/PerInstanceColorAppearance',
+ 'Scene/Appearance',
+ 'Scene/Material',
+ 'Scene/Primitive',
+ 'Core/ExtentGeometry',
+ 'Core/Extent',
+ 'Core/GeometryInstance',
+ 'Core/ColorGeometryInstanceAttribute',
+ 'Renderer/ClearCommand',
+ 'Specs/render',
+ 'Specs/createCanvas',
+ 'Specs/destroyCanvas',
+ 'Specs/createContext',
+ 'Specs/destroyContext',
+ 'Specs/createFrameState'
+ ], function(
+ PerInstanceColorAppearance,
+ Appearance,
+ Material,
+ Primitive,
+ ExtentGeometry,
+ Extent,
+ GeometryInstance,
+ ColorGeometryInstanceAttribute,
+ ClearCommand,
+ render,
+ createCanvas,
+ destroyCanvas,
+ createContext,
+ destroyContext,
+ createFrameState) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var context;
+ var frameState;
+ var primitive;
+
+ beforeAll(function() {
+ context = createContext();
+ frameState = createFrameState();
+
+ var extent = Extent.fromDegrees(-80.0, 20.0, -70.0, 40.0);
+ primitive = new Primitive({
+ geometryInstances : new GeometryInstance({
+ geometry : new ExtentGeometry({
+ vertexFormat : PerInstanceColorAppearance.VERTEX_FORMAT,
+ extent : extent
+ }),
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ })
+ });
+
+ frameState.camera.controller.viewExtent(extent);
+ var us = context.getUniformState();
+ us.update(frameState);
+ });
+
+ afterAll(function() {
+ primitive = primitive && primitive.destroy();
+ destroyContext(context);
+ });
+
+ it('constructor', function() {
+ var a = new PerInstanceColorAppearance();
+
+ expect(a.material).not.toBeDefined();
+ expect(a.vertexShaderSource).toBeDefined();
+ expect(a.fragmentShaderSource).toBeDefined();
+ expect(a.renderState).toEqual(Appearance.getDefaultRenderState(true, false));
+ expect(a.vertexFormat).toEqual(PerInstanceColorAppearance.VERTEX_FORMAT);
+ expect(a.flat).toEqual(false);
+ expect(a.faceForward).toEqual(false);
+ expect(a.translucent).toEqual(true);
+ expect(a.closed).toEqual(false);
+ });
+
+ it('renders', function() {
+ primitive.appearance = new PerInstanceColorAppearance();
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+ });
+
+ it('renders flat', function() {
+ primitive.appearance = new PerInstanceColorAppearance({
+ flat : true,
+ translucent : false,
+ closed : true
+ });
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+ });
+
+});
diff --git a/Specs/Scene/PolygonSpec.js b/Specs/Scene/PolygonSpec.js
index 01e1021d1145..c2746ada23de 100644
--- a/Specs/Scene/PolygonSpec.js
+++ b/Specs/Scene/PolygonSpec.js
@@ -8,16 +8,11 @@ defineSuite([
'Specs/frameState',
'Specs/pick',
'Specs/render',
- 'Core/BoundingRectangle',
'Core/BoundingSphere',
'Core/Cartesian3',
'Core/Cartographic',
'Core/Ellipsoid',
- 'Core/Extent',
- 'Core/Matrix4',
'Core/Math',
- 'Core/JulianDate',
- 'Renderer/BufferUsage',
'Renderer/ClearCommand',
'Scene/SceneMode'
], function(
@@ -29,16 +24,11 @@ defineSuite([
frameState,
pick,
render,
- BoundingRectangle,
BoundingSphere,
Cartesian3,
Cartographic,
Ellipsoid,
- Extent,
- Matrix4,
CesiumMath,
- JulianDate,
- BufferUsage,
ClearCommand,
SceneMode) {
"use strict";
@@ -173,23 +163,12 @@ defineSuite([
new Cartographic()
])
};
+ polygon.configureFromPolygonHierarchy(hierarchy);
expect(function() {
- polygon.configureFromPolygonHierarchy(hierarchy);
+ render(context, frameState, polygon);
}).toThrow();
});
- it('configures extent', function() {
- var extent = new Extent(
- 0.0,
- 0.0,
- CesiumMath.toRadians(10.0),
- CesiumMath.toRadians(10.0)
- );
-
- polygon.configureExtent(extent);
- expect(polygon.getPositions()).not.toBeDefined();
- });
-
it('gets the default color', function() {
expect(polygon.material.uniforms.color).toEqual({
red : 1.0,
@@ -199,16 +178,12 @@ defineSuite([
});
});
- it('gets default buffer usage', function() {
- expect(polygon.bufferUsage).toEqual(BufferUsage.STATIC_DRAW);
- });
-
it('has a default ellipsoid', function() {
expect(polygon.ellipsoid).toEqual(Ellipsoid.WGS84);
});
it('gets the default granularity', function() {
- expect(polygon.granularity).toEqual(CesiumMath.toRadians(1.0));
+ expect(polygon.granularity).toEqual(CesiumMath.RADIANS_PER_DEGREE);
});
it('renders', function() {
@@ -228,32 +203,6 @@ defineSuite([
expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
});
- it('renders extent', function() {
- // This test fails in Chrome if a breakpoint is set inside this function. Strange.
-
- var ellipsoid = Ellipsoid.UNIT_SPHERE;
- polygon.ellipsoid = ellipsoid;
- polygon.granularity = CesiumMath.toRadians(20.0);
- polygon.configureExtent(new Extent(
- 0.0,
- 0.0,
- CesiumMath.toRadians(10.0),
- CesiumMath.toRadians(10.0)
- ));
- polygon.material.uniforms.color = {
- red : 1.0,
- green : 0.0,
- blue : 0.0,
- alpha : 1.0
- };
-
- ClearCommand.ALL.execute(context);
- expect(context.readPixels()).toEqual([0, 0, 0, 0]);
-
- render(context, frameState, polygon);
- expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
- });
-
it('does not render when show is false', function() {
polygon = createPolygon();
polygon.material.uniforms.color = {
@@ -274,7 +223,7 @@ defineSuite([
expect(render(context, frameState, polygon)).toEqual(0);
});
- it('does not render without positions due to duplicates', function() {
+ it('throws without positions due to duplicates', function() {
var ellipsoid = Ellipsoid.UNIT_SPHERE;
polygon = new Polygon();
@@ -285,10 +234,12 @@ defineSuite([
ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(0.0, 0.0, 0.0))
]);
- expect(render(context, frameState, polygon)).toEqual(0);
+ expect(function() {
+ render(context, frameState, polygon);
+ }).toThrow();
});
- it('does not render without hierarchy positions due to duplicates', function() {
+ it('throws without hierarchy positions due to duplicates', function() {
var ellipsoid = Ellipsoid.UNIT_SPHERE;
var hierarchy = {
positions : ellipsoid.cartographicArrayToCartesianArray([
@@ -309,22 +260,9 @@ defineSuite([
polygon.ellipsoid = ellipsoid;
polygon.configureFromPolygonHierarchy(hierarchy);
- expect(render(context, frameState, polygon)).toEqual(0);
- });
-
- it('does not render with empty extent', function() {
- var extent = new Extent(
- 0.0,
- 0.0,
- 0.0,
- 0.0
- );
-
- polygon = new Polygon();
- polygon.ellipsoid = Ellipsoid.UNIT_SPHERE;
- polygon.configureExtent(extent);
-
- expect(render(context, frameState, polygon)).toEqual(0);
+ expect(function () {
+ render(context, frameState, polygon);
+ }).toThrow();
});
it('is picked', function() {
@@ -355,7 +293,7 @@ defineSuite([
var commandList = [];
polygon.update(context, frameState, commandList);
var boundingVolume = commandList[0].colorList[0].boundingVolume;
- expect(boundingVolume).toEqual(BoundingSphere.fromPoints(polygon._positions));
+ expect(boundingVolume).toEqual(BoundingSphere.fromPoints(polygon.getPositions()));
});
function test2DBoundingSphereFromPositions(testMode) {
@@ -380,18 +318,13 @@ defineSuite([
var boundingVolume = commandList[0].colorList[0].boundingVolume;
frameState.mode = mode;
- var projectedPositions = [];
- for (var i = 0; i < positions.length; ++i) {
- projectedPositions.push(projection.project(positions[i]));
- }
-
- var sphere = BoundingSphere.fromPoints(projectedPositions);
- sphere.center = new Cartesian3(0.0, sphere.center.x, sphere.center.y);
- expect(boundingVolume.center).toEqualEpsilon(sphere.center, CesiumMath.EPSILON9);
- expect(boundingVolume.radius).toEqualEpsilon(sphere.radius, CesiumMath.EPSILON9);
+ var sphere = BoundingSphere.projectTo2D(BoundingSphere.fromPoints(polygon.getPositions()));
+ sphere.center.x = (testMode === SceneMode.SCENE2D) ? 0.0 : sphere.center.x;
+ expect(boundingVolume.center).toEqualEpsilon(sphere.center, CesiumMath.EPSILON2);
+ expect(boundingVolume.radius).toEqualEpsilon(sphere.radius, CesiumMath.EPSILON2);
}
- it('test 2D bounding sphere from positions', function() {
+ it('test Columbus view bounding sphere from positions', function() {
test2DBoundingSphereFromPositions(SceneMode.COLUMBUS_VIEW);
});
@@ -399,57 +332,6 @@ defineSuite([
test2DBoundingSphereFromPositions(SceneMode.SCENE2D);
});
- it('test 3D bounding sphere from extent', function() {
- var ellipsoid = Ellipsoid.UNIT_SPHERE;
- var extent = new Extent(
- 0.0,
- 0.0,
- CesiumMath.toRadians(10.0),
- CesiumMath.toRadians(10.0));
-
- var polygon = new Polygon();
- polygon.ellipsoid = ellipsoid;
- polygon.configureExtent(extent);
-
- var commandList = [];
- polygon.update(context, frameState, commandList);
- var boundingVolume = commandList[0].colorList[0].boundingVolume;
- expect(boundingVolume).toEqual(BoundingSphere.fromExtent3D(extent, ellipsoid));
- });
-
- function test2DBoundingSphereFromExtent(testMode) {
- var projection = frameState.scene2D.projection;
- var ellipsoid = projection.getEllipsoid();
- var extent = new Extent(
- 0.0,
- 0.0,
- CesiumMath.toRadians(10.0),
- CesiumMath.toRadians(10.0));
-
- var polygon = new Polygon();
- polygon.ellipsoid = ellipsoid;
- polygon.configureExtent(extent);
-
- var mode = frameState.mode;
- frameState.mode = testMode;
- var commandList = [];
- polygon.update(context, frameState, commandList);
- var boundingVolume = commandList[0].colorList[0].boundingVolume;
- frameState.mode = mode;
-
- var sphere = BoundingSphere.fromExtent2D(extent, projection);
- sphere.center = new Cartesian3(0.0, sphere.center.x, sphere.center.y);
- expect(boundingVolume).toEqualEpsilon(sphere, CesiumMath.EPSILON9);
- }
-
- it('test 2D bounding sphere from extent', function() {
- test2DBoundingSphereFromExtent(SceneMode.COLUMBUS_VIEW);
- });
-
- it('test 2D bounding sphere from extent', function() {
- test2DBoundingSphereFromExtent(SceneMode.SCENE2D);
- });
-
it('isDestroyed', function() {
var p = new Polygon();
expect(p.isDestroyed()).toEqual(false);
diff --git a/Specs/Scene/PolylineCollectionSpec.js b/Specs/Scene/PolylineCollectionSpec.js
index 381e70b3fba2..aa2f1891f14a 100644
--- a/Specs/Scene/PolylineCollectionSpec.js
+++ b/Specs/Scene/PolylineCollectionSpec.js
@@ -430,7 +430,7 @@ defineSuite([
it('renders 64K vertices of same polyline', function() {
var positions = [];
- for ( var i = 0; i < (64 * 1024) / 2; ++i) {
+ for ( var i = 0; i < CesiumMath.SIXTY_FOUR_KILOBYTES / 2; ++i) {
positions.push({
x : 0,
y : -1,
@@ -455,7 +455,7 @@ defineSuite([
it('creates two vertex arrays and renders', function() {
var positions = [];
- for ( var i = 0; i < (64 * 1024) / 2; ++i) {
+ for ( var i = 0; i < CesiumMath.SIXTY_FOUR_KILOBYTES / 2; ++i) {
positions.push({
x : 0,
y : -1,
@@ -498,7 +498,7 @@ defineSuite([
it('renders more than 64K vertices of same polyline', function() {
var positions = [];
- for ( var i = 0; i < 64 * 1024; ++i) {
+ for ( var i = 0; i < CesiumMath.SIXTY_FOUR_KILOBYTES; ++i) {
positions.push({
x : 0,
y : -1,
@@ -757,7 +757,7 @@ defineSuite([
it('renders more than 64K vertices of different polylines', function() {
var positions = [];
- for ( var i = 0; i < 64 * 1024; ++i) {
+ for ( var i = 0; i < CesiumMath.SIXTY_FOUR_KILOBYTES; ++i) {
positions.push({
x : -1,
y : -1,
@@ -797,7 +797,7 @@ defineSuite([
it('renders more than 64K vertices of different polylines of different widths', function() {
var positions = [];
- for ( var i = 0; i < 64 * 1024 - 2; ++i) {
+ for ( var i = 0; i < CesiumMath.SIXTY_FOUR_KILOBYTES - 2; ++i) {
positions.push({
x : -1,
y : -1,
diff --git a/Specs/Scene/PrimitiveSpec.js b/Specs/Scene/PrimitiveSpec.js
new file mode 100644
index 000000000000..eb3bc352d6f5
--- /dev/null
+++ b/Specs/Scene/PrimitiveSpec.js
@@ -0,0 +1,595 @@
+/*global defineSuite*/
+defineSuite([
+ 'Scene/Primitive',
+ 'Core/ExtentGeometry',
+ 'Core/Geometry',
+ 'Core/GeometryAttribute',
+ 'Core/GeometryInstance',
+ 'Core/ColorGeometryInstanceAttribute',
+ 'Core/ShowGeometryInstanceAttribute',
+ 'Core/GeometryInstanceAttribute',
+ 'Core/ComponentDatatype',
+ 'Core/Cartesian3',
+ 'Core/Matrix4',
+ 'Core/Extent',
+ 'Core/Ellipsoid',
+ 'Core/PrimitiveType',
+ 'Renderer/ClearCommand',
+ 'Scene/MaterialAppearance',
+ 'Scene/PerInstanceColorAppearance',
+ 'Scene/SceneMode',
+ 'Scene/OrthographicFrustum',
+ 'Specs/render',
+ 'Specs/pick',
+ 'Specs/createCanvas',
+ 'Specs/destroyCanvas',
+ 'Specs/createContext',
+ 'Specs/destroyContext',
+ 'Specs/createFrameState'
+ ], function(
+ Primitive,
+ ExtentGeometry,
+ Geometry,
+ GeometryAttribute,
+ GeometryInstance,
+ ColorGeometryInstanceAttribute,
+ ShowGeometryInstanceAttribute,
+ GeometryInstanceAttribute,
+ ComponentDatatype,
+ Cartesian3,
+ Matrix4,
+ Extent,
+ Ellipsoid,
+ PrimitiveType,
+ ClearCommand,
+ MaterialAppearance,
+ PerInstanceColorAppearance,
+ SceneMode,
+ OrthographicFrustum,
+ render,
+ pick,
+ createCanvas,
+ destroyCanvas,
+ createContext,
+ destroyContext,
+ createFrameState) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var context;
+ var frameState;
+ var us;
+
+ var ellipsoid;
+
+ var extent1;
+ var extent2;
+
+ var extentInstance1;
+ var extentInstance2;
+
+ beforeAll(function() {
+ context = createContext();
+ frameState = createFrameState();
+
+ us = context.getUniformState();
+ us.update(frameState);
+
+ ellipsoid = Ellipsoid.WGS84;
+ });
+
+ afterAll(function() {
+ destroyContext(context);
+ });
+
+ beforeEach(function() {
+ extent1 = Extent.fromDegrees(-80.0, 20.0, -70.0, 30.0);
+ extent2 = Extent.fromDegrees(70.0, 20.0, 80.0, 30.0);
+
+ var translation = ellipsoid.cartographicToCartesian(extent1.getCenter()).normalize().multiplyByScalar(2.0);
+ extentInstance1 = new GeometryInstance({
+ geometry : new ExtentGeometry({
+ vertexFormat : PerInstanceColorAppearance.VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ extent : extent1
+ }),
+ modelMatrix : Matrix4.fromTranslation(translation),
+ id : 'extent1',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0),
+ show : new ShowGeometryInstanceAttribute(true)
+ }
+ });
+
+ translation = ellipsoid.cartographicToCartesian(extent2.getCenter()).normalize().multiplyByScalar(3.0);
+ extentInstance2 = new GeometryInstance({
+ geometry : new ExtentGeometry({
+ vertexFormat : PerInstanceColorAppearance.VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ extent : extent2
+ }),
+ modelMatrix : Matrix4.fromTranslation(translation),
+ id : 'extent2',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 1.0),
+ show : new ShowGeometryInstanceAttribute(true)
+ }
+ });
+ });
+
+ it('default constructs', function() {
+ var primitive = new Primitive();
+ expect(primitive.geometryInstances).not.toBeDefined();
+ expect(primitive.appearance).not.toBeDefined();
+ expect(primitive.modelMatrix).toEqual(Matrix4.IDENTITY);
+ expect(primitive.show).toEqual(true);
+ });
+
+ it('releases geometry instances when releaseGeometryInstances is true', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ releaseGeometryInstances : true
+ });
+
+ expect(primitive.geometryInstances).toBeDefined();
+ primitive.update(context, frameState, []);
+ expect(primitive.geometryInstances).not.toBeDefined();
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('does not release geometry instances when releaseGeometryInstances is false', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ releaseGeometryInstances : false
+ });
+
+ expect(primitive.geometryInstances).toBeDefined();
+ primitive.update(context, frameState, []);
+ expect(primitive.geometryInstances).toBeDefined();
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('does not render when show is false', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ var commands = [];
+ primitive.update(context, frameState, commands);
+ expect(commands.length).toBeGreaterThan(0);
+
+ commands.length = 0;
+ primitive.show = false;
+ primitive.update(context, frameState, commands);
+ expect(commands.length).toEqual(0);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('does not render other than for the color or pick pass', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ frameState.passes.color = false;
+ frameState.passes.pick = false;
+
+ var commands = [];
+ primitive.update(context, frameState, commands);
+ expect(commands.length).toEqual(0);
+
+ frameState.passes.color = true;
+ frameState.passes.pick = true;
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('does not render when allow3DOnly is true and the scene mode is SCENE2D', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ frameState.mode = SceneMode.SCENE2D;
+
+ var commands = [];
+ primitive.update(context, frameState, commands);
+ expect(commands.length).toEqual(0);
+
+ frameState.mode = SceneMode.SCENE3D;
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('does not render when allow3DOnly is true and the scene mode is COLUMBUS_VIEW', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ frameState.mode = SceneMode.COLUMBUS_VIEW;
+
+ var commands = [];
+ primitive.update(context, frameState, commands);
+ expect(commands.length).toEqual(0);
+
+ frameState.mode = SceneMode.SCENE3D;
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders in Columbus view when allow3DOnly is false', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : false
+ });
+
+ frameState.mode = SceneMode.COLUMBUS_VIEW;
+ frameState.morphTime = frameState.mode.morphTime;
+ frameState.camera.transform = new Matrix4(0.0, 0.0, 1.0, 0.0,
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+ frameState.camera.controller.update(frameState.mode, frameState.scene2D);
+
+ frameState.camera.controller.viewExtent(extent1);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ frameState.camera.controller.viewExtent(extent2);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ frameState = createFrameState(); // reset frame state
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('renders in 2D when allow3DOnly is false', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : false
+ });
+
+ frameState.mode = SceneMode.SCENE2D;
+ frameState.morphTime = frameState.mode.morphTime;
+ frameState.camera.transform = new Matrix4(0.0, 0.0, 1.0, 0.0,
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+ var frustum = new OrthographicFrustum();
+ frustum.right = Ellipsoid.WGS84.getMaximumRadius() * Math.PI;
+ frustum.left = -frustum.right;
+ frustum.top = frustum.right;
+ frustum.bottom = -frustum.top;
+ frameState.camera.frustum = frustum;
+ frameState.camera.controller.update(frameState.mode, frameState.scene2D);
+
+ frameState.camera.controller.viewExtent(extent1);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ frameState.camera.controller.viewExtent(extent2);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ frameState = createFrameState(); // reset frame state
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('transforms to world coordinates', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ frameState.camera.controller.viewExtent(extent1);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ frameState.camera.controller.viewExtent(extent2);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ expect(primitive.modelMatrix).toEqual(Matrix4.IDENTITY);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('does not transform to world coordinates', function() {
+ extentInstance2.modelMatrix = Matrix4.clone(extentInstance1.modelMatrix);
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ frameState.camera.controller.viewExtent(extent1);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ frameState.camera.controller.viewExtent(extent2);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ expect(primitive.modelMatrix).not.toEqual(Matrix4.IDENTITY);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('get common per instance attributes', function() {
+ extentInstance2.attributes.not_used = new GeometryInstanceAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 1,
+ value : [0.5]
+ });
+
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+ primitive.update(context, frameState, []);
+
+ var attributes = primitive.getGeometryInstanceAttributes('extent1');
+ expect(attributes.color).toBeDefined();
+ expect(attributes.show).toBeDefined();
+
+ attributes = primitive.getGeometryInstanceAttributes('extent2');
+ expect(attributes.color).toBeDefined();
+ expect(attributes.show).toBeDefined();
+ expect(attributes.not_used).not.toBeDefined();
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('modify color instance attribute', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance1,
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ frameState.camera.controller.viewExtent(extent1);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ var pixels = context.readPixels();
+ expect(pixels).not.toEqual([0, 0, 0, 0]);
+
+ var attributes = primitive.getGeometryInstanceAttributes('extent1');
+ expect(attributes.color).toBeDefined();
+ attributes.color = [255, 255, 255, 255];
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ var newPixels = context.readPixels();
+ expect(newPixels).not.toEqual([0, 0, 0, 0]);
+ expect(newPixels).not.toEqual(pixels);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('modify show instance attribute', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance1,
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ frameState.camera.controller.viewExtent(extent1);
+ us.update(frameState);
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]);
+
+ var attributes = primitive.getGeometryInstanceAttributes('extent1');
+ expect(attributes.show).toBeDefined();
+ attributes.show = [0];
+
+ ClearCommand.ALL.execute(context);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ render(context, frameState, primitive);
+ expect(context.readPixels()).toEqual([0, 0, 0, 0]);
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('picking', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ frameState.camera.controller.viewExtent(extent1);
+ us.update(frameState);
+
+ expect(pick(context, frameState, primitive)).toEqual('extent1');
+
+ frameState.camera.controller.viewExtent(extent2);
+ us.update(frameState);
+
+ expect(pick(context, frameState, primitive)).toEqual('extent2');
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('update throws when geometry primitive types are different', function() {
+ var primitive = new Primitive({
+ geometryInstances : [
+ new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : new Float32Array([1.0, 2.0, 3.0, 4.0])
+ })
+ },
+ primitiveType : PrimitiveType.LINES
+ })
+ }),
+ new GeometryInstance({
+ geometry : new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 3,
+ values : new Float32Array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
+ })
+ },
+ primitiveType : PrimitiveType.TRIANGLES
+ })
+ })
+ ],
+ appearance : new PerInstanceColorAppearance()
+ });
+
+ expect(function() {
+ primitive.update(context, frameState, []);
+ }).toThrow();
+ });
+
+ it('shader validation', function() {
+ var primitive = new Primitive({
+ geometryInstances : [extentInstance1, extentInstance2],
+ allow3DOnly : true,
+ appearance : new MaterialAppearance({
+ materialSupport : MaterialAppearance.MaterialSupport.ALL
+ })
+ });
+
+ expect(function() {
+ primitive.update(context, frameState, []);
+ }).toThrow();
+ });
+
+ it('setting per instance attribute throws when value is undefined', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance1,
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ primitive.update(context, frameState, []);
+ var attributes = primitive.getGeometryInstanceAttributes('extent1');
+
+ expect(function() {
+ attributes.color = undefined;
+ }).toThrow();
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('getGeometryInstanceAttributes throws without id', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance1,
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ primitive.update(context, frameState, []);
+
+ expect(function() {
+ primitive.getGeometryInstanceAttributes();
+ }).toThrow();
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('getGeometryInstanceAttributes throws if update was not called', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance1,
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ expect(function() {
+ primitive.getGeometryInstanceAttributes('extent1');
+ }).toThrow();
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('getGeometryInstanceAttributes returns undefined if id does not exist', function() {
+ var primitive = new Primitive({
+ geometryInstances : extentInstance1,
+ appearance : new PerInstanceColorAppearance(),
+ allow3DOnly : true
+ });
+
+ primitive.update(context, frameState, []);
+
+ expect(primitive.getGeometryInstanceAttributes('unknown')).not.toBeDefined();
+
+ primitive = primitive && primitive.destroy();
+ });
+
+ it('isDestroyed', function() {
+ var p = new Primitive();
+ expect(p.isDestroyed()).toEqual(false);
+ p.destroy();
+ expect(p.isDestroyed()).toEqual(true);
+ });
+
+});
diff --git a/Specs/Scene/createTangentSpaceDebugPrimitiveSpec.js b/Specs/Scene/createTangentSpaceDebugPrimitiveSpec.js
new file mode 100644
index 000000000000..d07b428ebb03
--- /dev/null
+++ b/Specs/Scene/createTangentSpaceDebugPrimitiveSpec.js
@@ -0,0 +1,63 @@
+/*global defineSuite*/
+defineSuite([
+ 'Scene/createTangentSpaceDebugPrimitive',
+ 'Core/EllipsoidGeometry',
+ 'Core/Cartesian3',
+ 'Core/Ellipsoid',
+ 'Core/Matrix4',
+ 'Core/VertexFormat',
+ 'Core/PrimitiveType'
+ ], function(
+ createTangentSpaceDebugPrimitive,
+ EllipsoidGeometry,
+ Cartesian3,
+ Ellipsoid,
+ Matrix4,
+ VertexFormat,
+ PrimitiveType) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+
+ it('computes all attributes', function() {
+ var geometry = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.ALL,
+ radii : new Cartesian3(500000.0, 500000.0, 1000000.0)
+ });
+ var modelMatrix = Matrix4.multiplyByTranslation(Matrix4.IDENTITY, new Cartesian3(0.0, 0.0, 11000000.0));
+
+ var primitive = createTangentSpaceDebugPrimitive({
+ geometry : geometry,
+ modelMatrix : modelMatrix,
+ length : 1000.0
+ });
+
+ expect(primitive.geometryInstances).toBeDefined();
+ expect(primitive.appearance).toBeDefined();
+
+ var instances = primitive.geometryInstances;
+ expect(instances.length).toEqual(3);
+
+ expect(instances[0].modelMatrix).toEqual(modelMatrix);
+ expect(instances[1].modelMatrix).toEqual(modelMatrix);
+ expect(instances[2].modelMatrix).toEqual(modelMatrix);
+
+ expect(instances[0].attributes).toBeDefined();
+ expect(instances[0].attributes.color).toBeDefined();
+ expect(instances[1].attributes).toBeDefined();
+ expect(instances[1].attributes.color).toBeDefined();
+ expect(instances[2].attributes).toBeDefined();
+ expect(instances[2].attributes.color).toBeDefined();
+
+ expect(instances[0].geometry.primitiveType).toEqual(PrimitiveType.LINES);
+ expect(instances[1].geometry.primitiveType).toEqual(PrimitiveType.LINES);
+ expect(instances[2].geometry.primitiveType).toEqual(PrimitiveType.LINES);
+ });
+
+ it('throws without geometry', function() {
+ expect(function() {
+ createTangentSpaceDebugPrimitive();
+ }).toThrow();
+ });
+
+});