Tile mesh manipulation


I am looking for a way to smooth and/or flatten certain portion of the terrain at run-time.

Ideally, I would pick up a tile and update its mesh but I am not sure this can be done directly.

The other option would be to generate terrain data and replace some tiles on the fly.

Could someone give me few pointers on how this could be done?

Many thanks,


Hey Xavier,

With the way you load terrain in Cesium normally, I don’t think this is possible. Normally, you would create a TerrainProvider and point it at a server url with processed terrain tiles.

However, you may be able to accomplish what you are looking to do by loading in a 3D model. You would need to load a copy of the mesh of the current tile, then somehow send that new mesh back to the server and have it update the tile.

Definitely an interesting problem, let us know if you find a good solution!



How can 3d models be used for terrain?

I am interested in a solution for the original question aswell, just dont see how 3d models can solve this?

To generalize the question abit into a subset of questions that might help solve this.

  1. If we create a wrapper terrain provider that wraps the normal mesh provider, do libraries exists (or where in cesiumjs is the code located that reads the mesh tiles) such we may call the base terrain provider, alter the terrain mesh tile before passing it on to cesium.

  2. Is it possible to reload a specific tile on a terrain provider without reloading everyhing?

If we can answer those two questions, we should be able to code a solution that allows to fill in soil or remove soil from a terrain solution in real time without sending to backend to compute?

I believe it is possible to get mesh data for a specific tile, manipulate it and generate a new tile using Cesium.HeightmapTerrainData

I am just not quite sur how to put this all together. This github issue is mentioning the process as I believe it should work:

Anybody able to fill in the blanks?



You would not use the 3d model as a terrain provider, rather you would just add the model to the scene at your intended position. See the models tutorial for more info.

Aha, thanks Xavier. It looks like you can manipulate the mesh verticies using HeightMapTerrainData. However, you’re terrain data would need to be in the heightmap format initially and you can manipulate the height values.


Following up on this topic.

With the great help of “slozier” in the mentioned Github issue, who shared a code example with me, I built a wrapper around the default CesiumTerrainProvider.

The wrapper test tiles for intersection (when requestTileGeometry is called) with a defined rectangle region and if matching returns a custom promise.

That promise generates a new tile using HeightmapTerrainData.

This works perfectly and generates valid mesh that renders fine in the viewer.

However, I can see artefacts in the form of flickering imagery tiles: they turn dark and normal again depending on camera movement or Sun position (see screenshot). Also, the issue is only visible when terrain provider is constructed with requestWaterMask=true.

Which leads me to think that vertex normals are not created properly by the HeightmapTerrainData method.

Any idea on that I am doing wrong?

Finally, I also tried to generate Quantized mesh instead but could not get the tiles to display (just get a hole instead)

Here the code I use - anything blatantly wrong in there?

var tileRect = this.baseProvider.tilingScheme.tileXYToRectangle(x, y, level);
var tileCenter = new Cesium.Cartesian3(tileRect.west + tileRect.width / 2, tileRect.north + tileRect.height / 2, height);

