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

Instancing billboards #3056

Merged
merged 15 commits into from
Oct 5, 2015
Merged
201 changes: 201 additions & 0 deletions Apps/Sandcastle/gallery/development/Billboards Instancing.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"> <!-- Use Chrome Frame in IE -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta name="description" content="Clamp billboards to terrain.">
<meta name="cesium-sandcastle-labels" content="Development">
<title>Cesium Demo</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.9/require.js"></script>
<script type="text/javascript">
require.config({
baseUrl : '../../../Source',
waitSeconds : 60
});
</script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<div id="terrainMenu"></div>
<div id="zoomButtons"></div>
<div id="toggleLighting"></div>
<div id="sampleButtons"></div>
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
"use strict";
//Sandcastle_Begin
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var context = scene.context;

var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({
url : '//assets.agi.com/stk-terrain/world'
});

viewer.terrainProvider = cesiumTerrainProviderMeshes;
scene.debugShowFramesPerSecond = true;

var ellipsoid = scene.globe.ellipsoid;
var billboardCollection;
var instancingEnabled = true;
var instancedArraysExtension = context._instancedArrays;
var billboardCount = 100489;
var scale = 1.0;

// seneca
var centerLongitude = -1.385205433269729;
var centerLatitude = 0.6777926580888163;
var rectangleHalfSize = 0.5;
var e = new Cesium.Rectangle(centerLongitude - rectangleHalfSize, centerLatitude - rectangleHalfSize, centerLongitude + rectangleHalfSize, centerLatitude + rectangleHalfSize);

function resetBillboardCollection() {
if (Cesium.defined(billboardCollection)) {
scene.primitives.remove(billboardCollection);
}

billboardCollection = scene.primitives.add(new Cesium.BillboardCollection({
scene : scene
}));

var gridSize = Math.sqrt(billboardCount);
for (var y = 0; y < gridSize; ++y) {
for (var x = 0; x < gridSize; ++x) {
var longitude = Cesium.Math.lerp(e.west, e.east, x / (gridSize - 1));
var latitude = Cesium.Math.lerp(e.south, e.north, y / (gridSize - 1));
var position = new Cesium.Cartographic(longitude, latitude, 10000.0);

billboardCollection.add({
position : ellipsoid.cartographicToCartesian(position),
image : '../images/facility.gif',
scale : scale
});
}
}
}

var moveAmount = new Cesium.Cartesian3(1000, 0.0, 0.0);
var positionScratch = new Cesium.Cartesian3();
function animateBillboards() {
var billboards = billboardCollection._billboards;
var length = billboards.length;
for (var i = 0; i < length; ++i) {
var billboard = billboards[i];
Cesium.Cartesian3.clone(billboard.position, positionScratch);
Cesium.Cartesian3.add(positionScratch, moveAmount, positionScratch);
billboard.position = positionScratch;
}
}

Sandcastle.addToolbarMenu([ {
text : 'Instancing Enabled',
onselect : function() {
if (!instancingEnabled) {
context._instancedArrays = instancedArraysExtension;
instancingEnabled = true;
resetBillboardCollection();
}
}
}, {
text : 'Instancing Disabled',
onselect : function() {
if (instancingEnabled) {
context._instancedArrays = undefined;
instancingEnabled = false;
resetBillboardCollection();
}
}
}]);

Sandcastle.addToolbarMenu([ {
text : '100489 billboards',
onselect : function() {
billboardCount = 100489;
resetBillboardCollection();
}
}, {
text : '10000 billboards',
onselect : function() {
billboardCount = 10000;
resetBillboardCollection();
}
}, {
text : '1024 billboards',
onselect : function() {
billboardCount = 1024;
resetBillboardCollection();
}
}, {
text : '100 billboards',
onselect : function() {
billboardCount = 100;
resetBillboardCollection();
}
}, {
text : '25 billboards',
onselect : function() {
billboardCount = 25;
resetBillboardCollection();
}
}, {
text : '4 billboard',
onselect : function() {
billboardCount = 4;
resetBillboardCollection();
}
}]);

Sandcastle.addToolbarMenu([ {
text : 'Static billboards',
onselect : function() {
resetBillboardCollection();
scene.preRender.removeEventListener(animateBillboards);
}
}, {
text : 'Animated billboards',
onselect : function() {
resetBillboardCollection();
scene.preRender.addEventListener(animateBillboards);
}
}]);

Sandcastle.addToolbarMenu([ {
text : 'Scale : 1.0',
onselect : function() {
scale = 1.0;
resetBillboardCollection();
}
}, {
text : 'Scale : 0.5',
onselect : function() {
scale = 0.5;
resetBillboardCollection();
}
}, {
text : 'Scale : 0.1',
onselect : function() {
scale = 0.1;
resetBillboardCollection();
}
}]);

