123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- import { I3DMLoaderBase } from '../base/I3DMLoaderBase.js';
- import { DefaultLoadingManager, Matrix4, InstancedMesh, Vector3, Quaternion } from 'three';
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
- const tempFwd = new Vector3();
- const tempUp = new Vector3();
- const tempRight = new Vector3();
- const tempPos = new Vector3();
- const tempQuat = new Quaternion();
- const tempSca = new Vector3();
- const tempMat = new Matrix4();
- export class I3DMLoader extends I3DMLoaderBase {
- constructor( manager = DefaultLoadingManager ) {
- super();
- this.manager = manager;
- }
- resolveExternalURL( url ) {
- return this.manager.resolveURL( super.resolveExternalURL( url ) );
- }
- parse( buffer ) {
- return super
- .parse( buffer )
- .then( i3dm => {
- const { featureTable, batchTable } = i3dm;
- const gltfBuffer = i3dm.glbBytes.slice().buffer;
- return new Promise( ( resolve, reject ) => {
- const fetchOptions = this.fetchOptions;
- const manager = this.manager;
- const loader = manager.getHandler( 'path.gltf' ) || new GLTFLoader( manager );
- if ( fetchOptions.credentials === 'include' && fetchOptions.mode === 'cors' ) {
- loader.setCrossOrigin( 'use-credentials' );
- }
- if ( 'credentials' in fetchOptions ) {
- loader.setWithCredentials( fetchOptions.credentials === 'include' );
- }
- if ( fetchOptions.headers ) {
- loader.setRequestHeader( fetchOptions.headers );
- }
- // GLTFLoader assumes the working path ends in a slash
- let workingPath = this.workingPath;
- if ( ! /[\\/]$/.test( workingPath ) ) {
- workingPath += '/';
- }
- loader.parse( gltfBuffer, workingPath, model => {
- const INSTANCES_LENGTH = featureTable.getData( 'INSTANCES_LENGTH' );
- const POSITION = featureTable.getData( 'POSITION', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
- const NORMAL_UP = featureTable.getData( 'NORMAL_UP', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
- const NORMAL_RIGHT = featureTable.getData( 'NORMAL_RIGHT', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
- const SCALE_NON_UNIFORM = featureTable.getData( 'SCALE_NON_UNIFORM', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
- const SCALE = featureTable.getData( 'SCALE', INSTANCES_LENGTH, 'FLOAT', 'SCALAR' );
- [
- 'RTC_CENTER',
- 'QUANTIZED_VOLUME_OFFSET',
- 'QUANTIZED_VOLUME_SCALE',
- 'EAST_NORTH_UP',
- 'POSITION_QUANTIZED',
- 'NORMAL_UP_OCT32P',
- 'NORMAL_RIGHT_OCT32P',
- ].forEach( feature => {
- if ( feature in featureTable.header ) {
- console.warn( `I3DMLoader: Unsupported FeatureTable feature "${ feature }" detected.` );
- }
- } );
- const instanceMap = new Map();
- const instances = [];
- model.scene.traverse( child => {
- if ( child.isMesh ) {
- const { geometry, material } = child;
- const instancedMesh = new InstancedMesh( geometry, material, INSTANCES_LENGTH );
- instancedMesh.position.copy( child.position );
- instancedMesh.rotation.copy( child.rotation );
- instancedMesh.scale.copy( child.scale );
- instances.push( instancedMesh );
- instanceMap.set( child, instancedMesh );
- }
- } );
- const averageVector = new Vector3();
- for ( let i = 0; i < INSTANCES_LENGTH; i ++ ) {
- averageVector.x += POSITION[ i * 3 + 0 ] / INSTANCES_LENGTH;
- averageVector.y += POSITION[ i * 3 + 1 ] / INSTANCES_LENGTH;
- averageVector.z += POSITION[ i * 3 + 2 ] / INSTANCES_LENGTH;
- }
- // replace the meshes with instanced meshes
- instanceMap.forEach( ( instancedMesh, mesh ) => {
- const parent = mesh.parent;
- if ( parent ) {
- // Mesh have no children
- parent.remove( mesh );
- parent.add( instancedMesh );
- // Center the instance around an average point to avoid jitter at large scales.
- // Transform the average vector by matrix world so we can account for any existing
- // transforms of the instanced mesh.
- instancedMesh.updateMatrixWorld();
- instancedMesh
- .position
- .copy( averageVector )
- .applyMatrix4( instancedMesh.matrixWorld );
- }
- } );
- for ( let i = 0; i < INSTANCES_LENGTH; i ++ ) {
- // position
- tempPos.set(
- POSITION[ i * 3 + 0 ] - averageVector.x,
- POSITION[ i * 3 + 1 ] - averageVector.y,
- POSITION[ i * 3 + 2 ] - averageVector.z,
- );
- // rotation
- if ( NORMAL_UP ) {
- tempUp.set(
- NORMAL_UP[ i * 3 + 0 ],
- NORMAL_UP[ i * 3 + 1 ],
- NORMAL_UP[ i * 3 + 2 ],
- );
- tempRight.set(
- NORMAL_RIGHT[ i * 3 + 0 ],
- NORMAL_RIGHT[ i * 3 + 1 ],
- NORMAL_RIGHT[ i * 3 + 2 ],
- );
- tempFwd.crossVectors( tempRight, tempUp )
- .normalize();
- tempMat.makeBasis(
- tempRight,
- tempUp,
- tempFwd,
- );
- tempQuat.setFromRotationMatrix( tempMat );
- } else {
- tempQuat.set( 0, 0, 0, 1 );
- }
- // scale
- if ( SCALE ) {
- tempSca.setScalar( SCALE[ i ] );
- } else if ( SCALE_NON_UNIFORM ) {
- tempSca.set(
- SCALE_NON_UNIFORM[ i * 3 + 0 ],
- SCALE_NON_UNIFORM[ i * 3 + 1 ],
- SCALE_NON_UNIFORM[ i * 3 + 2 ],
- );
- } else {
- tempSca.set( 1, 1, 1 );
- }
- tempMat.compose( tempPos, tempQuat, tempSca );
- for ( let j = 0, l = instances.length; j < l; j ++ ) {
- const instance = instances[ j ];
- instance.setMatrixAt( i, tempMat );
- }
- }
- model.batchTable = batchTable;
- model.featureTable = featureTable;
- model.scene.batchTable = batchTable;
- model.scene.featureTable = featureTable;
- resolve( model );
- }, reject );
- } );
- } );
- }
- }
|