static int MakeMeshTriangles(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], srfTriangle_t triangles[SHADER_MAX_TRIANGLES]) { int i, j; int numTriangles; int w, h; srfVert_t *dv; static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE]; h = height - 1; w = width - 1; numTriangles = 0; for(i = 0; i < h; i++) { for(j = 0; j < w; j++) { int v1, v2, v3, v4; // vertex order to be reckognized as tristrips v1 = i * width + j + 1; v2 = v1 - 1; v3 = v2 + width; v4 = v3 + 1; triangles[numTriangles].indexes[0] = v2; triangles[numTriangles].indexes[1] = v3; triangles[numTriangles].indexes[2] = v1; numTriangles++; triangles[numTriangles].indexes[0] = v1; triangles[numTriangles].indexes[1] = v3; triangles[numTriangles].indexes[2] = v4; numTriangles++; } } R_CalcSurfaceTriangleNeighbors(numTriangles, triangles); // FIXME: use more elegant way for(i = 0; i < width; i++) { for(j = 0; j < height; j++) { dv = &ctrl2[j * width + i]; *dv = ctrl[j][i]; } } R_CalcSurfaceTrianglePlanes(numTriangles, triangles, ctrl2); return numTriangles; }
/* ================= R_LoadMD3 ================= */ qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, int bufferSize, const char *modName) { int i, j, k;//, l; md3Header_t *md3Model; md3Frame_t *md3Frame; md3Surface_t *md3Surf; md3Shader_t *md3Shader; md3Triangle_t *md3Tri; md3St_t *md3st; md3XyzNormal_t *md3xyz; md3Tag_t *md3Tag; mdvModel_t *mdvModel; mdvFrame_t *frame; mdvSurface_t *surf;//, *surface; srfTriangle_t *tri; mdvXyz_t *v; mdvSt_t *st; mdvTag_t *tag; mdvTagName_t *tagName; int version; int size; md3Model = (md3Header_t *) buffer; version = LittleLong(md3Model->version); if(version != MD3_VERSION) { ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MD3_VERSION); return qfalse; } mod->type = MOD_MESH; size = LittleLong(md3Model->ofsEnd); mod->dataSize += size; mdvModel = mod->mdv[lod] = (mdvModel_t*)ri.Hunk_Alloc(sizeof(mdvModel_t), h_low); // Com_Memcpy(mod->md3[lod], buffer, LittleLong(md3Model->ofsEnd)); LL(md3Model->ident); LL(md3Model->version); LL(md3Model->numFrames); LL(md3Model->numTags); LL(md3Model->numSurfaces); LL(md3Model->ofsFrames); LL(md3Model->ofsTags); LL(md3Model->ofsSurfaces); LL(md3Model->ofsEnd); if(md3Model->numFrames < 1) { ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has no frames\n", modName); return qfalse; } // swap all the frames mdvModel->numFrames = md3Model->numFrames; mdvModel->frames = frame = (mdvFrame_t*)ri.Hunk_Alloc(sizeof(*frame) * md3Model->numFrames, h_low); md3Frame = (md3Frame_t *) ((byte *) md3Model + md3Model->ofsFrames); for(i = 0; i < md3Model->numFrames; i++, frame++, md3Frame++) { frame->radius = LittleFloat(md3Frame->radius); for(j = 0; j < 3; j++) { frame->bounds[0][j] = LittleFloat(md3Frame->bounds[0][j]); frame->bounds[1][j] = LittleFloat(md3Frame->bounds[1][j]); frame->localOrigin[j] = LittleFloat(md3Frame->localOrigin[j]); } } // swap all the tags mdvModel->numTags = md3Model->numTags; mdvModel->tags = tag = (mdvTag_t*)ri.Hunk_Alloc(sizeof(*tag) * (md3Model->numTags * md3Model->numFrames), h_low); md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); for(i = 0; i < md3Model->numTags * md3Model->numFrames; i++, tag++, md3Tag++) { for(j = 0; j < 3; j++) { tag->origin[j] = LittleFloat(md3Tag->origin[j]); tag->axis[0][j] = LittleFloat(md3Tag->axis[0][j]); tag->axis[1][j] = LittleFloat(md3Tag->axis[1][j]); tag->axis[2][j] = LittleFloat(md3Tag->axis[2][j]); } } mdvModel->tagNames = tagName = (mdvTagName_t*)ri.Hunk_Alloc(sizeof(*tagName) * (md3Model->numTags), h_low); md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); for(i = 0; i < md3Model->numTags; i++, tagName++, md3Tag++) { Q_strncpyz(tagName->name, md3Tag->name, sizeof(tagName->name)); } // swap all the surfaces mdvModel->numSurfaces = md3Model->numSurfaces; mdvModel->surfaces = surf = (mdvSurface_t*)ri.Hunk_Alloc(sizeof(*surf) * md3Model->numSurfaces, h_low); md3Surf = (md3Surface_t *) ((byte *) md3Model + md3Model->ofsSurfaces); for(i = 0; i < md3Model->numSurfaces; i++) { LL(md3Surf->ident); LL(md3Surf->flags); LL(md3Surf->numFrames); LL(md3Surf->numShaders); LL(md3Surf->numTriangles); LL(md3Surf->ofsTriangles); LL(md3Surf->numVerts); LL(md3Surf->ofsShaders); LL(md3Surf->ofsSt); LL(md3Surf->ofsXyzNormals); LL(md3Surf->ofsEnd); if(md3Surf->numVerts > SHADER_MAX_VERTEXES) { ri.Error(ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, md3Surf->numVerts); } if(md3Surf->numTriangles * 3 > SHADER_MAX_INDEXES) { ri.Error(ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", modName, SHADER_MAX_INDEXES / 3, md3Surf->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, md3Surf->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, qtrue); if(sh->defaultShader) { shader->shaderIndex = 0; } else { shader->shaderIndex = sh->index; } } */ // only consider the first shader md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders); surf->shader = R_FindShader(md3Shader->name, SHADER_3D_DYNAMIC, qtrue); // swap all the triangles surf->numTriangles = md3Surf->numTriangles; surf->triangles = tri = (srfTriangle_t*)ri.Hunk_Alloc(sizeof(*tri) * md3Surf->numTriangles, h_low); md3Tri = (md3Triangle_t *) ((byte *) md3Surf + md3Surf->ofsTriangles); for(j = 0; j < md3Surf->numTriangles; j++, tri++, md3Tri++) { tri->indexes[0] = LittleLong(md3Tri->indexes[0]); tri->indexes[1] = LittleLong(md3Tri->indexes[1]); tri->indexes[2] = LittleLong(md3Tri->indexes[2]); } R_CalcSurfaceTriangleNeighbors(surf->numTriangles, surf->triangles); // swap all the XyzNormals surf->numVerts = md3Surf->numVerts; surf->verts = v = (mdvXyz_t*)ri.Hunk_Alloc( sizeof( *v ) * ( md3Surf->numVerts * md3Surf->numFrames ), h_low ); md3xyz = (md3XyzNormal_t *) ((byte *) md3Surf + md3Surf->ofsXyzNormals); for(j = 0; j < md3Surf->numVerts * md3Surf->numFrames; j++, md3xyz++, v++) { v->xyz[0] = LittleShort(md3xyz->xyz[0]) * MD3_XYZ_SCALE; v->xyz[1] = LittleShort(md3xyz->xyz[1]) * MD3_XYZ_SCALE; v->xyz[2] = LittleShort(md3xyz->xyz[2]) * MD3_XYZ_SCALE; } // swap all the ST surf->st = st = (mdvSt_t*)ri.Hunk_Alloc(sizeof(*st) * md3Surf->numVerts, h_low); md3st = (md3St_t *) ((byte *) md3Surf + md3Surf->ofsSt); for(j = 0; j < md3Surf->numVerts; j++, md3st++, st++) { st->st[0] = LittleFloat(md3st->st[0]); st->st[1] = LittleFloat(md3st->st[1]); } // find the next surface md3Surf = (md3Surface_t *) ((byte *) md3Surf + md3Surf->ofsEnd); surf++; } #if defined(USE_D3D10) // TODO #else #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; 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); } } //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->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("staticMD3Mesh_IBO %s", surf->name), surf->numTriangles, surf->triangles, VBO_USAGE_STATIC); // create VBO vertexesNum = surf->numVerts; // allocate vbo data dataSize = (surf->numVerts * mdvModel->numFrames * sizeof(vec4_t) * 4) + // xyz, tangent, binormal, normal (surf->numVerts * sizeof(vec4_t)); // texcoords data = (byte*)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] = vertexes[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("staticMD3Mesh_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); } // 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 #endif // USE_D3D10 return qtrue; }
/* ================= 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; srfTriangle_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; int numRemaining; growList_t sortedTriangles; growList_t vboTriangles; growList_t vboSurfaces; int numBoneReferences; int boneReferences[MAX_BONES]; buf_p = (char *)buffer; // skip MD5Version indent string COM_ParseExt(&buf_p, qfalse); // check version token = COM_ParseExt(&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->md5 = ri.Hunk_Alloc(sizeof(md5Model_t), h_low); // skip commandline <arguments string> token = COM_ParseExt(&buf_p, qtrue); token = COM_ParseExt(&buf_p, qtrue); // ri.Printf(PRINT_ALL, "%s\n", token); // parse numJoints <number> token = COM_ParseExt(&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_ParseExt(&buf_p, qfalse); md5->numBones = atoi(token); // parse numMeshes <number> token = COM_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&buf_p, qfalse); boneOrigin[j] = atof(token); } // skip ) token = COM_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&buf_p, qtrue); if(Q_stricmp(token, "shader")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'shader' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt(&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, SHADER_3D_DYNAMIC, qtrue); if(sh->defaultShader) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } // parse numVerts <number> token = COM_ParseExt(&buf_p, qtrue); 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_ParseExt(&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_ParseExt(&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_ParseExt(&buf_p, qfalse); // skip ( token = COM_ParseExt(&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_ParseExt(&buf_p, qfalse); v->texCoords[k] = atof(token); } // skip ) token = COM_ParseExt(&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_ParseExt(&buf_p, qfalse); v->firstWeight = atoi(token); token = COM_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&buf_p, qfalse); for(k = 0; k < 3; k++) { token = COM_ParseExt(&buf_p, qfalse); tri->indexes[k] = atoi(token); } } R_CalcSurfaceTriangleNeighbors(surf->numTriangles, surf->triangles); // parse numWeights <number> token = COM_ParseExt(&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_ParseExt(&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_ParseExt(&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_ParseExt(&buf_p, qfalse); token = COM_ParseExt(&buf_p, qfalse); weight->boneIndex = atoi(token); token = COM_ParseExt(&buf_p, qfalse); weight->boneWeight = atof(token); // skip ( token = COM_ParseExt(&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_ParseExt(&buf_p, qfalse); weight->offset[k] = atof(token); } // skip ) token = COM_ParseExt(&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_ParseExt(&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 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); //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_LoadMD5: could not add triangles to a remaining VBO surfaces for model '%s'\n", modName); 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; }