浏览代码

camera rotation, math helper functions, validation/unit tests

Trevor Baron 6 年之前
父节点
当前提交
1b614d8450

+ 1 - 0
src/Cameras/XR/babylon.webXRCamera.ts

@@ -26,6 +26,7 @@ module BABYLON {
                 var newCamera = new BABYLON.TargetCamera("view: " + this.rigCameras.length, BABYLON.Vector3.Zero(), this.getScene());
                 newCamera.minZ = 0;
                 newCamera.parent = this;
+                newCamera.rotationQuaternion = new BABYLON.Quaternion();
                 this.rigCameras.push(newCamera);
             }
             while (this.rigCameras.length > viewCount) {

+ 8 - 1
src/Cameras/XR/babylon.webXREnterExitUI.ts

@@ -43,6 +43,13 @@ module BABYLON {
         private _overlay: HTMLDivElement;
         private _buttons: Array<WebXREnterExitUIButton> = [];
         private _activeButton: Nullable<WebXREnterExitUIButton> = null;
+        /**
+         * Fired every time the active button is changed.
+         *
+         * When xr is entered via a button that launches xr that button will be the callback parameter
+         *
+         * When exiting xr the callback parameter will be null)
+         */
         public activeButtonChangedObservable = new BABYLON.Observable<Nullable<WebXREnterExitUIButton>>();
         /**
          * Creates UI to allow the user to enter/exit XR mode
@@ -51,7 +58,7 @@ module BABYLON {
          * @param options options to configure the UI
          * @returns the created ui
          */
-        public static CreateAsync(scene: BABYLON.Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions):Promise<WebXREnterExitUI> {
+        public static CreateAsync(scene: BABYLON.Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions): Promise<WebXREnterExitUI> {
             var ui = new WebXREnterExitUI(scene, options);
             var supportedPromises = ui._buttons.map((btn) => {
                 return helper.supportsSessionAsync(btn.initializationOptions);

+ 26 - 0
src/Cameras/XR/babylon.webXRExperienceHelper.ts

@@ -44,6 +44,8 @@ module BABYLON {
             this.onStateChangedObservable.notifyObservers(this.state);
         }
 
+        private static _TmpVector = new BABYLON.Vector3();
+
         /**
          * Fires when the state of the experience helper has changed
          */
@@ -80,6 +82,7 @@ module BABYLON {
             this.camera = new BABYLON.WebXRCamera("", scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this.container = new AbstractMesh("", scene);
+            this.camera.parent = this.container;
         }
 
         /**
@@ -140,6 +143,29 @@ module BABYLON {
         }
 
         /**
+         * Updates the global position of the camera by moving the camera's container
+         * This should be used instead of modifying the camera's position as it will be overwritten by an xrSessions's update frame
+         * @param position The desired global position of the camera
+         */
+        public setPositionOfCameraUsingContainer(position: Vector3) {
+            this.camera.globalPosition.subtractToRef(position, WebXRExperienceHelper._TmpVector);
+            this.container.position.subtractInPlace(WebXRExperienceHelper._TmpVector);
+        }
+
+        /**
+         * Rotates the xr camera by rotating the camera's container around the camera's position
+         * This should be used instead of modifying the camera's rotation as it will be overwritten by an xrSessions's update frame
+         * @param rotation the desired quaternion rotation to apply to the camera
+         */
+        public rotateCameraByQuaternionUsingContainer(rotation: Quaternion) {
+            if (!this.container.rotationQuaternion) {
+                this.container.rotationQuaternion = Quaternion.FromEulerVector(this.container.rotation);
+            }
+            this.container.rotationQuaternion.multiplyInPlace(rotation);
+            this.container.position.rotateByQuaternionAroundPointToRef(rotation, this.camera.globalPosition, this.container.position);
+        }
+
+        /**
          * Checks if the creation options are supported by the xr session
          * @param options creation options
          * @returns true if supported

+ 102 - 0
src/Math/babylon.math.ts

@@ -1969,6 +1969,49 @@ module BABYLON {
         }
 
         /**
+         * Reorders the x y z properties of the vector in place
+         * @param order new ordering of the properties (eg. for vector 1,2,3 with "ZYX" will produce 3,2,1)
+         * @returns the current updated vector
+         */
+        public reorderInPlace(order: string) {
+            order = order.toLowerCase();
+            if (order === "xyz") {
+                return this;
+            }
+            MathTmp.Vector3[0].copyFrom(this);
+            ["x", "y", "z"].forEach((val, i) => {
+                (<any>this)[val] = (<any>MathTmp.Vector3[0])[order[i]];
+            });
+            return this;
+        }
+
+        /**
+         * Rotates the vector around 0,0,0 by a quaternion
+         * @param quaternion the rotation quaternion
+         * @param result vector to store the result
+         * @returns the resulting vector
+         */
+        public rotateByQuaternionToRef(quaternion: Quaternion, result: Vector3) {
+            quaternion.toRotationMatrix(MathTmp.Matrix[0]);
+            Vector3.TransformCoordinatesToRef(this, MathTmp.Matrix[0], result);
+            return result;
+        }
+
+        /**
+         * Rotates a vector around a given point
+         * @param quaternion the rotation quaternion
+         * @param point the point to rotate around
+         * @param result vector to store the result
+         * @returns the resulting vector
+         */
+        public rotateByQuaternionAroundPointToRef(quaternion: Quaternion, point: Vector3, result: Vector3) {
+            this.subtractToRef(point, MathTmp.Vector3[0]);
+            MathTmp.Vector3[0].rotateByQuaternionToRef(quaternion, MathTmp.Vector3[0]);
+            point.addToRef(MathTmp.Vector3[0], result);
+            return result;
+        }
+
+        /**
          * Normalize the current Vector3 with the given input length.
          * Please note that this is an in place operation.
          * @param len the length of the vector
@@ -3969,6 +4012,17 @@ module BABYLON {
         }
 
         /**
+         * Inverse a given quaternion
+         * @param q defines the source quaternion
+         * @param result the quaternion the result will be stored in
+         * @returns the result quaternion
+         */
+        public static InverseToRef(q: Quaternion, result: Quaternion): Quaternion {
+            result.set(-q.x, -q.y, -q.z, q.w);
+            return result;
+        }
+
+        /**
          * Creates an identity quaternion
          * @returns the identity quaternion
          */
@@ -4026,6 +4080,54 @@ module BABYLON {
         }
 
         /**
+         * Create a quaternion from Euler rotation angles
+         * @param x Pitch
+         * @param y Yaw
+         * @param z Roll
+         * @returns the new Quaternion
+         */
+        public static FromEulerAngles(x: number, y: number, z: number): Quaternion {
+            var q = new Quaternion();
+            Quaternion.RotationYawPitchRollToRef(y, x, z, q);
+            return q;
+        }
+
+        /**
+         * Updates a quaternion from Euler rotation angles
+         * @param x Pitch
+         * @param y Yaw
+         * @param z Roll
+         * @param result the quaternion to store the result
+         * @returns the updated quaternion
+         */
+        public static FromEulerAnglesToRef(x: number, y: number, z: number, result: Quaternion): Quaternion {
+            Quaternion.RotationYawPitchRollToRef(y, x, z, result);
+            return result;
+        }
+
+        /**
+         * Create a quaternion from Euler rotation vector
+         * @param vec the Euler vector (x Pitch, y Yaw, z Roll)
+         * @returns the new Quaternion
+         */
+        public static FromEulerVector(vec: Vector3): Quaternion {
+            var q = new Quaternion();
+            Quaternion.RotationYawPitchRollToRef(vec.y, vec.x, vec.z, q);
+            return q;
+        }
+
+        /**
+         * Updates a quaternion from Euler rotation vector
+         * @param vec the Euler vector (x Pitch, y Yaw, z Roll)
+         * @param result the quaternion to store the result
+         * @returns the updated quaternion
+         */
+        public static FromEulerVectorToRef(vec: Vector3, result: Quaternion): Quaternion {
+            Quaternion.RotationYawPitchRollToRef(vec.y, vec.x, vec.z, result);
+            return result;
+        }
+
+        /**
          * Creates a new quaternion from the given Euler float angles (y, x, z)
          * @param yaw defines the rotation around Y axis
          * @param pitch defines the rotation around X axis

+ 72 - 0
tests/unit/babylon/src/Mesh/babylon.positionAndRotation.tests.ts

@@ -0,0 +1,72 @@
+/**
+ * Describes the test suite.
+ */
+describe('Babylon position and rotation', () => {
+    let subject: BABYLON.Engine;
+
+    /**
+     * Loads the dependencies.
+     */
+    before(function (done) {
+        this.timeout(180000);
+        (BABYLONDEVTOOLS).Loader
+            .useDist()
+            .load(function () {
+                // Force apply promise polyfill for consistent behavior between PhantomJS, IE11, and other browsers.
+                BABYLON.PromisePolyfill.Apply(true);
+                done();
+            });
+    });
+
+    /**
+     * Create a new engine subject before each test.
+     */
+    beforeEach(function () {
+        subject = new BABYLON.NullEngine({
+            renderHeight: 256,
+            renderWidth: 256,
+            textureSize: 256,
+            deterministicLockstep: false,
+            lockstepMaxSteps: 1
+        });
+    });
+
+    describe('#position and rotation:', () => {
+        it('converts between quaternions/euler', () => {
+            // Converting between quaternions/euler
+            var originalRotation = new BABYLON.Vector3(0.1,0.2,0.3);
+            var v = originalRotation.clone()
+            var q = BABYLON.Quaternion.FromEulerVector(v)
+            q.toEulerAnglesToRef(v)
+            expect(v.subtract(originalRotation).length() < 0.00001).to.equal(true)
+        });
+        it('reorders vector in place', () => {
+            var originalRotation = new BABYLON.Vector3(0.1,0.2,0.3);
+            var v = originalRotation.clone()
+            v.reorderInPlace("ZYX")
+            expect(v.subtract(new BABYLON.Vector3(0.3,0.2,0.1)).length() < 0.00001).to.equal(true)
+        });
+        it('handles parenting', () => {
+            // Parent child positions
+            const scene = new BABYLON.Scene(subject);
+            var child = new BABYLON.AbstractMesh("", scene)
+            var parent = new BABYLON.AbstractMesh("", scene)
+            parent.position.set(0,0,1)
+            child.position.set(0,0,-1)
+            child.parent = parent
+            child.computeWorldMatrix()
+            expect(child.absolutePosition.equals(new BABYLON.Vector3(0,0,0))).to.equal(true)
+
+            //Rotate parent around child
+            parent.rotationQuaternion = new BABYLON.Quaternion()
+            var eulerRotation = new BABYLON.Vector3(0,Math.PI/2,0)
+            var rotation = new BABYLON.Quaternion()
+            BABYLON.Quaternion.RotationYawPitchRollToRef(eulerRotation.y, eulerRotation.x, eulerRotation.z, rotation)
+            parent.rotationQuaternion.multiplyInPlace(rotation);
+            parent.position.rotateByQuaternionAroundPointToRef(rotation, child.absolutePosition, parent.position)
+            expect(parent.position.subtract(new BABYLON.Vector3(1,0,0)).length() < 0.00001).to.equal(true)
+            expect(parent.rotationQuaternion.toEulerAngles().subtract(eulerRotation).length() < 0.00001).to.equal(true)
+            expect(child.absolutePosition.subtract(new BABYLON.Vector3(0,0,0)).length() < 0.00001).to.equal(true)
+        });
+    });
+});

+ 1 - 0
tests/unit/karma.conf.js

@@ -15,6 +15,7 @@ module.exports = function (config) {
             '!./**/*.d.ts',
             './Tools/DevLoader/BabylonLoader.js',
             './tests/unit/babylon/babylon.example.tests.js',
+            './tests/unit/babylon/src/Mesh/babylon.positionAndRotation.tests.js',
             './tests/unit/babylon/serializers/babylon.glTFSerializer.tests.js',
             './tests/unit/babylon/src/babylon.node.tests.js',
             './tests/unit/babylon/src/Animations/babylon.animationGroup.tests.js',

二进制
tests/validation/ReferenceImages/xrCameraContainerRotation.png


+ 5 - 0
tests/validation/config.json

@@ -2,6 +2,11 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
     {
+      "title": "XR camera container rotation",
+      "playgroundId": "#JV98QW#1",
+      "referenceImage": "xrCameraContainerRotation.png"
+    },
+    {
       "title": "Pointers",
       "playgroundId": "#1GLEJK#5",
       "referenceImage": "pointers.png",