diff --git a/glTF/GpuInstancesMetadata/GpuInstancesMetadata.gltf b/glTF/GpuInstancesMetadata/GpuInstancesMetadata.gltf
new file mode 100644
index 0000000..20d976e
--- /dev/null
+++ b/glTF/GpuInstancesMetadata/GpuInstancesMetadata.gltf
@@ -0,0 +1,178 @@
+{
+ "extensions" : {
+ "EXT_structural_metadata" : {
+ "schema" : {
+ "classes" : {
+ "exampleMetadataClass" : {
+ "name" : "Example metadata class",
+ "description" : "An example metadata class",
+ "properties" : {
+ "example_STRING" : {
+ "name" : "Example STRING property",
+ "description" : "An example property, with component type STRING",
+ "type" : "STRING"
+ }
+ }
+ }
+ }
+ },
+ "propertyTables" : [ {
+ "name" : "Example property table",
+ "class" : "exampleMetadataClass",
+ "count" : 10,
+ "properties" : {
+ "example_STRING" : {
+ "values" : 7,
+ "stringOffsets" : 8
+ }
+ }
+ } ]
+ }
+ },
+ "extensionsUsed" : [ "EXT_structural_metadata", "EXT_mesh_gpu_instancing", "EXT_instance_features" ],
+ "accessors" : [ {
+ "bufferView" : 0,
+ "byteOffset" : 0,
+ "componentType" : 5125,
+ "count" : 36,
+ "type" : "SCALAR",
+ "max" : [ 23 ],
+ "min" : [ 0 ]
+ }, {
+ "bufferView" : 1,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 24,
+ "type" : "VEC3",
+ "max" : [ 1.0, 1.0, 1.0 ],
+ "min" : [ 0.0, 0.0, 0.0 ]
+ }, {
+ "bufferView" : 2,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 24,
+ "type" : "VEC3",
+ "max" : [ 1.0, 1.0, 1.0 ],
+ "min" : [ -1.0, -1.0, -1.0 ]
+ }, {
+ "bufferView" : 3,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 10,
+ "type" : "VEC3",
+ "max" : [ 10.0, 10.0, 10.0 ],
+ "min" : [ 0.0, 0.0, 0.0 ]
+ }, {
+ "bufferView" : 4,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 10,
+ "type" : "VEC4",
+ "max" : [ 0.733, 0.462, 0.191, 1.0 ],
+ "min" : [ 0.0, 0.0, 0.0, 0.462 ]
+ }, {
+ "bufferView" : 5,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : 10,
+ "type" : "VEC3",
+ "max" : [ 2.0, 2.0, 2.0 ],
+ "min" : [ 1.0, 1.0, 1.0 ]
+ }, {
+ "bufferView" : 6,
+ "byteOffset" : 0,
+ "componentType" : 5123,
+ "count" : 10,
+ "type" : "SCALAR",
+ "max" : [ 9 ],
+ "min" : [ 0 ]
+ } ],
+ "asset" : {
+ "generator" : "JglTF from https://github.com/javagl/JglTF",
+ "version" : "2.0"
+ },
+ "buffers" : [ {
+ "uri" : "data:application/gltf-buffer;base64,AAAAAAIAAAABAAAAAAAAAAMAAAACAAAABAAAAAYAAAAFAAAABAAAAAcAAAAGAAAACAAAAAoAAAAJAAAACAAAAAsAAAAKAAAADAAAAA4AAAANAAAADAAAAA8AAAAOAAAAEAAAABIAAAARAAAAEAAAABMAAAASAAAAFAAAABYAAAAVAAAAFAAAABcAAAAWAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAACAPwAAAAAAAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAACAPwAAgD8AAIA/AAAAAAAAgD8AAIA/AAAAAAAAAAAAAIA/AACAPwAAAAAAAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAA",
+ "byteLength" : 720
+ }, {
+ "uri" : "data:application/gltf-buffer;base64,AAAAAAAAAAAAAAAA5DiOP+Q4jj/kOI4/5DgOQOQ4DkDkOA5AVlVVQFZVVUBWVVVA5DiOQOQ4jkDkOI5AHcexQB3HsUAdx7FAVlXVQFZV1UBWVdVAjuP4QI7j+ECO4/hA5DgOQeQ4DkHkOA5BAAAgQQAAIEEAACBBAAAAAAAAAAAAAAAAAACAP92ZzD0Q9YA9B0HVPHcffj/QGUs+AAMAPrywUz3rhHg/HXaWPuWqPT4f05w9Y0VvP3UqxT6+ing+IIHNPZiDYj+d+vA+w+KXPqEr+z1ub1I/DaEMP/RFsT6bkxI+REU/P9m0Hj+hD8g+H2slPgpNKT/WdC4/P+rbPqHVNT462RA/46U7P0SL7D6BlUM+RIvsPgAAgD8AAIA/AACAP+Q4jj/kOI4/5DiOP8dxnD/HcZw/x3GcP6uqqj+rqqo/q6qqP47juD+O47g/juO4P3Icxz9yHMc/chzHP1ZV1T9WVdU/VlXVPzmO4z85juM/OY7jPxzH8T8cx/E/HMfxPwAAAEAAAABAAAAAQAkACAAHAAYABQAEAAMAAgABAAAA",
+ "byteLength" : 420
+ }, {
+ "uri" : "data:application/gltf-buffer;base64,emVyb29uZXR3b3RocmVlZm91cmZpdmVzaXhzZXZlbmVpZ2h0bmluZQAAAAAEAAAABwAAAAoAAAAPAAAAEwAAABcAAAAaAAAAHwAAACQAAAAoAAAA",
+ "byteLength" : 84
+ } ],
+ "bufferViews" : [ {
+ "buffer" : 0,
+ "byteOffset" : 0,
+ "byteLength" : 144,
+ "target" : 34963
+ }, {
+ "buffer" : 0,
+ "byteOffset" : 144,
+ "byteLength" : 288,
+ "target" : 34962
+ }, {
+ "buffer" : 0,
+ "byteOffset" : 432,
+ "byteLength" : 288,
+ "target" : 34962
+ }, {
+ "buffer" : 1,
+ "byteOffset" : 0,
+ "byteLength" : 120
+ }, {
+ "buffer" : 1,
+ "byteOffset" : 120,
+ "byteLength" : 160
+ }, {
+ "buffer" : 1,
+ "byteOffset" : 280,
+ "byteLength" : 120
+ }, {
+ "buffer" : 1,
+ "byteOffset" : 400,
+ "byteLength" : 20
+ }, {
+ "buffer" : 2,
+ "byteOffset" : 0,
+ "byteLength" : 40
+ }, {
+ "buffer" : 2,
+ "byteOffset" : 40,
+ "byteLength" : 44
+ } ],
+ "meshes" : [ {
+ "primitives" : [ {
+ "attributes" : {
+ "POSITION" : 1,
+ "NORMAL" : 2
+ },
+ "indices" : 0,
+ "mode" : 4
+ } ]
+ } ],
+ "nodes" : [ {
+ "extensions" : {
+ "EXT_mesh_gpu_instancing" : {
+ "attributes" : {
+ "TRANSLATION" : 3,
+ "ROTATION" : 4,
+ "SCALE" : 5,
+ "_FEATURE_ID_0" : 6
+ }
+ },
+ "EXT_instance_features" : {
+ "featureIds" : [ {
+ "featureCount" : 10,
+ "attribute" : 0,
+ "propertyTable" : 0
+ } ]
+ }
+ },
+ "mesh" : 0
+ } ],
+ "scene" : 0,
+ "scenes" : [ {
+ "nodes" : [ 0 ]
+ } ]
+}
\ No newline at end of file
diff --git a/glTF/GpuInstancesMetadata/README.md b/glTF/GpuInstancesMetadata/README.md
new file mode 100644
index 0000000..ce2af6b
--- /dev/null
+++ b/glTF/GpuInstancesMetadata/README.md
@@ -0,0 +1,112 @@
+# Metadata for GPU instances
+
+An example showing how to assign metadata to GPU instances.
+
+The example combines three different extensions:
+
+- It uses `EXT_mesh_gpu_instancing` to create 10 GPU-based instances
+- It uses `EXT_instance_features` to assign feature IDs to these instances
+- It uses `EXT_structural_metadata` to define a property table with 10 rows, which can be looked up based on the feature IDs.
+
+## Screenshot
+
+
+
+## Example Sandcastle
+
+```JavaScript
+const viewer = new Cesium.Viewer("cesiumContainer");
+
+// Create the tileset, and move it to a certain position on the globe
+const tileset = viewer.scene.primitives.add(
+ new Cesium.Cesium3DTileset({
+ url: `http://localhost:8003/glTF/GpuInstanceMetadata/tileset.json`,
+ debugShowBoundingVolume: true,
+ })
+);
+tileset.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
+ Cesium.Cartesian3.fromDegrees(-75.152325, 39.94704, 0)
+);
+const offset = new Cesium.HeadingPitchRange(
+ Cesium.Math.toRadians(22.5),
+ Cesium.Math.toRadians(-22.5),
+ 40.0
+);
+viewer.zoomTo(tileset, offset);
+
+// Create an HTML element that will serve as the
+// tooltip that displays the feature information
+function createTooltip() {
+ const tooltip = document.createElement("div");
+ viewer.container.appendChild(tooltip);
+ tooltip.style.backgroundColor = "black";
+ tooltip.style.position = "absolute";
+ tooltip.style.left = "0";
+ tooltip.style.top = "0";
+ tooltip.style.padding = "14px";
+ tooltip.style["pointer-events"] = "none";
+ tooltip.style["block-size"] = "fit-content";
+ return tooltip;
+}
+const tooltip = createTooltip();
+
+// Show the given HTML content in the tooltip
+// at the given screen position
+function showTooltip(screenX, screenY, htmlContent) {
+ tooltip.style.display = "block";
+ tooltip.style.left = `${screenX}px`;
+ tooltip.style.top = `${screenY}px`;
+ tooltip.innerHTML = htmlContent;
+}
+
+// Create an HTML string that contains information
+// about the given feature, under the given title
+function createFeatureHtml(title, feature) {
+ if (!Cesium.defined(feature)) {
+ return `(No ${title})
`;
+ }
+ const propertyKeys = feature.getPropertyIds();
+ if (!Cesium.defined(propertyKeys)) {
+ return `(No properties for ${title})
`;
+ }
+ let html = `${title}:
`;
+ for (let i = 0; i < propertyKeys.length; i++) {
+ const propertyKey = propertyKeys[i];
+ const propertyValue = feature.getProperty(propertyKey);
+ html += ` ${propertyKey} : ${propertyValue}
`;
+ }
+ return html;
+}
+
+// Given an object that was obtained via Scene#pick: If it is
+// a Cesium3DTileFeature, then it is returned.
+// Otherwise, 'undefined' is returned.
+function obtainFeature(picked) {
+ if (!Cesium.defined(picked)) {
+ return undefined;
+ }
+ const isFeature = picked instanceof Cesium.Cesium3DTileFeature;
+ if (!isFeature) {
+ return undefined;
+ }
+ return picked;
+}
+
+// Install the handler that will perform picking when the
+// mouse is moved, and update the label entity when the
+// mouse is over a Cesium3DTileFeature
+const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
+handler.setInputAction(function (movement) {
+ let tooltipText = "";
+ const picked = viewer.scene.pick(movement.endPosition);
+ const feature = obtainFeature(picked);
+ tooltipText += createFeatureHtml("Feature", feature);
+ const screenX = movement.endPosition.x;
+ const screenY = movement.endPosition.y;
+ showTooltip(screenX, screenY, tooltipText);
+}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
+```
+
+## License
+
+[CC0](https://creativecommons.org/share-your-work/public-domain/cc0/)
diff --git a/glTF/GpuInstancesMetadata/screenshot/GpuInstancesMetadata.gif b/glTF/GpuInstancesMetadata/screenshot/GpuInstancesMetadata.gif
new file mode 100644
index 0000000..d183b4d
Binary files /dev/null and b/glTF/GpuInstancesMetadata/screenshot/GpuInstancesMetadata.gif differ
diff --git a/glTF/GpuInstancesMetadata/tileset.json b/glTF/GpuInstancesMetadata/tileset.json
new file mode 100644
index 0000000..552ccdf
--- /dev/null
+++ b/glTF/GpuInstancesMetadata/tileset.json
@@ -0,0 +1,20 @@
+{
+ "asset" : {
+ "version" : "1.1"
+ },
+ "geometricError" : 100.0,
+ "root" : {
+ "content" : {
+ "uri" : "GpuInstancesMetadata.gltf"
+ },
+ "boundingVolume" : {
+ "box" : [
+ 6.707386, -5.8532143, 5.8532143,
+ 6.707386, 0.0, 0.0,
+ 0.0, -5.8532143, 0.0,
+ 0.0, 0.0, 5.8532143
+ ]
+ },
+ "geometricError" : 0.0
+ }
+}
\ No newline at end of file
diff --git a/glTF/README.md b/glTF/README.md
index e91817e..447f493 100644
--- a/glTF/README.md
+++ b/glTF/README.md
@@ -1,15 +1,27 @@
# Samples for glTF extensions
+The following is a list of samples for different glTF extensions that are supported by Cesium. Each directory contains a dedicated `.gltf` file that contains the actual glTF asset. Additionally, each directory also contains a simple `tileset.json` file that is a [3D Tiles](https://github.com/CesiumGS/3d-tiles) tileset which just contains the respective glTF asset as its only content. These tilesets can be viewed in CesiumJS, by hosting the base directory of this repository on a local server.
+
+### `EXT_mesh_features`
+
The [`EXT_mesh_features`](https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features) extension is a glTF 2.0 extension that allows identifying geometry or subcomponents of geometry in glTF 2.0 assets as individual 'features', by associating them with a feature ID.
+The sandcastle code for viewing the `EXT_mesh_features` samples is shown in [glTF-Mesh-Features-Samples-Sandcastle.js](glTF-Mesh-Features-Samples-Sandcastle.js)
+
| Sample | Screenshot |
|:---|:--:|
| [`FeatureIdAttribute`](EXT_mesh_features/FeatureIdAttribute/)
Uses an attribute of a mesh primitive to assign feature IDs to vertices |
| [`FeatureIdTexture`](EXT_mesh_features/FeatureIdTexture/)
Uses a feature ID texture to assign feature IDs to texels on the surface of the geometry |
+
+### `EXT_structural_metadata`
+
The [`EXT_structural_metadata`](https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata) extension is a glTF 2.0 extension that allows storing structured metadata in glTF 2.0 assets in the form of _property tables_. Together with the [`EXT_mesh_features`](https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features) extension, the metadata can be looked up in these tables, using the feature IDs.
+The sandcastle code for viewing the `EXT_structural_metadata` samples is shown in [glTF-Structural-Metadata-Samples-Sandcastle.js](glTF-Structural-Metadata-Samples-Sandcastle.js)
+
+
| Sample | Screenshot |
|:---|:--:|
| [`FeatureIdAttributeAndPropertyTable`](EXT_structural_metadata/FeatureIdAttributeAndPropertyTable/)
Vertices that are associated with properties that are stored in a property table |
@@ -20,12 +32,10 @@ The [`EXT_structural_metadata`](https://github.com/CesiumGS/glTF/tree/3d-tiles-n
| [`ComplexTypes`](EXT_structural_metadata/ComplexTypes/)
Features that contain properties with more complex types |
| [`SimplePropertyTexture`](EXT_structural_metadata/SimplePropertyTexture/)
Properties that are stored in a texture |
-Each directory contains a dedicated `.gltf` file that contains the actual glTF asset. Additionally, each directory also contains a simple `tileset.json` file that is a [3D Tiles](https://github.com/CesiumGS/3d-tiles) tileset which just contains the respective glTF asset as its only content. These tilesets can be viewed in CesiumJS, by hosting the base directory of this repository on a local server, and using the following Cesium Sandcastle code:
-
-## Common Sandcastle Code
-The sandcastle code for viewing the `EXT_mesh_features` samples is shown in [glTF-Mesh-Features-Samples-Sandcastle.js](glTF-Mesh-Features-Samples-Sandcastle.js)
-
-The sandcastle code for viewing the `EXT_structural_metadata` samples is shown in [glTF-Structural-Metadata-Samples-Sandcastle.js](glTF-Structural-Metadata-Samples-Sandcastle.js)
+### Other glTF extensions
+| Sample | Screenshot |
+|:---|:--:|
+| [`GpuInstancesMetadata`](GpuInstancesMetadata/)
An example that uses `EXT_mesh_gpu_instancing` to create GPU instances that are associated with feature IDs, which are used for looking up metadata in a property table |