I3DMLoader.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import { I3DMLoaderBase } from '../base/I3DMLoaderBase.js';
  2. import { DefaultLoadingManager, Matrix4, InstancedMesh, Vector3, Quaternion } from 'three';
  3. import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
  4. const tempFwd = new Vector3();
  5. const tempUp = new Vector3();
  6. const tempRight = new Vector3();
  7. const tempPos = new Vector3();
  8. const tempQuat = new Quaternion();
  9. const tempSca = new Vector3();
  10. const tempMat = new Matrix4();
  11. export class I3DMLoader extends I3DMLoaderBase {
  12. constructor( manager = DefaultLoadingManager ) {
  13. super();
  14. this.manager = manager;
  15. }
  16. resolveExternalURL( url ) {
  17. return this.manager.resolveURL( super.resolveExternalURL( url ) );
  18. }
  19. parse( buffer ) {
  20. return super
  21. .parse( buffer )
  22. .then( i3dm => {
  23. const { featureTable, batchTable } = i3dm;
  24. const gltfBuffer = i3dm.glbBytes.slice().buffer;
  25. return new Promise( ( resolve, reject ) => {
  26. const fetchOptions = this.fetchOptions;
  27. const manager = this.manager;
  28. const loader = manager.getHandler( 'path.gltf' ) || new GLTFLoader( manager );
  29. if ( fetchOptions.credentials === 'include' && fetchOptions.mode === 'cors' ) {
  30. loader.setCrossOrigin( 'use-credentials' );
  31. }
  32. if ( 'credentials' in fetchOptions ) {
  33. loader.setWithCredentials( fetchOptions.credentials === 'include' );
  34. }
  35. if ( fetchOptions.headers ) {
  36. loader.setRequestHeader( fetchOptions.headers );
  37. }
  38. // GLTFLoader assumes the working path ends in a slash
  39. let workingPath = this.workingPath;
  40. if ( ! /[\\/]$/.test( workingPath ) ) {
  41. workingPath += '/';
  42. }
  43. loader.parse( gltfBuffer, workingPath, model => {
  44. const INSTANCES_LENGTH = featureTable.getData( 'INSTANCES_LENGTH' );
  45. const POSITION = featureTable.getData( 'POSITION', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
  46. const NORMAL_UP = featureTable.getData( 'NORMAL_UP', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
  47. const NORMAL_RIGHT = featureTable.getData( 'NORMAL_RIGHT', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
  48. const SCALE_NON_UNIFORM = featureTable.getData( 'SCALE_NON_UNIFORM', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
  49. const SCALE = featureTable.getData( 'SCALE', INSTANCES_LENGTH, 'FLOAT', 'SCALAR' );
  50. [
  51. 'RTC_CENTER',
  52. 'QUANTIZED_VOLUME_OFFSET',
  53. 'QUANTIZED_VOLUME_SCALE',
  54. 'EAST_NORTH_UP',
  55. 'POSITION_QUANTIZED',
  56. 'NORMAL_UP_OCT32P',
  57. 'NORMAL_RIGHT_OCT32P',
  58. ].forEach( feature => {
  59. if ( feature in featureTable.header ) {
  60. console.warn( `I3DMLoader: Unsupported FeatureTable feature "${ feature }" detected.` );
  61. }
  62. } );
  63. const instanceMap = new Map();
  64. const instances = [];
  65. model.scene.traverse( child => {
  66. if ( child.isMesh ) {
  67. const { geometry, material } = child;
  68. const instancedMesh = new InstancedMesh( geometry, material, INSTANCES_LENGTH );
  69. instancedMesh.position.copy( child.position );
  70. instancedMesh.rotation.copy( child.rotation );
  71. instancedMesh.scale.copy( child.scale );
  72. instances.push( instancedMesh );
  73. instanceMap.set( child, instancedMesh );
  74. }
  75. } );
  76. const averageVector = new Vector3();
  77. for ( let i = 0; i < INSTANCES_LENGTH; i ++ ) {
  78. averageVector.x += POSITION[ i * 3 + 0 ] / INSTANCES_LENGTH;
  79. averageVector.y += POSITION[ i * 3 + 1 ] / INSTANCES_LENGTH;
  80. averageVector.z += POSITION[ i * 3 + 2 ] / INSTANCES_LENGTH;
  81. }
  82. // replace the meshes with instanced meshes
  83. instanceMap.forEach( ( instancedMesh, mesh ) => {
  84. const parent = mesh.parent;
  85. if ( parent ) {
  86. // Mesh have no children
  87. parent.remove( mesh );
  88. parent.add( instancedMesh );
  89. // Center the instance around an average point to avoid jitter at large scales.
  90. // Transform the average vector by matrix world so we can account for any existing
  91. // transforms of the instanced mesh.
  92. instancedMesh.updateMatrixWorld();
  93. instancedMesh
  94. .position
  95. .copy( averageVector )
  96. .applyMatrix4( instancedMesh.matrixWorld );
  97. }
  98. } );
  99. for ( let i = 0; i < INSTANCES_LENGTH; i ++ ) {
  100. // position
  101. tempPos.set(
  102. POSITION[ i * 3 + 0 ] - averageVector.x,
  103. POSITION[ i * 3 + 1 ] - averageVector.y,
  104. POSITION[ i * 3 + 2 ] - averageVector.z,
  105. );
  106. // rotation
  107. if ( NORMAL_UP ) {
  108. tempUp.set(
  109. NORMAL_UP[ i * 3 + 0 ],
  110. NORMAL_UP[ i * 3 + 1 ],
  111. NORMAL_UP[ i * 3 + 2 ],
  112. );
  113. tempRight.set(
  114. NORMAL_RIGHT[ i * 3 + 0 ],
  115. NORMAL_RIGHT[ i * 3 + 1 ],
  116. NORMAL_RIGHT[ i * 3 + 2 ],
  117. );
  118. tempFwd.crossVectors( tempRight, tempUp )
  119. .normalize();
  120. tempMat.makeBasis(
  121. tempRight,
  122. tempUp,
  123. tempFwd,
  124. );
  125. tempQuat.setFromRotationMatrix( tempMat );
  126. } else {
  127. tempQuat.set( 0, 0, 0, 1 );
  128. }
  129. // scale
  130. if ( SCALE ) {
  131. tempSca.setScalar( SCALE[ i ] );
  132. } else if ( SCALE_NON_UNIFORM ) {
  133. tempSca.set(
  134. SCALE_NON_UNIFORM[ i * 3 + 0 ],
  135. SCALE_NON_UNIFORM[ i * 3 + 1 ],
  136. SCALE_NON_UNIFORM[ i * 3 + 2 ],
  137. );
  138. } else {
  139. tempSca.set( 1, 1, 1 );
  140. }
  141. tempMat.compose( tempPos, tempQuat, tempSca );
  142. for ( let j = 0, l = instances.length; j < l; j ++ ) {
  143. const instance = instances[ j ];
  144. instance.setMatrixAt( i, tempMat );
  145. }
  146. }
  147. model.batchTable = batchTable;
  148. model.featureTable = featureTable;
  149. model.scene.batchTable = batchTable;
  150. model.scene.featureTable = featureTable;
  151. resolve( model );
  152. }, reject );
  153. } );
  154. } );
  155. }
  156. }