1
0
Fork 0
forked from fte/fteqw

Integrate gltf2 support without external plugin.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5605 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-01-20 18:36:45 +00:00
parent 8e656b4af8
commit ffda35fae4
9 changed files with 211 additions and 60 deletions

View file

@ -1,3 +1,6 @@
#ifndef COM_MESH_H
#define COM_MESH_H
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -91,7 +94,6 @@ typedef struct
//we can't be bothered with animating skins. //we can't be bothered with animating skins.
//We'll load up to four of them but after that you're on your own //We'll load up to four of them but after that you're on your own
#ifndef SERVERONLY #ifndef SERVERONLY
typedef struct typedef struct
{ {
shader_t *shader; shader_t *shader;
@ -100,7 +102,7 @@ typedef struct
char shadername[MAX_QPATH]; char shadername[MAX_QPATH];
texnums_t texnums; texnums_t texnums;
} skinframe_t; } skinframe_t;
typedef struct struct galiasskin_s
{ {
int skinwidth; int skinwidth;
int skinheight; int skinheight;
@ -108,7 +110,7 @@ typedef struct
int numframes; int numframes;
skinframe_t *frame; skinframe_t *frame;
char name[MAX_QPATH]; char name[MAX_QPATH];
} galiasskin_t; };
typedef struct typedef struct
{ {
@ -121,9 +123,8 @@ typedef struct
unsigned int subframe; unsigned int subframe;
bucket_t bucket; bucket_t bucket;
} galiascolourmapped_t; } galiascolourmapped_t;
#else
typedef void galiasskin_t;
#endif #endif
typedef struct galiasskin_s galiasskin_t;
typedef struct typedef struct
{ {
@ -276,4 +277,5 @@ void R_Generate_Mesh_ST_Vectors(mesh_t *mesh);
#ifdef __cplusplus #ifdef __cplusplus
}; };
#endif #endif
#endif //COM_MESH_H

View file

@ -186,6 +186,7 @@
//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data. //#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data.
//#define MODELFMT_MDX //#define MODELFMT_MDX
//#define MODELFMT_OBJ //#define MODELFMT_OBJ
//#define MODELFMT_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies. //#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.

View file

@ -85,6 +85,7 @@
#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective. #define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets). #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 MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too
#define MODELFMT_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...). #define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
//Image formats //Image formats

View file

@ -87,6 +87,7 @@
//#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective. //#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
//#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets). //#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 MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too
//#define MODELFMT_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
//#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...). //#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
//Image formats //Image formats

View file

@ -85,6 +85,7 @@
#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective. #define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
//#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets). //#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 MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too
//#define MODELFMT_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...). #define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
//Image formats //Image formats

View file

@ -194,6 +194,7 @@
//#define HAVE_HTTPSV //#define HAVE_HTTPSV
//#define MODELFMT_MDX //#define MODELFMT_MDX
//#define MODELFMT_OBJ //#define MODELFMT_OBJ
//#define MODELFMT_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
#ifdef COMPILE_OPTS #ifdef COMPILE_OPTS
//things to configure qclib, which annoyingly doesn't include this file itself //things to configure qclib, which annoyingly doesn't include this file itself

View file

