/// <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); }
/// <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); }
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); }
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); }
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); } }
/// <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; }
/// <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); }