/*
==============
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;
}
Exemple #2
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;
}