Code Monkey home page Code Monkey logo

quantized-mesh's Introduction

quantized-mesh-1.0 terrain format

Have a question? Discuss the quantized-mesh specification on the Cesium community forum.

A terrain tileset in quantized-mesh-1.0 format is a simple multi-resolution quadtree pyramid of meshes. All tiles have the extension .terrain. So, if the Tiles URL for a tileset is:

http://example.com/tiles

Then the two root files of the pyramid are found at these URLs:

The eight tiles at the next level are found at these URLs:

When requesting tiles, be sure to include the following HTTP header in the request:

Accept: application/vnd.quantized-mesh,application/octet-stream;q=0.9

Otherwise, some servers may return a different representation of the tile than the one described here.

Each tile is a specially-encoded triangle mesh where vertices overlap their neighbors at tile edges. In other words, at the root, the eastern-most vertices in the western tile have the same longitude as the western-most vertices in the eastern tile.

Terrain tiles are served gzipped. Once extracted, tiles are little-endian, binary data. The first part of the file is a header with the following format. Doubles are IEEE 754 64-bit floating-point numbers, and Floats are IEEE 754 32-bit floating-point numbers.

struct QuantizedMeshHeader
{
    // The center of the tile in Earth-centered Fixed coordinates.
    double CenterX;
    double CenterY;
    double CenterZ;

    // The minimum and maximum heights in the area covered by this tile.
    // The minimum may be lower and the maximum may be higher than
    // the height of any vertex in this tile in the case that the min/max vertex
    // was removed during mesh simplification, but these are the appropriate
    // values to use for analysis or visualization.
    float MinimumHeight;
    float MaximumHeight;

    // The tile’s bounding sphere.  The X,Y,Z coordinates are again expressed
    // in Earth-centered Fixed coordinates, and the radius is in meters.
    double BoundingSphereCenterX;
    double BoundingSphereCenterY;
    double BoundingSphereCenterZ;
    double BoundingSphereRadius;

    // The horizon occlusion point, expressed in the ellipsoid-scaled Earth-centered Fixed frame.
    // If this point is below the horizon, the entire tile is below the horizon.
    // See http://cesiumjs.org/2013/04/25/Horizon-culling/ for more information.
    double HorizonOcclusionPointX;
    double HorizonOcclusionPointY;
    double HorizonOcclusionPointZ;
};

Immediately following the header is the vertex data. An unsigned int is a 32-bit unsigned integer and an unsigned short is a 16-bit unsigned integer.

struct VertexData
{
    unsigned int vertexCount;
    unsigned short u[vertexCount];
    unsigned short v[vertexCount];
    unsigned short height[vertexCount];
};

The vertexCount field indicates the size of the three arrays that follow. The three arrays contain the delta from the previous value that is then zig-zag encoded in order to make small integers, regardless of their sign, use a small number of bits. Decoding a value is straightforward:

var u = 0;
var v = 0;
var height = 0;

function zigZagDecode(value) {
    return (value >> 1) ^ (-(value & 1));
}

for (i = 0; i < vertexCount; ++i) {
    u += zigZagDecode(uBuffer[i]);
    v += zigZagDecode(vBuffer[i]);
    height += zigZagDecode(heightBuffer[i]);

    uBuffer[i] = u;
    vBuffer[i] = v;
    heightBuffer[i] = height;
}

Once decoded, the meaning of a value in each array is as follows:

Field Meaning
u The horizontal coordinate of the vertex in the tile. When the u value is 0, the vertex is on the Western edge of the tile. When the value is 32767, the vertex is on the Eastern edge of the tile. For other values, the vertex's longitude is a linear interpolation between the longitudes of the Western and Eastern edges of the tile.
v The vertical coordinate of the vertex in the tile. When the v value is 0, the vertex is on the Southern edge of the tile. When the value is 32767, the vertex is on the Northern edge of the tile. For other values, the vertex's latitude is a linear interpolation between the latitudes of the Southern and Nothern edges of the tile.
height The height of the vertex in the tile. When the height value is 0, the vertex's height is equal to the minimum height within the tile, as specified in the tile's header. When the value is 32767, the vertex's height is equal to the maximum height within the tile. For other values, the vertex's height is a linear interpolation between the minimum and maximum heights.

Immediately following the vertex data is the index data. Indices specify how the vertices are linked together into triangles. If tile has more than 65536 vertices, the tile uses the IndexData32 structure to encode indices. Otherwise, it uses the IndexData16 structure.

To enforce proper byte alignment, padding is added before the IndexData to ensure 2 byte alignment for IndexData16 and 4 byte alignment for IndexData32.

struct IndexData16
{
    unsigned int triangleCount;
    unsigned short indices[triangleCount * 3];
}

struct IndexData32
{
    unsigned int triangleCount;
    unsigned int indices[triangleCount * 3];
}

Indices are encoded using the high water mark encoding from webgl-loader. Indices are decoded as follows:

