diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/README.md b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/README.md
new file mode 100644
index 0000000000..d75f077c0c
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/README.md
@@ -0,0 +1,682 @@
+# EXT\_feature\_metadata
+
+**Version 0.0.0** December 4, 2020
+
+## Contributors
+
+* Peter Gagliardi, Cesium
+* Sean Lilley, Cesium
+* Bao Tran, Cesium
+* Sam Suhag, Cesium
+* Samuel Vargas, Cesium
+* Patrick Cozzi, Cesium
+
+## Status
+
+Draft
+
+## Contents
+
+- [EXT\_feature\_metadata](#ext_feature_metadata)
+ - [Contributors](#contributors)
+ - [Status](#status)
+ - [Contents](#contents)
+ - [Overview](#overview)
+ - [Architecture](#architecture)
+ - [Classes](#classes)
+ - [Feature Tables](#feature-tables)
+ - [Feature Textures](#feature-textures)
+ - [Feature Identification](#feature-identification)
+ - [Feature ID Accessors](#feature-id-accessors)
+ - [Feature ID Mappings](#feature-id-mappings)
+ - [Feature ID Textures](#feature-id-textures)
+ - [Examples](#examples)
+
+## Overview
+
+This extension is an implementation of the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/blob/3d-tiles-next/specification/Metadata/README.md) using glTF as a base data format. This adds application-specific metadata to glTF models. This metadata can be defined both per-vertex and per-texel.
+
+The Cesium 3D Metadata Specification defines two main concepts. **Classes** describe the data types of metadata. Meanwhile, an **instance** is a concrete entity with metadata that matches the format of a class. Instances are a broad concept, as they can refer to things as small as a vertex or as large as a collection of 3D models. In the context of this extension, an instance will always refer to a geometric entity (vertices or texels) with metadata. For this extension, the geospatial term **feature** is used instead of instance. This is more specific, and it avoids confusion with the concept of instancing from 3D graphics.
+
+The Cesium 3D Metadata Specification uses integer **instance IDs** (this extension refers to them as **feature IDs**) to access the metadata for a single instance. However, it does not provide a way to label geometry with IDs. This extension defines several methods for **feature identification** the process of labeling geometry (vertices or textures) with a feature ID.
+
+For a concrete example, consider a point cloud of the house, as in the diagram
+below. The point cloud might be segmented into different regions (roof, window, walls, door), each with properties such as a component name or the year the component was built. The point cloud could also be considered as individual LiDAR samples with an intensity and temperature value. Both types of metadata can be described with this extension.
+
+
+
+Also note that this extension is flexible, allowing for a mix of geometry types. In the below diagram, there are three separate glTF primitives. One is a point cloud, the other two are triangle meshes. Each primitive can be assigned metadata separately, so multiple types of objects can be modeled in a single glTF file.
+
+
+
+## Architecture
+
+
+
+This extension has three main parts:
+
+1. [**Feature Identification**](#feature-identification) - What geometry corresponds to each feature? This process labels vertices of a primitive or texels of a texture with feature IDs.
+2. **Metadata Storage** - In what format is the metadata and where in the glTF can it be found? This extension provides [feature tables](#feature-tables) for storing metadata in binary arrays and [feature textures](#feature-id-textures) for storing metadata in textures.
+3. [**Class Definition**](#classes) - How is the metadata interpreted? This includes data type information, as well as other information.
+
+## Classes
+
+Classes are a collection of one or more related **properties**. Each property defines a single data field with a unique ID, type information, and other information such as a field name and description. Classes are defined in the root extension object, and comply with the definitions of classes from the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/blob/3d-tiles-next/specification/Metadata/0.0.0/README.md#classes).
+
+Below is an example with several classes defined:
+
+```json
+{
+ "extensions": {
+ "EXT_feature_metadata": {
+ "classes": {
+ "car": {
+ "name": "Car",
+ "description": "Cars on the road",
+ "properties": {
+ "color": {
+ "name": "Color",
+ "description": "The body color of the car",
+ "type": "STRING"
+ },
+ "driver": {
+ "name": "Driver Name",
+ "description": "The driver of the car",
+ "type": "STRING"
+ },
+ "passengers": {
+ "name": "Passenger Names",
+ "description": "Other passengers in the car",
+ "type": "ARRAY",
+ "componentType": "STRING"
+ },
+ "fuelLevel": {
+ "name": "Fuel Level",
+ "description": "Fuel level from 0.0 (empty) to 100.0 (full tank/battery)",
+ "type": "FLOAT32"
+ },
+ "isElectric": {
+ "name": "Is Electric",
+ "description": "True if car is a fully electric vehicle",
+ "type": "BOOLEAN"
+ }
+ }
+ },
+ "building": {
+ "name": "Building",
+ "properties": {
+ "height": {
+ "name": "Height (m)",
+ "type": "FLOAT64"
+ },
+ "address": {
+ "name": "Street Address",
+ "type": "STRING"
+ },
+ "coordinates": {
+ "name": "Longitude/Latitude (degrees)",
+ "description": "Geographic Coordinates",
+ "type": "ARRAY",
+ "componentType": "FLOAT64",
+ "componentCount": 2
+ },
+ "occupants": {
+ "name": "Number of Occupants",
+ "type": "UINT32"
+ }
+ }
+ },
+ "elevation": {
+ "name": "Elevation",
+ "properties": {
+ "elevation": {
+ "name": "Elevation (m)",
+ "description": "Elevation of the terrain in the scene (per-texel)",
+ "type": "UINT8"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+## Feature Tables
+
+
+
+A **feature table** stores metadata for a class in a parallel array format. Each array is called a **property array** and corresponds to a class property. The data contained within a property array must match the data type of the class property. Furthermore, the set of property arrays must match one-to-one with the class properties.
+
+Feature tables in this extension conform to the definition of "instance table" from the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/blob/3d-tiles-next/specification/Metadata/0.0.0/README.md#instance-tables) except this extension uses slightly different terminology:
+
+| `EXT_feature_metadata` Terminology | Equivalent Cesium 3D Metadata Terminology |
+|---------|------|
+| Feature Table | Instance Table |
+| Feature ID | Instance ID |
+
+The Cesium 3D Metadata Specification allows two possible encodings for feature tables: JSON and binary. However, this glTF extension **requires** [binary encoding](https://github.com/CesiumGS/3d-tiles/blob/3d-tiles-next/specification/Metadata/0.0.0/README.md#binary-encoding). Requiring binary encoding enforces best practices for runtime performance.
+
+The binary encoding requires buffer views for storing the property arrays. glTF `bufferView`s fulfill this requirement exactly.
+
+Like classes, feature tables are defined in the root extension object in the glTF model. See the example below:
+
+```json
+{
+ "extensions": {
+ "EXT_feature_metadata": {
+ "classes": {
+ "tree": {
+ "properties": {
+ "height": {
+ "name": "Height (m)",
+ "description": "Height of tree measured from ground level",
+ "type": "FLOAT32"
+ },
+ "birdCount": {
+ "name": "Bird Count",
+ "description": "Number of birds perching on the tree",
+ "type": "UINT8"
+ },
+ "species": {
+ "name": "Species",
+ "description": "Species of the tree",
+ "type": "STRING"
+ }
+ }
+ }
+ },
+ "featureTables": {
+ "treeTable": {
+ "class": "tree",
+ "properties": {
+ "height": {
+ "bufferView": 3
+ },
+ "birdCount": {
+ "bufferView": 4
+ },
+ "species": {
+ "bufferView": 5,
+ "offsetBufferViews": [6]
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+## Feature Textures
+
+
+
+**Feature textures** (not to be confused with [Feature ID Textures](#feature-id-textures)) are an alternate form of metadata storage that uses textures rather than parallel arrays to store values.
+
+Feature textures are accessed directly by texture coordinates, rather than feature IDs. This makes them well-suited for representing properties that vary with position such as elevation heightmaps or vector fields.
+
+Feature textures comply with the [Metadata Texture Encoding](https://github.com/CesiumGS/3d-tiles/blob/3d-tiles-next/specification/Metadata/0.0.0/README.md#metadata-texture-encoding) of the Cesium 3D Metadata Specification. However, the terminology in this extension is slightly different; "feature texture" is used instead of the more generic "metadata texture".
+
+**Note**: in the core glTF standard, only PNG and JPEG textures are supported. This limits data types to those based on `UINT8`:
+
+* `UINT8`
+* normalized `UINT8` (the texture stores [0, 255], but values are remapped to [0, 1])
+* `ARRAY` of `UINT8` with `componentCount` of `1-4`
+* `ARRAY` of normalized `UINT8` with `componentCount` of `1-4`
+
+In this glTF extension, textures are defined with the following steps:
+1. A class is defined in the root `EXT_feature_metadata.classes` object. This is used to describe the metadata in the texture.
+2. A feature texture is defined in the root `EXT_feature_metadata.featureTextures` object. This must reference the class ID defined in step 1.
+3. A feature texture is associated with a primitive by listing the feature texture ID in the `primitive.EXT_feature_metadata.featureTextures` array.
+
+Example:
+
+```json
+{
+ "extensions": {
+ "EXT_feature_metadata": {
+ "classes": {
+ "elevation": {
+ "name": "Elevation",
+ "properties": {
+ "elevation": {
+ "name": "Elevation (m)",
+ "description": "Elevation above the WGS84 ellipsoid"
+ }
+ }
+ }
+ },
+ "featureTextures": {
+ "elevationTexture": {
+ "class": "elevation",
+ "properties": {
+ "elevation": {
+ "texture": {
+ "index": 0,
+ "texCoord": 0
+ },
+ "channels": "r"
+ }
+ }
+ }
+ }
+ }
+ },
+ "images": [
+ {
+ "mimeType": "image/png",
+ "name": "Elevation Heightmap",
+ "uri": "elevation.png"
+ }
+ ],
+ "textures": [
+ {
+ "name": "Elevation Texture",
+ "source": 0
+ }
+ ],
+ "meshes": [
+ {
+ "name": "Feature Texture Example",
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0,
+ "NORMAL": 1,
+ "TEXCOORD_0": 2
+ },
+ "indices": 3,
+ "material": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureTextures": ["elevationTexture"]
+ }
+ }
+ }
+ ]
+ }
+ ],
+}
+```
+
+## Feature Identification
+
+The Cesium 3D Metadata Specification lets the implementation determine how to identify features. This glTF extension provides three different methods for associating vertices or texels with **feature IDs**.
+
+### Feature ID Accessors
+
+
+
+The most straightforward method for defining feature IDs is to store them directly in a buffer. In this extension, this is done by using a glTF vertex attribute. Feature ID attributes must follow the naming convention `_FEATURE_ID_X` where `X` is a non-negative integer. The first feature ID attribute is `_FEATURE_ID_0`, the second `_FEATURE_ID_1`, and so on.
+
+**Note**: Since glTF accessors do not support `UNSIGNED_INT` types for 32-bit integer, `FLOAT` may be used instead. This allows for integer feature IDs up to `2e24`. For smaller ranges of feature IDs, `UNSIGNED_BYTE` or `UNSIGNED_SHORT` can still be used. Note that this requires aligning each feature ID to 4-byte boundaries to adhere to glTF's alignment rules.
+
+Once the attribute is defined, it must be associated with a primitive. This is done by setting `featureIds.attribute` to reference the `_FEATURE_ID_X`. See the following code snippet for an example of this.
+
+```json
+{
+ "meshes": [
+ {
+ "name": "Point Cloud Mesh",
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0,
+ "_FEATURE_ID_0": 1
+ },
+ "mode": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdAttributes": [
+ {
+ "featureTable": "rooms",
+ "featureIds": {
+ "attribute": "_FEATURE_ID_0"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "extensions": {
+ "EXT_feature_metadata": {
+ "classes": {
+ "room": {
+ "name": "Room",
+ "description": "room of the house",
+ "properties": {
+ "name": {
+ "description": "Name of the room such as 'kitchen' or 'living room'",
+ "type": "STRING"
+ },
+ "wallColor": {
+ "name": "Wall Color",
+ "type": "STRING"
+ },
+ "area": {
+ "name": "Area (m^2)",
+ "type": "FLOAT32"
+ },
+ "furniture": {
+ "name": "Furniture",
+ "description": "List of furniture in the room such as 'chair' or 'table'",
+ "type": "ARRAY",
+ "componentType": "STRING"
+ }
+ }
+ }
+ },
+ "featureTables": {
+ "rooms": {
+ "class": "room",
+ "count": 8,
+ "properties": {
+ "name": {
+ "bufferView": 4,
+ "offsetBufferViews": [5]
+ },
+ "wallColor": {
+ "bufferView": 6,
+ "offsetBufferViews": [7]
+ },
+ "area": {
+ "bufferView": 8,
+ },
+ "furniture": {
+ "bufferView": 9,
+ "offsetBufferViews": [10, 11]
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+### Feature ID Mappings
+
+
+
+For large datasets, explicitly listing every feature ID can be expensive. To help avoid this, **feature ID mappings** allow for an implicit feature ID numbering scheme for common cases. The "mapping" is between `vertex ID -> feature ID` for each vertex of a single primitive.
+
+The common cases are as follows:
+
+* All vertices in a primitive have the same feature ID
+* Each vertex in a primitive has a different feature ID
+
+Though in general, feature ID mappings can be used as long as the numbering scheme can be described with the formula `featureId = constant + vertexId * vertexStride`.
+
+| Case | `constant` | `vertexStride` |
+|------|------------|----------------|
+| All vertices have a feature ID of `C` | `C` | 0 |
+| Each vertex unique: `C, C+1, C+2...` | `C` | 1 |
+| General case: `C, C+S, C+2S...` | `C` | `S` |
+
+The feature ID mapping is associated with a primitive by setting `featureIds.constant` and `featureIds.vertexStride`. See the example below.
+
+**Note**: Since feature ID mappings are described by a formula, the range of feature IDs can be arbitrarily large. Implementations should use the largest size unsigned integer type available to represent feature IDs in this case to maximize flexibility. For example, on a 64-bit CPU-based implementation, an `uint64_t` would be a good idea.
+
+The example below demonstrates a couple different use cases for feature ID mappings. The ocean point cloud is an example of per-vertex metadata, where the feature IDs are `0, 1, 2, 3, 4, ..., 9999`. Meanwhile, for the fish, there is metadata per-primtive. In other words, each vertex of the first primitive has feature ID `0`, while the vertices of the second primitive have feature ID `1`. This can be accomplished by setting the `vertexStride` to `0` (so every vertex has the same value) and the `constant` to the desired feature ID (`0` or `1` in this case). This produces feature IDs `0, 0, ..., 0` for the first primitive and `1, 1, ..., 1` for the second.
+
+```json
+{
+ "extensions": {
+ "EXT_feature_metadata": {
+ "classes": {
+ "ocean": {
+ "name": "Ocean Point Cloud",
+ "properties": {
+ "depth": {
+ "name": "Depth(m)",
+ "description": "Depth below mean sea level",
+ "type": "FLOAT64"
+ },
+ "temperature": {
+ "name": "Temperature (°C)",
+ "type": "FLOAT64"
+ },
+ "pressure": {
+ "name": "Pressure (kPa)",
+ "type": "FLOAT64",
+ },
+ "density": {
+ "name": "Density (kg/m^3)",
+ "type": "FLOAT64"
+ }
+ }
+ },
+ "fish": {
+ "name": "Fish",
+ "properties": {
+ "species": {
+ "type": "STRING"
+ },
+ "scaleColor": {
+ "type": "STRING"
+ }
+ }
+ }
+ },
+ "featureTables": {
+ "oceanTable": {
+ "class": "ocean",
+ "count": 10000,
+ "properties": {
+ "depth": {
+ "bufferView": 8
+ },
+ "temperature": {
+ "bufferView": 9
+ },
+ "pressure": {
+ "bufferView": 10
+ },
+ "density": {
+ "bufferView": 11
+ }
+ }
+ },
+ "fishTable": {
+ "class": "fish",
+ "count": 2,
+ "properties": {
+ "species": {
+ "bufferView": 12,
+ "offsetBufferViews": [13]
+ },
+ "scaleColor": {
+ "bufferView": 14,
+ "offsetBufferViews": [15]
+ }
+ }
+ }
+ }
+ }
+ },
+ "meshes": [
+ {
+ "name": "Water Point Cloud",
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0
+ },
+ "mode": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdAttributes": [
+ {
+ "featureTable": "oceanTable",
+ "featureIds": {
+ "constant": 0,
+ "vertexStride": 1
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "Fish",
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 1
+ },
+ "mode": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdAttributes": [
+ {
+ "featureTable": "fishTable",
+ "featureIds": {
+ "constant": 0,
+ "vertexStride": 0
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "attributes": {
+ "POSITION": 2
+ },
+ "mode": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdAttributes": [
+ {
+ "featureTable": "fishTable",
+ "featureIds": {
+ "constant": 1,
+ "vertexStride": 0
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
+```
+
+### Feature ID Textures
+
+
+
+Sometimes it is helpful to classify the pixels of an image into different features. Some examples include segmenting an image in a computer vision project or marking regions on a map. This extension makes it possible to add metadata for these cases with the help of **feature ID textures** (not to be confused with [Feature Textures](#feature-textures)). These are simply 1-channel unsigned integer textures that store one feature ID per texel. These feature IDs can be used to index into a feature table.
+
+**Note**: core glTF only supports PNG/JPEG textures. This effectively limits the range of feature IDs to 0-255 (i.e. a `UINT8`)
+
+Defining a feature ID texture requires the following steps:
+
+1. Define a class and feature table as usual (see [Feature Tables](#feature-tables))
+2. Define a feature texture in a primitive's `EXT_feature_metadata.featureIdTextures` object. See the example below.
+
+**Note**: When defining the texture `channels` must be a single-channel texture (e.g. `r`, `g`, `b` or `a`).
+
+```json
+{
+ "extensions": {
+ "EXT_feature_metadata": {
+ "classes": {
+ "county": {
+ "name": "County",
+ "description": "This example demonstrates how one might classify ortho imagery into a map of different jurisdictions via a feature ID texture.",
+ "properties": {
+ "countyName": {
+ "name": "County Name",
+ "type": "STRING"
+ },
+ "population": {
+ "name": "Population",
+ "type": "UINT32"
+ },
+ "area": {
+ "name": "Area (km^2)",
+ "type": "FLOAT64"
+ }
+ }
+ }
+ },
+ "featureTables": {
+ "countyTable": {
+ "class": "county",
+ "properties": {
+ "countyName": {
+ "bufferView": 7,
+ "offsetBufferViews": [8]
+ },
+ "population": {
+ "bufferView": 9
+ },
+ "area": {
+ "bufferView": 10
+ }
+ }
+ }
+ }
+ }
+ },
+ "images": [
+ {
+ "mimeType": "image/png",
+ "name": "County Boundaries Texture",
+ "uri": "county-boundaries.png"
+ }
+ ],
+ "textures": [
+ {
+ "name": "County Boundaries Texture",
+ "source": 0
+ }
+ ],
+ "meshes": [
+ {
+ "name": "Feature ID Texture Example",
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0,
+ "NORMAL": 1,
+ "TEXCOORD_0": 2
+ },
+ "indices": 3,
+ "material": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdTextures": [
+ {
+ "featureTable": "countyTable",
+ "featureIds": {
+ "texture": {
+ "texCoord": 0,
+ "index": 0
+ },
+ "channels": "r"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
+```
+
+## Examples
+
+* [microcosm](examples/microcosm/README.md) - Texture metadata example
+* [weather](examples/weather/README.md) - per-vertex metadata example
\ No newline at end of file
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/README.md b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/README.md
new file mode 100644
index 0000000000..9f66daf3b5
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/README.md
@@ -0,0 +1,16 @@
+## Texture Metadata Example: Microcosm
+
+This glTF example is a mesh that represents a lot of different types of
+terrain in one small tile
+
+This example simulates using per-texel metadata for land cover
+classification as well as the Normalized Difference Vegetation Index (NDVI)
+values.
+
+The data here is hand-made until we can get more realistic data.
+
+### Script to generate metadata
+
+To generate the metadata buffer, run `node microcosm-metadata.js`. This
+will overwrite `microcosm.bin` and print out JSON to add to the bufferViews
+of the glTF.
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/land-cover.png b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/land-cover.png
new file mode 100644
index 0000000000..1d9fdb8e60
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/land-cover.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm-metadata.bin b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm-metadata.bin
new file mode 100644
index 0000000000..6c05efd0a7
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm-metadata.bin differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm-metadata.js b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm-metadata.js
new file mode 100644
index 0000000000..b165906caa
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm-metadata.js
@@ -0,0 +1,149 @@
+'use strict';
+
+const fs = require('fs');
+
+/**
+ * Need to know these lengths:
+ * - total buffer length
+ *
+ * - LULC Name bufferView length and offsets
+ * - LULC Name offsets bufferView length and offsets
+ * - LULC Color bufferView length and offsets
+ */
+
+// This is how I mapped the color values in GIMP. Kinda haphazard admittedly.
+const landCoverValues = {
+ 0: {
+ name: "Grassland",
+ color: [118, 163, 11]
+ },
+ 32: {
+ name: "Rocky Ground",
+ color: [110, 110, 110]
+ },
+ 64: {
+ name: "Desert",
+ color: [212, 209, 182]
+ },
+ 128: {
+ name: "River",
+ color: [82, 173, 204]
+ },
+ 208: {
+ name: "Forest",
+ color: [50, 84, 51]
+ },
+ 255: {
+ name: "Building",
+ color: [194, 194, 194]
+ }
+};
+
+const unusedLandCover = {
+ name: "Unused",
+ color: [0, 0, 0]
+};
+
+const numberOfLandCoverFeatures = 256;
+const sizeOfColor = 3;
+
+function makeLandCoverColors() {
+ const bufferLength = numberOfLandCoverFeatures * sizeOfColor;
+ const buffer = Buffer.alloc(bufferLength);
+ for (let i = 0; i < numberOfLandCoverFeatures; i++) {
+ const feature = landCoverValues[i];
+ if (feature !== undefined) {
+ const [r, g, b] = feature.color;
+ buffer[3 * i] = r;
+ buffer[3 * i + 1] = g;
+ buffer[3 * i + 2] = b;
+ }
+
+ // Otherwise the default color of 0, 0, 0 is used,
+ // but the buffer is allocated with all 0s so no work is needed here.
+ }
+ return buffer;
+}
+
+function makeLandCoverNames() {
+ const offsets = new Uint32Array(numberOfLandCoverFeatures + 1);
+ let nameText = '';
+ let currentOffset = 0;
+ for (let i = 0; i < numberOfLandCoverFeatures; i++) {
+ let feature = landCoverValues[i];
+ if (feature === undefined) {
+ feature = unusedLandCover;
+ }
+
+ const name = feature.name;
+ const nameLength = Buffer.byteLength(name, 'utf8');
+
+ offsets[i] = currentOffset;
+ currentOffset += nameLength;
+
+ nameText += name;
+ }
+ offsets[numberOfLandCoverFeatures] = currentOffset;
+
+ const namesBuffer = Buffer.from(nameText, 'utf8');
+
+ // Convert from Uint32Array -> Buffer (which is a Uint8Array)
+ const offsetsBuffer = Buffer.from(offsets.buffer);
+ return [namesBuffer, offsetsBuffer];
+}
+
+function makeBufferViews() {
+ const [landCoverNames, landCoverOffsets] = makeLandCoverNames();
+ const landCoverColor = makeLandCoverColors();
+
+ return [
+ {
+ name: "LULC Name",
+ bufferView: landCoverNames
+ },
+ {
+ name: "LULC Name Offsets",
+ bufferView: landCoverOffsets,
+ },
+ {
+ name: "LULC Color",
+ bufferView: landCoverColor
+ }
+ ]
+}
+
+function makeBinFile() {
+ const bufferViewInfos = makeBufferViews();
+ const bufferViews = [];
+ const stats = [];
+ let offset = 0;
+ for (const bufferViewInfo of bufferViewInfos) {
+ if (offset % 8 != 0) {
+ const paddingBytes = 8 - (offset % 8);
+ console.log(`Offset not aligned, adding ${paddingBytes} of padding`);
+ const padding = Buffer.alloc(paddingBytes);
+ bufferViews.push(padding);
+ offset += paddingBytes;
+ }
+
+ const bufferView = bufferViewInfo.bufferView;
+ const byteLength = bufferView.byteLength;
+ stats.push({
+ name: bufferViewInfo.name,
+ buffer: 1,
+ byteLength: byteLength,
+ byteOffset: offset
+ });
+ offset += byteLength;
+
+ bufferViews.push(bufferView);
+ }
+
+ const buffer = Buffer.concat(bufferViews);
+ fs.writeFileSync('microcosm-metadata.bin', buffer, 'utf8');
+
+ console.log(JSON.stringify(stats, undefined, 4));
+ console.log('totalLength:', buffer.byteLength);
+}
+
+makeBinFile();
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm.bin b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm.bin
new file mode 100644
index 0000000000..890074680b
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm.bin differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm.gltf b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm.gltf
new file mode 100644
index 0000000000..d3891cf2c4
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/microcosm.gltf
@@ -0,0 +1,273 @@
+{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensionsUsed": [
+ "EXT_feature_metadata"
+ ],
+ "extensions": {
+ "EXT_feature_metadata": {
+ "extras": {
+ "draftVersion": "0.0.0"
+ },
+ "classes": {
+ "NDVI": {
+ "name": "Normalized difference vegetation index",
+ "properties": {
+ "NDVI": {
+ "name": "Normalized Difference Vegetation Index",
+ "type": "UINT8",
+ "normalized": true
+ }
+ }
+ },
+ "LULC": {
+ "name": "Landcover/Landuse",
+ "properties": {
+ "Name": {
+ "name": "Name",
+ "type": "STRING"
+ },
+ "Color": {
+ "name": "Color",
+ "type": "ARRAY",
+ "componentType": "UINT8",
+ "componentCount": 3
+ }
+ }
+ }
+ },
+ "featureTables": {
+ "LandCover": {
+ "class": "LULC",
+ "count": 6,
+ "elementCount": 256,
+ "properties": {
+ "Name": {
+ "bufferView": 5,
+ "offsetBufferViews": [6]
+ },
+ "Color": {
+ "bufferView": 7
+ }
+ }
+ }
+ },
+ "featureTextures": {
+ "NDVITexture": {
+ "class": "NDVI",
+ "properties": {
+ "NDVI": {
+ "texture": {
+ "index": 1,
+ "texCoord": 1
+ },
+ "channels": "r"
+ }
+ }
+ }
+ }
+ }
+ },
+ "scene": 0,
+ "scenes": [
+ {
+ "name": "Scene",
+ "nodes": [0]
+ }
+ ],
+ "nodes": [
+ {
+ "mesh": 0,
+ "name": "Terrain"
+ }
+ ],
+ "materials": [
+ {
+ "doubleSided": true,
+ "name": "Photogrammetry",
+ "pbrMetallicRoughness": {
+ "baseColorTexture": {
+ "index": 0,
+ "texCoord": 0
+ },
+ "metallicFactor": 0,
+ "roughnessFactor": 0.5
+ }
+ }
+ ],
+ "meshes": [
+ {
+ "name": "Terrain Mesh",
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0,
+ "NORMAL": 1,
+ "TEXCOORD_0": 2,
+ "TEXCOORD_1": 3
+ },
+ "indices": 4,
+ "material": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdTextures": [
+ {
+ "featureTable": "LandCover",
+ "featureIds": {
+ "texture": {
+ "texCoord": 1,
+ "index": 2
+ },
+ "channels": "r"
+ }
+ }
+ ],
+ "featureTextures": ["NDVITexture"]
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "textures": [
+ {
+ "name": "Photogrammetry Texture",
+ "source": 0
+ },
+ {
+ "name": "NDVI Texture",
+ "source": 1
+ },
+ {
+ "name": "Land Cover Texture",
+ "source": 2
+ }
+ ],
+ "images": [
+ {
+ "mimeType": "image/png",
+ "name": "Photogrammetry",
+ "uri": "photogrammetry.png"
+ },
+ {
+ "mimeType": "image/png",
+ "name": "NDVI",
+ "uri": "ndvi.png"
+ },
+ {
+ "mimeType": "image/png",
+ "name": "Land Cover",
+ "uri": "land-cover.png"
+ }
+ ],
+ "accessors": [
+ {
+ "name": "Positions",
+ "bufferView": 0,
+ "componentType": 5126,
+ "count": 62802,
+ "max": [
+ 50,
+ 20.001466751098633,
+ 50.00535202026367
+ ],
+ "min": [
+ -50,
+ -16.49410629272461,
+ -50.00535202026367
+ ],
+ "type": "VEC3"
+ },
+ {
+ "name": "Normals",
+ "bufferView": 1,
+ "componentType": 5126,
+ "count": 62802,
+ "type": "VEC3"
+ },
+ {
+ "name": "Photogrammetry UVs",
+ "bufferView": 3,
+ "componentType": 5126,
+ "count": 62802,
+ "type": "VEC2"
+ },
+ {
+ "name": "Ortho UVs",
+ "bufferView": 2,
+ "componentType": 5126,
+ "count": 62802,
+ "type": "VEC2"
+ },
+ {
+ "name": "Indices",
+ "bufferView": 4,
+ "componentType": 5123,
+ "count": 95484,
+ "type": "SCALAR"
+ }
+ ],
+ "bufferViews": [
+ {
+ "name": "Positions",
+ "buffer": 0,
+ "byteLength": 753624,
+ "byteOffset": 0
+ },
+ {
+ "name": "Normals",
+ "buffer": 0,
+ "byteLength": 753624,
+ "byteOffset": 753624
+ },
+ {
+ "name": "Ortho UVs",
+ "buffer": 0,
+ "byteLength": 502416,
+ "byteOffset": 1507248
+ },
+ {
+ "name": "Photogrammetry UVs",
+ "buffer": 0,
+ "byteLength" : 502416,
+ "byteOffset" : 2009664
+ },
+ {
+ "name": "Indices",
+ "buffer": 0,
+ "byteLength" : 190968,
+ "byteOffset" : 2512080
+ },
+ {
+ "name": "LULC Name",
+ "buffer": 1,
+ "byteLength": 1546,
+ "byteOffset": 0
+ },
+ {
+ "name": "LULC Name Offsets",
+ "buffer": 1,
+ "byteLength": 1028,
+ "byteOffset": 1552
+ },
+ {
+ "name": "LULC Color",
+ "buffer": 1,
+ "byteLength": 768,
+ "byteOffset": 2584
+ }
+ ],
+ "buffers": [
+ {
+ "name": "Geometry Buffer",
+ "byteLength": 2703048,
+ "uri": "microcosm.bin"
+ },
+ {
+ "name": "Metadata Buffer",
+ "byteLength": 3352,
+ "uri": "microcosm-metadata.bin"
+ }
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/ndvi.png b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/ndvi.png
new file mode 100644
index 0000000000..d1412e360e
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/ndvi.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/photogrammetry.png b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/photogrammetry.png
new file mode 100644
index 0000000000..f2d223d22a
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/microcosm/photogrammetry.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/make-weather.js b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/make-weather.js
new file mode 100644
index 0000000000..1f4d47019f
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/make-weather.js
@@ -0,0 +1,458 @@
+'use strict';
+
+const fs = require('fs');
+
+function randomSigned() {
+ return 2.0 * Math.random() - 1.0;
+}
+
+function toUnsigned(signed) {
+ return 0.5 * signed + 0.5;
+}
+
+const townInfo = [
+ {
+ name: "Old Town",
+ population: 452
+ },
+ {
+ name: "New Town",
+ population: 5234
+ },
+ {
+ name: "Newer Town",
+ population: 34245
+ }
+];
+
+/**
+ * Pretend this weather data is at the corner between
+ */
+function getTownId(point) {
+ if (point.z > Math.sin(point.x)) {
+ return 0;
+ } else if (point.x > point.z) {
+ return 1;
+ }
+
+ return 2;
+}
+
+/**
+ * Create a simple pressure gradient. The pressure is
+ * higher on the left and lower on the right
+ *
+ * pressure is measured in atmospheres (atm)
+ */
+function getAirPressure(point) {
+ const t = toUnsigned(point.x);
+ const highPressure = 1.5;
+ const lowPressure = 0.9;
+ return (1.0 - t) * highPressure + t * lowPressure;
+}
+
+/**
+ * create a made-up wind velocity field that is moving along the
+ * x axis, but spreading in +z, -z and +y directions
+ */
+function getWindVelocity(point) {
+ // Wind is moving from left to right
+ const vx = 1.0;
+
+ // Wind spreads out upwards
+ const vy = 0.5 * toUnsigned(point.y);
+
+ // Wind spreads out in both directions in z
+ const vz = point.z * toUnsigned(point.x);
+
+ // Wind is spreading out as it goes to the right
+ return {
+ x: vx,
+ y: vy,
+ z: vz
+ };
+}
+
+/**
+ * Make a temperature gradient warmest at the bottom and coldest at the top
+ * with a little bit of noise thrown in.
+ *
+ * returns values around the range [20, 25] degrees celsius
+ */
+function getTemperature(point) {
+ const t = toUnsigned(point.y);
+ const highTemperature = 25.0;
+ const lowTemperature = 20.0;
+ const baseTemperature = (1.0 - t) * highTemperature + t * lowTemperature;
+ const noise = 0.1 * Math.random();
+ return baseTemperature + noise;
+}
+
+function makePoint() {
+ const point = {
+ x: randomSigned(),
+ y: randomSigned(),
+ z: randomSigned()
+ };
+
+ point.townId = getTownId(point);
+ point.airPressure = getAirPressure(point);
+ point.windVelocity = getWindVelocity(point);
+ point.temperature = getTemperature(point);
+
+ return point;
+}
+
+function makePoints(numberOfPoints) {
+ const points = [];
+ for (let i = 0; i < numberOfPoints; i++) {
+ points.push(makePoint());
+ }
+ return points;
+}
+
+/**
+ * Make the geometry buffer. This creates a .bin file
+ * and returns metadata to insert into the gltf file
+ */
+function makeGeometryBuffer(fname, points) {
+ const numberOfPoints = points.length;
+ const min = {
+ x: 1e10,
+ y: 1e10,
+ z: 1e10
+ };
+ const max = {
+ x: -1e10,
+ y: -1e10,
+ z: -1e10
+ };
+
+ const positions = new Float32Array(3 * numberOfPoints);
+ for (let i = 0; i < numberOfPoints; i++) {
+ const point = points[i];
+ min.x = Math.min(min.x, point.x);
+ min.y = Math.min(min.y, point.y);
+ min.z = Math.min(min.z, point.z);
+
+ max.x = Math.max(max.x, point.x);
+ max.y = Math.max(max.y, point.y);
+ max.z = Math.max(max.z, point.z);
+
+ positions[3 * i] = point.x;
+ positions[3 * i + 1] = point.y;
+ positions[3 * i + 2] = point.z;
+ }
+
+ const buffer = Buffer.from(positions.buffer);
+ fs.writeFileSync(fname, buffer);
+
+ return {
+ numberOfPoints,
+ accessor: {
+ name: "Positions",
+ bufferView: 0,
+ byteOffset: 0,
+ componentType: 5126, //float
+ count: numberOfPoints,
+ max: [max.x, max.y, max.z],
+ min: [min.x, min.y, min.z],
+ type: "VEC3"
+ },
+ bufferView: {
+ name: "Positions",
+ buffer: 0,
+ byteLength: buffer.byteLength,
+ byteOffset: 0
+ },
+ buffer: {
+ name: "Geometry Buffer",
+ byteLength: buffer.byteLength,
+ uri: fname
+ }
+ }
+}
+
+
+function makePerVertexFeatures(points) {
+ const numberOfPoints = points.length;
+ const townId = new Float32Array(numberOfPoints);
+ const temperature = new Float32Array(numberOfPoints);
+ const airPressure = new Float32Array(numberOfPoints);
+ const windVelocity = new Float32Array(numberOfPoints * 3);
+ for (let i = 0; i < numberOfPoints; i++) {
+ const point = points[i];
+ townId[i] = point.townId;
+ temperature[i] = point.temperature;
+ airPressure[i] = point.airPressure;
+
+ windVelocity[3 * i] = point.windVelocity.x;
+ windVelocity[3 * i + 1] = point.windVelocity.y;
+ windVelocity[3 * i + 2] = point.windVelocity.z;
+ }
+
+ return [
+ {
+ name: "Town Feature ID",
+ bufferView: Buffer.from(townId.buffer)
+ },
+ {
+ name: "Air Temperature",
+ bufferView: Buffer.from(temperature.buffer)
+ },
+ {
+ name: "Air Pressure",
+ bufferView: Buffer.from(airPressure.buffer)
+ },
+ {
+ name: "Wind Velocity",
+ bufferView: Buffer.from(windVelocity.buffer)
+ },
+ ];
+}
+
+function makeTownFeatures() {
+ const numberOfFeatures = townInfo.length;
+ const offsets = new Uint32Array(numberOfFeatures + 1);
+ const population = new Uint16Array(numberOfFeatures);
+ let nameText = '';
+ let currentOffset = 0;
+ for (let i = 0; i < numberOfFeatures; i++) {
+ const town = townInfo[i];
+ const name = town.name;
+
+ population[i] = town.population;
+
+ offsets[i] = currentOffset;
+ currentOffset += Buffer.byteLength(name, 'utf8');
+
+ nameText += name;
+ }
+ offsets[numberOfFeatures] = currentOffset;
+
+ return [
+ {
+ name: "Town Name",
+ bufferView: Buffer.from(nameText, 'utf8')
+ },
+ {
+ name: "Town Name Offsets",
+ bufferView: Buffer.from(offsets.buffer)
+ },
+ {
+ name: "Town Population",
+ bufferView: Buffer.from(population.buffer)
+ }
+ ];
+}
+
+function makeMetadataBuffer(fname, points) {
+ const bufferViewInfos = makePerVertexFeatures(points)
+ .concat(makeTownFeatures());
+
+ const bufferViews = [];
+ const stats = [];
+ let offset = 0;
+ for (const bufferViewInfo of bufferViewInfos) {
+ const bufferView = bufferViewInfo.bufferView;
+ const byteLength = bufferView.byteLength;
+ stats.push({
+ name: bufferViewInfo.name,
+ buffer: 1,
+ byteLength: byteLength,
+ byteOffset: offset
+ });
+ offset += byteLength;
+
+ bufferViews.push(bufferView);
+ }
+
+ const buffer = Buffer.concat(bufferViews);
+ fs.writeFileSync(fname, buffer, 'utf8');
+
+ return {
+ bufferViewIndices: {
+ townIds: 1,
+ airTemperature: 2,
+ airPressure: 3,
+ windVelocity: 4,
+ townName: 5,
+ townNameOffsets: 6,
+ townPopulation: 7
+ },
+ townIdAccessor: {
+ name: "Town Feature ID",
+ bufferView: 1,
+ byteOffset: 0,
+ componentType: 5126, //float
+ count: points.length,
+ type: "SCALAR"
+ },
+ bufferViews: stats,
+ buffer: {
+ name: "Metadata Buffer",
+ byteLength: buffer.byteLength,
+ uri: fname
+ }
+ };
+}
+
+function main() {
+ const points = makePoints(1000);
+ const geometryInfo = makeGeometryBuffer('weather.bin', points);
+ const metadataInfo = makeMetadataBuffer('weather-metadata.bin', points);
+ const numberOfPoints = points.length;
+ const numberOfTowns = townInfo.length;
+ const gltf = {
+ asset: {
+ version: "2.0"
+ },
+ extensionsUsed: [
+ "EXT_feature_metadata"
+ ],
+ extensions: {
+ EXT_feature_metadata: {
+ extras: {
+ draftVersion: "0.0.0"
+ },
+ classes: {
+ weather: {
+ name: "Weather Data",
+ properties: {
+ airTemperature: {
+ name: "Air Temperature",
+ type: "FLOAT32"
+ },
+ airPressure: {
+ name: "Air Pressure",
+ type: "FLOAT32"
+ },
+ windVelocity: {
+ name: "Wind Velocity",
+ type: "ARRAY",
+ componentType: "FLOAT32",
+ componentCount: 3
+ },
+ semantic: {
+ name: "Semantic",
+ type: "STRING",
+ optional: true,
+ default: "WEATHER"
+ }
+ }
+ },
+ town: {
+ name: "Town Data",
+ properties: {
+ name: {
+ name: "Name",
+ type: "STRING"
+ },
+ population: {
+ name: "Population",
+ type: "FLOAT32"
+ },
+ semantic: {
+ name: "Semantic",
+ type: "STRING",
+ optional: true,
+ default: "TOWN"
+ }
+ }
+ }
+ },
+ featureTables: {
+ weatherTable: {
+ class: "weather",
+ count: numberOfPoints,
+ properties: {
+ airTemperature: {
+ bufferView: metadataInfo.bufferViewIndices.airTemperature
+ },
+ airPressure: {
+ bufferView: metadataInfo.bufferViewIndices.airPressure
+ },
+ windVelocity: {
+ bufferView: metadataInfo.bufferViewIndices.windVelocity
+ }
+ }
+ },
+ townTable: {
+ class: "town",
+ count: numberOfTowns,
+ properties: {
+ name: {
+ bufferView: metadataInfo.bufferViewIndices.townName,
+ offsetBufferViews: [metadataInfo.bufferViewIndices.townNameOffsets]
+ },
+ population: {
+ bufferView: metadataInfo.bufferViewIndices.townPopulation
+ }
+ }
+ }
+ }
+ }
+ },
+ scene: 0,
+ scenes: [
+ {
+ name: "Scene",
+ nodes: [0]
+ }
+ ],
+ nodes: [
+ {
+ name: "Point Cloud",
+ mesh: 0
+ }
+ ],
+ meshes: [
+ {
+ name: "Point Cloud Mesh",
+ primitives: [
+ {
+ attributes: {
+ POSITION: 0,
+ _FEATURE_ID_0: 1
+ },
+ mode: 0, //POINTS
+ extensions: {
+ EXT_feature_metadata: {
+ featureIdAttributes: [
+ {
+ featureTable: "weatherTable",
+ featureIds: {
+ constant: 0,
+ vertexStride: 1
+ }
+ },
+ {
+ featureTable: "townTable",
+ featureIds: {
+ attribute: "_FEATURE_ID_0"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ ],
+ accessors: [
+ geometryInfo.accessor,
+ metadataInfo.townIdAccessor
+ ],
+ bufferViews: [
+ geometryInfo.bufferView,
+ ...metadataInfo.bufferViews
+ ],
+ buffers : [
+ geometryInfo.buffer,
+ metadataInfo.buffer
+ ]
+ };
+ fs.writeFileSync('weather.gltf', JSON.stringify(gltf, undefined, 2));
+}
+
+main();
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather-metadata.bin b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather-metadata.bin
new file mode 100644
index 0000000000..0c7df7e6c0
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather-metadata.bin differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather.bin b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather.bin
new file mode 100644
index 0000000000..3c54db4903
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather.bin differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather.gltf b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather.gltf
new file mode 100644
index 0000000000..c43ee33143
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/examples/weather/weather.gltf
@@ -0,0 +1,231 @@
+{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensionsUsed": [
+ "EXT_feature_metadata"
+ ],
+ "extensions": {
+ "EXT_feature_metadata": {
+ "extras": {
+ "draftVersion": "0.0.0"
+ },
+ "classes": {
+ "weather": {
+ "name": "Weather Data",
+ "properties": {
+ "airTemperature": {
+ "name": "Air Temperature",
+ "type": "FLOAT32"
+ },
+ "airPressure": {
+ "name": "Air Pressure",
+ "type": "FLOAT32"
+ },
+ "windVelocity": {
+ "name": "Wind Velocity",
+ "type": "ARRAY",
+ "componentType": "FLOAT32",
+ "componentCount": 3
+ },
+ "semantic": {
+ "name": "Semantic",
+ "type": "STRING",
+ "optional": true,
+ "default": "WEATHER"
+ }
+ }
+ },
+ "town": {
+ "name": "Town Data",
+ "properties": {
+ "name": {
+ "name": "Name",
+ "type": "STRING"
+ },
+ "population": {
+ "name": "Population",
+ "type": "FLOAT32"
+ },
+ "semantic": {
+ "name": "Semantic",
+ "type": "STRING",
+ "optional": true,
+ "default": "TOWN"
+ }
+ }
+ }
+ },
+ "featureTables": {
+ "weatherTable": {
+ "class": "weather",
+ "count": 1000,
+ "properties": {
+ "airTemperature": {
+ "bufferView": 2
+ },
+ "airPressure": {
+ "bufferView": 3
+ },
+ "windVelocity": {
+ "bufferView": 4
+ }
+ }
+ },
+ "townTable": {
+ "class": "town",
+ "count": 3,
+ "properties": {
+ "name": {
+ "bufferView": 5,
+ "offsetBufferViews": [
+ 6
+ ]
+ },
+ "population": {
+ "bufferView": 7
+ }
+ }
+ }
+ }
+ }
+ },
+ "scene": 0,
+ "scenes": [
+ {
+ "name": "Scene",
+ "nodes": [
+ 0
+ ]
+ }
+ ],
+ "nodes": [
+ {
+ "name": "Point Cloud",
+ "mesh": 0
+ }
+ ],
+ "meshes": [
+ {
+ "name": "Point Cloud Mesh",
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0,
+ "_FEATURE_ID_0": 1
+ },
+ "mode": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdAttributes": [
+ {
+ "featureTable": "weatherTable",
+ "featureIds": {
+ "constant": 0,
+ "vertexStride": 1
+ }
+ },
+ {
+ "featureTable": "townTable",
+ "featureIds": {
+ "attribute": "_FEATURE_ID_0"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "accessors": [
+ {
+ "name": "Positions",
+ "bufferView": 0,
+ "byteOffset": 0,
+ "componentType": 5126,
+ "count": 1000,
+ "max": [
+ 0.9972160837586665,
+ 0.999361498913022,
+ 0.9994072872635433
+ ],
+ "min": [
+ -0.9947182964094199,
+ -0.9997488393612186,
+ -0.999161109123424
+ ],
+ "type": "VEC3"
+ },
+ {
+ "name": "Town Feature ID",
+ "bufferView": 1,
+ "byteOffset": 0,
+ "componentType": 5126,
+ "count": 1000,
+ "type": "SCALAR"
+ }
+ ],
+ "bufferViews": [
+ {
+ "name": "Positions",
+ "buffer": 0,
+ "byteLength": 12000,
+ "byteOffset": 0
+ },
+ {
+ "name": "Town Feature ID",
+ "buffer": 1,
+ "byteLength": 4000,
+ "byteOffset": 0
+ },
+ {
+ "name": "Air Temperature",
+ "buffer": 1,
+ "byteLength": 4000,
+ "byteOffset": 4000
+ },
+ {
+ "name": "Air Pressure",
+ "buffer": 1,
+ "byteLength": 4000,
+ "byteOffset": 8000
+ },
+ {
+ "name": "Wind Velocity",
+ "buffer": 1,
+ "byteLength": 12000,
+ "byteOffset": 12000
+ },
+ {
+ "name": "Town Name",
+ "buffer": 1,
+ "byteLength": 26,
+ "byteOffset": 24000
+ },
+ {
+ "name": "Town Name Offsets",
+ "buffer": 1,
+ "byteLength": 16,
+ "byteOffset": 24026
+ },
+ {
+ "name": "Town Population",
+ "buffer": 1,
+ "byteLength": 6,
+ "byteOffset": 24042
+ }
+ ],
+ "buffers": [
+ {
+ "name": "Geometry Buffer",
+ "byteLength": 12000,
+ "uri": "weather.bin"
+ },
+ {
+ "name": "Metadata Buffer",
+ "byteLength": 24048,
+ "uri": "weather-metadata.bin"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/architecture.jpg b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/architecture.jpg
new file mode 100644
index 0000000000..39b0e9f0ca
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/architecture.jpg differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/composite-example.png b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/composite-example.png
new file mode 100644
index 0000000000..9246436be1
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/composite-example.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-accessor.jpg b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-accessor.jpg
new file mode 100644
index 0000000000..22d9c64f02
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-accessor.jpg differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-mappings.jpg b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-mappings.jpg
new file mode 100644
index 0000000000..dbee9295a6
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-mappings.jpg differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-texture.jpg b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-texture.jpg
new file mode 100644
index 0000000000..17d15e3137
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-id-texture.jpg differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-table.png b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-table.png
new file mode 100644
index 0000000000..ffc3cefb7d
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-table.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-texture.jpg b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-texture.jpg
new file mode 100644
index 0000000000..b53f2be473
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/feature-texture.jpg differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/point-cloud-layers.png b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/point-cloud-layers.png
new file mode 100644
index 0000000000..c2c61c3ad3
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/figures/point-cloud-layers.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/class.property.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/class.property.schema.json
new file mode 100644
index 0000000000..b709d211a6
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/class.property.schema.json
@@ -0,0 +1,102 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Class Property",
+ "type": "object",
+ "description": "A class property.",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength" : 1,
+ "description": "The name of the property, e.g. for display purposes. The string is UTF-8 encoded."
+ },
+ "description": {
+ "type": "string",
+ "minLength" : 1,
+ "description": "The description of the property. The string is UTF-8 encoded."
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "BOOLEAN",
+ "INT8",
+ "UINT8",
+ "INT16",
+ "UINT16",
+ "INT32",
+ "UINT32",
+ "INT64",
+ "UINT64",
+ "FLOAT16",
+ "FLOAT32",
+ "FLOAT64",
+ "BLOB",
+ "STRING",
+ "ARRAY"
+ ],
+ "description": "The type of each element of the property array. A `type` is either a basic type used for `componentType` or an `ARRAY`. If `ARRAY` is used, then `componentType` must also be specified. `ARRAY` is a fixed-length array when `componentCount` is defined, and variable-length otherwise. `BLOB` is variable-length unless `blobByteLength` is defined. `STRING` is variable-length unless `stringByteLength` is defined. In these cases, the blobs/strings must be exactly that many bytes. Variable-length arrays, `STRING`, and `BLOB` also require a corresponding `offsetBufferView`. This is used to indicate where each element starts in the buffer. See further notes about offset buffers in the description for `componentType`."
+ },
+ "componentType": {
+ "enum": [
+ "BOOLEAN",
+ "INT8",
+ "UINT8",
+ "INT16",
+ "UINT16",
+ "INT32",
+ "UINT32",
+ "INT64",
+ "UINT64",
+ "FLOAT16",
+ "FLOAT32",
+ "FLOAT64",
+ "STRING",
+ "BLOB"
+ ],
+ "description": "When `type` is `ARRAY` this indicates the type of each component of the array. `componentType` supports simple values (boolean, integer and float types) as well as support for strings and binary blobs. `BOOLEAN` values are packed as 1-bit fields in a bit vector. Bit vectors are represented as a sequence of bytes. The first bit is always stored in the first byte of the sequence. However, with each byte, bits are numbered from right to left. This allows for straightforward access using bitwise operations. `STRING` and `BLOB` always require at least one offset buffer to specify where each element starts in the buffer. When the array is variable-length, an offset buffer must be used to determine the start indices for each element. Note that for variable-size arrays of `STRING` or `BLOB`, two offset buffers must be specified. One buffer determines where each string/blob begins, and the second buffer groups these start indices into variable-length arrays of strings/blobs."
+ },
+ "componentCount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The number of components per element for `ARRAY` elements."
+ },
+ "stringByteLength": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "This property can only be used with a `STRING` property or an `ARRAY` of `STRING`. When present, all string values for this property must be exactly this length in bytes when UTF-8 encoded. If the property is an `ARRAY`, then each component must have this byte length when encoded as UTF-8. Furthermore, no `offsetBufferView` is needed for specifying the start byte of each string. When `stringByteLength` is ommitted, strings are variable-length."
+ },
+ "blobByteLength": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "This property can only be used with a `BLOB` property or an `ARRAY` of `BLOB`. When present, all blob values for this property must be exactly this length in bytes. If the property is an `ARRAY`, then each component must have this byte length. Furthermore, no `offsetBufferView` is needed for specifying the start byte of each blob."
+ },
+ "normalized": {
+ "type": "boolean",
+ "description": "Specifies whether integer values are normalized. This applies both when `type` is an integer type, or when `type` is `ARRAY` with a `componentType` that is an integer. For unsigned integer types, values are normalized between [0, 1]. For signed integer types, values are normalized between [-1, 1]. For all other types, this property is ignored.",
+ "default": false
+ },
+ "max": {
+ "type": "array",
+ "description": "Maximum allowed values for property values. Only applicable for fixed-size numeric element types (integer, floats and `ARRAY`s thereof). Both `min` and `max` arrays have the same length. When `type` is `ARRAY` the length is determined by `componentCount`; otherwise the length is 1. `normalized` property has no effect on array values: they always correspond to the integer values.",
+ "items": {
+ "type": "number"
+ },
+ "minItems": 1
+ },
+ "min": {
+ "type": "array",
+ "description": "Minimum allowed values for property values. Only applicable for fixed-size numeric element types (integer, floats and `ARRAY`s thereof). Both `min` and `max` arrays have the same length. When `type` is `ARRAY` the length is determined by `componentCount`; otherwise the length is 1. `normalized` property has no effect on array values: they always correspond to the integer values.",
+ "items": {
+ "type": "number"
+ },
+ "minItems": 1
+ },
+ "extensions": { },
+ "extras": { }
+ },
+ "dependencies": {
+ "componentCount": ["componentType"],
+ "default": ["optional"]
+ },
+ "required": ["type"]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/class.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/class.schema.json
new file mode 100644
index 0000000000..21bb785fd4
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/class.schema.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Class",
+ "type": "object",
+ "description": "A class defining the properties that distinguish one set of features from another.",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the class, e.g. for display purposes. The string is UTF-8 encoded."
+ },
+ "description": {
+ "type": "string",
+ "minLength" : 1,
+ "description": "The description of the class. The string is UTF-8 encoded."
+ },
+ "properties": {
+ "type": "object",
+ "description": "A dictionary, where each key is a property ID and each value is an object defining the property.",
+ "minProperties": 1,
+ "additionalProperties" : {
+ "$ref": "class.property.schema.json"
+ }
+ },
+ "extensions": { },
+ "extras": { }
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTable.property.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTable.property.schema.json
new file mode 100644
index 0000000000..991b90c5f6
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTable.property.schema.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Property array",
+ "type": "object",
+ "description": "Array containing property data",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "bufferView": {
+ "allOf": [ { "$ref": "glTFid.schema.json" } ],
+ "description": "The index of the `bufferView` containing property array data. The bufferView must be aligned to a multiple of 8 bytes. For a GLB file, this is measured relative to the beginning of the file. For a glTF + BIN file, this is relative to the beginning of the BIN file."
+ },
+ "offsetComponentType": {
+ "type": "string",
+ "default": "UINT32",
+ "enum": ["UINT8", "UINT16", "UINT32", "UINT64"],
+ "description": "The size of each offset in the `offsetBufferViews`. If multiple buffer views are used, they all must use the same offset component type"
+ },
+ "offsetBufferViews": {
+ "type": "array",
+ "items": {
+ "$ref": "glTFid.schema.json"
+ },
+ "minItems": 1,
+ "maxItems": 2,
+ "description": "One or two indices specifying `bufferViews` containing offsets for variable-size quantities. Required for variable-length `ARRAY` types. This is also required for a `BLOB` with an undefined `blobByteLength` or a `STRING` with an undefined `stringByteLength`. The first offset buffer contain `elementCount + 1` unsigned integers. The length of the second offset buffer is the combined lengths of all the arrays plus one. The byte length of these offsets is determined by `offsetComponentType`. The offsets represent the start position of each element in the buffer, with the last offset representing the position after the last element. The byte length of strings and binary blobs and the array length of element arrays is computed using the difference between the current offset and the subsequent offset. Each buffer view must be aligned to a multiple of 8 bytes in the same manner as the main `bufferView`. If a variable-length `ARRAY` of `STRING` or of `BLOB` is used, two offset buffer views must be used. In this case, the first offset buffer view indexes into the second offset buffer view to determine how to group the strings/blobs into variable-size arrays, one array per element. The second offset buffer indexes into the main `bufferView` and determines the start/end bytes of each string/blob."
+ },
+ "extensions": { },
+ "extras": { }
+ },
+ "required": ["bufferView"]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTable.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTable.schema.json
new file mode 100644
index 0000000000..28b762335d
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTable.schema.json
@@ -0,0 +1,45 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature table",
+ "type": "object",
+ "description": "A feature table designated by a class and property values stored in arrays.",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength" : 1,
+ "description": "The name of the feature table, e.g. for display purposes. The string is UTF-8 encoded."
+ },
+ "description": {
+ "type": "string",
+ "minLength" : 1,
+ "description": "The description of the feature table. The string is UTF-8 encoded."
+ },
+ "class": {
+ "type": "string",
+ "minLength" : 1,
+ "description": "The ID of the class."
+ },
+ "count": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The number of features in the table. This is the number of unique feature IDs in the model that reference this feature table, not to be confused with `elementCount`."
+ },
+ "elementCount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The number of elements in each property array. `elementCount` may be greater than `featureCount` if the array contains extra elements not used by this model, e.g. if different models refer to different sections of a global array or if the arrays represents a larger data dictionary. if `elementCount` is not specified, it is assumed to be equal to `featureCount`"
+ },
+ "properties": {
+ "type": "object",
+ "description": "A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value describes where property array data is stored.",
+ "minProperties": 1,
+ "additionalProperties" : {
+ "$ref": "featureTable.property.schema.json"
+ }
+ },
+ "extensions": { },
+ "extras": { }
+ },
+ "required": [ "class", "count" ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTexture.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTexture.schema.json
new file mode 100644
index 0000000000..e9d100d4dd
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/featureTexture.schema.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature Texture",
+ "type": "object",
+ "description": "Features whose properties are stored directly in the texture designated by a class and property values stored in texture channels. This not to be confused with feature ID textures which store feature IDs for use with a feature table.",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "class": {
+ "type": "string",
+ "description": "The ID of the class."
+ },
+ "properties": {
+ "type": "object",
+ "description": "A dictionary, where each key corresponds to a property ID in the class's `properties` dictionary and each value describes the texture channels containing property data.",
+ "minProperties": 1,
+ "additionalProperties" : {
+ "$ref": "textureAccessor.schema.json"
+ }
+ },
+ "extensions": { },
+ "extras": { }
+ },
+ "required": [ "class", "properties" ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/gltf.EXT_feature_metadata.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/gltf.EXT_feature_metadata.schema.json
new file mode 100644
index 0000000000..d95738accf
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/gltf.EXT_feature_metadata.schema.json
@@ -0,0 +1,35 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "EXT_feature_metadata glTF extension",
+ "type": "object",
+ "description": "glTF extension for applying semantic information to a model through per-vertex and per-texel feature assignment and storage of feature properties.",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "classes": {
+ "type": "object",
+ "description": "A dictionary, where each key is a class ID and each value is an object defining the class.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "class.schema.json"
+ }
+ },
+ "featureTables": {
+ "type": "object",
+ "description": "A dictionary, where each key is a feature table ID and each value is an object defining the feature table",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "featureTable.schema.json"
+ }
+ },
+ "featureTextures": {
+ "type": "object",
+ "description": "A dictionary, where each key is an ID for a feature texture and each value is an object defining a feature texture",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "featureTexture.schema.json"
+ }
+ },
+ "extensions": { },
+ "extras": { }
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdAttributes.featureIds.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdAttributes.featureIds.schema.json
new file mode 100644
index 0000000000..6e43ccddd0
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdAttributes.featureIds.schema.json
@@ -0,0 +1,33 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Per-vertex feature IDs",
+ "type": "object",
+ "description": "An object describing per-vertex feature IDs to be used as indices to property arrays in the feature table.",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "attribute": {
+ "type": "string",
+ "pattern": "^_FEATURE_ID_([1-9]\\d*|0)$",
+ "description": "The name of the attribute containing feature IDs. Feature IDs must be whole numbers in the range `[0, elementCount - 1]`, where `elementCount` is the total number of elements in each property array."
+ },
+ "constant": {
+ "type": "integer",
+ "minimum": 0,
+ "default": 0,
+ "description": "Sets a default feature ID for each vertex when the attribute property is not supplied."
+ },
+ "vertexStride": {
+ "type": "integer",
+ "minimum": 0,
+ "default": 0,
+ "description": "Specifies a per-vertex stride to apply to feature IDs. Set `vertexStride` to 1 and `constant` to 0 to treat each vertex (point) as a separate feature starting at feature ID 0."
+ },
+ "extensions": { },
+ "extras": { }
+ },
+ "not": {
+ "anyOf": [
+ {"required": ["attribute", "constant"]}
+ ]
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdAttributes.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdAttributes.schema.json
new file mode 100644
index 0000000000..dc00a59e77
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdAttributes.schema.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Primitive feature mapping",
+ "type": "object",
+ "description": "An object mapping per-vertex feature IDs to property arrays in a feature table.",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "featureTable": {
+ "type": "string",
+ "description": "The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary."
+ },
+ "featureIds": {
+ "allOf": [ { "$ref": "primitive.EXT_feature_metadata.featureIdAttributes.featureIds.schema.json" } ],
+ "description": "An object describing per-vertex feature IDs to be used as indices to property arrays in the feature table."
+ },
+ "extensions": { },
+ "extras": { }
+ },
+ "required": [ "featureTable", "featureIds" ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdTexture.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdTexture.schema.json
new file mode 100644
index 0000000000..f8d3067a10
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.featureIdTexture.schema.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature ID Texture",
+ "type": "object",
+ "description": "An object describing a texture used for storing per-texel feature IDs",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "featureTable": {
+ "type": "string",
+ "description": "The ID of the feature table to use"
+ },
+ "featureIds": {
+ "allOf": [ { "$ref": "textureAccessor.schema.json" }],
+ "description": "A description of the texture and channel to use for feature IDs. The `channels` property must have a single channel. Furthermore, feature IDs must be whole numbers in the range `[0, elementCount - 1]`, where `elementCount` is the total number of elements in each property array. Texel values must not be normalized. Texture filtering should be disabled when fetching feature IDs."
+ },
+ "extensions": { },
+ "extras": { }
+ },
+ "required": [ "featureTable", "featureIds" ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.schema.json
new file mode 100644
index 0000000000..0700b23895
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/primitive.EXT_feature_metadata.schema.json
@@ -0,0 +1,40 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "EXT_feature_metadata glTF Primitive extension",
+ "type": "object",
+ "description": "glTF extension for an individual primitive in a glTF model, to associate it with the model's root `EXT_feature_metadata` object.",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "featureIdAttributes": {
+ "type": "array",
+ "description": "An array of objects mapping per-vertex feature IDs to property arrays in a feature table.",
+ "items": {
+ "$ref": "primitive.EXT_feature_metadata.featureIdAttributes.schema.json"
+ },
+ "minItems": 1
+ },
+ "featureIdTextures": {
+ "type": "array",
+ "description": "An array of objects mapping per-texel feature IDs to a feature table",
+ "items": {
+ "$ref": "primitive.EXT_feature_metadata.featureIdTexture.schema.json"
+ },
+ "minItems": 1
+ },
+ "featureTextures": {
+ "type": "array",
+ "description": "An array of IDs of feature textures from the root `EXT_feature_metadata`",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1
+ },
+ "extensions": { },
+ "extras": { }
+ },
+ "anyOf": [
+ {"required": ["featureIdAttributes"]},
+ {"required": ["featureIdTextures"]},
+ {"required": ["featureTextures"]}
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/textureAccessor.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/textureAccessor.schema.json
new file mode 100644
index 0000000000..519081df49
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/0.0.0/schema/textureAccessor.schema.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Texture Accessor",
+ "description": "A description of how to access data values from the color channels of a texture",
+ "type": "object",
+ "allOf": [ { "$ref": "glTFProperty.schema.json" } ],
+ "properties": {
+ "channels": {
+ "type": "string",
+ "pattern": "^[rgba]{1,4}$",
+ "description": "Texture channels containing property values according to the property `componentType`. Channels are labeled by `rgba` and are swizzled with a string of 1-4 characters."
+ },
+ "texture": {
+ "allOf": [{"$ref": "textureInfo.schema.json"}],
+ "description": "The glTF texture and texture coordinates to use"
+ },
+ "extensions": { },
+ "extras": { }
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/README.md b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/README.md
new file mode 100644
index 0000000000..47c42286b6
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/README.md
@@ -0,0 +1,1665 @@
+
+# EXT_feature_metadata
+
+**Version 1.0.0**, February 24, 2020
+
+
+## Contributors
+
+* Peter Gagliardi, Cesium
+* Sean Lilley, Cesium
+* Sam Suhag, Cesium
+* Patrick Cozzi, Cesium
+* Bao Tran, Cesium
+* Samuel Vargas, Cesium
+
+
+## Status
+
+Draft
+
+
+## Dependencies
+
+Written against the glTF 2.0 specification.
+
+Adds new functionality to the [`EXT_mesh_gpu_instancing` extension](../../EXT_mesh_gpu_instancing).
+
+
+## Optional vs. Required
+
+This extension is optional, meaning it should be placed in the `extensionsUsed` list, but not in the `extensionsRequired` list.
+
+
+## Contents
+
+- [Overview](#overview)
+- [Feature Identification](#feature-identification)
+ - [Feature ID Vertex Attributes](#feature-id-vertex-attributes)
+ - [Feature ID Accessors](#feature-id-accessors)
+ - [Implicit Feature IDs](#implicit-feature-ids)
+ - [Feature ID Textures](#feature-id-textures)
+ - [Feature ID Instance Attributes](#feature-id-instance-attributes)
+- [Feature Metadata](#feature-metadata)
+ - [Schemas](#schemas)
+ - [Feature Tables](#feature-tables)
+ - [Feature Textures](#feature-textures)
+ - [Statistics](#statistics)
+- [Examples](#examples)
+- [JSON Schema Reference](#json-schema-reference)
+
+## Overview
+
+A **feature** is an entity that has both geometry and metadata. In Geographic Information Systems (GIS) a feature is an entity such as a point, polyline, or polygon that represents some element on a map. In another domain like CAD/BIM a feature might be a component of a design model. A feature could also be a 3D building in a city, a tree in a forest, a sample point in a weather model, or a patch of texels on a 3D model.
+
+This extension allows batching features for efficient streaming to a client for rendering and interaction. Efficiency comes from transferring multiple features in the same glTF and rendering them in the least number of draw calls necessary.
+
+Feature IDs enable individual features to be identified and updated at runtime. For example, a selected feature could be shown/hidden, or highlighted a different color. Feature IDs may be assigned on a per-vertex, per-texel, or per-instance basis.
+
+Feature IDs may be used to access metadata, such as passing a building's ID to get its address. Feature metadata is stored in a compact binary tabular format described in the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0).
+
+
+
+In the image above, a glTF consists of two houses batched together into a single primitive. A feature ID attribute on the primitive indicates that all of the vertices making up the first house have a feature ID of 0, while all vertices making up the second house have the feature ID 1. The feature ID is then used to access the building's metadata from the feature table.
+
+Feature metadata may also be stored directly in textures. This is especially useful when texture mapping high frequency data, such as material properties, to less detailed 3D surfaces. Feature textures enable new styling and analytics capabilities, and complement glTF PBR textures.
+
+See [Examples](#examples) for a full list of use cases for this extension.
+
+## Feature Identification
+
+Features in a glTF primitive are identified in three ways:
+
+* Per-vertex using a vertex attribute
+* Per-texel using a glTF texture
+* Per-instance using an instance attribute with the [`EXT_mesh_gpu_instancing` extension](../../EXT_mesh_gpu_instancing)
+
+
+
+### Feature ID Vertex Attributes
+
+#### Feature ID Accessors
+
+The most straightforward method for defining feature IDs is to store them in a glTF vertex attribute. Feature ID attributes must follow the naming convention `_FEATURE_ID_X` where `X` is a non-negative integer. The first feature ID attribute is `_FEATURE_ID_0`, the second `_FEATURE_ID_1`, and so on.
+
+Feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features in the feature table.
+
+The attribute's accessor `type` must be `"SCALAR"` and `normalized` must be false. There is no restriction on `componentType`.
+
+> **Implementation Note:** since glTF accessors do not support `UNSIGNED_INT` types for 32-bit integers, `FLOAT` may be used instead. This allows for integer feature IDs up to 2²⁴. For smaller ranges of feature IDs, `UNSIGNED_BYTE` or `UNSIGNED_SHORT` can still be used. Note that this requires aligning each feature ID to 4-byte boundaries to adhere to glTF's alignment rules.
+
+
+
+```jsonc
+{
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0,
+ "_FEATURE_ID_0": 1
+ },
+ "indices": 2,
+ "mode": 4,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdAttributes": [
+ {
+ "featureTable": "buildings",
+ "featureIds": {
+ "attribute": "_FEATURE_ID_0"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+}
+```
+
+#### Implicit Feature IDs
+
+In some cases it is possible to define feature IDs implicitly, such as when all vertices in a primitive have the same feature ID or when each vertex in a primitive has a different feature ID.
+
+This is accomplished by using `constant` and `divisor`.
+
+* `constant` sets a constant feature ID for each vertex. The default is `0`.
+* `divisor` sets the rate at which feature IDs increment. If `divisor` is zero then `constant` is used. If `divisor` is greater than zero the feature ID increments once per `divisor` sets of vertices, starting at `constant`. The default is `0`.
+
+For example
+
+* If `constant` is 0 and `divisor` is 0, the feature IDs are `[0, 0, 0, ...]`
+* If `constant` is 0 and `divisor` is 1, the feature IDs are `[0, 1, 2, ...]`
+* If `constant` is 0 and `divisor` is 2, the feature IDs are `[0, 0, 1, 1, 2, 2, ...]`
+* If `constant` is 2 and `divisor` is 3, the feature IDs are `[2, 2, 2, 3, 3, 3, 4, 4, 4, ...]`.
+
+`constant` and `divisor` must be omitted when `attribute` is used. These two methods of assigning feature IDs are mutually exclusive.
+
+
+
+```jsonc
+{
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0
+ },
+ "mode": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdAttributes": [
+ {
+ "featureTable": "placemarks",
+ "featureIds": {
+ "constant": 0,
+ "divisor": 1
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+}
+```
+### Feature ID Textures
+
+Feature ID textures classify the pixels of an image into different features. Some examples include image segmentation or marking regions on a map.
+
+Often per-texel feature IDs provide finer granularity than per-vertex feature IDs as in the example below.
+
+
+
+```jsonc
+{
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0,
+ "TEXCOORD_0": 1
+ },
+ "indices": 2,
+ "material": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdTextures": [
+ {
+ "featureTable": "buildingFeatures",
+ "featureIds": {
+ "texture": {
+ "texCoord": 0,
+ "index": 0
+ },
+ "channels": "r"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+}
+```
+
+`texture` is a glTF [`textureInfo`](../../../../../specification/2.0/schema/textureInfo.schema.json) object. `channels` must be a single channel (`"r"`, `"g"`, `"b"`, or `"a"`). Furthermore, feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features in the feature table. Texture filtering should be disabled when accessing feature IDs.
+
+### Feature ID Instance Attributes
+
+Feature IDs may also be assigned to individual instances when using the [`EXT_mesh_gpu_instancing` extension](../../EXT_mesh_gpu_instancing). This works the same way as assigning feature IDs to vertices. Feature IDs may be stored in accessors or generated implicitly.
+
+```jsonc
+{
+ "nodes": [
+ {
+ "mesh": 0,
+ "extensions": {
+ "EXT_mesh_gpu_instancing": {
+ "attributes": {
+ "TRANSLATION": 0,
+ "ROTATION": 1,
+ "SCALE": 2,
+ "_FEATURE_ID_0": 3
+ },
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureIdAttributes": [
+ {
+ "featureTable": "trees",
+ "featureIds": {
+ "attribute": "_FEATURE_ID_0"
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+## Feature Metadata
+
+Feature metadata is structured according to a **schema**. A schema has a set of **classes** and **enums**. A class contains a set of **properties**, which may be numeric, boolean, string, enum, or array types.
+
+A **feature** is a specific instantiation of class containing **property values**. Property values are stored in either a **feature table** or a **feature texture** depending on the use case. Both formats are designed for storing property values for a large number of features.
+
+**Statistics** provide aggregate information about the metadata. For example, statistics may include the min/max values of a numeric property for mapping property values to color ramps or the number of enum occurrences for creating histograms.
+
+By default properties do not have any inherent meaning. A property may be assigned a **semantic**, an identifier that describes how the property should be interpreted. The full list of built-in semantics can be found in the [Cesium Metadata Semantic Reference](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/Semantics). Model authors may define their own application- or domain-specific semantics separately.
+
+This extension implements the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0), which describes the metadata format in full detail.
+
+### Schemas
+
+A schema defines a set of classes and enums used in a model. Classes serve as templates for features - they provide a list of properties and the type information for those properties. Enums define the allowable values for enum properties. Schemas are defined in full detail in the [Schemas](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0#schemas) section of the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0).
+
+A schema may be embedded in the extension directly or referenced externally with the `schemaUri` property. Multiple glTF models may refer to the same external schema to avoid duplication.
+
+Schemas may be given a `name`, `description`, and `version`.
+
+### Feature Tables
+
+A feature table stores property values in a parallel array format. Each property array corresponds to a class property. The values contained within a property array must match the data type of the class property. Furthermore, the set of property arrays must match one-to-one with the class properties. There is one exception - if a property is optional the feature table may omit that property.
+
+The schema and feature tables are defined in the root extension object in the glTF model. See the example below:
+
+```jsonc
+{
+ "extensions": {
+ "EXT_feature_metadata": {
+ "schema": {
+ "classes": {
+ "tree": {
+ "properties": {
+ "height": {
+ "description": "Height of tree measured from ground level",
+ "type": "FLOAT32"
+ },
+ "birdCount": {
+ "description": "Number of birds perching on the tree",
+ "type": "UINT8"
+ },
+ "species": {
+ "description": "Species of the tree",
+ "type": "STRING"
+ }
+ }
+ }
+ }
+ },
+ "featureTables": {
+ "trees": {
+ "class": "tree",
+ "count": 10,
+ "properties": {
+ "height": {
+ "bufferView": 0
+ },
+ "birdCount": {
+ "bufferView": 1
+ },
+ "species": {
+ "bufferView": 2,
+ "stringOffsetBufferView": 3
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+`class` is the ID of the class in the schema. `count` is the number of features in the feature table, as well as the length of each property array. Property arrays are stored in glTF buffer views and use the binary encoding defined in the [Table Format](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0#table-format) section of the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0).
+
+Each buffer view `byteOffset` must be aligned to a multiple of 8 bytes. If the buffer view's buffer is the GLB-stored `BIN` chunk the byte offset is measured relative to the beginning of the GLB. Otherwise it is measured relative to the beginning of the buffer.
+
+### Feature Textures
+
+Feature textures (not to be confused with [Feature ID Textures](#feature-id-texture)) use textures rather than parallel arrays to store values. Feature textures are accessed directly by texture coordinates, rather than feature IDs. Feature textures are especially useful when texture mapping high frequency data to less detailed 3D surfaces.
+
+Feature textures use the [Raster Format](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0#raster-format) of the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0) with a few additional constraints:
+
+* A scalar property cannot be encoded into multiple channels. For example, it is not possible to encode a `UINT32` property in an `RGBA8` texture.
+* Components of fixed-length array properties must be separate channels within the same texture.
+* Variable-length arrays are not supported.
+
+Additionally, the data type and bit depth of the image must be compatible with the property type. An 8-bit per pixel RGB image is only compatible with `UINT8` or normalized `UINT8` properties, and array properties thereof with three components or less. Likewise, a floating point property requires a floating point-compatible image format like KTX2 which may require additional extensions.
+
+Feature textures are defined with the following steps:
+
+1. A class is defined in the root `EXT_feature_metadata` extension object. This is used to describe the metadata in the texture.
+2. A feature texture is defined in the root `EXT_feature_metadata.featureTextures` object. This must reference the class ID defined in step 1.
+3. A feature texture is associated with a primitive by listing the feature texture ID in the `primitive.EXT_feature_metadata.featureTextures` array.
+
+
+_Class and feature texture_
+
+```jsonc
+{
+ "extensions": {
+ "EXT_feature_metadata": {
+ "schema": {
+ "classes": {
+ "heatSample": {
+ "properties": {
+ "heatSample": {
+ "type": "UINT8",
+ "normalized": true
+ }
+ }
+ }
+ }
+ },
+ "featureTextures": {
+ "heatLossTexture": {
+ "class": "heatSample",
+ "properties": {
+ "heatLoss": {
+ "texture": {
+ "index": 0,
+ "texCoord": 0
+ },
+ "channels": "r"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+_Primitive_
+
+```jsonc
+{
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": 0,
+ "TEXCOORD_0": 1
+ },
+ "indices": 2,
+ "material": 0,
+ "extensions": {
+ "EXT_feature_metadata": {
+ "featureTextures": ["heatLossTexture"]
+ }
+ }
+ }
+ ]
+}
+```
+
+
+`texture` is a glTF [`textureInfo`](../../../../../specification/2.0/schema/textureInfo.schema.json) object. `texCoord` refers to the texture coordinate set of the referring primitive. `channels` is a string matching the pattern `"^[rgba]{1,4}$"` that specifies which texture channels store property values.
+
+### Statistics
+
+Statistics provide aggregate information about features in the model. Statistics are provided on a per-class basis.
+
+* `count` is the number of features that conform to the class
+* `properties` contains statistics about property values
+
+Properties have the following built-in statistics:
+
+Name|Description|Type
+--|--|--
+`min`|The minimum property value|Numeric types or fixed-length arrays of numeric types
+`max`|The maximum property value|...
+`mean`|The arithmetic mean of the property values|...
+`median`|The median of the property values|...
+`standardDeviation`|The standard deviation of the property values|...
+`variance`|The variance of the property values|...
+`sum`|The sum of the property values|...
+`occurrences`|Number of enum occurrences|Enums or fixed-length arrays of enums
+
+Model authors may define their own additional statistics, like `mode` below.
+
+```jsonc
+{
+ "extensions": {
+ "3DTILES_metadata": {
+ "schema": {
+ "classes": {
+ "building": {
+ "properties": {
+ "height": {
+ "type": "FLOAT32"
+ },
+ "owners": {
+ "type": "ARRAY",
+ "componentType": "STRING"
+ },
+ "buildingType": {
+ "type": "ENUM",
+ "enumType": "buildingType"
+ }
+ }
+ }
+ },
+ "enums": {
+ "buildingType": {
+ "valueType": "UINT8",
+ "values": [
+ {
+ "name": "Residential",
+ "value": 0
+ },
+ {
+ "name": "Commercial",
+ "value": 1
+ },
+ {
+ "name": "Hospital",
+ "value": 2
+ },
+ {
+ "name": "Other",
+ "value": 3
+ }
+ ]
+ }
+ }
+ },
+ "statistics": {
+ "classes": {
+ "building": {
+ "count": 100,
+ "properties": {
+ "height": {
+ "min": 3.9,
+ "max": 341.7,
+ "mode": 5.0
+ },
+ "buildingType": {
+ "occurrences": {
+ "Residential": 70,
+ "Commercial": 28,
+ "Hospital": 2
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+## Examples
+
+_This section is non-normative_
+
+The examples below shows the breadth of possible use cases for this extension.
+
+Example|Description|Image
+--|--|--
+Triangle mesh|Feature IDs are assigned to each vertex to distinguish components of a building.|
+Per-vertex metadata
|An implicit feature ID is assigned to each vertex. The feature table stores `FLOAT64` accuracy values. |
+Per-triangle metadata|An implicit feature ID is assigned to each set of three vertices. The feature table stores `FLOAT64` area values.|
+Per-point metadata|An implicit feature ID is assigned to each point. The feature table stores `FLOAT64` , `STRING`, and `ENUM` properties, which are not possible through glTF vertex attribute accessors alone.|
+Per-node metadata|Vertices in node 0 and node 1 are assigned different `constant` feature IDs. Because the door has articulations these two nodes can't be batched together.|
+Multi-point features|A point cloud with two feature tables, one storing metadata for groups of points and the other storing metadata for individual points.|
+Multi-instance features|Instanced tree models where trees are assigned to groups with a per-instance feature ID attribute. One feature table stores per-group metadata and the other stores per-tree metadata.|
+Material classification|A textured mesh using a feature texture to store both material enums and normalized `UINT8` thermal temperatures.|
+Composite|A glTF containing a 3D mesh (house), a point cloud (tree), and instanced models (fencing) with three feature tables.|
+
+## JSON Schema Reference
+
+* [glTF extension](#gltf-extension)
+* [Primitive extension](#primitive-extension)
+* [`EXT_mesh_gpu_instancing` extension](#ext_mesh_gpu_instancing-extension)
+
+
+## glTF extension
+
+* [`glTF extension`](#reference-gltf-extension) (root object)
+ * [`Schema`](#reference-schema)
+ * [`Class`](#reference-class)
+ * [`Class Property`](#reference-class-property)
+ * [`Enum`](#reference-enum)
+ * [`Enum Value`](#reference-enum-value)
+ * [`Feature Table`](#reference-featuretable)
+ * [`Feature Table Property`](#reference-featuretable-property)
+ * [`Feature Texture`](#reference-featuretexture)
+ * [`Texture Accessor`](#reference-textureaccessor)
+ * [`Statistics`](#reference-statistics)
+ * [`Class Statistics`](#reference-statistics-class)
+ * [`Property Statistics`](#reference-statistics-class-property)
+
+---------------------------------------
+
+
+### glTF extension
+
+glTF extension that assigns metadata to features in a model.
+
+**`glTF extension` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**schema**|`schema`|An object defining classes and enums.|No|
+|**schemaUri**|`string`|A uri to an external schema file.|No|
+|**statistics**|`statistics`|An object containing statistics about features.|No|
+|**featureTables**|`object`|A dictionary, where each key is a feature table ID and each value is an object defining the feature table.|No|
+|**featureTextures**|`object`|A dictionary, where each key is a feature texture ID and each value is an object defining the feature texture.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### glTF extension.schema
+
+An object defining classes and enums.
+
+* **Type**: `schema`
+* **Required**: No
+
+
+#### glTF extension.schemaUri
+
+A uri to an external schema file.
+
+* **Type**: `string`
+* **Required**: No
+* **Format**: uriref
+
+
+#### glTF extension.statistics
+
+An object containing statistics about features.
+
+* **Type**: `statistics`
+* **Required**: No
+
+
+#### glTF extension.featureTables
+
+A dictionary, where each key is a feature table ID and each value is an object defining the feature table.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `featureTable`
+
+
+#### glTF extension.featureTextures
+
+A dictionary, where each key is a feature texture ID and each value is an object defining the feature texture.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `featureTexture`
+
+
+#### glTF extension.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### glTF extension.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Schema
+
+An object defining classes and enums.
+
+**`Schema` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**name**|`string`|The name of the schema.|No|
+|**description**|`string`|The description of the schema.|No|
+|**version**|`string`|Application-specific version of the schema.|No|
+|**classes**|`object`|A dictionary, where each key is a class ID and each value is an object defining the class.|No|
+|**enums**|`object`|A dictionary, where each key is an enum ID and each value is an object defining the values for the enum.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### schema.name
+
+The name of the schema.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### schema.description
+
+The description of the schema.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### schema.version
+
+Application-specific version of the schema.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### schema.classes
+
+A dictionary, where each key is a class ID and each value is an object defining the class.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `class`
+
+
+#### schema.enums
+
+A dictionary, where each key is an enum ID and each value is an object defining the values for the enum.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `enum`
+
+
+#### schema.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### schema.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Class
+
+A class containing a set of properties.
+
+**`Class` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**name**|`string`|The name of the class, e.g. for display purposes.|No|
+|**description**|`string`|The description of the class.|No|
+|**properties**|`object`|A dictionary, where each key is a property ID and each value is an object defining the property.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### class.name
+
+The name of the class, e.g. for display purposes.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### class.description
+
+The description of the class.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### class.properties
+
+A dictionary, where each key is a property ID and each value is an object defining the property.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `class.property`
+
+
+#### class.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### class.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Class property
+
+A class property.
+
+**`Class property` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**name**|`string`|The name of the property, e.g. for display purposes.|No|
+|**description**|`string`|The description of the property.|No|
+|**type**|`string`|The property type. If `ENUM` is used, then `enumType` must also be specified. If `ARRAY` is used, then `componentType` must also be specified. `ARRAY` is a fixed-length array when `componentCount` is defined, and variable-length otherwise.| ✓ Yes|
+|**enumType**|`string`|An enum ID as declared in the `enums` dictionary. This value must be specified when `type` or `componentType` is `ENUM`.|No|
+|**componentType**|`any`|When `type` is `ARRAY` this indicates the type of each component of the array. If `ENUM` is used, then `enumType` must also be specified.|No|
+|**componentCount**|`integer`|The number of components per element for `ARRAY` elements.|No|
+|**normalized**|`boolean`|Specifies whether integer values are normalized. This applies both when `type` is an integer type, or when `type` is `ARRAY` with a `componentType` that is an integer type. For unsigned integer types, values are normalized between `[0.0, 1.0]`. For signed integer types, values are normalized between `[-1.0, 1.0]`. For all other types, this property is ignored.|No, default: `false`|
+|**max**|`number,array`|Maximum allowed values for property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values: they always correspond to the integer values.|No|
+|**min**|`number,array`|Minimum allowed values for property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values: they always correspond to the integer values.|No|
+|**default**|`boolean,number,string,array`|A default value to use when the property value is not defined. If used, `optional` must be set to true. The type of the default value must match the property definition: For `BOOLEAN` use `true` or `false`. For `STRING` use a JSON string. For a numeric type use a JSON number. For `ENUM` use the enum `name`, not the integer value. For `ARRAY` use a JSON array containing values matching the `componentType`.|No|
+|**optional**|`boolean`|If true, this property is optional.|No, default: `false`|
+|**semantic**|`string`|An identifier that describes how this property should be interpreted. The semantic cannot be used by other properties in the class.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### class.property.name
+
+The name of the property, e.g. for display purposes.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### class.property.description
+
+The description of the property.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### class.property.type
+
+The property type. If `ENUM` is used, then `enumType` must also be specified. If `ARRAY` is used, then `componentType` must also be specified. `ARRAY` is a fixed-length array when `componentCount` is defined, and variable-length otherwise.
+
+* **Type**: `string`
+* **Required**: ✓ Yes
+* **Allowed values**:
+ * `"INT8"`
+ * `"UINT8"`
+ * `"INT16"`
+ * `"UINT16"`
+ * `"INT32"`
+ * `"UINT32"`
+ * `"INT64"`
+ * `"UINT64"`
+ * `"FLOAT32"`
+ * `"FLOAT64"`
+ * `"BOOLEAN"`
+ * `"STRING"`
+ * `"ENUM"`
+ * `"ARRAY"`
+
+
+#### class.property.enumType
+
+An enum ID as declared in the `enums` dictionary. This value must be specified when `type` or `componentType` is `ENUM`.
+
+* **Type**: `string`
+* **Required**: No
+
+
+#### class.property.componentType
+
+When `type` is `ARRAY` this indicates the type of each component of the array. If `ENUM` is used, then `enumType` must also be specified.
+
+* **Type**: `any`
+* **Required**: No
+* **Allowed values**:
+ * `INT8`
+ * `UINT8`
+ * `INT16`
+ * `UINT16`
+ * `INT32`
+ * `UINT32`
+ * `INT64`
+ * `UINT64`
+ * `FLOAT32`
+ * `FLOAT64`
+ * `BOOLEAN`
+ * `STRING`
+ * `ENUM`
+
+
+#### class.property.componentCount
+
+The number of components per element for `ARRAY` elements.
+
+* **Type**: `integer`
+* **Required**: No
+* **Minimum**: ` >= 2`
+
+
+#### class.property.normalized
+
+Specifies whether integer values are normalized. This applies both when `type` is an integer type, or when `type` is `ARRAY` with a `componentType` that is an integer type. For unsigned integer types, values are normalized between `[0.0, 1.0]`. For signed integer types, values are normalized between `[-1.0, 1.0]`. For all other types, this property is ignored.
+
+* **Type**: `boolean`
+* **Required**: No, default: `false`
+
+
+#### class.property.max
+
+Maximum allowed values for property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values: they always correspond to the integer values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### class.property.min
+
+Minimum allowed values for property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values: they always correspond to the integer values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### class.property.default
+
+A default value to use when the property value is not defined. If used, `optional` must be set to true. The type of the default value must match the property definition: For `BOOLEAN` use `true` or `false`. For `STRING` use a JSON string. For a numeric type use a JSON number. For `ENUM` use the enum `name`, not the integer value. For `ARRAY` use a JSON array containing values matching the `componentType`.
+
+* **Type**: `boolean,number,string,array`
+* **Required**: No
+
+
+#### class.property.optional
+
+If true, this property is optional.
+
+* **Type**: `boolean`
+* **Required**: No, default: `false`
+
+
+#### class.property.semantic
+
+An identifier that describes how this property should be interpreted. The semantic cannot be used by other properties in the class.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### class.property.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### class.property.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Enum
+
+An object defining the values of an enum.
+
+**`Enum` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**name**|`string`|The name of the enum, e.g. for display purposes.|No|
+|**description**|`string`|The description of the enum.|No|
+|**valueType**|`string`|The type of the integer enum value.|No, default: `"UINT16"`|
+|**values**|`enum.value` `[1-*]`|An array of enum values. Duplicate names or duplicate integer values are not allowed.| ✓ Yes|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### enum.name
+
+The name of the enum, e.g. for display purposes.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### enum.description
+
+The description of the enum.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### enum.valueType
+
+The type of the integer enum value.
+
+* **Type**: `string`
+* **Required**: No, default: `"UINT16"`
+* **Allowed values**:
+ * `"INT8"`
+ * `"UINT8"`
+ * `"INT16"`
+ * `"UINT16"`
+ * `"INT32"`
+ * `"UINT32"`
+ * `"INT64"`
+ * `"UINT64"`
+
+
+#### enum.values
+
+An array of enum values. Duplicate names or duplicate integer values are not allowed.
+
+* **Type**: `enum.value` `[1-*]`
+* **Required**: ✓ Yes
+
+
+#### enum.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### enum.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Enum value
+
+An enum value.
+
+**`Enum value` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**name**|`string`|The name of the enum value.| ✓ Yes|
+|**description**|`string`|The description of the enum value.|No|
+|**value**|`integer`|The integer enum value.| ✓ Yes|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### enum.value.name
+
+The name of the enum value.
+
+* **Type**: `string`
+* **Required**: ✓ Yes
+* **Minimum Length**`: >= 1`
+
+
+#### enum.value.description
+
+The description of the enum value.
+
+* **Type**: `string`
+* **Required**: No
+* **Minimum Length**`: >= 1`
+
+
+#### enum.value.value
+
+The integer enum value.
+
+* **Type**: `integer`
+* **Required**: ✓ Yes
+
+
+#### enum.value.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### enum.value.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Feature Table
+
+A feature table defined by a class and property values stored in arrays.
+
+**`Feature Table` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**class**|`string`|The class that property values conform to. The value must be a class ID declared in the `classes` dictionary.|No|
+|**count**|`integer`|The number of features, as well as the number of elements in each property array.| ✓ Yes|
+|**properties**|`object`|A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value is an object describing where property values are stored. Optional properties may be excluded from this dictionary.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### featureTable.class
+
+The class that property values conform to. The value must be a class ID declared in the `classes` dictionary.
+
+* **Type**: `string`
+* **Required**: No
+
+
+#### featureTable.count
+
+The number of features, as well as the number of elements in each property array.
+
+* **Type**: `integer`
+* **Required**: ✓ Yes
+* **Minimum**: ` >= 1`
+
+
+#### featureTable.properties
+
+A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value is an object describing where property values are stored. Optional properties may be excluded from this dictionary.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `featureTable.property`
+
+
+#### featureTable.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### featureTable.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Feature Table Property
+
+An array of binary property values.
+
+**`Feature Table Property` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**bufferView**|`integer`|The index of the buffer view containing property values. The data type of property values is determined by the property definition: When `type` is `BOOLEAN` values are packed into a bitfield. When `type` is `STRING` values are stored as byte sequences and decoded as UTF-8 strings. When `type` is a numeric type values are stored as the provided `type`. When `type` is `ENUM` values are stored as the enum's `valueType`. When `type` is `ARRAY` elements are packed tightly together and the data type is based on the `componentType` following the same rules as above. `arrayOffsetBufferView` is required for variable-size arrays and `stringOffsetBufferView` is required for strings (for variable-length arrays of strings, both are required). The buffer view `byteOffset` must be aligned to a multiple of 8 bytes. If the buffer view's `buffer` is the GLB-stored `BIN` chunk the byte offset is measured relative to the beginning of the GLB. Otherwise it is measured relative to the beginning of the buffer.| ✓ Yes|
+|**offsetType**|`string`|The type of values in `arrayOffsetBufferView` and `stringOffsetBufferView`.|No, default: `"UINT32"`|
+|**arrayOffsetBufferView**|`integer`|The index of the buffer view containing offsets for variable-length arrays. The number of offsets is equal to the feature table `count` plus one. The offsets represent the start positions of each array, with the last offset representing the position after the last array. The array length is computed using the difference between the current offset and the subsequent offset. If `componentType` is `STRING` the offsets index into the string offsets array (stored in `stringOffsetBufferView`), otherwise they index into the property array (stored in `bufferView`). The data type of these offsets is determined by `offsetType`. The buffer view `byteOffset` must be aligned to a multiple of 8 bytes in the same manner as the main `bufferView`|No|
+|**stringOffsetBufferView**|`integer`|The index of the buffer view containing offsets for strings. The number of offsets is equal to the number of string components plus one. The offsets represent the byte offsets of each string in the main `bufferView`, with the last offset representing the byte offset after the last string. The string byte length is computed using the difference between the current offset and the subsequent offset. The data type of these offsets is determined by `offsetType`. The buffer view `byteOffset` must be aligned to a multiple of 8 bytes in the same manner as the main `bufferView`.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### featureTable.property.bufferView
+
+The index of the buffer view containing property values. The data type of property values is determined by the property definition: When `type` is `BOOLEAN` values are packed into a bitfield. When `type` is `STRING` values are stored as byte sequences and decoded as UTF-8 strings. When `type` is a numeric type values are stored as the provided `type`. When `type` is `ENUM` values are stored as the enum's `valueType`. When `type` is `ARRAY` elements are packed tightly together and the data type is based on the `componentType` following the same rules as above. `arrayOffsetBufferView` is required for variable-size arrays and `stringOffsetBufferView` is required for strings (for variable-length arrays of strings, both are required). The buffer view `byteOffset` must be aligned to a multiple of 8 bytes. If the buffer view's `buffer` is the GLB-stored `BIN` chunk the byte offset is measured relative to the beginning of the GLB. Otherwise it is measured relative to the beginning of the buffer.
+
+* **Type**: `integer`
+* **Required**: ✓ Yes
+* **Minimum**: `>= 0`
+
+
+#### featureTable.property.offsetType
+
+The type of values in `arrayOffsetBufferView` and `stringOffsetBufferView`.
+
+* **Type**: `string`
+* **Required**: No, default: `"UINT32"`
+* **Allowed values**:
+ * `"UINT8"`
+ * `"UINT16"`
+ * `"UINT32"`
+ * `"UINT64"`
+
+
+#### featureTable.property.arrayOffsetBufferView
+
+The index of the buffer view containing offsets for variable-length arrays. The number of offsets is equal to the feature table `count` plus one. The offsets represent the start positions of each array, with the last offset representing the position after the last array. The array length is computed using the difference between the current offset and the subsequent offset. If `componentType` is `STRING` the offsets index into the string offsets array (stored in `stringOffsetBufferView`), otherwise they index into the property array (stored in `bufferView`). The data type of these offsets is determined by `offsetType`. The buffer view `byteOffset` must be aligned to a multiple of 8 bytes in the same manner as the main `bufferView`
+
+* **Type**: `integer`
+* **Required**: No
+* **Minimum**: `>= 0`
+
+
+#### featureTable.property.stringOffsetBufferView
+
+The index of the buffer view containing offsets for strings. The number of offsets is equal to the number of string components plus one. The offsets represent the byte offsets of each string in the main `bufferView`, with the last offset representing the byte offset after the last string. The string byte length is computed using the difference between the current offset and the subsequent offset. The data type of these offsets is determined by `offsetType`. The buffer view `byteOffset` must be aligned to a multiple of 8 bytes in the same manner as the main `bufferView`.
+
+* **Type**: `integer`
+* **Required**: No
+* **Minimum**: `>= 0`
+
+
+#### featureTable.property.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### featureTable.property.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Feature Texture
+
+Features whose property values are stored directly in texture channels. This is not to be confused with feature ID textures which store feature IDs for use with a feature table.
+
+**`Feature Texture` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**class**|`string`|The class this feature texture conforms to. The value must be a class ID declared in the `classes` dictionary.| ✓ Yes|
+|**properties**|`object`|A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value describes the texture channels containing property values.| ✓ Yes|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### featureTexture.class
+
+The class this feature texture conforms to. The value must be a class ID declared in the `classes` dictionary.
+
+* **Type**: `string`
+* **Required**: ✓ Yes
+
+
+#### featureTexture.properties
+
+A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value describes the texture channels containing property values.
+
+* **Type**: `object`
+* **Required**: ✓ Yes
+* **Type of each property**: `textureAccessor`
+
+
+#### featureTexture.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### featureTexture.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Texture Accessor
+
+A description of how to access property values from the color channels of a texture.
+
+**`Texture Accessor` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**channels**|`string`|Texture channels containing property values. Channels are labeled by `rgba` and are swizzled with a string of 1-4 characters.| ✓ Yes|
+|**texture**|`textureInfo`|The glTF texture and texture coordinates to use.| ✓ Yes|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### textureAccessor.channels
+
+Texture channels containing property values. Channels are labeled by `rgba` and are swizzled with a string of 1-4 characters.
+
+* **Type**: `string`
+* **Required**: ✓ Yes
+* **Pattern**: `^[rgba]{1,4}$`
+
+
+#### textureAccessor.texture
+
+The glTF texture and texture coordinates to use.
+
+* **Type**: `textureInfo`
+* **Required**: ✓ Yes
+
+
+#### textureAccessor.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### textureAccessor.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Statistics
+
+Statistics about features.
+
+**`Statistics` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**classes**|`object`|A dictionary, where each key is a class ID declared in the `classes` dictionary and each value is an object containing statistics about features that conform to the class.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### statistics.classes
+
+A dictionary, where each key is a class ID declared in the `classes` dictionary and each value is an object containing statistics about features that conform to the class.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `statistics.class`
+
+
+#### statistics.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### statistics.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Class Statistics
+
+Statistics about features that conform to the class.
+
+**`Class Statistics` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**count**|`integer`|The number of features that conform to the class.|No|
+|**properties**|`object`|A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value is an object containing statistics about property values.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### statistics.class.count
+
+The number of features that conform to the class.
+
+* **Type**: `integer`
+* **Required**: No
+* **Minimum**: ` >= 0`
+
+
+#### statistics.class.properties
+
+A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value is an object containing statistics about property values.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `statistics.class.property`
+
+
+#### statistics.class.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### statistics.class.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Property Statistics
+
+Statistics about property values.
+
+**`Property Statistics` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**min**|`number,array`|The minimum property value. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.|No|
+|**max**|`number,array`|The maximum property value. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.|No|
+|**mean**|`number,array`|The arithmetic mean of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.|No|
+|**median**|`number,array`|The median of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.|No|
+|**standardDeviation**|`number,array`|The standard deviation of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.|No|
+|**variance**|`number,array`|The variance of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.|No|
+|**sum**|`number,array`|The sum of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.|No|
+|**occurrences**|`object`|A dictionary, where each key corresponds to an enum `name` and each value is the number of occurrences of that enum. Only applicable when `type` or `componentType` is `ENUM`. For fixed-length arrays, this is an array with `componentCount` number of elements.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### statistics.class.property.min
+
+The minimum property value. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### statistics.class.property.max
+
+The maximum property value. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### statistics.class.property.mean
+
+The arithmetic mean of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### statistics.class.property.median
+
+The median of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### statistics.class.property.standardDeviation
+
+The standard deviation of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### statistics.class.property.variance
+
+The variance of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### statistics.class.property.sum
+
+The sum of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values.
+
+* **Type**: `number,array`
+* **Required**: No
+
+
+#### statistics.class.property.occurrences
+
+A dictionary, where each key corresponds to an enum `name` and each value is the number of occurrences of that enum. Only applicable when `type` or `componentType` is `ENUM`. For fixed-length arrays, this is an array with `componentCount` number of elements.
+
+* **Type**: `object`
+* **Required**: No
+* **Type of each property**: `number,array`
+
+
+#### statistics.class.property.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### statistics.class.property.extras
+
+* **Type**: `any`
+* **Required**: No
+
+
+## Primitive extension
+
+* [`Primitive extension`](#reference-primitive-extension) (root object)
+ * [`Feature ID Attribute`](#reference-featureidattribute)
+ * [`Feature ID Texture`](#reference-featureidtexture)
+
+---------------------------------------
+
+
+### Primitive extension
+
+`EXT_feature_metadata` extension for a primitive in a glTF model, to associate it with the root `EXT_feature_metadata` object.
+
+**`Primitive extension` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**featureIdAttributes**|`featureIdAttribute` `[1-*]`|An array of objects mapping per-vertex feature IDs to a feature table.|No|
+|**featureIdTextures**|`featureIdTexture` `[1-*]`|An array of objects mapping per-texel feature IDs to a feature table.|No|
+|**featureTextures**|`string` `[1-*]`|An array of IDs of feature textures from the root `EXT_feature_metadata` object.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### Primitive extension.featureIdAttributes
+
+An array of objects mapping per-vertex feature IDs to a feature table.
+
+* **Type**: `featureIdAttribute` `[1-*]`
+* **Required**: No
+
+
+#### Primitive extension.featureIdTextures
+
+An array of objects mapping per-texel feature IDs to a feature table.
+
+* **Type**: `featureIdTexture` `[1-*]`
+* **Required**: No
+
+
+#### Primitive extension.featureTextures
+
+An array of IDs of feature textures from the root `EXT_feature_metadata` object.
+
+* **Type**: `string` `[1-*]`
+* **Required**: No
+
+
+#### Primitive extension.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### Primitive extension.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Feature ID Attribute
+
+An object mapping feature IDs to a feature table.
+
+**`Feature ID Attribute` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**featureTable**|`string`|The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary.| ✓ Yes|
+|**featureIds**|`object`|An object describing feature IDs to be used as indices to property arrays in the feature table. Feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features in the feature table.| ✓ Yes|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### featureIdAttribute.featureTable
+
+The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary.
+
+* **Type**: `string`
+* **Required**: ✓ Yes
+
+
+#### featureIdAttribute.featureIds
+
+An object describing feature IDs to be used as indices to property arrays in the feature table. Feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features in the feature table.
+
+* **Type**: `object`
+* **Required**: ✓ Yes
+
+
+#### featureIdAttribute.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### featureIdAttribute.extras
+
+* **Type**: `any`
+* **Required**: No
+
+---------------------------------------
+
+
+
+### Feature ID Texture
+
+An object describing a texture used for storing per-texel feature IDs.
+
+**`Feature ID Texture` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**featureTable**|`string`|The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary.| ✓ Yes|
+|**featureIds**|`textureAccessor`|A description of the texture and channel to use for feature IDs. The `channels` property must have a single channel. Furthermore, feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features in the feature table. Texel values must be read as integers. Texture filtering should be disabled when fetching feature IDs.| ✓ Yes|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### featureIdTexture.featureTable
+
+The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary.
+
+* **Type**: `string`
+* **Required**: ✓ Yes
+
+
+#### featureIdTexture.featureIds
+
+A description of the texture and channel to use for feature IDs. The `channels` property must have a single channel. Furthermore, feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features in the feature table. Texel values must be read as integers. Texture filtering should be disabled when fetching feature IDs.
+
+* **Type**: `textureAccessor`
+* **Required**: ✓ Yes
+
+
+#### featureIdTexture.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### featureIdTexture.extras
+
+* **Type**: `any`
+* **Required**: No
+
+
+## `EXT_mesh_gpu_instancing` extension
+* [`EXT_mesh_gpu_instancing` extension](#reference-ext_mesh_gpu_instancing-extension) (root object)
+
+---------------------------------------
+
+
+### `EXT_mesh_gpu_instancing` extension
+
+An object describing per-instance feature IDs to be used as indices to property arrays in the feature table.
+
+**`EXT_mesh_gpu_instancing extension` Properties**
+
+| |Type|Description|Required|
+|---|---|---|---|
+|**featureIdAttributes**|`featureIdAttribute` `[1-*]`|An array of objects mapping per-instance feature IDs to property arrays in a feature table.|No|
+|**extensions**|`any`||No|
+|**extras**|`any`||No|
+
+Additional properties are allowed.
+
+
+#### EXT_mesh_gpu_instancing extension.featureIdAttributes
+
+An array of objects mapping per-instance feature IDs to property arrays in a feature table.
+
+* **Type**: `featureIdAttribute` `[1-*]`
+* **Required**: No
+
+
+#### EXT_mesh_gpu_instancing extension.extensions
+
+* **Type**: `any`
+* **Required**: No
+
+
+#### EXT_mesh_gpu_instancing extension.extras
+
+* **Type**: `any`
+* **Required**: No
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/building-components.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/building-components.png
new file mode 100644
index 0000000000..a6b959f195
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/building-components.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/composite-example.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/composite-example.png
new file mode 100644
index 0000000000..9246436be1
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/composite-example.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-id-texture.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-id-texture.png
new file mode 100644
index 0000000000..09610050df
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-id-texture.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-table-buildings.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-table-buildings.png
new file mode 100644
index 0000000000..ee170d623e
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-table-buildings.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-table.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-table.png
new file mode 100644
index 0000000000..ffc3cefb7d
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-table.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-texture.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-texture.png
new file mode 100644
index 0000000000..e1a3f2d81a
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/feature-texture.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/implicit-feature-ids.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/implicit-feature-ids.png
new file mode 100644
index 0000000000..da6b987e4e
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/implicit-feature-ids.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/material-classification.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/material-classification.png
new file mode 100644
index 0000000000..7b0e150d8e
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/material-classification.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/metadata-access.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/metadata-access.png
new file mode 100644
index 0000000000..b7c043cb16
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/metadata-access.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/multi-instance-metadata.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/multi-instance-metadata.png
new file mode 100644
index 0000000000..dcb13356be
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/multi-instance-metadata.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-node-metadata.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-node-metadata.png
new file mode 100644
index 0000000000..23c5bfc502
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-node-metadata.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-triangle-metadata.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-triangle-metadata.png
new file mode 100644
index 0000000000..666a7cbe53
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-triangle-metadata.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-vertex-metadata.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-vertex-metadata.png
new file mode 100644
index 0000000000..7683638e78
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/per-vertex-metadata.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/placemarks.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/placemarks.png
new file mode 100644
index 0000000000..eb39992a49
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/placemarks.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/point-cloud-layers.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/point-cloud-layers.png
new file mode 100644
index 0000000000..c2c61c3ad3
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/point-cloud-layers.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/point-features.png b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/point-features.png
new file mode 100644
index 0000000000..b22d400836
Binary files /dev/null and b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/figures/point-features.png differ
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/EXT_mesh_gpu_instancing/EXT_feature_metadata.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/EXT_mesh_gpu_instancing/EXT_feature_metadata.schema.json
new file mode 100644
index 0000000000..f77144dbd7
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/EXT_mesh_gpu_instancing/EXT_feature_metadata.schema.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "EXT_feature_metadata extension for EXT_mesh_gpu_instancing",
+ "type": "object",
+ "description": "An object describing per-instance feature IDs to be used as indices to property arrays in the feature table.",
+ "properties": {
+ "featureIdAttributes": {
+ "type": "array",
+ "description": "An array of objects mapping per-instance feature IDs to property arrays in a feature table.",
+ "items": {
+ "$ref": "../featureIdAttribute.schema.json"
+ },
+ "minItems": 1
+ },
+ "extensions": {},
+ "extras": {}
+ }
+ }
+
\ No newline at end of file
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/class.property.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/class.property.schema.json
new file mode 100644
index 0000000000..c04dcbbd84
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/class.property.schema.json
@@ -0,0 +1,116 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Class property",
+ "type": "object",
+ "description": "A class property.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the property, e.g. for display purposes."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The description of the property."
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "INT8",
+ "UINT8",
+ "INT16",
+ "UINT16",
+ "INT32",
+ "UINT32",
+ "INT64",
+ "UINT64",
+ "FLOAT32",
+ "FLOAT64",
+ "BOOLEAN",
+ "STRING",
+ "ENUM",
+ "ARRAY"
+ ],
+ "description": "The property type. If `ENUM` is used, then `enumType` must also be specified. If `ARRAY` is used, then `componentType` must also be specified. `ARRAY` is a fixed-length array when `componentCount` is defined, and variable-length otherwise."
+ },
+ "enumType": {
+ "type": "string",
+ "description": "An enum ID as declared in the `enums` dictionary. This value must be specified when `type` or `componentType` is `ENUM`."
+ },
+ "componentType": {
+ "enum": [
+ "INT8",
+ "UINT8",
+ "INT16",
+ "UINT16",
+ "INT32",
+ "UINT32",
+ "INT64",
+ "UINT64",
+ "FLOAT32",
+ "FLOAT64",
+ "BOOLEAN",
+ "STRING",
+ "ENUM"
+ ],
+ "description": "When `type` is `ARRAY` this indicates the type of each component of the array. If `ENUM` is used, then `enumType` must also be specified."
+ },
+ "componentCount": {
+ "type": "integer",
+ "minimum": 2,
+ "description": "The number of components per element for `ARRAY` elements."
+ },
+ "normalized": {
+ "type": "boolean",
+ "description": "Specifies whether integer values are normalized. This applies both when `type` is an integer type, or when `type` is `ARRAY` with a `componentType` that is an integer type. For unsigned integer types, values are normalized between `[0.0, 1.0]`. For signed integer types, values are normalized between `[-1.0, 1.0]`. For all other types, this property is ignored.",
+ "default": false
+ },
+ "max": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "Maximum allowed values for property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values: they always correspond to the integer values."
+ },
+ "min": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "Minimum allowed values for property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values: they always correspond to the integer values."
+ },
+ "default": {
+ "type": [
+ "boolean",
+ "number",
+ "string",
+ "array"
+ ],
+ "description": "A default value to use when the property value is not defined. If used, `optional` must be set to true. The type of the default value must match the property definition: For `BOOLEAN` use `true` or `false`. For `STRING` use a JSON string. For a numeric type use a JSON number. For `ENUM` use the enum `name`, not the integer value. For `ARRAY` use a JSON array containing values matching the `componentType`."
+ },
+ "optional": {
+ "type": "boolean",
+ "description": "If true, this property is optional.",
+ "default": false
+ },
+ "semantic": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An identifier that describes how this property should be interpreted. The semantic cannot be used by other properties in the class."
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "dependencies": {
+ "componentCount": [
+ "componentType"
+ ],
+ "default": [
+ "optional"
+ ]
+ },
+ "required": [
+ "type"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/class.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/class.schema.json
new file mode 100644
index 0000000000..e8ee28efd3
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/class.schema.json
@@ -0,0 +1,28 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Class",
+ "type": "object",
+ "description": "A class containing a set of properties.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the class, e.g. for display purposes."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The description of the class."
+ },
+ "properties": {
+ "type": "object",
+ "description": "A dictionary, where each key is a property ID and each value is an object defining the property.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "class.property.schema.json"
+ }
+ },
+ "extensions": {},
+ "extras": {}
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/enum.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/enum.schema.json
new file mode 100644
index 0000000000..67691e88a0
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/enum.schema.json
@@ -0,0 +1,46 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Enum",
+ "type": "object",
+ "description": "An object defining the values of an enum.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the enum, e.g. for display purposes."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The description of the enum."
+ },
+ "valueType": {
+ "type": "string",
+ "default": "UINT16",
+ "enum": [
+ "INT8",
+ "UINT8",
+ "INT16",
+ "UINT16",
+ "INT32",
+ "UINT32",
+ "INT64",
+ "UINT64"
+ ],
+ "description": "The type of the integer enum value."
+ },
+ "values": {
+ "type": "array",
+ "description": "An array of enum values. Duplicate names or duplicate integer values are not allowed.",
+ "items": {
+ "$ref": "enum.value.schema.json"
+ },
+ "minItems": 1
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "required": [
+ "values"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/enum.value.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/enum.value.schema.json
new file mode 100644
index 0000000000..a25cf23688
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/enum.value.schema.json
@@ -0,0 +1,28 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Enum value",
+ "type": "object",
+ "description": "An enum value.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the enum value."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The description of the enum value."
+ },
+ "value": {
+ "type": "integer",
+ "description": "The integer enum value."
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "required": [
+ "name",
+ "value"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdAttribute.featureIds.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdAttribute.featureIds.schema.json
new file mode 100644
index 0000000000..3a32a65ca3
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdAttribute.featureIds.schema.json
@@ -0,0 +1,43 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature ID attribute",
+ "type": "object",
+ "description": "Feature IDs to be used as indices to property arrays in the feature table.",
+ "properties": {
+ "attribute": {
+ "type": "string",
+ "pattern": "^_FEATURE_ID_([1-9]\\d*|0)$",
+ "description": "The name of the attribute containing feature IDs."
+ },
+ "constant": {
+ "type": "integer",
+ "minimum": 0,
+ "default": 0,
+ "description": "Sets a constant feature ID when the attribute property is omitted."
+ },
+ "divisor": {
+ "type": "integer",
+ "minimum": 0,
+ "default": 0,
+ "description": "The rate at which feature IDs increment. If `divisor` is 0 then `constant` is used. If `divisor` is non-zero the feature ID increments once per `divisor` sets of elements, starting at `constant`. For example, if `constant` is 0 and `divisor` is 1 the feature IDs are [0, 1, 2, ...]; if `constant` is 2 and `divisor` is 3 the feature IDs are [2, 2, 2, 3, 3, 3, 4, 4, 4, ...]"
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "attribute",
+ "constant"
+ ]
+ },
+ {
+ "required": [
+ "attribute",
+ "divisor"
+ ]
+ }
+ ]
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdAttribute.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdAttribute.schema.json
new file mode 100644
index 0000000000..389a3f4a76
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdAttribute.schema.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature ID Attribute",
+ "type": "object",
+ "description": "An object mapping feature IDs to a feature table.",
+ "properties": {
+ "featureTable": {
+ "type": "string",
+ "description": "The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary."
+ },
+ "featureIds": {
+ "allOf": [ { "$ref": "featureIdAttribute.featureIds.schema.json" } ],
+ "description": "An object describing feature IDs to be used as indices to property arrays in the feature table. Feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features in the feature table."
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "required": [
+ "featureTable",
+ "featureIds"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdTexture.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdTexture.schema.json
new file mode 100644
index 0000000000..ed67854d01
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureIdTexture.schema.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature ID Texture",
+ "type": "object",
+ "description": "An object describing a texture used for storing per-texel feature IDs.",
+ "properties": {
+ "featureTable": {
+ "type": "string",
+ "description": "The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary."
+ },
+ "featureIds": {
+ "allOf": [ { "$ref": "textureAccessor.schema.json" } ],
+ "description": "A description of the texture and channel to use for feature IDs. The `channels` property must have a single channel. Furthermore, feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features in the feature table. Texel values must be read as integers. Texture filtering should be disabled when fetching feature IDs."
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "required": [
+ "featureTable",
+ "featureIds"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTable.property.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTable.property.schema.json
new file mode 100644
index 0000000000..42ed1e514b
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTable.property.schema.json
@@ -0,0 +1,36 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature Table Property",
+ "type": "object",
+ "description": "An array of binary property values.",
+ "properties": {
+ "bufferView": {
+ "allOf": [ { "$ref": "glTFid.schema.json" } ],
+ "description": "The index of the buffer view containing property values. The data type of property values is determined by the property definition: When `type` is `BOOLEAN` values are packed into a bitfield. When `type` is `STRING` values are stored as byte sequences and decoded as UTF-8 strings. When `type` is a numeric type values are stored as the provided `type`. When `type` is `ENUM` values are stored as the enum's `valueType`. When `type` is `ARRAY` elements are packed tightly together and the data type is based on the `componentType` following the same rules as above. `arrayOffsetBufferView` is required for variable-size arrays and `stringOffsetBufferView` is required for strings (for variable-length arrays of strings, both are required). The buffer view `byteOffset` must be aligned to a multiple of 8 bytes. If the buffer view's `buffer` is the GLB-stored `BIN` chunk, the byte offset is measured relative to the beginning of the GLB. Otherwise, it is measured relative to the beginning of the buffer."
+ },
+ "offsetType": {
+ "type": "string",
+ "default": "UINT32",
+ "enum": [
+ "UINT8",
+ "UINT16",
+ "UINT32",
+ "UINT64"
+ ],
+ "description": "The type of values in `arrayOffsetBufferView` and `stringOffsetBufferView`."
+ },
+ "arrayOffsetBufferView": {
+ "allOf": [ { "$ref": "glTFid.schema.json" } ],
+ "description": "The index of the buffer view containing offsets for variable-length arrays. The number of offsets is equal to the feature table `count` plus one. The offsets represent the start positions of each array, with the last offset representing the position after the last array. The array length is computed using the difference between the current offset and the subsequent offset. If `componentType` is `STRING` the offsets index into the string offsets array (stored in `stringOffsetBufferView`), otherwise they index into the property array (stored in `bufferView`). The data type of these offsets is determined by `offsetType`. The buffer view `byteOffset` must be aligned to a multiple of 8 bytes in the same manner as the main `bufferView`"
+ },
+ "stringOffsetBufferView": {
+ "allOf": [ { "$ref": "glTFid.schema.json" } ],
+ "description": "The index of the buffer view containing offsets for strings. The number of offsets is equal to the number of string components plus one. The offsets represent the byte offsets of each string in the main `bufferView`, with the last offset representing the byte offset after the last string. The string byte length is computed using the difference between the current offset and the subsequent offset. The data type of these offsets is determined by `offsetType`. The buffer view `byteOffset` must be aligned to a multiple of 8 bytes in the same manner as the main `bufferView`."
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "required": [
+ "bufferView"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTable.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTable.schema.json
new file mode 100644
index 0000000000..c21a20119f
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTable.schema.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature Table",
+ "type": "object",
+ "description": "A feature table defined by a class and property values stored in arrays.",
+ "properties": {
+ "class": {
+ "type": "string",
+ "description": "The class that property values conform to. The value must be a class ID declared in the `classes` dictionary."
+ },
+ "count": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The number of features, as well as the number of elements in each property array."
+ },
+ "properties": {
+ "type": "object",
+ "description": "A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value is an object describing where property values are stored. Optional properties may be excluded from this dictionary.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "featureTable.property.schema.json"
+ }
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "required": [
+ "count"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTexture.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTexture.schema.json
new file mode 100644
index 0000000000..a291d62243
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/featureTexture.schema.json
@@ -0,0 +1,26 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Feature Texture",
+ "type": "object",
+ "description": "Features whose property values are stored directly in texture channels. This is not to be confused with feature ID textures which store feature IDs for use with a feature table.",
+ "properties": {
+ "class": {
+ "type": "string",
+ "description": "The class this feature texture conforms to. The value must be a class ID declared in the `classes` dictionary."
+ },
+ "properties": {
+ "type": "object",
+ "description": "A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value describes the texture channels containing property values.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "textureAccessor.schema.json"
+ }
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "required": [
+ "class",
+ "properties"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/gltf.EXT_feature_metadata.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/gltf.EXT_feature_metadata.schema.json
new file mode 100644
index 0000000000..d3d5b382a2
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/gltf.EXT_feature_metadata.schema.json
@@ -0,0 +1,45 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "EXT_feature_metadata glTF extension",
+ "type": "object",
+ "description": "glTF extension that assigns metadata to features in a model.",
+ "properties": {
+ "schema": {
+ "allOf": [ { "$ref": "schema.schema.json" } ],
+ "description": "An object defining classes and enums."
+ },
+ "schemaUri": {
+ "type": "string",
+ "description": "A uri to an external schema file.",
+ "format": "uriref"
+ },
+ "statistics": {
+ "allOf": [ { "$ref": "statistics.schema.json" } ],
+ "description": "An object containing statistics about features."
+ },
+ "featureTables": {
+ "type": "object",
+ "description": "A dictionary, where each key is a feature table ID and each value is an object defining the feature table.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "featureTable.schema.json"
+ }
+ },
+ "featureTextures": {
+ "type": "object",
+ "description": "A dictionary, where each key is a feature texture ID and each value is an object defining the feature texture.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "featureTexture.schema.json"
+ }
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "not": {
+ "required": [
+ "schema",
+ "schemaUri"
+ ]
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/primitive.EXT_feature_metadata.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/primitive.EXT_feature_metadata.schema.json
new file mode 100644
index 0000000000..fa5d47e714
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/primitive.EXT_feature_metadata.schema.json
@@ -0,0 +1,51 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "EXT_feature_metadata glTF Primitive extension",
+ "type": "object",
+ "description": "`EXT_feature_metadata extension` for a primitive in a glTF model, to associate it with the root `EXT_feature_metadata` object.",
+ "properties": {
+ "featureIdAttributes": {
+ "type": "array",
+ "description": "An array of objects mapping per-vertex feature IDs to a feature table.",
+ "items": {
+ "$ref": "featureIdAttribute.schema.json"
+ },
+ "minItems": 1
+ },
+ "featureIdTextures": {
+ "type": "array",
+ "description": "An array of objects mapping per-texel feature IDs to a feature table.",
+ "items": {
+ "$ref": "featureIdTexture.schema.json"
+ },
+ "minItems": 1
+ },
+ "featureTextures": {
+ "type": "array",
+ "description": "An array of IDs of feature textures from the root `EXT_feature_metadata` object.",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "anyOf": [
+ {
+ "required": [
+ "featureIdAttributes"
+ ]
+ },
+ {
+ "required": [
+ "featureIdTextures"
+ ]
+ },
+ {
+ "required": [
+ "featureTextures"
+ ]
+ }
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/schema.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/schema.schema.json
new file mode 100644
index 0000000000..aee0e9c77a
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/schema.schema.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "https://json-schema.org/draft-04/schema",
+ "title": "Schema",
+ "type": "object",
+ "description": "An object defining classes and enums.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The name of the schema."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The description of the schema."
+ },
+ "version": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Application-specific version of the schema."
+ },
+ "classes": {
+ "type": "object",
+ "description": "A dictionary, where each key is a class ID and each value is an object defining the class.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "class.schema.json"
+ }
+ },
+ "enums": {
+ "type": "object",
+ "description": "A dictionary, where each key is an enum ID and each value is an object defining the values for the enum.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "enum.schema.json"
+ }
+ },
+ "extensions": {},
+ "extras": {}
+ }
+}
\ No newline at end of file
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.class.property.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.class.property.schema.json
new file mode 100644
index 0000000000..d260c92ef4
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.class.property.schema.json
@@ -0,0 +1,70 @@
+{
+ "$schema": "https://json-schema.org/draft-04/schema",
+ "title": "Property Statistics",
+ "type": "object",
+ "description": "Statistics about property values.",
+ "properties": {
+ "min": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "The minimum property value. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values."
+ },
+ "max": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "The maximum property value. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values."
+ },
+ "mean": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "The arithmetic mean of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values."
+ },
+ "median": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "The median of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values."
+ },
+ "standardDeviation": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "The standard deviation of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values."
+ },
+ "variance": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "The variance of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values."
+ },
+ "sum": {
+ "type": [
+ "number",
+ "array"
+ ],
+ "description": "The sum of the property values. Only applicable for numeric types and fixed-length arrays of numeric types. For numeric types this is a single number. For fixed-length arrays this is an array with `componentCount` number of elements. The `normalized` property has no effect on these values."
+ },
+ "occurrences": {
+ "type": "object",
+ "description": "A dictionary, where each key corresponds to an enum `name` and each value is the number of occurrences of that enum. Only applicable when `type` or `componentType` is `ENUM`. For fixed-length arrays, this is an array with `componentCount` number of elements.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "type": [
+ "number",
+ "array"
+ ]
+ }
+ },
+ "extensions": {},
+ "extras": {}
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.class.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.class.schema.json
new file mode 100644
index 0000000000..904ff0c75e
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.class.schema.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json-schema.org/draft-04/schema",
+ "title": "Class Statistics",
+ "type": "object",
+ "description": "Statistics about features that conform to the class.",
+ "properties": {
+ "count": {
+ "type": "integer",
+ "description": "The number of features that conform to the class.",
+ "minimum": 0
+ },
+ "properties": {
+ "type": "object",
+ "description": "A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value is an object containing statistics about property values.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "statistics.class.property.schema.json"
+ }
+ },
+ "extensions": {},
+ "extras": {}
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.schema.json
new file mode 100644
index 0000000000..25345577de
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/statistics.schema.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://json-schema.org/draft-04/schema",
+ "title": "Statistics",
+ "type": "object",
+ "description": "Statistics about features.",
+ "properties": {
+ "classes": {
+ "type": "object",
+ "description": "A dictionary, where each key is a class ID declared in the `classes` dictionary and each value is an object containing statistics about features that conform to the class.",
+ "minProperties": 1,
+ "additionalProperties": {
+ "$ref": "statistics.class.schema.json"
+ }
+ },
+ "extensions": {},
+ "extras": {}
+ }
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/textureAccessor.schema.json b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/textureAccessor.schema.json
new file mode 100644
index 0000000000..4a72717ac5
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0/schema/textureAccessor.schema.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "Texture Accessor",
+ "description": "A description of how to access property values from the color channels of a texture.",
+ "type": "object",
+ "properties": {
+ "channels": {
+ "type": "string",
+ "pattern": "^[rgba]{1,4}$",
+ "description": "Texture channels containing property values. Channels are labeled by `rgba` and are swizzled with a string of 1-4 characters."
+ },
+ "texture": {
+ "allOf": [ { "$ref": "textureInfo.schema.json" } ],
+ "description": "The glTF texture and texture coordinates to use."
+ },
+ "extensions": {},
+ "extras": {}
+ },
+ "required": [
+ "channels",
+ "texture"
+ ]
+}
diff --git a/extensions/2.0/Vendor/EXT_feature_metadata/README.md b/extensions/2.0/Vendor/EXT_feature_metadata/README.md
new file mode 100644
index 0000000000..09ae93ed47
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_feature_metadata/README.md
@@ -0,0 +1,32 @@
+# EXT_feature_metadata
+
+Assigns metadata to features in a model. Metadata may be assigned on a per-vertex, per-texel, or per-instance basis.
+
+This extension implements the [Cesium 3D Metadata Specification](https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/specification/Metadata/1.0.0).
+
+## Changelog
+
+* [**Version 0.0.0**](0.0.0) December 4, 2020
+ * Initial draft
+* [**Version 1.0.0**](1.0.0) February 24, 2021
+ * Changes to class properties
+ * Removed `FLOAT16` type
+ * Removed `BLOB` type
+ * Added `ENUM` to the list of supported types and component types and added `enumType` to refer to the chosen enum
+ * `min` and `max` are now numbers instead of single-element arrays for non-`ARRAY` properties
+ * Changes to feature table
+ * Removed `offsetBufferViews`, replaced with `arrayOffsetBufferView` and `stringOffsetBufferView`
+ * Removed `blobByteLength`
+ * Removed `stringByteLength`
+ * Removed `name` and `description`
+ * Removed `elementCount` and redefined `count` to mean the element count
+ * Added optional `semantic` property
+ * Changes to feature ID attribute
+ * Removed `vertexStride` and `instanceStride`
+ * Added `divisor` for incrementing feature IDs at fixed intervals, e.g. per-triangle or per-quad
+ * Changes to `EXT_feature_metadata` object
+ * Removed `classes` dictionary. Classes and enums are now contained in the `schema` object.
+ * Added `schema` and `schemaUri`. The schema object contains class and enum definitions. `schemaUri` refers to an external schema JSON file. `schema` and `schemaUri` are mutually exclusive.
+ * Added optional `statistics` object which provides aggregate information about select properties within the model
+ * Other changes
+ * Added `EXT_feature_metadata` extension to the [`EXT_mesh_gpu_instancing`](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing) extension for assigning metadata to instances
diff --git a/extensions/2.0/Vendor/EXT_geopose/0.0.0/README.md b/extensions/2.0/Vendor/EXT_geopose/0.0.0/README.md
new file mode 100644
index 0000000000..a3daee8d3b
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_geopose/0.0.0/README.md
@@ -0,0 +1,68 @@
+
+# EXT_geopose
+
+**Version 0.0.0**, May 17, 2021
+
+
+## Contributors
+
+* Sam Suhag, Cesium
+* Sean Lilley, Cesium
+* Peter Gagliardi, Cesium
+
+
+## Status
+
+Draft
+
+
+## Dependencies
+
+Written against the draft of [OGC GeoPose Standard 1.0](https://github.com/opengeospatial/GeoPose/tree/main/standard).
+
+
+## Optional vs. Required
+
+This extension is optional, meaning it should be placed in the `extensionsUsed` list, but not in the `extensionsRequired` list.
+
+
+## Contents
+- [Overview](#overview)
+ - [GeoPose 1.0 Standard](#geopose-10-standard)
+- [Coordinate Systems](#coordinate-systems)
+- [Schema Updates](#schema-updates)
+
+## Overview
+
+This extension to glTF enables static positioning and orienting of models on the Earth.
+
+### GeoPose 1.0 Standard
+
+GeoPose 1.0 is an OGC Implementation Standard for exchanging the location and orientation of real or virtual geometric objects (*Poses*) within reference frames anchored to the earth’s surface (*Geo*) or within other astronomical coordinate systems.
+
+This extension implements [Standardization Target 2: Basic-Euler](https://github.com/opengeospatial/GeoPose/blob/main/standard/standard/standard/clause_7_normative_text.adoc#standardization-target-2-basic-euler) in the OGC GeoPose 1.0 Standard.
+
+## Coordinate Systems
+
+This extension uses WGS84([EPSG:4979](https://epsg.io/4979)) as the coordinate reference system for specifying the position with longitude and latitude specified in degrees. Height above (or below) the ellipsoid must be specified in meters. The yaw-pitch-roll is provided as a rotation-only transform from a WGS84 referenced local tangent plane East-North-Up coordinate system..
+
+```json
+{
+ "extensions": {
+ "EXT_geopose": {
+ "longitude": 46.7,
+ "latitude": 25.067,
+ "height": 691.0,
+ "ypr": {
+ "yaw": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ }
+ }
+}
+```
+
+## Schema Updates
+
+Updates to the schema can be found in the [extension schema](schema/gltf.EXT_geopose.schema.json).
diff --git a/extensions/2.0/Vendor/EXT_geopose/0.0.0/schema/gltf.EXT_geopose.schema.json b/extensions/2.0/Vendor/EXT_geopose/0.0.0/schema/gltf.EXT_geopose.schema.json
new file mode 100644
index 0000000000..fd0636c30b
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_geopose/0.0.0/schema/gltf.EXT_geopose.schema.json
@@ -0,0 +1,54 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema",
+ "title": "EXT_geopose glTF extension",
+ "type": "object",
+ "description": "glTF extension for specifying the position and orientation of models on the Earth.",
+ "allOf": [
+ {
+ "$ref": "glTFProperty.schema.json"
+ }
+ ],
+ "properties": {
+ "longitude": {
+ "type": "number",
+ "description": "Longitude in the WGS84 datum. The angle must be specified in degrees.",
+ "minimum": -180.0,
+ "maximum": 180.0
+ },
+ "latitude": {
+ "type": "number",
+ "description": "Latitude in the WGS84 datum. The angle must be specified in degrees.",
+ "minimum": -90.0,
+ "maximum": 90.0
+ },
+ "height": {
+ "type": "number",
+ "description": "Heights are in meters above (or below) the WGS84 ellipsoid."
+ },
+ "ypr": {
+ "type": "object",
+ "description": "Rotation-only transformation from a WGS-84-referenced local tangent plane east-north-up coordinate system.",
+ "properties": {
+ "yaw": {
+ "type": "number",
+ "description": "Rotation about the LTP-ENU frame Z (up) axis. The angle must be specified in degrees."
+ },
+ "pitch": {
+ "type": "number",
+ "description": "Pitch is rotation about the y axis of the yaw-rotated local frame. The angle must be specified in degrees."
+ },
+ "roll": {
+ "type": "number",
+ "description": "Roll is rotation about the yaw and pitch rotated x axis of the local frame. The angle must be specified in degrees."
+ }
+ },
+ "required": [
+ "yaw",
+ "pitch",
+ "roll"
+ ]
+ },
+ "extensions": {},
+ "extras": {}
+ }
+}
\ No newline at end of file
diff --git a/extensions/2.0/Vendor/EXT_geopose/README.md b/extensions/2.0/Vendor/EXT_geopose/README.md
new file mode 100644
index 0000000000..04b80c1e7c
--- /dev/null
+++ b/extensions/2.0/Vendor/EXT_geopose/README.md
@@ -0,0 +1,8 @@
+# EXT_geopose
+
+Implementation of [Standardization Target 2: Basic-Euler](https://github.com/opengeospatial/GeoPose/blob/main/standard/standard/standard/clause_7_normative_text.adoc#standardization-target-2-basic-euler) in the OGC GeoPose 1.0 Standard.
+
+## Changelog
+
+* [**Version 0.0.0**](0.0.0) May 4, 2021
+ * Initial draft