import { MapMeshBasicMaterial , MapMeshStandardMaterial } from "@here/harp-materials" ; import { LoggerManager } from "@here/harp-utils" ; import * as THREE from "three" ; import { getFeatureDataSize , TileFeatureData } from "../Tile" ; import { LodMesh } from "./LodMesh" ; const MINIMUM_OBJECT3D_SIZE_ESTIMATION = 1000 ; const MINIMUM_ATTRIBUTE_SIZE_ESTIMATION = 56 ; const logger = LoggerManager . instance . create ( "Object3DUtils" ) ; internal export namespace Object3DUtils { /**
     * Describes estimated usage of memory on heap and GPU.
     */ export interface MemoryUsage { heapSize : number ; gpuSize : number ; } function estimateTextureSize ( texture , objectSize , visitedObjects ) { if ( texture === null || texture === undefined || texture . image === undefined || texture . image === null ) { return ; } if ( texture . uuid !== undefined && visitedObjects . get ( texture . uuid ) === true ) { return ; } visitedObjects . set ( texture . uuid , true ) ; const image = texture . image ; const imageBytes = 4 * image . width * image . height ; objectSize . heapSize += imageBytes ; objectSize . gpuSize += imageBytes ; } function estimateMaterialSize ( material , objectSize , visitedObjects ) { if ( material . uuid !== undefined && visitedObjects . get ( material . uuid ) === true ) { return ; } visitedObjects . set ( material . uuid , true ) ; if ( material instanceof THREE . RawShaderMaterial || material instanceof THREE . ShaderMaterial ) { const rawMaterial = material ; for ( const name in rawMaterial . uniforms ) { if ( rawMaterial . uniforms [ name ] !== undefined ) { const uniform = rawMaterial . uniforms [ name ] ; if ( uniform instanceof THREE . Texture ) { estimateTextureSize ( uniform , objectSize , visitedObjects ) ; } } } } else if ( material instanceof THREE . MeshBasicMaterial || material instanceof MapMeshBasicMaterial ) { const meshMaterial = material ; estimateTextureSize ( meshMaterial . map , objectSize , visitedObjects ) ; estimateTextureSize ( meshMaterial . aoMap , objectSize , visitedObjects ) ; estimateTextureSize ( meshMaterial . specularMap , objectSize , visitedObjects ) ; estimateTextureSize ( meshMaterial . alphaMap , objectSize , visitedObjects ) ; estimateTextureSize ( meshMaterial . envMap , objectSize , visitedObjects ) ; } else if ( material instanceof MapMeshStandardMaterial ) { const standardMaterial = material ; estimateTextureSize ( standardMaterial . map , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . lightMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . aoMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . emissiveMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . bumpMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . normalMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . displacementMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . roughnessMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . metalnessMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . alphaMap , objectSize , visitedObjects ) ; estimateTextureSize ( standardMaterial . envMap , objectSize , visitedObjects ) ; } else if ( material instanceof THREE . LineBasicMaterial || material instanceof THREE . LineDashedMaterial || material instanceof THREE . PointsMaterial ) { } else { logger . warn ( "estimateMeshSize: unidentified material: " , material ) ; } } function estimateAttributeSize ( attribute , attrName , objectSize , visitedObjects ) { if ( attribute . uuid === undefined ) { attribute . uuid = THREE . MathUtils . generateUUID ( ) ; } if ( visitedObjects . get ( attribute . uuid ) === true ) { return ; } visitedObjects . set ( attribute . uuid , true ) ; let attrBytes = 0 ; let bytesPerElement = 4 ; if ( attribute . array . BYTES_PER_ELEMENT !== undefined ) { bytesPerElement = attribute . array . BYTES_PER_ELEMENT ; } if ( attribute instanceof THREE . InterleavedBufferAttribute || attribute instanceof THREE . BufferAttribute ) { attrBytes = bytesPerElement * attribute . count * attribute . itemSize ; } else { logger . warn ( "estimateMeshSize: unidentified attribute: " , attrName ) ; } objectSize . heapSize += attrBytes + MINIMUM_ATTRIBUTE_SIZE_ESTIMATION ; objectSize . gpuSize += attrBytes ; } function estimateGeometrySize ( geometry , objectSize , visitedObjects ) { const isNewObject = geometry . uuid === undefined || visitedObjects . get ( geometry . uuid ) !== true ; if ( ! isNewObject ) { return ; } visitedObjects . set ( geometry . uuid , true ) ; if ( geometry === undefined ) { return ; } const attributes = geometry . attributes ; if ( attributes === undefined ) { logger . warn ( "estimateGeometrySize: unidentified geometry: " , geometry ) ; return ; } for ( const property in attributes ) { if ( attributes [ property ] !== undefined ) { estimateAttributeSize ( attributes [ property ] , property , objectSize , visitedObjects ) ; } } if ( geometry . index !== null ) { estimateAttributeSize ( geometry . index , "index" , objectSize , visitedObjects ) ; } } function estimateMeshSize ( object , objectSize , visitedObjects ) { if ( ! object . isObject3D || object instanceof THREE . Scene ) { return ; } if ( object . uuid !== undefined && visitedObjects . get ( object . uuid ) === true ) { return ; } visitedObjects . set ( object . uuid , true ) ; if ( ( object as any ) . isMesh || ( object as any ) . isLine || ( object as any ) . isPoints ) { let heapSize = MINIMUM_OBJECT3D_SIZE_ESTIMATION ; const gpuSize = 0 ; const mesh = object as LodMesh ; if ( mesh . material !== undefined ) { if ( Array . isArray ( mesh . material ) ) { const materials = mesh . material as THREE . Material [ ] ; for ( const material of materials ) { estimateMaterialSize ( material , objectSize , visitedObjects ) ; } } else { const material = mesh . material as THREE . Material ; estimateMaterialSize ( material , objectSize , visitedObjects ) ; } } if ( mesh . geometries !== undefined ) { for ( const geometry of mesh . geometries ) { estimateGeometrySize ( geometry , objectSize , visitedObjects ) ; } } else if ( mesh . geometry !== undefined ) { estimateGeometrySize ( mesh . geometry , objectSize , visitedObjects ) ; } const featureData = object . userData !== undefined ? ( object . userData . feature as TileFeatureData ) : undefined ; if ( featureData !== undefined ) { heapSize += getFeatureDataSize ( featureData ) ; } objectSize . heapSize += heapSize ; objectSize . gpuSize += gpuSize ; } else { logger . warn ( "estimateMeshSize: unidentified object" , object ) ; } } param object param size - The  MemoryUsage to update.
     *  param visitedObjects returns export function estimateSize ( object , parentSize ? , visitedObjects ? ) { const size = parentSize !== undefined ? parentSize : { heapSize : 0 , gpuSize : 0 } ; if ( visitedObjects === undefined ) { visitedObjects = new Map ( ) ; } estimateMeshSize ( object , size , visitedObjects ) ; if ( object . children . length > 0 ) { for ( const child of object . children ) { estimateSize ( child , size , visitedObjects ) ; } } return size ; } }