ソースを参照

Added angularSpeedGradients for particle

David Catuhe 7 年 前
コミット
a6b82d2b42
27 ファイル変更26107 行追加24932 行削除
  1. 10540 10416
      Playground/babylon.d.txt
  2. BIN
      Playground/scenes/Box/Box.png
  3. 13580 13457
      dist/preview release/babylon.d.ts
  4. 44 44
      dist/preview release/babylon.js
  5. 311 139
      dist/preview release/babylon.max.js
  6. 311 139
      dist/preview release/babylon.no-module.max.js
  7. 48 48
      dist/preview release/babylon.worker.js
  8. 311 139
      dist/preview release/es6.js
  9. 16 15
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  10. 100 76
      dist/preview release/serializers/babylon.glTF2Serializer.js
  11. 2 2
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  12. 16 15
      dist/preview release/serializers/babylonjs.serializers.d.ts
  13. 100 76
      dist/preview release/serializers/babylonjs.serializers.js
  14. 2 2
      dist/preview release/serializers/babylonjs.serializers.min.js
  15. 16 15
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  16. 2 58
      dist/preview release/typedocValidationBaseline.json
  17. 4 0
      dist/preview release/viewer/babylon.viewer.d.ts
  18. 67 67
      dist/preview release/viewer/babylon.viewer.js
  19. 315 141
      dist/preview release/viewer/babylon.viewer.max.js
  20. 4 0
      dist/preview release/viewer/babylon.viewer.module.d.ts
  21. 1 0
      dist/preview release/what's new.md
  22. 24 0
      src/Particles/babylon.IParticleSystem.ts
  23. 158 71
      src/Particles/babylon.gpuParticleSystem.ts
  24. 13 1
      src/Particles/babylon.particle.ts
  25. 92 4
      src/Particles/babylon.particleSystem.ts
  26. 7 7
      src/Shaders/gpuRenderParticles.vertex.fx
  27. 23 0
      src/Shaders/gpuUpdateParticles.vertex.fx

ファイルの差分が大きいため隠しています
+ 10540 - 10416
Playground/babylon.d.txt


BIN
Playground/scenes/Box/Box.png


ファイルの差分が大きいため隠しています
+ 13580 - 13457
dist/preview release/babylon.d.ts


ファイルの差分が大きいため隠しています
+ 44 - 44
dist/preview release/babylon.js


ファイルの差分が大きいため隠しています
+ 311 - 139
dist/preview release/babylon.max.js


ファイルの差分が大きいため隠しています
+ 311 - 139
dist/preview release/babylon.no-module.max.js


ファイルの差分が大きいため隠しています
+ 48 - 48
dist/preview release/babylon.worker.js


ファイルの差分が大きいため隠しています
+ 311 - 139
dist/preview release/es6.js


+ 16 - 15
dist/preview release/serializers/babylon.glTF2Serializer.d.ts

@@ -560,7 +560,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -575,7 +575,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -804,7 +804,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -817,7 +817,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -825,7 +825,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -834,52 +834,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 100 - 76
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -367,12 +367,26 @@ var BABYLON;
                     var vertex = vertices_1[_i];
                     if (this._convertToRightHandedSystem && !(vertexAttributeKind === BABYLON.VertexBuffer.ColorKind) && !(vertex instanceof BABYLON.Vector2)) {
                         if (vertex instanceof BABYLON.Vector3) {
-                            (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
+                            if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertex);
+                            }
+                            else if (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertex);
+                            }
+                            else {
+                                BABYLON.Tools.Error('Unsupported vertex attribute kind!');
+                            }
                         }
                         else {
-                            GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertex);
+                            GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertex);
                         }
                     }
+                    if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                        vertex.normalize();
+                    }
+                    else if (vertexAttributeKind === BABYLON.VertexBuffer.TangentKind && vertex instanceof BABYLON.Vector4) {
+                        GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertex);
+                    }
                     for (var _a = 0, _b = vertex.asArray(); _a < _b.length; _a++) {
                         var component = _b[_a];
                         binaryWriter.setFloat32(component, byteOffset);
@@ -398,7 +412,7 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertexData);
                             }
                             vertexAttributes.push(vertexData.asArray());
                         }
@@ -409,8 +423,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertexData);
                             }
+                            vertexData.normalize();
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -420,8 +435,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector4.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertexData);
                             }
+                            GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertexData);
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -513,7 +529,7 @@ var BABYLON;
                             if (image.uri) {
                                 imageData = _this._imageData[image.uri];
                                 imageName = image.uri.split('.')[0] + " image";
-                                bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
+                                bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                                 byteOffset += imageData.data.buffer.byteLength;
                                 _this._bufferViews.push(bufferView);
                                 image.bufferView = _this._bufferViews.length - 1;
@@ -663,7 +679,7 @@ var BABYLON;
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode) {
                 if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
                 }
                 if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
                     node.scale = babylonTransformNode.scaling.asArray();
@@ -674,7 +690,7 @@ var BABYLON;
                 }
                 if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
                     if (this._convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                     }
                     node.rotation = rotationQuaternion.normalize().asArray();
                 }
@@ -702,7 +718,7 @@ var BABYLON;
                     var vertexData = bufferMesh.getVerticesData(kind);
                     if (vertexData) {
                         var byteLength = vertexData.length * 4;
-                        var bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
+                        var bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                         this._bufferViews.push(bufferView);
                         this.writeAttributeData(kind, vertexData, byteStride, binaryWriter);
                     }
