瀏覽代碼

Merge pull request #530 from Palmer-JC/master

3D subcameras refactoring - let's do a first pass
David Catuhe 10 年之前
父節點
當前提交
783e02bd7d

+ 26 - 1
Babylon/Cameras/babylon.arcRotateCamera.ts

@@ -343,7 +343,7 @@
             }
         }
 
-        public _update(): void {
+        public _checkInputs(): void {
 
             //if (async) collision inspection was triggered, don't update the camera's position - until the collision callback was called.
             if (this._collisionTriggered) {
@@ -502,5 +502,30 @@
 
             this.maxZ = distance * 2;
         }
+        
+        /**
+         * @override
+         * needs to be overridden, so sub has required properties to be copied
+         */
+        public GetSubCamera(name : string, isA : boolean) : Camera{
+            var alphaSpace = this._subCamHalfSapce * (isA? -1 : 1);
+            return new BABYLON.ArcRotateCamera(name, this.alpha + alphaSpace, this.beta, this.radius, this.target, this.getScene());
+        }
+        
+        /**
+         * @override
+         * needs to be overridden, adding copy of alpha, beta & radius
+         */
+        public _updateSubCameras(){
+            var camA = <ArcRotateCamera> this.subCameras[Camera.SUB_CAM_A];
+            var camB = <ArcRotateCamera> this.subCameras[Camera.SUB_CAM_B];
+            
+            camA.alpha = this.alpha - this._subCamHalfSapce;
+            camB.alpha = this.alpha + this._subCamHalfSapce;
+            
+            camA.beta   = camB.beta   = this.beta;
+            camA.radius = camB.radius = this.radius;
+            super._updateSubCameras();
+        }
     }
 } 

+ 175 - 3
Babylon/Cameras/babylon.camera.ts

