mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-10 14:42:13 +00:00
Load .obj meshes (yuck). They should at least be useful for loading the odd q3 .map file.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5543 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
ea9936a340
commit
8f02c55729
3 changed files with 329 additions and 1 deletions
|
@ -9161,6 +9161,330 @@ static qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t f
|
|||
|
||||
#endif //MD5MODELS
|
||||
|
||||
#ifdef MODELFMT_OBJ
|
||||
#include <ctype.h>
|
||||
struct objvert { size_t attrib[3]; };
|
||||
|
||||
struct objbuf_s { char *ptr; char *end; };
|
||||
static char *obj_getline(struct objbuf_s *vf, char *buffer, size_t buflen)
|
||||
{
|
||||
char in;
|
||||
char *out = buffer;
|
||||
size_t len;
|
||||
if (buflen <= 1)
|
||||
return NULL;
|
||||
len = buflen-1;
|
||||
while (len > 0)
|
||||
{
|
||||
if (vf->ptr == vf->end)
|
||||
{
|
||||
if (len == buflen-1)
|
||||
return NULL;
|
||||
*out = '\0';
|
||||
return buffer;
|
||||
}
|
||||
in = *vf->ptr++;
|
||||
if (in == '\n')
|
||||
break;
|
||||
*out++ = in;
|
||||
len--;
|
||||
}
|
||||
*out = '\0';
|
||||
|
||||
//if there's a trailing \r, strip it.
|
||||
if (out > buffer)
|
||||
if (out[-1] == '\r')
|
||||
out[-1] = 0;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
struct objattrib_s {
|
||||
size_t length;
|
||||
size_t maxlength;
|
||||
float *data;
|
||||
};
|
||||
static qboolean parseobjvert(char *s, struct objattrib_s *out)
|
||||
{
|
||||
int i;
|
||||
float *v;
|
||||
char *n;
|
||||
if (out->length == out->maxlength)
|
||||
Z_ReallocElements((void**)&out->data,&out->maxlength,out->length+1024,3*sizeof(float));
|
||||
v = out->data+out->length*3;
|
||||
out->length++;
|
||||
while(isalpha(*s)) s++;
|
||||
for (i = 0; i < 3;)
|
||||
{
|
||||
v[i] = strtod(s, &n);
|
||||
if (n==s)
|
||||
return false;
|
||||
s = n;
|
||||
while(isspace(*s)) s++;
|
||||
i++;
|
||||
if(!*s) break;
|
||||
}
|
||||
for (; i < 3; i++)
|
||||
v[i] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static galiasinfo_t *Obj_FinishFace(model_t *mod, galiasinfo_t *m, struct objattrib_s *attribs, struct objvert *vert, size_t numverts, index_t *indexes, size_t *numelements)
|
||||
{ //this is really lame. not optimised and I don't care.
|
||||
qboolean calcnorms = false;
|
||||
size_t i;
|
||||
if (m && numverts < MAX_INDICIES)
|
||||
{
|
||||
m->ofs_skel_xyz = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_skel_xyz)*numverts);
|
||||
m->ofs_st_array = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_st_array)*numverts);
|
||||
m->ofs_skel_norm = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_skel_norm)*numverts);
|
||||
m->ofs_skel_svect = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_skel_svect)*numverts);
|
||||
m->ofs_skel_tvect = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_skel_tvect)*numverts);
|
||||
|
||||
for (i = 0; i < numverts; i++)
|
||||
{
|
||||
if (vert[i].attrib[0] >= attribs[0].length)
|
||||
VectorClear(m->ofs_skel_xyz[i]);
|
||||
else
|
||||
VectorCopy(attribs[0].data+3*vert[i].attrib[0], m->ofs_skel_xyz[i]);
|
||||
AddPointToBounds(m->ofs_skel_xyz[i], mod->mins, mod->maxs);
|
||||
|
||||
if (vert[i].attrib[1] >= attribs[1].length)
|
||||
Vector2Clear(m->ofs_st_array[i]);
|
||||
else
|
||||
Vector2Copy(attribs[1].data+3*vert[i].attrib[1], m->ofs_st_array[i]);
|
||||
|
||||
if (vert[i].attrib[2] >= attribs[2].length)
|
||||
{
|
||||
VectorClear(m->ofs_skel_norm[i]);
|
||||
calcnorms = true;
|
||||
}
|
||||
else
|
||||
VectorCopy(attribs[2].data+3*vert[i].attrib[2], m->ofs_skel_norm[i]);
|
||||
}
|
||||
m->numverts = i;
|
||||
|
||||
m->ofs_indexes = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_indexes)**numelements);
|
||||
memcpy(m->ofs_indexes, indexes, sizeof(*m->ofs_indexes)**numelements);
|
||||
m->numindexes = *numelements;
|
||||
|
||||
//calc tangents.
|
||||
Mod_AccumulateTextureVectors(m->ofs_skel_xyz, m->ofs_st_array, m->ofs_skel_norm, m->ofs_skel_svect, m->ofs_skel_tvect, m->ofs_indexes, m->numindexes, calcnorms);
|
||||
Mod_NormaliseTextureVectors(m->ofs_skel_norm, m->ofs_skel_svect, m->ofs_skel_tvect, m->numverts, calcnorms);
|
||||
|
||||
*numelements = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static qboolean QDECL Mod_LoadObjModel(model_t *mod, void *buffer, size_t fsize)
|
||||
{
|
||||
struct objbuf_s f = {buffer, buffer+fsize};
|
||||
struct objattrib_s attrib[3] = {{},{},{}};
|
||||
char buf[512];
|
||||
char *meshname = NULL, *matname = NULL;
|
||||
galiasinfo_t *m = NULL, **link = (galiasinfo_t**)&mod->meshinfo;
|
||||
|
||||
size_t numverts = 0;
|
||||
size_t maxverts = 0;
|
||||
struct objvert *vert = NULL, defaultvert={{-1,-1,-1}};
|
||||
|
||||
size_t numelems = 0;
|
||||
size_t maxelems = 0;
|
||||
index_t *elem = NULL;
|
||||
|
||||
qboolean badinput = false;
|
||||
int meshidx = 0;
|
||||
|
||||
ClearBounds(mod->mins, mod->maxs);
|
||||
|
||||
while(!badinput && obj_getline(&f, buf, sizeof(buf)))
|
||||
{
|
||||
char *c = buf;
|
||||
while(isspace(*c)) c++;
|
||||
switch(*c)
|
||||
{
|
||||
case '#': continue;
|
||||
case 'v':
|
||||
if(isspace(c[1])) badinput |= !parseobjvert(c, &attrib[0]);
|
||||
else if(c[1]=='t') badinput |= !parseobjvert(c, &attrib[1]);
|
||||
else if(c[1]=='n') badinput |= !parseobjvert(c, &attrib[2]);
|
||||
break;
|
||||
case 'g':
|
||||
{
|
||||
char *name;
|
||||
size_t namelen;
|
||||
while(isalpha(*c)) c++;
|
||||
while(isspace(*c)) c++;
|
||||
name = c;
|
||||
namelen = strlen(name);
|
||||
while(namelen > 0 && isspace(name[namelen-1])) namelen--;
|
||||
Z_Free(meshname);
|
||||
meshname = Z_Malloc(namelen+1);
|
||||
memcpy(meshname, name, namelen);
|
||||
meshname[namelen] = 0;
|
||||
|
||||
m = Obj_FinishFace(mod, m, attrib, vert, numverts, elem, &numelems);
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
char *name;
|
||||
size_t namelen;
|
||||
if(strncmp(c, "usemtl", 6)) continue;
|
||||
while(isalpha(*c)) c++;
|
||||
while(isspace(*c)) c++;
|
||||
name = c;
|
||||
namelen = strlen(name);
|
||||
while(namelen > 0 && isspace(name[namelen-1])) namelen--;
|
||||
|
||||
Z_Free(matname);
|
||||
matname = Z_Malloc(namelen+1);
|
||||
memcpy(matname, name, namelen);
|
||||
matname[namelen] = 0;
|
||||
|
||||
m = Obj_FinishFace(mod, m, attrib, vert, numverts, elem, &numelems);
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
if(!isspace(c[1])) continue;
|
||||
while(isalpha(*c)) c++;
|
||||
while(isspace(*c)) c++;
|
||||
int key = strtol(c, &c, 10);
|
||||
|
||||
//make sure that these verts are not merged, ensuring that they get smoothed.
|
||||
//different texture coords will still have discontinuities though.
|
||||
defaultvert.attrib[2] = -2-key;
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
size_t i, v = 0;
|
||||
struct objvert vkey={};
|
||||
index_t first=0, prev=0, cur=0;
|
||||
|
||||
//only generate a new mesh if something actually changed.
|
||||
if (!m)
|
||||
{
|
||||
#ifdef HAVE_CLIENT
|
||||
galiasskin_t *skin;
|
||||
skinframe_t *sframe;
|
||||
m = ZG_Malloc(&mod->memgroup, sizeof(*m)+sizeof(*skin)+sizeof(*sframe));
|
||||
#else
|
||||
m = ZG_Malloc(&mod->memgroup, sizeof(*m));
|
||||
#endif
|
||||
*link = m;
|
||||
link = &m->nextsurf;
|
||||
m->shares_verts = meshidx;
|
||||
Mod_DefaultMesh(m, COM_SkipPath(mod->name), meshidx++);
|
||||
|
||||
Q_strncpyz(m->surfacename, meshname?meshname:"", sizeof(m->surfacename));
|
||||
|
||||
#ifdef HAVE_CLIENT
|
||||
skin = (void*)(m+1);
|
||||
sframe = (void*)(skin+1);
|
||||
skin->frame = sframe;
|
||||
skin->numframes = 1;
|
||||
skin->skinspeed = 10;
|
||||
Q_strncpyz(skin->name, matname?matname:"", sizeof(skin->name));
|
||||
Q_strncpyz(sframe->shadername, matname?matname:"", sizeof(sframe->shadername));
|
||||
sframe->shader = NULL;
|
||||
|
||||
m->ofsskins = skin;
|
||||
m->numskins = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
while(isalpha(*c)) c++;
|
||||
for(;;)
|
||||
{
|
||||
while(isspace(*c)) c++;
|
||||
if(!*c) break;
|
||||
|
||||
for (i = 0; i < countof(vkey.attrib); )
|
||||
{
|
||||
char *n;
|
||||
long v;
|
||||
v = strtol(c, &n, 10);
|
||||
if (c == n) {badinput = true; break;} //not a number if we read nothing!
|
||||
if (v < 0)
|
||||
vkey.attrib[i] = attrib[i].length + v;
|
||||
else
|
||||
vkey.attrib[i] = v - 1; //0 is index-not-specified.
|
||||
i++;
|
||||
c = n;
|
||||
if(*c!='/') break;
|
||||
c++;
|
||||
}
|
||||
for (; i < countof(vkey.attrib); i++)
|
||||
vkey.attrib[i] = defaultvert.attrib[i];
|
||||
|
||||
//figure out the verts, to avoid dupes
|
||||
for (cur = 0; cur < numverts; cur++)
|
||||
{
|
||||
if (vert[cur].attrib[0] == vkey.attrib[0] &&
|
||||
vert[cur].attrib[1] == vkey.attrib[1] &&
|
||||
vert[cur].attrib[2] == vkey.attrib[2])
|
||||
break;
|
||||
}
|
||||
if (cur == numverts)
|
||||
{
|
||||
if (numverts == maxverts)
|
||||
Z_ReallocElements((void**)&vert,&maxverts,numverts+1024,sizeof(*vert));
|
||||
vert[numverts++] = vkey;
|
||||
}
|
||||
|
||||
//spew out the trifan
|
||||
if (v == 0)
|
||||
first = cur;
|
||||
else if (v > 1)
|
||||
{
|
||||
if (numelems+3 >= maxelems)
|
||||
{
|
||||
if (numelems >= 65535)
|
||||
{
|
||||
badinput = true;
|
||||
break; //don't depend upon the OOM killer... it kills everything else too
|
||||
}
|
||||
Z_ReallocElements((void**)&elem,&maxelems,numelems+1024,sizeof(*elem));
|
||||
}
|
||||
elem[numelems++] = cur;
|
||||
elem[numelems++] = prev;
|
||||
elem[numelems++] = first;
|
||||
}
|
||||
prev = cur;
|
||||
v++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m = Obj_FinishFace(mod, m, attrib, vert, numverts, elem, &numelems);
|
||||
|
||||
Z_Free(vert);
|
||||
Z_Free(elem);
|
||||
Z_Free(attrib[0].data);
|
||||
Z_Free(attrib[1].data);
|
||||
Z_Free(attrib[2].data);
|
||||
|
||||
if (badinput)
|
||||
{ //fail the load.
|
||||
Con_Printf(CON_WARNING "File \"%s\" with .obj extension does not appear to be an .obj file\n", mod->name);
|
||||
mod->meshinfo = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
Mod_ClampModelSize(mod);
|
||||
Mod_ParseModelEvents(mod, NULL, 0);
|
||||
mod->flags = 0;
|
||||
mod->type = mod_alias;
|
||||
mod->numframes = 0;
|
||||
mod->funcs.NativeTrace = Mod_Trace;
|
||||
return !!mod->meshinfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void Alias_Register(void)
|
||||
{
|
||||
|
@ -9203,4 +9527,7 @@ void Alias_Register(void)
|
|||
Mod_RegisterModelFormatText(NULL, "MD5 Mesh/Anim (md5mesh)", "MD5Version", Mod_LoadMD5MeshModel);
|
||||
Mod_RegisterModelFormatText(NULL, "External Anim", "EXTERNALANIM", Mod_LoadCompositeAnim);
|
||||
#endif
|
||||
#ifdef MODELFMT_OBJ
|
||||
Mod_RegisterModelFormatText(NULL, "Wavefront Object (obj)", ".obj", Mod_LoadObjModel);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
#define HALFLIFEMODELS //horrible format that doesn't interact well with the rest of FTE's code. Unusable tools (due to license reasons).
|
||||
#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
|
||||
#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets).
|
||||
#define MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too
|
||||
#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
|
||||
|
||||
//Image formats
|
||||
|
|
|
@ -1434,7 +1434,7 @@ static void Mod_LoadModelWorker (void *ctx, void *data, size_t a, size_t b)
|
|||
//look for known extensions first, to try to avoid issues with specific formats
|
||||
for(i = 0; i < countof(modelloaders); i++)
|
||||
{
|
||||
if (modelloaders[i].load && modelloaders[i].ident && *modelloaders[i].ident == '.' && !Q_strcasecmp(modelloaders[i].ident+1, ext))
|
||||
if (modelloaders[i].load && modelloaders[i].ident && *modelloaders[i].ident == '.' && !Q_strcasecmp(modelloaders[i].ident, COM_GetFileExtension(mod->name, NULL)))
|
||||
break;
|
||||
}
|
||||
//now look to see if we can find one with the right magic header
|
||||
|
|
Loading…
Reference in a new issue