@@ -843,7 +859,7 @@ var BABYLON;
                         var indices = bufferMesh.getIndices();
                         if (indices) {
                             var byteLength = indices.length * 4;
-                            bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
+                            bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                             this._bufferViews.push(bufferView);
                             indexBufferViewIndex = this._bufferViews.length - 1;
                             for (var k = 0, length_7 = indices.length; k < length_7; ++k) {
@@ -902,9 +918,9 @@ var BABYLON;
                                         if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                             minMax = { min: null, max: null };
                                             if (attributeKind == BABYLON.VertexBuffer.PositionKind) {
-                                                minMax = GLTF2._GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                                minMax = GLTF2._GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
                                             }
-                                            var accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                            var accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
                                             this._accessors.push(accessor);
                                             this.setAttributeKind(meshPrimitive, attributeKind);
                                             if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
@@ -916,7 +932,7 @@ var BABYLON;
                             }
                             if (indexBufferViewIndex) {
                                 // Create accessor
-                                var accessor = GLTF2._GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
+                                var accessor = GLTF2._GLTFUtilities._CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
                                 this._accessors.push(accessor);
                                 meshPrimitive.indices = this._accessors.length - 1;
                             }
@@ -1970,7 +1986,7 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._gonvertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var promises = [];
                 var metallicRoughness = {
                     baseColor: babylonPBRMaterial.albedoColor,
@@ -2102,39 +2118,41 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughness = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var _this = this;
-                var samplers = this._exporter._samplers;
-                var textures = this._exporter._textures;
-                var specGloss = {
-                    diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
-                    specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
-                    glossiness: babylonPBRMaterial.microSurface || 1,
-                };
-                var samplerIndex = null;
-                var sampler = this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
-                if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
-                    samplers.push(sampler);
-                    samplerIndex = samplers.length - 1;
-                }
-                if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
-                    return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
-                }
-                return this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
-                    if (hasTextureCoords) {
-                        if (metallicRoughnessFactors.baseColorTextureBase64) {
-                            var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFBaseColorTexture) {
-                                glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                return Promise.resolve().then(function () {
+                    var samplers = _this._exporter._samplers;
+                    var textures = _this._exporter._textures;
+                    var specGloss = {
+                        diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
+                        specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
+                        glossiness: babylonPBRMaterial.microSurface || 1,
+                    };
+                    var samplerIndex = null;
+                    var sampler = _this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
+                    if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
+                        samplers.push(sampler);
+                        samplerIndex = samplers.length - 1;
+                    }
+                    if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                        return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
+                    }
+                    if ((babylonPBRMaterial.albedoTexture || babylonPBRMaterial.reflectivityTexture) && hasTextureCoords) {
+                        return _this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
+                            if (metallicRoughnessFactors.baseColorTextureBase64) {
+                                var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFBaseColorTexture) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
                             }
-                        }
-                        if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
-                            var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFMRColorTexture) {
-                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                            if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
+                                var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFMRColorTexture) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
                             }
-                        }
-                        return metallicRoughnessFactors;
+                            return metallicRoughnessFactors;
+                        });
                     }
                     else {
                         return _this._convertSpecularGlossinessToMetallicRoughness(specGloss);
@@ -2167,12 +2185,12 @@ var BABYLON;
                             babylonPBRMaterial.alpha
                         ];
                     }
-                    return this._gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
                 else {
-                    return this._convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
@@ -2598,26 +2616,26 @@ var BABYLON;
                     var nodeIndex = nodeMap[babylonTransformNode.uniqueId];
                     // Creates buffer view and accessor for key frames.
                     var byteLength = animationData.inputs.length * 4;
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
                     bufferViews.push(bufferView);
                     animationData.inputs.forEach(function (input) {
                         binaryWriter.setFloat32(input);
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
                     accessors.push(accessor);
                     keyframeAccessorIndex = accessors.length - 1;
                     // create bufferview and accessor for keyed values.
                     outputLength = animationData.outputs.length;
                     byteLength = dataAccessorType === "VEC3" /* VEC3 */ ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
                     // check for in and out tangents
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
                     bufferViews.push(bufferView);
                     animationData.outputs.forEach(function (output) {
                         output.forEach(function (entry) {
                             binaryWriter.setFloat32(entry);
                         });
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
                     accessors.push(accessor);
                     dataAccessorIndex = accessors.length - 1;
                     // create sampler
@@ -2757,7 +2775,7 @@ var BABYLON;
                             BABYLON.Quaternion.RotationYawPitchRollToRef(cacheValue.y, cacheValue.x, cacheValue.z, quaternionCache);
                         }
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(quaternionCache);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(quaternionCache);
                             if (!babylonTransformNode.parent) {
                                 quaternionCache = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(quaternionCache);
                             }
@@ -2767,7 +2785,7 @@ var BABYLON;
                     else {
                         cacheValue = value;
                         if (convertToRightHandedSystem && (animationChannelTargetPath !== "scale" /* SCALE */)) {
-                            GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(cacheValue);
+                            GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(cacheValue);
                             if (!babylonTransformNode.parent) {
                                 cacheValue.x *= -1;
                                 cacheValue.z *= -1;
@@ -2822,7 +2840,7 @@ var BABYLON;
                         if (babylonTransformNode.rotationQuaternion) {
                             basePositionRotationOrScale = babylonTransformNode.rotationQuaternion.asArray();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     basePositionRotationOrScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(basePositionRotationOrScale)).asArray();
                                 }
@@ -2834,13 +2852,13 @@ var BABYLON;
                     }
                     else {
                         basePositionRotationOrScale = babylonTransformNode.rotation.asArray();
-                        GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                     basePositionRotationOrScale = babylonTransformNode.position.asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else { // scale
@@ -2868,7 +2886,7 @@ var BABYLON;
                         var array = BABYLON.Vector3.FromArray(value);
                         var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z);
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                             if (!babylonTransformNode.parent) {
                                 rotationQuaternion = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(rotationQuaternion);
                             }
@@ -2877,7 +2895,7 @@ var BABYLON;
                     }
                     else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(value);
+                            GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(value);
                             if (!babylonTransformNode.parent) {
                                 value[0] *= -1;
                                 value[2] *= -1;
@@ -2892,7 +2910,7 @@ var BABYLON;
                         if (animationChannelTargetPath === "rotation" /* ROTATION */) {
                             var posRotScale = useQuaternion ? newPositionRotationOrScale : BABYLON.Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(posRotScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
                                 if (!babylonTransformNode.parent) {
                                     posRotScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
                                 }
@@ -2901,7 +2919,7 @@ var BABYLON;
                         }
                         else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     newPositionRotationOrScale.x *= -1;
                                     newPositionRotationOrScale.z *= -1;
@@ -2914,7 +2932,7 @@ var BABYLON;
                 else if (animationType === BABYLON.Animation.ANIMATIONTYPE_QUATERNION) {
                     value = keyFrame.value.normalize().asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(value);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(value);
                         if (!babylonTransformNode.parent) {
                             value = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(value)).asArray();
                         }
@@ -3002,7 +3020,7 @@ var BABYLON;
                                 tangent = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                             }
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(tangent);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(tangent);
                                 if (!babylonTransformNode.parent) {
                                     tangent = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(tangent)).asArray();
                                 }
@@ -3017,7 +3035,7 @@ var BABYLON;
                             tangent = tangentValue.scale(frameDelta).asArray();
                             if (convertToRightHandedSystem) {
                                 if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
-                                    GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(tangent);
+                                    GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(tangent);
                                     if (!babylonTransformNode.parent) {
                                         tangent[0] *= -1; // x
                                         tangent[2] *= -1; // z
@@ -3074,7 +3092,7 @@ var BABYLON;
              * @param name name of the buffer view
              * @returns bufferView for glTF
              */
-            _GLTFUtilities.CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
+            _GLTFUtilities._CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
                 var bufferview = { buffer: bufferIndex, byteLength: byteLength };
                 if (byteOffset) {
                     bufferview.byteOffset = byteOffset;
@@ -3099,7 +3117,7 @@ var BABYLON;
              * @param max Maximum value of each component in this attribute
              * @returns accessor for glTF
              */
-            _GLTFUtilities.CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
+            _GLTFUtilities._CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
                 var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
                 if (min != null) {
                     accessor.min = min;
@@ -3119,7 +3137,7 @@ var BABYLON;
              * @param vertexCount Number of vertices to check for min and max values
              * @returns min number array and max number array
              */
-            _GLTFUtilities.CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
+            _GLTFUtilities._CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
                 var min = [Infinity, Infinity, Infinity];
                 var max = [-Infinity, -Infinity, -Infinity];
                 var positionStrideSize = 3;
@@ -3131,7 +3149,7 @@ var BABYLON;
                         indexOffset = positionStrideSize * i;
                         position = BABYLON.Vector3.FromArray(positions, indexOffset);
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedPositionVector3FromRef(position);
+                            _GLTFUtilities._GetRightHandedPositionVector3FromRef(position);
                         }
                         vector = position.asArray();
                         for (var j = 0; j < positionStrideSize; ++j) {
@@ -3153,21 +3171,21 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedPositionVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
@@ -3175,28 +3193,28 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedNormalVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedVector4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedVector4FromRef = function (vector) {
                 vector.z *= -1;
                 vector.w *= -1;
             };
@@ -3204,7 +3222,7 @@ var BABYLON;
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedArray4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedArray4FromRef = function (vector) {
                 vector[2] *= -1;
                 vector[3] *= -1;
             };
@@ -3212,7 +3230,7 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionFromRef = function (quaternion) {
                 quaternion.x *= -1;
                 quaternion.y *= -1;
             };
@@ -3220,10 +3238,16 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionArrayFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionArrayFromRef = function (quaternion) {
                 quaternion[0] *= -1;
                 quaternion[1] *= -1;
             };
+            _GLTFUtilities._NormalizeTangentFromRef = function (tangent) {
+                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
+                tangent.x /= length;
+                tangent.y /= length;
+                tangent.z /= length;
+            };
             return _GLTFUtilities;
         }());
         GLTF2._GLTFUtilities = _GLTFUtilities;

ファイルの差分が大きいため隠しています
+ 2 - 2
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 16 - 15
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -568,7 +568,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -583,7 +583,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -812,7 +812,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -825,7 +825,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -833,7 +833,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -842,52 +842,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 100 - 76
dist/preview release/serializers/babylonjs.serializers.js

@@ -517,12 +517,26 @@ var BABYLON;
                     var vertex = vertices_1[_i];
                     if (this._convertToRightHandedSystem && !(vertexAttributeKind === BABYLON.VertexBuffer.ColorKind) && !(vertex instanceof BABYLON.Vector2)) {
                         if (vertex instanceof BABYLON.Vector3) {
-                            (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
+                            if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertex);
+                            }
+                            else if (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertex);
+                            }
+                            else {
+                                BABYLON.Tools.Error('Unsupported vertex attribute kind!');
+                            }
                         }
                         else {
-                            GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertex);
+                            GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertex);
                         }
                     }
+                    if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                        vertex.normalize();
+                    }
+                    else if (vertexAttributeKind === BABYLON.VertexBuffer.TangentKind && vertex instanceof BABYLON.Vector4) {
+                        GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertex);
+                    }
                     for (var _a = 0, _b = vertex.asArray(); _a < _b.length; _a++) {
                         var component = _b[_a];
                         binaryWriter.setFloat32(component, byteOffset);
@@ -548,7 +562,7 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertexData);
                             }
                             vertexAttributes.push(vertexData.asArray());
                         }