@@ -1,4 +1,28 @@
 module BABYLON {
+    // Oculus source & derived constants
+    var OculusRiftDevKit2013_Metric = {
+        HResolution: 1280,
+        VResolution: 800,
+        HScreenSize: 0.149759993,
+        VScreenSize: 0.0935999975,
+        VScreenCenter: 0.0467999987,
+        EyeToScreenDistance: 0.0410000011,
+        LensSeparationDistance: 0.0635000020,
+        InterpupillaryDistance: 0.0640000030,
+        DistortionK: [1.0, 0.219999999, 0.239999995, 0.0],
+        ChromaAbCorrection: [0.995999992, -0.00400000019, 1.01400006, 0.0],
+        PostProcessScaleFactor: 1.714605507808412,
+        LensCenterOffset: 0.151976421
+    };
+    var OculusAspectRatio = OculusRiftDevKit2013_Metric.HResolution / (2 * OculusRiftDevKit2013_Metric.VResolution);
+    var OculusAspectRatioFov = (2 * Math.atan((OculusRiftDevKit2013_Metric.PostProcessScaleFactor * OculusRiftDevKit2013_Metric.VScreenSize) / (2 * OculusRiftDevKit2013_Metric.EyeToScreenDistance)));
+    var OculusHMeters = (OculusRiftDevKit2013_Metric.HScreenSize / 4) - (OculusRiftDevKit2013_Metric.LensSeparationDistance / 2);
+    var OculusH = (4 * OculusHMeters) / OculusRiftDevKit2013_Metric.HScreenSize;
+    var OculusLeftHMatrix  = Matrix.Translation( OculusH , 0, 0);
+    var OculusRightHMatrix = Matrix.Translation(-OculusH , 0, 0);
+    var OculusLeftPreViewMatrix  = Matrix.Translation( .5 * OculusRiftDevKit2013_Metric.InterpupillaryDistance, 0, 0);
+    var OculusRightPreViewMatrix = Matrix.Translation(-.5 * OculusRiftDevKit2013_Metric.InterpupillaryDistance, 0, 0);
+
     export class Camera extends Node {
         // Statics
         private static _PERSPECTIVE_CAMERA = 0;
@@ -7,6 +31,14 @@
         private static _FOVMODE_VERTICAL_FIXED = 0;
         private static _FOVMODE_HORIZONTAL_FIXED = 1;
 
+        private static _SUB_CAMS_NONE = 0;
+        private static _SUB_CAMS_ANAGLYPH = 1;
+        private static _SUB_CAMS_HORIZ_STEREOGRAM = 2;
+        private static _SUB_CAMS_VERT_STEREOGRAM = 3;
+        private static _SUB_CAMS_OCULUS = 4;
+        private static _SUB_CAM_A = 0;
+        private static _SUB_CAM_B = 1;
+        
         public static get PERSPECTIVE_CAMERA(): number {
             return Camera._PERSPECTIVE_CAMERA;
         }
@@ -22,7 +54,37 @@
         public static get FOVMODE_HORIZONTAL_FIXED(): number {
             return Camera._FOVMODE_HORIZONTAL_FIXED;
         }
-
+        
+        public static get SUB_CAMS_NONE(): number {
+            return Camera._SUB_CAMS_NONE;
+        }
+        
+        public static get SUB_CAMS_ANAGLYPH(): number {
+            return Camera._SUB_CAMS_ANAGLYPH;
+        }
+        
+        public static get SUB_CAMS_HORIZ_STEREOGRAM(): number {
+            return Camera._SUB_CAMS_HORIZ_STEREOGRAM;
+        }
+        
+        public static get SUB_CAMS_VERT_STEREOGRAM(): number {
+            return Camera._SUB_CAMS_VERT_STEREOGRAM;
+        }
+        
+        public static get SUB_CAMS_OCULUS(): number {
+            return Camera._SUB_CAMS_OCULUS;
+        }
+        
+        public static SUB_CAMS_UI_ENGLISH = ['None', 'Anaglyph', 'Stereogram Horizontal', 'Stereogram Vertical', 'Oculus Rift'];
+        
+        public static get SUB_CAM_A(): number {
+            return Camera._SUB_CAM_A;
+        }
+        
+        public static get SUB_CAM_B(): number {
+            return Camera._SUB_CAM_B;
+        }
+        
         // Members
         public upVector = Vector3.Up();
         public orthoLeft = null;
@@ -36,9 +98,17 @@
         public mode = Camera.PERSPECTIVE_CAMERA;
         public isIntermediate = false;
         public viewport = new Viewport(0, 0, 1.0, 1.0);
-        public subCameras = [];
-        public layerMask: number = 0xFFFFFFFF;
+        public layerMask: number = 0x0FFFFFFF;
         public fovMode: number = Camera.FOVMODE_VERTICAL_FIXED;
+   
+        // Subcamera members
+        public subCameras = new Array<Camera>();
+        public _subCameraMode = Camera._SUB_CAMS_NONE;
+        public _subCamHalfSapce: number;
+        private _OculusHMatrix : Matrix;
+        public _OculusPreViewMatrix : Matrix;
+        public _OculusWorkMatrix : Matrix;
+        public _OculusActualUp : Vector3;
 
         private _computedViewMatrix = Matrix.Identity();
         public _projectionMatrix = new Matrix();
@@ -173,8 +243,15 @@
         }
 
         public _update(): void {
+            this._checkInputs();
+            if (this._subCameraMode !== Camera._SUB_CAMS_NONE){
+                this._updateSubCameras();
+            }
         }
 
+        public _checkInputs(): void {            
+        }
+        
         public attachPostProcess(postProcess: PostProcess, insertAt: number = null): number {
             if (!postProcess.isReusable() && this._postProcesses.indexOf(postProcess) > -1) {
                 Tools.Error("You're trying to reuse a post process not defined as reusable.");
@@ -345,11 +422,106 @@
         public dispose(): void {
             // Remove from scene
             this.getScene().removeCamera(this);
+            while (this.subCameras.length > 0){
+                this.subCameras.pop().dispose();
+            }                    
 
             // Postprocesses
             for (var i = 0; i < this._postProcessesTakenIndices.length; ++i) {
                 this._postProcesses[this._postProcessesTakenIndices[i]].dispose(this);
             }
         }
+        
+        // ---- 3D cameras section ----
+        public setSubCameraMode(mode : number, halfSapce : number) : void{
+            // not likely in production that any prior sub cams, but in dev maybe
+            while (this.subCameras.length > 0){
+                this.subCameras.pop().dispose();
+            }                        
+            this._subCameraMode = mode;   
+            this._subCamHalfSapce = Tools.ToRadians(halfSapce);
+            
+            var camA = this.GetSubCamera(this.name + "_A", true );
+            var camB = this.GetSubCamera(this.name + "_B", false);
+            var postProcessA : PostProcess;
+            var postProcessB : PostProcess;
+            
+            switch (this._subCameraMode){
+                case Camera._SUB_CAMS_ANAGLYPH:
+                    postProcessA = new PassPostProcess(this.name + "_leftTexture", 1.0, camA);
+                    camA.isIntermediate = true;
+                    
+                    postProcessB = new AnaglyphPostProcess(this.name + "_anaglyph", 1.0, camB);
+                    postProcessB.onApply = effect => {
+                        effect.setTextureFromPostProcess("leftSampler", postProcessA);
+                    };
+                    break;
+                    
+                case Camera._SUB_CAMS_HORIZ_STEREOGRAM:
+                case Camera._SUB_CAMS_VERT_STEREOGRAM:
+                    var isStereogramHoriz = this._subCameraMode === Camera._SUB_CAMS_HORIZ_STEREOGRAM;
+                    postProcessA = new PassPostProcess("passthru", 1.0, camA);  
+                    camA.isIntermediate = true;
+                    
+                    postProcessB = new StereogramInterlacePostProcess("st_interlace" , camB, postProcessA, isStereogramHoriz);  
+                    break;
+                    
+                case Camera._SUB_CAMS_OCULUS:
+                    camA.viewport = new Viewport(  0,   0, 0.5, 1.0);
+                    camA._OculusWorkMatrix = new Matrix();
+                    
+                    camA._OculusHMatrix = OculusLeftHMatrix;
+                    camA._OculusPreViewMatrix = OculusLeftPreViewMatrix;                    
+                    camA.getProjectionMatrix = camA.getOculusProjectionMatrix;
+                    postProcessA = new OculusDistortionCorrectionPostProcess("Oculus Distortion Left", camA, false, OculusRiftDevKit2013_Metric);
+                    
+                    camB.viewport = new Viewport(0.5,   0, 0.5, 1.0);
+                    camB._OculusWorkMatrix = new Matrix();
+                    camB._OculusHMatrix = OculusRightHMatrix;
+                    camB._OculusPreViewMatrix = OculusRightPreViewMatrix;
+                    
+                    camB.getProjectionMatrix = camB.getOculusProjectionMatrix;
+                    postProcessB = new OculusDistortionCorrectionPostProcess("Oculus Distortion Right", camB, true , OculusRiftDevKit2013_Metric);
+            }
+            if (this._subCameraMode !== Camera._SUB_CAMS_NONE){
+                this.subCameras.push(camA);
+                this.subCameras.push(camB);
+            }
+            this._update();
+        }
+        
+        private getOculusProjectionMatrix(): Matrix {
+            Matrix.PerspectiveFovLHToRef(OculusAspectRatioFov, OculusAspectRatio, this.minZ, this.maxZ, this._OculusWorkMatrix);
+            this._OculusWorkMatrix.multiplyToRef(this._OculusHMatrix, this._projectionMatrix);
+            return this._projectionMatrix;
+        }
+        
+        public setSubCamHalfSapce( halfSapce : number){
+            this._subCamHalfSapce = Tools.ToRadians(halfSapce);
+        }
+        
+        /**
+         * needs to be overridden in ArcRotateCamera & TargetCamera, so sub has required properties to be copied
+         */
+        public GetSubCamera(name : string, isA : boolean) : Camera{ 
+            return null;  
+        }
+        
+        /**
+         * needs to be overridden in ArcRotateCamera, adding copy of alpha, beta & radius
+         * needs to be overridden in TargetCamera, adding copy of position, and rotation for Oculus, or target for rest
+         */
+        public _updateSubCameras(){
+            var camA = this.subCameras[Camera.SUB_CAM_A];
+            var camB = this.subCameras[Camera.SUB_CAM_B];
+            camA.minZ = camB.minZ = this.minZ;
+            camA.maxZ = camB.maxZ = this.maxZ;
+            camA.fov  = camB.fov  = this.fov; // original Oculus did not do this
+            
+            // only update viewport, when ANAGLYPH
+            if (this._subCameraMode === Camera.SUB_CAMS_ANAGLYPH){
+                camA.viewport = camB.viewport = this.viewport;                
+            }
+        }        
     }
 }

+ 2 - 2
Babylon/Cameras/babylon.followCamera.ts

@@ -55,8 +55,8 @@
             this.setTarget(cameraTarget.position);
         }
 
