B3DMLoaderBase.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // B3DM File Format
  2. // https://github.com/CesiumGS/3d-tiles/blob/master/specification/TileFormats/Batched3DModel/README.md
  3. import { arrayToString } from '../utilities/arrayToString.js';
  4. export class B3DMLoaderBase {
  5. constructor() {
  6. this.fetchOptions = {};
  7. }
  8. load( url ) {
  9. return fetch( url, this.fetchOptions )
  10. .then( res => res.arrayBuffer() )
  11. .then( buffer => this.parse( buffer ) );
  12. }
  13. parse( buffer ) {
  14. const dataView = new DataView( buffer );
  15. // 28-byte header
  16. // 4 bytes
  17. const magic =
  18. String.fromCharCode( dataView.getUint8( 0 ) ) +
  19. String.fromCharCode( dataView.getUint8( 1 ) ) +
  20. String.fromCharCode( dataView.getUint8( 2 ) ) +
  21. String.fromCharCode( dataView.getUint8( 3 ) );
  22. console.assert( magic === 'b3dm' );
  23. // 4 bytes
  24. const version = dataView.getUint32( 4, true );
  25. console.assert( version === 1 );
  26. // 4 bytes
  27. const byteLength = dataView.getUint32( 8, true );
  28. console.assert( byteLength === buffer.byteLength );
  29. // 4 bytes
  30. const featureTableJSONByteLength = dataView.getUint32( 12, true );
  31. // 4 bytes
  32. const featureTableBinaryByteLength = dataView.getUint32( 16, true );
  33. // 4 bytes
  34. const batchTableJSONByteLength = dataView.getUint32( 20, true );
  35. // 4 bytes
  36. const batchTableBinaryByteLength = dataView.getUint32( 24, true );
  37. // Feature Table
  38. const featureTableStart = 28;
  39. const jsonFeatureTableData = new Uint8Array( buffer, featureTableStart, featureTableJSONByteLength );
  40. const jsonFeatureTable = featureTableJSONByteLength === 0 ? {} : JSON.parse( arrayToString( jsonFeatureTableData ) );
  41. const featureTable = { ...jsonFeatureTable };
  42. // const binFeatureTableData = new Uint8Array( buffer, featureTableStart + featureTableJSONByteLength, featureTableBinaryByteLength );
  43. // TODO: dereference the json feature table data in to the binary array.
  44. // https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/TileFormats/FeatureTable/README.md#json-header
  45. // TODO: The feature table contains data with implicit stride and data types, which means we can't parse it into arrays
  46. // unless they are specified ahead of time?s
  47. // Batch Table
  48. const batchTableStart = featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength;
  49. const jsonBatchTableData = new Uint8Array( buffer, batchTableStart, batchTableJSONByteLength );
  50. const jsonBatchTable = batchTableJSONByteLength === 0 ? {} : JSON.parse( arrayToString( jsonBatchTableData ) );
  51. const batchTable = { ...jsonBatchTable };
  52. // dereference the json batch table data in to the binary array.
  53. // https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/TileFormats/FeatureTable/README.md#json-header
  54. // const binBatchTableData = new Uint8Array( buffer, batchTableStart + batchTableJSONByteLength, batchTableBinaryByteLength );
  55. const batchLength = jsonFeatureTable.BATCH_LENGTH;
  56. for ( const key in jsonBatchTable ) {
  57. const feature = jsonBatchTable[ key ];
  58. if ( Array.isArray( feature ) ) {
  59. batchTable[ key ] = {
  60. type: 'SCALAR',
  61. stride: 1,
  62. data: feature,
  63. };
  64. } else {
  65. let stride;
  66. let data;
  67. const arrayStart = batchTableStart + batchTableJSONByteLength;
  68. const arrayLength = batchLength * stride + feature.byteOffset;
  69. switch ( feature.type ) {
  70. case 'SCALAR':
  71. stride = 1;
  72. break;
  73. case 'VEC2':
  74. stride = 2;
  75. break;
  76. case 'VEC3':
  77. stride = 3;
  78. break;
  79. case 'VEC4':
  80. stride = 4;
  81. break;
  82. }
  83. switch ( feature.componentType ) {
  84. case 'BYTE':
  85. data = new Int8Array( buffer, arrayStart, arrayLength );
  86. break;
  87. case 'UNSIGNED_BYTE':
  88. data = new Uint8Array( buffer, arrayStart, arrayLength );
  89. break;
  90. case 'SHORT':
  91. data = new Int16Array( buffer, arrayStart, arrayLength );
  92. break;
  93. case 'UNSIGNED_SHORT':
  94. data = new Uint16Array( buffer, arrayStart, arrayLength );
  95. break;
  96. case 'INT':
  97. data = new Int32Array( buffer, arrayStart, arrayLength );
  98. break;
  99. case 'UNSIGNED_INT':
  100. data = new Uint32Array( buffer, arrayStart, arrayLength );
  101. break;
  102. case 'FLOAT':
  103. data = new Float32Array( buffer, arrayStart, arrayLength );
  104. break;
  105. case 'DOUBLE':
  106. data = new Float64Array( buffer, arrayStart, arrayLength );
  107. break;
  108. }
  109. batchTable[ key ] = {
  110. type: feature.type,
  111. stride,
  112. data,
  113. };
  114. }
  115. }
  116. const glbStart = batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength;
  117. const glbBytes = new Uint8Array( buffer, glbStart, byteLength - glbStart );
  118. // TODO: Understand how to apply the batchId semantics
  119. // https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/TileFormats/Batched3DModel/README.md#binary-gltf
  120. return {
  121. version,
  122. featureTable,
  123. batchTable,
  124. glbBytes,
  125. };
  126. }
  127. }