// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file hw_cache.c /// \brief load and convert graphics to the hardware format #include "../doomdef.h" #ifdef HWRENDER #include "hw_glob.h" #include "hw_drv.h" #include "hw_batching.h" #include "../doomstat.h" //gamemode #include "../i_video.h" //rendermode #include "../r_data.h" #include "../r_textures.h" #include "../w_wad.h" #include "../z_zone.h" #include "../v_video.h" #include "../r_draw.h" #include "../r_patch.h" #include "../r_picformats.h" #include "../p_setup.h" INT32 patchformat = GL_TEXFMT_AP_88; // use alpha for holes INT32 textureformat = GL_TEXFMT_P_8; // use chromakey for hole static INT32 format2bpp(GLTextureFormat_t format) { if (format == GL_TEXFMT_RGBA) return 4; else if (format == GL_TEXFMT_ALPHA_INTENSITY_88 || format == GL_TEXFMT_AP_88) return 2; else return 1; } // This code was originally placed directly in HWR_DrawPatchInCache. // It is now split from it for my sanity! (and the sanity of others) // -- Monster Iestyn (13/02/19) static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap, INT32 pblockheight, INT32 blockmodulo, fixed_t yfracstep, fixed_t scale_y, texpatch_t *originPatch, INT32 patchheight, INT32 bpp) { fixed_t yfrac, position, count; UINT8 *dest; const UINT8 *source; INT32 topdelta, prevdelta = -1; INT32 originy = 0; // for writing a pixel to dest RGBA_t colortemp; UINT8 alpha; UINT8 texel; UINT16 texelu16; (void)patchheight; // This parameter is unused if (originPatch) // originPatch can be NULL here, unlike in the software version originy = originPatch->originy; while (patchcol->topdelta != 0xff) { topdelta = patchcol->topdelta; if (topdelta <= prevdelta) topdelta += prevdelta; prevdelta = topdelta; source = (const UINT8 *)patchcol + 3; count = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS; position = originy + topdelta; yfrac = 0; //yfracstep = (patchcol->length << FRACBITS) / count; if (position < 0) { yfrac = -position<> FRACBITS); position = 0; } position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS; if (position < 0) position = 0; if (position + count >= pblockheight) count = pblockheight - position; dest = block + (position*blockmodulo); while (count > 0) { count--; texel = source[yfrac>>FRACBITS]; alpha = 0xFF; // Make pixel transparent if chroma keyed if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) alpha = 0x00; //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) texel = mipmap->colormap->data[texel]; // hope compiler will get this switch out of the loops (dreams...) // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?) // Alam: SRB2 uses Mingw, HUGS switch (bpp) { case 2 : // uhhhhhhhh.......... if ((originPatch != NULL) && (originPatch->style != AST_COPY)) texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha); texelu16 = (UINT16)((alpha<<8) | texel); memcpy(dest, &texelu16, sizeof(UINT16)); break; case 3 : colortemp = V_GetColor(texel); if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; case 4 : colortemp = V_GetColor(texel); colortemp.s.alpha = alpha; if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)); break; // default is 1 default: if ((originPatch != NULL) && (originPatch->style != AST_COPY)) *dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha); else *dest = texel; break; } dest += blockmodulo; yfrac += yfracstep; } patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4); } } static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap, INT32 pblockheight, INT32 blockmodulo, fixed_t yfracstep, fixed_t scale_y, texpatch_t *originPatch, INT32 patchheight, INT32 bpp) { fixed_t yfrac, position, count; UINT8 *dest; const UINT8 *source; INT32 topdelta, prevdelta = -1; INT32 originy = 0; // for writing a pixel to dest RGBA_t colortemp; UINT8 alpha; UINT8 texel; UINT16 texelu16; if (originPatch) // originPatch can be NULL here, unlike in the software version originy = originPatch->originy; while (patchcol->topdelta != 0xff) { topdelta = patchcol->topdelta; if (topdelta <= prevdelta) topdelta += prevdelta; prevdelta = topdelta; topdelta = patchheight-patchcol->length-topdelta; source = (const UINT8 *)patchcol + 3; count = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS; position = originy + topdelta; yfrac = (patchcol->length-1) << FRACBITS; if (position < 0) { yfrac += position<> FRACBITS); position = 0; } position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS; if (position < 0) position = 0; if (position + count >= pblockheight) count = pblockheight - position; dest = block + (position*blockmodulo); while (count > 0) { count--; texel = source[yfrac>>FRACBITS]; alpha = 0xFF; // Make pixel transparent if chroma keyed if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) alpha = 0x00; //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) texel = mipmap->colormap->data[texel]; // hope compiler will get this switch out of the loops (dreams...) // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?) // Alam: SRB2 uses Mingw, HUGS switch (bpp) { case 2 : // uhhhhhhhh.......... if ((originPatch != NULL) && (originPatch->style != AST_COPY)) texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha); texelu16 = (UINT16)((alpha<<8) | texel); memcpy(dest, &texelu16, sizeof(UINT16)); break; case 3 : colortemp = V_GetColor(texel); if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; case 4 : colortemp = V_GetColor(texel); colortemp.s.alpha = alpha; if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; rgbatexel.rgba = *(UINT32 *)dest; colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha); } memcpy(dest, &colortemp, sizeof(RGBA_t)); break; // default is 1 default: if ((originPatch != NULL) && (originPatch->style != AST_COPY)) *dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha); else *dest = texel; break; } dest += blockmodulo; yfrac -= yfracstep; } patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4); } } // Simplified patch caching function // for use by sprites and other patches that are not part of a wall texture // no alpha or flipping should be present since we do not want non-texture graphics to have them // no offsets are used either // -- Monster Iestyn (13/02/19) static void HWR_DrawPatchInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 pblockheight, INT32 pwidth, INT32 pheight, const patch_t *realpatch) { INT32 ncols; fixed_t xfrac, xfracstep; fixed_t yfracstep, scale_y; const column_t *patchcol; UINT8 *block = mipmap->data; INT32 bpp; INT32 blockmodulo; if (pwidth <= 0 || pheight <= 0) return; ncols = pwidth; // source advance xfrac = 0; xfracstep = FRACUNIT; yfracstep = FRACUNIT; scale_y = FRACUNIT; bpp = format2bpp(mipmap->format); if (bpp < 1 || bpp > 4) I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp); // NOTE: should this actually be pblockwidth*bpp? blockmodulo = pblockwidth*bpp; // Draw each column to the block cache for (; ncols--; block += bpp, xfrac += xfracstep) { patchcol = (const column_t *)((const UINT8 *)realpatch->columns + (realpatch->columnofs[xfrac>>FRACBITS])); HWR_DrawColumnInCache(patchcol, block, mipmap, pblockheight, blockmodulo, yfracstep, scale_y, NULL, pheight, // not that pheight is going to get used anyway... bpp); } } // This function we use for caching patches that belong to textures static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 pblockheight, texture_t *texture, texpatch_t *patch, const softwarepatch_t *realpatch) { INT32 x, x1, x2; INT32 col, ncols; fixed_t xfrac, xfracstep; fixed_t yfracstep, scale_y; const column_t *patchcol; UINT8 *block = mipmap->data; INT32 bpp; INT32 blockmodulo; INT32 width, height; // Column drawing function pointer. static void (*ColumnDrawerPointer)(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap, INT32 pblockheight, INT32 blockmodulo, fixed_t yfracstep, fixed_t scale_y, texpatch_t *originPatch, INT32 patchheight, INT32 bpp); if (texture->width <= 0 || texture->height <= 0) return; ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedColumnInCache : HWR_DrawColumnInCache; x1 = patch->originx; width = SHORT(realpatch->width); height = SHORT(realpatch->height); x2 = x1 + width; if (x1 > texture->width || x2 < 0) return; // patch not located within texture's x bounds, ignore if (patch->originy > texture->height || (patch->originy + height) < 0) return; // patch not located within texture's y bounds, ignore // patch is actually inside the texture! // now check if texture is partly off-screen and adjust accordingly // left edge if (x1 < 0) x = 0; else x = x1; // right edge if (x2 > texture->width) x2 = texture->width; col = x * pblockwidth / texture->width; ncols = ((x2 - x) * pblockwidth) / texture->width; /* CONS_Debug(DBG_RENDER, "patch %dx%d texture %dx%d block %dx%d\n", width, height, texture->width, texture->height, pblockwidth, pblockheight); CONS_Debug(DBG_RENDER, " col %d ncols %d x %d\n", col, ncols, x); */ // source advance xfrac = 0; if (x1 < 0) xfrac = -x1<width << FRACBITS) / pblockwidth; yfracstep = (texture->height<< FRACBITS) / pblockheight; scale_y = (pblockheight << FRACBITS) / texture->height; bpp = format2bpp(mipmap->format); if (bpp < 1 || bpp > 4) I_Error("HWR_DrawTexturePatchInCache: no drawer defined for this bpp (%d)\n",bpp); // NOTE: should this actually be pblockwidth*bpp? blockmodulo = pblockwidth*bpp; // Draw each column to the block cache for (block += col*bpp; ncols--; block += bpp, xfrac += xfracstep) { if (patch->flip & 1) patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[(width-1)-(xfrac>>FRACBITS)])); else patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[xfrac>>FRACBITS])); ColumnDrawerPointer(patchcol, block, mipmap, pblockheight, blockmodulo, yfracstep, scale_y, patch, height, bpp); } } static UINT8 *MakeBlock(GLMipmap_t *grMipmap) { UINT8 *block; INT32 bpp, i; UINT16 bu16 = ((0x00 <<8) | HWR_PATCHES_CHROMAKEY_COLORINDEX); INT32 blocksize = (grMipmap->width * grMipmap->height); bpp = format2bpp(grMipmap->format); block = Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(grMipmap->data)); switch (bpp) { case 1: memset(block, HWR_PATCHES_CHROMAKEY_COLORINDEX, blocksize); break; case 2: // fill background with chromakey, alpha = 0 for (i = 0; i < blocksize; i++) //[segabor] memcpy(block+i*sizeof(UINT16), &bu16, sizeof(UINT16)); break; case 4: memset(block, 0x00, blocksize*sizeof(UINT32)); break; } return block; } // // Create a composite texture from patches, adapt the texture size to a power of 2 // height and width for the hardware texture cache. // static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex) { UINT8 *block; texture_t *texture; texpatch_t *patch; softwarepatch_t *realpatch; UINT8 *pdata; INT32 blockwidth, blockheight, blocksize; INT32 i; boolean skyspecial = false; //poor hack for Legacy large skies.. texture = textures[texnum]; // hack the Legacy skies.. if (texture->name[0] == 'S' && texture->name[1] == 'K' && texture->name[2] == 'Y' && (texture->name[4] == 0 || texture->name[5] == 0) ) { skyspecial = true; grtex->mipmap.flags = TF_WRAPXY; // don't use the chromakey for sky } else grtex->mipmap.flags = TF_CHROMAKEYED | TF_WRAPXY; grtex->mipmap.width = (UINT16)texture->width; grtex->mipmap.height = (UINT16)texture->height; grtex->mipmap.format = textureformat; blockwidth = texture->width; blockheight = texture->height; blocksize = (blockwidth * blockheight); block = MakeBlock(&grtex->mipmap); if (skyspecial) //Hurdler: not efficient, but better than holes in the sky (and it's done only at level loading) { INT32 j; RGBA_t col; col = V_GetColor(HWR_PATCHES_CHROMAKEY_COLORINDEX); for (j = 0; j < blockheight; j++) { for (i = 0; i < blockwidth; i++) { block[4*(j*blockwidth+i)+0] = col.s.red; block[4*(j*blockwidth+i)+1] = col.s.green; block[4*(j*blockwidth+i)+2] = col.s.blue; block[4*(j*blockwidth+i)+3] = 0xff; } } } // Composite the columns together. for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { boolean dealloc = true; size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump); pdata = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE); realpatch = (softwarepatch_t *)pdata; #ifndef NO_PNG_LUMPS if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) realpatch = (softwarepatch_t *)Picture_PNGConvert(pdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0); else #endif #ifdef WALLFLATS if (texture->type == TEXTURETYPE_FLAT) realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_DOOMPATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); else #endif { (void)lumplength; dealloc = false; } HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, texture, patch, realpatch); if (dealloc) Z_Unlock(realpatch); } //Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :( if (format2bpp(grtex->mipmap.format)==4) { for (i = 3; i < blocksize*4; i += 4) // blocksize*4 because blocksize doesn't include the bpp { if (block[i] == 0) { grtex->mipmap.flags |= TF_TRANSPARENT; break; } } } grtex->scaleX = 1.0f/(texture->width*FRACUNIT); grtex->scaleY = 1.0f/(texture->height*FRACUNIT); } // patch may be NULL if grMipmap has been initialised already and makebitmap is false void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap) { if (grMipmap->width == 0) { grMipmap->width = grMipmap->height = 1; while (grMipmap->width < patch->width) grMipmap->width <<= 1; while (grMipmap->height < patch->height) grMipmap->height <<= 1; // no wrap around, no chroma key grMipmap->flags = 0; // setup the texture info grMipmap->format = patchformat; grPatch->max_s = (float)patch->width / (float)grMipmap->width; grPatch->max_t = (float)patch->height / (float)grMipmap->height; } Z_Free(grMipmap->data); grMipmap->data = NULL; if (makebitmap) { MakeBlock(grMipmap); HWR_DrawPatchInCache(grMipmap, grMipmap->width, grMipmap->height, patch->width, patch->height, patch); } } // ================================================= // CACHING HANDLING // ================================================= static size_t gl_numtextures = 0; // Texture count static GLMapTexture_t *gl_textures; // For all textures static GLMapTexture_t *gl_flats; // For all (texture) flats, as normal flats don't need to be cached boolean gl_maptexturesloaded = false; void HWR_FreeTextureData(patch_t *patch) { GLPatch_t *grPatch; if (!patch || !patch->hardware) return; grPatch = patch->hardware; if (vid.glstate == VID_GL_LIBRARY_LOADED) HWD.pfnDeleteTexture(grPatch->mipmap); if (grPatch->mipmap->data) Z_Free(grPatch->mipmap->data); } void HWR_FreeTexture(patch_t *patch) { if (!patch) return; if (patch->hardware) { GLPatch_t *grPatch = patch->hardware; HWR_FreeTextureColormaps(patch); if (grPatch->mipmap) { HWR_FreeTextureData(patch); Z_Free(grPatch->mipmap); } Z_Free(patch->hardware); } patch->hardware = NULL; } // Called by HWR_FreePatchCache. void HWR_FreeTextureColormaps(patch_t *patch) { GLPatch_t *pat; // The patch must be valid, obviously if (!patch) return; pat = patch->hardware; if (!pat) return; // The mipmap must be valid, obviously while (pat->mipmap) { // Confusing at first, but pat->mipmap->nextcolormap // at the beginning of the loop is the first colormap // from the linked list of colormaps. GLMipmap_t *next = NULL; // No mipmap in this patch, break out of the loop. if (!pat->mipmap) break; // No colormap mipmaps either. if (!pat->mipmap->nextcolormap) break; // Set the first colormap to the one that comes after it. next = pat->mipmap->nextcolormap; pat->mipmap->nextcolormap = next->nextcolormap; // Free image data from memory. if (next->data) Z_Free(next->data); if (next->colormap) Z_Free(next->colormap); next->data = NULL; next->colormap = NULL; HWD.pfnDeleteTexture(next); // Free the old colormap mipmap from memory. free(next); } } static boolean FreeTextureCallback(void *mem) { patch_t *patch = (patch_t *)mem; HWR_FreeTexture(patch); return false; } static boolean FreeColormapsCallback(void *mem) { patch_t *patch = (patch_t *)mem; HWR_FreeTextureColormaps(patch); return false; } static void HWR_FreePatchCache(boolean freeall) { boolean (*callback)(void *mem) = FreeTextureCallback; if (!freeall) callback = FreeColormapsCallback; Z_IterateTags(PU_PATCH, PU_PATCH_ROTATED, callback); Z_IterateTags(PU_SPRITE, PU_HUDGFX, callback); } // free all textures after each level void HWR_ClearAllTextures(void) { HWD.pfnClearMipMapCache(); // free references to the textures HWR_FreePatchCache(true); } void HWR_FreeColormapCache(void) { HWR_FreePatchCache(false); } void HWR_InitMapTextures(void) { gl_textures = NULL; gl_flats = NULL; gl_maptexturesloaded = false; } static void FreeMapTexture(GLMapTexture_t *tex) { HWD.pfnDeleteTexture(&tex->mipmap); if (tex->mipmap.data) Z_Free(tex->mipmap.data); tex->mipmap.data = NULL; } void HWR_FreeMapTextures(void) { size_t i; for (i = 0; i < gl_numtextures; i++) { FreeMapTexture(&gl_textures[i]); FreeMapTexture(&gl_flats[i]); } // now the heap don't have any 'user' pointing to our // texturecache info, we can free it if (gl_textures) free(gl_textures); if (gl_flats) free(gl_flats); gl_textures = NULL; gl_flats = NULL; gl_numtextures = 0; gl_maptexturesloaded = false; } void HWR_LoadMapTextures(size_t pnumtextures) { // we must free it since numtextures may have changed HWR_FreeMapTextures(); gl_numtextures = pnumtextures; gl_textures = calloc(gl_numtextures, sizeof(*gl_textures)); gl_flats = calloc(gl_numtextures, sizeof(*gl_flats)); if ((gl_textures == NULL) || (gl_flats == NULL)) I_Error("HWR_LoadMapTextures: ran out of memory for OpenGL textures"); gl_maptexturesloaded = true; } void HWR_SetPalette(RGBA_t *palette) { HWD.pfnSetPalette(palette); // hardware driver will flush there own cache if cache is non paletized // now flush data texture cache so 32 bit texture are recomputed if (patchformat == GL_TEXFMT_RGBA || textureformat == GL_TEXFMT_RGBA) { Z_FreeTag(PU_HWRCACHE); Z_FreeTag(PU_HWRCACHE_UNLOCKED); } } // -------------------------------------------------------------------------- // Make sure texture is downloaded and set it as the source // -------------------------------------------------------------------------- GLMapTexture_t *HWR_GetTexture(INT32 tex) { GLMapTexture_t *grtex; #ifdef PARANOIA if ((unsigned)tex >= gl_numtextures) I_Error("HWR_GetTexture: tex >= numtextures\n"); #endif // Every texture in memory, stored in the // hardware renderer's bit depth format. Wow! grtex = &gl_textures[tex]; // Generate texture if missing from the cache if (!grtex->mipmap.data && !grtex->mipmap.downloaded) HWR_GenerateTexture(tex, grtex); // If hardware does not have the texture, then call pfnSetTexture to upload it if (!grtex->mipmap.downloaded) HWD.pfnSetTexture(&grtex->mipmap); HWR_SetCurrentTexture(&grtex->mipmap); // The system-memory data can be purged now. Z_ChangeTag(grtex->mipmap.data, PU_HWRCACHE_UNLOCKED); return grtex; } static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) { size_t size, pflatsize; // setup the texture info grMipmap->format = GL_TEXFMT_P_8; grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; size = W_LumpLength(flatlumpnum); switch (size) { case 4194304: // 2048x2048 lump pflatsize = 2048; break; case 1048576: // 1024x1024 lump pflatsize = 1024; break; case 262144:// 512x512 lump pflatsize = 512; break; case 65536: // 256x256 lump pflatsize = 256; break; case 16384: // 128x128 lump pflatsize = 128; break; case 1024: // 32x32 lump pflatsize = 32; break; default: // 64x64 lump pflatsize = 64; break; } grMipmap->width = (UINT16)pflatsize; grMipmap->height = (UINT16)pflatsize; // the flat raw data needn't be converted with palettized textures W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum), PU_HWRCACHE, &grMipmap->data)); } static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) { UINT8 *flat; UINT8 *converted; size_t size; // setup the texture info grMipmap->format = GL_TEXFMT_P_8; grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; grMipmap->width = (UINT16)textures[texturenum]->width; grMipmap->height = (UINT16)textures[texturenum]->height; size = (grMipmap->width * grMipmap->height); flat = Z_Malloc(size, PU_HWRCACHE, &grMipmap->data); converted = (UINT8 *)Picture_TextureToFlat(texturenum); M_Memcpy(flat, converted, size); Z_Free(converted); } // Download a Doom 'flat' to the hardware cache and make it ready for use void HWR_GetRawFlat(lumpnum_t flatlumpnum) { GLMipmap_t *grmip; patch_t *patch; if (flatlumpnum == LUMPERROR) return; patch = HWR_GetCachedGLPatch(flatlumpnum); grmip = ((GLPatch_t *)Patch_AllocateHardwarePatch(patch))->mipmap; if (!grmip->downloaded && !grmip->data) HWR_CacheFlat(grmip, flatlumpnum); // If hardware does not have the texture, then call pfnSetTexture to upload it if (!grmip->downloaded) HWD.pfnSetTexture(grmip); HWR_SetCurrentTexture(grmip); // The system-memory data can be purged now. Z_ChangeTag(grmip->data, PU_HWRCACHE_UNLOCKED); } void HWR_GetLevelFlat(levelflat_t *levelflat) { // Who knows? if (levelflat == NULL) return; if (levelflat->type == LEVELFLAT_FLAT) HWR_GetRawFlat(levelflat->u.flat.lumpnum); else if (levelflat->type == LEVELFLAT_TEXTURE) { GLMapTexture_t *grtex; INT32 texturenum = levelflat->u.texture.num; #ifdef PARANOIA if ((unsigned)texturenum >= gl_numtextures) I_Error("HWR_GetLevelFlat: texturenum >= numtextures"); #endif // Who knows? if (texturenum == 0 || texturenum == -1) return; // Every texture in memory, stored as a 8-bit flat. Wow! grtex = &gl_flats[texturenum]; // Generate flat if missing from the cache if (!grtex->mipmap.data && !grtex->mipmap.downloaded) HWR_CacheTextureAsFlat(&grtex->mipmap, texturenum); // If hardware does not have the texture, then call pfnSetTexture to upload it if (!grtex->mipmap.downloaded) HWD.pfnSetTexture(&grtex->mipmap); HWR_SetCurrentTexture(&grtex->mipmap); // The system-memory data can be purged now. Z_ChangeTag(grtex->mipmap.data, PU_HWRCACHE_UNLOCKED); } else if (levelflat->type == LEVELFLAT_PATCH) { patch_t *patch = W_CachePatchNum(levelflat->u.flat.lumpnum, PU_CACHE); levelflat->width = (UINT16)(patch->width); levelflat->height = (UINT16)(patch->height); HWR_GetPatch(patch); } #ifndef NO_PNG_LUMPS else if (levelflat->type == LEVELFLAT_PNG) { GLMipmap_t *mipmap = levelflat->mipmap; // Cache the picture. if (!levelflat->mippic) { INT32 pngwidth = 0, pngheight = 0; void *pic = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); Z_ChangeTag(pic, PU_LEVEL); Z_SetUser(pic, &levelflat->mippic); levelflat->width = (UINT16)pngwidth; levelflat->height = (UINT16)pngheight; } // Make the mipmap. if (mipmap == NULL) { mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_STATIC, NULL); mipmap->format = GL_TEXFMT_P_8; mipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; levelflat->mipmap = mipmap; } if (!mipmap->data && !mipmap->downloaded) { UINT8 *flat; size_t size; if (levelflat->mippic == NULL) I_Error("HWR_GetLevelFlat: levelflat->mippic == NULL"); mipmap->width = levelflat->width; mipmap->height = levelflat->height; size = (mipmap->width * mipmap->height); flat = Z_Malloc(size, PU_LEVEL, &mipmap->data); M_Memcpy(flat, levelflat->mippic, size); } // Tell the hardware driver to bind the current texture to the flat's mipmap HWR_SetCurrentTexture(mipmap); } #endif else // set no texture HWR_SetCurrentTexture(NULL); } // --------------------+ // HWR_LoadPatchMipmap : Generates a patch into a mipmap, usually the mipmap inside the patch itself // --------------------+ static void HWR_LoadPatchMipmap(patch_t *patch, GLMipmap_t *grMipmap) { GLPatch_t *grPatch = patch->hardware; if (!grMipmap->downloaded && !grMipmap->data) HWR_MakePatch(patch, grPatch, grMipmap, true); // If hardware does not have the texture, then call pfnSetTexture to upload it if (!grMipmap->downloaded) HWD.pfnSetTexture(grMipmap); HWR_SetCurrentTexture(grMipmap); // The system-memory data can be purged now. Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED); } // ----------------------+ // HWR_UpdatePatchMipmap : Updates a mipmap. // ----------------------+ static void HWR_UpdatePatchMipmap(patch_t *patch, GLMipmap_t *grMipmap) { GLPatch_t *grPatch = patch->hardware; HWR_MakePatch(patch, grPatch, grMipmap, true); // If hardware does not have the texture, then call pfnSetTexture to upload it // If it does have the texture, then call pfnUpdateTexture to update it if (!grMipmap->downloaded) HWD.pfnSetTexture(grMipmap); else HWD.pfnUpdateTexture(grMipmap); HWR_SetCurrentTexture(grMipmap); // The system-memory data can be purged now. Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED); } // -----------------+ // HWR_GetPatch : Downloads a patch to the hardware cache and make it ready for use // -----------------+ void HWR_GetPatch(patch_t *patch) { if (!patch->hardware) Patch_CreateGL(patch); HWR_LoadPatchMipmap(patch, ((GLPatch_t *)patch->hardware)->mipmap); } // -------------------+ // HWR_GetMappedPatch : Same as HWR_GetPatch for sprite color // -------------------+ void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap) { GLPatch_t *grPatch; GLMipmap_t *grMipmap, *newMipmap; if (!patch->hardware) Patch_CreateGL(patch); grPatch = patch->hardware; if (colormap == colormaps || colormap == NULL) { // Load the default (green) color in hardware cache HWR_GetPatch(patch); return; } // search for the mipmap // skip the first (no colormap translated) for (grMipmap = grPatch->mipmap; grMipmap->nextcolormap; ) { grMipmap = grMipmap->nextcolormap; if (grMipmap->colormap && grMipmap->colormap->source == colormap) { if (memcmp(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8))) { M_Memcpy(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8)); HWR_UpdatePatchMipmap(patch, grMipmap); } else HWR_LoadPatchMipmap(patch, grMipmap); return; } } // not found, create it! // If we are here, the sprite with the current colormap is not already in hardware memory //BP: WARNING: don't free it manually without clearing the cache of harware renderer // (it have a liste of mipmap) // this malloc is cleared in HWR_FreeColormapCache // (...) unfortunately z_malloc fragment alot the memory :(so malloc is better newMipmap = calloc(1, sizeof (*newMipmap)); if (newMipmap == NULL) I_Error("%s: Out of memory", "HWR_GetMappedPatch"); grMipmap->nextcolormap = newMipmap; newMipmap->colormap = Z_Calloc(sizeof(*newMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL); newMipmap->colormap->source = colormap; M_Memcpy(newMipmap->colormap->data, colormap, 256 * sizeof(UINT8)); HWR_LoadPatchMipmap(patch, newMipmap); } void HWR_UnlockCachedPatch(GLPatch_t *gpatch) { if (!gpatch) return; Z_ChangeTag(gpatch->mipmap->data, PU_HWRCACHE_UNLOCKED); } static const INT32 picmode2GR[] = { GL_TEXFMT_P_8, // PALETTE 0, // INTENSITY (unsupported yet) GL_TEXFMT_ALPHA_INTENSITY_88, // INTENSITY_ALPHA (corona use this) 0, // RGB24 (unsupported yet) GL_TEXFMT_RGBA, // RGBA32 (opengl only) }; static void HWR_DrawPicInCache(UINT8 *block, INT32 pblockwidth, INT32 pblockheight, INT32 blockmodulo, pic_t *pic, INT32 bpp) { INT32 i,j; fixed_t posx, posy, stepx, stepy; UINT8 *dest, *src, texel; UINT16 texelu16; INT32 picbpp; RGBA_t col; stepy = ((INT32)SHORT(pic->height)<width)<mode]); posy = 0; for (j = 0; j < pblockheight; j++) { posx = 0; dest = &block[j*blockmodulo]; src = &pic->data[(posy>>FRACBITS)*SHORT(pic->width)*picbpp]; for (i = 0; i < pblockwidth;i++) { switch (pic->mode) { // source bpp case PALETTE : texel = src[(posx+FRACUNIT/2)>>FRACBITS]; switch (bpp) { // destination bpp case 1 : *dest++ = texel; break; case 2 : texelu16 = (UINT16)(texel | 0xff00); memcpy(dest, &texelu16, sizeof(UINT16)); dest += sizeof(UINT16); break; case 3 : col = V_GetColor(texel); memcpy(dest, &col, sizeof(RGBA_t)-sizeof(UINT8)); dest += sizeof(RGBA_t)-sizeof(UINT8); break; case 4 : memcpy(dest, &V_GetColor(texel), sizeof(RGBA_t)); dest += sizeof(RGBA_t); break; } break; case INTENSITY : *dest++ = src[(posx+FRACUNIT/2)>>FRACBITS]; break; case INTENSITY_ALPHA : // assume dest bpp = 2 memcpy(dest, src + ((posx+FRACUNIT/2)>>FRACBITS)*sizeof(UINT16), sizeof(UINT16)); dest += sizeof(UINT16); break; case RGB24 : break; // not supported yet case RGBA32 : // assume dest bpp = 4 dest += sizeof(UINT32); memcpy(dest, src + ((posx+FRACUNIT/2)>>FRACBITS)*sizeof(UINT32), sizeof(UINT32)); break; } posx += stepx; } posy += stepy; } } // -----------------+ // HWR_GetPic : Download a Doom pic (raw row encoded with no 'holes') // Returns : // -----------------+ patch_t *HWR_GetPic(lumpnum_t lumpnum) { patch_t *patch = HWR_GetCachedGLPatch(lumpnum); GLPatch_t *grPatch = (GLPatch_t *)(patch->hardware); if (!grPatch->mipmap->downloaded && !grPatch->mipmap->data) { pic_t *pic; UINT8 *block; size_t len; pic = W_CacheLumpNum(lumpnum, PU_CACHE); patch->width = SHORT(pic->width); patch->height = SHORT(pic->height); len = W_LumpLength(lumpnum) - sizeof (pic_t); grPatch->mipmap->width = (UINT16)patch->width; grPatch->mipmap->height = (UINT16)patch->height; if (pic->mode == PALETTE) grPatch->mipmap->format = textureformat; // can be set by driver else grPatch->mipmap->format = picmode2GR[pic->mode]; Z_Free(grPatch->mipmap->data); // allocate block block = MakeBlock(grPatch->mipmap); if (patch->width == SHORT(pic->width) && patch->height == SHORT(pic->height) && format2bpp(grPatch->mipmap->format) == format2bpp(picmode2GR[pic->mode])) { // no conversion needed M_Memcpy(grPatch->mipmap->data, pic->data,len); } else HWR_DrawPicInCache(block, SHORT(pic->width), SHORT(pic->height), SHORT(pic->width)*format2bpp(grPatch->mipmap->format), pic, format2bpp(grPatch->mipmap->format)); Z_Unlock(pic); Z_ChangeTag(block, PU_HWRCACHE_UNLOCKED); grPatch->mipmap->flags = 0; grPatch->max_s = grPatch->max_t = 1.0f; } HWD.pfnSetTexture(grPatch->mipmap); //CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grPatch->mipmap->data, grPatch->mipmap->downloaded); return patch; } patch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum) { lumpcache_t *lumpcache = wadfiles[wadnum]->patchcache; if (!lumpcache[lumpnum]) { void *ptr = Z_Calloc(sizeof(patch_t), PU_PATCH, &lumpcache[lumpnum]); Patch_Create(NULL, 0, ptr); Patch_AllocateHardwarePatch(ptr); } return (patch_t *)(lumpcache[lumpnum]); } patch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum) { return HWR_GetCachedGLPatchPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum)); } // Need to do this because they aren't powers of 2 static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 pblockheight, lumpnum_t fademasklumpnum, UINT16 fmwidth, UINT16 fmheight) { INT32 i,j; fixed_t posx, posy, stepx, stepy; UINT8 *block = mipmap->data; // places the data directly into here UINT8 *flat; UINT8 *dest, *src, texel; RGBA_t col; // Place the flats data into flat W_ReadLump(fademasklumpnum, Z_Malloc(W_LumpLength(fademasklumpnum), PU_HWRCACHE, &flat)); stepy = ((INT32)fmheight<width)]; // 1bpp src = &flat[(posy>>FRACBITS)*SHORT(fmwidth)]; for (i = 0; i < pblockwidth;i++) { // fademask bpp is always 1, and is used just for alpha texel = src[(posx)>>FRACBITS]; col = V_GetColor(texel); *dest = col.s.red; // take the red level of the colour and use it for alpha, as fademasks do dest++; posx += stepx; } posy += stepy; } Z_Free(flat); } static void HWR_CacheFadeMask(GLMipmap_t *grMipmap, lumpnum_t fademasklumpnum) { size_t size; UINT16 fmheight = 0, fmwidth = 0; // setup the texture info grMipmap->format = GL_TEXFMT_ALPHA_8; // put the correct alpha levels straight in so I don't need to convert it later grMipmap->flags = 0; size = W_LumpLength(fademasklumpnum); switch (size) { // None of these are powers of 2, so I'll need to do what is done for textures and make them powers of 2 before they can be used case 256000: // 640x400 fmwidth = 640; fmheight = 400; break; case 64000: // 320x200 fmwidth = 320; fmheight = 200; break; case 16000: // 160x100 fmwidth = 160; fmheight = 100; break; case 4000: // 80x50 (minimum) fmwidth = 80; fmheight = 50; break; default: // Bad lump CONS_Alert(CONS_WARNING, "Fade mask lump of incorrect size, ignored\n"); // I should avoid this by checking the lumpnum in HWR_RunWipe break; } // Thankfully, this will still work for this scenario grMipmap->width = fmwidth; grMipmap->height = fmheight; MakeBlock(grMipmap); HWR_DrawFadeMaskInCache(grMipmap, fmwidth, fmheight, fademasklumpnum, fmwidth, fmheight); // I DO need to convert this because it isn't power of 2 and we need the alpha } void HWR_GetFadeMask(lumpnum_t fademasklumpnum) { patch_t *patch = HWR_GetCachedGLPatch(fademasklumpnum); GLMipmap_t *grmip = ((GLPatch_t *)Patch_AllocateHardwarePatch(patch))->mipmap; if (!grmip->downloaded && !grmip->data) HWR_CacheFadeMask(grmip, fademasklumpnum); HWD.pfnSetTexture(grmip); // The system-memory data can be purged now. Z_ChangeTag(grmip->data, PU_HWRCACHE_UNLOCKED); } #endif //HWRENDER