static void ComputeJointMats(iqmData_t* data, int frame, int oldframe, float backlerp, float* mat) { float* mat1, *mat2; int* joint = data->jointParents; int i; if (oldframe == frame) { mat1 = data->poseMats + 12 * data->num_joints * frame; for (i = 0; i < data->num_joints; i++, joint++) { if (*joint >= 0) { Matrix34Multiply(mat + 12 * *joint, mat1 + 12 * i, mat + 12 * i); } else { Com_Memcpy(mat + 12 * i, mat1 + 12 * i, 12 * sizeof(float)); } } } else { mat1 = data->poseMats + 12 * data->num_joints * frame; mat2 = data->poseMats + 12 * data->num_joints * oldframe; for (i = 0; i < data->num_joints; i++, joint++) { if (*joint >= 0) { float tmpMat[12]; InterpolateMatrix(mat1 + 12 * i, mat2 + 12 * i, backlerp, tmpMat); Matrix34Multiply(mat + 12 * *joint, tmpMat, mat + 12 * i); } else { InterpolateMatrix(mat1 + 12 * i, mat2 + 12 * i, backlerp, mat); } } } }
static void BuildPoseMat( iqmData_t *iqmData, iqmData_t *skeleton, float *poseMat, int poseNum, int parent, float *mat ) { float tmpMat[12]; if( parent >= 0 ) { Matrix34Multiply( iqmData->jointMats + 12 * parent, poseMat, tmpMat ); } else { Com_Memcpy( tmpMat, poseMat, sizeof(tmpMat) ); } Matrix34Multiply( tmpMat, iqmData->jointInvMats + 12 * poseNum, mat ); }
static void ComputePoseMats( iqmData_t *data, iqmData_t *skeleton, iqmData_t *oldSkeleton, int frame, int oldframe, float backlerp, float *mat ) { float tmpMat1[12], tmpMat2[12]; float *mat1, *mat2; int *joint = data->jointParents; int i; if ( skeleton->num_poses == 0 ) { for( i = 0; i < data->num_joints; i++, joint++ ) { if( *joint >= 0 ) { Matrix34Multiply( mat + 12 * *joint, identityMatrix, mat + 12*i ); } else { Com_Memcpy( mat + 12*i, identityMatrix, 12 * sizeof(float) ); } } return; } if ( oldframe == frame && skeleton == oldSkeleton ) { mat1 = skeleton->poseMats + 12 * skeleton->num_poses * frame; for( i = 0; i < skeleton->num_poses; i++, joint++ ) { BuildPoseMat( data, skeleton, mat1 + 12*i, i, *joint, tmpMat1 ); if( *joint >= 0 ) { Matrix34Multiply( mat + 12 * *joint, tmpMat1, mat + 12*i ); } else { Com_Memcpy( mat + 12*i, tmpMat1, 12 * sizeof(float) ); } } } else { mat1 = skeleton->poseMats + 12 * skeleton->num_poses * frame; mat2 = oldSkeleton->poseMats + 12 * oldSkeleton->num_poses * oldframe; for( i = 0; i < skeleton->num_poses; i++, joint++ ) { BuildPoseMat( data, skeleton, mat1 + 12*i, i, *joint, tmpMat1 ); BuildPoseMat( data, oldSkeleton, mat2 + 12*i, i, *joint, tmpMat2 ); if( *joint >= 0 ) { float tmpMat[12]; InterpolateMatrix( tmpMat1, tmpMat2, backlerp, tmpMat ); Matrix34Multiply( mat + 12 * *joint, tmpMat, mat + 12*i ); } else { InterpolateMatrix( tmpMat1, tmpMat2, backlerp, mat ); } } } }
/* ================= R_LoadIQM Load an IQM model and compute the joint matrices for every frame. ================= */ qboolean R_LoadIQM(model_t* mod, void* buffer, int filesize, const char* mod_name) { iqmHeader_t* header; iqmVertexArray_t* vertexarray; iqmTriangle_t* triangle; iqmMesh_t* mesh; iqmJoint_t* joint; iqmPose_t* pose; iqmBounds_t* bounds; unsigned short* framedata; char* str; int i, j; float jointMats[IQM_MAX_JOINTS * 2 * 12]; float* mat; size_t size, joint_names; iqmData_t* iqmData; srfIQModel_t* surface; if (filesize < sizeof(iqmHeader_t)) { return qfalse; } header = (iqmHeader_t*)buffer; if (Q_strncmp(header->magic, IQM_MAGIC, sizeof(header->magic))) { return qfalse; } LL(header->version); if (header->version != IQM_VERSION) { ri.Printf(PRINT_WARNING, "R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.\n", mod_name, header->version, IQM_VERSION); return qfalse; } LL(header->filesize); if (header->filesize > filesize || header->filesize > 16 << 20) { return qfalse; } LL(header->flags); LL(header->num_text); LL(header->ofs_text); LL(header->num_meshes); LL(header->ofs_meshes); LL(header->num_vertexarrays); LL(header->num_vertexes); LL(header->ofs_vertexarrays); LL(header->num_triangles); LL(header->ofs_triangles); LL(header->ofs_adjacency); LL(header->num_joints); LL(header->ofs_joints); LL(header->num_poses); LL(header->ofs_poses); LL(header->num_anims); LL(header->ofs_anims); LL(header->num_frames); LL(header->num_framechannels); LL(header->ofs_frames); LL(header->ofs_bounds); LL(header->num_comment); LL(header->ofs_comment); LL(header->num_extensions); LL(header->ofs_extensions); // check ioq3 joint limit if (header->num_joints > IQM_MAX_JOINTS) { ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %d joints (%d).\n", mod_name, IQM_MAX_JOINTS, header->num_joints); return qfalse; } // check and swap vertex arrays if (IQM_CheckRange(header, header->ofs_vertexarrays, header->num_vertexarrays, sizeof(iqmVertexArray_t))) { return qfalse; } vertexarray = (iqmVertexArray_t*)((byte*)header + header->ofs_vertexarrays); for (i = 0; i < header->num_vertexarrays; i++, vertexarray++) { int j, n, *intPtr; if (vertexarray->size <= 0 || vertexarray->size > 4) { return qfalse; } // total number of values n = header->num_vertexes * vertexarray->size; switch (vertexarray->format) { case IQM_BYTE: case IQM_UBYTE: // 1 byte, no swapping necessary if (IQM_CheckRange(header, vertexarray->offset, n, sizeof(byte))) { return qfalse; } break; case IQM_INT: case IQM_UINT: case IQM_FLOAT: // 4-byte swap if (IQM_CheckRange(header, vertexarray->offset, n, sizeof(float))) { return qfalse; } intPtr = (int*)((byte*)header + vertexarray->offset); for (j = 0; j < n; j++, intPtr++) { LL(*intPtr); } break; default: // not supported return qfalse; break; } switch (vertexarray->type) { case IQM_POSITION: case IQM_NORMAL: if (vertexarray->format != IQM_FLOAT || vertexarray->size != 3) { return qfalse; } break; case IQM_TANGENT: if (vertexarray->format != IQM_FLOAT || vertexarray->size != 4) { return qfalse; } break; case IQM_TEXCOORD: if (vertexarray->format != IQM_FLOAT || vertexarray->size != 2) { return qfalse; } break; case IQM_BLENDINDEXES: case IQM_BLENDWEIGHTS: if (vertexarray->format != IQM_UBYTE || vertexarray->size != 4) { return qfalse; } break; case IQM_COLOR: if (vertexarray->format != IQM_UBYTE || vertexarray->size != 4) { return qfalse; } break; } } // check and swap triangles if (IQM_CheckRange(header, header->ofs_triangles, header->num_triangles, sizeof(iqmTriangle_t))) { return qfalse; } triangle = (iqmTriangle_t*)((byte*)header + header->ofs_triangles); for (i = 0; i < header->num_triangles; i++, triangle++) { LL(triangle->vertex[0]); LL(triangle->vertex[1]); LL(triangle->vertex[2]); if (triangle->vertex[0] > header->num_vertexes || triangle->vertex[1] > header->num_vertexes || triangle->vertex[2] > header->num_vertexes) { return qfalse; } } // check and swap meshes if (IQM_CheckRange(header, header->ofs_meshes, header->num_meshes, sizeof(iqmMesh_t))) { return qfalse; } mesh = (iqmMesh_t*)((byte*)header + header->ofs_meshes); for (i = 0; i < header->num_meshes; i++, mesh++) { LL(mesh->name); LL(mesh->material); LL(mesh->first_vertex); LL(mesh->num_vertexes); LL(mesh->first_triangle); LL(mesh->num_triangles); // check ioq3 limits if (mesh->num_vertexes > SHADER_MAX_VERTEXES) { ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on a surface (%i).\n", mod_name, SHADER_MAX_VERTEXES, mesh->num_vertexes); return qfalse; } if (mesh->num_triangles * 3 > SHADER_MAX_INDEXES) { ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on a surface (%i).\n", mod_name, SHADER_MAX_INDEXES / 3, mesh->num_triangles); return qfalse; } if (mesh->first_vertex >= header->num_vertexes || mesh->first_vertex + mesh->num_vertexes > header->num_vertexes || mesh->first_triangle >= header->num_triangles || mesh->first_triangle + mesh->num_triangles > header->num_triangles || mesh->name >= header->num_text || mesh->material >= header->num_text) { return qfalse; } } // check and swap joints if (IQM_CheckRange(header, header->ofs_joints, header->num_joints, sizeof(iqmJoint_t))) { return qfalse; } joint = (iqmJoint_t*)((byte*)header + header->ofs_joints); joint_names = 0; for (i = 0; i < header->num_joints; i++, joint++) { LL(joint->name); LL(joint->parent); LL(joint->translate[0]); LL(joint->translate[1]); LL(joint->translate[2]); LL(joint->rotate[0]); LL(joint->rotate[1]); LL(joint->rotate[2]); LL(joint->rotate[3]); LL(joint->scale[0]); LL(joint->scale[1]); LL(joint->scale[2]); if (joint->parent < -1 || joint->parent >= (int)header->num_joints || joint->name >= (int)header->num_text) { return qfalse; } joint_names += strlen((char*)header + header->ofs_text + joint->name) + 1; } // check and swap poses if (header->num_poses != header->num_joints) { return qfalse; } if (IQM_CheckRange(header, header->ofs_poses, header->num_poses, sizeof(iqmPose_t))) { return qfalse; } pose = (iqmPose_t*)((byte*)header + header->ofs_poses); for (i = 0; i < header->num_poses; i++, pose++) { LL(pose->parent); LL(pose->mask); LL(pose->channeloffset[0]); LL(pose->channeloffset[1]); LL(pose->channeloffset[2]); LL(pose->channeloffset[3]); LL(pose->channeloffset[4]); LL(pose->channeloffset[5]); LL(pose->channeloffset[6]); LL(pose->channeloffset[7]); LL(pose->channeloffset[8]); LL(pose->channeloffset[9]); LL(pose->channelscale[0]); LL(pose->channelscale[1]); LL(pose->channelscale[2]); LL(pose->channelscale[3]); LL(pose->channelscale[4]); LL(pose->channelscale[5]); LL(pose->channelscale[6]); LL(pose->channelscale[7]); LL(pose->channelscale[8]); LL(pose->channelscale[9]); } if (header->ofs_bounds) { // check and swap model bounds if (IQM_CheckRange(header, header->ofs_bounds, header->num_frames, sizeof(*bounds))) { return qfalse; } bounds = (iqmBounds_t*)((byte*) header + header->ofs_bounds); for (i = 0; i < header->num_frames; i++) { LL(bounds->bbmin[0]); LL(bounds->bbmin[1]); LL(bounds->bbmin[2]); LL(bounds->bbmax[0]); LL(bounds->bbmax[1]); LL(bounds->bbmax[2]); bounds++; } } // allocate the model and copy the data size = sizeof(iqmData_t); size += header->num_meshes * sizeof(srfIQModel_t); size += header->num_joints * header->num_frames * 12 * sizeof(float); if (header->ofs_bounds) size += header->num_frames * 6 * sizeof(float); // model bounds size += header->num_vertexes * 3 * sizeof(float); // positions size += header->num_vertexes * 2 * sizeof(float); // texcoords size += header->num_vertexes * 3 * sizeof(float); // normals size += header->num_vertexes * 4 * sizeof(float); // tangents size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes size += header->num_vertexes * 4 * sizeof(byte); // blendWeights size += header->num_vertexes * 4 * sizeof(byte); // colors size += header->num_joints * sizeof(int); // parents size += header->num_triangles * 3 * sizeof(int); // triangles size += joint_names; // joint names mod->type = MOD_IQM; iqmData = (iqmData_t*)ri.Hunk_Alloc(size, h_low); mod->modelData = iqmData; // fill header iqmData->num_vertexes = header->num_vertexes; iqmData->num_triangles = header->num_triangles; iqmData->num_frames = header->num_frames; iqmData->num_surfaces = header->num_meshes; iqmData->num_joints = header->num_joints; iqmData->surfaces = (srfIQModel_t*)(iqmData + 1); iqmData->poseMats = (float*)(iqmData->surfaces + iqmData->num_surfaces); if (header->ofs_bounds) { iqmData->bounds = iqmData->poseMats + 12 * header->num_joints * header->num_frames; iqmData->positions = iqmData->bounds + 6 * header->num_frames; } else iqmData->positions = iqmData->poseMats + 12 * header->num_joints * header->num_frames; iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes; iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes; iqmData->tangents = iqmData->normals + 3 * header->num_vertexes; iqmData->blendIndexes = (byte*)(iqmData->tangents + 4 * header->num_vertexes); iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes; iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes; iqmData->jointParents = (int*)(iqmData->colors + 4 * header->num_vertexes); iqmData->triangles = iqmData->jointParents + header->num_joints; iqmData->names = (char*)(iqmData->triangles + 3 * header->num_triangles); // calculate joint matrices and their inverses // they are needed only until the pose matrices are calculated mat = jointMats; joint = (iqmJoint_t*)((byte*)header + header->ofs_joints); for (i = 0; i < header->num_joints; i++, joint++) { float baseFrame[12], invBaseFrame[12]; JointToMatrix(joint->rotate, joint->scale, joint->translate, baseFrame); Matrix34Invert(baseFrame, invBaseFrame); if (joint->parent >= 0) { Matrix34Multiply(jointMats + 2 * 12 * joint->parent, baseFrame, mat); mat += 12; Matrix34Multiply(invBaseFrame, jointMats + 2 * 12 * joint->parent + 12, mat); mat += 12; } else { Com_Memcpy(mat, baseFrame, sizeof(baseFrame)); mat += 12; Com_Memcpy(mat, invBaseFrame, sizeof(invBaseFrame)); mat += 12; } } // calculate pose matrices framedata = (unsigned short*)((byte*)header + header->ofs_frames); mat = iqmData->poseMats; for (i = 0; i < header->num_frames; i++) { pose = (iqmPose_t*)((byte*)header + header->ofs_poses); for (j = 0; j < header->num_poses; j++, pose++) { vec3_t translate; vec4_t rotate; vec3_t scale; float mat1[12], mat2[12]; translate[0] = pose->channeloffset[0]; if (pose->mask & 0x001) translate[0] += *framedata++ * pose->channelscale[0]; translate[1] = pose->channeloffset[1]; if (pose->mask & 0x002) translate[1] += *framedata++ * pose->channelscale[1]; translate[2] = pose->channeloffset[2]; if (pose->mask & 0x004) translate[2] += *framedata++ * pose->channelscale[2]; rotate[0] = pose->channeloffset[3]; if (pose->mask & 0x008) rotate[0] += *framedata++ * pose->channelscale[3]; rotate[1] = pose->channeloffset[4]; if (pose->mask & 0x010) rotate[1] += *framedata++ * pose->channelscale[4]; rotate[2] = pose->channeloffset[5]; if (pose->mask & 0x020) rotate[2] += *framedata++ * pose->channelscale[5]; rotate[3] = pose->channeloffset[6]; if (pose->mask & 0x040) rotate[3] += *framedata++ * pose->channelscale[6]; scale[0] = pose->channeloffset[7]; if (pose->mask & 0x080) scale[0] += *framedata++ * pose->channelscale[7]; scale[1] = pose->channeloffset[8]; if (pose->mask & 0x100) scale[1] += *framedata++ * pose->channelscale[8]; scale[2] = pose->channeloffset[9]; if (pose->mask & 0x200) scale[2] += *framedata++ * pose->channelscale[9]; // construct transformation matrix JointToMatrix(rotate, scale, translate, mat1); if (pose->parent >= 0) { Matrix34Multiply(jointMats + 12 * 2 * pose->parent, mat1, mat2); } else { Com_Memcpy(mat2, mat1, sizeof(mat1)); } Matrix34Multiply(mat2, jointMats + 12 * (2 * j + 1), mat); mat += 12; } } // register shaders // overwrite the material offset with the shader index mesh = (iqmMesh_t*)((byte*)header + header->ofs_meshes); surface = iqmData->surfaces; str = (char*)header + header->ofs_text; for (i = 0; i < header->num_meshes; i++, mesh++, surface++) { surface->surfaceType = SF_IQM; Q_strncpyz(surface->name, str + mesh->name, sizeof(surface->name)); Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster surface->shader = R_FindShader(str + mesh->material, LIGHTMAP_NONE, qtrue); if (surface->shader->defaultShader) surface->shader = tr.defaultShader; surface->data = iqmData; surface->first_vertex = mesh->first_vertex; surface->num_vertexes = mesh->num_vertexes; surface->first_triangle = mesh->first_triangle; surface->num_triangles = mesh->num_triangles; } // copy vertexarrays and indexes vertexarray = (iqmVertexArray_t*)((byte*)header + header->ofs_vertexarrays); for (i = 0; i < header->num_vertexarrays; i++, vertexarray++) { int n; // total number of values n = header->num_vertexes * vertexarray->size; switch (vertexarray->type) { case IQM_POSITION: Com_Memcpy(iqmData->positions, (byte*)header + vertexarray->offset, n * sizeof(float)); break; case IQM_NORMAL: Com_Memcpy(iqmData->normals, (byte*)header + vertexarray->offset, n * sizeof(float)); break; case IQM_TANGENT: Com_Memcpy(iqmData->tangents, (byte*)header + vertexarray->offset, n * sizeof(float)); break; case IQM_TEXCOORD: Com_Memcpy(iqmData->texcoords, (byte*)header + vertexarray->offset, n * sizeof(float)); break; case IQM_BLENDINDEXES: Com_Memcpy(iqmData->blendIndexes, (byte*)header + vertexarray->offset, n * sizeof(byte)); break; case IQM_BLENDWEIGHTS: Com_Memcpy(iqmData->blendWeights, (byte*)header + vertexarray->offset, n * sizeof(byte)); break; case IQM_COLOR: Com_Memcpy(iqmData->colors, (byte*)header + vertexarray->offset, n * sizeof(byte)); break; } } // copy joint parents joint = (iqmJoint_t*)((byte*)header + header->ofs_joints); for (i = 0; i < header->num_joints; i++, joint++) { iqmData->jointParents[i] = joint->parent; } // copy triangles triangle = (iqmTriangle_t*)((byte*)header + header->ofs_triangles); for (i = 0; i < header->num_triangles; i++, triangle++) { iqmData->triangles[3 * i + 0] = triangle->vertex[0]; iqmData->triangles[3 * i + 1] = triangle->vertex[1]; iqmData->triangles[3 * i + 2] = triangle->vertex[2]; } // copy joint names str = iqmData->names; joint = (iqmJoint_t*)((byte*)header + header->ofs_joints); for (i = 0; i < header->num_joints; i++, joint++) { char* name = (char*)header + header->ofs_text + joint->name; int len = strlen(name) + 1; Com_Memcpy(str, name, len); str += len; } // copy model bounds if (header->ofs_bounds) { mat = iqmData->bounds; bounds = (iqmBounds_t*)((byte*) header + header->ofs_bounds); for (i = 0; i < header->num_frames; i++) { mat[0] = bounds->bbmin[0]; mat[1] = bounds->bbmin[1]; mat[2] = bounds->bbmin[2]; mat[3] = bounds->bbmax[0]; mat[4] = bounds->bbmax[1]; mat[5] = bounds->bbmax[2]; mat += 6; bounds++; } } return qtrue; }