@ -9,6 +9,13 @@
#ifdef PLUGINS #ifdef PLUGINS
#ifdef MODELFMT_GLTF
#define Q_snprintf Q_snprintfz
#define Q_strlcpy Q_strncpyz
#define Q_strlcat Q_strncatz
#include "../plugins/models/gltf.c"
#endif
cvar_t plug_sbar = CVARD("plug_sbar", "3", "Controls whether plugins are allowed to draw the hud, rather than the engine (when allowed by csqc). This is typically used to permit the ezhud plugin without needing to bother unloading it.\n=0: never use hud plugins.\n&1: Use hud plugins in deathmatch.\n&2: Use hud plugins in singleplayer/coop.\n=3: Always use hud plugins (when loaded)."); cvar_t plug_sbar = CVARD("plug_sbar", "3", "Controls whether plugins are allowed to draw the hud, rather than the engine (when allowed by csqc). This is typically used to permit the ezhud plugin without needing to bother unloading it.\n=0: never use hud plugins.\n&1: Use hud plugins in deathmatch.\n&2: Use hud plugins in singleplayer/coop.\n=3: Always use hud plugins (when loaded).");
cvar_t plug_loaddefault = CVARD("plug_loaddefault", "1", "0: Load plugins only via explicit plug_load commands\n1: Load built-in plugins and those selected via the package manager\n2: Scan for misc plugins, loading all that can be found, but not built-ins.\n3: Scan for plugins, and then load any built-ins"); cvar_t plug_loaddefault = CVARD("plug_loaddefault", "1", "0: Load plugins only via explicit plug_load commands\n1: Load built-in plugins and those selected via the package manager\n2: Scan for misc plugins, loading all that can be found, but not built-ins.\n3: Scan for plugins, and then load any built-ins");
@ -23,6 +30,9 @@ static struct
#endif #endif
#if defined(USE_INTERNAL_ODE) #if defined(USE_INTERNAL_ODE)
{"ODE_internal", Plug_ODE_Init}, {"ODE_internal", Plug_ODE_Init},
#endif
#if defined(MODELFMT_GLTF)
{"GLTF", Plug_GLTF_Init},
#endif #endif
{NULL} {NULL}
}; };
@ -236,6 +246,7 @@ static plugin_t *Plug_Load(const char *file)
Con_DPrintf("Activated module %s\n", file); Con_DPrintf("Activated module %s\n", file);
newplug->lib = NULL; newplug->lib = NULL;
Q_strncpyz(newplug->filename, staticplugins[u].name, sizeof(newplug->filename));
currentplug = newplug; currentplug = newplug;
success = staticplugins[u].initfunction(); success = staticplugins[u].initfunction();
break; break;
@ -1614,6 +1625,7 @@ int QDECL Plug_List_Print(const char *fname, qofs_t fsize, time_t modtime, void
//lots of awkward logic so we hide modules for other cpus. //lots of awkward logic so we hide modules for other cpus.
size_t nl = strlen(fname); size_t nl = strlen(fname);
size_t u; size_t u;
char *arch_ext = ARCH_DL_POSTFIX;
static const char *knownarch[] = static const char *knownarch[] =
{ {
"x32", "x64", "amd64", "x86", //various x86 ABIs "x32", "x64", "amd64", "x86", //various x86 ABIs
@ -1625,9 +1637,14 @@ int QDECL Plug_List_Print(const char *fname, qofs_t fsize, time_t modtime, void
while ((mssuck=strchr(fname, '\\'))) while ((mssuck=strchr(fname, '\\')))
*mssuck = '/'; *mssuck = '/';
#endif #endif
if (nl >= strlen(ARCH_DL_POSTFIX) && !Q_strcasecmp(fname+nl-strlen(ARCH_DL_POSTFIX), ARCH_DL_POSTFIX)) if (!parm)
{ {
nl -= strlen(ARCH_DL_POSTFIX); parm = "";
arch_ext = ""; //static plugins have no extension.
}
if (nl >= strlen(arch_ext) && !Q_strcasecmp(fname+nl-strlen(arch_ext), arch_ext))
{
nl -= strlen(arch_ext);
for (u = 0; u < countof(knownarch); u++) for (u = 0; u < countof(knownarch); u++)
{ {
size_t al = strlen(knownarch[u]); size_t al = strlen(knownarch[u]);
@ -1664,10 +1681,18 @@ void Plug_List_f(void)
char rootpath[MAX_OSPATH]; char rootpath[MAX_OSPATH];
unsigned int u; unsigned int u;
plugin_t *plug; plugin_t *plug;
Con_Printf("Loaded plugins:\n"); Con_Printf("Loaded plugins:\n");
for (plug = plugs; plug; plug = plug->next) for (plug = plugs; plug; plug = plug->next)
Con_Printf("^[^2%s\\type\\plug_close %s\\^]: loaded\n", plug->filename, plug->name); Con_Printf("^[^2%s\\type\\plug_close %s\\^]: loaded\n", plug->filename, plug->name);
if (staticplugins[0].name)
{
Con_DPrintf("Internal plugins:\n");
for (u = 0; staticplugins[u].name; u++)
Plug_List_Print(staticplugins[u].name, 0, 0, NULL, NULL);
}
if (FS_NativePath("", FS_BINARYPATH, binarypath, sizeof(binarypath))) if (FS_NativePath("", FS_BINARYPATH, binarypath, sizeof(binarypath)))
{ {
#ifdef _WIN32 #ifdef _WIN32
@ -1675,7 +1700,7 @@ void Plug_List_f(void)
while ((mssuck=strchr(binarypath, '\\'))) while ((mssuck=strchr(binarypath, '\\')))
*mssuck = '/'; *mssuck = '/';
#endif #endif
Con_Printf("Scanning for plugins at %s:\n", binarypath); Con_DPrintf("Scanning for plugins at %s:\n", binarypath);
Sys_EnumerateFiles(binarypath, PLUGINPREFIX"*" ARCH_DL_POSTFIX, Plug_List_Print, binarypath, NULL); Sys_EnumerateFiles(binarypath, PLUGINPREFIX"*" ARCH_DL_POSTFIX, Plug_List_Print, binarypath, NULL);
} }
if (FS_NativePath("", FS_ROOT, rootpath, sizeof(rootpath))) if (FS_NativePath("", FS_ROOT, rootpath, sizeof(rootpath)))
@ -1687,13 +1712,12 @@ void Plug_List_f(void)
#endif #endif
if (strcmp(binarypath, rootpath)) if (strcmp(binarypath, rootpath))
{ {
Con_Printf("Scanning for plugins at %s:\n", rootpath); Con_DPrintf("Scanning for plugins at %s:\n", rootpath);
Sys_EnumerateFiles(rootpath, PLUGINPREFIX"*" ARCH_DL_POSTFIX, Plug_List_Print, rootpath, NULL); Sys_EnumerateFiles(rootpath, PLUGINPREFIX"*" ARCH_DL_POSTFIX, Plug_List_Print, rootpath, NULL);
} }
} }
for (u = 0; staticplugins[u].name; u++) //should probably check downloadables too.
Plug_List_Print(staticplugins[u].name, 0, 0, "", NULL);
} }
void Plug_Shutdown(qboolean preliminary) void Plug_Shutdown(qboolean preliminary)

View file

@ -4,8 +4,8 @@
#include "quakedef.h" #include "quakedef.h"
#include "../plugin.h" #include "../plugin.h"
#include "com_mesh.h" #include "com_mesh.h"
extern plugmodfuncs_t *modfuncs; static plugmodfuncs_t *modfuncs;
extern plugfsfuncs_t *filefuncs; static plugfsfuncs_t *filefuncs;
#ifdef SKELETALMODELS #ifdef SKELETALMODELS
#define GLTFMODELS #define GLTFMODELS
@ -76,10 +76,13 @@ static void JSON_Orphan(json_t *t)
} }
static void JSON_Destroy(json_t *t) static void JSON_Destroy(json_t *t)
{ {
while(t->child) if (t)
JSON_Destroy(t->child); {
JSON_Orphan(t); while(t->child)
free(t); JSON_Destroy(t->child);
JSON_Orphan(t);
free(t);
}
} }
//node creation //node creation
@ -127,22 +130,142 @@ static json_t *JSON_CreateNode(json_t *parent, const char *namestart, const char
//node parsing //node parsing
static void JSON_SkipWhite(const char *msg, int *pos, int max) static void JSON_SkipWhite(const char *msg, int *pos, int max)
{ {
while (*pos < max && ( while (*pos < max)
msg[*pos] == ' ' || {
msg[*pos] == '\t' || //if its simple whitespace then keep skipping over it
msg[*pos] == '\r' || if (msg[*pos] == ' ' ||
msg[*pos] == '\n' msg[*pos] == '\t' ||
)) msg[*pos] == '\r' ||
*pos+=1; msg[*pos] == '\n' )
{
*pos+=1;
continue;
}
//BEGIN NON-STANDARD - Note that comments are NOT part of json, but people insist on using them anyway (c-style, like javascript).
else if (msg[*pos] == '/' && *pos+1 < max)
{
if (msg[*pos+1] == '/')
{ //C++ style single-line comments that continue till the next line break
*pos+=2;
while (*pos < max)
{
if (msg[*pos] == '\r' || msg[*pos] == '\n')
break; //ends on first line break (the break is then whitespace will will be skipped naturally)
*pos+=1; //not yet
}
continue;
}
else if (msg[*pos+1] == '*')
{ /*C style multi-line comment*/
*pos+=2;
while (*pos+1 < max)
{
if (msg[*pos] == '*' && msg[*pos+1] == '/')
{
*pos+=2; //skip past the terminator ready for whitespace or trailing comments directly after
break;
}
*pos+=1; //not yet
}
continue;
}
}
//END NON-STANDARD
break; //not whitespace/comment/etc.
}
} }
//writes the body to a null-terminated string, handling escapes as needed.
//returns required body length (without terminator) (NOTE: return value is not escape-aware, so this is an over-estimate).
static size_t JSON_ReadBody(json_t *t, char *out, size_t outsize)
{
// size_t bodysize;
if (!t)
{
if (out)
*out = 0;
return 0;
}
if (out && outsize)
{
char *outend = out+outsize-1; //compensate for null terminator
const char *in = t->bodystart;
while (in < t->bodyend && out < outend)
{
if (*in == '\\')
{
if (++in < t->bodyend)
{
switch(*in++)
{
case '\"': *out++ = '\"'; break;
case '\\': *out++ = '\\'; break;
case '/': *out++ = '/'; break; //json is not C...
case 'b': *out++ = '\b'; break;
case 'f': *out++ = '\f'; break;
case 'n': *out++ = '\n'; break;
case 'r': *out++ = '\r'; break;
case 't': *out++ = '\t'; break;
// case 'u':
// out += utf8_encode(out, code, outend-out);
// break;
default:
//unknown escape. will warn when actually reading it.
*out++ = '\\';
if (out < outend)
*out++ = in[-1];
break;
}
}
else
*out++ = '\\'; //error...
}
else
*out++ = *in++;
}
*out = 0;
}
return t->bodyend-t->bodystart;
}
static qboolean JSON_ParseString(char const*msg, int *pos, int max, char const**start, char const** end) static qboolean JSON_ParseString(char const*msg, int *pos, int max, char const**start, char const** end)
{ {
if (*pos < max && msg[*pos] == '\"') if (*pos < max && msg[*pos] == '\"')
{ { //quoted string
//FIXME: no handling of backslash followed by one of "\/bfnrtu
*pos+=1; *pos+=1;
*start = msg+*pos; *start = msg+*pos;
while (*pos < max && msg[*pos] != '\"') while (*pos < max)
*pos+=1; {
if (msg[*pos] == '\"')
break;
if (msg[*pos] == '\\')
{ //escapes are expanded elsewhere, we're just skipping over them here.
switch(msg[*pos+1])
{
case '\"':
case '\\':
case '/':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
*pos+=2;
break;
case 'u':
*pos+=2;
//*pos+=4; //4 hex digits, not escapes so just wait till later before parsing them properly.
break;
default:
//unknown escape. will warn when actually reading it.
*pos+=1;
break;
}
}
else
*pos+=1;
}
if (*pos < max && msg[*pos] == '\"') if (*pos < max && msg[*pos] == '\"')
{ {
*end = msg+*pos; *end = msg+*pos;
@ -151,7 +274,7 @@ static qboolean JSON_ParseString(char const*msg, int *pos, int max, char const**
} }
} }
else else
{ { //name
*start = msg+*pos; *start = msg+*pos;
while (*pos < max while (*pos < max
&& msg[*pos] != ' ' && msg[*pos] != ' '
@ -258,6 +381,7 @@ static json_t *JSON_Parse(json_t *t, const char *namestart, const char *nameend,
return NULL; return NULL;
} }
//we don't understand arrays here (we just treat them as tables) so eg "foo.0.bar" to find t->foo[0]->bar
static json_t *JSON_FindChild(json_t *t, const char *child) static json_t *JSON_FindChild(json_t *t, const char *child)
{ {
if (t) if (t)
@ -384,11 +508,7 @@ static const char *JSON_GetString(json_t *t, const char *child, char *buffer, si
t = JSON_FindChild(t, child); t = JSON_FindChild(t, child);
if (t) if (t)
{ //copy it to another buffer. can probably skip that tbh. { //copy it to another buffer. can probably skip that tbh.
size_t l = t->bodyend-t->bodystart; JSON_ReadBody(t, buffer, buffersize);
if (l > buffersize-1)
l = buffersize-1;
memcpy(buffer, t->bodystart, l);
buffer[l] = 0;
return buffer; return buffer;
} }
return fallback; return fallback;
@ -461,7 +581,7 @@ static unsigned int FromBase64(char c)
return 63; return 63;
return 64; return 64;
} }
//fancy parsing of content //fancy parsing of content. NOTE: doesn't bother to handle escape codes, which shouldn't be present (\u for ascii chars is horribly wasteful).
static void *JSON_MallocDataURI(json_t *t, size_t *outlen) static void *JSON_MallocDataURI(json_t *t, size_t *outlen)
{ {
size_t bl = t->bodyend-t->bodystart; size_t bl = t->bodyend-t->bodystart;
@ -528,27 +648,6 @@ static void *JSON_MallocDataURI(json_t *t, size_t *outlen)
return NULL; return NULL;
} }
static size_t JSON_ReadBody(json_t *t, char *out, size_t outsize)
{
size_t bodysize;
if (!t)
{
if (out)
*out = 0;
return 0;
}
if (out)
{
bodysize = t->bodyend-t->bodystart;
if (bodysize > outsize-1)
bodysize = outsize-1;
memcpy(out, t->bodystart, bodysize);
out[bodysize] = 0;
}
return t->bodyend-t->bodystart;
}
@ -1217,6 +1316,7 @@ void TransformArrayA(vec3_t *data, size_t vcount, double matrix[])
data++; data++;
} }
} }
#ifndef SERVERONLY
static texid_t GLTF_LoadImage(gltf_t *gltf, int imageidx, unsigned int flags) static texid_t GLTF_LoadImage(gltf_t *gltf, int imageidx, unsigned int flags)
{ {
size_t size; size_t size;
@ -1567,6 +1667,7 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
Q_strlcpy(ret->name, ret->frame->shadername, sizeof(ret->name)); Q_strlcpy(ret->name, ret->frame->shadername, sizeof(ret->name));
return ret; return ret;
} }
#endif
static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double pmatrix[]) static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double pmatrix[])
{ {
model_t *mod = gltf->mod; model_t *mod = gltf->mod;
@ -1580,7 +1681,6 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double
for(prim = JSON_FindIndexedChild(mesh, "primitives", 0); prim; prim = prim->sibling) for(prim = JSON_FindIndexedChild(mesh, "primitives", 0); prim; prim = prim->sibling)
{ {
int mat = JSON_GetInteger(prim, "material", -1);
int mode = JSON_GetInteger(prim, "mode", 4); int mode = JSON_GetInteger(prim, "mode", 4);
json_t *attr = JSON_FindChild(prim, "attributes"); json_t *attr = JSON_FindChild(prim, "attributes");
struct gltf_accessor tc_0, tc_1, norm, tang, vpos, col0, idx, sidx, swgt; struct gltf_accessor tc_0, tc_1, norm, tang, vpos, col0, idx, sidx, swgt;
@ -1721,8 +1821,10 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double
} }
} }
#ifndef SERVERONLY
surf->numskins = 1; surf->numskins = 1;
surf->ofsskins = GLTF_LoadMaterial(gltf, mat, surf->ofs_rgbaub||surf->ofs_rgbaf); surf->ofsskins = GLTF_LoadMaterial(gltf, JSON_GetInteger(prim, "material", -1), surf->ofs_rgbaub||surf->ofs_rgbaf);
#endif
if (!tang.data) if (!tang.data)
{ {
@ -2661,5 +2763,21 @@ qboolean QDECL Mod_LoadGLBModel (struct model_s *mod, void *buffer, size_t fsize
return GLTF_LoadModel(mod, json, jsonlen, bin, binlen); return GLTF_LoadModel(mod, json, jsonlen, bin, binlen);
} }
qboolean Plug_GLTF_Init(void)
{
filefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));
modfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));
if (modfuncs && modfuncs->version < MODPLUGFUNCS_VERSION)
modfuncs = NULL;
if (modfuncs && filefuncs)
{
modfuncs->RegisterModelFormatText("glTF2 models (glTF)", ".gltf", Mod_LoadGLTFModel);
modfuncs->RegisterModelFormatMagic("glTF2 models (glb)", (('F'<<24)+('T'<<16)+('l'<<8)+'g'), Mod_LoadGLBModel);
return true;
}
return false;
}
#endif #endif