@@ -559,8 +573,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertexData);
                             }
+                            vertexData.normalize();
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -570,8 +585,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector4.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertexData);
                             }
+                            GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertexData);
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -663,7 +679,7 @@ var BABYLON;
                             if (image.uri) {
                                 imageData = _this._imageData[image.uri];
                                 imageName = image.uri.split('.')[0] + " image";
-                                bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
+                                bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                                 byteOffset += imageData.data.buffer.byteLength;
                                 _this._bufferViews.push(bufferView);
                                 image.bufferView = _this._bufferViews.length - 1;
@@ -813,7 +829,7 @@ var BABYLON;
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode) {
                 if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
                 }
                 if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
                     node.scale = babylonTransformNode.scaling.asArray();
@@ -824,7 +840,7 @@ var BABYLON;
                 }
                 if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
                     if (this._convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                     }
                     node.rotation = rotationQuaternion.normalize().asArray();
                 }
@@ -852,7 +868,7 @@ var BABYLON;
                     var vertexData = bufferMesh.getVerticesData(kind);
                     if (vertexData) {
                         var byteLength = vertexData.length * 4;
-                        var bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
+                        var bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                         this._bufferViews.push(bufferView);
                         this.writeAttributeData(kind, vertexData, byteStride, binaryWriter);
                     }
@@ -993,7 +1009,7 @@ var BABYLON;
                         var indices = bufferMesh.getIndices();
                         if (indices) {
                             var byteLength = indices.length * 4;
-                            bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
+                            bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                             this._bufferViews.push(bufferView);
                             indexBufferViewIndex = this._bufferViews.length - 1;
                             for (var k = 0, length_7 = indices.length; k < length_7; ++k) {
@@ -1052,9 +1068,9 @@ var BABYLON;
                                         if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                             minMax = { min: null, max: null };
                                             if (attributeKind == BABYLON.VertexBuffer.PositionKind) {
-                                                minMax = GLTF2._GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                                minMax = GLTF2._GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
                                             }
-                                            var accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                            var accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
                                             this._accessors.push(accessor);
                                             this.setAttributeKind(meshPrimitive, attributeKind);
                                             if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
@@ -1066,7 +1082,7 @@ var BABYLON;
                             }
                             if (indexBufferViewIndex) {
                                 // Create accessor
-                                var accessor = GLTF2._GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
+                                var accessor = GLTF2._GLTFUtilities._CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
                                 this._accessors.push(accessor);
                                 meshPrimitive.indices = this._accessors.length - 1;
                             }
@@ -2120,7 +2136,7 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._gonvertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var promises = [];
                 var metallicRoughness = {
                     baseColor: babylonPBRMaterial.albedoColor,
@@ -2252,39 +2268,41 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughness = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var _this = this;
-                var samplers = this._exporter._samplers;
-                var textures = this._exporter._textures;
-                var specGloss = {
-                    diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
-                    specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
-                    glossiness: babylonPBRMaterial.microSurface || 1,
-                };
-                var samplerIndex = null;
-                var sampler = this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
-                if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
-                    samplers.push(sampler);
-                    samplerIndex = samplers.length - 1;
-                }
-                if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
-                    return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
-                }
-                return this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
-                    if (hasTextureCoords) {
-                        if (metallicRoughnessFactors.baseColorTextureBase64) {
-                            var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFBaseColorTexture) {
-                                glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                return Promise.resolve().then(function () {
+                    var samplers = _this._exporter._samplers;
+                    var textures = _this._exporter._textures;
+                    var specGloss = {
+                        diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
+                        specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
+                        glossiness: babylonPBRMaterial.microSurface || 1,
+                    };
+                    var samplerIndex = null;
+                    var sampler = _this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
+                    if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
+                        samplers.push(sampler);
+                        samplerIndex = samplers.length - 1;
+                    }
+                    if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                        return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
+                    }
+                    if ((babylonPBRMaterial.albedoTexture || babylonPBRMaterial.reflectivityTexture) && hasTextureCoords) {
+                        return _this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
+                            if (metallicRoughnessFactors.baseColorTextureBase64) {
+                                var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFBaseColorTexture) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
                             }
-                        }
-                        if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
-                            var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFMRColorTexture) {
-                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                            if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
+                                var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFMRColorTexture) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
                             }
