瀏覽代碼

Camera don't compute their view matrix at each frame anymore

A cache system has been added to BABYLON.Node to be used in BABYLON.Camera like it was already done in BABYLON.Mesh.
The comparisons between the current values and the values of the cache enable to compute the view matrix only when needed like it's done for BABYLON.Mesh.prototype.computeWorldMatrix.

In derived classes, you can override the following functions

- _isSynchronized() method (which returns false if a change is needed)
- _initCache() method (which allows you to init the new variables that you append to cache)
- _updateCache() method (which is called to update the cache after changes detection)

If you override them, do NOT forget to call the parent class function (except for _initCache). See what it is done in BABYLON.Camera.

Warning: pay attention to the "_", do NOT override the public counterpart of these methods: isSynchronized and updateCache

Remark: I didn't override _updateCache in BABYLON.Mesh to avoid modifications in this code. I was not sure to do the right things. Of course, BABYLON.Mesh can override it.

Fixed BABYLON.ArcRotateCamera.prototype._getViewMatrix
Julien 11 年之前
父節點
當前提交
290503bd77

+ 40 - 3
Babylon/Cameras/babylon.arcRotateCamera.js

@@ -22,6 +22,8 @@ var BABYLON = BABYLON || {};
         this._viewMatrix = new BABYLON.Matrix();
 
         this.getViewMatrix();
+
+        BABYLON.ArcRotateCamera.prototype._initCache.call(this);
     };
     
     BABYLON.ArcRotateCamera.prototype = Object.create(BABYLON.Camera.prototype);
@@ -37,6 +39,39 @@ var BABYLON = BABYLON || {};
     BABYLON.ArcRotateCamera.prototype.upperRadiusLimit = null;
     BABYLON.ArcRotateCamera.prototype.angularSensibility = 1000.0;
 
+    BABYLON.ArcRotateCamera.prototype._getTargetPosition = function () {       
+        return this.target.position || this.target;
+    };
+    
+    // Cache
+    BABYLON.ArcRotateCamera.prototype._initCache = function () {
+        this._cache.target = this._getTargetPosition().clone();
+        this._cache.alpha = this.alpha;
+        this._cache.beta = this.beta;
+        this._cache.radius = this.radius;
+    };
+
+    BABYLON.ArcRotateCamera.prototype._updateCache = function (ignoreParentClass) {
+        if(!ignoreParentClass)
+            BABYLON.Camera.prototype._updateCache.call(this);
+        
+        this._cache.target.copyFrom(this._getTargetPosition());
+        this._cache.alpha = this.alpha;
+        this._cache.beta = this.beta;
+        this._cache.radius = this.radius;
+    };
+
+    // Synchronized
+    BABYLON.ArcRotateCamera.prototype._isSynchronized = function () {
+        if (!BABYLON.Camera.prototype._isSynchronized.call(this))
+            return false;
+        
+        return this._cache.target.equals(this._getTargetPosition())
+            && this._cache.alpha === this.alpha
+            && this._cache.beta === this.beta
+            && this._cache.radius === this.radius;
+    };
+    
     // Methods
     BABYLON.ArcRotateCamera.prototype.attachControl = function(canvas, noPreventDefault) {
         var previousPosition;
@@ -304,7 +339,7 @@ var BABYLON = BABYLON || {};
     };
 
     BABYLON.ArcRotateCamera.prototype.setPosition = function(position) {
-        var radiusv3 = position.subtract(this.target.position ? this.target.position : this.target);
+        var radiusv3 = position.subtract(this._getTargetPosition());
         this.radius = radiusv3.length();
 
         this.alpha = Math.atan(radiusv3.z / radiusv3.x);
@@ -324,8 +359,10 @@ var BABYLON = BABYLON || {};
         var cosb = Math.cos(this.beta);
         var sinb = Math.sin(this.beta);
 
-        this.target.addToRef(new BABYLON.Vector3(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb), this.position);
-        BABYLON.Matrix.LookAtLHToRef(this.position, this.target, this.upVector, this._viewMatrix);
+        var target = this._getTargetPosition();
+        
+        target.addToRef(new BABYLON.Vector3(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb), this.position);
+        BABYLON.Matrix.LookAtLHToRef(this.position, target, this.upVector, this._viewMatrix);
 
         return this._viewMatrix;
     };

