예제 #1
0
        /// <summary>
        /// Gets the view matrix corresponding to a "look at" point</summary>
        /// <param name="azimuth">Camera pan, in radians</param>
        /// <param name="elevation">Camera tilt, in radians</param>
        /// <param name="lookAt">"Look at" direction</param>
        /// <returns>View matrix corresponding to the "look at" point</returns>
        public static Matrix4F LookAtRotationMatrix(float azimuth, float elevation, Vec3F lookAt)
        {
            double sy = Math.Sin(azimuth);
            double cy = Math.Cos(azimuth);
            double sx = Math.Sin(elevation);
            double cx = Math.Cos(elevation);

            // Calc eye vector as the rotation matrix * (0,0,1)
            Vec3F eye = new Vec3F((float)sy, (float)(-cy * sx), (float)(cy * cx));

            eye = eye + lookAt;

            // Calc up vector as the rotation matrix * (0,1,0)
            Vec3F up = new Vec3F(0, (float)cx, (float)sx);

            Vec3F fvec = lookAt - eye;
            Vec3F f    = Vec3F.Normalize(fvec);
            Vec3F s    = Vec3F.Cross(f, up);
            Vec3F u    = Vec3F.Cross(s, f);

            return(new Matrix4F
                   (
                       s.X, s.Y, s.Z, 0,
                       u.X, u.Y, u.Z, 0,
                       -f.X, -f.Y, -f.Z, 0,
                       0, 0, 0, 1
                   ));
        }
        /// <summary>
        /// Synchronizes the camera to the controller's current state</summary>
        /// <param name="camera">Camera</param>
        protected override void ControllerToCamera(Camera camera)
        {
            Vec3F lookAt = camera.LookAt;
            Vec3F right  = camera.Right;
            Vec3F up     = camera.Up;

            if (camera.ViewType == ViewTypes.Perspective)
            {
                // override the camera's frame of reference
                float sinPhi   = (float)Math.Sin(m_elevation);
                float cosPhi   = (float)Math.Cos(m_elevation);
                float sinTheta = (float)Math.Sin(m_azimuth);
                float cosTheta = (float)Math.Cos(m_azimuth);

                lookAt = new Vec3F(-cosPhi * sinTheta, -sinPhi, -cosPhi * cosTheta);
                right  = new Vec3F(cosTheta, 0, -sinTheta);
                up     = Vec3F.Cross(right, lookAt); // TODO compute from sin/cos values
            }

            float lookAtOffset = 0;

            if (m_distanceFromLookAt < m_dollyThreshold) // do we need to start dollying?
            {
                lookAtOffset = m_distanceFromLookAt - m_dollyThreshold;
            }

            float eyeOffset = m_distanceFromLookAt;

            Camera.Set(m_lookAtPoint - (eyeOffset * lookAt),    // eye point
                       m_lookAtPoint - (lookAtOffset * lookAt), // look-at point
                       up);                                     // up vector

            base.ControllerToCamera(camera);
        }
예제 #3
0
        /// <summary>
        /// Gets the view matrix corresponding to a view direction</summary>
        /// <param name="viewDirection">View direction</param>
        /// <returns>View matrix corresponding to the view direction</returns>
        public static Matrix4F LookAtMatrix(Vec3F viewDirection)
        {
            // check if viewDirection is non-zero length and normalize it if so.
            float length = viewDirection.Length;

            if (length == 0.0f)
            {
                return(new Matrix4F());
            }
            viewDirection = viewDirection * (1.0f / length);

            // find a basis vector (x-axis or y-axis) that is not parallel to viewDirection.
            // give preference to y-axis for historical purposes.
            // Create 's' and 'u' that are orthonormal vectors, relative to viewDirection.
            // 'viewDirection' is like the z-axis. 's' is the x-axis, and 'u' is the y-axis.
            // We want: s X u = viewDir so that resulting coordinate system is right-handed.
            // Otherwise, you get the triangles all flipped.
            Vec3F s;
            Vec3F xAxis = new Vec3F(1, 0, 0);
            Vec3F yAxis = new Vec3F(0, 1, 0);

            if (Math.Abs(Vec3F.Dot(yAxis, viewDirection)) < 0.98)
            {
                s = Vec3F.Cross(yAxis, viewDirection); //x' = y X z
            }
            else
            {
                // viewDirection == yAxis
                s = Vec3F.Cross(viewDirection, xAxis); //x' = y X x
            }
            Vec3F u = Vec3F.Cross(viewDirection, s);   //y' = z X x'

            s = s / s.Length;
            u = u / u.Length;

            Matrix4F T = new Matrix4F
                         (
                s.X, s.Y, s.Z, 0,
                u.X, u.Y, u.Z, 0,
                viewDirection.X, viewDirection.Y, viewDirection.Z, 0,
                0, 0, 0, 1
                         );

            return(T);
        }
        private void Track(Point current)
        {
            if (current.X < 0 || current.X > m_width)
            {
                return;
            }

            if (current.Y < 0 || current.Y > m_height)
            {
                return;
            }

            Vec3F currentPoint = ProjectToArcball(current);
            Vec3F i            = Vec3F.Cross(currentPoint, m_firstPoint);
            float r            = Vec3F.Dot(m_firstPoint, currentPoint);

            m_currentRotation = new QuatF(i.X, i.Y, i.Z, r);
        }
