1
0
Fork 0
forked from fte/fteqw
fteqw/plugins/models/models.c
Spoike b3050121cf Some Android tweaks.
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
2019-05-10 09:31:21 +00:00

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;
}