-        public _update():void {
-            super._update();
+        public _checkInputs():void {
+            super._checkInputs();
             this.follow(this.target);
         }
     }

+ 0 - 6
Babylon/Cameras/babylon.freeCamera.ts

@@ -258,11 +258,5 @@
                 this.position.addInPlace(this.cameraDirection);
             }
         }
-
-        public _update(): void {
-            this._checkInputs();
-            super._update();
-        }
-
     }
 } 

+ 69 - 1
Babylon/Cameras/babylon.targetCamera.ts

@@ -14,6 +14,8 @@
         public _camMatrix = Matrix.Zero();
         public _cameraTransformMatrix = Matrix.Zero();
         public _cameraRotationMatrix = Matrix.Zero();
+        private _subCamTransformMatrix: Matrix;
+
         public _referencePoint = new Vector3(0, 0, 1);
         public _transformedReferencePoint = Vector3.Zero();
         public _lookAtTemp = Matrix.Zero();
@@ -125,7 +127,7 @@
         public _updatePosition():void{
             this.position.addInPlace(this.cameraDirection);
         }
-        public _update():void {
+        public _checkInputs():void {
             var needToMove = this._decideIfNeedsToMove();
             var needToRotate = Math.abs(this.cameraRotation.x) > 0 || Math.abs(this.cameraRotation.y) > 0;
 
@@ -206,5 +208,71 @@
             Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
             return this._viewMatrix;
         }
+        
+        public _getOculusViewMatrix(): Matrix {
+            BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
+
+            BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
+            BABYLON.Vector3.TransformNormalToRef(this.upVector, this._cameraRotationMatrix, this._OculusActualUp);
+
+            // Computing target and final matrix
+            this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
+
+            BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this._OculusActualUp, this._OculusWorkMatrix);
+
+            this._OculusWorkMatrix.multiplyToRef(this._OculusPreViewMatrix, this._viewMatrix);
+            return this._viewMatrix;
+        }
+        
+        /**
+         * @override
+         * needs to be overridden, so sub has required properties to be copied
+         */
+        public GetSubCamera(name : string, isA : boolean) : Camera{
+            var subCamera = new BABYLON.TargetCamera(name, this.position.clone(), this.getScene());
+            if (this._subCameraMode === Camera.SUB_CAMS_OCULUS){
+                subCamera._OculusActualUp = new Vector3(0, 0, 0);
+                subCamera._getViewMatrix = subCamera._getOculusViewMatrix;
+            }
+            return subCamera;
+        }
+        
+        /**
+         * @override
+         * needs to be overridden, adding copy of position, and rotation for Oculus, or target for rest
+         */
+        public _updateSubCameras(){
+            var camA = <TargetCamera> this.subCameras[Camera.SUB_CAM_A];
+            var camB = <TargetCamera> this.subCameras[Camera.SUB_CAM_B];
+
+            if (this._subCameraMode === Camera.SUB_CAMS_OCULUS){
+                camA.rotation.x = camB.rotation.x = this.rotation.x;
+                camA.rotation.y = camB.rotation.y = this.rotation.y;
+                camA.rotation.z = camB.rotation.z = this.rotation.z;
+                
+                camA.position.copyFrom(this.position);
+                camB.position.copyFrom(this.position);
+                
+            }else{
+                camA.setTarget(this.getTarget());
+                camB.setTarget(this.getTarget());
+                
+                this._getSubCamPosition(-this._subCamHalfSapce, camA.position);
+                this._getSubCamPosition( this._subCamHalfSapce, camB.position);
+            }
+            super._updateSubCameras();
+        }
+        
+        private _getSubCamPosition(halfSapce, result) {
+            if (!this._subCamTransformMatrix){
+                this._subCamTransformMatrix = new BABYLON.Matrix();
+            }
+            var target = this.getTarget();
+            BABYLON.Matrix.Translation(-target.x, -target.y, -target.z).multiplyToRef(BABYLON.Matrix.RotationY(halfSapce), this._subCamTransformMatrix);
+
+            this._subCamTransformMatrix = this._subCamTransformMatrix.multiply(BABYLON.Matrix.Translation(target.x, target.y, target.z));
+
+            BABYLON.Vector3.TransformCoordinatesToRef(this.position, this._subCamTransformMatrix, result);
+        }
     }
 } 

