diff --git a/Apps/Sandcastle/gallery/development/Ground Polygon.html b/Apps/Sandcastle/gallery/development/Ground Polygon.html
new file mode 100644
index 000000000000..275240ca6bce
--- /dev/null
+++ b/Apps/Sandcastle/gallery/development/Ground Polygon.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+Loading...
+
+
+
+
diff --git a/Source/Core/PolygonGeometry.js b/Source/Core/PolygonGeometry.js
index 915c81832ea7..81167788ec16 100644
--- a/Source/Core/PolygonGeometry.js
+++ b/Source/Core/PolygonGeometry.js
@@ -20,9 +20,7 @@ define([
'./Matrix3',
'./PolygonGeometryLibrary',
'./PolygonPipeline',
- './PrimitiveType',
'./Quaternion',
- './Queue',
'./VertexFormat',
'./WindingOrder'
], function(
@@ -46,9 +44,7 @@ define([
Matrix3,
PolygonGeometryLibrary,
PolygonPipeline,
- PrimitiveType,
Quaternion,
- Queue,
VertexFormat,
WindingOrder) {
"use strict";
@@ -89,55 +85,6 @@ define([
return result;
}
- var createGeometryFromPositionsPositions = [];
-
- function createGeometryFromPositions(ellipsoid, positions, granularity, perPositionHeight) {
- var tangentPlane = EllipsoidTangentPlane.fromPoints(positions, ellipsoid);
- var positions2D = tangentPlane.projectPointsOntoPlane(positions, createGeometryFromPositionsPositions);
-
- var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D);
- if (originalWindingOrder === WindingOrder.CLOCKWISE) {
- positions2D.reverse();
- positions.reverse();
- }
-
- var indices = PolygonPipeline.triangulate(positions2D);
- /* If polygon is completely unrenderable, just use the first three vertices */
- if (indices.length < 3) {
- indices = [0, 1, 2];
- }
-
- var geo;
- if (!perPositionHeight) {
- geo = PolygonPipeline.computeSubdivision(ellipsoid, positions, indices, granularity);
- } else {
- var length = positions.length;
- var flattenedPositions = new Array(length * 3);
- var index = 0;
- for ( var i = 0; i < length; i++) {
- var p = positions[i];
- flattenedPositions[index++] = p.x;
- flattenedPositions[index++] = p.y;
- flattenedPositions[index++] = p.z;
- }
- geo = new Geometry({
- attributes : {
- position : new GeometryAttribute({
- componentDatatype : ComponentDatatype.DOUBLE,
- componentsPerAttribute : 3,
- values : flattenedPositions
- })
- },
- indices : indices,
- primitiveType : PrimitiveType.TRIANGLES
- });
- }
-
- return new GeometryInstance({
- geometry : geo
- });
- }
-
var scratchBoundingRectangle = new BoundingRectangle();
var scratchPosition = new Cartesian3();
var scratchNormal = new Cartesian3();
@@ -329,114 +276,10 @@ define([
return geometry;
}
- var computeWallIndicesSubdivided = [];
-
- function computeWallIndices(positions, ellipsoid, granularity, perPositionHeight){
- var edgePositions;
- var topEdgeLength;
- var i;
- var p1;
- var p2;
-
- var length = positions.length;
- var index = 0;
-
- if (!perPositionHeight) {
- 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];
-
- 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[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;
- }
- }
-
- length = edgePositions.length;
- var indices = IndexDatatype.createTypedArray(length / 3, length - positions.length * 6);
- var edgeIndex = 0;
- length /= 6;
-
- for (i = 0; i < length; i++) {
- 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.EPSILON14)) {
- continue;
- }
-
- indices[edgeIndex++] = UL;
- indices[edgeIndex++] = LL;
- indices[edgeIndex++] = UR;
- indices[edgeIndex++] = UR;
- indices[edgeIndex++] = LL;
- indices[edgeIndex++] = LR;
- }
-
- return new Geometry({
- attributes : new GeometryAttributes({
- position : new GeometryAttribute({
- componentDatatype : ComponentDatatype.DOUBLE,
- componentsPerAttribute : 3,
- values : edgePositions
- })
- }),
- indices : indices,
- primitiveType : PrimitiveType.TRIANGLES
- });
- }
-
var createGeometryFromPositionsExtrudedPositions = [];
function createGeometryFromPositionsExtruded(ellipsoid, positions, granularity, hierarchy, perPositionHeight) {
- var topGeo = createGeometryFromPositions(ellipsoid, positions, granularity, perPositionHeight).geometry;
+ var topGeo = PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, positions, granularity, perPositionHeight);
var edgePoints = topGeo.attributes.position.values;
var indices = topGeo.indices;
@@ -489,7 +332,7 @@ define([
outerRing.reverse();
}
- var wallGeo = computeWallIndices(outerRing, ellipsoid, granularity, perPositionHeight);
+ var wallGeo = PolygonGeometryLibrary.computeWallGeometry(outerRing, ellipsoid, granularity, perPositionHeight);
geos.walls.push(new GeometryInstance({
geometry : wallGeo
}));
@@ -506,7 +349,7 @@ define([
hole.reverse();
}
- wallGeo = computeWallIndices(hole, ellipsoid, granularity);
+ wallGeo = PolygonGeometryLibrary.computeWallGeometry(hole, ellipsoid, granularity);
geos.walls.push(new GeometryInstance({
geometry : wallGeo
}));
@@ -812,50 +655,9 @@ define([
var topAndBottom;
var outerPositions;
- // create from a polygon hierarchy
- // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
- var polygons = [];
- var queue = new Queue();
- queue.enqueue(polygonHierarchy);
- polygonHierarchy = [];
- var i;
- while (queue.length !== 0) {
- var outerNode = queue.dequeue();
- var outerRing = outerNode.positions;
- var holes = outerNode.holes;
- outerRing = PolygonPipeline.removeDuplicates(outerRing);
- if (outerRing.length < 3) {
- continue;
- }
-
- var numChildren = defined(holes) ? holes.length : 0;
- var polygonHoles = [];
- for (i = 0; i < numChildren; i++) {
- var hole = holes[i];
- hole.positions = PolygonPipeline.removeDuplicates(hole.positions);
- if (hole.positions.length < 3) {
- continue;
- }
- polygonHoles.push(hole.positions);
-
- var numGrandchildren = 0;
- if (defined(hole.holes)) {
- numGrandchildren = hole.holes.length;
- }
-
- for ( var j = 0; j < numGrandchildren; j++) {
- queue.enqueue(hole.holes[j]);
- }
- }
-
- polygonHierarchy.push({
- outerRing : outerRing,
- holes : polygonHoles
- });
-
- var combinedPolygon = polygonHoles.length > 0 ? PolygonPipeline.eliminateHoles(outerRing, polygonHoles) : outerRing;
- polygons.push(combinedPolygon);
- }
+ var results = PolygonGeometryLibrary.polygonsFromHierarchy(polygonHierarchy);
+ var hierarchy = results.hierarchy;
+ var polygons = results.polygons;
if (polygons.length === 0) {
return undefined;
@@ -865,10 +667,11 @@ define([
var geometry;
var geometries = [];
+ var i;
if (extrude) {
for (i = 0; i < polygons.length; i++) {
- geometry = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], granularity, polygonHierarchy[i], perPositionHeight);
+ geometry = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], granularity, hierarchy[i], perPositionHeight);
topAndBottom = geometry.topAndBottom;
topAndBottom.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(topAndBottom.geometry, height, extrudedHeight, ellipsoid, perPositionHeight);
topAndBottom.geometry = computeAttributes(vertexFormat, topAndBottom.geometry, outerPositions, ellipsoid, stRotation, true, false);
@@ -884,7 +687,9 @@ define([
}
} else {
for (i = 0; i < polygons.length; i++) {
- geometry = createGeometryFromPositions(ellipsoid, polygons[i], granularity, perPositionHeight);
+ geometry = new GeometryInstance({
+ geometry : PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, polygons[i], granularity, perPositionHeight)
+ });
geometry.geometry = PolygonPipeline.scaleToGeodeticHeight(geometry.geometry, height, ellipsoid, !perPositionHeight);
geometry.geometry = computeAttributes(vertexFormat, geometry.geometry, outerPositions, ellipsoid, stRotation, false, false);
geometries.push(geometry);
diff --git a/Source/Core/PolygonGeometryLibrary.js b/Source/Core/PolygonGeometryLibrary.js
index f3014bbe82ce..855b73d66810 100644
--- a/Source/Core/PolygonGeometryLibrary.js
+++ b/Source/Core/PolygonGeometryLibrary.js
@@ -1,14 +1,36 @@
/*global define*/
define([
'./Cartesian3',
+ './ComponentDatatype',
'./defaultValue',
'./defined',
- './Ellipsoid'
+ './Ellipsoid',
+ './EllipsoidTangentPlane',
+ './Geometry',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './IndexDatatype',
+ './Math',
+ './PolygonPipeline',
+ './PrimitiveType',
+ './Queue',
+ './WindingOrder'
], function(
Cartesian3,
+ ComponentDatatype,
defaultValue,
defined,
- Ellipsoid) {
+ Ellipsoid,
+ EllipsoidTangentPlane,
+ Geometry,
+ GeometryAttribute,
+ GeometryAttributes,
+ IndexDatatype,
+ CesiumMath,
+ PolygonPipeline,
+ PrimitiveType,
+ Queue,
+ WindingOrder) {
"use strict";
/**
@@ -109,9 +131,6 @@ define([
return [distanceScratch.x, distanceScratch.y, distanceScratch.z];
}
- /**
- * @private
- */
PolygonGeometryLibrary.subdivideLineCount = function(p0, p1, minDistance) {
var distance = Cartesian3.distance(p0, p1);
var n = distance / minDistance;
@@ -119,9 +138,6 @@ define([
return Math.pow(2, countDivide);
};
- /**
- * @private
- */
PolygonGeometryLibrary.subdivideLine = function(p0, p1, minDistance, result) {
var numVertices = PolygonGeometryLibrary.subdivideLineCount(p0, p1, minDistance);
var length = Cartesian3.distance(p0, p1);
@@ -149,9 +165,7 @@ define([
var scaleToGeodeticHeightN2 = new Cartesian3();
var scaleToGeodeticHeightP1 = new Cartesian3();
var scaleToGeodeticHeightP2 = new Cartesian3();
- /**
- * @private
- */
+
PolygonGeometryLibrary.scaleToGeodeticHeightExtruded = function(geometry, maxHeight, minHeight, ellipsoid, perPositionHeight) {
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
@@ -188,5 +202,211 @@ define([
return geometry;
};
+ PolygonGeometryLibrary.polygonsFromHierarchy = function(polygonHierarchy) {
+ // create from a polygon hierarchy
+ // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
+ var polygons = [];
+ var hierarchy = [];
+
+ var queue = new Queue();
+ queue.enqueue(polygonHierarchy);
+
+ while (queue.length !== 0) {
+ var outerNode = queue.dequeue();
+ var outerRing = outerNode.positions;
+ var holes = outerNode.holes;
+
+ outerRing = PolygonPipeline.removeDuplicates(outerRing);
+ if (outerRing.length < 3) {
+ continue;
+ }
+
+ var numChildren = defined(holes) ? holes.length : 0;
+ var polygonHoles = [];
+
+ for (var i = 0; i < numChildren; i++) {
+ var hole = holes[i];
+ hole.positions = PolygonPipeline.removeDuplicates(hole.positions);
+ if (hole.positions.length < 3) {
+ continue;
+ }
+ polygonHoles.push(hole.positions);
+
+ var numGrandchildren = 0;
+ if (defined(hole.holes)) {
+ numGrandchildren = hole.holes.length;
+ }
+
+ for ( var j = 0; j < numGrandchildren; j++) {
+ queue.enqueue(hole.holes[j]);
+ }
+ }
+
+ hierarchy.push({
+ outerRing : outerRing,
+ holes : polygonHoles
+ });
+
+ var combinedPolygon = polygonHoles.length > 0 ? PolygonPipeline.eliminateHoles(outerRing, polygonHoles) : outerRing;
+ polygons.push(combinedPolygon);
+ }
+
+ return {
+ hierarchy : hierarchy,
+ polygons : polygons
+ };
+ };
+
+ var createGeometryFromPositionsPositions = [];
+
+ PolygonGeometryLibrary.createGeometryFromPositions = function(ellipsoid, positions, granularity, perPositionHeight) {
+ var tangentPlane = EllipsoidTangentPlane.fromPoints(positions, ellipsoid);
+ var positions2D = tangentPlane.projectPointsOntoPlane(positions, createGeometryFromPositionsPositions);
+
+ var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D);
+ if (originalWindingOrder === WindingOrder.CLOCKWISE) {
+ positions2D.reverse();
+ positions.reverse();
+ }
+
+ var indices = PolygonPipeline.triangulate(positions2D);
+ /* If polygon is completely unrenderable, just use the first three vertices */
+ if (indices.length < 3) {
+ indices = [0, 1, 2];
+ }
+
+ if (perPositionHeight) {
+ var length = positions.length;
+ var flattenedPositions = new Array(length * 3);
+ var index = 0;
+ for ( var i = 0; i < length; i++) {
+ var p = positions[i];
+ flattenedPositions[index++] = p.x;
+ flattenedPositions[index++] = p.y;
+ flattenedPositions[index++] = p.z;
+ }
+
+ return new Geometry({
+ attributes : {
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : flattenedPositions
+ })
+ },
+ indices : indices,
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ }
+
+ return PolygonPipeline.computeSubdivision(ellipsoid, positions, indices, granularity);
+ };
+
+ var computeWallIndicesSubdivided = [];
+ var p1Scratch = new Cartesian3();
+ var p2Scratch = new Cartesian3();
+
+ PolygonGeometryLibrary.computeWallGeometry = function(positions, ellipsoid, granularity, perPositionHeight) {
+ var edgePositions;
+ var topEdgeLength;
+ var i;
+ var p1;
+ var p2;
+
+ var length = positions.length;
+ var index = 0;
+
+ if (!perPositionHeight) {
+ 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];
+
+ 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[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;
+ }
+ }
+
+ length = edgePositions.length;
+ var indices = IndexDatatype.createTypedArray(length / 3, length - positions.length * 6);
+ var edgeIndex = 0;
+ length /= 6;
+
+ for (i = 0; i < length; i++) {
+ 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.EPSILON14)) {
+ continue;
+ }
+
+ indices[edgeIndex++] = UL;
+ indices[edgeIndex++] = LL;
+ indices[edgeIndex++] = UR;
+ indices[edgeIndex++] = UR;
+ indices[edgeIndex++] = LL;
+ indices[edgeIndex++] = LR;
+ }
+
+ return new Geometry({
+ attributes : new GeometryAttributes({
+ position : new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : edgePositions
+ })
+ }),
+ indices : indices,
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+ };
+
return PolygonGeometryLibrary;
});
\ No newline at end of file
diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js
index 3e3e360d7055..424587d566ba 100644
--- a/Source/Renderer/RenderState.js
+++ b/Source/Renderer/RenderState.js
@@ -77,7 +77,7 @@ define([
(stencilOperation === WebGLRenderingContext.INCR) ||
(stencilOperation === WebGLRenderingContext.DECR) ||
(stencilOperation === WebGLRenderingContext.INVERT) ||
- (stencilOperation === WebGLRenderingContext.INCREMENT_WRAP) ||
+ (stencilOperation === WebGLRenderingContext.INCR_WRAP) ||
(stencilOperation === WebGLRenderingContext.DECR_WRAP));
}
diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js
index 444d35c887d5..7b2c040548bd 100644
--- a/Source/Scene/GlobeDepth.js
+++ b/Source/Scene/GlobeDepth.js
@@ -163,6 +163,7 @@ define([
if (!defined(globeDepth._clearColorCommand)) {
globeDepth._clearColorCommand = new ClearCommand({
color : new Color(0.0, 0.0, 0.0, 0.0),
+ stencil : 0.0,
owner : globeDepth
});
}
diff --git a/Source/Scene/GroundPolygon.js b/Source/Scene/GroundPolygon.js
new file mode 100644
index 000000000000..de2f737c16d9
--- /dev/null
+++ b/Source/Scene/GroundPolygon.js
@@ -0,0 +1,424 @@
+/*global define*/
+define([
+ '../Core/BoundingSphere',
+ '../Core/Cartesian3',
+ '../Core/ComponentDatatype',
+ '../Core/defaultValue',
+ '../Core/defined',
+ '../Core/Ellipsoid',
+ '../Core/EllipsoidTangentPlane',
+ '../Core/EncodedCartesian3',
+ '../Core/IndexDatatype',
+ '../Core/Math',
+ '../Core/Matrix4',
+ '../Core/PolygonGeometryLibrary',
+ '../Core/PolygonPipeline',
+ '../Core/PrimitiveType',
+ '../Core/WindingOrder',
+ '../Renderer/BufferUsage',
+ '../Renderer/DrawCommand',
+ '../Renderer/ShaderSource',
+ '../Shaders/ShadowVolumeFS',
+ '../Shaders/ShadowVolumeVS',
+ './BlendingState',
+ './CullFace',
+ './Pass',
+ './StencilFunction',
+ './StencilOperation'
+ ], function(
+ BoundingSphere,
+ Cartesian3,
+ ComponentDatatype,
+ defaultValue,
+ defined,
+ Ellipsoid,
+ EllipsoidTangentPlane,
+ EncodedCartesian3,
+ IndexDatatype,
+ CesiumMath,
+ Matrix4,
+ PolygonGeometryLibrary,
+ PolygonPipeline,
+ PrimitiveType,
+ WindingOrder,
+ BufferUsage,
+ DrawCommand,
+ ShaderSource,
+ ShadowVolumeFS,
+ ShadowVolumeVS,
+ BlendingState,
+ CullFace,
+ Pass,
+ StencilFunction,
+ StencilOperation) {
+ "use strict";
+
+ var GroundPolygon = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
+ var granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
+ var polygonHierarchy = options.polygonHierarchy;
+
+ this._ellipsoid = ellipsoid;
+ this._granularity = granularity;
+ this._polygonHierarchy = polygonHierarchy;
+
+ this._boundingSphere = undefined;
+
+ this._va = undefined;
+ this._sp = undefined;
+ this._rs = undefined;
+
+ this.debugVolume = defaultValue(options.debugVolume, false);
+ this._debugVolumeCommand = undefined;
+
+ var that = this;
+ this._uniformMap = {
+ centralBodyMinimumAltitude : function() {
+ return -8500.0;
+ },
+ LODNegativeToleranceOverDistance : function() {
+ return -0.01;
+ }
+ };
+
+ this._stencilPassCommand = undefined;
+ this._colorPassCommand = undefined;
+ };
+
+ var attributeLocations = {
+ positionHigh : 0,
+ positionLow : 1,
+ normal : 2
+ };
+
+ function getSurfaceDelta(ellipsoid, granularity) {
+ var refDistance = ellipsoid.maximumRadius;
+ return refDistance - (refDistance * Math.cos(granularity / 2.0));
+ }
+
+ var scratchPosition = new Cartesian3();
+ var scratchNormal = new Cartesian3();
+ var scratchDeltaNormal = new Cartesian3();
+
+ // TODO: More than one level of the polygon hierarchy
+ function createShadowVolume(polygon, context) {
+ var polygonHierarchy = polygon._polygonHierarchy;
+ var ellipsoid = polygon._ellipsoid;
+ var granularity = polygon._granularity;
+
+ var results = PolygonGeometryLibrary.polygonsFromHierarchy(polygonHierarchy);
+ var positions = results.polygons[0];
+ var hierarchy = results.hierarchy[0];
+
+ var bottomCap = PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, positions, granularity, false);
+ var bottomPositions = bottomCap.attributes.position.values;
+ var numBottomCapVertices = bottomPositions.length / 3;
+ var numCapVertices = numBottomCapVertices + numBottomCapVertices;
+ var bottomIndices = bottomCap.indices;
+ var numBottomIndices = bottomIndices.length;
+ var numCapIndices = numBottomIndices + numBottomIndices;
+
+ var outerRing = hierarchy.outerRing;
+ var tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid);
+ var positions2D = tangentPlane.projectPointsOntoPlane(outerRing);
+
+ var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D);
+ if (windingOrder === WindingOrder.CLOCKWISE) {
+ outerRing.reverse();
+ }
+
+ var wall = PolygonGeometryLibrary.computeWallGeometry(outerRing, ellipsoid, granularity, false);
+ var walls = [wall];
+ var numWallVertices = wall.attributes.position.values.length / 3;
+ var numWallIndices = wall.indices.length;
+
+ var holes = hierarchy.holes;
+ var i;
+ var j;
+
+ for (i = 0; i < holes.length; i++) {
+ var hole = holes[i];
+
+ tangentPlane = EllipsoidTangentPlane.fromPoints(hole, ellipsoid);
+ positions2D = tangentPlane.projectPointsOntoPlane(hole);
+
+ windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D);
+ if (windingOrder === WindingOrder.COUNTER_CLOCKWISE) {
+ hole.reverse();
+ }
+
+ walls.push(PolygonGeometryLibrary.computeWallGeometry(hole, ellipsoid, granularity, false));
+ numWallVertices += wall.attributes.position.values.length / 3;
+ numWallIndices += wall.indices.length;
+ walls.push(wall);
+ }
+
+ var maxAlt = 8500.0; // TODO: get max alt of terrain
+ var surfaceDelta = getSurfaceDelta(ellipsoid, granularity);
+ var upDelta = maxAlt + surfaceDelta;
+
+ var numVertices = numCapVertices + numWallVertices;
+
+ var vbPositions = new Float32Array(numVertices * 3 * 2);
+ var vbNormals = new Float32Array(numVertices * 3);
+
+ var position;
+ var normal;
+ var topPosition;
+
+ var index = 0;
+ var normalIndex = 0;
+
+ for (i = 0; i < numBottomCapVertices * 3; i += 3) {
+ position = Cartesian3.unpack(bottomPositions, i, scratchPosition);
+ ellipsoid.scaleToGeodeticSurface(position, position);
+
+ normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal);
+ Cartesian3.multiplyByScalar(normal, upDelta, normal);
+
+ EncodedCartesian3.writeElements(position, vbPositions, index);
+ EncodedCartesian3.writeElements(position, vbPositions, index + 6);
+ index += 12;
+
+ Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex);
+ Cartesian3.pack(normal, vbNormals, normalIndex + 3);
+ normalIndex += 6;
+ }
+
+ var numWalls = walls.length;
+ var wallPositions;
+ var wallLength;
+
+ for (i = 0; i < numWalls; ++i) {
+ wall = walls[i];
+ wallPositions = wall.attributes.position.values;
+ wallLength = wallPositions.length / 2;
+
+ for (j = 0; j < wallLength; j += 3) {
+ position = Cartesian3.unpack(wallPositions, j, scratchPosition);
+ ellipsoid.scaleToGeodeticSurface(position, position);
+
+ normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal);
+ Cartesian3.multiplyByScalar(normal, upDelta, normal);
+
+ EncodedCartesian3.writeElements(position, vbPositions, index);
+ EncodedCartesian3.writeElements(position, vbPositions, index + wallLength * 2);
+ index += 6;
+
+ Cartesian3.pack(normal, vbNormals, normalIndex);
+ Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex + wallLength);
+ normalIndex += 3;
+ }
+
+ index += wallLength * 2;
+ normalIndex += wallLength;
+ }
+
+ var numIndices = numCapIndices + numWallIndices;
+ var ibIndices = IndexDatatype.createTypedArray(numVertices, numIndices);
+
+ var i0;
+ var i1;
+ var i2;
+
+ index = 0;
+ for (i = 0; i < numBottomIndices; i += 3) {
+ i0 = bottomIndices[i] * 2;
+ i1 = bottomIndices[i + 1] * 2;
+ i2 = bottomIndices[i + 2] * 2;
+
+ ibIndices[index++] = i2;
+ ibIndices[index++] = i1;
+ ibIndices[index++] = i0;
+ }
+
+ for (i = 0; i < numBottomIndices; i += 3) {
+ i0 = bottomIndices[i] * 2;
+ i1 = bottomIndices[i + 1] * 2;
+ i2 = bottomIndices[i + 2] * 2;
+
+ ibIndices[index++] = i0 + 1;
+ ibIndices[index++] = i1 + 1;
+ ibIndices[index++] = i2 + 1;
+ }
+
+ var offset = numCapVertices;
+ for (i = 0; i < numWalls; ++i) {
+ wall = walls[i];
+ var wallIndices = wall.indices;
+ wallLength = wallIndices.length;
+
+ for (j = 0; j < wallLength; ++j) {
+ ibIndices[index++] = wallIndices[j] + offset;
+ }
+ offset += wall.attributes.position.values.length / 3;
+ }
+
+ var positionBuffer = context.createVertexBuffer(vbPositions, BufferUsage.STATIC_DRAW);
+ var normalBuffer = context.createVertexBuffer(vbNormals, BufferUsage.STATIC_DRAW);
+
+ var indexDatatype = (ibIndices.BYTES_PER_ELEMENT === 2) ? IndexDatatype.UNSIGNED_SHORT : IndexDatatype.UNSIGNED_INT;
+ var indexBuffer = context.createIndexBuffer(ibIndices, BufferUsage.STATIC_DRAW, indexDatatype);
+
+ var attributes = [{
+ index : attributeLocations.positionHigh,
+ vertexBuffer : positionBuffer,
+ componentsPerAttribute : 3,
+ componentDatatype : ComponentDatatype.FLOAT,
+ offsetInBytes : 0,
+ strideInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3 * 2
+ }, {
+ index : attributeLocations.positionLow,
+ vertexBuffer : positionBuffer,
+ componentsPerAttribute : 3,
+ componentDatatype : ComponentDatatype.FLOAT,
+ offsetInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3,
+ strideInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3 * 2
+ }, {
+ index : attributeLocations.normal,
+ vertexBuffer : normalBuffer,
+ componentsPerAttribute : 3,
+ componentDatatype : ComponentDatatype.FLOAT
+ }];
+
+ polygon._va = context.createVertexArray(attributes, indexBuffer);
+
+ polygon._boundingSphere = BoundingSphere.fromPoints(outerRing);
+ polygon._boundingSphere.radius = upDelta; // TODO: Correct bounding sphere
+ }
+
+ GroundPolygon.prototype.update = function(context, frameState, commandList) {
+ if (!defined(this._va)) {
+ createShadowVolume(this, context);
+ }
+
+ if (!defined(this._sp)) {
+ this._sp = context.createShaderProgram(ShadowVolumeVS, ShadowVolumeFS, attributeLocations);
+ }
+
+ if (!defined(this._stencilPassCommand)) {
+ var stencilPassRenderState = context.createRenderState({
+ colorMask : {
+ red : false,
+ green : false,
+ blue : false,
+ alpha : false
+ },
+ stencilTest : {
+ enabled : true,
+ frontFunction : StencilFunction.ALWAYS,
+ frontOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.INCREMENT_WRAP
+ },
+ backFunction : StencilFunction.ALWAYS,
+ backOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.DECREMENT_WRAP
+ },
+ reference : 0,
+ mask : ~0
+ },
+ depthTest : {
+ enabled : true
+ },
+ depthMask : false
+ });
+
+ this._stencilPassCommand = new DrawCommand({
+ primitiveType : PrimitiveType.TRIANGLES,
+ vertexArray : this._va,
+ renderState : stencilPassRenderState,
+ shaderProgram : this._sp,
+ uniformMap : this._uniformMap,
+ boundingVolume : this._boundingSphere,
+ owner : this,
+ modelMatrix : Matrix4.IDENTITY,
+ pass : Pass.GROUND
+ });
+
+ var colorRenderState = context.createRenderState({
+ stencilTest : {
+ enabled : true,
+ frontFunction : StencilFunction.NOT_EQUAL,
+ frontOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.DECREMENT
+ },
+ backFunction : StencilFunction.NOT_EQUAL,
+ backOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.DECREMENT
+ },
+ reference : 0,
+ mask : ~0
+ },
+ cull : {
+ enabled : true,
+ face : CullFace.BACK
+ },
+ depthTest : {
+ enabled : true
+ },
+ depthMask : false,
+ blending : BlendingState.ALPHA_BLEND
+ });
+
+ this._colorPassCommand = new DrawCommand({
+ primitiveType : PrimitiveType.TRIANGLES,
+ vertexArray : this._va,
+ renderState : colorRenderState,
+ shaderProgram : this._sp,
+ uniformMap : this._uniformMap,
+ boundingVolume : this._boundingSphere,
+ owner : this,
+ modelMatrix : Matrix4.IDENTITY,
+ pass : Pass.GROUND
+ });
+ }
+
+ if (this.debugVolume && !defined(this._debugVolumeCommand)) {
+ var pauseRS = context.createRenderState({
+ cull : {
+ enabled : false
+ },
+ depthTest : {
+ enabled : true
+ },
+ depthMask : false,
+ blending : BlendingState.ALPHA_BLEND
+ });
+
+ var sp = context.createShaderProgram(ShadowVolumeVS, ShadowVolumeFS, attributeLocations);
+
+ this._debugVolumeCommand = new DrawCommand({
+ primitiveType : PrimitiveType.TRIANGLES,
+ vertexArray : this._va,
+ renderState : pauseRS,
+ shaderProgram : sp,
+ uniformMap : this._uniformMap,
+ boundingVolume : this._boundingSphere,
+ owner : this,
+ modelMatrix : Matrix4.IDENTITY,
+ pass : Pass.GROUND
+ });
+ }
+
+ var pass = frameState.passes;
+ if (pass.render) {
+ if (this.debugVolume) {
+ commandList.push(this._debugVolumeCommand);
+ } else {
+ commandList.push(this._stencilPassCommand, this._colorPassCommand);
+ }
+ }
+ };
+
+ return GroundPolygon;
+});
diff --git a/Source/Scene/Pass.js b/Source/Scene/Pass.js
index 4469bbd6c019..60be1a2d6741 100644
--- a/Source/Scene/Pass.js
+++ b/Source/Scene/Pass.js
@@ -12,13 +12,14 @@ define([
*/
var Pass = {
GLOBE : 0,
- OPAQUE : 1,
+ GROUND : 1,
+ OPAQUE : 2,
// Commands are executed in order by pass up to the translucent pass.
// Translucent geometry needs special handling (sorting/OIT). Overlays
// are also special (they're executed last, they're not sorted by frustum).
- TRANSLUCENT : 2,
- OVERLAY : 3,
- NUMBER_OF_PASSES : 4
+ TRANSLUCENT : 3,
+ OVERLAY : 4,
+ NUMBER_OF_PASSES : 5
};
return freezeObject(Pass);
diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js
index c20f3c5d9eb1..f6186c644850 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -232,6 +232,7 @@ define([
this._clearColorCommand = new ClearCommand({
color : new Color(),
+ stencil : 0.0,
owner : this
});
this._depthClearCommand = new ClearCommand({
@@ -1294,10 +1295,9 @@ define([
frustum.near = frustumCommands.near;
frustum.far = frustumCommands.far;
- if (index !== 0) {
- // Avoid tearing artifacts between adjacent frustums
- frustum.near *= 0.99;
- }
+ // Avoid tearing artifacts between adjacent frustums
+ var overlappedNear = index !== 0 ? frustum.near * 0.99 : frustum.near;
+ frustum.near = overlappedNear;
us.updateFrustum(frustum);
clearDepth.execute(context, passState);
@@ -1310,9 +1310,21 @@ define([
scene._globeDepth.executeCopyDepth(context, passState);
+ frustum.near = frustumCommands.near;
+ us.updateFrustum(frustum);
+
+ commands = frustumCommands.commands[Pass.GROUND];
+ length = frustumCommands.indices[Pass.GROUND];
+ for (j = 0; j < length; ++j) {
+ executeCommand(commands[j], scene, context, passState);
+ }
+
+ frustum.near = overlappedNear;
+ us.updateFrustum(frustum);
+
// Execute commands in order by pass up to the translucent pass.
// Translucent geometry needs special handling (sorting/OIT).
- var startPass = Pass.GLOBE + 1;
+ var startPass = Pass.GROUND + 1;
var endPass = Pass.TRANSLUCENT;
for (var pass = startPass; pass < endPass; ++pass) {
commands = frustumCommands.commands[pass];
diff --git a/Source/Shaders/ShadowVolumeFS.glsl b/Source/Shaders/ShadowVolumeFS.glsl
new file mode 100644
index 000000000000..5df05c448392
--- /dev/null
+++ b/Source/Shaders/ShadowVolumeFS.glsl
@@ -0,0 +1,18 @@
+#ifdef GL_EXT_frag_depth
+#extension GL_EXT_frag_depth : enable
+#endif
+
+varying float v_z;
+
+void czm_writeDepthClampedToFarPlane()
+{
+ // That is really 1/w
+ //gl_FragDepthEXT = min(v_z * gl_FragCoord.w, 1.0);
+ //gl_FragDepthEXT = clamp(v_z * gl_FragCoord.w, 0.0, 1.0);
+}
+
+void main(void)
+{
+ gl_FragColor = vec4(1.0, 1.0, 0.0, 0.5);
+ czm_writeDepthClampedToFarPlane();
+}
\ No newline at end of file
diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl
new file mode 100644
index 000000000000..4e840c05ce55
--- /dev/null
+++ b/Source/Shaders/ShadowVolumeVS.glsl
@@ -0,0 +1,85 @@
+attribute vec3 positionHigh;
+attribute vec3 positionLow;
+attribute vec3 normal;
+
+uniform float centralBodyMinimumAltitude;
+uniform float LODNegativeToleranceOverDistance;
+
+varying float v_z;
+
+vec4 czm_depthClampNearFarPlane(vec4 vertexInClipCoordinates)
+{
+ //v_z = (0.5 * (vertexInClipCoordinates.z / vertexInClipCoordinates.w) + 0.5) * vertexInClipCoordinates.w;
+ //vertexInClipCoordinates.z = min(vertexInClipCoordinates.z, vertexInClipCoordinates.w);
+ return vertexInClipCoordinates;
+}
+
+vec4 clipPointToPlane(vec3 p0, vec3 p1, bool nearPlane)
+{
+ float planeDistance = nearPlane ? czm_entireFrustum.x : czm_entireFrustum.y;
+ //float offset = nearPlane ? 0.001 : -0.001;
+ float offset = 0.001;
+
+ p0 = (czm_modelViewRelativeToEye * vec4(p0, 1.0)).xyz;
+ p1 = (czm_modelViewRelativeToEye * vec4(p1, 1.0)).xyz;
+
+ vec3 diff = p1 - p0;
+ float magnitude = length(diff);
+ vec3 direction = normalize(diff);
+ float denominator = -direction.z;
+ float pointDistance = -(planeDistance + p0.z);
+ bool behindPlane = nearPlane ? pointDistance < 0.0 : pointDistance > 0.0;
+
+ bool culledByPlane = false;
+
+ if (behindPlane && abs(denominator) < czm_epsilon7)
+ {
+ // point is behind and parallel to the plane
+ culledByPlane = true;
+ }
+ else if (behindPlane && abs(denominator) > czm_epsilon7)
+ {
+ // find intersection of ray and the plane
+ // t = (-dot(plane normal, point on plane) - dot(plane normal, ray origin)) / dot(plane normal, ray direction)
+ float t = (planeDistance + p0.z) / denominator;
+ if (t < 0.0 || t > magnitude)
+ {
+ // entire segment is behind the plane
+ culledByPlane = true;
+ }
+ else
+ {
+ // compute intersection with plane slightly offset
+ // to prevent precision artifacts
+ t += offset;
+ p0 = p0 + t * direction;
+ }
+ }
+
+ if (culledByPlane) {
+ // the segment is behind the plane. push to plane and
+ // slightly offset to prevent precision artifacts
+ //p0.z = min(p0.z, -(planeDistance + offset));
+ }
+
+ return czm_projection * vec4(p0, 1.0);
+}
+
+void main()
+{
+ vec4 position = czm_translateRelativeToEye(positionHigh, positionLow);
+
+ float delta = 1.0; // TODO: moving the vertex is a function of the view
+
+ vec3 eyePosition = position.xyz;
+ vec3 movedPosition = position.xyz + normal * delta;
+
+ if (all(equal(normal, vec3(0.0))))
+ {
+ gl_Position = czm_depthClampNearFarPlane(clipPointToPlane(eyePosition, movedPosition, false));
+ }
+ else
+ {
+ gl_Position = czm_depthClampNearFarPlane(clipPointToPlane(movedPosition, eyePosition, true));
+ }
+}