Explorar el Código

Add sparse accessor support toglTF loader

Gary Hsu hace 7 años
padre
commit
dc46309c68
Se han modificado 1 ficheros con 109 adiciones y 72 borrados
  1. 109 72
      loaders/src/glTF/2.0/babylon.glTFLoader.ts

+ 109 - 72
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -597,8 +597,8 @@ module BABYLON.GLTF2 {
             }
             else {
                 const accessor = GLTFLoader._GetProperty(context + "/indices", this._gltf.accessors, primitive.indices);
-                promises.push(this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(data => {
-                    babylonGeometry.setIndices(data as IndicesArray);
+                promises.push(this._loadIndicesAccessorAsync("#/accessors/" + accessor._index, accessor).then(data => {
+                    babylonGeometry.setIndices(data);
                 }));
             }
 
@@ -690,11 +690,7 @@ module BABYLON.GLTF2 {
                 }
 
                 const accessor = GLTFLoader._GetProperty(`${context}/${attribute}`, this._gltf.accessors, attributes[attribute]);
-                promises.push(this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
-                    if (!(data instanceof Float32Array)) {
-                        throw new Error(`${context}: Morph target accessor must have float data`);
-                    }
-
+                promises.push(this._loadFloatAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
                     setData(babylonVertexBuffer, data);
                 }));
             };
@@ -818,9 +814,7 @@ module BABYLON.GLTF2 {
             }
 
             const accessor = GLTFLoader._GetProperty(`${context}/inverseBindMatrices`, this._gltf.accessors, skin.inverseBindMatrices);
-            return this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
-                return data as Float32Array;
-            });
+            return this._loadFloatAccessorAsync(`#/accessors/${accessor._index}`, accessor);
         }
 
         private _updateBoneMatrices(babylonSkeleton: Skeleton, inverseBindMatricesData: Nullable<Float32Array>): void {
@@ -1053,24 +1047,17 @@ module BABYLON.GLTF2 {
                 }
             }
 
-            let inputData: Nullable<Float32Array>;
-            let outputData: Nullable<Float32Array>;
-
             const inputAccessor = GLTFLoader._GetProperty(`${context}/input`, this._gltf.accessors, sampler.input);
             const outputAccessor = GLTFLoader._GetProperty(`${context}/output`, this._gltf.accessors, sampler.output);
 
             sampler._data = Promise.all([
-                this._loadAccessorAsync(`#/accessors/${inputAccessor._index}`, inputAccessor).then(data => {
-                    inputData = data as Float32Array;
-                }),
-                this._loadAccessorAsync(`#/accessors/${outputAccessor._index}`, outputAccessor).then(data => {
-                    outputData = data as Float32Array;
-                })
-            ]).then(() => {
+                this._loadFloatAccessorAsync(`#/accessors/${inputAccessor._index}`, inputAccessor),
+                this._loadFloatAccessorAsync(`#/accessors/${outputAccessor._index}`, outputAccessor)
+            ]).then(([inputData, outputData]) => {
                 return {
-                    input: inputData!,
+                    input: inputData,
                     interpolation: interpolation,
-                    output: outputData!,
+                    output: outputData,
                 };
             });
 
@@ -1097,8 +1084,8 @@ module BABYLON.GLTF2 {
                 return bufferView._data;
             }
 
