Possible Bug: Billboard 2 pings back to Billboard 1 position during LoD re-render period

1. A concise explanation of the problem you’re experiencing.

Load 3dTileset

  1. Add a fixed marker (billboard) - M1.

  2. Add a polyline entity with a CallbackProperty( () => [lastMarker, lastGoodCartesianMouseCoordinate], false) for the entity.polyline.positions where one end is at M1 and the other end tracks under the mouse but from the depth picker.

  3. Add a marker(billboard) - M2- with a CallbackProperty( () => lastGoodCartesianMouseCoordinate, false) for the entity.position, i.e. it tracks under the mouse.

Bug observed. lastMarker is M1 position

(Optional)

  1. Add a marker (billboard) - M3 - fixed. lastMarker becomes M3 position.

polyline has correctly dynamically updated to M3 -> mouse

Bug Observed: M2 stays under mouse only while the mouse events are rapidly firing and somehow pings back to M1 position.

Bug: Our model is very big and as you zoom and rotate around is rerendering LODs. if you are in the guts of it it might take up to 20 seconds to stop loading, but depending on what they are doing they might only rarely wait for a full render to finish before moving on again in the model.

During the render phase as you are dragging the mouse around M2 is under the mouse but as soon as the mouse stops moving it pings back to the M1 position.

I have tried global variables, instance variables, redux store and they all behave the same - seems to be intrinsically related to the callback evaluation while a render is in progress.

Nothing seems to be globally wrong with the value of lastGoodCartesianMouse coordinate because there is nothing wrong with the endpoint of the polyline, it tracks under the mouse perfectly. The setter doesn’t weirdly log M1 position it is the correct position under the mouse. Logging in the move event setter shows its not being set to the M1 position anywhere near logging callback.getValue shows the callback is returning M1.position value (Cartesian3.equals() = true). not clear how the callback is evaluating to that unless the entities are getting mixed up when evaluating the position (possibly?).

Also steps 1 and 2 alone renders fine at all times, and steps 1 and 3 renders fine at all times (i.e. only add one but not both of the entities)

2. A minimal code example. If you’ve found a bug, this helps us reproduce and repair it.

This works fine in the sandbox environment, and weird behaviour is limited to the callback firing while a full render update is in progress. In our app increasing the MSSE dramatically decreases the length of time the render takes and takes us from a 20 second render with weird behaviour to 2-5 seconds of weird behaviour - so it is limited to the LoD reload phase.

I don’t know if there is a straightforward way using common public assets to deliberately recreate this heavy render environment to reproduce this window of time in the sandbox? I’m happy to try to get a proper reproduction if there is any guidance on the best way to overwork render in the sandbox.

I adapted this onto the clipping planes example disabling the planes stuff to explore in the sandbox.

var position = null;

function getPositions() {

return [tileset.boundingSphere.center, position]

}

function getPosition() {

return position

}

var moveHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

moveHandler.setInputAction(function(movement) {

if (position) {

var p = viewer.scene.pickPosition(movement.endPosition)

if§ {

position = p

}

}

}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

tileset.readyPromise.then(function() {

var boundingSphere = tileset.boundingSphere;

var radius = boundingSphere.radius;

position = boundingSphere.center;

viewer.entities.add({polyline:{positions:new Cesium.CallbackProperty(getPositions,false)}})

viewer.entities.add({position:new Cesium.CallbackProperty(getPosition,false), billboard:{eyeOffset:new Cesium.Cartesian3(0,0,-10),width:10,height:10, image:"…"}})

[…]

}

– can’t reproduce bug in sandbox, but also can’t actively cause this to happen during a massive render/LoD rebuild.

3. Context. Why do you need to do this? We might know a better way to accomplish your goal.

Show an asset at the end of the polyline as the mouse is moved around.

4. The Cesium version you’re using, your operating system and browser.

Cesium 1.46, OSX, Chrome 67

Hi Shane,

Is it possible to duplicate in a Sandcastle example with the NYC building tileset or the Chappes Church point cloud tileset?

Unfortunately this may be hard to track down without being able to duplicate. Nothing looks wrong with your code offhand.

In the meantime, Cesium3dTileset has some events you can watch for like allTilesLoaded and temporarily modify the behavior until the tiles are loaded in.

Thanks,

Gabby

Hi Gabby,

I can reproduce it in the SandCastle environment with the code below.

When the mouse stops moving the big billboard asset (width 20, height 20) with the dynamic “position” attribute pings back to the position of the fixed marker (width 2, height 2).

Thanks if anyone has time to look into this :slight_smile:

Thanks,

Shane.

Hi Gabby,

Further to reproducing this in the SandCastle environment, I am able to get this running smoothly as the mouse moves around by adding the marker(s) to a BillboardCollection() directly added via scene.primitives.add().

Each time the BillboardCollection is updated the dynamic entities seem to ping back to the now second last entity on the end of the collection, but they hide property with a dynamic “show” attribute detecting if they have shifted from under the mouse.

So this is good enough for me for now in my application, but there still seems to be some underlying moment the callback methods return something weird and unrelated to the entity they apply to - and ideally if there is something I can do to prevent it that’d be great.

Thanks,

Shane.

Hi Gabby,

As the workarounds weren’t really working - I have checked out the cesium repository and littered BillboardVisualizer.js, BillboardCollection.js, Billboard.js, Property.js, and CallbackProperty.js with console logging (thank you very much to whoever setup that SandCastle to load all the JS in separately making this very straight forward via the dev node server).

It appears that there are a lot of global variables used for scratch space and CallbackProperty.getValue() - which was a global var itself in the SandCastle example or a redux store variable in our app - was getting set to another entity position via an intermediate variable that persists across entity loops in the render i.e. the mouse moves causing a render with it under the mouse, then a tick occurs causing it to pop back to the position of the other entity.

I don’t believe it is clear that the callback provided to CallbackProperty needs to return a unique JS object to avoid this, but I apologise if I am wrong and it is spelled out clearly somewhere. It looks like it is a pattern within the codebase to Cesium.Cartesian3.clone for safety, so that is what I have done.

Sandcastle - dynamic entity pings back to other entity position

Sandcastle - Fixed

Thanks,

Shane.

Thanks to everyone who waded through my posts,

tl;dr - fixed up PR 6815 - Don’t overwrite scratch variables in entity visualizers #6815

Hi Shane, nice catch and thanks for all your work in tracking down the problem! I’m glad you were able to track it down to solve your issue.