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)
- Mod_LoadModel(mod, true, false, mod->isworldmodel);
+ ++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)
+ {
+ SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
+ Mod_LoadModel(mod, true, false);
+ SCR_PopLoadingScreen(false);
+ }
+ SCR_PopLoadingScreen(false);
}
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)
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);
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;
}
return;
}
+
+typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, void *pass);
+
+int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
+{
+ const char *bufptr;
+ int start, len;
+ float fps;
+ unsigned int i;
+ qboolean loop;
+
+ bufptr = buf;
+ i = 0;
+ for(;;)
+ {
+ // an anim scene!
+ if (!COM_ParseToken_Simple(&bufptr, true, false))
+ break;
+ if (!strcmp(com_token, "\n"))
+ continue; // empty line
+ start = atoi(com_token);
+ if (!COM_ParseToken_Simple(&bufptr, true, false))
+ break;
+ if (!strcmp(com_token, "\n"))
+ {
+ Con_Printf("framegroups file: missing number of frames\n");
+ continue;
+ }
+ len = atoi(com_token);
+ if (!COM_ParseToken_Simple(&bufptr, true, false))
+ break;
+ // we default to looping as it's usually wanted, so to NOT loop you append a 0
+ if (strcmp(com_token, "\n"))
+ {
+ fps = atof(com_token);
+ if (!COM_ParseToken_Simple(&bufptr, true, false))
+ break;
+ if (strcmp(com_token, "\n"))
+ loop = atoi(com_token) != 0;
+ else
+ loop = true;
+ }
+ else
+ {
+ fps = 20;
+ loop = true;
+ }
+
+ if(cb)
+ cb(i, start, len, fps, loop, pass);
+ ++i;
+ }
+
+ return i;
+}
+
+void Mod_FrameGroupify_ParseGroups_Count (unsigned int i, int start, int len, float fps, qboolean loop, void *pass)
+{
+ unsigned int *cnt = (unsigned int *) pass;
+ ++*cnt;
+}
+
+void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, void *pass)
+{
+ dp_model_t *mod = (dp_model_t *) pass;
+ animscene_t *anim = &mod->animscenes[i];
+ dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d", i);
+ anim->firstframe = bound(0, start, mod->num_poses - 1);
+ anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
+ anim->framerate = max(1, fps);
+ anim->loop = !!loop;
+ //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
+}
+
+void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
+{
+ unsigned int cnt;
+
+ // 0. count
+ cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
+ if(!cnt)
+ {
+ Con_Printf("no scene found in framegroups file, aborting\n");
+ return;
+ }
+ mod->numframes = cnt;
+
+ // 1. reallocate
+ // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
+ mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
+
+ // 2. parse
+ Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
+}
+
/*
==================
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;
if (!strcmp(mod->name, "null"))
{
- if (mod->isworldmodel != isworldmodel)
- mod->loaded = false;
-
if(mod->loaded)
return mod;
if (developer_loading.integer)
Con_Printf("loading model %s\n", mod->name);
- mod->isworldmodel = isworldmodel;
mod->used = true;
- mod->crc = -1;
+ mod->crc = (unsigned int)-1;
mod->loaded = false;
VectorClear(mod->normalmins);
// 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)
{
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;
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();
}
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);
else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
Mem_Free(buf);
- Mod_BuildVBOs();
+ buf = FS_LoadFile (va("%s.framegroups", mod->name), tempmempool, false, &filesize);
+ if(buf)
+ {
+ Mod_FrameGroupify(mod, (const char *)buf);
+ Mem_Free(buf);
+ }
- // no fatal errors occurred, so this model is ready to use.
- mod->loaded = true;
+ Mod_BuildVBOs();
}
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;
}
}
}
-// 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();
// 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;
// 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;
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;
}
*/
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;
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);
+ }
+ }
}
/*
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 <filename>\n");
}
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)
{
if (strcasecmp (entry->shader.name, shader->name) == 0)
{
- Con_Printf("Shader '%s' already defined\n", shader->name);
+ unsigned char *start, *end, *start2;
+ start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
+ end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
+ start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
+ if(memcmp(start, start2, end - start))
+ Con_Printf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
+ else
+ Con_DPrintf("Shader '%s' already defined\n", shader->name);
return;
}
lastEntry = entry;
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)
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)
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)
{
{
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();
if (shader->backgroundlayer >= 0)
{
q3shaderinfo_layer_t* backgroundlayer = shader->layers + shader->backgroundlayer;
+ // copy over one secondarylayer parameter
+ memcpy(texture->backgroundtcmods, backgroundlayer->tcmods, sizeof(texture->backgroundtcmods));
+ // load the textures
texture->backgroundnumskinframes = backgroundlayer->numframes;
texture->backgroundskinframerate = backgroundlayer->framerate;
for (j = 0;j < backgroundlayer->numframes;j++)
{
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();
*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)
}
}
-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++)
{
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)
{
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)
{
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)
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);
}
{
outbuffermax *= 2;
oldbuffer = outbuffer;
- outbuffer = Z_Malloc(outbuffermax);
+ outbuffer = (char *) Z_Malloc(outbuffermax);
memcpy(outbuffer, oldbuffer, outbufferpos);
Z_Free(oldbuffer);
}
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;
}
{
outbuffermax *= 2;
oldbuffer = outbuffer;
- outbuffer = Z_Malloc(outbuffermax);
+ outbuffer = (char *) Z_Malloc(outbuffermax);
memcpy(outbuffer, oldbuffer, outbufferpos);
Z_Free(oldbuffer);
}
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)
{
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");
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));
if ((animname[l] < '0' || animname[l] > '9') && animname[l] != '_')
k = l + 1;
animname[k] = 0;
- count = (mod->num_poses / mod->num_bones) - first;
+ count = mod->num_poses - first;
for (j = i + 1;j < mod->numframes;j++)
{
strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
}
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);
}
}