From 27168ec47b156d743381164e052c309ad0a040f8 Mon Sep 17 00:00:00 2001 From: Shpoike Date: Sat, 7 Mar 2020 09:03:46 +0000 Subject: [PATCH] Added support for my extended miptex stuff for high-colour map textures. --- quakespasm/Quake/gl_model.c | 158 +++++++++++++++++--------- quakespasm/Quake/gl_sky.c | 51 +++++---- quakespasm/Quake/gl_texmgr.c | 209 +++++++++++++++++++++++++++++++---- quakespasm/Quake/gl_texmgr.h | 6 +- quakespasm/Quake/gl_vidsdl.c | 8 ++ quakespasm/Quake/glquake.h | 2 +- 6 files changed, 342 insertions(+), 92 deletions(-) diff --git a/quakespasm/Quake/gl_model.c b/quakespasm/Quake/gl_model.c index 841e7ec4..a6b532db 100644 --- a/quakespasm/Quake/gl_model.c +++ b/quakespasm/Quake/gl_model.c @@ -560,6 +560,90 @@ qboolean Mod_CheckFullbrights (byte *pixels, int count) return false; } +static texture_t *Mod_LoadMipTex(miptex_t *mt, byte *lumpend, enum srcformat *fmt, unsigned int *width, unsigned int *height, unsigned int *pixelbytes) +{ + //if offsets[0] is 0, then we've no legacy data (offsets[3] signifies the end of the extension data. + byte *extdata; + texture_t *tx; + byte *srcdata = NULL; + size_t sz; + + if (!mt->offsets[0]) //the legacy data was omitted. we may still have block-compression though. + extdata = (byte*)(mt+1); + else if (mt->offsets[0] == sizeof(miptex_t) && + mt->offsets[1] == mt->offsets[0]+(mt->width>>0)*(mt->height>>0) && + mt->offsets[2] == mt->offsets[1]+(mt->width>>1)*(mt->height>>1) && + mt->offsets[3] == mt->offsets[2]+(mt->width>>2)*(mt->height>>2)) + { //miptex makes sense and matches the standard 4-mip-levels. + extdata = (byte*)mt + mt->offsets[3]+(mt->width>>3)*(mt->height>>3); + //FIXME: halflife - leshort=256, palette[256][3]. + //extdata += 2+256*3; + } + else //the numbers don't match what we expect... something weird is going on here... don't misinterpret it. + extdata = lumpend; + + if (extdata+4 <= lumpend && extdata[0] == 0 && extdata[1]==0xfb && extdata[2]==0x2b && extdata[3]==0xaf) + for (extdata+=4; extdata+8 < lumpend; extdata += sz) + { + sz = (extdata[0]<<0)|(extdata[1]<<8)|(extdata[2]<<16)|(extdata[3]<<24); + if (sz < 8 || sz > lumpend-extdata) break; //bad! bad! bad! + else if (sz <= 16) continue; //nope, no idea + + *fmt = TexMgr_FormatForCode((char*)extdata+4); + if (*fmt == SRC_EXTERNAL) + continue; //nope, no idea + + *width = (extdata[8]<<0)|(extdata[9]<<8)|(extdata[10]<<16)|(extdata[11]<<24); + *height = (extdata[12]<<0)|(extdata[13]<<8)|(extdata[14]<<16)|(extdata[15]<<24); + + if (*width != TexMgr_SafeTextureSize(*width) || *width != TexMgr_SafeTextureSize(*width)) + continue; //nope, can't use that. drivers are too lame (or gl_max_size is too low). + + *pixelbytes = TexMgr_ImageSize(*width, *height, *fmt); + if (16+*pixelbytes == sz) + srcdata = extdata+16; + break; + } + + if (!srcdata) + { //no replacements, load the 8bit data. + *fmt = SRC_INDEXED; + *width = mt->width; + *height = mt->height; + *pixelbytes = mt->width*mt->height; + if (LittleLong (mt->offsets[0])) + srcdata = (byte*)mt+LittleLong(mt->offsets[0]); + } + + tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) + *pixelbytes, loadname ); + memcpy (tx->name, mt->name, sizeof(tx->name)); + tx->name[sizeof(tx->name)-1] = 0; //just in case... + tx->width = mt->width; + tx->height = mt->height; + + if (srcdata) + { + // ericw -- check for pixels extending past the end of the lump. + // appears in the wild; e.g. jam2_tronyn.bsp (func_mapjam2), + // kellbase1.bsp (quoth), and can lead to a segfault if we read past + // the end of the .bsp file buffer + if ((srcdata + *pixelbytes) > lumpend) + { + Con_DPrintf("Texture %s extends past end of lump\n", mt->name); + *pixelbytes = q_max(0, lumpend - srcdata); + } + + memcpy ( tx+1, srcdata, *pixelbytes); + } + else + { + size_t x,y; + for(y=0;ywidth;y++) + for(x=0;xwidth;x++) + ((byte*)(tx+1))[y*tx->width+x] = (((x>>2)^(y>>2))&1)?6:2; + } + return tx; +} /* ================= Mod_LoadTextures @@ -567,7 +651,7 @@ Mod_LoadTextures */ void Mod_LoadTextures (lump_t *l) { - int i, j, pixels, num, maxanim, altmax; + int i, j, num, maxanim, altmax; miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10]; @@ -583,6 +667,9 @@ void Mod_LoadTextures (lump_t *l) extern byte *hunk_base; //johnfitz qboolean malloced; //spike + enum srcformat fmt; //spike + unsigned int imgwidth, imgheight, imgpixels; + unsigned int mipend; //johnfitz -- don't return early if no textures; still need to create dummy texture if (!l->filelen) @@ -602,68 +689,37 @@ void Mod_LoadTextures (lump_t *l) loadmodel->numtextures = nummiptex + 2; //johnfitz -- need 2 dummy texture chains for missing textures loadmodel->textures = (texture_t **) Hunk_AllocName (loadmodel->numtextures * sizeof(*loadmodel->textures) , loadname); - for (i=0 ; ifilelen; i --> 0; ) { m->dataofs[i] = LittleLong(m->dataofs[i]); if (m->dataofs[i] == -1) continue; + if (m->dataofs[i] >= mipend) + mipend = l->filelen; //o.O something weird! mt = (miptex_t *)((byte *)m + m->dataofs[i]); mt->width = LittleLong (mt->width); mt->height = LittleLong (mt->height); -// for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); + for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); if ( (mt->width & 15) || (mt->height & 15) ) Sys_Error ("Texture %s is not 16 aligned", mt->name); - // spike -- quakespasm doesn't use the submips anyway - pixels = mt->width*mt->height; - //pixels = mt->width*mt->height/64*85; - tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); + tx = Mod_LoadMipTex(mt, (mod_base + l->fileofs + mipend), &fmt, &imgwidth, &imgheight, &imgpixels); loadmodel->textures[i] = tx; - memcpy (tx->name, mt->name, sizeof(tx->name)); - tx->width = mt->width; - tx->height = mt->height; -// for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); - // the pixels immediately follow the structures - - if (!LittleLong (mt->offsets[0])) - { // spike -- support tyrlight-ericw's -notex argument to avoid gpl violations from embedded textures - size_t x,y; - for(y=0;ywidth;y++) - for(x=0;xwidth;x++) - ((byte*)(tx+1))[y*tx->width+x] = (((x>>2)^(y>>2))&1)?6:2; - } - else - { - // ericw -- check for pixels extending past the end of the lump. - // appears in the wild; e.g. jam2_tronyn.bsp (func_mapjam2), - // kellbase1.bsp (quoth), and can lead to a segfault if we read past - // the end of the .bsp file buffer - if (((byte*)(mt+1) + pixels) > (mod_base + l->fileofs + l->filelen)) - { - Con_DPrintf("Texture %s extends past end of lump\n", mt->name); - pixels = q_max(0, (mod_base + l->fileofs + l->filelen) - (byte*)(mt+1)); - } - - //spike -- this is actually a pointless waste of memory. - //its not like this data will actually be used beyond this function in any gl renderer. - //this makes copying it pointless - //which in turn makes the pointer-to-array-of-pointers-to-texture a bit silly. - memcpy ( tx+1, mt+1, pixels); - } - tx->update_warp = false; //johnfitz tx->warpimage = NULL; //johnfitz tx->fullbright = NULL; //johnfitz + mipend = m->dataofs[i]; + //johnfitz -- lots of changes if (!isDedicated) //no texture uploading for dedicated server { if (!q_strncasecmp(tx->name,"sky",3)) //sky texture //also note -- was Q_strncmp, changed to match qbsp - Sky_LoadTexture (tx); + Sky_LoadTexture (tx, fmt, imgwidth, imgheight); else if (tx->name[0] == '*') //warping texture { //external textures -- first look in "textures/mapname/" then look in "textures/" @@ -688,8 +744,8 @@ void Mod_LoadTextures (lump_t *l) { q_snprintf (texturename, sizeof(texturename), "%s:%s", loadmodel->name, tx->name); offset = (src_offset_t)(mt+1) - (src_offset_t)mod_base; - tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, - SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_NONE); + tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, imgwidth, imgheight, + fmt, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_NONE); } //now create the warpimage, using dummy data from the hunk to create the initial image @@ -749,18 +805,18 @@ void Mod_LoadTextures (lump_t *l) { q_snprintf (texturename, sizeof(texturename), "%s:%s", loadmodel->name, tx->name); offset = (src_offset_t)(mt+1) - (src_offset_t)mod_base; - if (Mod_CheckFullbrights ((byte *)(tx+1), pixels)) + if (fmt == SRC_INDEXED && Mod_CheckFullbrights ((byte *)(tx+1), imgpixels)) { - tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, - SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_NOBRIGHT | extraflags); + tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, imgwidth, imgheight, + fmt, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_NOBRIGHT | extraflags); q_snprintf (texturename, sizeof(texturename), "%s:%s_glow", loadmodel->name, tx->name); - tx->fullbright = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, - SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_FULLBRIGHT | extraflags); + tx->fullbright = TexMgr_LoadImage (loadmodel, texturename, imgwidth, imgheight, + fmt, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_FULLBRIGHT | extraflags); } else { - tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, - SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | extraflags); + tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, imgwidth, imgheight, + fmt, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | extraflags); } } if (malloced) diff --git a/quakespasm/Quake/gl_sky.c b/quakespasm/Quake/gl_sky.c index 1bfd31e2..3f461f3f 100644 --- a/quakespasm/Quake/gl_sky.c +++ b/quakespasm/Quake/gl_sky.c @@ -97,43 +97,55 @@ Sky_LoadTexture A sky texture is 256*128, with the left side being a masked overlay ============== */ -void Sky_LoadTexture (texture_t *mt) +void Sky_LoadTexture (texture_t *mt, enum srcformat fmt, unsigned int srcwidth, unsigned int height) { char texturename[64]; - int i, j, p, r, g, b, count; + int i, p, r, g, b, count; byte *src; - static byte front_data[128*128]; //FIXME: Hunk_Alloc - static byte back_data[128*128]; //FIXME: Hunk_Alloc + byte *front_data; + byte *back_data; unsigned *rgba; + int rows, columns; + int bb,bw,bh; + int width = srcwidth/2; + + TexMgr_BlockSize(fmt, &bb, &bw, &bh); + columns = (width+bw-1) / bw; + rows = (height+bh-1) / bh; + + front_data = Hunk_AllocName (bb*columns*rows*2, "skytex"); + back_data = front_data+bb*columns*rows; src = (byte *)(mt+1); // extract back layer and upload - for (i=0 ; i<128 ; i++) - for (j=0 ; j<128 ; j++) - back_data[(i*128) + j] = src[i*256 + j + 128]; + for (i=0 ; iname, mt->name); - solidskytexture = TexMgr_LoadImage (loadmodel, texturename, 128, 128, SRC_INDEXED, back_data, "", (src_offset_t)back_data, TEXPREF_NONE); + solidskytexture = TexMgr_LoadImage (loadmodel, texturename, width, height, fmt, back_data, "", (src_offset_t)back_data, TEXPREF_NONE); // extract front layer and upload - for (i=0 ; i<128 ; i++) - for (j=0 ; j<128 ; j++) + for (i=0 ; iname, mt->name); - alphaskytexture = TexMgr_LoadImage (loadmodel, texturename, 128, 128, SRC_INDEXED, front_data, "", (src_offset_t)front_data, TEXPREF_ALPHA); + alphaskytexture = TexMgr_LoadImage (loadmodel, texturename, width, height, fmt, front_data, "", (src_offset_t)front_data, TEXPREF_ALPHA); -// calculate r_fastsky color based on average of all opaque foreground colors +// calculate r_fastsky color based on average of all opaque foreground colors, if we can. r = g = b = count = 0; - for (i=0 ; i<128 ; i++) - for (j=0 ; j<128 ; j++) + if (fmt == SRC_INDEXED) + { + for (i=0 ; i> miplevel; + mipheight = height >> miplevel; + if (!mipwidth && !mipheight) + break; + mipwidth = q_max(1,mipwidth); //include the 1*1 mip with non-square textures. + mipheight = q_max(1,mipheight); + mipbytes += blockbytes*((mipwidth+blockwidth-1)/blockwidth)*((mipheight+blockheight-1)/blockheight); + } + return mipbytes; + } +} +enum srcformat TexMgr_FormatForCode (const char *code) +{ + size_t i; + for (i = 0; i < sizeof(compressedformats)/sizeof(compressedformats[0]); i++) + { + if (!compressedformats[i].mipextname) + continue; + if (!strncasecmp(code, compressedformats[i].mipextname, 4)) + return i; + } + return SRC_EXTERNAL; +} +static void TexMgr_LoadImageCompressed (gltexture_t *glt, byte *data) +{ + int internalformat, format, type, miplevel, mipwidth, mipheight, picmip; + size_t mipbytes, blockbytes; + unsigned int blockwidth, blockheight; + + internalformat = compressedformats[glt->source_format].internalformat; + format = compressedformats[glt->source_format].format; + type = compressedformats[glt->source_format].type; + blockbytes = compressedformats[glt->source_format].blockbytes; + blockwidth = compressedformats[glt->source_format].blockwidth; + blockheight = compressedformats[glt->source_format].blockheight; + + //no premultiply support. + //no npot fallback support + + // mipmap down + picmip = ((glt->flags & TEXPREF_NOPICMIP) || !(glt->flags & TEXPREF_MIPMAP)) ? 0 : q_max((int)gl_picmip.value, 0); + + //make sure the picmip level is not bigger than the number of mips that we have available... + while (picmip && (!(glt->width>>picmip) || !(glt->height>>picmip))) + picmip--; + + if (type && blockbytes < 4) + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //makes stuff work more reliably, if slower. + + //upload each mip level in turn. + GL_Bind (glt); + for (miplevel = 0; ; miplevel++) + { + mipwidth = glt->width >> miplevel; + mipheight = glt->height >> miplevel; + if (!mipwidth && !mipheight) + break; + mipwidth = q_max(1,mipwidth); //include the 1*1 mip with non-square textures. + mipheight = q_max(1,mipheight); + mipbytes = blockbytes*((mipwidth+blockwidth-1)/blockwidth)*((mipheight+blockheight-1)/blockheight); + if (miplevel-picmip >= 0) + { + if (type) + glTexImage2D(GL_TEXTURE_2D, miplevel-picmip, internalformat, mipwidth, mipheight, 0, format, type, data); + else + glCompressedTexImage2D(GL_TEXTURE_2D, miplevel-picmip, internalformat, mipwidth, mipheight, 0, mipbytes, data); + } + data += mipbytes; + + if (!(glt->flags & TEXPREF_MIPMAP)) + break; + } + + if (type && blockbytes < 4) + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); //back to opengl's default. + + // set filter modes + TexMgr_SetFilterModes (glt); +} + /* ================ TexMgr_LoadImage8 -- handles 8bit source data, then passes it to LoadImage32 @@ -1225,21 +1403,10 @@ gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int return NULL; // cache check - switch (format) - { - case SRC_INDEXED: - crc = CRC_Block(data, width * height); - break; - case SRC_LIGHTMAP: - crc = CRC_Block(data, width * height * lightmap_bytes); - break; - case SRC_RGBA: - crc = CRC_Block(data, width * height * 4); - break; - case SRC_EXTERNAL: - default: /* not reachable but avoids compiler warnings */ + if (format == SRC_EXTERNAL) crc = 0; - } + else + crc = CRC_Block(data, TexMgr_ImageSize(width, height, format)); if ((flags & TEXPREF_OVERWRITE) && (glt = TexMgr_FindTexture (owner, name))) { if (glt->source_crc == crc) @@ -1295,6 +1462,9 @@ gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int case SRC_RGBA: TexMgr_LoadImage32 (glt, (unsigned *)data); break; + default: + TexMgr_LoadImageCompressed (glt, data); + break; } Hunk_FreeToLowMark(mark); @@ -1335,12 +1505,8 @@ void TexMgr_ReloadImage (gltexture_t *glt, int shirt, int pants) if (!f) goto invalid; fseek (f, glt->source_offset, SEEK_CUR); - size = (long) (glt->source_width * glt->source_height); - /* should be SRC_INDEXED, but no harm being paranoid: */ - if (glt->source_format == SRC_RGBA) - size *= 4; - else if (glt->source_format == SRC_LIGHTMAP) - size *= lightmap_bytes; + + size = TexMgr_ImageSize(glt->source_width, glt->source_height, glt->source_format); data = (byte *) Hunk_Alloc (size); fread (data, 1, size, f); fclose (f); @@ -1430,6 +1596,9 @@ invalid: case SRC_RGBA: TexMgr_LoadImage32 (glt, (unsigned *)data); break; + default: + TexMgr_LoadImageCompressed (glt, data); + break; } if (malloced) diff --git a/quakespasm/Quake/gl_texmgr.h b/quakespasm/Quake/gl_texmgr.h index def7aa54..12cbdd6b 100644 --- a/quakespasm/Quake/gl_texmgr.h +++ b/quakespasm/Quake/gl_texmgr.h @@ -40,7 +40,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define TEXPREF_WARPIMAGE 0x0800 // resize this texture when warpimagesize changes #define TEXPREF_PREMULTIPLY 0x1000 // rgb = rgb*a; a=a; -enum srcformat {SRC_INDEXED, SRC_LIGHTMAP, SRC_RGBA, SRC_EXTERNAL}; +enum srcformat {SRC_INDEXED, SRC_LIGHTMAP, SRC_RGBA, SRC_EXTERNAL, SRC_FIRSTCOMPRESSED}; +extern qboolean gl_texture_s3tc, gl_texture_rgtc, gl_texture_bptc, gl_texture_etc2, gl_texture_astc; typedef uintptr_t src_offset_t; @@ -87,6 +88,9 @@ void TexMgr_FreeTexturesForOwner (qmodel_t *owner); void TexMgr_NewGame (void); void TexMgr_Init (void); void TexMgr_DeleteTextureObjects (void); +enum srcformat TexMgr_FormatForCode (const char *code); //returns SRC_EXTERNAL when not known. +size_t TexMgr_ImageSize (int width, int height, enum srcformat format); +void TexMgr_BlockSize (enum srcformat format, int *bytes, int *width, int *height); // IMAGE LOADING gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int height, enum srcformat format, diff --git a/quakespasm/Quake/gl_vidsdl.c b/quakespasm/Quake/gl_vidsdl.c index 2331a2f2..245ecf0f 100644 --- a/quakespasm/Quake/gl_vidsdl.c +++ b/quakespasm/Quake/gl_vidsdl.c @@ -68,6 +68,8 @@ static int gl_version_minor; static const char *gl_extensions; static char * gl_extensions_nice; +qboolean gl_texture_s3tc, gl_texture_rgtc, gl_texture_bptc, gl_texture_etc2, gl_texture_astc; + static vmode_t modelist[MAX_MODE_LIST]; static int nummodes; @@ -1172,6 +1174,12 @@ static void GL_CheckExtensions (void) { Con_Warning ("texture_non_power_of_two not supported\n"); } + + gl_texture_s3tc = ( GL_ParseExtensionList(gl_extensions, "GL_EXT_texture_compression_s3tc")); + gl_texture_rgtc = (gl_version_major >= 3 || GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_compression_rgtc")); + gl_texture_bptc = (gl_version_major > 4 || (gl_version_major == 4 && gl_version_minor >= 2) || GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_compression_bptc")); + gl_texture_etc2 = (gl_version_major > 4 || (gl_version_major == 4 && gl_version_minor >= 3) || GL_ParseExtensionList(gl_extensions, "GL_ARB_ES3_compatibility")); + gl_texture_astc = (gl_version_major > 4 || (gl_version_major == 4 && gl_version_minor >= 3) || GL_ParseExtensionList(gl_extensions, "GL_ARB_ES3_2_compatibility") || GL_ParseExtensionList(gl_extensions, "GL_KHR_texture_compression_astc_ldr")); // GLSL // diff --git a/quakespasm/Quake/glquake.h b/quakespasm/Quake/glquake.h index a4075e29..e6584394 100644 --- a/quakespasm/Quake/glquake.h +++ b/quakespasm/Quake/glquake.h @@ -433,7 +433,7 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr); void Sky_Init (void); void Sky_DrawSky (void); void Sky_NewMap (void); -void Sky_LoadTexture (texture_t *mt); +void Sky_LoadTexture (texture_t *mt, enum srcformat fmt, unsigned int width, unsigned int height); void Sky_LoadSkyBox (const char *name); extern qboolean skyroom_drawn; //we draw a skyroom this frame extern qboolean skyroom_enabled; //we know where the skyroom is ...