Skip to content

Commit a76cfdd

Browse files
committed
add batching for ground primitives
1 parent b82f24e commit a76cfdd

File tree

8 files changed

+775
-26
lines changed

8 files changed

+775
-26
lines changed

Apps/Sandcastle/gallery/batchingGroundPrims.html

+11-11
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
new Cesium.Cartesian3(-2352875.095159641, -3742564.819171856, 4582173.540953957),
7979
new Cesium.Cartesian3(-2350669.646050987, -3743751.6823160048, 4582334.8406995395)
8080
];
81-
81+
/*
8282
// concave polygon
8383
var redPolygon1 = viewer.entities.add({
8484
name : 'concave polygon on surface',
@@ -87,7 +87,7 @@
8787
material : '../images/Cesium_Logo_Color.jpg'
8888
}
8989
});
90-
90+
*/
9191
// polygons with non-overlapping extents seem to be batchable without problems
9292
/*
9393
var redPolygon1 = viewer.entities.add({
@@ -136,7 +136,7 @@
136136
});*/
137137

138138
// nearly overlapping rectangles over mt. st. helens
139-
/*
139+
140140
var latitude = 46.1922;
141141
var longitude = -122.1934;
142142

@@ -167,7 +167,12 @@
167167
material : Cesium.Color.YELLOW.withAlpha(0.5)
168168
}
169169
});
170-
*/
170+
171+
var checkerboard = new Cesium.CheckerboardMaterialProperty({
172+
evenColor : Cesium.Color.ORANGE,
173+
oddColor : Cesium.Color.YELLOW,
174+
repeat : new Cesium.Cartesian2(14, 14)
175+
});
171176