+ 51 - 9
Babylon/Cameras/babylon.camera.js

@@ -4,6 +4,8 @@ var BABYLON = BABYLON || {};
 
 (function () {
     BABYLON.Camera = function (name, position, scene) {
+        BABYLON.Node.call(this);
+        
         this.name = name;
         this.id = name;
         this.position = position;
@@ -28,6 +30,9 @@ var BABYLON = BABYLON || {};
         
         // Viewport
         this.viewport = new BABYLON.Viewport(0, 0, 1.0, 1.0);
+        
+        //Cache
+        BABYLON.Camera.prototype._initCache.call(this);
     };
 
     BABYLON.Camera.prototype = Object.create(BABYLON.Node.prototype);
@@ -51,6 +56,30 @@ var BABYLON = BABYLON || {};
     BABYLON.Camera.prototype.getScene = function () {
         return this._scene;
     };
+    
+    //Cache
+    BABYLON.Camera.prototype._initCache = function () {
+        this._cache.position = this.position.clone();
+        this._cache.upVector = this.upVector.clone();
+    };
+
+    BABYLON.Camera.prototype._updateCache = function (ignoreParentClass) {
+        
+        if(!ignoreParentClass)
+            BABYLON.Node.prototype._updateCache.call(this);
+        
+        this._cache.position.copyFrom(this.position);
+        this._cache.upVector.copyFrom(this.upVector);
+    };
+
+    // Synchronized
+    BABYLON.Camera.prototype._isSynchronized = function () {
+        if (!BABYLON.Node.prototype._isSynchronized.call(this))
+            return false;
+        
+        return this._cache.position.equals(this.position) 
+            && this._cache.upVector.equals(this.upVector);
+    };    
 
     // Methods
     BABYLON.Camera.prototype.attachControl = function (canvas) {
@@ -61,6 +90,11 @@ var BABYLON = BABYLON || {};
 
     BABYLON.Camera.prototype._update = function () {
     };
+    
+    BABYLON.Camera.prototype._updateFromScene = function () {
+        this.updateCache();
+        this._update();
+    };
 
     BABYLON.Camera.prototype.getWorldMatrix = function () {
         var viewMatrix = this.getViewMatrix();
@@ -79,22 +113,30 @@ var BABYLON = BABYLON || {};
     };
 
     BABYLON.Camera.prototype.getViewMatrix = function () {
-        this._computedViewMatrix = this._getViewMatrix();
+        this._computedViewMatrix = this._computeViewMatrix();
 
-        if (this.parent && this.parent.getWorldMatrix) {
-            if (!this._worldMatrix) {
-                this._worldMatrix = BABYLON.Matrix.Identity();
-            }
+        if(!this.parent 
+            ||  !this.parent.getWorldMatrix
+            || (!this.hasNewParent() && this.parent.isSynchronized())) {
+            return this._computedViewMatrix;
+        }
+        
+        this._computedViewMatrix.invertToRef(this._worldMatrix);
 
-            this._computedViewMatrix.invertToRef(this._worldMatrix);
+        this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._computedViewMatrix);
 
-            this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._computedViewMatrix);
+        this._computedViewMatrix.invert();
 
-            this._computedViewMatrix.invert();
+        return this._computedViewMatrix;
+    };
+    
+    BABYLON.Camera.prototype._computeViewMatrix = function (force) {
 
+        if (!force && this.isSynchronized()) {
             return this._computedViewMatrix;
         }
-
+        
+        this._computedViewMatrix = this._getViewMatrix();
         return this._computedViewMatrix;
     };
 

+ 46 - 5
Babylon/Cameras/babylon.freeCamera.js

@@ -34,6 +34,8 @@ var BABYLON = BABYLON || {};
         this._newPosition = BABYLON.Vector3.Zero();
         this._lookAtTemp = BABYLON.Matrix.Zero();
         this._tempMatrix = BABYLON.Matrix.Zero();
+        
+        BABYLON.FreeCamera.prototype._initCache.call(this);
     };
 
     BABYLON.FreeCamera.prototype = Object.create(BABYLON.Camera.prototype);
@@ -47,6 +49,49 @@ var BABYLON = BABYLON || {};
     BABYLON.FreeCamera.prototype.lockedTarget = null;
     BABYLON.FreeCamera.prototype.onCollide = null;
 
+    BABYLON.FreeCamera.prototype._getLockedTargetPosition = function () {       
+        if(!this.lockedTarget)
+            return null;
+        
+        return this.lockedTarget.position || this.lockedTarget;
+    };
+
+    // Cache
+    BABYLON.FreeCamera.prototype._initCache = function () {
+        var lockedTargetPosition = this._getLockedTargetPosition();
+        this._cache.lockedTarget = lockedTargetPosition ? lockedTargetPosition.clone() : null;
+        this._cache.rotation = this.rotation.clone();
+    };
+
+    BABYLON.FreeCamera.prototype._updateCache = function (ignoreParentClass) {
+        if(!ignoreParentClass)
+            BABYLON.Camera.prototype._updateCache.call(this);
+        
+        var lockedTargetPosition = this._getLockedTargetPosition();
+        if (!lockedTargetPosition) {
+            this._cache.lockedTarget = null;
+        }
+        else {
+            if (!this._cache.lockedTarget) 
+                this._cache.lockedTarget = lockedTargetPosition.clone();
+            else
+                this._cache.lockedTarget.copyFrom(lockedTargetPosition);
+        }
+        
+        this._cache.rotation.copyFrom(this.rotation);
+    };
+
+    // Synchronized
+    BABYLON.FreeCamera.prototype._isSynchronized = function () {
+        if (!BABYLON.Camera.prototype._isSynchronized.call(this))
+            return false;
+
+        var lockedTargetPosition = this._getLockedTargetPosition();
+ 
+        return (this._cache.lockedTarget ? this._cache.lockedTarget.equals(lockedTargetPosition) : !lockedTargetPosition)
+            && this._cache.rotation.equals(this.rotation);
+    };    
+    
     // Methods
     BABYLON.FreeCamera.prototype._computeLocalCameraSpeed = function () {
         return this.speed * ((BABYLON.Tools.GetDeltaTime() / (BABYLON.Tools.GetFps() * 10.0)));
@@ -327,11 +372,7 @@ var BABYLON = BABYLON || {};
             // Computing target and final matrix
             this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
         } else {
-            if (this.lockedTarget.position) {
-                this._currentTarget.copyFrom(this.lockedTarget.position);
-            } else {
-                this._currentTarget.copyFrom(this.lockedTarget);
-            }
+            this._currentTarget.copyFrom(this._getLockedTargetPosition());
         }
         
         BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);

