From c39d9bef62a4df76d4be0fe34fd51f12adcef364 Mon Sep 17 00:00:00 2001 From: SiPlus Date: Sat, 30 May 2015 22:06:19 +0300 Subject: [PATCH] KTX support - something Git didn't want to commit --- Makefile | 8 + download-gamepacks.sh | 3 +- libs/bytestreamutils.h | 35 ++++ plugins/image/image.cpp | 22 +++ tools/quake3/common/imagelib.c | 300 +++++++++++++++++++++++++++++++++ tools/quake3/common/imagelib.h | 2 + tools/quake3/q3map2/image.c | 10 ++ 7 files changed, 379 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9eee1212..51b3fa9f 100644 --- a/Makefile +++ b/Makefile @@ -536,6 +536,7 @@ $(INSTALLDIR)/q3map2.$(EXE): \ tools/quake3/q3map2/vis.o \ tools/quake3/q3map2/writebsp.o \ libddslib.$(A) \ + libetclib.$(A) \ libfilematch.$(A) \ libl_net.$(A) \ libmathlib.$(A) \ @@ -585,6 +586,10 @@ libddslib.$(A): CPPFLAGS_EXTRA := -Ilibs libddslib.$(A): \ libs/ddslib/ddslib.o \ +libetclib.$(A): CPPFLAGS_EXTRA := -Ilibs +libetclib.$(A): \ + libs/etclib.o \ + $(INSTALLDIR)/q3data.$(EXE): LIBS_EXTRA := $(LIBS_XML) $(LIBS_GLIB) $(LIBS_ZLIB) $(INSTALLDIR)/q3data.$(EXE): CPPFLAGS_EXTRA := $(CPPFLAGS_XML) $(CPPFLAGS_GLIB) $(CPPFLAGS_ZLIB) -Itools/quake3/common -Ilibs -Iinclude $(INSTALLDIR)/q3data.$(EXE): \ @@ -609,6 +614,7 @@ $(INSTALLDIR)/q3data.$(EXE): \ tools/quake3/q3data/stripper.o \ tools/quake3/q3data/video.o \ libfilematch.$(A) \ + libetclib.$(A) \ libl_net.$(A) \ libmathlib.$(A) \ $(if $(findstring $(OS),Win32),icons/q3data.o,) \ @@ -803,9 +809,11 @@ $(INSTALLDIR)/modules/image.$(DLL): \ plugins/image/dds.o \ plugins/image/image.o \ plugins/image/jpeg.o \ + plugins/image/ktx.o \ plugins/image/pcx.o \ plugins/image/tga.o \ libddslib.$(A) \ + libetclib.$(A) \ $(INSTALLDIR)/modules/imageq2.$(DLL): CPPFLAGS_EXTRA := -Ilibs -Iinclude $(INSTALLDIR)/modules/imageq2.$(DLL): \ diff --git a/download-gamepacks.sh b/download-gamepacks.sh index 7717cd2d..ff5b9840 100755 --- a/download-gamepacks.sh +++ b/download-gamepacks.sh @@ -158,5 +158,6 @@ pack QuakePack GPL zip1 http://ingar.satgnu.net/files/gtkradiant pack TremulousPack proprietary zip1 http://ingar.satgnu.net/files/gtkradiant/gamepacks/TremulousPack.zip pack UFOAIPack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/UFOAIPack/branches/1.5/ #pack WarsowPack GPL svn https://svn.bountysource.com/wswpack/trunk/netradiant/games/WarsowPack/ -pack WarsowPack GPL zip1 http://ingar.satgnu.net/files/gtkradiant/gamepacks/WarsowPack.zip +#pack WarsowPack GPL zip1 http://ingar.satgnu.net/files/gtkradiant/gamepacks/WarsowPack.zip +pack WarsowPack GPL git https://github.com/Warsow/NetRadiantPack.git pack XonoticPack GPL git http://git.xonotic.org/xonotic/netradiant-xonoticpack.git diff --git a/libs/bytestreamutils.h b/libs/bytestreamutils.h index 156333a1..90a9048a 100644 --- a/libs/bytestreamutils.h +++ b/libs/bytestreamutils.h @@ -82,6 +82,13 @@ inline int16_t istream_read_int16_le( InputStreamType& istream ){ return value; } +template +inline int16_t istream_read_int16_be( InputStreamType& istream ){ + int16_t value; + istream_read_big_endian( istream, value ); + return value; +} + template inline uint16_t istream_read_uint16_le( InputStreamType& istream ){ uint16_t value; @@ -89,6 +96,13 @@ inline uint16_t istream_read_uint16_le( InputStreamType& istream ){ return value; } +template +inline uint16_t istream_read_uint16_be( InputStreamType& istream ){ + uint16_t value; + istream_read_big_endian( istream, value ); + return value; +} + template inline int32_t istream_read_int32_le( InputStreamType& istream ){ int32_t value; @@ -96,6 +110,13 @@ inline int32_t istream_read_int32_le( InputStreamType& istream ){ return value; } +template +inline int32_t istream_read_int32_be( InputStreamType& istream ){ + int32_t value; + istream_read_big_endian( istream, value ); + return value; +} + template inline uint32_t istream_read_uint32_le( InputStreamType& istream ){ uint32_t value; @@ -103,6 +124,13 @@ inline uint32_t istream_read_uint32_le( InputStreamType& istream ){ return value; } +template +inline uint32_t istream_read_uint32_be( InputStreamType& istream ){ + uint32_t value; + istream_read_big_endian( istream, value ); + return value; +} + template inline float istream_read_float32_le( InputStreamType& istream ){ float value; @@ -110,6 +138,13 @@ inline float istream_read_float32_le( InputStreamType& istream ){ return value; } +template +inline float istream_read_float32_be( InputStreamType& istream ){ + float value; + istream_read_big_endian( istream, value ); + return value; +} + template inline typename InputStreamType::byte_type istream_read_byte( InputStreamType& istream ){ typename InputStreamType::byte_type b; diff --git a/plugins/image/image.cpp b/plugins/image/image.cpp index fcfdd018..4f3e07a8 100644 --- a/plugins/image/image.cpp +++ b/plugins/image/image.cpp @@ -29,6 +29,7 @@ #include "bmp.h" #include "pcx.h" #include "dds.h" +#include "ktx.h" #include "modulesystem/singletonmodule.h" @@ -137,6 +138,26 @@ typedef SingletonModule ImageDDSModule; ImageDDSModule g_ImageDDSModule; +class ImageKTXAPI +{ +_QERPlugImageTable m_imagektx; +public: +typedef _QERPlugImageTable Type; +STRING_CONSTANT( Name, "ktx" ); + +ImageKTXAPI(){ + m_imagektx.loadImage = LoadKTX; +} +_QERPlugImageTable* getTable(){ + return &m_imagektx; +} +}; + +typedef SingletonModule ImageKTXModule; + +ImageKTXModule g_ImageKTXModule; + + extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server ){ initialiseModule( server ); @@ -145,4 +166,5 @@ extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server g_ImageBMPModule.selfRegister(); g_ImagePCXModule.selfRegister(); g_ImageDDSModule.selfRegister(); + g_ImageKTXModule.selfRegister(); } diff --git a/tools/quake3/common/imagelib.c b/tools/quake3/common/imagelib.c index c349b498..3a590292 100644 --- a/tools/quake3/common/imagelib.c +++ b/tools/quake3/common/imagelib.c @@ -23,6 +23,7 @@ #include "inout.h" #include "cmdlib.h" +#include "etclib.h" #include "imagelib.h" #include "vfs.h" @@ -1232,3 +1233,302 @@ void Load32BitImage( const char *name, unsigned **pixels, int *width, int *heig } } } + + +/* + ============================================================================ + + KHRONOS TEXTURE + + ============================================================================ + */ + + +#define KTX_UINT32_LE( buf ) ( ( unsigned int )( (buf)[0] | ( (buf)[1] << 8 ) | ( (buf)[2] << 16 ) | ( (buf)[3] << 24 ) ) ) +#define KTX_UINT32_BE( buf ) ( ( unsigned int )( (buf)[3] | ( (buf)[2] << 8 ) | ( (buf)[1] << 16 ) | ( (buf)[0] << 24 ) ) ) + +#define KTX_TYPE_UNSIGNED_BYTE 0x1401 +#define KTX_TYPE_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define KTX_TYPE_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define KTX_TYPE_UNSIGNED_SHORT_5_6_5 0x8363 + +#define KTX_FORMAT_ALPHA 0x1906 +#define KTX_FORMAT_RGB 0x1907 +#define KTX_FORMAT_RGBA 0x1908 +#define KTX_FORMAT_LUMINANCE 0x1909 +#define KTX_FORMAT_LUMINANCE_ALPHA 0x190A +#define KTX_FORMAT_BGR 0x80E0 +#define KTX_FORMAT_BGRA 0x80E1 + +#define KTX_FORMAT_ETC1_RGB8 0x8D64 + +static void KTX_DecodeA8( const byte *in, qboolean bigEndian, byte *out ){ + out[0] = out[1] = out[2] = 0; + out[3] = in[0]; +} + +static void KTX_DecodeRGB8( const byte *in, qboolean bigEndian, byte *out ){ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = 255; +} + +static void KTX_DecodeRGBA8( const byte *in, qboolean bigEndian, byte *out ){ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +static void KTX_DecodeL8( const byte *in, qboolean bigEndian, byte *out ){ + out[0] = out[1] = out[2] = in[0]; + out[3] = 255; +} + +static void KTX_DecodeLA8( const byte *in, qboolean bigEndian, byte *out ){ + out[0] = out[1] = out[2] = in[0]; + out[3] = in[1]; +} + +static void KTX_DecodeBGR8( const byte *in, qboolean bigEndian, byte *out ){ + out[0] = in[2]; + out[1] = in[1]; + out[2] = in[0]; + out[3] = 255; +} + +static void KTX_DecodeBGRA8( const byte *in, qboolean bigEndian, byte *out ){ + out[0] = in[2]; + out[1] = in[1]; + out[2] = in[0]; + out[3] = in[3]; +} + +static void KTX_DecodeRGBA4( const byte *in, qboolean bigEndian, byte *out ){ + unsigned short rgba; + int r, g, b, a; + + if ( bigEndian ) { + rgba = ( in[0] << 8 ) | in[1]; + } + else { + rgba = ( in[1] << 8 ) | in[0]; + } + + r = ( rgba >> 12 ) & 0xf; + g = ( rgba >> 8 ) & 0xf; + b = ( rgba >> 4 ) & 0xf; + a = rgba & 0xf; + out[0] = ( r << 4 ) | r; + out[1] = ( g << 4 ) | g; + out[2] = ( b << 4 ) | b; + out[3] = ( a << 4 ) | a; +} + +static void KTX_DecodeRGBA5( const byte *in, qboolean bigEndian, byte *out ){ + unsigned short rgba; + int r, g, b; + + if ( bigEndian ) { + rgba = ( in[0] << 8 ) | in[1]; + } + else { + rgba = ( in[1] << 8 ) | in[0]; + } + + r = ( rgba >> 11 ) & 0x1f; + g = ( rgba >> 6 ) & 0x1f; + b = ( rgba >> 1 ) & 0x1f; + out[0] = ( r << 3 ) | ( r >> 2 ); + out[1] = ( g << 3 ) | ( g >> 2 ); + out[2] = ( b << 3 ) | ( b >> 2 ); + out[3] = ( rgba & 1 ) * 255; +} + +static void KTX_DecodeRGB5( const byte *in, qboolean bigEndian, byte *out ){ + unsigned short rgba; + int r, g, b; + + if ( bigEndian ) { + rgba = ( in[0] << 8 ) | in[1]; + } + else { + rgba = ( in[1] << 8 ) | in[0]; + } + + r = ( rgba >> 11 ) & 0x1f; + g = ( rgba >> 5 ) & 0x3f; + b = rgba & 0x1f; + out[0] = ( r << 3 ) | ( r >> 2 ); + out[1] = ( g << 2 ) | ( g >> 4 ); + out[2] = ( b << 3 ) | ( b >> 2 ); + out[3] = 255; +} + +typedef struct +{ + unsigned int type; + unsigned int format; + unsigned int pixelSize; + void ( *decode )( const byte *in, qboolean bigEndian, byte *out ); +} KTX_UncompressedFormat_t; + +static const KTX_UncompressedFormat_t KTX_UncompressedFormats[] = +{ + { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_ALPHA, 1, KTX_DecodeA8 }, + { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_RGB, 3, KTX_DecodeRGB8 }, + { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_RGBA, 4, KTX_DecodeRGBA8 }, + { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_LUMINANCE, 1, KTX_DecodeL8 }, + { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_LUMINANCE_ALPHA, 2, KTX_DecodeLA8 }, + { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_BGR, 3, KTX_DecodeBGR8 }, + { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_BGRA, 4, KTX_DecodeBGRA8 }, + { KTX_TYPE_UNSIGNED_SHORT_4_4_4_4, KTX_FORMAT_RGBA, 2, KTX_DecodeRGBA4 }, + { KTX_TYPE_UNSIGNED_SHORT_5_5_5_1, KTX_FORMAT_RGBA, 2, KTX_DecodeRGBA5 }, + { KTX_TYPE_UNSIGNED_SHORT_5_6_5, KTX_FORMAT_RGB, 2, KTX_DecodeRGB5 }, + { 0, 0, 0, NULL } +}; + +static qboolean KTX_DecodeETC1( const byte* in, size_t inSize, unsigned int width, unsigned int height, byte* out ){ + unsigned int y, stride = width * 4; + byte rgba[64]; + + if ( inSize < ( ( ( ( width + 3 ) & ~3 ) * ( ( height + 3 ) & ~3 ) ) >> 1 ) ) { + return qfalse; + } + + for ( y = 0; y < height; y += 4, out += stride * 4 ) + { + byte *p; + unsigned int x, blockrows; + + blockrows = height - y; + if ( blockrows > 4 ) { + blockrows = 4; + } + + p = out; + for ( x = 0; x < width; x += 4, p += 16 ) + { + unsigned int blockrowsize, blockrow; + + ETC_DecodeETC1Block( in, rgba, qtrue ); + in += 8; + + blockrowsize = width - x; + if ( blockrowsize > 4 ) { + blockrowsize = 4; + } + blockrowsize *= 4; + for ( blockrow = 0; blockrow < blockrows; blockrow++ ) + { + memcpy( p + blockrow * stride, rgba + blockrow * 16, blockrowsize ); + } + } + } + + return qtrue; +} + +#define KTX_HEADER_UINT32( buf ) ( bigEndian ? KTX_UINT32_BE( buf ) : KTX_UINT32_LE( buf ) ) + +void LoadKTXBufferFirstImage( const byte *buffer, size_t bufSize, byte **pic, int *picWidth, int *picHeight ){ + unsigned int type, format, width, height, imageOffset; + byte *pixels; + + if ( bufSize < 64 ) { + Error( "LoadKTX: Image doesn't have a header" ); + } + + if ( memcmp( buffer, "\xABKTX 11\xBB\r\n\x1A\n", 12 ) ) { + Error( "LoadKTX: Image has the wrong identifier" ); + } + + qboolean bigEndian = ( buffer[4] == 4 ); + + type = KTX_HEADER_UINT32( buffer + 16 ); + if ( type ) { + format = KTX_HEADER_UINT32( buffer + 32 ); + } + else { + format = KTX_HEADER_UINT32( buffer + 28 ); + } + + width = KTX_HEADER_UINT32( buffer + 36 ); + height = KTX_HEADER_UINT32( buffer + 40 ); + if ( !width ) { + Error( "LoadKTX: Image has zero width" ); + } + if ( !height ) { + height = 1; + } + if ( picWidth ) { + *picWidth = width; + } + if ( picHeight ) { + *picHeight = height; + } + + imageOffset = 64 + KTX_HEADER_UINT32( buffer + 60 ) + 4; + if ( bufSize < imageOffset ) { + Error( "LoadKTX: No image in the file" ); + } + buffer += imageOffset; + bufSize -= imageOffset; + + pixels = safe_malloc( width * height * 4 ); + *pic = pixels; + + if ( type ) { + const KTX_UncompressedFormat_t *ktxFormat = KTX_UncompressedFormats; + unsigned int pixelSize; + unsigned int inRowLength, inPadding; + unsigned int y; + + while ( ktxFormat->type ) + { + if ( ktxFormat->type == type && ktxFormat->format == format ) { + break; + } + ktxFormat++; + } + if ( !ktxFormat->type ) { + Error( "LoadKTX: Image has an unsupported pixel type 0x%X or format 0x%X", type, format ); + } + + pixelSize = ktxFormat->pixelSize; + inRowLength = width * pixelSize; + inPadding = ( ( inRowLength + 3 ) & ~3 ) - inRowLength; + + if ( bufSize < height * ( inRowLength + inPadding ) ) { + Error( "LoadKTX: Image is truncated" ); + } + + for ( y = 0; y < height; y++ ) + { + unsigned int x; + for ( x = 0; x < width; x++, buffer += pixelSize, pixels += 4 ) + { + ktxFormat->decode( buffer, bigEndian, pixels ); + } + buffer += inPadding; + } + } + else { + qboolean decoded = qfalse; + + switch ( format ) + { + case KTX_FORMAT_ETC1_RGB8: + decoded = KTX_DecodeETC1( buffer, bufSize, width, height, pixels ); + break; + default: + Error( "LoadKTX: Image has an unsupported compressed format format 0x%X", format ); + break; + } + + if ( !decoded ) { + Error( "LoadKTX: Image is truncated" ); + } + } +} diff --git a/tools/quake3/common/imagelib.h b/tools/quake3/common/imagelib.h index 194ac5b1..8c00f7ce 100644 --- a/tools/quake3/common/imagelib.h +++ b/tools/quake3/common/imagelib.h @@ -43,3 +43,5 @@ void WriteTGAGray( const char *filename, byte *data, int width, int height ); int LoadJPGBuff( void *src_buffer, int src_size, unsigned char **pic, int *width, int *height ); void Load32BitImage( const char *name, unsigned **pixels, int *width, int *height ); + +void LoadKTXBufferFirstImage( const byte *buffer, size_t bufSize, byte **pic, int *picWidth, int *picHeight ); diff --git a/tools/quake3/q3map2/image.c b/tools/quake3/q3map2/image.c index 60062a17..c1c737f5 100644 --- a/tools/quake3/q3map2/image.c +++ b/tools/quake3/q3map2/image.c @@ -430,6 +430,16 @@ image_t *ImageLoad( const char *filename ){ } #endif } + else + { + /* attempt to load ktx */ + StripExtension( name ); + strcat( name, ".ktx" ); + size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 ); + if ( size > 0 ) { + LoadKTXBufferFirstImage( buffer, size, &image->pixels, &image->width, &image->height ); + } + } } } } -- 2.39.2