Feature coloring and tileset tree levels?

Hi!

I have copied this code to my own test:

https://github.com/AnalyticalGraphicsInc/cesium/blob/5838c6ff8e98366b5d6d929bc29d2a5ff5d69ec6/Apps/Sandcastle/gallery/3D%20Tiles%20Feature%20Picking.html#L103

When I do these:

var pickedFeature = viewer.scene.pick(movement.endPosition);

pickedFeature.color = Cesium.Color.YELLOW;

… the color changes correctly, but when zooming out and dropping to lower LOD-tileset tree level, color is not there anymore. I have confirmed that the batch-id of this feature is the same on each tree level. Is there any way to color accross lod levels?

The ability to color the same batchId across different tiles is not supported right now.

The manual approach would be to store a list of all tiles in the tree (by getting tileset._root and then recursively populating with each tile’s children array), and for each tile calling tile.content.getFeature(pickedFeature._batchId).color = Ceisum.Color.YELLOW. This approach accesses a bunch of private variables though so I wouldn’t recommend it as a long term solution.

This is quite a showstopper actually…

I also just realized that in my tiles the batchId is -not- the same accross tiles. Instead it is this way:
“In the Binary glTF section, each vertex has an numeric batchId attribute in the integer range [0, number of models in the batch - 1].”

First of all I quess I would need a custom batchId. It seems to be a float-type parameter in shader code at least, so having an arbitrary id could be possible.

Right now the only option would be to map external id from batchTable to batchId and then somehow set the color using that… but it easily ends up in very costly traversal of every feature in every tile!

I ended up adding the “custom id” to batchId map like this:

function createFeatures(content) {

var tileset = content._tileset;

var featuresLength = content.featuresLength;

if (!defined(content._features) && (featuresLength > 0)) {

content._idToFeatureMap = {};

var features = new Array(featuresLength);

for (var i = 0; i < featuresLength; ++i) {

features[i] = new Cesium3DTileFeature(tileset, content, i);

var featureId = features[i].getProperty(“id”);

if (defined(featureId))

{

if (!(featureId in content._idToFeatureMap))

content._idToFeatureMap[featureId] = ;

content._idToFeatureMap[featureId].push(features[i]);

}

}

content._features = features;

}

}

Then I use this map to color accross tileset, just like you suggested. This seems to work very well for now.

Only I am wondering that does this work even if tiles get unloaded from time to time?

There actually was a problem with this approach still. Not all the tiles are in memory when color is applied. I was able to fix this problem by doing this for now:

tileset.tileVisible.addEventListener(function(tile)

{

for (var i in coloredFeatureIds)

{

var features = tile.content.getFeaturesById(coloredFeatureIds[i]);

if (defined(features))

for(var f in features)

applyColor(features[f]);

}

});

… but it seems to fire very often and performance might suffer with lots of colorings. I am looking for a way to fire event in “tile loaded” instead, but was not succesfull yet.

The code was definitely lacking a tileLoad event, so I added that here: https://github.com/AnalyticalGraphicsInc/cesium/pull/5628

Hopefully that will help out some more.

I tried this new event but it does not work. When tileVisible is fired, i quess the batchTable texture etc. are initialized more properly and we can actually get the feature’s properties. At the tileLoad event something still seems uninitialized for the coloring to work.

Hi! Would there be any ideas on this? The new event works as expected, but tile has in-complete state at the time of the event. Would there be some “update” etc. function that I could call for the loaded tile to execute initialization?

I replied to your personal message a few days ago, not sure if you received it.

I’m not sure why the tile state would be incomplete when the event is called since update needs to be called before the tile’s content becomes ready. The code below at least works for me. Do you have some example code that shows the problem you’re having?

The batch table should be updated by the time the promise is resolved, unless I’m missing something. The code below works for me in Sandcastle:

// Building data courtesy of NYC OpenData portal: http://www1.nyc.gov/site/doitt/initiatives/3d-building.page

var viewer = new Cesium.Viewer(‘cesiumContainer’);

var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({

url: ‘https://beta.cesium.com/api/assets/1461?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYWJmM2MzNS02OWM5LTQ3OWItYjEyYS0xZmNlODM5ZDNkMTYiLCJpZCI6NDQsImFzc2V0cyI6WzE0NjFdLCJpYXQiOjE0OTkyNjQ3NDN9.vuR75SqPDKcggvUrG_vpx0Av02jdiAxnnB1fNf-9f7s

}));

tileset.readyPromise.then(function() {

var boundingSphere = tileset.boundingSphere;

viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0.0, -0.5, boundingSphere.radius / 4.0));

viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);

tileset.tileLoad.addEventListener(function(tile) {

   var feature = tile.content.getFeature(0);

   feature.color = Cesium.Color.YELLOW;

   console.log(feature.getProperty('name'));

});

}).otherwise(function(error) {

throw(error);

});

``