@@ -144,6 +144,90 @@ export function enableMeshoptDecoder(MeshOptDecoder) {
144144 itownsGLTFLoader . setMeshoptDecoder ( MeshOptDecoder ) ;
145145}
146146
147+ async function getMeshFeatures ( meshFeatures , options ) {
148+ const { faceIndex, barycoord } = options ;
149+
150+ const features = await meshFeatures . getFeaturesAsync ( faceIndex , barycoord ) ;
151+ return {
152+ features,
153+ featureIds : meshFeatures . getFeatureInfo ( ) ,
154+ } ;
155+ }
156+
157+ function getStructuralMetadata ( structuralMetadata , options ) {
158+ const { index, faceIndex, barycoord, tableIndices, features } = options ;
159+
160+ const tableData = [ ] ;
161+ if ( tableIndices !== undefined && features !== undefined ) {
162+ structuralMetadata . getPropertyTableData (
163+ tableIndices ,
164+ features ,
165+ tableData ,
166+ ) ;
167+ }
168+
169+ const attributeData = [ ] ;
170+ if ( index !== undefined ) {
171+ structuralMetadata . getPropertyAttributeData ( index , attributeData ) ;
172+ }
173+
174+ const textureData = [ ] ;
175+ if ( faceIndex !== undefined ) {
176+ structuralMetadata . getPropertyTextureData (
177+ faceIndex ,
178+ barycoord ,
179+ textureData ,
180+ ) ;
181+ }
182+
183+ const metadata = [
184+ ...tableData ,
185+ ...textureData ,
186+ ...attributeData ,
187+ ] ;
188+
189+ return metadata ;
190+ }
191+
192+ async function getMetadataFromIntersection ( intersection ) {
193+ const { point, object, face, faceIndex } = intersection ;
194+ const { meshFeatures, structuralMetadata } = object . userData ;
195+
196+ const barycoord = new THREE . Vector3 ( ) ;
197+ if ( face ) {
198+ const position = object . geometry . getAttribute ( 'position' ) ;
199+ const triangle = new THREE . Triangle ( ) . setFromAttributeAndIndices (
200+ position ,
201+ face . a ,
202+ face . b ,
203+ face . c ,
204+ ) ;
205+ triangle . a . applyMatrix4 ( object . matrixWorld ) ;
206+ triangle . b . applyMatrix4 ( object . matrixWorld ) ;
207+ triangle . c . applyMatrix4 ( object . matrixWorld ) ;
208+ triangle . getBarycoord ( point , barycoord ) ;
209+ } else {
210+ barycoord . set ( 0 , 0 , 0 ) ;
211+ }
212+
213+ // EXT_mesh_features
214+ const { features, featureIds } = meshFeatures ? await getMeshFeatures ( meshFeatures , {
215+ faceIndex,
216+ barycoord,
217+ } ) : { } ;
218+ const tableIndices = featureIds ?. map ( p => p . propertyTable ) ;
219+
220+ // EXT_structural_metadata
221+ const metadata = structuralMetadata ? getStructuralMetadata ( structuralMetadata , {
222+ ...intersection ,
223+ barycoord,
224+ tableIndices,
225+ features,
226+ } ) : [ ] ;
227+
228+ return metadata ;
229+ }
230+
147231class OGC3DTilesLayer extends GeometryLayer {
148232 /**
149233 * Layer for [3D Tiles](https://www.ogc.org/standard/3dtiles/) datasets.
@@ -285,7 +369,8 @@ class OGC3DTilesLayer extends GeometryLayer {
285369 this . _res ( ) ;
286370 }
287371 } ) ;
288- this . tilesRenderer . addEventListener ( 'load-model' , ( { scene } ) => {
372+ this . tilesRenderer . addEventListener ( 'load-model' , ( e ) => {
373+ const { scene } = e ;
289374 scene . traverse ( ( obj ) => {
290375 this . _assignFinalMaterial ( obj ) ;
291376 this . _assignFinalAttributes ( obj ) ;
@@ -369,10 +454,35 @@ class OGC3DTilesLayer extends GeometryLayer {
369454 this . tilesRenderer . dispose ( ) ;
370455 }
371456
457+ /**
458+ * Get the [metadata](https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata)
459+ * of the closest intersected object from a list of intersections.
460+ *
461+ * This method retrieves structured metadata stored in GLTF 2.0 assets using
462+ * the [`EXT_structural_metadata`](https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata)
463+ * extension.
464+ *
465+ * Internally, it uses the closest intersected point to index metadata
466+ * stored in property attributes and textures.
467+ *
468+ * If present in GLTF 2.0 assets, this method leverages the
469+ * [`EXT_mesh_features`](`https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features)
470+ * extension and the returned featured to index metadata stored in property tables.
471+ *
472+ * @param {Array<THREE.Intersection> } intersections
473+ * @returns {Promise<Object | null> } - the intersected object's metadata
474+ */
475+ async getMetadataFromIntersections ( intersections ) {
476+ if ( ! intersections . length ) { return null ; }
477+
478+ const metadata = await getMetadataFromIntersection ( intersections [ 0 ] ) ;
479+ return metadata ;
480+ }
481+
372482 /**
373483 * Get the attributes for the closest intersection from a list of
374484 * intersects.
375- * @param {Array } intersects - An array containing all
485+ * @param {Array<THREE.Intersection> } intersects - An array containing all
376486 * objects picked under mouse coordinates computed with view.pickObjectsAt(..).
377487 * @returns {Object | null } - An object containing
378488 */
0 commit comments