예제 #5
0
        private static float CalcAngle(Vec3F v0, Vec3F v1, Vec3F axis, float snapAngle)
        {
            const float twoPi = (float)(2.0 * Math.PI);
            float       angle = Vec3F.Dot(v0, v1);

            if (angle > 1.0f)
            {
                angle = 1.0f;
            }
            else if (angle <= -1.0f)
            {
                angle = 1.0f;
            }
            angle = (float)Math.Acos(angle);

            // Check if v0 is left of v1
            Vec3F cross = Vec3F.Cross(v0, v1);

            if (Vec3F.Dot(cross, axis) < 0)
            {
                angle = twoPi - angle;
            }
            // round to nearest multiple of preference
            if (snapAngle > 0)
            {
                if (angle >= 0)
                {
                    int n = (int)((angle + snapAngle * 0.5f) / snapAngle);
                    angle = n * snapAngle;
                }
                else
                {
                    int n = (int)((angle - snapAngle * 0.5f) / snapAngle);
                    angle = n * snapAngle;
                }
            }
            // const float epsilone = (float)1e-7;
            // if (twoPi - angle <= epsilone) angle = 0.0f;
            return(angle);
        }
예제 #6
0
        private static float CalcAngle(Vec3F v0, Vec3F v1, Vec3F axis, float snapAngle)
        {
            float angle = Vec3F.Dot(v0, v1);

            if (angle > 1.0f)
            {
                angle = 1.0f;
            }
            else if (angle < -1.0f)
            {
                angle = 1.0f;
            }
            angle = (float)Math.Acos(angle);

            // Check if v0 is left of v1
            Vec3F cross = Vec3F.Cross(v0, v1);

            if (Vec3F.Dot(cross, axis) < 0)
            {
                angle = -angle;
            }

            // round to nearest multiple of preference
            if (snapAngle > 0)
            {
                if (angle >= 0)
                {
                    int n = (int)((angle + snapAngle * 0.5) / snapAngle);
                    angle = n * snapAngle;
                }
                else
                {
                    int n = (int)((angle - snapAngle * 0.5) / snapAngle);
                    angle = n * snapAngle;
                }
            }

            return(angle);
        }
예제 #7
0
        private void UpdateGeometry()
        {
            // create orthornormal basis from lookAt and up vectors
            m_lookAt = m_lookAtPoint - m_eye;
            m_lookAt.Normalize();

            m_right = Vec3F.Cross(m_lookAt, m_up);
            m_right.Normalize();

            m_up = Vec3F.Cross(m_right, m_lookAt);
            //m_up.Normalize(); // m_right is already unit length

            m_lookAtDistance = Vec3F.Distance(m_eye, m_lookAtPoint);

            if (ProjectionType == ProjectionType.Orthographic)
            {
                SetOrthographic(m_lookAtDistance);
            }
            else
            {
                OnCameraChanged(EventArgs.Empty);
            }
        }
