/* * CG_RecurseBlendSkeletalBone * Combine 2 different poses in one from a given root bone */ void CG_RecurseBlendSkeletalBone( bonepose_t *inboneposes, bonepose_t *outboneposes, bonenode_t *bonenode, float frac ) { int i; bonepose_t *inbone, *outbone; if( bonenode->bonenum != -1 ) { inbone = inboneposes + bonenode->bonenum; outbone = outboneposes + bonenode->bonenum; if( frac == 1 ) { memcpy( &outboneposes[bonenode->bonenum], &inboneposes[bonenode->bonenum], sizeof( bonepose_t ) ); } else { // blend current node pose DualQuat_Lerp( inbone->dualquat, outbone->dualquat, frac, outbone->dualquat ); } } for( i = 0; i < bonenode->numbonechildren; i++ ) { if( bonenode->bonechildren[i] ) CG_RecurseBlendSkeletalBone( inboneposes, outboneposes, bonenode->bonechildren[i], frac ); } }
/* * CG_LerpBoneposes * Interpolate between 2 poses. It doesn't matter where they come * from nor if they are previously transformed or not */ qboolean CG_LerpBoneposes( cgs_skeleton_t *skel, bonepose_t *curboneposes, bonepose_t *oldboneposes, bonepose_t *outboneposes, float frontlerp ) { int i; assert( curboneposes && oldboneposes && outboneposes ); assert( skel && skel->numBones && skel->numFrames ); if( frontlerp == 1 ) { memcpy( outboneposes, curboneposes, sizeof( bonepose_t ) * skel->numBones ); } else if( frontlerp == 0 ) { memcpy( outboneposes, oldboneposes, sizeof( bonepose_t ) * skel->numBones ); } else { // lerp all bone poses for( i = 0; i < (int)skel->numBones; i++, curboneposes++, oldboneposes++, outboneposes++ ) { DualQuat_Lerp( oldboneposes->dualquat, curboneposes->dualquat, frontlerp, outboneposes->dualquat ); } } return qtrue; }
/* * R_DrawSkeletalSurf */ qboolean R_DrawSkeletalSurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, drawSurfaceSkeletal_t *drawSurf ) { unsigned int i, j; int framenum = e->frame; int oldframenum = e->oldframe; float backlerp = e->backlerp; float frontlerp = 1.0 - backlerp; bonepose_t tempbonepose[256]; const bonepose_t *bp, *oldbp, *bonepose, *oldbonepose, *lerpedbonepose; bonepose_t *out, tp; mskbone_t *bone; mat4_t *bonePoseRelativeMat; dualquat_t *bonePoseRelativeDQ; size_t bonePoseRelativeMatSize, bonePoseRelativeDQSize; const model_t *mod = drawSurf->model; const mskmodel_t *skmodel = ( const mskmodel_t * )mod->extradata; const mskmesh_t *skmesh = drawSurf->mesh; qboolean hardwareTransform = skmesh->vbo != NULL && glConfig.maxGLSLBones > 0 ? qtrue : qfalse; vattribmask_t vattribs; bonePoseRelativeMat = NULL; bonePoseRelativeDQ = NULL; bp = e->boneposes; oldbp = e->oldboneposes; // not sure if it's really needed if( bp == skmodel->frames[0].boneposes ) { bp = NULL; framenum = oldframenum = 0; } // choose boneposes for lerping if( bp ) { if( !oldbp ) oldbp = bp; } else { if( ( framenum >= (int)skmodel->numframes ) || ( framenum < 0 ) ) { #ifndef PUBLIC_BUILD ri.Com_DPrintf( "R_DrawBonesFrameLerp %s: no such frame %d\n", mod->name, framenum ); #endif framenum = 0; } if( ( oldframenum >= (int)skmodel->numframes ) || ( oldframenum < 0 ) ) { #ifndef PUBLIC_BUILD ri.Com_DPrintf( "R_DrawBonesFrameLerp %s: no such oldframe %d\n", mod->name, oldframenum ); #endif oldframenum = 0; } bp = skmodel->frames[framenum].boneposes; oldbp = skmodel->frames[oldframenum].boneposes; } if( bp == oldbp && !framenum && skmesh->vbo != NULL ) { // fastpath: render static frame 0 as is RB_BindVBO( skmesh->vbo->index, GL_TRIANGLES ); RB_DrawElements( 0, skmesh->numverts, 0, skmesh->numtris * 3 ); return qfalse; } // see what vertex attribs backend needs vattribs = RB_GetVertexAttribs(); // cache size bonePoseRelativeMatSize = sizeof( mat4_t ) * (skmodel->numbones + skmodel->numblends); bonePoseRelativeDQSize = sizeof( dualquat_t ) * skmodel->numbones; // fetch bones tranforms from cache (both matrices and dual quaternions) bonePoseRelativeDQ = ( dualquat_t * )R_GetSketalCache( R_ENT2NUM( e ), mod->lodnum ); if( bonePoseRelativeDQ ) { bonePoseRelativeMat = ( mat4_t * )(( qbyte * )bonePoseRelativeDQ + bonePoseRelativeDQSize); } else { // lerp boneposes and store results in cache lerpedbonepose = tempbonepose; if( bp == oldbp || frontlerp == 1 ) { if( e->boneposes ) { // assume that parent transforms have already been applied lerpedbonepose = bp; } else { for( i = 0; i < skmodel->numbones; i++ ) { j = i; out = tempbonepose + j; bonepose = bp + j; bone = skmodel->bones + j; if( bone->parent >= 0 ) { DualQuat_Multiply( tempbonepose[bone->parent].dualquat, bonepose->dualquat, out->dualquat ); } else { DualQuat_Copy( bonepose->dualquat, out->dualquat ); } } } } else { if( e->boneposes ) { // lerp, assume that parent transforms have already been applied for( i = 0, out = tempbonepose, bonepose = bp, oldbonepose = oldbp, bone = skmodel->bones; i < skmodel->numbones; i++, out++, bonepose++, oldbonepose++, bone++ ) { DualQuat_Lerp( oldbonepose->dualquat, bonepose->dualquat, frontlerp, out->dualquat ); } } else { // lerp and transform for( i = 0; i < skmodel->numbones; i++ ) { j = i; out = tempbonepose + j; bonepose = bp + j; oldbonepose = oldbp + j; bone = skmodel->bones + j; DualQuat_Lerp( oldbonepose->dualquat, bonepose->dualquat, frontlerp, out->dualquat ); if( bone->parent >= 0 ) { DualQuat_Copy( out->dualquat, tp.dualquat ); DualQuat_Multiply( tempbonepose[bone->parent].dualquat, tp.dualquat, out->dualquat ); } } } } bonePoseRelativeDQ = ( dualquat_t * )R_AllocSkeletalDataCache( R_ENT2NUM( e ), mod->lodnum, bonePoseRelativeDQSize + bonePoseRelativeMatSize ); // generate dual quaternions for all bones for( i = 0; i < skmodel->numbones; i++ ) { DualQuat_Multiply( lerpedbonepose[i].dualquat, skmodel->invbaseposes[i].dualquat, bonePoseRelativeDQ[i] ); DualQuat_Normalize( bonePoseRelativeDQ[i] ); } // CPU transforms if( !hardwareTransform ) { bonePoseRelativeMat = ( mat4_t * )(( qbyte * )bonePoseRelativeDQ + bonePoseRelativeDQSize); // generate matrices for all bones for( i = 0; i < skmodel->numbones; i++ ) { Matrix4_FromDualQuaternion( bonePoseRelativeDQ[i], bonePoseRelativeMat[i] ); } // generate matrices for all blend combinations R_SkeletalBlendPoses( skmodel->numblends, skmodel->blends, skmodel->numbones, bonePoseRelativeMat ); } } if( hardwareTransform ) { RB_BindVBO( skmesh->vbo->index, GL_TRIANGLES ); RB_SetBonesData( skmodel->numbones, bonePoseRelativeDQ, skmesh->maxWeights ); RB_DrawElements( 0, skmesh->numverts, 0, skmesh->numtris * 3 ); } else { mesh_t *rb_mesh; RB_BindVBO( RB_VBO_STREAM, GL_TRIANGLES ); rb_mesh = RB_MapBatchMesh( skmesh->numverts, skmesh->numtris * 3 ); if( !rb_mesh ) { ri.Com_DPrintf( S_COLOR_YELLOW "R_DrawAliasSurf: RB_MapBatchMesh returned NULL for (%s)(%s)", drawSurf->model->name, skmesh->name ); return qfalse; } R_SkeletalTransformVerts( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat, ( vec_t * )skmesh->xyzArray[0], ( vec_t * )rb_mesh->xyzArray ); if( vattribs & VATTRIB_SVECTOR_BIT ) { R_SkeletalTransformNormalsAndSVecs( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat, ( vec_t * )skmesh->normalsArray[0], ( vec_t * )rb_mesh->normalsArray, ( vec_t * )skmesh->sVectorsArray[0], ( vec_t * )rb_mesh->sVectorsArray ); } else if( vattribs & VATTRIB_NORMAL_BIT ) { R_SkeletalTransformNormals( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat, ( vec_t * )skmesh->normalsArray[0], ( vec_t * )rb_mesh->normalsArray ); } rb_mesh->elems = skmesh->elems; rb_mesh->numElems = skmesh->numtris * 3; rb_mesh->numVerts = skmesh->numverts; rb_mesh->stArray = skmesh->stArray; RB_UploadMesh( rb_mesh ); RB_EndBatch(); } return qfalse; }