static Skins_ApplyVariant(ModelContainer_t *pContainer, SkinSet_t::iterator itEthnic, ShadersForMaterial_t::iterator itMaterialShaders, string strMaterialName, int iVariant) { if (itMaterialShaders != (*itEthnic).second.end()) { LPCSTR psShaderName = (*itMaterialShaders).second[iVariant].c_str(); // shader name to load from skinset pContainer->MaterialShaders[strMaterialName] = psShaderName; LPCSTR psLocalTexturePath = R_FindShader( psShaderName ); // shader->texture name if (psLocalTexturePath && strlen(psLocalTexturePath)) { TextureHandle_t hTexture = TextureHandle_ForName( psLocalTexturePath ); if (hTexture == -1) { hTexture = Texture_Load(psLocalTexturePath); } GLuint uiBind = Texture_GetGLBind( hTexture ); pContainer->MaterialBinds[strMaterialName] = uiBind; } } }
// if return == true, no further action needed by the caller... // void *RE_RegisterModels_Malloc(int iSize, void *pvDiskBufferIfJustLoaded, const char *psModelFileName, qboolean *pqbAlreadyFound, memtag_t eTag) { char sModelName[MAX_QPATH]; Q_strncpyz(sModelName,psModelFileName,sizeof(sModelName)); Q_strlwr (sModelName); CachedEndianedModelBinary_t &ModelBin = (*CachedModels)[sModelName]; if (ModelBin.pModelDiskImage == NULL) { // ... then this entry has only just been created, ie we need to load it fully... // // new, instead of doing a R_Malloc and assigning that we just morph the disk buffer alloc // then don't thrown it away on return - cuts down on mem overhead // // ... groan, but not if doing a limb hierarchy creation (some VV stuff?), in which case it's NULL // if ( pvDiskBufferIfJustLoaded ) { R_MorphMallocTag( pvDiskBufferIfJustLoaded, eTag ); } else { pvDiskBufferIfJustLoaded = R_Malloc(iSize,eTag, qfalse ); } ModelBin.pModelDiskImage= pvDiskBufferIfJustLoaded; ModelBin.iAllocSize = iSize; *pqbAlreadyFound = qfalse; } else { // if we already had this model entry, then re-register all the shaders it wanted... // const int iEntries = ModelBin.ShaderRegisterData.size(); for (int i=0; i<iEntries; i++) { int iShaderNameOffset = ModelBin.ShaderRegisterData[i].first; int iShaderPokeOffset = ModelBin.ShaderRegisterData[i].second; const char *const psShaderName = &((char*)ModelBin.pModelDiskImage)[iShaderNameOffset]; int *const piShaderPokePtr= (int *) &((char*)ModelBin.pModelDiskImage)[iShaderPokeOffset]; shader_t *sh = R_FindShader( psShaderName, lightmapsNone, stylesDefault, qtrue ); if ( sh->defaultShader ) { *piShaderPokePtr = 0; } else { *piShaderPokePtr = sh->index; } } *pqbAlreadyFound = qtrue; // tell caller not to re-Endian or re-Shader this binary } ModelBin.iLastLevelUsedOn = RE_RegisterMedia_GetLevel(); return ModelBin.pModelDiskImage; }
// if return == true, no further action needed by the caller... // void *RE_RegisterModels_Malloc(int iSize, const char *psModelFileName, qboolean *pqbAlreadyFound, memtag_t eTag) { char sModelName[MAX_QPATH]; Q_strncpyz(sModelName,psModelFileName,sizeof(sModelName)); Q_strlwr (sModelName); CachedEndianedModelBinary_t &ModelBin = CachedModels[sModelName]; if (ModelBin.pModelDiskImage == NULL) { // ... then this entry has only just been created, ie we need to load it fully... // void *pMalloc = ri.Malloc(iSize,eTag, qfalse ); if (!pMalloc) // not needed anymore, but wtf? { ri.Error( ERR_FATAL, "RE_RegisterModels_Malloc(): Failed to alloc %d bytes for \"%s\"!", iSize, psModelFileName ); } ModelBin.pModelDiskImage= pMalloc; ModelBin.iAllocSize = iSize; *pqbAlreadyFound = qfalse; } else { // if we already had this model entry, then re-register all the shaders it wanted... // const int iEntries = ModelBin.ShaderRegisterData.size(); for (int i=0; i<iEntries; i++) { int iShaderNameOffset = ModelBin.ShaderRegisterData[i].first; int iShaderPokeOffset = ModelBin.ShaderRegisterData[i].second; const char *const psShaderName = &((char*)ModelBin.pModelDiskImage)[iShaderNameOffset]; int *const piShaderPokePtr= (int *) &((char*)ModelBin.pModelDiskImage)[iShaderPokeOffset]; shader_t *sh = R_FindShader( psShaderName, lightmapsNone, stylesDefault, qtrue ); if ( sh->defaultShader ) { *piShaderPokePtr = 0; } else { *piShaderPokePtr = sh->index; } } *pqbAlreadyFound = qtrue; // tell caller not to re-Endian or re-Shader this binary } ModelBin.iLastLevelUsedOn = RE_RegisterMedia_GetLevel(); return ModelBin.pModelDiskImage; }
static void R_x42LoadDefaultShaders( x42data_t *x42 ) { uint i; x42->groupMats = (int*)ri.Hunk_Alloc( sizeof( int ) * x42->header.numGroups, h_low ); for( i = 0; i < x42->header.numGroups; i++ ) { shader_t *shader = R_FindShader( x42->strings + x42->groups[i].material, LIGHTMAP_NONE, qtrue ); x42->groupMats[i] = shader->defaultShader ? 0 : shader->index; Q_strlwr( (char*)x42->strings + x42->groups[i].surfaceName ); } }
// loads a model's shadow script static void R_LoadModelShadow( idRenderModel* mod ) { // set default shadow mod->q3_shadowShader = 0; // build name char filename[ 1024 ]; String::StripExtension2( mod->name, filename, sizeof ( filename ) ); String::DefaultExtension( filename, 1024, ".shadow" ); // load file char* buf; FS_ReadFile( filename, ( void** )&buf ); if ( !buf ) { return; } char* shadowBits = strchr( buf, ' ' ); if ( shadowBits != NULL ) { *shadowBits = '\0'; shadowBits++; if ( String::Length( buf ) >= MAX_QPATH ) { common->Printf( "R_LoadModelShadow: Shader name exceeds MAX_QPATH\n" ); mod->q3_shadowShader = 0; } else { shader_t* sh = R_FindShader( buf, LIGHTMAP_NONE, true ); if ( sh->defaultShader ) { mod->q3_shadowShader = 0; } else { mod->q3_shadowShader = sh->index; } } sscanf( shadowBits, "%f %f %f %f %f %f", &mod->q3_shadowParms[ 0 ], &mod->q3_shadowParms[ 1 ], &mod->q3_shadowParms[ 2 ], &mod->q3_shadowParms[ 3 ], &mod->q3_shadowParms[ 4 ], &mod->q3_shadowParms[ 5 ] ); } FS_FreeFile( buf ); }
/* =============== ShaderForShaderNum =============== */ static shader_t *ShaderForShaderNum( int shaderNum, const int *lightmapNum, const byte *lightmapStyles, const byte *vertexStyles ) { shader_t *shader; dshader_t *dsh; const byte *styles; styles = lightmapStyles; shaderNum = LittleLong( shaderNum ); if ( shaderNum < 0 || shaderNum >= s_worldData.numShaders ) { ri.Error( ERR_DROP, "ShaderForShaderNum: bad num %i", shaderNum ); } dsh = &s_worldData.shaders[ shaderNum ]; if (lightmapNum[0] == LIGHTMAP_BY_VERTEX) { styles = vertexStyles; } if ( r_vertexLight->integer ) { lightmapNum = lightmapsVertex; styles = vertexStyles; } /* if ( r_fullbright->integer ) { lightmapNum = lightmapsFullBright; styles = vertexStyles; } */ shader = R_FindShader( dsh->shader, lightmapNum, styles, qtrue ); // if the shader had errors, just use default shader if ( shader->defaultShader ) { return tr.defaultShader; } return shader; }
/* ================= 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; }
qboolean R_LoadGLM( model_t *mod, void *buffer, int filesize, const char *mod_name ) { int i, j, lodindex, surfindex; glmHeader_t *header, *glm; glmLOD_t *lod; glmLODSurfOffset_t *lod_surf_ofs; glmSurfHierarchy_t *surfh; glmVertexTexCoord_t *vcor; glmSurface_t *surf; glmTriangle_t *tri; glmVertex_t *v; int version; int size; shader_t *sh; vec3_t tempVert; vec3_t tempNorm; //int frameSize; float zrots[3][4]; qboolean do_zrot = qfalse; zrots[0][0] = 0.0; zrots[0][1] = -1.0; zrots[0][2] = 0.0; zrots[0][3] = 0.0; zrots[1][0] = 1.0; zrots[1][1] = 0.0; zrots[1][2] = 0.0; zrots[1][3] = 0.0; zrots[2][0] = 0.0; zrots[2][1] = 0.0; zrots[2][2] = 1.0; zrots[2][3] = 0.0; if( filesize < sizeof( glmHeader_t ) ) { return qfalse; } header = (glmHeader_t *)buffer; version = LittleLong( header->version ); if( version != GLM_VERSION ) { ri.Printf( PRINT_WARNING, "R_LoadGLM: %s has wrong version (%i should be %i)\n", mod_name, version, GLM_VERSION ); return qfalse; } size = LittleLong( header->ofsEnd ); if( size > filesize ) { ri.Printf( PRINT_WARNING, "R_LoadGLM: Header of %s is broken. Wrong filesize declared!\n", mod_name ); return qfalse; } mod->type = MOD_GLM; size = LittleLong(header->ofsEnd); mod->dataSize += size; //ri.Printf( PRINT_WARNING, "R_LoadGLM: %s alloc %d\n", mod_name, size ); mod->modelData = ri.Hunk_Alloc( size, h_low ); glm = (glmHeader_t *)mod->modelData; Com_Memcpy( glm, buffer, size ); LL( glm->ident ); LL( glm->animIndex ); LL( glm->numBones ); LL( glm->numLODs ); LL( glm->ofsLODs ); LL( glm->numSurfaces ); LL( glm->ofsSurfHierarchy ); if ( GLM_CheckRange( glm, glm->ofsSurfHierarchy, glm->numSurfaces, sizeof( glmSurfHierarchy_t ) ) ) { return qfalse; } // swap the surf Hierarchy! surfh = (glmSurfHierarchy_t *) ( (byte *)glm + glm->ofsSurfHierarchy ); for( surfindex = 0; surfindex < glm->numSurfaces; surfindex++ ) { Q_strlwr( surfh->name ); LL(surfh->flags); Q_strlwr( surfh->shader ); //ri.Printf( PRINT_ALL, "surf %d, name '%s' shader '%s'\n", surfindex, surfh->name, surfh->shader ); sh = R_FindShader( surfh->shader, LIGHTMAP_NONE, qtrue ); if ( sh->defaultShader ) { surfh->shaderIndex = 0; } else { LL(surfh->shaderIndex); surfh->shaderIndex = sh->index; } LL(surfh->parentIndex); LL(surfh->numChildren); for( i = 0; i < surfh->numChildren; i++ ) { LL(surfh->childIndexes[i]); } surfh = (glmSurfHierarchy_t *) ( (byte *)&surfh->childIndexes[surfh->numChildren] ); } if ( GLM_CheckRange( glm, glm->ofsLODs, glm->numLODs, sizeof( glmLOD_t ) ) ) { return qfalse; } // swap all the LOD's lod = (glmLOD_t *) ( (byte *)glm + glm->ofsLODs ); LL( lod->ofsEnd ); //ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name); //ri.Printf (PRINT_WARNING,"lod ofsEnd is 0x%08x\n", lod->ofsEnd ); for ( lodindex = 0 ; lodindex < glm->numLODs ; lodindex++ ) { lod_surf_ofs = ( glmLODSurfOffset_t *) ((byte *)lod + 4 ); for( i = 0; i < glm->numSurfaces; i++ ) { LL(lod_surf_ofs->offsets[i]); //lod_surf_ofs->offsets[i] += 4; //printf( "lod %d surf ofs %d is 0x%08x\n", lodindex, lod_surf_ofs->offsets[i] ); } // swap all the surfaces for ( i = 0 ; i < glm->numSurfaces ; i++) { surf = (glmSurface_t *) ( (byte *)lod_surf_ofs + lod_surf_ofs->offsets[i] ); LL(surf->ident); LL(surf->thisSurfaceIndex); LL(surf->ofsHeader); LL(surf->numVerts); LL(surf->ofsVerts); LL(surf->numTriangles); LL(surf->ofsTriangles); LL(surf->numBoneReferences); LL(surf->ofsBoneReferences); LL(surf->ofsEnd); if(0) ri.Printf( PRINT_ALL, "surf %d, ident %d, index %d, numverts %d, numtris %d, num bone refs %d\n", i, surf->ident, surf->thisSurfaceIndex, surf->numVerts, surf->numTriangles, surf->numBoneReferences ); // change to surface identifier surf->ident = SF_GLM; tri = (glmTriangle_t *) ( (byte *)surf + surf->ofsBoneReferences ); for( j=0; j<surf->numBoneReferences; j++ ) { LL(tri->indexes[j]); if(0) ri.Printf( PRINT_ALL, "j is %d\n", j ); if(0) ri.Printf( PRINT_ALL, "surf %d, boneref %d is %d\n", i, j, tri->indexes[j] ); } if(0) ri.Printf( PRINT_ALL, "swap the triangles!\n" ); // swap all the triangles tri = (glmTriangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++ ) { LL(tri->indexes[0]); LL(tri->indexes[1]); LL(tri->indexes[2]); tri = (glmTriangle_t *)((byte *)tri + 12 ); } v = (glmVertex_t *) ( (byte *)surf + surf->ofsVerts); for ( j = 0 ; j < surf->numVerts ; j++ ) { v->normal[0] = LittleFloat( v->normal[0] ); v->normal[1] = LittleFloat( v->normal[1] ); v->normal[2] = LittleFloat( v->normal[2] ); v->vertCoords[0] = LittleFloat( v->vertCoords[0] ); v->vertCoords[1] = LittleFloat( v->vertCoords[1] ); v->vertCoords[2] = LittleFloat( v->vertCoords[2] ); if( do_zrot ) { VectorCopy( v->normal, tempNorm ); VectorCopy( v->vertCoords, tempVert ); Matrix34VectorRotate( zrots, tempVert, v->vertCoords ); Matrix34VectorRotate( zrots, tempNorm, v->normal ); } LL(v->uiNmWeightsAndBoneIndexes); //v->texCoords[0] = LittleFloat( v->texCoords[0] ); //v->texCoords[1] = LittleFloat( v->texCoords[1] ); /* v->numWeights = LittleLong( v->numWeights ); for ( k = 0 ; k < v->numWeights ; k++ ) { v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex ); v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight ); v->weights[k].offset[0] = LittleFloat( v->weights[k].offset[0] ); v->weights[k].offset[1] = LittleFloat( v->weights[k].offset[1] ); v->weights[k].offset[2] = LittleFloat( v->weights[k].offset[2] ); } */ v = (glmVertex_t *)( ( byte *)v + 32 ); } vcor = (glmVertexTexCoord_t *)( ( byte *)v ); for ( j = 0 ; j < surf->numVerts ; j++ ) { vcor->texCoords[0] = LittleFloat( vcor->texCoords[0] ); vcor->texCoords[1] = LittleFloat( vcor->texCoords[1] ); vcor = (glmVertexTexCoord_t *)( ( byte *)vcor + 8 ); } } lod = (glmLOD_t *)( (byte *)lod + lod->ofsEnd ); } glm->animIndex = 0; if(0) ri.Printf( PRINT_DEVELOPER, "glm (%s) animname is '%s'\n", mod_name, glm->animName ); if( Q_strncmp( glm->animName, sDEFAULT_GLA_NAME, strlen(sDEFAULT_GLA_NAME) ) ) //if there's a difference { Q_strcat( glm->animName, MAX_QPATH, ".gla" ); //Com_DPrintf ( "glm: trying to load animfile: '%s'\n", glm->animName ); //ri.Printf( PRINT_ALL, "glm: trying to load animfile: '%s'\n", glm->animName ); glm->animIndex = RE_RegisterModel( glm->animName ); } else { glm->animIndex = 0; } return qtrue; }
/* ==================== RE_BeginFrame If running in stereo, RE_BeginFrame will be called twice for each RE_EndFrame ==================== */ void RE_BeginFrame( stereoFrame_t stereoFrame ) { drawBufferCommand_t *cmd; if ( !tr.registered ) { return; } glState.finishCalled = qfalse; tr.frameCount++; tr.frameSceneNum = 0; backEnd.traceShader = tr.traceShader; backEnd.doneBloom = qfalse; backEnd.doneSurfaces = qfalse; backEnd.sceneZfar = 2048; tr.traceShader = qfalse; // // do overdraw measurement // if ( r_measureOverdraw->integer ) { if ( glConfig.stencilBits < 4 ) { ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); ri.Cvar_Set( "r_measureOverdraw", "0" ); r_measureOverdraw->modified = qfalse; } else if ( r_shadows->integer == 2 ) { ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" ); ri.Cvar_Set( "r_measureOverdraw", "0" ); r_measureOverdraw->modified = qfalse; } else if ( mme_saveStencil->integer ) { ri.Printf( PRINT_ALL, "Warning: mme stencil masks and overdraw measurement are mutually exclusive\n" ); ri.Cvar_Set( "r_measureOverdraw", "0" ); r_measureOverdraw->modified = qfalse; } else { R_SyncRenderThread(); qglEnable( GL_STENCIL_TEST ); qglStencilMask( ~0U ); qglClearStencil( 0U ); qglStencilFunc( GL_ALWAYS, 0U, ~0U ); qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); } r_measureOverdraw->modified = qfalse; } else { // this is only reached if it was on and is now off if ( r_measureOverdraw->modified ) { R_SyncRenderThread(); qglDisable( GL_STENCIL_TEST ); } r_measureOverdraw->modified = qfalse; } if ( mme_saveStencil->integer == 1 ) { R_SyncRenderThread(); qglEnable( GL_STENCIL_TEST ); qglStencilMask( ~0U ); qglClearStencil( 0U ); qglStencilFunc( GL_ALWAYS, 255U, 255U ); qglStencilOp( GL_KEEP, GL_KEEP, GL_ZERO ); backEnd.doingStencil = qfalse; } else if ( mme_saveStencil->integer == 2 ) { R_SyncRenderThread(); qglEnable( GL_STENCIL_TEST ); qglStencilMask( ~0U ); qglClearStencil( 0U ); qglStencilFunc( GL_ALWAYS, 255U, 255U ); qglStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE ); backEnd.doingStencil = qfalse; } else { qglDisable( GL_STENCIL_TEST ); } // // texturemode stuff // if ( r_textureMode->modified ) { R_SyncRenderThread(); GL_TextureMode( r_textureMode->string ); r_textureMode->modified = qfalse; } if ( r_anisotropy->modified ) { R_SyncRenderThread(); GL_Anisotropy( r_anisotropy->integer ); r_anisotropy->modified = qfalse; } // // gamma stuff // if ( r_gamma->modified ) { r_gamma->modified = qfalse; R_SyncRenderThread(); R_SetColorMappings(); } // check for errors if ( !r_ignoreGLErrors->integer ) { int err; R_SyncRenderThread(); if ( ( err = qglGetError() ) != GL_NO_ERROR ) { ri.Error( ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err ); } } if ( mme_worldShader->modified) { if (R_FindShaderText( mme_worldShader->string )) { tr.mmeWorldShader = R_FindShader( mme_worldShader->string, LIGHTMAP_NONE, qtrue ); } else { tr.mmeWorldShader = 0; } mme_worldShader->modified = qfalse; } if ( mme_pip->integer ) { tr.mmeWorldShader = R_FindShader( "mme/pip", -1, qtrue ); } // // draw buffer stuff // cmd = R_GetCommandBuffer( RC_DRAW_BUFFER, sizeof( *cmd ) ); if ( !cmd ) { return; } if ( glConfig.stereoEnabled ) { if ( stereoFrame == STEREO_LEFT ) { cmd->buffer = (int)GL_BACK_LEFT; } else if ( stereoFrame == STEREO_RIGHT ) { cmd->buffer = (int)GL_BACK_RIGHT; } else { ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); } } else { if ( stereoFrame != STEREO_CENTER ) { ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame ); } if ( !Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) ) { cmd->buffer = (int)GL_FRONT; } else { cmd->buffer = (int)GL_BACK; } } }
// given a name, go get the skin we want and return qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin) { skin_t *skin; skinSurface_t *surf; char *text, *text_p; char *token; char surfName[MAX_QPATH]; // load and parse the skin file ri->FS_ReadFile( name, (void **)&text ); if ( !text ) { #ifndef FINAL_BUILD Com_Printf( "WARNING: RE_RegisterSkin( '%s' ) failed to load!\n", name ); #endif return 0; } assert (tr.skins[hSkin]); //should already be setup, but might be an 3part append skin = tr.skins[hSkin]; text_p = text; while ( text_p && *text_p ) { // get surface name token = CommaParse( &text_p ); Q_strncpyz( surfName, token, sizeof( surfName ) ); if ( !token[0] ) { break; } // lowercase the surface name so skin compares are faster Q_strlwr( surfName ); if ( *text_p == ',' ) { text_p++; } if ( !strncmp( token, "tag_", 4 ) ) { //these aren't in there, but just in case you load an id style one... continue; } // parse the shader name token = CommaParse( &text_p ); if ( !strcmp( &surfName[strlen(surfName)-4], "_off") ) { if ( !strcmp( token ,"*off" ) ) { continue; //don't need these double offs } surfName[strlen(surfName)-4] = 0; //remove the "_off" } if ( (unsigned)skin->numSurfaces >= ARRAY_LEN( skin->surfaces ) ) { assert( ARRAY_LEN( skin->surfaces ) > (unsigned)skin->numSurfaces ); Com_Printf( "WARNING: RE_RegisterSkin( '%s' ) more than %d surfaces!\n", name, ARRAY_LEN( skin->surfaces ) ); break; } surf = (skinSurface_t *) Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); skin->surfaces[skin->numSurfaces] = (_skinSurface_t *)surf; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); if (gServerSkinHack) surf->shader = R_FindServerShader( token, lightmapsNone, stylesDefault, qtrue ); else surf->shader = R_FindShader( token, lightmapsNone, stylesDefault, qtrue ); skin->numSurfaces++; } ri->FS_FreeFile( text ); // never let a skin have 0 shaders if ( skin->numSurfaces == 0 ) { return 0; // use default skin } return hSkin; }
/* ================= R_LoadMD5 ================= */ qboolean R_LoadMD5( model_t *mod, void *buffer, int bufferSize, const char *modName ) { int i, j, k; md5Model_t *md5; md5Bone_t *bone; md5Surface_t *surf; md5Triangle_t *tri; md5Vertex_t *v; md5Weight_t *weight; int version; shader_t *sh; char *buf_p; char *token; vec3_t boneOrigin; quat_t boneQuat; matrix_t boneMat; buf_p = ( char * ) buffer; // skip MD5Version indent string COM_ParseExt2( &buf_p, qfalse ); // check version token = COM_ParseExt2( &buf_p, qfalse ); version = atoi( token ); if ( version != MD5_VERSION ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: %s has wrong version (%i should be %i)\n", modName, version, MD5_VERSION ); return qfalse; } mod->type = MOD_MD5; mod->dataSize += sizeof( md5Model_t ); md5 = mod->model.md5 = ri.Hunk_Alloc( sizeof( md5Model_t ), h_low ); // skip commandline <arguments string> token = COM_ParseExt2( &buf_p, qtrue ); token = COM_ParseExt2( &buf_p, qtrue ); // ri.Printf(PRINT_ALL, "%s\n", token); // parse numJoints <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numJoints" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numJoints' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); md5->numBones = atoi( token ); // parse numMeshes <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numMeshes" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numMeshes' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); md5->numSurfaces = atoi( token ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); if ( md5->numBones < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has no bones\n", modName ); return qfalse; } if ( md5->numBones > MAX_BONES ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones ); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i bones\n", modName, md5->numBones); // parse all the bones md5->bones = ri.Hunk_Alloc( sizeof( *bone ) * md5->numBones, h_low ); // parse joints { token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "joints" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'joints' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "{" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( i = 0, bone = md5->bones; i < md5->numBones; i++, bone++ ) { token = COM_ParseExt2( &buf_p, qtrue ); Q_strncpyz( bone->name, token, sizeof( bone->name ) ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s'\n", modName, bone->name); token = COM_ParseExt2( &buf_p, qfalse ); bone->parentIndex = atoi( token ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s' with parent index %i\n", modName, bone->name, bone->parentIndex); if ( bone->parentIndex >= md5->numBones ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has bone '%s' with bad parent index %i while numBones is %i\n", modName, bone->name, bone->parentIndex, md5->numBones ); } // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( j = 0; j < 3; j++ ) { token = COM_ParseExt2( &buf_p, qfalse ); boneOrigin[ j ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( j = 0; j < 3; j++ ) { token = COM_ParseExt2( &buf_p, qfalse ); boneQuat[ j ] = atof( token ); } QuatCalcW( boneQuat ); MatrixFromQuat( boneMat, boneQuat ); VectorCopy( boneOrigin, bone->origin ); QuatCopy( boneQuat, bone->rotation ); MatrixSetupTransformFromQuat( bone->inverseTransform, boneQuat, boneOrigin ); MatrixInverse( bone->inverseTransform ); // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } } // parse } token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "}" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName ); return qfalse; } // parse all the surfaces if ( md5->numSurfaces < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has no surfaces\n", modName ); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); md5->surfaces = ri.Hunk_Alloc( sizeof( *surf ) * md5->numSurfaces, h_low ); for ( i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++ ) { // parse mesh { token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "mesh" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'mesh' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "{" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName ); return qfalse; } // change to surface identifier surf->surfaceType = SF_MD5; // give pointer to model for Tess_SurfaceMD5 surf->model = md5; // parse shader <name> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "shader" ) ) { Q_strncpyz( surf->shader, "<default>", sizeof( surf->shader ) ); surf->shaderIndex = 0; } else { token = COM_ParseExt2( &buf_p, qfalse ); Q_strncpyz( surf->shader, token, sizeof( surf->shader ) ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' uses shader '%s'\n", modName, surf->shader); // FIXME .md5mesh meshes don't have surface names // lowercase the surface name so skin compares are faster //Q_strlwr(surf->name); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has surface '%s'\n", modName, surf->name); // register the shaders sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue ); if ( sh->defaultShader ) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } token = COM_ParseExt2( &buf_p, qtrue ); } // parse numVerts <number> if ( Q_stricmp( token, "numVerts" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numVerts' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numVerts = atoi( token ); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, surf->numVerts ); } surf->verts = ri.Hunk_Alloc( sizeof( *v ) * surf->numVerts, h_low ); for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { // skip vert <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "vert" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'vert' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( k = 0; k < 2; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); v->texCoords[ k ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); v->firstWeight = atoi( token ); token = COM_ParseExt2( &buf_p, qfalse ); v->numWeights = atoi( token ); if ( v->numWeights > MAX_WEIGHTS ) { ri.Error( ERR_DROP, "R_LoadMD5: vertex %i requires more than %i weights on surface (%i) in model '%s'", j, MAX_WEIGHTS, i, modName ); } } // parse numTris <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numTris" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numTris' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numTriangles = atoi( token ); if ( surf->numTriangles > SHADER_MAX_TRIANGLES ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, surf->numTriangles ); } surf->triangles = ri.Hunk_Alloc( sizeof( *tri ) * surf->numTriangles, h_low ); for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { // skip tri <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "tri" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'tri' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); for ( k = 0; k < 3; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); tri->indexes[ k ] = atoi( token ); } } // parse numWeights <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numWeights" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numWeights' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numWeights = atoi( token ); surf->weights = ri.Hunk_Alloc( sizeof( *weight ) * surf->numWeights, h_low ); for ( j = 0, weight = surf->weights; j < surf->numWeights; j++, weight++ ) { // skip weight <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "weight" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'weight' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); token = COM_ParseExt2( &buf_p, qfalse ); weight->boneIndex = atoi( token ); token = COM_ParseExt2( &buf_p, qfalse ); weight->boneWeight = atof( token ); // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( k = 0; k < 3; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); weight->offset[ k ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } } // parse } token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "}" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName ); return qfalse; } // loop trough all vertices and set up the vertex weights for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { v->weights = ri.Hunk_Alloc( sizeof( *v->weights ) * v->numWeights, h_low ); for ( k = 0; k < v->numWeights; k++ ) { v->weights[ k ] = surf->weights + ( v->firstWeight + k ); } } } // loading is done now calculate the bounding box and tangent spaces ClearBounds( md5->bounds[ 0 ], md5->bounds[ 1 ] ); for ( i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++ ) { for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { vec3_t tmpVert; md5Weight_t *w; VectorClear( tmpVert ); for ( k = 0, w = v->weights[ 0 ]; k < v->numWeights; k++, w++ ) { vec3_t offsetVec; bone = &md5->bones[ w->boneIndex ]; QuatTransformVector( bone->rotation, w->offset, offsetVec ); VectorAdd( bone->origin, offsetVec, offsetVec ); VectorMA( tmpVert, w->boneWeight, offsetVec, tmpVert ); } VectorCopy( tmpVert, v->position ); AddPointToBounds( tmpVert, md5->bounds[ 0 ], md5->bounds[ 1 ] ); } // calc normals { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t normal; for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { VectorClear( v->tangent ); VectorClear( v->binormal ); VectorClear( v->normal ); } for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { v0 = surf->verts[ tri->indexes[ 0 ] ].position; v1 = surf->verts[ tri->indexes[ 1 ] ].position; v2 = surf->verts[ tri->indexes[ 2 ] ].position; t0 = surf->verts[ tri->indexes[ 0 ] ].texCoords; t1 = surf->verts[ tri->indexes[ 1 ] ].texCoords; t2 = surf->verts[ tri->indexes[ 2 ] ].texCoords; R_CalcNormalForTriangle( normal, v0, v1, v2 ); for ( k = 0; k < 3; k++ ) { float *v; v = surf->verts[ tri->indexes[ k ] ].normal; VectorAdd( v, normal, v ); } } for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { VectorNormalize( v->normal ); } } #if 0 // do another extra smoothing for normals to avoid flat shading for ( j = 0; j < surf->numVerts; j++ ) { for ( k = 0; k < surf->numVerts; k++ ) { if ( j == k ) { continue; } if ( VectorCompare( surf->verts[ j ].position, surf->verts[ k ].position ) ) { VectorAdd( surf->verts[ j ].normal, surf->verts[ k ].normal, surf->verts[ j ].normal ); } } VectorNormalize( surf->verts[ j ].normal ); } #endif } return qtrue; }
/* ================= R_LoadMD3 ================= */ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name, qboolean bAlreadyCached ) { int i, j; md3Header_t *pinmodel; md3Surface_t *surf; md3Shader_t *shader; int version; int size; #ifndef _M_IX86 md3Frame_t *frame; md3Triangle_t *tri; md3St_t *st; md3XyzNormal_t *xyz; md3Tag_t *tag; #endif pinmodel= (md3Header_t *)buffer; // // read some fields from the binary, but only LittleLong() them when we know this wasn't an already-cached model... // version = pinmodel->version; size = pinmodel->ofsEnd; if (!bAlreadyCached) { version = LittleLong(version); size = LittleLong(size); } if (version != MD3_VERSION) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", mod_name, version, MD3_VERSION); return qfalse; } mod->type = MOD_MESH; mod->dataSize += size; qboolean bAlreadyFound = qfalse; mod->md3[lod] = (md3Header_t *) //ri.Hunk_Alloc( size ); RE_RegisterModels_Malloc(size, mod_name, &bAlreadyFound, TAG_MODEL_MD3); assert(bAlreadyCached == bAlreadyFound); // I should probably eliminate 'bAlreadyFound', but wtf? if (!bAlreadyFound) { memcpy (mod->md3[lod], buffer, size ); LL(mod->md3[lod]->ident); LL(mod->md3[lod]->version); LL(mod->md3[lod]->numFrames); LL(mod->md3[lod]->numTags); LL(mod->md3[lod]->numSurfaces); LL(mod->md3[lod]->ofsFrames); LL(mod->md3[lod]->ofsTags); LL(mod->md3[lod]->ofsSurfaces); LL(mod->md3[lod]->ofsEnd); } if ( mod->md3[lod]->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name ); return qfalse; } if (bAlreadyFound) { return qtrue; // All done. Stop, go no further, do not pass Go... } #ifndef _M_IX86 // // optimisation, we don't bother doing this for standard intel case since our data's already in that format... // // swap all the frames frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames ); for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) { frame->radius = LittleFloat( frame->radius ); for ( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); } } // swap all the tags tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags ); for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) { for ( j = 0 ; j < 3 ; j++ ) { tag->origin[j] = LittleFloat( tag->origin[j] ); tag->axis[0][j] = LittleFloat( tag->axis[0][j] ); tag->axis[1][j] = LittleFloat( tag->axis[1][j] ); tag->axis[2][j] = LittleFloat( tag->axis[2][j] ); } } #endif // swap all the surfaces surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { LL(surf->flags); LL(surf->numFrames); LL(surf->numShaders); LL(surf->numTriangles); LL(surf->ofsTriangles); LL(surf->numVerts); LL(surf->ofsShaders); LL(surf->ofsSt); LL(surf->ofsXyzNormals); LL(surf->ofsEnd); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // change to surface identifier surf->ident = SF_MD3; // lowercase the surface name so skin compares are faster Q_strlwr( surf->name ); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess j = strlen( surf->name ); if ( j > 2 && surf->name[j-2] == '_' ) { surf->name[j-2] = 0; } // register the shaders shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { shader_t *sh; sh = R_FindShader( shader->name, lightmapsNone, stylesDefault, qtrue ); if ( sh->defaultShader ) { shader->shaderIndex = 0; } else { shader->shaderIndex = sh->index; } RE_RegisterModels_StoreShaderRequest(mod_name, &shader->name[0], &shader->shaderIndex); } #ifndef _M_IX86 // // optimisation, we don't bother doing this for standard intel case since our data's already in that format... // // swap all the triangles tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { LL(tri->indexes[0]); LL(tri->indexes[1]); LL(tri->indexes[2]); } // swap all the ST st = (md3St_t *) ( (byte *)surf + surf->ofsSt ); for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { st->st[0] = LittleFloat( st->st[0] ); st->st[1] = LittleFloat( st->st[1] ); } // swap all the XyzNormals xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals ); for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) { xyz->xyz[0] = LittleShort( xyz->xyz[0] ); xyz->xyz[1] = LittleShort( xyz->xyz[1] ); xyz->xyz[2] = LittleShort( xyz->xyz[2] ); xyz->normal = LittleShort( xyz->normal ); } #endif // find the next surface surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); } return qtrue; }
void R_RMGInit(void) { char newSky[MAX_QPATH]; char newFog[MAX_QPATH]; shader_t *fog; fog_t *gfog; mgrid_t *grid; char temp[MAX_QPATH]; int i; unsigned short *pos; ri.Cvar_VariableStringBuffer("RMG_sky", newSky, MAX_QPATH); // Get sunlight - this should set up all the sunlight data R_FindShader( newSky, lightmapsNone, stylesDefault, qfalse ); // Remap sky R_RemapShader("textures/tools/_sky", newSky, NULL); // Fill in the lightgrid with sunlight if(tr.world->lightGridData) { grid = tr.world->lightGridData; grid->ambientLight[0][0] = (byte)Com_Clampi(0, 255, tr.sunAmbient[0] * 255.0f); grid->ambientLight[0][1] = (byte)Com_Clampi(0, 255, tr.sunAmbient[1] * 255.0f); grid->ambientLight[0][2] = (byte)Com_Clampi(0, 255, tr.sunAmbient[2] * 255.0f); R_ColorShiftLightingBytes(grid->ambientLight[0], grid->ambientLight[0]); grid->directLight[0][0] = (byte)Com_Clampi(0, 255, tr.sunLight[0]); grid->directLight[0][1] = (byte)Com_Clampi(0, 255, tr.sunLight[1]); grid->directLight[0][2] = (byte)Com_Clampi(0, 255, tr.sunLight[2]); R_ColorShiftLightingBytes(grid->directLight[0], grid->directLight[0]); NormalToLatLong(tr.sunDirection, grid->latLong); pos = tr.world->lightGridArray; for(i=0;i<tr.world->numGridArrayElements;i++) { *pos = 0; pos++; } } // Override the global fog with the defined one if(tr.world->globalFog != -1) { ri.Cvar_VariableStringBuffer("RMG_fog", newFog, MAX_QPATH); fog = R_FindShader( newFog, lightmapsNone, stylesDefault, qfalse); if (fog != tr.defaultShader) { gfog = tr.world->fogs + tr.world->globalFog; gfog->parms = *fog->fogParms; if (gfog->parms.depthForOpaque) { gfog->tcScale = 1.0f / ( gfog->parms.depthForOpaque * 8.0f ); tr.distanceCull = gfog->parms.depthForOpaque; tr.distanceCullSquared = tr.distanceCull * tr.distanceCull; ri.Cvar_Set("RMG_distancecull", va("%f", tr.distanceCull)); } else { gfog->tcScale = 1.0f; } gfog->colorInt = ColorBytes4 ( gfog->parms.color[0], gfog->parms.color[1], gfog->parms.color[2], 1.0f ); } } ri.Cvar_VariableStringBuffer("RMG_weather", temp, MAX_QPATH); // Set up any weather effects switch(atol(temp)) { case 0: break; case 1: RE_WorldEffectCommand("rain init 1000"); RE_WorldEffectCommand("rain outside"); break; case 2: RE_WorldEffectCommand("snow init 1000 outside"); RE_WorldEffectCommand("snow outside"); break; } }
/* ================= R_LoadMD3 ================= */ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name, qboolean &bAlreadyCached ) { int i, j; md3Header_t *pinmodel; md3Surface_t *surf; md3Shader_t *shader; int version; int size; #ifdef Q3_BIG_ENDIAN md3Frame_t *frame; md3Triangle_t *tri; md3St_t *st; md3XyzNormal_t *xyz; md3Tag_t *tag; #endif pinmodel= (md3Header_t *)buffer; // // read some fields from the binary, but only LittleLong() them when we know this wasn't an already-cached model... // version = pinmodel->version; size = pinmodel->ofsEnd; if (!bAlreadyCached) { version = LittleLong(version); size = LittleLong(size); } if (version != MD3_VERSION) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", mod_name, version, MD3_VERSION); return qfalse; } mod->type = MOD_MESH; mod->dataSize += size; qboolean bAlreadyFound = qfalse; mod->md3[lod] = (md3Header_t *) RE_RegisterModels_Malloc(size, buffer, mod_name, &bAlreadyFound, TAG_MODEL_MD3); assert(bAlreadyCached == bAlreadyFound); if (!bAlreadyFound) { // horrible new hackery, if !bAlreadyFound then we've just done a tag-morph, so we need to set the // bool reference passed into this function to true, to tell the caller NOT to do an FS_Freefile since // we've hijacked that memory block... // // Aaaargh. Kill me now... // bAlreadyCached = qtrue; assert( mod->md3[lod] == buffer ); // memcpy( mod->md3[lod], buffer, size ); // and don't do this now, since it's the same thing LL(mod->md3[lod]->ident); LL(mod->md3[lod]->version); LL(mod->md3[lod]->numFrames); LL(mod->md3[lod]->numTags); LL(mod->md3[lod]->numSurfaces); LL(mod->md3[lod]->ofsFrames); LL(mod->md3[lod]->ofsTags); LL(mod->md3[lod]->ofsSurfaces); LL(mod->md3[lod]->ofsEnd); } if ( mod->md3[lod]->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name ); return qfalse; } if (bAlreadyFound) { return qtrue; // All done. Stop, go no further, do not pass Go... } #ifdef Q3_BIG_ENDIAN // swap all the frames frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames ); for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) { LF(frame->radius); for ( j = 0 ; j < 3 ; j++ ) { LF(frame->bounds[0][j]); LF(frame->bounds[1][j]); LF(frame->localOrigin[j]); } } // swap all the tags tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags ); for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) { for ( j = 0 ; j < 3 ; j++ ) { LF(tag->origin[j]); LF(tag->axis[0][j]); LF(tag->axis[1][j]); LF(tag->axis[2][j]); } } #endif // swap all the surfaces surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { LL(surf->flags); LL(surf->numFrames); LL(surf->numShaders); LL(surf->numTriangles); LL(surf->ofsTriangles); LL(surf->numVerts); LL(surf->ofsShaders); LL(surf->ofsSt); LL(surf->ofsXyzNormals); LL(surf->ofsEnd); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { Com_Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { Com_Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // change to surface identifier surf->ident = SF_MD3; // lowercase the surface name so skin compares are faster Q_strlwr( surf->name ); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess j = strlen( surf->name ); if ( j > 2 && surf->name[j-2] == '_' ) { surf->name[j-2] = 0; } // register the shaders shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { shader_t *sh; sh = R_FindShader( shader->name, lightmapsNone, stylesDefault, qtrue ); if ( sh->defaultShader ) { shader->shaderIndex = 0; } else { shader->shaderIndex = sh->index; } RE_RegisterModels_StoreShaderRequest(mod_name, &shader->name[0], &shader->shaderIndex); } #ifdef Q3_BIG_ENDIAN // swap all the triangles tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { LL(tri->indexes[0]); LL(tri->indexes[1]); LL(tri->indexes[2]); } // swap all the ST st = (md3St_t *) ( (byte *)surf + surf->ofsSt ); for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { LF(st->st[0]); LF(st->st[1]); } // swap all the XyzNormals xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals ); for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) { LS(xyz->xyz[0]); LS(xyz->xyz[1]); LS(xyz->xyz[2]); LS(xyz->normal); } #endif // find the next surface surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); } return qtrue; }
/* ============== RE_GetShaderFromModel return a shader index for a given model's surface 'withlightmap' set to '0' will create a new shader that is a copy of the one found on the model, without the lighmap stage, if the shader has a lightmap stage NOTE: only works for bmodels right now. Could modify for other models (md3's etc.) ============== */ qhandle_t RE_GetShaderFromModel( qhandle_t modelid, int surfnum, int withlightmap ) { model_t *model; bmodel_t *bmodel; msurface_t *surf; shader_t *shd; if ( surfnum < 0 ) { surfnum = 0; } model = R_GetModelByHandle( modelid ); // (SA) should be correct now if ( model ) { bmodel = model->model.bmodel; if ( bmodel && bmodel->firstSurface ) { if ( surfnum >= bmodel->numSurfaces ) { // if it's out of range, return the first surface surfnum = 0; } surf = bmodel->firstSurface + surfnum; // RF, check for null shader (can happen on func_explosive entities with botclips attached) if ( !surf->shader ) { return 0; } // if(surf->shader->lightmapIndex != LIGHTMAP_NONE) { if ( surf->shader->lightmapIndex > LIGHTMAP_NONE ) { image_t *image; long hash; qboolean mip = qtrue; // mip generation on by default // get mipmap info for original texture hash = GenerateImageHashValue( surf->shader->name ); for ( image = r_imageHashTable[ hash ]; image; image = image->next ) { if ( !strcmp( surf->shader->name, image->imgName ) ) { mip = image->mipmap; break; } } shd = R_FindShader( surf->shader->name, LIGHTMAP_NONE, mip ); shd->stages[ 0 ]->rgbGen = CGEN_LIGHTING_DIFFUSE; // (SA) new } else { shd = surf->shader; } return shd->index; } } return 0; }
qboolean R_LoadMD5(model_t *mod, void *buffer, int bufferSize, const char *modName) { int i, j, k; md5Model_t *md5; md5Bone_t *bone; md5Surface_t *surf; srfTriangle_t *tri; md5Vertex_t *v; md5Weight_t *weight; int version; shader_t *sh; char *buf_p = ( char * ) buffer; char *token; vec3_t boneOrigin; quat_t boneQuat; matrix_t boneMat; int numRemaining; growList_t sortedTriangles; growList_t vboTriangles; growList_t vboSurfaces; int numBoneReferences; int boneReferences[MAX_BONES]; // skip MD5Version indent string COM_ParseExt2(&buf_p, qfalse); // check version token = COM_ParseExt2(&buf_p, qfalse); version = atoi(token); if (version != MD5_VERSION) { Ren_Warning("R_LoadMD5: %s has wrong version (%i should be %i)\n", modName, version, MD5_VERSION); return qfalse; } mod->type = MOD_MD5; mod->dataSize += sizeof(md5Model_t); md5 = mod->md5 = ri.Hunk_Alloc(sizeof(md5Model_t), h_low); // skip commandline <arguments string> token = COM_ParseExt2(&buf_p, qtrue); token = COM_ParseExt2(&buf_p, qtrue); // Ren_Print("%s\n", token); // parse numJoints <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numJoints")) { Ren_Warning("R_LoadMD5: expected 'numJoints' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); md5->numBones = atoi(token); // parse numMeshes <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numMeshes")) { Ren_Warning("R_LoadMD5: expected 'numMeshes' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); md5->numSurfaces = atoi(token); //Ren_Print("R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); if (md5->numBones < 1) { Ren_Warning("R_LoadMD5: '%s' has no bones\n", modName); return qfalse; } if (md5->numBones > MAX_BONES) { Ren_Warning("R_LoadMD5: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones); return qfalse; } //Ren_Print("R_LoadMD5: '%s' has %i bones\n", modName, md5->numBones); // parse all the bones md5->bones = ri.Hunk_Alloc(sizeof(*bone) * md5->numBones, h_low); // parse joints { token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "joints")) { Ren_Warning("R_LoadMD5: expected 'joints' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "{")) { Ren_Warning("R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName); return qfalse; } for (i = 0, bone = md5->bones; i < md5->numBones; i++, bone++) { token = COM_ParseExt2(&buf_p, qtrue); Q_strncpyz(bone->name, token, sizeof(bone->name)); //Ren_Print("R_LoadMD5: '%s' has bone '%s'\n", modName, bone->name); token = COM_ParseExt2(&buf_p, qfalse); bone->parentIndex = atoi(token); //Ren_Print("R_LoadMD5: '%s' has bone '%s' with parent index %i\n", modName, bone->name, bone->parentIndex); if (bone->parentIndex >= md5->numBones) { Ren_Drop("R_LoadMD5: '%s' has bone '%s' with bad parent index %i while numBones is %i", modName, bone->name, bone->parentIndex, md5->numBones); } // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (j = 0; j < 3; j++) { token = COM_ParseExt2(&buf_p, qfalse); boneOrigin[j] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { Ren_Warning("R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (j = 0; j < 3; j++) { token = COM_ParseExt2(&buf_p, qfalse); boneQuat[j] = atof(token); } QuatCalcW(boneQuat); MatrixFromQuat(boneMat, boneQuat); VectorCopy(boneOrigin, bone->origin); QuatCopy(boneQuat, bone->rotation); MatrixSetupTransformFromQuat(bone->inverseTransform, boneQuat, boneOrigin); MatrixInverse(bone->inverseTransform); // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } } // parse } token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "}")) { Ren_Warning("R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName); return qfalse; } // parse all the surfaces if (md5->numSurfaces < 1) { Ren_Warning("R_LoadMD5: '%s' has no surfaces\n", modName); return qfalse; } //Ren_Print("R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); md5->surfaces = ri.Hunk_Alloc(sizeof(*surf) * md5->numSurfaces, h_low); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { // parse mesh { token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "mesh")) { Ren_Warning("R_LoadMD5: expected 'mesh' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "{")) { Ren_Warning("R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName); return qfalse; } // change to surface identifier surf->surfaceType = SF_MD5; // give pointer to model for Tess_SurfaceMD5 surf->model = md5; // parse shader <name> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "shader")) { Ren_Warning("R_LoadMD5: expected 'shader' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); Q_strncpyz(surf->shader, token, sizeof(surf->shader)); //Ren_Print("R_LoadMD5: '%s' uses shader '%s'\n", modName, surf->shader); // FIXME .md5mesh meshes don't have surface names // lowercase the surface name so skin compares are faster //Q_strlwr(surf->name); //Ren_Print("R_LoadMD5: '%s' has surface '%s'\n", modName, surf->name); // register the shaders sh = R_FindShader(surf->shader, SHADER_3D_DYNAMIC, qtrue); if (sh->defaultShader) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } // parse numVerts <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numVerts")) { Ren_Warning("R_LoadMD5: expected 'numVerts' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numVerts = atoi(token); if (surf->numVerts > SHADER_MAX_VERTEXES) { Ren_Drop("R_LoadMD5: '%s' has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, surf->numVerts); } surf->verts = ri.Hunk_Alloc(sizeof(*v) * surf->numVerts, h_low); for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { // skip vert <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "vert")) { Ren_Warning("R_LoadMD5: expected 'vert' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (k = 0; k < 2; k++) { token = COM_ParseExt2(&buf_p, qfalse); v->texCoords[k] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { Ren_Warning("R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); v->firstWeight = atoi(token); token = COM_ParseExt2(&buf_p, qfalse); v->numWeights = atoi(token); if (v->numWeights > MAX_WEIGHTS) { Ren_Drop("R_LoadMD5: vertex %i requires more than %i weights on surface (%i) in model '%s'", j, MAX_WEIGHTS, i, modName); } } // parse numTris <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numTris")) { Ren_Warning("R_LoadMD5: expected 'numTris' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numTriangles = atoi(token); if (surf->numTriangles > SHADER_MAX_TRIANGLES) { Ren_Drop("R_LoadMD5: '%s' has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, surf->numTriangles); } surf->triangles = ri.Hunk_Alloc(sizeof(*tri) * surf->numTriangles, h_low); for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { // skip tri <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "tri")) { Ren_Warning("R_LoadMD5: expected 'tri' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); for (k = 0; k < 3; k++) { token = COM_ParseExt2(&buf_p, qfalse); tri->indexes[k] = atoi(token); } } // parse numWeights <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numWeights")) { Ren_Warning("R_LoadMD5: expected 'numWeights' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numWeights = atoi(token); surf->weights = ri.Hunk_Alloc(sizeof(*weight) * surf->numWeights, h_low); for (j = 0, weight = surf->weights; j < surf->numWeights; j++, weight++) { // skip weight <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "weight")) { Ren_Warning("R_LoadMD5: expected 'weight' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); token = COM_ParseExt2(&buf_p, qfalse); weight->boneIndex = atoi(token); token = COM_ParseExt2(&buf_p, qfalse); weight->boneWeight = atof(token); // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { Ren_Warning("R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (k = 0; k < 3; k++) { token = COM_ParseExt2(&buf_p, qfalse); weight->offset[k] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { Ren_Warning("R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } } // parse } token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "}")) { Ren_Warning("R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName); return qfalse; } // loop trough all vertices and set up the vertex weights for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { v->weights = ri.Hunk_Alloc(sizeof(*v->weights) * v->numWeights, h_low); for (k = 0; k < v->numWeights; k++) { v->weights[k] = surf->weights + (v->firstWeight + k); } } } // loading is done now calculate the bounding box and tangent spaces ClearBounds(md5->bounds[0], md5->bounds[1]); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { vec3_t tmpVert; md5Weight_t *w; VectorClear(tmpVert); for (k = 0, w = v->weights[0]; k < v->numWeights; k++, w++) { vec3_t offsetVec; bone = &md5->bones[w->boneIndex]; QuatTransformVector(bone->rotation, w->offset, offsetVec); VectorAdd(bone->origin, offsetVec, offsetVec); VectorMA(tmpVert, w->boneWeight, offsetVec, tmpVert); } VectorCopy(tmpVert, v->position); AddPointToBounds(tmpVert, md5->bounds[0], md5->bounds[1]); } // calc tangent spaces #if 1 { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { VectorClear(v->tangent); VectorClear(v->binormal); VectorClear(v->normal); } for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { v0 = surf->verts[tri->indexes[0]].position; v1 = surf->verts[tri->indexes[1]].position; v2 = surf->verts[tri->indexes[2]].position; t0 = surf->verts[tri->indexes[0]].texCoords; t1 = surf->verts[tri->indexes[1]].texCoords; t2 = surf->verts[tri->indexes[2]].texCoords; #if 1 R_CalcTangentSpace(tangent, binormal, normal, v0, v1, v2, t0, t1, t2); #else R_CalcNormalForTriangle(normal, v0, v1, v2); R_CalcTangentsForTriangle(tangent, binormal, v0, v1, v2, t0, t1, t2); #endif for (k = 0; k < 3; k++) { float *v; v = surf->verts[tri->indexes[k]].tangent; VectorAdd(v, tangent, v); v = surf->verts[tri->indexes[k]].binormal; VectorAdd(v, binormal, v); v = surf->verts[tri->indexes[k]].normal; VectorAdd(v, normal, v); } } for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { VectorNormalize(v->tangent); VectorNormalize(v->binormal); VectorNormalize(v->normal); } } #else { int k; float bb, s, t; vec3_t bary; vec3_t faceNormal; md5Vertex_t *dv[3]; for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { dv[0] = &surf->verts[tri->indexes[0]]; dv[1] = &surf->verts[tri->indexes[1]]; dv[2] = &surf->verts[tri->indexes[2]]; R_CalcNormalForTriangle(faceNormal, dv[0]->position, dv[1]->position, dv[2]->position); // calculate barycentric basis for the triangle bb = (dv[1]->texCoords[0] - dv[0]->texCoords[0]) * (dv[2]->texCoords[1] - dv[0]->texCoords[1]) - (dv[2]->texCoords[0] - dv[0]->texCoords[0]) * (dv[1]->texCoords[1] - dv[0]->texCoords[1]); if (fabs(bb) < 0.00000001f) { continue; } // do each vertex for (k = 0; k < 3; k++) { // calculate s tangent vector s = dv[k]->texCoords[0] + 10.0f; t = dv[k]->texCoords[1]; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->tangent[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->tangent[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->tangent[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->tangent, dv[k]->position, dv[k]->tangent); VectorNormalize(dv[k]->tangent); // calculate t tangent vector (binormal) s = dv[k]->texCoords[0]; t = dv[k]->texCoords[1] + 10.0f; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->binormal[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->binormal[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->binormal[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->binormal, dv[k]->position, dv[k]->binormal); VectorNormalize(dv[k]->binormal); // calculate the normal as cross product N=TxB #if 0 CrossProduct(dv[k]->tangent, dv[k]->binormal, dv[k]->normal); VectorNormalize(dv[k]->normal); // Gram-Schmidt orthogonalization process for B // compute the cross product B=NxT to obtain // an orthogonal basis CrossProduct(dv[k]->normal, dv[k]->tangent, dv[k]->binormal); if (DotProduct(dv[k]->normal, faceNormal) < 0) { VectorInverse(dv[k]->normal); //VectorInverse(dv[k]->tangent); //VectorInverse(dv[k]->binormal); } #else VectorAdd(dv[k]->normal, faceNormal, dv[k]->normal); #endif } } #if 1 for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { //VectorNormalize(v->tangent); //VectorNormalize(v->binormal); VectorNormalize(v->normal); } #endif } #endif #if 0 // do another extra smoothing for normals to avoid flat shading for (j = 0; j < surf->numVerts; j++) { for (k = 0; k < surf->numVerts; k++) { if (j == k) { continue; } if (VectorCompare(surf->verts[j].position, surf->verts[k].position)) { VectorAdd(surf->verts[j].normal, surf->verts[k].normal, surf->verts[j].normal); } } VectorNormalize(surf->verts[j].normal); } #endif } // split the surfaces into VBO surfaces by the maximum number of GPU vertex skinning bones Com_InitGrowList(&vboSurfaces, 10); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { // sort triangles Com_InitGrowList(&sortedTriangles, 1000); for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { skelTriangle_t *sortTri = Com_Allocate(sizeof(*sortTri)); for (k = 0; k < 3; k++) { sortTri->indexes[k] = tri->indexes[k]; sortTri->vertexes[k] = &surf->verts[tri->indexes[k]]; } sortTri->referenced = qfalse; Com_AddToGrowList(&sortedTriangles, sortTri); } //qsort(sortedTriangles.elements, sortedTriangles.currentElements, sizeof(void *), CompareTrianglesByBoneReferences); #if 0 for (j = 0; j < sortedTriangles.currentElements; j++) { int b[MAX_WEIGHTS * 3]; skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); for (k = 0; k < 3; k++) { v = sortTri->vertexes[k]; for (l = 0; l < MAX_WEIGHTS; l++) { b[k * 3 + l] = (l < v->numWeights) ? v->weights[l]->boneIndex : 9999; } qsort(b, MAX_WEIGHTS * 3, sizeof(int), CompareBoneIndices); //Ren_Print("bone indices: %i %i %i %i\n", b[k * 3 + 0], b[k * 3 + 1], b[k * 3 + 2], b[k * 3 + 3]); } } #endif numRemaining = sortedTriangles.currentElements; while (numRemaining) { numBoneReferences = 0; Com_Memset(boneReferences, 0, sizeof(boneReferences)); Com_InitGrowList(&vboTriangles, 1000); for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); if (sortTri->referenced) { continue; } if (AddTriangleToVBOTriangleList(&vboTriangles, sortTri, &numBoneReferences, boneReferences)) { sortTri->referenced = qtrue; } } if (!vboTriangles.currentElements) { Ren_Warning("R_LoadMD5: could not add triangles to a remaining VBO surfaces for model '%s'\n", modName); Com_DestroyGrowList(&vboTriangles); break; } AddSurfaceToVBOSurfacesList(&vboSurfaces, &vboTriangles, md5, surf, i, numBoneReferences, boneReferences); numRemaining -= vboTriangles.currentElements; Com_DestroyGrowList(&vboTriangles); } for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); Com_Dealloc(sortTri); } Com_DestroyGrowList(&sortedTriangles); } // move VBO surfaces list to hunk md5->numVBOSurfaces = vboSurfaces.currentElements; md5->vboSurfaces = ri.Hunk_Alloc(md5->numVBOSurfaces * sizeof(*md5->vboSurfaces), h_low); for (i = 0; i < md5->numVBOSurfaces; i++) { md5->vboSurfaces[i] = ( srfVBOMD5Mesh_t * ) Com_GrowListElement(&vboSurfaces, i); } Com_DestroyGrowList(&vboSurfaces); return qtrue; }
/* ================= R_LoadIQModel Load an IQM model and compute the joint matrices for every frame. ================= */ bool R_LoadIQModel( 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; iqmAnim_t *anim; unsigned short *framedata; char *str, *name; int len; transform_t *trans, *poses; float *bounds; size_t size, len_names; IQModel_t *IQModel; IQAnim_t *IQAnim; srfIQModel_t *surface; vboData_t vboData; float *weightbuf; int *indexbuf; i16vec4_t *qtangentbuf; VBO_t *vbo; IBO_t *ibo; void *ptr; u8vec4_t *weights; if( !LoadIQMFile( buffer, filesize, mod_name, &len_names ) ) { return false; } header = (iqmHeader_t *)buffer; // compute required space size = sizeof(IQModel_t); size += header->num_meshes * sizeof( srfIQModel_t ); size += header->num_anims * sizeof( IQAnim_t ); size += header->num_joints * sizeof( transform_t ); size = PAD( size, 16 ); size += header->num_joints * header->num_frames * sizeof( transform_t ); 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 * 3 * sizeof(float); // normals size += header->num_vertexes * 3 * sizeof(float); // tangents size += header->num_vertexes * 3 * sizeof(float); // bitangents size += header->num_vertexes * 2 * sizeof(int16_t); // texcoords 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_triangles * 3 * sizeof(int); // triangles size += header->num_joints * sizeof(int); // parents size += len_names; // joint and anim names IQModel = (IQModel_t *)ri.Hunk_Alloc( size, ha_pref::h_low ); mod->type = modtype_t::MOD_IQM; mod->iqm = IQModel; ptr = IQModel + 1; // fill header and setup pointers IQModel->num_vertexes = header->num_vertexes; IQModel->num_triangles = header->num_triangles; IQModel->num_frames = header->num_frames; IQModel->num_surfaces = header->num_meshes; IQModel->num_joints = header->num_joints; IQModel->num_anims = header->num_anims; IQModel->surfaces = (srfIQModel_t *)ptr; ptr = IQModel->surfaces + header->num_meshes; if( header->ofs_anims ) { IQModel->anims = (IQAnim_t *)ptr; ptr = IQModel->anims + header->num_anims; } else { IQModel->anims = nullptr; } IQModel->joints = (transform_t *)PADP(ptr, 16); ptr = IQModel->joints + header->num_joints; if( header->ofs_poses ) { poses = (transform_t *)ptr; ptr = poses + header->num_poses * header->num_frames; } else { poses = nullptr; } if( header->ofs_bounds ) { bounds = (float *)ptr; ptr = bounds + 6 * header->num_frames; } else { bounds = nullptr; } IQModel->positions = (float *)ptr; ptr = IQModel->positions + 3 * header->num_vertexes; IQModel->normals = (float *)ptr; ptr = IQModel->normals + 3 * header->num_vertexes; IQModel->tangents = (float *)ptr; ptr = IQModel->tangents + 3 * header->num_vertexes; IQModel->bitangents = (float *)ptr; ptr = IQModel->bitangents + 3 * header->num_vertexes; IQModel->texcoords = (int16_t *)ptr; ptr = IQModel->texcoords + 2 * header->num_vertexes; IQModel->blendIndexes = (byte *)ptr; ptr = IQModel->blendIndexes + 4 * header->num_vertexes; IQModel->blendWeights = (byte *)ptr; ptr = IQModel->blendWeights + 4 * header->num_vertexes; IQModel->colors = (byte *)ptr; ptr = IQModel->colors + 4 * header->num_vertexes; IQModel->jointParents = (int *)ptr; ptr = IQModel->jointParents + header->num_joints; IQModel->triangles = (int *)ptr; ptr = IQModel->triangles + 3 * header->num_triangles; str = (char *)ptr; IQModel->jointNames = str; // copy joint names joint = ( iqmJoint_t* )IQMPtr( header, header->ofs_joints ); for(unsigned i = 0; i < header->num_joints; i++, joint++ ) { name = ( char* )IQMPtr( header, header->ofs_text + joint->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } // setup animations IQAnim = IQModel->anims; anim = ( iqmAnim_t* )IQMPtr( header, header->ofs_anims ); for(int i = 0; i < IQModel->num_anims; i++, IQAnim++, anim++ ) { IQAnim->num_frames = anim->num_frames; IQAnim->framerate = anim->framerate; IQAnim->num_joints = header->num_joints; IQAnim->flags = anim->flags; IQAnim->jointParents = IQModel->jointParents; if( poses ) { IQAnim->poses = poses + anim->first_frame * header->num_poses; } else { IQAnim->poses = nullptr; } if( bounds ) { IQAnim->bounds = bounds + anim->first_frame * 6; } else { IQAnim->bounds = nullptr; } IQAnim->name = str; IQAnim->jointNames = IQModel->jointNames; name = ( char* )IQMPtr( header, header->ofs_text + anim->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } // calculate joint transforms trans = IQModel->joints; joint = ( iqmJoint_t* )IQMPtr( header, header->ofs_joints ); for(unsigned i = 0; i < header->num_joints; i++, joint++, trans++ ) { if( joint->parent >= (int) i ) { Log::Warn("R_LoadIQModel: file %s contains an invalid parent joint number.", mod_name ); return false; } TransInitRotationQuat( joint->rotate, trans ); TransAddScale( joint->scale[0], trans ); TransAddTranslation( joint->translate, trans ); if( joint->parent >= 0 ) { TransCombine( trans, &IQModel->joints[ joint->parent ], trans ); } IQModel->jointParents[i] = joint->parent; } // calculate pose transforms framedata = ( short unsigned int* )IQMPtr( header, header->ofs_frames ); trans = poses; for(unsigned i = 0; i < header->num_frames; i++ ) { pose = ( iqmPose_t* )IQMPtr( header, header->ofs_poses ); for(unsigned j = 0; j < header->num_poses; j++, pose++, trans++ ) { vec3_t translate; quat_t rotate; vec3_t scale; 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]; if( scale[0] < 0.0f || (int)( scale[0] - scale[1] ) || (int)( scale[1] - scale[2] ) ) { Log::Warn("R_LoadIQM: file %s contains an invalid scale.", mod_name ); return false; } // construct transformation TransInitRotationQuat( rotate, trans ); TransAddScale( scale[0], trans ); TransAddTranslation( translate, trans ); } } // copy vertexarrays and indexes vertexarray = ( iqmVertexArray_t* )IQMPtr( header, header->ofs_vertexarrays ); for(unsigned 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: ClearBounds( IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); Com_Memcpy( IQModel->positions, IQMPtr( header, vertexarray->offset ), n * sizeof(float) ); for( int j = 0; j < n; j += vertexarray->size ) { AddPointToBounds( &IQModel->positions[ j ], IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); } IQModel->internalScale = BoundsMaxExtent( IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); if( IQModel->internalScale > 0.0f ) { float inverseScale = 1.0f / IQModel->internalScale; for( int j = 0; j < n; j += vertexarray->size ) { VectorScale( &IQModel->positions[ j ], inverseScale, &IQModel->positions[ j ] ); } } break; case IQM_NORMAL: Com_Memcpy( IQModel->normals, IQMPtr( header, vertexarray->offset ), n * sizeof(float) ); break; case IQM_TANGENT: BuildTangents( header->num_vertexes, ( float* )IQMPtr( header, vertexarray->offset ), IQModel->normals, IQModel->tangents, IQModel->bitangents ); break; case IQM_TEXCOORD: for( int j = 0; j < n; j++ ) { IQModel->texcoords[ j ] = floatToHalf( ((float *)IQMPtr( header, vertexarray->offset ))[ j ] ); } break; case IQM_BLENDINDEXES: Com_Memcpy( IQModel->blendIndexes, IQMPtr( header, vertexarray->offset ), n * sizeof(byte) ); break; case IQM_BLENDWEIGHTS: weights = (u8vec4_t *)IQMPtr( header, vertexarray->offset ); for(unsigned j = 0; j < header->num_vertexes; j++ ) { IQModel->blendWeights[ 4 * j + 0 ] = 255 - weights[ j ][ 1 ] - weights[ j ][ 2 ] - weights[ j ][ 3 ]; IQModel->blendWeights[ 4 * j + 1 ] = weights[ j ][ 1 ]; IQModel->blendWeights[ 4 * j + 2 ] = weights[ j ][ 2 ]; IQModel->blendWeights[ 4 * j + 3 ] = weights[ j ][ 3 ]; } break; case IQM_COLOR: Com_Memcpy( IQModel->colors, IQMPtr( header, vertexarray->offset ), n * sizeof(byte) ); break; } } // copy triangles triangle = ( iqmTriangle_t* )IQMPtr( header, header->ofs_triangles ); for(unsigned i = 0; i < header->num_triangles; i++, triangle++ ) { IQModel->triangles[3*i+0] = triangle->vertex[0]; IQModel->triangles[3*i+1] = triangle->vertex[1]; IQModel->triangles[3*i+2] = triangle->vertex[2]; } // convert data where necessary and create VBO if( r_vboModels->integer && glConfig2.vboVertexSkinningAvailable && IQModel->num_joints <= glConfig2.maxVertexSkinningBones ) { if( IQModel->blendIndexes ) { indexbuf = (int *)ri.Hunk_AllocateTempMemory( sizeof(int[4]) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { indexbuf[ 4 * i + 0 ] = IQModel->blendIndexes[ 4 * i + 0 ]; indexbuf[ 4 * i + 1 ] = IQModel->blendIndexes[ 4 * i + 1 ]; indexbuf[ 4 * i + 2 ] = IQModel->blendIndexes[ 4 * i + 2 ]; indexbuf[ 4 * i + 3 ] = IQModel->blendIndexes[ 4 * i + 3 ]; } } else { indexbuf = nullptr; } if( IQModel->blendWeights ) { const float weightscale = 1.0f / 255.0f; weightbuf = (float *)ri.Hunk_AllocateTempMemory( sizeof(vec4_t) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { if( IQModel->blendWeights[ 4 * i + 0 ] == 0 && IQModel->blendWeights[ 4 * i + 1 ] == 0 && IQModel->blendWeights[ 4 * i + 2 ] == 0 && IQModel->blendWeights[ 4 * i + 3 ] == 0 ) IQModel->blendWeights[ 4 * i + 0 ] = 255; weightbuf[ 4 * i + 0 ] = weightscale * IQModel->blendWeights[ 4 * i + 0 ]; weightbuf[ 4 * i + 1 ] = weightscale * IQModel->blendWeights[ 4 * i + 1 ]; weightbuf[ 4 * i + 2 ] = weightscale * IQModel->blendWeights[ 4 * i + 2 ]; weightbuf[ 4 * i + 3 ] = weightscale * IQModel->blendWeights[ 4 * i + 3 ]; } } else { weightbuf = nullptr; } qtangentbuf = (i16vec4_t *)ri.Hunk_AllocateTempMemory( sizeof( i16vec4_t ) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { R_TBNtoQtangents( &IQModel->tangents[ 3 * i ], &IQModel->bitangents[ 3 * i ], &IQModel->normals[ 3 * i ], qtangentbuf[ i ] ); } vboData.xyz = (vec3_t *)IQModel->positions; vboData.qtangent = qtangentbuf; vboData.numFrames = 0; vboData.color = (u8vec4_t *)IQModel->colors; vboData.st = (i16vec2_t *)IQModel->texcoords; vboData.noLightCoords = true; vboData.boneIndexes = (int (*)[4])indexbuf; vboData.boneWeights = (vec4_t *)weightbuf; vboData.numVerts = IQModel->num_vertexes; vbo = R_CreateStaticVBO( "IQM surface VBO", vboData, vboLayout_t::VBO_LAYOUT_SKELETAL ); if( qtangentbuf ) { ri.Hunk_FreeTempMemory( qtangentbuf ); } if( weightbuf ) { ri.Hunk_FreeTempMemory( weightbuf ); } if( indexbuf ) { ri.Hunk_FreeTempMemory( indexbuf ); } // create IBO ibo = R_CreateStaticIBO( "IQM surface IBO", ( glIndex_t* )IQModel->triangles, IQModel->num_triangles * 3 ); } else { vbo = nullptr; ibo = nullptr; } // register shaders // overwrite the material offset with the shader index mesh = ( iqmMesh_t* )IQMPtr( header, header->ofs_meshes ); surface = IQModel->surfaces; for(unsigned i = 0; i < header->num_meshes; i++, mesh++, surface++ ) { surface->surfaceType = surfaceType_t::SF_IQM; if( mesh->name ) { surface->name = str; name = ( char* )IQMPtr( header, header->ofs_text + mesh->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } else { surface->name = nullptr; } surface->shader = R_FindShader( ( char* )IQMPtr(header, header->ofs_text + mesh->material), shaderType_t::SHADER_3D_DYNAMIC, RSF_DEFAULT ); if( surface->shader->defaultShader ) surface->shader = tr.defaultShader; surface->data = IQModel; surface->first_vertex = mesh->first_vertex; surface->num_vertexes = mesh->num_vertexes; surface->first_triangle = mesh->first_triangle; surface->num_triangles = mesh->num_triangles; surface->vbo = vbo; surface->ibo = ibo; } // copy model bounds if(header->ofs_bounds) { iqmBounds_t *ptr = ( iqmBounds_t* )IQMPtr( header, header->ofs_bounds ); for(unsigned i = 0; i < header->num_frames; i++) { VectorCopy( ptr->bbmin, bounds ); bounds += 3; VectorCopy( ptr->bbmax, bounds ); bounds += 3; ptr++; } } // register animations IQAnim = IQModel->anims; if( header->num_anims == 1 ) { RE_RegisterAnimationIQM( mod_name, IQAnim ); } for(unsigned i = 0; i < header->num_anims; i++, IQAnim++ ) { char name[ MAX_QPATH ]; Com_sprintf( name, MAX_QPATH, "%s:%s", mod_name, IQAnim->name ); RE_RegisterAnimationIQM( name, IQAnim ); } // build VBO return true; }
/* ================= R_LoadMDC ================= */ qboolean R_LoadMDC( model_t *mod, int lod, void *buffer, int bufferSize, const char *modName ) { int i, j, k; mdcHeader_t *mdcModel; md3Frame_t *mdcFrame; mdcSurface_t *mdcSurf; md3Shader_t *mdcShader; md3Triangle_t *mdcTri; md3St_t *mdcst; md3XyzNormal_t *mdcxyz; mdcXyzCompressed_t *mdcxyzComp; mdcTag_t *mdcTag; mdcTagName_t *mdcTagName; mdvModel_t *mdvModel; mdvFrame_t *frame; mdvSurface_t *surf; //, *surface; //unused srfTriangle_t *tri; mdvXyz_t *v; mdvSt_t *st; mdvTag_t *tag; mdvTagName_t *tagName; short *ps; int version; int size; mdcModel = ( mdcHeader_t * ) buffer; version = LittleLong( mdcModel->version ); if ( version != MDC_VERSION ) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MDC_VERSION ); return qfalse; } mod->type = MOD_MESH; size = LittleLong( mdcModel->ofsEnd ); mod->dataSize += size; mdvModel = mod->mdv[ lod ] = (mdvModel_t*) ri.Hunk_Alloc( sizeof( mdvModel_t ), h_low ); LL( mdcModel->ident ); LL( mdcModel->version ); LL( mdcModel->numFrames ); LL( mdcModel->numTags ); LL( mdcModel->numSurfaces ); LL( mdcModel->ofsFrames ); LL( mdcModel->ofsTags ); LL( mdcModel->ofsSurfaces ); LL( mdcModel->ofsEnd ); LL( mdcModel->ofsEnd ); LL( mdcModel->flags ); LL( mdcModel->numSkins ); if ( mdcModel->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMDC: '%s' has no frames\n", modName ); return qfalse; } // swap all the frames mdvModel->numFrames = mdcModel->numFrames; mdvModel->frames = frame = (mdvFrame_t*) ri.Hunk_Alloc( sizeof( *frame ) * mdcModel->numFrames, h_low ); mdcFrame = ( md3Frame_t * )( ( byte * ) mdcModel + mdcModel->ofsFrames ); for ( i = 0; i < mdcModel->numFrames; i++, frame++, mdcFrame++ ) { #if 1 // RB: ET HACK if ( strstr( mod->name, "sherman" ) || strstr( mod->name, "mg42" ) ) { frame->radius = 256; for ( j = 0; j < 3; j++ ) { frame->bounds[ 0 ][ j ] = 128; frame->bounds[ 1 ][ j ] = -128; frame->localOrigin[ j ] = LittleFloat( mdcFrame->localOrigin[ j ] ); } } else #endif { frame->radius = LittleFloat( mdcFrame->radius ); for ( j = 0; j < 3; j++ ) { frame->bounds[ 0 ][ j ] = LittleFloat( mdcFrame->bounds[ 0 ][ j ] ); frame->bounds[ 1 ][ j ] = LittleFloat( mdcFrame->bounds[ 1 ][ j ] ); frame->localOrigin[ j ] = LittleFloat( mdcFrame->localOrigin[ j ] ); } } } // swap all the tags mdvModel->numTags = mdcModel->numTags; mdvModel->tags = tag = (mdvTag_t*) ri.Hunk_Alloc( sizeof( *tag ) * ( mdcModel->numTags * mdcModel->numFrames ), h_low ); mdcTag = ( mdcTag_t * )( ( byte * ) mdcModel + mdcModel->ofsTags ); for ( i = 0; i < mdcModel->numTags * mdcModel->numFrames; i++, tag++, mdcTag++ ) { vec3_t angles; for ( j = 0; j < 3; j++ ) { tag->origin[ j ] = ( float ) LittleShort( mdcTag->xyz[ j ] ) * MD3_XYZ_SCALE; angles[ j ] = ( float ) LittleShort( mdcTag->angles[ j ] ) * MDC_TAG_ANGLE_SCALE; } AnglesToAxis( angles, tag->axis ); } mdvModel->tagNames = tagName = (mdvTagName_t*) ri.Hunk_Alloc( sizeof( *tagName ) * ( mdcModel->numTags ), h_low ); mdcTagName = ( mdcTagName_t * )( ( byte * ) mdcModel + mdcModel->ofsTagNames ); for ( i = 0; i < mdcModel->numTags; i++, tagName++, mdcTagName++ ) { Q_strncpyz( tagName->name, mdcTagName->name, sizeof( tagName->name ) ); } // swap all the surfaces mdvModel->numSurfaces = mdcModel->numSurfaces; mdvModel->surfaces = surf = (mdvSurface_t*) ri.Hunk_Alloc( sizeof( *surf ) * mdcModel->numSurfaces, h_low ); mdcSurf = ( mdcSurface_t * )( ( byte * ) mdcModel + mdcModel->ofsSurfaces ); for ( i = 0; i < mdcModel->numSurfaces; i++ ) { LL( mdcSurf->ident ); LL( mdcSurf->flags ); LL( mdcSurf->numBaseFrames ); LL( mdcSurf->numCompFrames ); LL( mdcSurf->numShaders ); LL( mdcSurf->numTriangles ); LL( mdcSurf->ofsTriangles ); LL( mdcSurf->numVerts ); LL( mdcSurf->ofsShaders ); LL( mdcSurf->ofsSt ); LL( mdcSurf->ofsXyzNormals ); LL( mdcSurf->ofsXyzNormals ); LL( mdcSurf->ofsXyzCompressed ); LL( mdcSurf->ofsFrameBaseFrames ); LL( mdcSurf->ofsFrameCompFrames ); LL( mdcSurf->ofsEnd ); if ( mdcSurf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error( ERR_DROP, "R_LoadMDC: %s has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, mdcSurf->numVerts ); } if ( mdcSurf->numTriangles > SHADER_MAX_TRIANGLES ) { ri.Error( ERR_DROP, "R_LoadMDC: %s has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, mdcSurf->numTriangles ); } // change to surface identifier surf->surfaceType = SF_MDV; // give pointer to model for Tess_SurfaceMDV surf->model = mdvModel; // copy surface name Q_strncpyz( surf->name, mdcSurf->name, sizeof( surf->name ) ); // lowercase the surface name so skin compares are faster Q_strlwr( surf->name ); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess j = strlen( surf->name ); if ( j > 2 && surf->name[ j - 2 ] == '_' ) { surf->name[ j - 2 ] = 0; } // register the shaders /* surf->numShaders = md3Surf->numShaders; surf->shaders = shader = ri.Hunk_Alloc(sizeof(*shader) * md3Surf->numShaders, h_low); md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders); for(j = 0; j < md3Surf->numShaders; j++, shader++, md3Shader++) { shader_t *sh; sh = R_FindShader(md3Shader->name, SHADER_3D_DYNAMIC, RSF_DEFAULT); if(sh->defaultShader) { shader->shaderIndex = 0; } else { shader->shaderIndex = sh->index; } } */ // only consider the first shader mdcShader = ( md3Shader_t * )( ( byte * ) mdcSurf + mdcSurf->ofsShaders ); surf->shader = R_FindShader( mdcShader->name, SHADER_3D_DYNAMIC, RSF_DEFAULT ); // swap all the triangles surf->numTriangles = mdcSurf->numTriangles; surf->triangles = tri = (srfTriangle_t*) ri.Hunk_Alloc( sizeof( *tri ) * mdcSurf->numTriangles, h_low ); mdcTri = ( md3Triangle_t * )( ( byte * ) mdcSurf + mdcSurf->ofsTriangles ); for ( j = 0; j < mdcSurf->numTriangles; j++, tri++, mdcTri++ ) { tri->indexes[ 0 ] = LittleLong( mdcTri->indexes[ 0 ] ); tri->indexes[ 1 ] = LittleLong( mdcTri->indexes[ 1 ] ); tri->indexes[ 2 ] = LittleLong( mdcTri->indexes[ 2 ] ); } // swap all the XyzNormals mdcxyz = ( md3XyzNormal_t * )( ( byte * ) mdcSurf + mdcSurf->ofsXyzNormals ); for ( j = 0; j < mdcSurf->numVerts * mdcSurf->numBaseFrames; j++, mdcxyz++ ) { mdcxyz->xyz[ 0 ] = LittleShort( mdcxyz->xyz[ 0 ] ); mdcxyz->xyz[ 1 ] = LittleShort( mdcxyz->xyz[ 1 ] ); mdcxyz->xyz[ 2 ] = LittleShort( mdcxyz->xyz[ 2 ] ); mdcxyz->normal = LittleShort( mdcxyz->normal ); } // swap all the XyzCompressed mdcxyzComp = ( mdcXyzCompressed_t * )( ( byte * ) mdcSurf + mdcSurf->ofsXyzCompressed ); for ( j = 0; j < mdcSurf->numVerts * mdcSurf->numCompFrames; j++, mdcxyzComp++ ) { LL( mdcxyzComp->ofsVec ); } // swap the frameBaseFrames ps = ( short * )( ( byte * ) mdcSurf + mdcSurf->ofsFrameBaseFrames ); for ( j = 0; j < mdcModel->numFrames; j++, ps++ ) { *ps = LittleShort( *ps ); } // swap the frameCompFrames ps = ( short * )( ( byte * ) mdcSurf + mdcSurf->ofsFrameCompFrames ); for ( j = 0; j < mdcModel->numFrames; j++, ps++ ) { *ps = LittleShort( *ps ); } surf->numVerts = mdcSurf->numVerts; surf->verts = v = (mdvXyz_t*) ri.Hunk_Alloc( sizeof( *v ) * ( mdcSurf->numVerts * mdcModel->numFrames ), h_low ); for ( j = 0; j < mdcModel->numFrames; j++ ) { int baseFrame; int compFrame = 0; baseFrame = ( int ) * ( ( short * )( ( byte * ) mdcSurf + mdcSurf->ofsFrameBaseFrames ) + j ); mdcxyz = ( md3XyzNormal_t * )( ( byte * ) mdcSurf + mdcSurf->ofsXyzNormals + baseFrame * mdcSurf->numVerts * sizeof( md3XyzNormal_t ) ); if ( mdcSurf->numCompFrames > 0 ) { compFrame = ( int ) * ( ( short * )( ( byte * ) mdcSurf + mdcSurf->ofsFrameCompFrames ) + j ); if ( compFrame >= 0 ) { mdcxyzComp = ( mdcXyzCompressed_t * )( ( byte * ) mdcSurf + mdcSurf->ofsXyzCompressed + compFrame * mdcSurf->numVerts * sizeof( mdcXyzCompressed_t ) ); } } for ( k = 0; k < mdcSurf->numVerts; k++, v++, mdcxyz++ ) { v->xyz[ 0 ] = LittleShort( mdcxyz->xyz[ 0 ] ) * MD3_XYZ_SCALE; v->xyz[ 1 ] = LittleShort( mdcxyz->xyz[ 1 ] ) * MD3_XYZ_SCALE; v->xyz[ 2 ] = LittleShort( mdcxyz->xyz[ 2 ] ) * MD3_XYZ_SCALE; if ( mdcSurf->numCompFrames > 0 && compFrame >= 0 ) { vec3_t ofsVec; //vec3_t normal; R_MDC_DecodeXyzCompressed2( LittleShort( mdcxyzComp->ofsVec ), ofsVec /*, normal*/ ); VectorAdd( v->xyz, ofsVec, v->xyz ); mdcxyzComp++; } } } // swap all the ST surf->st = st = (mdvSt_t*) ri.Hunk_Alloc( sizeof( *st ) * mdcSurf->numVerts, h_low ); mdcst = ( md3St_t * )( ( byte * ) mdcSurf + mdcSurf->ofsSt ); for ( j = 0; j < mdcSurf->numVerts; j++, mdcst++, st++ ) { st->st[ 0 ] = LittleFloat( mdcst->st[ 0 ] ); st->st[ 1 ] = LittleFloat( mdcst->st[ 1 ] ); } // find the next surface mdcSurf = ( mdcSurface_t * )( ( byte * ) mdcSurf + mdcSurf->ofsEnd ); surf++; } #if 1 // create VBO surfaces from md3 surfaces { growList_t vboSurfaces; srfVBOMDVMesh_t *vboSurf; vboData_t data; int f; Com_InitGrowList( &vboSurfaces, 10 ); for ( i = 0, surf = mdvModel->surfaces; i < mdvModel->numSurfaces; i++, surf++ ) { //allocate temp memory for vertex data memset( &data, 0, sizeof( data ) ); data.xyz = ( vec3_t * ) ri.Hunk_AllocateTempMemory( sizeof( *data.xyz ) * mdvModel->numFrames * surf->numVerts ); data.normal = ( vec3_t * ) ri.Hunk_AllocateTempMemory( sizeof( *data.normal ) * mdvModel->numFrames * surf->numVerts ); data.tangent = ( vec3_t * ) ri.Hunk_AllocateTempMemory( sizeof( *data.tangent ) * mdvModel->numFrames * surf->numVerts ); data.binormal = ( vec3_t * ) ri.Hunk_AllocateTempMemory( sizeof( *data.binormal ) * mdvModel->numFrames * surf->numVerts ); data.numFrames = mdvModel->numFrames; data.st = ( vec2_t * ) ri.Hunk_AllocateTempMemory( sizeof( *data.st ) * surf->numVerts ); data.numVerts = surf->numVerts; // feed vertex XYZ for ( f = 0; f < mdvModel->numFrames; f++ ) { for ( j = 0; j < surf->numVerts; j++ ) { VectorCopy( surf->verts[ f * surf->numVerts + j ].xyz, data.xyz[ f * surf->numVerts + j ] ); } } // feed vertex texcoords for ( j = 0; j < surf->numVerts; j++ ) { data.st[ j ][ 0 ] = surf->st[ j ].st[ 0 ]; data.st[ j ][ 1 ] = surf->st[ j ].st[ 1 ]; } // calc and feed tangent spaces { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for ( j = 0; j < ( surf->numVerts * mdvModel->numFrames ); j++ ) { VectorClear( data.tangent[ j ] ); VectorClear( data.binormal[ j ] ); VectorClear( data.normal[ j ] ); } for ( f = 0; f < mdvModel->numFrames; f++ ) { for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { v0 = surf->verts[ surf->numVerts * f + tri->indexes[ 0 ] ].xyz; v1 = surf->verts[ surf->numVerts * f + tri->indexes[ 1 ] ].xyz; v2 = surf->verts[ surf->numVerts * f + tri->indexes[ 2 ] ].xyz; t0 = surf->st[ tri->indexes[ 0 ] ].st; t1 = surf->st[ tri->indexes[ 1 ] ].st; t2 = surf->st[ tri->indexes[ 2 ] ].st; #if 1 R_CalcTangentSpace( tangent, binormal, normal, v0, v1, v2, t0, t1, t2 ); #else R_CalcNormalForTriangle( normal, v0, v1, v2 ); R_CalcTangentsForTriangle( tangent, binormal, v0, v1, v2, t0, t1, t2 ); #endif for ( k = 0; k < 3; k++ ) { float *v; v = data.tangent[ surf->numVerts * f + tri->indexes[ k ] ]; VectorAdd( v, tangent, v ); v = data.binormal[ surf->numVerts * f + tri->indexes[ k ] ]; VectorAdd( v, binormal, v ); v = data.normal[ surf->numVerts * f + tri->indexes[ k ] ]; VectorAdd( v, normal, v ); } } } for ( j = 0; j < ( surf->numVerts * mdvModel->numFrames ); j++ ) { VectorNormalize( data.tangent[ j ] ); VectorNormalize( data.binormal[ j ] ); VectorNormalize( data.normal[ j ] ); } } //ri.Printf(PRINT_ALL, "...calculating MD3 mesh VBOs ( '%s', %i verts %i tris )\n", surf->name, surf->numVerts, surf->numTriangles); // create surface vboSurf = (srfVBOMDVMesh_t*) ri.Hunk_Alloc( sizeof( *vboSurf ), h_low ); Com_AddToGrowList( &vboSurfaces, vboSurf ); vboSurf->surfaceType = SF_VBO_MDVMESH; vboSurf->mdvModel = mdvModel; vboSurf->mdvSurface = surf; vboSurf->numIndexes = surf->numTriangles * 3; vboSurf->numVerts = surf->numVerts; vboSurf->ibo = R_CreateStaticIBO2( va( "staticMD3Mesh_IBO %s", surf->name ), surf->numTriangles, surf->triangles ); vboSurf->vbo = R_CreateStaticVBO( va( "staticMD3Mesh_VBO '%s'", surf->name ), data, VBO_LAYOUT_VERTEX_ANIMATION ); ri.Hunk_FreeTempMemory( data.st ); ri.Hunk_FreeTempMemory( data.binormal ); ri.Hunk_FreeTempMemory( data.tangent ); ri.Hunk_FreeTempMemory( data.normal ); ri.Hunk_FreeTempMemory( data.xyz ); } // move VBO surfaces list to hunk mdvModel->numVBOSurfaces = vboSurfaces.currentElements; mdvModel->vboSurfaces = (srfVBOMDVMesh_t**) ri.Hunk_Alloc( mdvModel->numVBOSurfaces * sizeof( *mdvModel->vboSurfaces ), h_low ); for ( i = 0; i < mdvModel->numVBOSurfaces; i++ ) { mdvModel->vboSurfaces[ i ] = ( srfVBOMDVMesh_t * ) Com_GrowListElement( &vboSurfaces, i ); } Com_DestroyGrowList( &vboSurfaces ); } #endif return qtrue; }
bool idRenderModelMD4::Load( idList<byte>& buffer, idSkinTranslation* skinTranslation ) { md4Header_t* pinmodel = ( md4Header_t* )buffer.Ptr(); int version = LittleLong( pinmodel->version ); if ( version != MD4_VERSION ) { common->Printf( S_COLOR_YELLOW "R_LoadMD4: %s has wrong version (%i should be %i)\n", name, version, MD4_VERSION ); return false; } type = MOD_MD4; int size = LittleLong( pinmodel->ofsEnd ); q3_dataSize += size; md4Header_t* md4 = q3_md4 = ( md4Header_t* )Mem_Alloc( size ); Com_Memcpy( md4, buffer.Ptr(), LittleLong( pinmodel->ofsEnd ) ); LL( md4->ident ); LL( md4->version ); LL( md4->numFrames ); LL( md4->numBones ); LL( md4->numLODs ); LL( md4->ofsFrames ); LL( md4->ofsLODs ); LL( md4->ofsEnd ); if ( md4->numFrames < 1 ) { common->Printf( S_COLOR_YELLOW "R_LoadMD4: %s has no frames\n", name ); return false; } // we don't need to swap tags in the renderer, they aren't used // swap all the frames int frameSize = ( qintptr )( &( ( md4Frame_t* )0 )->bones[ md4->numBones ] ); for ( int i = 0; i < md4->numFrames; i++ ) { md4Frame_t* frame = ( md4Frame_t* )( ( byte* )md4 + md4->ofsFrames + i * frameSize ); frame->radius = LittleFloat( frame->radius ); for ( int j = 0; j < 3; j++ ) { frame->bounds[ 0 ][ j ] = LittleFloat( frame->bounds[ 0 ][ j ] ); frame->bounds[ 1 ][ j ] = LittleFloat( frame->bounds[ 1 ][ j ] ); frame->localOrigin[ j ] = LittleFloat( frame->localOrigin[ j ] ); } for ( int j = 0; j < md4->numBones * ( int )sizeof ( md4Bone_t ) / 4; j++ ) { ( ( float* )frame->bones )[ j ] = LittleFloat( ( ( float* )frame->bones )[ j ] ); } } // swap all the LOD's q3_md4Lods = new mmd4Lod_t[ md4->numLODs ]; md4LOD_t* lod = ( md4LOD_t* )( ( byte* )md4 + md4->ofsLODs ); for ( int lodindex = 0; lodindex < md4->numLODs; lodindex++ ) { // swap all the surfaces q3_md4Lods[ lodindex ].numSurfaces = lod->numSurfaces; q3_md4Lods[ lodindex ].surfaces = new idSurfaceMD4[ lod->numSurfaces ]; md4Surface_t* surf = ( md4Surface_t* )( ( byte* )lod + lod->ofsSurfaces ); for ( int i = 0; i < lod->numSurfaces; i++ ) { q3_md4Lods[ lodindex ].surfaces[ i ].SetMd4Data( surf ); LL( surf->ident ); LL( surf->numTriangles ); LL( surf->ofsTriangles ); LL( surf->numVerts ); LL( surf->ofsVerts ); LL( surf->ofsEnd ); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { common->Error( "R_LoadMD3: %s has more than %i verts on a surface (%i)", name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles * 3 > SHADER_MAX_INDEXES ) { common->Error( "R_LoadMD3: %s has more than %i triangles on a surface (%i)", name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // lowercase the surface name so skin compares are faster String::ToLower( surf->name ); // register the shaders shader_t* sh = R_FindShader( surf->shader, LIGHTMAP_NONE, true ); if ( sh->defaultShader ) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } // swap all the triangles md4Triangle_t* tri = ( md4Triangle_t* )( ( byte* )surf + surf->ofsTriangles ); for ( int j = 0; j < surf->numTriangles; j++, tri++ ) { LL( tri->indexes[ 0 ] ); LL( tri->indexes[ 1 ] ); LL( tri->indexes[ 2 ] ); } // swap all the vertexes // FIXME // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left // in for reference. //v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts + 12); md4Vertex_t* v = ( md4Vertex_t* )( ( byte* )surf + surf->ofsVerts ); for ( int j = 0; j < surf->numVerts; j++ ) { v->normal[ 0 ] = LittleFloat( v->normal[ 0 ] ); v->normal[ 1 ] = LittleFloat( v->normal[ 1 ] ); v->normal[ 2 ] = LittleFloat( v->normal[ 2 ] ); v->texCoords[ 0 ] = LittleFloat( v->texCoords[ 0 ] ); v->texCoords[ 1 ] = LittleFloat( v->texCoords[ 1 ] ); v->numWeights = LittleLong( v->numWeights ); for ( int k = 0; k < v->numWeights; k++ ) { v->weights[ k ].boneIndex = LittleLong( v->weights[ k ].boneIndex ); v->weights[ k ].boneWeight = LittleFloat( v->weights[ k ].boneWeight ); v->weights[ k ].offset[ 0 ] = LittleFloat( v->weights[ k ].offset[ 0 ] ); v->weights[ k ].offset[ 1 ] = LittleFloat( v->weights[ k ].offset[ 1 ] ); v->weights[ k ].offset[ 2 ] = LittleFloat( v->weights[ k ].offset[ 2 ] ); } // FIXME // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left // in for reference. //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); v = ( md4Vertex_t* )( ( byte* )&v->weights[ v->numWeights ] ); } // find the next surface surf = ( md4Surface_t* )( ( byte* )surf + surf->ofsEnd ); } // find the next LOD lod = ( md4LOD_t* )( ( byte* )lod + lod->ofsEnd ); } return true; }
/* ================= R_LoadMDC ================= */ qboolean R_LoadMDC(model_t *mod, int lod, void *buffer, int bufferSize, const char *modName) { int i, j, k; mdcHeader_t *mdcModel = ( mdcHeader_t * ) buffer; md3Frame_t *mdcFrame; mdcSurface_t *mdcSurf; md3Shader_t *mdcShader; md3Triangle_t *mdcTri; md3St_t *mdcst; md3XyzNormal_t *mdcxyz; mdcXyzCompressed_t *mdcxyzComp; mdcTag_t *mdcTag; mdcTagName_t *mdcTagName; mdvModel_t *mdvModel; mdvFrame_t *frame; mdvSurface_t *surf; //, *surface; //unused srfTriangle_t *tri; mdvXyz_t *v; mdvSt_t *st; mdvTag_t *tag; mdvTagName_t *tagName; short *ps; int version; int size; version = LittleLong(mdcModel->version); if (version != MDC_VERSION) { Ren_Warning("R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MDC_VERSION); return qfalse; } mod->type = MOD_MESH; size = LittleLong(mdcModel->ofsEnd); mod->dataSize += size; mdvModel = mod->mdv[lod] = ri.Hunk_Alloc(sizeof(mdvModel_t), h_low); LL(mdcModel->ident); LL(mdcModel->version); LL(mdcModel->numFrames); LL(mdcModel->numTags); LL(mdcModel->numSurfaces); LL(mdcModel->ofsFrames); LL(mdcModel->ofsTags); LL(mdcModel->ofsSurfaces); LL(mdcModel->ofsEnd); LL(mdcModel->ofsEnd); LL(mdcModel->flags); LL(mdcModel->numSkins); if (mdcModel->numFrames < 1) { Ren_Warning("R_LoadMDC: '%s' has no frames\n", modName); return qfalse; } // swap all the frames mdvModel->numFrames = mdcModel->numFrames; mdvModel->frames = frame = ri.Hunk_Alloc(sizeof(*frame) * mdcModel->numFrames, h_low); mdcFrame = ( md3Frame_t * )(( byte * ) mdcModel + mdcModel->ofsFrames); for (i = 0; i < mdcModel->numFrames; i++, frame++, mdcFrame++) { #if 1 // ET HACK if (strstr(mod->name, "sherman") || strstr(mod->name, "mg42")) { frame->radius = 256; for (j = 0; j < 3; j++) { frame->bounds[0][j] = 128; frame->bounds[1][j] = -128; frame->localOrigin[j] = LittleFloat(mdcFrame->localOrigin[j]); } } else #endif { frame->radius = LittleFloat(mdcFrame->radius); for (j = 0; j < 3; j++) { frame->bounds[0][j] = LittleFloat(mdcFrame->bounds[0][j]); frame->bounds[1][j] = LittleFloat(mdcFrame->bounds[1][j]); frame->localOrigin[j] = LittleFloat(mdcFrame->localOrigin[j]); } } } // swap all the tags mdvModel->numTags = mdcModel->numTags; mdvModel->tags = tag = ri.Hunk_Alloc(sizeof(*tag) * (mdcModel->numTags * mdcModel->numFrames), h_low); mdcTag = ( mdcTag_t * )(( byte * ) mdcModel + mdcModel->ofsTags); for (i = 0; i < mdcModel->numTags * mdcModel->numFrames; i++, tag++, mdcTag++) { vec3_t angles; for (j = 0; j < 3; j++) { tag->origin[j] = ( float ) LittleShort(mdcTag->xyz[j]) * MD3_XYZ_SCALE; angles[j] = ( float ) LittleShort(mdcTag->angles[j]) * MDC_TAG_ANGLE_SCALE; } AnglesToAxis(angles, tag->axis); } mdvModel->tagNames = tagName = ri.Hunk_Alloc(sizeof(*tagName) * (mdcModel->numTags), h_low); mdcTagName = ( mdcTagName_t * )(( byte * ) mdcModel + mdcModel->ofsTagNames); for (i = 0; i < mdcModel->numTags; i++, tagName++, mdcTagName++) { Q_strncpyz(tagName->name, mdcTagName->name, sizeof(tagName->name)); } // swap all the surfaces mdvModel->numSurfaces = mdcModel->numSurfaces; mdvModel->surfaces = surf = ri.Hunk_Alloc(sizeof(*surf) * mdcModel->numSurfaces, h_low); mdcSurf = ( mdcSurface_t * )(( byte * ) mdcModel + mdcModel->ofsSurfaces); for (i = 0; i < mdcModel->numSurfaces; i++) { LL(mdcSurf->ident); LL(mdcSurf->flags); LL(mdcSurf->numBaseFrames); LL(mdcSurf->numCompFrames); LL(mdcSurf->numShaders); LL(mdcSurf->numTriangles); LL(mdcSurf->ofsTriangles); LL(mdcSurf->numVerts); LL(mdcSurf->ofsShaders); LL(mdcSurf->ofsSt); LL(mdcSurf->ofsXyzNormals); LL(mdcSurf->ofsXyzNormals); LL(mdcSurf->ofsXyzCompressed); LL(mdcSurf->ofsFrameBaseFrames); LL(mdcSurf->ofsFrameCompFrames); LL(mdcSurf->ofsEnd); if (mdcSurf->numVerts > SHADER_MAX_VERTEXES) { Ren_Drop("R_LoadMDC: %s has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, mdcSurf->numVerts); } if (mdcSurf->numTriangles > SHADER_MAX_TRIANGLES) { Ren_Drop("R_LoadMDC: %s has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, mdcSurf->numTriangles); } // change to surface identifier surf->surfaceType = SF_MDV; // give pointer to model for Tess_SurfaceMDX surf->model = mdvModel; // copy surface name Q_strncpyz(surf->name, mdcSurf->name, sizeof(surf->name)); // lowercase the surface name so skin compares are faster Q_strlwr(surf->name); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess j = strlen(surf->name); if (j > 2 && surf->name[j - 2] == '_') { surf->name[j - 2] = 0; } // register the shaders /* surf->numShaders = md3Surf->numShaders; surf->shaders = shader = ri.Hunk_Alloc(sizeof(*shader) * md3Surf->numShaders, h_low); md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders); for(j = 0; j < md3Surf->numShaders; j++, shader++, md3Shader++) { shader_t *sh; sh = R_FindShader(md3Shader->name, SHADER_3D_DYNAMIC, RSF_DEFAULT); if(sh->defaultShader) { shader->shaderIndex = 0; } else { shader->shaderIndex = sh->index; } } */ // only consider the first shader mdcShader = ( md3Shader_t * )(( byte * ) mdcSurf + mdcSurf->ofsShaders); surf->shader = R_FindShader(mdcShader->name, SHADER_3D_DYNAMIC, qtrue); // swap all the triangles surf->numTriangles = mdcSurf->numTriangles; surf->triangles = tri = ri.Hunk_Alloc(sizeof(*tri) * mdcSurf->numTriangles, h_low); mdcTri = ( md3Triangle_t * )(( byte * ) mdcSurf + mdcSurf->ofsTriangles); for (j = 0; j < mdcSurf->numTriangles; j++, tri++, mdcTri++) { tri->indexes[0] = LittleLong(mdcTri->indexes[0]); tri->indexes[1] = LittleLong(mdcTri->indexes[1]); tri->indexes[2] = LittleLong(mdcTri->indexes[2]); } // swap all the XyzNormals mdcxyz = ( md3XyzNormal_t * )(( byte * ) mdcSurf + mdcSurf->ofsXyzNormals); for (j = 0; j < mdcSurf->numVerts * mdcSurf->numBaseFrames; j++, mdcxyz++) { mdcxyz->xyz[0] = LittleShort(mdcxyz->xyz[0]); mdcxyz->xyz[1] = LittleShort(mdcxyz->xyz[1]); mdcxyz->xyz[2] = LittleShort(mdcxyz->xyz[2]); mdcxyz->normal = LittleShort(mdcxyz->normal); } // swap all the XyzCompressed mdcxyzComp = ( mdcXyzCompressed_t * )(( byte * ) mdcSurf + mdcSurf->ofsXyzCompressed); for (j = 0; j < mdcSurf->numVerts * mdcSurf->numCompFrames; j++, mdcxyzComp++) { LL(mdcxyzComp->ofsVec); } // swap the frameBaseFrames ps = ( short * )(( byte * ) mdcSurf + mdcSurf->ofsFrameBaseFrames); for (j = 0; j < mdcModel->numFrames; j++, ps++) { *ps = LittleShort(*ps); } // swap the frameCompFrames ps = ( short * )(( byte * ) mdcSurf + mdcSurf->ofsFrameCompFrames); for (j = 0; j < mdcModel->numFrames; j++, ps++) { *ps = LittleShort(*ps); } surf->numVerts = mdcSurf->numVerts; surf->verts = v = ri.Hunk_Alloc(sizeof(*v) * (mdcSurf->numVerts * mdcModel->numFrames), h_low); for (j = 0; j < mdcModel->numFrames; j++) { int baseFrame; int compFrame = 0; baseFrame = ( int ) *(( short * )(( byte * ) mdcSurf + mdcSurf->ofsFrameBaseFrames) + j); mdcxyz = ( md3XyzNormal_t * )(( byte * ) mdcSurf + mdcSurf->ofsXyzNormals + baseFrame * mdcSurf->numVerts * sizeof(md3XyzNormal_t)); if (mdcSurf->numCompFrames > 0) { compFrame = ( int ) *(( short * )(( byte * ) mdcSurf + mdcSurf->ofsFrameCompFrames) + j); if (compFrame >= 0) { mdcxyzComp = ( mdcXyzCompressed_t * )(( byte * ) mdcSurf + mdcSurf->ofsXyzCompressed + compFrame * mdcSurf->numVerts * sizeof(mdcXyzCompressed_t)); } } for (k = 0; k < mdcSurf->numVerts; k++, v++, mdcxyz++) { v->xyz[0] = LittleShort(mdcxyz->xyz[0]) * MD3_XYZ_SCALE; v->xyz[1] = LittleShort(mdcxyz->xyz[1]) * MD3_XYZ_SCALE; v->xyz[2] = LittleShort(mdcxyz->xyz[2]) * MD3_XYZ_SCALE; if (mdcSurf->numCompFrames > 0 && compFrame >= 0) { vec3_t ofsVec; R_MDC_DecodeXyzCompressed2(LittleShort(mdcxyzComp->ofsVec), ofsVec); VectorAdd(v->xyz, ofsVec, v->xyz); mdcxyzComp++; } } } // swap all the ST surf->st = st = ri.Hunk_Alloc(sizeof(*st) * mdcSurf->numVerts, h_low); mdcst = ( md3St_t * )(( byte * ) mdcSurf + mdcSurf->ofsSt); for (j = 0; j < mdcSurf->numVerts; j++, mdcst++, st++) { st->st[0] = LittleFloat(mdcst->st[0]); st->st[1] = LittleFloat(mdcst->st[1]); } // find the next surface mdcSurf = ( mdcSurface_t * )(( byte * ) mdcSurf + mdcSurf->ofsEnd); surf++; } #if 1 // create VBO surfaces from md3 surfaces { mdvNormTanBi_t *vertexes; mdvNormTanBi_t *vert; growList_t vboSurfaces; srfVBOMDVMesh_t *vboSurf; byte *data; int dataSize; int dataOfs; vec4_t tmp; GLuint ofsTexCoords; GLuint ofsTangents; GLuint ofsBinormals; GLuint ofsNormals; GLuint sizeXYZ = 0; GLuint sizeTangents = 0; GLuint sizeBinormals = 0; GLuint sizeNormals = 0; int vertexesNum; int f; Com_InitGrowList(&vboSurfaces, 10); for (i = 0, surf = mdvModel->surfaces; i < mdvModel->numSurfaces; i++, surf++) { //allocate temp memory for vertex data vertexes = (mdvNormTanBi_t *)ri.Hunk_AllocateTempMemory(sizeof(*vertexes) * surf->numVerts * mdvModel->numFrames); // calc tangent spaces { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for (j = 0, vert = vertexes; j < (surf->numVerts * mdvModel->numFrames); j++, vert++) { VectorClear(vert->tangent); VectorClear(vert->binormal); VectorClear(vert->normal); } for (f = 0; f < mdvModel->numFrames; f++) { for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { v0 = surf->verts[surf->numVerts * f + tri->indexes[0]].xyz; v1 = surf->verts[surf->numVerts * f + tri->indexes[1]].xyz; v2 = surf->verts[surf->numVerts * f + tri->indexes[2]].xyz; t0 = surf->st[tri->indexes[0]].st; t1 = surf->st[tri->indexes[1]].st; t2 = surf->st[tri->indexes[2]].st; #if 1 R_CalcTangentSpace(tangent, binormal, normal, v0, v1, v2, t0, t1, t2); #else R_CalcNormalForTriangle(normal, v0, v1, v2); R_CalcTangentsForTriangle(tangent, binormal, v0, v1, v2, t0, t1, t2); #endif for (k = 0; k < 3; k++) { float *v; v = vertexes[surf->numVerts * f + tri->indexes[k]].tangent; VectorAdd(v, tangent, v); v = vertexes[surf->numVerts * f + tri->indexes[k]].binormal; VectorAdd(v, binormal, v); v = vertexes[surf->numVerts * f + tri->indexes[k]].normal; VectorAdd(v, normal, v); } } } for (j = 0, vert = vertexes; j < (surf->numVerts * mdvModel->numFrames); j++, vert++) { VectorNormalize(vert->tangent); VectorNormalize(vert->binormal); VectorNormalize(vert->normal); } } //Ren_Print("...calculating MDC mesh VBOs ( '%s', %i verts %i tris )\n", surf->name, surf->numVerts, surf->numTriangles); // create surface vboSurf = ri.Hunk_Alloc(sizeof(*vboSurf), h_low); Com_AddToGrowList(&vboSurfaces, vboSurf); vboSurf->surfaceType = SF_VBO_MDVMESH; vboSurf->mdvModel = mdvModel; vboSurf->mdvSurface = surf; vboSurf->numIndexes = surf->numTriangles * 3; vboSurf->numVerts = surf->numVerts; /* vboSurf->vbo = R_CreateVBO2(va("staticWorldMesh_vertices %i", vboSurfaces.currentElements), numVerts, optimizedVerts, ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BINORMAL | ATTR_NORMAL | ATTR_COLOR); */ vboSurf->ibo = R_CreateIBO2(va("staticMDCMesh_IBO %s", surf->name), surf->numTriangles, surf->triangles, VBO_USAGE_STATIC); // create VBO vertexesNum = surf->numVerts; dataSize = (surf->numVerts * mdvModel->numFrames * sizeof(vec4_t) * 4) + // xyz, tangent, binormal, normal (surf->numVerts * sizeof(vec4_t)); // texcoords data = ri.Hunk_AllocateTempMemory(dataSize); dataOfs = 0; // feed vertex XYZ for (f = 0; f < mdvModel->numFrames; f++) { for (j = 0; j < vertexesNum; j++) { for (k = 0; k < 3; k++) { tmp[k] = surf->verts[f * vertexesNum + j].xyz[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } if (f == 0) { sizeXYZ = dataOfs; } } // feed vertex texcoords ofsTexCoords = dataOfs; for (j = 0; j < vertexesNum; j++) { for (k = 0; k < 2; k++) { tmp[k] = surf->st[j].st[k]; } tmp[2] = 0; tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } // feed vertex tangents ofsTangents = dataOfs; for (f = 0; f < mdvModel->numFrames; f++) { for (j = 0; j < vertexesNum; j++) { for (k = 0; k < 3; k++) { tmp[k] = vertexes[f * vertexesNum + j].tangent[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } if (f == 0) { sizeTangents = dataOfs - ofsTangents; } } // feed vertex binormals ofsBinormals = dataOfs; for (f = 0; f < mdvModel->numFrames; f++) { for (j = 0; j < vertexesNum; j++) { for (k = 0; k < 3; k++) { tmp[k] = vertexes[f * vertexesNum + j].binormal[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } if (f == 0) { sizeBinormals = dataOfs - ofsBinormals; } } // feed vertex normals ofsNormals = dataOfs; for (f = 0; f < mdvModel->numFrames; f++) { for (j = 0; j < vertexesNum; j++) { for (k = 0; k < 3; k++) { tmp[k] = vertexes[f * vertexesNum + j].normal[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } if (f == 0) { sizeNormals = dataOfs - ofsNormals; } } vboSurf->vbo = R_CreateVBO(va("staticMDCMesh_VBO '%s'", surf->name), data, dataSize, VBO_USAGE_STATIC); vboSurf->vbo->ofsXYZ = 0; vboSurf->vbo->ofsTexCoords = ofsTexCoords; vboSurf->vbo->ofsLightCoords = ofsTexCoords; vboSurf->vbo->ofsTangents = ofsTangents; vboSurf->vbo->ofsBinormals = ofsBinormals; vboSurf->vbo->ofsNormals = ofsNormals; vboSurf->vbo->sizeXYZ = sizeXYZ; vboSurf->vbo->sizeTangents = sizeTangents; vboSurf->vbo->sizeBinormals = sizeBinormals; vboSurf->vbo->sizeNormals = sizeNormals; ri.Hunk_FreeTempMemory(data); ri.Hunk_FreeTempMemory(vertexes); } // move VBO surfaces list to hunk mdvModel->numVBOSurfaces = vboSurfaces.currentElements; mdvModel->vboSurfaces = ri.Hunk_Alloc(mdvModel->numVBOSurfaces * sizeof(*mdvModel->vboSurfaces), h_low); for (i = 0; i < mdvModel->numVBOSurfaces; i++) { mdvModel->vboSurfaces[i] = ( srfVBOMDVMesh_t * ) Com_GrowListElement(&vboSurfaces, i); } Com_DestroyGrowList(&vboSurfaces); } #endif return qtrue; }
/* ================= R_LoadFogs ================= */ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump, world_t &worldData, int index ) { int i; fog_t *out; dfog_t *fogs; dbrush_t *brushes, *brush; dbrushside_t *sides; int count, brushesCount, sidesCount; int sideNum; int planeNum; shader_t *shader; float d; int firstSide=0; int lightmaps[MAXLIGHTMAPS] = { LIGHTMAP_NONE } ; fogs = (dfog_t *)(fileBase + l->fileofs); if (l->filelen % sizeof(*fogs)) { Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",worldData.name); } count = l->filelen / sizeof(*fogs); // create fog strucutres for them worldData.numfogs = count + 1; worldData.fogs = (fog_t *)R_Hunk_Alloc ( (worldData.numfogs+1)*sizeof(*out), qtrue); worldData.globalFog = -1; out = worldData.fogs + 1; // Copy the global fog from the main world into the bsp instance if(index) { if(tr.world && (tr.world->globalFog != -1)) { // Use the nightvision fog slot worldData.fogs[worldData.numfogs] = tr.world->fogs[tr.world->globalFog]; worldData.globalFog = worldData.numfogs; worldData.numfogs++; } } if ( !count ) { return; } brushes = (dbrush_t *)(fileBase + brushesLump->fileofs); if (brushesLump->filelen % sizeof(*brushes)) { Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",worldData.name); } brushesCount = brushesLump->filelen / sizeof(*brushes); sides = (dbrushside_t *)(fileBase + sidesLump->fileofs); if (sidesLump->filelen % sizeof(*sides)) { Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",worldData.name); } sidesCount = sidesLump->filelen / sizeof(*sides); for ( i=0 ; i<count ; i++, fogs++) { out->originalBrushNumber = LittleLong( fogs->brushNum ); if (out->originalBrushNumber == -1) { if(index) { Com_Error (ERR_DROP, "LoadMap: global fog not allowed in bsp instances - %s", worldData.name); } VectorSet(out->bounds[0], MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD); VectorSet(out->bounds[1], MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD); worldData.globalFog = i + 1; } else { if ( (unsigned)out->originalBrushNumber >= (unsigned)brushesCount ) { Com_Error( ERR_DROP, "fog brushNumber out of range" ); } brush = brushes + out->originalBrushNumber; firstSide = LittleLong( brush->firstSide ); if ( (unsigned)firstSide > (unsigned)(sidesCount - 6) ) { Com_Error( ERR_DROP, "fog brush sideNumber out of range" ); } // brushes are always sorted with the axial sides first sideNum = firstSide + 0; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[0][0] = -worldData.planes[ planeNum ].dist; sideNum = firstSide + 1; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[1][0] = worldData.planes[ planeNum ].dist; sideNum = firstSide + 2; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[0][1] = -worldData.planes[ planeNum ].dist; sideNum = firstSide + 3; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[1][1] = worldData.planes[ planeNum ].dist; sideNum = firstSide + 4; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[0][2] = -worldData.planes[ planeNum ].dist; sideNum = firstSide + 5; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[1][2] = worldData.planes[ planeNum ].dist; } // get information from the shader for fog parameters shader = R_FindShader( fogs->shader, lightmaps, stylesDefault, qtrue ); assert(shader->fogParms); if (!shader->fogParms) {//bad shader!! out->parms.color[0] = 1.0f; out->parms.color[1] = 0.0f; out->parms.color[2] = 0.0f; out->parms.depthForOpaque = 250.0f; } else { out->parms = *shader->fogParms; } out->colorInt = ColorBytes4 ( out->parms.color[0], out->parms.color[1], out->parms.color[2], 1.0 ); d = out->parms.depthForOpaque < 1 ? 1 : out->parms.depthForOpaque; out->tcScale = 1.0f / ( d * 8 ); // set the gradient vector sideNum = LittleLong( fogs->visibleSide ); if ( sideNum == -1 ) { out->hasSurface = qfalse; } else { out->hasSurface = qtrue; planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); VectorSubtract( vec3_origin, worldData.planes[ planeNum ].normal, out->surface ); out->surface[3] = -worldData.planes[ planeNum ].dist; } out++; } if (!index) { // Initialise the last fog so we can use it with the LA Goggles // NOTE: We are might appear to be off the end of the array, but we allocated an extra memory slot above but [purposely] didn't // increment the total world numFogs to match our array size VectorSet(out->bounds[0], MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD); VectorSet(out->bounds[1], MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD); out->originalBrushNumber = -1; out->parms.color[0] = 0.0f; out->parms.color[1] = 0.0f; out->parms.color[2] = 0.0f; out->parms.depthForOpaque = 0.0f; out->colorInt = 0x00000000; out->tcScale = 0.0f; out->hasSurface = qfalse; } }
static qboolean R_LoadMDR( model_t *mod, void *buffer, int filesize, const char *mod_name ) { int i, j, k, l; mdrHeader_t *pinmodel, *mdr; mdrFrame_t *frame; mdrLOD_t *lod, *curlod; mdrSurface_t *surf, *cursurf; mdrTriangle_t *tri, *curtri; mdrVertex_t *v, *curv; mdrWeight_t *weight, *curweight; mdrTag_t *tag, *curtag; int size; shader_t *sh; pinmodel = (mdrHeader_t *)buffer; pinmodel->version = LittleLong(pinmodel->version); if (pinmodel->version != MDR_VERSION) { ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has wrong version (%i should be %i)\n", mod_name, pinmodel->version, MDR_VERSION); return qfalse; } size = LittleLong(pinmodel->ofsEnd); if(size > filesize) { ri.Printf(PRINT_WARNING, "R_LoadMDR: Header of %s is broken. Wrong filesize declared!\n", mod_name); return qfalse; } mod->type = MOD_MDR; pinmodel->numFrames = LittleLong(pinmodel->numFrames); pinmodel->numBones = LittleLong(pinmodel->numBones); pinmodel->ofsFrames = LittleLong(pinmodel->ofsFrames); // This is a model that uses some type of compressed Bones. We don't want to uncompress every bone for each rendered frame // over and over again, we'll uncompress it in this function already, so we must adjust the size of the target md4. if(pinmodel->ofsFrames < 0) { // mdrFrame_t is larger than mdrCompFrame_t: size += pinmodel->numFrames * sizeof(frame->name); // now add enough space for the uncompressed bones. size += pinmodel->numFrames * pinmodel->numBones * ((sizeof(mdrBone_t) - sizeof(mdrCompBone_t))); } mod->dataSize += size; mod->md4 = mdr = ri.Hunk_Alloc( size, h_low ); // Copy all the values over from the file and fix endian issues in the process, if necessary. mdr->ident = LittleLong(pinmodel->ident); mdr->version = pinmodel->version; // Don't need to swap byte order on this one, we already did above. strlcpy(mdr->name, pinmodel->name, sizeof(mdr->name)); mdr->numFrames = pinmodel->numFrames; mdr->numBones = pinmodel->numBones; mdr->numLODs = LittleLong(pinmodel->numLODs); mdr->numTags = LittleLong(pinmodel->numTags); // We don't care about offset values, we'll generate them ourselves while loading. mod->numLods = mdr->numLODs; if ( mdr->numFrames < 1 ) { ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has no frames\n", mod_name); return qfalse; } /* The first frame will be put into the first free space after the header */ frame = (mdrFrame_t *)(mdr + 1); mdr->ofsFrames = (int)((byte *) frame - (byte *) mdr); if (pinmodel->ofsFrames < 0) { mdrCompFrame_t *cframe; // compressed model... cframe = (mdrCompFrame_t *)((byte *) pinmodel - pinmodel->ofsFrames); for(i = 0; i < mdr->numFrames; i++) { for(j = 0; j < 3; j++) { frame->bounds[0][j] = LittleFloat(cframe->bounds[0][j]); frame->bounds[1][j] = LittleFloat(cframe->bounds[1][j]); frame->localOrigin[j] = LittleFloat(cframe->localOrigin[j]); } frame->radius = LittleFloat(cframe->radius); frame->name[0] = '\0'; // No name supplied in the compressed version. for(j = 0; j < mdr->numBones; j++) { for(k = 0; k < (sizeof(cframe->bones[j].Comp) / 2); k++) { // Do swapping for the uncompressing functions. They seem to use shorts // values only, so I assume this will work. Never tested it on other // platforms, though. ((unsigned short *)(cframe->bones[j].Comp))[k] = LittleShort( ((unsigned short *)(cframe->bones[j].Comp))[k] ); } /* Now do the actual uncompressing */ MC_UnCompress(frame->bones[j].matrix, cframe->bones[j].Comp); } // Next Frame... cframe = (mdrCompFrame_t *) &cframe->bones[j]; frame = (mdrFrame_t *) &frame->bones[j]; } } else { mdrFrame_t *curframe; // uncompressed model... // curframe = (mdrFrame_t *)((byte *) pinmodel + pinmodel->ofsFrames); // swap all the frames for ( i = 0 ; i < mdr->numFrames ; i++) { for(j = 0; j < 3; j++) { frame->bounds[0][j] = LittleFloat(curframe->bounds[0][j]); frame->bounds[1][j] = LittleFloat(curframe->bounds[1][j]); frame->localOrigin[j] = LittleFloat(curframe->localOrigin[j]); } frame->radius = LittleFloat(curframe->radius); strlcpy(frame->name, curframe->name, sizeof(frame->name)); for (j = 0; j < (int) (mdr->numBones * sizeof(mdrBone_t) / 4); j++) { ((float *)frame->bones)[j] = LittleFloat( ((float *)curframe->bones)[j] ); } curframe++; frame++; } } // frame should now point to the first free address after all frames. lod = (mdrLOD_t *) frame; mdr->ofsLODs = (int) ((byte *) lod - (byte *)mdr); curlod = (mdrLOD_t *)((byte *) pinmodel + LittleLong(pinmodel->ofsLODs)); // swap all the LOD's for ( l = 0 ; l < mdr->numLODs ; l++) { lod->numSurfaces = LittleLong(curlod->numSurfaces); // swap all the surfaces surf = (mdrSurface_t *) (lod + 1); lod->ofsSurfaces = (int)((byte *) surf - (byte *) lod); cursurf = (mdrSurface_t *) ((byte *)curlod + LittleLong(curlod->ofsSurfaces)); for ( i = 0 ; i < lod->numSurfaces ; i++) { // first do some copying stuff surf->ident = SF_MDR; strlcpy(surf->name, cursurf->name, sizeof(surf->name)); strlcpy(surf->shader, cursurf->shader, sizeof(surf->shader)); surf->ofsHeader = (byte *) mdr - (byte *) surf; surf->numVerts = LittleLong(cursurf->numVerts); surf->numTriangles = LittleLong(cursurf->numTriangles); // numBoneReferences and BoneReferences generally seem to be unused // now do the checks that may fail. if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); return qfalse; } if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); return qfalse; } // lowercase the surface name so skin compares are faster Q_strlwr( surf->name ); // register the shaders sh = R_FindShader(surf->shader, LIGHTMAP_NONE, qtrue); if ( sh->defaultShader ) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } // now copy the vertexes. v = (mdrVertex_t *) (surf + 1); surf->ofsVerts = (int)((byte *) v - (byte *) surf); curv = (mdrVertex_t *) ((byte *)cursurf + LittleLong(cursurf->ofsVerts)); for(j = 0; j < surf->numVerts; j++) { v->normal[0] = LittleFloat(curv->normal[0]); v->normal[1] = LittleFloat(curv->normal[1]); v->normal[2] = LittleFloat(curv->normal[2]); v->texCoords[0] = LittleFloat(curv->texCoords[0]); v->texCoords[1] = LittleFloat(curv->texCoords[1]); v->numWeights = LittleLong(curv->numWeights); weight = &v->weights[0]; curweight = &curv->weights[0]; // Now copy all the weights for(k = 0; k < v->numWeights; k++) { weight->boneIndex = LittleLong(curweight->boneIndex); weight->boneWeight = LittleFloat(curweight->boneWeight); weight->offset[0] = LittleFloat(curweight->offset[0]); weight->offset[1] = LittleFloat(curweight->offset[1]); weight->offset[2] = LittleFloat(curweight->offset[2]); weight++; curweight++; } v = (mdrVertex_t *) weight; curv = (mdrVertex_t *) curweight; } // we know the offset to the triangles now: tri = (mdrTriangle_t *) v; surf->ofsTriangles = (int)((byte *) tri - (byte *) surf); curtri = (mdrTriangle_t *)((byte *) cursurf + LittleLong(cursurf->ofsTriangles)); for(j = 0; j < surf->numTriangles; j++) { tri->indexes[0] = LittleLong(curtri->indexes[0]); tri->indexes[1] = LittleLong(curtri->indexes[1]); tri->indexes[2] = LittleLong(curtri->indexes[2]); tri++; curtri++; } // tri now points to the end of the surface. surf->ofsEnd = (byte *) tri - (byte *) surf; surf = (mdrSurface_t *) tri; // find the next surface. cursurf = (mdrSurface_t *) ((byte *) cursurf + LittleLong(cursurf->ofsEnd)); } // surf points to the next lod now. lod->ofsEnd = (int)((byte *) surf - (byte *) lod); lod = (mdrLOD_t *) surf; // find the next LOD. curlod = (mdrLOD_t *)((byte *) curlod + LittleLong(curlod->ofsEnd)); } // lod points to the first tag now, so update the offset too. tag = (mdrTag_t *) lod; mdr->ofsTags = (int)((byte *) tag - (byte *) mdr); curtag = (mdrTag_t *) ((byte *)pinmodel + LittleLong(pinmodel->ofsTags)); for (i = 0 ; i < mdr->numTags ; i++) { tag->boneIndex = LittleLong(curtag->boneIndex); strlcpy(tag->name, curtag->name, sizeof(tag->name)); tag++; curtag++; } // And finally we know the offset to the end. mdr->ofsEnd = (int)((byte *) tag - (byte *) mdr); // phew! we're done. return qtrue; }
/* =============== RE_RegisterSkin =============== */ qhandle_t RE_RegisterSkin(const char *name) { qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; skinModel_t *model; //----(SA) added char *text, *text_p; char *token; char surfName[MAX_QPATH]; if(!name || !name[0]) { Com_Printf("Empty name passed to RE_RegisterSkin\n"); return 0; } if(strlen(name) >= MAX_QPATH) { Com_Printf("Skin name exceeds MAX_QPATH\n"); return 0; } // see if the skin is already loaded for(hSkin = 1; hSkin < tr.numSkins; hSkin++) { skin = tr.skins[hSkin]; if(!Q_stricmp(skin->name, name)) { if(skin->numSurfaces == 0) { return 0; // default skin } return hSkin; } } // allocate a new skin if(tr.numSkins == MAX_SKINS) { ri.Printf(PRINT_WARNING, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name); return 0; } //----(SA) moved things around slightly to fix the problem where you restart // a map that has ai characters who had invalid skin names entered // in thier "skin" or "head" field // make sure the render thread is stopped R_SyncRenderThread(); #if 0 // If not a .skin file, load as a single shader if(strcmp(name + strlen(name) - 5, ".skin")) { skin->numSurfaces = 1; skin->surfaces[0] = ri.Hunk_Alloc(sizeof(skin->surfaces[0]), h_low); skin->surfaces[0]->shader = R_FindShader(name, SHADER_3D_DYNAMIC, true); return hSkin; } #endif // load and parse the skin file ri.FS_ReadFile(name, (void **)&text); if(!text) { return 0; } tr.numSkins++; skin = (skin_t*)ri.Hunk_Alloc(sizeof(skin_t), h_low); tr.skins[hSkin] = skin; Q_strncpyz(skin->name, name, sizeof(skin->name)); skin->numSurfaces = 0; skin->numModels = 0; //----(SA) added //----(SA) end text_p = text; while(text_p && *text_p) { // get surface name token = CommaParse(&text_p); Q_strncpyz(surfName, token, sizeof(surfName)); if(!token[0]) { break; } // lowercase the surface name so skin compares are faster Q_strlwr(surfName); if(*text_p == ',') { text_p++; } if(!Q_stricmpn(token, "tag_", 4)) { continue; } if(!Q_stricmpn(token, "md3_", 4)) { // this is specifying a model model = skin->models[skin->numModels] = (skinModel_t*)ri.Hunk_Alloc(sizeof(*skin->models[0]), h_low); Q_strncpyz(model->type, token, sizeof(model->type)); model->hash = Com_HashKey(model->type, sizeof(model->type)); // get the model name token = CommaParse(&text_p); Q_strncpyz(model->model, token, sizeof(model->model)); skin->numModels++; continue; } // parse the shader name token = CommaParse(&text_p); surf = skin->surfaces[skin->numSurfaces] = (skinSurface_t*)ri.Hunk_Alloc(sizeof(*skin->surfaces[0]), h_low); Q_strncpyz(surf->name, surfName, sizeof(surf->name)); // RB: bspSurface not not have ::hash yet // surf->hash = Com_HashKey(surf->name, sizeof(surf->name)); surf->shader = R_FindShader(token, SHADER_3D_DYNAMIC, true); skin->numSurfaces++; } ri.FS_FreeFile(text); // never let a skin have 0 shaders if(skin->numSurfaces == 0) { return 0; // use default skin } return hSkin; }
/* ================= R_LoadMD4 ================= */ static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name, qboolean bAlreadyCached ) { int i,l; md4Header_t *pinmodel, *md4; md4LOD_t *lod; md4Surface_t *surf; int version; int size; shader_t *sh; #ifndef _M_IX86 int j, k; int frameSize; md4Tag_t *tag; md4Triangle_t *tri; md4Vertex_t *v; md4Frame_t *frame; md4CompFrame_t *cframe; #endif pinmodel = (md4Header_t *)buffer; // // read some fields from the binary, but only LittleLong() them when we know this wasn't an already-cached model... // version = (pinmodel->version); size = (pinmodel->ofsEnd); if (!bAlreadyCached) { version = LittleLong(version); size = LittleLong(size); } if (version != MD4_VERSION) { ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has wrong version (%i should be %i)\n", mod_name, version, MD4_VERSION); return qfalse; } mod->type = MOD_MD4; mod->dataSize += size; qboolean bAlreadyFound = qfalse; md4 = mod->md4 = (md4Header_t*) //ri.Hunk_Alloc( size ); RE_RegisterModels_Malloc(size, mod_name, &bAlreadyFound, TAG_MODEL_MD4); assert(bAlreadyCached == bAlreadyFound); // I should probably eliminate 'bAlreadyFound', but wtf? if (!bAlreadyFound) { memcpy( md4, buffer, size ); LL(md4->ident); LL(md4->version); LL(md4->numFrames); LL(md4->numBones); LL(md4->numLODs); LL(md4->ofsFrames); LL(md4->ofsLODs); LL(md4->numTags); LL(md4->ofsTags); LL(md4->ofsEnd); } mod->numLods = md4->numLODs -1 ; //copy this up to the model for ease of use - it wil get inced after this. if ( md4->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has no frames\n", mod_name ); return qfalse; } if (bAlreadyFound) { return qtrue; // All done. Stop, go no further, do not LittleLong(), do not pass Go... } #ifndef _M_IX86 // // optimisation, we don't bother doing this for standard intel case since our data's already in that format... // if (md4->ofsFrames<0) // Compressed . { // swap all the frames frameSize = (int)( &((md4CompFrame_t *)0)->bones[ md4->numBones ] ); for ( i = 0 ; i < md4->numFrames ; i++) { cframe = (md4CompFrame_t *) ( (byte *)md4 - md4->ofsFrames + i * frameSize ); cframe->radius = LittleFloat( cframe->radius ); for ( j = 0 ; j < 3 ; j++ ) { cframe->bounds[0][j] = LittleFloat( cframe->bounds[0][j] ); cframe->bounds[1][j] = LittleFloat( cframe->bounds[1][j] ); cframe->localOrigin[j] = LittleFloat( cframe->localOrigin[j] ); } for ( j = 0 ; j < md4->numBones * sizeof( md4CompBone_t ) / 2 ; j++ ) { ((short *)cframe->bones)[j] = LittleShort( ((short *)cframe->bones)[j] ); } } } else { // swap all the frames frameSize = (int)( &((md4Frame_t *)0)->bones[ md4->numBones ] ); for ( i = 0 ; i < md4->numFrames ; i++) { frame = (md4Frame_t *) ( (byte *)md4 + md4->ofsFrames + i * frameSize ); frame->radius = LittleFloat( frame->radius ); for ( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); } for ( j = 0 ; j < md4->numBones * sizeof( md4Bone_t ) / 4 ; j++ ) { ((float *)frame->bones)[j] = LittleFloat( ((float *)frame->bones)[j] ); } } } #endif // swap all the LOD's (we need to do the middle part of this even for intel, because of shader reg and err-check) lod = (md4LOD_t *) ( (byte *)md4 + md4->ofsLODs ); for ( l = 0 ; l < md4->numLODs ; l++) { LL(lod->ofsSurfaces); LL(lod->numSurfaces); LL(lod->ofsEnd); // swap all the surfaces surf = (md4Surface_t *) ( (byte *)lod + lod->ofsSurfaces ); for ( i = 0 ; i < lod->numSurfaces ; i++) { LL(surf->numTriangles); LL(surf->ofsTriangles); LL(surf->numVerts); LL(surf->ofsVerts); LL(surf->ofsEnd); LL(surf->ofsHeader); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error (ERR_DROP, "R_LoadMD4: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { ri.Error (ERR_DROP, "R_LoadMD4: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // change to surface identifier surf->ident = SF_MD4; // register the shaders sh = R_FindShader( surf->shader, lightmapsNone, stylesDefault, qtrue ); if ( sh->defaultShader ) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } RE_RegisterModels_StoreShaderRequest(mod_name, &surf->shader[0], &surf->shaderIndex); #ifndef _M_IX86 // // optimisation, we don't bother doing this for standard intel case since our data's already in that format... // // swap all the triangles tri = (md4Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { LL(tri->indexes[0]); LL(tri->indexes[1]); LL(tri->indexes[2]); } // swap all the vertexes v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts ); for ( j = 0 ; j < surf->numVerts ; j++ ) { v->normal[0] = LittleFloat( v->normal[0] ); v->normal[1] = LittleFloat( v->normal[1] ); v->normal[2] = LittleFloat( v->normal[2] ); v->texCoords[0] = LittleFloat( v->texCoords[0] ); v->texCoords[1] = LittleFloat( v->texCoords[1] ); v->numWeights = LittleLong( v->numWeights ); for ( k = 0 ; k < v->numWeights ; k++ ) { v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex ); v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight ); v->weights[k].offset[0] = LittleFloat( v->weights[k].offset[0] ); v->weights[k].offset[1] = LittleFloat( v->weights[k].offset[1] ); v->weights[k].offset[2] = LittleFloat( v->weights[k].offset[2] ); } v = (md4Vertex_t *)&v->weights[v->numWeights]; } #endif // find the next surface surf = (md4Surface_t *)( (byte *)surf + surf->ofsEnd ); } // find the next LOD lod = (md4LOD_t *)( (byte *)lod + lod->ofsEnd ); } #ifndef _M_IX86 // // optimisation, we don't bother doing this for standard intel case since our data's already in that format... // tag = (md4Tag_t *) ( (byte *)md4 + md4->ofsTags ); for ( i = 0 ; i < md4->numTags ; i++) { LL(tag->boneIndex); tag++; } #endif return qtrue; }
/* ================= R_LoadMD3 ================= */ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name ) { int i, j; md3Header_t *pinmodel; md3Frame_t *frame; md3Surface_t *surf; md3Shader_t *shader; md3Triangle_t *tri; md3St_t *st; md3XyzNormal_t *xyz; md3Tag_t *tag; int version; int size; pinmodel = (md3Header_t *)buffer; version = LittleLong (pinmodel->version); if (version != MD3_VERSION) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", mod_name, version, MD3_VERSION); return qfalse; } mod->type = MOD_MESH; size = LittleLong(pinmodel->ofsEnd); mod->dataSize += size; mod->md3[lod] = ri.Hunk_Alloc( size, h_low ); memcpy (mod->md3[lod], buffer, LittleLong(pinmodel->ofsEnd) ); LL(mod->md3[lod]->ident); LL(mod->md3[lod]->version); LL(mod->md3[lod]->numFrames); LL(mod->md3[lod]->numTags); LL(mod->md3[lod]->numSurfaces); LL(mod->md3[lod]->ofsFrames); LL(mod->md3[lod]->ofsTags); LL(mod->md3[lod]->ofsSurfaces); LL(mod->md3[lod]->ofsEnd); if ( mod->md3[lod]->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name ); return qfalse; } // swap all the frames frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames ); for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) { frame->radius = LittleFloat( frame->radius ); for ( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); } } // swap all the tags tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags ); for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) { for ( j = 0 ; j < 3 ; j++ ) { tag->origin[j] = LittleFloat( tag->origin[j] ); tag->axis[0][j] = LittleFloat( tag->axis[0][j] ); tag->axis[1][j] = LittleFloat( tag->axis[1][j] ); tag->axis[2][j] = LittleFloat( tag->axis[2][j] ); } } // swap all the surfaces surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { LL(surf->ident); LL(surf->flags); LL(surf->numFrames); LL(surf->numShaders); LL(surf->numTriangles); LL(surf->ofsTriangles); LL(surf->numVerts); LL(surf->ofsShaders); LL(surf->ofsSt); LL(surf->ofsXyzNormals); LL(surf->ofsEnd); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // change to surface identifier surf->ident = SF_MD3; // lowercase the surface name so skin compares are faster Q_strlwr( surf->name ); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess j = strlen( surf->name ); if ( j > 2 && surf->name[j-2] == '_' ) { surf->name[j-2] = 0; } // register the shaders shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { shader_t *sh; sh = R_FindShader( shader->name, LIGHTMAP_NONE, qtrue ); if ( sh->defaultShader ) { shader->shaderIndex = 0; } else { shader->shaderIndex = sh->index; } } // swap all the triangles tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { LL(tri->indexes[0]); LL(tri->indexes[1]); LL(tri->indexes[2]); } // swap all the ST st = (md3St_t *) ( (byte *)surf + surf->ofsSt ); for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { st->st[0] = LittleFloat( st->st[0] ); st->st[1] = LittleFloat( st->st[1] ); } // swap all the XyzNormals xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals ); for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) { xyz->xyz[0] = LittleShort( xyz->xyz[0] ); xyz->xyz[1] = LittleShort( xyz->xyz[1] ); xyz->xyz[2] = LittleShort( xyz->xyz[2] ); xyz->normal = LittleShort( xyz->normal ); } // find the next surface surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); } return qtrue; }
static bool R_LoadMd3Lod( idRenderModel* mod, int lod, const void* buffer, const char* mod_name ) { md3Header_t* pinmodel = ( md3Header_t* )buffer; int version = LittleLong( pinmodel->version ); if ( version != MD3_VERSION ) { common->Printf( S_COLOR_YELLOW "R_LoadMD3: %s has wrong version (%i should be %i)\n", mod_name, version, MD3_VERSION ); return false; } mod->type = MOD_MESH3; int size = LittleLong( pinmodel->ofsEnd ); mod->q3_dataSize += size; mod->q3_md3[ lod ].header = ( md3Header_t* )Mem_Alloc( size ); Com_Memcpy( mod->q3_md3[ lod ].header, buffer, LittleLong( pinmodel->ofsEnd ) ); LL( mod->q3_md3[ lod ].header->ident ); LL( mod->q3_md3[ lod ].header->version ); LL( mod->q3_md3[ lod ].header->numFrames ); LL( mod->q3_md3[ lod ].header->numTags ); LL( mod->q3_md3[ lod ].header->numSurfaces ); LL( mod->q3_md3[ lod ].header->ofsFrames ); LL( mod->q3_md3[ lod ].header->ofsTags ); LL( mod->q3_md3[ lod ].header->ofsSurfaces ); LL( mod->q3_md3[ lod ].header->ofsEnd ); if ( mod->q3_md3[ lod ].header->numFrames < 1 ) { common->Printf( S_COLOR_YELLOW "R_LoadMD3: %s has no frames\n", mod_name ); return false; } bool fixRadius = false; if ( GGameType & ( GAME_WolfSP | GAME_WolfMP | GAME_ET ) && ( strstr( mod->name,"sherman" ) || strstr( mod->name, "mg42" ) ) ) { fixRadius = true; } // swap all the frames md3Frame_t* frame = ( md3Frame_t* )( ( byte* )mod->q3_md3[ lod ].header + mod->q3_md3[ lod ].header->ofsFrames ); for ( int i = 0; i < mod->q3_md3[ lod ].header->numFrames; i++, frame++ ) { frame->radius = LittleFloat( frame->radius ); if ( fixRadius ) { frame->radius = 256; for ( int j = 0; j < 3; j++ ) { frame->bounds[ 0 ][ j ] = 128; frame->bounds[ 1 ][ j ] = -128; frame->localOrigin[ j ] = LittleFloat( frame->localOrigin[ j ] ); } } // Hack for Bug using plugin generated model else if ( GGameType & ( GAME_WolfSP | GAME_WolfMP | GAME_ET ) && frame->radius == 1 ) { frame->radius = 256; for ( int j = 0; j < 3; j++ ) { frame->bounds[ 0 ][ j ] = 128; frame->bounds[ 1 ][ j ] = -128; frame->localOrigin[ j ] = LittleFloat( frame->localOrigin[ j ] ); } } else { for ( int j = 0; j < 3; j++ ) { frame->bounds[ 0 ][ j ] = LittleFloat( frame->bounds[ 0 ][ j ] ); frame->bounds[ 1 ][ j ] = LittleFloat( frame->bounds[ 1 ][ j ] ); frame->localOrigin[ j ] = LittleFloat( frame->localOrigin[ j ] ); } } } // swap all the tags md3Tag_t* tag = ( md3Tag_t* )( ( byte* )mod->q3_md3[ lod ].header + mod->q3_md3[ lod ].header->ofsTags ); for ( int i = 0; i < mod->q3_md3[ lod ].header->numTags * mod->q3_md3[ lod ].header->numFrames; i++, tag++ ) { for ( int j = 0; j < 3; j++ ) { tag->origin[ j ] = LittleFloat( tag->origin[ j ] ); tag->axis[ 0 ][ j ] = LittleFloat( tag->axis[ 0 ][ j ] ); tag->axis[ 1 ][ j ] = LittleFloat( tag->axis[ 1 ][ j ] ); tag->axis[ 2 ][ j ] = LittleFloat( tag->axis[ 2 ][ j ] ); } } // swap all the surfaces mod->q3_md3[ lod ].surfaces = new idSurfaceMD3[ mod->q3_md3[ lod ].header->numSurfaces ]; md3Surface_t* surf = ( md3Surface_t* )( ( byte* )mod->q3_md3[ lod ].header + mod->q3_md3[ lod ].header->ofsSurfaces ); for ( int i = 0; i < mod->q3_md3[ lod ].header->numSurfaces; i++ ) { mod->q3_md3[ lod ].surfaces[ i ].SetMd3Data( surf ); LL( surf->ident ); LL( surf->flags ); LL( surf->numFrames ); LL( surf->numShaders ); LL( surf->numTriangles ); LL( surf->ofsTriangles ); LL( surf->numVerts ); LL( surf->ofsShaders ); LL( surf->ofsSt ); LL( surf->ofsXyzNormals ); LL( surf->ofsEnd ); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { common->Error( "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles * 3 > SHADER_MAX_INDEXES ) { common->Error( "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // lowercase the surface name so skin compares are faster String::ToLower( surf->name ); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess int Len = String::Length( surf->name ); if ( Len > 2 && surf->name[ Len - 2 ] == '_' ) { surf->name[ Len - 2 ] = 0; } // register the shaders md3Shader_t* shader = ( md3Shader_t* )( ( byte* )surf + surf->ofsShaders ); for ( int j = 0; j < surf->numShaders; j++, shader++ ) { shader_t* sh = R_FindShader( shader->name, LIGHTMAP_NONE, true ); if ( sh->defaultShader ) { shader->shaderIndex = 0; } else { shader->shaderIndex = sh->index; } } // swap all the triangles md3Triangle_t* tri = ( md3Triangle_t* )( ( byte* )surf + surf->ofsTriangles ); for ( int j = 0; j < surf->numTriangles; j++, tri++ ) { LL( tri->indexes[ 0 ] ); LL( tri->indexes[ 1 ] ); LL( tri->indexes[ 2 ] ); } // swap all the ST md3St_t* st = ( md3St_t* )( ( byte* )surf + surf->ofsSt ); for ( int j = 0; j < surf->numVerts; j++, st++ ) { st->st[ 0 ] = LittleFloat( st->st[ 0 ] ); st->st[ 1 ] = LittleFloat( st->st[ 1 ] ); } // swap all the XyzNormals md3XyzNormal_t* xyz = ( md3XyzNormal_t* )( ( byte* )surf + surf->ofsXyzNormals ); for ( int j = 0; j < surf->numVerts * surf->numFrames; j++, xyz++ ) { xyz->xyz[ 0 ] = LittleShort( xyz->xyz[ 0 ] ); xyz->xyz[ 1 ] = LittleShort( xyz->xyz[ 1 ] ); xyz->xyz[ 2 ] = LittleShort( xyz->xyz[ 2 ] ); xyz->normal = LittleShort( xyz->normal ); } // find the next surface surf = ( md3Surface_t* )( ( byte* )surf + surf->ofsEnd ); } return true; }
static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) { int i, j, k, lodindex; md4Header_t *pinmodel, *md4; md4Frame_t *frame; md4LOD_t *lod; md4Surface_t *surf; md4Triangle_t *tri; md4Vertex_t *v; int version; int size; shader_t *sh; int frameSize; pinmodel = (md4Header_t *)buffer; version = LittleLong (pinmodel->version); if (version != MD4_VERSION) { ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has wrong version (%i should be %i)\n", mod_name, version, MD4_VERSION); return qfalse; } mod->type = MOD_MD4; size = LittleLong(pinmodel->ofsEnd); mod->dataSize += size; md4 = mod->md4 = ri.Hunk_Alloc( size, h_low ); memcpy(md4, buffer, size); LL(md4->ident); LL(md4->version); LL(md4->numFrames); LL(md4->numBones); LL(md4->numLODs); LL(md4->ofsFrames); LL(md4->ofsLODs); md4->ofsEnd = size; if ( md4->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has no frames\n", mod_name ); return qfalse; } // we don't need to swap tags in the renderer, they aren't used // swap all the frames frameSize = (size_t)( &((md4Frame_t *)0)->bones[ md4->numBones ] ); for ( i = 0 ; i < md4->numFrames ; i++, frame++) { frame = (md4Frame_t *) ( (byte *)md4 + md4->ofsFrames + i * frameSize ); frame->radius = LittleFloat( frame->radius ); for ( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); } for ( j = 0 ; j < md4->numBones * sizeof( md4Bone_t ) / 4 ; j++ ) { ((float *)frame->bones)[j] = LittleFloat( ((float *)frame->bones)[j] ); } } // swap all the LOD's lod = (md4LOD_t *) ( (byte *)md4 + md4->ofsLODs ); for ( lodindex = 0 ; lodindex < md4->numLODs ; lodindex++ ) { // swap all the surfaces surf = (md4Surface_t *) ( (byte *)lod + lod->ofsSurfaces ); for ( i = 0 ; i < lod->numSurfaces ; i++) { LL(surf->ident); LL(surf->numTriangles); LL(surf->ofsTriangles); LL(surf->numVerts); LL(surf->ofsVerts); LL(surf->ofsEnd); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // change to surface identifier surf->ident = SF_MD4; // lowercase the surface name so skin compares are faster Q_strlwr( surf->name ); // register the shaders sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue ); if ( sh->defaultShader ) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } // swap all the triangles tri = (md4Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { LL(tri->indexes[0]); LL(tri->indexes[1]); LL(tri->indexes[2]); } // swap all the vertexes // FIXME // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left // in for reference. //v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts + 12); v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts); for ( j = 0 ; j < surf->numVerts ; j++ ) { v->normal[0] = LittleFloat( v->normal[0] ); v->normal[1] = LittleFloat( v->normal[1] ); v->normal[2] = LittleFloat( v->normal[2] ); v->texCoords[0] = LittleFloat( v->texCoords[0] ); v->texCoords[1] = LittleFloat( v->texCoords[1] ); v->numWeights = LittleLong( v->numWeights ); for ( k = 0 ; k < v->numWeights ; k++ ) { v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex ); v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight ); v->weights[k].offset[0] = LittleFloat( v->weights[k].offset[0] ); v->weights[k].offset[1] = LittleFloat( v->weights[k].offset[1] ); v->weights[k].offset[2] = LittleFloat( v->weights[k].offset[2] ); } // FIXME // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left // in for reference. //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights]); } // find the next surface surf = (md4Surface_t *)( (byte *)surf + surf->ofsEnd ); } // find the next LOD lod = (md4LOD_t *)( (byte *)lod + lod->ofsEnd ); } return qtrue; }
/* ================= R_LoadMDM ================= */ qboolean R_LoadMDM( model_t *mod, void *buffer, const char *modName ) { int i, j, k; mdmHeader_t *mdm; // mdmFrame_t *frame; mdmSurface_t *mdmSurf; mdmTriangle_t *mdmTri; mdmVertex_t *mdmVertex; mdmTag_t *mdmTag; int version; // int size; shader_t *sh; int32_t *collapseMap, *collapseMapOut, *boneref, *bonerefOut; mdmModel_t *mdmModel; mdmTagIntern_t *tag; mdmSurfaceIntern_t *surf; srfTriangle_t *tri; md5Vertex_t *v; mdm = ( mdmHeader_t * ) buffer; version = LittleLong( mdm->version ); if ( version != MDM_VERSION ) { ri.Printf( PRINT_WARNING, "R_LoadMDM: %s has wrong version (%i should be %i)\n", modName, version, MDM_VERSION ); return qfalse; } mod->type = MOD_MDM; // size = LittleLong(mdm->ofsEnd); mod->dataSize += sizeof( mdmModel_t ); //mdm = mod->mdm = ri.Hunk_Alloc(size, h_low); //memcpy(mdm, buffer, LittleLong(pinmodel->ofsEnd)); mdmModel = mod->mdm = ri.Hunk_Alloc( sizeof( mdmModel_t ), h_low ); LL( mdm->ident ); LL( mdm->version ); // LL(mdm->numFrames); LL( mdm->numTags ); LL( mdm->numSurfaces ); // LL(mdm->ofsFrames); LL( mdm->ofsTags ); LL( mdm->ofsEnd ); LL( mdm->ofsSurfaces ); mdmModel->lodBias = LittleFloat( mdm->lodBias ); mdmModel->lodScale = LittleFloat( mdm->lodScale ); /* mdm->skel = RE_RegisterModel(mdm->bonesfile); if ( !mdm->skel ) { ri.Error (ERR_DROP, "R_LoadMDM: %s skeleton not found", mdm->bonesfile ); } if ( mdm->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMDM: %s has no frames\n", modName ); return qfalse; }*/ // swap all the frames /*frameSize = (int) ( sizeof( mdmFrame_t ) ); for ( i = 0 ; i < mdm->numFrames ; i++, frame++) { frame = (mdmFrame_t *) ( (byte *)mdm + mdm->ofsFrames + i * frameSize ); frame->radius = LittleFloat( frame->radius ); for ( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); frame->parentOffset[j] = LittleFloat( frame->parentOffset[j] ); } } */ // swap all the tags mdmModel->numTags = mdm->numTags; mdmModel->tags = tag = ri.Hunk_Alloc( sizeof( *tag ) * mdm->numTags, h_low ); mdmTag = ( mdmTag_t * )( ( byte * ) mdm + mdm->ofsTags ); for ( i = 0; i < mdm->numTags; i++, tag++ ) { int ii; Q_strncpyz( tag->name, mdmTag->name, sizeof( tag->name ) ); for ( ii = 0; ii < 3; ii++ ) { tag->axis[ ii ][ 0 ] = LittleFloat( mdmTag->axis[ ii ][ 0 ] ); tag->axis[ ii ][ 1 ] = LittleFloat( mdmTag->axis[ ii ][ 1 ] ); tag->axis[ ii ][ 2 ] = LittleFloat( mdmTag->axis[ ii ][ 2 ] ); } tag->boneIndex = LittleLong( mdmTag->boneIndex ); //tag->torsoWeight = LittleFloat( tag->torsoWeight ); tag->offset[ 0 ] = LittleFloat( mdmTag->offset[ 0 ] ); tag->offset[ 1 ] = LittleFloat( mdmTag->offset[ 1 ] ); tag->offset[ 2 ] = LittleFloat( mdmTag->offset[ 2 ] ); LL( mdmTag->numBoneReferences ); LL( mdmTag->ofsBoneReferences ); LL( mdmTag->ofsEnd ); tag->numBoneReferences = mdmTag->numBoneReferences; tag->boneReferences = ri.Hunk_Alloc( sizeof( *bonerefOut ) * mdmTag->numBoneReferences, h_low ); // swap the bone references boneref = ( int32_t * )( ( byte * ) mdmTag + mdmTag->ofsBoneReferences ); for ( j = 0, bonerefOut = tag->boneReferences; j < mdmTag->numBoneReferences; j++, boneref++, bonerefOut++ ) { *bonerefOut = LittleLong( *boneref ); } // find the next tag mdmTag = ( mdmTag_t * )( ( byte * ) mdmTag + mdmTag->ofsEnd ); } // swap all the surfaces mdmModel->numSurfaces = mdm->numSurfaces; mdmModel->surfaces = ri.Hunk_Alloc( sizeof( *surf ) * mdmModel->numSurfaces, h_low ); mdmSurf = ( mdmSurface_t * )( ( byte * ) mdm + mdm->ofsSurfaces ); for ( i = 0, surf = mdmModel->surfaces; i < mdm->numSurfaces; i++, surf++ ) { LL( mdmSurf->shaderIndex ); LL( mdmSurf->ofsHeader ); LL( mdmSurf->ofsCollapseMap ); LL( mdmSurf->numTriangles ); LL( mdmSurf->ofsTriangles ); LL( mdmSurf->numVerts ); LL( mdmSurf->ofsVerts ); LL( mdmSurf->numBoneReferences ); LL( mdmSurf->ofsBoneReferences ); LL( mdmSurf->ofsEnd ); surf->minLod = LittleLong( mdmSurf->minLod ); // change to surface identifier surf->surfaceType = SF_MDM; surf->model = mdmModel; Q_strncpyz( surf->name, mdmSurf->name, sizeof( surf->name ) ); if ( mdmSurf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error( ERR_DROP, "R_LoadMDM: %s has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, mdmSurf->numVerts ); } if ( mdmSurf->numTriangles > SHADER_MAX_TRIANGLES ) { ri.Error( ERR_DROP, "R_LoadMDM: %s has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, mdmSurf->numTriangles ); } // register the shaders if ( mdmSurf->shader[ 0 ] ) { Q_strncpyz( surf->shader, mdmSurf->shader, sizeof( surf->shader ) ); sh = R_FindShader( surf->shader, SHADER_3D_DYNAMIC, qtrue ); if ( sh->defaultShader ) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } } else { surf->shaderIndex = 0; } // swap all the triangles surf->numTriangles = mdmSurf->numTriangles; surf->triangles = ri.Hunk_Alloc( sizeof( *tri ) * surf->numTriangles, h_low ); mdmTri = ( mdmTriangle_t * )( ( byte * ) mdmSurf + mdmSurf->ofsTriangles ); for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, mdmTri++, tri++ ) { tri->indexes[ 0 ] = LittleLong( mdmTri->indexes[ 0 ] ); tri->indexes[ 1 ] = LittleLong( mdmTri->indexes[ 1 ] ); tri->indexes[ 2 ] = LittleLong( mdmTri->indexes[ 2 ] ); } // swap all the vertexes surf->numVerts = mdmSurf->numVerts; surf->verts = ri.Hunk_Alloc( sizeof( *v ) * surf->numVerts, h_low ); mdmVertex = ( mdmVertex_t * )( ( byte * ) mdmSurf + mdmSurf->ofsVerts ); for ( j = 0, v = surf->verts; j < mdmSurf->numVerts; j++, v++ ) { v->normal[ 0 ] = LittleFloat( mdmVertex->normal[ 0 ] ); v->normal[ 1 ] = LittleFloat( mdmVertex->normal[ 1 ] ); v->normal[ 2 ] = LittleFloat( mdmVertex->normal[ 2 ] ); v->texCoords[ 0 ] = LittleFloat( mdmVertex->texCoords[ 0 ] ); v->texCoords[ 1 ] = LittleFloat( mdmVertex->texCoords[ 1 ] ); v->numWeights = LittleLong( mdmVertex->numWeights ); if ( v->numWeights > MAX_WEIGHTS ) { #if 0 ri.Error( ERR_DROP, "R_LoadMDM: vertex %i requires %i instead of maximum %i weights on surface (%i) in model '%s'", j, v->numWeights, MAX_WEIGHTS, i, modName ); #else ri.Printf( PRINT_WARNING, "WARNING: R_LoadMDM: vertex %i requires %i instead of maximum %i weights on surface (%i) in model '%s'\n", j, v->numWeights, MAX_WEIGHTS, i, modName ); #endif } v->weights = ri.Hunk_Alloc( sizeof( *v->weights ) * v->numWeights, h_low ); for ( k = 0; k < v->numWeights; k++ ) { md5Weight_t *weight = ri.Hunk_Alloc( sizeof( *weight ), h_low ); weight->boneIndex = LittleLong( mdmVertex->weights[ k ].boneIndex ); weight->boneWeight = LittleFloat( mdmVertex->weights[ k ].boneWeight ); weight->offset[ 0 ] = LittleFloat( mdmVertex->weights[ k ].offset[ 0 ] ); weight->offset[ 1 ] = LittleFloat( mdmVertex->weights[ k ].offset[ 1 ] ); weight->offset[ 2 ] = LittleFloat( mdmVertex->weights[ k ].offset[ 2 ] ); v->weights[ k ] = weight; } mdmVertex = ( mdmVertex_t * ) &mdmVertex->weights[ v->numWeights ]; } // swap the collapse map surf->collapseMap = ri.Hunk_Alloc( sizeof( *collapseMapOut ) * mdmSurf->numVerts, h_low ); collapseMap = ( int32_t * )( ( byte * ) mdmSurf + mdmSurf->ofsCollapseMap ); //ri.Printf(PRINT_ALL, "collapse map for mdm surface '%s': ", surf->name); for ( j = 0, collapseMapOut = surf->collapseMap; j < mdmSurf->numVerts; j++, collapseMap++, collapseMapOut++ ) { int32_t value = LittleLong( *collapseMap ); //surf->collapseMap[j] = value; *collapseMapOut = value; //ri.Printf(PRINT_ALL, "(%i -> %i) ", j, value); } //ri.Printf(PRINT_ALL, "\n"); #if 0 ri.Printf( PRINT_ALL, "collapse map for mdm surface '%s': ", surf->name ); for ( j = 0, collapseMap = surf->collapseMap; j < mdmSurf->numVerts; j++, collapseMap++ ) { ri.Printf( PRINT_ALL, "(%i -> %i) ", j, *collapseMap ); } ri.Printf( PRINT_ALL, "\n" ); #endif // swap the bone references surf->numBoneReferences = mdmSurf->numBoneReferences; surf->boneReferences = ri.Hunk_Alloc( sizeof( *bonerefOut ) * mdmSurf->numBoneReferences, h_low ); boneref = ( int32_t * )( ( byte * ) mdmSurf + mdmSurf->ofsBoneReferences ); for ( j = 0, bonerefOut = surf->boneReferences; j < surf->numBoneReferences; j++, boneref++, bonerefOut++ ) { *bonerefOut = LittleLong( *boneref ); } // find the next surface mdmSurf = ( mdmSurface_t * )( ( byte * ) mdmSurf + mdmSurf->ofsEnd ); } // loading is done now calculate the bounding box and tangent spaces ClearBounds( mdmModel->bounds[ 0 ], mdmModel->bounds[ 1 ] ); for ( i = 0, surf = mdmModel->surfaces; i < mdmModel->numSurfaces; i++, surf++ ) { for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { vec3_t tmpVert; md5Weight_t *w; VectorClear( tmpVert ); for ( k = 0, w = v->weights[ 0 ]; k < v->numWeights; k++, w++ ) { //vec3_t offsetVec; //VectorClear(offsetVec); //bone = &md5->bones[w->boneIndex]; //QuatTransformVector(bone->rotation, w->offset, offsetVec); //VectorAdd(bone->origin, offsetVec, offsetVec); VectorMA( tmpVert, w->boneWeight, w->offset, tmpVert ); } VectorCopy( tmpVert, v->position ); AddPointToBounds( tmpVert, mdmModel->bounds[ 0 ], mdmModel->bounds[ 1 ] ); } // calc tangent spaces #if 0 { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { VectorClear( v->tangent ); VectorClear( v->binormal ); VectorClear( v->normal ); } for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { v0 = surf->verts[ tri->indexes[ 0 ] ].position; v1 = surf->verts[ tri->indexes[ 1 ] ].position; v2 = surf->verts[ tri->indexes[ 2 ] ].position; t0 = surf->verts[ tri->indexes[ 0 ] ].texCoords; t1 = surf->verts[ tri->indexes[ 1 ] ].texCoords; t2 = surf->verts[ tri->indexes[ 2 ] ].texCoords; #if 1 R_CalcTangentSpace( tangent, binormal, normal, v0, v1, v2, t0, t1, t2 ); #else R_CalcNormalForTriangle( normal, v0, v1, v2 ); R_CalcTangentsForTriangle( tangent, binormal, v0, v1, v2, t0, t1, t2 ); #endif for ( k = 0; k < 3; k++ ) { float *v; v = surf->verts[ tri->indexes[ k ] ].tangent; VectorAdd( v, tangent, v ); v = surf->verts[ tri->indexes[ k ] ].binormal; VectorAdd( v, binormal, v ); v = surf->verts[ tri->indexes[ k ] ].normal; VectorAdd( v, normal, v ); } } for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { VectorNormalize( v->tangent ); VectorNormalize( v->binormal ); VectorNormalize( v->normal ); } } #else { int k; float bb, s, t; vec3_t bary; vec3_t faceNormal; md5Vertex_t *dv[ 3 ]; for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { dv[ 0 ] = &surf->verts[ tri->indexes[ 0 ] ]; dv[ 1 ] = &surf->verts[ tri->indexes[ 1 ] ]; dv[ 2 ] = &surf->verts[ tri->indexes[ 2 ] ]; R_CalcNormalForTriangle( faceNormal, dv[ 0 ]->position, dv[ 1 ]->position, dv[ 2 ]->position ); // calculate barycentric basis for the triangle bb = ( dv[ 1 ]->texCoords[ 0 ] - dv[ 0 ]->texCoords[ 0 ] ) * ( dv[ 2 ]->texCoords[ 1 ] - dv[ 0 ]->texCoords[ 1 ] ) - ( dv[ 2 ]->texCoords[ 0 ] - dv[ 0 ]->texCoords[ 0 ] ) * ( dv[ 1 ]->texCoords[ 1 ] - dv[ 0 ]->texCoords[ 1 ] ); if ( fabs( bb ) < 0.00000001f ) { continue; } // do each vertex for ( k = 0; k < 3; k++ ) { // calculate s tangent vector s = dv[ k ]->texCoords[ 0 ] + 10.0f; t = dv[ k ]->texCoords[ 1 ]; bary[ 0 ] = ( ( dv[ 1 ]->texCoords[ 0 ] - s ) * ( dv[ 2 ]->texCoords[ 1 ] - t ) - ( dv[ 2 ]->texCoords[ 0 ] - s ) * ( dv[ 1 ]->texCoords[ 1 ] - t ) ) / bb; bary[ 1 ] = ( ( dv[ 2 ]->texCoords[ 0 ] - s ) * ( dv[ 0 ]->texCoords[ 1 ] - t ) - ( dv[ 0 ]->texCoords[ 0 ] - s ) * ( dv[ 2 ]->texCoords[ 1 ] - t ) ) / bb; bary[ 2 ] = ( ( dv[ 0 ]->texCoords[ 0 ] - s ) * ( dv[ 1 ]->texCoords[ 1 ] - t ) - ( dv[ 1 ]->texCoords[ 0 ] - s ) * ( dv[ 0 ]->texCoords[ 1 ] - t ) ) / bb; dv[ k ]->tangent[ 0 ] = bary[ 0 ] * dv[ 0 ]->position[ 0 ] + bary[ 1 ] * dv[ 1 ]->position[ 0 ] + bary[ 2 ] * dv[ 2 ]->position[ 0 ]; dv[ k ]->tangent[ 1 ] = bary[ 0 ] * dv[ 0 ]->position[ 1 ] + bary[ 1 ] * dv[ 1 ]->position[ 1 ] + bary[ 2 ] * dv[ 2 ]->position[ 1 ]; dv[ k ]->tangent[ 2 ] = bary[ 0 ] * dv[ 0 ]->position[ 2 ] + bary[ 1 ] * dv[ 1 ]->position[ 2 ] + bary[ 2 ] * dv[ 2 ]->position[ 2 ]; VectorSubtract( dv[ k ]->tangent, dv[ k ]->position, dv[ k ]->tangent ); VectorNormalize( dv[ k ]->tangent ); // calculate t tangent vector (binormal) s = dv[ k ]->texCoords[ 0 ]; t = dv[ k ]->texCoords[ 1 ] + 10.0f; bary[ 0 ] = ( ( dv[ 1 ]->texCoords[ 0 ] - s ) * ( dv[ 2 ]->texCoords[ 1 ] - t ) - ( dv[ 2 ]->texCoords[ 0 ] - s ) * ( dv[ 1 ]->texCoords[ 1 ] - t ) ) / bb; bary[ 1 ] = ( ( dv[ 2 ]->texCoords[ 0 ] - s ) * ( dv[ 0 ]->texCoords[ 1 ] - t ) - ( dv[ 0 ]->texCoords[ 0 ] - s ) * ( dv[ 2 ]->texCoords[ 1 ] - t ) ) / bb; bary[ 2 ] = ( ( dv[ 0 ]->texCoords[ 0 ] - s ) * ( dv[ 1 ]->texCoords[ 1 ] - t ) - ( dv[ 1 ]->texCoords[ 0 ] - s ) * ( dv[ 0 ]->texCoords[ 1 ] - t ) ) / bb; dv[ k ]->binormal[ 0 ] = bary[ 0 ] * dv[ 0 ]->position[ 0 ] + bary[ 1 ] * dv[ 1 ]->position[ 0 ] + bary[ 2 ] * dv[ 2 ]->position[ 0 ]; dv[ k ]->binormal[ 1 ] = bary[ 0 ] * dv[ 0 ]->position[ 1 ] + bary[ 1 ] * dv[ 1 ]->position[ 1 ] + bary[ 2 ] * dv[ 2 ]->position[ 1 ]; dv[ k ]->binormal[ 2 ] = bary[ 0 ] * dv[ 0 ]->position[ 2 ] + bary[ 1 ] * dv[ 1 ]->position[ 2 ] + bary[ 2 ] * dv[ 2 ]->position[ 2 ]; VectorSubtract( dv[ k ]->binormal, dv[ k ]->position, dv[ k ]->binormal ); VectorNormalize( dv[ k ]->binormal ); // calculate the normal as cross product N=TxB #if 0 CrossProduct( dv[ k ]->tangent, dv[ k ]->binormal, dv[ k ]->normal ); VectorNormalize( dv[ k ]->normal ); // Gram-Schmidt orthogonalization process for B // compute the cross product B=NxT to obtain // an orthogonal basis CrossProduct( dv[ k ]->normal, dv[ k ]->tangent, dv[ k ]->binormal ); if ( DotProduct( dv[ k ]->normal, faceNormal ) < 0 ) { VectorInverse( dv[ k ]->normal ); //VectorInverse(dv[k]->tangent); //VectorInverse(dv[k]->binormal); } #else //VectorAdd(dv[k]->normal, faceNormal, dv[k]->normal); #endif } } #if 1 for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { //VectorNormalize(v->tangent); //VectorNormalize(v->binormal); //VectorNormalize(v->normal); } #endif } #endif #if 0 // do another extra smoothing for normals to avoid flat shading for ( j = 0; j < surf->numVerts; j++ ) { for ( k = 0; k < surf->numVerts; k++ ) { if ( j == k ) { continue; } if ( VectorCompare( surf->verts[ j ].position, surf->verts[ k ].position ) ) { VectorAdd( surf->verts[ j ].normal, surf->verts[ k ].normal, surf->verts[ j ].normal ); } } VectorNormalize( surf->verts[ j ].normal ); } #endif } // split the surfaces into VBO surfaces by the maximum number of GPU vertex skinning bones { int numRemaining; growList_t sortedTriangles; growList_t vboTriangles; growList_t vboSurfaces; int numBoneReferences; int boneReferences[ MAX_BONES ]; Com_InitGrowList( &vboSurfaces, 10 ); for ( i = 0, surf = mdmModel->surfaces; i < mdmModel->numSurfaces; i++, surf++ ) { // sort triangles Com_InitGrowList( &sortedTriangles, 1000 ); for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { skelTriangle_t *sortTri = Com_Allocate( sizeof( *sortTri ) ); for ( k = 0; k < 3; k++ ) { sortTri->indexes[ k ] = tri->indexes[ k ]; sortTri->vertexes[ k ] = &surf->verts[ tri->indexes[ k ] ]; } sortTri->referenced = qfalse; Com_AddToGrowList( &sortedTriangles, sortTri ); } //qsort(sortedTriangles.elements, sortedTriangles.currentElements, sizeof(void *), CompareTrianglesByBoneReferences); #if 0 for ( j = 0; j < sortedTriangles.currentElements; j++ ) { int b[ MAX_WEIGHTS * 3 ]; skelTriangle_t *sortTri = Com_GrowListElement( &sortedTriangles, j ); for ( k = 0; k < 3; k++ ) { v = sortTri->vertexes[ k ]; for ( l = 0; l < MAX_WEIGHTS; l++ ) { b[ k * 3 + l ] = ( l < v->numWeights ) ? v->weights[ l ]->boneIndex : 9999; } qsort( b, MAX_WEIGHTS * 3, sizeof( int ), CompareBoneIndices ); //ri.Printf(PRINT_ALL, "bone indices: %i %i %i %i\n", b[k * 3 + 0], b[k * 3 + 1], b[k * 3 + 2], b[k * 3 + 3]); } } #endif numRemaining = sortedTriangles.currentElements; while ( numRemaining ) { numBoneReferences = 0; Com_Memset( boneReferences, 0, sizeof( boneReferences ) ); Com_InitGrowList( &vboTriangles, 1000 ); for ( j = 0; j < sortedTriangles.currentElements; j++ ) { skelTriangle_t *sortTri = Com_GrowListElement( &sortedTriangles, j ); if ( sortTri->referenced ) { continue; } if ( AddTriangleToVBOTriangleList( &vboTriangles, sortTri, &numBoneReferences, boneReferences ) ) { sortTri->referenced = qtrue; } } if ( !vboTriangles.currentElements ) { ri.Printf( PRINT_WARNING, "R_LoadMDM: could not add triangles to a remaining VBO surface for model '%s'\n", modName ); break; } AddSurfaceToVBOSurfacesListMDM( &vboSurfaces, &vboTriangles, mdmModel, surf, i, numBoneReferences, boneReferences ); numRemaining -= vboTriangles.currentElements; Com_DestroyGrowList( &vboTriangles ); } for ( j = 0; j < sortedTriangles.currentElements; j++ ) { skelTriangle_t *sortTri = Com_GrowListElement( &sortedTriangles, j ); Com_Dealloc( sortTri ); } Com_DestroyGrowList( &sortedTriangles ); } // move VBO surfaces list to hunk mdmModel->numVBOSurfaces = vboSurfaces.currentElements; mdmModel->vboSurfaces = ri.Hunk_Alloc( mdmModel->numVBOSurfaces * sizeof( *mdmModel->vboSurfaces ), h_low ); for ( i = 0; i < mdmModel->numVBOSurfaces; i++ ) { mdmModel->vboSurfaces[ i ] = ( srfVBOMDMMesh_t * ) Com_GrowListElement( &vboSurfaces, i ); } Com_DestroyGrowList( &vboSurfaces ); } return qtrue; }
/* ================= R_LoadFogs ================= */ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) { int i; fog_t *out; dfog_t *fogs; dbrush_t *brushes, *brush; dbrushside_t *sides; int count, brushesCount, sidesCount; int sideNum; int planeNum; shader_t *shader; float d; int firstSide=0; int lightmaps[MAXLIGHTMAPS] = { LIGHTMAP_NONE } ; fogs = (dfog_t *)(fileBase + l->fileofs); if (l->filelen % sizeof(*fogs)) { ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); } count = l->filelen / sizeof(*fogs); // create fog structres for them // NOTE: we allocate memory for an extra one so that the LA goggles can turn on their own fog s_worldData.numfogs = count + 1; s_worldData.fogs = (fog_t *)ri.Hunk_Alloc (( s_worldData.numfogs + 1)*sizeof(*out), qtrue ); s_worldData.globalFog = -1; out = s_worldData.fogs + 1; if ( !count ) { return; } brushes = (dbrush_t *)(fileBase + brushesLump->fileofs); if (brushesLump->filelen % sizeof(*brushes)) { ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); } brushesCount = brushesLump->filelen / sizeof(*brushes); sides = (dbrushside_t *)(fileBase + sidesLump->fileofs); if (sidesLump->filelen % sizeof(*sides)) { ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); } sidesCount = sidesLump->filelen / sizeof(*sides); for ( i=0 ; i<count ; i++, fogs++) { out->originalBrushNumber = LittleLong( fogs->brushNum ); if (out->originalBrushNumber == -1) { out->bounds[0][0] = out->bounds[0][1] = out->bounds[0][2] = MIN_WORLD_COORD; out->bounds[1][0] = out->bounds[1][1] = out->bounds[1][2] = MAX_WORLD_COORD; s_worldData.globalFog = i+1; } else { if ( (unsigned)out->originalBrushNumber >= brushesCount ) { ri.Error( ERR_DROP, "fog brushNumber out of range" ); } brush = brushes + out->originalBrushNumber; firstSide = LittleLong( brush->firstSide ); if ( (unsigned)firstSide > sidesCount - 6 ) { ri.Error( ERR_DROP, "fog brush sideNumber out of range" ); } // brushes are always sorted with the axial sides first sideNum = firstSide + 0; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 1; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[1][0] = s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 2; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 3; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[1][1] = s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 4; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 5; planeNum = LittleLong( sides[ sideNum ].planeNum ); out->bounds[1][2] = s_worldData.planes[ planeNum ].dist; } // get information from the shader for fog parameters shader = R_FindShader( fogs->shader, lightmaps, stylesDefault, qtrue ); out->parms = shader->fogParms; out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight, shader->fogParms.color[1] * tr.identityLight, shader->fogParms.color[2] * tr.identityLight, 1.0 ); d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque; out->tcScale = 1.0 / ( d * 8 ); // set the gradient vector sideNum = LittleLong( fogs->visibleSide ); if ( sideNum == -1 ) { out->hasSurface = qfalse; } else { out->hasSurface = qtrue; planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); out->surface[3] = -s_worldData.planes[ planeNum ].dist; } out++; } // Initialise the last fog so we can use it with the LA Goggles // NOTE: We are might appear to be off the end of the array, but we allocated an extra memory slot above but [purposely] didn't // increment the total world numFogs to match our array size VectorSet(out->bounds[0], MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD); VectorSet(out->bounds[1], MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD); out->originalBrushNumber = -1; out->parms.color[0] = 0.0f; out->parms.color[1] = 0.0f; out->parms.color[2] = 0.0f; out->parms.color[3] = 0.0f; out->parms.depthForOpaque = 0.0f; out->colorInt = 0x00000000; out->tcScale = 0.0f; out->hasSurface = false; }
void AddSurfaceToVBOSurfacesList2(growList_t *vboSurfaces, growList_t *vboTriangles, growList_t *vboVertexes, md5Model_t *md5, int skinIndex, const char *materialName, int numBoneReferences, int boneReferences[MAX_BONES]) { int j, k; int vertexesNum; byte *data; int dataSize; int dataOfs; GLuint ofsTexCoords; GLuint ofsTangents; GLuint ofsBinormals; GLuint ofsNormals; GLuint ofsBoneIndexes; GLuint ofsBoneWeights; int indexesNum; byte *indexes; int indexesSize; int indexesOfs; skelTriangle_t *tri; vec4_t tmp; int index; srfVBOMD5Mesh_t *vboSurf; md5Vertex_t *v; shader_t *shader; int shaderIndex; vertexesNum = vboVertexes->currentElements; indexesNum = vboTriangles->currentElements * 3; // create surface vboSurf = ri.Hunk_Alloc(sizeof(*vboSurf), h_low); Com_AddToGrowList(vboSurfaces, vboSurf); vboSurf->surfaceType = SF_VBO_MD5MESH; vboSurf->md5Model = md5; ri.Printf(PRINT_ALL, "AddSurfaceToVBOSurfacesList2: loading shader '%s'\n", materialName); shader = R_FindShader(materialName, SHADER_3D_DYNAMIC, qtrue); if (shader->defaultShader) { shaderIndex = 0; } else { shaderIndex = shader->index; } vboSurf->shader = R_GetShaderByHandle(shaderIndex); vboSurf->skinIndex = skinIndex; vboSurf->numIndexes = indexesNum; vboSurf->numVerts = vertexesNum; dataSize = vertexesNum * (sizeof(vec4_t) * 8); data = ri.Hunk_AllocateTempMemory(dataSize); dataOfs = 0; indexesSize = indexesNum * sizeof(int); indexes = ri.Hunk_AllocateTempMemory(indexesSize); indexesOfs = 0; //ri.Printf(PRINT_ALL, "AddSurfaceToVBOSurfacesList( %i verts, %i tris )\n", surf->numVerts, vboTriangles->currentElements); vboSurf->numBoneRemap = 0; Com_Memset(vboSurf->boneRemap, 0, sizeof(vboSurf->boneRemap)); Com_Memset(vboSurf->boneRemapInverse, 0, sizeof(vboSurf->boneRemapInverse)); //ri.Printf(PRINT_ALL, "referenced bones: "); for (j = 0; j < MAX_BONES; j++) { if (boneReferences[j] > 0) { vboSurf->boneRemap[j] = vboSurf->numBoneRemap; vboSurf->boneRemapInverse[vboSurf->numBoneRemap] = j; vboSurf->numBoneRemap++; //ri.Printf(PRINT_ALL, "(%i -> %i) ", j, vboSurf->boneRemap[j]); } } //ri.Printf(PRINT_ALL, "\n"); //for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) for (j = 0; j < vboTriangles->currentElements; j++) { tri = Com_GrowListElement(vboTriangles, j); for (k = 0; k < 3; k++) { index = tri->indexes[k]; Com_Memcpy(indexes + indexesOfs, &index, sizeof(int)); indexesOfs += sizeof(int); } } // feed vertex XYZ for (j = 0; j < vertexesNum; j++) { v = Com_GrowListElement(vboVertexes, j); for (k = 0; k < 3; k++) { tmp[k] = v->position[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } // feed vertex texcoords ofsTexCoords = dataOfs; for (j = 0; j < vertexesNum; j++) { v = Com_GrowListElement(vboVertexes, j); for (k = 0; k < 2; k++) { tmp[k] = v->texCoords[k]; } tmp[2] = 0; tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } // feed vertex tangents ofsTangents = dataOfs; for (j = 0; j < vertexesNum; j++) { v = Com_GrowListElement(vboVertexes, j); for (k = 0; k < 3; k++) { tmp[k] = v->tangent[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } // feed vertex binormals ofsBinormals = dataOfs; for (j = 0; j < vertexesNum; j++) { v = Com_GrowListElement(vboVertexes, j); for (k = 0; k < 3; k++) { tmp[k] = v->binormal[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } // feed vertex normals ofsNormals = dataOfs; for (j = 0; j < vertexesNum; j++) { v = Com_GrowListElement(vboVertexes, j); for (k = 0; k < 3; k++) { tmp[k] = v->normal[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } // feed bone indices ofsBoneIndexes = dataOfs; for (j = 0; j < vertexesNum; j++) { v = Com_GrowListElement(vboVertexes, j); for (k = 0; k < MAX_WEIGHTS; k++) { if (k < v->numWeights) { index = vboSurf->boneRemap[v->weights[k]->boneIndex]; } else { index = 0; } Com_Memcpy(data + dataOfs, &index, sizeof(int)); dataOfs += sizeof(int); } } // feed bone weights ofsBoneWeights = dataOfs; for (j = 0; j < vertexesNum; j++) { v = Com_GrowListElement(vboVertexes, j); for (k = 0; k < MAX_WEIGHTS; k++) { if (k < v->numWeights) { tmp[k] = v->weights[k]->boneWeight; } else { tmp[k] = 0; } } Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } vboSurf->vbo = R_CreateVBO(va("staticMD5Mesh_VBO %i", vboSurfaces->currentElements), data, dataSize, VBO_USAGE_STATIC); vboSurf->vbo->ofsXYZ = 0; vboSurf->vbo->ofsTexCoords = ofsTexCoords; vboSurf->vbo->ofsLightCoords = ofsTexCoords; vboSurf->vbo->ofsTangents = ofsTangents; vboSurf->vbo->ofsBinormals = ofsBinormals; vboSurf->vbo->ofsNormals = ofsNormals; vboSurf->vbo->ofsBoneIndexes = ofsBoneIndexes; vboSurf->vbo->ofsBoneWeights = ofsBoneWeights; vboSurf->ibo = R_CreateIBO(va("staticMD5Mesh_IBO %i", vboSurfaces->currentElements), indexes, indexesSize, VBO_USAGE_STATIC); ri.Hunk_FreeTempMemory(indexes); ri.Hunk_FreeTempMemory(data); // megs /* ri.Printf(PRINT_ALL, "md5 mesh data VBO size: %d.%02d MB\n", dataSize / (1024 * 1024), (dataSize % (1024 * 1024)) * 100 / (1024 * 1024)); ri.Printf(PRINT_ALL, "md5 mesh tris VBO size: %d.%02d MB\n", indexesSize / (1024 * 1024), (indexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); */ ri.Printf(PRINT_ALL, "created VBO surface %i with %i vertices and %i triangles\n", vboSurfaces->currentElements, vboSurf->numVerts, vboSurf->numIndexes / 3); }