NASA-AMMOS / 3DTilesRendererJS

Renderer for 3D Tiles in Javascript using three.js
https://nasa-ammos.github.io/3DTilesRendererJS/example/bundle/mars.html
Apache License 2.0
1.46k stars 265 forks source link

StructuralMetadata: Validate all fields from test data #569

Closed gkjohnson closed 2 weeks ago

gkjohnson commented 1 month ago

All files can be tested in Cesium sand castle to see values:

https://github.com/CesiumGS/3d-tiles-samples/tree/main/glTF/EXT_structural_metadata

Open Questions

gkjohnson commented 2 weeks ago

For evaluation here

Mesh Features Sandcastle Demo ```js const viewer = new Cesium.Viewer("cesiumContainer"); // Stores the tileset that is currently selected let currentTileset; // Creates a custom fragment shader for visualizing the feature IDs. // This fetches the feature ID for the fragment from the // fsInput.featureIds.featureId_0 structure, and just assigns // a color to the fragment, based on this feature ID function createCustomShader() { const customShader = new Cesium.CustomShader({ fragmentShaderText: ` void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) { int id = fsInput.featureIds.featureId_0; vec3 color = vec3(0.0, 0.0, 0.0); if (id == 0) { color = vec3(0.0, 1.0, 0.0); } else if (id == 1) { color = vec3(0.5, 0.5, 0.5); } else if (id == 2) { color = vec3(1.0, 0.0, 0.0); } else if (id == 3) { color = vec3(0.0, 0.0, 1.0); } material.diffuse = color; } `, }); return customShader; } // Creates the tileset from the tileset.json in the given subdirectory async function createTileset(subdirectory) { if (Cesium.defined(currentTileset)) { viewer.scene.primitives.remove(currentTileset); currentTileset = undefined; } // Create the tileset, and move it to a certain position on the globe currentTileset = viewer.scene.primitives.add( await Cesium.Cesium3DTileset.fromUrl( `https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples/main/${subdirectory}/tileset.json`, { debugShowBoundingVolume: true, } ) ); currentTileset.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees(-75.152325, 39.94704, 0) ); const offset = new Cesium.HeadingPitchRange( 0, Cesium.Math.toRadians(-22.5), 4.0 ); viewer.zoomTo(currentTileset, offset); // Assign the custom shader that visualizes the feature IDs to the tileset currentTileset.customShader = createCustomShader(); } //============================================================================ // Sandcastle UI setup: // Create a label that will show some information for // the currently selected example function createInfoTextDisplay() { const infoTextDisplay = document.createElement("div"); infoTextDisplay.style.background = "rgba(42, 42, 42, 0.7)"; infoTextDisplay.style.padding = "5px 10px"; infoTextDisplay.style.marginTop = "5px"; return infoTextDisplay; } const infoTextDisplay = createInfoTextDisplay(); function setInfoText(infoText) { infoTextDisplay.textContent = infoText; } // Create one entry for the list of examples that can // be selected in the dropdown menu. Selecting one of // these will load the tileset for the sample in the // given directory, and display the given info text in // the infoTextDisplay function createSampleOption(name, directory, infoText) { return { text: name, onselect: async function () { await createTileset(directory); setInfoText(infoText); }, }; } // Create the list of available samples, and add them // to the sandcastle toolbar const sampleOptions = [ createSampleOption( "FeatureIdAttribute", "glTF/EXT_mesh_features/FeatureIdAttribute", "Feature IDs for the vertices, using a feature ID attribute" ), createSampleOption( "FeatureIdTexture", "glTF/EXT_mesh_features/FeatureIdTexture", "Feature IDs for texels, using a feature ID texture" ), ]; Sandcastle.addToolbarMenu(sampleOptions); // Add a toggle button to the toolbar, for selecting whether // feature IDs should be visualized. When it is selected, // then the tileset receives the custom shader that visualizes // the feature IDs. Otherwise, the custom shader of the tileset // is set to 'undefined', causing it to be rendered with the // default shader. Sandcastle.addToggleButton("Visualize Feature IDs", true, function (checked) { if (checked) { currentTileset.customShader = createCustomShader(); } else { currentTileset.customShader = undefined; } }); // Add the component that will dispplay the info text // to the sandcastle toolbar document.getElementById("toolbar").appendChild(infoTextDisplay); ```
Structural Metadata Example ```js const viewer = new Cesium.Viewer("cesiumContainer"); // Stores the tileset that is currently selected let currentTileset; // Stores the currently selected feature ID label, which // is the index of `FEATURE_ID_n` let currentActiveFeatureIdLabel = 0; // Creates the tileset from the tileset.json in the given subdirectory async function createTileset(subdirectory) { if (Cesium.defined(currentTileset)) { viewer.scene.primitives.remove(currentTileset); currentTileset = undefined; } // Create the tileset, and move it to a certain position on the globe currentTileset = viewer.scene.primitives.add( await Cesium.Cesium3DTileset.fromUrl( `https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples/main/${subdirectory}/tileset.json`, { debugShowBoundingVolume: true, } ) ); currentTileset.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees(-75.152325, 39.94704, 0) ); const offset = new Cesium.HeadingPitchRange( 0, Cesium.Math.toRadians(-22.5), 4.0 ); viewer.zoomTo(currentTileset, offset); // Make sure that picking refers to the FEATURE_ID index that // is currently selected in the UI currentTileset.featureIdLabel = currentActiveFeatureIdLabel; } // 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); //============================================================================ // Sandcastle UI setup: // Create a label that will show some information for // the currently selected example function createInfoTextDisplay() { const infoTextDisplay = document.createElement("div"); infoTextDisplay.style.background = "rgba(42, 42, 42, 0.7)"; infoTextDisplay.style.padding = "5px 10px"; infoTextDisplay.style.marginTop = "5px"; return infoTextDisplay; } const infoTextDisplay = createInfoTextDisplay(); function setInfoText(infoText) { infoTextDisplay.textContent = infoText; } // Create one entry for the list of examples that can // be selected in the dropdown menu. Selecting one of // these will load the tileset for the sample in the // given directory, and display the given info text in // the infoTextDisplay function createSampleOption(name, directory, infoText) { return { text: name, onselect: async function () { await createTileset(directory); setInfoText(infoText); }, }; } // Create the list of available samples, and add them // to the sandcastle toolbar const sampleOptions = [ createSampleOption( "FeatureIdAttributeAndPropertyTable", "glTF/EXT_structural_metadata/FeatureIdAttributeAndPropertyTable", "Feature IDs for vertices, based on a feature ID attribute, and a simple property table containing metadata" ), createSampleOption( "FeatureIdTextureAndPropertyTable", "glTF/EXT_structural_metadata/FeatureIdTextureAndPropertyTable", "Feature IDs for vertices, based on a feature ID texture, and a simple property table containing metadata" ), createSampleOption( "MultipleFeatureIdsAndProperties", "glTF/EXT_structural_metadata/MultipleFeatureIdsAndProperties", "A mesh primitive with two sets of feature IDs, each associated with metadata with multiple properties" ), createSampleOption( "SharedPropertyTable", "glTF/EXT_structural_metadata/SharedPropertyTable", "A mesh with two mesh primitives, each having a set of feature IDs, that refer to the same property table" ), createSampleOption( "MultipleClasses", "glTF/EXT_structural_metadata/MultipleClasses", "A mesh primitive with multiple feature ID sets that can be activated separately, and are associated with different metadata classes" ), createSampleOption( "ComplexTypes", "glTF/EXT_structural_metadata/ComplexTypes", "A mesh primitive with fetaure IDs that are associated with metadata that contains complex, structured types" ), ]; Sandcastle.addToolbarMenu(sampleOptions); // Creates an option for selecting the active feature ID // with the given index function createFeatureIdSetOption(index) { return { text: `Active feature ID: FEATURE_ID_${index}`, onselect: function () { currentActiveFeatureIdLabel = index; currentTileset.featureIdLabel = index; }, }; } // Create the list of available feature IDs, and add them // to the sandcastle toolbar const featureIdSetOptions = [ createFeatureIdSetOption(0), createFeatureIdSetOption(1), ]; Sandcastle.addToolbarMenu(featureIdSetOptions); // Add the component that will dispplay the info text // to the sandcastle toolbar document.getElementById("toolbar").appendChild(infoTextDisplay); ```
Property Texture Demo ```js 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( await Cesium.Cesium3DTileset.fromUrl( `https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples/main/glTF/EXT_structural_metadata/SimplePropertyTexture/tileset.json`, { debugShowBoundingVolume: true, } ) ); tileset.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees(-75.152325, 39.94704, 0) ); const offset = new Cesium.HeadingPitchRange( 0, Cesium.Math.toRadians(-22.5), 4.0 ); viewer.zoomTo(tileset, offset); // Create a custom (fragment) shader that accesses the metadata value with the // given property name, normalizes it to a value in [0,1] based on the given // source range, and uses that value as the brightness for the fragment. function createShader(propertyName, sourceMin, sourceMax) { const shader = new Cesium.CustomShader({ fragmentShaderText: ` void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) { float value = float(fsInput.metadata.${propertyName}); float range = float(${sourceMax}) - float(${sourceMin}); float brightness = (value - float(${sourceMin})) / range; material.diffuse = vec3(brightness); } `, }); return shader; } // Create one entry for the list of shaders that can // be selected in the dropdown menu. function createShaderOption(title, propertyName, sourceMin, sourceMax) { return { text: title, onselect: function () { if (!Cesium.defined(propertyName)) { tileset.customShader = undefined; } else { tileset.customShader = createShader(propertyName, sourceMin, sourceMax); } }, }; } const shaderOptions = [ createShaderOption("Default Shading", undefined, 0.0, 1.0), createShaderOption("Inside Temperature", "insideTemperature", 0.0, 255.0), createShaderOption("Outside Temperature", "outsideTemperature", 0.0, 255.0), createShaderOption("Insulation Thickness", "insulation", 0.0, 1.0), ]; Sandcastle.addToolbarMenu(shaderOptions); ```