@@ -147,13 +147,37 @@ const gltfToEngineSemanticMap = {
147
147
'TEXCOORD_1' : SEMANTIC_TEXCOORD1
148
148
} ;
149
149
150
+ // returns a function for dequantizing the data type
151
+ const getDequantizeFunc = ( srcType ) => {
152
+ // see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data
153
+ switch ( srcType ) {
154
+ case TYPE_INT8 : return ( x ) => Math . max ( x / 127.0 , - 1.0 ) ;
155
+ case TYPE_UINT8 : return ( x ) => x / 255.0 ;
156
+ case TYPE_INT16 : return ( x ) => Math . max ( x / 32767.0 , - 1.0 ) ;
157
+ case TYPE_UINT16 : return ( x ) => x / 65535.0 ;
158
+ default : return ( x ) => x ;
159
+ }
160
+ } ;
161
+
162
+ // dequantize an array of data
163
+ const dequantizeArray = function ( dstArray , srcArray , srcType ) {
164
+ const convFunc = getDequantizeFunc ( srcType ) ;
165
+ const len = srcArray . length ;
166
+ for ( let i = 0 ; i < len ; ++ i ) {
167
+ dstArray [ i ] = convFunc ( srcArray [ i ] ) ;
168
+ }
169
+ return dstArray ;
170
+ } ;
171
+
150
172
// get accessor data, making a copy and patching in the case of a sparse accessor
151
- const getAccessorData = function ( gltfAccessor , bufferViews ) {
173
+ const getAccessorData = function ( gltfAccessor , bufferViews , flatten = false ) {
152
174
const numComponents = getNumComponents ( gltfAccessor . type ) ;
153
175
const dataType = getComponentDataType ( gltfAccessor . componentType ) ;
154
176
if ( ! dataType ) {
155
177
return null ;
156
178
}
179
+
180
+ const bufferView = bufferViews [ gltfAccessor . bufferView ] ;
157
181
let result ;
158
182
159
183
if ( gltfAccessor . sparse ) {
@@ -165,15 +189,15 @@ const getAccessorData = function (gltfAccessor, bufferViews) {
165
189
count : sparse . count ,
166
190
type : "SCALAR"
167
191
} ;
168
- const indices = getAccessorData ( Object . assign ( indicesAccessor , sparse . indices ) , bufferViews ) ;
192
+ const indices = getAccessorData ( Object . assign ( indicesAccessor , sparse . indices ) , bufferViews , true ) ;
169
193
170
194
// data values data
171
195
const valuesAccessor = {
172
196
count : sparse . count ,
173
197
type : gltfAccessor . scalar ,
174
198
componentType : gltfAccessor . componentType
175
199
} ;
176
- const values = getAccessorData ( Object . assign ( valuesAccessor , sparse . values ) , bufferViews ) ;
200
+ const values = getAccessorData ( Object . assign ( valuesAccessor , sparse . values ) , bufferViews , true ) ;
177
201
178
202
// get base data
179
203
if ( gltfAccessor . hasOwnProperty ( 'bufferView' ) ) {
@@ -185,7 +209,7 @@ const getAccessorData = function (gltfAccessor, bufferViews) {
185
209
type : gltfAccessor . type
186
210
} ;
187
211
// make a copy of the base data since we'll patch the values
188
- result = getAccessorData ( baseAccessor , bufferViews ) . slice ( ) ;
212
+ result = getAccessorData ( baseAccessor , bufferViews , true ) . slice ( ) ;
189
213
} else {
190
214
// there is no base data, create empty 0'd out data
191
215
result = new dataType ( gltfAccessor . count * numComponents ) ;
@@ -197,8 +221,23 @@ const getAccessorData = function (gltfAccessor, bufferViews) {
197
221
result [ targetIndex * numComponents + j ] = values [ i * numComponents + j ] ;
198
222
}
199
223
}
224
+ } else if ( flatten && bufferView . hasOwnProperty ( 'byteStride' ) ) {
225
+ // flatten stridden data
226
+ const bytesPerElement = numComponents * dataType . BYTES_PER_ELEMENT ;
227
+ const storage = new ArrayBuffer ( gltfAccessor . count * bytesPerElement ) ;
228
+ const tmpArray = new Uint8Array ( storage ) ;
229
+
230
+ let dstOffset = 0 ;
231
+ for ( let i = 0 ; i < gltfAccessor . count ; ++ i ) {
232
+ // no need to add bufferView.byteOffset because accessor takes this into account
233
+ let srcOffset = ( gltfAccessor . byteOffset || 0 ) + i * bufferView . byteStride ;
234
+ for ( let b = 0 ; b < bytesPerElement ; ++ b ) {
235
+ tmpArray [ dstOffset ++ ] = bufferView [ srcOffset ++ ] ;
236
+ }
237
+ }
238
+
239
+ result = new dataType ( storage ) ;
200
240
} else {
201
- const bufferView = bufferViews [ gltfAccessor . bufferView ] ;
202
241
result = new dataType ( bufferView . buffer ,
203
242
bufferView . byteOffset + ( gltfAccessor . byteOffset || 0 ) ,
204
243
gltfAccessor . count * numComponents ) ;
@@ -207,6 +246,42 @@ const getAccessorData = function (gltfAccessor, bufferViews) {
207
246
return result ;
208
247
} ;
209
248
249
+ // get accessor data as (unnormalized, unquantized) Float32 data
250
+ const getAccessorDataFloat32 = function ( gltfAccessor , bufferViews ) {
251
+ const data = getAccessorData ( gltfAccessor , bufferViews , true ) ;
252
+ if ( data instanceof Float32Array || ! gltfAccessor . normalized ) {
253
+ // if the source data is quantized (say to int16), but not normalized
254
+ // then reading the values of the array is the same whether the values
255
+ // are stored as float32 or int16. so probably no need to convert to
256
+ // float32.
257
+ return data ;
258
+ }
259
+
260
+ const float32Data = new Float32Array ( data . length ) ;
261
+ dequantizeArray ( float32Data , data , getComponentType ( gltfAccessor . componentType ) ) ;
262
+ return float32Data ;
263
+ } ;
264
+
265
+ // returns a dequantized bounding box for the accessor
266
+ const getAccessorBoundingBox = function ( gltfAccessor ) {
267
+ let min = gltfAccessor . min ;
268
+ let max = gltfAccessor . max ;
269
+ if ( ! min || ! max ) {
270
+ return null ;
271
+ }
272
+
273
+ if ( gltfAccessor . normalized ) {
274
+ const ctype = getComponentType ( gltfAccessor . componentType ) ;
275
+ min = dequantizeArray ( [ ] , min , ctype ) ;
276
+ max = dequantizeArray ( [ ] , max , ctype ) ;
277
+ }
278
+
279
+ return new BoundingBox (
280
+ new Vec3 ( ( max [ 0 ] + min [ 0 ] ) * 0.5 , ( max [ 1 ] + min [ 1 ] ) * 0.5 , ( max [ 2 ] + min [ 2 ] ) * 0.5 ) ,
281
+ new Vec3 ( ( max [ 0 ] - min [ 0 ] ) * 0.5 , ( max [ 1 ] - min [ 1 ] ) * 0.5 , ( max [ 2 ] - min [ 2 ] ) * 0.5 )
282
+ ) ;
283
+ } ;
284
+
210
285
const getPrimitiveType = function ( primitive ) {
211
286
if ( ! primitive . hasOwnProperty ( 'mode' ) ) {
212
287
return PRIMITIVE_TRIANGLES ;
@@ -619,7 +694,7 @@ const createSkin = function (device, gltfSkin, accessors, bufferViews, nodes, gl
619
694
const ibp = [ ] ;
620
695
if ( gltfSkin . hasOwnProperty ( 'inverseBindMatrices' ) ) {
621
696
const inverseBindMatrices = gltfSkin . inverseBindMatrices ;
622
- const ibmData = getAccessorData ( accessors [ inverseBindMatrices ] , bufferViews ) ;
697
+ const ibmData = getAccessorData ( accessors [ inverseBindMatrices ] , bufferViews , true ) ;
623
698
const ibmValues = [ ] ;
624
699
625
700
for ( i = 0 ; i < numJoints ; i ++ ) {
@@ -748,7 +823,7 @@ const createMesh = function (device, gltfMesh, accessors, bufferViews, callback,
748
823
749
824
// if mesh was not constructed from draco data, use uncompressed
750
825
if ( ! vertexBuffer ) {
751
- indices = primitive . hasOwnProperty ( 'indices' ) ? getAccessorData ( accessors [ primitive . indices ] , bufferViews ) : null ;
826
+ indices = primitive . hasOwnProperty ( 'indices' ) ? getAccessorData ( accessors [ primitive . indices ] , bufferViews , true ) : null ;
752
827
vertexBuffer = createVertexBuffer ( device , primitive . attributes , indices , accessors , bufferViews , flipV , vertexBufferDict ) ;
753
828
primitiveType = getPrimitiveType ( primitive ) ;
754
829
}
@@ -799,13 +874,7 @@ const createMesh = function (device, gltfMesh, accessors, bufferViews, callback,
799
874
mesh . materialIndex = primitive . material ;
800
875
801
876
let accessor = accessors [ primitive . attributes . POSITION ] ;
802
- const min = accessor . min ;
803
- const max = accessor . max ;
804
- const aabb = new BoundingBox (
805
- new Vec3 ( ( max [ 0 ] + min [ 0 ] ) / 2 , ( max [ 1 ] + min [ 1 ] ) / 2 , ( max [ 2 ] + min [ 2 ] ) / 2 ) ,
806
- new Vec3 ( ( max [ 0 ] - min [ 0 ] ) / 2 , ( max [ 1 ] - min [ 1 ] ) / 2 , ( max [ 2 ] - min [ 2 ] ) / 2 )
807
- ) ;
808
- mesh . aabb = aabb ;
877
+ mesh . aabb = getAccessorBoundingBox ( accessor ) ;
809
878
810
879
// morph targets
811
880
if ( canUseMorph && primitive . hasOwnProperty ( 'targets' ) ) {
@@ -816,18 +885,16 @@ const createMesh = function (device, gltfMesh, accessors, bufferViews, callback,
816
885
817
886
if ( target . hasOwnProperty ( 'POSITION' ) ) {
818
887
accessor = accessors [ target . POSITION ] ;
819
- options . deltaPositions = getAccessorData ( accessor , bufferViews ) ;
820
- options . deltaPositionsType = getComponentType ( accessor . componentType ) ;
821
- if ( accessor . hasOwnProperty ( 'min' ) && accessor . hasOwnProperty ( 'max' ) ) {
822
- options . aabb = new BoundingBox ( ) ;
823
- options . aabb . setMinMax ( new Vec3 ( accessor . min ) , new Vec3 ( accessor . max ) ) ;
824
- }
888
+ options . deltaPositions = getAccessorDataFloat32 ( accessor , bufferViews ) ;
889
+ options . deltaPositionsType = TYPE_FLOAT32 ;
890
+ options . aabb = getAccessorBoundingBox ( accessor ) ;
825
891
}
826
892
827
893
if ( target . hasOwnProperty ( 'NORMAL' ) ) {
828
894
accessor = accessors [ target . NORMAL ] ;
829
- options . deltaNormals = getAccessorData ( accessor , bufferViews ) ;
830
- options . deltaNormalsType = getComponentType ( accessor . componentType ) ;
895
+ // NOTE: the morph targets can't currently accept quantized normals
896
+ options . deltaNormals = getAccessorDataFloat32 ( accessor , bufferViews ) ;
897
+ options . deltaNormalsType = TYPE_FLOAT32 ;
831
898
}
832
899
833
900
// name if specified
@@ -1223,9 +1290,7 @@ const createAnimation = function (gltfAnimation, animationIndex, gltfAccessors,
1223
1290
1224
1291
// create animation data block for the accessor
1225
1292
const createAnimData = function ( gltfAccessor ) {
1226
- const data = getAccessorData ( gltfAccessor , bufferViews ) ;
1227
- // TODO: this assumes data is tightly packed, handle the case data is interleaved
1228
- return new AnimData ( getNumComponents ( gltfAccessor . type ) , new data . constructor ( data ) ) ;
1293
+ return new AnimData ( getNumComponents ( gltfAccessor . type ) , getAccessorDataFloat32 ( gltfAccessor , bufferViews ) ) ;
1229
1294
} ;
1230
1295
1231
1296
const interpMap = {
0 commit comments