resetBillboardCollection();

//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
} else if (typeof require === "function") {
require(["Cesium"], startup);
}
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Change Log
==========

### 1.15 - 2015-11-02

* Breaking changes
* ...
* Deprecated
* ...
* Decreased GPU memory usage in `BillboardCollection` and `LabelCollection` by using the WebGL ANGLE_instanced_arrays extension.

### 1.14 - 2015-10-01

* Fixed issues causing the terrain and sky to disappear when the camera is near the surface. [#2415](https:/AnalyticalGraphicsInc/cesium/issues/2415) and [#2271](https:/AnalyticalGraphicsInc/cesium/issues/2271)
Expand Down
46 changes: 42 additions & 4 deletions Source/Renderer/Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ define([

this._vertexArrayObject = getExtension(gl, ['OES_vertex_array_object']);
this._fragDepth = getExtension(gl, ['EXT_frag_depth']);
this._instancedArrays = getExtension(gl, ['ANGLE_instanced_arrays']);

this._drawBuffers = getExtension(gl, ['WEBGL_draw_buffers']);
ContextLimits._maximumDrawBuffers = defined(this._drawBuffers) ? gl.getParameter(this._drawBuffers.MAX_DRAW_BUFFERS_WEBGL) : 1;
Expand Down Expand Up @@ -307,6 +308,13 @@ define([
this._currentFramebuffer = undefined;
this._maxFrameTextureUnitIndex = 0;

// Vertex attribute divisor state cache. Workaround for ANGLE (also look at VertexArray.setVertexAttribDivisor)
this._vertexAttribDivisors = [];
this._previousDrawInstanced = false;
for (var i = 0; i < ContextLimits._maximumVertexAttributes; i++) {
this._vertexAttribDivisors.push(0);
}

this._pickObjects = {};
this._nextPickColor = new Uint32Array(1);

Expand Down Expand Up @@ -544,6 +552,19 @@ define([
}
},

/**
* <code>true</code> if the ANGLE_instanced_arrays extension is supported. This
* extension provides access to instanced rendering.
* @memberof Context.prototype
* @type {Boolean}
* @see {@link https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays}
*/
instancedArrays : {
get : function() {
return !!this._instancedArrays;
}
},

/**
* <code>true</code> if the WEBGL_draw_buffers extension is supported. This
* extensions provides support for multiple render targets. The framebuffer object can have mutiple
Expand Down Expand Up @@ -807,6 +828,7 @@ define([
var va = drawCommand.vertexArray;
var offset = drawCommand.offset;
var count = drawCommand.count;
var instanceCount = drawCommand.instanceCount;

//>>includeStart('debug', pragmas.debug);
if (!PrimitiveType.validate(primitiveType)) {
Expand All @@ -818,11 +840,19 @@ define([
}

if (offset < 0) {
throw new DeveloperError('drawCommand.offset must be omitted or greater than or equal to zero.');
throw new DeveloperError('drawCommand.offset must be greater than or equal to zero.');
}

if (count < 0) {
throw new DeveloperError('drawCommand.count must be omitted or greater than or equal to zero.');
throw new DeveloperError('drawCommand.count must be greater than or equal to zero.');
}

if (instanceCount < 0) {
throw new DeveloperError('drawCommand.instanceCount must be greater than or equal to zero.');
}

if (instanceCount > 0 && !context.instancedArrays) {
throw new DeveloperError('Instanced arrays extension is not supported');
}
//>>includeEnd('debug');

Expand All @@ -836,10 +866,18 @@ define([
if (defined(indexBuffer)) {
offset = offset * indexBuffer.bytesPerIndex; // offset in vertices to offset in bytes
count = defaultValue(count, indexBuffer.numberOfIndices);
context._gl.drawElements(primitiveType, count, indexBuffer.indexDatatype, offset);
if (instanceCount === 0) {
context._gl.drawElements(primitiveType, count, indexBuffer.indexDatatype, offset);
} else {
context._instancedArrays.drawElementsInstancedANGLE(primitiveType, count, indexBuffer.indexDatatype, offset, instanceCount);
}
} else {
count = defaultValue(count, va.numberOfVertices);
context._gl.drawArrays(primitiveType, offset, count);
if (instanceCount === 0) {
context._gl.drawArrays(primitiveType, offset, count);
} else {
context._instancedArrays.drawArraysInstancedANGLE(primitiveType, offset, count, instanceCount);
}
}

va._unBind();
Expand Down
8 changes: 8 additions & 0 deletions Source/Renderer/DrawCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ define([
*/
this.offset = defaultValue(options.offset, 0);

/**
* The number of instances to draw.
*
* @type {Number}
* @default 1
*/
this.instanceCount = defaultValue(options.instanceCount, 0);

/**
* The shader program to apply.
*
Expand Down
Loading