Skip to content

Commit c94c510

Browse files
committed
add geographic limit rectangle
1 parent c8cf473 commit c94c510

8 files changed

+198
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
7+
<meta name="description" content="Use Viewer to start building new applications or easily embed Cesium into existing applications.">
8+
<meta name="cesium-sandcastle-labels" content="Showcases">
9+
<title>Cesium Demo</title>
10+
<script type="text/javascript" src="../Sandcastle-header.js"></script>
11+
<script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.20/require.js"></script>
12+
<script type="text/javascript">
13+
if(typeof require === 'function') {
14+
require.config({
15+
baseUrl : '../../../Source',
16+
waitSeconds : 120
17+
});
18+
}
19+
</script>
20+
</head>
21+
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
22+
<style>
23+
@import url(../templates/bucket.css);
24+
</style>
25+
<div id="cesiumContainer" class="fullSize"></div>
26+
<div id="loadingOverlay"><h1>Loading...</h1></div>
27+
<div id="toolbar"></div>
28+
<script id="cesium_sandcastle_script">
29+
function startup(Cesium) {
30+
'use strict';
31+
//Sandcastle_Begin
32+
var viewer = new Cesium.Viewer('cesiumContainer', {
33+
terrainProvider: Cesium.createWorldTerrain()
34+
});
35+
36+
// Tropics of Cancer and Capricorn
37+
var coffeeBeltRectangle = Cesium.Rectangle.fromDegrees(-180, -23.43687, 180, 23.43687);
38+
39+
viewer.scene.globe.geographicLimitRectangle = coffeeBeltRectangle;
40+
viewer.scene.skyAtmosphere.show = false;
41+
42+
// Add rectangles to show bounds
43+
var rectangles = [];
44+
45+
for (var i = 0; i < 10; i++) {
46+
rectangles.push(viewer.entities.add({
47+
name : 'EPSG 2093 bounds',
48+
rectangle : {
49+
coordinates : coffeeBeltRectangle,
50+
material : Cesium.Color.WHITE.withAlpha(0.0),
51+
height : i * 5000.0,
52+
outline : true,
53+
outlineWidth : 4.0,
54+
outlineColor : Cesium.Color.WHITE
55+
}
56+
}));
57+
}
58+
59+
Sandcastle.addToolbarButton('Show/Hide Bounds', function() {
60+
var rectanglesLength = rectangles.length;
61+
for (var i = 0; i < rectanglesLength; i++) {
62+
var rectangleEntity = rectangles[i];
63+
rectangleEntity.show = !rectangleEntity.show;
64+
}
65+
});
66+
67+
var limited = true;
68+
Sandcastle.addToolbarButton('enable/disable limiter', function() {
69+
if (limited) {
70+
viewer.scene.globe.geographicLimitRectangle = Cesium.Rectangle.MAX_VALUE;
71+
limited = false;
72+
} else {
73+
viewer.scene.globe.geographicLimitRectangle = coffeeBeltRectangle;
74+
limited = true;
75+
}
76+
});
77+
78+
//Sandcastle_End
79+
Sandcastle.finishedLoading();
80+
}
81+
if (typeof Cesium !== 'undefined') {
82+
startup(Cesium);
83+
} else if (typeof require === 'function') {
84+
require(['Cesium'], startup);
85+
}
86+
</script>
87+
</body>
88+
</html>
Loading

Source/Scene/Globe.js

