int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, int startTagIndex, qhandle_t frameModel, int startFrame, qhandle_t endFrameModel, int endFrame, float frac, const char *tagName ) { iqmData_t *startSkeleton, *endSkeleton; float jointMats[IQM_MAX_JOINTS * 12]; int joint; char *names = data->names; // get joint number by reading the joint names for( joint = 0; joint < data->num_joints; joint++ ) { if( joint >= startTagIndex && !strcmp( tagName, names ) ) break; names += strlen( names ) + 1; } if( joint >= data->num_joints ) { return -1; } // just checking if tag exists if( !tag ) { return joint; } startSkeleton = R_GetIQMModelDataByHandle( frameModel, data ); endSkeleton = R_GetIQMModelDataByHandle( endFrameModel, data ); ComputeJointMats( data, startSkeleton, endSkeleton, startFrame, endFrame, frac, jointMats ); tag->axis[0][0] = jointMats[12 * joint + 0]; tag->axis[1][0] = jointMats[12 * joint + 1]; tag->axis[2][0] = jointMats[12 * joint + 2]; tag->origin[0] = jointMats[12 * joint + 3]; tag->axis[0][1] = jointMats[12 * joint + 4]; tag->axis[1][1] = jointMats[12 * joint + 5]; tag->axis[2][1] = jointMats[12 * joint + 6]; tag->origin[1] = jointMats[12 * joint + 7]; tag->axis[0][2] = jointMats[12 * joint + 8]; tag->axis[1][2] = jointMats[12 * joint + 9]; tag->axis[2][2] = jointMats[12 * joint + 10]; tag->origin[2] = jointMats[12 * joint + 11]; return joint; }
/* ================= R_AddIQMSurfaces Add all surfaces of this model ================= */ void R_AddIQMSurfaces( trRefEntity_t *ent ) { iqmData_t *data; iqmData_t *skeleton; iqmData_t *oldSkeleton; srfIQModel_t *surface; int i, j; qboolean personalModel; int cull; int fogNum; int cubemapIndex; shader_t *shader; skin_t *skin; skinSurface_t *skinSurf; data = tr.currentModel->modelData; surface = data->surfaces; if ( !data->num_surfaces || !data->num_triangles || !data->num_vertexes ) { ri.Printf( PRINT_WARNING, "WARNING: Tried to render IQM '%s' with no surfaces\n", tr.currentModel->name ); return; } skeleton = R_GetIQMModelDataByHandle( ent->e.frameModel, data ); oldSkeleton = R_GetIQMModelDataByHandle( ent->e.oldframeModel, data ); // don't add mirror only objects if not in a mirror/portal personalModel = (ent->e.renderfx & RF_ONLY_MIRROR) && !tr.viewParms.isPortal; if ( ent->e.renderfx & RF_WRAP_FRAMES ) { ent->e.frame %= skeleton->num_frames; ent->e.oldframe %= oldSkeleton->num_frames; } // // 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 ( (ent->e.frame >= skeleton->num_frames) || (ent->e.frame < 0) || (ent->e.oldframe >= oldSkeleton->num_frames) || (ent->e.oldframe < 0) ) { ri.Printf( PRINT_DEVELOPER, "R_AddIQMSurfaces: no such frame %d to %d for '%s'\n", ent->e.oldframe, ent->e.frame, tr.currentModel->name ); ent->e.frame = 0; ent->e.oldframe = 0; } // // cull the entire model if merged bounding box of both frames // is outside the view frustum. // cull = R_CullIQM ( skeleton, oldSkeleton, ent ); if ( cull == CULL_OUT ) { return; } // // set up lighting now that we know we aren't culled // if ( !personalModel || r_shadows->integer > 1 ) { R_SetupEntityLighting( &tr.refdef, ent ); } // // see if we are in a fog volume // fogNum = R_ComputeIQMFogNum( skeleton, ent ); cubemapIndex = R_CubemapForPoint(ent->e.origin); for ( i = 0 ; i < data->num_surfaces ; i++ ) { if(ent->e.customShader) shader = R_GetShaderByHandle( ent->e.customShader ); else if(ent->e.customSkin > 0 && ent->e.customSkin <= tr.refdef.numSkins) { skin = &tr.refdef.skins[ent->e.customSkin - 1]; shader = tr.defaultShader; for(j = 0 ; j < skin->numSurfaces ; j++) { skinSurf = &tr.skinSurfaces[ skin->surfaces[ j ] ]; if (!strcmp(skinSurf->name, surface->name)) { shader = skinSurf->shader; break; } } if (shader == tr.nodrawShader) { surface++; continue; } } else { shader = surface->shader; } // we will add shadows even if the main object isn't visible in the view // stencil shadows can't do personal models unless I polyhedron clip if ( !personalModel && r_shadows->integer == 2 && fogNum == 0 && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) && shader->sort == SS_OPAQUE ) { R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0, 0, 0 ); } // projection shadows work fine with personal models if ( r_shadows->integer == 3 && fogNum == 0 && (ent->e.renderfx & RF_SHADOW_PLANE ) && shader->sort == SS_OPAQUE ) { R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0, 0, 0 ); } if( !personalModel ) { R_AddEntDrawSurf( ent, (void *)surface, shader, fogNum, 0, 0, cubemapIndex ); } surface++; } }
/* ================= RB_AddIQMSurfaces Compute vertices for this model surface ================= */ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { srfIQModel_t *surf = (srfIQModel_t *)surface; iqmData_t *data = surf->data; float jointMats[IQM_MAX_JOINTS * 12]; int i; vec4_t *outXYZ; uint32_t *outNormal; #ifdef USE_VERT_TANGENT_SPACE uint32_t *outTangent; #endif vec2_t (*outTexCoord)[2]; vec4_t *outColor; iqmData_t *skeleton = R_GetIQMModelDataByHandle( backEnd.currentEntity->e.frameModel, data ); iqmData_t *oldSkeleton = R_GetIQMModelDataByHandle( backEnd.currentEntity->e.oldframeModel, data ); int frame = skeleton->num_frames ? backEnd.currentEntity->e.frame % skeleton->num_frames : 0; int oldframe = oldSkeleton->num_frames ? backEnd.currentEntity->e.oldframe % oldSkeleton->num_frames : 0; float backlerp = backEnd.currentEntity->e.backlerp; int *tri; glIndex_t *ptr; glIndex_t base; if ( data != skeleton && data->num_joints != skeleton->num_poses ) { ri.Printf( PRINT_WARNING, "WARNING: frameModel '%s' for model '%s' has different number of joints\n", R_GetModelByHandle( backEnd.currentEntity->e.frameModel )->name, R_GetModelByHandle( backEnd.currentEntity->e.hModel )->name ); skeleton = data; } if ( data != oldSkeleton && data->num_joints != oldSkeleton->num_poses ) { ri.Printf( PRINT_WARNING, "WARNING: oldframeModel '%s' for model '%s' has different number of joints\n", R_GetModelByHandle( backEnd.currentEntity->e.oldframeModel )->name, R_GetModelByHandle( backEnd.currentEntity->e.hModel )->name ); oldSkeleton = data; } RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); outXYZ = &tess.xyz[tess.numVertexes]; outNormal = &tess.normal[tess.numVertexes]; #ifdef USE_VERT_TANGENT_SPACE outTangent = &tess.tangent[tess.numVertexes]; #endif outTexCoord = &tess.texCoords[tess.numVertexes]; outColor = &tess.vertexColors[tess.numVertexes]; // compute interpolated joint matrices if ( skeleton->num_poses > 0 ) { ComputePoseMats( data, skeleton, oldSkeleton, frame, oldframe, backlerp, jointMats ); } // transform vertexes and fill other data for( i = 0; i < surf->num_vertexes; i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) { int j, k; float vtxMat[12]; float nrmMat[9]; int vtx = i + surf->first_vertex; float blendWeights[4]; int numWeights; for ( numWeights = 0; numWeights < 4; numWeights++ ) { if ( data->blendWeightsType == IQM_FLOAT ) blendWeights[numWeights] = data->blendWeights.f[4*vtx + numWeights]; else blendWeights[numWeights] = (float)data->blendWeights.b[4*vtx + numWeights] / 255.0f; if ( blendWeights[numWeights] <= 0 ) break; } if ( skeleton->num_poses == 0 || numWeights == 0 ) { // no blend joint, use identity matrix. Com_Memcpy( vtxMat, identityMatrix, 12 * sizeof (float) ); } else { // compute the vertex matrix by blending the up to // four blend weights Com_Memset( vtxMat, 0, 12 * sizeof (float) ); for( j = 0; j < numWeights; j++ ) { for( k = 0; k < 12; k++ ) { vtxMat[k] += blendWeights[j] * jointMats[12*data->blendIndexes[4*vtx + j] + k]; } } } // compute the normal matrix as transpose of the adjoint // of the vertex matrix nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; (*outTexCoord)[0][0] = data->texcoords[2*vtx + 0]; (*outTexCoord)[0][1] = data->texcoords[2*vtx + 1]; (*outTexCoord)[1][0] = (*outTexCoord)[0][0]; (*outTexCoord)[1][1] = (*outTexCoord)[0][1]; (*outXYZ)[0] = vtxMat[ 0] * data->positions[3*vtx+0] + vtxMat[ 1] * data->positions[3*vtx+1] + vtxMat[ 2] * data->positions[3*vtx+2] + vtxMat[ 3]; (*outXYZ)[1] = vtxMat[ 4] * data->positions[3*vtx+0] + vtxMat[ 5] * data->positions[3*vtx+1] + vtxMat[ 6] * data->positions[3*vtx+2] + vtxMat[ 7]; (*outXYZ)[2] = vtxMat[ 8] * data->positions[3*vtx+0] + vtxMat[ 9] * data->positions[3*vtx+1] + vtxMat[10] * data->positions[3*vtx+2] + vtxMat[11]; (*outXYZ)[3] = 1.0f; { vec3_t normal; vec4_t tangent; normal[0] = DotProduct(&nrmMat[0], &data->normals[3*vtx]); normal[1] = DotProduct(&nrmMat[3], &data->normals[3*vtx]); normal[2] = DotProduct(&nrmMat[6], &data->normals[3*vtx]); *outNormal = R_VboPackNormal(normal); #ifdef USE_VERT_TANGENT_SPACE tangent[0] = DotProduct(&nrmMat[0], &data->tangents[4*vtx]); tangent[1] = DotProduct(&nrmMat[3], &data->tangents[4*vtx]); tangent[2] = DotProduct(&nrmMat[6], &data->tangents[4*vtx]); tangent[3] = data->tangents[4*vtx+3]; *outTangent++ = R_VboPackTangent(tangent); #endif } (*outColor)[0] = data->colors[4*vtx+0] / 255.0f; (*outColor)[1] = data->colors[4*vtx+1] / 255.0f; (*outColor)[2] = data->colors[4*vtx+2] / 255.0f; (*outColor)[3] = data->colors[4*vtx+3] / 255.0f; } tri = data->triangles + 3 * surf->first_triangle; ptr = &tess.indexes[tess.numIndexes]; base = tess.numVertexes; for( i = 0; i < surf->num_triangles; i++ ) { *ptr++ = base + (*tri++ - surf->first_vertex); *ptr++ = base + (*tri++ - surf->first_vertex); *ptr++ = base + (*tri++ - surf->first_vertex); } tess.numIndexes += 3 * surf->num_triangles; tess.numVertexes += surf->num_vertexes; }