-                        }
-                        return metallicRoughnessFactors;
+                            return metallicRoughnessFactors;
+                        });
                     }
                     else {
                         return _this._convertSpecularGlossinessToMetallicRoughness(specGloss);
@@ -2317,12 +2335,12 @@ var BABYLON;
                             babylonPBRMaterial.alpha
                         ];
                     }
-                    return this._gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
                 else {
-                    return this._convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
@@ -2748,26 +2766,26 @@ var BABYLON;
                     var nodeIndex = nodeMap[babylonTransformNode.uniqueId];
                     // Creates buffer view and accessor for key frames.
                     var byteLength = animationData.inputs.length * 4;
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
                     bufferViews.push(bufferView);
                     animationData.inputs.forEach(function (input) {
                         binaryWriter.setFloat32(input);
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
                     accessors.push(accessor);
                     keyframeAccessorIndex = accessors.length - 1;
                     // create bufferview and accessor for keyed values.
                     outputLength = animationData.outputs.length;
                     byteLength = dataAccessorType === "VEC3" /* VEC3 */ ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
                     // check for in and out tangents
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
                     bufferViews.push(bufferView);
                     animationData.outputs.forEach(function (output) {
                         output.forEach(function (entry) {
                             binaryWriter.setFloat32(entry);
                         });
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
                     accessors.push(accessor);
                     dataAccessorIndex = accessors.length - 1;
                     // create sampler
@@ -2907,7 +2925,7 @@ var BABYLON;
                             BABYLON.Quaternion.RotationYawPitchRollToRef(cacheValue.y, cacheValue.x, cacheValue.z, quaternionCache);
                         }
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(quaternionCache);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(quaternionCache);
                             if (!babylonTransformNode.parent) {
                                 quaternionCache = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(quaternionCache);
                             }
@@ -2917,7 +2935,7 @@ var BABYLON;
                     else {
                         cacheValue = value;
                         if (convertToRightHandedSystem && (animationChannelTargetPath !== "scale" /* SCALE */)) {
-                            GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(cacheValue);
+                            GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(cacheValue);
                             if (!babylonTransformNode.parent) {
                                 cacheValue.x *= -1;
                                 cacheValue.z *= -1;
@@ -2972,7 +2990,7 @@ var BABYLON;
                         if (babylonTransformNode.rotationQuaternion) {
                             basePositionRotationOrScale = babylonTransformNode.rotationQuaternion.asArray();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     basePositionRotationOrScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(basePositionRotationOrScale)).asArray();
                                 }
@@ -2984,13 +3002,13 @@ var BABYLON;
                     }
                     else {
                         basePositionRotationOrScale = babylonTransformNode.rotation.asArray();
-                        GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                     basePositionRotationOrScale = babylonTransformNode.position.asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else { // scale
@@ -3018,7 +3036,7 @@ var BABYLON;
                         var array = BABYLON.Vector3.FromArray(value);
                         var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z);
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                             if (!babylonTransformNode.parent) {
                                 rotationQuaternion = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(rotationQuaternion);
                             }
@@ -3027,7 +3045,7 @@ var BABYLON;
                     }
                     else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(value);
+                            GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(value);
                             if (!babylonTransformNode.parent) {
                                 value[0] *= -1;
                                 value[2] *= -1;
@@ -3042,7 +3060,7 @@ var BABYLON;
                         if (animationChannelTargetPath === "rotation" /* ROTATION */) {
                             var posRotScale = useQuaternion ? newPositionRotationOrScale : BABYLON.Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(posRotScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
                                 if (!babylonTransformNode.parent) {
                                     posRotScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
                                 }
@@ -3051,7 +3069,7 @@ var BABYLON;
                         }
                         else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     newPositionRotationOrScale.x *= -1;
                                     newPositionRotationOrScale.z *= -1;
@@ -3064,7 +3082,7 @@ var BABYLON;
                 else if (animationType === BABYLON.Animation.ANIMATIONTYPE_QUATERNION) {
                     value = keyFrame.value.normalize().asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(value);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(value);
                         if (!babylonTransformNode.parent) {
                             value = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(value)).asArray();
                         }
@@ -3152,7 +3170,7 @@ var BABYLON;
                                 tangent = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                             }
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(tangent);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(tangent);
                                 if (!babylonTransformNode.parent) {
                                     tangent = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(tangent)).asArray();
                                 }
@@ -3167,7 +3185,7 @@ var BABYLON;
                             tangent = tangentValue.scale(frameDelta).asArray();
                             if (convertToRightHandedSystem) {
                                 if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
-                                    GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(tangent);
+                                    GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(tangent);
                                     if (!babylonTransformNode.parent) {
                                         tangent[0] *= -1; // x
                                         tangent[2] *= -1; // z
@@ -3224,7 +3242,7 @@ var BABYLON;
              * @param name name of the buffer view
              * @returns bufferView for glTF
              */
-            _GLTFUtilities.CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
+            _GLTFUtilities._CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
                 var bufferview = { buffer: bufferIndex, byteLength: byteLength };
                 if (byteOffset) {
                     bufferview.byteOffset = byteOffset;
@@ -3249,7 +3267,7 @@ var BABYLON;
              * @param max Maximum value of each component in this attribute
              * @returns accessor for glTF
              */
-            _GLTFUtilities.CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
+            _GLTFUtilities._CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
                 var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
                 if (min != null) {
                     accessor.min = min;
@@ -3269,7 +3287,7 @@ var BABYLON;
              * @param vertexCount Number of vertices to check for min and max values
              * @returns min number array and max number array
              */
-            _GLTFUtilities.CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
+            _GLTFUtilities._CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
                 var min = [Infinity, Infinity, Infinity];
                 var max = [-Infinity, -Infinity, -Infinity];
                 var positionStrideSize = 3;
@@ -3281,7 +3299,7 @@ var BABYLON;
                         indexOffset = positionStrideSize * i;
                         position = BABYLON.Vector3.FromArray(positions, indexOffset);
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedPositionVector3FromRef(position);
+                            _GLTFUtilities._GetRightHandedPositionVector3FromRef(position);
                         }
                         vector = position.asArray();
                         for (var j = 0; j < positionStrideSize; ++j) {
@@ -3303,21 +3321,21 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedPositionVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
@@ -3325,28 +3343,28 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedNormalVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedVector4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedVector4FromRef = function (vector) {
                 vector.z *= -1;
                 vector.w *= -1;
             };
@@ -3354,7 +3372,7 @@ var BABYLON;
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedArray4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedArray4FromRef = function (vector) {
                 vector[2] *= -1;
                 vector[3] *= -1;
             };
@@ -3362,7 +3380,7 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionFromRef = function (quaternion) {
                 quaternion.x *= -1;
                 quaternion.y *= -1;
             };
@@ -3370,10 +3388,16 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionArrayFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionArrayFromRef = function (quaternion) {
                 quaternion[0] *= -1;
                 quaternion[1] *= -1;
             };
+            _GLTFUtilities._NormalizeTangentFromRef = function (tangent) {
+                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
+                tangent.x /= length;
+                tangent.y /= length;
+                tangent.z /= length;
+            };
             return _GLTFUtilities;
         }());
         GLTF2._GLTFUtilities = _GLTFUtilities;

ファイルの差分が大きいため隠しています
+ 2 - 2
dist/preview release/serializers/babylonjs.serializers.min.js


+ 16 - 15
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -575,7 +575,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -590,7 +590,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -819,7 +819,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -832,7 +832,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -840,7 +840,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -849,52 +849,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 2 - 58
dist/preview release/typedocValidationBaseline.json

@@ -1,7 +1,7 @@
 {
-  "errors": 4295,
+  "errors": 4283,
   "babylon.typedoc.json": {
-    "errors": 4295,
+    "errors": 4283,
     "AbstractScene": {
       "Property": {
         "effectLayers": {
@@ -6303,9 +6303,6 @@
     "GPUParticleSystem": {
       "Method": {
         "addColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          },
           "Parameter": {
             "color1": {
               "Comments": {
@@ -6313,21 +6310,6 @@
               }
             }
           }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
         }
       }
     },
@@ -10792,31 +10774,11 @@
             "MissingReturn": true
           }
         },
-        "addLifeTimeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
         "removeColorGradient": {
           "Comments": {
             "MissingReturn": true
           }
         },
-        "removeLifeTimeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
         "_GetEffectCreationOptions": {
           "Comments": {
             "MissingText": true
@@ -22879,9 +22841,6 @@
     "IParticleSystem": {
       "Method": {
         "addColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          },
           "Parameter": {
             "color1": {
               "Comments": {
@@ -22889,21 +22848,6 @@
               }
             }
           }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
         }
       }
     },

+ 4 - 0
dist/preview release/viewer/babylon.viewer.d.ts

@@ -1510,6 +1510,10 @@ declare module BabylonViewer {
                 */
             onViewerInitDoneObservable: BABYLON.Observable<any>;
             /**
+                * Will notify when the viewer init started (after configuration was loaded)
+                */
+            onViewerInitStartedObservable: BABYLON.Observable<any>;
+            /**
                 * Functions added to this observable will be executed on each frame rendered.
                 */
             onFrameRenderedObservable: BABYLON.Observable<any>;

ファイルの差分が大きいため隠しています
+ 67 - 67
dist/preview release/viewer/babylon.viewer.js


ファイルの差分が大きいため隠しています
+ 315 - 141
dist/preview release/viewer/babylon.viewer.max.js


+ 4 - 0
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -1510,6 +1510,10 @@ declare module 'babylonjs-viewer/managers/observablesManager' {
                 */
             onViewerInitDoneObservable: Observable<any>;
             /**
+                * Will notify when the viewer init started (after configuration was loaded)
+                */
+            onViewerInitStartedObservable: Observable<any>;
+            /**
                 * Functions added to this observable will be executed on each frame rendered.
                 */
             onFrameRenderedObservable: Observable<any>;

+ 1 - 0
dist/preview release/what's new.md

@@ -23,6 +23,7 @@
   - Added support for `minInitialRotation` and `maxInitialRotation`. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
   - Added support for size gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
   - Added support for life time gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#lifetime)
+  - Added support for angular speed gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
 - Added SceneComponent to help decoupling Scene from its components ([sebavan](http://www.github.com/sebavan))
 
 ## Updates

+ 24 - 0
src/Particles/babylon.IParticleSystem.ts

@@ -255,11 +255,13 @@ module BABYLON {
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
          * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
+         * @returns the current particle system
          */
         addColorGradient(gradient: number, color1: Color4, color2?: Color4): IParticleSystem;   
         /**
          * Remove a specific color gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         removeColorGradient(gradient: number): IParticleSystem;
         /**
@@ -267,11 +269,13 @@ module BABYLON {
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the size factor to affect to the specified gradient
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         addSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         removeSizeGradient(gradient: number): IParticleSystem;
         /**
@@ -286,5 +290,25 @@ module BABYLON {
          * @returns the list of size gradients
          */
         getSizeGradients(): Nullable<Array<FactorGradient>>;
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        getAngularSpeedGradients(): Nullable<Array<FactorGradient>>;   
+        /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addAngularSpeedGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeAngularSpeedGradient(gradient: number): IParticleSystem;        
     }  
 }

+ 158 - 71
src/Particles/babylon.gpuParticleSystem.ts

@@ -63,7 +63,9 @@
         private _updateEffectOptions: EffectCreationOptions;
 
         private _randomTextureSize: number;
-        private _actualFrame = 0;        
+        private _actualFrame = 0;      
+        
+        private readonly _rawTextureWidth = 256;
 
         /**
          * List of animations used by the particle system.
@@ -441,13 +443,46 @@
          */
         public getSizeGradients(): Nullable<Array<FactorGradient>> {
             return this._sizeGradients;
-        }               
+        }      
+        
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
+            return this._angularSpeedGradients;
+        } 
+
+        private _removeGradient(gradient: number, gradients: Nullable<IValueGradient[]>, texture: RawTexture): GPUParticleSystem {
+            if (!gradients) {
+                return this;
+            }
+
+            let index = 0;
+            for (var valueGradient of gradients) {
+                if (valueGradient.gradient === gradient) {
+                    gradients.splice(index, 1);
+                    break;
+                }
+                index++;
+            }
+
+            if (texture) {
+                texture.dispose();
+            }            
+
+            this._releaseBuffers();
+
+            return this;
+        }    
         
         /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
          * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
+         * @returns the current particle system
          */
         public addColorGradient(gradient: number, color1: Color4, color2?: Color4): GPUParticleSystem {
             if (!this._colorGradients) {
@@ -482,58 +517,52 @@
         /**
          * Remove a specific color gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeColorGradient(gradient: number): GPUParticleSystem {
-            if (!this._colorGradients) {
-                return this;
-            }
-
-            let index = 0;
-            for (var colorGradient of this._colorGradients) {
-                if (colorGradient.gradient === gradient) {
-                    this._colorGradients.splice(index, 1);
-                    break;
-                }
-                index++;
-            }
-
-            if (this._colorGradientsTexture) {
-                this._colorGradientsTexture.dispose();
-                (<any>this._colorGradientsTexture) = null;
-            }            
-
-            this._releaseBuffers();
+            this._removeGradient(gradient, this._colorGradients, this._colorGradientsTexture);
+            (<any>this._colorGradientsTexture) = null;
 
             return this;
         }    
+
+        private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
+        private _angularSpeedGradientsTexture: RawTexture;    
         
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
-        private _sizeGradientsTexture: RawTexture;        
+        private _sizeGradientsTexture: RawTexture;     
+        
+        private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number) {
+            let valueGradient = new FactorGradient();
+            valueGradient.gradient = gradient;
+            valueGradient.factor1 = factor;
+            factorGradients.push(valueGradient);
+
+            factorGradients.sort((a, b) => {
+                if (a.gradient < b.gradient) {
+                    return -1;
+                } else if (a.gradient > b.gradient) {
+                    return 1;
+                }
+
+                return 0;
+            });
+
+            this._releaseBuffers();               
+        }
         
         /**
          * Adds a new size gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the size factor to affect to the specified gradient
+         * @returns the current particle system
          */
         public addSizeGradient(gradient: number, factor: number): GPUParticleSystem {
             if (!this._sizeGradients) {
                 this._sizeGradients = [];
             }
 
-            let sizeGradient = new FactorGradient();
-            sizeGradient.gradient = gradient;
-            sizeGradient.factor1 = factor;
-            this._sizeGradients.push(sizeGradient);
-
-            this._sizeGradients.sort((a, b) => {
-                if (a.gradient < b.gradient) {
-                    return -1;
-                } else if (a.gradient > b.gradient) {
-                    return 1;
-                }
-
-                return 0;
-            });
+            this._addFactorGradient(this._sizeGradients, gradient, factor);
 
             if (this._sizeGradientsTexture) {
                 this._sizeGradientsTexture.dispose();
@@ -548,29 +577,48 @@
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeSizeGradient(gradient: number): GPUParticleSystem {
-            if (!this._sizeGradients) {
-                return this;
-            }
+            this._removeGradient(gradient, this._sizeGradients, this._sizeGradientsTexture);
+            (<any>this._sizeGradientsTexture) = null;
 
-            let index = 0;
-            for (var sizeGradient of this._sizeGradients) {
-                if (sizeGradient.gradient === gradient) {
-                    this._sizeGradients.splice(index, 1);
-                    break;
-                }
-                index++;
+            return this;            
+        }   
+        
+        /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient    
+         * @returns the current particle system     
+         */
+        public addAngularSpeedGradient(gradient: number, factor: number): GPUParticleSystem {
+            if (!this._angularSpeedGradients) {
+                this._angularSpeedGradients = [];
             }
 
-            if (this._sizeGradientsTexture) {
-                this._sizeGradientsTexture.dispose();
-                (<any>this._sizeGradientsTexture) = null;
+            this._addFactorGradient(this._angularSpeedGradients, gradient, factor);
+
+            if (this._angularSpeedGradientsTexture) {
+                this._angularSpeedGradientsTexture.dispose();
+                (<any>this._angularSpeedGradientsTexture) = null;
             }
 
-            this._releaseBuffers();               
+            this._releaseBuffers();   
 
             return this;
+        }
+
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeAngularSpeedGradient(gradient: number): GPUParticleSystem {
+            this._removeGradient(gradient, this._angularSpeedGradients, this._angularSpeedGradientsTexture);
+            (<any>this._angularSpeedGradientsTexture) = null;
+
+            return this;           
         }            
 
         /**
@@ -618,7 +666,7 @@
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
                                 "angleRange", "radiusRange", "cellInfos"],
                 uniformBuffersNames: [],
-                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler"],
                 defines: "",
                 fallbacks: null,  
                 onCompiled: null,
@@ -679,8 +727,13 @@
                 offset += 3;
             }
 
-            updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2);
-            offset += 2;
+            if (this._angularSpeedGradientsTexture) {
+                updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 1);
+                offset += 1;
+            } else {
+                updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2);
+                offset += 2;
+            }
 
             if (this._isAnimationSheetEnabled) {
                 updateVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1);
@@ -713,8 +766,13 @@
                 renderVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3, this._attributesStrideSize, true);
                 offset += 3;
             }
-            renderVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2, this._attributesStrideSize, true);
-            offset += 2;
+
+            renderVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 1, this._attributesStrideSize, true);
+            if (this._angularSpeedGradientsTexture) {
+                offset++;
+            } else {
+                offset += 2;
+            }
 
             if (this._isAnimationSheetEnabled) {
                 renderVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1, this._attributesStrideSize, true);
@@ -746,6 +804,10 @@
                 this._attributesStrideSize -= 4;
             }
 
+            if (this._angularSpeedGradientsTexture) {
+                this._attributesStrideSize -= 1;
+            }            
+
             if (this._isAnimationSheetEnabled) {
                 this._attributesStrideSize += 1;
             }            
@@ -793,7 +855,10 @@
 
                 // angle
                 data.push(0.0);  
-                data.push(0.0); 
+
+                if (!this._angularSpeedGradientsTexture) {
+                    data.push(0.0); 
+                }
 
                 if (this._isAnimationSheetEnabled) {
                     data.push(0.0); 
@@ -842,6 +907,10 @@
             if (this._sizeGradientsTexture) {
                 defines += "\n#define SIZEGRADIENTS";
             }     
+
+            if (this._angularSpeedGradientsTexture) {
+                defines += "\n#define ANGULARSPEEDGRADIENTS";
+            }                 
             
             if (this.isAnimationSheetEnabled) {
                 defines += "\n#define ANIMATESHEET";
@@ -926,36 +995,44 @@
             }             
         }    
 
-        private _createSizeGradientTexture() {
-            if (!this._sizeGradients || !this._sizeGradients.length || this._sizeGradientsTexture) {
+        private _createFactorGradientTexture(factorGradients: Nullable<IValueGradient[]>, textureName: string) {
+            let texture:RawTexture = (<any>this)[textureName];
+
+            if (!factorGradients || !factorGradients.length || texture) {
                 return;
             }
 
-            let textureWidth = 256;
-            let data = new Float32Array(textureWidth);
+            let data = new Float32Array(this._rawTextureWidth);
 
-            for (var x = 0; x < textureWidth; x++) {
-                var ratio = x / textureWidth;
+            for (var x = 0; x < this._rawTextureWidth; x++) {
+                var ratio = x / this._rawTextureWidth;
 
-                Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
+                Tools.GetCurrentGradient(ratio, factorGradients, (currentGradient, nextGradient, scale) => {
                     data[x] = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
                 });
             }
 
-            this._sizeGradientsTexture = RawTexture.CreateRTexture(data, textureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
-        }        
+            (<any>this)[textureName] = RawTexture.CreateRTexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+        }            
+
+        private _createSizeGradientTexture() {
+            this._createFactorGradientTexture(this._sizeGradients, "_sizeGradientsTexture");
+        }     
+        
+        private _createAngularSpeedGradientTexture() {
+            this._createFactorGradientTexture(this._angularSpeedGradients, "_angularSpeedGradientsTexture");
+        }     
             
         private _createColorGradientTexture() {
             if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
                 return;
             }
 
-            let textureWidth = 256;
-            let data = new Uint8Array(textureWidth * 4);
+            let data = new Uint8Array(this._rawTextureWidth * 4);
             let tmpColor = Tmp.Color4[0];
 
-            for (var x = 0; x < textureWidth; x++) {
-                var ratio = x / textureWidth;
+            for (var x = 0; x < this._rawTextureWidth; x++) {
+                var ratio = x / this._rawTextureWidth;
 
                 Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
 
@@ -968,7 +1045,7 @@
 
             }
 
-            this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, textureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+            this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
         }
 
         /**
@@ -983,6 +1060,7 @@
 
             this._createColorGradientTexture();
             this._createSizeGradientTexture();
+            this._createAngularSpeedGradientTexture();
 
             this._recreateUpdateEffect();
             this._recreateRenderEffect();
@@ -1046,6 +1124,10 @@
                 this._updateEffect.setTexture("sizeGradientSampler", this._sizeGradientsTexture);      
             }
 
+            if (this._angularSpeedGradientsTexture) {      
+                this._updateEffect.setTexture("angularSpeedGradientSampler", this._angularSpeedGradientsTexture);      
+            }
+
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }
@@ -1205,7 +1287,12 @@
             if (this._sizeGradientsTexture) {
                 this._sizeGradientsTexture.dispose();
                 (<any>this._sizeGradientsTexture) = null;
-            }            
+            }    
+            
+            if (this._angularSpeedGradientsTexture) {
+                this._angularSpeedGradientsTexture.dispose();
+                (<any>this._angularSpeedGradientsTexture) = null;
+            }             
          
             if (this._randomTexture) {
                 this._randomTexture.dispose();

+ 13 - 1
src/Particles/babylon.particle.ts

@@ -79,7 +79,14 @@
         /** @hidden */
         public _currentSize1 = 0;
         /** @hidden */
-        public _currentSize2 = 0;        
+        public _currentSize2 = 0;      
+        
+        /** @hidden */
+        public _currentAngularSpeedGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentAngularSpeed1 = 0;
+        /** @hidden */
+        public _currentAngularSpeed2 = 0;          
 
         /**
          * Creates a new instance Particle
@@ -147,6 +154,11 @@
                 other._currentSize1 = this._currentSize1;
                 other._currentSize2 = this._currentSize2;
             }
+            if (this._currentAngularSpeedGradient) {
+                other._currentAngularSpeedGradient = this._currentAngularSpeedGradient;
+                other._currentAngularSpeed1 = this._currentAngularSpeed1;
+                other._currentAngularSpeed2 = this._currentAngularSpeed2;
+            }            
             if (this.particleSystem.isAnimationSheetEnabled) {
                 other._initialStartSpriteCellID = this._initialStartSpriteCellID;
                 other._initialEndSpriteCellID = this._initialEndSpriteCellID;

+ 92 - 4
src/Particles/babylon.particleSystem.ts

@@ -187,6 +187,7 @@
         private _colorGradients: Nullable<Array<ColorGradient>> = null;
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
         private _lifeTimeGradients: Nullable<Array<FactorGradient>> = null;
+        private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
 
         /**
          * Gets the current list of color gradients.
@@ -213,7 +214,16 @@
          */
         public getLifeTimeGradients(): Nullable<Array<FactorGradient>> {
             return this._lifeTimeGradients;
-        }          
+        }   
+        
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
+            return this._angularSpeedGradients;
+        } 
 
         /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
@@ -523,6 +533,17 @@
                                 particle.color.a = 0;
                             }
                         }
+
+                        if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {                  
+                            Tools.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
+                                if (currentGradient !== particle._currentAngularSpeedGradient) {
+                                    particle._currentAngularSpeed1 = particle._currentAngularSpeed2;
+                                    particle._currentAngularSpeed2 = (<FactorGradient>nextGradient).getFactor();    
+                                    particle._currentAngularSpeedGradient = (<FactorGradient>currentGradient);
+                                }                                
+                                particle.angularSpeed = Scalar.Lerp(particle._currentAngularSpeed1, particle._currentAngularSpeed2, scale);
+                            });
+                        }                        
                         particle.angle += particle.angularSpeed * this._scaledUpdateSpeed;
 
                         particle.direction.scaleToRef(this._scaledUpdateSpeed, this._scaledDirection);
@@ -531,7 +552,7 @@
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
                         particle.direction.addInPlace(this._scaledGravity);
 
-                        // Gradient
+                        // Size
                         if (this._sizeGradients && this._sizeGradients.length > 0) {                  
                             Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
                                 if (currentGradient !== particle._currentSizeGradient) {
@@ -589,6 +610,7 @@
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the life time factor to affect to the specified gradient         
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         public addLifeTimeGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
             if (!this._lifeTimeGradients) {
@@ -603,6 +625,7 @@
         /**
          * Remove a specific life time gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeLifeTimeGradient(gradient: number): ParticleSystem {
             this._removeFactorGradient(this._lifeTimeGradients, gradient);
@@ -615,6 +638,7 @@
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the size factor to affect to the specified gradient         
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         public addSizeGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
             if (!this._sizeGradients) {
@@ -629,6 +653,7 @@
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeSizeGradient(gradient: number): ParticleSystem {
             this._removeFactorGradient(this._sizeGradients, gradient);
@@ -637,6 +662,34 @@
         }        
 
         /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addAngularSpeedGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
+            if (!this._angularSpeedGradients) {
+                this._angularSpeedGradients = [];
+            }
+
+            this._addFactorGradient(this._angularSpeedGradients, gradient, factor, factor2);
+
+            return this;
+        }
+
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeAngularSpeedGradient(gradient: number): ParticleSystem {
+            this._removeFactorGradient(this._angularSpeedGradients, gradient);
+
+            return this;
+        }           
+
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
@@ -1043,7 +1096,19 @@
                 particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
 
                 // Angle
-                particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
+                if (!this._angularSpeedGradients || this._angularSpeedGradients.length === 0) {
+                    particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
+                } else {
+                    particle._currentAngularSpeedGradient = this._angularSpeedGradients[0];
+                    particle.angularSpeed =  particle._currentAngularSpeedGradient.getFactor();
+                    particle._currentAngularSpeed1 = particle.angularSpeed;
+
+                    if (this._angularSpeedGradients.length > 1) {
+                        particle._currentAngularSpeed2 = this._angularSpeedGradients[1].getFactor();
+                    } else {
+                        particle._currentAngularSpeed2 = particle._currentAngularSpeed1;
+                    }
+                }
                 particle.angle = Scalar.RandomRange(this.minInitialRotation, this.maxInitialRotation);
 
                 // Color
@@ -1580,8 +1645,25 @@
 
                     serializationObject.sizeGradients.push(serializedGradient);
                 }
-            }            
+            }       
+                        
+            let angularSpeedGradients = particleSystem.getAngularSpeedGradients();
+            if (angularSpeedGradients) {
+                serializationObject.angularSpeedGradients = [];
+                for (var angularSpeedGradient of angularSpeedGradients) {
 
+                    var serializedGradient: any = {
+                        gradient: angularSpeedGradient.gradient,
+                        factor1: angularSpeedGradient.factor1
+                    };
+
+                    if (angularSpeedGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = angularSpeedGradient.factor2;
+                    }
+
+                    serializationObject.angularSpeedGradients.push(serializedGradient);
+                }
+            }  
         }
 
         /** @hidden */
@@ -1671,6 +1753,12 @@
                     particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ?  sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
                 }
             }       
+
+            if (parsedParticleSystem.angularSpeedGradients) {
+                for (var angularSpeedGradient of parsedParticleSystem.angularSpeedGradients) {
+                    particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ?  angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
+                }
+            }               
             
             // Emitter
             let emitterType: IParticleEmitterType;

+ 7 - 7
src/Shaders/gpuRenderParticles.vertex.fx

@@ -13,7 +13,7 @@ in vec3 size;
 #ifndef BILLBOARD
 in vec3 initialDirection;
 #endif
-in vec2 angle;
+in float angle;
 #ifdef ANIMATESHEET
 in float cellIndex;
 #endif
@@ -85,8 +85,8 @@ void main() {
 	rotatedCorner.w = 0.;
 
 	#ifdef BILLBOARDY	
-		rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
-		rotatedCorner.z = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+		rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
+		rotatedCorner.z = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 		rotatedCorner.y = 0.;
 
 		vec3 yaxis = position - eyePosition;
@@ -96,8 +96,8 @@ void main() {
 		vec4 viewPosition = (view * vec4(worldPos, 1.0)); 
 	#else
 		// Rotate
-		rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
-		rotatedCorner.y = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+		rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
+		rotatedCorner.y = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 		rotatedCorner.z = 0.;
 
 		// Expand position
@@ -107,9 +107,9 @@ void main() {
 #else
   // Rotate
 	vec3 rotatedCorner;
-	rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
+	rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
 	rotatedCorner.y = 0.;
-	rotatedCorner.z = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+	rotatedCorner.z = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 
 	vec3 yaxis = normalize(initialDirection);
 	vec3 worldPos = rotate(yaxis, rotatedCorner);

+ 23 - 0
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -57,7 +57,11 @@ in vec3 direction;
 #ifndef BILLBOARD
 in vec3 initialDirection;
 #endif
+#ifdef ANGULARSPEEDGRADIENTS
+in float angle;
+#else
 in vec2 angle;
+#endif
 #ifdef ANIMATESHEET
 in float cellIndex;
 #endif
@@ -75,7 +79,11 @@ out vec3 outDirection;
 #ifndef BILLBOARD
 out vec3 outInitialDirection;
 #endif
+#ifdef ANGULARSPEEDGRADIENTS
+out float outAngle;
+#else
 out vec2 outAngle;
+#endif
 #ifdef ANIMATESHEET
 out float outCellIndex;
 #endif
@@ -84,6 +92,10 @@ out float outCellIndex;
 uniform sampler2D sizeGradientSampler;
 #endif 
 
+#ifdef ANGULARSPEEDGRADIENTS
+uniform sampler2D angularSpeedGradientSampler;
+#endif 
+
 #ifdef ANIMATESHEET
 uniform vec3 cellInfos;
 #endif
@@ -146,8 +158,12 @@ void main() {
 #endif
 
     // Angular speed
+#ifndef ANGULARSPEEDGRADIENTS    
     outAngle.y = angleRange.x + (angleRange.y - angleRange.x) * randoms.a;
     outAngle.x = angleRange.z + (angleRange.w - angleRange.z) * randoms.r;
+#else
+    outAngle = angleRange.z + (angleRange.w - angleRange.z) * randoms.r;
+#endif        
 
     // Position / Direction (based on emitter type)
 #ifdef BOXEMITTER
@@ -240,7 +256,14 @@ void main() {
     outInitialDirection = initialDirection;
 #endif
     outDirection = direction + gravity * timeDelta;
+
+#ifdef ANGULARSPEEDGRADIENTS
+    float angularSpeed = texture(angularSpeedGradientSampler, vec2(age / life, 0)).r;
+    outAngle = angle + angularSpeed * timeDelta;
+#else
     outAngle = vec2(angle.x + angle.y * timeDelta, angle.y);
+#endif
+
 #ifdef ANIMATESHEET      
     float dist = cellInfos.y - cellInfos.x;
     float ratio = clamp(mod(outAge * cellInfos.z, life) / life, 0., 1.0);