+8
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ define([
277277
this._surface.tileProvider.clippingPlanes = value;
278278
}
279279
},
280+
geographicLimitRectangle : {
281+
get : function() {
282+
return this._surface.tileProvider.geographicLimitRectangle;
283+
},
284+
set : function(value) {
285+
this._surface.tileProvider.geographicLimitRectangle = value;
286+
}
287+
},
280288
/**
281289
* The normal map to use for rendering waves in the ocean. Setting this property will
282290
* only have an effect if the configured terrain provider includes a water mask.

Source/Scene/GlobeSurfaceShaderSet.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ define([
6666
return useWebMercatorProjection ? get2DYPositionFractionMercatorProjection : get2DYPositionFractionGeographicProjection;
6767
}
6868

69-
GlobeSurfaceShaderSet.prototype.getShaderProgram = function(frameState, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, applySplit, showReflectiveOcean, showOceanWaves, enableLighting, hasVertexNormals, useWebMercatorProjection, enableFog, enableClippingPlanes, clippingPlanes) {
69+
GlobeSurfaceShaderSet.prototype.getShaderProgram = function(frameState, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, applySplit, showReflectiveOcean, showOceanWaves, enableLighting, hasVertexNormals, useWebMercatorProjection, enableFog, enableClippingPlanes, clippingPlanes, clippedByBoundaries) {
7070
var quantization = 0;
7171
var quantizationDefine = '';
7272

@@ -84,6 +84,13 @@ define([
8484
vertexLogDepthDefine = 'DISABLE_GL_POSITION_LOG_DEPTH';
8585
}
8686

87+
var geographicLimitRectangleFlag = 0;
88+
var geographicLimitRectangleDefine = '';
89+
if (clippedByBoundaries) {//} && frameState.mode !== SceneMode.SCENE3D) {
90+
geographicLimitRectangleFlag = 1;
91+
geographicLimitRectangleDefine = 'TILE_LIMIT_RECTANGLE';
92+
}
93+
8794
var sceneMode = frameState.mode;
8895
var flags = sceneMode |
8996
(applyBrightness << 2) |
@@ -101,7 +108,8 @@ define([
101108
(quantization << 14) |
102109
(applySplit << 15) |
103110
(enableClippingPlanes << 16) |
104-
(vertexLogDepth << 17);
111+
(vertexLogDepth << 17) |
112+
(geographicLimitRectangleFlag << 18);
105113

106114
var currentClippingShaderState = 0;
107115
if (defined(clippingPlanes)) {
@@ -134,7 +142,7 @@ define([
134142
}
135143

136144
vs.defines.push(quantizationDefine, vertexLogDepthDefine);
137-
fs.defines.push('TEXTURE_UNITS ' + numberOfDayTextures);
145+
fs.defines.push('TEXTURE_UNITS ' + numberOfDayTextures, geographicLimitRectangleDefine);
138146

139147
if (applyBrightness) {
140148
fs.defines.push('APPLY_BRIGHTNESS');

Source/Scene/GlobeSurfaceTile.js

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ define([
8181

8282
this.surfaceShader = undefined;
8383
this.isClipped = true;
84+
85+
this.clippedByBoundaries = false;
8486
}
8587

8688
defineProperties(GlobeSurfaceTile.prototype, {

Source/Scene/GlobeSurfaceTileProvider.js

+40-2
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ define([
168168
* @private
169169
*/
170170
this._clippingPlanes = undefined;
171+
172+
/**
173+
* A property specifying a {@link Rectangle} used to selectively prevent tiles outside a region from loading.
174+
* For limiting terrain in scenes that use custom projections or Proj4JS projections that cause overlapping tiles.
175+
* @type {Rectangle}
176+
*/
177+
this.geographicLimitRectangle = Rectangle.clone(Rectangle.MAX_VALUE);
171178
}
172179

