1
0
Fork 0
forked from fte/fteqw

Potentially support a highly-compressed variety of gltf2 files... but needs third party library so will probably remain permanently disabled at compiletime.

This commit is contained in:
Shpoike 2023-07-02 03:58:51 +01:00
parent 8066f712e0
commit a7a620f690
3 changed files with 421 additions and 41 deletions

View file

@ -845,6 +845,27 @@ SET(FTE_Q3_FILES
plugins/quake3/q3g_public.h plugins/quake3/q3g_public.h
) )
#For annoying compressed gltf2 files.
SET(FTE_DEP_DRACO false CACHE BOOL "Link against libdraco.")
IF(FTE_DEP_DRACO)
FIND_LIBRARY(
DRACO_LIBRARY
NAMES draco
)
IF(DRACO_LIBRARY)
SET(DRACO_FILES plugins/models/draco.cpp)
SET(DRACO_CFLAGS HAVE_DRACO)
SET(FTE_COMMON_FILES ${FTE_COMMON_FILES} ${DRACO_FILES})
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};${DRACO_CFLAGS})
SET(FTE_LIBS ${FTE_LIBS} ${DRACO_LIBRARY})
SET(FTESV_LIBS ${FTESV_LIBS} ${DRACO_LIBRARY})
ELSE()
MESSAGE(WARNING "draco library not found, needed for GLTF's KHR_draco_mesh_compression to be usable.")
ENDIF()
ENDIF()
SET(FTE_PLUG_QUAKE3 true CACHE BOOL "Compile Quake3 plugin.") SET(FTE_PLUG_QUAKE3 true CACHE BOOL "Compile Quake3 plugin.")
IF(FTE_PLUG_QUAKE3) IF(FTE_PLUG_QUAKE3)
IF (0) IF (0)
@ -912,7 +933,7 @@ IF(FTE_PLUG_ODE)
#SET (FTE_COMMON_FILES ${FTE_COMMON_FILES} engine/common/com_phys_ode.c) #SET (FTE_COMMON_FILES ${FTE_COMMON_FILES} engine/common/com_phys_ode.c)
SET(FTE_LIB_DEFINES "${FTE_LIB_DEFINES};USE_INTERNAL_ODE;ODE_STATIC") SET(FTE_LIB_DEFINES "${FTE_LIB_DEFINES};USE_INTERNAL_ODE;ODE_STATIC")
SET(FTE_LIBS ${FTE_LIBS} ${LIBODE_LIBRARY}) SET(FTE_LIBS ${FTE_LIBS} ${LIBODE_LIBRARY})
SET(FTESV_LIBS ${FTE_LIBS} ${LIBODE_LIBRARY}) SET(FTESV_LIBS ${FTESV_LIBS} ${LIBODE_LIBRARY})
SET(FTE_INCLUDES ${FTE_INCLUDES} ${ODE_INCLUDE_DIRS}) SET(FTE_INCLUDES ${FTE_INCLUDES} ${ODE_INCLUDE_DIRS})
ELSE() ELSE()
ADD_LIBRARY(plug_ode MODULE ADD_LIBRARY(plug_ode MODULE
@ -1009,13 +1030,14 @@ ELSE()
ADD_EXECUTABLE(iqmtool ADD_EXECUTABLE(iqmtool
iqm/iqm.cpp iqm/iqm.cpp
plugins/models/gltf.c plugins/models/gltf.c
${DRACO_FILES}
engine/common/json.c engine/common/json.c
engine/client/image.c engine/client/image.c
imgtool.c imgtool.c
iqm/iqm.h iqm/iqm.h
) )
SET_TARGET_PROPERTIES(iqmtool PROPERTIES COMPILE_DEFINITIONS "IQMTOOL;${FTE_REVISON}") SET_TARGET_PROPERTIES(iqmtool PROPERTIES COMPILE_DEFINITIONS "IQMTOOL;${DRACO_CFLAGS};${FTE_REVISON}")
TARGET_LINK_LIBRARIES(iqmtool ${CMAKE_DL_LIBS}) TARGET_LINK_LIBRARIES(iqmtool ${CMAKE_DL_LIBS} ${DRACO_LIBRARY})
SET(INSTALLTARGS ${INSTALLTARGS} iqmtool) SET(INSTALLTARGS ${INSTALLTARGS} iqmtool)
ENDIF() ENDIF()
@ -1362,11 +1384,12 @@ IF(FTE_PLUG_MODELS)
plugins/plugin.c plugins/plugin.c
plugins/models/models.c plugins/models/models.c
plugins/models/gltf.c plugins/models/gltf.c
${DRACO_FILES}
engine/common/json.c engine/common/json.c
plugins/models/exportiqm.c plugins/models/exportiqm.c
) )
SET_TARGET_PROPERTIES(plug_models PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES}") SET_TARGET_PROPERTIES(plug_models PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${DRACO_CFLAGS};${FTE_LIB_DEFINES}")
TARGET_LINK_LIBRARIES(plug_models ${SYS_LIBS}) TARGET_LINK_LIBRARIES(plug_models ${SYS_LIBS} ${DRACO_LIBRARY})
EMBED_PLUGIN_META(models "Models Plugin" "Kinda redundant now that the engine has gltf2 loading.") EMBED_PLUGIN_META(models "Models Plugin" "Kinda redundant now that the engine has gltf2 loading.")
ENDIF() ENDIF()

