void cFrustum::UpdateEndPoints() { float fXAngle = m_fFOV*0.5f; float fYAngle = m_fFOV*0.5f*m_fAspect; cVector3f vForward = m_mtxModelView.GetForward(); cVector3f vUp = m_mtxModelView.GetUp(); cVector3f vRight = m_mtxModelView.GetRight(); cVector3f vUpDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(fXAngle, vRight)), vForward); cVector3f vDownDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(-fXAngle, vRight)), vForward); vForward = vForward * -1; float fRightAdd = sin(fYAngle); cVector3f vVec0 = vUpDir + vRight * fRightAdd; cVector3f vVec2 = vDownDir + vRight * fRightAdd; cVector3f vVec3 = vDownDir + vRight * -fRightAdd; cVector3f vVec1 = vUpDir + vRight * -fRightAdd; float fAngle0 = cMath::Vector3Angle(vVec0, vForward); float fAngle1 = cMath::Vector3Angle(vVec1, vForward); float fAngle2 = cMath::Vector3Angle(vVec2, vForward); float fAngle3 = cMath::Vector3Angle(vVec3, vForward); m_vEndPoints[0] = m_vOrigin + vVec0 * (m_fFarPlane/cos(fAngle0)); m_vEndPoints[1] = m_vOrigin + vVec1 * (m_fFarPlane/cos(fAngle1)); m_vEndPoints[2] = m_vOrigin + vVec2 * (m_fFarPlane/cos(fAngle2)); m_vEndPoints[3] = m_vOrigin + vVec3 * (m_fFarPlane/cos(fAngle3)); }
void cFrustum::UpdateEndPoints() { float fXAngle = mfFOV*0.5f; float fYAngle = mfFOV*0.5f*mfAspect; cVector3f vForward = m_mtxModelView.GetForward(); cVector3f vUp = m_mtxModelView.GetUp(); cVector3f vRight = m_mtxModelView.GetRight(); //Point the forward vec in different dirs. cVector3f vUpDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(fXAngle,vRight)), vForward); cVector3f vDownDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(-fXAngle,vRight)), vForward); //cVector3f vRightDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(fYAngle,vUp)), vForward); //cVector3f vLeftDir = cMath::MatrixMul(cMath::MatrixQuaternion(cQuaternion(-fYAngle,vUp)), vForward); vForward = vForward *-1; float fRightAdd = sin(fYAngle); //float fUpAdd = sin(fXAngle); cVector3f vVec0 = vUpDir + vRight * fRightAdd; cVector3f vVec2 = vDownDir + vRight * fRightAdd; cVector3f vVec3 = vDownDir + vRight * -fRightAdd; cVector3f vVec1 = vUpDir + vRight * -fRightAdd; /*cVector3f vVec0 = vUpDir;//cMath::Vector3Normalize(vUpDir+vRightDir); cVector3f vVec1 = vRightDir;//cMath::Vector3Normalize(vUpDir+vLeftDir); cVector3f vVec2 = vDownDir;//cMath::Vector3Normalize(vDownDir+vLeftDir); cVector3f vVec3 = vLeftDir;//cMath::Vector3Normalize(vDownDir+vRightDir);*/ //angles between forward and the vectors float fAngle0 = cMath::Vector3Angle(vVec0, vForward); float fAngle1 = cMath::Vector3Angle(vVec1, vForward); float fAngle2 = cMath::Vector3Angle(vVec2, vForward); float fAngle3 = cMath::Vector3Angle(vVec3, vForward); //create end points. mvEndPoints[0] = mvOrigin + vVec0 * (mfFarPlane/cos(fAngle0)); mvEndPoints[1] = mvOrigin + vVec1 * (mfFarPlane/cos(fAngle1)); mvEndPoints[2] = mvOrigin + vVec2 * (mfFarPlane/cos(fAngle2)); mvEndPoints[3] = mvOrigin + vVec3 * (mfFarPlane/cos(fAngle3)); /*mvEndPoints[0] = mvOrigin + (vUpDir+vRightDir) * mfFarPlane *-1; mvEndPoints[1] = mvOrigin + (vUpDir+vLeftDir) * mfFarPlane*-1; mvEndPoints[2] = mvOrigin + (vDownDir+vRightDir)* mfFarPlane*-1; mvEndPoints[3] = mvOrigin + (vDownDir+vLeftDir) * mfFarPlane*-1;*/ }
// *** Spherical linear interpolated quaternion between q1 and q2, with respect to t // // Everyone understands // the terms, "matrix to quaternion", "quaternion to matrix", "create quaternion matrix", // "quaternion multiplication", etc.. but "SLERP" is the stumbling block, even a little // bit after hearing what it stands for, "Spherical Linear Interpolation". What that // means is that we have 2 quaternions (or rotations) and we want to interpolate between // them. The reason what it's called "spherical" is that quaternions deal with a sphere. // Linear interpolation just deals with 2 points primarily, where when dealing with angles // and rotations, we need to use sin() and cos() for interpolation. If we wanted to use // quaternions for camera rotations, which have much more instant and jerky changes in // rotations, we would use Spherical-Cubic Interpolation. The equation for SLERP is this: // // q = (((b.a)^-1)^t)a // void cQuaternion::SlerpForMD3(const cQuaternion& q1, const cQuaternion& q2Original, float t) { cQuaternion q2(q2Original); // Here we do a check to make sure the 2 quaternions aren't the same, return q1 if they are if (q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w) { SetFromQuaternion(q1); return; } // Following the (b.a) part of the equation, we do a dot product between q1 and q2. // We can do a dot product because the same math applied for a 3D vector as a 4D vector. float result = (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z) + (q1.w * q2.w); // If the dot product is less than 0, the angle is greater than 90 degrees if (result < 0.0f) { // Negate the second quaternion and the result of the dot product q2 = cQuaternion(-q2.x, -q2.y, -q2.z, -q2.w); result = -result; } // Set the first and second scale for the interpolation float scale0 = 1.0f - t; float scale1 = t; // Next, we want to actually calculate the spherical interpolation. Since this // calculation is quite computationally expensive, we want to only perform it // if the angle between the 2 quaternions is large enough to warrant it. If the // angle is fairly small, we can actually just do a simpler linear interpolation // of the 2 quaternions, and skip all the complex math. We create a "delta" value // of 0.1 to say that if the cosine of the angle (result of the dot product) between // the 2 quaternions is smaller than 0.1, then we do NOT want to perform the full on // interpolation using. This is because you won't really notice the difference. // Check if the angle between the 2 quaternions was big enough to warrant such calculations if (1.0f - result > 0.1f) { // Get the angle between the 2 quaternions, and then store the sin() of that angle float theta = (float)acos(result); float sinTheta = (float)sin(theta); // Calculate the scale for q1 and q2, according to the angle and it's sine value scale0 = (float)sin((1.0f - t)* theta) / sinTheta; scale1 = (float)sin((t * theta)) / sinTheta; } // Calculate the x, y, z and w values for the quaternion by using a special form of linear interpolation for quaternions x = (scale0 * q1.x) + (scale1 * q2.x), y = (scale0 * q1.y) + (scale1 * q2.y), z = (scale0 * q1.z) + (scale1 * q2.z), w = (scale0 * q1.w) + (scale1 * q2.w); }
namespace hpl { ////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cQuaternion::cQuaternion() { } //----------------------------------------------------------------------- cQuaternion::cQuaternion(float afAngle, const cVector3f & avAxis) { FromAngleAxis(afAngle,avAxis); } cQuaternion::cQuaternion(float afW, float afX, float afY, float afZ) { w = afW; v.x = afX; v.y = afY; v.z = afZ; } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////////////////// const cQuaternion cQuaternion::Identity = cQuaternion(1.0f,0.0f,0.0f,0.0f); //----------------------------------------------------------------------- void cQuaternion::Normalise() { float fLen = w*w + v.x*v.x + v.y*v.y + v.z*v.z; float fFactor = 1.0f / sqrt(fLen); v = v * fFactor; w = w * fFactor; } //----------------------------------------------------------------------- void cQuaternion::ToRotationMatrix(cMatrixf &a_mtxDest) const { cMatrixf mtxA; float fTx = 2.0f*v.x; float fTy = 2.0f*v.y; float fTz = 2.0f*v.z; float fTwx = fTx*w; float fTwy = fTy*w; float fTwz = fTz*w; float fTxx = fTx*v.x; float fTxy = fTy*v.x; float fTxz = fTz*v.x; float fTyy = fTy*v.y; float fTyz = fTz*v.y; float fTzz = fTz*v.z; a_mtxDest.m[0][0] = 1.0f-(fTyy+fTzz); a_mtxDest.m[0][1] = fTxy-fTwz; a_mtxDest.m[0][2] = fTxz+fTwy; a_mtxDest.m[1][0] = fTxy+fTwz; a_mtxDest.m[1][1] = 1.0f-(fTxx+fTzz); a_mtxDest.m[1][2] = fTyz-fTwx; a_mtxDest.m[2][0] = fTxz-fTwy; a_mtxDest.m[2][1] = fTyz+fTwx; a_mtxDest.m[2][2] = 1.0f-(fTxx+fTyy); } //----------------------------------------------------------------------- void cQuaternion::FromRotationMatrix(const cMatrix<float> &a_mtxRot) { float fTrace = a_mtxRot.m[0][0]+a_mtxRot.m[1][1]+a_mtxRot.m[2][2]; float fRoot; if ( fTrace > 0.0 ) { // |w| > 1/2, may as well choose w > 1/2 fRoot = sqrt(fTrace + 1.0f); // 2w w = 0.5f*fRoot; fRoot = 0.5f/fRoot; // 1/(4w) v.x = (a_mtxRot.m[2][1]-a_mtxRot.m[1][2])*fRoot; v.y = (a_mtxRot.m[0][2]-a_mtxRot.m[2][0])*fRoot; v.z = (a_mtxRot.m[1][0]-a_mtxRot.m[0][1])*fRoot; } else { // |w| <= 1/2 static size_t s_iNext[3] = { 1, 2, 0 }; size_t i = 0; if ( a_mtxRot.m[1][1] > a_mtxRot.m[0][0] ) i = 1; if ( a_mtxRot.m[2][2] > a_mtxRot.m[i][i] ) i = 2; size_t j = s_iNext[i]; size_t k = s_iNext[j]; fRoot = sqrt(a_mtxRot.m[i][i]-a_mtxRot.m[j][j]-a_mtxRot.m[k][k] + 1.0f); float* apkQuat[3] = { &v.x, &v.y, &v.z }; *apkQuat[i] = 0.5f*fRoot; fRoot = 0.5f/fRoot; w = (a_mtxRot.m[k][j]-a_mtxRot.m[j][k])*fRoot; *apkQuat[j] = (a_mtxRot.m[j][i]+a_mtxRot.m[i][j])*fRoot; *apkQuat[k] = (a_mtxRot.m[k][i]+a_mtxRot.m[i][k])*fRoot; } } //----------------------------------------------------------------------- void cQuaternion::FromAngleAxis(float afAngle, const cVector3f &avAxis) { // assert: axis[] is unit length // // The quaternion representing the rotation is // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) float fHalfAngle = 0.5f*afAngle; float fSin = sin(fHalfAngle); w = cos(fHalfAngle); v.x = fSin*avAxis.x; v.y = fSin*avAxis.y; v.z = fSin*avAxis.z; } //----------------------------------------------------------------------- cQuaternion cQuaternion::operator+(const cQuaternion &aqB) const { cQuaternion qOut; qOut.v = v + aqB.v; qOut.w = w + aqB.w; return qOut; } //----------------------------------------------------------------------- cQuaternion cQuaternion::operator-(const cQuaternion &aqB) const { cQuaternion qOut; qOut.v = v - aqB.v; qOut.w = w - aqB.w; return qOut; } //----------------------------------------------------------------------- cQuaternion cQuaternion::operator*(const cQuaternion &aqB) const { cQuaternion qOut; qOut.w = w * aqB.w - v.x * aqB.v.x - v.y * aqB.v.y - v.z * aqB.v.z; qOut.v.x = w * aqB.v.x + v.x * aqB.w + v.y * aqB.v.z - v.z * aqB.v.y; qOut.v.y = w * aqB.v.y + v.y * aqB.w + v.z * aqB.v.x - v.x * aqB.v.z; qOut.v.z = w * aqB.v.z + v.z * aqB.w + v.x * aqB.v.y - v.y * aqB.v.x; return qOut; } //----------------------------------------------------------------------- cQuaternion cQuaternion::operator*(float afScalar) const { cQuaternion qOut; qOut.v = v * afScalar; qOut.w = w * afScalar; return qOut; } //----------------------------------------------------------------------- }