From 73f92fb7998a56e5648fc71c3e12660ae000b84f Mon Sep 17 00:00:00 2001 From: Spoike Date: Fri, 10 Jan 2020 12:23:25 +0000 Subject: [PATCH] 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 --- CMakeLists.txt | 8 +- engine/client/cl_parse.c | 2 +- engine/client/r_d3.c | 6 +- engine/common/gl_q2bsp.c | 13 +- engine/gl/gl_backend.c | 7 +- engine/gl/gl_model.c | 6 +- engine/qclib/execloop.h | 3 +- engine/server/pr_cmds.c | 64 ++++--- engine/server/server.h | 1 + engine/server/sv_send.c | 6 +- engine/server/svq2_game.c | 2 +- imgtool.c | 374 +++++++++++++++++++++++++++++++------ plugins/Makefile | 2 +- plugins/avplug/avencode.c | 43 +++-- plugins/ezhud/hud_common.c | 2 +- plugins/models/gltf.c | 257 +++++++++++++++++++------ 16 files changed, 621 insertions(+), 175 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1715de4f..a7ad33946 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ IF (NOT "${FTE_REVISON}" STREQUAL "") OUTPUT_VARIABLE FTE_DATE ) - SET(FTE_REVISON SVNREVISION="${FTE_REVISON}" SVNDATE="${FTE_DATE}") + SET(FTE_REVISON SVNREVISION=${FTE_REVISON} SVNDATE="${FTE_DATE}") ENDIF() ENDIF() @@ -91,7 +91,7 @@ IF(BZIP2_FOUND) SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};AVAIL_BZLIB;BZLIB_STATIC) SET(FTE_LIBS ${FTE_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() MESSAGE(WARNING "bzip2 library NOT available. bz2-compressed pk3s will not be available, as if anyone cares.") ENDIF() @@ -262,6 +262,10 @@ ELSEIF(${UNIX}) #linux(ish) SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_CURSOR) MESSAGE(WARNING "Xcursor library NOT available.") ENDIF() + IF (NOT X11_Xrandr_FOUND) + SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_RANDR) + MESSAGE(WARNING "Xrandr library NOT available.") + ENDIF() ELSE() MESSAGE(WARNING "x11 library NOT available.") SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 741d642d9..871124c3e 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -4461,7 +4461,7 @@ static void CLQ2_ParseConfigString (void) } 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) { diff --git a/engine/client/r_d3.c b/engine/client/r_d3.c index a440cb444..a9baf29ff 100644 --- a/engine/client/r_d3.c +++ b/engine/client/r_d3.c @@ -236,9 +236,9 @@ static qboolean Mod_LoadMap_Proc(model_t *model, char *data) b[surf].lightmap[2] = -1; b[surf].lightmap[3] = -1; b[surf].lmlightstyle[0] = 0; - b[surf].lmlightstyle[1] = 255; - b[surf].lmlightstyle[2] = 255; - b[surf].lmlightstyle[3] = 255; + b[surf].lmlightstyle[1] = INVALID_LIGHTSTYLE; + b[surf].lmlightstyle[2] = INVALID_LIGHTSTYLE; + b[surf].lmlightstyle[3] = INVALID_LIGHTSTYLE; data = COM_ParseOut(data, token, sizeof(token)); b[surf].shader = R_RegisterShader_Vertex(token); diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index ffb30b125..7c9bd97d7 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -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_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++) { - out->styles[sty] = 255; + out->styles[sty] = INVALID_LIGHTSTYLE; + out->vlstyles[sty] = 255; out->lightmaptexturenums[sty] = -1; } + for (; sty < MAXQ1LIGHTMAPS; sty++) + out->styles[sty] = INVALID_LIGHTSTYLE; out->lmshift = LMSHIFT_DEFAULT; //fixme: determine texturemins from lightmap_origin out->extents[0] = (LittleLong(in->lightmap_width)-1)<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->light_s[j] = LittleLong(in->lightmap_offs[0][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) mod->lightmaps.count = out->lightmaptexturenums[j]+1; } + for (; j < MAXQ1LIGHTMAPS; j++) + out->styles[j] = INVALID_LIGHTSTYLE; if (facetype == MST_FLARE) out->texinfo = mod->texinfo + mod->numtexinfo*2; else if (out->lightmaptexturenums[0]<0 || r_vertexlight.value) diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 52d10856e..13ff09b5a 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -3641,7 +3641,7 @@ static void BE_Program_Set_Attributes(const program_t *prog, struct programpermu for (j = 0; j < MAXRLIGHTMAPS ; j++) { s = shaderstate.curbatch->lmlightstyle[j]; - if (s == 255) + if (s == INVALID_LIGHTSTYLE) { for (; j < MAXRLIGHTMAPS ; j++) { @@ -3672,6 +3672,7 @@ static void BE_Program_Set_Attributes(const program_t *prog, struct programpermu else #endif { + unsigned short s; if (shaderstate.curentity->model && (shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT) && !shaderstate.force2d) { float sc = (1<lmlightstyle[0]; //only one style. + if (s != INVALID_LIGHTSTYLE) + VectorScale(param4, d_lightstylevalue[s]/256.0f, param4); + qglUniform4fvARB(ph, 1, (GLfloat*)param4); } break; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index a4b947a27..8b3bc8a27 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -161,10 +161,8 @@ static void Mod_BatchList_f(void) 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]); else - if (batch->lightmap[1] >= 0) -#else - if (batch->lmlightstyle[0] != 255) #endif + if (batch->lmlightstyle[0] != INVALID_LIGHTSTYLE) Con_Printf("^2 lm=(%i:%i)", batch->lightmap[0], batch->lmlightstyle[0]); else 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++) { 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. } diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 2afd6f205..a82573eb9 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -730,7 +730,8 @@ reeval: s = PR_LeaveFunction (progfuncs); st = &pr_statements[s]; if (prinst.pr_depth == prinst.exitdepth) - { + { + prinst.pr_xstatement = s; return -1; // all done } return s; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index f877f4dbd..2cfb2d364 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1552,7 +1552,7 @@ void PR_Init(void) Cmd_AddCommand ("extensionlist_ssqc", PR_SVExtensionList_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 @@ -4721,30 +4721,52 @@ static void QCBUILTIN PF_lightstyle (pubprogfuncs_t *prinst, struct globalvars_s static void PR_Lightstyle_f(void) { 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"); - else if (!SV_MayCheat()) + + if (!SV_MayCheat()) Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n"); - else if (Cmd_Argc() <= 2) + else switch(svs.gametype) { - 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]); - else for (style = 0; style < sv.maxlightstyles; style++) - if (sv.lightstyles[style].str) - 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]); - } - else - { - vec3_t rgb = {1,1,1}; - if (Cmd_Argc() > 5) + default: + Con_TPrintf ("not supported in the current game mode.\n"); + break; +#ifdef Q2SERVER + case GT_QUAKE2: + if (Cmd_Argc() <= 2) { - rgb[0] = atof(Cmd_Argv(3)); - rgb[1] = atof(Cmd_Argv(4)); - rgb[2] = atof(Cmd_Argv(5)); + 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 (Cmd_Argc() > 3) - rgb[0] = rgb[1] = rgb[2] = atof(Cmd_Argv(3)); - PF_applylightstyle(style, Cmd_Argv(2), rgb); + 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) + 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]); + else for (style = 0; style < sv.maxlightstyles; style++) + if (sv.lightstyles[style].str) + 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]); + } + else + { + vec3_t rgb = {1,1,1}; + if (Cmd_Argc() > 5) + { + rgb[0] = atof(Cmd_Argv(3)); + rgb[1] = atof(Cmd_Argv(4)); + rgb[2] = atof(Cmd_Argv(5)); + } + else if (Cmd_Argc() > 3) + rgb[0] = rgb[1] = rgb[2] = atof(Cmd_Argv(3)); + PF_applylightstyle(style, Cmd_Argv(2), rgb); + } + break; } } diff --git a/engine/server/server.h b/engine/server/server.h index 2a281256b..4d84e00fb 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -1270,6 +1270,7 @@ qboolean SV_FilterImpulse(int imp, int level); //svq2_game.c qboolean SVQ2_InitGameProgs(void); void VARGS SVQ2_ShutdownGameProgs (void); +void VARGS PFQ2_Configstring (int i, const char *val); //for engine cheats. //svq2_ents.c void SVQ2_BuildClientFrame (client_t *client); diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 7eecef996..ab8482ac7 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -1538,7 +1538,7 @@ void SV_SendLightstyle(client_t *cl, sizebuf_t *forcemsg, int style, qboolean in 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 ((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]); if (forcemsg) @@ -1551,7 +1551,9 @@ void SV_SendLightstyle(client_t *cl, sizebuf_t *forcemsg, int style, qboolean in ClientReliable_FinishWrite(cl); 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) diff --git a/engine/server/svq2_game.c b/engine/server/svq2_game.c index c06dbb9e2..3e45e296f 100644 --- a/engine/server/svq2_game.c +++ b/engine/server/svq2_game.c @@ -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) Sys_Error ("configstring: bad index %i\n", i); diff --git a/imgtool.c b/imgtool.c index e7f3e880b..6548cf9d3 100644 --- a/imgtool.c +++ b/imgtool.c @@ -4,6 +4,7 @@ #define stderr stdout #include +#include #ifdef _WIN32 #include #endif @@ -348,11 +349,29 @@ static void ImgTool_FreeMips(struct pendingtextureinfo *mips) sh_config_t sh_config; 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 { 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 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. @@ -376,23 +395,16 @@ static enum uploadfmt ImgTool_ASTCToLDR(uploadfmt_t fmt) return fmt; } #ifdef _WIN32 -#include -#include static void FS_MakeTempName(char *out, size_t outsize, char *prefix, char *suffix) { - int fd; - unsigned int n; - unsigned int s = rand(); - for (n = 0; n < 0xffffff; n++) - { - 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"); + static char temp_path[MAX_PATH]; + char temp_file_name[MAX_PATH]; + if (!*temp_path && !GetTempPathA(sizeof(temp_path), temp_path)) + Sys_Error("FS_MakeTempName failed to get temp path\n"); + if (!GetTempFileNameA(temp_path, prefix, 0, temp_file_name)) + Sys_Error("FS_MakeTempName failed\n"); + + Q_snprintfz(out, outsize, "%s%s", temp_file_name, suffix); } #else #include @@ -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 } #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) { struct pendingtextureinfo tmp, *ret; @@ -415,10 +479,10 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna int bb,bw,bh; qboolean canktx = false; 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. - 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; 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) 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) 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) 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) 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) Q_snprintfz(command, sizeof(command), "nvcompress -bc4"); 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"); else if (targfmt == PTI_BC7_RGBA) Q_snprintfz(command, sizeof(command), "nvcompress -bc7"); + else if (targfmt == PTI_BC7_RGBA_SRGB) + Q_snprintfz(command, sizeof(command), "nvcompress -bc7 -srgb"); else { 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)); 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, ">> /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) { @@ -549,7 +627,12 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna 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); 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 == '.') return dot; } - return ""; + return term+strlen(term); } 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) { size_t k; - const char *outext = COM_GetFileExtension(outname, NULL); + const char *outext; 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")) allowcompressed = true; @@ -918,7 +1013,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, /*(k == PTI_L16) ||*/ (k == PTI_BGR8) || (k == PTI_BGR8) || 0; - if (!sh_config.texfmt[in->encoding]) + if (!outformats[in->encoding]) { Image_ChangeFormat(in, outformats, PTI_INVALID, outname); 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); if (!indata) 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 { in = Image_LoadMipsFromMemory(args->flags, inname, inname, indata, fsize); @@ -996,6 +1120,7 @@ struct filelist_s const char **exts; size_t numfiles; struct { + const char *rootpath; //the basepath that was passed to the filelist scan. char *name; size_t baselen; //length up to but not including the filename extension. } *file; @@ -1010,7 +1135,7 @@ static void FileList_Release(struct filelist_s *list) list->numfiles = 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 baselen; @@ -1036,15 +1161,47 @@ static void FileList_Add(struct filelist_s *list, char *fname) list->maxfiles += 64; list->file = realloc(list->file, sizeof(*list->file)*list->maxfiles); } + list->file[i].rootpath = rootpath; list->file[i].name = strdup(fname); list->file[i].baselen = baselen; list->numfiles++; } #ifdef _WIN32 -static void ImgTool_TreeScan(struct filelist_s *list, const char *basepath, const char *subpath) -{ - (void)FileList_Add; - Con_Printf("ImgTool_TreeScan not implemented on windows.\n"); +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) + { + do + { + 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 #include @@ -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); else Q_snprintfz(file, sizeof(file), "%s", ent->d_name); - FileList_Add(list, file); + FileList_Add(list, basepath, file); } } closedir(dir); } #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; char file[MAX_OSPATH]; @@ -1145,25 +1302,55 @@ static void ImgTool_TreeConvert(struct opts_s *args, const char *srcpath, const - - -typedef struct +static void ImgTool_WadExtract(struct opts_s *args, const char *wadname) { - 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 void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const char *destpath, int wadtype/*x,2,3*/) + qbyte *indata; + size_t fsize; + size_t m; + indata = FS_LoadMallocFile(wadname, &fsize); + if (!indata) + printf("%s: unable to read\n", wadname); + 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); + + for (m = 0; m < w->num; m++, e++) + { + 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]; 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; qboolean wadpixelformats[PTI_MAX] = {0}; 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); 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++) 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++) { - 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; if (list.file[i].baselen > 15) { @@ -1204,7 +1417,9 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c continue; } 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); Image_GenerateMips(in, args->flags); @@ -1276,7 +1491,14 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c entry->name[list.file[i].baselen] = 0; //kill any .tga 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->type = TYP_MIPTEX; + 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->cmprs = 0; entry->dummy = 0; entry->offset = VFS_TELL(f); @@ -1326,6 +1548,7 @@ int main(int argc, const char **argv) mode_genwadx, mode_genwad2, mode_genwad3, + mode_extractwad, } mode = mode_info; size_t u, f; qboolean nomoreopts = false; @@ -1338,6 +1561,7 @@ int main(int argc, const char **argv) args.newpixelformat = PTI_INVALID; args.mipnum = 0; args.textype = -1; + args.defaultext = NULL; sh_config.texture2d_maxsize = 1u<<31; sh_config.texture3d_maxsize = 1u<<31; @@ -1364,13 +1588,14 @@ int main(int argc, const char **argv) else if (!strcmp(argv[u], "-?") || !strcmp(argv[u], "--help")) { showhelp: - Con_Printf("show info : %s -i *.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("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("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("show info : %s -i *.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("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 --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("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(); Con_Printf("Supported compressed/interesting pixelformats are:\n"); @@ -1426,6 +1651,8 @@ showhelp: mode = mode_genwad2; else if (!files && (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwadx"))) mode = mode_genwadx; + else if (!files && (!strcmp(argv[u], "-x") || !strcmp(argv[u], "--extractwad"))) + mode = mode_extractwad; else if (!strcmp(argv[u], "--2d")) args.textype = PTI_2D; else if (!strcmp(argv[u], "--3d")) @@ -1444,6 +1671,16 @@ showhelp: args.flags |= IF_PREMULTIPLYALPHA; else if (!strcmp(argv[u], "--nopremul")) 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")) { char *e = "erk"; @@ -1480,6 +1717,14 @@ showhelp: 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) { //just print info about each listed file. for (u = 0; u < files; u++) @@ -1500,10 +1745,15 @@ showhelp: } else if (mode == mode_autotree && files == 2) ImgTool_TreeConvert(&args, argv[0], argv[1]); - else if ((mode == mode_genwad2 || mode == mode_genwad3 || mode == mode_genwadx) && files == 2) - ImgTool_WadConvert(&args, argv[0], argv[1], mode-mode_genwadx); + else if ((mode == mode_genwad2 || mode == mode_genwad3 || 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 + { + printf("%u files\n", (int)files); + printf("unsupported arg count for mode\n"); return EXIT_FAILURE; - + } return EXIT_SUCCESS; } diff --git a/plugins/Makefile b/plugins/Makefile index cbe7d19af..d6054cd48 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -326,6 +326,6 @@ NATIVE_PLUGINS+=ezhud $(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) -#NATIVE_PLUGINS+=models +NATIVE_PLUGINS+=models all: $(foreach FOO,$(NATIVE_PLUGINS), $(PLUG_PREFIX)$(FOO)$(PLUG_NATIVE_EXT)) diff --git a/plugins/avplug/avencode.c b/plugins/avplug/avencode.c index 73de973c2..36f828709 100644 --- a/plugins/avplug/avencode.c +++ b/plugins/avplug/avencode.c @@ -116,17 +116,19 @@ static AVStream *add_video_stream(struct encctx *ctx, AVCodec *codec, int fps, i int forcewidth = ffmpeg_videoforcewidth->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); if (!st) return NULL; - st->id = ctx->fc->nb_streams-1; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101) c = st->codec; -#else - c = avcodec_alloc_context3(codec); - if(avcodec_parameters_to_context(c, st->codecpar)) - return NULL; + st->id = ctx->fc->nb_streams-1; #endif ctx->video_codec = c; 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) av_opt_set(c->priv_data, "crf", ffmpeg_video_crf->string, AV_OPT_SEARCH_CHILDREN); - return st; } 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; int bitrate = ffmpeg_audiobitrate->value; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101) st = avformat_new_stream(ctx->fc, codec); if (!st) return NULL; - st->id = ctx->fc->nb_streams-1; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101) c = st->codec; #else + st = avformat_new_stream(ctx->fc, NULL); + if (!st) + return NULL; + c = avcodec_alloc_context3(codec); if(avcodec_parameters_to_context(c, st->codecpar)) return NULL; #endif + st->id = ctx->fc->nb_streams-1; ctx->audio_codec = c; c->codec_id = codec->id; 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); if (!ctx->video_outbuf) 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) { @@ -717,9 +732,6 @@ static void AVEnc_End (void *vctx) } #endif - close_video(ctx); - close_audio(ctx); - //don't write trailers if this is an error case and we never even wrote the headers. if (ctx->doneheaders) { @@ -728,12 +740,15 @@ static void AVEnc_End (void *vctx) Con_Printf("Finished writing %s\n", ctx->abspath); } + close_video(ctx); + close_audio(ctx); + for(i = 0; i < ctx->fc->nb_streams; i++) av_freep(&ctx->fc->streams[i]); // if (!(fmt->flags & AVFMT_NOFILE)) avio_close(ctx->fc->pb); av_free(ctx->audio_outbuf); - av_free(ctx->fc); + avformat_free_context(ctx->fc); free(ctx); } static media_encoder_funcs_t encoderfuncs = @@ -741,7 +756,7 @@ static media_encoder_funcs_t encoderfuncs = sizeof(media_encoder_funcs_t), "ffmpeg", "Use ffmpeg's various codecs. Various settings are configured with the "ENCODERNAME"_* cvars.", - ".mp4", + ".mp4;.*", AVEnc_Begin, AVEnc_Video, AVEnc_Audio, diff --git a/plugins/ezhud/hud_common.c b/plugins/ezhud/hud_common.c index c98bbad6c..72abb316e 100644 --- a/plugins/ezhud/hud_common.c +++ b/plugins/ezhud/hud_common.c @@ -42,7 +42,7 @@ along with this program. If not, see . #include "ezquakeisms.h" #include "hud.h" -//#define WITH_PNG +//#define WITH_PNG //more WITH_RADAR than anything else. #define draw_disc draw_disc2 diff --git a/plugins/models/gltf.c b/plugins/models/gltf.c index 5fbf96c84..bc0c82c10 100644 --- a/plugins/models/gltf.c +++ b/plugins/models/gltf.c @@ -1,5 +1,5 @@ #ifndef GLQUAKE -//#define GLQUAKE //this is shit. +#define GLQUAKE //this is shit, but ensures index sizes come out the right size #endif #include "quakedef.h" #include "../plugin.h" @@ -7,10 +7,38 @@ extern plugmodfuncs_t *modfuncs; extern plugfsfuncs_t *filefuncs; +#if MAX_INDICIES == 0xffffu +#warning 16bit indexes +#else +#warning 32bit indexes +#endif + #ifdef SKELETALMODELS #define GLTFMODELS #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 data; size_t v, c; + float side; *sdir = os; *tdir = ot; 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(ot, 0, sizeof(*ot) * outverts); break; -// case 5120: //BYTE -// case 5121: //UNSIGNED_BYTE -// case 5122: //SHORT + 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 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 5125: //UNSIGNED_INT 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++) os[v][c] = ((float*)in)[c]; + side = ((float*)in)[3]; //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][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * ((float*)in)[3]; - ot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * ((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]) * side; + ot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * side; in += a->bytestride; } break; } } + 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; @@ -872,58 +928,133 @@ static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int ou memset(ret, 0, sizeof(*ret) * outcomponents * outverts); break; case 5120: //BYTE - while(outverts --> 0) + if (!a->normalized) + { //KHR_mesh_quantization + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((signed char*)in)[c]; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + } + else { - 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 - for (; c < outcomponents; c++) - o[c] = 0; - o += outcomponents; - in += a->bytestride; + 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; case 5121: //UNSIGNED_BYTE - while(outverts --> 0) + 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 { - for (c = 0; c < ic; c++) - o[c] = ((unsigned char*)in)[c] / 255.0; - for (; c < outcomponents; c++) - o[c] = 0; - o += outcomponents; - in += a->bytestride; + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((unsigned char*)in)[c] / 255.0; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } } break; case 5122: //SHORT - while(outverts --> 0) + 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 { - for (c = 0; c < ic; c++) - o[c] = max(-1.0, ((signed short*)in)[c] / 32767.0); //negative values are larger, but we want to allow 1.0 - for (; c < outcomponents; c++) - o[c] = 0; - o += outcomponents; - in += a->bytestride; + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = max(-1.0, ((signed short*)in)[c] / 32767.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; case 5123: //UNSIGNED_SHORT - while(outverts --> 0) + 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 { - for (c = 0; c < ic; c++) - o[c] = ((unsigned short*)in)[c] / 65535.0; - for (; c < outcomponents; c++) - o[c] = 0; - o += outcomponents; - in += a->bytestride; + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((unsigned short*)in)[c] / 65535.0; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } } break; case 5125: //UNSIGNED_INT - while(outverts --> 0) + 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 { - for (c = 0; c < ic; c++) - o[c] = ((unsigned int*)in)[c] / (double)~0u; //stupid format to use. will be lossy. - for (; c < outcomponents; c++) - o[c] = 0; - o += outcomponents; - in += a->bytestride; + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((unsigned int*)in)[c] / (double)~0u; //stupid format to use. will be lossy. + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } } break; case 5126: //FLOAT @@ -997,15 +1128,18 @@ static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_ if (ic > outcomponents) ic = outcomponents; 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) { default: 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: memset(ret, 0, sizeof(*ret) * outcomponents * outverts); break; -// case 5120: //BYTE + case 5120: //BYTE - should not be negative, so ignore sign bit case 5121: //UNSIGNED_BYTE while(outverts --> 0) { @@ -1021,7 +1155,7 @@ static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_ in += a->bytestride; } break; - case 5122: //SHORT + case 5122: //SHORT - should not be negative, so ignore sign bit case 5123: //UNSIGNED_SHORT 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)); else if (mat) 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 - 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) 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) ); } - else if (pbrmr) + else// if (pbrmr) { //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 mrt = JSON_GetInteger(pbrmr, "metallicRoughnessTexture.index", -1); //.r = unused, .g = roughness, .b = metalic, .a = unused 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 { 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[] = { - {"KHR_materials_pbrSpecularGlossiness", true}, -//draft {"KHR_materials_cmnBlinnPhong", true}, - {"KHR_materials_unlit", true}, - {"KHR_texture_transform", false}, - {"KHR_draco_mesh_compression", false}, - {"MSFT_texture_dds", true}, - {"MSFT_packing_occlusionRoughnessMetallic", true}, + {"KHR_materials_pbrSpecularGlossiness", true, false}, +// {"KHR_materials_cmnBlinnPhong", true, true}, + {"KHR_materials_unlit", true, false}, + {"KHR_texture_transform", false, true}, //probably not fatal + {"KHR_draco_mesh_compression", false, true}, //probably fatal + {"KHR_mesh_quantization", true, true}, + {"MSFT_texture_dds", true, false}, + {"MSFT_packing_occlusionRoughnessMetallic", true, false}, }; gltf_t gltf; 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) 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);