Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve extruded polygon and wall performance. #2329

Merged
merged 9 commits into from
Dec 17, 2014
1 change: 0 additions & 1 deletion Apps/Sandcastle/gallery/Wall.html
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@
maximumHeights: maximumHeights,
minimumHeights: minimumHeights,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT

}),
attributes : {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE)
Expand Down
10 changes: 5 additions & 5 deletions Source/Core/IntersectionTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ define([
var sScratch = new Cartesian3();
var closestScratch = new Cartesian3();
var surfPointScratch = new Cartographic();

/**
* Provides the point along the ray which is nearest to the ellipsoid.
*
Expand All @@ -593,19 +594,18 @@ define([
var position = ray.origin;
var direction = ray.direction;

var normal = ellipsoid.geodeticSurfaceNormal(position);

var normal = ellipsoid.geodeticSurfaceNormal(position, firstAxisScratch);
if (Cartesian3.dot(direction, normal) >= 0.0) { // The location provided is the closest point in altitude
return position;
}

var intersects = defined(this.rayEllipsoid(ray, ellipsoid));

// Compute the scaled direction vector.
var f = ellipsoid.transformPositionToScaledSpace(direction);
var f = ellipsoid.transformPositionToScaledSpace(direction, firstAxisScratch);

// Constructs a basis from the unit scaled direction vector. Construct its rotation and transpose.
var firstAxis = Cartesian3.normalize(f, firstAxisScratch);
var firstAxis = Cartesian3.normalize(f, f);
var reference = Cartesian3.mostOrthogonalAxis(f, referenceScratch);
var secondAxis = Cartesian3.normalize(Cartesian3.cross(reference, firstAxis, secondAxisScratch), secondAxisScratch);
var thirdAxis = Cartesian3.normalize(Cartesian3.cross(firstAxis, secondAxis, thirdAxisScratch), thirdAxisScratch);
Expand Down Expand Up @@ -667,7 +667,7 @@ define([
altitude = Cartesian3.magnitude(Cartesian3.subtract(closest, position, referenceScratch)) * Math.sqrt(1.0 - maximumValue * maximumValue);
altitude = intersects ? -altitude : altitude;
surfacePoint.height = altitude;
return ellipsoid.cartographicToCartesian(surfacePoint);
return ellipsoid.cartographicToCartesian(surfacePoint, new Cartesian3());
}

return undefined;
Expand Down
41 changes: 35 additions & 6 deletions Source/Core/Math.js
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ define([
* @param {Number} value The value to constrain.
* @param {Number} min The minimum value.
* @param {Number} max The maximum value.
* @returns The value clamped so that min <= value <= max.
* @returns {Number} The value clamped so that min <= value <= max.
*/
CesiumMath.clamp = function(value, min, max) {
//>>includeStart('debug', pragmas.debug);
Expand Down Expand Up @@ -718,7 +718,7 @@ define([
* Generates a random number in the range of [0.0, 1.0)
* using a Mersenne twister.
*
* @returns A random number in the range of [0.0, 1.0).
* @returns {Number} A random number in the range of [0.0, 1.0).
*
* @see CesiumMath.setRandomNumberSeed
* @see {@link http://en.wikipedia.org/wiki/Mersenne_twister|Mersenne twister on Wikipedia}
Expand All @@ -731,25 +731,54 @@ define([
* Computes <code>Math.acos(value)</acode>, but first clamps <code>value</code> to the range [-1.0, 1.0]
* so that the function will never return NaN.
*
* @param value The value for which to compute acos.
* @returns {number} The acos of the value if the value is in the range [-1.0, 1.0], or the acos of -1.0 or 1.0,
* @param {Number} value The value for which to compute acos.
* @returns {Number} The acos of the value if the value is in the range [-1.0, 1.0], or the acos of -1.0 or 1.0,
* whichever is closer, if the value is outside the range.
*/
CesiumMath.acosClamped = function(value) {
//>>includeStart('debug', pragmas.debug);
if (!defined(value)) {
throw new DeveloperError('value is required.');
}
//>>includeEnd('debug');
return Math.acos(CesiumMath.clamp(value, -1.0, 1.0));
};

/**
* Computes <code>Math.asin(value)</acode>, but first clamps <code>value</code> to the range [-1.0, 1.0]
* so that the function will never return NaN.
*
* @param value The value for which to compute asin.
* @returns {number} The asin of the value if the value is in the range [-1.0, 1.0], or the asin of -1.0 or 1.0,
* @param {Number} value The value for which to compute asin.
* @returns {Number} The asin of the value if the value is in the range [-1.0, 1.0], or the asin of -1.0 or 1.0,
* whichever is closer, if the value is outside the range.
*/
CesiumMath.asinClamped = function(value) {
//>>includeStart('debug', pragmas.debug);
if (!defined(value)) {
throw new DeveloperError('value is required.');
}
//>>includeEnd('debug');
return Math.asin(CesiumMath.clamp(value, -1.0, 1.0));
};

/**
* Finds the chord length between two points given the circle's radius and the angle between the points.
*
* @param {Number} angle The angle between the two points.
* @param {Number} radius The radius of the circle.
* @returns {Number} The chord length.
*/
CesiumMath.chordLength = function(angle, radius) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests and developer errors.

Could also go in CHANGES.md, but probably not important enough.

//>>includeStart('debug', pragmas.debug);
if (!defined(angle)) {
throw new DeveloperError('angle is required.');
}
if (!defined(radius)) {
throw new DeveloperError('radius is required.');
}
//>>includeEnd('debug');
return 2.0 * radius * Math.sin(angle * 0.5);
};

return CesiumMath;
});
116 changes: 78 additions & 38 deletions Source/Core/PolygonGeometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,51 +329,89 @@ define([
return geometry;
}

var computeWallIndicesSubdivided = [];

function computeWallIndices(positions, ellipsoid, granularity, perPositionHeight){
var edgePositions = [];
var subdividedEdge;
var edgeIndex;
var UL, UR, LL, LR;
var edgePositions;
var topEdgeLength;
var i;

var length = positions.length;
var p1;
var p2;

var length = positions.length;
var index = 0;

if (!perPositionHeight) {
var radius = ellipsoid.maximumRadius;
var minDistance = 2.0 * radius * Math.sin(granularity * 0.5);
var minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius);

var numVertices = 0;
for (i = 0; i < length; i++) {
numVertices += PolygonGeometryLibrary.subdivideLineCount(positions[i], positions[(i + 1) % length], minDistance);
}

topEdgeLength = (numVertices + length) * 3;
edgePositions = new Array(topEdgeLength * 2);
for (i = 0; i < length; i++) {
p1 = positions[i];
p2 = positions[(i + 1) % length];
subdividedEdge = PolygonGeometryLibrary.subdivideLine(p1, p2, minDistance);
subdividedEdge.push(p2.x, p2.y, p2.z);
edgePositions = edgePositions.concat(subdividedEdge);

var tempPositions = PolygonGeometryLibrary.subdivideLine(p1, p2, minDistance, computeWallIndicesSubdivided);
var tempPositionsLength = tempPositions.length;
for (var j = 0; j < tempPositionsLength; ++j, ++index) {
edgePositions[index] = tempPositions[j];
edgePositions[index + topEdgeLength] = tempPositions[j];
}

edgePositions[index] = p2.x;
edgePositions[index + topEdgeLength] = p2.x;
++index;

edgePositions[index] = p2.y;
edgePositions[index + topEdgeLength] = p2.y;
++index;

edgePositions[index] = p2.z;
edgePositions[index + topEdgeLength] = p2.z;
++index;
}
} else {
topEdgeLength = length * 3 * 2;
edgePositions = new Array(topEdgeLength * 2);
for (i = 0; i < length; i++) {
p1 = positions[i];
p2 = positions[(i + 1) % length];
edgePositions.push(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
edgePositions[index] = edgePositions[index + topEdgeLength] = p1.x;
++index;
edgePositions[index] = edgePositions[index + topEdgeLength] = p1.y;
++index;
edgePositions[index] = edgePositions[index + topEdgeLength] = p1.z;
++index;
edgePositions[index] = edgePositions[index + topEdgeLength] = p2.x;
++index;
edgePositions[index] = edgePositions[index + topEdgeLength] = p2.y;
++index;
edgePositions[index] = edgePositions[index + topEdgeLength] = p2.z;
++index;
}
}

edgePositions = edgePositions.concat(edgePositions);
length = edgePositions.length;
var indices = IndexDatatype.createTypedArray(length / 3, length - positions.length * 6);
edgeIndex = 0;
var edgeIndex = 0;
length /= 6;

for (i = 0; i < length; i++) {
UL = i;
UR = UL + 1;
var UL = i;
var UR = UL + 1;
var LL = UL + length;
var LR = LL + 1;

p1 = Cartesian3.fromArray(edgePositions, UL * 3, p1Scratch);
p2 = Cartesian3.fromArray(edgePositions, UR * 3, p2Scratch);
if (Cartesian3.equalsEpsilon(p1, p2, CesiumMath.EPSILON6)) {
continue;
}
LL = UL + length;
LR = LL + 1;

indices[edgeIndex++] = UL;
indices[edgeIndex++] = LL;
indices[edgeIndex++] = UR;
Expand All @@ -399,15 +437,20 @@ define([

function createGeometryFromPositionsExtruded(ellipsoid, positions, granularity, hierarchy, perPositionHeight) {
var topGeo = createGeometryFromPositions(ellipsoid, positions, granularity, perPositionHeight).geometry;

var edgePoints = topGeo.attributes.position.values;
var indices = topGeo.indices;

var topBottomPositions = edgePoints.concat(edgePoints);
var numPositions = topBottomPositions.length / 3;

var newIndices = IndexDatatype.createTypedArray(numPositions, indices.length * 2);
newIndices.set(indices);
var ilength = indices.length;

var i;
var length = numPositions / 2;

for (i = 0; i < ilength; i += 3) {
var i0 = newIndices[i] + length;
var i1 = newIndices[i + 1] + length;
Expand All @@ -417,6 +460,7 @@ define([
newIndices[i + 1 + ilength] = i1;
newIndices[i + 2 + ilength] = i0;
}

var topAndBottomGeo = new Geometry({
attributes : new GeometryAttributes({
position : new GeometryAttribute({
Expand All @@ -432,10 +476,10 @@ define([
var geos = {
topAndBottom : new GeometryInstance({
geometry : topAndBottomGeo
})
}),
walls : []
};

geos.walls = [];
var outerRing = hierarchy.outerRing;
var tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid);
var positions2D = tangentPlane.projectPointsOntoPlane(outerRing, createGeometryFromPositionsExtrudedPositions);
Expand Down Expand Up @@ -732,29 +776,25 @@ define([
if (extrude) {
for (i = 0; i < polygons.length; i++) {
geometry = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], granularity, polygonHierarchy[i], perPositionHeight);
if (defined(geometry)) {
topAndBottom = geometry.topAndBottom;
topAndBottom.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(topAndBottom.geometry, height, extrudedHeight, ellipsoid, perPositionHeight);
topAndBottom.geometry = computeAttributes(vertexFormat, topAndBottom.geometry, outerPositions, ellipsoid, stRotation, true, false);
geometries.push(topAndBottom);

walls = geometry.walls;
for ( var k = 0; k < walls.length; k++) {
var wall = walls[k];
wall.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(wall.geometry, height, extrudedHeight, ellipsoid, perPositionHeight);
wall.geometry = computeAttributes(vertexFormat, wall.geometry, outerPositions, ellipsoid, stRotation, true, true);
geometries.push(wall);
}
topAndBottom = geometry.topAndBottom;
topAndBottom.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(topAndBottom.geometry, height, extrudedHeight, ellipsoid, perPositionHeight);
topAndBottom.geometry = computeAttributes(vertexFormat, topAndBottom.geometry, outerPositions, ellipsoid, stRotation, true, false);
geometries.push(topAndBottom);

walls = geometry.walls;
for ( var k = 0; k < walls.length; k++) {
var wall = walls[k];
wall.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(wall.geometry, height, extrudedHeight, ellipsoid, perPositionHeight);
wall.geometry = computeAttributes(vertexFormat, wall.geometry, outerPositions, ellipsoid, stRotation, true, true);
geometries.push(wall);
}
}
} else {
for (i = 0; i < polygons.length; i++) {
geometry = createGeometryFromPositions(ellipsoid, polygons[i], granularity, perPositionHeight);
if (defined(geometry)) {
geometry.geometry = PolygonPipeline.scaleToGeodeticHeight(geometry.geometry, height, ellipsoid, !perPositionHeight);
geometry.geometry = computeAttributes(vertexFormat, geometry.geometry, outerPositions, ellipsoid, stRotation, false, false);
geometries.push(geometry);
}
geometry.geometry = PolygonPipeline.scaleToGeodeticHeight(geometry.geometry, height, ellipsoid, !perPositionHeight);
geometry.geometry = computeAttributes(vertexFormat, geometry.geometry, outerPositions, ellipsoid, stRotation, false, false);
geometries.push(geometry);
}
}

Expand Down
4 changes: 1 addition & 3 deletions Source/Core/PolygonOutlineGeometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,7 @@ define([

var geometry;
var geometries = [];

var radius = ellipsoid.maximumRadius;
var minDistance = 2.0 * radius * Math.sin(granularity * 0.5);
var minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius);

if (extrude) {
for (i = 0; i < polygons.length; i++) {
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/PolygonPipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ define([
var edges = {};

var radius = ellipsoid.maximumRadius;
var minDistance = 2.0 * radius * Math.sin(granularity * 0.5);
var minDistance = CesiumMath.chordLength(granularity, radius);
var minDistanceSqrd = minDistance * minDistance;

while (triangles.length > 0) {
Expand Down
12 changes: 7 additions & 5 deletions Source/Core/PolylineGeometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ define([
VertexFormat) {
"use strict";

function interpolateColors(p0, p1, color0, color1, granularity) {
var numPoints = PolylinePipeline.numberOfPoints(p0, p1, granularity);
function interpolateColors(p0, p1, color0, color1, minDistance) {
var numPoints = PolylinePipeline.numberOfPoints(p0, p1, minDistance);
var colors = new Array(numPoints);
var i;

Expand Down Expand Up @@ -160,6 +160,8 @@ define([
var granularity = polylineGeometry._granularity;
var ellipsoid = polylineGeometry._ellipsoid;

var minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius);

var i;
var j;
var k;
Expand Down Expand Up @@ -192,9 +194,9 @@ define([

if (perVertex && i < colors.length) {
c1 = colors[i+1];
newColors = newColors.concat(interpolateColors(p0, p1, c0, c1, granularity));
newColors = newColors.concat(interpolateColors(p0, p1, c0, c1, minDistance));
} else {
var l = PolylinePipeline.numberOfPoints(p0, p1, granularity);
var l = PolylinePipeline.numberOfPoints(p0, p1, minDistance);
for (j = 0; j < l; j++) {
newColors.push(Color.clone(c0));
}
Expand All @@ -206,7 +208,7 @@ define([

positions = PolylinePipeline.generateCartesianArc({
positions: positions,
granularity: granularity,
minDistance: minDistance,
ellipsoid: ellipsoid,
height: heights
});
Expand Down
Loading