Browse Source

Optim step 2

David Catuhe 6 years ago
parent
commit
2c177bcd54
6 changed files with 297 additions and 153 deletions
  1. 9 0
      sandbox/index.js
  2. 72 44
      src/Animations/runtimeAnimation.ts
  3. 1 1
      src/Lights/shadowLight.ts
  4. 28 5
      src/Maths/math.ts
  5. 181 103
      src/Meshes/transformNode.ts
  6. 6 0
      src/node.ts

+ 9 - 0
sandbox/index.js

@@ -213,6 +213,15 @@ if (BABYLON.Engine.isSupported()) {
         if (debugLayerEnabled) {
             currentScene.debugLayer.show();
         }
+
+        currentScene.dispatchAllSubMeshesOfActiveMeshes = true;
+        currentScene.meshes.forEach((mesh) => mesh.alwaysSelectAsActiveMesh = true);
+        currentScene.getEngine().disableTextureBindingOptimization = true;
+        currentScene.meshes.forEach((mesh) => mesh.doNotSyncBoundingInfo = true);
+        currentScene.materials.forEach((mat) => mat.freeze());
+
+        currentScene.meshes.forEach((mesh) => mesh.ignoreNonUniformScaling = true);
+        currentScene.transformNodes.forEach((node) => node.ignoreNonUniformScaling = true);
     };
 
     var sceneError = function(sceneFile, babylonScene, message) {

+ 72 - 44
src/Animations/runtimeAnimation.ts

@@ -96,7 +96,8 @@ export class RuntimeAnimation {
     /**
      * The active target of the runtime animation
      */
-    private _activeTarget: any;
+    private _activeTargets: any[];
+    private _currentActiveTarget: any;
 
     /**
      * The target path of the runtime animation
@@ -158,7 +159,7 @@ export class RuntimeAnimation {
      * Gets the actual target of the runtime animation
      */
     public get target(): any {
-        return this._activeTarget;
+        return this._currentActiveTarget;
     }
 
     /**
@@ -173,9 +174,24 @@ export class RuntimeAnimation {
         this._target = target;
         this._scene = scene;
         this._host = host;
+        this._activeTargets = [];
 
         animation._runtimeAnimations.push(this);
 
+        // Check data
+        if (this._target instanceof Array) {
+            var index = 0;
+            for (const target of this._target) {
+                this._preparePath(target, index);
+                this._getOriginalValues(index);
+                index++;
+            }
+        }
+        else {
+            this._preparePath(this._target);
+            this._getOriginalValues();
+        }
+
         // Cloning events locally
         var events = animation.getEvents();
         if (events && events.length > 0) {
@@ -186,6 +202,30 @@ export class RuntimeAnimation {
 
         this._correctLoopMode = this._getCorrectLoopMode();
         this._enableBlending = target && target.animationPropertiesOverride ? target.animationPropertiesOverride.enableBlending : this._animation.enableBlending;
+
+        if (this._enableBlending) {
+            this._activeBlendingProcessor = this._blendingProcessor;
+        } else {
+            this._activeBlendingProcessor = this._noBlendingProcessor;
+        }
+    }
+
+    private _preparePath(target: any, targetIndex = 0) {
+        let targetPropertyPath = this._animation.targetPropertyPath;
+
+        if (targetPropertyPath.length > 1) {
+            var property = target[targetPropertyPath[0]];
+
+            for (var index = 1; index < targetPropertyPath.length - 1; index++) {
+                property = property[targetPropertyPath[index]];
+            }
+
+            this._targetPath = targetPropertyPath[targetPropertyPath.length - 1];
+            this._activeTargets[targetIndex] = property;
+        } else {
+            this._targetPath = targetPropertyPath[0];
+            this._activeTargets[targetIndex] = target;
+        }
     }
 
     /**
@@ -285,56 +325,33 @@ export class RuntimeAnimation {
         }
     }
 
-    private _setValue(target: any, currentValue: any, weight: number, targetIndex = 0): void {
-        // Set value
-        var path: any;
-        var destination: any;
-
-        if (!this._targetPath) {
-            let targetPropertyPath = this._animation.targetPropertyPath;
+    private _getOriginalValues(targetIndex = 0) {
+        let originalValue: any;
+        let target = this._activeTargets[targetIndex];
 
-            if (targetPropertyPath.length > 1) {
-                var property = target[targetPropertyPath[0]];
-
-                for (var index = 1; index < targetPropertyPath.length - 1; index++) {
-                    property = property[targetPropertyPath[index]];
-                }
-
-                path = targetPropertyPath[targetPropertyPath.length - 1];
-                destination = property;
-            } else {
-                path = targetPropertyPath[0];
-                destination = target;
-            }
-
-            this._targetPath = path;
-            this._activeTarget = destination;
+        if (target.getRestPose && this._targetPath === "_matrix") { // For bones
+            originalValue = target.getRestPose();
         } else {
-            path = this._targetPath;
-            destination = this._activeTarget;
+            originalValue = target[this._targetPath];
         }
-        this._weight = weight;
 
-        if (this._originalValue[targetIndex] === undefined) {
-            let originalValue: any;
+        if (originalValue && originalValue.clone) {
+            this._originalValue[targetIndex] = originalValue.clone();
+        } else {
+            this._originalValue[targetIndex] = originalValue;
+        }
+    }
 
-            if (destination.getRestPose && path === "_matrix") { // For bones
-                originalValue = destination.getRestPose();
-            } else {
-                originalValue = destination[path];
-            }
+    private _activeBlendingProcessor: (currentValue: any, target: any) => void;
 
-            if (originalValue && originalValue.clone) {
-                this._originalValue[targetIndex] = originalValue.clone();
-            } else {
-                this._originalValue[targetIndex] = originalValue;
-            }
-        }
+    private _noBlendingProcessor = (currentValue: any) => {
+        this._currentValue = currentValue;
+    }
 
-        // Blending
-        if (this._enableBlending && this._blendingFactor <= 1.0) {
+    private _blendingProcessor = (currentValue: any, target: any) => {
+        if (this._blendingFactor <= 1.0) {
             if (!this._originalBlendValue) {
-                let originalValue = destination[path];
+                let originalValue = this._currentActiveTarget[this._targetPath];
 
                 if (originalValue.clone) {
                     this._originalBlendValue = originalValue.clone();
@@ -366,6 +383,17 @@ export class RuntimeAnimation {
         } else {
             this._currentValue = currentValue;
         }
+    }
+
+    private _setValue(target: any, currentValue: any, weight: number, targetIndex = 0): void {
+        // Set value
+        var path = this._targetPath;
+        var destination = this._activeTargets[targetIndex];
+        this._currentActiveTarget = destination;
+
+        this._weight = weight;
+
+        this._activeBlendingProcessor(currentValue, target);
 
         if (weight !== -1.0) {
             this._scene._registerTargetForLateAnimationBinding(this, this._originalValue[targetIndex]);

+ 1 - 1
src/Lights/shadowLight.ts

@@ -350,7 +350,7 @@ export abstract class ShadowLight extends Light implements IShadowLight {
         }
 
         // Cache the determinant
-        this._worldMatrixDeterminant = this._worldMatrix.determinant();
+        this._worldMatrixDeterminantIsDirty = true;
 
         return this._worldMatrix;
     }

+ 28 - 5
src/Maths/math.ts

@@ -5293,11 +5293,34 @@ export class Matrix {
      * @param result defines the target matrix
      */
     public static ComposeToRef(scale: DeepImmutable<Vector3>, rotation: DeepImmutable<Quaternion>, translation: DeepImmutable<Vector3>, result: Matrix): void {
-        Matrix.ScalingToRef(scale.x, scale.y, scale.z, MathTmp.Matrix[1]);
-        rotation.toRotationMatrix(MathTmp.Matrix[0]);
-        MathTmp.Matrix[1].multiplyToRef(MathTmp.Matrix[0], result);
-
-        result.setTranslation(translation);
+        let m = result._m;
+        var x = rotation.x, y = rotation.y, z = rotation.z, w = rotation.w;
+        var x2 = x + x, y2 = y + y, z2 = z + z;
+        var xx = x * x2, xy = x * y2, xz = x * z2;
+        var yy = y * y2, yz = y * z2, zz = z * z2;
+        var wx = w * x2, wy = w * y2, wz = w * z2;
+
+        var sx = scale.x, sy = scale.y, sz = scale.z;
+
+        m[0] = (1 - (yy + zz)) * sx;
+        m[1] = (xy + wz) * sx;
+        m[2] = (xz - wy) * sx;
+        m[3] = 0;
+
+        m[4] = (xy - wz) * sy;
+        m[5] = (1 - (xx + zz)) * sy;
+        m[6] = (yz + wx) * sy;
+        m[7] = 0;
+
+        m[8] = (xz + wy) * sz;
+        m[9] = (yz - wx) * sz;
+        m[10] = (1 - (xx + yy)) * sz;
+        m[11] = 0;
+
+        m[12] = translation.x;
+        m[13] = translation.y;
+        m[14] = translation.z;
+        m[15] = 1;
     }
 
     /**

+ 181 - 103
src/Meshes/transformNode.ts

@@ -40,7 +40,6 @@ export class TransformNode extends Node {
     private _up = new Vector3(0, 1, 0);
     private _right = new Vector3(1, 0, 0);
     private _rightInverted = new Vector3(-1, 0, 0);
-    private _usePivotMatrix = false;
 
     // Properties
     @serializeAsVector3("position")
@@ -57,8 +56,11 @@ export class TransformNode extends Node {
     protected _isDirty = false;
     private _transformToBoneReferal: Nullable<TransformNode>;
 
+    @serialize("billboardMode")
+    private _billboardMode = TransformNode.BILLBOARDMODE_NONE;
+
     /**
-    * Set the billboard mode. Default is 0.
+    * Gets or sets the billboard mode. Default is 0.
     *
     * | Value | Type | Description |
     * | --- | --- | --- |
@@ -69,14 +71,36 @@ export class TransformNode extends Node {
     * | 7 | BILLBOARDMODE_ALL |  |
     *
     */
-    @serialize()
-    public billboardMode = TransformNode.BILLBOARDMODE_NONE;
+    public get billboardMode() {
+        return this._billboardMode;
+    }
+
+    public set billboardMode(value: number) {
+        if (this._billboardMode === value) {
+            return;
+        }
+        this._billboardMode = value;
+
+        this._connectBillboardProcessors();
+    }
 
+    private _preserveParentRotationForBillboard = false;
     /**
      * Gets or sets a boolean indicating that parent rotation should be preserved when using billboards.
      * This could be useful for glTF objects where parent rotation helps converting from right handed to left handed
      */
-    public preserveParentRotationForBillboard = false;
+    public get preserveParentRotationForBillboard() {
+        return this._preserveParentRotationForBillboard;
+    };
+
+    public set preserveParentRotationForBillboard(value: boolean) {
+        if (value === this._preserveParentRotationForBillboard) {
+            return;
+        }
+        this._preserveParentRotationForBillboard = value;
+
+        this._connectBillboardProcessors();
+    };
 
     /**
      * Multiplication factor on scale x/y/z when computing the world matrix. Eg. for a 1x1x1 cube setting this to 2 will make it a 2x2x2 cube
@@ -97,6 +121,12 @@ export class TransformNode extends Node {
     @serialize()
     public ignoreNonUniformScaling = false;
 
+    /**
+     * Gets or sets a boolean indicating that even if rotationQuaternion is defined, you can keep updating rotation property and Babylon.js will just mix both
+     */
+    @serialize()
+    public reIntegrateRotationIntoRotationQuaternion = false;
+
     // Cache
     /** @hidden */
     public _poseMatrix: Matrix;
@@ -113,6 +143,16 @@ export class TransformNode extends Node {
     /** @hidden */
     public _indexInSceneTransformNodesArray = -1;
 
+    private _connectBillboardProcessors() {
+        if (this._billboardMode !== TransformNode.BILLBOARDMODE_NONE && !this.preserveParentRotationForBillboard) {
+            this._activeParentProcessor = this._billboardParentProcessor;
+            this._activeBillboardPostProcessor = this._billboardPostProcessor;
+        } else {
+            this._activeParentProcessor = this._defaultParentProcessor;
+            this._activeBillboardPostProcessor = () => { };
+        }
+    }
+
     /**
     * An event triggered after the world matrix is updated
     */
@@ -124,6 +164,9 @@ export class TransformNode extends Node {
         if (isPure) {
             this.getScene().addTransformNode(this);
         }
+
+        this._activeParentProcessor = this._defaultParentProcessor;
+        this._activeCompositionProcess = this._defaultCompositionProcessor;
     }
 
     /**
@@ -288,7 +331,7 @@ export class TransformNode extends Node {
     * @param property if set to "rotation" the objects rotationQuaternion will be set to null
     * @returns this transform node
     */
-   public markAsDirty(property: string): TransformNode {
+    public markAsDirty(property: string): TransformNode {
         this._currentRenderId = Number.MAX_VALUE;
         this._isDirty = true;
         return this;
@@ -319,7 +362,11 @@ export class TransformNode extends Node {
     */
     public setPivotMatrix(matrix: DeepImmutable<Matrix>, postMultiplyPivotMatrix = true): TransformNode {
         this._pivotMatrix.copyFrom(matrix);
-        this._usePivotMatrix = !this._pivotMatrix.isIdentity();
+        if (this._pivotMatrix.isIdentity()) {
+            this._activeCompositionProcess = this._defaultCompositionProcessor;
+        } else {
+            this._activeCompositionProcess = this._pivotCompositionProcessor;
+        }
         this._cache.pivotMatrixUpdated = true;
         this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
 
@@ -856,65 +903,131 @@ export class TransformNode extends Node {
         return this.parent;
     }
 
+    private _activeCompositionProcess: (scaling: Vector3, rotation: Quaternion, translation: Vector3) => void;
+
+    private _defaultCompositionProcessor = (scaling: Vector3, rotation: Quaternion, translation: Vector3) => {
+        Matrix.ComposeToRef(scaling, rotation, translation, this._localMatrix);
+    }
+
+    private _pivotCompositionProcessor = (scaling: Vector3, rotation: Quaternion, translation: Vector3) => {
+        let scaleMatrix = Tmp.Matrix[1];
+        Matrix.ScalingToRef(scaling.x, scaling.y, scaling.z, scaleMatrix);
+
+        // Rotation
+        let rotationMatrix = Tmp.Matrix[0];
+        rotation.toRotationMatrix(rotationMatrix);
+
+        // Composing transformations
+        this._pivotMatrix.multiplyToRef(scaleMatrix, Tmp.Matrix[4]);
+        Tmp.Matrix[4].multiplyToRef(rotationMatrix, this._localMatrix);
+
+        // Post multiply inverse of pivotMatrix
+        if (this._postMultiplyPivotMatrix) {
+            this._localMatrix.multiplyToRef(this._pivotMatrixInverse, this._localMatrix);
+        }
+
+        this._localMatrix.addTranslationFromFloats(translation.x, translation.y, translation.z);
+    }
+
+    // Billboards
+    private _activeParentProcessor: (parent: Node) => void;
+    private _activeBillboardPostProcessor = () => { };
+
+    private _defaultParentProcessor = (parent: Node) => {
+        if (this._transformToBoneReferal) {
+            this._localMatrix.multiplyToRef(parent.getWorldMatrix(), Tmp.Matrix[6]);
+            Tmp.Matrix[6].multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), this._worldMatrix);
+        } else {
+            this._localMatrix.multiplyToRef(parent.getWorldMatrix(), this._worldMatrix);
+        }
+    };
+
+    private _billboardParentProcessor = (parent: Node) => {
+        if (this._transformToBoneReferal) {
+            parent.getWorldMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), Tmp.Matrix[7]);
+        } else {
+            Tmp.Matrix[7].copyFrom(parent.getWorldMatrix());
+        }
+
+        // Extract scaling and translation from parent
+        let translation = Tmp.Vector3[5];
+        let scale = Tmp.Vector3[6];
+        Tmp.Matrix[7].decompose(scale, undefined, translation);
+        Matrix.ScalingToRef(scale.x, scale.y, scale.z, Tmp.Matrix[7]);
+        Tmp.Matrix[7].setTranslation(translation);
+
+        this._localMatrix.multiplyToRef(Tmp.Matrix[7], this._worldMatrix);
+    }
+
+    private _billboardPostProcessor = () => {
+        let camera = (<Camera>this.getScene().activeCamera);
+        if (!camera) {
+            return;
+        }
+        let storedTranslation = Tmp.Vector3[0];
+        this._worldMatrix.getTranslationToRef(storedTranslation); // Save translation
+
+        // Cancel camera rotation
+        Tmp.Matrix[1].copyFrom(camera.getViewMatrix());
+        Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
+        Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
+
+        if ((this.billboardMode & TransformNode.BILLBOARDMODE_ALL) !== TransformNode.BILLBOARDMODE_ALL) {
+            Tmp.Matrix[0].decompose(undefined, Tmp.Quaternion[0], undefined);
+            let eulerAngles = Tmp.Vector3[1];
+            Tmp.Quaternion[0].toEulerAnglesToRef(eulerAngles);
+
+            if ((this.billboardMode & TransformNode.BILLBOARDMODE_X) !== TransformNode.BILLBOARDMODE_X) {
+                eulerAngles.x = 0;
+            }
+
+            if ((this.billboardMode & TransformNode.BILLBOARDMODE_Y) !== TransformNode.BILLBOARDMODE_Y) {
+                eulerAngles.y = 0;
+            }
+
+            if ((this.billboardMode & TransformNode.BILLBOARDMODE_Z) !== TransformNode.BILLBOARDMODE_Z) {
+                eulerAngles.z = 0;
+            }
+
+            Matrix.RotationYawPitchRollToRef(eulerAngles.y, eulerAngles.x, eulerAngles.z, Tmp.Matrix[0]);
+        }
+        this._worldMatrix.setTranslationFromFloats(0, 0, 0);
+        this._worldMatrix.multiplyToRef(Tmp.Matrix[0], this._worldMatrix);
+
+        // Restore translation
+        this._worldMatrix.setTranslation(Tmp.Vector3[0]);
+    }
+
     /**
      * Computes the world matrix of the node
      * @param force defines if the cache version should be invalidated forcing the world matrix to be created from scratch
      * @returns the world matrix
      */
     public computeWorldMatrix(force?: boolean): Matrix {
+        let currentRenderId = this.getScene().getRenderId();
+
         if (this._isWorldMatrixFrozen) {
             return this._worldMatrix;
         }
 
         if (!this._isDirty && !force && this.isSynchronized()) {
-            this._currentRenderId = this.getScene().getRenderId();
+            this._currentRenderId = currentRenderId;
             return this._worldMatrix;
         }
 
         this._updateCache();
-        this._cache.position.copyFrom(this._position);
-        this._cache.scaling.copyFrom(this._scaling);
         this._cache.pivotMatrixUpdated = false;
         this._cache.billboardMode = this.billboardMode;
         this._cache.infiniteDistance = this.infiniteDistance;
-        this._currentRenderId = this.getScene().getRenderId();
+
+        this._currentRenderId = currentRenderId;
         this._childUpdateId++;
         this._isDirty = false;
         let parent = this._getEffectiveParent();
 
         // Scaling
-        let scaleMatrix = Tmp.Matrix[1];
-        Matrix.ScalingToRef(this._scaling.x * this.scalingDeterminant, this._scaling.y * this.scalingDeterminant, this._scaling.z * this.scalingDeterminant, scaleMatrix);
-
-        // Rotation
-        let rotationMatrix = Tmp.Matrix[0];
-
-        // Rotate, if quaternion is set and rotation was used
-        if (this._rotationQuaternion) {
-            var len = this.rotation.lengthSquared();
-            if (len) {
-                this._rotationQuaternion.multiplyInPlace(Quaternion.RotationYawPitchRoll(this._rotation.y, this._rotation.x, this._rotation.z));
-                this._rotation.copyFromFloats(0, 0, 0);
-            }
-            this._rotationQuaternion.toRotationMatrix(rotationMatrix);
-            this._cache.rotationQuaternion.copyFrom(this._rotationQuaternion);
-        } else {
-            Matrix.RotationYawPitchRollToRef(this._rotation.y, this._rotation.x, this._rotation.z, rotationMatrix);
-            this._cache.rotation.copyFrom(this._rotation);
-        }
-
-        // Composing transformations
-        if (this._usePivotMatrix) {
-            this._pivotMatrix.multiplyToRef(scaleMatrix, Tmp.Matrix[4]);
-            Tmp.Matrix[4].multiplyToRef(rotationMatrix, this._localMatrix);
-
-            // Post multiply inverse of pivotMatrix
-            if (this._postMultiplyPivotMatrix) {
-                this._localMatrix.multiplyToRef(this._pivotMatrixInverse, this._localMatrix);
-            }
-        } else {
-            scaleMatrix.multiplyToRef(rotationMatrix, this._localMatrix);
-        }
+        let scaling: Vector3 = this._cache.scaling;
+        let translation: Vector3 = this._cache.position;
 
         // Translation
         let camera = (<Camera>this.getScene().activeCamera);
@@ -923,78 +1036,43 @@ export class TransformNode extends Node {
             var cameraWorldMatrix = camera.getWorldMatrix();
             var cameraGlobalPosition = new Vector3(cameraWorldMatrix.m[12], cameraWorldMatrix.m[13], cameraWorldMatrix.m[14]);
 
-            this._localMatrix.addTranslationFromFloats(this._position.x + cameraGlobalPosition.x, this._position.y + cameraGlobalPosition.y, this._position.z + cameraGlobalPosition.z);
+            translation.copyFromFloats(this._position.x + cameraGlobalPosition.x, this._position.y + cameraGlobalPosition.y, this._position.z + cameraGlobalPosition.z);
         } else {
-            this._localMatrix.addTranslationFromFloats(this._position.x, this._position.y, this._position.z);
+            translation.copyFrom(this._position);
         }
 
-        // Parent
-        if (parent && parent.getWorldMatrix) {
-            // We do not want parent rotation
-            if (this.billboardMode !== TransformNode.BILLBOARDMODE_NONE && !this.preserveParentRotationForBillboard) {
-                if (this._transformToBoneReferal) {
-                    parent.getWorldMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), Tmp.Matrix[7]);
-                } else {
-                    Tmp.Matrix[7].copyFrom(parent.getWorldMatrix());
-                }
-
-                // Extract scaling and translation from parent
-                let translation = Tmp.Vector3[5];
-                let scale = Tmp.Vector3[6];
-                Tmp.Matrix[7].decompose(scale, undefined, translation);
-                Matrix.ScalingToRef(scale.x, scale.y, scale.z, Tmp.Matrix[7]);
-                Tmp.Matrix[7].setTranslation(translation);
+        // Scaling
+        scaling.copyFromFloats(this._scaling.x * this.scalingDeterminant, this._scaling.y * this.scalingDeterminant, this._scaling.z * this.scalingDeterminant);
 
-                this._localMatrix.multiplyToRef(Tmp.Matrix[7], this._worldMatrix);
-            } else {
-                if (this._transformToBoneReferal) {
-                    this._localMatrix.multiplyToRef(parent.getWorldMatrix(), Tmp.Matrix[6]);
-                    Tmp.Matrix[6].multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), this._worldMatrix);
-                } else {
-                    this._localMatrix.multiplyToRef(parent.getWorldMatrix(), this._worldMatrix);
+        // Rotation
+        let rotation: Quaternion = this._cache.rotationQuaternion;
+        if (this._rotationQuaternion) {
+            if (this.reIntegrateRotationIntoRotationQuaternion) {
+                var len = this.rotation.lengthSquared();
+                if (len) {
+                    this._rotationQuaternion.multiplyInPlace(Quaternion.RotationYawPitchRoll(this._rotation.y, this._rotation.x, this._rotation.z));
+                    this._rotation.copyFromFloats(0, 0, 0);
                 }
             }
+            rotation.copyFrom(this._rotationQuaternion);
+        } else {
+            Quaternion.RotationYawPitchRollToRef(this._rotation.y, this._rotation.x, this._rotation.z, rotation);
+            this._cache.rotation.copyFrom(this._rotation);
+        }
 
+        // Compose
+        this._activeCompositionProcess(scaling, rotation, translation);
+
+        // Parent
+        if (parent && parent.getWorldMatrix) {
+            this._activeParentProcessor(parent);
             this._markSyncedWithParent();
         } else {
             this._worldMatrix.copyFrom(this._localMatrix);
         }
 
         // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
-        if (this.billboardMode !== TransformNode.BILLBOARDMODE_NONE && camera) {
-            let storedTranslation = Tmp.Vector3[0];
-            this._worldMatrix.getTranslationToRef(storedTranslation); // Save translation
-
-            // Cancel camera rotation
-            Tmp.Matrix[1].copyFrom(camera.getViewMatrix());
-            Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
-            Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
-
-            if ((this.billboardMode & TransformNode.BILLBOARDMODE_ALL) !== TransformNode.BILLBOARDMODE_ALL) {
-                Tmp.Matrix[0].decompose(undefined, Tmp.Quaternion[0], undefined);
-                let eulerAngles = Tmp.Vector3[1];
-                Tmp.Quaternion[0].toEulerAnglesToRef(eulerAngles);
-
-                if ((this.billboardMode & TransformNode.BILLBOARDMODE_X) !== TransformNode.BILLBOARDMODE_X) {
-                    eulerAngles.x = 0;
-                }
-
-                if ((this.billboardMode & TransformNode.BILLBOARDMODE_Y) !== TransformNode.BILLBOARDMODE_Y) {
-                    eulerAngles.y = 0;
-                }
-
-                if ((this.billboardMode & TransformNode.BILLBOARDMODE_Z) !== TransformNode.BILLBOARDMODE_Z) {
-                    eulerAngles.z = 0;
-                }
-
-                Matrix.RotationYawPitchRollToRef(eulerAngles.y, eulerAngles.x, eulerAngles.z, Tmp.Matrix[0]);
-            }
-            this._worldMatrix.setTranslationFromFloats(0, 0, 0);
-            this._worldMatrix.multiplyToRef(Tmp.Matrix[0], this._worldMatrix);
-
-            // Restore translation
-            this._worldMatrix.setTranslation(Tmp.Vector3[0]);
-        }
+        this._activeBillboardPostProcessor();
 
         // Normal matrix
         if (!this.ignoreNonUniformScaling) {
@@ -1022,7 +1100,7 @@ export class TransformNode extends Node {
         }
 
         // Cache the determinant
-        this._worldMatrixDeterminant = this._worldMatrix.determinant();
+        this._worldMatrixDeterminantIsDirty = true;
 
         return this._worldMatrix;
     }

+ 6 - 0
src/node.ts

@@ -141,6 +141,8 @@ export class Node implements IBehaviorAware<Node> {
     public _worldMatrix = Matrix.Identity();
     /** @hidden */
     public _worldMatrixDeterminant = 0;
+    /** @hidden */
+    public _worldMatrixDeterminantIsDirty = true;
 
     /** @hidden */
     private _sceneRootNodesIndex = -1;
@@ -381,6 +383,10 @@ export class Node implements IBehaviorAware<Node> {
 
     /** @hidden */
     public _getWorldMatrixDeterminant(): number {
+        if (this._worldMatrixDeterminantIsDirty) {
+            this._worldMatrixDeterminantIsDirty = false;
+            this._worldMatrixDeterminant = this._worldMatrix.determinant();
+        }
         return this._worldMatrixDeterminant;
     }