#include "global.h" /* ----------------------------------------------------------------------------- File: RageMath Desc: See header. Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. Chris Danford Peter S. May (GetHashForString implementation) ----------------------------------------------------------------------------- */ #include "RageMath.h" #include "RageTypes.h" #include "RageUtil.h" #include "RageDisplay.h" #include "RageLog.h" #include void RageVec2Normalize( RageVector2* pOut, const RageVector2* pV ) { float scale = 1.0f / sqrtf( pV->x*pV->x + pV->y*pV->y ); pOut->x = pV->x * scale; pOut->y = pV->y * scale; } void RageVec3Normalize( RageVector3* pOut, const RageVector3* pV ) { float scale = 1.0f / sqrtf( pV->x*pV->x + pV->y*pV->y + pV->z*pV->z ); pOut->x = pV->x * scale; pOut->y = pV->y * scale; pOut->z = pV->z * scale; } void RageVec3TransformCoord( RageVector3* pOut, const RageVector3* pV, const RageMatrix* pM ) { RageVector4 temp( pV->x, pV->y, pV->z, 1.0f ); RageVec4TransformCoord( &temp, &temp, pM ); *pOut = RageVector3( temp.x/temp.w, temp.y/temp.w, temp.z/temp.w ); } void RageVec4TransformCoord( RageVector4* pOut, const RageVector4* pV, const RageMatrix* pM ) { const RageMatrix &a = *pM; const RageVector4 &v = *pOut; *pOut = RageVector4( a.m00*v.x+a.m10*v.y+a.m20*v.z+a.m30*v.w, a.m01*v.x+a.m11*v.y+a.m21*v.z+a.m31*v.w, a.m02*v.x+a.m12*v.y+a.m22*v.z+a.m32*v.w, a.m03*v.x+a.m13*v.y+a.m23*v.z+a.m33*v.w ); } void RageMatrixIdentity( RageMatrix* pOut ) { *pOut = RageMatrix( 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ); } void RageMatrixMultiply( RageMatrix* pOut, const RageMatrix* pA, const RageMatrix* pB ) { const RageMatrix &a = *pA; const RageMatrix &b = *pB; *pOut = RageMatrix( b.m00*a.m00+b.m01*a.m10+b.m02*a.m20+b.m03*a.m30, b.m00*a.m01+b.m01*a.m11+b.m02*a.m21+b.m03*a.m31, b.m00*a.m02+b.m01*a.m12+b.m02*a.m22+b.m03*a.m32, b.m00*a.m03+b.m01*a.m13+b.m02*a.m23+b.m03*a.m33, b.m10*a.m00+b.m11*a.m10+b.m12*a.m20+b.m13*a.m30, b.m10*a.m01+b.m11*a.m11+b.m12*a.m21+b.m13*a.m31, b.m10*a.m02+b.m11*a.m12+b.m12*a.m22+b.m13*a.m32, b.m10*a.m03+b.m11*a.m13+b.m12*a.m23+b.m13*a.m33, b.m20*a.m00+b.m21*a.m10+b.m22*a.m20+b.m23*a.m30, b.m20*a.m01+b.m21*a.m11+b.m22*a.m21+b.m23*a.m31, b.m20*a.m02+b.m21*a.m12+b.m22*a.m22+b.m23*a.m32, b.m20*a.m03+b.m21*a.m13+b.m22*a.m23+b.m23*a.m33, b.m30*a.m00+b.m31*a.m10+b.m32*a.m20+b.m33*a.m30, b.m30*a.m01+b.m31*a.m11+b.m32*a.m21+b.m33*a.m31, b.m30*a.m02+b.m31*a.m12+b.m32*a.m22+b.m33*a.m32, b.m30*a.m03+b.m31*a.m13+b.m32*a.m23+b.m33*a.m33 ); // phew! } void RageMatrixTranslation( RageMatrix* pOut, float x, float y, float z ) { RageMatrixIdentity(pOut); pOut->m[3][0] = x; pOut->m[3][1] = y; pOut->m[3][2] = z; } void RageMatrixScaling( RageMatrix* pOut, float x, float y, float z ) { RageMatrixIdentity(pOut); pOut->m[0][0] = x; pOut->m[1][1] = y; pOut->m[2][2] = z; } void RageMatrixRotationX( RageMatrix* pOut, float theta ) { theta *= PI/180; RageMatrixIdentity(pOut); pOut->m[1][1] = cosf(theta); pOut->m[2][2] = pOut->m[1][1]; pOut->m[2][1] = sinf(theta); pOut->m[1][2] = -pOut->m[2][1]; } void RageMatrixRotationY( RageMatrix* pOut, float theta ) { theta *= PI/180; RageMatrixIdentity(pOut); pOut->m[0][0] = cosf(theta); pOut->m[2][2] = pOut->m[0][0]; pOut->m[0][2] = sinf(theta); pOut->m[2][0] = -pOut->m[0][2]; } void RageMatrixRotationZ( RageMatrix* pOut, float theta ) { theta *= PI/180; RageMatrixIdentity(pOut); pOut->m[0][0] = cosf(theta); pOut->m[1][1] = pOut->m[0][0]; pOut->m[0][1] = sinf(theta); pOut->m[1][0] = -pOut->m[0][1]; } /* This is similar in style to Actor::Command. However, Actors don't store * matrix stacks; they only store offsets and scales, and compound them into * a single transformations at once. This makes some things easy, but it's not * convenient for generic 3d transforms. For that, we have this, which has the * small subset of the actor commands that applies to raw matrices, and we apply * commands in the order given. "scale,2;x,1;" is very different from * "x,1;scale,2;". */ static CString GetParam( const CStringArray& sParams, int iIndex, int& iMaxIndexAccessed ) { iMaxIndexAccessed = max( iIndex, iMaxIndexAccessed ); if( iIndex < int(sParams.size()) ) return sParams[iIndex]; else return ""; } void RageMatrixCommand( CString sCommandString, RageMatrix &mat ) { CStringArray asCommands; split( sCommandString, ";", asCommands, true ); for( unsigned c=0; cWarn( sError ); #if defined(WIN32) // XXX arch? if( DISPLAY->IsWindowed() ) MessageBox(NULL, sError, "MatrixCommand", MB_OK); #endif continue; } if( iMaxIndexAccessed != (int)asTokens.size()-1 ) { CString sError = ssprintf( "Wrong number of parameters in command '%s'. Expected %d but there are %d.", join(",",asTokens).GetString(), iMaxIndexAccessed+1, (int)asTokens.size() ); LOG->Warn( sError ); #if defined(WIN32) // XXX arch? if( DISPLAY->IsWindowed() ) MessageBox(NULL, sError, "MatrixCommand", MB_OK); #endif continue; } RageMatrix a(mat); RageMatrixMultiply(&mat, &a, &b); } } void RageQuatMultiply( RageVector4* pOut, const RageVector4 &pA, const RageVector4 &pB ) { RageVector4 out; out.x = pA.w * pB.x + pA.x * pB.w + pA.y * pB.z - pA.z * pB.y; out.y = pA.w * pB.y + pA.y * pB.w + pA.z * pB.x - pA.x * pB.z; out.z = pA.w * pB.z + pA.z * pB.w + pA.x * pB.y - pA.y * pB.x; out.w = pA.w * pB.w - pA.x * pB.x - pA.y * pB.y - pA.z * pB.z; float dist, square; square = out.x * out.x + out.y * out.y + out.z * out.z + out.w * out.w; if (square > 0.0) dist = 1.0f / sqrtf(square); else dist = 1; out.x *= dist; out.y *= dist; out.z *= dist; out.w *= dist; *pOut = out; } RageVector4 RageQuatFromH(float theta ) { theta *= PI/180.0f; theta /= 2.0f; theta *= -1; const float c = cosf(theta); const float s = sinf(theta); return RageVector4(0, s, 0, c); } RageVector4 RageQuatFromP(float theta ) { theta *= PI/180.0f; theta /= 2.0f; theta *= -1; const float c = cosf(theta); const float s = sinf(theta); return RageVector4(s, 0, 0, c); } RageVector4 RageQuatFromR(float theta ) { theta *= PI/180.0f; theta /= 2.0f; theta *= -1; const float c = cosf(theta); const float s = sinf(theta); return RageVector4(0, 0, s, c); } /* From http://www.gamasutra.com/features/19980703/quaternions_01.htm . * * The math on this page treats HPR as if Z is up and we're looking down Y; that * is, if you're in a room, the floor is on the XY plane and Z is height. * However, we treat the floor as the XZ plane, and Y is height; in other * words, as if the screen is the XY plane and negative Z goes into it. * So, instead of HPR.xyz being heading, pitch, roll, it's pitch, roll, heading. */ void RageQuatFromHPR(RageVector4* pOut, RageVector3 hpr ) { hpr *= PI; hpr /= 180.0f; hpr /= 2.0f; /* Set cX to the cosine of the angle we want to rotate on the X axis, * and so on. Here, hpr.z (roll) rotates on the Z axis, hpr.x (heading) * on Y, and hpr.y (pitch) on X. */ const float cZ = cosf(hpr.z); const float cY = cosf(hpr.x); const float cX = cosf(hpr.y); const float sZ = sinf(hpr.z); const float sY = sinf(hpr.x); const float sX = sinf(hpr.y); const float cYcZ = cY * cZ; const float sYsZ = sY * sZ; pOut->w = cX * cYcZ + sX * sYsZ; pOut->x = sX * cYcZ - cX * sYsZ; pOut->y = cX * sY * cZ + sX * cY * sZ; pOut->z = cX * cY * sZ - sX * sY * cZ; } void RageMatrixFromQuat( RageMatrix* pOut, const RageVector4 q ) { float xx = q.x * (q.x + q.x); float xy = q.x * (q.y + q.y); float xz = q.x * (q.z + q.z); float wx = q.w * (q.x + q.x); float wy = q.w * (q.y + q.y); float wz = q.w * (q.z + q.z); float yy = q.y * (q.y + q.y); float yz = q.y * (q.z + q.z); float zz = q.z * (q.z + q.z); *pOut = RageMatrix( 1.0f-(yy+zz), xy-wz, xz+wy, 0, xy+wz, 1-(xx+zz), yz-wx, 0, xz-wy, yz+wx, 1-(xx+yy), 0, 0, 0, 0, 1 ); } void RageQuatSlerp(RageVector4 *pOut, const RageVector4 &from, const RageVector4 &to, float t) { float to1[4]; // calc cosine float cosom = from.x * to.x + from.y * to.y + from.z * to.z + from.w * to.w; // adjust signs (if necessary) if ( cosom < 0 ) { cosom = -cosom; to1[0] = - to.x; to1[1] = - to.y; to1[2] = - to.z; to1[3] = - to.w; } else { to1[0] = to.x; to1[1] = to.y; to1[2] = to.z; to1[3] = to.w; } // calculate coefficients float scale0, scale1; if ( 1.0f - cosom > 0 ) { // standard case (slerp) float omega = acosf(cosom); float sinom = sinf(omega); scale0 = sinf((1.0f - t) * omega) / sinom; scale1 = sinf(t * omega) / sinom; } else { // "from" and "to" quaternions are very close // ... so we can do a linear interpolation scale0 = 1.0f - t; scale1 = t; } // calculate final values pOut->x = scale0 * from.x + scale1 * to1[0]; pOut->y = scale0 * from.y + scale1 * to1[1]; pOut->z = scale0 * from.z + scale1 * to1[2]; pOut->w = scale0 * from.w + scale1 * to1[3]; }