-            const buffer = GLTFLoader._GetProperty(context + "/buffer", this._gltf.buffers, bufferView.buffer);
-            bufferView._data = this._loadBufferAsync("#/buffers/" + buffer._index, buffer).then(data => {
+            const buffer = GLTFLoader._GetProperty(`${context}/buffer`, this._gltf.buffers, bufferView.buffer);
+            bufferView._data = this._loadBufferAsync(`#/buffers/${buffer._index}`, buffer).then(data => {
                 try {
                     return new Uint8Array(data.buffer, data.byteOffset + (bufferView.byteOffset || 0), bufferView.byteLength);
                 }
@@ -1110,52 +1097,79 @@ module BABYLON.GLTF2 {
             return bufferView._data;
         }
 
-        private _loadAccessorAsync(context: string, accessor: _ILoaderAccessor): Promise<ArrayBufferView> {
-            if (accessor.sparse) {
-                throw new Error(`${context}: Sparse accessors are not currently supported`);
+        private _loadIndicesAccessorAsync(context: string, accessor: _ILoaderAccessor): Promise<IndicesArray> {
+            if (accessor.type !== AccessorType.SCALAR) {
+                throw new Error(`${context}: Invalid type ${accessor.type}`);
+            }
+
+            if (accessor.componentType !== AccessorComponentType.UNSIGNED_BYTE &&
+                accessor.componentType !== AccessorComponentType.UNSIGNED_SHORT &&
+                accessor.componentType !== AccessorComponentType.UNSIGNED_INT) {
+                throw new Error(`${context}: Invalid component type ${accessor.componentType}`);
             }
 
             if (accessor._data) {
-                return accessor._data;
+                return accessor._data as Promise<IndicesArray>;
             }
 
-            const bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, accessor.bufferView);
-            accessor._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView).then(data => {
-                const buffer = data.buffer;
-                const byteOffset = data.byteOffset + (accessor.byteOffset || 0);
-                const length = GLTFLoader._GetNumComponents(context, accessor.type) * accessor.count;
+            const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
+            accessor._data = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(data => {
+                return GLTFLoader._GetTypedArray(context, accessor.componentType, data, accessor.byteOffset, accessor.count);
+            });
 
-                try {
-                    switch (accessor.componentType) {
-                        case AccessorComponentType.BYTE: {
-                            return new Int8Array(buffer, byteOffset, length);
-                        }
-                        case AccessorComponentType.UNSIGNED_BYTE: {
-                            return new Uint8Array(buffer, byteOffset, length);
-                        }
-                        case AccessorComponentType.SHORT: {
-                            return new Int16Array(buffer, byteOffset, length);
-                        }
-                        case AccessorComponentType.UNSIGNED_SHORT: {
-                            return new Uint16Array(buffer, byteOffset, length);
-                        }
-                        case AccessorComponentType.UNSIGNED_INT: {
-                            return new Uint32Array(buffer, byteOffset, length);
-                        }
-                        case AccessorComponentType.FLOAT: {
-                            return new Float32Array(buffer, byteOffset, length);
-                        }
-                        default: {
-                            throw new Error(`${context}: Invalid accessor component type ${accessor.componentType}`);
+            return accessor._data as Promise<IndicesArray>;
+        }
+
+        private _loadFloatAccessorAsync(context: string, accessor: _ILoaderAccessor): Promise<Float32Array> {
+            // TODO: support normalized and stride
+
+            if (accessor.componentType !== AccessorComponentType.FLOAT) {
+                throw new Error(`Invalid component type ${accessor.componentType}`);
+            }
+
+            if (accessor._data) {
+                return accessor._data as Promise<Float32Array>;
+            }
+
+            const numComponents = GLTFLoader._GetNumComponents(context, accessor.type);
+            const length = numComponents * accessor.count;
+
+            if (accessor.bufferView == undefined) {
+                accessor._data = Promise.resolve(new Float32Array(length));
+            }
+            else {
+                const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
+                accessor._data = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(data => {
+                    return GLTFLoader._GetTypedArray(context, accessor.componentType, data, accessor.byteOffset, length);
+                });
+            }
+
+            if (accessor.sparse) {
+                const sparse = accessor.sparse;
+                accessor._data = accessor._data.then((data: Float32Array) => {
+                    const indicesBufferView = GLTFLoader._GetProperty(`${context}/sparse/indices/bufferView`, this._gltf.bufferViews, sparse.indices.bufferView);
+                    const valuesBufferView = GLTFLoader._GetProperty(`${context}/sparse/values/bufferView`, this._gltf.bufferViews, sparse.values.bufferView);
+                    return Promise.all([
+                        this._loadBufferViewAsync(`#/bufferViews/${indicesBufferView._index}`, indicesBufferView),
+                        this._loadBufferViewAsync(`#/bufferViews/${valuesBufferView._index}`, valuesBufferView)
+                    ]).then(([indicesData, valuesData]) => {
+                        const indices = GLTFLoader._GetTypedArray(`${context}/sparse/indices`, sparse.indices.componentType, indicesData, sparse.indices.byteOffset, sparse.count) as IndicesArray;
+                        const values = GLTFLoader._GetTypedArray(`${context}/sparse/values`, accessor.componentType, valuesData, sparse.values.byteOffset, numComponents * sparse.count) as Float32Array;
+
+                        let valuesIndex = 0;
+                        for (let indicesIndex = 0; indicesIndex < indices.length; indicesIndex++) {
+                            let dataIndex = indices[indicesIndex] * numComponents;
+                            for (let componentIndex = 0; componentIndex < numComponents; componentIndex++) {
+                                data[dataIndex++] = values[valuesIndex++];
+                            }
                         }
-                    }
-                }
-                catch (e) {
-                    throw new Error(`${context}: ${e}`);
-                }
-            });
 
-            return accessor._data;
+                        return data;
+                    });
+                });
+            }
+
+            return accessor._data as Promise<Float32Array>;
         }
 
         /** @ignore */
@@ -1172,20 +1186,23 @@ module BABYLON.GLTF2 {
         }
 
         private _loadVertexAccessorAsync(context: string, accessor: _ILoaderAccessor, kind: string): Promise<VertexBuffer> {
-            if (accessor.sparse) {
-                throw new Error(`${context}: Sparse accessors are not currently supported`);
-            }
-
             if (accessor._babylonVertexBuffer) {
                 return accessor._babylonVertexBuffer;
             }
 
-            const bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, accessor.bufferView);
-            accessor._babylonVertexBuffer = this._loadVertexBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView, kind).then(buffer => {
-                const size = GLTFLoader._GetNumComponents(context, accessor.type);
-                return new VertexBuffer(this._babylonScene.getEngine(), buffer, kind, false, false, bufferView.byteStride,
-                    false, accessor.byteOffset, size, accessor.componentType, accessor.normalized, true);
-            });
+            if (accessor.sparse) {
+                accessor._babylonVertexBuffer = this._loadFloatAccessorAsync(context, accessor).then(data => {
+                    return new VertexBuffer(this._babylonScene.getEngine(), data, kind, false);
+                });
+            }
+            else {
+                const bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, accessor.bufferView);
+                accessor._babylonVertexBuffer = this._loadVertexBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView, kind).then(buffer => {
+                    const size = GLTFLoader._GetNumComponents(context, accessor.type);
+                    return new VertexBuffer(this._babylonScene.getEngine(), buffer, kind, false, false, bufferView.byteStride,
+                        false, accessor.byteOffset, size, accessor.componentType, accessor.normalized, true);
+                });
+            }
 
             return accessor._babylonVertexBuffer;
         }
@@ -1557,6 +1574,26 @@ module BABYLON.GLTF2 {
             }
         }
 
+        private static _GetTypedArray(context: string, componentType: AccessorComponentType, bufferView: ArrayBufferView, byteOffset: number | undefined, length: number): ArrayBufferView {
+            const buffer = bufferView.buffer;
+            byteOffset = bufferView.byteOffset + (byteOffset || 0);
+
+            try {
+                switch (componentType) {
+                    case AccessorComponentType.BYTE: return new Int8Array(buffer, byteOffset, length);
+                    case AccessorComponentType.UNSIGNED_BYTE: return new Uint8Array(buffer, byteOffset, length);
+                    case AccessorComponentType.SHORT: return new Int16Array(buffer, byteOffset, length);
+                    case AccessorComponentType.UNSIGNED_SHORT: return new Uint16Array(buffer, byteOffset, length);
+                    case AccessorComponentType.UNSIGNED_INT: return new Uint32Array(buffer, byteOffset, length);
+                    case AccessorComponentType.FLOAT: return new Float32Array(buffer, byteOffset, length);
+                    default: throw new Error(`Invalid component type ${componentType}`);
+                }
+            }
+            catch (e) {
+                throw new Error(`${context}: ${e}`);
+            }
+        }
+
         private static _GetNumComponents(context: string, type: string): number {
             switch (type) {
                 case "SCALAR": return 1;