+ 16 - 14
Babylon/Mesh/babylon.mesh.js

@@ -4,6 +4,8 @@ var BABYLON = BABYLON || {};
 
 (function () {
     BABYLON.Mesh = function (name, scene) {
+        BABYLON.Node.call(this);
+        
         this.name = name;
         this.id = name;
         this._scene = scene;
@@ -30,15 +32,10 @@ var BABYLON = BABYLON || {};
         // Animations
         this.animations = [];
 
-        // Cache
         this._positions = null;
-        this._cache = {
-            localMatrixUpdated: false,
-            position: BABYLON.Vector3.Zero(),
-            scaling: BABYLON.Vector3.Zero(),
-            rotation: BABYLON.Vector3.Zero(),
-            rotationQuaternion: new BABYLON.Quaternion(0, 0, 0, 0)
-        };
+        
+        // Cache
+        BABYLON.Mesh.prototype._initCache.call(this);
 
         this._childrenFlag = false;
         this._localScaling = BABYLON.Matrix.Zero();
@@ -143,7 +140,7 @@ var BABYLON = BABYLON || {};
         return this._pivotMatrix;
     };
 
-    BABYLON.Mesh.prototype.isSynchronized = function () {
+    BABYLON.Mesh.prototype._isSynchronized = function () {
         if (this.billboardMode !== BABYLON.Mesh.BILLBOARDMODE_NONE)
             return false;
 
@@ -169,9 +166,6 @@ var BABYLON = BABYLON || {};
         if (!this._cache.scaling.equals(this.scaling))
             return false;
 
-        if (this.parent)
-            return !this.parent._needToSynchonizeChildren();
-
         return true;
     };
 
@@ -231,7 +225,7 @@ var BABYLON = BABYLON || {};
     };
 
     BABYLON.Mesh.prototype.computeWorldMatrix = function (force) {
-        if (!force && (this._currentRenderId == this._scene.getRenderId() || this.isSynchronized())) {
+        if (!force && (this._currentRenderId == this._scene.getRenderId() || this.isSynchronized(true))) {
             this._childrenFlag = false;
             return this._worldMatrix;
         }
@@ -1354,6 +1348,14 @@ var BABYLON = BABYLON || {};
 
         return ground;
     };
+    
+    BABYLON.Mesh.prototype._initCache = function (
+        this._cache.localMatrixUpdated = false;
+        this._cache.position = BABYLON.Vector3.Zero();
+        this._cache.scaling = BABYLON.Vector3.Zero();
+        this._cache.rotation = BABYLON.Vector3.Zero();
+        this._cache.rotationQuaternion = new BABYLON.Quaternion(0, 0, 0, 0);
+    };
 
     // Tools
     BABYLON.Mesh.ComputeNormal = function (positions, normals, indices) {
@@ -1400,5 +1402,5 @@ var BABYLON = BABYLON || {};
             normals[index * 3 + 1] = normal.y;
             normals[index * 3 + 2] = normal.z;
         }
-    };   
+    };
 })();

