Best practices to display an info overlay above a model that has been clamped to terrain?

I am using the Entity API to display GLTF based models on my scene with terrain enabled. The models use I am trying to have an html overlay that will work like a video game health bar - hovering above the top of the model. The goal is to translate a position “above” my model to the screen space and display it there. I think problem I have is how to get the actual position of the rendered primitive and the primitive’s bounding sphere so I can find the top. I don’t seem to have access to the primitive through the entity api.

What are the best practices for getting:

  1. the entities position with respect to the ground clamping?

  2. the top of the model’s bounding sphere (top meaning - with respect to the viewport)

I’ve edited the 3D models Sandcastle demo to show a situation where placing the html overlay at position takes the position before the clamping.

HTML:

@import url(../templates/bucket.css);

Loading...

JS:

var viewer = new Cesium.Viewer(‘cesiumContainer’, {

infoBox : false,

selectionIndicator : false,

shadows : true,

shouldAnimate : true

});

var htmlOverlay = document.getElementById(‘htmlOverlay’);

var longitude = -123.0744619;

var latitude = 44.0503706;

function createModel(url, height) {

viewer.entities.removeAll();

var position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height - 100);

var heading = Cesium.Math.toRadians(135);

var pitch = 0;

var roll = 0;

var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);

var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);

var entity = viewer.entities.add({

name : url,

position : position,

orientation : orientation,

model : {

uri : url,

minimumPixelSize : 128,

maximumScale : 20000,

heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,

}

});

viewer.trackedEntity = entity;

viewer.scene.preRender.addEventListener(function() {

var canvasPosition = viewer.scene.cartesianToCanvasCoordinates(entity.position.getValue(viewer.clock.currentTime));

if (Cesium.defined(canvasPosition)) {

htmlOverlay.style.top = canvasPosition.y + ‘px’;

htmlOverlay.style.left = canvasPosition.x + ‘px’;

}

});

}

var options = [{

text : ‘Aircraft’,

onselect : function() {

createModel(’…/…/…/…/Apps/SampleData/models/CesiumAir/Cesium_Air.glb’, 5000.0);

}

}, {

text : ‘Ground Vehicle’,

onselect : function() {

createModel(’…/…/…/…/Apps/SampleData/models/GroundVehicle/GroundVehicle.glb’, 0);

}

}, {

text : ‘Hot Air Balloon’,

onselect : function() {

createModel(’…/…/…/…/Apps/SampleData/models/CesiumBalloon/CesiumBalloon.glb’, 1000.0);

}

}, {

text : ‘Milk Truck’,

onselect : function() {

createModel(’…/…/…/…/Apps/SampleData/models/CesiumMilkTruck/CesiumMilkTruck-kmc.glb’, 0);

}

}, {

text : ‘Skinned Character’,

onselect : function() {

createModel(’…/…/…/…/Apps/SampleData/models/CesiumMan/Cesium_Man.glb’, 0);

}

}, {

text : ‘Draco Compressed Model’,

onselect : function() {

createModel(’…/…/…/…/Apps/SampleData/models/DracoCompressed/CesiumMilkTruck.gltf’, 0);

}

}];

Sandcastle.addToolbarMenu(options);

Okay. I think I figured the terrain height thing.

calculateTerrainPosition(position) {
  const carto = Cesium.Cartographic.fromCartesian(position);
  const height = this.viewer.scene.globe.getHeight(carto);
  if (height > carto.height) carto.height = height;
  return Cesium.Cartographic.toCartesian(carto);
}

does this make sense?

Hey Jan,

CesiumJS doesn’t currently expose the clamped height for entities (see the discussion here https://github.com/AnalyticalGraphicsInc/cesium/issues/6995) but using scene.getHeight is the right way to get the visual terrain height right now.

Does that solve your problem? Another way you could do it is to just create a billboard entity and display your text/graphic on top of the entity that way.

I had trouble running your Sandcastle example, an easier way to share it just click on the “Share” button at the top in Sandcastle and share that link!

Hey Omar,

Yes! scene.getHeight is now working perfectly for me, thanks. I’ll be sure to use the “Share” button next time :slight_smile: