Преглед изворни кода

Add support for interleaved, normalized, non-float buffers

Gary Hsu пре 7 година
родитељ
комит
24ef917689

+ 7 - 3
loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts

@@ -32,8 +32,8 @@ module BABYLON.GLTF2.Extensions {
             super.dispose();
         }
 
-        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> {
-            return this._loadExtensionAsync<IKHRDracoMeshCompression, VertexData>(context, primitive, (extensionContext, extension) => {
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>> {
+            return this._loadExtensionAsync<IKHRDracoMeshCompression, Geometry>(context, primitive, (extensionContext, extension) => {
                 if (primitive.mode != undefined) {
                     if (primitive.mode !== MeshPrimitiveMode.TRIANGLE_STRIP &&
                         primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
@@ -77,7 +77,11 @@ module BABYLON.GLTF2.Extensions {
                             this._dracoCompression = new DracoCompression();
                         }
 
-                        return this._dracoCompression.decodeMeshAsync(data, attributes);
+                        return this._dracoCompression.decodeMeshAsync(data, attributes).then(babylonVertexData => {
+                            const babylonGeometry = new Geometry(babylonMesh.name, this._loader._babylonScene);
+                            babylonVertexData.applyToGeometry(babylonGeometry);
+                            return babylonGeometry;
+                        });
                     }
                     catch (e) {
                         throw new Error(`${context}: ${e.message}`);

+ 87 - 155
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -7,15 +7,6 @@ module BABYLON.GLTF2 {
         _total?: number;
     }
 
-    interface TypedArrayConstructor<T extends TypedArray> {
-        readonly prototype: T;
-        new(length: number): T;
-        new(array: ArrayLike<number>): T;
-        new(buffer: ArrayBuffer, byteOffset?: number, length?: number): T;
-
-        readonly BYTES_PER_ELEMENT: number;
-    }
-
     export interface MaterialConstructor<T extends Material> {
         readonly prototype: T;
         new(name: string, scene: Scene): T;
@@ -483,10 +474,10 @@ module BABYLON.GLTF2 {
             const promises = new Array<Promise<void>>();
 
             this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
-
-            promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(babylonVertexData => {
-                new Geometry(babylonMesh.name, this._babylonScene, babylonVertexData, false, babylonMesh);
-                return this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);
+            promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(babylonGeometry => {
+                return this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry).then(() => {
+                    babylonGeometry.applyToMesh(babylonMesh);
+                });
             }));
 
             const babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
@@ -503,7 +494,7 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => {});
         }
 
-        private _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<VertexData> {
+        private _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<Geometry> {
             const promise = GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
             if (promise) {
                 return promise;
@@ -516,22 +507,15 @@ module BABYLON.GLTF2 {
 
             const promises = new Array<Promise<void>>();
 
-            const babylonVertexData = new VertexData();
+            const babylonGeometry = new Geometry(babylonMesh.name, this._babylonScene);
 
             if (primitive.indices == undefined) {
-                const positionAccessorIndex = attributes["POSITION"];
-                if (positionAccessorIndex != undefined) {
-                    const accessor = GLTFLoader._GetProperty(`${context}/attributes/POSITION`, this._gltf.accessors, positionAccessorIndex);
-                    babylonVertexData.indices = new Uint32Array(accessor.count);
-                    for (let i = 0; i < babylonVertexData.indices.length; i++) {
-                        babylonVertexData.indices[i] = i;
-                    }
-                }
+                babylonMesh.isUnIndexed = true;
             }
             else {
-                const indicesAccessor = GLTFLoader._GetProperty(`${context}/indices`, this._gltf.accessors, primitive.indices);
-                promises.push(this._loadAccessorAsync(`#/accessors/${indicesAccessor._index}`, indicesAccessor).then(data => {
-                    babylonVertexData.indices = data as IndicesArray;
+                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);
                 }));
             }
 
@@ -545,21 +529,9 @@ module BABYLON.GLTF2 {
                     babylonMesh._delayInfo.push(kind);
                 }
 
-                if (attribute === "COLOR_0") {
-                    // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
-                    babylonMesh.hasVertexAlpha = true;
-                }
-
-                const accessor = GLTFLoader._GetProperty(`${context}/attributes/${attribute}`, this._gltf.accessors, attributes[attribute]);
-
-                promises.push(this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
-                    let attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
-
-                    if (attribute === "COLOR_0" && accessor.type === "VEC3") {
-                        attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
-                    }
-
-                    babylonVertexData.set(attributeData, kind);
+                const accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, this._gltf.accessors, attributes[attribute]);
+                promises.push(this._loadVertexAccessorAsync("#/accessors/" + accessor._index, accessor, kind).then(babylonVertexBuffer => {
+                    babylonGeometry.setVerticesBuffer(babylonVertexBuffer, accessor.count);
                 }));
             };
 
@@ -573,7 +545,7 @@ module BABYLON.GLTF2 {
             loadAttribute("COLOR_0", VertexBuffer.ColorKind);
 
             return Promise.all(promises).then(() => {
-                return babylonVertexData;
+                return babylonGeometry;
             });
         }
 
@@ -597,7 +569,7 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _loadMorphTargetsAsync(context: string, primitive: IMeshPrimitive, babylonMesh: Mesh, babylonVertexData: VertexData): Promise<void> {
+        private _loadMorphTargetsAsync(context: string, primitive: IMeshPrimitive, babylonMesh: Mesh, babylonGeometry: Geometry): Promise<void> {
             if (!primitive.targets) {
                 return Promise.resolve();
             }
@@ -607,108 +579,67 @@ module BABYLON.GLTF2 {
             const morphTargetManager = babylonMesh.morphTargetManager!;
             for (let index = 0; index < morphTargetManager.numTargets; index++) {
                 const babylonMorphTarget = morphTargetManager.getTarget(index);
-                promises.push(this._loadMorphTargetVertexDataAsync(`${context}/targets/${index}`, babylonVertexData, primitive.targets[index], babylonMorphTarget));
+                promises.push(this._loadMorphTargetVertexDataAsync(`${context}/targets/${index}`, babylonGeometry, primitive.targets[index], babylonMorphTarget));
             }
 
             return Promise.all(promises).then(() => {});
         }
 
-        private _loadMorphTargetVertexDataAsync(context: string, babylonVertexData: VertexData, attributes: { [name: string]: number }, babylonMorphTarget: MorphTarget): Promise<void> {
+        private _loadMorphTargetVertexDataAsync(context: string, babylonGeometry: Geometry, attributes: { [name: string]: number }, babylonMorphTarget: MorphTarget): Promise<void> {
             const promises = new Array<Promise<void>>();
 
-            const loadAttribute = (attribute: string, setData: (data: Float32Array) => void) => {
+            const loadAttribute = (attribute: string, kind: string, setData: (babylonVertexBuffer: VertexBuffer, data: Float32Array) => void) => {
                 if (attributes[attribute] == undefined) {
                     return;
                 }
 
+                const babylonVertexBuffer = babylonGeometry.getVertexBuffer(kind);
+                if (!babylonVertexBuffer) {
+                    return;
+                }
+
                 const accessor = GLTFLoader._GetProperty(`${context}/${attribute}`, this._gltf.accessors, attributes[attribute]);
                 promises.push(this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
-                    setData(data as Float32Array);
+                    if (!(data instanceof Float32Array)) {
+                        throw new Error(`${context}: Morph target accessor must have float data`);
+                    }
+
+                    setData(babylonVertexBuffer, data);
                 }));
             };
 
-            loadAttribute("POSITION", data => {
-                if (babylonVertexData.positions) {
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] += babylonVertexData.positions[i];
-                    }
-                    babylonMorphTarget.setPositions(data);
-                }
+            loadAttribute("POSITION", VertexBuffer.PositionKind, (babylonVertexBuffer, data) => {
+                babylonVertexBuffer.forEach(data.length, (value, index) => {
+                    data[index] += value;
+                });
+
+                babylonMorphTarget.setPositions(data);
             });
 
-            loadAttribute("NORMAL", data => {
-                if (babylonVertexData.normals) {
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] += babylonVertexData.normals[i];
-                    }
-                    babylonMorphTarget.setNormals(data);
-                }
+            loadAttribute("NORMAL", VertexBuffer.NormalKind, (babylonVertexBuffer, data) => {
+                babylonVertexBuffer.forEach(data.length, (value, index) => {
+                    data[index] += value;
+                });
+
+                babylonMorphTarget.setNormals(data);
             });
 
-            loadAttribute("TANGENT", data => {
-                if (babylonVertexData.tangents) {
+            loadAttribute("TANGENT", VertexBuffer.TangentKind, (babylonVertexBuffer, data) => {
+                let dataIndex = 0;
+                babylonVertexBuffer.forEach(data.length, (value, index) => {
                     // Tangent data for morph targets is stored as xyz delta.
                     // The vertexData.tangent is stored as xyzw.
                     // So we need to skip every fourth vertexData.tangent.
-                    for (let i = 0, j = 0; i < data.length; i++) {
-                        data[i] += babylonVertexData.tangents[j++];
-                        if ((i + 1) % 3 == 0) {
-                            j++;
-                        }
+                    if (((index + 1) % 4) !== 0) {
+                        data[dataIndex++] += value;
                     }
-                    babylonMorphTarget.setTangents(data);
-                }
+                });
+                babylonMorphTarget.setTangents(data);
             });
 
             return Promise.all(promises).then(() => {});
         }
 
-        private static _ConvertToFloat32Array(context: string, accessor: ILoaderAccessor, data: TypedArray): Float32Array {
-            if (accessor.componentType == AccessorComponentType.FLOAT) {
-                return data as Float32Array;
-            }
-
-            let factor = 1;
-            if (accessor.normalized) {
-                switch (accessor.componentType) {
-                    case AccessorComponentType.UNSIGNED_BYTE: {
-                        factor = 1 / 255;
-                        break;
-                    }
-                    case AccessorComponentType.UNSIGNED_SHORT: {
-                        factor = 1 / 65535;
-                        break;
-                    }
-                    default: {
-                        throw new Error(`${context}: Invalid component type (${accessor.componentType})`);
-                    }
-                }
-            }
-
-            const result = new Float32Array(accessor.count * GLTFLoader._GetNumComponents(context, accessor.type));
-            for (let i = 0; i < result.length; i++) {
-                result[i] = data[i] * factor;
-            }
-
-            return result;
-        }
-
-        private static _ConvertVec3ToVec4(context: string, data: Float32Array): Float32Array {
-            const result = new Float32Array(data.length / 3 * 4);
-
-            let offset = 0;
-            for (let i = 0; i < result.length; i++) {
-                if ((i + 1) % 4 === 0) {
-                    result[i] = 1;
-                }
-                else {
-                    result[i] = data[offset++];
-                }
-            }
-
-            return result;
-        }
-
         private static _LoadTransform(node: ILoaderNode, babylonNode: TransformNode): void {
             let position = Vector3.Zero();
             let rotation = Quaternion.Identity();
@@ -1066,10 +997,10 @@ 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(bufferData => {
+            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(bufferData.buffer, bufferData.byteOffset + (bufferView.byteOffset || 0), bufferView.byteLength);
+                    return new Uint8Array(data.buffer, data.byteOffset + (bufferView.byteOffset || 0), bufferView.byteLength);
                 }
                 catch (e) {
                     throw new Error(`${context}: ${e.message}`);
@@ -1079,7 +1010,7 @@ module BABYLON.GLTF2 {
             return bufferView._data;
         }
 
-        private _loadAccessorAsync(context: string, accessor: ILoaderAccessor): Promise<TypedArray> {
+        private _loadAccessorAsync(context: string, accessor: ILoaderAccessor): Promise<ArrayBufferView> {
             if (accessor.sparse) {
                 throw new Error(`${context}: Sparse accessors are not currently supported`);
             }
@@ -1088,73 +1019,74 @@ module BABYLON.GLTF2 {
                 return accessor._data;
             }
 
-            const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
-            accessor._data = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(bufferViewData => {
-                const numComponents = GLTFLoader._GetNumComponents(context, accessor.type);
-                const byteOffset = accessor.byteOffset || 0;
-                const byteStride = bufferView.byteStride;
-
-                if (byteStride === 0) {
-                    Tools.Warn(`${context}: Byte stride of 0 is not valid`);
-                }
+            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;
 
                 try {
                     switch (accessor.componentType) {
                         case AccessorComponentType.BYTE: {
-                            return this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
+                            return new Int8Array(buffer, byteOffset, length);
                         }
                         case AccessorComponentType.UNSIGNED_BYTE: {
-                            return this._buildArrayBuffer(Uint8Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
+                            return new Uint8Array(buffer, byteOffset, length);
                         }
                         case AccessorComponentType.SHORT: {
-                            return this._buildArrayBuffer(Int16Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
+                            return new Int16Array(buffer, byteOffset, length);
                         }
                         case AccessorComponentType.UNSIGNED_SHORT: {
-                            return this._buildArrayBuffer(Uint16Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
+                            return new Uint16Array(buffer, byteOffset, length);
                         }
                         case AccessorComponentType.UNSIGNED_INT: {
-                            return this._buildArrayBuffer(Uint32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
+                            return new Uint32Array(buffer, byteOffset, length);
                         }
                         case AccessorComponentType.FLOAT: {
-                            return this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
+                            return new Float32Array(buffer, byteOffset, length);
                         }
                         default: {
-                            throw new Error(`${context}: Invalid component type (${accessor.componentType})`);
+                            throw new Error(`${context}: Invalid accessor component type ${accessor.componentType}`);
                         }
                     }
                 }
                 catch (e) {
-                    throw new Error(`${context}: ${e.messsage}`);
+                    throw new Error(`${context}: ${e}`);
                 }
             });
 
             return accessor._data;
         }
 
-        private _buildArrayBuffer<T extends TypedArray>(typedArray: TypedArrayConstructor<T>, data: ArrayBufferView, byteOffset: number, count: number, numComponents: number, byteStride?: number): T {
-            byteOffset += data.byteOffset;
+        public _loadVertexBufferViewAsync(context: string, bufferView: ILoaderBufferView, kind: string): Promise<Buffer> {
+            if (bufferView._babylonBuffer) {
+                return bufferView._babylonBuffer;
+            }
+
+            bufferView._babylonBuffer = this._loadBufferViewAsync(context, bufferView).then(data => {
+                return new Buffer(this._babylonScene.getEngine(), data, false);
+            });
 
-            const targetLength = count * numComponents;
+            return bufferView._babylonBuffer;
+        }
 
-            if (!byteStride || byteStride === numComponents * typedArray.BYTES_PER_ELEMENT) {
-                return new typedArray(data.buffer, byteOffset, targetLength);
+        private _loadVertexAccessorAsync(context: string, accessor: ILoaderAccessor, kind: string): Promise<VertexBuffer> {
+            if (accessor.sparse) {
+                throw new Error(`${context}: Sparse accessors are not currently supported`);
             }
 
-            const elementStride = byteStride / typedArray.BYTES_PER_ELEMENT;
-            const sourceBuffer = new typedArray(data.buffer, byteOffset, elementStride * count);
-            const targetBuffer = new typedArray(targetLength);
-            let sourceIndex = 0;
-            let targetIndex = 0;
-
-            while (targetIndex < targetLength) {
-                for (let componentIndex = 0; componentIndex < numComponents; componentIndex++) {
-                    targetBuffer[targetIndex] = sourceBuffer[sourceIndex + componentIndex];
-                    targetIndex++;
-                }
-                sourceIndex += elementStride;
+            if (accessor._babylonVertexBuffer) {
+                return accessor._babylonVertexBuffer;
             }
 
-            return targetBuffer;
+            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;
         }
 
         private _getDefaultMaterial(drawMode: number): Material {

+ 2 - 2
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -24,7 +24,7 @@ module BABYLON.GLTF2 {
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>> { return null; }
 
         /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
-        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> { return null; }
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>> { return null; }
 
         /** Override this method to modify the default behavior for loading materials. */
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
@@ -70,7 +70,7 @@ module BABYLON.GLTF2 {
         }
 
         /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
-        public static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> {
+        public static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>> {
             return loader._applyExtensions(extension => extension._loadVertexDataAsync(context, primitive, babylonMesh));
         }
 

+ 3 - 1
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -3,7 +3,8 @@
 
 module BABYLON.GLTF2 {
     export interface ILoaderAccessor extends IAccessor, IArrayItem {
-        _data?: Promise<TypedArray>;
+        _data?: Promise<ArrayBufferView>;
+        _babylonVertexBuffer?: Promise<VertexBuffer>;
     }
 
     export interface ILoaderAnimationChannel extends IAnimationChannel, IArrayItem {
@@ -32,6 +33,7 @@ module BABYLON.GLTF2 {
 
     export interface ILoaderBufferView extends IBufferView, IArrayItem {
         _data?: Promise<ArrayBufferView>;
+        _babylonBuffer?: Promise<Buffer>;
     }
 
     export interface ILoaderCamera extends ICamera, IArrayItem {

+ 0 - 4
loaders/src/glTF/2.0/babylon.glTFLoaderUtilities.ts

@@ -1,10 +1,6 @@
 /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
 module BABYLON.GLTF2 {
-    export interface TypedArray extends ArrayBufferView {
-        [index: number]: number;
-    }
-
     export interface IArrayItem {
         _index: number;
     }

+ 64 - 84
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -239,20 +239,20 @@ module BABYLON.GLTF2 {
          * Returns the bytelength of the data.
          * @param vertexBufferKind - Indicates what kind of vertex data is being passed in.
          * @param meshAttributeArray - Array containing the attribute data. 
-         * @param strideSize - Represents the offset between consecutive attributes
          * @param byteOffset - The offset to start counting bytes from.
          * @param dataBuffer - The buffer to write the binary data to.
          * @returns - Byte length of the attribute data.
          */
-        private writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, strideSize: number, vertexBufferOffset: number, byteOffset: number, dataBuffer: DataView): number {
+        private writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, dataBuffer: DataView): number {
             let byteOff = byteOffset;
 
-            const end = meshAttributeArray.length / strideSize;
+            const stride = VertexBuffer.DeduceStride(vertexBufferKind);
+            const end = meshAttributeArray.length / stride;
 
             let byteLength = 0;
 
             for (let k = 0; k < end; ++k) {
-                const index = k * strideSize;
+                const index = k * stride;
                 let vector: number[] = [];
 
                 if (vertexBufferKind === VertexBuffer.PositionKind || vertexBufferKind === VertexBuffer.NormalKind) {
@@ -533,36 +533,22 @@ module BABYLON.GLTF2 {
                 bufferMesh = (babylonMesh as InstancedMesh).sourceMesh;
             }
             if (bufferMesh) {
-                let vertexBuffer = null;
-                let vertexBufferOffset = null;
-                let vertexData = null;
-                let vertexStrideSize = null;
-                if (bufferMesh.isVerticesDataPresent(kind)) {
-                    vertexBuffer = bufferMesh.getVertexBuffer(kind);
-                    if (vertexBuffer) {
-                        vertexBufferOffset = vertexBuffer.getOffset();
-                        vertexData = vertexBuffer.getData();
-                        if (vertexData) {
-                            vertexStrideSize = vertexBuffer.getStrideSize();
-
-                            if (dataBuffer && vertexData) { // write data to buffer
-                                byteLength = this.writeAttributeData(
-                                    kind,
-                                    vertexData,
-                                    vertexStrideSize,
-                                    vertexBufferOffset,
-                                    byteOffset,
-                                    dataBuffer,
-                                );
-                                byteOffset += byteLength;
-                            }
-                            else {
-                                byteLength = vertexData.length * 4;
-                                const bufferView = this.createBufferView(0, byteOffset, byteLength, vertexStrideSize * 4, kind + " - " + bufferMesh.name);
-                                byteOffset += byteLength;
-                                this.bufferViews.push(bufferView);
-                            }
-                        }
+                const vertexData = bufferMesh.getVerticesData(kind);
+                if (vertexData) {
+                    if (dataBuffer && vertexData) { // write data to buffer
+                        byteLength = this.writeAttributeData(
+                            kind,
+                            vertexData,
+                            byteOffset,
+                            dataBuffer,
+                        );
+                        byteOffset += byteLength;
+                    }
+                    else {
+                        byteLength = vertexData.length * 4;
+                        const bufferView = this.createBufferView(0, byteOffset, byteLength, undefined, kind + " - " + bufferMesh.name);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferView);
                     }
                 }
             }
@@ -638,57 +624,51 @@ module BABYLON.GLTF2 {
                         if (!dataBuffer) {
                             for (const attribute of attributeData) {
                                 const attributeKind = attribute.kind;
+                                const vertexData = bufferMesh.getVerticesData(attributeKind);
+                                if (vertexData) {
+                                    const stride = VertexBuffer.DeduceStride(attributeKind);
+                                    let minMax: Nullable<{ min: number[], max: number[] }>;
+                                    let min = null;
+                                    let max = null;
+                                    const bufferViewIndex = attribute.bufferViewIndex;
+                                    if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
+                                        if (attributeKind == VertexBuffer.PositionKind) {
+                                            minMax = this.calculateMinMaxPositions(vertexData, 0, vertexData.length / stride);
+                                            min = minMax.min;
+                                            max = minMax.max;
+                                        }
+                                        const accessor = this.createAccessor(bufferViewIndex, attributeKind + " - " + babylonMesh.name, attribute.accessorType, AccessorComponentType.FLOAT, vertexData.length / stride, 0, min, max);
+                                        this.accessors.push(accessor);
 
-                                if (bufferMesh.isVerticesDataPresent(attributeKind)) {
-                                    const vertexBuffer = bufferMesh.getVertexBuffer(attributeKind);
-                                    if (vertexBuffer) {
-                                        const bufferData = vertexBuffer.getData();
-                                        if (bufferData) {
-                                            const strideSize = vertexBuffer.getStrideSize();
-                                            let minMax: Nullable<{ min: number[], max: number[] }>;
-                                            let min = null;
-                                            let max = null;
-                                            const bufferViewIndex = attribute.bufferViewIndex;
-                                            if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
-                                                if (attributeKind == VertexBuffer.PositionKind) {
-                                                    minMax = this.calculateMinMaxPositions(bufferData, 0, bufferData.length / strideSize);
-                                                    min = minMax.min;
-                                                    max = minMax.max;
-                                                }
-                                                const accessor = this.createAccessor(bufferViewIndex, attributeKind + " - " + babylonMesh.name, attribute.accessorType, AccessorComponentType.FLOAT, bufferData.length / strideSize, 0, min, max);
-                                                this.accessors.push(accessor);
-
-                                                switch (attributeKind) {
-                                                    case VertexBuffer.PositionKind: {
-                                                        meshPrimitive.attributes.POSITION = this.accessors.length - 1;
-                                                        break;
-                                                    }
-                                                    case VertexBuffer.NormalKind: {
-                                                        meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
-                                                        break;
-                                                    }
-                                                    case VertexBuffer.ColorKind: {
-                                                        meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
-                                                        break;
-                                                    }
-                                                    case VertexBuffer.TangentKind: {
-                                                        meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
-                                                        break;
-                                                    }
-                                                    case VertexBuffer.UVKind: {
-                                                        meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
-                                                        uvCoordsPresent = true;
-                                                        break;
-                                                    }
-                                                    case VertexBuffer.UV2Kind: {
-                                                        meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
-                                                        uvCoordsPresent = true;
-                                                        break;
-                                                    }
-                                                    default: {
-                                                        Tools.Warn("Unsupported Vertex Buffer Type: " + attributeKind);
-                                                    }
-                                                }
+                                        switch (attributeKind) {
+                                            case VertexBuffer.PositionKind: {
+                                                meshPrimitive.attributes.POSITION = this.accessors.length - 1;
+                                                break;
+                                            }
+                                            case VertexBuffer.NormalKind: {
+                                                meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
+                                                break;
+                                            }
+                                            case VertexBuffer.ColorKind: {
+                                                meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
+                                                break;
+                                            }
+                                            case VertexBuffer.TangentKind: {
+                                                meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
+                                                break;
+                                            }
+                                            case VertexBuffer.UVKind: {
+                                                meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
+                                                uvCoordsPresent = true;
+                                                break;
+                                            }
+                                            case VertexBuffer.UV2Kind: {
+                                                meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
+                                                uvCoordsPresent = true;
+                                                break;
+                                            }
+                                            default: {
+                                                Tools.Warn("Unsupported Vertex Buffer Type: " + attributeKind);
                                             }
                                         }
                                     }

+ 26 - 19
src/Engine/babylon.engine.ts

@@ -2192,7 +2192,7 @@
             this._cachedVertexBuffers = null;
         }
 
-        public createVertexBuffer(vertices: FloatArray): WebGLBuffer {
+        public createVertexBuffer(data: DataArray): WebGLBuffer {
             var vbo = this._gl.createBuffer();
 
             if (!vbo) {
@@ -2201,10 +2201,10 @@
 
             this.bindArrayBuffer(vbo);
 
-            if (vertices instanceof Float32Array) {
-                this._gl.bufferData(this._gl.ARRAY_BUFFER, <Float32Array>vertices, this._gl.STATIC_DRAW);
+            if (data instanceof Array) {
+                this._gl.bufferData(this._gl.ARRAY_BUFFER, new Float32Array(data), this._gl.STATIC_DRAW);
             } else {
-                this._gl.bufferData(this._gl.ARRAY_BUFFER, new Float32Array(<number[]>vertices), this._gl.STATIC_DRAW);
+                this._gl.bufferData(this._gl.ARRAY_BUFFER, data, this._gl.STATIC_DRAW);
             }
 
             this._resetVertexBufferBinding();
@@ -2212,7 +2212,7 @@
             return vbo;
         }
 
-        public createDynamicVertexBuffer(vertices: FloatArray): WebGLBuffer {
+        public createDynamicVertexBuffer(data: DataArray): WebGLBuffer {
             var vbo = this._gl.createBuffer();
 
             if (!vbo) {
@@ -2221,11 +2221,12 @@
 
             this.bindArrayBuffer(vbo);
 
-            if (vertices instanceof Float32Array) {
-                this._gl.bufferData(this._gl.ARRAY_BUFFER, <Float32Array>vertices, this._gl.DYNAMIC_DRAW);
+            if (data instanceof Array) {
+                this._gl.bufferData(this._gl.ARRAY_BUFFER, new Float32Array(data), this._gl.DYNAMIC_DRAW);
             } else {
-                this._gl.bufferData(this._gl.ARRAY_BUFFER, new Float32Array(<number[]>vertices), this._gl.DYNAMIC_DRAW);
+                this._gl.bufferData(this._gl.ARRAY_BUFFER, data, this._gl.DYNAMIC_DRAW);
             }
+
             this._resetVertexBufferBinding();
             vbo.references = 1;
             return vbo;
@@ -2248,24 +2249,30 @@
             this._resetIndexBufferBinding();
         }
 
-        public updateDynamicVertexBuffer(vertexBuffer: WebGLBuffer, vertices: FloatArray, offset?: number, count?: number): void {
+        public updateDynamicVertexBuffer(vertexBuffer: WebGLBuffer, data: DataArray, byteOffset?: number, byteLength?: number): void {
             this.bindArrayBuffer(vertexBuffer);
 
-            if (offset === undefined) {
-                offset = 0;
+            if (byteOffset === undefined) {
+                byteOffset = 0;
             }
 
-            if (count === undefined) {
-                if (vertices instanceof Float32Array) {
-                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, offset, <Float32Array>vertices);
+            if (byteLength === undefined) {
+                if (data instanceof Array) {
+                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, byteOffset, new Float32Array(data));
                 } else {
-                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, offset, new Float32Array(<number[]>vertices));
+                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, byteOffset, data);
                 }
             } else {
-                if (vertices instanceof Float32Array) {
-                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, <Float32Array>vertices.subarray(offset, offset + count));
+                if (data instanceof Array) {
+                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, new Float32Array(data).subarray(byteOffset, byteOffset + byteLength));
                 } else {
-                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, new Float32Array(<number[]>vertices).subarray(offset, offset + count));
+                    if (data instanceof ArrayBuffer) {
+                        data = new Uint8Array(data, byteOffset, byteLength);
+                    } else {
+                        data = new Uint8Array(data.buffer, data.byteOffset + byteOffset, byteLength);
+                    }
+
+                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, data);
                 }
             }
 
@@ -2427,7 +2434,7 @@
 
                     var buffer = vertexBuffer.getBuffer();
                     if (buffer) {
-                        this.vertexAttribPointer(buffer, order, vertexBuffer.getSize(), this._gl.FLOAT, false, vertexBuffer.getStrideSize() * 4, vertexBuffer.getOffset() * 4);
+                        this.vertexAttribPointer(buffer, order, vertexBuffer.getSize(), vertexBuffer.type, vertexBuffer.normalized, vertexBuffer.byteStride, vertexBuffer.byteOffset);
 
                         if (vertexBuffer.getIsInstanced()) {
                             this._gl.vertexAttribDivisor(order, vertexBuffer.getInstanceDivisor());

+ 19 - 32
src/Mesh/babylon.buffer.ts

@@ -2,12 +2,16 @@
     export class Buffer {
         private _engine: Engine;
         private _buffer: Nullable<WebGLBuffer>;
-        private _data: Nullable<FloatArray>;
+        private _data: Nullable<DataArray>;
         private _updatable: boolean;
-        private _strideSize: number;
         private _instanced: boolean;
 
-        constructor(engine: any, data: FloatArray, updatable: boolean, stride: number, postponeInternalCreation?: boolean, instanced: boolean = false) {
+        /**
+         * Gets the byte stride.
+         */
+        public readonly byteStride: number;
+
+        constructor(engine: any, data: DataArray, updatable: boolean, stride = 0, postponeInternalCreation = false, instanced = false, useBytes = false) {
             if (engine instanceof Mesh) { // old versions of BABYLON.VertexBuffer accepted 'mesh' instead of 'engine'
                 this._engine = engine.getScene().getEngine();
             }
@@ -20,7 +24,7 @@
 
             this._data = data;
 
-            this._strideSize = stride;
+            this.byteStride = useBytes ? stride : stride * 4;
 
             if (!postponeInternalCreation) { // by default
                 this.create();
@@ -34,11 +38,15 @@
          * @param size defines the size in floats of attributes (position is 3 for instance)
          * @param stride defines the stride size in floats in the buffer (the offset to apply to reach next value when data is interleaved)
          * @param instanced defines if the vertex buffer contains indexed data
+         * @param useBytes defines if the offset and stride are in bytes
          * @returns the new vertex buffer
          */
-        public createVertexBuffer(kind: string, offset: number, size: number, stride?: number, instanced?: boolean): VertexBuffer {
+        public createVertexBuffer(kind: string, offset: number, size: number, stride?: number, instanced?: boolean, useBytes = false): VertexBuffer {
+            const byteOffset = useBytes ? offset : offset * 4;
+            const byteStride = stride ? (useBytes ? stride : stride * 4) : this.byteStride;
+
             // a lot of these parameters are ignored as they are overriden by the buffer
-            return new VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, instanced === undefined ? this._instanced : instanced, offset, size);
+            return new VertexBuffer(this._engine, this, kind, this._updatable, true, byteStride, instanced === undefined ? this._instanced : instanced, byteOffset, size, undefined, undefined, true);
         }
 
         // Properties
@@ -46,7 +54,7 @@
             return this._updatable;
         }
 
-        public getData(): Nullable<FloatArray> {
+        public getData(): Nullable<DataArray> {
             return this._data;
         }
 
@@ -54,29 +62,8 @@
             return this._buffer;
         }
 
-        public getStrideSize(): number {
-            return this._strideSize;
-        }
-
-        // public getIsInstanced(): boolean {
-        //     return this._instanced;
-        // }
-
-        // public get instanceDivisor(): number {
-        //     return this._instanceDivisor;
-        // }
-
-        // public set instanceDivisor(value: number) {
-        //     this._instanceDivisor = value;
-        //     if (value == 0) {
-        //         this._instanced = false;
-        //     } else {
-        //         this._instanced = true;
-        //     }
-        // }
-
         // Methods
-        public create(data: Nullable<FloatArray> = null): void {
+        public create(data: Nullable<DataArray> = null): void {
             if (!data && this._buffer) {
                 return; // nothing to do
             }
@@ -105,17 +92,17 @@
             this.create(this._data);
         }
 
-        public update(data: FloatArray): void {
+        public update(data: DataArray): void {
             this.create(data);
         }
 
-        public updateDirectly(data: Float32Array, offset: number, vertexCount?: number): void {
+        public updateDirectly(data: DataArray, offset: number, vertexCount?: number, useBytes = false): void {
             if (!this._buffer) {
                 return;
             }
 
             if (this._updatable) { // update buffer
-                this._engine.updateDynamicVertexBuffer(this._buffer, data, offset, (vertexCount ? vertexCount * this.getStrideSize() : undefined));
+                this._engine.updateDynamicVertexBuffer(this._buffer, data, useBytes ? offset : offset * 4, (vertexCount ? vertexCount * this.byteStride : undefined));
                 this._data = null;
             }
         }

+ 56 - 32
src/Mesh/babylon.geometry.ts

@@ -65,7 +65,7 @@
 
             this._boundingBias = value.clone();
 
-            this.updateBoundingInfo(true, null);
+            this._updateBoundingInfo(true, null);
         }
 
         /**
@@ -116,7 +116,7 @@
             if (mesh) {
                 if (mesh.getClassName() === "LinesMesh") {
                     this.boundingBias = new Vector2(0, (<LinesMesh>mesh).intersectionThreshold);
-                    this.updateExtend();
+                    this._updateExtend();
                 }
 
                 this.applyToMesh(mesh);
@@ -204,8 +204,11 @@
          * @param stride defines the stride to use (0 by default). This value is deduced from the kind value if not specified
          */
         public setVerticesData(kind: string, data: FloatArray, updatable: boolean = false, stride?: number): void {
-            var buffer = new VertexBuffer(this._engine, data, kind, updatable, this._meshes.length === 0, stride);
+            if (kind === VertexBuffer.PositionKind) {
+                this._totalVertices = data.length / (stride || 3);
+            }
 
+            var buffer = new VertexBuffer(this._engine, data, kind, updatable, this._meshes.length === 0, stride);
             this.setVerticesBuffer(buffer);
         }
 
@@ -223,8 +226,9 @@
         /**
          * Affect a vertex buffer to the geometry. the vertexBuffer.getKind() function is used to determine where to store the data
          * @param buffer defines the vertex buffer to use
+         * @param totalVertices defines the total number of vertices for position kind (could be null)
          */
-        public setVerticesBuffer(buffer: VertexBuffer): void {
+        public setVerticesBuffer(buffer: VertexBuffer, totalVertices: Nullable<number> = null): void {
             var kind = buffer.getKind();
             if (this._vertexBuffers[kind]) {
                 this._vertexBuffers[kind].dispose();
@@ -233,12 +237,11 @@
             this._vertexBuffers[kind] = buffer;
 
             if (kind === VertexBuffer.PositionKind) {
-                var data = <FloatArray>buffer.getData();
-                var stride = buffer.getStrideSize();
-
-                this._totalVertices = data.length / stride;
+                if (totalVertices != null) {
+                    this._totalVertices = totalVertices;
+                }
 
-                this.updateExtend(data, stride);
+                this._updateExtend();
                 this._resetPointsArrayCache();
 
                 var meshes = this._meshes;
@@ -285,7 +288,7 @@
          * @param kind defines the data kind (Position, normal, etc...)
          * @param data defines the data to use 
          * @param updateExtends defines if the geometry extends must be recomputed (false by default)
-         */        
+         */
         public updateVerticesData(kind: string, data: FloatArray, updateExtends: boolean = false): void {
             var vertexBuffer = this.getVertexBuffer(kind);
 
@@ -296,18 +299,14 @@
             vertexBuffer.update(data);
 
             if (kind === VertexBuffer.PositionKind) {
-
-                var stride = vertexBuffer.getStrideSize();
-                this._totalVertices = data.length / stride;
-
-                this.updateBoundingInfo(updateExtends, data);
+                this._updateBoundingInfo(updateExtends, data);
             }
             this.notifyUpdate(kind);
         }
 
-        private updateBoundingInfo(updateExtends: boolean, data: Nullable<FloatArray>) {
+        private _updateBoundingInfo(updateExtends: boolean, data: Nullable<FloatArray>) {
             if (updateExtends) {
-                this.updateExtend(data);
+                this._updateExtend(data);
             }
 
             var meshes = this._meshes;
@@ -369,28 +368,53 @@
         }
 
         /**
-         * Gets a specific vertex data attached to this geometry
+         * Gets a specific vertex data attached to this geometry. Float data is constructed if the vertex buffer data cannot be returned directly.
          * @param kind defines the data kind (Position, normal, etc...)
          * @param copyWhenShared defines if the returned array must be cloned upon returning it if the current geometry is shared between multiple meshes
          * @param forceCopy defines a boolean indicating that the returned array must be cloned upon returning it
          * @returns a float array containing vertex data
          */
         public getVerticesData(kind: string, copyWhenShared?: boolean, forceCopy?: boolean): Nullable<FloatArray> {
-            var vertexBuffer = this.getVertexBuffer(kind);
+            const vertexBuffer = this.getVertexBuffer(kind);
             if (!vertexBuffer) {
                 return null;
             }
-            var orig = <FloatArray>vertexBuffer.getData();
-            if (!forceCopy && (!copyWhenShared || this._meshes.length === 1)) {
-                return orig;
-            } else {
-                var len = orig.length;
-                var copy = [];
-                for (var i = 0; i < len; i++) {
-                    copy.push(orig[i]);
-                }
+
+            let data = vertexBuffer.getData();
+            if (!data) {
+                 return null;
+            }
+
+            const defaultStride = VertexBuffer.DeduceStride(vertexBuffer.getKind());
+            const defaultByteStride = defaultStride * VertexBuffer.GetTypeByteLength(vertexBuffer.type);
+            const count = this._totalVertices * defaultStride;
+
+            if (vertexBuffer.type !== VertexBuffer.FLOAT || vertexBuffer.byteStride !== defaultByteStride) {
+                const copy = new Array<number>(count);
+                vertexBuffer.forEach(count, (value, index) => {
+                    copy[index] = value;
+                });
                 return copy;
             }
+
+            if (!(data instanceof Array || data instanceof Float32Array) || vertexBuffer.byteOffset !== 0 || data.length !== count) {
+                if (data instanceof Array) {
+                    const offset = vertexBuffer.byteOffset / 4;
+                    return Tools.Slice(data, offset, offset + count);
+                }
+                else if (data instanceof ArrayBuffer) {
+                    return new Float32Array(data, vertexBuffer.byteOffset, count);
+                }
+                else {
+                    return new Float32Array(data.buffer, data.byteOffset + vertexBuffer.byteOffset, count);
+                }
+            }
+
+            if (forceCopy || (copyWhenShared && this._meshes.length !== 1)) {
+                return Tools.Slice(data);
+            }
+
+            return data;
         }
 
         /**
@@ -624,12 +648,12 @@
             }
         }
 
-        private updateExtend(data: Nullable<FloatArray> = null, stride?: number) {
+        private _updateExtend(data: Nullable<FloatArray> = null) {
             if (!data) {
-                data = <FloatArray>this._vertexBuffers[VertexBuffer.PositionKind].getData();
+                data = this.getVerticesData(VertexBuffer.PositionKind)!;
             }
 
-            this._extend = Tools.ExtractMinAndMax(data, 0, this._totalVertices, this.boundingBias, stride);
+            this._extend = Tools.ExtractMinAndMax(data, 0, this._totalVertices, this.boundingBias, 3);
         }
 
         private _applyToMesh(mesh: Mesh): void {
@@ -646,7 +670,7 @@
 
                 if (kind === VertexBuffer.PositionKind) {
                     if (!this._extend) {
-                        this.updateExtend(this._vertexBuffers[kind].getData());
+                        this._updateExtend();
                     }
                     mesh._boundingInfo = new BoundingInfo(this._extend.minimum, this._extend.maximum);
 

+ 1 - 1
src/Mesh/babylon.linesMesh.ts

@@ -105,7 +105,7 @@
         }
 
         public _draw(subMesh: SubMesh, fillMode: number, instancesCount?: number): LinesMesh {
-            if (!this._geometry || !this._geometry.getVertexBuffers() || !this._geometry.getIndexBuffer()) {
+            if (!this._geometry || !this._geometry.getVertexBuffers() || (!this._unIndexed && !this._geometry.getIndexBuffer())) {
                 return this;
             }
 

+ 2 - 2
src/Mesh/babylon.mesh.ts

@@ -1123,7 +1123,7 @@
         }
 
         public _draw(subMesh: SubMesh, fillMode: number, instancesCount?: number, alternate = false): Mesh {
-            if (!this._geometry || !this._geometry.getVertexBuffers() || !this._geometry.getIndexBuffer()) {
+            if (!this._geometry || !this._geometry.getVertexBuffers() || (!this._unIndexed && !this._geometry.getIndexBuffer())) {
                 return this;
             }
 
@@ -1359,7 +1359,7 @@
             }
 
             // Checking geometry state
-            if (!this._geometry || !this._geometry.getVertexBuffers() || !this._geometry.getIndexBuffer()) {
+            if (!this._geometry || !this._geometry.getVertexBuffers() || (!this._unIndexed && !this._geometry.getIndexBuffer())) {
                 return this;
             }
 

+ 161 - 29
src/Mesh/babylon.vertexBuffer.ts

@@ -2,13 +2,19 @@
     export class VertexBuffer {
         private _buffer: Buffer;
         private _kind: string;
-        private _offset: number;
         private _size: number;
-        private _stride: number;
         private _ownsBuffer: boolean;
-        private _instanced: boolean;        
+        private _instanced: boolean;
         private _instanceDivisor: number;
 
+        public static readonly BYTE = 5120;
+        public static readonly UNSIGNED_BYTE = 5121;
+        public static readonly SHORT = 5122;
+        public static readonly UNSIGNED_SHORT = 5123;
+        public static readonly INT = 5124;
+        public static readonly UNSIGNED_INT = 5125;
+        public static readonly FLOAT = 5126;
+
         /**
          * Gets or sets the instance divisor when in instanced mode
          */
@@ -23,31 +29,70 @@
             } else {
                 this._instanced = true;
             }
-        }        
+        }
+
+        /**
+         * Gets the byte stride.
+         */
+        public readonly byteStride: number;
+
+        /**
+         * Gets the byte offset.
+         */
+        public readonly byteOffset: number;
+
+        /**
+         * Gets whether integer data values should be normalized into a certain range when being casted to a float.
+         */
+        public readonly normalized: boolean;
+
+        /**
+         * Gets the data type of each component in the array.
+         */
+        public readonly type: number;
 
-        constructor(engine: any, data: FloatArray | Buffer, kind: string, updatable: boolean, postponeInternalCreation?: boolean, stride?: number, instanced?: boolean, offset?: number, size?: number) {
+        constructor(engine: any, data: DataArray | Buffer, kind: string, updatable: boolean, postponeInternalCreation?: boolean, stride?: number, instanced?: boolean, offset?: number, size?: number, type?: number, normalized = false, useBytes = false) {
             if (data instanceof Buffer) {
-                if (!stride) {
-                    stride = data.getStrideSize();
-                }
                 this._buffer = data;
                 this._ownsBuffer = false;
             } else {
-                if (!stride) {
-                    stride = VertexBuffer.DeduceStride(kind);
-                }
-                this._buffer = new Buffer(engine, <FloatArray>data, updatable, stride, postponeInternalCreation, instanced);
+                this._buffer = new Buffer(engine, data, updatable, stride, postponeInternalCreation, instanced, useBytes);
                 this._ownsBuffer = true;
             }
 
-            this._stride = stride;
-            this._instanced = instanced !== undefined ? instanced : false;
-            this._instanceDivisor = instanced ? 1 : 0;
+            this._kind = kind;
 
-            this._offset = offset ? offset : 0;
-            this._size = size ? size : stride;
+            if (type == undefined) {
+                const data = this.getData();
+                this.type = VertexBuffer.FLOAT;
+                if (data instanceof Int8Array) this.type = VertexBuffer.BYTE;
+                else if (data instanceof Uint8Array) this.type = VertexBuffer.UNSIGNED_BYTE;
+                else if (data instanceof Int16Array) this.type = VertexBuffer.SHORT;
+                else if (data instanceof Uint16Array) this.type = VertexBuffer.UNSIGNED_SHORT;
+                else if (data instanceof Int32Array) this.type = VertexBuffer.INT;
+                else if (data instanceof Uint32Array) this.type = VertexBuffer.UNSIGNED_INT;
+            }
+            else {
+                this.type = type;
+            }
 
-            this._kind = kind;
+            const typeByteLength = VertexBuffer.GetTypeByteLength(this.type);
+
+            if (useBytes) {
+                this._size = size || (stride ? (stride / typeByteLength) : VertexBuffer.DeduceStride(kind));
+                this.byteStride = stride || this._buffer.byteStride || (this._size * typeByteLength);
+                this.byteOffset = offset || 0;
+            }
+            else {
+                this._size = size || stride || VertexBuffer.DeduceStride(kind);
+                this.byteStride = stride ? (stride * typeByteLength) : (this._buffer.byteStride || (this._size * typeByteLength));
+                this.byteOffset = (offset || 0) * typeByteLength;
+            }
+
+            this.normalized = normalized;
+
+            this._instanced = instanced !== undefined ? instanced : false;
+            this._instanceDivisor = instanced ? 1 : 0;
         }
 
         public _rebuild(): void {
@@ -74,9 +119,9 @@
         }
 
         /**
-         * Returns an array of numbers or a Float32Array containing the VertexBuffer data.  
+         * Returns an array of numbers or a typed array containing the VertexBuffer data.  
          */
-        public getData(): Nullable<FloatArray> {
+        public getData(): Nullable<DataArray> {
             return this._buffer.getData();
         }
 
@@ -88,21 +133,23 @@
         }
 
         /**
-         * Returns the stride of the VertexBuffer (integer).  
+         * Returns the stride as a multiple of the type byte length.
+         * DEPRECATED. Use byteStride instead.
          */
         public getStrideSize(): number {
-            return this._stride;
+            return this.byteStride / VertexBuffer.GetTypeByteLength(this.type);
         }
 
         /**
-         * Returns the offset (integer).  
+         * Returns the offset as a multiple of the type byte length.
+         * DEPRECATED. Use byteOffset instead.
          */
         public getOffset(): number {
-            return this._offset;
+            return this.byteOffset / VertexBuffer.GetTypeByteLength(this.type);
         }
 
         /**
-         * Returns the VertexBuffer total size (integer).  
+         * Returns the number of components per vertex attribute (integer).  
          */
         public getSize(): number {
             return this._size;
@@ -128,7 +175,7 @@
          * Creates the underlying WebGLBuffer from the passed numeric array or Float32Array.  
          * Returns the created WebGLBuffer.   
          */
-        public create(data?: FloatArray): void {
+        public create(data?: DataArray): void {
             return this._buffer.create(data);
         }
 
@@ -137,7 +184,7 @@
          * This function will create a new buffer if the current one is not updatable
          * Returns the updated WebGLBuffer.  
          */
-        public update(data: FloatArray): void {
+        public update(data: DataArray): void {
             return this._buffer.update(data);
         }
 
@@ -145,7 +192,7 @@
          * Updates directly the underlying WebGLBuffer according to the passed numeric array or Float32Array.  
          * Returns the directly updated WebGLBuffer. 
          */
-        public updateDirectly(data: Float32Array, offset: number): void {
+        public updateDirectly(data: DataArray, offset: number): void {
             return this._buffer.updateDirectly(data, offset);
         }
 
@@ -158,6 +205,10 @@
             }
         }
 
+        public forEach(count: number, callback: (value: number, index: number) => void): void {
+            VertexBuffer.ForEach(this._buffer.getData()!, this.byteOffset, this.byteStride, this._size, this.type, count, this.normalized, callback);
+        }
+
         // Enums
         private static _PositionKind = "position";
         private static _NormalKind = "normal";
@@ -258,5 +309,86 @@
                     throw new Error("Invalid kind '" + kind + "'");
             }
         }
+
+        public static GetTypeByteLength(type: number): number {
+            switch (type) {
+                case VertexBuffer.BYTE:
+                case VertexBuffer.UNSIGNED_BYTE:
+                    return 1;
+                case VertexBuffer.SHORT:
+                case VertexBuffer.UNSIGNED_SHORT:
+                    return 2;
+                case VertexBuffer.INT:
+                case VertexBuffer.FLOAT:
+                    return 4;
+                default:
+                    throw new Error(`Invalid type '${type}'`);
+            }
+        }
+
+        public static ForEach(data: DataArray, byteOffset: number, byteStride: number, componentCount: number, componentType: number, count: number, normalized: boolean, callback: (value: number, index: number) => void): void {
+            if (data instanceof Array) {
+                let offset = byteOffset / 4;
+                const stride = byteStride / 4;
+                for (let index = 0; index < count; index += componentCount) {
+                    for (let componentIndex = 0; componentIndex < componentCount; componentIndex++) {
+                        callback(data[offset + componentIndex], index + componentIndex);
+                    }
+                    offset += stride;
+                }
+            }
+            else {
+                const dataView = data instanceof ArrayBuffer ? new DataView(data) : new DataView(data.buffer, data.byteOffset, data.byteLength);
+                const componentByteLength = VertexBuffer.GetTypeByteLength(componentType);
+                for (let index = 0; index < count; index += componentCount) {
+                    let componentByteOffset = byteOffset;
+                    for (let componentIndex = 0; componentIndex < componentCount; componentIndex++) {
+                        const value = VertexBuffer._GetFloatValue(dataView, componentType, componentByteOffset, normalized);
+                        callback(value, index + componentIndex);
+                        componentByteOffset += componentByteLength;
+                    }
+                    byteOffset += byteStride;
+                }
+            }
+        }
+
+        private static _GetFloatValue(dataView: DataView, type: number, byteOffset: number, normalized: boolean): number {
+            switch (type) {
+                case VertexBuffer.BYTE: {
+                    let value = dataView.getInt8(byteOffset);
+                    if (normalized) {
+                        value = (value + 0.5) / 127.5;
+                    }
+                    return value;
+                }
+                case VertexBuffer.UNSIGNED_BYTE: {
+                    let value = dataView.getUint8(byteOffset);
+                    if (normalized) {
+                        value = value / 255;
+                    }
+                    return value;
+                }
+                case VertexBuffer.SHORT: {
+                    let value = dataView.getInt16(byteOffset, true);
+                    if (normalized) {
+                        value = (value + 0.5) / 16383.5;
+                    }
+                    return value;
+                }
+                case VertexBuffer.UNSIGNED_SHORT: {
+                    let value = dataView.getUint16(byteOffset, true);
+                    if (normalized) {
+                        value = value / 65535;
+                    }
+                    return value;
+                }
+                case VertexBuffer.FLOAT: {
+                    return dataView.getFloat32(byteOffset, true);
+                }
+                default: {
+                    throw new Error(`Invalid component type ${type}`);
+                }
+            }
+        }
     }
-} 
+}

+ 4 - 4
src/Tools/babylon.tools.ts

@@ -118,12 +118,12 @@
          * @param data defines the array to slice
          * @returns the new sliced array
          */
-        public static Slice(data: FloatArray): FloatArray {
-            if (data.slice) {
-                return data.slice();
+        public static Slice<T>(data: T, start?: number, end?: number): T {
+            if ((data as any).slice) {
+                return (data as any).slice(start, end);
             }
 
-            return Array.prototype.slice.call(data);
+            return Array.prototype.slice.call(data, start, end);
         }
 
         public static SetImmediate(action: () => void) {

+ 1 - 0
src/babylon.types.ts

@@ -6,4 +6,5 @@ module BABYLON {
 
     export type FloatArray = number[] | Float32Array;
     export type IndicesArray = number[] | Int32Array | Uint32Array | Uint16Array;
+    export type DataArray = number[] | ArrayBuffer | ArrayBufferView;
 }