예제 #8
0
        /// <summary>
        /// Creates Billboard matrix from the given parameters.</summary>
        public static void CreateBillboard(Matrix4F matrix, Vec3F objectPos, Vec3F camPos, Vec3F camUp, Vector3 camLook)
        {
            Vector3 look = objectPos - camPos;
            float   len  = look.LengthSquared;

            if (len < 0.0001f)
            {
                look = -camLook;
            }
            else
            {
                look.Normalize();
            }

            Vector3 right = Vec3F.Cross(camUp, look);

            right.Normalize();
            Vector3 up = Vec3F.Cross(look, right);

            matrix.M11 = right.X;
            matrix.M12 = right.Y;
            matrix.M13 = right.Z;
            matrix.M14 = 0f;
            matrix.M21 = up.X;
            matrix.M22 = up.Y;
            matrix.M23 = up.Z;
            matrix.M24 = 0f;
            matrix.M31 = look.X;
            matrix.M32 = look.Y;
            matrix.M33 = look.Z;
            matrix.M34 = 0f;
            matrix.M41 = objectPos.X;
            matrix.M42 = objectPos.Y;
            matrix.M43 = objectPos.Z;
            matrix.M44 = 1f;
        }
예제 #9
0
        /// <summary>
        /// Given an object's current Euler angles and a surface normal, will calculate
        ///  the Euler angles necessary to rotate the object so that it's up-vector is
        ///  aligned with the surface normal.
        /// </summary>
        /// <param name="originalEulers">From an IRenderableNode.Rotation, for example.</param>
        /// <param name="surfaceNormal">a unit vector that the object should be rotate-snapped to</param>
        /// <param name="upAxis">y or z is up?</param>
        /// <returns>The resulting angles to be assigned to IRenderableNode.Rotation</returns>
        /// <remarks>
        /// Note that QuatF was attempted to be used, but I could not get it to work reliably
        ///  with the Matrix3F.GetEulerAngles(). Numerical instability? The basis vector
        ///  method below works well except for when the target surface is 90 degrees different
        ///  than the starting up vector in which case the rotation around the up vector is lost
        ///  (gimbal lock) but the results are always valid in the sense that the up vector
        ///  is aligned with the surface normal. --Ron Little
        /// </remarks>
        public static Vec3F RotateToVector(Vec3F originalEulers, Vec3F surfaceNormal, AxisSystemType upAxis)
        {
            // get basis vectors for the current rotation
            Matrix3F rotMat = new Matrix3F();

            rotMat.Rotation(originalEulers);
            Vec3F a1 = rotMat.XAxis;
            Vec3F a2 = rotMat.YAxis;
            Vec3F a3 = rotMat.ZAxis;

            // calculate destination basis vectors
            Vec3F b1, b2, b3;

            if (upAxis == AxisSystemType.YIsUp)
            {
                // a2 is the current up vector. b2 is the final up vector.
                // now, find either a1 or a3, whichever is most orthogonal to surface
                b2 = new Vec3F(surfaceNormal);
                float a1DotS = Vec3F.Dot(a1, surfaceNormal);
                float a3DotS = Vec3F.Dot(a3, surfaceNormal);
                if (Math.Abs(a1DotS) < Math.Abs(a3DotS))
                {
                    b1 = new Vec3F(a1);
                    b3 = Vec3F.Cross(b1, b2);
                    b1 = Vec3F.Cross(b2, b3);
                }
                else
                {
                    b3 = new Vec3F(a3);
                    b1 = Vec3F.Cross(b2, b3);
                    b3 = Vec3F.Cross(b1, b2);
                }
            }
            else
            {
                // a3 is the current up vector. b3 is the final up vector.
                // now, find either a1 or a2, whichever is most orthogonal to surface
                b3 = new Vec3F(surfaceNormal);
                float a1DotS = Vec3F.Dot(a1, surfaceNormal);
                float a2DotS = Vec3F.Dot(a2, surfaceNormal);
                if (Math.Abs(a1DotS) < Math.Abs(a2DotS))
                {
                    b1 = new Vec3F(a1);
                    b2 = Vec3F.Cross(b3, b1);
                    b1 = Vec3F.Cross(b2, b3);
                }
                else
                {
                    b2 = new Vec3F(a2);
                    b1 = Vec3F.Cross(b2, b3);
                    b2 = Vec3F.Cross(b3, b1);
                }
            }

            // in theory, this isn't necessary, but in practice...
            b1.Normalize();
            b2.Normalize();
            b3.Normalize();

            // construct new rotation matrix and extract euler angles
            rotMat.XAxis = b1;
            rotMat.YAxis = b2;
            rotMat.ZAxis = b3;

            Vec3F newEulers = new Vec3F();

            rotMat.GetEulerAngles(out newEulers.X, out newEulers.Y, out newEulers.Z);
            return(newEulers);
        }