var data = new Cesium.QuantizedMeshTerrainData({
    minimumHeight : height,
    maximumHeight : height,
    quantizedVertices : new Uint16Array([// order is SW NW SE NE
                                         // longitude
                                         0, 0, 32767, 32767,
                                         // latitude
                                         0, 32767, 0, 32767,
                                         // heights
                                         0, 0, 0, 0]),
    indices : new Uint16Array([0, 3, 1,
                               0, 2, 3]),
    boundingSphere : new Cesium.BoundingSphere(tileCenter, 100000),
    orientedBoundingBox : new Cesium.OrientedBoundingBox(tileCenter, Cesium.Matrix3.fromRotationX(Cesium.Math.PI, new Cesium.Matrix3())),
    horizonOcclusionPoint : tileCenter,
    westIndices : [0, 1],
    southIndices : [0, 2],
    eastIndices : [2, 3],
    northIndices : [1, 3],
    westSkirtHeight : 1.0,
    southSkirtHeight : 1.0,
    eastSkirtHeight : 1.0,
    northSkirtHeight : 1.0,

    childTileMask: 0




Is the code on github ? then I could also play around and see if I could get it working and solve the issues you mention

Here is my (very crude) test terrain provider. It flattens a piece of terrain (0 alt) near London Hearthrow airport.
Artefact becomes visible when getting close to the ground.

var FlatTerrainProvider = function(options) {
    this.baseProvider = new Cesium.CesiumTerrainProvider(options);

    this.setRegion(51.46491,-0.44692); // London Heathrow

FlatTerrainProvider.prototype = {

   get availability() {
       return this.baseProvider.availability;
   get credit() {
       return this.baseProvider.credit;
   get errorEvent() {
       return this.baseProvider.errorEvent;
   get hasVertexNormals() {
       return this.baseProvider.hasVertexNormals;
   get hasWaterMask() {
       return this.baseProvider.hasWaterMask;
   get ready() {
       return this.baseProvider.ready;
   get readyPromise() {
       return this.baseProvider.readyPromise;
   get tilingScheme() {
       return this.baseProvider.tilingScheme;
   getLevelMaximumGeometricError: function(level) {
       return this.baseProvider.getLevelMaximumGeometricError(level);
   getTileDataAvailable: function(x, y, level) {
       return this.baseProvider.getTileDataAvailable(x, y, level);
    setRegion: function(lat, lon) {
        this.region = Cesium.Rectangle.fromDegrees(lon - 0.02, lat - 0.02, lon + 0.02, lat + 0.02);

    requestTileGeometry: function(x, y, level, request) {
      var t00 = this.baseProvider.getTileDataAvailable(x * 2, y * 2, level + 1);
      var t01 = this.baseProvider.getTileDataAvailable(x * 2, y * 2 + 1, level + 1);
      var t10 = this.baseProvider.getTileDataAvailable(x * 2 + 1, y * 2, level + 1);
      var t11 = this.baseProvider.getTileDataAvailable(x * 2 + 1, y * 2 + 1, level + 1);

      if (level > 13 || !(t00 && t01 && t10 && t11)) {
         var size = 2;
         var tileRect = this.baseProvider.tilingScheme.tileXYToRectangle(x, y, level);
         var widthBuffer = 2 * tileRect.width / (size - 1);
         var heightBuffer = 2 * tileRect.height / (size - 1);
         var union = Cesium.Rectangle;

            var regionRect = new Cesium.Rectangle(this.region.west - widthBuffer, this.region.south - heightBuffer, this.region.east + widthBuffer, this.region.north + heightBuffer);
            var intersection = Cesium.Rectangle.intersection(regionRect, tileRect);
            if (intersection !== undefined) {
                var promise = this.baseProvider.requestTileGeometry(x, y, level, request);
                if (promise === undefined) return undefined;
            return this.getPromise(promise, size, tileRect, intersection);

      return this.baseProvider.requestTileGeometry(x, y, level, request);

   getPromise: function(promise, size, tileRect, intersection) {

      if (promise === undefined) return undefined;

        var height = 0;

      return promise.then((terrainData) => {

            var size = 2;
         var heights = new Float32Array(size * size);
         for (var i = 0; i < size; i++) {
            for (var j = 0; j < size; j++) {
                    heights[i * size + j] = height;

         var childTileMask = 0;

         return new Cesium.HeightmapTerrainData({ buffer: heights, width: size, height: size, childTileMask: childTileMask });

By the way, there is a mistake in the QuantizedMeshTerrainData documentation example: it sates southIndices : [0, 1] where it should be**southIndices : [0, 2]

So, nobody has any idea why vertex normals are messed up after using HeightmapTerrainData?



Hi, Xavier, if you are still available, I have some questions! I am working on an issue very similar to this where terrain can be selected, flattened, then raised or lowered by users. Thus I am wondering if you were able to solve the tile flickering issue? Also, if you have any other comments you think could be helpful, they would be greatly appreciated!