fteqw/plugins/models/models.c

865 lines
22 KiB
C
Raw Normal View History

#ifndef GLQUAKE
#define GLQUAKE //this is shit.
#endif
#include "quakedef.h"
#include "../plugin.h"
#include "com_mesh.h"
plugmodfuncs_t *modfuncs;
plugfsfuncs_t *filefuncs;
//#define ASEMODELS //FIXME: TEST TEST TEST. shold be working iiuc
//#define LWOMODELS //not working
#ifdef SKELETALMODELS
#define GLTFMODELS //FIXME: not yet working properly.
extern qboolean Plug_GLTF_Init(void);
#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
extern qboolean QDECL Mod_LoadGLTFModel (struct model_s *mod, void *buffer, size_t fsize);
extern qboolean QDECL Mod_LoadGLBModel (struct model_s *mod, void *buffer, size_t fsize);
extern void Mod_ExportIQM(char *fname, int flags, galiasinfo_t *mesh);
#if !defined(SERVERONLY) && defined(SKELETALMODELS)
static void Mod_ExportIQM_f(void)
{
char tok[128];
model_t *mod;
cmdfuncs->Argv(1, tok, sizeof(tok));
mod = modfuncs->GetModel(tok, MLV_WARNSYNC);
if (!mod || mod->type != mod_alias || !mod->meshinfo)
Con_Printf("Couldn't load \"%s\"\n", tok);
else
{
cmdfuncs->Argv(2, tok, sizeof(tok));
Mod_ExportIQM(tok, mod->flags, mod->meshinfo);
}
}
#endif
qboolean Plug_Init(void)
{
filefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));
modfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));
Added sys_openfile console command(and menu option) to web and flatpak(via cmake+dbus) builds, to 'install' packages on sandboxed systems a bit more easily. Cmake: Add FTE_WERROR option, defaults to true in debug builds and off in release builds (in case future compilers have issues). Cmake: Pull in libXscreensaver so we don't get interrupted by screensavers when playing demos. Make: Added `make webcl-rel` for a web build without server bloat (eg for sites focused on demo playback. Yes, this means you XantoM). fteqcc: Include the decompiler in fteqcc (non-gui) builds ('-d' arg). fteqcc: Decompiler can now mostly handle hexen2 mods without any unknown opcodes. Allow ezHud and OpenSSL to be compiled as in-engine plugins, potentially for web and windows ports respectively. Web: Fix support for ogg vorbis. Add support for voip. Web: Added basic support for WebXR. QTV: Don't try seeking on unseekable qtv streams. Don't spam when developer 1 is set. QTV: add support for some eztv extensions. MVD: added hack to use ktx's vweps in mvd where mvdsv doesn't bother to record the info. qwfwd: hack around a hack in qwfwd, allowing it to work again. recording: favour qwd in single player, instead of mvd. Protocol: reduce client memory used for precache names. Bump maximum precache counts - some people are just abusive, yes you Orl. hexen2: add enough clientside protocol compat to play the demo included with h2mp. lacks effects. in_xflip: restored this setting. fs_hidesyspaths: new cvar, defaults to enabled so you won't find your username or whatever turning up in screenshots or the like. change it to 0 before debuging stuff eg via 'path'. gl_overbright_models: Added cvar to match QS. netchan: Added MTU determination, we'll no longer fail to connect when routers stupidly drop icmp packets. Win: try a few other versions of xinput too. CSQC: Added a CSQC_GenerateMaterial function, to give the csqc a chance to generate custom materials. MenuQC: Added support for the skeletal objects API.
2024-04-09 17:13:59 +00:00
if (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION)
modfuncs = NULL;
if (modfuncs && filefuncs)
{
#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
Plug_GLTF_Init();
#endif
#if !defined(SERVERONLY) && defined(SKELETALMODELS)
cmdfuncs->AddCommand("exportiqm", Mod_ExportIQM_f, NULL);
#endif
return true;
}
return false;
}