+ 5 - 5
Babylon/Tools/babylon.math.js

@@ -191,7 +191,7 @@ var BABYLON = BABYLON || {};
     };
 
     BABYLON.Color3.prototype.equals = function (otherColor) {
-        return this.r === otherColor.r && this.g === otherColor.g && this.b === otherColor.b;
+        return otherColor && this.r === otherColor.r && this.g === otherColor.g && this.b === otherColor.b;
     };
 
     BABYLON.Color3.prototype.scale = function (scale) {
@@ -334,7 +334,7 @@ var BABYLON = BABYLON || {};
     };
 
     BABYLON.Vector2.prototype.equals = function (otherVector) {
-        return this.x === otherVector.x && this.y === otherVector.y;
+        return otherVector && this.x === otherVector.x && this.y === otherVector.y;
     };
 
     // Properties
@@ -540,7 +540,7 @@ var BABYLON = BABYLON || {};
     };
 
     BABYLON.Vector3.prototype.equals = function (otherVector) {
-        return this.x === otherVector.x && this.y === otherVector.y && this.z === otherVector.z;
+        return otherVector && this.x === otherVector.x && this.y === otherVector.y && this.z === otherVector.z;
     };
 
     BABYLON.Vector3.prototype.equalsToFloats = function (x, y, z) {
@@ -858,7 +858,7 @@ var BABYLON = BABYLON || {};
     };
     
     BABYLON.Quaternion.prototype.equals = function (otherQuaternion) {
-        return this.x === otherQuaternion.x && this.y === otherQuaternion.y && this.z === otherQuaternion.z && this.w === otherQuaternion.w;
+        return otherQuaternion && this.x === otherQuaternion.x && this.y === otherQuaternion.y && this.z === otherQuaternion.z && this.w === otherQuaternion.w;
     };
 
     BABYLON.Quaternion.prototype.clone = function () {
@@ -1176,7 +1176,7 @@ var BABYLON = BABYLON || {};
     };
 
     BABYLON.Matrix.prototype.equals = function (value) {
-        return (this.m[0] === value.m[0] && this.m[1] === value.m[1] && this.m[2] === value.m[2] && this.m[3] === value.m[3] &&
+        return value && (this.m[0] === value.m[0] && this.m[1] === value.m[1] && this.m[2] === value.m[2] && this.m[3] === value.m[3] &&
                 this.m[4] === value.m[4] && this.m[5] === value.m[5] && this.m[6] === value.m[6] && this.m[7] === value.m[7] &&
                 this.m[8] === value.m[8] && this.m[9] === value.m[9] && this.m[10] === value.m[10] && this.m[11] === value.m[11] &&
                 this.m[12] === value.m[12] && this.m[13] === value.m[13] && this.m[14] === value.m[14] && this.m[15] === value.m[15]);

+ 53 - 1
Babylon/babylon.node.js

@@ -4,6 +4,7 @@ var BABYLON = BABYLON || {};
 
 (function () {
     BABYLON.Node = function () {
+        BABYLON.Node.prototype._initCache.call(this);
     };
     
     // Properties
@@ -12,8 +13,59 @@ var BABYLON = BABYLON || {};
     BABYLON.Node.prototype._isReady = true;
     BABYLON.Node.prototype._isEnabled = true;
 
+    // Cache
+    BABYLON.Node.prototype._cache = null;
     
-    BABYLON.Node.prototype.isSynchronized = function () {
+    // override it in derived class if you add new variables to the cache
+    // and call it in the constructor of your class like this
+    // BABYLON.YourClass.prototype._initCache.call(this)
+    // DO NOT call parent class method
+    BABYLON.Node.prototype._initCache = function () {
+        this._cache = {};
+    };
+
+    BABYLON.Node.prototype.updateCache = function (force) {
+        if (!force && this.isSynchronized())
+            return;
+        
+        this._cache.parent = this.parent;
+        
+        this._updateCache();
+    };
+
+    // override it in derived class if you add new variables to the cache
+    // and call the parent class method if !ignoreParentClass
+    // BABYLON.ParentClass.prototype._updateCache.call(this, ignoreParentClass)
+    BABYLON.Node.prototype._updateCache = function (ignoreParentClass) {
+    };
+
+    // override it in derived class if you add new variables to the cache
+    // and call the parent class method
+    // BABYLON.ParentClass.prototype._isSynchronized.call(this)
+    BABYLON.Node.prototype._isSynchronized = function () {
+        return true;
+    };
+
+    BABYLON.Node.prototype.isSynchronized = function (updateCache) {
+        var r = this.hasNewParent();
+
+        r = r ? true : (this.parent && this.parent._needToSynchonizeChildren());
+        
+        r = r ? true : !this._isSynchronized();
+        
+        if(updateCache)
+            this.updateCache(true);
+        
+        return !r;
+    };
+
+    BABYLON.Node.prototype.hasNewParent = function (update) {
+        if (this._cache.parent === this.parent)
+            return false;
+        
+        if(update)
+            this._cache.parent = this.parent;
+        
         return true;
     };
     

+ 1 - 1
Babylon/babylon.scene.js

@@ -660,7 +660,7 @@ var BABYLON = BABYLON || {};
         this.postProcessManager._finalizeFrame();
 
         // Update camera
-        this.activeCamera._update();
+        this.activeCamera._updateFromScene();
         
         // Reset some special arrays
         this._renderTargets.reset();