137
plugins/models/draco.cpp Normal file
View file

@ -0,0 +1,137 @@
#ifdef __cplusplus
extern "C"
{
#endif
#include <stddef.h>
//I fucking hate c++
typedef struct ftedracofuncs_s
{
void (*Release)(struct ftedracofuncs_s *ctx); //frees all memory
struct ftedracoattr_s
{
void *ptr;
int isnormalised;
unsigned int type; //gl constants, because why not
unsigned int components; //1-4
unsigned int bytestride;
unsigned int usage; //typical usage type.
unsigned int uniqueid; //annoying extra lookup.
} *attrib;
unsigned int num_attribs;
//mesh data
char triangleloop;
unsigned int num_indexes;
unsigned int *ptr_indexes, num_vertexes;
} ftedracofuncs_t;
ftedracofuncs_t *Draco_Decode(void *ptr, size_t size);
#ifdef __cplusplus
};
#endif
#if !defined(DRACO_API_ONLY) && defined(HAVE_DRACO)
#include <draco/compression/decode.h>
struct dracofuncs_t : public ftedracofuncs_s
{
draco::Decoder decoder;
draco::DecoderBuffer buffer;
draco::Mesh mesh;
static void DoRelease(ftedracofuncs_s *ctx)
{
auto d = reinterpret_cast<dracofuncs_t*>(ctx);
delete[] d->ptr_indexes;
for (auto i = d->num_attribs; i --> 0; )
delete[] (char*)d->attrib[i].ptr;
delete[] d->attrib;
delete d; //do all the third-party destructor stuff too.
}
dracofuncs_t()
{
Release = DoRelease;
}
};
template <class attrtype, int gltype>
static void CopyAttribute(unsigned int numpoints, draco::PointAttribute *attr, ftedracofuncs_s::ftedracoattr_s *oattr)
{
auto nc = oattr->components;
auto *out = new attrtype[numpoints*nc];
for (size_t i = 0; i < numpoints; i++)
attr->GetMappedValue(draco::PointIndex(i), &out[i*nc]);
oattr->bytestride = sizeof(attrtype)*nc; //the output data is tightly packed.
oattr->ptr = out;
oattr->type = gltype;
};
ftedracofuncs_t *Draco_Decode(void *ptr, size_t size)
{
size_t tris;
dracofuncs_t *d = new dracofuncs_t();
d->buffer.Init((const char *)ptr, size);
if (!d->decoder.DecodeBufferToGeometry(&d->buffer, &d->mesh).ok())
{ //check for corruption...
delete d;
return NULL;
}
tris = d->mesh.num_faces();
d->num_indexes = tris*3;
d->ptr_indexes = new unsigned int[d->num_indexes];
while (tris --> 0)
{
auto &tri = d->mesh.face(draco::FaceIndex(tris));
d->ptr_indexes[tris*3+0] = tri[0].value();
d->ptr_indexes[tris*3+1] = tri[1].value();
d->ptr_indexes[tris*3+2] = tri[2].value();
}
d->num_vertexes = d->mesh.num_points();
d->num_attribs = d->mesh.num_attributes();
d->attrib = new struct ftedracofuncs_s::ftedracoattr_s[d->num_attribs];
for (unsigned int i = 0; i < d->num_attribs; i++)
{
draco::PointAttribute *attr = d->mesh.attribute(i);
auto oattr = &d->attrib[i];
oattr->isnormalised = attr->normalized();
oattr->bytestride = attr->byte_stride();
oattr->components = attr->num_components();
oattr->usage = attr->attribute_type();
oattr->uniqueid = attr->unique_id();
#define GL_BYTE 0x1400
#define GL_UNSIGNED_BYTE 0x1401
#define GL_SHORT 0x1402
#define GL_UNSIGNED_SHORT 0x1403
#define GL_INT 0x1404
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
#define GL_DOUBLE 0x140A
#define GL_INT64_ARB 0x140E
#define GL_UNSIGNED_INT64_ARB 0x140F
switch(attr->data_type())
{
default:
memset(oattr, 0, sizeof(*oattr));
break;
case draco::DataType::DT_INT8: CopyAttribute< int8_t, GL_BYTE >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_UINT8: CopyAttribute<uint8_t, GL_UNSIGNED_BYTE >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_INT16: CopyAttribute< int16_t, GL_SHORT >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_UINT16: CopyAttribute<uint16_t, GL_UNSIGNED_SHORT >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_INT32: CopyAttribute< int32_t, GL_INT >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_UINT32: CopyAttribute<uint32_t, GL_UNSIGNED_INT >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_INT64: CopyAttribute< int64_t, GL_INT64_ARB >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_UINT64: CopyAttribute<uint64_t, GL_UNSIGNED_INT64_ARB >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_FLOAT32: CopyAttribute<float, GL_FLOAT >(d->num_vertexes, attr, oattr); break;
case draco::DataType::DT_FLOAT64: CopyAttribute<double, GL_DOUBLE >(d->num_vertexes, attr, oattr); break;
}
}
return d;
}
#endif

