I3DMLoaderBase.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // I3DM File Format
  2. // https://github.com/CesiumGS/3d-tiles/blob/master/specification/TileFormats/Instanced3DModel/README.md
  3. import { FeatureTable, BatchTable } from '../utilities/FeatureTable.js';
  4. import { arrayToString } from '../utilities/arrayToString.js';
  5. export class I3DMLoaderBase {
  6. constructor() {
  7. this.fetchOptions = {};
  8. this.workingPath = '';
  9. }
  10. resolveExternalURL( url ) {
  11. if ( /^[^\\/]/ ) {
  12. return this.workingPath + '/' + url;
  13. } else {
  14. return url;
  15. }
  16. }
  17. load( url ) {
  18. return fetch( url, this.fetchOptions )
  19. .then( res => {
  20. if ( ! res.ok ) {
  21. throw new Error( `Failed to load file "${ url }" with status ${ res.status } : ${ res.statusText }` );
  22. }
  23. return res.arrayBuffer();
  24. } )
  25. .then( buffer => {
  26. if ( this.workingPath === '' ) {
  27. const splits = url.split( /\\\//g );
  28. splits.pop();
  29. this.workingPath = splits.join( '/' );
  30. }
  31. return this.parse( buffer );
  32. } );
  33. }
  34. parse( buffer ) {
  35. const dataView = new DataView( buffer );
  36. // 32-byte header
  37. // 4 bytes
  38. const magic =
  39. String.fromCharCode( dataView.getUint8( 0 ) ) +
  40. String.fromCharCode( dataView.getUint8( 1 ) ) +
  41. String.fromCharCode( dataView.getUint8( 2 ) ) +
  42. String.fromCharCode( dataView.getUint8( 3 ) );
  43. console.assert( magic === 'i3dm' );
  44. // 4 bytes
  45. const version = dataView.getUint32( 4, true );
  46. console.assert( version === 1 );
  47. // 4 bytes
  48. const byteLength = dataView.getUint32( 8, true );
  49. console.assert( byteLength === buffer.byteLength );
  50. // 4 bytes
  51. const featureTableJSONByteLength = dataView.getUint32( 12, true );
  52. // 4 bytes
  53. const featureTableBinaryByteLength = dataView.getUint32( 16, true );
  54. // 4 bytes
  55. const batchTableJSONByteLength = dataView.getUint32( 20, true );
  56. // 4 bytes
  57. const batchTableBinaryByteLength = dataView.getUint32( 24, true );
  58. // 4 bytes
  59. const gltfFormat = dataView.getUint32( 28, true );
  60. // Feature Table
  61. const featureTableStart = 32;
  62. const featureTableBuffer = buffer.slice(
  63. featureTableStart,
  64. featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength,
  65. );
  66. const featureTable = new FeatureTable(
  67. featureTableBuffer,
  68. 0,
  69. featureTableJSONByteLength,
  70. featureTableBinaryByteLength,
  71. );
  72. // Batch Table
  73. const batchTableStart = featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength;
  74. const batchTableBuffer = buffer.slice(
  75. batchTableStart,
  76. batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength,
  77. );
  78. const batchTable = new BatchTable(
  79. batchTableBuffer,
  80. featureTable.getData( 'INSTANCES_LENGTH' ),
  81. 0,
  82. batchTableJSONByteLength,
  83. batchTableBinaryByteLength,
  84. );
  85. const glbStart = batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength;
  86. const bodyBytes = new Uint8Array( buffer, glbStart, byteLength - glbStart );
  87. let glbBytes = null;
  88. let promise = null;
  89. if ( gltfFormat ) {
  90. glbBytes = bodyBytes;
  91. promise = Promise.resolve();
  92. } else {
  93. const externalUri = this.resolveExternalURL( arrayToString( bodyBytes ) );
  94. promise = fetch( externalUri, this.fetchOptions )
  95. .then( res => {
  96. if ( ! res.ok ) {
  97. throw new Error( `I3DMLoaderBase : Failed to load file "${ externalUri }" with status ${ res.status } : ${ res.statusText }` );
  98. }
  99. return res.arrayBuffer();
  100. } )
  101. .then( buffer => {
  102. glbBytes = new Uint8Array( buffer );
  103. } );
  104. }
  105. return promise.then( () => {
  106. return {
  107. version,
  108. featureTable,
  109. batchTable,
  110. glbBytes,
  111. };
  112. } );
  113. }
  114. }