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

Widget tweaks #870

Merged
merged 15 commits into from
Jun 18, 2013
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Beta Releases
* Fixed an issue in `BaseLayerPicker` where destroy wasn't properly cleaning everything up.
* Added the ability to unsubscribe to `Timeline` update event.
* Added a `screenSpaceEventHandler` property to `CesiumWidget`. Also added a `sceneMode` option to the constructor to set the initial scene mode.
* Added `useDefaultRenderLoop` property to `CesiumWidget` that allows the default render loop to be disabled so that a custom render loop can be used.
* Fix resizing issues in `CesiumWidget` ([#608](https://github.com/AnalyticalGraphicsInc/cesium/issues/608)) by having the default render loop force a resize event every 60 frames.

### b17 - 2013-06-03

Expand Down
75 changes: 50 additions & 25 deletions Source/Widgets/CesiumWidget/CesiumWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ define([
return buildModuleUrl('Assets/Textures/SkyBox/tycho2t3_80_' + suffix + '.jpg');
}

function startRenderLoop(widget) {
function render() {
if (widget._useDefaultRenderLoop) {
var frameNumber = widget._scene.getFrameState().frameNumber;
if (widget._needResize || (frameNumber % 60) === 0) {
widget.resize();
widget._needResize = false;
}

widget.render();
requestAnimationFrame(render);
}
}
requestAnimationFrame(render);
}

/**
* A widget containing a Cesium scene.
*
Expand All @@ -63,6 +79,7 @@ define([
* @param {ImageryProvider} [options.imageryProvider=new BingMapsImageryProvider()] The imagery provider to serve as the base layer. If set to false, no imagery provider will be added.
* @param {TerrainProvider} [options.terrainProvider=new EllipsoidTerrainProvider] The terrain provider.
* @param {SceneMode} [options.sceneMode=SceneMode.SCENE3D] The initial scene mode.
* @param {Object} [options.useDefaultRenderLoop=true] True if this widget should control the render loop, false otherwise.
* @param {Object} [options.contextOptions=undefined] Properties corresponding to <a href='http://www.khronos.org/registry/webgl/specs/latest/#5.2'>WebGLContextAttributes</a> used to create the WebGL context. This object will be passed to the {@link Scene} constructor.
*
* @exception {DeveloperError} container is required.
Expand Down Expand Up @@ -162,6 +179,7 @@ define([
this._clock = defaultValue(options.clock, new Clock());
this._transitioner = new SceneTransitioner(scene, ellipsoid);
this._screenSpaceEventHandler = new ScreenSpaceEventHandler(canvas);
this._useDefaultRenderLoop = undefined;

if (options.sceneMode) {
if (options.sceneMode === SceneMode.SCENE2D) {
Expand All @@ -180,15 +198,7 @@ define([
};
window.addEventListener('resize', this._resizeCallback, false);

//Create and start the render loop
this._isDestroyed = false;
function render() {
if (!that._isDestroyed) {
that.render();
requestAnimationFrame(render);
}
}
requestAnimationFrame(render);
this.useDefaultRenderLoop = defaultValue(options.useDefaultRenderLoop, true);
};

defineProperties(CesiumWidget.prototype, {
Expand Down Expand Up @@ -280,12 +290,37 @@ define([
* Gets the screen space event handler.
* @memberof CesiumWidget.prototype
*
* @returns {ScreenSpaceEventHandler}
* @type {ScreenSpaceEventHandler}
*/
screenSpaceEventHandler : {
get : function() {
return this._screenSpaceEventHandler;
}
},

/**
* Gets or sets whether or not this widget should control the render loop.
* If set to true the widget will use {@link requestAnimationFrame} to
* perform rendering and resizing of the widget, as well as drive the
* simulation clock. If set to false, you must manually call the
* <code>resize</code>, <code>render</code> methods as part of a custom
* render loop.
* @memberof CesiumWidget.prototype
*
* @type {Boolean}
*/
useDefaultRenderLoop : {
get : function() {
return this._useDefaultRenderLoop;
},
set : function(value) {
if (this._useDefaultRenderLoop !== value) {
this._useDefaultRenderLoop = value;
if (value) {
startRenderLoop(this);
Copy link
Contributor

Choose a reason for hiding this comment

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

The problem here is that setting this to false and true repeatedly inside a single animation frame could kick off multiple render loops. We're assuming that this will stay false long enough that the current loop will fire and fizzle one last time.

Instead, we could have a private boolean tracking whether the render loop is active or not, that would get set to false on the next animation frame after the user turns this off. That way we could be certain that the previous loop has ended. The downside is, if the render loop crashed, the value would never get set false, and you could never start a new loop. But, that's the situation master is in right now. Not sure if there's a better plan. I think the official spec of requestAnimationFrame returns a handle to the request that can be cancelled directly, but of course we're using polyfills and vendor-prefixed versions of that function that may not follow the spec correctly there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a good point, and I'll look to address it in the code before this comes in. It used to be that the official spec for requestAnimationFrame had no way to cancel it and Firefox had support but it was non-standard. Looking at the spec on MDN, it looks like we can actually cancel the running request. https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame

Copy link
Contributor

Choose a reason for hiding this comment

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

if the render loop crashed, the value would never get set false, and you could never start a new loop

You could just make sure to clear the flag at the very top of render, before doing anything else.

Copy link
Contributor

Choose a reason for hiding this comment

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

Be careful, our implementation of requestAnimationFrame does not preserve the return value.

}
}
}
}
});

Expand All @@ -305,24 +340,19 @@ define([
CesiumWidget.prototype.destroy = function() {
window.removeEventListener('resize', this._resizeCallback, false);
this._container.removeChild(this._element);
this._isDestroyed = true;
destroyObject(this);
};

/**
* Call this function when the widget changes size, to update the canvas
* size, camera aspect ratio, and viewport size. This function is called
* automatically on window resize.
* Updates the canvas size, camera aspect ratio, and viewport size.
* This function is called automatically as needed unless
* <code>useDefaultRenderLoop</code> is set to false.
* @memberof CesiumWidget
*/
CesiumWidget.prototype.resize = function() {
var width = this._canvas.clientWidth;
var height = this._canvas.clientHeight;

if (this._canvas.width === width && this._canvas.height === height) {
return;
}

this._canvas.width = width;
this._canvas.height = height;

Expand All @@ -336,16 +366,11 @@ define([
};

/**
* Forces an update and render of the scene. This function is called
* automatically.
* Renders the scene. This function is called automatically
* unless <code>useDefaultRenderLoop</code> is set to false;
* @memberof CesiumWidget
*/
CesiumWidget.prototype.render = function() {
if (this._needResize) {
this.resize();
this._needResize = false;
}

var currentTime = this._clock.tick();
this._scene.initializeFrame();
this._scene.render(currentTime);
Expand Down
Loading