173180
defineProperties(GlobeSurfaceTileProvider.prototype, {
@@ -509,6 +516,7 @@ define([
509516
};
510517

511518
var boundingSphereScratch = new BoundingSphere();
519+
var rectangleIntersectionScratch = new Rectangle();
512520

513521
/**
514522
* Determines the visibility of a given tile. The tile may be fully visible, partially visible, or not
@@ -536,6 +544,16 @@ define([
536544
var cullingVolume = frameState.cullingVolume;
537545
var boundingVolume = defaultValue(surfaceTile.orientedBoundingBox, surfaceTile.boundingSphere3D);
538546

547+
// Check if the tile is outside the limit area in cartographic space
548+
surfaceTile.clippedByBoundaries = false;
549+
var areaLimitIntersection = Rectangle.simpleIntersection(this.geographicLimitRectangle, tile.rectangle, rectangleIntersectionScratch);
550+
if (!defined(areaLimitIntersection)) {
551+
return Visibility.NONE;
552+
}
553+
if (!Rectangle.equals(areaLimitIntersection, tile.rectangle)) {
554+
surfaceTile.clippedByBoundaries = true;
555+
}
556+
539557
if (frameState.mode !== SceneMode.SCENE3D) {
540558
boundingVolume = boundingSphereScratch;
541559
BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.mapProjection, surfaceTile.minimumHeight, surfaceTile.maximumHeight, boundingVolume);
@@ -580,6 +598,7 @@ define([
580598
var modifiedModelViewScratch = new Matrix4();
581599
var modifiedModelViewProjectionScratch = new Matrix4();
582600
var tileRectangleScratch = new Cartesian4();
601+
var localizedGeographicLimitRectangleScratch = new Cartesian4();
583602
var rtcScratch = new Cartesian3();
584603
var centerEyeScratch = new Cartesian3();
585604
var southwestScratch = new Cartesian3();
@@ -921,6 +940,9 @@ define([
921940
}
922941
return frameState.context.defaultTexture;
923942
},
943+
u_geographicLimitRectangle : function() {
944+
return this.properties.localizedGeographicLimitRectangle;
945+
},
924946
u_clippingPlanesMatrix : function() {
925947
var clippingPlanes = globeSurfaceTileProvider._clippingPlanes;
926948
return defined(clippingPlanes) ? Matrix4.multiply(frameState.context.uniformState.view, clippingPlanes.modelMatrix, scratchClippingPlaneMatrix) : Matrix4.IDENTITY;
@@ -969,7 +991,9 @@ define([
969991
minMaxHeight : new Cartesian2(),
970992
scaleAndBias : new Matrix4(),
971993
clippingPlanesEdgeColor : Color.clone(Color.WHITE),
972-
clippingPlanesEdgeWidth : 0.0
994+
clippingPlanesEdgeWidth : 0.0,
995+
996+
localizedGeographicLimitRectangle : new Cartesian4()
973997
}
974998
};
975999

@@ -1255,6 +1279,20 @@ define([
12551279
uniformMapProperties.southMercatorYAndOneOverHeight.x = southMercatorY;
12561280
uniformMapProperties.southMercatorYAndOneOverHeight.y = oneOverMercatorHeight;
12571281

1282+
// Convert tile limiter rectangle from cartographic to texture space using the tileRectangle.
1283+
var localizedGeographicLimitRectangle = localizedGeographicLimitRectangleScratch;
1284+
var geographicLimitRectangle = tileProvider.geographicLimitRectangle;
1285+
1286+
var cartographicTileRectangle = tile.rectangle;
1287+
var inverseTileWidth = 1.0 / cartographicTileRectangle.width;
1288+
var inverseTileHeight = 1.0 / cartographicTileRectangle.height;
1289+
localizedGeographicLimitRectangle.x = (geographicLimitRectangle.west - cartographicTileRectangle.west) * inverseTileWidth;
1290+
localizedGeographicLimitRectangle.y = (geographicLimitRectangle.south - cartographicTileRectangle.south) * inverseTileHeight;
1291+
localizedGeographicLimitRectangle.z = (geographicLimitRectangle.east - cartographicTileRectangle.west) * inverseTileWidth;
1292+
localizedGeographicLimitRectangle.w = (geographicLimitRectangle.north - cartographicTileRectangle.south) * inverseTileHeight;
1293+
1294+
Cartesian4.clone(localizedGeographicLimitRectangle, uniformMapProperties.localizedGeographicLimitRectangle);
1295+
12581296
// For performance, use fog in the shader only when the tile is in fog.
12591297
var applyFog = enableFog && CesiumMath.fog(tile._distance, frameState.fog.density) > CesiumMath.EPSILON3;
12601298

@@ -1357,7 +1395,7 @@ define([
13571395
uniformMap = combine(uniformMap, tileProvider.uniformMap);
13581396
}
13591397

1360-
command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(frameState, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, applySplit, showReflectiveOcean, showOceanWaves, tileProvider.enableLighting, hasVertexNormals, useWebMercatorProjection, applyFog, clippingPlanesEnabled, clippingPlanes);
1398+
command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(frameState, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, applySplit, showReflectiveOcean, showOceanWaves, tileProvider.enableLighting, hasVertexNormals, useWebMercatorProjection, applyFog, clippingPlanesEnabled, clippingPlanes, surfaceTile.clippedByBoundaries);
13611399
command.castShadows = castShadows;
13621400
command.receiveShadows = receiveShadows;
13631401
command.renderState = renderState;

Source/Shaders/GlobeFS.glsl

+13
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ uniform sampler2D u_oceanNormalMap;
5151
uniform vec2 u_lightingFadeDistance;
5252
#endif
5353

54+
#ifdef TILE_LIMIT_RECTANGLE
55+
uniform vec4 u_geographicLimitRectangle;
56+
#endif
57+
5458
#ifdef ENABLE_CLIPPING_PLANES
5559
uniform sampler2D u_clippingPlanes;
5660
uniform mat4 u_clippingPlanesMatrix;
@@ -155,6 +159,15 @@ vec4 computeWaterColor(vec3 positionEyeCoordinates, vec2 textureCoordinates, mat
155159

156160
void main()
157161
{
162+
163+
#ifdef TILE_LIMIT_RECTANGLE
164+
if (v_textureCoordinates.x < u_geographicLimitRectangle.x || u_geographicLimitRectangle.z < v_textureCoordinates.x ||
165+
v_textureCoordinates.y < u_geographicLimitRectangle.y || u_geographicLimitRectangle.w < v_textureCoordinates.y)
166+
{
167+
discard;
168+
}
169+
#endif
170+
158171
#ifdef ENABLE_CLIPPING_PLANES
159172
float clipDistance = clip(gl_FragCoord, u_clippingPlanes, u_clippingPlanesMatrix);
160173
#endif

Specs/Scene/GlobeSurfaceTileProviderSpec.js

+36-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ defineSuite([
88
'Core/Ellipsoid',
99
'Core/EllipsoidTerrainProvider',
1010
'Core/GeographicProjection',
11-
'Core/Intersect',
1211
'Core/Rectangle',
1312
'Core/WebMercatorProjection',
1413
'Renderer/ContextLimits',
@@ -39,7 +38,6 @@ defineSuite([
3938
Ellipsoid,
4039
EllipsoidTerrainProvider,
4140
GeographicProjection,
42-
Intersect,
4341
Rectangle,
4442
WebMercatorProjection,
4543
ContextLimits,
@@ -116,6 +114,7 @@ defineSuite([
116114

117115
afterEach(function() {
118116
scene.imageryLayers.removeAll();
117+
scene.primitives.removeAll();
119118
});
120119

121120
it('conforms to QuadtreeTileProvider interface', function() {
@@ -939,4 +938,39 @@ defineSuite([
939938
}).toThrowDeveloperError();
940939
});
941940

941+
it('geographicLimitRectangle selectively enables rendering globe surface', function() {
942+
expect(scene).toRender([0, 0, 0, 255]);
943+
switchViewMode(SceneMode.COLUMBUS_VIEW, new GeographicProjection(Ellipsoid.WGS84));
944+
var result;
945+
return updateUntilDone(scene.globe).then(function() {
946+
expect(scene).notToRender([0, 0, 0, 255]);
947+
expect(scene).toRenderAndCall(function(rgba) {
948+
result = rgba;
949+
expect(rgba).not.toEqual([0, 0, 0, 255]);
950+
});
951+
scene.globe.geographicLimitRectangle = Rectangle.fromDegrees(-2, -2, -1, -1);
952+
expect(scene).notToRender(result);
953+
scene.camera.setView({
954+
destination : scene.globe.geographicLimitRectangle
955+
});
956+
return updateUntilDone(scene.globe);
957+
})
958+
.then(function() {
959+
expect(scene).toRender(result);
960+
});
961+
});
962+
963+
it('geographicLimitRectangle culls tiles outside the region', function() {
964+
switchViewMode(SceneMode.COLUMBUS_VIEW, new GeographicProjection(Ellipsoid.WGS84));
965+
var unculledCommandCount;
966+
return updateUntilDone(scene.globe).then(function() {
967+
unculledCommandCount = scene.frameState.commandList.length;
968+
scene.globe.geographicLimitRectangle = Rectangle.fromDegrees(-2, -2, -1, -1);
969+
return updateUntilDone(scene.globe);
970+
})
971+
.then(function() {
972+
expect(unculledCommandCount).toBeGreaterThan(scene.frameState.commandList.length);
973+
});
974+
});
975+
942976
}, 'WebGL');

0 commit comments

Comments
 (0)