1
0
Fork 0
forked from fte/fteqw

Fix problems with last commit.

Fix rbsp not responding to lightstyle0 consistently.
Fix q2 not responding to any lightstyles.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5599 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-01-10 12:23:25 +00:00
parent bfea433dff
commit 73f92fb799
16 changed files with 621 additions and 175 deletions

View file

@ -37,7 +37,7 @@ IF (NOT "${FTE_REVISON}" STREQUAL "")
OUTPUT_VARIABLE FTE_DATE OUTPUT_VARIABLE FTE_DATE
) )
SET(FTE_REVISON SVNREVISION="${FTE_REVISON}" SVNDATE="${FTE_DATE}") SET(FTE_REVISON SVNREVISION=${FTE_REVISON} SVNDATE="${FTE_DATE}")
ENDIF() ENDIF()
ENDIF() ENDIF()
@ -91,7 +91,7 @@ IF(BZIP2_FOUND)
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};AVAIL_BZLIB;BZLIB_STATIC) SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};AVAIL_BZLIB;BZLIB_STATIC)
SET(FTE_LIBS ${FTE_LIBS} bz2) SET(FTE_LIBS ${FTE_LIBS} bz2)
SET(FTESV_LIBS ${FTESV_LIBS} bz2) SET(FTESV_LIBS ${FTESV_LIBS} bz2)
MESSAGE(STATUS "bzip2 library found. bz2-compressed pk3s will work for the price of extra bloat! yay!") # MESSAGE(STATUS "bzip2 library found. bz2-compressed pk3s will work for the price of extra bloat! yay!")
ELSE() ELSE()
MESSAGE(WARNING "bzip2 library NOT available. bz2-compressed pk3s will not be available, as if anyone cares.") MESSAGE(WARNING "bzip2 library NOT available. bz2-compressed pk3s will not be available, as if anyone cares.")
ENDIF() ENDIF()
@ -262,6 +262,10 @@ ELSEIF(${UNIX}) #linux(ish)
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_CURSOR) SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_CURSOR)
MESSAGE(WARNING "Xcursor library NOT available.") MESSAGE(WARNING "Xcursor library NOT available.")
ENDIF() ENDIF()
IF (NOT X11_Xrandr_FOUND)
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_RANDR)
MESSAGE(WARNING "Xrandr library NOT available.")
ENDIF()
ELSE() ELSE()
MESSAGE(WARNING "x11 library NOT available.") MESSAGE(WARNING "x11 library NOT available.")
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11) SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11)

View file

@ -4461,7 +4461,7 @@ static void CLQ2_ParseConfigString (void)
} }
else if (i >= Q2CS_LIGHTS && i < Q2CS_LIGHTS+Q2MAX_LIGHTSTYLES) else if (i >= Q2CS_LIGHTS && i < Q2CS_LIGHTS+Q2MAX_LIGHTSTYLES)
{ {
R_UpdateLightStyle(i, s, 1, 1, 1); R_UpdateLightStyle(i-Q2CS_LIGHTS, s, 1, 1, 1);
} }
else if (i == Q2CS_CDTRACK) else if (i == Q2CS_CDTRACK)
{ {

View file

@ -236,9 +236,9 @@ static qboolean Mod_LoadMap_Proc(model_t *model, char *data)
b[surf].lightmap[2] = -1; b[surf].lightmap[2] = -1;
b[surf].lightmap[3] = -1; b[surf].lightmap[3] = -1;
b[surf].lmlightstyle[0] = 0; b[surf].lmlightstyle[0] = 0;
b[surf].lmlightstyle[1] = 255; b[surf].lmlightstyle[1] = INVALID_LIGHTSTYLE;
b[surf].lmlightstyle[2] = 255; b[surf].lmlightstyle[2] = INVALID_LIGHTSTYLE;
b[surf].lmlightstyle[3] = 255; b[surf].lmlightstyle[3] = INVALID_LIGHTSTYLE;
data = COM_ParseOut(data, token, sizeof(token)); data = COM_ParseOut(data, token, sizeof(token));
b[surf].shader = R_RegisterShader_Vertex(token); b[surf].shader = R_RegisterShader_Vertex(token);

View file

@ -3199,12 +3199,16 @@ static qboolean CModQ3_LoadRFaces (model_t *mod, qbyte *mod_base, lump_t *l)
out->light_s[0] = LittleLong(in->lightmap_x); out->light_s[0] = LittleLong(in->lightmap_x);
out->light_t[0] = LittleLong(in->lightmap_y); out->light_t[0] = LittleLong(in->lightmap_y);
out->styles[0] = 255; out->styles[0] = INVALID_LIGHTSTYLE;
out->vlstyles[0] = 255;
for (sty = 1; sty < MAXRLIGHTMAPS; sty++) for (sty = 1; sty < MAXRLIGHTMAPS; sty++)
{ {
out->styles[sty] = 255; out->styles[sty] = INVALID_LIGHTSTYLE;
out->vlstyles[sty] = 255;
out->lightmaptexturenums[sty] = -1; out->lightmaptexturenums[sty] = -1;
} }
for (; sty < MAXQ1LIGHTMAPS; sty++)
out->styles[sty] = INVALID_LIGHTSTYLE;
out->lmshift = LMSHIFT_DEFAULT; out->lmshift = LMSHIFT_DEFAULT;
//fixme: determine texturemins from lightmap_origin //fixme: determine texturemins from lightmap_origin
out->extents[0] = (LittleLong(in->lightmap_width)-1)<<out->lmshift; out->extents[0] = (LittleLong(in->lightmap_width)-1)<<out->lmshift;
@ -3315,11 +3319,14 @@ static qboolean CModRBSP_LoadRFaces (model_t *mod, qbyte *mod_base, lump_t *l)
out->lightmaptexturenums[j] = LittleLong(in->lightmapnum[j]); out->lightmaptexturenums[j] = LittleLong(in->lightmapnum[j]);
out->light_s[j] = LittleLong(in->lightmap_offs[0][j]); out->light_s[j] = LittleLong(in->lightmap_offs[0][j]);
out->light_t[j] = LittleLong(in->lightmap_offs[1][j]); out->light_t[j] = LittleLong(in->lightmap_offs[1][j]);
out->styles[j] = in->lm_styles[j]; out->styles[j] = (in->lm_styles[j]!=255)?in->lm_styles[j]:INVALID_LIGHTSTYLE;
out->vlstyles[j] = in->vt_styles[j];
if (mod->lightmaps.count < out->lightmaptexturenums[j]+1) if (mod->lightmaps.count < out->lightmaptexturenums[j]+1)
mod->lightmaps.count = out->lightmaptexturenums[j]+1; mod->lightmaps.count = out->lightmaptexturenums[j]+1;
} }
for (; j < MAXQ1LIGHTMAPS; j++)
out->styles[j] = INVALID_LIGHTSTYLE;
if (facetype == MST_FLARE) if (facetype == MST_FLARE)
out->texinfo = mod->texinfo + mod->numtexinfo*2; out->texinfo = mod->texinfo + mod->numtexinfo*2;
else if (out->lightmaptexturenums[0]<0 || r_vertexlight.value) else if (out->lightmaptexturenums[0]<0 || r_vertexlight.value)

View file

