/* ============== RE_AnimFrameRate ============== */ int RE_AnimFrameRate( qhandle_t hAnim ) { skelAnimation_t *anim; anim = R_GetAnimationByHandle( hAnim ); if ( anim->type == AT_MD5 && anim->md5 ) { return anim->md5->frameRate; } return 0; }
/* ============== RE_AnimNumFrames ============== */ int RE_AnimNumFrames(qhandle_t hAnim) { skelAnimation_t* anim; anim = R_GetAnimationByHandle(hAnim); if (anim->type == AT_IQM && anim->iqm) { return anim->iqm->num_frames; } else if (anim->type == AT_MD5 && anim->md5) { return anim->md5->numFrames; } return 0; }
/* ============== RE_AnimFrameRate ============== */ int RE_AnimFrameRate( qhandle_t hAnim ) { skelAnimation_t *anim; anim = R_GetAnimationByHandle( hAnim ); if( anim->type == animType_t::AT_IQM && anim->iqm ) { return anim->iqm->framerate; } else if ( anim->type == animType_t::AT_MD5 && anim->md5 ) { return anim->md5->frameRate; } return 0; }
/* ============== RE_AnimNumFrames ============== */ int RE_AnimNumFrames(qhandle_t hAnim) { skelAnimation_t *anim; anim = R_GetAnimationByHandle(hAnim); if(anim->type == AT_MD5 && anim->md5) { return anim->md5->numFrames; } if(anim->type == AT_PSA && anim->psa) { return anim->psa->info.numRawFrames; } return 0; }
/* ============== RE_BuildSkeleton ============== */ int RE_BuildSkeleton(refSkeleton_t * skel, qhandle_t hAnim, int startFrame, int endFrame, float frac, qboolean clearOrigin) { skelAnimation_t *skelAnim; skelAnim = R_GetAnimationByHandle(hAnim); if(skelAnim->type == AT_MD5 && skelAnim->md5) { int i; md5Animation_t *anim; md5Channel_t *channel; md5Frame_t *newFrame, *oldFrame; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; int componentsApplied; anim = skelAnim->md5; // Validate the frames so there is no chance of a crash. // This will write directly into the entity structure, so // when the surfaces are rendered, they don't need to be // range checked again. /* if((startFrame >= anim->numFrames) || (startFrame < 0) || (endFrame >= anim->numFrames) || (endFrame < 0)) { ri.Printf(PRINT_DEVELOPER, "RE_BuildSkeleton: no such frame %d to %d for '%s'\n", startFrame, endFrame, anim->name); //startFrame = 0; //endFrame = 0; } */ Q_clamp(startFrame, 0, anim->numFrames - 1); Q_clamp(endFrame, 0, anim->numFrames - 1); // compute frame pointers oldFrame = &anim->frames[startFrame]; newFrame = &anim->frames[endFrame]; // calculate a bounding box in the current coordinate system for(i = 0; i < 3; i++) { skel->bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; skel->bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; } for(i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++) { // set baseframe values VectorCopy(channel->baseOrigin, newOrigin); VectorCopy(channel->baseOrigin, oldOrigin); QuatCopy(channel->baseQuat, newQuat); QuatCopy(channel->baseQuat, oldQuat); componentsApplied = 0; // update tranlation bits if(channel->componentsBits & COMPONENT_BIT_TX) { oldOrigin[0] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[0] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_TY) { oldOrigin[1] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[1] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_TZ) { oldOrigin[2] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[2] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } // update quaternion rotation bits if(channel->componentsBits & COMPONENT_BIT_QX) { ((vec_t *) oldQuat)[0] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[0] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_QY) { ((vec_t *) oldQuat)[1] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[1] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_QZ) { ((vec_t *) oldQuat)[2] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[2] = newFrame->components[channel->componentsOffset + componentsApplied]; } QuatCalcW(oldQuat); QuatNormalize(oldQuat); QuatCalcW(newQuat); QuatNormalize(newQuat); #if 1 VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin); QuatSlerp(oldQuat, newQuat, frac, lerpedQuat); #else VectorCopy(newOrigin, lerpedOrigin); QuatCopy(newQuat, lerpedQuat); #endif // copy lerped information to the bone + extra data skel->bones[i].parentIndex = channel->parentIndex; if(channel->parentIndex < 0 && clearOrigin) { VectorClear(skel->bones[i].origin); QuatClear(skel->bones[i].rotation); // move bounding box back VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]); VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]); } else { VectorCopy(lerpedOrigin, skel->bones[i].origin); } QuatCopy(lerpedQuat, skel->bones[i].rotation); #if defined(REFBONE_NAMES) Q_strncpyz(skel->bones[i].name, channel->name, sizeof(skel->bones[i].name)); #endif } skel->numBones = anim->numChannels; skel->type = SK_RELATIVE; return qtrue; } else if(skelAnim->type == AT_PSA && skelAnim->psa) { int i; psaAnimation_t *anim; axAnimationKey_t *newKey, *oldKey; axReferenceBone_t *refBone; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; refSkeleton_t skeleton; anim = skelAnim->psa; Q_clamp(startFrame, 0, anim->info.numRawFrames - 1); Q_clamp(endFrame, 0, anim->info.numRawFrames - 1); ClearBounds(skel->bounds[0], skel->bounds[1]); skel->numBones = anim->info.numBones; for(i = 0, refBone = anim->bones; i < anim->info.numBones; i++, refBone++) { oldKey = &anim->keys[startFrame * anim->info.numBones + i]; newKey = &anim->keys[endFrame * anim->info.numBones + i]; VectorCopy(newKey->position, newOrigin); VectorCopy(oldKey->position, oldOrigin); QuatCopy(newKey->quat, newQuat); QuatCopy(oldKey->quat, oldQuat); //QuatCalcW(oldQuat); //QuatNormalize(oldQuat); //QuatCalcW(newQuat); //QuatNormalize(newQuat); VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin); QuatSlerp(oldQuat, newQuat, frac, lerpedQuat); // copy lerped information to the bone + extra data skel->bones[i].parentIndex = refBone->parentIndex; if(refBone->parentIndex < 0 && clearOrigin) { VectorClear(skel->bones[i].origin); QuatClear(skel->bones[i].rotation); // move bounding box back VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]); VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]); } else { VectorCopy(lerpedOrigin, skel->bones[i].origin); } QuatCopy(lerpedQuat, skel->bones[i].rotation); #if defined(REFBONE_NAMES) Q_strncpyz(skel->bones[i].name, refBone->name, sizeof(skel->bones[i].name)); #endif // calculate absolute values for the bounding box approximation VectorCopy(skel->bones[i].origin, skeleton.bones[i].origin); QuatCopy(skel->bones[i].rotation, skeleton.bones[i].rotation); if(refBone->parentIndex >= 0) { vec3_t rotated; quat_t quat; refBone_t *parent; refBone_t *bone; bone = &skeleton.bones[i]; parent = &skeleton.bones[refBone->parentIndex]; QuatTransformVector(parent->rotation, bone->origin, rotated); VectorAdd(parent->origin, rotated, bone->origin); QuatMultiply1(parent->rotation, bone->rotation, quat); QuatCopy(quat, bone->rotation); AddPointToBounds(bone->origin, skel->bounds[0], skel->bounds[1]); } } skel->numBones = anim->info.numBones; skel->type = SK_RELATIVE; return qtrue; } //ri.Printf(PRINT_WARNING, "RE_BuildSkeleton: bad animation '%s' with handle %i\n", anim->name, hAnim); // FIXME: clear existing bones and bounds? return qfalse; }
/* ============== RE_CheckSkeleton Tr3B: check if the skeleton bones are the same in the model and animation and copy the parentIndex entries into the refSkeleton_t ============== */ int RE_CheckSkeleton(refSkeleton_t * skel, qhandle_t hModel, qhandle_t hAnim) { int i; model_t *model; md5Model_t *md5Model; skelAnimation_t *skelAnim; model = R_GetModelByHandle(hModel); skelAnim = R_GetAnimationByHandle(hAnim); if(model->type != MOD_MD5 || !model->md5) { ri.Printf(PRINT_WARNING, "RE_CheckSkeleton: '%s' is not a skeletal model\n", model->name); return qfalse; } md5Model = model->md5; if(md5Model->numBones < 1) { ri.Printf(PRINT_WARNING, "RE_CheckSkeleton: '%s' has no bones\n", model->name); return qfalse; } if(md5Model->numBones > MAX_BONES) { ri.Printf(PRINT_WARNING, "RE_CheckSkeleton: '%s' has more than %i bones (%i)\n", model->name, MAX_BONES, md5Model->numBones); return qfalse; } if(skelAnim->type == AT_MD5 && skelAnim->md5) { md5Animation_t *md5Animation; md5Bone_t *md5Bone; md5Channel_t *md5Channel; md5Animation = skelAnim->md5; if(md5Model->numBones != md5Animation->numChannels) { ri.Printf(PRINT_WARNING, "RE_CheckSkeleton: model '%s' has different number of bones than animation '%s': %d != %d\n", model->name, skelAnim->name, md5Model->numBones, md5Animation->numChannels); return qfalse; } // check bone names for(i = 0, md5Bone = md5Model->bones, md5Channel = md5Animation->channels; i < md5Model->numBones; i++, md5Bone++, md5Channel++) { if(Q_stricmp(md5Bone->name, md5Channel->name)) return qfalse; skel->bones[i].parentIndex = md5Bone->parentIndex; } return qtrue; } else if(skelAnim->type == AT_PSA && skelAnim->psa) { psaAnimation_t *psaAnimation; axReferenceBone_t *refBone; md5Bone_t *md5Bone; psaAnimation = skelAnim->psa; if(md5Model->numBones != psaAnimation->info.numBones) { ri.Printf(PRINT_WARNING, "RE_CheckSkeleton: model '%s' has different number of bones than animation '%s': %d != %d\n", model->name, skelAnim->name, md5Model->numBones, psaAnimation->info.numBones); return qfalse; } // check bone names for(i = 0, md5Bone = md5Model->bones, refBone = psaAnimation->bones; i < md5Model->numBones; i++, md5Bone++, refBone++) { if(Q_stricmp(md5Bone->name, refBone->name)) return qfalse; skel->bones[i].parentIndex = md5Bone->parentIndex; } return qtrue; } ri.Printf(PRINT_WARNING, "RE_BuildSkeleton: bad animation '%s' with handle %i\n", skelAnim->name, hAnim); return qfalse; }
/* ============== RE_BuildSkeleton ============== */ int RE_BuildSkeleton( refSkeleton_t *skel, qhandle_t hAnim, int startFrame, int endFrame, float frac, bool clearOrigin ) { skelAnimation_t *skelAnim; skelAnim = R_GetAnimationByHandle( hAnim ); if ( skelAnim->type == animType_t::AT_IQM && skelAnim->iqm ) { return IQMBuildSkeleton( skel, skelAnim, startFrame, endFrame, frac ); } else if ( skelAnim->type == animType_t::AT_MD5 && skelAnim->md5 ) { int i; md5Animation_t *anim; md5Channel_t *channel; md5Frame_t *newFrame, *oldFrame; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; int componentsApplied; anim = skelAnim->md5; // Validate the frames so there is no chance of a crash. // This will write directly into the entity structure, so // when the surfaces are rendered, they don't need to be // range checked again. /* if((startFrame >= anim->numFrames) || (startFrame < 0) || (endFrame >= anim->numFrames) || (endFrame < 0)) { Log::Debug("RE_BuildSkeleton: no such frame %d to %d for '%s'\n", startFrame, endFrame, anim->name); //startFrame = 0; //endFrame = 0; } */ startFrame = Math::Clamp( startFrame, 0, anim->numFrames - 1 ); endFrame = Math::Clamp( endFrame, 0, anim->numFrames - 1 ); // compute frame pointers oldFrame = &anim->frames[ startFrame ]; newFrame = &anim->frames[ endFrame ]; // calculate a bounding box in the current coordinate system for ( i = 0; i < 3; i++ ) { skel->bounds[ 0 ][ i ] = oldFrame->bounds[ 0 ][ i ] < newFrame->bounds[ 0 ][ i ] ? oldFrame->bounds[ 0 ][ i ] : newFrame->bounds[ 0 ][ i ]; skel->bounds[ 1 ][ i ] = oldFrame->bounds[ 1 ][ i ] > newFrame->bounds[ 1 ][ i ] ? oldFrame->bounds[ 1 ][ i ] : newFrame->bounds[ 1 ][ i ]; } for ( i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++ ) { // set baseframe values VectorCopy( channel->baseOrigin, newOrigin ); VectorCopy( channel->baseOrigin, oldOrigin ); QuatCopy( channel->baseQuat, newQuat ); QuatCopy( channel->baseQuat, oldQuat ); componentsApplied = 0; // update tranlation bits if ( channel->componentsBits & COMPONENT_BIT_TX ) { oldOrigin[ 0 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; newOrigin[ 0 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } if ( channel->componentsBits & COMPONENT_BIT_TY ) { oldOrigin[ 1 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; newOrigin[ 1 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } if ( channel->componentsBits & COMPONENT_BIT_TZ ) { oldOrigin[ 2 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; newOrigin[ 2 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } // update quaternion rotation bits if ( channel->componentsBits & COMPONENT_BIT_QX ) { ( ( vec_t * ) oldQuat ) [ 0 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; ( ( vec_t * ) newQuat ) [ 0 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } if ( channel->componentsBits & COMPONENT_BIT_QY ) { ( ( vec_t * ) oldQuat ) [ 1 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; ( ( vec_t * ) newQuat ) [ 1 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; componentsApplied++; } if ( channel->componentsBits & COMPONENT_BIT_QZ ) { ( ( vec_t * ) oldQuat ) [ 2 ] = oldFrame->components[ channel->componentsOffset + componentsApplied ]; ( ( vec_t * ) newQuat ) [ 2 ] = newFrame->components[ channel->componentsOffset + componentsApplied ]; } QuatCalcW( oldQuat ); QuatNormalize( oldQuat ); QuatCalcW( newQuat ); QuatNormalize( newQuat ); #if 1 VectorLerp( oldOrigin, newOrigin, frac, lerpedOrigin ); QuatSlerp( oldQuat, newQuat, frac, lerpedQuat ); #else VectorCopy( newOrigin, lerpedOrigin ); QuatCopy( newQuat, lerpedQuat ); #endif // copy lerped information to the bone + extra data skel->bones[ i ].parentIndex = channel->parentIndex; if ( channel->parentIndex < 0 && clearOrigin ) { VectorClear( skel->bones[ i ].t.trans ); QuatClear( skel->bones[ i ].t.rot ); // move bounding box back VectorSubtract( skel->bounds[ 0 ], lerpedOrigin, skel->bounds[ 0 ] ); VectorSubtract( skel->bounds[ 1 ], lerpedOrigin, skel->bounds[ 1 ] ); } else { VectorCopy( lerpedOrigin, skel->bones[ i ].t.trans ); } QuatCopy( lerpedQuat, skel->bones[ i ].t.rot ); skel->bones[ i ].t.scale = 1.0f; #if defined( REFBONE_NAMES ) Q_strncpyz( skel->bones[ i ].name, channel->name, sizeof( skel->bones[ i ].name ) ); #endif } skel->numBones = anim->numChannels; skel->type = refSkeletonType_t::SK_RELATIVE; return true; } // FIXME: clear existing bones and bounds? return false; }
/* ============== RE_CheckSkeleton Tr3B: check if the skeleton bones are the same in the model and animation and copy the parentIndex entries into the refSkeleton_t ============== */ int RE_CheckSkeleton( refSkeleton_t *skel, qhandle_t hModel, qhandle_t hAnim ) { int i; model_t *model; md5Model_t *md5Model; skelAnimation_t *skelAnim; model = R_GetModelByHandle( hModel ); skelAnim = R_GetAnimationByHandle( hAnim ); if( model->type == modtype_t::MOD_IQM && model->iqm ) { return IQMCheckSkeleton( skel, model, skelAnim ); } else if ( model->type != modtype_t::MOD_MD5 || !model->md5 ) { Log::Warn("RE_CheckSkeleton: '%s' is not a skeletal model", model->name ); return false; } md5Model = model->md5; if ( md5Model->numBones < 1 ) { Log::Warn("RE_CheckSkeleton: '%s' has no bones", model->name ); return false; } if ( md5Model->numBones > MAX_BONES ) { Log::Warn("RE_CheckSkeleton: '%s' has more than %i bones (%i)", model->name, MAX_BONES, md5Model->numBones ); return false; } if ( skelAnim->type == animType_t::AT_MD5 && skelAnim->md5 ) { md5Animation_t *md5Animation; md5Bone_t *md5Bone; md5Channel_t *md5Channel; md5Animation = skelAnim->md5; if ( md5Model->numBones != md5Animation->numChannels ) { Log::Warn("RE_CheckSkeleton: model '%s' has different number of bones than animation '%s': %d != %d", model->name, skelAnim->name, md5Model->numBones, md5Animation->numChannels ); return false; } // check bone names for ( i = 0, md5Bone = md5Model->bones, md5Channel = md5Animation->channels; i < md5Model->numBones; i++, md5Bone++, md5Channel++ ) { if ( Q_stricmp( md5Bone->name, md5Channel->name ) ) { return false; } skel->bones[ i ].parentIndex = md5Bone->parentIndex; } return true; } Log::Warn("RE_BuildSkeleton: bad animation '%s' with handle %i", skelAnim->name, hAnim ); return false; }