View file

@ -518,6 +518,13 @@ static qboolean GLTF_GetAccessor(gltf_t *gltf, json_t *accessorid, struct gltf_a
offset = JSON_GetUInteger(a, "byteOffset", 0); offset = JSON_GetUInteger(a, "byteOffset", 0);
if (offset > bv.length) if (offset > bv.length)
return false; return false;
if (JSON_FindChild(a, "sparse"))
{ //0-initialised, with separate index+values tables for ones that need an actual value.
if (gltf->warnlimit --> 0)
Con_Printf(CON_WARNING"%s: sparse accessors are not supported\n", gltf->mod->name);
return false;
}
out->length = bv.length - offset; out->length = bv.length - offset;
if (gltf->ver <= 1) if (gltf->ver <= 1)
out->bytestride = JSON_GetInteger(a, "byteStride", 0); out->bytestride = JSON_GetInteger(a, "byteStride", 0);
@ -578,7 +585,6 @@ static qboolean GLTF_GetAccessor(gltf_t *gltf, json_t *accessorid, struct gltf_a
out->maxs[j] = JSON_GetIndexedFloat(maxs, j, 0); out->maxs[j] = JSON_GetIndexedFloat(maxs, j, 0);
} }
// JSON_WarnIfChild(a, "sparse");
// JSON_WarnIfChild(a, "name"); // JSON_WarnIfChild(a, "name");
GLTF_FlagExtras(a); GLTF_FlagExtras(a);
@ -1749,6 +1755,8 @@ static void GLTF_LoadMaterial(gltf_t *gltf, json_t *materialid, galiasskin_t *re
ret->frame->texnums.base = GLTF_LoadTexture(gltf, albedo, 0); ret->frame->texnums.base = GLTF_LoadTexture(gltf, albedo, 0);
if (mrt) if (mrt)
ret->frame->texnums.specular = GLTF_LoadTexture(gltf, mrt, IF_NOSRGB); ret->frame->texnums.specular = GLTF_LoadTexture(gltf, mrt, IF_NOSRGB);
else //else depend upon specularfactor
ret->frame->texnums.specular = modfuncs->GetTexture("$whiteimage", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);
Q_snprintf(shader, sizeof(shader), Q_snprintf(shader, sizeof(shader),
"{\n" "{\n"
@ -1795,13 +1803,164 @@ static void GLTF_LoadMaterial(gltf_t *gltf, json_t *materialid, galiasskin_t *re
Q_strlcpy(ret->name, ret->frame->shadername, sizeof(ret->name)); Q_strlcpy(ret->name, ret->frame->shadername, sizeof(ret->name));
} }
#endif #endif
#ifdef HAVE_DRACO
#define DRACO_API_ONLY
#include "draco.cpp"
#endif
typedef struct {
gltf_t *gltf;
json_t *prim;
json_t *primattrs;
#ifdef HAVE_DRACO
json_t *dracoattrs;
struct ftedracofuncs_s *draco;
#endif
} gltf_prim_t;
static qboolean GLTF_GetAttributeAccessor(gltf_prim_t *state, char *attributename, struct gltf_accessor *out)
{
json_t *primaccessor = JSON_FindChild(state->primattrs, attributename);
#ifdef HAVE_DRACO
if (state->draco && primaccessor)
{
json_t *da = JSON_FindChild(state->dracoattrs, attributename);
if (da)
{ //comes from compressed data instead.
//we're still meant to require some attributes match the original accessors.
struct ftedracoattr_s *dattr;
json_t *a, *mins, *maxs;
unsigned int j, attridx;
memset(out, 0, sizeof(*out));
if (!strcmp(attributename, "NORMAL") || !strcmp(attributename, "TANGENT"))
return false; //these come out shite. don't use.
a = GLTF_FindJSONID(state->gltf, "accessors", primaccessor, NULL);
if (!a)
return false;
j = JSON_GetInteger(da, NULL, -1);
for (attridx = 0; attridx < state->draco->num_attribs; attridx++)
if (state->draco->attrib[attridx].uniqueid == j)
break;
if (attridx >= state->draco->num_attribs)
{
if (state->gltf->warnlimit --> 0)
Con_Printf(CON_WARNING"%s: draco lacks specified uniqueid %i\n", state->gltf->mod->name, j);
return false;
}
JSON_FlagAsUsed(a, "name");
out->bytestride = 0; //
out->componentType = JSON_GetInteger(a, "componentType", 0);
out->normalized = JSON_GetInteger(a, "normalized", false);
out->count = JSON_GetInteger(a, "count", 0);
if (JSON_Equals(a, "type", "SCALAR"))
out->type = (1<<8) | 1;
else if (JSON_Equals(a, "type", "VEC2"))
out->type = (1<<8) | 2;
else if (JSON_Equals(a, "type", "VEC3"))
out->type = (1<<8) | 3;
else if (JSON_Equals(a, "type", "VEC4"))
out->type = (1<<8) | 4;
else if (JSON_Equals(a, "type", "MAT2"))
out->type = (2<<8) | 2;
else if (JSON_Equals(a, "type", "MAT3"))
out->type = (3<<8) | 3;
else if (JSON_Equals(a, "type", "MAT4"))
out->type = (4<<8) | 4;
else
{
if (state->gltf->warnlimit --> 0)
Con_Printf(CON_WARNING"%s: glTF2 unsupported type\n", state->gltf->mod->name);
out->type = 1;
}
if (!out->bytestride)
{
out->bytestride = (out->type & 0xff) * (out->type>>8);
switch(out->componentType)
{
default:
if (state->gltf->warnlimit --> 0)
Con_Printf(CON_WARNING"GLTF_GetAccessor: %s: glTF2 unsupported componentType (%i)\n", state->gltf->mod->name, out->componentType);
case 5120: //BYTE
case 5121: //UNSIGNED_BYTE
break;
case 5122: //SHORT
case 5123: //UNSIGNED_SHORT
out->bytestride *= 2;
break;
case 5125: //UNSIGNED_INT
case 5126: //FLOAT
out->bytestride *= 4;
break;
}
}
mins = JSON_FindChild(a, "min");
maxs = JSON_FindChild(a, "max");
for (j = 0; j < (out->type>>8)*(out->type&0xff); j++)
{ //'must' be set in various situations.
out->mins[j] = JSON_GetIndexedFloat(mins, j, 0);
out->maxs[j] = JSON_GetIndexedFloat(maxs, j, 0);
}
GLTF_FlagExtras(a);
dattr = &state->draco->attrib[attridx];
if (out->count < state->draco->num_vertexes)
out->count = state->draco->num_vertexes; //hack. seems to be needed for one of our test models.
if (out->count != state->draco->num_vertexes ||
!out->normalized != !dattr->isnormalised ||
out->componentType != dattr->type ||
out->type != ((1<<8)|dattr->components) ||
out->bytestride != dattr->bytestride)
{
if (state->gltf->warnlimit --> 0)
Con_Printf(CON_WARNING"%s: %s (%i) draco/accessor mismatch\n", state->gltf->mod->name, attributename, dattr->usage);
memset(out, 0, sizeof(*out)); //abort! abort!
return false;
}
out->data = dattr->ptr;
out->length = out->bytestride*out->count;
return true;
}
}
#endif
return GLTF_GetAccessor(state->gltf, primaccessor, out);
}
static void GLTF_GetIndiciesAccessor(gltf_prim_t *state, struct gltf_accessor *out)
{
#ifdef HAVE_DRACO
if (state->draco)
{
memset(out->mins, 0, sizeof(out->mins));
memset(out->maxs, 0, sizeof(out->maxs));
out->componentType = 5125; //unsigned int
out->normalized = false;
out->type = (1<<8) | 1; //'scaler'
out->bytestride = sizeof(*state->draco->ptr_indexes);
out->count = state->draco->num_indexes;
out->data = state->draco->ptr_indexes;
out->length = out->bytestride*out->count;
return;
}
#endif
GLTF_GetAccessor(state->gltf, JSON_FindChild(state->prim, "indices"), out);
}
static const float *QDECL GLTF_AnimateMorphs(const galiasinfo_t *surf, const framestate_t *framestate); static const float *QDECL GLTF_AnimateMorphs(const galiasinfo_t *surf, const framestate_t *framestate);
static qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, double skinmatrix[]) static qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, double skinmatrix[])
{ {
model_t *mod = gltf->mod; model_t *mod = gltf->mod;
quintptr_t meshidx; quintptr_t meshidx;
json_t *mesh = GLTF_FindJSONID(gltf, "meshes", meshid, &meshidx); json_t *mesh = GLTF_FindJSONID(gltf, "meshes", meshid, &meshidx);
json_t *prim; json_t *primnode;
json_t *meshname = JSON_FindChild(mesh, "name"); json_t *meshname = JSON_FindChild(mesh, "name");
json_t *target = NULL; json_t *target = NULL;
float morphweights[MAX_MORPHWEIGHTS]; float morphweights[MAX_MORPHWEIGHTS];
@ -1813,26 +1972,66 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, dou
morphtargets = ~0; morphtargets = ~0;
GLTF_FlagExtras(mesh); GLTF_FlagExtras(mesh);
for(prim = JSON_FindIndexedChild(mesh, "primitives", 0); prim; prim = prim->sibling) for(primnode = JSON_FindIndexedChild(mesh, "primitives", 0); primnode; primnode = primnode->sibling)
{ {
int mode = JSON_GetInteger(prim, "mode", 4); int mode = JSON_GetInteger(primnode, "mode", 4);
json_t *attr = JSON_FindChild(prim, "attributes"); json_t *attr = JSON_FindChild(primnode, "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;
struct gltf_accessor morph_vpos[MAX_MORPHWEIGHTS], morph_norm[MAX_MORPHWEIGHTS], morph_tang[MAX_MORPHWEIGHTS]; struct gltf_accessor morph_vpos[MAX_MORPHWEIGHTS], morph_norm[MAX_MORPHWEIGHTS], morph_tang[MAX_MORPHWEIGHTS];
galiasinfo_t *surf; galiasinfo_t *surf;
size_t i, j; size_t i, j;
index_t maxvert;
prim->used = true; gltf_prim_t prim = {gltf, primnode, attr};
if (mode != 4) #ifdef HAVE_DRACO
#define PRIMCLEANUP() do{if (prim.draco) prim.draco->Release(prim.draco);}while(0) //frees memory allocations from inside this loop
json_t *draconode = JSON_FindChild(primnode, "extensions.KHR_draco_mesh_compression");
if (draconode)
{ //decompress the ext.bufferview and replace matching primative.attributes[n] with any listed ext.attributes[n] entries, and the indicies
//accessor's componentType, type, count should match that from draco.
struct gltf_bufferview bv;
if (!GLTF_GetBufferViewData(gltf, JSON_FindChild(draconode, "bufferView"), &bv))
{
if (gltf->warnlimit --> 0)
Con_Printf(CON_WARNING "%s: KHR_draco_mesh_compression without bufferview\n", gltf->mod->name);
continue;
}
prim.draco = Draco_Decode(bv.data, bv.length);
if (!prim.draco)
{
if (gltf->warnlimit --> 0)
Con_Printf(CON_WARNING "%s: KHR_draco_mesh_compression decompression failure\n", gltf->mod->name); //in case a model tries supplying more. we ought to renormalise the weights in this case.
continue;
}
prim.dracoattrs = JSON_FindChild(draconode, "attributes");
}
#else
#define PRIMCLEANUP() do{}while(0)
#endif
primnode->used = true;
switch(mode)
{ {
Con_Printf("Primitive mode %i not supported\n", mode); case 4:
break;
case 0: //points
case 1: //lines
case 2: //line loop
case 3: //line strip
case 5: //triangle strip -- FIXME: probably relevant (with degenerates)
case 6: //triangle fan
default:
if (gltf->warnlimit --> 0)
Con_Printf("Primitive mode %i not supported\n", mode);
PRIMCLEANUP();
continue; continue;
} }
for (i = 0; ; i++) for (i = 0; ; i++)
{ {
target = JSON_FindIndexedChild(prim, "targets", i); target = JSON_FindIndexedChild(primnode, "targets", i);
if (!target) if (!target)
break; break;
GLTF_GetAccessor(gltf, JSON_FindChild(target, "POSITION"), &morph_vpos[i]); GLTF_GetAccessor(gltf, JSON_FindChild(target, "POSITION"), &morph_vpos[i]);
@ -1853,42 +2052,44 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, dou
} }
} }
GLTF_FlagExtras(prim); GLTF_FlagExtras(primnode);
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "TEXCOORD_0"), &tc_0); //float, ubyte, ushort GLTF_GetAttributeAccessor(&prim, "POSITION", &vpos); //float
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "TEXCOORD_1"), &tc_1); //float, ubyte, ushort if (!vpos.count)
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "NORMAL"), &norm); //float {
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "TANGENT"), &tang); //float PRIMCLEANUP();
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "POSITION"), &vpos); //float continue;
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "COLOR_0"), &col0); //float, ubyte, ushort }
GLTF_GetAccessor(gltf, JSON_FindChild(prim, "indices"), &idx); GLTF_GetAttributeAccessor(&prim, "TEXCOORD_0", &tc_0); //float, ubyte, ushort
GLTF_GetAttributeAccessor(&prim, "TEXCOORD_1", &tc_1); //float, ubyte, ushort
GLTF_GetAttributeAccessor(&prim, "NORMAL", &norm); //float
GLTF_GetAttributeAccessor(&prim, "TANGENT", &tang); //float
GLTF_GetAttributeAccessor(&prim, "COLOR_0", &col0); //float, ubyte, ushort
GLTF_GetIndiciesAccessor(&prim, &idx);
if (gltf->ver <= 1) if (gltf->ver <= 1)
{ {
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "JOINT"), &sidx); //ubyte, ushort GLTF_GetAttributeAccessor(&prim, "JOINT", &sidx); //ubyte, ushort
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "WEIGHT"), &swgt); //float, ubyte, ushort GLTF_GetAttributeAccessor(&prim, "WEIGHT", &swgt); //float, ubyte, ushort
} }
else else
{ //potentially multiple, each a vec4. { //potentially multiple, each a vec4.
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "JOINTS_0"), &sidx); //ubyte, ushort GLTF_GetAttributeAccessor(&prim, "JOINTS_0", &sidx); //ubyte, ushort
GLTF_GetAccessor(gltf, JSON_FindChild(attr, "WEIGHTS_0"), &swgt); //float, ubyte, ushort GLTF_GetAttributeAccessor(&prim, "WEIGHTS_0", &swgt); //float, ubyte, ushort
} }
if (JSON_GetInteger(attr, "JOINTS_1", -1) != -1 || JSON_GetInteger(attr, "WEIGHTS_1", -1) != -1) if (JSON_GetInteger(attr, "JOINTS_1", -1) != -1 || JSON_GetInteger(attr, "WEIGHTS_1", -1) != -1)
if (gltf->warnlimit --> 0) if (gltf->warnlimit --> 0)
Con_Printf(CON_WARNING "%s: only 4 bones supported per vert\n", gltf->mod->name); //in case a model tries supplying more. we ought to renormalise the weights in this case. Con_Printf(CON_WARNING "%s: only 4 bones supported per vert\n", gltf->mod->name); //in case a model tries supplying more. we ought to renormalise the weights in this case.
if (!vpos.count)
continue;
surf = plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf) + (morphtargets*sizeof(float))); surf = plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf) + (morphtargets*sizeof(float)));
surf->surfaceid = JSON_GetInteger(prim, "extras.fte.surfaceid", meshidx); surf->surfaceid = JSON_GetInteger(primnode, "extras.fte.surfaceid", meshidx);
surf->contents = JSON_GetInteger(prim, "extras.fte.contents", FTECONTENTS_BODY); surf->contents = JSON_GetInteger(primnode, "extras.fte.contents", FTECONTENTS_BODY);
surf->csurface.flags = JSON_GetInteger(prim, "extras.fte.surfaceflags", 0); surf->csurface.flags = JSON_GetInteger(primnode, "extras.fte.surfaceflags", 0);
surf->geomset = JSON_GetInteger(prim, "extras.fte.geomset", ~0u); surf->geomset = JSON_GetInteger(primnode, "extras.fte.geomset", ~0u);
surf->geomid = JSON_GetInteger(prim, "extras.fte.geomid", 0); surf->geomid = JSON_GetInteger(primnode, "extras.fte.geomid", 0);
surf->mindist = JSON_GetInteger(prim, "extras.fte.mindist", 0); surf->mindist = JSON_GetInteger(primnode, "extras.fte.mindist", 0);
surf->maxdist = JSON_GetInteger(prim, "extras.fte.maxdist", 0); surf->maxdist = JSON_GetInteger(primnode, "extras.fte.maxdist", 0);
surf->shares_bones = gltf->numsurfaces; surf->shares_bones = gltf->numsurfaces;
surf->shares_verts = gltf->numsurfaces; surf->shares_verts = gltf->numsurfaces;
@ -1910,12 +2111,15 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, dou
surf->ofs_indexes[i] = *(unsigned char *)((char*)idx.data + i*idx.bytestride); surf->ofs_indexes[i] = *(unsigned char *)((char*)idx.data + i*idx.bytestride);
} }
else if (idx.componentType == 5125) else if (idx.componentType == 5125)
{ //unsigned ints { //unsigned ints. -- FIXME: catch overflows.
for (i = 0; i < idx.count; i++) for (i = 0; i < idx.count; i++)
surf->ofs_indexes[i] = *(unsigned int *)((char*)idx.data + i*idx.bytestride); //FIXME: bounds check. surf->ofs_indexes[i] = *(unsigned int *)((char*)idx.data + i*idx.bytestride); //FIXME: bounds check.
} }
else else
{
PRIMCLEANUP();
continue; continue;
}
} }
else else
{ {
@ -1933,6 +2137,16 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, dou
surf->ofs_indexes[i+2] = t; surf->ofs_indexes[i+2] = t;
} }
for (maxvert = 0, i = 0; i < idx.count; i++)
if (maxvert < surf->ofs_indexes[i])
maxvert = surf->ofs_indexes[i];
if (maxvert >= surf->numverts)
{
Con_Printf(CON_WARNING "%s: %s Index list exceeds vertex count range\n", gltf->mod->name, surf->surfacename); //in case a model tries supplying more. we ought to renormalise the weights in this case.
PRIMCLEANUP();
continue;
}
surf->AnimateMorphs = GLTF_AnimateMorphs; surf->AnimateMorphs = GLTF_AnimateMorphs;
memcpy((float*)(surf+1), morphweights, sizeof(float)*morphtargets); memcpy((float*)(surf+1), morphweights, sizeof(float)*morphtargets);
surf->nummorphs = morphtargets; surf->nummorphs = morphtargets;
@ -2020,11 +2234,11 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, dou
json_t *mapping, *var; json_t *mapping, *var;
surf->numskins = 1+gltf->variations; surf->numskins = 1+gltf->variations;
surf->ofsskins = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(*surf->ofsskins)*surf->numskins); surf->ofsskins = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(*surf->ofsskins)*surf->numskins);
GLTF_LoadMaterial(gltf, JSON_FindChild(prim, "material"), surf->ofsskins, surf->ofs_rgbaub||surf->ofs_rgbaf); GLTF_LoadMaterial(gltf, JSON_FindChild(primnode, "material"), surf->ofsskins, surf->ofs_rgbaub||surf->ofs_rgbaf);
for (i = 0; i < gltf->variations; i++) for (i = 0; i < gltf->variations; i++)
surf->ofsskins[1+i] = surf->ofsskins[0]; //unspecified matches defaults... surf->ofsskins[1+i] = surf->ofsskins[0]; //unspecified matches defaults...
for (mapping=JSON_FindIndexedChild(prim, "extensions.KHR_materials_variants.mappings", 0); mapping; mapping = mapping->sibling) for (mapping=JSON_FindIndexedChild(primnode, "extensions.KHR_materials_variants.mappings", 0); mapping; mapping = mapping->sibling)
{ {
i = 0; i = 0;
for(var = JSON_FindIndexedChild(mapping, "variants", 0); var; var = var->sibling) for(var = JSON_FindIndexedChild(mapping, "variants", 0); var; var = var->sibling)
@ -2051,6 +2265,8 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, dou
gltf->numsurfaces++; gltf->numsurfaces++;
surf->nextsurf = mod->meshinfo; surf->nextsurf = mod->meshinfo;
mod->meshinfo = surf; mod->meshinfo = surf;
PRIMCLEANUP();
} }
return true; return true;
} }
@ -2741,13 +2957,17 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
{"KHR_materials_pbrSpecularGlossiness", true, false}, {"KHR_materials_pbrSpecularGlossiness", true, false},
// {"KHR_materials_cmnBlinnPhong", true, true}, // {"KHR_materials_cmnBlinnPhong", true, true},
{"KHR_materials_unlit", true, false}, {"KHR_materials_unlit", true, false},
{"KHR_mesh_quantization", true, true}, {"KHR_mesh_quantization", true, false},
{"MSFT_texture_dds", true, false}, {"MSFT_texture_dds", true, false},
{"MSFT_packing_occlusionRoughnessMetallic", true, false}, {"MSFT_packing_occlusionRoughnessMetallic", true, false},
{"KHR_materials_variants", true, false}, {"KHR_materials_variants", true, false},
{"KHR_materials_ior", true, false}, {"KHR_materials_ior", true, false},
{"KHR_draco_mesh_compression", false, true}, //probably fatal #ifdef HAVE_DRACO
{"KHR_draco_mesh_compression", true, false}, //probably fatal
#else
{"KHR_draco_mesh_compression", false, false}, //probably fatal
#endif
{"KHR_texture_transform", false, false}, //requires glsl tweaks, per texmap. can't use tcmod if its only on the bumpmap etc. {"KHR_texture_transform", false, false}, //requires glsl tweaks, per texmap. can't use tcmod if its only on the bumpmap etc.
{"KHR_materials_sheen", false, false}, //requires glsl tweaks, extra brdf layer in the middle for velvet. {"KHR_materials_sheen", false, false}, //requires glsl tweaks, extra brdf layer in the middle for velvet.
{"KHR_materials_clearcoat", false, false}, //requires glsl tweaks, extra brdf layer over the top for varnish etc. {"KHR_materials_clearcoat", false, false}, //requires glsl tweaks, extra brdf layer over the top for varnish etc.