From 5a415ee38e71eee09332e57236046101e2fb1cb2 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Wed, 23 Oct 2024 01:35:55 +0300 Subject: [PATCH] Move some skeleton building to cgame, batch IPC --- src/engine/client/cg_msgdef.h | 10 ++ src/engine/client/cl_cgame.cpp | 18 +++ src/engine/null/null_renderer.cpp | 4 + src/engine/renderer/iqm.h | 5 - src/engine/renderer/tr_animation.cpp | 14 ++ src/engine/renderer/tr_init.cpp | 1 + src/engine/renderer/tr_local.h | 75 +-------- src/engine/renderer/tr_public.h | 1 + src/engine/renderer/tr_types.h | 72 +++++++++ src/shared/client/cg_api.cpp | 217 +++++++++++++++++++++++++++ src/shared/client/cg_api.h | 2 + 11 files changed, 340 insertions(+), 79 deletions(-) diff --git a/src/engine/client/cg_msgdef.h b/src/engine/client/cg_msgdef.h index 37e0c8ec37..76fe799596 100644 --- a/src/engine/client/cg_msgdef.h +++ b/src/engine/client/cg_msgdef.h @@ -196,6 +196,8 @@ enum cgameImport_t CG_R_LIGHTFORPOINT, CG_R_REGISTERANIMATION, CG_R_BUILDSKELETON, + CG_R_GETANIMATION, + CG_R_BATCHGETANIMATION, CG_R_BONEINDEX, CG_R_ANIMNUMFRAMES, CG_R_ANIMFRAMERATE, @@ -378,6 +380,14 @@ namespace Render { IPC::Message, int, int, int, float, bool>, IPC::Reply >; + using GetAnimationMsg = IPC::SyncMessage< + IPC::Message, int>, + IPC::Reply + >; + using BatchGetAnimationsMsg = IPC::SyncMessage< + IPC::Message, std::vector>, + IPC::Reply> + >; using BoneIndexMsg = IPC::SyncMessage< IPC::Message, int, std::string>, IPC::Reply diff --git a/src/engine/client/cl_cgame.cpp b/src/engine/client/cl_cgame.cpp index 23f9325064..d54f0bfed4 100644 --- a/src/engine/client/cl_cgame.cpp +++ b/src/engine/client/cl_cgame.cpp @@ -1320,6 +1320,24 @@ void CGameVM::QVMSyscall(int syscallNum, Util::Reader& reader, IPC::Channel& cha }); break; + case CG_R_GETANIMATION: + IPC::HandleMsg( channel, std::move( reader ), [this]( int anim, skelAnimation_t& res ) { + res = re.GetAnimation( anim ); + }); + break; + + case CG_R_BATCHGETANIMATION: + IPC::HandleMsg( channel, std::move( reader ), [this]( + const std::vector& anims, + std::vector& skelAnimations ) { + skelAnimations.reserve( anims.size() ); + + for ( const qhandle_t anim : anims ) { + skelAnimations.push_back( re.GetAnimation( anim ) ); + } + }); + break; + case CG_R_BONEINDEX: IPC::HandleMsg(channel, std::move(reader), [this] (int model, const std::string& boneName, int& index) { index = re.BoneIndex(model, boneName.c_str()); diff --git a/src/engine/null/null_renderer.cpp b/src/engine/null/null_renderer.cpp index 9e4b6e24c0..773d27f9ea 100644 --- a/src/engine/null/null_renderer.cpp +++ b/src/engine/null/null_renderer.cpp @@ -139,6 +139,9 @@ int RE_BuildSkeleton( refSkeleton_t *skel, qhandle_t, int, int, float, bool ) skel->numBones = 0; return 1; } +skelAnimation_t RE_GetAnimation( qhandle_t ) { + return skelAnimation_t{}; +} int RE_BlendSkeleton( refSkeleton_t*, const refSkeleton_t*, float ) { return 1; @@ -263,6 +266,7 @@ refexport_t *GetRefAPI( int, refimport_t* ) re.RegisterAnimation = RE_RegisterAnimation; re.CheckSkeleton = RE_CheckSkeleton; re.BuildSkeleton = RE_BuildSkeleton; + re.GetAnimation = RE_GetAnimation; re.BlendSkeleton = RE_BlendSkeleton; re.BoneIndex = RE_BoneIndex; re.AnimNumFrames = RE_AnimNumFrames; diff --git a/src/engine/renderer/iqm.h b/src/engine/renderer/iqm.h index 4e000227a0..eb8351ae6e 100644 --- a/src/engine/renderer/iqm.h +++ b/src/engine/renderer/iqm.h @@ -88,11 +88,6 @@ struct iqmanim unsigned int flags; }; -enum -{ - IQM_LOOP = 1<<0 -}; - struct iqmvertexarray { unsigned int type; diff --git a/src/engine/renderer/tr_animation.cpp b/src/engine/renderer/tr_animation.cpp index b57f43aae8..1a638043cb 100644 --- a/src/engine/renderer/tr_animation.cpp +++ b/src/engine/renderer/tr_animation.cpp @@ -1471,6 +1471,20 @@ int RE_BuildSkeleton( refSkeleton_t *skel, qhandle_t hAnim, int startFrame, int return false; } +skelAnimation_t RE_GetAnimation( qhandle_t index ) { + skelAnimation_t* anim; + + // out of range gets the default animation + if ( index < 0 || index >= tr.numAnimations ) { + Log::Warn( "R_GetAnimationByHandle: index=%d out of range", index ); + anim = tr.animations[0]; + } else { + anim = tr.animations[index]; + } + + return *anim; +} + /* ============== RE_BlendSkeleton diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 39b9f4efcf..7ecf35cd13 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -1703,6 +1703,7 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p re.RegisterAnimation = RE_RegisterAnimation; re.CheckSkeleton = RE_CheckSkeleton; re.BuildSkeleton = RE_BuildSkeleton; + re.GetAnimation = RE_GetAnimation; re.BlendSkeleton = RE_BlendSkeleton; re.BoneIndex = RE_BoneIndex; re.AnimNumFrames = RE_AnimNumFrames; diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index fee84c6608..ec2e9d3c4c 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -2138,54 +2138,6 @@ enum class cubeProbesAutoBuildMode { float internalScale; }; - enum class animType_t - { - AT_BAD, - AT_MD5, - AT_IQM, - }; - - enum - { - COMPONENT_BIT_TX = 1 << 0, - COMPONENT_BIT_TY = 1 << 1, - COMPONENT_BIT_TZ = 1 << 2, - COMPONENT_BIT_QX = 1 << 3, - COMPONENT_BIT_QY = 1 << 4, - COMPONENT_BIT_QZ = 1 << 5 - }; - - struct md5Channel_t - { - char name[ MAX_QPATH ]; - int8_t parentIndex; - - uint8_t componentsBits; // e.g. (COMPONENT_BIT_TX | COMPONENT_BIT_TY | COMPONENT_BIT_TZ) - uint16_t componentsOffset; - - vec3_t baseOrigin; - quat_t baseQuat; - }; - - struct md5Frame_t - { - vec3_t bounds[ 2 ]; // bounds of all surfaces of all LODs for this frame - float *components; // numAnimatedComponents many - }; - - struct md5Animation_t - { - uint16_t numFrames; - md5Frame_t *frames; - - uint8_t numChannels; // same as numBones in model - md5Channel_t *channels; - - int16_t frameRate; - - uint32_t numAnimatedComponents; - }; - //====================================================================== // inter-quake-model format //====================================================================== @@ -2230,20 +2182,6 @@ enum class cubeProbesAutoBuildMode { char *jointNames; }; - struct IQAnim_t { - int num_frames; - int num_joints; - int framerate; - int flags; - - // skeleton data - int *jointParents; - transform_t *poses; - float *bounds; - char *name; - char *jointNames; - }; - // inter-quake-model surface struct srfIQModel_t { surfaceType_t surfaceType; @@ -2257,18 +2195,6 @@ enum class cubeProbesAutoBuildMode { IBO_t *ibo; }; - struct skelAnimation_t - { - char name[ MAX_QPATH ]; // game path, including extension - animType_t type; - int index; // anim = tr.animations[anim->index] - - union { - md5Animation_t *md5; - IQAnim_t *iqm; - }; - }; - struct skelTriangle_t { int indexes[ 3 ]; @@ -3714,6 +3640,7 @@ inline bool checkGLErrors() int RE_CheckSkeleton( refSkeleton_t *skel, qhandle_t hModel, qhandle_t hAnim ); int RE_BuildSkeleton( refSkeleton_t *skel, qhandle_t anim, int startFrame, int endFrame, float frac, bool clearOrigin ); + skelAnimation_t RE_GetAnimation( qhandle_t anim ); int RE_BlendSkeleton( refSkeleton_t *skel, const refSkeleton_t *blend, float frac ); int RE_AnimNumFrames( qhandle_t hAnim ); int RE_AnimFrameRate( qhandle_t hAnim ); diff --git a/src/engine/renderer/tr_public.h b/src/engine/renderer/tr_public.h index 7aab7ab746..22e847f44b 100644 --- a/src/engine/renderer/tr_public.h +++ b/src/engine/renderer/tr_public.h @@ -245,6 +245,7 @@ struct refexport_t int ( *CheckSkeleton )( refSkeleton_t *skel, qhandle_t model, qhandle_t anim ); int ( *BuildSkeleton )( refSkeleton_t *skel, qhandle_t anim, int startFrame, int endFrame, float frac, bool clearOrigin ); + skelAnimation_t ( *GetAnimation )( qhandle_t anim ); int ( *BlendSkeleton )( refSkeleton_t *skel, const refSkeleton_t *blend, float frac ); int ( *BoneIndex )( qhandle_t hModel, const char *boneName ); int ( *AnimNumFrames )( qhandle_t hAnim ); diff --git a/src/engine/renderer/tr_types.h b/src/engine/renderer/tr_types.h index 6d6e9f05a6..82eb1d01e8 100644 --- a/src/engine/renderer/tr_types.h +++ b/src/engine/renderer/tr_types.h @@ -170,6 +170,78 @@ struct alignas(16) refSkeleton_t // XreaL END +struct md5Channel_t { + char name[MAX_QPATH]; + int8_t parentIndex; + + uint8_t componentsBits; // e.g. (COMPONENT_BIT_TX | COMPONENT_BIT_TY | COMPONENT_BIT_TZ) + uint16_t componentsOffset; + + vec3_t baseOrigin; + quat_t baseQuat; +}; + +struct md5Frame_t { + vec3_t bounds[2]; // bounds of all surfaces of all LODs for this frame + float* components; // numAnimatedComponents many +}; + +struct md5Animation_t { + uint16_t numFrames; + md5Frame_t* frames; + + uint8_t numChannels; // same as numBones in model + md5Channel_t* channels; + + int16_t frameRate; + + uint32_t numAnimatedComponents; +}; + +struct IQAnim_t { + int num_frames; + int num_joints; + int framerate; + int flags; + + // skeleton data + int* jointParents; + transform_t* poses; + float* bounds; + char* name; + char* jointNames; +}; + +enum { + IQM_LOOP = 1 << 0 +}; + +enum class animType_t { + AT_BAD, + AT_MD5, + AT_IQM, +}; + +enum { + COMPONENT_BIT_TX = 1 << 0, + COMPONENT_BIT_TY = 1 << 1, + COMPONENT_BIT_TZ = 1 << 2, + COMPONENT_BIT_QX = 1 << 3, + COMPONENT_BIT_QY = 1 << 4, + COMPONENT_BIT_QZ = 1 << 5 +}; + +struct skelAnimation_t { + char name[MAX_QPATH]; // game path, including extension + animType_t type; + int index; // anim = tr.animations[anim->index] + + union { + md5Animation_t* md5; + IQAnim_t* iqm; + }; +}; + struct refEntity_t { refEntityType_t reType; diff --git a/src/shared/client/cg_api.cpp b/src/shared/client/cg_api.cpp index 1f1892df2c..bfed5e2de5 100644 --- a/src/shared/client/cg_api.cpp +++ b/src/shared/client/cg_api.cpp @@ -499,6 +499,217 @@ qhandle_t trap_R_RegisterAnimation( const char *name ) return handle; } +skelAnimation_t trap_R_GetAnimation( qhandle_t anim ) { + skelAnimation_t result; + VM::SendMsg( anim, result ); + return result; +} + +std::vector trap_R_BatchGetAnimations( const std::vector& anims ) { + std::vector skelAnimations; + VM::SendMsg( anims, skelAnimations ); + return skelAnimations; +} + +static int IQMBuildSkeleton( refSkeleton_t* skel, skelAnimation_t* skelAnim, + int startFrame, int endFrame, float frac ) { + int i; + IQAnim_t* anim; + transform_t* newPose, * oldPose; + vec3_t mins, maxs; + + anim = skelAnim->iqm; + + // 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 ( anim->flags & IQM_LOOP ) { + startFrame %= anim->num_frames; + endFrame %= anim->num_frames; + } else { + startFrame = Math::Clamp( startFrame, 0, anim->num_frames - 1 ); + endFrame = Math::Clamp( endFrame, 0, anim->num_frames - 1 ); + } + + // compute frame pointers + oldPose = &anim->poses[startFrame * anim->num_joints]; + newPose = &anim->poses[endFrame * anim->num_joints]; + + // calculate a bounding box in the current coordinate system + if ( anim->bounds ) { + float* bounds = &anim->bounds[6 * startFrame]; + VectorCopy( bounds, mins ); + VectorCopy( bounds + 3, maxs ); + + bounds = &anim->bounds[6 * endFrame]; + BoundsAdd( mins, maxs, bounds, bounds + 3 ); + } + +#if defined( REFBONE_NAMES ) + const char* boneNames = anim->jointNames; +#endif + for ( i = 0; i < anim->num_joints; i++ ) { + TransStartLerp( &skel->bones[i].t ); + TransAddWeight( 1.0f - frac, &oldPose[i], &skel->bones[i].t ); + TransAddWeight( frac, &newPose[i], &skel->bones[i].t ); + TransEndLerp( &skel->bones[i].t ); + +#if defined( REFBONE_NAMES ) + Q_strncpyz( skel->bones[i].name, boneNames, sizeof( skel->bones[i].name ) ); + boneNames += strlen( boneNames ) + 1; +#endif + + skel->bones[i].parentIndex = anim->jointParents[i]; + } + + skel->numBones = anim->num_joints; + skel->type = refSkeletonType_t::SK_RELATIVE; + VectorCopy( mins, skel->bounds[0] ); + VectorCopy( maxs, skel->bounds[1] ); + return true; +} + +static int BuildSkeleton( refSkeleton_t* skel, skelAnimation_t* skelAnim, int startFrame, int endFrame, float frac, bool clearOrigin ) { + 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'", 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; +} + +static int BuildSkeleton( refSkeleton_t* skel, qhandle_t anim, int startFrame, int endFrame, float frac, bool clearOrigin ) { + skelAnimation_t skelAnimation = trap_R_GetAnimation( anim ); + BuildSkeleton( skel, &skelAnimation, startFrame, endFrame, frac, clearOrigin); +} + int trap_R_BuildSkeleton( refSkeleton_t *skel, qhandle_t anim, int startFrame, int endFrame, float frac, bool clearOrigin ) { int result; @@ -506,6 +717,12 @@ int trap_R_BuildSkeleton( refSkeleton_t *skel, qhandle_t anim, int startFrame, i return result; } +int trap_R_BuildSkeleton2( refSkeleton_t* skel, skelAnimation_t* anim, int startFrame, int endFrame, float frac, bool clearOrigin ) { + int result; + result = BuildSkeleton( skel, anim, startFrame, endFrame, frac, clearOrigin ); + return result; +} + // Shamelessly stolen from tr_animation.cpp int trap_R_BlendSkeleton( refSkeleton_t *skel, const refSkeleton_t *blend, float frac ) { diff --git a/src/shared/client/cg_api.h b/src/shared/client/cg_api.h index 6146d28ad7..be4b62894d 100644 --- a/src/shared/client/cg_api.h +++ b/src/shared/client/cg_api.h @@ -113,6 +113,8 @@ std::vector trap_R_BatchInPVS( bool trap_R_inPVVS( const vec3_t p1, const vec3_t p2 ); int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); qhandle_t trap_R_RegisterAnimation( const char *name ); +std::vector trap_R_BatchGetAnimations( const std::vector& anims ); +int trap_R_BuildSkeleton2( refSkeleton_t* skel, skelAnimation_t* anim, int startFrame, int endFrame, float frac, bool clearOrigin ); int trap_R_BuildSkeleton( refSkeleton_t *skel, qhandle_t anim, int startFrame, int endFrame, float frac, bool clearOrigin ); int trap_R_BlendSkeleton( refSkeleton_t *skel, const refSkeleton_t *blend, float frac ); int trap_R_BoneIndex( qhandle_t hModel, const char *boneName );