@ -3641,7 +3641,7 @@ static void BE_Program_Set_Attributes(const program_t *prog, struct programpermu
for (j = 0; j < MAXRLIGHTMAPS ; j++) for (j = 0; j < MAXRLIGHTMAPS ; j++)
{ {
s = shaderstate.curbatch->lmlightstyle[j]; s = shaderstate.curbatch->lmlightstyle[j];
if (s == 255) if (s == INVALID_LIGHTSTYLE)
{ {
for (; j < MAXRLIGHTMAPS ; j++) for (; j < MAXRLIGHTMAPS ; j++)
{ {
@ -3672,6 +3672,7 @@ static void BE_Program_Set_Attributes(const program_t *prog, struct programpermu
else else
#endif #endif
{ {
unsigned short s;
if (shaderstate.curentity->model && (shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT) && !shaderstate.force2d) if (shaderstate.curentity->model && (shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT) && !shaderstate.force2d)
{ {
float sc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting; float sc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting;
@ -3682,6 +3683,10 @@ static void BE_Program_Set_Attributes(const program_t *prog, struct programpermu
Vector4Set(param4, shaderstate.identitylighting, shaderstate.identitylighting, shaderstate.identitylighting, 1); Vector4Set(param4, shaderstate.identitylighting, shaderstate.identitylighting, shaderstate.identitylighting, 1);
} }
s = shaderstate.curbatch->lmlightstyle[0]; //only one style.
if (s != INVALID_LIGHTSTYLE)
VectorScale(param4, d_lightstylevalue[s]/256.0f, param4);
qglUniform4fvARB(ph, 1, (GLfloat*)param4); qglUniform4fvARB(ph, 1, (GLfloat*)param4);
} }
break; break;

View file

@ -161,10 +161,8 @@ static void Mod_BatchList_f(void)
else if (batch->lightmap[1] >= 0) else if (batch->lightmap[1] >= 0)
Con_Printf("^2 lm=(%i:%i %i:%i)", batch->lightmap[0], batch->lmlightstyle[0], batch->lightmap[1], batch->lmlightstyle[1]); Con_Printf("^2 lm=(%i:%i %i:%i)", batch->lightmap[0], batch->lmlightstyle[0], batch->lightmap[1], batch->lmlightstyle[1]);
else else
if (batch->lightmap[1] >= 0)
#else
if (batch->lmlightstyle[0] != 255)
#endif #endif
if (batch->lmlightstyle[0] != INVALID_LIGHTSTYLE)
Con_Printf("^2 lm=(%i:%i)", batch->lightmap[0], batch->lmlightstyle[0]); Con_Printf("^2 lm=(%i:%i)", batch->lightmap[0], batch->lmlightstyle[0]);
else else
Con_Printf("^2 lm=%i", batch->lightmap[0]); Con_Printf("^2 lm=%i", batch->lightmap[0]);
@ -3255,7 +3253,7 @@ static void Mod_Batches_AllocLightmaps(model_t *mod)
for (sty = 0; sty < MAXRLIGHTMAPS; sty++) for (sty = 0; sty < MAXRLIGHTMAPS; sty++)
{ {
batch->lightmap[sty] = surf->lightmaptexturenums[sty]; batch->lightmap[sty] = surf->lightmaptexturenums[sty];
batch->lmlightstyle[sty] = 255;//don't do special backend rendering of lightstyles. batch->lmlightstyle[sty] = INVALID_LIGHTSTYLE;//don't do special backend rendering of lightstyles.
batch->vtlightstyle[sty] = 255;//don't do special backend rendering of lightstyles. batch->vtlightstyle[sty] = 255;//don't do special backend rendering of lightstyles.
} }

View file

@ -731,6 +731,7 @@ reeval:
st = &pr_statements[s]; st = &pr_statements[s];
if (prinst.pr_depth == prinst.exitdepth) if (prinst.pr_depth == prinst.exitdepth)
{ {
prinst.pr_xstatement = s;
return -1; // all done return -1; // all done
} }
return s; return s;

View file

@ -1552,7 +1552,7 @@ void PR_Init(void)
Cmd_AddCommand ("extensionlist_ssqc", PR_SVExtensionList_f); Cmd_AddCommand ("extensionlist_ssqc", PR_SVExtensionList_f);
Cmd_AddCommand ("pr_dumpplatform", PR_DumpPlatform_f); Cmd_AddCommand ("pr_dumpplatform", PR_DumpPlatform_f);
Cmd_AddCommand ("sv_lightstyle", PR_Lightstyle_f); Cmd_AddCommandD ("sv_lightstyle", PR_Lightstyle_f, "Overrides lightstyles from the server's console, mostly for debug use.");
/* /*
#ifdef _DEBUG #ifdef _DEBUG
@ -4721,11 +4721,31 @@ static void QCBUILTIN PF_lightstyle (pubprogfuncs_t *prinst, struct globalvars_s
static void PR_Lightstyle_f(void) static void PR_Lightstyle_f(void)
{ {
int style = atoi(Cmd_Argv(1)); int style = atoi(Cmd_Argv(1));
if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)
Con_TPrintf ("not supported in the current game mode.\n"); if (!SV_MayCheat())
else if (!SV_MayCheat())
Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n"); Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n");
else if (Cmd_Argc() <= 2) else switch(svs.gametype)
{
default:
Con_TPrintf ("not supported in the current game mode.\n");
break;
#ifdef Q2SERVER
case GT_QUAKE2:
if (Cmd_Argc() <= 2)
{
if ((unsigned)style < (unsigned)Q2MAX_LIGHTSTYLES && Cmd_Argc() >= 2)
Con_Printf ("Style %i: %s\n", style, sv.strings.configstring[Q2CS_LIGHTS+style]);
else for (style = 0; style < Q2MAX_LIGHTSTYLES; style++)
if (sv.strings.configstring[Q2CS_LIGHTS+style])
Con_Printf("Style %i: %s\n", style, sv.strings.configstring[Q2CS_LIGHTS+style]);
}
else if ((unsigned)style < (unsigned)Q2MAX_LIGHTSTYLES)
PFQ2_Configstring (Q2CS_LIGHTS+style, Cmd_Argv(2));
break;
#endif
case GT_PROGS:
case GT_Q1QVM:
if (Cmd_Argc() <= 2)
{ {
if (style >= 0 && style < sv.maxlightstyles && Cmd_Argc() >= 2) if (style >= 0 && style < sv.maxlightstyles && Cmd_Argc() >= 2)
Con_Printf("Style %i: %s %g %g %g\n", style, sv.lightstyles[style].str, sv.lightstyles[style].colours[0], sv.lightstyles[style].colours[1], sv.lightstyles[style].colours[2]); Con_Printf("Style %i: %s %g %g %g\n", style, sv.lightstyles[style].str, sv.lightstyles[style].colours[0], sv.lightstyles[style].colours[1], sv.lightstyles[style].colours[2]);
@ -4746,6 +4766,8 @@ static void PR_Lightstyle_f(void)
rgb[0] = rgb[1] = rgb[2] = atof(Cmd_Argv(3)); rgb[0] = rgb[1] = rgb[2] = atof(Cmd_Argv(3));
PF_applylightstyle(style, Cmd_Argv(2), rgb); PF_applylightstyle(style, Cmd_Argv(2), rgb);
} }
break;
}
} }
static void QCBUILTIN PF_getlightstyle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_getlightstyle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)

View file

@ -1270,6 +1270,7 @@ qboolean SV_FilterImpulse(int imp, int level);
//svq2_game.c //svq2_game.c
qboolean SVQ2_InitGameProgs(void); qboolean SVQ2_InitGameProgs(void);
void VARGS SVQ2_ShutdownGameProgs (void); void VARGS SVQ2_ShutdownGameProgs (void);
void VARGS PFQ2_Configstring (int i, const char *val); //for engine cheats.
//svq2_ents.c //svq2_ents.c
void SVQ2_BuildClientFrame (client_t *client); void SVQ2_BuildClientFrame (client_t *client);

View file

@ -1538,7 +1538,7 @@ void SV_SendLightstyle(client_t *cl, sizebuf_t *forcemsg, int style, qboolean in
if (!(cl->fteprotocolextensions & PEXT_LIGHTSTYLECOL)) if (!(cl->fteprotocolextensions & PEXT_LIGHTSTYLECOL))
{ //if they don't support it then just drop the extra colours, so long as it still makes sense. { //if they don't support it then just drop the extra colours, so long as it still makes sense.
if ((flags & ~0x87u) || (ISNQCLIENT(cl) && !ISDPCLIENT(cl) && !cl->fteprotocolextensions2)) if ((flags & ~0x87u) && (ISNQCLIENT(cl) && !ISDPCLIENT(cl) && cl->fteprotocolextensions2))
{ {
char *text = va("//ls %i \"%s\" %g %g %g\n", style, sv.lightstyles[style].str, sv.lightstyles[style].colours[0], sv.lightstyles[style].colours[1], sv.lightstyles[style].colours[2]); char *text = va("//ls %i \"%s\" %g %g %g\n", style, sv.lightstyles[style].str, sv.lightstyles[style].colours[0], sv.lightstyles[style].colours[1], sv.lightstyles[style].colours[2]);
if (forcemsg) if (forcemsg)
@ -1551,7 +1551,9 @@ void SV_SendLightstyle(client_t *cl, sizebuf_t *forcemsg, int style, qboolean in
ClientReliable_FinishWrite(cl); ClientReliable_FinishWrite(cl);
return; //erk, can't handle this! return; //erk, can't handle this!
} }
flags = 7; if (style >= ((cl->fteprotocolextensions2||ISDPCLIENT(cl))?255:64))
return; //client probably doesn't support this lightstyle.
flags = 7; //force vanilla protocol as fallback.
} }
if (forcemsg) if (forcemsg)

View file

@ -291,7 +291,7 @@ PF_Configstring
=============== ===============
*/ */
static void VARGS PFQ2_Configstring (int i, const char *val) void VARGS PFQ2_Configstring (int i, const char *val)
{ {
if (i < 0 || i >= Q2MAX_CONFIGSTRINGS) if (i < 0 || i >= Q2MAX_CONFIGSTRINGS)
Sys_Error ("configstring: bad index %i\n", i); Sys_Error ("configstring: bad index %i\n", i);

356
imgtool.c
View file

@ -4,6 +4,7 @@
#define stderr stdout #define stderr stdout
#include <limits.h> #include <limits.h>
#include <ctype.h>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#endif #endif
@ -348,11 +349,29 @@ static void ImgTool_FreeMips(struct pendingtextureinfo *mips)
sh_config_t sh_config; sh_config_t sh_config;
viddef_t vid; viddef_t vid;
static const char *imagetypename[] = {"2D", "3D", "Cube", "2DArray", "CubemapArray", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"};
typedef struct
{
unsigned int offset; // Position of the entry in WAD
unsigned int dsize; // Size of the entry in WAD file
unsigned int size; // Size of the entry in memory
char type; // type of entry
char cmprs; // Compression. 0 if none.
short dummy; // Not used
char name[16]; // we use only first 8
} wad2entry_t;
typedef struct
{
char magic[4]; //should be WAD2
unsigned int num; //number of entries
unsigned int offset; //location of directory
} wad2_t;
static const char *imagetypename[] = {"2D", "3D", "Cube", "2DArray", "CubemapArray", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"};
struct opts_s struct opts_s
{ {
int textype; int textype;
const char *defaultext; //.dds or whatever when the output's extension is not explicitly given.
unsigned int flags; //image flags to use (affects how textures get interpreted a little) unsigned int flags; //image flags to use (affects how textures get interpreted a little)
unsigned int mipnum; //when exporting to a mipless format, this is the mip level that is actually written. default 0. unsigned int mipnum; //when exporting to a mipless format, this is the mip level that is actually written. default 0.
uploadfmt_t newpixelformat; //try to convert to this pixel format on export. uploadfmt_t newpixelformat; //try to convert to this pixel format on export.
@ -376,23 +395,16 @@ static enum uploadfmt ImgTool_ASTCToLDR(uploadfmt_t fmt)
return fmt; return fmt;
} }
#ifdef _WIN32 #ifdef _WIN32
#include <io.h>
#include <fcntl.h>
static void FS_MakeTempName(char *out, size_t outsize, char *prefix, char *suffix) static void FS_MakeTempName(char *out, size_t outsize, char *prefix, char *suffix)
{ {
int fd; static char temp_path[MAX_PATH];
unsigned int n; char temp_file_name[MAX_PATH];
unsigned int s = rand(); if (!*temp_path && !GetTempPathA(sizeof(temp_path), temp_path))
for (n = 0; n < 0xffffff; n++) Sys_Error("FS_MakeTempName failed to get temp path\n");
{ if (!GetTempFileNameA(temp_path, prefix, 0, temp_file_name))
Q_snprintfz(out, outsize, "/tmp/%s%06x%s", prefix, (n+s)&0xffffff, suffix);
fd = _open(out, _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
if (fd == -1)
continue;
close(fd);
return;
}
Sys_Error("FS_MakeTempName failed\n"); Sys_Error("FS_MakeTempName failed\n");
Q_snprintfz(out, outsize, "%s%s", temp_file_name, suffix);
} }
#else #else
#include <unistd.h> #include <unistd.h>
@ -402,6 +414,58 @@ static void FS_MakeTempName(char *out, size_t outsize, char *prefix, char *suffi
close(mkstemps(out, strlen(suffix))); //bsd4.3/posix1-2001 close(mkstemps(out, strlen(suffix))); //bsd4.3/posix1-2001
} }
#endif #endif
static qboolean ImgTool_HasAlpha(struct pendingtextureinfo *mips)
{
if (mips->encoding == PTI_RGBA8 || mips->encoding == PTI_BGRA8 || mips->encoding == PTI_LLLA8 || mips->encoding == PTI_RGBA8_SRGB || mips->encoding == PTI_BGRA8_SRGB)
{
size_t l = 0, pixels, p;
qbyte *d;
for (l = 0; l < mips->mipcount; l++)
{
pixels = mips->mip[l].width * mips->mip[l].height * mips->mip[l].depth * 4;
d = mips->mip[l].data;
d+=3;
for (p = 0; p < pixels; p+=4)
if (d[p] != 255)
return true; //a transparent pixel!
}
return false;
}
else if (mips->encoding == PTI_L8A8 || mips->encoding == PTI_L8A8_SRGB)
{
size_t l = 0, pixels, p;
qbyte *d;
for (l = 0; l < mips->mipcount; l++)
{
pixels = mips->mip[l].width * mips->mip[l].height * mips->mip[l].depth * 2;
d = mips->mip[l].data;
d+=1;
for (p = 0; p < pixels; p+=2)
if (d[p] != 255)
return true; //a transparent pixel!
}
return false;
}
else if (mips->encoding == PTI_RGBA16)
{
size_t l = 0, pixels, p;
unsigned short *d;
for (l = 0; l < mips->mipcount; l++)
{
pixels = mips->mip[l].width * mips->mip[l].height * mips->mip[l].depth * 4;
d = mips->mip[l].data;
d+=3;
for (p = 0; p < pixels; p+=4)
if (d[p] != 0xffff)
return true; //a transparent pixel!
}
return false;
}
else
return Image_FormatHasAlpha(mips->encoding);
}
static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, struct pendingtextureinfo *mips) static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, struct pendingtextureinfo *mips)
{ {
struct pendingtextureinfo tmp, *ret; struct pendingtextureinfo tmp, *ret;
@ -415,10 +479,10 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
int bb,bw,bh; int bb,bw,bh;
qboolean canktx = false; qboolean canktx = false;
uploadfmt_t targfmt = args->newpixelformat; uploadfmt_t targfmt = args->newpixelformat;
int d,l, layers; int d,l, layers, r;
//force it to bc1 if bc2 or bc3 with no alpha channel. //force it to bc1 if bc2 or bc3 with no alpha channel.
if ((targfmt == PTI_BC2_RGBA || targfmt == PTI_BC3_RGBA) && !Image_FormatHasAlpha(mips->encoding)) if ((targfmt == PTI_BC2_RGBA || targfmt == PTI_BC3_RGBA) && !ImgTool_HasAlpha(mips))
targfmt = PTI_BC1_RGB; targfmt = PTI_BC1_RGB;
if (targfmt >= PTI_ASTC_FIRST && targfmt <= PTI_ASTC_LAST) if (targfmt >= PTI_ASTC_FIRST && targfmt <= PTI_ASTC_LAST)
@ -428,12 +492,20 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
} }
else if (targfmt == PTI_BC1_RGB) else if (targfmt == PTI_BC1_RGB)
Q_snprintfz(command, sizeof(command), "nvcompress -bc1%s", (args->flags&IF_TRYBUMP)?"n":""); Q_snprintfz(command, sizeof(command), "nvcompress -bc1%s", (args->flags&IF_TRYBUMP)?"n":"");
else if (targfmt == PTI_BC1_RGB_SRGB)
Q_snprintfz(command, sizeof(command), "nvcompress -bc1%s -srgb -dds10", (args->flags&IF_TRYBUMP)?"n":"");
else if (targfmt == PTI_BC1_RGBA) else if (targfmt == PTI_BC1_RGBA)
Q_snprintfz(command, sizeof(command), "nvcompress -bc1a"); Q_snprintfz(command, sizeof(command), "nvcompress -bc1a");
else if (targfmt == PTI_BC1_RGBA_SRGB)
Q_snprintfz(command, sizeof(command), "nvcompress -bc1a -srgb -dds10");
else if (targfmt == PTI_BC2_RGBA) else if (targfmt == PTI_BC2_RGBA)
Q_snprintfz(command, sizeof(command), "nvcompress -bc2"); Q_snprintfz(command, sizeof(command), "nvcompress -bc2");
else if (targfmt == PTI_BC2_RGBA_SRGB)
Q_snprintfz(command, sizeof(command), "nvcompress -bc2 -srgb -dds10");
else if (targfmt == PTI_BC3_RGBA) else if (targfmt == PTI_BC3_RGBA)
Q_snprintfz(command, sizeof(command), "nvcompress -bc3%s", (args->flags&IF_TRYBUMP)?"n":""); Q_snprintfz(command, sizeof(command), "nvcompress -bc3%s", (args->flags&IF_TRYBUMP)?"n":"");
else if (targfmt == PTI_BC3_RGBA_SRGB)
Q_snprintfz(command, sizeof(command), "nvcompress -bc3%s -srgb -dds10", (args->flags&IF_TRYBUMP)?"n":"");
else if (targfmt == PTI_BC4_R8) else if (targfmt == PTI_BC4_R8)
Q_snprintfz(command, sizeof(command), "nvcompress -bc4"); Q_snprintfz(command, sizeof(command), "nvcompress -bc4");
else if (targfmt == PTI_BC5_RG8) else if (targfmt == PTI_BC5_RG8)
@ -442,6 +514,8 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
Q_snprintfz(command, sizeof(command), "nvcompress -bc6"); Q_snprintfz(command, sizeof(command), "nvcompress -bc6");
else if (targfmt == PTI_BC7_RGBA) else if (targfmt == PTI_BC7_RGBA)
Q_snprintfz(command, sizeof(command), "nvcompress -bc7"); Q_snprintfz(command, sizeof(command), "nvcompress -bc7");
else if (targfmt == PTI_BC7_RGBA_SRGB)
Q_snprintfz(command, sizeof(command), "nvcompress -bc7 -srgb");
else else
{ {
if (mips->encoding != targfmt) if (mips->encoding != targfmt)
@ -487,8 +561,12 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
Q_strncatz(command, " -hdr", sizeof(command)); Q_strncatz(command, " -hdr", sizeof(command));
if (targfmt >= PTI_BC1_RGB && targfmt <= PTI_BC7_RGBA_SRGB && (strstr(inname, "_n.")||strstr(inname, "_norm."))) if (targfmt >= PTI_BC1_RGB && targfmt <= PTI_BC7_RGBA_SRGB && (strstr(inname, "_n.")||strstr(inname, "_norm.")))
Q_strncatz(command, " -normal", sizeof(command)); //looks like a normalmap... tweak metrics to favour normalised results. Q_strncatz(command, " -normal", sizeof(command)); //looks like a normalmap... tweak metrics to favour normalised results.
Q_strncatz(command, ">> /dev/null", sizeof(command));
#ifdef _WIN32
Q_strncatz(command, "> NUL 2>&1", sizeof(command));
#else
Q_strncatz(command, ">> /dev/null", sizeof(command));
#endif
if (!canktx) if (!canktx)
{ {
@ -549,7 +627,12 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
break; break;
} }
system(command); r = system(command);
if (r != EXIT_SUCCESS)
{
Con_Printf("The following system command failed with code %i: %s\n", r, command);
break;
}
fdata = FS_LoadMallocFile(comp, &fsize); fdata = FS_LoadMallocFile(comp, &fsize);
ret = Image_LoadMipsFromMemory(IF_NOMIPMAP, comp, comp, fdata, fsize); ret = Image_LoadMipsFromMemory(IF_NOMIPMAP, comp, comp, fdata, fsize);
@ -608,7 +691,7 @@ const char *COM_GetFileExtension (const char *in, const char *term)
if (*dot == '.') if (*dot == '.')
return dot; return dot;
} }
return ""; return term+strlen(term);
} }
static struct pendingtextureinfo *ImgTool_Read(struct opts_s *args, const char *inname) static struct pendingtextureinfo *ImgTool_Read(struct opts_s *args, const char *inname)
{ {
@ -845,9 +928,21 @@ static struct pendingtextureinfo *ImgTool_Combine(struct opts_s *args, const cha
static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, const char *inname, const char *outname) static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, const char *inname, const char *outname)
{ {
size_t k; size_t k;
const char *outext = COM_GetFileExtension(outname, NULL); const char *outext;
qboolean allowcompressed = false; qboolean allowcompressed = false;
char newout[MAX_OSPATH];
if (!outname)
{
outext = COM_GetFileExtension(inname, NULL);
k = min(MAX_OSPATH-2-strlen(args->defaultext), outext-inname);
memcpy(newout, inname, k);
newout[k++] = '.';
strcpy(newout+k, args->defaultext);
outname = newout;
}
outext = COM_GetFileExtension(outname, NULL);
if (!strcmp(outext, ".dds") || !strcmp(outext, ".ktx")) if (!strcmp(outext, ".dds") || !strcmp(outext, ".ktx"))
allowcompressed = true; allowcompressed = true;
@ -918,7 +1013,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
/*(k == PTI_L16) ||*/ /*(k == PTI_L16) ||*/
(k == PTI_BGR8) || (k == PTI_BGR8) || (k == PTI_BGR8) || (k == PTI_BGR8) ||
0; 0;
if (!sh_config.texfmt[in->encoding]) if (!outformats[in->encoding])
{ {
Image_ChangeFormat(in, outformats, PTI_INVALID, outname); Image_ChangeFormat(in, outformats, PTI_INVALID, outname);
printf("\t(Exporting as %s)\n", Image_FormatName(in->encoding)); printf("\t(Exporting as %s)\n", Image_FormatName(in->encoding));
@ -974,6 +1069,35 @@ static void ImgTool_Info(struct opts_s *args, const char *inname)
indata = FS_LoadMallocFile(inname, &fsize); indata = FS_LoadMallocFile(inname, &fsize);
if (!indata) if (!indata)
printf("%s: unable to read\n", inname); printf("%s: unable to read\n", inname);
else if (fsize >= sizeof(wad2_t) && indata[0] == 'W' && indata[1] == 'A' && indata[2] == 'D')
{
const wad2_t *w = (const wad2_t *)indata;
const wad2entry_t *e = (const wad2entry_t *)(indata+w->offset);
printf("%s: wad%c file with %i entries\n", inname, w->magic[3], w->num);
for (m = 0; m < w->num; m++, e++)
{
switch(e->type)
{
case 67: //hl...
case TYP_MIPTEX:
{
const miptex_t *mip = (const miptex_t *)(indata+e->offset);
/*mip name SHOULD match entry name... but gah!*/
if (strcasecmp(e->name, mip->name))
printf("\t%16.16s (%s): %u*%u%s\n", e->name, mip->name, mip->width, mip->height, mip->offsets[0]?"":" (external data)");
else
printf("\t%16.16s: %u*%u%s\n", mip->name, mip->width, mip->height, mip->offsets[0]?"":" (external data)");
}
break;
case TYP_PALETTE:
printf("\t%16.16s: palette - %u bytes\n", e->name, e->size);
break;
default:
printf("\t%16.16s: ENTRY TYPE %u (%u bytes)\n", e->name, e->type, e->size);
break;
}
}
}
else else
{ {
in = Image_LoadMipsFromMemory(args->flags, inname, inname, indata, fsize); in = Image_LoadMipsFromMemory(args->flags, inname, inname, indata, fsize);
@ -996,6 +1120,7 @@ struct filelist_s
const char **exts; const char **exts;
size_t numfiles; size_t numfiles;
struct { struct {
const char *rootpath; //the basepath that was passed to the filelist scan.
char *name; char *name;
size_t baselen; //length up to but not including the filename extension. size_t baselen; //length up to but not including the filename extension.
} *file; } *file;
@ -1010,7 +1135,7 @@ static void FileList_Release(struct filelist_s *list)
list->numfiles = 0; list->numfiles = 0;
list->maxfiles = 0; list->maxfiles = 0;
} }
static void FileList_Add(struct filelist_s *list, char *fname) static void FileList_Add(struct filelist_s *list, const char *rootpath, char *fname)
{ {
size_t i; size_t i;
size_t baselen; size_t baselen;
@ -1036,15 +1161,47 @@ static void FileList_Add(struct filelist_s *list, char *fname)
list->maxfiles += 64; list->maxfiles += 64;
list->file = realloc(list->file, sizeof(*list->file)*list->maxfiles); list->file = realloc(list->file, sizeof(*list->file)*list->maxfiles);
} }
list->file[i].rootpath = rootpath;
list->file[i].name = strdup(fname); list->file[i].name = strdup(fname);
list->file[i].baselen = baselen; list->file[i].baselen = baselen;
list->numfiles++; list->numfiles++;
} }
#ifdef _WIN32 #ifdef _WIN32
static void ImgTool_TreeScan(struct filelist_s *list, const char *basepath, const char *subpath) static void ImgTool_TreeScan(struct filelist_s *list, const char *rootpath, const char *subpath)
{ //FIXME: convert to utf-8.
HANDLE h;
WIN32_FIND_DATAA fd;
char file[MAX_OSPATH];
if (subpath && *subpath)
Q_snprintfz(file, sizeof(file), "%s/%s", rootpath, subpath);
else
Q_snprintfz(file, sizeof(file), "%s", rootpath);
if (GetFileAttributesA(file) & FILE_ATTRIBUTE_DIRECTORY) //if its a directory then scan it.
Q_snprintfz(file+strlen(file), sizeof(file)-strlen(file), "/*");
h = FindFirstFileA(file, &fd);
if (h != INVALID_HANDLE_VALUE)
{ {
(void)FileList_Add; do
Con_Printf("ImgTool_TreeScan not implemented on windows.\n"); {
if (*fd.cFileName == '.')
continue; //skip .. (and unix hidden files, because urgh)
if (subpath && *subpath)
Q_snprintfz(file, sizeof(file), "%s/%s", subpath, fd.cFileName);
else
Q_snprintfz(file, sizeof(file), "%s", fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
; //don't report hidden entries.
else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
ImgTool_TreeScan(list, rootpath, file);
else
FileList_Add(list, rootpath, file);
} while(FindNextFileA(h, &fd));
FindClose(h);
}
} }
#else #else
#include <dirent.h> #include <dirent.h>
@ -1088,13 +1245,13 @@ static void ImgTool_TreeScan(struct filelist_s *list, const char *basepath, cons
Q_snprintfz(file, sizeof(file), "%s/%s", subpath, ent->d_name); Q_snprintfz(file, sizeof(file), "%s/%s", subpath, ent->d_name);
else else
Q_snprintfz(file, sizeof(file), "%s", ent->d_name); Q_snprintfz(file, sizeof(file), "%s", ent->d_name);
FileList_Add(list, file); FileList_Add(list, basepath, file);
} }
} }
closedir(dir); closedir(dir);
} }
#endif #endif
static void ImgTool_TreeConvert(struct opts_s *args, const char *srcpath, const char *destpath) static void ImgTool_TreeConvert(struct opts_s *args, const char *destpath, const char *srcpath)
{ {
size_t newfiles=0, skippedfiles=0, processedfiles=0; size_t newfiles=0, skippedfiles=0, processedfiles=0;
char file[MAX_OSPATH]; char file[MAX_OSPATH];
@ -1145,25 +1302,55 @@ static void ImgTool_TreeConvert(struct opts_s *args, const char *srcpath, const
static void ImgTool_WadExtract(struct opts_s *args, const char *wadname)
typedef struct
{ {
unsigned int offset; // Position of the entry in WAD qbyte *indata;
unsigned int dsize; // Size of the entry in WAD file size_t fsize;
unsigned int size; // Size of the entry in memory size_t m;
char type; // type of entry indata = FS_LoadMallocFile(wadname, &fsize);
char cmprs; // Compression. 0 if none. if (!indata)
short dummy; // Not used printf("%s: unable to read\n", wadname);
char name[16]; // we use only first 8 else if (fsize >= sizeof(wad2_t) && indata[0] == 'W' && indata[1] == 'A' && indata[2] == 'D')
} wad2entry_t;
typedef struct
{ {
char magic[4]; //should be WAD2 const wad2_t *w = (const wad2_t *)indata;
unsigned int num; //number of entries const wad2entry_t *e = (const wad2entry_t *)(indata+w->offset);
unsigned int offset; //location of directory
} wad2_t; for (m = 0; m < w->num; m++, e++)
static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const char *destpath, int wadtype/*x,2,3*/) {
switch(e->type)
{
case 67: //hl...
case TYP_MIPTEX:
{
miptex_t *mip = (miptex_t *)(indata+e->offset);
struct pendingtextureinfo *out = Z_Malloc(sizeof(*out));
out->encoding = PTI_P8;
out->type = PTI_2D;
for (out->mipcount = 0; out->mipcount < 4 && mip->offsets[out->mipcount]; out->mipcount++)
{
out->mip[out->mipcount].width = mip->width>>out->mipcount;
out->mip[out->mipcount].height = mip->height>>out->mipcount;
out->mip[out->mipcount].depth = 1;
out->mip[out->mipcount].datasize = out->mip[out->mipcount].width*out->mip[out->mipcount].height*out->mip[out->mipcount].depth;
out->mip[out->mipcount].data = (char*)mip + mip->offsets[out->mipcount];
}
if (*mip->name == '*')
*mip->name = '#'; //convert from * to #, so its a valid file name.
ImgTool_Convert(args, out, mip->name, NULL);
}
break;
case TYP_PALETTE:
default:
printf("skipping %s\n", e->name);
break;
}
}
}
else
printf("%s: does not appear to be a wad file\n", wadname);
}
static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const char **srcpaths, size_t numpaths, int wadtype/*x,2,3*/)
{ {
char file[MAX_OSPATH]; char file[MAX_OSPATH];
const char *exts[] = {".png", ".bmp", ".tga", ".exr", ".hdr", ".dds", ".ktx", ".xcf", ".pcx", ".jpg", NULL}; const char *exts[] = {".png", ".bmp", ".tga", ".exr", ".hdr", ".dds", ".ktx", ".xcf", ".pcx", ".jpg", NULL};
@ -1179,7 +1366,16 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c
miptex_t mip; miptex_t mip;
qboolean wadpixelformats[PTI_MAX] = {0}; qboolean wadpixelformats[PTI_MAX] = {0};
wadpixelformats[PTI_P8] = true; wadpixelformats[PTI_P8] = true;
ImgTool_TreeScan(&list, srcpath, NULL); if (!numpaths)
ImgTool_TreeScan(&list, ".", NULL);
else while(numpaths --> 0)
ImgTool_TreeScan(&list, *srcpaths++, NULL);
if (!list.numfiles)
{
printf("%s: No files specified\n", destpath);
return;
}
f = FS_OpenVFS(destpath, "wb", FS_SYSTEM); f = FS_OpenVFS(destpath, "wb", FS_SYSTEM);
wad2.magic[0] = 'W'; wad2.magic[0] = 'W';
@ -1194,9 +1390,26 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c
for (u = 1; u < countof(sh_config.texfmt); u++) for (u = 1; u < countof(sh_config.texfmt); u++)
sh_config.texfmt[u] = (u==PTI_RGBA8)||(u==PTI_RGBX8)||(u==PTI_P8); sh_config.texfmt[u] = (u==PTI_RGBA8)||(u==PTI_RGBX8)||(u==PTI_P8);
if (wadtype == 2)
{ //WAD2 files generally have a palette lump.
if (wad2.num == maxentries)
{
maxentries += 64;
wadentries = realloc(wadentries, sizeof(*wadentries)*maxentries);
}
entry = &wadentries[wad2.num++];
memset(entry, 0, sizeof(*entry));
Q_strncpyz(entry->name, "PALETTE", 16);
entry->type = TYP_PALETTE;
entry->offset = VFS_TELL(f);
//and the lump data.
VFS_WRITE(f, host_basepal, 256*3);
}
for (i = 0; i < list.numfiles; i++) for (i = 0; i < list.numfiles; i++)
{ {
Q_snprintfz(file, sizeof(file), "%s/%s", srcpath, list.file[i].name); Q_snprintfz(file, sizeof(file), "%s/%s", list.file[i].rootpath, list.file[i].name);
inname = list.file[i].name; inname = list.file[i].name;
if (list.file[i].baselen > 15) if (list.file[i].baselen > 15)
{ {
@ -1204,7 +1417,9 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c
continue; continue;
} }
indata = FS_LoadMallocFile(file, &fsize); indata = FS_LoadMallocFile(file, &fsize);
if (indata) if (!indata)
printf("Unable to open %s\n", inname);
else
{ {
struct pendingtextureinfo *in = Image_LoadMipsFromMemory(args->flags, inname, file, indata, fsize); struct pendingtextureinfo *in = Image_LoadMipsFromMemory(args->flags, inname, file, indata, fsize);
Image_GenerateMips(in, args->flags); Image_GenerateMips(in, args->flags);
@ -1276,6 +1491,13 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c
entry->name[list.file[i].baselen] = 0; //kill any .tga entry->name[list.file[i].baselen] = 0; //kill any .tga
if (*entry->name == '#') if (*entry->name == '#')
*entry->name = '*'; //* is not valid in a filename, yet needed for turbs, so by convention # is used instead. this is only relevant for the first char. *entry->name = '*'; //* is not valid in a filename, yet needed for turbs, so by convention # is used instead. this is only relevant for the first char.
if (wadtype == 3)
{
for (u = 0; u < sizeof(entry->name); u++)
entry->name[u] = toupper(entry->name[u]);
entry->type = 67;
}
else
entry->type = TYP_MIPTEX; entry->type = TYP_MIPTEX;
entry->cmprs = 0; entry->cmprs = 0;
entry->dummy = 0; entry->dummy = 0;
@ -1326,6 +1548,7 @@ int main(int argc, const char **argv)
mode_genwadx, mode_genwadx,
mode_genwad2, mode_genwad2,
mode_genwad3, mode_genwad3,
mode_extractwad,
} mode = mode_info; } mode = mode_info;
size_t u, f; size_t u, f;
qboolean nomoreopts = false; qboolean nomoreopts = false;
@ -1338,6 +1561,7 @@ int main(int argc, const char **argv)
args.newpixelformat = PTI_INVALID; args.newpixelformat = PTI_INVALID;
args.mipnum = 0; args.mipnum = 0;
args.textype = -1; args.textype = -1;
args.defaultext = NULL;
sh_config.texture2d_maxsize = 1u<<31; sh_config.texture2d_maxsize = 1u<<31;
sh_config.texture3d_maxsize = 1u<<31; sh_config.texture3d_maxsize = 1u<<31;
@ -1368,9 +1592,10 @@ showhelp:
Con_Printf("compress : %s --astc_6x6_ldr [--nomips] in.png out.ktx [in2.png out2.ktx]\n", argv[0]); Con_Printf("compress : %s --astc_6x6_ldr [--nomips] in.png out.ktx [in2.png out2.ktx]\n", argv[0]);
Con_Printf("compress : %s --bc3_rgba [--premul] [--nomips] in.png out.dds\n\tConvert pixel format (to bc3 aka dxt5) before writing to output file.\n", argv[0]); Con_Printf("compress : %s --bc3_rgba [--premul] [--nomips] in.png out.dds\n\tConvert pixel format (to bc3 aka dxt5) before writing to output file.\n", argv[0]);
Con_Printf("convert : %s --convert in.exr out.dds\n\tConvert to different file format, while trying to preserve pixel formats.\n", argv[0]); Con_Printf("convert : %s --convert in.exr out.dds\n\tConvert to different file format, while trying to preserve pixel formats.\n", argv[0]);
Con_Printf("recursive : %s --astc_6x6_ldr -r srcdir destdir\n", argv[0]); Con_Printf("recursive : %s --auto --astc_6x6_ldr destdir srcdir\n\tCompresses the files to dds (writing to an optionally different directory)", argv[0]);
Con_Printf("decompress : %s --decompress [--exportmip 0] [--nomips] in.ktx out.png\n\tDecompresses any block-compressed pixel data.\n", argv[0]); Con_Printf("decompress : %s --decompress [--exportmip 0] [--nomips] in.ktx out.png\n\tDecompresses any block-compressed pixel data.\n", argv[0]);
Con_Printf("gen wad : %s --genwad3 [--exportmip 2] srcdir out.wad\n", argv[0]); Con_Printf("create wad : %s -w [--exportmip 2] out.wad srcdir\n", argv[0]);
Con_Printf("extract wad: %s -x [--ext png] src.wad\n", argv[0]);
Image_PrintInputFormatVersions(); Image_PrintInputFormatVersions();
Con_Printf("Supported compressed/interesting pixelformats are:\n"); Con_Printf("Supported compressed/interesting pixelformats are:\n");
@ -1426,6 +1651,8 @@ showhelp:
mode = mode_genwad2; mode = mode_genwad2;
else if (!files && (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwadx"))) else if (!files && (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwadx")))
mode = mode_genwadx; mode = mode_genwadx;
else if (!files && (!strcmp(argv[u], "-x") || !strcmp(argv[u], "--extractwad")))
mode = mode_extractwad;
else if (!strcmp(argv[u], "--2d")) else if (!strcmp(argv[u], "--2d"))
args.textype = PTI_2D; args.textype = PTI_2D;
else if (!strcmp(argv[u], "--3d")) else if (!strcmp(argv[u], "--3d"))
@ -1444,6 +1671,16 @@ showhelp:
args.flags |= IF_PREMULTIPLYALPHA; args.flags |= IF_PREMULTIPLYALPHA;
else if (!strcmp(argv[u], "--nopremul")) else if (!strcmp(argv[u], "--nopremul"))
args.flags &= ~IF_PREMULTIPLYALPHA; args.flags &= ~IF_PREMULTIPLYALPHA;
else if (!strcmp(argv[u], "--ext"))
{
if (u+1 < argc)
args.defaultext = argv[++u];
else
{
Con_Printf("--exportmip requires trailing numeric argument\n");
return 1;
}
}
else if (!strcmp(argv[u], "--exportmip")) else if (!strcmp(argv[u], "--exportmip"))
{ {
char *e = "erk"; char *e = "erk";
@ -1480,6 +1717,14 @@ showhelp:
argv[files++] = argv[u]; argv[files++] = argv[u];
} }
if (!args.defaultext)
{
if (mode == mode_extractwad)
args.defaultext = "png"; //something the user expects to be able to view easily (and lossless)
else
args.defaultext = "ktx";
}
if (mode == mode_info) if (mode == mode_info)
{ //just print info about each listed file. { //just print info about each listed file.
for (u = 0; u < files; u++) for (u = 0; u < files; u++)
@ -1500,10 +1745,15 @@ showhelp:
} }
else if (mode == mode_autotree && files == 2) else if (mode == mode_autotree && files == 2)
ImgTool_TreeConvert(&args, argv[0], argv[1]); ImgTool_TreeConvert(&args, argv[0], argv[1]);
else if ((mode == mode_genwad2 || mode == mode_genwad3 || mode == mode_genwadx) && files == 2) else if ((mode == mode_genwad2 || mode == mode_genwad3 || mode == mode_genwadx))
ImgTool_WadConvert(&args, argv[0], argv[1], mode-mode_genwadx); ImgTool_WadConvert(&args, argv[0], argv+1, files-1, mode-mode_genwadx);
else if ((mode == mode_extractwad) && files == 1)
ImgTool_WadExtract(&args, argv[0]);
else else
{
printf("%u files\n", (int)files);
printf("unsupported arg count for mode\n");
return EXIT_FAILURE; return EXIT_FAILURE;
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View file

@ -326,6 +326,6 @@ NATIVE_PLUGINS+=ezhud
$(PLUG_PREFIX)models$(PLUG_NATIVE_EXT): models/gltf.c models/exportiqm.c models/models.c plugin.c $(PLUG_PREFIX)models$(PLUG_NATIVE_EXT): models/gltf.c models/exportiqm.c models/models.c plugin.c
$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Imodels $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Imodels $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS)
#NATIVE_PLUGINS+=models NATIVE_PLUGINS+=models
all: $(foreach FOO,$(NATIVE_PLUGINS), $(PLUG_PREFIX)$(FOO)$(PLUG_NATIVE_EXT)) all: $(foreach FOO,$(NATIVE_PLUGINS), $(PLUG_PREFIX)$(FOO)$(PLUG_NATIVE_EXT))

View file

@ -116,17 +116,19 @@ static AVStream *add_video_stream(struct encctx *ctx, AVCodec *codec, int fps, i
int forcewidth = ffmpeg_videoforcewidth->value; int forcewidth = ffmpeg_videoforcewidth->value;
int forceheight = ffmpeg_videoforceheight->value; int forceheight = ffmpeg_videoforceheight->value;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
st = avformat_new_stream(ctx->fc, NULL);
if (!st)
return NULL;
st->id = ctx->fc->nb_streams-1;
c = avcodec_alloc_context3(codec);
#else
st = avformat_new_stream(ctx->fc, codec); st = avformat_new_stream(ctx->fc, codec);
if (!st) if (!st)
return NULL; return NULL;
st->id = ctx->fc->nb_streams-1;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101)
c = st->codec; c = st->codec;
#else st->id = ctx->fc->nb_streams-1;
c = avcodec_alloc_context3(codec);
if(avcodec_parameters_to_context(c, st->codecpar))
return NULL;
#endif #endif
ctx->video_codec = c; ctx->video_codec = c;
c->codec_id = codec->id; c->codec_id = codec->id;
@ -166,7 +168,6 @@ static AVStream *add_video_stream(struct encctx *ctx, AVCodec *codec, int fps, i
if (*ffmpeg_video_crf->string) if (*ffmpeg_video_crf->string)
av_opt_set(c->priv_data, "crf", ffmpeg_video_crf->string, AV_OPT_SEARCH_CHILDREN); av_opt_set(c->priv_data, "crf", ffmpeg_video_crf->string, AV_OPT_SEARCH_CHILDREN);
return st; return st;
} }
static void close_video(struct encctx *ctx) static void close_video(struct encctx *ctx)
@ -301,18 +302,22 @@ static AVStream *add_audio_stream(struct encctx *ctx, AVCodec *codec, int *sampl
AVStream *st; AVStream *st;
int bitrate = ffmpeg_audiobitrate->value; int bitrate = ffmpeg_audiobitrate->value;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101)
st = avformat_new_stream(ctx->fc, codec); st = avformat_new_stream(ctx->fc, codec);
if (!st) if (!st)
return NULL; return NULL;
st->id = ctx->fc->nb_streams-1;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101)
c = st->codec; c = st->codec;
#else #else
st = avformat_new_stream(ctx->fc, NULL);
if (!st)
return NULL;
c = avcodec_alloc_context3(codec); c = avcodec_alloc_context3(codec);
if(avcodec_parameters_to_context(c, st->codecpar)) if(avcodec_parameters_to_context(c, st->codecpar))
return NULL; return NULL;
#endif #endif
st->id = ctx->fc->nb_streams-1;
ctx->audio_codec = c; ctx->audio_codec = c;
c->codec_id = codec->id; c->codec_id = codec->id;
c->codec_type = codec->type; c->codec_type = codec->type;
@ -645,6 +650,16 @@ static void *AVEnc_Begin (char *streamname, int videorate, int width, int height
ctx->video_outbuf = av_malloc(ctx->video_outbuf_size); ctx->video_outbuf = av_malloc(ctx->video_outbuf_size);
if (!ctx->video_outbuf) if (!ctx->video_outbuf)
ctx->video_outbuf_size = 0; ctx->video_outbuf_size = 0;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
//copy the avcodec parameters over to avformat
err = avcodec_parameters_from_context(ctx->video_st->codecpar, c);
if(err < 0)
{
AVEnc_End(ctx);
return NULL;
}
#endif
} }
if (ctx->audio_st) if (ctx->audio_st)
{ {
@ -717,9 +732,6 @@ static void AVEnc_End (void *vctx)
} }
#endif #endif
close_video(ctx);
close_audio(ctx);
//don't write trailers if this is an error case and we never even wrote the headers. //don't write trailers if this is an error case and we never even wrote the headers.
if (ctx->doneheaders) if (ctx->doneheaders)
{ {
@ -728,12 +740,15 @@ static void AVEnc_End (void *vctx)
Con_Printf("Finished writing %s\n", ctx->abspath); Con_Printf("Finished writing %s\n", ctx->abspath);
} }
close_video(ctx);
close_audio(ctx);
for(i = 0; i < ctx->fc->nb_streams; i++) for(i = 0; i < ctx->fc->nb_streams; i++)
av_freep(&ctx->fc->streams[i]); av_freep(&ctx->fc->streams[i]);
// if (!(fmt->flags & AVFMT_NOFILE)) // if (!(fmt->flags & AVFMT_NOFILE))
avio_close(ctx->fc->pb); avio_close(ctx->fc->pb);
av_free(ctx->audio_outbuf); av_free(ctx->audio_outbuf);
av_free(ctx->fc); avformat_free_context(ctx->fc);
free(ctx); free(ctx);
} }
static media_encoder_funcs_t encoderfuncs = static media_encoder_funcs_t encoderfuncs =
@ -741,7 +756,7 @@ static media_encoder_funcs_t encoderfuncs =
sizeof(media_encoder_funcs_t), sizeof(media_encoder_funcs_t),
"ffmpeg", "ffmpeg",
"Use ffmpeg's various codecs. Various settings are configured with the "ENCODERNAME"_* cvars.", "Use ffmpeg's various codecs. Various settings are configured with the "ENCODERNAME"_* cvars.",
".mp4", ".mp4;.*",
AVEnc_Begin, AVEnc_Begin,
AVEnc_Video, AVEnc_Video,
AVEnc_Audio, AVEnc_Audio,

View file

@ -42,7 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ezquakeisms.h" #include "ezquakeisms.h"
#include "hud.h" #include "hud.h"
//#define WITH_PNG //#define WITH_PNG //more WITH_RADAR than anything else.
#define draw_disc draw_disc2 #define draw_disc draw_disc2

View file

@ -1,5 +1,5 @@
#ifndef GLQUAKE #ifndef GLQUAKE
//#define GLQUAKE //this is shit. #define GLQUAKE //this is shit, but ensures index sizes come out the right size
#endif #endif
#include "quakedef.h" #include "quakedef.h"
#include "../plugin.h" #include "../plugin.h"
@ -7,10 +7,38 @@
extern plugmodfuncs_t *modfuncs; extern plugmodfuncs_t *modfuncs;
extern plugfsfuncs_t *filefuncs; extern plugfsfuncs_t *filefuncs;
#if MAX_INDICIES == 0xffffu
#warning 16bit indexes
#else
#warning 32bit indexes
#endif
#ifdef SKELETALMODELS #ifdef SKELETALMODELS
#define GLTFMODELS #define GLTFMODELS
#endif #endif
/*Limitations:
materials:
material names (when present) are assumed to be either globally unique, or the material attributes must match those of all other materials with the same name.
texture modes (like clamp) must match on both axis (either both clamp or both wrap, no mixing)
mirrored-repeat not supported
mip-mag-mip filters must match (all linear, or all nearest)
animations:
input framerates are not well-defined. this can result in issues when converting to other formats (especially with stepping anims).
morph targets are not supported.
total nodes(+joints) must be < MAX_BONES, and ideally <MAX_GPU_BONES too but it is sufficient for that to be per-mesh.
meshes:
multiple texture coord sets are not supported.
additional colours/weights attributes are not supported.
multiple meshes with the same material will not be merged.
scene:
cameras can be parsed, but are not necessarily useful as they are not exposed to the gamecode.
extensions:
no KHR_draco_mesh_compression
unknown extensions will result in warning spam for each occurence.
gltf1 is NOT supported, only gltf2.
*/
//'The units for all linear distances are meters.' //'The units for all linear distances are meters.'
//'feh: 1 metre is approx. 26.24671916 qu.' //'feh: 1 metre is approx. 26.24671916 qu.'
@ -536,9 +564,6 @@ static size_t JSON_ReadBody(json_t *t, char *out, size_t outsize)
//glTF 1.0 and 2.0 differ in that 1 uses names and 2 uses indexes. There's also some significant differences with materials. //glTF 1.0 and 2.0 differ in that 1 uses names and 2 uses indexes. There's also some significant differences with materials.
//we only support 2.0 //we only support 2.0
//articulated models are handled by loading them as skeletal (should probably optimise the engine for this usecase)
//we don't support skeletal models either right now.
//buffers are raw blobs that can come from multiple different sources //buffers are raw blobs that can come from multiple different sources
struct gltf_buffer struct gltf_buffer
{ {
@ -820,6 +845,7 @@ static void GLTF_AccessorToTangents(gltf_t *gltf, vec3_t *norm, vec3_t **sdir, v
char *in = a->data; char *in = a->data;
size_t v, c; size_t v, c;
float side;
*sdir = os; *sdir = os;
*tdir = ot; *tdir = ot;
if ((a->type&0xff) != 4) if ((a->type&0xff) != 4)
@ -833,9 +859,37 @@ static void GLTF_AccessorToTangents(gltf_t *gltf, vec3_t *norm, vec3_t **sdir, v
memset(os, 0, sizeof(*os) * outverts); memset(os, 0, sizeof(*os) * outverts);
memset(ot, 0, sizeof(*ot) * outverts); memset(ot, 0, sizeof(*ot) * outverts);
break; break;
// case 5120: //BYTE case 5120: //BYTE KHR_mesh_quantization (always normalized)
for (v = 0; v < outverts; v++)
{
for (c = 0; c < 3; c++)
os[v][c] = max(-1.0, ((signed char*)in)[c] / 127.0); //negative values are larger, but we want to allow 1.0
side = max(-1.0, ((signed char*)in)[3] / 127.0);
//bitangent = cross(normal, tangent.xyz) * tangent.w
ot[v][0] = (norm[v][1]*os[v][2] - norm[v][2]*os[v][1]) * side;
ot[v][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * side;
ot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * side;
in += a->bytestride;
}
break;
// case 5121: //UNSIGNED_BYTE // case 5121: //UNSIGNED_BYTE
// case 5122: //SHORT case 5122: //SHORT KHR_mesh_quantization (always normalized)
for (v = 0; v < outverts; v++)
{
for (c = 0; c < 3; c++)
os[v][c] = max(-1.0, ((signed short*)in)[c] / 32767.0);
side = max(-1.0, ((signed short*)in)[3] / 32767.0);
//bitangent = cross(normal, tangent.xyz) * tangent.w
ot[v][0] = (norm[v][1]*os[v][2] - norm[v][2]*os[v][1]) * side;
ot[v][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * side;
ot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * side;
in += a->bytestride;
}
break;
// case 5123: //UNSIGNED_SHORT // case 5123: //UNSIGNED_SHORT
// case 5125: //UNSIGNED_INT // case 5125: //UNSIGNED_INT
case 5126: //FLOAT case 5126: //FLOAT
@ -843,17 +897,19 @@ static void GLTF_AccessorToTangents(gltf_t *gltf, vec3_t *norm, vec3_t **sdir, v
{ {
for (c = 0; c < 3; c++) for (c = 0; c < 3; c++)
os[v][c] = ((float*)in)[c]; os[v][c] = ((float*)in)[c];
side = ((float*)in)[3];
//bitangent = cross(normal, tangent.xyz) * tangent.w //bitangent = cross(normal, tangent.xyz) * tangent.w
ot[v][0] = (norm[v][1]*os[v][2] - norm[v][2]*os[v][1]) * ((float*)in)[3]; ot[v][0] = (norm[v][1]*os[v][2] - norm[v][2]*os[v][1]) * side;
ot[v][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * ((float*)in)[3]; ot[v][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * side;
ot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * ((float*)in)[3]; ot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * side;
in += a->bytestride; in += a->bytestride;
} }
break; break;
} }
} }
static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int outcomponents, struct gltf_accessor *a) static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int outcomponents, struct gltf_accessor *a)
{ {
float *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o; float *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o;
@ -872,17 +928,46 @@ static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int ou
memset(ret, 0, sizeof(*ret) * outcomponents * outverts); memset(ret, 0, sizeof(*ret) * outcomponents * outverts);
break; break;
case 5120: //BYTE case 5120: //BYTE
if (!a->normalized)
{ //KHR_mesh_quantization
while(outverts --> 0) while(outverts --> 0)
{ {
for (c = 0; c < ic; c++) for (c = 0; c < ic; c++)
o[c] = max(-1.0, ((char*)in)[c] / 127.0); //negative values are larger, but we want to allow 1.0 o[c] = ((signed char*)in)[c];
for (; c < outcomponents; c++) for (; c < outcomponents; c++)
o[c] = 0; o[c] = 0;
o += outcomponents; o += outcomponents;
in += a->bytestride; in += a->bytestride;
} }
}
else
{
while(outverts --> 0)
{
for (c = 0; c < ic; c++)
o[c] = max(-1.0, ((signed char*)in)[c] / 127.0); //negative values are larger, but we want to allow 1.0
for (; c < outcomponents; c++)
o[c] = 0;
o += outcomponents;
in += a->bytestride;
}
}
break; break;
case 5121: //UNSIGNED_BYTE case 5121: //UNSIGNED_BYTE
if (!a->normalized)
{ //KHR_mesh_quantization
while(outverts --> 0)
{
for (c = 0; c < ic; c++)
o[c] = ((unsigned char*)in)[c];
for (; c < outcomponents; c++)
o[c] = 0;
o += outcomponents;
in += a->bytestride;
}
}
else
{
while(outverts --> 0) while(outverts --> 0)
{ {
for (c = 0; c < ic; c++) for (c = 0; c < ic; c++)
@ -892,8 +977,23 @@ static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int ou
o += outcomponents; o += outcomponents;
in += a->bytestride; in += a->bytestride;
} }
}
break; break;
case 5122: //SHORT case 5122: //SHORT
if (!a->normalized)
{ //KHR_mesh_quantization
while(outverts --> 0)
{
for (c = 0; c < ic; c++)
o[c] = ((signed short*)in)[c];
for (; c < outcomponents; c++)
o[c] = 0;
o += outcomponents;
in += a->bytestride;
}
}
else
{
while(outverts --> 0) while(outverts --> 0)
{ {
for (c = 0; c < ic; c++) for (c = 0; c < ic; c++)
@ -903,8 +1003,23 @@ static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int ou
o += outcomponents; o += outcomponents;
in += a->bytestride; in += a->bytestride;
} }
}
break; break;
case 5123: //UNSIGNED_SHORT case 5123: //UNSIGNED_SHORT
if (!a->normalized)
{ //KHR_mesh_quantization
while(outverts --> 0)
{
for (c = 0; c < ic; c++)
o[c] = ((unsigned short*)in)[c];
for (; c < outcomponents; c++)
o[c] = 0;
o += outcomponents;
in += a->bytestride;
}
}
else
{
while(outverts --> 0) while(outverts --> 0)
{ {
for (c = 0; c < ic; c++) for (c = 0; c < ic; c++)
@ -914,8 +1029,23 @@ static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int ou
o += outcomponents; o += outcomponents;
in += a->bytestride; in += a->bytestride;
} }
}
break; break;
case 5125: //UNSIGNED_INT case 5125: //UNSIGNED_INT
if (!a->normalized)
{ //?!?!?!?!
while(outverts --> 0)
{
for (c = 0; c < ic; c++)
o[c] = ((unsigned int*)in)[c];
for (; c < outcomponents; c++)
o[c] = 0;
o += outcomponents;
in += a->bytestride;
}
}
else
{
while(outverts --> 0) while(outverts --> 0)
{ {
for (c = 0; c < ic; c++) for (c = 0; c < ic; c++)
@ -925,6 +1055,7 @@ static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int ou
o += outcomponents; o += outcomponents;
in += a->bytestride; in += a->bytestride;
} }
}
break; break;
case 5126: //FLOAT case 5126: //FLOAT
while(outverts --> 0) while(outverts --> 0)
@ -997,15 +1128,18 @@ static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_
if (ic > outcomponents) if (ic > outcomponents)
ic = outcomponents; ic = outcomponents;
o = ret; o = ret;
if (a->normalized)
if (gltf->warnlimit --> 0)
Con_Printf(CON_WARNING"GLTF_AccessorToDataBone: %s: normalised input\n", gltf->mod->name);
switch(a->componentType) switch(a->componentType)
{ {
default: default:
if (gltf->warnlimit --> 0) if (gltf->warnlimit --> 0)
Con_Printf(CON_WARNING"GLTF_AccessorToDataUB: %s: glTF2 unsupported componentType (%i)\n", gltf->mod->name, a->componentType); Con_Printf(CON_WARNING"GLTF_AccessorToDataBone: %s: glTF2 unsupported componentType (%i)\n", gltf->mod->name, a->componentType);
case 0: case 0:
memset(ret, 0, sizeof(*ret) * outcomponents * outverts); memset(ret, 0, sizeof(*ret) * outcomponents * outverts);
break; break;
// case 5120: //BYTE case 5120: //BYTE - should not be negative, so ignore sign bit
case 5121: //UNSIGNED_BYTE case 5121: //UNSIGNED_BYTE
while(outverts --> 0) while(outverts --> 0)
{ {
@ -1021,7 +1155,7 @@ static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_
in += a->bytestride; in += a->bytestride;
} }
break; break;
case 5122: //SHORT case 5122: //SHORT - should not be negative, so ignore sign bit
case 5123: //UNSIGNED_SHORT case 5123: //UNSIGNED_SHORT
while(outverts --> 0) while(outverts --> 0)
{ {
@ -1256,8 +1390,10 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
JSON_ReadBody(nam, ret->frame->shadername, sizeof(ret->frame->shadername)); JSON_ReadBody(nam, ret->frame->shadername, sizeof(ret->frame->shadername));
else if (mat) else if (mat)
JSON_GetPath(mat, false, ret->frame->shadername, sizeof(ret->frame->shadername)); JSON_GetPath(mat, false, ret->frame->shadername, sizeof(ret->frame->shadername));
else if (material == -1) //explicit invalid material
Q_snprintf(ret->frame->shadername, sizeof(ret->frame->shadername), "%s", gltf->mod->name);
else else
Q_snprintf(ret->frame->shadername, sizeof(ret->frame->shadername), "%i", material); Q_snprintf(ret->frame->shadername, sizeof(ret->frame->shadername), "%.100s/%i", gltf->mod->name, material);
if (alphaMode == 1) if (alphaMode == 1)
Q_snprintf(alphaCutoffmodifier, sizeof(alphaCutoffmodifier), "#ALPHATEST=>%f", alphaCutoff); Q_snprintf(alphaCutoffmodifier, sizeof(alphaCutoffmodifier), "#ALPHATEST=>%f", alphaCutoff);
@ -1374,8 +1510,9 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
JSON_GetFloat(mat, "emissiveFactor.2", 0) JSON_GetFloat(mat, "emissiveFactor.2", 0)
); );
} }
else if (pbrmr) else// if (pbrmr)
{ //this is the standard lighting model for gltf2 { //this is the standard lighting model for gltf2
//'When not specified, all the default values of pbrMetallicRoughness apply'
int albedo = JSON_GetInteger(pbrmr, "baseColorTexture.index", -1); //.rgba int albedo = JSON_GetInteger(pbrmr, "baseColorTexture.index", -1); //.rgba
int mrt = JSON_GetInteger(pbrmr, "metallicRoughnessTexture.index", -1); //.r = unused, .g = roughness, .b = metalic, .a = unused int mrt = JSON_GetInteger(pbrmr, "metallicRoughnessTexture.index", -1); //.r = unused, .g = roughness, .b = metalic, .a = unused
int occ = JSON_GetInteger(mat, "occlusionTexture.index", -1); //.r int occ = JSON_GetInteger(mat, "occlusionTexture.index", -1); //.r
@ -2197,16 +2334,18 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
static struct static struct
{ {
const char *name; const char *name;
qboolean supported; //unsupported extensions don't really need to be listed, but they do prevent warnings from unkown-but-used extensions qboolean supported; //unsupported extensions don't really need to be listed, but they do prevent warnings from unknown-but-used extensions
qboolean draft; //true when our implementation is probably buggy on account of the spec maybe changing.
} extensions[] = } extensions[] =
{ {
{"KHR_materials_pbrSpecularGlossiness", true}, {"KHR_materials_pbrSpecularGlossiness", true, false},
//draft {"KHR_materials_cmnBlinnPhong", true}, // {"KHR_materials_cmnBlinnPhong", true, true},
{"KHR_materials_unlit", true}, {"KHR_materials_unlit", true, false},
{"KHR_texture_transform", false}, {"KHR_texture_transform", false, true}, //probably not fatal
{"KHR_draco_mesh_compression", false}, {"KHR_draco_mesh_compression", false, true}, //probably fatal
{"MSFT_texture_dds", true}, {"KHR_mesh_quantization", true, true},
{"MSFT_packing_occlusionRoughnessMetallic", true}, {"MSFT_texture_dds", true, false},
{"MSFT_packing_occlusionRoughnessMetallic", true, false},
}; };
gltf_t gltf; gltf_t gltf;
int pos=0, j, k; int pos=0, j, k;
@ -2267,6 +2406,8 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
} }
if (j==countof(extensions) || !extensions[j].supported) if (j==countof(extensions) || !extensions[j].supported)
Con_Printf(CON_WARNING "%s: gltf2 extension \"%s\" not known\n", mod->name, extname); Con_Printf(CON_WARNING "%s: gltf2 extension \"%s\" not known\n", mod->name, extname);
else if (extensions[j].draft)
Con_Printf(CON_WARNING "%s: gltf2 extension \"%s\" follows draft implementation, and may be non-standard/buggy\n", mod->name, extname);
} }
VectorClear(mod->maxs); VectorClear(mod->maxs);