var highest = 0;
for (var i = 0; i < indices.length; ++i) {
    var code = indices[i];
    indices[i] = highest - code;
    if (code === 0) {
        ++highest;
    }
}

Each triplet of indices specifies one triangle to be rendered, in counter-clockwise winding order. Following the triangle indices is four more lists of indices:

struct EdgeIndices16
{
    unsigned int westVertexCount;
    unsigned short westIndices[westVertexCount];

    unsigned int southVertexCount;
    unsigned short southIndices[southVertexCount];

    unsigned int eastVertexCount;
    unsigned short eastIndices[eastVertexCount];

    unsigned int northVertexCount;
    unsigned short northIndices[northVertexCount];
}

struct EdgeIndices32
{
    unsigned int westVertexCount;
    unsigned int westIndices[westVertexCount];

    unsigned int southVertexCount;
    unsigned int southIndices[southVertexCount];

    unsigned int eastVertexCount;
    unsigned int eastIndices[eastVertexCount];

    unsigned int northVertexCount;
    unsigned int northIndices[northVertexCount];
}

These index lists enumerate the vertices that are on the edges of the tile. It is helpful to know which vertices are on the edges in order to add skirts to hide cracks between adjacent levels of detail.

Tiling Scheme and Coordinate System

By default, the data is tiled according to the Tile Map Service (TMS) layout and global-geodetic system. These defaults can be varied by specifying the projection and scheme.

Allowed values for the projection are EPSG:3857 (Web Mercator as used by Google Maps) and EPSG:4326 (Lat/Lng coordinates in the Global-Geodetic System). It is worth noting that the EPSG3857 projection has only 1 tile at the root (zoom level 0) while EPSG:4326 has 2.

The options for the tiling scheme are tms and slippyMap. The Y coordinates are numbered from the south northwards (eg. latitudes) in the tms standard whereas slippyMap coordinates have their origin at top left (NW).

For Cesium terrain layers these options can be set in the layer.json manifest file. If not specified, they default to EPSG:4326 and tms.

Extensions

Extension data may follow to supplement the quantized-mesh with additional information. Each extension begins with an ExtensionHeader, consisting of a unique identifier and the size of the extension data in bytes. An unsigned char is a 8-bit unsigned integer.

struct ExtensionHeader
{
    unsigned char extensionId;
    unsigned int extensionLength;
}

As new extensions are defined, they will be assigned a unique identifier. If no extensions are defined for the tileset, an ExtensionHeader will not included in the quanitzed-mesh. Multiple extensions may be appended to the quantized-mesh data, where ordering of each extension is determined by the server.

Multiple extensions may be requested by the client by delimiting extension names with a -. For example, a client can request vertex normals and watermask using the following Accept header:

Accept : 'application/vnd.quantized-mesh;extensions=octvertexnormals-watermask'

The following extensions may be defined for a quantized-mesh:

Terrain Lighting

Name: Oct-Encoded Per-Vertex Normals

Id: 1

Description: Adds per vertex lighting attributes to the quantized-mesh. Each vertex normal uses oct-encoding to compress the traditional x, y, z 96-bit floating point unit vector into an x,y 16-bit representation. The 'oct' encoding is described in "A Survey of Efficient Representations of Independent Unit Vectors", Cigolle et al 2014: http://jcgt.org/published/0003/02/01/

Data Definition:

struct OctEncodedVertexNormals
{
    unsigned char xy[vertexCount * 2];
}

Requesting: For oct-encoded per-vertex normals to be included in the quantized-mesh, the client must request this extension by using the following HTTP Header:

Accept : 'application/vnd.quantized-mesh;extensions=octvertexnormals'

Comments: The original implementation of this extension was requested using the extension name vertexnormals. The vertexnormals extension identifier is deprecated and implementations must now request vertex normals by adding octvertexnormals in the request header extensions parameter, as shown above.

Water Mask

Name: Water Mask

Id: 2

Description: Adds coastline data used for rendering water effects. The water mask is either 1 byte, in the case that the tile is all land or all water, or it is 256 * 256 * 1 = 65536 bytes if the tile has a mix of land and water. Each mask value is 0 for land and 255 for water. Values in the mask are defined from north-to-south, west-to-east; the first byte in the mask defines the watermask value for the northwest corner of the tile. Values between 0 and 255 are allowed as well in order to support anti-aliasing of the coastline.

Data Definition:

A Terrain Tile covered entirely by land or water is defined by a single byte.

struct WaterMask
{
    unsigned char mask;
}

A Terrain Tile containing a mix of land and water define a 256 x 256 grid of height values.

struct WaterMask
{
    unsigned char mask[256 * 256];
}

Requesting: For the watermask to be included in the quantized-mesh, the client must request this extension by using the following HTTP Header:

Accept : 'application/vnd.quantized-mesh;extensions=watermask'

Metadata

Name: Metadata

Id: 4

