fteqw/plugins/models/models.c
Spoike fa0c73d33b Fixed crash from too many csqc entities (reported by shpuld)
Added .psd, .pbm/.pgm/.ppm, .pfm, and .hdr image formats. Extensions NOT added to r_imageextensions.
png (and the above formats) can now be loaded as RGBA16, instead of being truncated to RGBA8 (8bit pngs not affected).
r_imagelist will now show images from memory, instead of potentially loading new/different ones from disk.
Fix serverbrowser bug being too eager to join the server (eg from alt+tab).
Don't send ipv6 packets to qw/q2 masters. They won't be able to report ipv6 addresses anyway, and this reduces warnings when a host STILL has no ipv6 (my ISP sucks). this does not affect q3/dpmasters, for people without ipv4 addresses.
Tried to improve compat with Bloodshot's particle effects.
Fixed a couple of issues with R_AddTrisoup.
Fixed string tokenizing bug where it was using the wrong buffer size values.
Don't show link-local/localhost addresses in eg the status command (unless developer).
qtv-rel is now an easier target, for new qtv releases.
qtv warning fixes.
added a nailtrail effect to 'high' particles.
fixed terrain shaders.
fixed fogged water issue (on one of bal's maps).
first attempt at gltf2 format support



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5400 fc73d0e0-1445-4013-8a0c-d673dee63da5
2019-02-16 19:09:07 +00:00

843 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
#define GLTFMODELS //FIXME: not yet working properly.
#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;
}