mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 22:01:50 +00:00
b3050121cf
clean up args to Net_PextMask. some build config fixes. attempt to support meag's 'status 32' qtv address lists. fix bug with vulkan+sdl renderer. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5460 fc73d0e0-1445-4013-8a0c-d673dee63da5
845 lines
22 KiB
C
845 lines
22 KiB
C
#ifndef GLQUAKE
|
|
#define GLQUAKE //this is shit.
|
|
#endif
|
|
#include "quakedef.h"
|
|
#include "../plugin.h"
|
|
#include "com_mesh.h"
|
|
modplugfuncs_t *modfuncs;
|
|
|
|
//#define ASEMODELS //FIXME: TEST TEST TEST. shold be working iiuc
|
|
//#define LWOMODELS //not working
|
|
#ifdef SKELETALMODELS
|
|
#define GLTFMODELS //FIXME: not yet working properly.
|
|
#endif
|
|
|
|
#ifdef ASEMODELS
|
|
struct aseimport_s
|
|
{
|
|
char *file;
|
|
|
|
struct asematerial_s
|
|
{
|
|
char name[MAX_QPATH];
|
|
|
|
struct asemesh_s
|
|
{
|
|
struct asemesh_s *next;
|
|
|
|
struct asevert_s
|
|
{
|
|
vec3_t xyz;
|
|
vec3_t norm;
|
|
vec2_t st;
|
|
} *vert;
|
|
unsigned int numverts;
|
|
unsigned int maxverts;
|
|
|
|
index_t *index;
|
|
unsigned int numindexes;
|
|
unsigned int maxindexes;
|
|
|
|
} *meshes;
|
|
} *materials;
|
|
unsigned int nummaterials;
|
|
};
|
|
|
|
static char *ASE_GetToken(struct aseimport_s *ase, char *token, size_t tokensize)
|
|
{
|
|
char *ret = token;
|
|
tokensize--;
|
|
|
|
while(*ase->file == ' ' || *ase->file == '\t')
|
|
ase->file++;
|
|
if (*ase->file == '\n')
|
|
{
|
|
*ret = 0;
|
|
return ret;
|
|
}
|
|
|
|
if (*ase->file == '\"')
|
|
{
|
|
ase->file += 1;
|
|
while(tokensize-- > 0)
|
|
{
|
|
char i = *ase->file;
|
|
if (i == '\n')
|
|
break;
|
|
ase->file+=1;
|
|
if (i == '\"')
|
|
break;
|
|
|
|
*token++ = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(tokensize-- > 0)
|
|
{
|
|
char i = *ase->file;
|
|
if (i == ' ' || i == '\t' || i == '\r' || i == '\n')
|
|
break;
|
|
ase->file+=1;
|
|
|
|
*token++ = i;
|
|
}
|
|
}
|
|
*token = 0;
|
|
return ret;
|
|
}
|
|
|
|
static void ASE_SkipLine(struct aseimport_s *ase)
|
|
{
|
|
while (*ase->file)
|
|
{
|
|
if (*ase->file == '\n')
|
|
{
|
|
ase->file += 1;
|
|
break;
|
|
}
|
|
ase->file += 1;
|
|
}
|
|
}
|
|
|
|
static void ASE_SkipBlock(struct aseimport_s *ase)
|
|
{
|
|
while (*ase->file)
|
|
{
|
|
if (*ase->file == '\n' || *ase->file == '{')
|
|
break;
|
|
ase->file += 1;
|
|
}
|
|
if (*ase->file == '{')
|
|
{
|
|
char token[1024];
|
|
ASE_SkipLine(ase);
|
|
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else
|
|
{
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ASE_Material(struct aseimport_s *ase)
|
|
{
|
|
int idx;
|
|
char token[1024];
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
idx = atoi(token);
|
|
|
|
if (idx < 0 || idx >= ase->nummaterials)
|
|
{
|
|
Con_Printf("invalid material index: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
return;
|
|
}
|
|
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
ASE_SkipLine(ase);
|
|
if (strcmp(token, "{"))
|
|
return;
|
|
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*MATERIAL_NAME"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
Q_strlcpy(ase->materials[idx].name, token, sizeof(ase->materials[idx].name));
|
|
}
|
|
else if (!strcmp(token, "*MATERIAL_CLASS") || !strcmp(token, "*MATERIAL_DIFFUSE") || !strcmp(token, "*MATERIAL_SHADING") || !strcmp(token, "*MAP_DIFFUSE"))
|
|
ASE_SkipBlock(ase);
|
|
else
|
|
{
|
|
Con_Printf("Unknown top-level identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
static void ASE_MaterialList(struct aseimport_s *ase)
|
|
{
|
|
char token[1024];
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
ASE_SkipLine(ase);
|
|
if (strcmp(token, "{"))
|
|
return;
|
|
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*MATERIAL_COUNT"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
|
|
if (!ase->materials)
|
|
{
|
|
ase->nummaterials = atoi(token);
|
|
ase->materials = malloc(sizeof(*ase->materials) * ase->nummaterials);
|
|
memset(ase->materials, 0, sizeof(*ase->materials) * ase->nummaterials);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*MATERIAL"))
|
|
{
|
|
ASE_Material(ase);
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown top-level identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
|
|
static void ASE_GeomObject(struct aseimport_s *ase)
|
|
{
|
|
size_t materialidx = 0;
|
|
size_t numverts = 0;
|
|
size_t numtverts = 0;
|
|
size_t numtris = 0;
|
|
struct
|
|
{
|
|
vec3_t xyz;
|
|
vec3_t norm;
|
|
} *verts = NULL;
|
|
struct
|
|
{
|
|
vec3_t st;
|
|
} *tverts = NULL;
|
|
struct
|
|
{
|
|
index_t vidx[3];
|
|
index_t tidx[3];
|
|
} *tris = NULL;
|
|
size_t idx;
|
|
|
|
char token[1024];
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
ASE_SkipLine(ase);
|
|
if (strcmp(token, "{"))
|
|
return;
|
|
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*MATERIAL_REF"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
materialidx = atoi(token);
|
|
}
|
|
else if (!strcmp(token, "*MESH"))
|
|
{
|
|
ASE_SkipLine(ase);
|
|
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*TIMEVALUE"))
|
|
ASE_SkipBlock(ase); //not useful
|
|
else if (!strcmp(token, "*MESH_NUMVERTEX"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!verts)
|
|
{
|
|
numverts = atoi(token);
|
|
verts = malloc(sizeof(*verts) * numverts);
|
|
memset(verts, 0, sizeof(*verts) * numverts);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*MESH_NUMFACES"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!tris)
|
|
{
|
|
numtris = atoi(token);
|
|
tris = malloc(sizeof(*tris) * numtris);
|
|
memset(tris, 0, sizeof(*tris) * numtris);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*COMMENT"))
|
|
ASE_SkipBlock(ase); //unused
|
|
else if (!strcmp(token, "*MESH_VERTEX_LIST"))
|
|
{
|
|
ASE_SkipLine(ase);
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*MESH_VERTEX"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
idx = atoi(token);
|
|
if (idx >= 0 && idx < numverts)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
verts[idx].xyz[0] = atof(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
verts[idx].xyz[1] = atof(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
verts[idx].xyz[2] = atof(token);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown MESH_VERTEX_LIST identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*MESH_NORMALS"))
|
|
{
|
|
ASE_SkipLine(ase);
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*MESH_FACENORMAL"))
|
|
ASE_SkipBlock(ase); //we don't give a fuck about these. its not usable for us. we'll calculate these if we actually need them, that way we're sure it actually matches the geometry and doesn't bug out.
|
|
else if (!strcmp(token, "*MESH_VERTEXNORMAL"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
idx = atoi(token);
|
|
if (idx >= 0 && idx < numverts)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
verts[idx].norm[0] = atof(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
verts[idx].norm[1] = atof(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
verts[idx].norm[2] = atof(token);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown MESH_NORMALS identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*MESH_FACE_LIST"))
|
|
{
|
|
ASE_SkipLine(ase);
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*MESH_FACE"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
idx = atoi(token);
|
|
if (idx >= 0 && idx < numtris)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
while(*token)
|
|
{
|
|
if (!strcmp(token, "A:"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tris[idx].vidx[0] = atoi(token);
|
|
}
|
|
else if (!strcmp(token, "B:"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tris[idx].vidx[1] = atoi(token);
|
|
}
|
|
else if (!strcmp(token, "C:"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tris[idx].vidx[2] = atoi(token);
|
|
}
|
|
else if (!strcmp(token, "AB:") || !strcmp(token, "BC:") || !strcmp(token, "CA:") || !strcmp(token, "*MESH_SMOOTHING") || !strcmp(token, "*MESH_MTLID"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown MESH_FACE identifier: %s\n", token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
}
|
|
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
}
|
|
tris[idx].tidx[0] = atoi(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tris[idx].tidx[1] = atoi(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tris[idx].tidx[2] = atoi(token);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown MESH_FACE_LIST identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*MESH_NUMTVERTEX"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!tverts)
|
|
{
|
|
numtverts = atoi(token);
|
|
tverts = malloc(sizeof(*tverts) * numtverts);
|
|
memset(tverts, 0, sizeof(*tverts) * numtverts);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*MESH_TVERTLIST"))
|
|
{
|
|
ASE_SkipLine(ase);
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*MESH_TVERT"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
idx = atoi(token);
|
|
if (idx >= 0 && idx < numverts)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tverts[idx].st[0] = atof(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tverts[idx].st[1] = atof(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tverts[idx].st[2] = atof(token);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown MESH_TVERTLIST identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*MESH_NUMTVFACES"))
|
|
ASE_SkipBlock(ase); //should be equal to MESH_NUMFACES
|
|
else if (!strcmp(token, "*MESH_TFACELIST"))
|
|
{
|
|
ASE_SkipLine(ase);
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "}"))
|
|
break;
|
|
else if (!strcmp(token, "*MESH_TFACE"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
idx = atoi(token);
|
|
if (idx >= 0 && idx < numtris)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tris[idx].tidx[0] = atoi(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tris[idx].tidx[1] = atoi(token);
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
tris[idx].tidx[2] = atoi(token);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown MESH_TFACELIST identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown top-level identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "*NODE_NAME") || !strcmp(token, "*NODE_TM")
|
|
|| !strcmp(token, "*PROP_MOTIONBLUR")|| !strcmp(token, "*PROP_CASTSHADOW")|| !strcmp(token, "*PROP_RECVSHADOW"))
|
|
ASE_SkipBlock(ase);
|
|
else
|
|
{
|
|
Con_Printf("Unknown top-level identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
ASE_SkipLine(ase);
|
|
}
|
|
|
|
//merge into a mesh
|
|
if (materialidx >= 0 && materialidx < ase->nummaterials)
|
|
{
|
|
size_t v, tri, idx;
|
|
struct asemesh_s *mesh;
|
|
struct asevert_s asevert;
|
|
mesh = ase->materials[materialidx].meshes;
|
|
|
|
//don't let any single mesh exceed 65k verts. stuff bugs out then.
|
|
if (!mesh || mesh->numverts + numtris*3 > 0xffff)
|
|
{
|
|
mesh = malloc(sizeof(*mesh));
|
|
memset(mesh, 0, sizeof(*mesh));
|
|
mesh->next = ase->materials[materialidx].meshes;
|
|
ase->materials[materialidx].meshes = mesh;
|
|
}
|
|
|
|
//make sure there's going to be enough space
|
|
if (mesh->numverts + numtris*3 > mesh->maxverts)
|
|
{
|
|
mesh->maxverts = (mesh->maxverts+numtris*3)*2;
|
|
mesh->vert = realloc(mesh->vert, sizeof(*mesh->vert) * mesh->maxverts);
|
|
}
|
|
if (mesh->numindexes + numtris*3 > mesh->maxindexes)
|
|
{
|
|
mesh->maxindexes = (mesh->maxindexes+numtris*3)*2;
|
|
mesh->index = realloc(mesh->index, sizeof(*mesh->index) * mesh->maxindexes);
|
|
}
|
|
|
|
//insert each triangle into the mesh
|
|
for (tri = 0; tri < numtris; tri++)
|
|
{
|
|
for (v = 3; v --> 0; )
|
|
{
|
|
VectorCopy(verts[tris[tri].vidx[v]].xyz, asevert.xyz);
|
|
VectorCopy(verts[tris[tri].vidx[v]].norm, asevert.norm);
|
|
Vector2Copy(tverts[tris[tri].tidx[v]].st, asevert.st);
|
|
|
|
for (idx = 0; idx < mesh->numverts; idx++)
|
|
{
|
|
if (!memcmp(&mesh->vert[idx], &asevert, sizeof(asevert)))
|
|
break;
|
|
}
|
|
if (idx == mesh->numverts)
|
|
mesh->vert[mesh->numverts++] = asevert;
|
|
mesh->index[mesh->numindexes++] = idx;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(verts);
|
|
free(tverts);
|
|
free(tris);
|
|
}
|
|
|
|
static void ASE_TopLevel(struct aseimport_s *ase)
|
|
{
|
|
char token[1024];
|
|
while (*ase->file)
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token));
|
|
if (!strcmp(token, "*3DSMAX_ASCIIEXPORT"))
|
|
{
|
|
ASE_GetToken(ase, token, sizeof(token)); //version
|
|
}
|
|
else if (!strcmp(token, "*COMMENT") || !strcmp(token, "*SCENE"))
|
|
ASE_SkipBlock(ase);
|
|
else if (!strcmp(token, "*MATERIAL_LIST"))
|
|
ASE_MaterialList(ase);
|
|
else if (!strcmp(token, "*GEOMOBJECT"))
|
|
ASE_GeomObject(ase);
|
|
else
|
|
{
|
|
Con_Printf("Unknown top-level identifier: %s\n", token);
|
|
ASE_SkipBlock(ase);
|
|
}
|
|
|
|
ASE_SkipLine(ase);
|
|
}
|
|
}
|
|
static qboolean QDECL Mod_LoadASEModel (struct model_s *mod, void *buffer, size_t fsize)
|
|
{
|
|
galiaspose_t *pose;
|
|
galiasinfo_t *surf;
|
|
size_t i, m;
|
|
struct aseimport_s ase;
|
|
memset(&ase, 0, sizeof(ase));
|
|
ase.file = buffer;
|
|
ASE_TopLevel(&ase);
|
|
|
|
//we need to generate engine meshes and clean up any dynamic memory
|
|
for (m = 0; m < ase.nummaterials; m++)
|
|
{
|
|
struct asemesh_s *mesh;
|
|
while(ase.materials[m].meshes)
|
|
{
|
|
mesh = ase.materials[m].meshes;
|
|
ase.materials[m].meshes = mesh->next;
|
|
|
|
surf = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf));
|
|
surf->nextsurf = mod->meshinfo;
|
|
mod->meshinfo = surf;
|
|
|
|
surf->numverts = mesh->numverts;
|
|
surf->numindexes = mesh->numindexes;
|
|
|
|
surf->ofs_indexes = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofs_indexes) * mesh->numindexes);
|
|
for (i = 0; i < mesh->numindexes; i++)
|
|
surf->ofs_indexes[i] = mesh->index[i];
|
|
|
|
surf->numanimations = 1;
|
|
surf->ofsanimations = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsanimations));
|
|
surf->ofsanimations->poseofs = pose = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose));
|
|
surf->ofsanimations->loop = true;
|
|
surf->ofsanimations->numposes = 1;
|
|
surf->ofsanimations->rate = 10;
|
|
|
|
pose->ofsverts = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose->ofsverts) * mesh->numverts);
|
|
pose->ofsnormals = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose->ofsnormals) * mesh->numverts);
|
|
surf->ofs_st_array = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofs_st_array) * mesh->numverts);
|
|
|
|
for (i = 0; i < mesh->numverts; i++)
|
|
{
|
|
VectorCopy(mesh->vert[i].xyz, pose->ofsverts[i]);
|
|
VectorCopy(mesh->vert[i].norm, pose->ofsnormals[i]);
|
|
Vector2Copy(mesh->vert[i].st, surf->ofs_st_array[i]);
|
|
}
|
|
|
|
surf->numskins = 1;
|
|
surf->ofsskins = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsskins));
|
|
surf->ofsskins->numframes = 1;
|
|
surf->ofsskins->skinspeed = 0.1;
|
|
surf->ofsskins->frame = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsskins->frame));
|
|
Q_strlcpy(surf->ofsskins->frame->shadername, ase.materials[m].name, sizeof(surf->ofsskins->frame->shadername));
|
|
Q_strlcpy(surf->ofsskins->name, ase.materials[m].name, sizeof(surf->ofsskins->name));
|
|
|
|
free(mesh->vert);
|
|
free(mesh->index);
|
|
free(mesh);
|
|
}
|
|
}
|
|
free(ase.materials);
|
|
|
|
mod->type = mod_alias;
|
|
return !!mod->meshinfo;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef LWOMODELS
|
|
static char *LWO_ReadString(unsigned char **file)
|
|
{ //strings are just null terminated.
|
|
//however, they may have an extra byte of padding. yay shorts...
|
|
//so skip the null and round up the length.
|
|
char *ret = *file;
|
|
size_t len = strlen(ret);
|
|
*file = *file + ((len + 2)&~1);
|
|
return ret;
|
|
}
|
|
|
|
static qboolean QDECL Mod_LoadLWOModel (struct model_s *mod, void *buffer, size_t fsize)
|
|
{
|
|
unsigned char *file = buffer, *fileend;
|
|
char *tags;
|
|
vec3_t *points;
|
|
size_t numpoints;
|
|
unsigned int tagssize;
|
|
size_t subsize;
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
if (strncmp(file, "FORM", 4) || subsize+8 != fsize)
|
|
return false; //not an lwo, or corrupt, or something
|
|
file += 8;
|
|
fileend = file + subsize;
|
|
if (strncmp(file, "LWO2", 4))
|
|
return false;
|
|
file += 4;
|
|
while (file < fileend)
|
|
{
|
|
if (!strncmp(file, "TAGS", 4))
|
|
{
|
|
tagssize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
tags = file+8;
|
|
file += 8+tagssize;
|
|
}
|
|
else if (!strncmp(file, "LAYR", 4))
|
|
{
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
//fixme:
|
|
file += 8+subsize;
|
|
}
|
|
else if (!strncmp(file, "PNTS", 4))
|
|
{
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
numpoints = subsize / sizeof(vec3_t);
|
|
points = (vec3_t*)(file+8);
|
|
file += 8+subsize;
|
|
}
|
|
else if (!strncmp(file, "BBOX", 4))
|
|
{
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
//we don't really care.
|
|
file += 8+subsize;
|
|
}
|
|
else if (!strncmp(file, "VMAP", 4))
|
|
{
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
if (!strcmp(file, "TXUV"))
|
|
{
|
|
}
|
|
// else if (!strcmp(file, "RGBA") || !strcmp(file, "RGB"))
|
|
// {
|
|
// }
|
|
file += 8+subsize;
|
|
}
|
|
else if (!strncmp(file, "POLS", 4))
|
|
{
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
file += 8;
|
|
if (!strcmp(file, "FACE") || !strcmp(file, "PTCH"))
|
|
{
|
|
|
|
}
|
|
file += subsize;
|
|
}
|
|
else if (!strncmp(file, "PTAG", 4))
|
|
{
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
//fixme:
|
|
file += 8+subsize;
|
|
}
|
|
else if (!strncmp(file, "VMAD", 4))
|
|
{
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
//fixme:
|
|
file += 8+subsize;
|
|
}
|
|
else if (!strncmp(file, "SURF", 4))
|
|
{
|
|
char *surfend;
|
|
char *surfname, *sourcename;
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
file += 8;
|
|
surfend = file + subsize;
|
|
surfname = LWO_ReadString(&file);
|
|
sourcename = LWO_ReadString(&file);
|
|
Con_Printf("surf=%s source=%s\n", surfname, sourcename);
|
|
while (file < surfend)
|
|
{
|
|
if (!strncmp(file, "COLR", 4))
|
|
{
|
|
subsize = file[5] | (file[4]<<8);
|
|
//fixme
|
|
file += 6+subsize;
|
|
}
|
|
else if (!strncmp(file, "DIFF", 4))
|
|
{
|
|
subsize = file[5] | (file[4]<<8);
|
|
//fixme
|
|
file += 6+subsize;
|
|
}
|
|
else if (!strncmp(file, "SMAN", 4))
|
|
{
|
|
subsize = file[5] | (file[4]<<8);
|
|
//fixme
|
|
file += 6+subsize;
|
|
}
|
|
else if (!strncmp(file, "BLOK", 4))
|
|
{
|
|
char *blokend;
|
|
subsize = file[5] | (file[4]<<8);
|
|
file += 6;
|
|
blokend = file + subsize;
|
|
while (file < blokend)
|
|
{
|
|
if (!strncmp(file, "IMAP", 4))
|
|
;
|
|
else if (!strncmp(file, "TMAP", 4))
|
|
;
|
|
else if (!strncmp(file, "PROJ", 4))
|
|
;
|
|
else if (!strncmp(file, "AXIS", 4))
|
|
;
|
|
else if (!strncmp(file, "IMAG", 4))
|
|
;
|
|
else if (!strncmp(file, "WRAP", 4))
|
|
;
|
|
else if (!strncmp(file, "WRPW", 4))
|
|
;
|
|
else if (!strncmp(file, "WRPH", 4))
|
|
;
|
|
else if (!strncmp(file, "VMAP", 4))
|
|
;
|
|
else if (!strncmp(file, "AAST", 4))
|
|
;
|
|
else if (!strncmp(file, "PIXB", 4))
|
|
;
|
|
else
|
|
Con_Printf("Unknown BLOK ID: %c%c%c%c\n", file[0], file[1], file[2], file[3]);
|
|
subsize = file[5] | (file[4]<<8);
|
|
file += 6+subsize;
|
|
}
|
|
file = blokend;
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown SURF ID: %c%c%c%c\n", file[0], file[1], file[2], file[3]);
|
|
subsize = file[5] | (file[4]<<8);
|
|
file += 6+subsize;
|
|
}
|
|
}
|
|
file = surfend;
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Unknown ID: %c%c%c%c\n", file[0], file[1], file[2], file[3]);
|
|
subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);
|
|
file += 8+subsize;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
qboolean QDECL Mod_LoadGLTFModel (struct model_s *mod, void *buffer, size_t fsize);
|
|
qboolean QDECL Mod_LoadGLBModel (struct model_s *mod, void *buffer, size_t fsize);
|
|
|
|
qintptr_t Plug_Init(qintptr_t *args)
|
|
{
|
|
CHECKBUILTIN(Mod_GetPluginModelFuncs);
|
|
|
|
if (BUILTINISVALID(Mod_GetPluginModelFuncs))
|
|
{
|
|
modfuncs = pMod_GetPluginModelFuncs(sizeof(modplugfuncs_t));
|
|
if (modfuncs && modfuncs->version < MODPLUGFUNCS_VERSION)
|
|
modfuncs = NULL;
|
|
}
|
|
|
|
if (modfuncs)
|
|
{
|
|
#ifdef ASEMODELS
|
|
modfuncs->RegisterModelFormatText("ASE models (ase)", "*3DSMAX_ASCIIEXPORT", Mod_LoadASEModel);
|
|
#endif
|
|
#ifdef LWOMODELS
|
|
modfuncs->RegisterModelFormatMagic("LWO models (lwo)", (('M'<<24)+('R'<<16)+('O'<<8)+'F'), Mod_LoadLWOModel);
|
|
#endif
|
|
#ifdef GLTFMODELS
|
|
modfuncs->RegisterModelFormatText("glTF2 models (glTF)", ".gltf", Mod_LoadGLTFModel);
|
|
modfuncs->RegisterModelFormatMagic("glTF2 models (glb)", (('F'<<24)+('T'<<16)+('l'<<8)+'g'), Mod_LoadGLBModel);
|
|
#endif
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|