X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=model_shared.c;h=842d2b7ddaadada0ac3b4abf2324013e4e5b3920;hb=34e18371c4b0593a50e787ee21a96d1594a733f8;hp=23a1d3d0ae8016223e96fbc4cfd1ce248d24edaa;hpb=48127ce30e368602588e0edfbcce8826bd12091b;p=xonotic%2Fdarkplaces.git diff --git a/model_shared.c b/model_shared.c index 23a1d3d0..842d2b7d 100644 --- a/model_shared.c +++ b/model_shared.c @@ -50,17 +50,25 @@ static q3shader_data_t* q3shader_data; static void mod_start(void) { - int i; + int i, count; int nummodels = Mem_ExpandableArray_IndexRange(&models); dp_model_t *mod; - // parse the Q3 shader files - Mod_LoadQ3Shaders(); - + SCR_PushLoadingScreen(false, "Loading models", 1.0); + count = 0; + for (i = 0;i < nummodels;i++) + if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*') + if (mod->used) + ++count; for (i = 0;i < nummodels;i++) if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*') if (mod->used) - Mod_LoadModel(mod, true, false, mod->isworldmodel); + { + SCR_PushLoadingScreen(true, mod->name, 1.0 / count); + Mod_LoadModel(mod, true, false); + SCR_PopLoadingScreen(false); + } + SCR_PopLoadingScreen(false); } static void mod_shutdown(void) @@ -73,7 +81,7 @@ static void mod_shutdown(void) if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool)) Mod_UnloadModel(mod); - Mem_FreePool (&q3shaders_mem); + Mod_FreeQ3Shaders(); } static void mod_newmap(void) @@ -152,14 +160,14 @@ void Mod_RenderInit(void) void Mod_UnloadModel (dp_model_t *mod) { char name[MAX_QPATH]; - qboolean isworldmodel; qboolean used; + dp_model_t *parentmodel; if (developer_loading.integer) Con_Printf("unloading model %s\n", mod->name); strlcpy(name, mod->name, sizeof(name)); - isworldmodel = mod->isworldmodel; + parentmodel = mod->brush.parentmodel; used = mod->used; if (mod->surfmesh.ebo3i) R_Mesh_DestroyBufferObject(mod->surfmesh.ebo3i); @@ -174,7 +182,7 @@ void Mod_UnloadModel (dp_model_t *mod) memset(mod, 0, sizeof(dp_model_t)); // restore the fields we want to preserve strlcpy(mod->name, name, sizeof(mod->name)); - mod->isworldmodel = isworldmodel; + mod->brush.parentmodel = parentmodel; mod->used = used; mod->loaded = false; } @@ -191,7 +199,7 @@ Mod_LoadModel Loads a model ================== */ -dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, qboolean isworldmodel) +dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk) { int num; unsigned int crc; @@ -205,9 +213,6 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q if (!strcmp(mod->name, "null")) { - if (mod->isworldmodel != isworldmodel) - mod->loaded = false; - if(mod->loaded) return mod; @@ -217,7 +222,6 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q if (developer_loading.integer) Con_Printf("loading model %s\n", mod->name); - mod->isworldmodel = isworldmodel; mod->used = true; mod->crc = -1; mod->loaded = false; @@ -246,12 +250,6 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q // even if the model is loaded it still may need reloading... - // if the model is a worldmodel and is being referred to as a - // non-worldmodel here, then it needs reloading to get rid of the - // submodels - if (mod->isworldmodel != isworldmodel) - mod->loaded = false; - // if it is not loaded or checkdisk is true we need to calculate the crc if (!mod->loaded || checkdisk) { @@ -277,13 +275,14 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q if (developer_loading.integer) Con_Printf("loading model %s\n", mod->name); + + SCR_PushLoadingScreen(true, mod->name, 1); // LordHavoc: unload the existing model in this slot (if there is one) if (mod->loaded || mod->mempool) Mod_UnloadModel(mod); // load the model - mod->isworldmodel = isworldmodel; mod->used = true; mod->crc = crc; // errors can prevent the corresponding mod->loaded = true; @@ -298,15 +297,9 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius); VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius); - // if we're loading a worldmodel, then this is a level change - if (mod->isworldmodel) + if (!q3shaders_mem) { - // clear out any stale submodels or worldmodels lying around - // if we did this clear before now, an error might abort loading and - // leave things in a bad state - Mod_RemoveStaleWorldModels(mod); - // reload q3 shaders, to make sure they are ready to go for this level - // (including any models loaded for this level) + // load q3 shaders for the first time, or after a level change Mod_LoadQ3Shaders(); } @@ -320,7 +313,8 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q num = LittleLong(*((int *)buf)); // call the apropriate loader loadmodel = mod; - if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend); + if (!strcasecmp(FS_FileExtension(mod->name), "obj")) Mod_OBJ_Load(mod, buf, bufend); + else if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend); else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend); else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend); else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend); @@ -335,15 +329,18 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk, q Mem_Free(buf); Mod_BuildVBOs(); - - // no fatal errors occurred, so this model is ready to use. - mod->loaded = true; } else if (crash) { // LordHavoc: Sys_Error was *ANNOYING* Con_Printf ("Mod_LoadModel: %s not found\n", mod->name); } + + // no fatal errors occurred, so this model is ready to use. + mod->loaded = true; + + SCR_PopLoadingScreen(false); + return mod; } @@ -372,35 +369,21 @@ void Mod_PurgeUnused(void) } } -// only used during loading! -void Mod_RemoveStaleWorldModels(dp_model_t *skip) -{ - int i; - int nummodels = Mem_ExpandableArray_IndexRange(&models); - dp_model_t *mod; - for (i = 0;i < nummodels;i++) - { - if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->isworldmodel && mod->loaded && skip != mod) - { - Mod_UnloadModel(mod); - mod->isworldmodel = false; - mod->used = false; - } - } -} - /* ================== Mod_FindName ================== */ -dp_model_t *Mod_FindName(const char *name) +dp_model_t *Mod_FindName(const char *name, const char *parentname) { int i; int nummodels; dp_model_t *mod; + if (!parentname) + parentname = ""; + // if we're not dedicatd, the renderer calls will crash without video Host_StartVideo(); @@ -412,7 +395,7 @@ dp_model_t *Mod_FindName(const char *name) // search the currently loaded models for (i = 0;i < nummodels;i++) { - if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name)) + if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name) && ((!mod->brush.parentmodel && !parentname[0]) || (mod->brush.parentmodel && parentname[0] && !strcmp(mod->brush.parentmodel->name, parentname)))) { mod->used = true; return mod; @@ -422,6 +405,10 @@ dp_model_t *Mod_FindName(const char *name) // no match found, create a new one mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models); strlcpy(mod->name, name, sizeof(mod->name)); + if (parentname[0]) + mod->brush.parentmodel = Mod_FindName(parentname, NULL); + else + mod->brush.parentmodel = NULL; mod->loaded = false; mod->used = true; return mod; @@ -434,12 +421,12 @@ Mod_ForName Loads in a model for the given name ================== */ -dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, qboolean isworldmodel) +dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname) { dp_model_t *model; - model = Mod_FindName(name); - if (model->name[0] != '*' && (!model->loaded || checkdisk)) - Mod_LoadModel(model, crash, checkdisk, isworldmodel); + model = Mod_FindName(name, parentname); + if (!model->loaded || checkdisk) + Mod_LoadModel(model, crash, checkdisk); return model; } @@ -452,12 +439,23 @@ Reloads all models if they have changed */ void Mod_Reload(void) { - int i; + int i, count; int nummodels = Mem_ExpandableArray_IndexRange(&models); dp_model_t *mod; + + SCR_PushLoadingScreen(false, "Reloading models", 1.0); + count = 0; + for (i = 0;i < nummodels;i++) + if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used) + ++count; for (i = 0;i < nummodels;i++) if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used) - Mod_LoadModel(mod, true, true, mod->isworldmodel); + { + SCR_PushLoadingScreen(true, mod->name, 1.0 / count); + Mod_LoadModel(mod, true, true); + SCR_PopLoadingScreen(false); + } + SCR_PopLoadingScreen(false); } unsigned char *mod_base; @@ -478,8 +476,15 @@ static void Mod_Print(void) Con_Print("Loaded models:\n"); for (i = 0;i < nummodels;i++) - if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0]) - Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name); + { + if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*') + { + if (mod->brush.numsubmodels) + Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels); + else + Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name); + } + } } /* @@ -490,7 +495,7 @@ Mod_Precache static void Mod_Precache(void) { if (Cmd_Argc() == 2) - Mod_ForName(Cmd_Argv(1), false, true, cl.worldmodel && !strcasecmp(Cmd_Argv(1), cl.worldmodel->name)); + Mod_ForName(Cmd_Argv(1), false, true, Cmd_Argv(1)[0] == '*' ? cl.model_name[1] : NULL); else Con_Print("usage: modelprecache \n"); } @@ -1218,18 +1223,9 @@ q3wavefunc_t Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s) return Q3WAVEFUNC_NONE; } -static void Q3Shaders_Clear() +void Mod_FreeQ3Shaders(void) { - /* Just clear out everything... */ - Mem_FreePool (&q3shaders_mem); - /* ...and alloc the structs again. */ - q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL); - q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem, - sizeof (q3shader_data_t)); - Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries, - q3shaders_mem, sizeof (q3shader_hash_entry_t), 256); - Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs, - q3shaders_mem, sizeof (char**), 256); + Mem_FreePool(&q3shaders_mem); } static void Q3Shader_AddToHash (q3shaderinfo_t* shader) @@ -1279,7 +1275,15 @@ void Mod_LoadQ3Shaders(void) int numparameters; char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH]; - Q3Shaders_Clear(); + Mod_FreeQ3Shaders(); + + q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL); + q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem, + sizeof (q3shader_data_t)); + Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries, + q3shaders_mem, sizeof (q3shader_hash_entry_t), 256); + Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs, + q3shaders_mem, sizeof (char**), 256); search = FS_Search("scripts/*.shader", true, false); if (!search) @@ -1773,8 +1777,12 @@ void Mod_LoadQ3Shaders(void) q3shaderinfo_t *Mod_LookupQ3Shader(const char *name) { - unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name)); - q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE); + unsigned short hash; + q3shader_hash_entry_t* entry; + if (!q3shaders_mem) + Mod_LoadQ3Shaders(); + hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name)); + entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE); while (entry != NULL) { if (strcasecmp (entry->shader.name, name) == 0) @@ -1787,24 +1795,28 @@ q3shaderinfo_t *Mod_LookupQ3Shader(const char *name) qboolean Mod_LoadTextureFromQ3Shader(texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags) { int j; + int texflagsmask; qboolean success = true; q3shaderinfo_t *shader; if (!name) name = ""; strlcpy(texture->name, name, sizeof(texture->name)); shader = name[0] ? Mod_LookupQ3Shader(name) : NULL; + + texflagsmask = ~0; + if(!(defaulttexflags & TEXF_PICMIP)) + texflagsmask &= ~TEXF_PICMIP; + if(!(defaulttexflags & TEXF_COMPRESS)) + texflagsmask &= ~TEXF_COMPRESS; + if (shader) { if (developer_loading.integer) Con_Printf("%s: loaded shader for %s\n", loadmodel->name, name); texture->surfaceparms = shader->surfaceparms; - texture->textureflags = shader->textureflags; // allow disabling of picmip or compression by defaulttexflags - if(!(defaulttexflags & TEXF_PICMIP)) - texture->textureflags &= ~TEXF_PICMIP; - if(!(defaulttexflags & TEXF_COMPRESS)) - texture->textureflags &= ~TEXF_COMPRESS; + texture->textureflags = shader->textureflags & texflagsmask; if (shader->surfaceparms & Q3SURFACEPARM_SKY) { @@ -1890,7 +1902,7 @@ nothing GL_ZERO GL_ONE { texture->skinframes[j] = NULL; } - else if (!(texture->skinframes[j] = R_SkinFrame_LoadExternal(primarylayer->texturename[j], primarylayer->texflags, false))) + else if (!(texture->skinframes[j] = R_SkinFrame_LoadExternal(primarylayer->texturename[j], primarylayer->texflags & texflagsmask, false))) { Con_Printf("^1%s:^7 could not load texture ^3\"%s\"^7 (frame %i) for shader ^2\"%s\"\n", loadmodel->name, primarylayer->texturename[j], j, texture->name); texture->skinframes[j] = R_SkinFrame_LoadMissing(); @@ -1908,7 +1920,7 @@ nothing GL_ZERO GL_ONE { texture->skinframes[j] = NULL; } - else if (!(texture->backgroundskinframes[j] = R_SkinFrame_LoadExternal(backgroundlayer->texturename[j], backgroundlayer->texflags, false))) + else if (!(texture->backgroundskinframes[j] = R_SkinFrame_LoadExternal(backgroundlayer->texturename[j], backgroundlayer->texflags & texflagsmask, false))) { Con_Printf("^1%s:^7 could not load texture ^3\"%s\"^7 (background frame %i) for shader ^2\"%s\"\n", loadmodel->name, backgroundlayer->texturename[j], j, texture->name); texture->backgroundskinframes[j] = R_SkinFrame_LoadMissing(); @@ -2168,6 +2180,39 @@ void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firs *lastvertexpointer = lastvertex; } +void Mod_MakeSortedSurfaces(dp_model_t *mod) +{ + // make an optimal set of texture-sorted batches to draw... + int j, t; + int *firstsurfacefortexture; + int *numsurfacesfortexture; + if (!mod->sortedmodelsurfaces) + mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces)); + firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture)); + numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture)); + memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture)); + for (j = 0;j < mod->nummodelsurfaces;j++) + { + const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface; + int t = (int)(surface->texture - mod->data_textures); + numsurfacesfortexture[t]++; + } + j = 0; + for (t = 0;t < mod->num_textures;t++) + { + firstsurfacefortexture[t] = j; + j += numsurfacesfortexture[t]; + } + for (j = 0;j < mod->nummodelsurfaces;j++) + { + const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface; + int t = (int)(surface->texture - mod->data_textures); + mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface; + } + Mem_Free(firstsurfacefortexture); + Mem_Free(numsurfacesfortexture); +} + static void Mod_BuildVBOs(void) { if (!gl_support_arb_vertex_buffer_object) @@ -2217,25 +2262,58 @@ static void Mod_BuildVBOs(void) } } -static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *originalfilename) +static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename) { - int vertexindex, surfaceindex, triangleindex, countvertices = 0, countsurfaces = 0, countfaces = 0; + int vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0; + int a, b, c; + const char *texname; const int *e; const float *v, *vn, *vt; size_t l; size_t outbufferpos = 0; - size_t outbuffermax = 0x10000; - char *outbuffer = Z_Malloc(outbuffermax), *oldbuffer; + size_t outbuffermax = 0x100000; + char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer; const msurface_t *surface; + const int maxtextures = 256; + char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH); + // construct the mtllib file + l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename); + if (l > 0) + outbufferpos += l; for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++) { - countmaterials++; + countsurfaces++; countvertices += surface->num_vertices; countfaces += surface->num_triangles; + texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default"; + for (textureindex = 0;textureindex < maxtextures && texturenames[textureindex*MAX_QPATH];textureindex++) + if (!strcmp(texturenames + textureindex * MAX_QPATH, texname)) + break; + if (textureindex >= maxtextures) + continue; // just a precaution + if (counttextures < textureindex + 1) + counttextures = textureindex + 1; + strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH); + if (outbufferpos >= outbuffermax >> 1) + { + outbuffermax *= 2; + oldbuffer = outbuffer; + outbuffer = (char *) Z_Malloc(outbuffermax); + memcpy(outbuffer, oldbuffer, outbufferpos); + Z_Free(oldbuffer); + } + l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "newmtl %s\nNs 96.078431\nKa 0 0 0\nKd 0.64 0.64 0.64\nKs 0.5 0.5 0.5\nNi 1\nd 1\nillum 2\nmap_Kd %s%s\n\n", texname, texname, strstr(texname, ".tga") ? "" : ".tga"); + if (l > 0) + outbufferpos += l; } - l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# model exported from %s by darkplaces engine\n# %i vertices, %i faces, %i surfaces\n", originalfilename, countvertices, countfaces, countsurfaces); + // write the mtllib file + FS_WriteFile(mtlfilename, outbuffer, outbufferpos); + outbufferpos = 0; + + // construct the obj file + l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# model exported from %s by darkplaces engine\n# %i vertices, %i faces, %i surfaces\nmtllib %s\n", originalfilename, countvertices, countfaces, countsurfaces, mtlfilename); if (l > 0) outbufferpos += l; for (vertexindex = 0, v = model->surfmesh.data_vertex3f, vn = model->surfmesh.data_normal3f, vt = model->surfmesh.data_texcoordtexture2f;vertexindex < model->surfmesh.num_vertices;vertexindex++, v += 3, vn += 3, vt += 2) @@ -2244,21 +2322,17 @@ static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const cha { outbuffermax *= 2; oldbuffer = outbuffer; - outbuffer = Z_Malloc(outbuffermax); + outbuffer = (char *) Z_Malloc(outbuffermax); memcpy(outbuffer, oldbuffer, outbufferpos); Z_Free(oldbuffer); } - l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "v %f %f %f\nvn %f %f %f\nvt %f %f\n", v[0], v[2], v[1], vn[0], vn[2], vn[1], vt[0], vt[1]); + l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "v %f %f %f\nvn %f %f %f\nvt %f %f\n", v[0], v[2], -v[1], vn[0], vn[2], -vn[1], vt[0], 1-vt[1]); if (l > 0) outbufferpos += l; } - for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++) { - if (surface->texture && surface->texture->name[0]) - l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", surface->texture->name); - else - l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl default\n"); + l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default"); if (l > 0) outbufferpos += l; for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3) @@ -2267,20 +2341,28 @@ static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const cha { outbuffermax *= 2; oldbuffer = outbuffer; - outbuffer = Z_Malloc(outbuffermax); + outbuffer = (char *) Z_Malloc(outbuffermax); memcpy(outbuffer, oldbuffer, outbufferpos); Z_Free(oldbuffer); } - l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", e[0], e[0], e[0], e[2], e[2], e[2], e[1], e[1], e[1]); + a = e[0]+1; + b = e[2]+1; + c = e[1]+1; + l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", a,a,a,b,b,b,c,c,c); if (l > 0) outbufferpos += l; } } + // write the obj file FS_WriteFile(filename, outbuffer, outbufferpos); + + // clean up Z_Free(outbuffer); + Z_Free(texturenames); - Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces); + // print some stats + Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures); } static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles) @@ -2291,23 +2373,34 @@ static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int first int transformindex; int poseindex; int cornerindex; + float modelscale; const int *e; const float *pose; size_t l; size_t outbufferpos = 0; - size_t outbuffermax = 0x10000; - char *outbuffer = Z_Malloc(outbuffermax), *oldbuffer; + size_t outbuffermax = 0x100000; + char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer; const msurface_t *surface; l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n"); if (l > 0) outbufferpos += l; + modelscale = 1; + if(model->num_poses >= 0) + modelscale = sqrt(model->data_poses[0] * model->data_poses[0] + model->data_poses[1] * model->data_poses[1] + model->data_poses[2] * model->data_poses[2]); + if(fabs(modelscale - 1) > 1e-4) + { + if(firstpose == 0) // only print the when writing the reference pose + Con_Printf("The model has an old-style model scale of %f\n", modelscale); + } + else + modelscale = 1; for (transformindex = 0;transformindex < model->num_bones;transformindex++) { if (outbufferpos >= outbuffermax >> 1) { outbuffermax *= 2; oldbuffer = outbuffer; - outbuffer = Z_Malloc(outbuffermax); + outbuffer = (char *) Z_Malloc(outbuffermax); memcpy(outbuffer, oldbuffer, outbufferpos); Z_Free(oldbuffer); } @@ -2334,7 +2427,7 @@ static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int first { outbuffermax *= 2; oldbuffer = outbuffer; - outbuffer = Z_Malloc(outbuffermax); + outbuffer = (char *) Z_Malloc(outbuffermax); memcpy(outbuffer, oldbuffer, outbufferpos); Z_Free(oldbuffer); } @@ -2388,7 +2481,7 @@ static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int first test[2][3] = pose[11]; } #endif - l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f\n", transformindex, pose[3], pose[7], pose[11], DEG2RAD(angles[ROLL]), DEG2RAD(angles[PITCH]), DEG2RAD(angles[YAW])); + l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f\n", transformindex, pose[3] * modelscale, pose[7] * modelscale, pose[11] * modelscale, DEG2RAD(angles[ROLL]), DEG2RAD(angles[PITCH]), DEG2RAD(angles[YAW])); if (l > 0) outbufferpos += l; } @@ -2410,7 +2503,7 @@ static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int first { outbuffermax *= 2; oldbuffer = outbuffer; - outbuffer = Z_Malloc(outbuffermax); + outbuffer = (char *) Z_Malloc(outbuffermax); memcpy(outbuffer, oldbuffer, outbufferpos); Z_Free(oldbuffer); } @@ -2458,11 +2551,14 @@ static void Mod_Decompile_f(void) dp_model_t *mod; char inname[MAX_QPATH]; char outname[MAX_QPATH]; + char mtlname[MAX_QPATH]; char basename[MAX_QPATH]; char animname[MAX_QPATH]; char animname2[MAX_QPATH]; - char textbuffer[32768]; - int textsize = 0; + char zymtextbuffer[16384]; + char dpmtextbuffer[16384]; + int zymtextsize = 0; + int dpmtextsize = 0; if (Cmd_Argc() != 2) { @@ -2473,7 +2569,14 @@ static void Mod_Decompile_f(void) strlcpy(inname, Cmd_Argv(1), sizeof(inname)); FS_StripExtension(inname, basename, sizeof(basename)); - mod = Mod_ForName(inname, false, true, cl.worldmodel && !strcasecmp(inname, cl.worldmodel->name)); + mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL); + if (mod->brush.submodel) + { + // if we're decompiling a submodel, be sure to give it a proper name based on its parent + FS_StripExtension(cl.model_name[1], outname, sizeof(outname)); + dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name); + outname[0] = 0; + } if (!mod) { Con_Print("No such model\n"); @@ -2489,16 +2592,19 @@ static void Mod_Decompile_f(void) if (mod->surfmesh.num_triangles) { dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename); - Mod_Decompile_OBJ(mod, outname, inname); + dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename); + Mod_Decompile_OBJ(mod, outname, mtlname, inname); } // export SMD if possible (only for skeletal models) - if (mod->surfmesh.num_triangles && mod->num_poses) + if (mod->surfmesh.num_triangles && mod->num_bones) { dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename); Mod_Decompile_SMD(mod, outname, 0, 1, true); - l = dpsnprintf(textbuffer + textsize, sizeof(textbuffer) - textsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n"); - if (l > 0) textsize += l; + l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n"); + if (l > 0) zymtextsize += l; + l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n"); + if (l > 0) dpmtextsize += l; for (i = 0;i < mod->numframes;i = j) { strlcpy(animname, mod->animscenes[i].name, sizeof(animname)); @@ -2538,14 +2644,21 @@ static void Mod_Decompile_f(void) } dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname); Mod_Decompile_SMD(mod, outname, first, count, false); - if (textsize < (int)sizeof(textbuffer) - 100) + if (zymtextsize < (int)sizeof(zymtextbuffer) - 100) + { + l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g\n", animname, mod->animscenes[i].framerate); + if (l > 0) zymtextsize += l; + } + if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100) { - l = dpsnprintf(textbuffer + textsize, sizeof(textbuffer) - textsize, "scene %s.smd\n", animname); - if (l > 0) textsize += l; + l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd\n", animname); + if (l > 0) dpmtextsize += l; } } - if (textsize) - FS_WriteFile(va("%s_decompiled/out.txt", basename), textbuffer, (fs_offset_t)textsize); + if (zymtextsize) + FS_WriteFile(va("%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize); + if (dpmtextsize) + FS_WriteFile(va("%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize); } }