+ 26 - 0
Babylon/PostProcess/babylon.stereogramInterlacePostProcess.js

@@ -0,0 +1,26 @@
+var __extends = this.__extends || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    __.prototype = b.prototype;
+    d.prototype = new __();
+};
+var BABYLON;
+(function (BABYLON) {
+    var StereogramInterlacePostProcess = (function (_super) {
+        __extends(StereogramInterlacePostProcess, _super);
+        function StereogramInterlacePostProcess(name, camB, postProcessA, isStereogramHoriz, samplingMode) {
+            var _this = this;
+            _super.call(this, name, "stereogramInterlace", ['stepSize'], ['camASampler'], 1, camB, samplingMode, camB.getScene().getEngine(), false, isStereogramHoriz ? "#define IS_STEREOGRAM_HORIZ 1" : undefined);
+            this._stepSize = new BABYLON.Vector2(1 / this.width, 1 / this.height);
+            this.onSizeChanged = function () {
+                _this._stepSize = new BABYLON.Vector2(1 / _this.width, 1 / _this.height);
+            };
+            this.onApply = function (effect) {
+                effect.setTextureFromPostProcess("camASampler", postProcessA);
+                effect.setFloat2("stepSize", _this._stepSize.x, _this._stepSize.y);
+            };
+        }
+        return StereogramInterlacePostProcess;
+    })(BABYLON.PostProcess);
+    BABYLON.StereogramInterlacePostProcess = StereogramInterlacePostProcess;
+})(BABYLON || (BABYLON = {}));