View file

@ -11,6 +11,7 @@ plugfsfuncs_t *filefuncs;
//#define LWOMODELS //not working //#define LWOMODELS //not working
#ifdef SKELETALMODELS #ifdef SKELETALMODELS
#define GLTFMODELS //FIXME: not yet working properly. #define GLTFMODELS //FIXME: not yet working properly.
extern qboolean Plug_GLTF_Init(void);
#endif #endif
#ifdef ASEMODELS #ifdef ASEMODELS
@ -821,6 +822,7 @@ qboolean Mod_ExecuteCommand(qboolean isinsecure)
{ {
char tok[128]; char tok[128];
cmdfuncs->Argv(0, tok, sizeof(tok)); cmdfuncs->Argv(0, tok, sizeof(tok));
#ifndef SERVERONLY
if (!strcmp(tok, "exportiqm")) if (!strcmp(tok, "exportiqm"))
{ {
model_t *mod; model_t *mod;
@ -835,6 +837,7 @@ qboolean Mod_ExecuteCommand(qboolean isinsecure)
} }
return true; return true;
} }
#endif
return false; return false;
} }
@ -854,8 +857,7 @@ qboolean Plug_Init(void)
modfuncs->RegisterModelFormatMagic("LWO models (lwo)", (('M'<<24)+('R'<<16)+('O'<<8)+'F'), Mod_LoadLWOModel); modfuncs->RegisterModelFormatMagic("LWO models (lwo)", (('M'<<24)+('R'<<16)+('O'<<8)+'F'), Mod_LoadLWOModel);
#endif #endif
#ifdef GLTFMODELS #ifdef GLTFMODELS
modfuncs->RegisterModelFormatText("glTF2 models (glTF)", ".gltf", Mod_LoadGLTFModel); Plug_GLTF_Init();
modfuncs->RegisterModelFormatMagic("glTF2 models (glb)", (('F'<<24)+('T'<<16)+('l'<<8)+'g'), Mod_LoadGLBModel);
#endif #endif
plugfuncs->ExportFunction("ExecuteCommand", Mod_ExecuteCommand); plugfuncs->ExportFunction("ExecuteCommand", Mod_ExecuteCommand);