mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-10 14:42:13 +00:00
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:
parent
8e656b4af8
commit
ffda35fae4
9 changed files with 211 additions and 60 deletions
|
@ -1,3 +1,6 @@
|
|||
#ifndef COM_MESH_H
|
||||
#define COM_MESH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -91,7 +94,6 @@ typedef struct
|
|||
//we can't be bothered with animating skins.
|
||||
//We'll load up to four of them but after that you're on your own
|
||||
#ifndef SERVERONLY
|
||||
|
||||
typedef struct
|
||||
{
|
||||
shader_t *shader;
|
||||
|
@ -100,7 +102,7 @@ typedef struct
|
|||
char shadername[MAX_QPATH];
|
||||
texnums_t texnums;
|
||||
} skinframe_t;
|
||||
typedef struct
|
||||
struct galiasskin_s
|
||||
{
|
||||
int skinwidth;
|
||||
int skinheight;
|
||||
|
@ -108,7 +110,7 @@ typedef struct
|
|||
int numframes;
|
||||
skinframe_t *frame;
|
||||
char name[MAX_QPATH];
|
||||
} galiasskin_t;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -121,9 +123,8 @@ typedef struct
|
|||
unsigned int subframe;
|
||||
bucket_t bucket;
|
||||
} galiascolourmapped_t;
|
||||
#else
|
||||
typedef void galiasskin_t;
|
||||
#endif
|
||||
typedef struct galiasskin_s galiasskin_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -276,4 +277,5 @@ void R_Generate_Mesh_ST_Vectors(mesh_t *mesh);
|
|||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
#endif //COM_MESH_H
|
||||
|
|
|
@ -186,6 +186,7 @@
|
|||
//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data.
|
||||
//#define MODELFMT_MDX
|
||||
//#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.
|
||||
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
|
||||
#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_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
|
||||
#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
|
||||
|
||||
//Image formats
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
//#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
|
||||
//#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_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
|
||||
//#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
|
||||
|
||||
//Image formats
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
|
||||
//#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_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
|
||||
#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
|
||||
|
||||
//Image formats
|
||||
|
|
|
@ -194,6 +194,7 @@
|
|||
//#define HAVE_HTTPSV
|
||||
//#define MODELFMT_MDX
|
||||
//#define MODELFMT_OBJ
|
||||
//#define MODELFMT_GLTF //khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.
|
||||
|
||||
#ifdef COMPILE_OPTS
|
||||
//things to configure qclib, which annoyingly doesn't include this file itself
|
||||
|
|
|
@ -9,6 +9,13 @@
|
|||
|
||||
#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_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
|
||||
#if defined(USE_INTERNAL_ODE)
|
||||
{"ODE_internal", Plug_ODE_Init},
|
||||
#endif
|
||||
#if defined(MODELFMT_GLTF)
|
||||
{"GLTF", Plug_GLTF_Init},
|
||||
#endif
|
||||
{NULL}
|
||||
};
|
||||
|
@ -236,6 +246,7 @@ static plugin_t *Plug_Load(const char *file)
|
|||
Con_DPrintf("Activated module %s\n", file);
|
||||
newplug->lib = NULL;
|
||||
|
||||
Q_strncpyz(newplug->filename, staticplugins[u].name, sizeof(newplug->filename));
|
||||
currentplug = newplug;
|
||||
success = staticplugins[u].initfunction();
|
||||
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.
|
||||
size_t nl = strlen(fname);
|
||||
size_t u;
|
||||
char *arch_ext = ARCH_DL_POSTFIX;
|
||||
static const char *knownarch[] =
|
||||
{
|
||||
"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, '\\')))
|
||||
*mssuck = '/';
|
||||
#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++)
|
||||
{
|
||||
size_t al = strlen(knownarch[u]);
|
||||
|
@ -1664,10 +1681,18 @@ void Plug_List_f(void)
|
|||
char rootpath[MAX_OSPATH];
|
||||
unsigned int u;
|
||||
plugin_t *plug;
|
||||
|
||||
Con_Printf("Loaded plugins:\n");
|
||||
for (plug = plugs; plug; plug = plug->next)
|
||||
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)))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -1675,7 +1700,7 @@ void Plug_List_f(void)
|
|||
while ((mssuck=strchr(binarypath, '\\')))
|
||||
*mssuck = '/';
|
||||
#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);
|
||||
}
|
||||
if (FS_NativePath("", FS_ROOT, rootpath, sizeof(rootpath)))
|
||||
|
@ -1687,13 +1712,12 @@ void Plug_List_f(void)
|
|||
#endif
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
for (u = 0; staticplugins[u].name; u++)
|
||||
Plug_List_Print(staticplugins[u].name, 0, 0, "", NULL);
|
||||
//should probably check downloadables too.
|
||||
}
|
||||
|
||||
void Plug_Shutdown(qboolean preliminary)
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "quakedef.h"
|
||||
#include "../plugin.h"
|
||||
#include "com_mesh.h"
|
||||
extern plugmodfuncs_t *modfuncs;
|
||||
extern plugfsfuncs_t *filefuncs;
|
||||
static plugmodfuncs_t *modfuncs;
|
||||
static plugfsfuncs_t *filefuncs;
|
||||
|
||||
#ifdef SKELETALMODELS
|
||||
#define GLTFMODELS
|
||||
|
@ -76,10 +76,13 @@ static void JSON_Orphan(json_t *t)
|
|||
}
|
||||
static void JSON_Destroy(json_t *t)
|
||||
{
|
||||
while(t->child)
|
||||
JSON_Destroy(t->child);
|
||||
JSON_Orphan(t);
|
||||
free(t);
|
||||
if (t)
|
||||
{
|
||||
while(t->child)
|
||||
JSON_Destroy(t->child);
|
||||
JSON_Orphan(t);
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
//node creation
|
||||
|
@ -127,22 +130,142 @@ static json_t *JSON_CreateNode(json_t *parent, const char *namestart, const char
|
|||
//node parsing
|
||||
static void JSON_SkipWhite(const char *msg, int *pos, int max)
|
||||
{
|
||||
while (*pos < max && (
|
||||
msg[*pos] == ' ' ||
|
||||
msg[*pos] == '\t' ||
|
||||
msg[*pos] == '\r' ||
|
||||
msg[*pos] == '\n'
|
||||
))
|
||||
*pos+=1;
|
||||
while (*pos < max)
|
||||
{
|
||||
//if its simple whitespace then keep skipping over it
|
||||
if (msg[*pos] == ' ' ||
|
||||
msg[*pos] == '\t' ||
|
||||
msg[*pos] == '\r' ||
|
||||
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)
|
||||
{
|
||||
if (*pos < max && msg[*pos] == '\"')
|
||||
{
|
||||
{ //quoted string
|
||||
//FIXME: no handling of backslash followed by one of "\/bfnrtu
|
||||
*pos+=1;
|
||||
*start = msg+*pos;
|
||||
while (*pos < max && msg[*pos] != '\"')
|
||||
*pos+=1;
|
||||
while (*pos < max)
|
||||
{
|
||||
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] == '\"')
|
||||
{
|
||||
*end = msg+*pos;
|
||||
|
@ -151,7 +274,7 @@ static qboolean JSON_ParseString(char const*msg, int *pos, int max, char const**
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{ //name
|
||||
*start = msg+*pos;
|
||||
while (*pos < max
|
||||
&& msg[*pos] != ' '
|
||||
|
@ -258,6 +381,7 @@ static json_t *JSON_Parse(json_t *t, const char *namestart, const char *nameend,
|
|||
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)
|
||||
{
|
||||
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);
|
||||
if (t)
|
||||
{ //copy it to another buffer. can probably skip that tbh.
|
||||
size_t l = t->bodyend-t->bodystart;
|
||||
if (l > buffersize-1)
|
||||
l = buffersize-1;
|
||||
memcpy(buffer, t->bodystart, l);
|
||||
buffer[l] = 0;
|
||||
JSON_ReadBody(t, buffer, buffersize);
|
||||
return buffer;
|
||||
}
|
||||
return fallback;
|
||||
|
@ -461,7 +581,7 @@ static unsigned int FromBase64(char c)
|
|||
return 63;
|
||||
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)
|
||||
{
|
||||
size_t bl = t->bodyend-t->bodystart;
|
||||
|
@ -528,27 +648,6 @@ static void *JSON_MallocDataURI(json_t *t, size_t *outlen)
|
|||
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++;
|
||||
}
|
||||
}
|
||||
#ifndef SERVERONLY
|
||||
static texid_t GLTF_LoadImage(gltf_t *gltf, int imageidx, unsigned int flags)
|
||||
{
|
||||
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));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double pmatrix[])
|
||||
{
|
||||
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)
|
||||
{
|
||||
int mat = JSON_GetInteger(prim, "material", -1);
|
||||
int mode = JSON_GetInteger(prim, "mode", 4);
|
||||
json_t *attr = JSON_FindChild(prim, "attributes");
|
||||
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->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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ plugfsfuncs_t *filefuncs;
|
|||
//#define LWOMODELS //not working
|
||||
#ifdef SKELETALMODELS
|
||||
#define GLTFMODELS //FIXME: not yet working properly.
|
||||
extern qboolean Plug_GLTF_Init(void);
|
||||
#endif
|
||||
|
||||
#ifdef ASEMODELS
|
||||
|
@ -821,6 +822,7 @@ qboolean Mod_ExecuteCommand(qboolean isinsecure)
|
|||
{
|
||||
char tok[128];
|
||||
cmdfuncs->Argv(0, tok, sizeof(tok));
|
||||
#ifndef SERVERONLY
|
||||
if (!strcmp(tok, "exportiqm"))
|
||||
{
|
||||
model_t *mod;
|
||||
|
@ -835,6 +837,7 @@ qboolean Mod_ExecuteCommand(qboolean isinsecure)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -854,8 +857,7 @@ qboolean Plug_Init(void)
|
|||
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);
|
||||
Plug_GLTF_Init();
|
||||
#endif
|
||||
|
||||
plugfuncs->ExportFunction("ExecuteCommand", Mod_ExecuteCommand);
|
||||
|
|
Loading…
Reference in a new issue