+ 19 - 0
Babylon/PostProcess/babylon.stereogramInterlacePostProcess.ts

@@ -0,0 +1,19 @@
+module BABYLON {
+    export class StereogramInterlacePostProcess extends PostProcess {
+        private _stepSize : Vector2;
+
+        constructor(name: string, camB: Camera, postProcessA : PostProcess, isStereogramHoriz: boolean, samplingMode?: number) {
+            super(name, "stereogramInterlace", ['stepSize'], ['camASampler'], 1, camB, samplingMode, camB.getScene().getEngine(), false, isStereogramHoriz ? "#define IS_STEREOGRAM_HORIZ 1" : undefined);
+            
+            this._stepSize = new Vector2(1 / this.width, 1 / this.height);
+
+            this.onSizeChanged = () => {
+                this._stepSize = new Vector2(1 / this.width, 1 / this.height);
+            };
+            this.onApply = (effect: Effect) => {
+                effect.setTextureFromPostProcess("camASampler", postProcessA);
+                effect.setFloat2("stepSize", this._stepSize.x, this._stepSize.y);
+            };
+        }
+    }
+}

+ 41 - 0
Babylon/Shaders/stereogramInterlace.fragment.fx

@@ -0,0 +1,41 @@
+#ifdef GL_ES
+precision highp float;
+#endif
+
+const vec3 TWO = vec3(2.0, 2.0, 2.0);
+
+varying vec2 vUV;
+uniform sampler2D camASampler;
+uniform sampler2D textureSampler;
+uniform vec2 stepSize;
+
+void main(void)
+{
+    bool useCamB;
+    vec2 texCoord1;
+    vec2 texCoord2;
+    
+    vec3 frag1;
+    vec3 frag2;
+    
+#ifdef IS_STEREOGRAM_HORIZ
+	    useCamB = vUV.x > 0.5;
+	    texCoord1 = vec2(useCamB ? (vUV.x - 0.5) * 2.0 : vUV.x * 2.0, vUV.y);
+	    texCoord2 = vec2(texCoord1.x + stepSize.x, vUV.y);
+#else
+	    useCamB = vUV.y > 0.5;
+	    texCoord1 = vec2(vUV.x, useCamB ? (vUV.y - 0.5) * 2.0 : vUV.y * 2.0);
+	    texCoord2 = vec2(vUV.x, texCoord1.y + stepSize.y);
+#endif
+    
+    // cannot assign a sampler to a variable, so must duplicate texture accesses
+    if (useCamB){
+        frag1 = texture2D(textureSampler, texCoord1).rgb;
+        frag2 = texture2D(textureSampler, texCoord2).rgb;
+    }else{
+        frag1 = texture2D(camASampler   , texCoord1).rgb;
+        frag2 = texture2D(camASampler   , texCoord2).rgb;
+    }
+    
+    gl_FragColor = vec4((frag1 + frag2) / TWO, 1.0);
+}

+ 1 - 0
Tools/Gulp/config.json

@@ -88,6 +88,7 @@
             "../../Babylon/PostProcess/babylon.convolutionPostProcess.js",
             "../../Babylon/PostProcess/babylon.filterPostProcess.js",
             "../../Babylon/PostProcess/babylon.fxaaPostProcess.js",
+            "../../Babylon/PostProcess/babylon.StereogramInterlacePostProcess.js",
             "../../Babylon/LensFlare/babylon.lensFlare.js",
             "../../Babylon/LensFlare/babylon.lensFlareSystem.js",
             "../../Babylon/Physics/Plugins/babylon.cannonJSPlugin.js",