/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #pragma hdrstop #include "../../idlib/precompiled.h" #include "../Game_local.h" idCVar binaryLoadAnim( "binaryLoadAnim", "1", 0, "enable binary load/write of idMD5Anim" ); static const byte B_ANIM_MD5_VERSION = 101; static const unsigned int B_ANIM_MD5_MAGIC = ( 'B' << 24 ) | ( 'M' << 16 ) | ( 'D' << 8 ) | B_ANIM_MD5_VERSION; static const int JOINT_FRAME_PAD = 1; // one extra to be able to read one more float than is necessary bool idAnimManager::forceExport = false; /*********************************************************************** idMD5Anim ***********************************************************************/ /* ==================== idMD5Anim::idMD5Anim ==================== */ idMD5Anim::idMD5Anim() { ref_count = 0; numFrames = 0; numJoints = 0; frameRate = 24; animLength = 0; numAnimatedComponents = 0; totaldelta.Zero(); } /* ==================== idMD5Anim::idMD5Anim ==================== */ idMD5Anim::~idMD5Anim() { Free(); } /* ==================== idMD5Anim::Free ==================== */ void idMD5Anim::Free() { numFrames = 0; numJoints = 0; frameRate = 24; animLength = 0; numAnimatedComponents = 0; //name = ""; totaldelta.Zero(); jointInfo.Clear(); bounds.Clear(); componentFrames.Clear(); } /* ==================== idMD5Anim::NumFrames ==================== */ int idMD5Anim::NumFrames() const { return numFrames; } /* ==================== idMD5Anim::NumJoints ==================== */ int idMD5Anim::NumJoints() const { return numJoints; } /* ==================== idMD5Anim::Length ==================== */ int idMD5Anim::Length() const { return animLength; } /* ===================== idMD5Anim::TotalMovementDelta ===================== */ const idVec3 &idMD5Anim::TotalMovementDelta() const { return totaldelta; } /* ===================== idMD5Anim::TotalMovementDelta ===================== */ const char *idMD5Anim::Name() const { return name; } /* ==================== idMD5Anim::Reload ==================== */ bool idMD5Anim::Reload() { idStr filename; filename = name; Free(); return LoadAnim( filename ); } /* ==================== idMD5Anim::Allocated ==================== */ size_t idMD5Anim::Allocated() const { size_t size = bounds.Allocated() + jointInfo.Allocated() + componentFrames.Allocated() + name.Allocated(); return size; } /* ==================== idMD5Anim::LoadAnim ==================== */ bool idMD5Anim::LoadAnim( const char * filename ) { idLexer parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT ); idToken token; idStr generatedFileName = "generated/anim/"; generatedFileName.AppendPath( filename ); generatedFileName.SetFileExtension( ".bMD5anim" ); // Get the timestamp on the original file, if it's newer than what is stored in binary model, regenerate it ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( filename ); idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) ); if ( binaryLoadAnim.GetBool() && LoadBinary( file, sourceTimeStamp ) ) { name = filename; if ( cvarSystem->GetCVarBool( "fs_buildresources" ) ) { // for resource gathering write this anim to the preload file for this map fileSystem->AddAnimPreload( name ); } return true; } if ( !parser.LoadFile( filename ) ) { return false; } name = filename; Free(); parser.ExpectTokenString( MD5_VERSION_STRING ); int version = parser.ParseInt(); if ( version != MD5_VERSION ) { parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION ); } // skip the commandline parser.ExpectTokenString( "commandline" ); parser.ReadToken( &token ); // parse num frames parser.ExpectTokenString( "numFrames" ); numFrames = parser.ParseInt(); if ( numFrames <= 0 ) { parser.Error( "Invalid number of frames: %d", numFrames ); } // parse num joints parser.ExpectTokenString( "numJoints" ); numJoints = parser.ParseInt(); if ( numJoints <= 0 ) { parser.Error( "Invalid number of joints: %d", numJoints ); } // parse frame rate parser.ExpectTokenString( "frameRate" ); frameRate = parser.ParseInt(); if ( frameRate < 0 ) { parser.Error( "Invalid frame rate: %d", frameRate ); } // parse number of animated components parser.ExpectTokenString( "numAnimatedComponents" ); numAnimatedComponents = parser.ParseInt(); if ( ( numAnimatedComponents < 0 ) || ( numAnimatedComponents > numJoints * 6 ) ) { parser.Error( "Invalid number of animated components: %d", numAnimatedComponents ); } // parse the hierarchy jointInfo.SetGranularity( 1 ); jointInfo.SetNum( numJoints ); parser.ExpectTokenString( "hierarchy" ); parser.ExpectTokenString( "{" ); for ( int i = 0; i < numJoints; i++ ) { parser.ReadToken( &token ); jointInfo[ i ].nameIndex = animationLib.JointIndex( token ); // parse parent num jointInfo[ i ].parentNum = parser.ParseInt(); if ( jointInfo[ i ].parentNum >= i ) { parser.Error( "Invalid parent num: %d", jointInfo[ i ].parentNum ); } if ( ( i != 0 ) && ( jointInfo[ i ].parentNum < 0 ) ) { parser.Error( "Animations may have only one root joint" ); } // parse anim bits jointInfo[ i ].animBits = parser.ParseInt(); if ( jointInfo[ i ].animBits & ~63 ) { parser.Error( "Invalid anim bits: %d", jointInfo[ i ].animBits ); } // parse first component jointInfo[ i ].firstComponent = parser.ParseInt(); if ( ( numAnimatedComponents > 0 ) && ( ( jointInfo[ i ].firstComponent < 0 ) || ( jointInfo[ i ].firstComponent >= numAnimatedComponents ) ) ) { parser.Error( "Invalid first component: %d", jointInfo[ i ].firstComponent ); } } parser.ExpectTokenString( "}" ); // parse bounds parser.ExpectTokenString( "bounds" ); parser.ExpectTokenString( "{" ); bounds.SetGranularity( 1 ); bounds.SetNum( numFrames ); for ( int i = 0; i < numFrames; i++ ) { parser.Parse1DMatrix( 3, bounds[ i ][ 0 ].ToFloatPtr() ); parser.Parse1DMatrix( 3, bounds[ i ][ 1 ].ToFloatPtr() ); } parser.ExpectTokenString( "}" ); // parse base frame baseFrame.SetGranularity( 1 ); baseFrame.SetNum( numJoints ); parser.ExpectTokenString( "baseframe" ); parser.ExpectTokenString( "{" ); for ( int i = 0; i < numJoints; i++ ) { idCQuat q; parser.Parse1DMatrix( 3, baseFrame[ i ].t.ToFloatPtr() ); parser.Parse1DMatrix( 3, q.ToFloatPtr() );//baseFrame[ i ].q.ToFloatPtr() ); baseFrame[ i ].q = q.ToQuat();//.w = baseFrame[ i ].q.CalcW(); baseFrame[ i ].w = 0.0f; } parser.ExpectTokenString( "}" ); // parse frames componentFrames.SetGranularity( 1 ); componentFrames.SetNum( numAnimatedComponents * numFrames + JOINT_FRAME_PAD ); componentFrames[numAnimatedComponents * numFrames + JOINT_FRAME_PAD - 1] = 0.0f; float *componentPtr = componentFrames.Ptr(); for ( int i = 0; i < numFrames; i++ ) { parser.ExpectTokenString( "frame" ); int num = parser.ParseInt(); if ( num != i ) { parser.Error( "Expected frame number %d", i ); } parser.ExpectTokenString( "{" ); for ( int j = 0; j < numAnimatedComponents; j++, componentPtr++ ) { *componentPtr = parser.ParseFloat(); } parser.ExpectTokenString( "}" ); } // get total move delta if ( !numAnimatedComponents ) { totaldelta.Zero(); } else { componentPtr = &componentFrames[ jointInfo[ 0 ].firstComponent ]; if ( jointInfo[ 0 ].animBits & ANIM_TX ) { for ( int i = 0; i < numFrames; i++ ) { componentPtr[ numAnimatedComponents * i ] -= baseFrame[ 0 ].t.x; } totaldelta.x = componentPtr[ numAnimatedComponents * ( numFrames - 1 ) ]; componentPtr++; } else { totaldelta.x = 0.0f; } if ( jointInfo[ 0 ].animBits & ANIM_TY ) { for ( int i = 0; i < numFrames; i++ ) { componentPtr[ numAnimatedComponents * i ] -= baseFrame[ 0 ].t.y; } totaldelta.y = componentPtr[ numAnimatedComponents * ( numFrames - 1 ) ]; componentPtr++; } else { totaldelta.y = 0.0f; } if ( jointInfo[ 0 ].animBits & ANIM_TZ ) { for ( int i = 0; i < numFrames; i++ ) { componentPtr[ numAnimatedComponents * i ] -= baseFrame[ 0 ].t.z; } totaldelta.z = componentPtr[ numAnimatedComponents * ( numFrames - 1 ) ]; } else { totaldelta.z = 0.0f; } } baseFrame[ 0 ].t.Zero(); // we don't count last frame because it would cause a 1 frame pause at the end animLength = ( ( numFrames - 1 ) * 1000 + frameRate - 1 ) / frameRate; if ( binaryLoadAnim.GetBool() ) { idLib::Printf( "Writing %s\n", generatedFileName.c_str() ); idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) ); WriteBinary( outputFile, sourceTimeStamp ); } // done return true; } /* ======================== idMD5Anim::LoadBinary ======================== */ bool idMD5Anim::LoadBinary( idFile * file, ID_TIME_T sourceTimeStamp ) { if ( file == NULL ) { return false; } unsigned int magic = 0; file->ReadBig( magic ); if ( magic != B_ANIM_MD5_MAGIC ) { return false; } ID_TIME_T loadedTimeStamp; file->ReadBig( loadedTimeStamp ); if ( !fileSystem->InProductionMode() && sourceTimeStamp != loadedTimeStamp ) { return false; } file->ReadBig( numFrames ); file->ReadBig( frameRate ); file->ReadBig( animLength ); file->ReadBig( numJoints ); file->ReadBig( numAnimatedComponents ); int num; file->ReadBig( num ); bounds.SetNum( num ); for ( int i = 0; i < num; i++ ) { idBounds & b = bounds[i]; file->ReadBig( b[0] ); file->ReadBig( b[1] ); } file->ReadBig( num ); jointInfo.SetNum( num ); for ( int i = 0; i < num; i++ ) { jointAnimInfo_t & j = jointInfo[i]; idStr jointName; file->ReadString( jointName ); if ( jointName.IsEmpty() ) { j.nameIndex = -1; } else { j.nameIndex = animationLib.JointIndex( jointName.c_str() ); } file->ReadBig( j.parentNum ); file->ReadBig( j.animBits ); file->ReadBig( j.firstComponent ); } file->ReadBig( num ); baseFrame.SetNum( num ); for ( int i = 0; i < num; i++ ) { idJointQuat & j = baseFrame[i]; file->ReadBig( j.q.x ); file->ReadBig( j.q.y ); file->ReadBig( j.q.z ); file->ReadBig( j.q.w ); file->ReadVec3( j.t ); j.w = 0.0f; } file->ReadBig( num ); componentFrames.SetNum( num + JOINT_FRAME_PAD ); for ( int i = 0; i < componentFrames.Num(); i++ ) { file->ReadFloat( componentFrames[i] ); } //file->ReadString( name ); file->ReadVec3( totaldelta ); //file->ReadBig( ref_count ); return true; } /* ======================== idMD5Anim::WriteBinary ======================== */ void idMD5Anim::WriteBinary( idFile * file, ID_TIME_T sourceTimeStamp ) { if ( file == NULL ) { return; } file->WriteBig( B_ANIM_MD5_MAGIC ); file->WriteBig( sourceTimeStamp ); file->WriteBig( numFrames ); file->WriteBig( frameRate ); file->WriteBig( animLength ); file->WriteBig( numJoints ); file->WriteBig( numAnimatedComponents ); file->WriteBig( bounds.Num() ); for ( int i = 0; i < bounds.Num(); i++ ) { idBounds & b = bounds[i]; file->WriteBig( b[0] ); file->WriteBig( b[1] ); } file->WriteBig( jointInfo.Num() ); for ( int i = 0; i < jointInfo.Num(); i++ ) { jointAnimInfo_t & j = jointInfo[i]; idStr jointName = animationLib.JointName( j.nameIndex ); file->WriteString( jointName ); file->WriteBig( j.parentNum ); file->WriteBig( j.animBits ); file->WriteBig( j.firstComponent ); } file->WriteBig( baseFrame.Num() ); for ( int i = 0; i < baseFrame.Num(); i++ ) { idJointQuat & j = baseFrame[i]; file->WriteBig( j.q.x ); file->WriteBig( j.q.y ); file->WriteBig( j.q.z ); file->WriteBig( j.q.w ); file->WriteVec3( j.t ); } file->WriteBig( componentFrames.Num() - JOINT_FRAME_PAD ); for ( int i = 0; i < componentFrames.Num(); i++ ) { file->WriteFloat( componentFrames[i] ); } //file->WriteString( name ); file->WriteVec3( totaldelta ); //file->WriteBig( ref_count ); } /* ==================== idMD5Anim::IncreaseRefs ==================== */ void idMD5Anim::IncreaseRefs() const { ref_count++; } /* ==================== idMD5Anim::DecreaseRefs ==================== */ void idMD5Anim::DecreaseRefs() const { ref_count--; } /* ==================== idMD5Anim::NumRefs ==================== */ int idMD5Anim::NumRefs() const { return ref_count; } /* ==================== idMD5Anim::GetFrameBlend ==================== */ void idMD5Anim::GetFrameBlend( int framenum, frameBlend_t &frame ) const { frame.cycleCount = 0; frame.backlerp = 0.0f; frame.frontlerp = 1.0f; // frame 1 is first frame framenum--; if ( framenum < 0 ) { framenum = 0; } else if ( framenum >= numFrames ) { framenum = numFrames - 1; } frame.frame1 = framenum; frame.frame2 = framenum; } /* ==================== idMD5Anim::ConvertTimeToFrame ==================== */ void idMD5Anim::ConvertTimeToFrame( int time, int cyclecount, frameBlend_t &frame ) const { int frameTime; int frameNum; if ( numFrames <= 1 ) { frame.frame1 = 0; frame.frame2 = 0; frame.backlerp = 0.0f; frame.frontlerp = 1.0f; frame.cycleCount = 0; return; } if ( time <= 0 ) { frame.frame1 = 0; frame.frame2 = 1; frame.backlerp = 0.0f; frame.frontlerp = 1.0f; frame.cycleCount = 0; return; } frameTime = time * frameRate; frameNum = frameTime / 1000; frame.cycleCount = frameNum / ( numFrames - 1 ); if ( ( cyclecount > 0 ) && ( frame.cycleCount >= cyclecount ) ) { frame.cycleCount = cyclecount - 1; frame.frame1 = numFrames - 1; frame.frame2 = frame.frame1; frame.backlerp = 0.0f; frame.frontlerp = 1.0f; return; } frame.frame1 = frameNum % ( numFrames - 1 ); frame.frame2 = frame.frame1 + 1; if ( frame.frame2 >= numFrames ) { frame.frame2 = 0; } frame.backlerp = ( frameTime % 1000 ) * 0.001f; frame.frontlerp = 1.0f - frame.backlerp; } /* ==================== idMD5Anim::GetOrigin ==================== */ void idMD5Anim::GetOrigin( idVec3 &offset, int time, int cyclecount ) const { offset = baseFrame[ 0 ].t; if ( !( jointInfo[ 0 ].animBits & ( ANIM_TX | ANIM_TY | ANIM_TZ ) ) ) { // just use the baseframe return; } frameBlend_t frame; ConvertTimeToFrame( time, cyclecount, frame ); const float *componentPtr1 = &componentFrames[ numAnimatedComponents * frame.frame1 + jointInfo[ 0 ].firstComponent ]; const float *componentPtr2 = &componentFrames[ numAnimatedComponents * frame.frame2 + jointInfo[ 0 ].firstComponent ]; if ( jointInfo[ 0 ].animBits & ANIM_TX ) { offset.x = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; componentPtr1++; componentPtr2++; } if ( jointInfo[ 0 ].animBits & ANIM_TY ) { offset.y = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; componentPtr1++; componentPtr2++; } if ( jointInfo[ 0 ].animBits & ANIM_TZ ) { offset.z = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; } if ( frame.cycleCount ) { offset += totaldelta * ( float )frame.cycleCount; } } /* ==================== idMD5Anim::GetOriginRotation ==================== */ void idMD5Anim::GetOriginRotation( idQuat &rotation, int time, int cyclecount ) const { int animBits = jointInfo[ 0 ].animBits; if ( !( animBits & ( ANIM_QX | ANIM_QY | ANIM_QZ ) ) ) { // just use the baseframe rotation = baseFrame[ 0 ].q; return; } frameBlend_t frame; ConvertTimeToFrame( time, cyclecount, frame ); const float *jointframe1 = &componentFrames[ numAnimatedComponents * frame.frame1 + jointInfo[ 0 ].firstComponent ]; const float *jointframe2 = &componentFrames[ numAnimatedComponents * frame.frame2 + jointInfo[ 0 ].firstComponent ]; if ( animBits & ANIM_TX ) { jointframe1++; jointframe2++; } if ( animBits & ANIM_TY ) { jointframe1++; jointframe2++; } if ( animBits & ANIM_TZ ) { jointframe1++; jointframe2++; } idQuat q1; idQuat q2; switch( animBits & (ANIM_QX|ANIM_QY|ANIM_QZ) ) { case ANIM_QX: q1.x = jointframe1[0]; q2.x = jointframe2[0]; q1.y = baseFrame[ 0 ].q.y; q2.y = q1.y; q1.z = baseFrame[ 0 ].q.z; q2.z = q1.z; q1.w = q1.CalcW(); q2.w = q2.CalcW(); break; case ANIM_QY: q1.y = jointframe1[0]; q2.y = jointframe2[0]; q1.x = baseFrame[ 0 ].q.x; q2.x = q1.x; q1.z = baseFrame[ 0 ].q.z; q2.z = q1.z; q1.w = q1.CalcW(); q2.w = q2.CalcW(); break; case ANIM_QZ: q1.z = jointframe1[0]; q2.z = jointframe2[0]; q1.x = baseFrame[ 0 ].q.x; q2.x = q1.x; q1.y = baseFrame[ 0 ].q.y; q2.y = q1.y; q1.w = q1.CalcW(); q2.w = q2.CalcW(); break; case ANIM_QX|ANIM_QY: q1.x = jointframe1[0]; q1.y = jointframe1[1]; q2.x = jointframe2[0]; q2.y = jointframe2[1]; q1.z = baseFrame[ 0 ].q.z; q2.z = q1.z; q1.w = q1.CalcW(); q2.w = q2.CalcW(); break; case ANIM_QX|ANIM_QZ: q1.x = jointframe1[0]; q1.z = jointframe1[1]; q2.x = jointframe2[0]; q2.z = jointframe2[1]; q1.y = baseFrame[ 0 ].q.y; q2.y = q1.y; q1.w = q1.CalcW(); q2.w = q2.CalcW(); break; case ANIM_QY|ANIM_QZ: q1.y = jointframe1[0]; q1.z = jointframe1[1]; q2.y = jointframe2[0]; q2.z = jointframe2[1]; q1.x = baseFrame[ 0 ].q.x; q2.x = q1.x; q1.w = q1.CalcW(); q2.w = q2.CalcW(); break; case ANIM_QX|ANIM_QY|ANIM_QZ: q1.x = jointframe1[0]; q1.y = jointframe1[1]; q1.z = jointframe1[2]; q2.x = jointframe2[0]; q2.y = jointframe2[1]; q2.z = jointframe2[2]; q1.w = q1.CalcW(); q2.w = q2.CalcW(); break; } rotation.Slerp( q1, q2, frame.backlerp ); } /* ==================== idMD5Anim::GetBounds ==================== */ void idMD5Anim::GetBounds( idBounds &bnds, int time, int cyclecount ) const { frameBlend_t frame; ConvertTimeToFrame( time, cyclecount, frame ); bnds = bounds[ frame.frame1 ]; bnds.AddBounds( bounds[ frame.frame2 ] ); // origin position idVec3 offset = baseFrame[ 0 ].t; if ( jointInfo[ 0 ].animBits & ( ANIM_TX | ANIM_TY | ANIM_TZ ) ) { const float *componentPtr1 = &componentFrames[ numAnimatedComponents * frame.frame1 + jointInfo[ 0 ].firstComponent ]; const float *componentPtr2 = &componentFrames[ numAnimatedComponents * frame.frame2 + jointInfo[ 0 ].firstComponent ]; if ( jointInfo[ 0 ].animBits & ANIM_TX ) { offset.x = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; componentPtr1++; componentPtr2++; } if ( jointInfo[ 0 ].animBits & ANIM_TY ) { offset.y = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; componentPtr1++; componentPtr2++; } if ( jointInfo[ 0 ].animBits & ANIM_TZ ) { offset.z = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; } } bnds[ 0 ] -= offset; bnds[ 1 ] -= offset; } /* ==================== DecodeInterpolatedFrames ==================== */ int DecodeInterpolatedFrames( idJointQuat * joints, idJointQuat * blendJoints, int * lerpIndex, const float * frame1, const float * frame2, const jointAnimInfo_t * jointInfo, const int * index, const int numIndexes ) { int numLerpJoints = 0; for ( int i = 0; i < numIndexes; i++ ) { const int j = index[i]; const jointAnimInfo_t * infoPtr = &jointInfo[j]; const int animBits = infoPtr->animBits; if ( animBits != 0 ) { lerpIndex[numLerpJoints++] = j; idJointQuat * jointPtr = &joints[j]; idJointQuat * blendPtr = &blendJoints[j]; *blendPtr = *jointPtr; const float * jointframe1 = frame1 + infoPtr->firstComponent; const float * jointframe2 = frame2 + infoPtr->firstComponent; if ( animBits & (ANIM_TX|ANIM_TY|ANIM_TZ) ) { if ( animBits & ANIM_TX ) { jointPtr->t.x = *jointframe1++; blendPtr->t.x = *jointframe2++; } if ( animBits & ANIM_TY ) { jointPtr->t.y = *jointframe1++; blendPtr->t.y = *jointframe2++; } if ( animBits & ANIM_TZ ) { jointPtr->t.z = *jointframe1++; blendPtr->t.z = *jointframe2++; } } if ( animBits & (ANIM_QX|ANIM_QY|ANIM_QZ) ) { if ( animBits & ANIM_QX ) { jointPtr->q.x = *jointframe1++; blendPtr->q.x = *jointframe2++; } if ( animBits & ANIM_QY ) { jointPtr->q.y = *jointframe1++; blendPtr->q.y = *jointframe2++; } if ( animBits & ANIM_QZ ) { jointPtr->q.z = *jointframe1++; blendPtr->q.z = *jointframe2++; } jointPtr->q.w = jointPtr->q.CalcW(); blendPtr->q.w = blendPtr->q.CalcW(); } } } return numLerpJoints; } /* ==================== idMD5Anim::GetInterpolatedFrame ==================== */ void idMD5Anim::GetInterpolatedFrame( frameBlend_t &frame, idJointQuat *joints, const int *index, int numIndexes ) const { // copy the baseframe SIMDProcessor->Memcpy( joints, baseFrame.Ptr(), baseFrame.Num() * sizeof( baseFrame[ 0 ] ) ); if ( numAnimatedComponents == 0 ) { // just use the base frame return; } idJointQuat * blendJoints = (idJointQuat *)_alloca16( baseFrame.Num() * sizeof( blendJoints[ 0 ] ) ); int * lerpIndex = (int *)_alloca16( baseFrame.Num() * sizeof( lerpIndex[ 0 ] ) ); const float * frame1 = &componentFrames[frame.frame1 * numAnimatedComponents]; const float * frame2 = &componentFrames[frame.frame2 * numAnimatedComponents]; int numLerpJoints = DecodeInterpolatedFrames( joints, blendJoints, lerpIndex, frame1, frame2, jointInfo.Ptr(), index, numIndexes ); SIMDProcessor->BlendJoints( joints, blendJoints, frame.backlerp, lerpIndex, numLerpJoints ); if ( frame.cycleCount ) { joints[ 0 ].t += totaldelta * ( float )frame.cycleCount; } } /* ==================== DecodeSingleFrame ==================== */ void DecodeSingleFrame( idJointQuat * joints, const float * frame, const jointAnimInfo_t * jointInfo, const int * index, const int numIndexes ) { for ( int i = 0; i < numIndexes; i++ ) { const int j = index[i]; const jointAnimInfo_t * infoPtr = &jointInfo[j]; const int animBits = infoPtr->animBits; if ( animBits != 0 ) { idJointQuat * jointPtr = &joints[j]; const float * jointframe = frame + infoPtr->firstComponent; if ( animBits & (ANIM_TX|ANIM_TY|ANIM_TZ) ) { if ( animBits & ANIM_TX ) { jointPtr->t.x = *jointframe++; } if ( animBits & ANIM_TY ) { jointPtr->t.y = *jointframe++; } if ( animBits & ANIM_TZ ) { jointPtr->t.z = *jointframe++; } } if ( animBits & (ANIM_QX|ANIM_QY|ANIM_QZ) ) { if ( animBits & ANIM_QX ) { jointPtr->q.x = *jointframe++; } if ( animBits & ANIM_QY ) { jointPtr->q.y = *jointframe++; } if ( animBits & ANIM_QZ ) { jointPtr->q.z = *jointframe++; } jointPtr->q.w = jointPtr->q.CalcW(); } } } } /* ==================== idMD5Anim::GetSingleFrame ==================== */ void idMD5Anim::GetSingleFrame( int framenum, idJointQuat *joints, const int *index, int numIndexes ) const { // copy the baseframe SIMDProcessor->Memcpy( joints, baseFrame.Ptr(), baseFrame.Num() * sizeof( baseFrame[ 0 ] ) ); if ( framenum == 0 || numAnimatedComponents == 0 ) { // just use the base frame return; } const float * frame = &componentFrames[framenum * numAnimatedComponents]; DecodeSingleFrame( joints, frame, jointInfo.Ptr(), index, numIndexes ); } /* ==================== idMD5Anim::CheckModelHierarchy ==================== */ void idMD5Anim::CheckModelHierarchy( const idRenderModel *model ) const { if ( jointInfo.Num() != model->NumJoints() ) { if ( !fileSystem->InProductionMode() ) { gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", model->Name(), name.c_str() ); } else { //gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", model->Name(), name.c_str() ); return; } } const idMD5Joint *modelJoints = model->GetJoints(); for( int i = 0; i < jointInfo.Num(); i++ ) { int jointNum = jointInfo[ i ].nameIndex; if ( modelJoints[ i ].name != animationLib.JointName( jointNum ) ) { gameLocal.Error( "Model '%s''s joint names don't match anim '%s''s", model->Name(), name.c_str() ); } int parent; if ( modelJoints[ i ].parent ) { parent = modelJoints[ i ].parent - modelJoints; } else { parent = -1; } if ( parent != jointInfo[ i ].parentNum ) { gameLocal.Error( "Model '%s' has different joint hierarchy than anim '%s'", model->Name(), name.c_str() ); } } } /*********************************************************************** idAnimManager ***********************************************************************/ /* ==================== idAnimManager::idAnimManager ==================== */ idAnimManager::idAnimManager() { } /* ==================== idAnimManager::~idAnimManager ==================== */ idAnimManager::~idAnimManager() { Shutdown(); } /* ==================== idAnimManager::Shutdown ==================== */ void idAnimManager::Shutdown() { animations.DeleteContents(); jointnames.Clear(); jointnamesHash.Free(); } /* ==================== idAnimManager::GetAnim ==================== */ idMD5Anim *idAnimManager::GetAnim( const char *name ) { idMD5Anim **animptrptr; idMD5Anim *anim; // see if it has been asked for before animptrptr = NULL; if ( animations.Get( name, &animptrptr ) ) { anim = *animptrptr; } else { idStr extension; idStr filename = name; filename.ExtractFileExtension( extension ); if ( extension != MD5_ANIM_EXT ) { return NULL; } anim = new (TAG_ANIM) idMD5Anim(); if ( !anim->LoadAnim( filename ) ) { gameLocal.Warning( "Couldn't load anim: '%s'", filename.c_str() ); delete anim; anim = NULL; } animations.Set( filename, anim ); } return anim; } /* ================ idAnimManager::Preload ================ */ void idAnimManager::Preload( const idPreloadManifest &manifest ) { if ( manifest.NumResources() >= 0 ) { common->Printf( "Preloading anims...\n" ); int start = Sys_Milliseconds(); int numLoaded = 0; for ( int i = 0; i < manifest.NumResources(); i++ ) { const preloadEntry_s & p = manifest.GetPreloadByIndex( i ); if ( p.resType == PRELOAD_ANIM ) { GetAnim( p.resourceName ); numLoaded++; } } int end = Sys_Milliseconds(); common->Printf( "%05d anims preloaded ( or were already loaded ) in %5.1f seconds\n", numLoaded, ( end - start ) * 0.001 ); common->Printf( "----------------------------------------\n" ); } } /* ================ idAnimManager::ReloadAnims ================ */ void idAnimManager::ReloadAnims() { int i; idMD5Anim **animptr; for( i = 0; i < animations.Num(); i++ ) { animptr = animations.GetIndex( i ); if ( animptr != NULL && *animptr != NULL ) { ( *animptr )->Reload(); } } } /* ================ idAnimManager::JointIndex ================ */ int idAnimManager::JointIndex( const char *name ) { int i, hash; hash = jointnamesHash.GenerateKey( name ); for ( i = jointnamesHash.First( hash ); i != -1; i = jointnamesHash.Next( i ) ) { if ( jointnames[i].Cmp( name ) == 0 ) { return i; } } i = jointnames.Append( name ); jointnamesHash.Add( hash, i ); return i; } /* ================ idAnimManager::JointName ================ */ const char *idAnimManager::JointName( int index ) const { return jointnames[ index ]; } /* ================ idAnimManager::ListAnims ================ */ void idAnimManager::ListAnims() const { int i; idMD5Anim **animptr; idMD5Anim *anim; size_t size; size_t s; size_t namesize; int num; num = 0; size = 0; for( i = 0; i < animations.Num(); i++ ) { animptr = animations.GetIndex( i ); if ( animptr != NULL && *animptr != NULL ) { anim = *animptr; s = anim->Size(); gameLocal.Printf( "%8d bytes : %2d refs : %s\n", s, anim->NumRefs(), anim->Name() ); size += s; num++; } } namesize = jointnames.Size() + jointnamesHash.Size(); for( i = 0; i < jointnames.Num(); i++ ) { namesize += jointnames[ i ].Size(); } gameLocal.Printf( "\n%d memory used in %d anims\n", size, num ); gameLocal.Printf( "%d memory used in %d joint names\n", namesize, jointnames.Num() ); } /* ================ idAnimManager::FlushUnusedAnims ================ */ void idAnimManager::FlushUnusedAnims() { int i; idMD5Anim **animptr; idList removeAnims; for( i = 0; i < animations.Num(); i++ ) { animptr = animations.GetIndex( i ); if ( animptr != NULL && *animptr != NULL ) { if ( ( *animptr )->NumRefs() <= 0 ) { removeAnims.Append( *animptr ); } } } for( i = 0; i < removeAnims.Num(); i++ ) { animations.Remove( removeAnims[ i ]->Name() ); delete removeAnims[ i ]; } }