B3DMLoaderBase.js 4.9 KB

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