"Dynamic billboard" issues?

Hi there,
I am working on a project where a billbard should change according to the zoom.
The event I am listening to is scene.prerender.
each time there is a zoom change - I add a new billboard to the billboard collection.

The problem I am facing is that after a few zoom in\zoom out - I get an exception of "height must be less than 16384".

As I understand - there is a problem for keeping all of the billboards in the billboards collection and after a while - it gets full. Seems that it is related to the textureAtlas.

Does anyone can think of a solution?

Well, first thing is that you’re probably adding a new billboard way too often. To start with, you probably want to debounce/throttle the “add a billboard” logic or check for a minimum amount of distance changed before you add a new billboard, because if you’re doing it on every single render loop where the camera has moved, that’s potentially a couple dozen+ per second.

A second approach would be to pre-add several specific billboards, and either change their visibility based on calculated zoom, or give them different values for scaleByDistance/translucencyByDistance.

The third option is to keep track of how many billboards you’ve added, and delete and recreate the BillboardCollection after a specified number of changes.

The ultimate solution would be for someone to actually go fix up Cesium’s billboard and texture atlas handling so that it can reuse space, and submit those changes.

Regarding the third option - removeAll won't clean the texture atlas which will still remain full.
Is there a normal way to clean it?

I was suggesting actually removing the old BillboardCollection entirely (remove from scene primitives, remove references, let it get garbage-collected), and creating an entirely new one as a replacement.

Another option (depending on the use case) is to not use billboard at all and just render a canvas on the screen at the location the billboard is supposed to be. You can still hook into the preRender event to do this. The one downside of this approach is that the canvas would not be obscured by the earth, so rotating so it’s behind a mountain or on the other side of the world would cause it to show through. If you’re interest, I can probably post a small sample proof of concept.

Obviously enhancing BillboardCollection to make it easy to replace a texture in place is the ideal solution, but I think there’s some gotchas there that make it trickier than it sounds. One day I’ll get around to looking into it, but if someone gets there first we would be happy to look at a pull request.

Any chance you could describe what you think those “gotchas” might be? My skims through the BillboardCollection and TextureAtlas classes left me with the impression it was basically a matter of implementing the right bookkeeping for tracking what areas were used and what was available. Certainly not trivial, but not unreasonable either. Would be interested to hear what you think might make it difficult.

The nature of the texture atlas is that it may have several images that get used at different times, so you simply can’t look at all billboards currently in use and rebuild the atlas (throwing away everything not in use). Instead, you would want to add the ability to tag an item in the atlas as “dynamic” and then enhance the billboard setImage functionality so that if a dynamic image is updated it is replaced (the restriction would be that the new image would have to be the same size as the old one in order to avoid having to grow the atlas), if the image is a larger, you would have to grow it, if it’s smaller you can get away with it.

That’s at the primitive level. At the Entity API level, you could then add a Billboard property that also specifies if the texture is dynamic and use that when the image is set.

It may not actually be all that bad, but I haven’t tried it so I could be oversimplifying some things. If you take a shot at implementing this, we would definitely be interested in a pull request.

Matthew,

I followed your hints to fix the issue when using dynamic billboards. I was successfully able to update 10,000 dynamic billboards (200 updates every 200 milliseconds). Before the fix the map was crashing after 10 seconds of updates. Please let me know if I missed something.

I did the following (see code below):

Entity billboard:

  • Added a custom isDynamic boolean property when creating new billboard graphics to identify dynamic billboards. I’m tagging a billboard as dynamic when using image of type canvas (not for URL). The canvas are the ones my application constantly updates.

TextureAtlas class:

  • Addded wrapper functions to handle the _idHash.
  • Added an updateImage (imageId, image) function that is called for billboards tagged as dynamic. This function is the one that interacts with the _idHash to add indexes, check for the presence of indexes, and store new indexes. If an index is already present in the hash then that index is reused when calling the textureAtlas addImage(that, image, index);

Billboard:

  • Modified the _loadImage() function to check is the billboard is dynamic. If it is dynamic the textureAtlas updateImage (imageId, image) is called, else the addImage((imageId, image) is called. Inside this function I also have a modification to add a default icon when rendering KML containing icon URLs that are not accessible (like the X icon in google Earth).

texture atlas overflow fix:

TextureAtlas class:

/global define/
define([
‘…/Core/BoundingRectangle’,
‘…/Core/Cartesian2’,
‘…/Core/createGuid’,
‘…/Core/defaultValue’,
‘…/Core/defined’,
‘…/Core/defineProperties’,
‘…/Core/destroyObject’,
‘…/Core/DeveloperError’,
‘…/Core/loadImage’,
‘…/Core/PixelFormat’,
‘…/Core/RuntimeError’,
‘…/Renderer/Framebuffer’,
‘…/Renderer/RenderState’,
‘…/Renderer/Texture’,
‘…/ThirdParty/when’
], function(
BoundingRectangle,
Cartesian2,
createGuid,
defaultValue,
defined,
defineProperties,
destroyObject,
DeveloperError,
loadImage,
PixelFormat,
RuntimeError,
Framebuffer,
RenderState,
Texture,
when) {
“use strict”;

// The atlas is made up of regions of space called nodes that contain images or child nodes.
function TextureAtlasNode(bottomLeft, topRight, childNode1, childNode2, imageIndex) {
    this.bottomLeft = defaultValue(bottomLeft, Cartesian2.ZERO);
    this.topRight = defaultValue(topRight, Cartesian2.ZERO);
    this.childNode1 = childNode1;
    this.childNode2 = childNode2;
    this.imageIndex = imageIndex;
}

var defaultInitialSize = new Cartesian2(16.0, 16.0);

/**
 * A TextureAtlas stores multiple images in one square texture and keeps
 * track of the texture coordinates for each image. TextureAtlas is dynamic,
 * meaning new images can be added at any point in time.
 * Texture coordinates are subject to change if the texture atlas resizes, so it is
 * important to check {@link TextureAtlas#getGUID} before using old values.

Sounds useful. Maybe go put together a pull request on Github?

Wow, three years have passed since the last message on this thread.
I have implemented a working version of dynamic billboards based on Alberto’s code above. Is this still wanted as a pull request?

Yes, definitely! This capability is still missing as far as I know.