172177
var rightHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
173178
rightHandler.setInputAction(function(movement) {
@@ -181,17 +186,12 @@
181186
var normalized = Cesium.Cartesian3.normalize(cartesian, cartesian);
182187
console.log(normalized);
183188

184-
viewer.entities.removeAll();
189+
//viewer.entities.removeAll();
185190
viewer.entities.add({
186191
name : lat + ' ' + long,
187192
rectangle : {
188193
coordinates : Cesium.Rectangle.fromDegrees(long - 0.0002, lat - 0.0001, long + 0.0002, lat + 0.0001),
189-
material :
190-
new Cesium.CheckerboardMaterialProperty({
191-
evenColor : Cesium.Color.ORANGE,
192-
oddColor : Cesium.Color.YELLOW,
193-
repeat : new Cesium.Cartesian2(14, 14)
194-
})
194+
material : checkerboard
195195
}
196196
});
197197
}

LICENSE.md

+32
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,38 @@ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
509509
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
510510
THIS SOFTWARE.
511511

512+
### rbush
513+
514+
https://github.com/mourner/rbush
515+
516+
> MIT License
517+
518+
> Copyright (c) 2016 Vladimir Agafonkin
519+
520+
>Permission is hereby granted, free of charge, to any person obtaining a copy
521+
of this software and associated documentation files (the "Software"), to deal
522+
in the Software without restriction, including without limitation the rights
523+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
524+
copies of the Software, and to permit persons to whom the Software is
525+
furnished to do so, subject to the following conditions:
526+
>
527+
The above copyright notice and this permission notice shall be included in
528+
all copies or substantial portions of the Software.
529+
>
530+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
531+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
532+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
533+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
534+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
535+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
536+
THE SOFTWARE.
537+
538+
### quickselect
539+
540+
https://github.com/mourner/quickselect
541+
542+
No license given, used by rbush
543+
512544
### crunch
513545

514546
https://github.com/BinomialLLC/crunch

Source/Core/RectangleRbush.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
define([
2+
'../ThirdParty/rbush',
3+
'./Check'
4+
], function(
5+
rbush,
6+
Check) {
7+
'use strict';
8+
9+
/**
10+
* Wrapper around rbush for use with Rectangle types.
11+
* @private
12+
*/
13+
function RectangleRbush() {
14+
this._tree = rbush();
15+
}
16+
17+
function RectangleWithId() {
18+
this.minX = 0.0;
19+
this.minY = 0.0;
20+
this.maxX = 0.0;
21+
this.maxY = 0.0;
22+
this.id = '';
23+
}
24+
25+
function fromRectangleAndId(rectangle, id, result) {
26+
result.minX = rectangle.west;
27+
result.minY = rectangle.south;
28+
result.maxX = rectangle.east;
29+
result.maxY = rectangle.north;
30+
result.id = id;
31+
return result;
32+
}
33+
34+
function idCompare(a, b) {
35+
return a.id === b.id;
36+
}
37+
38+
RectangleRbush.prototype.insert = function(id, rectangle) {
39+
//>>includeStart('debug', pragmas.debug);
40+
Check.typeOf.string('id', id);
41+
Check.typeOf.object('rectangle', rectangle);
42+
//>>includeEnd('debug');
43+
44+
var withId = fromRectangleAndId(rectangle, id, new RectangleWithId());
45+
this._tree.insert(withId);
46+
};
47+
48+
var removalScratch = new RectangleWithId();
49+
RectangleRbush.prototype.remove = function(id, rectangle) {
50+
//>>includeStart('debug', pragmas.debug);
51+
Check.typeOf.string('id', id);
52+
Check.typeOf.object('rectangle', rectangle);
53+
//>>includeEnd('debug');
54+
55+
var withId = fromRectangleAndId(rectangle, id, removalScratch);
56+
this._tree.remove(withId, idCompare);
57+
};
58+
59+
var collisionScratch = new RectangleWithId();
60+
RectangleRbush.prototype.collides = function(rectangle) {
61+
//>>includeStart('debug', pragmas.debug);
62+
Check.typeOf.object('rectangle', rectangle);
63+
//>>includeEnd('debug');
64+
65+
var withId = fromRectangleAndId(rectangle, '', collisionScratch);
66+
return this._tree.collides(withId);
67+
};
68+
69+
return RectangleRbush;
70+
});

Source/DataSources/GeometryVisualizer.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ define([
2525
'./RectangleGeometryUpdater',
2626
'./StaticGeometryColorBatch',
2727
'./StaticGeometryPerMaterialBatch',
28-
'./StaticGroundGeometryColorBatch',
2928
'./StaticGroundGeometryPerMaterialBatch',
3029
'./StaticOutlineGeometryBatch',
3130
'./WallGeometryUpdater'
@@ -56,7 +55,6 @@ define([
5655
RectangleGeometryUpdater,
5756
StaticGeometryColorBatch,
5857
StaticGeometryPerMaterialBatch,
59-
StaticGroundGeometryColorBatch,
6058
StaticGroundGeometryPerMaterialBatch,
6159
StaticOutlineGeometryBatch,
6260
WallGeometryUpdater) {
@@ -160,7 +158,7 @@ define([
160158
this._groundMaterialBatches = new Array(numberOfClassificationTypes); // TODO: why is this?
161159

162160
for (i = 0; i < numberOfClassificationTypes; ++i) {
163-
this._groundColorBatches[i] = new StaticGroundGeometryColorBatch(groundPrimitives, PerInstanceColorAppearance, i);
161+
this._groundColorBatches[i] = new StaticGroundGeometryPerMaterialBatch(groundPrimitives, PerInstanceColorAppearance, i);
164162
this._groundMaterialBatches[i] = new StaticGroundGeometryPerMaterialBatch(groundPrimitives, MaterialAppearance, i);
165163
}
166164

Source/DataSources/StaticGroundGeometryPerMaterialBatch.js

+24-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ define([
66
'../Core/DistanceDisplayCondition',
77
'../Core/DistanceDisplayConditionGeometryInstanceAttribute',
88
'../Core/ShowGeometryInstanceAttribute',
9+
'../Core/RectangleRbush',
910
'../Scene/GroundPrimitive',
1011
'./BoundingSphereState',
1112
'./ColorMaterialProperty',
@@ -19,6 +20,7 @@ define([
1920
DistanceDisplayCondition,
2021
DistanceDisplayConditionGeometryInstanceAttribute,
2122
ShowGeometryInstanceAttribute,
23+
RectangleRbush,
2224
GroundPrimitive,
2325
BoundingSphereState,
2426
ColorMaterialProperty,
@@ -29,13 +31,13 @@ define([
2931
var distanceDisplayConditionScratch = new DistanceDisplayCondition();
3032

3133
// Encapsulates a Primitive and all the entities that it represents.
32-
function Batch(primitives, appearanceType, materialProperty, shadows) {
34+
function Batch(primitives, appearanceType, materialProperty, shadows, usingSphericalCoordinates) {
3335
this.primitives = primitives; // scene level primitive collection
3436
this.appearanceType = appearanceType;
3537
this.materialProperty = materialProperty;
3638
this.updaters = new AssociativeArray();
3739
this.createPrimitive = true;
38-
this.primitive = undefined;
40+
this.primitive = undefined; // a GroundPrimitive encapsulating all the entities
3941
this.oldPrimitive = undefined;
4042
this.geometry = new AssociativeArray();
4143
this.material = undefined;
@@ -46,30 +48,34 @@ define([
4648
this.subscriptions = new AssociativeArray();
4749
this.showsUpdated = new AssociativeArray();
4850
this.shadows = shadows;
51+
this.usingSphericalCoordinates = usingSphericalCoordinates;
52+
this.rbush = new RectangleRbush();
4953
}
5054

5155
Batch.prototype.onMaterialChanged = function() {
5256
this.invalidated = true;
5357
};
5458

55-
Batch.prototype.nonOverlapping = function(updater) {
56-
return false;
59+
Batch.prototype.nonOverlapping = function(rectangle) {
60+
return !this.rbush.collides(rectangle);
5761
};
5862

5963
Batch.prototype.isMaterial = function(updater) {
6064
var material = this.materialProperty;
6165
var updaterMaterial = updater.fillMaterialProperty;
6266

63-
if (updaterMaterial === material) {
67+
if (updaterMaterial === material ||
68+
(updaterMaterial instanceof ColorMaterialProperty && material instanceof ColorMaterialProperty)) {
6469
return true;
6570
}
6671
return defined(material) && material.equals(updaterMaterial);
6772
};
6873

69-
Batch.prototype.add = function(time, updater) {
74+
Batch.prototype.add = function(time, updater, geometryInstance) {
7075
var id = updater.id;
7176
this.updaters.set(id, updater);
72-
this.geometry.set(id, updater.createFillGeometryInstance(time));
77+
this.geometry.set(id, geometryInstance);
78+
this.rbush.insert(id, geometryInstance.geometry.rectangle);
7379
if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty)) {
7480
this.updatersWithAttributes.set(id, updater);
7581
} else {
@@ -85,8 +91,10 @@ define([
8591

8692
Batch.prototype.remove = function(updater) {
8793
var id = updater.id;
94+
var geometryInstance = this.geometry.get(id);
8895
this.createPrimitive = this.geometry.remove(id) || this.createPrimitive;
8996
if (this.updaters.remove(id)) {
97+
this.rbush.remove(id, geometryInstance.geometry.rectangle);
9098
this.updatersWithAttributes.remove(id);
9199
var unsubscribe = this.subscriptions.get(id);
92100
if (defined(unsubscribe)) {
@@ -309,15 +317,19 @@ define([
309317
StaticGroundGeometryPerMaterialBatch.prototype.add = function(time, updater) {
310318
var items = this._items;
311319
var length = items.length;
312-
for (var i = 0; i < length; i++) {
320+
var geometryInstance = updater.createFillGeometryInstance(time);
321+
var usingSphericalCoordinates = GroundPrimitive.shouldUseSphericalCoordinates(geometryInstance.geometry.rectangle);
322+
for (var i = 0; i < length; ++i) {
313323
var item = items[i];
314-
if (item.isMaterial(updater) && item.nonOverlapping(updater)) {
315-
item.add(time, updater);
324+
if (item.isMaterial(updater) &&
325+
item.nonOverlapping(geometryInstance.geometry.rectangle) &&
326+
item.usingSphericalCoordinates === usingSphericalCoordinates) {
327+
item.add(time, updater, geometryInstance);
316328
return;
317329
}
318330
}
319-
var batch = new Batch(this._primitives, this._appearanceType, updater.fillMaterialProperty, this._shadows);
320-
batch.add(time, updater);
331+
var batch = new Batch(this._primitives, this._appearanceType, updater.fillMaterialProperty, this._shadows, usingSphericalCoordinates);
332+
batch.add(time, updater, geometryInstance);
321333
items.push(batch);
322334
};
323335

Source/Scene/GroundPrimitive.js

+17
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ define([
55
'../Core/Cartesian3',
66
'../Core/Cartesian4',
77
'../Core/Cartographic',
8+
'../Core/Check',
89
'../Core/defaultValue',
910
'../Core/defined',
1011
'../Core/defineProperties',
@@ -32,6 +33,7 @@ define([
3233
Cartesian3,
3334
Cartesian4,
3435
Cartographic,
36+
Check,
3537
defaultValue,
3638
defined,
3739
defineProperties,
@@ -926,6 +928,21 @@ define([
926928
return destroyObject(this);
927929
};
928930

931+
/**
932+
* Computes whether the given rectangle is wide enough that texture coordinates
933+
* over its area should be computed using spherical extents instead of distance to planes.
934+
*
935+
* @param {Rectangle} rectangle A rectangle
936+
* @private
937+
*/
938+
GroundPrimitive.shouldUseSphericalCoordinates = function(rectangle) {
939+
//>>includeStart('debug', pragmas.debug);
940+
Check.typeOf.object('rectangle', rectangle);
941+
//>>includeEnd('debug');
942+
943+
return shouldUseSpherical(rectangle);
944+
};
945+
929946
/**
930947
* Texture coordinates for ground primitives are computed either using spherical coordinates for large areas or
931948
* using distance from planes for small areas.

0 commit comments

Comments
 (0)