From 1c1f04370f7681234d15f31c360427d03d921a2a Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 6 Oct 2019 01:59:13 +0000 Subject: [PATCH] preliminary .xcf support. imgtool tweaks. some build system fixups. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5560 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- CMakeLists.txt | 28 +- build_setup.sh | 12 +- engine/Makefile | 8 +- engine/client/cl_main.c | 2 +- engine/client/cl_pred.c | 1 + engine/client/image.c | 1276 ++++++++++++++++++++++++++----- engine/client/pr_csqc.c | 1 + engine/client/pr_skelobj.c | 2 +- engine/client/r_surf.c | 20 +- engine/client/render.h | 1 + engine/client/screen.h | 2 + engine/common/config_freecs.h | 1 + engine/common/config_fteqw.h | 1 + engine/common/config_minimal.h | 1 + engine/common/config_nocompat.h | 1 + engine/common/config_wastes.h | 1 + engine/common/fs.c | 17 +- engine/common/q1bsp.c | 4 +- engine/common/world.h | 2 + engine/d3d/d3d11_image.c | 7 +- engine/gl/gl_draw.c | 11 +- engine/gl/gl_warp.c | 2 + engine/gl/glsupp.h | 5 + engine/server/pr_cmds.c | 7 +- engine/vk/vk_init.c | 3 + imgtool.c | 366 +++++++-- plugins/bullet/bulletplug.cpp | 2 +- 27 files changed, 1483 insertions(+), 301 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a5765440..6ad0ef7c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,15 +61,15 @@ IF(ZLIB_FOUND) SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};ZLIB_STATIC) SET(FTE_LIBS ${FTE_LIBS} ${ZLIB_LIBRARIES}) SET(FTESV_LIBS ${FTESV_LIBS} ${ZLIB_LIBRARIES}) + SET(FTEQCC_LIBS ${FTEQCC_LIBS} ${ZLIB_LIBRARIES}) ELSE() MESSAGE(WARNING "libz library NOT available. compressed pk3 will not be available.") SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_ZLIB) - SET(ZLIB_LIBRARIES ) ENDIF() FIND_PACKAGE(BZip2) IF(BZIP2_FOUND) - SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};AVAIL_BZLIB) + 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!") @@ -723,7 +723,7 @@ ELSE() plugins/bullet/bulletplug.cpp ) TARGET_INCLUDE_DIRECTORIES(bullet PUBLIC ${BULLET_INCLUDE_DIRS}) - SET_TARGET_PROPERTIES(bullet PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN") + SET_TARGET_PROPERTIES(bullet PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES}") SET_TARGET_PROPERTIES(bullet PROPERTIES PREFIX "fteplug_") SET_TARGET_PROPERTIES(bullet PROPERTIES LINK_FLAGS "-Wl,--no-undefined") TARGET_LINK_LIBRARIES(bullet m ${BULLET_LIBRARIES}) @@ -767,13 +767,15 @@ ELSE() SET_TARGET_PROPERTIES(iqmtool PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON}") SET(INSTALLTARGS ${INSTALLTARGS} iqmtool) - ADD_EXECUTABLE(imgtool - engine/client/image.c - imgtool.c - ) - SET_TARGET_PROPERTIES(imgtool PROPERTIES COMPILE_DEFINITIONS "IMGTOOL;${FTE_LIB_DEFINES};${FTE_DEFINES};${FTE_REVISON}") - TARGET_LINK_LIBRARIES(imgtool ${FTE_LIBS} ) - SET(INSTALLTARGS ${INSTALLTARGS} imgtool) + IF(NOT WIN32) + ADD_EXECUTABLE(imgtool + engine/client/image.c + imgtool.c + ) + SET_TARGET_PROPERTIES(imgtool PROPERTIES COMPILE_DEFINITIONS "IMGTOOL;${FTE_LIB_DEFINES};${FTE_DEFINES};${FTE_REVISON}") + TARGET_LINK_LIBRARIES(imgtool ${FTE_LIBS} ) + SET(INSTALLTARGS ${INSTALLTARGS} imgtool) + ENDIF() ADD_EXECUTABLE(qtv fteqtv/netchan.c @@ -846,7 +848,7 @@ ELSE() engine/qclib/qcd_main.c ) SET_TARGET_PROPERTIES(fteqcc PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_REVISON}") - TARGET_LINK_LIBRARIES(fteqcc ${ZLIB_LIBRARIES} m) + TARGET_LINK_LIBRARIES(fteqcc ${FTEQCC_LIBS} m) SET(INSTALLTARGS ${INSTALLTARGS} fteqcc) IF(${WIN32}) @@ -864,7 +866,7 @@ ELSE() engine/qclib/qcd_main.c ) SET_TARGET_PROPERTIES(fteqccgui PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_REVISON}") - TARGET_LINK_LIBRARIES(fteqccgui ${ZLIB_LIBRARIES} shlwapi ole32 comctl32 comdlg32) + TARGET_LINK_LIBRARIES(fteqccgui ${FTEQCC_LIBS} shlwapi ole32 comctl32 comdlg32) SET(INSTALLTARGS ${INSTALLTARGS} fteqccgui) ELSE() FIND_PACKAGE(Qt5Widgets) @@ -900,7 +902,7 @@ ELSE() TARGET_INCLUDE_DIRECTORIES(fteqccgui PUBLIC ${Qt5Widgets_INCLUDE_DIRS} ${QSCINTILLA_INCLUDE_DIR}) SET_TARGET_PROPERTIES(fteqccgui PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_REVISON};${Qt5Widgets_COMPILE_DEFINITIONS}") SET_PROPERTY(TARGET fteqccgui PROPERTY POSITION_INDEPENDENT_CODE TRUE) - TARGET_LINK_LIBRARIES(fteqccgui ${ZLIB_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${QSCINTILLA_LIBRARY}) + TARGET_LINK_LIBRARIES(fteqccgui ${FTEQCC_LIBS} ${Qt5Widgets_LIBRARIES} ${QSCINTILLA_LIBRARY}) ELSE() MESSAGE(WARNING "qscintilla/qt5widgets library not detected, no fteqccgui for you") SET(INSTALLTARGS ${INSTALLTARGS} fteqccgui) diff --git a/build_setup.sh b/build_setup.sh index 85d74a266..4d8dfdcaa 100755 --- a/build_setup.sh +++ b/build_setup.sh @@ -33,8 +33,8 @@ ANDROID_ZIPALIGN=$ANDROIDROOT/build-tools/$ANDROIDBUILDTOOLS/zipalign #relative THREADS="-j 4" -TARGETS_LINUX="qcc-rel rel dbg vk-rel plugins-rel plugins-dbg" -TARGETS_WINDOWS="sv-rel gl-rel vk-rel mingl-rel m-rel d3d-rel qcc-rel qccgui-scintilla qccgui-dbg gl-dbg sv-dbg plugins-dbg plugins-rel" +TARGETS_LINUX="qcc-rel rel dbg plugins-rel plugins-dbg" #gl-rel vk-rel +TARGETS_WINDOWS="sv-rel m-rel qcc-rel qccgui-scintilla qccgui-dbg m-dbg sv-dbg plugins-dbg plugins-rel" #gl-rel vk-rel mingl-rel d3d-rel PLUGINS_DROID="qi ezhud irc" @@ -382,7 +382,7 @@ if [ $UID -ne 0 ] && [ $REBUILD_TOOLCHAINS == "y" ]; then #linux distros vary too much with various dependancies and versions and such, so we might as well pre-build our own copies of certain libraries. this really only needs to be done once, but its safe to retry anyway. cd $SVNROOT/engine if [ "$BUILD_LINUXx86" == "y" ]; then - echo "Making libraries (x86)..." + echo "Making libraries (linux x86)..." make FTE_TARGET=linux32 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null fi if [ "$BUILD_LINUXx64" == "y" ]; then @@ -397,6 +397,12 @@ if [ $UID -ne 0 ] && [ $REBUILD_TOOLCHAINS == "y" ]; then echo "Making libraries (linux armhf)..." make FTE_TARGET=linuxarmhf makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null fi + if [ "$BUILD_WINDOWS" == "y" ]; then + echo "Making libraries (linux armhf)..." + make FTE_TARGET=win32 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null + echo "Making libraries (linux armhf)..." + make FTE_TARGET=win64 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null + fi if [ "$BUILD_WINDOWS" == "y" ] && [[ "$PLUGINS_WINDOWS" =~ "ode" ]]; then echo "Prebuilding ODE library (win32)..." make FTE_TARGET=win32 plugins-rel NATIVE_PLUGINS=ode 2>&1 >>/dev/null diff --git a/engine/Makefile b/engine/Makefile index d564804b8..781f8e28b 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -2003,7 +2003,7 @@ endif plugins-dbg: @-mkdir -p $(DEBUG_DIR) @if test -e ../plugins/Makefile; \ - then $(MAKE) native -C ../plugins OUT_DIR="$(DEBUG_DIR)" CC="$(CC) $(W32_CFLAGS) $(DEBUG_CFLAGS)" CCX="$(CC) $(W32_CFLAGS) $(subst -Wno-pointer-sign,,$(DEBUG_CFLAGS))" ARCH="$(ARCH)" BASE_CFLAGS="$(BASE_CFLAGS)" BASE_CXXFLAGS="$(subst -Wno-pointer-sign,,$(BASE_CFLAGS))" FTE_TARGET="$(FTE_TARGET)"; \ + then $(MAKE) native -C ../plugins OUT_DIR="$(DEBUG_DIR)" CC="$(CC) $(W32_CFLAGS) $(DEBUG_CFLAGS)" CCX="$(CC) $(W32_CFLAGS) $(subst -Wno-pointer-sign,,$(DEBUG_CFLAGS))" ARCH="$(ARCH)" BASE_CFLAGS="$(BASE_CFLAGS) $(BRANDFLAGS)" BASE_CXXFLAGS="$(subst -Wno-pointer-sign,,$(BASE_CFLAGS)) $(BRANDFLAGS)" FTE_TARGET="$(FTE_TARGET)"; \ else echo no plugins directory installed; \ fi plugins: @@ -2011,7 +2011,7 @@ plugins: plugins-rel: @-mkdir -p $(RELEASE_DIR) @if test -e ../plugins/Makefile; \ - then $(MAKE) native -C ../plugins OUT_DIR="$(RELEASE_DIR)" CC="$(CC) $(W32_CFLAGS) $(RELEASE_CFLAGS)" CCX="$(CXX) $(W32_CFLAGS) $(subst -Wno-pointer-sign,,$(RELEASE_CFLAGS))" ARCH="$(ARCH)" BASE_CFLAGS="$(BASE_CFLAGS)" BASE_CXXFLAGS="$(subst -Wno-pointer-sign,,$(BASE_CFLAGS))" FTE_TARGET="$(FTE_TARGET)"; \ + then $(MAKE) native -C ../plugins OUT_DIR="$(RELEASE_DIR)" CC="$(CC) $(W32_CFLAGS) $(RELEASE_CFLAGS)" CCX="$(CXX) $(W32_CFLAGS) $(subst -Wno-pointer-sign,,$(RELEASE_CFLAGS))" ARCH="$(ARCH)" BASE_CFLAGS="$(BASE_CFLAGS) $(BRANDFLAGS)" BASE_CXXFLAGS="$(subst -Wno-pointer-sign,,$(BASE_CFLAGS)) $(BRANDFLAGS)" FTE_TARGET="$(FTE_TARGET)"; \ else echo no plugins directory installed; \ fi plugins-rel: @@ -2281,9 +2281,9 @@ libs-$(ARCH)/libspeexdsp.a: test -f speexdsp-$(SPEEXDSPVER).tar.gz || wget http://downloads.xiph.org/releases/speex/speexdsp-$(SPEEXDSPVER).tar.gz -test -f libs-$(ARCH)/libspeexdsp.a || (mkdir -p libs-$(ARCH)/speex && cd libs-$(ARCH) && tar -xvzf ../speexdsp-$(SPEEXDSPVER).tar.gz && cd speexdsp-$(SPEEXDSPVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp libspeexdsp/.libs/libspeexdsp.a ../ && cp -r include/speex/*.h ../speex/) -libs-$(ARCH)/libfreetype.a: libs-$(ARCH)/libpng.a +libs-$(ARCH)/libfreetype.a libs-$(ARCH)/ft2build.h: libs-$(ARCH)/libpng.a test -f freetype-$(FREETYPEVER).tar.gz || wget https://download.savannah.gnu.org/releases/freetype/freetype-$(FREETYPEVER).tar.gz - -test -f libs-$(ARCH)/libfreetype.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../freetype-$(FREETYPEVER).tar.gz && cd freetype-$(FREETYPEVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) ./configure CPPFLAGS=-I$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ LDFLAGS=-L$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ $(CONFIGARGS) --with-bzip2=no --with-harfbuzz=no && $(TOOLOVERRIDES) $(MAKE) && cp objs/.libs/libfreetype.a ../ && cp -r include/ ../) + -test -f libs-$(ARCH)/libfreetype.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../freetype-$(FREETYPEVER).tar.gz && cd freetype-$(FREETYPEVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) ./configure CPPFLAGS=-I$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ LDFLAGS=-L$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ $(CONFIGARGS) --with-bzip2=no --with-harfbuzz=no && $(TOOLOVERRIDES) $(MAKE) && cp objs/.libs/libfreetype.a ../ && bash && cp -r include/* ../) libs-$(ARCH)/libBulletDynamics.a: test -f bullet3-$(BULLETVER).tar.gz || wget https://github.com/bulletphysics/bullet3/archive/$(BULLETVER).tar.gz -O bullet3-$(BULLETVER).tar.gz diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index d30641df7..900dc96a1 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -4905,7 +4905,7 @@ void CL_Init (void) Cmd_AddCommandD ("fog", CL_Fog_f, "fog "); Cmd_AddCommandD ("waterfog", CL_Fog_f, "waterfog "); - Cmd_AddCommand ("skygroup", CL_Skygroup_f); + Cmd_AddCommandD ("skygroup", CL_Skygroup_f, "Provides a way to associate a skybox name with a series of maps, so that the requested skybox will override on a per-map basis."); // // Windows commands // diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 52aa85dfb..4fdd9b103 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -1080,6 +1080,7 @@ void CL_PredictMovePNum (int seat) if (nopred) { + lerpangles = false; //match interpolation info from.frame = ((char*)cl.previouspackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t); from.time = cl.inframes[from.frame & UPDATE_MASK].packet_entities.servertime; diff --git a/engine/client/image.c b/engine/client/image.c index 66695dbfd..95d60e2fc 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -61,6 +61,9 @@ char *r_defaultimageextensions = #ifdef IMAGEFMT_PSD //" psd" //paintshop images (8bit+16bit, but base layer only) #endif +#ifdef IMAGEFMT_XCF + //" xcf" //gimp's own format +#endif #ifdef IMAGEFMT_HDR //" hdr" //some file that uses RGBE formatted data, for hdr images. #endif @@ -202,6 +205,14 @@ static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int fla #include "image_astc.h" #endif +//for soft-decoding e5bgr9's exponent. multiply by the per-channel mantissa for a 0-1 result. +const float rgb9e5tab[32] = { + //aka: pow(2, biasedexponent - bias-bits) where bias is 15 and bits is 9 + 1.0/(1<<24), 1.0/(1<<23), 1.0/(1<<22), 1.0/(1<<21), 1.0/(1<<20), 1.0/(1<<19), 1.0/(1<<18), 1.0/(1<<17), + 1.0/(1<<16), 1.0/(1<<15), 1.0/(1<<14), 1.0/(1<<13), 1.0/(1<<12), 1.0/(1<<11), 1.0/(1<<10), 1.0/(1<<9), + 1.0/(1<<8), 1.0/(1<<7), 1.0/(1<<6), 1.0/(1<<5), 1.0/(1<<4), 1.0/(1<<3), 1.0/(1<<2), 1.0/(1<<1), + 1.0, 1.0*(1<<1), 1.0*(1<<2), 1.0*(1<<3), 1.0*(1<<4), 1.0*(1<<5), 1.0*(1<<6), 1.0*(1<<7), +}; #if defined(AVAIL_JPEGLIB) || defined(AVAIL_PNGLIB) @@ -420,6 +431,7 @@ void *ReadTargaFile(qbyte *buf, int length, int *width, int *height, uploadfmt_t if (buf[16] != 8 && buf[16] != 16 && buf[16] != 24 && buf[16] != 32) return NULL; //unsupported bitdepths break; + //0x80+ are third-party extensions... case 0x82: //half-float rgb case 0x83: //half-float greyscale if (forceformat != PTI_INVALID) @@ -1081,60 +1093,97 @@ qboolean WriteTGA(const char *filename, enum fs_relative fsroot, const qbyte *ft qboolean success = false; size_t c, i; vfsfile_t *vfs; - if (fmt != PTI_BGRA8 && fmt != PTI_RGB8 && fmt != PTI_RGBA8 && fmt != PTI_BGR8 && fmt != PTI_RGBX8 && fmt != PTI_BGRX8 && fmt != PTI_R16F && fmt != PTI_RGBA16F) - return false; + int ipx,opx; + qboolean rgb; + unsigned char header[18]; + memset (header, 0, 18); + + if (fmt == PTI_RGBA16F) + { + header[2] = 0x82; + opx = 8; + ipx = 8; + rgb = true; + } + else if (fmt == PTI_R16F) + { + header[2] = 0x83; + opx = 2; + ipx = 2; + rgb = false; + } + else + { + header[2] = 2; // uncompressed type + if (fmt == PTI_RGBA8 || fmt == PTI_BGRA8) + { + rgb = fmt==TF_RGBA32; + ipx = 4; + opx = 4; + header[17] = 8; + } + else if (fmt == PTI_RGBX8 || fmt == PTI_BGRX8) + { + rgb = fmt==PTI_RGBX8; + ipx = 4; + opx = 3; + } + else if (fmt == PTI_RGB8 || fmt == PTI_BGR8) + { + rgb = fmt==PTI_RGB8; + ipx = 3; + opx = 3; + } + /*else if (fmt == PTI_RGBA16) + { + rgb = true; + ipx = 8; + opx = 8; + }*/ + else if (fmt==PTI_LLLA8) + { + rgb = false; + ipx = 4; + opx = 2; + header[2] = 3; // greyscale + header[17] = 8; // with alpha + } + else if (fmt==PTI_LLLX8) + { + rgb = false; + ipx = 4; + opx = 1; + header[2] = 3; // greyscale + } + else if (fmt == PTI_L8) + { + rgb = false; + ipx = 1; + opx = 1; + header[2] = 3; // greyscale + } + else if (fmt == PTI_L8A8) + { + rgb = false; + ipx = 2; + opx = 2; + header[2] = 3; // greyscale + header[17] = 8; //with alpha + } + else + return false; + } + FS_CreatePath(filename, fsroot); vfs = FS_OpenVFS(filename, "wb", fsroot); if (vfs) { - int ipx,opx; - qboolean rgb; - unsigned char header[18]; - memset (header, 0, 18); - - if (fmt == PTI_RGBA16F) - { - header[2] = 0x82; - opx = 8; - ipx = 8; - rgb = true; - } - else if (fmt == PTI_R16F) - { - header[2] = 0x83; - opx = 2; - ipx = 2; - rgb = false; - } - else - { - header[2] = 2; // uncompressed type - if (fmt == PTI_BGRA8 || fmt == PTI_RGBA8 || fmt==PTI_LLLA8) - { - rgb = fmt==TF_RGBA32; - ipx = 4; - opx = 4; - } - else if (fmt == PTI_RGBX8 || fmt == PTI_BGRX8 || fmt==PTI_LLLX8) - { - rgb = fmt==PTI_RGBX8; - ipx = 4; - opx = 3; - } - else - { - rgb = fmt==PTI_RGB8; - ipx = 3; - opx = 3; - } - } - header[12] = width&255; header[13] = width>>8; header[14] = height&255; header[15] = height>>8; header[16] = opx*8; // pixel size - header[17] = 0x00; // flags + header[17] |= 0x00; // flags if (bytestride < 0) { //if we're upside down, lets just use an upside down tga. @@ -1143,7 +1192,7 @@ qboolean WriteTGA(const char *filename, enum fs_relative fsroot, const qbyte *ft //now we can just do everything without worrying about rows } else //our data is top-down, set up the header to also be top-down. - header[17] = 0x20; + header[17] |= 0x20; if (ipx == opx && !rgb) { //can just directly write it @@ -1157,43 +1206,61 @@ qboolean WriteTGA(const char *filename, enum fs_relative fsroot, const qbyte *ft { qbyte *fte_restrict rgb_out = malloc((size_t)width*opx*height); - //no need to swap alpha, and if we're just swapping alpha will be fine in-place. - if (rgb) - { //rgb24, rgbx32, rgba32 - // compact, and swap - if (opx == 8) - { - c = (size_t)width*height; - for (i=0 ; irgb may require copying out entirely for the first pixel to work properly. - if (fmt == TF_BGRA32 || fmt == TF_BGRX32 || fmt == TF_BGR24) - { //byteswap and strip alpha - size_t ps = (fmt == TF_BGR24)?3:4; - qbyte *in=screendata, *out=rgbdata=Hunk_TempAlloc(screenwidth*screenheight*3); - size_t y, x; - for (y = 0; y < screenheight; y++) - { - for (x = 0; x < screenwidth; x++) - { - int r = in[2]; - int g = in[1]; - int b = in[0]; - out[0] = r; - out[1] = g; - out[2] = b; - in+=ps; - out+=3; - } - in-=screenwidth*ps; - in+=stride; - } - fmt = TF_RGB24; - stride = screenwidth*3; - screendata = rgbdata; - } - else if (fmt == TF_RGBA32 || fmt == TF_RGBX32 || (fmt == TF_RGB24 && stride < 0)) - { //strip alpha, no need to byteswap - size_t ps = (fmt == TF_RGB24)?3:4; - qbyte *in=screendata, *out=rgbdata=Hunk_TempAlloc(screenwidth*screenheight*3); - size_t y, x; - for (y = 0; y < screenheight; y++) - { - for (x = 0; x < screenwidth; x++) - { - int r = in[0]; - int g = in[1]; - int b = in[2]; - out[0] = r; - out[1] = g; - out[2] = b; - in+=ps; - out+=3; - } - in-=screenwidth*ps; - in+=stride; - } - fmt = TF_RGB24; - stride = screenwidth*3; - screendata = rgbdata; - } - else if (fmt != TF_RGB24) + int ic; + qboolean byteswap; + switch(fmt) { + case PTI_RGB8: //yay! nothing to do. + byteswap = false; + ic = 3; + break; + case PTI_BGR8: + byteswap = true; + ic = 3; + break; + case PTI_BGRA8: + case PTI_BGRX8: + byteswap = true; + ic = 4; + break; + case PTI_RGBA8: + case PTI_RGBX8: + case PTI_LLLA8: + case PTI_LLLX8: + byteswap = false; + ic = 4; + break; + case PTI_L8: +// case PTI_L8A8: + byteswap = false; + ic = 1; + break; + default: //nope, not happening. Con_Printf("screenshotJPEG: image format not supported\n"); return false; } - if (!LIBJPEG_LOADED()) - return false; + if (byteswap || ic == 4) + { //jpeg doesn't support many input layouts... + qbyte *in=screendata; + qbyte *out=tmpdata=BZ_Malloc(screenwidth*screenheight*3); + size_t y, x; + int rc = byteswap?2:0; + int bc = byteswap?0:2; + for (y = 0; y < screenheight; y++) + { + for (x = 0; x < screenwidth; x++, in+=ic, out+=3) + { + out[0] = in[rc]; + out[1] = in[1]; + out[2] = in[bc]; + } + in-=screenwidth*ic; + in+=stride; + } + ic = 3; + stride = screenwidth*3; + screendata = tmpdata; + } - if (!(outfile = FS_OpenVFS(filename, "wb", fsroot))) + if (LIBJPEG_LOADED()) { FS_CreatePath (filename, fsroot); - if (!(outfile = FS_OpenVFS(filename, "wb", fsroot))) + if ((outfile = FS_OpenVFS(filename, "wb", fsroot))) { - Con_Printf("Error opening %s\n", filename); - return false; + cinfo.err = qjpeg_std_error(&jerr.pub); + jerr.pub.error_exit = jpeg_error_exit; + if (setjmp(jerr.setjmp_buffer)) + { + qjpeg_destroy_compress(&cinfo); + VFS_CLOSE(outfile); + FS_Remove(filename, FS_GAME); + Con_Printf("Failed to create jpeg\n"); + BZ_Free(tmpdata); + return false; + } + else + { + qjpeg_create_compress(&cinfo); + + ftejpeg_mem_dest(&cinfo, outfile); + cinfo.image_width = screenwidth; + cinfo.image_height = screenheight; + cinfo.input_components = ic; + cinfo.in_color_space = (ic<3)?JCS_GRAYSCALE:JCS_RGB; + qjpeg_set_defaults(&cinfo); + qjpeg_set_quality (&cinfo, bound(0, compression, 100), true); + qjpeg_start_compress(&cinfo, true); + + if (writemeta) + { + static const char header[] = "http://ns.adobe.com/xap/1.0/"; + char blob[8192]; + memcpy(blob, header, sizeof(header)); //MUST include the null terminator. + GenerateXMPData(blob+sizeof(header), sizeof(blob)-sizeof(header), screenwidth, screenheight, writemeta); + qjpeg_write_marker(&cinfo, JPEG_APP0+1, blob, sizeof(header)+strlen(blob+sizeof(header))); + } + + while (cinfo.next_scanline < cinfo.image_height) + { + *row_pointer = screendata+(cinfo.next_scanline * stride); + qjpeg_write_scanlines(&cinfo, row_pointer, 1); + } + qjpeg_finish_compress(&cinfo); + qjpeg_destroy_compress(&cinfo); + ret = true; + } + + VFS_CLOSE(outfile); } } - - cinfo.err = qjpeg_std_error(&jerr.pub); - jerr.pub.error_exit = jpeg_error_exit; - if (setjmp(jerr.setjmp_buffer)) - { - qjpeg_destroy_compress(&cinfo); - VFS_CLOSE(outfile); - FS_Remove(filename, FS_GAME); - Con_Printf("Failed to create jpeg\n"); - return false; - } - qjpeg_create_compress(&cinfo); - - buffer = screendata; - - ftejpeg_mem_dest(&cinfo, outfile); - cinfo.image_width = screenwidth; - cinfo.image_height = screenheight; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - qjpeg_set_defaults(&cinfo); - qjpeg_set_quality (&cinfo, bound(0, compression, 100), true); - qjpeg_start_compress(&cinfo, true); - - if (writemeta) - { - static const char header[] = "http://ns.adobe.com/xap/1.0/"; - char blob[8192]; - memcpy(blob, header, sizeof(header)); //MUST include the null terminator. - GenerateXMPData(blob+sizeof(header), sizeof(blob)-sizeof(header), screenwidth, screenheight, writemeta); - qjpeg_write_marker(&cinfo, JPEG_APP0+1, blob, sizeof(header)+strlen(blob+sizeof(header))); - } - - while (cinfo.next_scanline < cinfo.image_height) - { - *row_pointer = &buffer[cinfo.next_scanline * stride]; - qjpeg_write_scanlines(&cinfo, row_pointer, 1); - } - qjpeg_finish_compress(&cinfo); - VFS_CLOSE(outfile); - qjpeg_destroy_compress(&cinfo); - return true; + BZ_Free(tmpdata); + return ret; } #endif #endif @@ -3547,6 +3627,512 @@ static void *ReadPSDFile(qbyte *buf, size_t len, const char *fname, int *outwidt } #endif +#ifdef IMAGEFMT_XCF +struct xcf_s +{ + const qbyte *filestart; + size_t filesize; + size_t offset; + + unsigned int version; + qbyte compression; + size_t width; + size_t height; + int basetype, precision; + + size_t numlayers; + size_t *layeroffsets; + qbyte *flat; +}; +static unsigned int XCF_ReadData(struct xcf_s *f, qbyte *out, size_t len) +{ + size_t avail; + if (f->offset >= f->filesize) + avail = 0; + else + avail = f->filesize - f->offset; + if (len > avail) + memset(out+avail, 0, len-avail); + else + avail = len; + memcpy(out, f->filestart+f->offset, avail); + f->offset += avail; + return avail; +} +static unsigned char XCF_ReadByte(struct xcf_s *f) +{ + if (f->offset >= f->filesize) + return 0; + return f->filestart[f->offset++]; +} +static unsigned int XCF_Read32(struct xcf_s *f) +{ //XCF is natively big-endian. + qbyte w[4]; + XCF_ReadData(f, w, sizeof(w)); + return (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0); +} +static size_t XCF_ReadOffset(struct xcf_s *f) +{ //XCF is natively big-endian. + qbyte w[8]; + size_t h, l; + if (f->version >= 11) + { + XCF_ReadData(f, w, sizeof(w)); + h = (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0); + l = (w[4]<<24)|(w[5]<<16)|(w[6]<<8)|(w[7]<<0); + return (h<<32)|l; + } + return XCF_Read32(f); +} +static size_t XCF_ReadString(struct xcf_s *f, char *out, size_t outsize) +{ + size_t len = XCF_Read32(f); + if (!len) + *out = 0; + else + { + if (outsize > len) + outsize = len; + outsize--; + XCF_ReadData(f, out, outsize); + out[outsize]=0; + f->offset += len-outsize; + } + return len; +} +static void XCF_ReadHeaderProperties(struct xcf_s *f) +{ //XCF is natively big-endian. + unsigned int proptype, propsize; + + for(;;) + { + proptype = XCF_Read32(f); + propsize = XCF_Read32(f); + if (!proptype) + break; + else if (proptype == 17) //compression + XCF_ReadData(f, &f->compression,1); + else if (proptype == 19) //prop_resolution + f->offset += propsize; + else if (proptype == 20) //tatoo + f->offset += propsize; + else if (proptype == 21) //parasites + f->offset += propsize; + else if (proptype == 22) //prop_unit + f->offset += propsize; + else + { + Con_Printf("Unknown image property %i\n", proptype); + f->offset += propsize; + } + } +} +static qboolean XCF_ReadTile(struct xcf_s *f, qbyte *out, int pxsize, int bytestride, int bytew, int h) +{ + if (f->compression==0) + { + while(h --> 0) + { + XCF_ReadData(f, out, bytew); + out += bytestride; + } + return true; + } + else if (f->compression == 1) + { + int runsize; + int runtype; + int runval; + int rowbyte; + int c = 0, y; + qbyte *outdata = out; + //data is ordered by channels +nextchan: + if (c == pxsize) + return true; + out = outdata+c; + rowbyte = 0; + c++; + for(y=0;;) + { + runsize = XCF_ReadByte(f); + if (runsize <= 127) + { //compressed run + if (runsize == 127) + { + runsize = XCF_ReadByte(f)<<8; + runsize |= XCF_ReadByte(f); + } + else + runsize++; + runval = XCF_ReadByte(f); + runtype = 0; + } + else + { //unpacked run + if (runsize == 128) + { + runsize = XCF_ReadByte(f)<<8; + runsize |= XCF_ReadByte(f); + } + else + runsize = 256-runsize; + runval = -1; + runtype = 1; + } + if (!runsize) + return false; //buggy input + + while(runsize --> 0) + { + if (runtype) + runval = XCF_ReadByte(f); + out[rowbyte] = runval; + rowbyte+=pxsize; + + if (rowbyte == bytew) + { //reached the end of the row, move on to the next + if (++y == h) + { + if (runsize) + return false; //runsize was too long... + goto nextchan; //reached the end of the tile. + } + rowbyte = 0; + out += bytestride; + } + } + } + } + else + return false; +} +struct xcf_heirachy_s +{ + uint32_t width; + uint32_t height; + uint32_t bpp; + qbyte *data; +}; +static struct xcf_heirachy_s XCF_ReadHeirachy(struct xcf_s *f) +{ + struct xcf_heirachy_s ctx; + uint32_t x, y, lw, lh; + size_t ofs, tofs; + if (!f->offset) + { + memset(&ctx, 0, sizeof(ctx)); + return ctx; + } + ctx.width = XCF_Read32(f); + ctx.height = XCF_Read32(f); + ctx.bpp = XCF_Read32(f); + ctx.data = NULL; + + ofs = XCF_ReadOffset(f); + while (XCF_ReadOffset(f)) + ; //we don't care about these dummy offsets. we could use them for mipmaps but we don't expect them to be valid or bug-free + + f->offset = ofs; //jump to level 0 + lw = XCF_Read32(f); + lh = XCF_Read32(f); + if (lw == ctx.width && lh == ctx.height) + { + ctx.data = BZ_Malloc(ctx.width*ctx.bpp*ctx.height); + + for (y = 0; y < ctx.height; y+=lh) + { + lh = min(64, ctx.height-y); + for (x = 0; x < ctx.width; x+=lw) + { + lw = min(64, ctx.width-x); + tofs = XCF_ReadOffset(f); + + ofs = f->offset; + f->offset = tofs; + if (!XCF_ReadTile(f, ctx.data + x*ctx.bpp + y*ctx.width*ctx.bpp, ctx.bpp, ctx.width*ctx.bpp, lw*ctx.bpp, lh)) + { + BZ_Free(ctx.data); + ctx.data = NULL; + return ctx; + } + f->offset = ofs; + } + } + } + return ctx; +} +static struct xcf_heirachy_s XCF_ReadChannel(struct xcf_s *f) +{ + struct xcf_heirachy_s h; + char name[1024]; + uint32_t width = XCF_Read32(f); + uint32_t height = XCF_Read32(f); + unsigned int proptype, propsize; + XCF_ReadString(f, name, sizeof(name)); + for(;;) + { + proptype = XCF_Read32(f); + propsize = XCF_Read32(f); + if (!proptype) + break; + else if (proptype == 3) //prop_active_channel + ; //ui state + else if (proptype == 16) //prop_colour + f->offset += 3; //ui state + else if (proptype == 38) //prop_colour + f->offset += 3*4; //ui state + else if (proptype == 4) //prop_selection + ; //ui state + else if (proptype == 14) //prop_showmask + f->offset += 4; //ui state + else + { + Con_DPrintf("Unknown channel property %i\n", proptype); + f->offset += propsize; + } + } + f->offset = XCF_ReadOffset(f); + h = XCF_ReadHeirachy(f); + if (h.width != width || h.height != height) + { + BZ_Free(h.data); + h.data = NULL; + } + return h; +} +static qboolean XCF_CombineLayer(struct xcf_s *f) +{ + struct xcf_heirachy_s h, l={0}; + unsigned int proptype, propsize; + size_t width, height, type, heirachyoffset, layermaskoffset; + char name[1024]; + uint32_t applylayermask = false; + int32_t blendmode = 0; + qboolean unsupported = false; + uint32_t x,y, ofsx=0,ofsy=0; + uint32_t visible = true; + width = XCF_Read32(f); + height = XCF_Read32(f); + type = XCF_Read32(f); + XCF_ReadString(f, name, sizeof(name)); + + for(;;) + { + proptype = XCF_Read32(f); + propsize = XCF_Read32(f); + if (!proptype) + break; + else if (proptype == 2) //prop_active_layer + ; //ui state + else if (proptype == 11) //prop_apply_mask + applylayermask = XCF_Read32(f); + else if (proptype == 35) //prop_composite_mode + /*compositemode =*/ XCF_Read32(f); + else if (proptype == 36) //prop_composite_space + /*colourspace = */ XCF_Read32(f); + else if (proptype == 12) //prop_edit_mask + /*editmask = */ XCF_Read32(f); //ui data, uninteresting + else if (proptype == 5) //prop_floating_selection + { //just ignore this layer. + //FIXME: the *other* layer needs to composite differently + return true; + XCF_ReadOffset(f); + } + else if (proptype == 29) //prop_group_item + { + Con_DPrintf("Unsupported layer property: prop_group_item\n"); + visible = false; +// unsupported = true; //panic + } + else if (proptype == 30) //prop_item_path + { + Con_DPrintf("Unsupported layer property: prop_item_path\n"); + visible = false; +// unsupported = true; + while(XCF_ReadOffset(f)) + ; + } + else if (proptype == 31) //prop_group_item_flags + XCF_Read32(f); + else if (proptype == 10) //prop_lock_alpha + XCF_Read32(f); //ui state + else if (proptype == 7) //prop_mode + blendmode = XCF_Read32(f); + else if (proptype == 8) //prop_visible + visible = XCF_Read32(f); + else if (proptype == 15) //prop_offsets + { + ofsx = XCF_Read32(f); + ofsy = XCF_Read32(f); + } + else if (proptype == 13) //prop_show_mask + XCF_Read32(f); //ui data, uninteresting + else if (proptype == 26) //prop_text_layer_flags + XCF_Read32(f); //ui data, uninteresting + else + { + Con_DPrintf("Unknown layer property %i\n", proptype); + f->offset += propsize; + } + } + + heirachyoffset = XCF_ReadOffset(f); + layermaskoffset = XCF_ReadOffset(f); + + if (unsupported || !visible) + heirachyoffset = layermaskoffset = 0; + + f->offset = heirachyoffset; + h = XCF_ReadHeirachy(f); + + if (applylayermask) + { //blend the alpha...? + f->offset = layermaskoffset; + l = XCF_ReadChannel(f); + if (!l.data || l.bpp!=1 || l.width != width || l.height != height) + applylayermask = false; //erk? + } + + if (!h.data) + { //its valid to have just a layermask... + h.bpp = 0; //don't try reading anything. + h.width = width; + h.height = height; + } + else if (h.width != width || h.height != height) + unsupported = true; //level0 must match the layer's size + + if (unsupported || !visible) + ; + else + { + width = min(h.width, f->width-ofsx); + height = min(h.height, f->height-ofsy); + if (ofsx < f->width && ofsy < f->height) + { + qbyte *in=h.data, *out = f->flat + ofsx*4+ofsy*f->width*4; + vec4_t px; + float sa, da, k; + for (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width)) + { + for (x = 0; x < width; x++, out+=4, in+=h.bpp) + { + switch(h.bpp) + { + case 4: Vector4Set(px, in[0], in[1], in[2], in[3]); break; + case 3: Vector4Set(px, in[0], in[1], in[2], 0xff); break; + case 2: Vector4Set(px, in[0], in[0], in[0], in[1]); break; + case 1: Vector4Set(px, in[0], in[0], in[0], 0xff); break; + case 0: Vector4Set(px, 0xff, 0xff, 0xff, 0xff); break; + } + if (applylayermask) + px[3] *= l.data[x+y*width]/(float)0xff; + switch(blendmode) + { + case 0: //normal(legacy) + case 28: //normal + da = out[3]/255.0; + sa = px[3]/255; + k = 1-(1-da)*(1-sa); + out[3] = k*255; + k = sa/k; + out[0] = out[0]*(1-k)+px[0]*k; + out[1] = out[1]*(1-k)+px[1]*k; + out[2] = out[2]*(1-k)+px[2]*k; + break; + default: + Con_Printf("xcf: blend mode %i is not supported\n", blendmode); + unsupported = true; + goto parseerror; + } + } + } + } + } + +parseerror: + BZ_Free(l.data); + BZ_Free(h.data); + + (void)type; + (void)layermaskoffset; + + return !unsupported; +} +static qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname, int *width, int *height, uploadfmt_t *format) +{ + size_t offs; + struct xcf_s ctx; + if (len < 14 || strncmp(filedata, "gimp xcf ", 9) || filedata[13]) + return NULL; + memset(&ctx, 0, sizeof(ctx)); + ctx.version = atoi(filedata+10); + ctx.filestart = filedata; + ctx.filesize = len; + ctx.offset = 14; + + ctx.width = XCF_Read32(&ctx); + ctx.height = XCF_Read32(&ctx); + ctx.basetype = XCF_Read32(&ctx); + if (ctx.basetype != 0/*rgb*/ && ctx.basetype != 1/*grey*/) + return false; //doesn't really matter what format it is, we're going to output rgba regardless. we can just do it based upon the bytes per pixel. + if (ctx.version >= 4) + { + ctx.precision = XCF_Read32(&ctx); + if (ctx.version < 7) + ctx.precision = 150; //hack + switch(ctx.precision) + { + case 150: //usually this one. + // *format = PTI_RGBA8_SRGB; + // break; + case 100: + *format = PTI_RGBA8; + break; + /*case 200: + *format = PTI_RGBA16; + break; + case 500: + *format = PTI_RGBA16F; + break; + case 600: + *format = PTI_RGBA32F; + break;*/ + default: + return false; + } + } + XCF_ReadHeaderProperties(&ctx); + while((offs=XCF_ReadOffset(&ctx))) + { + ctx.layeroffsets = realloc(ctx.layeroffsets, sizeof(*ctx.layeroffsets)*(ctx.numlayers+1)); + ctx.layeroffsets[ctx.numlayers++] = offs; + } + //channels + + //without any layers, its fully transparent + ctx.flat = Z_Malloc(ctx.width*ctx.height*4); + *width = ctx.width; + *height = ctx.height; + + while(ctx.numlayers --> 0) + { + ctx.offset = ctx.layeroffsets[ctx.numlayers]; + if (!XCF_CombineLayer(&ctx)) + { + Z_Free(ctx.flat); + ctx.flat = NULL; + break; + } + } + + + BZ_Free(ctx.layeroffsets); + return ctx.flat; +} +#endif + #ifdef IMAGEFMT_EXR #if 0 #include "OpenEXR/ImfCRgbaFile.h" @@ -3637,7 +4223,7 @@ static void *ReadEXRFile(qbyte *buf, size_t len, const char *fname, int *outwidt write(fd, buf, len); ctx = exr.OpenInputFile(tname); close(fd); //we don't need the input file now. - Sys_remove(tname); + unlink(tname); #endif if (ctx) @@ -3974,6 +4560,7 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc case PTI_RGBA32F: header.glinternalformat = 0x8814/*GL_RGBA32F*/; header.glbaseinternalformat = 0x1908/*GL_RGBA*/; header.glformat = 0x1908/*GL_RGBA*/; header.gltype = 0x1406/*GL_FLOAT*/; header.gltypesize = 4; break; case PTI_A2BGR10: header.glinternalformat = 0x8059/*GL_RGB10_A2*/; header.glbaseinternalformat = 0x1908/*GL_RGBA*/; header.glformat = 0x1908/*GL_RGBA*/; header.gltype = 0x8368/*GL_UNSIGNED_INT_2_10_10_10_REV*/; header.gltypesize = 4; break; case PTI_E5BGR9: header.glinternalformat = 0x8C3D/*GL_RGB9_E5*/; header.glbaseinternalformat = 0x8C3D/*GL_RGB9_E5*/; header.glformat = 0x1907/*GL_RGB*/; header.gltype = 0x8C3E/*GL_UNSIGNED_INT_5_9_9_9_REV*/; header.gltypesize = 4; break; + case PTI_B10G11R11F: header.glinternalformat = 0x8C3A/*GL_R11F_G11F_B10F*/; header.glbaseinternalformat = 0x8C3D/*GL_R11F_G11F_B10F*/; header.glformat = 0x1907/*GL_RGB*/; header.gltype = 0x8C3B/*GL_UNSIGNED_INT_10_11_11_REV*/; header.gltypesize = 4; break; case PTI_P8: case PTI_R8: header.glinternalformat = 0x8229/*GL_R8*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; case PTI_RG8: header.glinternalformat = 0x822B/*GL_RG8*/; header.glbaseinternalformat = 0x8227/*GL_RG*/; header.glformat = 0x8227/*GL_RG*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; @@ -3993,6 +4580,7 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc case PTI_DEPTH32: header.glinternalformat = 0x81A7/*GL_DEPTH_COMPONENT32*/; header.glbaseinternalformat = 0x1902/*GL_DEPTH_COMPONENT*/; header.glformat = 0x1902/*GL_DEPTH_COMPONENT*/; header.gltype = 0x1406/*GL_FLOAT*/; header.gltypesize = 4; break; case PTI_DEPTH24_8: header.glinternalformat = 0x88F0/*GL_DEPTH24_STENCIL8*/; header.glbaseinternalformat = 0x84F9/*GL_DEPTH_STENCIL*/; header.glformat = 0x84F9/*GL_DEPTH_STENCIL*/; header.gltype = 0x84FA/*GL_UNSIGNED_INT_24_8*/; header.gltypesize = 4; break; + case PTI_RGB32F: #ifdef FTE_TARGET_WEB case PTI_WHOLEFILE: #endif @@ -4579,6 +5167,9 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch case 2/*DXGI_FORMAT_R32G32B32A32_FLOAT*/: encoding = PTI_RGBA32F; break; + case 6/*DXGI_FORMAT_R32G32B32_FLOAT*/: + encoding = PTI_RGB32F; + break; case 10/*DXGI_FORMAT_R16G16B16A16_FLOAT*/: encoding = PTI_RGBA16F; break; @@ -4899,8 +5490,10 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc case PTI_R32F: h10.dxgiformat = 41/*DXGI_FORMAT_R32_FLOAT*/; break; case PTI_RGBA16F: h10.dxgiformat = 10/*DXGI_FORMAT_R16G16B16A16_FLOAT*/; break; case PTI_RGBA32F: h10.dxgiformat = 2/*DXGI_FORMAT_R32G32B32A32_FLOAT*/; break; + case PTI_RGB32F: h10.dxgiformat = 6/*DXGI_FORMAT_R32G32B32_FLOAT*/; break; case PTI_A2BGR10: h10.dxgiformat = 24/*DXGI_FORMAT_R10G10B10A2_UNORM*/; break; case PTI_E5BGR9: h10.dxgiformat = 67/*DXGI_FORMAT_R9G9B9E5_SHAREDEXP*/; break; + case PTI_B10G11R11F: h10.dxgiformat = 26/*DXGI_FORMAT_R11G11B10_FLOAT*/; break; case PTI_P8: case PTI_R8: h10.dxgiformat = 61/*DXGI_FORMAT_R8_UNORM*/; break; case PTI_RG8: h10.dxgiformat = 49/*DXGI_FORMAT_R8G8_UNORM*/; break; @@ -5525,6 +6118,10 @@ qbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_ if (!force_rgba8 && len > 26 && !strncmp(buf, "8BPS", 4) && (data = ReadPSDFile(buf, len, fname, width, height, format))) return data; #endif +#ifdef IMAGEFMT_XCF + if (len > 9 && !strncmp(buf, "gimp xcf ", 9) && (data = ReadXCFFile(buf, len, fname, width, height, format))) + return data; +#endif #ifdef IMAGEFMT_EXR if (!force_rgba8 && len > 4 && (buf[0]|(buf[1]<<8)|(buf[2]<<16)|(buf[3]<<24)) == 20000630 && (data = ReadEXRFile(buf, len, fname, width, height, format))) @@ -6644,6 +7241,7 @@ static void Image_8888to565(struct pendingtextureinfo *mips, qboolean bgra) mips->mip[mip].needfree = true; mips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p); } + mips->mip[mip].datasize = p*sizeof(*out); if (bgra) { @@ -6686,6 +7284,7 @@ static void Image_8888to1555(struct pendingtextureinfo *mips, qboolean bgra) mips->mip[mip].needfree = true; mips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p); } + mips->mip[mip].datasize = p*sizeof(*out); if (bgra) { @@ -6728,6 +7327,7 @@ static void Image_8888to5551(struct pendingtextureinfo *mips, qboolean bgra) mips->mip[mip].needfree = true; mips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p); } + mips->mip[mip].datasize = p*sizeof(*out); if (bgra) { @@ -6770,6 +7370,7 @@ static void Image_8888to4444(struct pendingtextureinfo *mips, qboolean bgra) mips->mip[mip].needfree = true; mips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p); } + mips->mip[mip].datasize = p*sizeof(*out); if (bgra) { @@ -6812,6 +7413,7 @@ static void Image_8888toARGB4444(struct pendingtextureinfo *mips, qboolean bgra) mips->mip[mip].needfree = true; mips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p); } + mips->mip[mip].datasize = p*sizeof(*out); if (bgra) { @@ -6853,13 +7455,125 @@ static void Image_4X16to8888(struct pendingtextureinfo *mips) mips->mip[mip].needfree = true; mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); } + mips->mip[mip].datasize = p*sizeof(*out); while(p-->0) *out++ = *in++>>8; } } -//R8,G8,B8,X8 (aligned) -> R8,G8,B8 (tightly packed) +//in place: E5BGR9->RGBA8 +static void Image_E5BGR9ToByte(struct pendingtextureinfo *mips) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + unsigned int *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*4*p); + } + mips->mip[mip].datasize = p*sizeof(*out)*4; + + while(p-->0) + { + unsigned int l = *in++; + float e = rgb9e5tab[l>>27]; + e *= 255; //prescale to bytes. + *out++ = bound(0, e * ((l>> 0)&0x1ff), 255); + *out++ = bound(0, e * ((l>> 9)&0x1ff), 255); + *out++ = bound(0, e * ((l>>18)&0x1ff), 255); + *out++ = 255; + } + } +} + +//in place: RGBA16F->RGBA8, or R16F->R8 +static void Image_HalfToByte(struct pendingtextureinfo *mips, int channels) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + int v; + unsigned short *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h*channels; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); + } + mips->mip[mip].datasize = p*sizeof(*out); + + while(p-->0) + { + v = HalfToFloat(*in++)*255; + *out++ = bound(0, v, 255); + } + } +} + +//always out of place +static void Image_RGB32FToFloat(struct pendingtextureinfo *mips) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + float *in = mips->mip[mip].data; + float *out = mips->mip[mip].data; + float *dofree = mips->mip[mip].needfree?in:NULL; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4); + mips->mip[mip].datasize = p*sizeof(*out)*4; + while(p-->0) + { + *out++ = *in++; + *out++ = *in++; + *out++ = *in++; + *out++ = 1.0; + } + BZ_Free(dofree); + } +} + +//in place: RGBA32F->RGBA8, or R32F->R8 +static void Image_FloatToByte(struct pendingtextureinfo *mips, int channels) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + int v; + float *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h*channels; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); + } + mips->mip[mip].datasize = p*sizeof(*out); + + while(p-->0) + { + v = *in++*255; + *out++ = bound(0, v, 255); + } + } +} + +//in place: R8,G8,B8,X8 (aligned) -> R8,G8,B8 (tightly packed). static void Image_32To24(struct pendingtextureinfo *mips) { int mip; @@ -6875,6 +7589,8 @@ static void Image_32To24(struct pendingtextureinfo *mips) mips->mip[mip].needfree = true; mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*3); } + mips->mip[mip].datasize = p*sizeof(*out)*3; + while(p-->0) { *out++ = *in++; @@ -8298,7 +9014,7 @@ static void Image_Decode_ASTC_HDR_HF_Block(qbyte *fte_restrict in, pixel64_t *ft ASTC_Decode_HDR(in, out->v, stride, bw, bh); } -static unsigned int RGB16F_to_E5BGR9(unsigned short Cr, unsigned short Cg, unsigned short Cb) +/*static unsigned int RGB16F_to_E5BGR9(unsigned short Cr, unsigned short Cg, unsigned short Cb) { int Re,Ge,Be, Rex,Gex,Bex, Xm, Xe; uint32_t rshift, gshift, bshift, expo; @@ -8362,7 +9078,7 @@ static void Image_Decode_ASTC_HDR_E5_Block(qbyte *fte_restrict in, pixel32_t *ft for (y = 0; y < bh; y++, out += stride) for (x = 0; x < bw; x++) out[x].u = RGB16F_to_E5BGR9(hdr[y][x][0], hdr[y][x][1], hdr[y][x][2]); -} +}*/ #endif #endif @@ -8401,6 +9117,7 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, case PTI_BGRX8_SRGB: case PTI_A2BGR10: case PTI_E5BGR9: + case PTI_B10G11R11F: b = 4; break; @@ -8418,6 +9135,9 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, case PTI_RGBA32F: b = 4*4; break; + case PTI_RGB32F: + b = 3*4; + break; case PTI_P8: case PTI_R8: case PTI_R8_SNORM: @@ -8547,6 +9267,133 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, *blockheight = h; } +qboolean Image_FormatHasAlpha(uploadfmt_t encoding) +{ + switch(encoding) + { + case PTI_RGB565: + case PTI_RGBX8: + case PTI_BGRX8: + case PTI_RGBX8_SRGB: + case PTI_BGRX8_SRGB: + case PTI_E5BGR9: + case PTI_B10G11R11F: + case PTI_R16: + case PTI_R16F: + case PTI_R32F: + case PTI_RGB32F: + case PTI_P8: + case PTI_R8: + case PTI_R8_SNORM: + case PTI_RG8: + case PTI_RG8_SNORM: + case PTI_DEPTH16: + case PTI_DEPTH24: + case PTI_DEPTH32: + case PTI_DEPTH24_8: + case PTI_RGB8: + case PTI_BGR8: + case PTI_RGB8_SRGB: + case PTI_BGR8_SRGB: + case PTI_L8: + case PTI_L8_SRGB: + case PTI_BC1_RGB: + case PTI_BC1_RGB_SRGB: + case PTI_BC4_R8: + case PTI_BC4_R8_SNORM: + case PTI_BC5_RG8: + case PTI_BC5_RG8_SNORM: + case PTI_BC6_RGB_UFLOAT: + case PTI_BC6_RGB_SFLOAT: + case PTI_ETC1_RGB8: + case PTI_ETC2_RGB8: + case PTI_ETC2_RGB8_SRGB: + case PTI_EAC_R11: + case PTI_EAC_R11_SNORM: + case PTI_EAC_RG11: + case PTI_EAC_RG11_SNORM: + return false; + + case PTI_RGBA4444: + case PTI_ARGB4444: + case PTI_RGBA5551: + case PTI_ARGB1555: + case PTI_RGBA8: + case PTI_BGRA8: + case PTI_RGBA8_SRGB: + case PTI_BGRA8_SRGB: + case PTI_A2BGR10: + case PTI_RGBA16: + case PTI_RGBA16F: + case PTI_RGBA32F: + case PTI_L8A8: + case PTI_L8A8_SRGB: + case PTI_BC1_RGBA: + case PTI_BC1_RGBA_SRGB: + case PTI_ETC2_RGB8A1: + case PTI_ETC2_RGB8A1_SRGB: + case PTI_BC2_RGBA: + case PTI_BC2_RGBA_SRGB: + case PTI_BC3_RGBA: + case PTI_BC3_RGBA_SRGB: + case PTI_BC7_RGBA: + case PTI_BC7_RGBA_SRGB: + case PTI_ETC2_RGB8A8: + case PTI_ETC2_RGB8A8_SRGB: + case PTI_ASTC_4X4_HDR: + case PTI_ASTC_4X4_SRGB: + case PTI_ASTC_4X4_LDR: + case PTI_ASTC_5X4_HDR: + case PTI_ASTC_5X4_SRGB: + case PTI_ASTC_5X4_LDR: + case PTI_ASTC_5X5_HDR: + case PTI_ASTC_5X5_SRGB: + case PTI_ASTC_5X5_LDR: + case PTI_ASTC_6X5_HDR: + case PTI_ASTC_6X5_SRGB: + case PTI_ASTC_6X5_LDR: + case PTI_ASTC_6X6_HDR: + case PTI_ASTC_6X6_SRGB: + case PTI_ASTC_6X6_LDR: + case PTI_ASTC_8X5_HDR: + case PTI_ASTC_8X5_SRGB: + case PTI_ASTC_8X5_LDR: + case PTI_ASTC_8X6_HDR: + case PTI_ASTC_8X6_SRGB: + case PTI_ASTC_8X6_LDR: + case PTI_ASTC_10X5_HDR: + case PTI_ASTC_10X5_SRGB: + case PTI_ASTC_10X5_LDR: + case PTI_ASTC_10X6_HDR: + case PTI_ASTC_10X6_SRGB: + case PTI_ASTC_10X6_LDR: + case PTI_ASTC_8X8_HDR: + case PTI_ASTC_8X8_SRGB: + case PTI_ASTC_8X8_LDR: + case PTI_ASTC_10X8_HDR: + case PTI_ASTC_10X8_SRGB: + case PTI_ASTC_10X8_LDR: + case PTI_ASTC_10X10_HDR: + case PTI_ASTC_10X10_SRGB: + case PTI_ASTC_10X10_LDR: + case PTI_ASTC_12X10_HDR: + case PTI_ASTC_12X10_SRGB: + case PTI_ASTC_12X10_LDR: + case PTI_ASTC_12X12_HDR: + case PTI_ASTC_12X12_SRGB: + case PTI_ASTC_12X12_LDR: + return true; + + case PTI_EMULATED: +#ifdef FTE_TARGET_WEB + case PTI_WHOLEFILE: //UNKNOWN! +#endif + case PTI_MAX: + return false; + } + return false; +} + const char *Image_FormatName(uploadfmt_t fmt) { switch(fmt) @@ -8566,10 +9413,12 @@ const char *Image_FormatName(uploadfmt_t fmt) case PTI_BGRX8_SRGB: return "BGRX8_SRGB"; case PTI_A2BGR10: return "A2BGR10"; case PTI_E5BGR9: return "E5BGR9"; + case PTI_B10G11R11F: return "B10G11R11F"; case PTI_R16F: return "R16F"; case PTI_R32F: return "R32F"; case PTI_RGBA16F: return "RGBA16F"; case PTI_RGBA32F: return "RGBA32F"; + case PTI_RGB32F: return "RGB32F"; case PTI_R16: return "R16"; case PTI_RGBA16: return "RGBA16"; case PTI_P8: return "P8"; @@ -8975,8 +9824,8 @@ static qboolean Image_DecompressFormat(struct pendingtextureinfo *mips, const ch case PTI_ASTC_12X10_HDR: case PTI_ASTC_12X12_HDR: #if defined(DECOMPRESS_ASTC) && defined(ASTC_WITH_HDR) - decodefunc = Image_Decode_ASTC_HDR_E5_Block; - rcoding = PTI_E5BGR9; +// decodefunc = Image_Decode_ASTC_HDR_E5_Block; +// rcoding = PTI_E5BGR9; decodefunc64 = Image_Decode_ASTC_HDR_HF_Block; rcoding = PTI_RGBA16F; @@ -9131,9 +9980,34 @@ static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int fla if (sh_config.texfmt[mips->encoding]) return; //okay, that got it. - //Fixme: PTI_E5BGR9 -> PTI_RGBA32F - //Fixme: PTI_RGBA16F -> PTI_RGBA32F - //FIXME: PTI_RGBA32F -> PTI_RGBA8 + if (mips->encoding == PTI_E5BGR9) + { //assume that the gpu also doesn't support float formats when it doesn't support this stuff. + Image_E5BGR9ToByte(mips); + mips->encoding = PTI_RGBX8; + if (sh_config.texfmt[mips->encoding]) + return; //okay, that got it. + } + if (mips->encoding == PTI_RGBA16F || mips->encoding == PTI_R16F) + { //just convert straight to bytes, if half doesn't work then floats probably won't either. + Image_HalfToByte(mips, (mips->encoding==PTI_RGBA16F)?4:1); + mips->encoding = (mips->encoding==PTI_RGBA16F)?PTI_RGBA8:PTI_R8; + if (sh_config.texfmt[mips->encoding]) + return; //okay, that got it. + } + if (mips->encoding == PTI_RGB32F) + { + Image_RGB32FToFloat(mips); + mips->encoding = PTI_RGBA32F; + if (sh_config.texfmt[mips->encoding]) + return; //okay, that got it. + } + if (mips->encoding == PTI_RGBA32F || mips->encoding == PTI_R32F) + { + Image_FloatToByte(mips, (mips->encoding==PTI_RGBA32F)?4:1); + mips->encoding = (mips->encoding==PTI_RGBA32F)?PTI_RGBA8:PTI_R8; + if (sh_config.texfmt[mips->encoding]) + return; //okay, that got it. + } if (mips->encoding == PTI_RGBA16) { @@ -9751,7 +10625,9 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case PTI_BGR8: case PTI_RGB8_SRGB: case PTI_BGR8_SRGB: + case PTI_RGB32F: case PTI_E5BGR9: + case PTI_B10G11R11F: case PTI_RGBX8: case PTI_BGRX8: case PTI_RGBX8_SRGB: diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 3b510c358..de51ccfe3 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -2140,6 +2140,7 @@ uploadfmt_t PR_TranslateTextureFormat(int qcformat) case 11: return PTI_RGB565; case 12: return PTI_RGBA4444; case 13: return PTI_RG8; + case 14: return PTI_RGB32F; default:return PTI_INVALID; } diff --git a/engine/client/pr_skelobj.c b/engine/client/pr_skelobj.c index 310025e88..f36b1de5a 100644 --- a/engine/client/pr_skelobj.c +++ b/engine/client/pr_skelobj.c @@ -929,7 +929,7 @@ void skel_generateragdoll_f(void) //FIXME: only write out the bodies specified as arguments, and with no // for (i = 0; i < numbones; i++) - VFS_PUTS(f, va("//body b_%s %s\n", bones[i].name, bones[i].name)); + VFS_PUTS(f, va("//body \"b_%s\" \"%s\"\n", bones[i].name, bones[i].name)); VFS_PUTS(f, "\n"); VFS_PUTS(f, "updatejoint default\n"); diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index ce849c40f..2cdd5ccfd 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -34,25 +34,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. qboolean r_pushdepth; #endif -extern cvar_t r_ambient; +extern cvar_t r_ambient; -static vec3_t modelorg; /*set before recursively entering the visible surface finder*/ +static vec3_t modelorg; /*set before recursively entering the visible surface finder*/ -model_t *currentmodel; +model_t *currentmodel; -size_t maxblocksize; -vec3_t *blocknormals; -unsigned *blocklights; +static size_t maxblocksize; +static vec3_t *blocknormals; +static unsigned *blocklights; lightmapinfo_t **lightmap; int numlightmaps; -static const float rgb9e5tab[32] = { //multipliers for the 9-bit mantissa, according to the biased mantissa - //aka: pow(2, biasedexponent - bias-bits) where bias is 15 and bits is 9 - 1.0/(1<<24), 1.0/(1<<23), 1.0/(1<<22), 1.0/(1<<21), 1.0/(1<<20), 1.0/(1<<19), 1.0/(1<<18), 1.0/(1<<17), - 1.0/(1<<16), 1.0/(1<<15), 1.0/(1<<14), 1.0/(1<<13), 1.0/(1<<12), 1.0/(1<<11), 1.0/(1<<10), 1.0/(1<<9), - 1.0/(1<<8), 1.0/(1<<7), 1.0/(1<<6), 1.0/(1<<5), 1.0/(1<<4), 1.0/(1<<3), 1.0/(1<<2), 1.0/(1<<1), - 1.0, 1.0*(1<<1), 1.0*(1<<2), 1.0*(1<<3), 1.0*(1<<4), 1.0*(1<<5), 1.0*(1<<6), 1.0*(1<<7), -}; +extern const float rgb9e5tab[32]; extern mleaf_t *r_vischain; // linked list of visible leafs diff --git a/engine/client/render.h b/engine/client/render.h index 6654f231e..ef85b9c44 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -473,6 +473,7 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips); void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight); const char *Image_FormatName(uploadfmt_t encoding); +qboolean Image_FormatHasAlpha(uploadfmt_t encoding); image_t *Image_LoadTexture (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname, const char *fname, qbyte *filedata, int filesize); diff --git a/engine/client/screen.h b/engine/client/screen.h index b55d01166..c514f09e3 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -117,11 +117,13 @@ typedef enum uploadfmt //floating point formats PTI_R16F, PTI_R32F, + PTI_RGB32F, //so qc can just use vectors for rgb. not really recommended. PTI_RGBA16F, //consider using e5bgr9 or bc6/astc PTI_RGBA32F, //usually overkill //packed/misaligned formats: these are specified in native endian order (high bits listed first because that's how things are represented in hex), so may need byte swapping... PTI_A2BGR10, //mostly for rendertargets, might also be useful for overbight lightmaps. PTI_E5BGR9, //mostly for fancy lightmaps + PTI_B10G11R11F, //unshared exponents PTI_RGB565, //16bit alphaless format. PTI_RGBA4444, //16bit format (gl) PTI_ARGB4444, //16bit format (d3d) diff --git a/engine/common/config_freecs.h b/engine/common/config_freecs.h index 39c895c1f..b29b7957f 100644 --- a/engine/common/config_freecs.h +++ b/engine/common/config_freecs.h @@ -163,6 +163,7 @@ //#define IMAGEFMT_HDR //#define IMAGEFMT_PBM //#define IMAGEFMT_PSD +//#define IMAGEFMT_XCF //flattens, most of the time //#define IMAGEFMT_VTF //#define IPLOG //#define MVD_RECORDING diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index 4d1d45f9a..703afbde4 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -93,6 +93,7 @@ #define IMAGEFMT_ASTC //lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files. #define IMAGEFMT_PBM //pbm/ppm/pgm/pfm family formats. #define IMAGEFMT_PSD //baselayer only. +#define IMAGEFMT_XCF //flattens, most of the time #define IMAGEFMT_HDR //an RGBE format. #define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load. #define IMAGEFMT_TGA //somewhat mandatory diff --git a/engine/common/config_minimal.h b/engine/common/config_minimal.h index e2fc06816..9ce4debe2 100644 --- a/engine/common/config_minimal.h +++ b/engine/common/config_minimal.h @@ -95,6 +95,7 @@ //#define IMAGEFMT_ASTC //lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files. //#define IMAGEFMT_PBM //pbm/ppm/pgm/pfm family formats. //#define IMAGEFMT_PSD //baselayer only. +//#define IMAGEFMT_XCF //flattens, most of the time //#define IMAGEFMT_HDR //an RGBE format. //#define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load. #define IMAGEFMT_TGA //somewhat mandatory diff --git a/engine/common/config_nocompat.h b/engine/common/config_nocompat.h index 9f74114b2..3ece3862d 100644 --- a/engine/common/config_nocompat.h +++ b/engine/common/config_nocompat.h @@ -93,6 +93,7 @@ //#define IMAGEFMT_ASTC //lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files. //#define IMAGEFMT_PBM //pbm/ppm/pgm/pfm family formats. //#define IMAGEFMT_PSD //baselayer only. +//#define IMAGEFMT_XCF //flattens, most of the time //#define IMAGEFMT_HDR //an RGBE format. #define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load. #define IMAGEFMT_TGA //somewhat mandatory diff --git a/engine/common/config_wastes.h b/engine/common/config_wastes.h index 140619cde..4963908f3 100644 --- a/engine/common/config_wastes.h +++ b/engine/common/config_wastes.h @@ -178,6 +178,7 @@ //#define IMAGEFMT_HDR //#define IMAGEFMT_PBM //#define IMAGEFMT_PSD +//#define IMAGEFMT_XCF //flattens, most of the time #define IMAGEFMT_TGA #define IMAGEFMT_LMP //#define IMAGEFMT_PNG diff --git a/engine/common/fs.c b/engine/common/fs.c index ef2698b69..03cfffff4 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -845,7 +845,7 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void #endif else if (!Q_strcasecmp(ext, "tga") || !Q_strcasecmp(ext, "png") || !Q_strcasecmp(ext, "jpg") || !Q_strcasecmp(ext, "jpeg")|| !Q_strcasecmp(ext, "lmp") || !Q_strcasecmp(ext, "ico") || !Q_strcasecmp(ext, "pcx") || !Q_strcasecmp(ext, "bmp") || !Q_strcasecmp(ext, "dds") || !Q_strcasecmp(ext, "ktx") || !Q_strcasecmp(ext, "vtf") || !Q_strcasecmp(ext, "psd") || - !Q_strcasecmp(ext, "astc")|| !Q_strcasecmp(ext, "htga")|| !Q_strcasecmp(ext, "exr") || + !Q_strcasecmp(ext, "astc")|| !Q_strcasecmp(ext, "htga")|| !Q_strcasecmp(ext, "exr") || !Q_strcasecmp(ext, "xcf") || !Q_strcasecmp(ext, "pbm") || !Q_strcasecmp(ext, "ppm") || !Q_strcasecmp(ext, "pgm") || !Q_strcasecmp(ext, "pam") || !Q_strcasecmp(ext, "pfm") || !Q_strcasecmp(ext, "hdr") ) { //FIXME: image replacements are getting in the way here. @@ -3877,7 +3877,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) { searchpath_t *oldpaths; searchpath_t *next; - int i; + int i, j; int orderkey; COM_AssertMainThread("FS_ReloadPackFilesFlags"); @@ -3912,7 +3912,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) #ifdef NQPROT standard_quake = true; #endif - for (i = 0; i < sizeof(fs_manifest->gamepath) / sizeof(fs_manifest->gamepath[0]); i++) + for (i = 0; i < countof(fs_manifest->gamepath); i++) { char *dir = fs_manifest->gamepath[i].path; if (dir && fs_manifest->gamepath[i].base) @@ -3973,7 +3973,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) next->flags |= SPF_BASEPATH; com_base_searchpaths = com_searchpaths; - for (i = 0; i < sizeof(fs_manifest->gamepath) / sizeof(fs_manifest->gamepath[0]); i++) + for (i = 0; i < countof(fs_manifest->gamepath); i++) { char *dir = fs_manifest->gamepath[i].path; if (dir && !fs_manifest->gamepath[i].base) @@ -3987,6 +3987,15 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) continue; } + for (j = 0; j < countof(fs_manifest->gamepath); j++) + { + char *dir2 = fs_manifest->gamepath[j].path; + if (dir2 && fs_manifest->gamepath[j].base && !strcmp(dir, dir2)) + break; + } + if (j < countof(fs_manifest->gamepath)) + continue; //already loaded above. don't mess up gameonly_gamedir. + if (*dir == '*') { } diff --git a/engine/common/q1bsp.c b/engine/common/q1bsp.c index dfc24660d..81d25e6e7 100644 --- a/engine/common/q1bsp.c +++ b/engine/common/q1bsp.c @@ -1338,11 +1338,11 @@ static unsigned int Q1BSP_TranslateContents(int contents) case Q1CONTENTS_LAVA: return FTECONTENTS_LAVA; case Q1CONTENTS_SKY: - return FTECONTENTS_SKY; + return FTECONTENTS_SKY|FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP; case Q1CONTENTS_LADDER: return FTECONTENTS_LADDER; case Q1CONTENTS_CLIP: - return FTECONTENTS_PLAYERCLIP; + return FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP; case Q1CONTENTS_TRANS: return FTECONTENTS_SOLID; diff --git a/engine/common/world.h b/engine/common/world.h index f4ab4d187..3549b5baf 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -19,6 +19,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // world.h +#include "quakedef.h" + typedef struct plane_s { vec3_t normal; diff --git a/engine/d3d/d3d11_image.c b/engine/d3d/d3d11_image.c index d673e4469..0a47baabd 100644 --- a/engine/d3d/d3d11_image.c +++ b/engine/d3d/d3d11_image.c @@ -221,7 +221,9 @@ qboolean D3D11_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mi case PTI_E5BGR9: tdesc.Format = DXGI_FORMAT_R9G9B9E5_SHAREDEXP; break; - + case PTI_B10G11R11F: + tdesc.Format = DXGI_FORMAT_R11G11B10_FLOAT; + break; case PTI_RGBA8_SRGB: tdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; break; @@ -299,6 +301,9 @@ qboolean D3D11_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mi case PTI_RGBA32F: tdesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break; + case PTI_RGB32F: + tdesc.Format = DXGI_FORMAT_R32G32B32_FLOAT; + break; case PTI_L8: //UNSUPPORTED case PTI_P8: //R8, but different usage. case PTI_R8: diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 9ba2ff499..f73717042 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -163,9 +163,12 @@ void GL_SetupFormats(void) // glfmtc(PTI_RGBA5551,(ver>=3)?GL_RGB555A1:0, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, tc_rgba1); } if (GL_CheckExtension("GL_OES_texture_half_float")) - glfmtc(PTI_RGBA16F, (ver>=3)?GL_RGBA16F:0, GL_RGBA, GL_RGBA, GL_HALF_FLOAT_OES, 0); //not to be confused with GL_HALF_FLOAT[_ARB] which has a different value + glfmt(PTI_RGBA16F, (ver>=3)?GL_RGBA16F:0, GL_RGBA, GL_RGBA, GL_HALF_FLOAT_OES); //not to be confused with GL_HALF_FLOAT[_ARB] which has a different value if (GL_CheckExtension("GL_OES_texture_float")) - glfmtc(PTI_RGBA32F, (ver>=3)?GL_RGBA32F:0, GL_RGBA, GL_RGBA, GL_FLOAT, 0); + { + glfmt(PTI_RGBA32F, (ver>=3)?GL_RGBA32F:0, GL_RGBA, GL_RGBA, GL_FLOAT); + glfmt(PTI_RGB32F, (ver>=3)?GL_RGB32F:0, GL_RGB, GL_RGB, GL_FLOAT); + } if (GL_CheckExtension("GL_WEBGL_depth_texture")) { //24bit is okay with this one. @@ -236,6 +239,8 @@ void GL_SetupFormats(void) } if (ver >= 3.0 || GL_CheckExtension("GL_EXT_texture_shared_exponent")) glfmt(PTI_E5BGR9, GL_RGB9_E5, GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV); + if (ver >= 3.0 || GL_CheckExtension("GL_EXT_packed_float")) + glfmt(PTI_B10G11R11F, GL_R11F_G11F_B10F, GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV); if (ver >= 3.0 || GL_CheckExtension("GL_EXT_packed_pixels")) //so gl1.2 then. glfmt(PTI_A2BGR10, GL_RGB10_A2, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV); if (ver >= 3.0 || GL_CheckExtension("GL_ARB_texture_rg")) @@ -260,6 +265,8 @@ void GL_SetupFormats(void) glfmtc(PTI_RGBA16F, GL_RGBA16F, GL_RGBA, GL_RGBA, GL_HALF_FLOAT, 0); glfmtc(PTI_RGBA32F, GL_RGBA32F, GL_RGBA, GL_RGBA, GL_FLOAT, 0); + + glfmt(PTI_RGB32F, GL_RGB32F, GL_RGB, GL_RGB, GL_FLOAT); } if (ver >= 1.2 && !gl_config_gles) { diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index 745c98c74..b3efe9187 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -58,6 +58,8 @@ void R_SetSky(const char *sky) const char *shadername; extern cvar_t r_skyboxname; Q_strncpyz(cl.skyname, sky, sizeof(cl.skyname)); + if (qrenderer <= QR_NONE) + return; //not ready yet... if (*r_skyboxname.string) //override it with the user's preference sky = r_skyboxname.string; diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h index 8e1d89833..854421d90 100644 --- a/engine/gl/glsupp.h +++ b/engine/gl/glsupp.h @@ -696,6 +696,11 @@ typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei #define GL_RG8_SNORM 0x8F95 /*opengl 3.1*/ #endif +#ifndef GL_R11F_G11F_B10F +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#endif + #ifndef GL_TEXTURE_SWIZZLE_R #define GL_TEXTURE_SWIZZLE_R 0x8E42 #define GL_TEXTURE_SWIZZLE_G 0x8E43 diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 0fcbeada6..4dd78644b 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -10842,7 +10842,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"crossproduct", PF_crossproduct, 0, 0, 0, 0, D("#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2))\nvector(vector v1, vector v2)", "Small helper function to calculate the crossproduct of two vectors.")}, {"pushmove", PF_pushmove, 0, 0, 0, 0, "float(entity pusher, vector move, vector amove)"}, #ifdef TERRAIN - {"terrain_edit", PF_terrain_edit, 0, 0, 0, 278, D("void(float action, optional vector pos, optional float radius, optional float quant, ...)", "Realtime terrain editing. Actions are the TEREDIT_ constants.")},// (??FTE_TERRAIN_EDIT?? + {"terrain_edit", PF_terrain_edit, 0, 0, 0, 278, D("__variant(float action, optional vector pos, optional float radius, optional float quant, ...)", "Realtime terrain editing. Actions are the TEREDIT_ constants.")},// (??FTE_TERRAIN_EDIT?? #define qcbrushface \ "typedef struct\n{\n" \ @@ -10924,8 +10924,8 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"drawline", PF_Fixme, 0, 0, 0, 315, D("void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag)", "Draws a 2d line between the two 2d points.")},// (EXT_CSQC) {"iscachedpic", PF_Fixme, 0, 0, 0, 316, D("float(string name)", "Checks to see if the image is currently loaded. Engines might lie, or cache between maps.")},// (EXT_CSQC) {"precache_pic", PF_Fixme, 0, 0, 0, 317, D("string(string name, optional float trywad)", "Forces the engine to load the named image. If trywad is specified, the specified name must any lack path and extension.")},// (EXT_CSQC) - {"r_uploadimage", PF_Fixme, 0, 0, 0, 0, D("void(string imagename, int width, int height, void *pixeldata, optional int datasize, optional int format)", "Updates a texture with the specified rgba data. Will be created if needed. If blobsize is specified then the image is decoded (eg .ktx or .dds data) instead of being raw R8G8B8A data. You'll typically want shaderforname to also generate a shader to use the texture.")}, - {"r_readimage", PF_Fixme, 0, 0, 0, 0, D("int*(string filename, __out int width, __out int height)", "Reads and decodes an image from disk, providing raw R8G8B8A pixel data. Should not be used for dds or ktx etc formats. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it.")}, + {"r_uploadimage", PF_Fixme, 0, 0, 0, 0, D("void(string imagename, int width, int height, void *pixeldata, optional int datasize, optional int format)", "Updates a texture with the specified rgba data (uploading it to the gpu). Will be created if needed. If datasize is specified then the image is decoded (eg .ktx or .dds data) instead of being raw R8G8B8A data. You'll typically want shaderforname to also generate a shader to use the texture.")}, + {"r_readimage", PF_Fixme, 0, 0, 0, 0, D("int*(string filename, __out int width, __out int height)", "Reads and decodes an image from disk, providing raw R8G8B8A8 pixel data. Should not be used for dds or ktx etc formats. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it.")}, {"drawgetimagesize",PF_Fixme, 0, 0, 0, 318, D("#define draw_getimagesize drawgetimagesize\nvector(string picname)", "Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution.")},// (EXT_CSQC) {"freepic", PF_Fixme, 0, 0, 0, 319, D("void(string name)", "Tells the engine that the image is no longer needed. The image will appear to be new the next time its needed.")},// (EXT_CSQC) //320 @@ -12544,6 +12544,7 @@ void PR_DumpPlatform_f(void) {"IMGFMT_R5G6B5", "const float", CS|MENU, D("Packed 16-bit colour pixel format."), 11}, {"IMGFMT_R4G4B4A4", "const float", CS|MENU, D("Packed 16-bit colour pixel format, with alpha"), 12}, {"IMGFMT_R8G8", "const float", CS|MENU, D("16-bit two-channel pixel format."), 13}, + {"IMGFMT_R32G32B32F", "const float", CS|MENU, D("A pixel format that matches QC's vector type."), 14}, {"RF_VIEWMODEL", "const float", CS, D("Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob."), CSQCRF_VIEWMODEL}, {"RF_EXTERNALMODEL", "const float", CS, D("Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible."), CSQCRF_EXTERNALMODEL}, diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 76072f2c0..81b846f86 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -1366,6 +1366,7 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay case PTI_RG8_SNORM: format = VK_FORMAT_R8G8_SNORM; break; case PTI_A2BGR10: format = VK_FORMAT_A2B10G10R10_UNORM_PACK32; break; case PTI_E5BGR9: format = VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; break; + case PTI_B10G11R11F: format = VK_FORMAT_B10G11R11_UFLOAT_PACK32; break; //swizzled/legacy formats case PTI_L8: format = VK_FORMAT_R8_UNORM; break; case PTI_L8A8: format = VK_FORMAT_R8G8_UNORM; break; @@ -1478,6 +1479,7 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay //misaligned formats case PTI_RGB8: format = VK_FORMAT_R8G8B8_UNORM; break; case PTI_BGR8: format = VK_FORMAT_B8G8R8_UNORM; break; + case PTI_RGB32F: format = VK_FORMAT_R32G32B32_SFLOAT; break; case PTI_RGB8_SRGB: format = VK_FORMAT_R8G8B8_SRGB; break; case PTI_BGR8_SRGB: format = VK_FORMAT_B8G8R8_SRGB; break; @@ -4126,6 +4128,7 @@ void VK_CheckTextureFormats(void) {PTI_ARGB1555, VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, {PTI_RGBA16F, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT}, {PTI_RGBA32F, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT}, + {PTI_RGB32F, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT}, {PTI_L8, VK_FORMAT_R8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT}, {PTI_L8A8, VK_FORMAT_R8G8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT}, {PTI_L8_SRGB, VK_FORMAT_R8_SRGB, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT}, diff --git a/imgtool.c b/imgtool.c index d3d3d635d..f3449b12e 100644 --- a/imgtool.c +++ b/imgtool.c @@ -3,6 +3,7 @@ #undef stderr #define stderr stdout +#include #ifdef _WIN32 #include #endif @@ -97,7 +98,7 @@ void FS_CreatePath(const char *pname, enum fs_relative relativeto) } free(t); } -qboolean Sys_remove (const char *path) +qboolean FS_Remove (const char *path, enum fs_relative relativeto) { //remove is part of c89. if (remove(path) == -1) @@ -293,7 +294,7 @@ void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags); int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, qintptr_t bufferstride, int width, int height, enum uploadfmt fmt, qboolean writemetadata); qboolean WriteTGA(const char *filename, enum fs_relative fsroot, const qbyte *fte_restrict rgb_buffer, qintptr_t bytestride, int width, int height, enum uploadfmt fmt); -static qboolean ImgTool_ASTCToLDR(uploadfmt_t fmt) +static enum uploadfmt ImgTool_ASTCToLDR(uploadfmt_t fmt) { if (fmt >= PTI_ASTC_FIRST && fmt <= PTI_ASTC_LAST) { @@ -302,6 +303,8 @@ static qboolean ImgTool_ASTCToLDR(uploadfmt_t fmt) if (fmt >= PTI_ASTC_4X4_SRGB) return (fmt-PTI_ASTC_4X4_SRGB)+PTI_ASTC_4X4_LDR; } + if (fmt == PTI_BC1_RGB) + return PTI_BC1_RGBA; return fmt; } #ifdef _WIN32 @@ -330,7 +333,7 @@ static void FS_MakeTempName(char *out, size_t outsize, char *prefix, char *suffi close(mkstemps(out, strlen(suffix))); //bsd4.3/posix1-2001 } #endif -static void 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; size_t m; @@ -344,6 +347,10 @@ static void ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, qboolean canktx = false; uploadfmt_t targfmt = args->newpixelformat; + //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)) + targfmt = PTI_BC1_RGB; + if (targfmt >= PTI_ASTC_FIRST && targfmt <= PTI_ASTC_LAST) { Q_snprintfz(command, sizeof(command), "astcenc -c"); @@ -361,8 +368,12 @@ static void ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, Q_snprintfz(command, sizeof(command), "nvcompress -bc4"); else if (targfmt == PTI_BC5_RG8) Q_snprintfz(command, sizeof(command), "nvcompress -bc5"); + else if (targfmt == PTI_BC6_RGB_SFLOAT || targfmt == PTI_BC6_RGB_UFLOAT) + Q_snprintfz(command, sizeof(command), "nvcompress -bc6"); + else if (targfmt == PTI_BC7_RGBA) + Q_snprintfz(command, sizeof(command), "nvcompress -bc7"); else - return; + return false; if (canktx) FS_MakeTempName(raw, sizeof(raw), "itr", ".ktx"); else @@ -382,6 +393,8 @@ static void ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, Q_strncatz(command, " -srgb", sizeof(command)); if (targfmt >= PTI_ASTC_4X4_HDR && targfmt <= PTI_ASTC_12X12_HDR) 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)); Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh); @@ -389,10 +402,13 @@ static void ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, { tmp.mip[0] = mips->mip[m]; tmp.mip[0].needfree = false; + (void)tmp; if (canktx) { +#ifdef IMAGEFMT_KTX if (!Image_WriteKTXFile(raw, FS_SYSTEM, &tmp)) +#endif break; } else @@ -426,11 +442,12 @@ static void ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, //do not warn for astc files, their block sizes are too weird. Image_BlockSizeForEncoding(targfmt, &bb, &bw, &bh); if (mips->mip[0].width%bw || mips->mip[0].height%bh) - Con_Printf("%s: d3d warning: %i*%i is not a multiple of %i*%i\n", inname, mips->mip[0].width, mips->mip[0].height, bw, bh); + Con_Printf("%s: mip0 of %i*%i is not a multiple of %i*%i (d3d warning)\n", inname, mips->mip[0].width, mips->mip[0].height, bw, bh); } - Sys_remove(raw); - Sys_remove(comp); + FS_Remove(raw, FS_SYSTEM); + FS_Remove(comp, FS_SYSTEM); + return true; } const char *COM_GetFileExtension (const char *in, const char *term) @@ -453,11 +470,15 @@ static void ImgTool_Convert(struct opts_s *args, const char *inname, const char size_t fsize; struct pendingtextureinfo *in; const char *outext = COM_GetFileExtension(outname, NULL); + qboolean allowcompressed = false; + + if (!strcmp(outext, ".dds") || !strcmp(outext, ".ktx")) + allowcompressed = true; indata = FS_LoadMallocFile(inname, &fsize); if (indata) { - in = Image_LoadMipsFromMemory(args->flags, inname, inname, indata, fsize); + in = Image_LoadMipsFromMemory(args->flags|(allowcompressed?0:IF_NOMIPMAP), inname, inname, indata, fsize); if (in) { printf("%s: %s, %i*%i, %i mips\n", inname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); @@ -468,8 +489,8 @@ static void ImgTool_Convert(struct opts_s *args, const char *inname, const char if (!(args->flags & IF_NOMIPMAP) && in->mipcount == 1) Image_GenerateMips(in, args->flags); - if (args->newpixelformat != PTI_INVALID) - ImgTool_ConvertPixelFormat(args, inname, in); + if (args->newpixelformat != PTI_INVALID && allowcompressed && ImgTool_ConvertPixelFormat(args, inname, in)) + printf("\t(Converted to %s)\n", Image_FormatName(in->encoding)); } if (!in->mipcount) @@ -478,28 +499,40 @@ static void ImgTool_Convert(struct opts_s *args, const char *inname, const char return; } - if (!strcmp(outext, ".ktx")) + if (0) + ; +#ifdef IMAGEFMT_KTX + else if (!strcmp(outext, ".ktx")) Image_WriteKTXFile(outname, FS_SYSTEM, in); +#endif +#ifdef IMAGEFMT_DDS else if (!strcmp(outext, ".dds")) Image_WriteDDSFile(outname, FS_SYSTEM, in); +#endif else { int bb,bw,bh; Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh); if (args->mipnum < in->mipcount) { - if (!strcmp(outext, ".png")) + if (0) + ; +#ifdef IMAGEFMT_PNG + else if (!strcmp(outext, ".png")) { #ifdef AVAIL_PNGLIB if (!Image_WritePNG(outname, FS_SYSTEM, 0, &in->mip[args->mipnum].data, 1, in->mip[args->mipnum].width*bb, in->mip[args->mipnum].width, in->mip[args->mipnum].height, in->encoding, false)) #endif Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); } +#endif +#ifdef IMAGEFMT_TGA else if (!strcmp(outext, ".tga")) { if (!WriteTGA(outname, FS_SYSTEM, in->mip[args->mipnum].data, in->mip[args->mipnum].width*bb, in->mip[args->mipnum].width, in->mip[args->mipnum].height, in->encoding)) Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); } +#endif else Con_Printf("%s: Unknown output file format\n", outname); } @@ -543,25 +576,77 @@ static void ImgTool_Info(struct opts_s *args, const char *inname) fflush(stdout); } -#ifdef _WIN32 -static void ImgTool_TreeConvert(struct opts_s *args, const char *srcpath, const char *destpath) +struct filelist_s { - Con_Printf("ImgTool_TreeConvert not implemented on windows.\n"); + const char **exts; + size_t numfiles; + struct { + char *name; + size_t baselen; //length up to but not including the filename extension. + } *file; + size_t maxfiles; //to avoid reallocs +}; +static void FileList_Release(struct filelist_s *list) +{ + size_t i; + for (i = 0; i < list->numfiles; i++) + free(list->file[i].name); + free(list->file); + list->numfiles = 0; + list->maxfiles = 0; +} +static void FileList_Add(struct filelist_s *list, char *fname) +{ + size_t i; + size_t baselen; + const char *ext = COM_GetFileExtension(fname, NULL); + for (i = 0; ; i++) + { + if (!list->exts[i]) + return; //extension wasn't in the list. + if (!strcmp(list->exts[i], ext)) + break; //one of the accepted extensions + } + baselen = ext?ext-fname:strlen(fname); + for (i = 0; i < list->numfiles; i++) + { + if (list->file[i].baselen == baselen && !strncasecmp(list->file[i].name, fname, baselen)) + { + Con_Printf("Ignoring dupe file %s (using %s)\n", fname, list->file[i].name); + return; //file already listed, but maybe with a different extension + } + } + if (i == list->maxfiles) + { + list->maxfiles += 64; + list->file = realloc(list->file, sizeof(*list->file)*list->maxfiles); + } + 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) +{ + Con_Printf("ImgTool_TreeScan not implemented on windows.\n"); } #else #include #include -static void ImgTool_TreeConvert(struct opts_s *args, const char *srcpath, const char *destpath) +static void ImgTool_TreeScan(struct filelist_s *list, const char *basepath, const char *subpath) { DIR *dir; char file[MAX_OSPATH]; - char dest[MAX_OSPATH]; struct dirent *ent; - dir = opendir(srcpath); + if (subpath && *subpath) + Q_snprintfz(file, sizeof(file), "%s/%s", basepath, subpath); + else + Q_snprintfz(file, sizeof(file), "%s", basepath); + dir = opendir(file); if (!dir) { - Con_Printf("Failed to open dir %s\n", srcpath); + Con_Printf("Failed to open dir %s\n", file); return; } for (;;) @@ -573,43 +658,200 @@ static void ImgTool_TreeConvert(struct opts_s *args, const char *srcpath, const continue; else if (ent->d_type == DT_DIR) { - Q_snprintfz(file, sizeof(file), "%s/%s", srcpath, ent->d_name); - Q_snprintfz(dest, sizeof(dest), "%s/%s", destpath, ent->d_name); - Con_Printf("Recurse %s -> %s\n", file, dest); - ImgTool_TreeConvert(args, file, dest); + if (!subpath) + continue; + if (*subpath) + Q_snprintfz(file, sizeof(file), "%s/%s", subpath, ent->d_name); + else + Q_snprintfz(file, sizeof(file), "%s", ent->d_name); + ImgTool_TreeScan(list, basepath, file); } else if (ent->d_type == DT_REG) { - const char *ext = COM_GetFileExtension(ent->d_name, NULL); - if (!strcmp(ext, ".png")||!strcmp(ext, ".bmp")||!strcmp(ext, ".tga")||!strcmp(ext, ".jpg")||!strcmp(ext, ".exr")||!strcmp(ext, ".hdr")) - { - struct stat statsrc, statdst; - Q_snprintfz(file, sizeof(file), "%s/%s", srcpath, ent->d_name); - Q_snprintfz(dest, sizeof(dest), "%s/%s", destpath, ent->d_name); - Q_snprintfz(dest+strlen(dest)-strlen(ext), sizeof(dest)-(strlen(dest)-strlen(ext)), ".ktx"); - - if (stat(file, &statsrc) < 0) - { - Con_Printf("stat(\"%s\") failed...\n", file); - continue; - } - if (stat(dest, &statdst) < 0) - statdst.st_mtim.tv_sec = INT_MIN; //make it look old - if (statdst.st_mtim.tv_sec <= statsrc.st_mtim.tv_sec) - { - Con_Printf("Image file %s -> %s\n", file, dest); - FS_CreatePath(dest, FS_SYSTEM); - ImgTool_Convert(args, file, dest); - } - else - Con_Printf("Unmodified image file %s -> %s\n", file, dest); - } + if (subpath && *subpath) + 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); } } closedir(dir); - return; } #endif +static void ImgTool_TreeConvert(struct opts_s *args, const char *srcpath, const char *destpath) +{ + size_t newfiles=0, skippedfiles=0, processedfiles=0; + char file[MAX_OSPATH]; + char dest[MAX_OSPATH]; + const char *exts[] = {".png", ".bmp", ".tga", ".jpg", ".exr", ".hdr", NULL}; + struct filelist_s list = {exts}; + size_t i, destlen = strlen(destpath)+1; + ImgTool_TreeScan(&list, srcpath, ""); + + if (!list.numfiles) + Con_Printf("No suitable files found in directory: %s\n", srcpath); + + for (i = 0; i < list.numfiles; i++) + { + struct stat statsrc, statdst; + Q_snprintfz(file, sizeof(file), "%s/%s", srcpath, list.file[i].name); + Q_snprintfz(dest, sizeof(dest), "%s/%s", destpath, list.file[i].name); + Q_snprintfz(dest+destlen+list.file[i].baselen, sizeof(dest)-destlen-list.file[i].baselen, ".dds"); + + if (stat(file, &statsrc) < 0) + { + Con_Printf("stat(\"%s\") failed...\n", file); + continue; + } + if (stat(dest, &statdst) < 0) + { + statdst.st_mtim.tv_sec = INT_MIN; //make it look old + newfiles++; + } + if (statdst.st_mtim.tv_sec <= statsrc.st_mtim.tv_sec) + { + processedfiles++; +// Con_Printf("Image file %s -> %s\n", file, dest); + FS_CreatePath(dest, FS_SYSTEM); + ImgTool_Convert(args, file, dest); + } + else + { + skippedfiles++; +// Con_Printf("Unmodified image file %s -> %s\n", file, dest); + } + } + Con_Printf("found: %u, processed: %u, skipped: %u, new: %u\n", (unsigned int)list.numfiles, (unsigned int)processedfiles, (unsigned int)skippedfiles, (unsigned int)newfiles); + FileList_Release(&list); + return; +} + + + + + + +/* +typedef struct +{ + long offset; // Position of the entry in WAD + long dsize; // Size of the entry in WAD file + long 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 + long num; //number of entries + long offset; //location of directory +} wad2_t; +static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const char *destpath) +{ + char file[MAX_OSPATH]; + const char *exts[] = {".png", ".bmp", ".tga", ".jpg", ".exr", ".hdr", NULL}; + struct filelist_s list = {exts}; + size_t i, u; + vfsfile_t *f; + char *inname; + qbyte *indata; + size_t fsize; + wad2_t wad2; + wad2entry_t *wadentries = NULL, *entry; + size_t maxentries; + miptex_t mip; + ImgTool_TreeScan(&list, srcpath, NULL); + + f = FS_OpenVFS(destpath, "wb", FS_SYSTEM); + wad2.magic[0] = 'W'; + wad2.magic[1] = 'A'; + wad2.magic[2] = 'D'; + wad2.magic[3] = '3'; //wad3 instead of 2, so we can include a palette for tools to validate against + VFS_WRITE(f, &wad2, 12); + + //try to decompress everything to a nice friendly palletizable range. + for (u = 1; u < countof(sh_config.texfmt); u++) + sh_config.texfmt[u] = (u==PTI_RGBA8)||(u==PTI_RGBX8)||(u==PTI_P8); + + for (i = 0; i < list.numfiles; i++) + { + Q_snprintfz(file, sizeof(file), "%s/%s", srcpath, list.file[i].name); + inname = list.file[i].name; + if (list.file[i].baselen > 15) + { + Con_Printf("Path too long for wad - %s\n", inname); + continue; + } + indata = FS_LoadMallocFile(file, &fsize); + if (indata) + { + struct pendingtextureinfo *in = Image_LoadMipsFromMemory(args->flags|IF_PALETTIZE, inname, file, indata, fsize); + Image_GenerateMips(in, args->flags); + if (in) + { + if (in->mipcount == 1) + Image_GenerateMips(in, args->flags); + + if (!in->mipcount) + { + printf("%s: unable to load any mips\n", inname); + continue; + } + } + if (args->mipnum >= in->mipcount) + { + printf("%s: not enough mips\n", inname); + continue; + } + if ((in->mip[args->mipnum].width|in->mip[args->mipnum].height) & 15) + printf("%s(%i): WARNING: not multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[args->mipnum].width, in->mip[args->mipnum].height); + + if (wad2.num == maxentries) + { + maxentries += 64; + wadentries = realloc(wadentries, sizeof(*wadentries)*maxentries); + } + entry = &wadentries[wad2.num++]; + Q_strncpyz(entry->name, inname, 16); + entry->name[list.file[i].baselen] = 0; //kill any .tga + entry->type = TYP_MIPTEX; + entry->cmprs = 0; + entry->dummy = 0; + entry->offset = VFS_TELL(f); + + memcpy(mip.name, entry->name, sizeof(mip.name)); + mip.width = in->mip[args->mipnum].width; + mip.height = in->mip[args->mipnum].height; + mip.offsets[0] = in->mip[args->mipnum+0].datasize?sizeof(mip):0; + mip.offsets[1] = in->mip[args->mipnum+1].datasize?mip.offsets[args->mipnum+0]+in->mip[args->mipnum+0].datasize:0; + mip.offsets[2] = in->mip[args->mipnum+2].datasize?mip.offsets[args->mipnum+1]+in->mip[args->mipnum+1].datasize:0; + mip.offsets[3] = in->mip[args->mipnum+3].datasize?mip.offsets[args->mipnum+2]+in->mip[args->mipnum+2].datasize:0; + + VFS_WRITE(f, &mip, sizeof(mip)); + VFS_WRITE(f, in->mip[args->mipnum+0].data, in->mip[args->mipnum+0].datasize); + VFS_WRITE(f, in->mip[args->mipnum+1].data, in->mip[args->mipnum+1].datasize); + VFS_WRITE(f, in->mip[args->mipnum+2].data, in->mip[args->mipnum+2].datasize); + VFS_WRITE(f, in->mip[args->mipnum+3].data, in->mip[args->mipnum+3].datasize); + if (wad2.magic[3] == '3') + { + VFS_WRITE(f, "\x00\x01", 2); + VFS_WRITE(f, host_basepal, 256*3); + } + + entry->size = entry->dsize = VFS_TELL(f)-entry->offset; + } + } + wad2.offset = VFS_TELL(f); + VFS_WRITE(f, wadentries, sizeof(*wadentries)*wad2.num); + VFS_SEEK(f, 0); + VFS_WRITE(f, &wad2, sizeof(wad2)); + VFS_CLOSE(f); + + FileList_Release(&list); +} +*/ int main(int argc, const char **argv) { @@ -617,7 +859,8 @@ int main(int argc, const char **argv) { mode_info, mode_convert, - mode_autotree + mode_autotree, + mode_genwad } mode = mode_info; size_t u, f; struct opts_s args; @@ -652,9 +895,10 @@ int main(int argc, const char **argv) 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 [--nomips] in.png out.dds\n", argv[0]); - Con_Printf("convert : %s -c in.exr out.dds\n", argv[0]); - Con_Printf("decompress: %s -d [--exportmip 0] [--nomips] in.ktx out.png\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("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 --genwad [--exportmip 2] srcdir out.wad\n", argv[0]); // Con_Printf("auto : %s --astc_6x6_ldr -r _postfix.png srcdir destdir\n", argv[0]); Image_PrintInputFormatVersions(); @@ -667,13 +911,15 @@ showhelp: Con_Printf(" --%-15s %.2fbpp (requires astcenc)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); else if (f==PTI_BC1_RGB||f==PTI_BC1_RGBA||f==PTI_BC2_RGBA||f==PTI_BC3_RGBA||f==PTI_BC4_R8||f==PTI_BC5_RG8) Con_Printf(" --%-15s %.2fbpp (requires nvcompress)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); + else if (f==PTI_BC6_RGB_UFLOAT || f==PTI_BC6_RGB_SFLOAT || f==PTI_BC7_RGBA) + Con_Printf(" --%-15s %.2fbpp (requires nvcompress 2.1+)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); } break; } else if (!strcmp(argv[u], "-c") || !strcmp(argv[u], "--convert")) mode = mode_convert; else if (!strcmp(argv[u], "-d") || !strcmp(argv[u], "--decompress")) - { + { //remove any (weird) gpu formats for (f = PTI_BC1_RGB; f < PTI_ASTC_LAST; f++) sh_config.texfmt[f] = false; mode = mode_convert; @@ -682,10 +928,16 @@ showhelp: mode = mode_autotree; else if (!strcmp(argv[u], "-i") || !strcmp(argv[u], "--info")) mode = mode_info; + else if (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwad")) + mode = mode_genwad; else if (!strcmp(argv[u], "--nomips") ) args.flags |= IF_NOMIPMAP; else if (!strcmp(argv[u], "--mips")) args.flags &= ~IF_NOMIPMAP; + else if (!strcmp(argv[u], "--premul") ) + args.flags |= IF_PREMULTIPLYALPHA; + else if (!strcmp(argv[u], "--nopremul")) + args.flags &= ~IF_PREMULTIPLYALPHA; else if (!strcmp(argv[u], "--exportmip")) { char *e = "erk"; @@ -737,6 +989,14 @@ showhelp: u++; } } + else if (mode == mode_genwad) + { + if (u+1 < argc) + { + //ImgTool_WadConvert(&args, argv[u], argv[u+1]); + u++; + } + } } } return 0; diff --git a/plugins/bullet/bulletplug.cpp b/plugins/bullet/bulletplug.cpp index ae3f475d7..2f1675432 100644 --- a/plugins/bullet/bulletplug.cpp +++ b/plugins/bullet/bulletplug.cpp @@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define Plug_Init Plug_Bullet_Init #pragma comment(lib,"../../plugins/bullet/libs/bullet_dbg.lib") #endif - +#include "quakedef.h" #include "../plugin.h" #include "../engine.h"