Description: Adds a JSON object to each tile that can store extra information about the tile. Potential uses include storing the what type of land is in the tile (eg. forest, desert, etc) or availability of child tiles.

Data Definition:

struct Metadata
{
    unsigned int jsonLength;
    char json[jsonLength];
}

Requesting: For the metadata to be included in the quantized-mesh, the client must request this extension by using the following HTTP Header:

Accept : 'application/vnd.quantized-mesh;extensions=metadata'

quantized-mesh's People

Contributors

abwood avatar analyticalgraphics avatar jtfell avatar kring avatar lilleyse avatar mramato avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quantized-mesh's Issues

Improve spec by including info on encoding and layer.json

For devs who want to create their own quantized mesh tiles, it would be helpful in the spec to have algorithms describing that process. It's great that there are very specific algorithms on the decoding process, but that makes the assumption that there is data already encoded. Some applications may need to create their own tile sets dynamically and serve them.

The encoding process has been the biggest hurdle for me, and I imagine others have faced similar issues.

Also, I've found various information scattered about regarding the layer.json file. Much more information on this file would be extremely helpful.

Clarification of Oct-Encoded Per-Vertex Normals

The paper that the spec refers to that defines oct encoding of normals talks about the snorm8 data type, which they define on page 6 as having an integer representation of -127 to +127. This spec uses unsigned char for the values, which is 0 to 255. Do we subtract 128, or do we cast to signed char? And either way we can end up with -128, so how is that handled?

Specify the JSON structure

The quantized mesh representation relies on a layer.json file that is mentioned in the README, but there is virtually no information about what this file actually is.

There was a thread in the forum which contained an example and a very basic description of the contents. (Some further internal discussion revolved around the role of availability, but that seems to be a broader topic). Essentially, people seem to reverse engineer the CesiumTerrainProvider.js to find out what this JSON may contain. Doing a websearch for "layer.json" cesium makes clear that people are struggling with this file in many ways. From what I've found until now, the layer.json seems to be a subset of the tilejson format, but it's not clear which parts of this are actually supported.

In any case: If it is a tilejson file, then this should be mentioned in the README - in doubt, with a tilejson version number, or details about which parts of tilejson are actually understood by the CesiumTerrainProvider.

precision of VertexData::height

if the type of height is unsigned short, we should use [0, 65,535] instead of [0, 32767]. I guess we choose 32767 due to compatibility issues. Thank you for the explanation.

VertexData description does not specify delta encoding

Reading the document in this repos and here there is no mention of the fact that u, v, and height arrays are both delta and zigzag encoded.

According to the source, it is evident that CesiumTerrainProvider interprets the values as deltas:

       var i;
        var u = 0;
        var v = 0;
        var height = 0;

        function zigZagDecode(value) {
            return (value >> 1) ^ (-(value & 1));
        }

        for (i = 0; i < vertexCount; ++i) {
            u += zigZagDecode(uBuffer[i]);
            v += zigZagDecode(vBuffer[i]);
            height += zigZagDecode(heightBuffer[i]);

            uBuffer[i] = u;
            vBuffer[i] = v;
            heightBuffer[i] = height;
        }

Alternative Tiling Schemes

I would like to use the quantized-mesh terrain format using a WebMercator tiling scheme. The spec states that it uses the Tile Map Service (TMS) layout and global-geodetic profile, but I can't see why it can't be configurable per dataset.

I have implemented a POC in Cesium that allows alternative tiling schemes, but would like to discuss getting the option formalised in the spec. As this spec does not specify a manifest file (Cesium calls it the layer.json), I am unsure where it should go.

Road mask - feature request


name: Road Mask Feature Request
about: Similar to the water mask, the road mask would allow a separate rendering style for roads.


The Problem To Be Solved
The problems is that there is not a way to distinguish "types" of terrain with the expiation of water. I would like the ability to render road terrain differently than ground terrain.

Suggested Solution
Develop a road mask, similar to the water mask

Water mask array scan order is ambiguous

"Values in the mask are defined from north-to-south, west-to-east; the first byte in the mask defines the watermask value for the northwest corner of the tile."

This doesn't make it clear whether the values are in latitude-major or longitude-major order. Either order can be interpreted as complying with that sentence. I'm guess latitude-major order, so that the north edge is traversed first?

Spec clarifications

Just adding some notes regarding the spec and what should be clarified.

Judging from the description of the index buffer, it sounds like you could run into a situation where the extra skirt vertices generated client-side could push the count over 65536 which means the u16 index buffer needs to be copied over to u32 index buffer.

Don't think it's an issue in practice but something that should be clarified/addressed if the 3dtiles spec ever absorbs quantized mesh that assumes some client side post processing like skirt generation. Also pretty sure there's a reliable way to generate all 4 skirt sides of a tile using only 4 more vertices.

It doesn't mention if the edge vertices are sorted but I think it should explicitly state that they are.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.