2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
2018-11-25 12:35:38 +00:00
// Copyright (C) 1999-2018 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// 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 r_data.c
/// \brief Preparation of data for rendering,generation of lookups, caching, retrieval by name
# include "doomdef.h"
# include "g_game.h"
# include "i_video.h"
# include "r_local.h"
# include "r_sky.h"
# include "p_local.h"
# include "m_misc.h"
# include "r_data.h"
# include "w_wad.h"
# include "z_zone.h"
# include "p_setup.h" // levelflats
2017-04-29 15:40:07 +00:00
# include "v_video.h" // pMasterPalette
2019-09-20 01:40:23 +00:00
# include "byteptr.h"
2014-03-15 16:59:03 +00:00
# include "dehacked.h"
2017-09-29 22:25:34 +00:00
# ifdef _WIN32
2014-03-15 16:59:03 +00:00
# include <malloc.h> // alloca(sizeof)
# endif
# if defined(_MSC_VER)
# pragma pack(1)
# endif
// Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog
# ifdef _WIN32_WCE
# define AVOID_ERRNO
# else
# include <errno.h>
# endif
2019-05-27 02:37:23 +00:00
# ifdef HAVE_PNG
# ifndef _MSC_VER
# ifndef _LARGEFILE64_SOURCE
# define _LARGEFILE64_SOURCE
# endif
# endif
# ifndef _LFS64_LARGEFILE
# define _LFS64_LARGEFILE
# endif
# ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 0
# endif
# include "png.h"
# ifndef PNG_READ_SUPPORTED
# undef HAVE_PNG
# endif
# endif
2014-03-15 16:59:03 +00:00
//
// Texture definition.
// Each texture is composed of one or more patches,
// with patches being lumps stored in the WAD.
// The lumps are referenced by number, and patched
// into the rectangular texture space using origin
// and possibly other attributes.
//
typedef struct
{
INT16 originx , originy ;
INT16 patch , stepdir , colormap ;
} ATTRPACK mappatch_t ;
//
// Texture definition.
// An SRB2 wall texture is a list of patches
// which are to be combined in a predefined order.
//
typedef struct
{
char name [ 8 ] ;
INT32 masked ;
INT16 width ;
INT16 height ;
INT32 columndirectory ; // FIXTHIS: OBSOLETE
INT16 patchcount ;
mappatch_t patches [ 1 ] ;
} ATTRPACK maptexture_t ;
# if defined(_MSC_VER)
# pragma pack()
# endif
// Store lists of lumps for F_START/F_END etc.
typedef struct
{
UINT16 wadfile ;
UINT16 firstlump ;
size_t numlumps ;
} lumplist_t ;
//
// Graphics.
// SRB2 graphics for walls and sprites
// is stored in vertical runs of opaque pixels (posts).
// A column is composed of zero or more posts,
// a patch or sprite is composed of zero or more columns.
//
size_t numspritelumps , max_spritelumps ;
// textures
INT32 numtextures = 0 ; // total number of textures found,
// size of following tables
texture_t * * textures = NULL ;
2019-05-21 18:24:26 +00:00
textureflat_t * texflats = NULL ;
2014-03-15 16:59:03 +00:00
static UINT32 * * texturecolumnofs ; // column offset lookup table for each texture
static UINT8 * * texturecache ; // graphics data for each generated full-size texture
2019-05-21 03:28:52 +00:00
INT32 * texturewidth ;
2014-03-15 16:59:03 +00:00
fixed_t * textureheight ; // needed for texture pegging
INT32 * texturetranslation ;
// needed for pre rendering
sprcache_t * spritecachedinfo ;
lighttable_t * colormaps ;
// for debugging/info purposes
static size_t flatmemory , spritememory , texturememory ;
// highcolor stuff
INT16 color8to16 [ 256 ] ; // remap color index to highcolor rgb value
INT16 * hicolormaps ; // test a 32k colormap remaps high -> high
// Painfully simple texture id cacheing to make maps load faster. :3
static struct {
char name [ 9 ] ;
INT32 id ;
} * tidcache = NULL ;
static INT32 tidcachelen = 0 ;
//
// MAPTEXTURE_T CACHING
// When a texture is first needed, it counts the number of composite columns
// required in the texture and allocates space for a column directory and
// any new columns.
// The directory will simply point inside other patches if there is only one
// patch in a given column, but any columns with multiple patches will have
// new column_ts generated.
//
//
// R_DrawColumnInCache
// Clip and draw a column from a patch into a cached post.
//
2017-03-05 11:49:09 +00:00
static inline void R_DrawColumnInCache ( column_t * patch , UINT8 * cache , texpatch_t * originPatch , INT32 cacheheight , INT32 patchheight )
2014-03-15 16:59:03 +00:00
{
INT32 count , position ;
UINT8 * source ;
INT32 topdelta , prevdelta = - 1 ;
2017-03-12 19:26:45 +00:00
INT32 originy = originPatch - > originy ;
2014-03-15 16:59:03 +00:00
2017-03-19 19:43:02 +00:00
( void ) patchheight ; // This parameter is unused
2014-03-15 16:59:03 +00:00
while ( patch - > topdelta ! = 0xff )
{
topdelta = patch - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
source = ( UINT8 * ) patch + 3 ;
count = patch - > length ;
position = originy + topdelta ;
if ( position < 0 )
{
count + = position ;
2016-12-01 21:43:27 +00:00
source - = position ; // start further down the column
2014-03-15 16:59:03 +00:00
position = 0 ;
}
if ( position + count > cacheheight )
count = cacheheight - position ;
if ( count > 0 )
M_Memcpy ( cache + position , source , count ) ;
patch = ( column_t * ) ( ( UINT8 * ) patch + patch - > length + 4 ) ;
}
}
2017-03-12 19:02:29 +00:00
//
// R_DrawFlippedColumnInCache
// Similar to R_DrawColumnInCache; it draws the column inverted, however.
//
2017-03-05 11:49:09 +00:00
static inline void R_DrawFlippedColumnInCache ( column_t * patch , UINT8 * cache , texpatch_t * originPatch , INT32 cacheheight , INT32 patchheight )
2016-11-28 22:21:54 +00:00
{
INT32 count , position ;
UINT8 * source , * dest ;
INT32 topdelta , prevdelta = - 1 ;
2017-03-12 19:26:45 +00:00
INT32 originy = originPatch - > originy ;
2016-11-28 22:21:54 +00:00
while ( patch - > topdelta ! = 0xff )
{
topdelta = patch - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
topdelta = patchheight - patch - > length - topdelta ;
source = ( UINT8 * ) patch + 2 + patch - > length ; // patch + 3 + (patch->length-1)
count = patch - > length ;
position = originy + topdelta ;
if ( position < 0 )
{
count + = position ;
2016-12-01 21:43:27 +00:00
source + = position ; // start further UP the column
2016-11-28 22:21:54 +00:00
position = 0 ;
}
if ( position + count > cacheheight )
count = cacheheight - position ;
dest = cache + position ;
if ( count > 0 )
{
for ( ; dest < cache + position + count ; - - source )
* dest + + = * source ;
}
patch = ( column_t * ) ( ( UINT8 * ) patch + patch - > length + 4 ) ;
}
}
2017-03-04 23:29:10 +00:00
2019-09-18 01:29:53 +00:00
UINT32 ASTBlendPixel ( RGBA_t background , RGBA_t foreground , int style , UINT8 alpha )
2019-09-08 16:21:00 +00:00
{
RGBA_t output ;
if ( style = = AST_TRANSLUCENT )
{
if ( alpha = = 0 )
output . rgba = background . rgba ;
else if ( alpha = = 0xFF )
output . rgba = foreground . rgba ;
else if ( alpha < 0xFF )
{
UINT8 beta = ( 0xFF - alpha ) ;
output . s . red = ( ( background . s . red * beta ) + ( foreground . s . red * alpha ) ) / 0xFF ;
output . s . green = ( ( background . s . green * beta ) + ( foreground . s . green * alpha ) ) / 0xFF ;
output . s . blue = ( ( background . s . blue * beta ) + ( foreground . s . blue * alpha ) ) / 0xFF ;
}
// write foreground pixel alpha
// if there's no pixel in here
if ( ! background . rgba )
output . s . alpha = foreground . s . alpha ;
2019-09-23 21:27:43 +00:00
else
output . s . alpha = 0xFF ;
2019-09-23 21:25:16 +00:00
return output . rgba ;
2019-09-08 16:21:00 +00:00
}
# define clamp(c) max(min(c, 0xFF), 0x00);
else
{
float falpha = ( ( float ) alpha / 256.0f ) ;
float fr = ( ( float ) foreground . s . red * falpha ) ;
float fg = ( ( float ) foreground . s . green * falpha ) ;
float fb = ( ( float ) foreground . s . blue * falpha ) ;
if ( style = = AST_ADD )
{
output . s . red = clamp ( ( int ) ( background . s . red + fr ) ) ;
output . s . green = clamp ( ( int ) ( background . s . green + fg ) ) ;
output . s . blue = clamp ( ( int ) ( background . s . blue + fb ) ) ;
}
else if ( style = = AST_SUBTRACT )
{
output . s . red = clamp ( ( int ) ( background . s . red - fr ) ) ;
output . s . green = clamp ( ( int ) ( background . s . green - fg ) ) ;
output . s . blue = clamp ( ( int ) ( background . s . blue - fb ) ) ;
}
else if ( style = = AST_REVERSESUBTRACT )
{
output . s . red = clamp ( ( int ) ( ( - background . s . red ) + fr ) ) ;
output . s . green = clamp ( ( int ) ( ( - background . s . green ) + fg ) ) ;
output . s . blue = clamp ( ( int ) ( ( - background . s . blue ) + fb ) ) ;
}
else if ( style = = AST_MODULATE )
{
fr = ( ( float ) foreground . s . red / 256.0f ) ;
fg = ( ( float ) foreground . s . green / 256.0f ) ;
fb = ( ( float ) foreground . s . blue / 256.0f ) ;
output . s . red = clamp ( ( int ) ( background . s . red * fr ) ) ;
output . s . green = clamp ( ( int ) ( background . s . green * fg ) ) ;
output . s . blue = clamp ( ( int ) ( background . s . blue * fb ) ) ;
}
// just copy the pixel
else if ( style = = AST_COPY )
2019-09-18 01:29:53 +00:00
output . rgba = foreground . rgba ;
output . s . alpha = 0xFF ;
return output . rgba ;
2019-09-08 16:21:00 +00:00
}
# undef clamp
2019-09-18 01:29:53 +00:00
return 0 ;
2019-09-08 16:21:00 +00:00
}
UINT8 ASTBlendPixel_8bpp ( UINT8 background , UINT8 foreground , int style , UINT8 alpha )
{
2019-09-23 21:25:16 +00:00
// Alpha style set to translucent?
if ( style = = AST_TRANSLUCENT )
2019-09-08 16:21:00 +00:00
{
2019-09-23 21:25:16 +00:00
// Is the alpha small enough for translucency?
if ( alpha < = ( 10 * 255 / 11 ) )
{
UINT8 * mytransmap ;
// Is the patch way too translucent? Don't blend then.
if ( alpha < 255 / 11 )
return background ;
// The equation's not exact but it works as intended. I'll call it a day for now.
mytransmap = transtables + ( ( 8 * ( alpha ) + 255 / 8 ) / ( 255 - 255 / 11 ) < < FF_TRANSSHIFT ) ;
if ( background ! = 0xFF )
return * ( mytransmap + ( background < < 8 ) + foreground ) ;
}
else // just copy the pixel
return foreground ;
2019-09-08 16:21:00 +00:00
}
// just copy the pixel
else if ( style = = AST_COPY )
2019-09-18 01:29:53 +00:00
return foreground ;
2019-09-08 16:21:00 +00:00
// use ASTBlendPixel for all other blend modes
// and find the nearest colour in the palette
else if ( style ! = AST_TRANSLUCENT )
{
RGBA_t texel ;
RGBA_t bg = V_GetColor ( background ) ;
RGBA_t fg = V_GetColor ( foreground ) ;
2019-09-18 01:29:53 +00:00
texel . rgba = ASTBlendPixel ( bg , fg , style , alpha ) ;
2019-09-08 16:21:00 +00:00
return NearestColor ( texel . s . red , texel . s . green , texel . s . blue ) ;
}
// fallback if all above fails, somehow
// return the background pixel
return background ;
}
2017-03-12 19:02:29 +00:00
//
2019-09-08 16:21:00 +00:00
// R_DrawBlendColumnInCache
2017-03-12 19:02:29 +00:00
// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()).
//
2019-09-08 16:21:00 +00:00
static inline void R_DrawBlendColumnInCache ( column_t * patch , UINT8 * cache , texpatch_t * originPatch , INT32 cacheheight , INT32 patchheight )
2017-03-04 19:59:43 +00:00
{
INT32 count , position ;
2017-03-04 23:29:10 +00:00
UINT8 * source , * dest ;
2017-03-04 19:59:43 +00:00
INT32 topdelta , prevdelta = - 1 ;
2017-03-12 19:26:45 +00:00
INT32 originy = originPatch - > originy ;
2017-03-04 19:59:43 +00:00
2017-03-19 19:43:02 +00:00
( void ) patchheight ; // This parameter is unused
2017-03-04 19:59:43 +00:00
while ( patch - > topdelta ! = 0xff )
{
topdelta = patch - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
source = ( UINT8 * ) patch + 3 ;
count = patch - > length ;
position = originy + topdelta ;
if ( position < 0 )
{
count + = position ;
source - = position ; // start further down the column
position = 0 ;
}
if ( position + count > cacheheight )
count = cacheheight - position ;
2017-03-12 19:26:45 +00:00
dest = cache + position ;
2017-03-04 19:59:43 +00:00
if ( count > 0 )
2017-03-12 19:26:45 +00:00
{
2017-03-05 17:53:34 +00:00
for ( ; dest < cache + position + count ; source + + , dest + + )
2019-09-23 22:20:59 +00:00
if ( * source ! = 0xFF )
* dest = ASTBlendPixel_8bpp ( * dest , * source , originPatch - > style , originPatch - > alpha ) ;
2017-03-12 19:26:45 +00:00
}
2017-03-04 19:59:43 +00:00
patch = ( column_t * ) ( ( UINT8 * ) patch + patch - > length + 4 ) ;
}
}
2017-03-12 19:02:29 +00:00
//
2019-09-23 21:25:16 +00:00
// R_DrawBlendFlippedColumnInCache
2017-03-12 19:02:29 +00:00
// Similar to the one above except that the column is inverted.
//
2019-09-08 16:21:00 +00:00
static inline void R_DrawBlendFlippedColumnInCache ( column_t * patch , UINT8 * cache , texpatch_t * originPatch , INT32 cacheheight , INT32 patchheight )
2017-03-05 17:53:34 +00:00
{
INT32 count , position ;
UINT8 * source , * dest ;
INT32 topdelta , prevdelta = - 1 ;
2017-03-12 19:26:45 +00:00
INT32 originy = originPatch - > originy ;
2017-03-04 19:59:43 +00:00
2017-03-05 17:53:34 +00:00
while ( patch - > topdelta ! = 0xff )
{
topdelta = patch - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
topdelta = patchheight - patch - > length - topdelta ;
source = ( UINT8 * ) patch + 2 + patch - > length ; // patch + 3 + (patch->length-1)
count = patch - > length ;
position = originy + topdelta ;
if ( position < 0 )
{
count + = position ;
source + = position ; // start further UP the column
position = 0 ;
}
if ( position + count > cacheheight )
count = cacheheight - position ;
dest = cache + position ;
if ( count > 0 )
{
for ( ; dest < cache + position + count ; - - source , dest + + )
2019-09-23 22:20:59 +00:00
if ( * source ! = 0xFF )
* dest = ASTBlendPixel_8bpp ( * dest , * source , originPatch - > style , originPatch - > alpha ) ;
2017-03-05 17:53:34 +00:00
}
patch = ( column_t * ) ( ( UINT8 * ) patch + patch - > length + 4 ) ;
}
}
2016-11-28 22:21:54 +00:00
2014-03-15 16:59:03 +00:00
//
// R_GenerateTexture
//
// Allocate space for full size texture, either single patch or 'composite'
// Build the full textures from patches.
// The texture caching system is a little more hungry of memory, but has
2019-05-27 02:37:23 +00:00
// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed.
2014-03-15 16:59:03 +00:00
//
// This is not optimised, but it's supposed to be executed only once
// per level, when enough memory is available.
//
static UINT8 * R_GenerateTexture ( size_t texnum )
{
UINT8 * block ;
UINT8 * blocktex ;
texture_t * texture ;
texpatch_t * patch ;
patch_t * realpatch ;
2016-11-28 22:21:54 +00:00
int x , x1 , x2 , i , width , height ;
2014-03-15 16:59:03 +00:00
size_t blocksize ;
column_t * patchcol ;
UINT32 * colofs ;
2019-05-27 02:37:23 +00:00
UINT16 wadnum ;
lumpnum_t lumpnum ;
size_t lumplength ;
2014-03-15 16:59:03 +00:00
I_Assert ( texnum < = ( size_t ) numtextures ) ;
texture = textures [ texnum ] ;
I_Assert ( texture ! = NULL ) ;
// allocate texture column offset lookup
// single-patch textures can have holes in them and may be used on
// 2sided lines so they need to be kept in 'packed' format
// BUT this is wrong for skies and walls with over 255 pixels,
// so check if there's holes and if not strip the posts.
if ( texture - > patchcount = = 1 )
{
boolean holey = false ;
patch = texture - > patches ;
2019-05-27 02:37:23 +00:00
wadnum = patch - > wad ;
lumpnum = patch - > lump ;
lumplength = W_LumpLengthPwad ( wadnum , lumpnum ) ;
realpatch = W_CacheLumpNumPwad ( wadnum , lumpnum , PU_CACHE ) ;
2019-09-07 19:54:26 +00:00
2019-09-06 22:41:29 +00:00
# ifndef NO_PNG_LUMPS
2019-05-27 02:37:23 +00:00
if ( R_IsLumpPNG ( ( UINT8 * ) realpatch , lumplength ) )
2019-09-07 19:54:26 +00:00
{
2019-09-11 19:59:28 +00:00
realpatch = R_PNGToPatch ( ( UINT8 * ) realpatch , lumplength , NULL , false ) ;
2019-09-07 19:54:26 +00:00
goto multipatch ;
}
2019-09-06 22:41:29 +00:00
# endif
2014-03-15 16:59:03 +00:00
// Check the patch for holes.
if ( texture - > width > SHORT ( realpatch - > width ) | | texture - > height > SHORT ( realpatch - > height ) )
holey = true ;
colofs = ( UINT32 * ) realpatch - > columnofs ;
for ( x = 0 ; x < texture - > width & & ! holey ; x + + )
{
column_t * col = ( column_t * ) ( ( UINT8 * ) realpatch + LONG ( colofs [ x ] ) ) ;
INT32 topdelta , prevdelta = - 1 , y = 0 ;
while ( col - > topdelta ! = 0xff )
{
topdelta = col - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
if ( topdelta > y )
break ;
y = topdelta + col - > length + 1 ;
col = ( column_t * ) ( ( UINT8 * ) col + col - > length + 4 ) ;
}
if ( y < texture - > height )
holey = true ; // this texture is HOLEy! D:
}
// If the patch uses transparency, we have to save it this way.
if ( holey )
{
texture - > holes = true ;
2016-11-28 22:21:54 +00:00
texture - > flip = patch - > flip ;
2019-05-27 02:37:23 +00:00
blocksize = lumplength ;
2014-03-15 16:59:03 +00:00
block = Z_Calloc ( blocksize , PU_STATIC , // will change tag at end of this function
& texturecache [ texnum ] ) ;
M_Memcpy ( block , realpatch , blocksize ) ;
texturememory + = blocksize ;
// use the patch's column lookup
colofs = ( UINT32 * ) ( void * ) ( block + 8 ) ;
texturecolumnofs [ texnum ] = colofs ;
blocktex = block ;
2016-12-01 22:37:33 +00:00
if ( patch - > flip & 1 ) // flip the patch horizontally
{
UINT32 * realcolofs = ( UINT32 * ) realpatch - > columnofs ;
for ( x = 0 ; x < texture - > width ; x + + )
colofs [ x ] = realcolofs [ texture - > width - 1 - x ] ; // swap with the offset of the other side of the texture
}
// we can't as easily flip the patch vertically sadly though,
// we have wait until the texture itself is drawn to do that
2014-03-15 16:59:03 +00:00
for ( x = 0 ; x < texture - > width ; x + + )
colofs [ x ] = LONG ( LONG ( colofs [ x ] ) + 3 ) ;
goto done ;
}
// Otherwise, do multipatch format.
}
// multi-patch textures (or 'composite')
2019-09-07 19:54:26 +00:00
# ifndef NO_PNG_LUMPS
multipatch :
# endif
2014-03-15 16:59:03 +00:00
texture - > holes = false ;
2016-11-28 22:21:54 +00:00
texture - > flip = 0 ;
2014-03-15 16:59:03 +00:00
blocksize = ( texture - > width * 4 ) + ( texture - > width * texture - > height ) ;
texturememory + = blocksize ;
block = Z_Malloc ( blocksize + 1 , PU_STATIC , & texturecache [ texnum ] ) ;
2016-11-07 20:27:12 +00:00
memset ( block , 0xFF , blocksize + 1 ) ; // Transparency hack
2014-03-15 16:59:03 +00:00
// columns lookup table
colofs = ( UINT32 * ) ( void * ) block ;
texturecolumnofs [ texnum ] = colofs ;
// texture data after the lookup table
blocktex = block + ( texture - > width * 4 ) ;
// Composite the columns together.
for ( i = 0 , patch = texture - > patches ; i < texture - > patchcount ; i + + , patch + + )
{
2017-03-12 19:26:45 +00:00
static void ( * ColumnDrawerPointer ) ( column_t * , UINT8 * , texpatch_t * , INT32 , INT32 ) ; // Column drawing function pointer.
2019-09-08 16:21:00 +00:00
if ( patch - > style ! = AST_COPY )
ColumnDrawerPointer = ( patch - > flip & 2 ) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache ;
2017-03-12 19:26:45 +00:00
else
2017-03-19 20:08:33 +00:00
ColumnDrawerPointer = ( patch - > flip & 2 ) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache ;
2017-03-05 11:49:09 +00:00
2019-05-27 02:37:23 +00:00
wadnum = patch - > wad ;
lumpnum = patch - > lump ;
lumplength = W_LumpLengthPwad ( wadnum , lumpnum ) ;
realpatch = W_CacheLumpNumPwad ( wadnum , lumpnum , PU_CACHE ) ;
2019-09-06 22:41:29 +00:00
# ifndef NO_PNG_LUMPS
2019-05-27 02:37:23 +00:00
if ( R_IsLumpPNG ( ( UINT8 * ) realpatch , lumplength ) )
2019-09-11 19:59:28 +00:00
realpatch = R_PNGToPatch ( ( UINT8 * ) realpatch , lumplength , NULL , false ) ;
2019-09-06 22:41:29 +00:00
# endif
2019-05-27 02:37:23 +00:00
2014-03-15 16:59:03 +00:00
x1 = patch - > originx ;
2016-11-28 22:21:54 +00:00
width = SHORT ( realpatch - > width ) ;
height = SHORT ( realpatch - > height ) ;
x2 = x1 + width ;
2014-03-15 16:59:03 +00:00
2018-02-09 17:23:35 +00:00
if ( x1 > texture - > width | | x2 < 0 )
continue ; // patch not located within texture's x bounds, ignore
if ( patch - > originy > texture - > height | | ( patch - > originy + height ) < 0 )
continue ; // 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
2014-03-15 16:59:03 +00:00
if ( x1 < 0 )
x = 0 ;
else
x = x1 ;
2018-02-09 17:23:35 +00:00
// right edge
2014-03-15 16:59:03 +00:00
if ( x2 > texture - > width )
x2 = texture - > width ;
for ( ; x < x2 ; x + + )
{
2016-11-28 22:21:54 +00:00
if ( patch - > flip & 1 )
patchcol = ( column_t * ) ( ( UINT8 * ) realpatch + LONG ( realpatch - > columnofs [ ( x1 + width - 1 ) - x ] ) ) ;
else
patchcol = ( column_t * ) ( ( UINT8 * ) realpatch + LONG ( realpatch - > columnofs [ x - x1 ] ) ) ;
2014-03-15 16:59:03 +00:00
// generate column ofset lookup
colofs [ x ] = LONG ( ( x * texture - > height ) + ( texture - > width * 4 ) ) ;
2017-03-19 20:08:33 +00:00
ColumnDrawerPointer ( patchcol , block + LONG ( colofs [ x ] ) , patch , texture - > height , height ) ;
2014-03-15 16:59:03 +00:00
}
}
done :
// Now that the texture has been built in column cache, it is purgable from zone memory.
Z_ChangeTag ( block , PU_CACHE ) ;
return blocktex ;
}
2016-12-16 21:38:53 +00:00
//
// R_GetTextureNum
//
// Returns the actual texture id that we should use.
// This can either be texnum, the current frame for texnum's anim (if animated),
// or 0 if not valid.
//
INT32 R_GetTextureNum ( INT32 texnum )
{
if ( texnum < 0 | | texnum > = numtextures )
return 0 ;
return texturetranslation [ texnum ] ;
}
2016-12-17 19:59:54 +00:00
//
// R_CheckTextureCache
//
// Use this if you need to make sure the texture is cached before R_GetColumn calls
// e.g.: midtextures and FOF walls
//
void R_CheckTextureCache ( INT32 tex )
{
if ( ! texturecache [ tex ] )
R_GenerateTexture ( tex ) ;
}
2014-03-15 16:59:03 +00:00
//
// R_GetColumn
//
UINT8 * R_GetColumn ( fixed_t tex , INT32 col )
{
UINT8 * data ;
2019-05-21 03:28:52 +00:00
INT32 width = texturewidth [ tex ] ;
2014-03-15 16:59:03 +00:00
2019-05-21 03:28:52 +00:00
if ( width & ( width - 1 ) )
col = ( UINT32 ) col % width ;
else
col & = ( width - 1 ) ;
2014-03-15 16:59:03 +00:00
2019-05-21 03:28:52 +00:00
data = texturecache [ tex ] ;
2014-03-15 16:59:03 +00:00
if ( ! data )
data = R_GenerateTexture ( tex ) ;
return data + LONG ( texturecolumnofs [ tex ] [ col ] ) ;
}
// convert flats to hicolor as they are requested
//
UINT8 * R_GetFlat ( lumpnum_t flatlumpnum )
{
return W_CacheLumpNum ( flatlumpnum , PU_CACHE ) ;
}
//
// Empty the texture cache (used for load wad at runtime)
//
void R_FlushTextureCache ( void )
{
INT32 i ;
if ( numtextures )
for ( i = 0 ; i < numtextures ; i + + )
Z_Free ( texturecache [ i ] ) ;
}
// Need these prototypes for later; defining them here instead of r_data.h so they're "private"
2017-05-16 19:10:02 +00:00
int R_CountTexturesInTEXTURESLump ( UINT16 wadNum , UINT16 lumpNum ) ;
void R_ParseTEXTURESLump ( UINT16 wadNum , UINT16 lumpNum , INT32 * index ) ;
2014-03-15 16:59:03 +00:00
//
// R_LoadTextures
// Initializes the texture list with the textures from the world map.
//
# define TX_START "TX_START"
# define TX_END "TX_END"
void R_LoadTextures ( void )
{
2019-05-21 03:28:52 +00:00
INT32 i , w ;
2014-03-15 16:59:03 +00:00
UINT16 j ;
UINT16 texstart , texend , texturesLumpPos ;
patch_t * patchlump ;
texpatch_t * patch ;
texture_t * texture ;
// Free previous memory before numtextures change.
if ( numtextures )
{
for ( i = 0 ; i < numtextures ; i + + )
{
Z_Free ( textures [ i ] ) ;
Z_Free ( texturecache [ i ] ) ;
}
Z_Free ( texturetranslation ) ;
Z_Free ( textures ) ;
2019-05-21 18:24:26 +00:00
Z_Free ( texflats ) ;
2014-03-15 16:59:03 +00:00
}
// Load patches and textures.
// Get the number of textures to check.
// NOTE: Make SURE the system does not process
// the markers.
// This system will allocate memory for all duplicate/patched textures even if it never uses them,
// but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures.
for ( w = 0 , numtextures = 0 ; w < numwadfiles ; w + + )
{
2019-03-03 22:09:34 +00:00
// Count the textures from TEXTURES lumps
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , 0 ) ;
while ( texturesLumpPos ! = INT16_MAX )
{
numtextures + = R_CountTexturesInTEXTURESLump ( ( UINT16 ) w , ( UINT16 ) texturesLumpPos ) ;
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , texturesLumpPos + 1 ) ;
}
// Count single-patch textures
2017-05-01 14:37:32 +00:00
if ( wadfiles [ w ] - > type = = RET_PK3 )
{
2017-05-28 19:47:15 +00:00
texstart = W_CheckNumForFolderStartPK3 ( " textures/ " , ( UINT16 ) w , 0 ) ;
2017-05-06 14:52:53 +00:00
texend = W_CheckNumForFolderEndPK3 ( " textures/ " , ( UINT16 ) w , texstart ) ;
2017-05-01 14:37:32 +00:00
}
else
{
2019-03-03 22:09:34 +00:00
texstart = W_CheckNumForNamePwad ( TX_START , ( UINT16 ) w , 0 ) ;
2017-05-01 14:37:32 +00:00
texend = W_CheckNumForNamePwad ( TX_END , ( UINT16 ) w , 0 ) ;
2017-10-26 15:58:18 +00:00
}
2019-03-03 22:09:34 +00:00
if ( texstart = = INT16_MAX | | texend = = INT16_MAX )
continue ;
texstart + + ; // Do not count the first marker
2014-03-15 16:59:03 +00:00
2019-03-03 22:09:34 +00:00
// PK3s have subfolders, so we can't just make a simple sum
if ( wadfiles [ w ] - > type = = RET_PK3 )
2014-03-15 16:59:03 +00:00
{
2019-03-03 22:09:34 +00:00
for ( j = texstart ; j < texend ; j + + )
{
if ( ! W_IsLumpFolder ( ( UINT16 ) w , j ) ) // Check if lump is a folder; if not, then count it
numtextures + + ;
}
2014-03-15 16:59:03 +00:00
}
2019-03-03 22:09:34 +00:00
else // Add all the textures between TX_START and TX_END
2014-03-15 16:59:03 +00:00
{
2019-03-03 22:09:34 +00:00
numtextures + = ( UINT32 ) ( texend - texstart ) ;
2014-03-15 16:59:03 +00:00
}
}
2019-03-03 22:09:34 +00:00
// If no textures found by this point, bomb out
if ( ! numtextures )
I_Error ( " No textures detected in any WADs! \n " ) ;
2014-03-15 16:59:03 +00:00
// Allocate memory and initialize to 0 for all the textures we are initialising.
// There are actually 5 buffers allocated in one for convenience.
textures = Z_Calloc ( ( numtextures * sizeof ( void * ) ) * 5 , PU_STATIC , NULL ) ;
2019-05-21 18:24:26 +00:00
texflats = Z_Calloc ( ( numtextures * sizeof ( * texflats ) ) , PU_STATIC , NULL ) ;
2014-03-15 16:59:03 +00:00
// Allocate texture column offset table.
texturecolumnofs = ( void * ) ( ( UINT8 * ) textures + ( numtextures * sizeof ( void * ) ) ) ;
// Allocate texture referencing cache.
texturecache = ( void * ) ( ( UINT8 * ) textures + ( ( numtextures * sizeof ( void * ) ) * 2 ) ) ;
2019-05-21 03:28:52 +00:00
// Allocate texture width table.
texturewidth = ( void * ) ( ( UINT8 * ) textures + ( ( numtextures * sizeof ( void * ) ) * 3 ) ) ;
// Allocate texture height table.
2014-03-15 16:59:03 +00:00
textureheight = ( void * ) ( ( UINT8 * ) textures + ( ( numtextures * sizeof ( void * ) ) * 4 ) ) ;
// Create translation table for global animation.
texturetranslation = Z_Malloc ( ( numtextures + 1 ) * sizeof ( * texturetranslation ) , PU_STATIC , NULL ) ;
for ( i = 0 ; i < numtextures ; i + + )
texturetranslation [ i ] = i ;
for ( i = 0 , w = 0 ; w < numwadfiles ; w + + )
{
// Get the lump numbers for the markers in the WAD, if they exist.
2017-05-01 14:37:32 +00:00
if ( wadfiles [ w ] - > type = = RET_PK3 )
{
2017-05-28 19:47:15 +00:00
texstart = W_CheckNumForFolderStartPK3 ( " textures/ " , ( UINT16 ) w , 0 ) ;
2017-05-06 14:52:53 +00:00
texend = W_CheckNumForFolderEndPK3 ( " textures/ " , ( UINT16 ) w , texstart ) ;
2017-06-25 12:02:39 +00:00
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , 0 ) ;
2017-05-28 19:47:15 +00:00
while ( texturesLumpPos ! = INT16_MAX )
{
R_ParseTEXTURESLump ( w , texturesLumpPos , & i ) ;
2017-06-25 12:02:39 +00:00
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , texturesLumpPos + 1 ) ;
2017-05-28 19:47:15 +00:00
}
2017-05-01 14:37:32 +00:00
}
else
{
2019-03-03 22:09:34 +00:00
texstart = W_CheckNumForNamePwad ( TX_START , ( UINT16 ) w , 0 ) ;
2017-05-01 14:37:32 +00:00
texend = W_CheckNumForNamePwad ( TX_END , ( UINT16 ) w , 0 ) ;
2017-05-16 19:10:02 +00:00
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , 0 ) ;
2017-05-28 19:47:15 +00:00
if ( texturesLumpPos ! = INT16_MAX )
R_ParseTEXTURESLump ( w , texturesLumpPos , & i ) ;
2017-05-01 14:37:32 +00:00
}
2014-03-15 16:59:03 +00:00
if ( texstart = = INT16_MAX | | texend = = INT16_MAX )
continue ;
2019-03-03 22:09:34 +00:00
texstart + + ; // Do not count the first marker
2014-03-15 16:59:03 +00:00
// Work through each lump between the markers in the WAD.
2019-03-03 22:09:34 +00:00
for ( j = 0 ; j < ( texend - texstart ) ; j + + )
2014-03-15 16:59:03 +00:00
{
2019-05-27 02:37:23 +00:00
UINT16 wadnum = ( UINT16 ) w ;
lumpnum_t lumpnum = texstart + j ;
2019-09-06 22:20:45 +00:00
size_t lumplength ;
2019-03-03 22:09:34 +00:00
if ( wadfiles [ w ] - > type = = RET_PK3 )
{
2019-09-06 22:20:45 +00:00
if ( W_IsLumpFolder ( wadnum , lumpnum ) ) // Check if lump is a folder
2019-03-03 22:09:34 +00:00
continue ; // If it is then SKIP IT
}
2019-09-06 22:20:45 +00:00
lumplength = W_LumpLengthPwad ( wadnum , lumpnum ) ;
2019-05-27 02:37:23 +00:00
patchlump = W_CacheLumpNumPwad ( wadnum , lumpnum , PU_CACHE ) ;
2014-03-15 16:59:03 +00:00
2017-08-07 19:33:24 +00:00
//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
texture = textures [ i ] = Z_Calloc ( sizeof ( texture_t ) + sizeof ( texpatch_t ) , PU_STATIC , NULL ) ;
2014-03-15 16:59:03 +00:00
2017-08-07 19:33:24 +00:00
// Set texture properties.
2019-07-24 23:07:36 +00:00
M_Memcpy ( texture - > name , W_CheckNameForNumPwad ( wadnum , lumpnum ) , sizeof ( texture - > name ) ) ;
2019-09-06 22:41:29 +00:00
# ifndef NO_PNG_LUMPS
2019-07-24 23:07:36 +00:00
if ( R_IsLumpPNG ( ( UINT8 * ) patchlump , lumplength ) )
2014-03-15 16:59:03 +00:00
{
2019-07-24 23:07:36 +00:00
INT16 width , height ;
R_PNGDimensions ( ( UINT8 * ) patchlump , & width , & height , lumplength ) ;
texture - > width = width ;
texture - > height = height ;
2014-03-15 16:59:03 +00:00
}
else
2019-09-06 22:41:29 +00:00
# endif
2014-03-15 16:59:03 +00:00
{
2019-07-24 23:07:36 +00:00
texture - > width = SHORT ( patchlump - > width ) ;
texture - > height = SHORT ( patchlump - > height ) ;
}
2017-08-07 19:33:24 +00:00
texture - > patchcount = 1 ;
texture - > holes = false ;
texture - > flip = 0 ;
2014-03-15 16:59:03 +00:00
2017-08-07 19:33:24 +00:00
// Allocate information for the texture's patches.
patch = & texture - > patches [ 0 ] ;
2017-04-19 19:00:19 +00:00
2017-08-07 19:33:24 +00:00
patch - > originx = patch - > originy = 0 ;
patch - > wad = ( UINT16 ) w ;
patch - > lump = texstart + j ;
patch - > flip = 0 ;
2014-03-15 16:59:03 +00:00
2017-08-07 19:33:24 +00:00
Z_Unlock ( patchlump ) ;
2014-03-15 16:59:03 +00:00
2019-07-24 23:07:36 +00:00
texturewidth [ i ] = texture - > width ;
2017-08-07 19:33:24 +00:00
textureheight [ i ] = texture - > height < < FRACBITS ;
2019-03-03 22:09:34 +00:00
i + + ;
2014-03-15 16:59:03 +00:00
}
}
}
static texpatch_t * R_ParsePatch ( boolean actuallyLoadPatch )
{
char * texturesToken ;
2016-05-22 04:44:12 +00:00
size_t texturesTokenLength ;
2014-03-15 16:59:03 +00:00
char * endPos ;
char * patchName = NULL ;
INT16 patchXPos ;
INT16 patchYPos ;
2016-10-22 22:19:24 +00:00
UINT8 flip = 0 ;
2017-03-04 19:59:43 +00:00
UINT8 alpha = 255 ;
enum patchalphastyle style = AST_COPY ;
2014-03-15 16:59:03 +00:00
texpatch_t * resultPatch = NULL ;
lumpnum_t patchLumpNum ;
// Patch identifier
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch name should be " ) ;
}
texturesTokenLength = strlen ( texturesToken ) ;
if ( texturesTokenLength > 8 )
{
I_Error ( " Error parsing TEXTURES lump: Patch name \" %s \" exceeds 8 characters " , texturesToken ) ;
}
else
{
if ( patchName ! = NULL )
{
Z_Free ( patchName ) ;
}
patchName = ( char * ) Z_Malloc ( ( texturesTokenLength + 1 ) * sizeof ( char ) , PU_STATIC , NULL ) ;
M_Memcpy ( patchName , texturesToken , texturesTokenLength * sizeof ( char ) ) ;
patchName [ texturesTokenLength ] = ' \0 ' ;
}
// Comma 1
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where comma after \" %s \" 's patch name should be " , patchName ) ;
}
if ( strcmp ( texturesToken , " , " ) ! = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Expected \" , \" after %s's patch name, got \" %s \" " , patchName , texturesToken ) ;
}
// XPos
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch \" %s \" 's x coordinate should be " , patchName ) ;
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
patchXPos = strtol ( texturesToken , & endPos , 10 ) ;
( void ) patchXPos ; //unused for now
if ( endPos = = texturesToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
)
{
I_Error ( " Error parsing TEXTURES lump: Expected an integer for patch \" %s \" 's x coordinate, got \" %s \" " , patchName , texturesToken ) ;
}
// Comma 2
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where comma after patch \" %s \" 's x coordinate should be " , patchName ) ;
}
if ( strcmp ( texturesToken , " , " ) ! = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Expected \" , \" after patch \" %s \" 's x coordinate, got \" %s \" " , patchName , texturesToken ) ;
}
// YPos
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch \" %s \" 's y coordinate should be " , patchName ) ;
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
patchYPos = strtol ( texturesToken , & endPos , 10 ) ;
( void ) patchYPos ; //unused for now
if ( endPos = = texturesToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
)
{
I_Error ( " Error parsing TEXTURES lump: Expected an integer for patch \" %s \" 's y coordinate, got \" %s \" " , patchName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
2016-10-22 22:19:24 +00:00
// Patch parameters block (OPTIONAL)
// added by Monster Iestyn (22/10/16)
// Left Curly Brace
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
; // move on and ignore, R_ParseTextures will deal with this
else
{
if ( strcmp ( texturesToken , " { " ) = = 0 )
{
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch \" %s \" 's parameters should be " , patchName ) ;
}
while ( strcmp ( texturesToken , " } " ) ! = 0 )
{
2017-03-12 19:26:45 +00:00
if ( stricmp ( texturesToken , " ALPHA " ) = = 0 )
{
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
alpha = 255 * strtof ( texturesToken , NULL ) ;
}
else if ( stricmp ( texturesToken , " STYLE " ) = = 0 )
{
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
2019-09-08 16:21:00 +00:00
if ( stricmp ( texturesToken , " TRANSLUCENT " ) = = 0 )
2017-03-12 19:26:45 +00:00
style = AST_TRANSLUCENT ;
2019-09-08 16:21:00 +00:00
else if ( stricmp ( texturesToken , " ADD " ) = = 0 )
style = AST_ADD ;
else if ( stricmp ( texturesToken , " SUBTRACT " ) = = 0 )
style = AST_SUBTRACT ;
else if ( stricmp ( texturesToken , " REVERSESUBTRACT " ) = = 0 )
style = AST_REVERSESUBTRACT ;
else if ( stricmp ( texturesToken , " MODULATE " ) = = 0 )
style = AST_MODULATE ;
2017-03-12 19:26:45 +00:00
}
2017-03-04 19:59:43 +00:00
else if ( stricmp ( texturesToken , " FLIPX " ) = = 0 )
2016-10-22 22:19:24 +00:00
flip | = 1 ;
else if ( stricmp ( texturesToken , " FLIPY " ) = = 0 )
flip | = 2 ;
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch \" %s \" 's parameters or right curly brace should be " , patchName ) ;
}
}
}
else
{
// this is not what we wanted...
// undo last read so R_ParseTextures can re-get the token for its own purposes
M_UnGetToken ( ) ;
}
Z_Free ( texturesToken ) ;
}
2014-03-15 16:59:03 +00:00
if ( actuallyLoadPatch = = true )
{
// Check lump exists
patchLumpNum = W_GetNumForName ( patchName ) ;
// If so, allocate memory for texpatch_t and fill 'er up
resultPatch = ( texpatch_t * ) Z_Malloc ( sizeof ( texpatch_t ) , PU_STATIC , NULL ) ;
resultPatch - > originx = patchXPos ;
resultPatch - > originy = patchYPos ;
resultPatch - > lump = patchLumpNum & 65535 ;
resultPatch - > wad = patchLumpNum > > 16 ;
2016-10-22 22:19:24 +00:00
resultPatch - > flip = flip ;
2017-03-04 19:59:43 +00:00
resultPatch - > alpha = alpha ;
resultPatch - > style = style ;
2014-03-15 16:59:03 +00:00
// Clean up a little after ourselves
Z_Free ( patchName ) ;
// Then return it
return resultPatch ;
}
else
{
Z_Free ( patchName ) ;
return NULL ;
}
}
static texture_t * R_ParseTexture ( boolean actuallyLoadTexture )
{
char * texturesToken ;
2016-05-22 04:44:12 +00:00
size_t texturesTokenLength ;
2014-03-15 16:59:03 +00:00
char * endPos ;
INT32 newTextureWidth ;
INT32 newTextureHeight ;
texture_t * resultTexture = NULL ;
texpatch_t * newPatch ;
2016-05-09 03:34:43 +00:00
char newTextureName [ 9 ] ; // no longer dynamically allocated
2014-03-15 16:59:03 +00:00
// Texture name
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where texture name should be " ) ;
}
texturesTokenLength = strlen ( texturesToken ) ;
if ( texturesTokenLength > 8 )
{
I_Error ( " Error parsing TEXTURES lump: Texture name \" %s \" exceeds 8 characters " , texturesToken ) ;
}
else
{
2016-05-09 03:34:43 +00:00
memset ( & newTextureName , 0 , 9 ) ;
M_Memcpy ( newTextureName , texturesToken , texturesTokenLength ) ;
// ^^ we've confirmed that the token is <= 8 characters so it will never overflow a 9 byte char buffer
strupr ( newTextureName ) ; // Just do this now so we don't have to worry about it
2014-03-15 16:59:03 +00:00
}
Z_Free ( texturesToken ) ;
// Comma 1
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where comma after texture \" %s \" 's name should be " , newTextureName ) ;
}
else if ( strcmp ( texturesToken , " , " ) ! = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Expected \" , \" after texture \" %s \" 's name, got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
// Width
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where texture \" %s \" 's width should be " , newTextureName ) ;
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
newTextureWidth = strtol ( texturesToken , & endPos , 10 ) ;
if ( endPos = = texturesToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
| | newTextureWidth < 0 ) // Number is not positive
{
I_Error ( " Error parsing TEXTURES lump: Expected a positive integer for texture \" %s \" 's width, got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
// Comma 2
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where comma after texture \" %s \" 's width should be " , newTextureName ) ;
}
if ( strcmp ( texturesToken , " , " ) ! = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Expected \" , \" after texture \" %s \" 's width, got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
// Height
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where texture \" %s \" 's height should be " , newTextureName ) ;
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
newTextureHeight = strtol ( texturesToken , & endPos , 10 ) ;
if ( endPos = = texturesToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
| | newTextureHeight < 0 ) // Number is not positive
{
I_Error ( " Error parsing TEXTURES lump: Expected a positive integer for texture \" %s \" 's height, got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
// Left Curly Brace
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where open curly brace for texture \" %s \" should be " , newTextureName ) ;
}
if ( strcmp ( texturesToken , " { " ) = = 0 )
{
if ( actuallyLoadTexture )
{
// Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily.
resultTexture = ( texture_t * ) Z_Calloc ( sizeof ( texture_t ) , PU_STATIC , NULL ) ;
M_Memcpy ( resultTexture - > name , newTextureName , 8 ) ;
resultTexture - > width = newTextureWidth ;
resultTexture - > height = newTextureHeight ;
}
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch definition for texture \" %s \" should be " , newTextureName ) ;
}
while ( strcmp ( texturesToken , " } " ) ! = 0 )
{
if ( stricmp ( texturesToken , " PATCH " ) = = 0 )
{
Z_Free ( texturesToken ) ;
if ( resultTexture )
{
// Get that new patch
newPatch = R_ParsePatch ( true ) ;
// Make room for the new patch
resultTexture = Z_Realloc ( resultTexture , sizeof ( texture_t ) + ( resultTexture - > patchcount + 1 ) * sizeof ( texpatch_t ) , PU_STATIC , NULL ) ;
// Populate the uninitialized values in the new patch entry of our array
M_Memcpy ( & resultTexture - > patches [ resultTexture - > patchcount ] , newPatch , sizeof ( texpatch_t ) ) ;
// Account for the new number of patches in the texture
resultTexture - > patchcount + + ;
// Then free up the memory assigned to R_ParsePatch, as it's unneeded now
Z_Free ( newPatch ) ;
}
else
{
R_ParsePatch ( false ) ;
}
}
else
{
I_Error ( " Error parsing TEXTURES lump: Expected \" PATCH \" in texture \" %s \" , got \" %s \" " , newTextureName , texturesToken ) ;
}
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch declaration or right curly brace for texture \" %s \" should be " , newTextureName ) ;
}
}
if ( resultTexture & & resultTexture - > patchcount = = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Texture \" %s \" must have at least one patch " , newTextureName ) ;
}
}
else
{
I_Error ( " Error parsing TEXTURES lump: Expected \" { \" for texture \" %s \" , got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
if ( actuallyLoadTexture ) return resultTexture ;
else return NULL ;
}
// Parses the TEXTURES lump... but just to count the number of textures.
2017-05-16 19:10:02 +00:00
int R_CountTexturesInTEXTURESLump ( UINT16 wadNum , UINT16 lumpNum )
2014-03-15 16:59:03 +00:00
{
char * texturesLump ;
size_t texturesLumpLength ;
char * texturesText ;
UINT32 numTexturesInLump = 0 ;
char * texturesToken ;
// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
// need to make a space of memory where I can ensure that it will terminate
// correctly. Start by loading the relevant data from the WAD.
2017-05-16 19:10:02 +00:00
texturesLump = ( char * ) W_CacheLumpNumPwad ( wadNum , lumpNum , PU_STATIC ) ;
2014-03-15 16:59:03 +00:00
// If that didn't exist, we have nothing to do here.
if ( texturesLump = = NULL ) return 0 ;
// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
2017-05-16 19:10:02 +00:00
texturesLumpLength = W_LumpLengthPwad ( wadNum , lumpNum ) ;
2014-03-15 16:59:03 +00:00
texturesText = ( char * ) Z_Malloc ( ( texturesLumpLength + 1 ) * sizeof ( char ) , PU_STATIC , NULL ) ;
// Now move the contents of the lump into this new location.
memmove ( texturesText , texturesLump , texturesLumpLength ) ;
// Make damn well sure the last character in our new memory location is \0.
texturesText [ texturesLumpLength ] = ' \0 ' ;
// Finally, free up the memory from the first data load, because we really
// don't need it.
Z_Free ( texturesLump ) ;
texturesToken = M_GetToken ( texturesText ) ;
while ( texturesToken ! = NULL )
{
2019-05-26 10:02:43 +00:00
if ( stricmp ( texturesToken , " WALLTEXTURE " ) = = 0 | | stricmp ( texturesToken , " TEXTURE " ) = = 0 )
2014-03-15 16:59:03 +00:00
{
numTexturesInLump + + ;
Z_Free ( texturesToken ) ;
R_ParseTexture ( false ) ;
}
else
{
2019-05-26 10:02:43 +00:00
I_Error ( " Error parsing TEXTURES lump: Expected \" WALLTEXTURE \" or \" TEXTURE \" , got \" %s \" " , texturesToken ) ;
2014-03-15 16:59:03 +00:00
}
texturesToken = M_GetToken ( NULL ) ;
}
Z_Free ( texturesToken ) ;
Z_Free ( ( void * ) texturesText ) ;
return numTexturesInLump ;
}
// Parses the TEXTURES lump... for real, this time.
2017-05-16 19:10:02 +00:00
void R_ParseTEXTURESLump ( UINT16 wadNum , UINT16 lumpNum , INT32 * texindex )
2014-03-15 16:59:03 +00:00
{
char * texturesLump ;
size_t texturesLumpLength ;
char * texturesText ;
char * texturesToken ;
texture_t * newTexture ;
I_Assert ( texindex ! = NULL ) ;
// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
// need to make a space of memory where I can ensure that it will terminate
// correctly. Start by loading the relevant data from the WAD.
2017-05-16 19:10:02 +00:00
texturesLump = ( char * ) W_CacheLumpNumPwad ( wadNum , lumpNum , PU_STATIC ) ;
2014-03-15 16:59:03 +00:00
// If that didn't exist, we have nothing to do here.
if ( texturesLump = = NULL ) return ;
// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
2017-05-16 19:10:02 +00:00
texturesLumpLength = W_LumpLengthPwad ( wadNum , lumpNum ) ;
2014-03-15 16:59:03 +00:00
texturesText = ( char * ) Z_Malloc ( ( texturesLumpLength + 1 ) * sizeof ( char ) , PU_STATIC , NULL ) ;
// Now move the contents of the lump into this new location.
memmove ( texturesText , texturesLump , texturesLumpLength ) ;
// Make damn well sure the last character in our new memory location is \0.
texturesText [ texturesLumpLength ] = ' \0 ' ;
// Finally, free up the memory from the first data load, because we really
// don't need it.
Z_Free ( texturesLump ) ;
texturesToken = M_GetToken ( texturesText ) ;
while ( texturesToken ! = NULL )
{
2019-05-26 10:02:43 +00:00
if ( stricmp ( texturesToken , " WALLTEXTURE " ) = = 0 | | stricmp ( texturesToken , " TEXTURE " ) = = 0 )
2014-03-15 16:59:03 +00:00
{
Z_Free ( texturesToken ) ;
// Get the new texture
newTexture = R_ParseTexture ( true ) ;
// Store the new texture
textures [ * texindex ] = newTexture ;
2019-05-21 03:28:52 +00:00
texturewidth [ * texindex ] = newTexture - > width ;
2014-03-15 16:59:03 +00:00
textureheight [ * texindex ] = newTexture - > height < < FRACBITS ;
// Increment i back in R_LoadTextures()
( * texindex ) + + ;
}
else
{
2019-05-26 10:02:43 +00:00
I_Error ( " Error parsing TEXTURES lump: Expected \" WALLTEXTURE \" or \" TEXTURE \" , got \" %s \" " , texturesToken ) ;
2014-03-15 16:59:03 +00:00
}
texturesToken = M_GetToken ( NULL ) ;
}
Z_Free ( texturesToken ) ;
Z_Free ( ( void * ) texturesText ) ;
}
2018-09-15 06:10:17 +00:00
# ifdef EXTRACOLORMAPLUMPS
static lumplist_t * colormaplumps = NULL ; ///\todo free leak
static size_t numcolormaplumps = 0 ;
2014-03-15 16:59:03 +00:00
static inline lumpnum_t R_CheckNumForNameList ( const char * name , lumplist_t * list , size_t listsize )
{
size_t i ;
UINT16 lump ;
for ( i = listsize - 1 ; i < INT16_MAX ; i - - )
{
lump = W_CheckNumForNamePwad ( name , list [ i ] . wadfile , list [ i ] . firstlump ) ;
if ( lump = = INT16_MAX | | lump > ( list [ i ] . firstlump + list [ i ] . numlumps ) )
continue ;
else
return ( list [ i ] . wadfile < < 16 ) + lump ;
}
return LUMPERROR ;
}
static void R_InitExtraColormaps ( void )
{
lumpnum_t startnum , endnum ;
UINT16 cfile , clump ;
static size_t maxcolormaplumps = 16 ;
for ( cfile = clump = 0 ; cfile < numwadfiles ; cfile + + , clump = 0 )
{
startnum = W_CheckNumForNamePwad ( " C_START " , cfile , clump ) ;
2017-05-15 13:29:31 +00:00
if ( startnum = = INT16_MAX )
2014-03-15 16:59:03 +00:00
continue ;
endnum = W_CheckNumForNamePwad ( " C_END " , cfile , clump ) ;
2017-05-15 13:29:31 +00:00
if ( endnum = = INT16_MAX )
2014-03-15 16:59:03 +00:00
I_Error ( " R_InitExtraColormaps: C_START without C_END \n " ) ;
2017-05-15 13:38:55 +00:00
// This shouldn't be possible when you use the Pwad function, silly
//if (WADFILENUM(startnum) != WADFILENUM(endnum))
//I_Error("R_InitExtraColormaps: C_START and C_END in different wad files!\n");
2014-03-15 16:59:03 +00:00
if ( numcolormaplumps > = maxcolormaplumps )
maxcolormaplumps * = 2 ;
colormaplumps = Z_Realloc ( colormaplumps ,
sizeof ( * colormaplumps ) * maxcolormaplumps , PU_STATIC , NULL ) ;
2017-05-15 13:38:55 +00:00
colormaplumps [ numcolormaplumps ] . wadfile = cfile ;
colormaplumps [ numcolormaplumps ] . firstlump = startnum + 1 ;
2014-03-15 16:59:03 +00:00
colormaplumps [ numcolormaplumps ] . numlumps = endnum - ( startnum + 1 ) ;
numcolormaplumps + + ;
}
CONS_Printf ( M_GetText ( " Number of Extra Colormaps: %s \n " ) , sizeu1 ( numcolormaplumps ) ) ;
}
2018-09-11 00:36:34 +00:00
# endif
2014-03-15 16:59:03 +00:00
2018-11-23 15:58:16 +00:00
// Search for flat name.
2014-03-15 16:59:03 +00:00
lumpnum_t R_GetFlatNumForName ( const char * name )
{
2017-05-07 10:30:06 +00:00
INT32 i ;
lumpnum_t lump ;
lumpnum_t start ;
lumpnum_t end ;
// Scan wad files backwards so patched flats take preference.
for ( i = numwadfiles - 1 ; i > = 0 ; i - - )
{
2018-11-23 15:58:16 +00:00
switch ( wadfiles [ i ] - > type )
{
case RET_WAD :
if ( ( start = W_CheckNumForNamePwad ( " F_START " , ( UINT16 ) i , 0 ) ) = = INT16_MAX )
{
if ( ( start = W_CheckNumForNamePwad ( " FF_START " , ( UINT16 ) i , 0 ) ) = = INT16_MAX )
continue ;
else if ( ( end = W_CheckNumForNamePwad ( " FF_END " , ( UINT16 ) i , start ) ) = = INT16_MAX )
continue ;
}
else
if ( ( end = W_CheckNumForNamePwad ( " F_END " , ( UINT16 ) i , start ) ) = = INT16_MAX )
continue ;
break ;
case RET_PK3 :
if ( ( start = W_CheckNumForFolderStartPK3 ( " Flats/ " , i , 0 ) ) = = INT16_MAX )
continue ;
if ( ( end = W_CheckNumForFolderEndPK3 ( " Flats/ " , i , start ) ) = = INT16_MAX )
continue ;
break ;
default :
continue ;
}
2017-05-07 10:30:06 +00:00
// Now find lump with specified name in that range.
lump = W_CheckNumForNamePwad ( name , ( UINT16 ) i , start ) ;
if ( lump < end )
{
lump + = ( i < < 16 ) ; // found it, in our constraints
break ;
}
lump = LUMPERROR ;
}
2019-05-21 18:24:26 +00:00
// Detect textures
2019-05-21 03:28:52 +00:00
if ( lump = = LUMPERROR )
{
2019-05-21 18:24:26 +00:00
// Scan wad files backwards so patched textures take preference.
2019-05-21 03:28:52 +00:00
for ( i = numwadfiles - 1 ; i > = 0 ; i - - )
{
switch ( wadfiles [ i ] - > type )
{
case RET_WAD :
if ( ( start = W_CheckNumForNamePwad ( " TX_START " , ( UINT16 ) i , 0 ) ) = = INT16_MAX )
continue ;
if ( ( end = W_CheckNumForNamePwad ( " TX_END " , ( UINT16 ) i , start ) ) = = INT16_MAX )
continue ;
break ;
case RET_PK3 :
if ( ( start = W_CheckNumForFolderStartPK3 ( " Textures/ " , i , 0 ) ) = = INT16_MAX )
continue ;
if ( ( end = W_CheckNumForFolderEndPK3 ( " Textures/ " , i , start ) ) = = INT16_MAX )
continue ;
break ;
default :
continue ;
}
// Now find lump with specified name in that range.
lump = W_CheckNumForNamePwad ( name , ( UINT16 ) i , start ) ;
if ( lump < end )
{
lump + = ( i < < 16 ) ; // found it, in our constraints
break ;
}
lump = LUMPERROR ;
}
}
2014-03-15 16:59:03 +00:00
if ( lump = = LUMPERROR )
{
if ( strcmp ( name , SKYFLATNAME ) )
CONS_Debug ( DBG_SETUP , " R_GetFlatNumForName: Could not find flat %.8s \n " , name ) ;
lump = W_CheckNumForName ( " REDFLR " ) ;
}
return lump ;
}
//
// R_InitSpriteLumps
// Finds the width and hoffset of all sprites in the wad, so the sprite does not need to be
// cached completely, just for having the header info ready during rendering.
//
//
// allocate sprite lookup tables
//
static void R_InitSpriteLumps ( void )
{
numspritelumps = 0 ;
max_spritelumps = 8192 ;
Z_Malloc ( max_spritelumps * sizeof ( * spritecachedinfo ) , PU_STATIC , & spritecachedinfo ) ;
}
//
// R_InitColormaps
//
static void R_InitColormaps ( void )
{
lumpnum_t lump ;
// Load in the light tables
lump = W_GetNumForName ( " COLORMAP " ) ;
colormaps = Z_MallocAlign ( W_LumpLength ( lump ) , PU_STATIC , NULL , 8 ) ;
W_ReadLump ( lump , colormaps ) ;
// Init Boom colormaps.
R_ClearColormaps ( ) ;
2018-09-11 00:36:34 +00:00
# ifdef EXTRACOLORMAPLUMPS
2014-03-15 16:59:03 +00:00
R_InitExtraColormaps ( ) ;
2018-09-11 00:36:34 +00:00
# endif
2014-03-15 16:59:03 +00:00
}
void R_ReInitColormaps ( UINT16 num )
{
char colormap [ 9 ] = " COLORMAP " ;
lumpnum_t lump ;
2019-06-23 14:02:32 +00:00
const lumpnum_t basecolormaplump = W_GetNumForName ( colormap ) ;
2014-08-04 03:49:33 +00:00
if ( num > 0 & & num < = 10000 )
2014-03-15 16:59:03 +00:00
snprintf ( colormap , 8 , " CLM%04u " , num - 1 ) ;
// Load in the light tables, now 64k aligned for smokie...
lump = W_GetNumForName ( colormap ) ;
if ( lump = = LUMPERROR )
2019-06-23 14:02:32 +00:00
lump = basecolormaplump ;
2019-06-23 11:26:52 +00:00
else
{
2019-06-23 14:02:32 +00:00
if ( W_LumpLength ( lump ) ! = W_LumpLength ( basecolormaplump ) )
2019-06-23 11:26:52 +00:00
{
2019-06-23 14:02:32 +00:00
CONS_Alert ( CONS_WARNING , " %s lump size does not match COLORMAP, results may be unexpected. \n " , colormap ) ;
2019-06-23 11:26:52 +00:00
}
}
2019-06-23 14:02:32 +00:00
W_ReadLumpHeader ( lump , colormaps , W_LumpLength ( basecolormaplump ) , 0U ) ;
2014-03-15 16:59:03 +00:00
// Init Boom colormaps.
R_ClearColormaps ( ) ;
}
//
// R_ClearColormaps
//
// Clears out extra colormaps between levels.
//
void R_ClearColormaps ( void )
{
2018-09-11 17:46:34 +00:00
// Purged by PU_LEVEL, just overwrite the pointer
extra_colormaps = R_CreateDefaultColormap ( true ) ;
}
2014-03-15 16:59:03 +00:00
2018-09-11 17:46:34 +00:00
//
// R_CreateDefaultColormap()
2018-09-11 19:50:12 +00:00
// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
2018-09-11 17:46:34 +00:00
//
extracolormap_t * R_CreateDefaultColormap ( boolean lighttable )
{
extracolormap_t * exc = Z_Calloc ( sizeof ( * exc ) , PU_LEVEL , NULL ) ;
2018-09-11 01:56:09 +00:00
exc - > fadestart = 0 ;
exc - > fadeend = 31 ;
exc - > fog = 0 ;
exc - > rgba = 0 ;
exc - > fadergba = 0x19000000 ;
2018-09-11 17:46:34 +00:00
exc - > colormap = lighttable ? R_CreateLightTable ( exc ) : NULL ;
2018-09-11 01:56:09 +00:00
# ifdef EXTRACOLORMAPLUMPS
exc - > lump = LUMPERROR ;
exc - > lumpname [ 0 ] = 0 ;
# endif
exc - > next = exc - > prev = NULL ;
2018-09-11 17:46:34 +00:00
return exc ;
2018-09-10 19:59:31 +00:00
}
2014-03-15 16:59:03 +00:00
2018-09-11 19:01:05 +00:00
//
// R_GetDefaultColormap()
//
extracolormap_t * R_GetDefaultColormap ( void )
{
# ifdef COLORMAPREVERSELIST
extracolormap_t * exc ;
# endif
if ( ! extra_colormaps )
return ( extra_colormaps = R_CreateDefaultColormap ( true ) ) ;
# ifdef COLORMAPREVERSELIST
for ( exc = extra_colormaps ; exc - > next ; exc = exc - > next ) ;
return exc ;
# else
return extra_colormaps ;
# endif
}
//
2018-09-11 19:20:30 +00:00
// R_CopyColormap()
2018-09-11 19:50:12 +00:00
// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
2018-09-11 19:01:05 +00:00
//
2018-09-11 19:20:30 +00:00
extracolormap_t * R_CopyColormap ( extracolormap_t * extra_colormap , boolean lighttable )
2018-09-11 19:01:05 +00:00
{
2018-09-11 19:20:30 +00:00
extracolormap_t * exc = Z_Calloc ( sizeof ( * exc ) , PU_LEVEL , NULL ) ;
2018-09-11 19:01:05 +00:00
if ( ! extra_colormap )
2018-09-11 19:20:30 +00:00
extra_colormap = R_GetDefaultColormap ( ) ;
* exc = * extra_colormap ;
exc - > next = exc - > prev = NULL ;
2018-09-11 19:01:05 +00:00
# ifdef EXTRACOLORMAPLUMPS
2018-09-11 19:20:30 +00:00
strncpy ( exc - > lumpname , extra_colormap - > lumpname , 9 ) ;
if ( exc - > lump ! = LUMPERROR & & lighttable )
{
// aligned on 8 bit for asm code
exc - > colormap = Z_MallocAlign ( W_LumpLength ( lump ) , PU_LEVEL , NULL , 16 ) ;
W_ReadLump ( lump , exc - > colormap ) ;
}
else
2018-09-11 19:01:05 +00:00
# endif
2018-09-11 19:20:30 +00:00
if ( lighttable )
exc - > colormap = R_CreateLightTable ( exc ) ;
else
exc - > colormap = NULL ;
return exc ;
2018-09-11 19:01:05 +00:00
}
2018-09-10 19:59:31 +00:00
//
// R_AddColormapToList
//
// Sets prev/next chain for extra_colormaps var
// Copypasta from P_AddFFloorToList
//
void R_AddColormapToList ( extracolormap_t * extra_colormap )
{
2018-09-11 02:35:03 +00:00
# ifndef COLORMAPREVERSELIST
2018-09-10 19:59:31 +00:00
extracolormap_t * exc ;
2018-09-11 02:35:03 +00:00
# endif
2014-03-15 16:59:03 +00:00
2018-09-10 19:59:31 +00:00
if ( ! extra_colormaps )
{
extra_colormaps = extra_colormap ;
extra_colormap - > next = 0 ;
extra_colormap - > prev = 0 ;
return ;
}
2018-09-11 02:35:03 +00:00
# ifdef COLORMAPREVERSELIST
extra_colormaps - > prev = extra_colormap ;
extra_colormap - > next = extra_colormaps ;
extra_colormaps = extra_colormap ;
extra_colormap - > prev = 0 ;
# else
2018-09-10 19:59:31 +00:00
for ( exc = extra_colormaps ; exc - > next ; exc = exc - > next ) ;
exc - > next = extra_colormap ;
extra_colormap - > prev = exc ;
extra_colormap - > next = 0 ;
2018-09-11 02:35:03 +00:00
# endif
2014-03-15 16:59:03 +00:00
}
2018-09-11 19:20:30 +00:00
//
2018-09-11 21:06:44 +00:00
// R_CheckDefaultColormapByValues()
2018-09-11 19:20:30 +00:00
//
2018-09-11 21:06:44 +00:00
# ifdef EXTRACOLORMAPLUMPS
boolean R_CheckDefaultColormapByValues ( boolean checkrgba , boolean checkfadergba , boolean checkparams ,
2018-09-13 16:08:46 +00:00
INT32 rgba , INT32 fadergba , UINT8 fadestart , UINT8 fadeend , UINT8 fog , lumpnum_t lump )
2018-09-11 21:06:44 +00:00
# else
boolean R_CheckDefaultColormapByValues ( boolean checkrgba , boolean checkfadergba , boolean checkparams ,
2018-09-13 16:08:46 +00:00
INT32 rgba , INT32 fadergba , UINT8 fadestart , UINT8 fadeend , UINT8 fog )
2018-09-11 21:06:44 +00:00
# endif
{
return (
( ! checkparams ? true :
( fadestart = = 0
& & fadeend = = 31
& & ! fog )
)
& & ( ! checkrgba ? true : rgba = = 0 )
& & ( ! checkfadergba ? true : fadergba = = 0x19000000 )
# ifdef EXTRACOLORMAPLUMPS
& & lump = = LUMPERROR
& & extra_colormap - > lumpname [ 0 ] = = 0
# endif
) ;
}
boolean R_CheckDefaultColormap ( extracolormap_t * extra_colormap , boolean checkrgba , boolean checkfadergba , boolean checkparams )
2018-09-11 19:20:30 +00:00
{
if ( ! extra_colormap )
return true ;
2018-09-11 21:06:44 +00:00
2018-09-11 19:20:30 +00:00
# ifdef EXTRACOLORMAPLUMPS
2018-09-11 21:06:44 +00:00
return R_CheckDefaultColormapByValues ( checkrgba , checkfadergba , checkparams , extra_colormap - > rgba , extra_colormap - > fadergba , extra_colormap - > fadestart , extra_colormap - > fadeend , extra_colormap - > fog , extra_colormap - > lump ) ;
# else
return R_CheckDefaultColormapByValues ( checkrgba , checkfadergba , checkparams , extra_colormap - > rgba , extra_colormap - > fadergba , extra_colormap - > fadestart , extra_colormap - > fadeend , extra_colormap - > fog ) ;
2018-09-11 19:20:30 +00:00
# endif
}
2018-09-12 02:34:29 +00:00
boolean R_CheckEqualColormaps ( extracolormap_t * exc_a , extracolormap_t * exc_b , boolean checkrgba , boolean checkfadergba , boolean checkparams )
{
// Treat NULL as default colormap
// We need this because what if one exc is a default colormap, and the other is NULL? They're really both equal.
if ( ! exc_a )
exc_a = R_GetDefaultColormap ( ) ;
if ( ! exc_b )
exc_b = R_GetDefaultColormap ( ) ;
if ( exc_a = = exc_b )
return true ;
return (
( ! checkparams ? true :
( exc_a - > fadestart = = exc_b - > fadestart
& & exc_a - > fadeend = = exc_b - > fadeend
& & exc_a - > fog = = exc_b - > fog )
)
& & ( ! checkrgba ? true : exc_a - > rgba = = exc_b - > rgba )
& & ( ! checkfadergba ? true : exc_a - > fadergba = = exc_b - > fadergba )
# ifdef EXTRACOLORMAPLUMPS
& & exc_a - > lump = = exc_b - > lump
& & ! strncmp ( exc_a - > lumpname , exc_b - > lumpname , 9 )
# endif
) ;
}
2018-09-11 19:50:12 +00:00
//
// R_GetColormapFromListByValues()
// NOTE: Returns NULL if no match is found
//
# ifdef EXTRACOLORMAPLUMPS
2018-09-13 16:08:46 +00:00
extracolormap_t * R_GetColormapFromListByValues ( INT32 rgba , INT32 fadergba , UINT8 fadestart , UINT8 fadeend , UINT8 fog , lumpnum_t lump )
2018-09-11 19:50:12 +00:00
# else
2018-09-13 16:08:46 +00:00
extracolormap_t * R_GetColormapFromListByValues ( INT32 rgba , INT32 fadergba , UINT8 fadestart , UINT8 fadeend , UINT8 fog )
2018-09-11 19:50:12 +00:00
# endif
{
extracolormap_t * exc ;
2018-09-15 05:47:56 +00:00
UINT32 dbg_i = 0 ;
2018-09-11 19:50:12 +00:00
for ( exc = extra_colormaps ; exc ; exc = exc - > next )
{
if ( rgba = = exc - > rgba
& & fadergba = = exc - > fadergba
& & fadestart = = exc - > fadestart
& & fadeend = = exc - > fadeend
& & fog = = exc - > fog
# ifdef EXTRACOLORMAPLUMPS
& & ( lump ! = LUMPERROR & & lump = = exc - > lump )
# endif
)
{
CONS_Debug ( DBG_RENDER , " Found Colormap %d: rgba(%d,%d,%d,%d) fadergba(%d,%d,%d,%d) \n " ,
2018-09-15 05:47:56 +00:00
dbg_i , R_GetRgbaR ( rgba ) , R_GetRgbaG ( rgba ) , R_GetRgbaB ( rgba ) , R_GetRgbaA ( rgba ) ,
R_GetRgbaR ( fadergba ) , R_GetRgbaG ( fadergba ) , R_GetRgbaB ( fadergba ) , R_GetRgbaA ( fadergba ) ) ;
2018-09-11 19:50:12 +00:00
return exc ;
}
dbg_i + + ;
}
return NULL ;
}
extracolormap_t * R_GetColormapFromList ( extracolormap_t * extra_colormap )
{
# ifdef EXTRACOLORMAPLUMPS
return R_GetColormapFromListByValues ( extra_colormap - > rgba , extra_colormap - > fadergba , extra_colormap - > fadestart , extra_colormap - > fadeend , extra_colormap - > fog , extra_colormap - > lump ) ;
2018-09-11 19:59:13 +00:00
# else
2018-09-11 19:50:12 +00:00
return R_GetColormapFromListByValues ( extra_colormap - > rgba , extra_colormap - > fadergba , extra_colormap - > fadestart , extra_colormap - > fadeend , extra_colormap - > fog ) ;
# endif
}
2018-09-11 00:36:34 +00:00
# ifdef EXTRACOLORMAPLUMPS
2018-09-10 20:28:39 +00:00
extracolormap_t * R_ColormapForName ( char * name )
2014-03-15 16:59:03 +00:00
{
2018-09-10 19:59:31 +00:00
lumpnum_t lump ;
extracolormap_t * exc ;
2014-03-15 16:59:03 +00:00
lump = R_CheckNumForNameList ( name , colormaplumps , numcolormaplumps ) ;
if ( lump = = LUMPERROR )
2018-09-10 20:28:39 +00:00
I_Error ( " R_ColormapForName: Cannot find colormap lump %.8s \n " , name ) ;
2014-03-15 16:59:03 +00:00
2018-09-11 19:50:12 +00:00
exc = R_GetColormapFromListByValues ( 0 , 0x19000000 , 0 , 31 , 0 , lump ) ;
if ( exc )
return exc ;
2018-09-10 19:59:31 +00:00
exc = Z_Calloc ( sizeof ( * exc ) , PU_LEVEL , NULL ) ;
2014-03-15 16:59:03 +00:00
2018-09-10 19:59:31 +00:00
exc - > lump = lump ;
2018-09-11 00:36:34 +00:00
strncpy ( exc - > lumpname , name , 9 ) ;
exc - > lumpname [ 8 ] = 0 ;
2014-03-15 16:59:03 +00:00
// aligned on 8 bit for asm code
2018-09-10 19:59:31 +00:00
exc - > colormap = Z_MallocAlign ( W_LumpLength ( lump ) , PU_LEVEL , NULL , 16 ) ;
W_ReadLump ( lump , exc - > colormap ) ;
2014-03-15 16:59:03 +00:00
// We set all params of the colormap to normal because there
// is no real way to tell how GL should handle a colormap lump anyway..
2018-09-10 19:59:31 +00:00
exc - > fadestart = 0 ;
exc - > fadeend = 31 ;
exc - > fog = 0 ;
2018-09-11 00:36:34 +00:00
exc - > rgba = 0 ;
exc - > fadergba = 0x19000000 ;
R_AddColormapToList ( exc ) ;
2018-09-10 19:59:31 +00:00
return exc ;
2014-03-15 16:59:03 +00:00
}
2018-09-11 00:36:34 +00:00
# endif
2014-03-15 16:59:03 +00:00
//
// R_CreateColormap
//
// This is a more GL friendly way of doing colormaps: Specify colormap
// data in a special linedef's texture areas and use that to generate
// custom colormaps at runtime. NOTE: For GL mode, we only need to color
// data and not the colormap data.
//
static double deltas [ 256 ] [ 3 ] , map [ 256 ] [ 3 ] ;
static int RoundUp ( double number ) ;
2018-09-10 19:59:31 +00:00
lighttable_t * R_CreateLightTable ( extracolormap_t * extra_colormap )
2014-03-15 16:59:03 +00:00
{
2018-09-11 00:36:34 +00:00
double cmaskr , cmaskg , cmaskb , cdestr , cdestg , cdestb ;
double maskamt = 0 , othermask = 0 ;
2018-09-11 21:06:44 +00:00
UINT8 cr = R_GetRgbaR ( extra_colormap - > rgba ) ,
cg = R_GetRgbaG ( extra_colormap - > rgba ) ,
cb = R_GetRgbaB ( extra_colormap - > rgba ) ,
ca = R_GetRgbaA ( extra_colormap - > rgba ) ,
cfr = R_GetRgbaR ( extra_colormap - > fadergba ) ,
cfg = R_GetRgbaG ( extra_colormap - > fadergba ) ,
cfb = R_GetRgbaB ( extra_colormap - > fadergba ) ;
// cfa = R_GetRgbaA(extra_colormap->fadergba); // unused in software
2018-09-11 00:36:34 +00:00
2018-09-11 01:56:09 +00:00
UINT8 fadestart = extra_colormap - > fadestart ,
fadedist = extra_colormap - > fadeend - extra_colormap - > fadestart ;
2014-03-15 16:59:03 +00:00
2018-09-10 20:28:39 +00:00
lighttable_t * lighttable = NULL ;
size_t i ;
2014-03-15 16:59:03 +00:00
2018-09-11 00:36:34 +00:00
/////////////////////
// Calc the RGBA mask
/////////////////////
cmaskr = cr ;
cmaskg = cg ;
cmaskb = cb ;
maskamt = ( double ) ( ca / 24.0 l ) ;
othermask = 1 - maskamt ;
maskamt / = 0xff ;
cmaskr * = maskamt ;
cmaskg * = maskamt ;
cmaskb * = maskamt ;
/////////////////////
// Calc the RGBA fade mask
/////////////////////
cdestr = cfr ;
cdestg = cfg ;
cdestb = cfb ;
// fade alpha unused in software
// maskamt = (double)(cfa/24.0l);
// othermask = 1 - maskamt;
// maskamt /= 0xff;
// cdestr *= maskamt;
// cdestg *= maskamt;
// cdestb *= maskamt;
/////////////////////
2018-09-09 21:48:09 +00:00
// This code creates the colormap array used by software renderer
2018-09-11 00:36:34 +00:00
/////////////////////
2014-03-15 16:59:03 +00:00
if ( rendermode = = render_soft )
{
2018-09-09 21:48:09 +00:00
double r , g , b , cbrightness ;
int p ;
char * colormap_p ;
// Initialise the map and delta arrays
// map[i] stores an RGB color (as double) for index i,
// which is then converted to SRB2's palette later
// deltas[i] stores a corresponding fade delta between the RGB color and the final fade color;
// map[i]'s values are decremented by after each use
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < 256 ; i + + )
{
2017-04-29 15:40:07 +00:00
r = pMasterPalette [ i ] . s . red ;
g = pMasterPalette [ i ] . s . green ;
b = pMasterPalette [ i ] . s . blue ;
2014-03-15 16:59:03 +00:00
cbrightness = sqrt ( ( r * r ) + ( g * g ) + ( b * b ) ) ;
map [ i ] [ 0 ] = ( cbrightness * cmaskr ) + ( r * othermask ) ;
if ( map [ i ] [ 0 ] > 255.0 l )
map [ i ] [ 0 ] = 255.0 l ;
deltas [ i ] [ 0 ] = ( map [ i ] [ 0 ] - cdestr ) / ( double ) fadedist ;
map [ i ] [ 1 ] = ( cbrightness * cmaskg ) + ( g * othermask ) ;
if ( map [ i ] [ 1 ] > 255.0 l )
map [ i ] [ 1 ] = 255.0 l ;
deltas [ i ] [ 1 ] = ( map [ i ] [ 1 ] - cdestg ) / ( double ) fadedist ;
map [ i ] [ 2 ] = ( cbrightness * cmaskb ) + ( b * othermask ) ;
if ( map [ i ] [ 2 ] > 255.0 l )
map [ i ] [ 2 ] = 255.0 l ;
deltas [ i ] [ 2 ] = ( map [ i ] [ 2 ] - cdestb ) / ( double ) fadedist ;
}
2018-09-09 21:48:09 +00:00
// Now allocate memory for the actual colormap array itself!
2018-09-10 19:59:31 +00:00
// aligned on 8 bit for asm code
2014-03-15 16:59:03 +00:00
colormap_p = Z_MallocAlign ( ( 256 * 34 ) + 10 , PU_LEVEL , NULL , 8 ) ;
2018-09-10 19:59:31 +00:00
lighttable = ( UINT8 * ) colormap_p ;
2014-03-15 16:59:03 +00:00
2018-09-09 21:48:09 +00:00
// Calculate the palette index for each palette index, for each light level
// (as well as the two unused colormap lines we inherited from Doom)
2014-03-15 16:59:03 +00:00
for ( p = 0 ; p < 34 ; p + + )
{
for ( i = 0 ; i < 256 ; i + + )
{
* colormap_p = NearestColor ( ( UINT8 ) RoundUp ( map [ i ] [ 0 ] ) ,
( UINT8 ) RoundUp ( map [ i ] [ 1 ] ) ,
( UINT8 ) RoundUp ( map [ i ] [ 2 ] ) ) ;
colormap_p + + ;
if ( ( UINT32 ) p < fadestart )
continue ;
2018-09-09 21:48:09 +00:00
# define ABS2(x) ((x) < 0 ? -(x) : (x))
2014-03-15 16:59:03 +00:00
if ( ABS2 ( map [ i ] [ 0 ] - cdestr ) > ABS2 ( deltas [ i ] [ 0 ] ) )
map [ i ] [ 0 ] - = deltas [ i ] [ 0 ] ;
else
map [ i ] [ 0 ] = cdestr ;
if ( ABS2 ( map [ i ] [ 1 ] - cdestg ) > ABS2 ( deltas [ i ] [ 1 ] ) )
map [ i ] [ 1 ] - = deltas [ i ] [ 1 ] ;
else
map [ i ] [ 1 ] = cdestg ;
if ( ABS2 ( map [ i ] [ 2 ] - cdestb ) > ABS2 ( deltas [ i ] [ 1 ] ) )
map [ i ] [ 2 ] - = deltas [ i ] [ 2 ] ;
else
map [ i ] [ 2 ] = cdestb ;
2018-09-09 21:48:09 +00:00
# undef ABS2
2014-03-15 16:59:03 +00:00
}
}
}
2018-09-10 19:59:31 +00:00
return lighttable ;
}
extracolormap_t * R_CreateColormap ( char * p1 , char * p2 , char * p3 )
{
extracolormap_t * extra_colormap , * exc ;
2018-09-11 19:01:05 +00:00
// default values
UINT8 cr = 0 , cg = 0 , cb = 0 , ca = 0 , cfr = 0 , cfg = 0 , cfb = 0 , cfa = 25 ;
2018-09-11 01:56:09 +00:00
UINT32 fadestart = 0 , fadeend = 31 ;
2018-09-13 16:08:46 +00:00
UINT8 fog = 0 ;
2018-09-11 19:01:05 +00:00
INT32 rgba = 0 , fadergba = 0x19000000 ;
2018-09-10 19:59:31 +00:00
# define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
2018-09-10 20:28:39 +00:00
# define ALPHA2INT(x) (x >= 'a' && x <= 'z' ? x - 'a' : x >= 'A' && x <= 'Z' ? x - 'A' : x >= '0' && x <= '9' ? 25 : 0)
2018-09-11 19:01:05 +00:00
// Get base colormap value
// First alpha-only, then full value
if ( p1 [ 0 ] > = ' a ' & & p1 [ 0 ] < = ' z ' & & ! p1 [ 1 ] )
ca = ( p1 [ 0 ] - ' a ' ) ;
else if ( p1 [ 0 ] = = ' # ' & & p1 [ 1 ] > = ' a ' & & p1 [ 1 ] < = ' z ' & & ! p1 [ 2 ] )
ca = ( p1 [ 1 ] - ' a ' ) ;
else if ( p1 [ 0 ] > = ' A ' & & p1 [ 0 ] < = ' Z ' & & ! p1 [ 1 ] )
ca = ( p1 [ 0 ] - ' A ' ) ;
else if ( p1 [ 0 ] = = ' # ' & & p1 [ 1 ] > = ' A ' & & p1 [ 1 ] < = ' Z ' & & ! p1 [ 2 ] )
ca = ( p1 [ 1 ] - ' A ' ) ;
else if ( p1 [ 0 ] = = ' # ' )
2018-09-10 19:59:31 +00:00
{
2018-09-11 19:01:05 +00:00
// For each subsequent value, the value before it must exist
// If we don't get every value, then set alpha to max
if ( p1 [ 1 ] & & p1 [ 2 ] )
{
cr = ( ( HEX2INT ( p1 [ 1 ] ) * 16 ) + HEX2INT ( p1 [ 2 ] ) ) ;
if ( p1 [ 3 ] & & p1 [ 4 ] )
{
cg = ( ( HEX2INT ( p1 [ 3 ] ) * 16 ) + HEX2INT ( p1 [ 4 ] ) ) ;
if ( p1 [ 5 ] & & p1 [ 6 ] )
{
cb = ( ( HEX2INT ( p1 [ 5 ] ) * 16 ) + HEX2INT ( p1 [ 6 ] ) ) ;
if ( p1 [ 7 ] > = ' a ' & & p1 [ 7 ] < = ' z ' )
ca = ( p1 [ 7 ] - ' a ' ) ;
else if ( p1 [ 7 ] > = ' A ' & & p1 [ 7 ] < = ' Z ' )
ca = ( p1 [ 7 ] - ' A ' ) ;
else
ca = 25 ;
}
else
ca = 25 ;
}
else
ca = 25 ;
}
2018-09-10 19:59:31 +00:00
else
2018-09-11 17:10:14 +00:00
ca = 25 ;
2018-09-10 19:59:31 +00:00
}
# define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0)
2018-09-11 19:01:05 +00:00
// Get parameters like fadestart, fadeend, and the fogflag
2018-09-10 19:59:31 +00:00
if ( p2 [ 0 ] = = ' # ' )
{
2018-09-11 19:01:05 +00:00
if ( p2 [ 1 ] )
{
2018-09-13 16:18:53 +00:00
fog = NUMFROMCHAR ( p2 [ 1 ] ) ;
2018-09-11 19:01:05 +00:00
if ( p2 [ 2 ] & & p2 [ 3 ] )
{
fadestart = NUMFROMCHAR ( p2 [ 3 ] ) + ( NUMFROMCHAR ( p2 [ 2 ] ) * 10 ) ;
if ( p2 [ 4 ] & & p2 [ 5 ] )
fadeend = NUMFROMCHAR ( p2 [ 5 ] ) + ( NUMFROMCHAR ( p2 [ 4 ] ) * 10 ) ;
}
}
2018-09-10 19:59:31 +00:00
if ( fadestart > 30 )
fadestart = 0 ;
if ( fadeend > 31 | | fadeend < 1 )
fadeend = 31 ;
}
2018-09-11 19:01:05 +00:00
2018-09-10 19:59:31 +00:00
# undef NUMFROMCHAR
2018-09-11 19:01:05 +00:00
// Get fade (dark) colormap value
// First alpha-only, then full value
if ( p3 [ 0 ] > = ' a ' & & p3 [ 0 ] < = ' z ' & & ! p3 [ 1 ] )
cfa = ( p3 [ 0 ] - ' a ' ) ;
else if ( p3 [ 0 ] = = ' # ' & & p3 [ 1 ] > = ' a ' & & p3 [ 1 ] < = ' z ' & & ! p3 [ 2 ] )
cfa = ( p3 [ 1 ] - ' a ' ) ;
else if ( p3 [ 0 ] > = ' A ' & & p3 [ 0 ] < = ' Z ' & & ! p3 [ 1 ] )
cfa = ( p3 [ 0 ] - ' A ' ) ;
else if ( p3 [ 0 ] = = ' # ' & & p3 [ 1 ] > = ' A ' & & p3 [ 1 ] < = ' Z ' & & ! p3 [ 2 ] )
cfa = ( p3 [ 1 ] - ' A ' ) ;
else if ( p3 [ 0 ] = = ' # ' )
2018-09-10 19:59:31 +00:00
{
2018-09-11 19:01:05 +00:00
// For each subsequent value, the value before it must exist
// If we don't get every value, then set alpha to max
if ( p3 [ 1 ] & & p3 [ 2 ] )
{
cfr = ( ( HEX2INT ( p3 [ 1 ] ) * 16 ) + HEX2INT ( p3 [ 2 ] ) ) ;
if ( p3 [ 3 ] & & p3 [ 4 ] )
{
cfg = ( ( HEX2INT ( p3 [ 3 ] ) * 16 ) + HEX2INT ( p3 [ 4 ] ) ) ;
if ( p3 [ 5 ] & & p3 [ 6 ] )
{
cfb = ( ( HEX2INT ( p3 [ 5 ] ) * 16 ) + HEX2INT ( p3 [ 6 ] ) ) ;
if ( p3 [ 7 ] > = ' a ' & & p3 [ 7 ] < = ' z ' )
cfa = ( p3 [ 7 ] - ' a ' ) ;
else if ( p3 [ 7 ] > = ' A ' & & p3 [ 7 ] < = ' Z ' )
cfa = ( p3 [ 7 ] - ' A ' ) ;
else
cfa = 25 ;
}
else
cfa = 25 ;
}
else
cfa = 25 ;
}
2018-09-11 00:36:34 +00:00
else
2018-09-11 17:10:14 +00:00
cfa = 25 ;
2018-09-10 19:59:31 +00:00
}
2018-09-10 20:28:39 +00:00
# undef ALPHA2INT
2018-09-10 19:59:31 +00:00
# undef HEX2INT
2018-09-11 19:01:05 +00:00
// Pack rgba values into combined var
// OpenGL also uses this instead of lighttables for rendering
2018-09-11 21:30:43 +00:00
rgba = R_PutRgbaRGBA ( cr , cg , cb , ca ) ;
fadergba = R_PutRgbaRGBA ( cfr , cfg , cfb , cfa ) ;
2018-09-11 19:01:05 +00:00
2018-09-11 21:06:44 +00:00
// Did we just make a default colormap?
# ifdef EXTRACOLORMAPLUMPS
if ( R_CheckDefaultColormapByValues ( true , true , true , rgba , fadergba , fadestart , fadeend , fog , LUMPERROR ) )
return NULL ;
# else
if ( R_CheckDefaultColormapByValues ( true , true , true , rgba , fadergba , fadestart , fadeend , fog ) )
return NULL ;
# endif
2018-09-11 19:01:05 +00:00
// Look for existing colormaps
2018-09-11 00:36:34 +00:00
# ifdef EXTRACOLORMAPLUMPS
2018-09-11 19:50:12 +00:00
exc = R_GetColormapFromListByValues ( rgba , fadergba , fadestart , fadeend , fog , LUMPERROR ) ;
# else
exc = R_GetColormapFromListByValues ( rgba , fadergba , fadestart , fadeend , fog ) ;
2018-09-11 00:36:34 +00:00
# endif
2018-09-11 19:50:12 +00:00
if ( exc )
return exc ;
2018-09-10 19:59:31 +00:00
2018-09-11 19:50:12 +00:00
CONS_Debug ( DBG_RENDER , " Creating Colormap: rgba(%d,%d,%d,%d) fadergba(%d,%d,%d,%d) \n " ,
cr , cg , cb , ca , cfr , cfg , cfb , cfa ) ;
2018-09-11 00:52:37 +00:00
2018-09-10 19:59:31 +00:00
extra_colormap = Z_Calloc ( sizeof ( * extra_colormap ) , PU_LEVEL , NULL ) ;
extra_colormap - > fadestart = ( UINT16 ) fadestart ;
extra_colormap - > fadeend = ( UINT16 ) fadeend ;
extra_colormap - > fog = fog ;
extra_colormap - > rgba = rgba ;
extra_colormap - > fadergba = fadergba ;
2018-09-11 00:36:34 +00:00
# ifdef EXTRACOLORMAPLUMPS
2018-09-10 19:59:31 +00:00
extra_colormap - > lump = LUMPERROR ;
2018-09-11 00:36:34 +00:00
extra_colormap - > lumpname [ 0 ] = 0 ;
# endif
2018-09-10 19:59:31 +00:00
2018-09-11 19:01:05 +00:00
// Having lighttables for alpha-only entries is kind of pointless,
// but if there happens to be a matching rgba entry that is NOT alpha-only (but has same rgb values),
// then it needs this lighttable because we share matching entries.
2018-09-10 19:59:31 +00:00
extra_colormap - > colormap = R_CreateLightTable ( extra_colormap ) ;
R_AddColormapToList ( extra_colormap ) ;
return extra_colormap ;
2014-03-15 16:59:03 +00:00
}
2018-09-12 01:02:58 +00:00
//
// R_AddColormaps()
2018-09-12 11:24:22 +00:00
// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
2018-09-12 01:02:58 +00:00
//
extracolormap_t * R_AddColormaps ( extracolormap_t * exc_augend , extracolormap_t * exc_addend ,
boolean subR , boolean subG , boolean subB , boolean subA ,
boolean subFadeR , boolean subFadeG , boolean subFadeB , boolean subFadeA ,
2018-09-12 11:06:45 +00:00
boolean subFadeStart , boolean subFadeEnd , boolean ignoreFog ,
2018-09-12 01:02:58 +00:00
boolean useAltAlpha , INT16 altAlpha , INT16 altFadeAlpha ,
boolean lighttable )
{
2018-09-15 04:48:34 +00:00
INT16 red , green , blue , alpha ;
2018-09-12 01:02:58 +00:00
// exc_augend is added (or subtracted) onto by exc_addend
// In Rennaisance times, the first number was considered the augend, the second number the addend
// But since the commutative property was discovered, today they're both called addends!
// So let's be Olde English for a hot second.
exc_augend = R_CopyColormap ( exc_augend , false ) ;
if ( ! exc_addend )
exc_addend = R_GetDefaultColormap ( ) ;
2018-09-12 11:06:45 +00:00
///////////////////
2018-09-12 01:02:58 +00:00
// base rgba
2018-09-12 11:06:45 +00:00
///////////////////
2018-09-12 01:02:58 +00:00
red = max ( min (
R_GetRgbaR ( exc_augend - > rgba )
+ ( subR ? - 1 : 1 ) // subtract R
* R_GetRgbaR ( exc_addend - > rgba )
, 255 ) , 0 ) ;
green = max ( min (
R_GetRgbaG ( exc_augend - > rgba )
+ ( subG ? - 1 : 1 ) // subtract G
* R_GetRgbaG ( exc_addend - > rgba )
, 255 ) , 0 ) ;
blue = max ( min (
R_GetRgbaB ( exc_augend - > rgba )
+ ( subB ? - 1 : 1 ) // subtract B
* R_GetRgbaB ( exc_addend - > rgba )
, 255 ) , 0 ) ;
alpha = useAltAlpha ? altAlpha : R_GetRgbaA ( exc_addend - > rgba ) ;
alpha = max ( min ( R_GetRgbaA ( exc_augend - > rgba ) + ( subA ? - 1 : 1 ) * alpha , 25 ) , 0 ) ;
exc_augend - > rgba = R_PutRgbaRGBA ( red , green , blue , alpha ) ;
2018-09-12 11:06:45 +00:00
///////////////////
// fade/dark rgba
///////////////////
2018-09-12 01:02:58 +00:00
red = max ( min (
R_GetRgbaR ( exc_augend - > fadergba )
+ ( subFadeR ? - 1 : 1 ) // subtract R
* R_GetRgbaR ( exc_addend - > fadergba )
, 255 ) , 0 ) ;
green = max ( min (
R_GetRgbaG ( exc_augend - > fadergba )
+ ( subFadeG ? - 1 : 1 ) // subtract G
* R_GetRgbaG ( exc_addend - > fadergba )
, 255 ) , 0 ) ;
blue = max ( min (
R_GetRgbaB ( exc_augend - > fadergba )
+ ( subFadeB ? - 1 : 1 ) // subtract B
* R_GetRgbaB ( exc_addend - > fadergba )
, 255 ) , 0 ) ;
alpha = useAltAlpha ? altFadeAlpha : R_GetRgbaA ( exc_addend - > fadergba ) ;
2018-09-12 11:14:23 +00:00
if ( alpha = = 25 & & ! useAltAlpha & & ! R_GetRgbaRGB ( exc_addend - > fadergba ) )
2018-09-12 01:02:58 +00:00
alpha = 0 ; // HACK: fadergba A defaults at 25, so don't add anything in this case
alpha = max ( min ( R_GetRgbaA ( exc_augend - > fadergba ) + ( subFadeA ? - 1 : 1 ) * alpha , 25 ) , 0 ) ;
exc_augend - > fadergba = R_PutRgbaRGBA ( red , green , blue , alpha ) ;
2018-09-12 11:06:45 +00:00
///////////////////
// parameters
///////////////////
exc_augend - > fadestart = max ( min (
exc_augend - > fadestart
+ ( subFadeStart ? - 1 : 1 ) // subtract fadestart
* exc_addend - > fadestart
, 31 ) , 0 ) ;
exc_augend - > fadeend = max ( min (
exc_augend - > fadeend
+ ( subFadeEnd ? - 1 : 1 ) // subtract fadeend
2018-09-12 11:14:23 +00:00
* ( exc_addend - > fadeend = = 31 & & ! exc_addend - > fadestart ? 0 : exc_addend - > fadeend )
// HACK: fadeend defaults to 31, so don't add anything in this case
2018-09-12 11:06:45 +00:00
, 31 ) , 0 ) ;
if ( ! ignoreFog ) // overwrite fog with new value
exc_augend - > fog = exc_addend - > fog ;
///////////////////
// put it together
///////////////////
2018-09-12 11:24:22 +00:00
exc_augend - > colormap = lighttable ? R_CreateLightTable ( exc_augend ) : NULL ;
exc_augend - > next = exc_augend - > prev = NULL ;
return exc_augend ;
2018-09-12 01:02:58 +00:00
}
2014-03-15 16:59:03 +00:00
// Thanks to quake2 source!
// utils3/qdata/images.c
2019-05-27 02:37:23 +00:00
UINT8 NearestColor ( UINT8 r , UINT8 g , UINT8 b )
2014-03-15 16:59:03 +00:00
{
int dr , dg , db ;
int distortion , bestdistortion = 256 * 256 * 4 , bestcolor = 0 , i ;
for ( i = 0 ; i < 256 ; i + + )
{
2017-04-29 15:40:07 +00:00
dr = r - pMasterPalette [ i ] . s . red ;
dg = g - pMasterPalette [ i ] . s . green ;
db = b - pMasterPalette [ i ] . s . blue ;
2014-03-15 16:59:03 +00:00
distortion = dr * dr + dg * dg + db * db ;
if ( distortion < bestdistortion )
{
if ( ! distortion )
return ( UINT8 ) i ;
bestdistortion = distortion ;
bestcolor = i ;
}
}
return ( UINT8 ) bestcolor ;
}
// Rounds off floating numbers and checks for 0 - 255 bounds
static int RoundUp ( double number )
{
if ( number > 255.0 l )
return 255 ;
if ( number < 0.0 l )
return 0 ;
if ( ( int ) number < = ( int ) ( number - 0.5f ) )
return ( int ) number + 1 ;
return ( int ) number ;
}
2018-09-11 00:36:34 +00:00
# ifdef EXTRACOLORMAPLUMPS
const char * R_NameForColormap ( extracolormap_t * extra_colormap )
2014-03-15 16:59:03 +00:00
{
2018-09-10 20:28:39 +00:00
if ( ! extra_colormap )
2014-03-15 16:59:03 +00:00
return " NONE " ;
2018-09-10 20:28:39 +00:00
if ( extra_colormap - > lump = = LUMPERROR )
2014-03-15 16:59:03 +00:00
return " INLEVEL " ;
2018-09-11 00:36:34 +00:00
return extra_colormap - > lumpname ;
2014-03-15 16:59:03 +00:00
}
2018-09-11 00:36:34 +00:00
# endif
2014-03-15 16:59:03 +00:00
//
// build a table for quick conversion from 8bpp to 15bpp
//
//
// added "static inline" keywords, linking with the debug version
// of allegro, it have a makecol15 function of it's own, now
// with "static inline" keywords,it sloves this problem ;)
//
FUNCMATH static inline int makecol15 ( int r , int g , int b )
{
return ( ( ( r > > 3 ) < < 10 ) | ( ( g > > 3 ) < < 5 ) | ( ( b > > 3 ) ) ) ;
}
static void R_Init8to16 ( void )
{
UINT8 * palette ;
int i ;
palette = W_CacheLumpName ( " PLAYPAL " , PU_CACHE ) ;
for ( i = 0 ; i < 256 ; i + + )
{
// PLAYPAL uses 8 bit values
color8to16 [ i ] = ( INT16 ) makecol15 ( palette [ 0 ] , palette [ 1 ] , palette [ 2 ] ) ;
palette + = 3 ;
}
// test a big colormap
hicolormaps = Z_Malloc ( 16384 * sizeof ( * hicolormaps ) , PU_STATIC , NULL ) ;
for ( i = 0 ; i < 16384 ; i + + )
hicolormaps [ i ] = ( INT16 ) ( i < < 1 ) ;
}
//
// R_InitData
//
// Locates all the lumps that will be used by all views
// Must be called after W_Init.
//
void R_InitData ( void )
{
if ( highcolor )
{
CONS_Printf ( " InitHighColor... \n " ) ;
R_Init8to16 ( ) ;
}
CONS_Printf ( " R_LoadTextures()... \n " ) ;
R_LoadTextures ( ) ;
2016-10-19 07:22:11 +00:00
CONS_Printf ( " P_InitPicAnims()... \n " ) ;
P_InitPicAnims ( ) ;
2014-03-15 16:59:03 +00:00
CONS_Printf ( " R_InitSprites()... \n " ) ;
R_InitSpriteLumps ( ) ;
R_InitSprites ( ) ;
CONS_Printf ( " R_InitColormaps()... \n " ) ;
R_InitColormaps ( ) ;
}
void R_ClearTextureNumCache ( boolean btell )
{
if ( tidcache )
Z_Free ( tidcache ) ;
tidcache = NULL ;
if ( btell )
CONS_Debug ( DBG_SETUP , " Fun Fact: There are %d textures used in this map. \n " , tidcachelen ) ;
tidcachelen = 0 ;
}
//
// R_CheckTextureNumForName
//
// Check whether texture is available. Filter out NoTexture indicator.
//
INT32 R_CheckTextureNumForName ( const char * name )
{
INT32 i ;
// "NoTexture" marker.
if ( name [ 0 ] = = ' - ' )
return 0 ;
for ( i = 0 ; i < tidcachelen ; i + + )
if ( ! strncasecmp ( tidcache [ i ] . name , name , 8 ) )
return tidcache [ i ] . id ;
// Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier
//for (i = 0; i < numtextures; i++) <- old
for ( i = ( numtextures - 1 ) ; i > = 0 ; i - - ) // <- new
if ( ! strncasecmp ( textures [ i ] - > name , name , 8 ) )
{
tidcachelen + + ;
Z_Realloc ( tidcache , tidcachelen * sizeof ( * tidcache ) , PU_STATIC , & tidcache ) ;
strncpy ( tidcache [ tidcachelen - 1 ] . name , name , 8 ) ;
tidcache [ tidcachelen - 1 ] . name [ 8 ] = ' \0 ' ;
# ifndef ZDEBUG
CONS_Debug ( DBG_SETUP , " texture #%s: %s \n " , sizeu1 ( tidcachelen ) , tidcache [ tidcachelen - 1 ] . name ) ;
# endif
tidcache [ tidcachelen - 1 ] . id = i ;
return i ;
}
return - 1 ;
}
//
// R_TextureNumForName
//
// Calls R_CheckTextureNumForName, aborts with error message.
//
INT32 R_TextureNumForName ( const char * name )
{
const INT32 i = R_CheckTextureNumForName ( name ) ;
if ( i = = - 1 )
{
static INT32 redwall = - 2 ;
CONS_Debug ( DBG_SETUP , " WARNING: R_TextureNumForName: %.8s not found \n " , name ) ;
if ( redwall = = - 2 )
redwall = R_CheckTextureNumForName ( " REDWALL " ) ;
if ( redwall ! = - 1 )
return redwall ;
return 1 ;
}
return i ;
}
//
// R_PrecacheLevel
//
// Preloads all relevant graphics for the level.
//
void R_PrecacheLevel ( void )
{
char * texturepresent , * spritepresent ;
size_t i , j , k ;
lumpnum_t lump ;
thinker_t * th ;
spriteframe_t * sf ;
if ( demoplayback )
return ;
// do not flush the memory, Z_Malloc twice with same user will cause error in Z_CheckHeap()
if ( rendermode ! = render_soft )
return ;
// Precache flats.
flatmemory = P_PrecacheLevelFlats ( ) ;
//
// Precache textures.
//
// no need to precache all software textures in 3D mode
// (note they are still used with the reference software view)
2016-05-10 19:19:42 +00:00
texturepresent = calloc ( numtextures , sizeof ( * texturepresent ) ) ;
2014-03-15 16:59:03 +00:00
if ( texturepresent = = NULL ) I_Error ( " %s: Out of memory looking up textures " , " R_PrecacheLevel " ) ;
for ( j = 0 ; j < numsides ; j + + )
{
// huh, a potential bug here????
2016-05-10 19:19:42 +00:00
if ( sides [ j ] . toptexture > = 0 & & sides [ j ] . toptexture < numtextures )
2014-03-15 16:59:03 +00:00
texturepresent [ sides [ j ] . toptexture ] = 1 ;
2016-05-10 19:19:42 +00:00
if ( sides [ j ] . midtexture > = 0 & & sides [ j ] . midtexture < numtextures )
2014-03-15 16:59:03 +00:00
texturepresent [ sides [ j ] . midtexture ] = 1 ;
2016-05-10 19:19:42 +00:00
if ( sides [ j ] . bottomtexture > = 0 & & sides [ j ] . bottomtexture < numtextures )
2014-03-15 16:59:03 +00:00
texturepresent [ sides [ j ] . bottomtexture ] = 1 ;
}
// Sky texture is always present.
// Note that F_SKY1 is the name used to indicate a sky floor/ceiling as a flat,
// while the sky texture is stored like a wall texture, with a skynum dependent name.
texturepresent [ skytexture ] = 1 ;
texturememory = 0 ;
for ( j = 0 ; j < ( unsigned ) numtextures ; j + + )
{
if ( ! texturepresent [ j ] )
continue ;
if ( ! texturecache [ j ] )
R_GenerateTexture ( j ) ;
// pre-caching individual patches that compose textures became obsolete,
// since we cache entire composite textures
}
free ( texturepresent ) ;
//
// Precache sprites.
//
spritepresent = calloc ( numsprites , sizeof ( * spritepresent ) ) ;
if ( spritepresent = = NULL ) I_Error ( " %s: Out of memory looking up sprites " , " R_PrecacheLevel " ) ;
2019-04-20 21:29:20 +00:00
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
2019-07-12 23:42:03 +00:00
if ( th - > function . acp1 ! = ( actionf_p1 ) P_RemoveThinkerDelayed )
spritepresent [ ( ( mobj_t * ) th ) - > sprite ] = 1 ;
2014-03-15 16:59:03 +00:00
spritememory = 0 ;
for ( i = 0 ; i < numsprites ; i + + )
{
if ( ! spritepresent [ i ] )
continue ;
for ( j = 0 ; j < sprites [ i ] . numframes ; j + + )
{
sf = & sprites [ i ] . spriteframes [ j ] ;
for ( k = 0 ; k < 8 ; k + + )
{
// see R_InitSprites for more about lumppat,lumpid
lump = sf - > lumppat [ k ] ;
if ( devparm )
spritememory + = W_LumpLength ( lump ) ;
W_CachePatchNum ( lump , PU_CACHE ) ;
}
}
}
free ( spritepresent ) ;
// FIXME: this is no longer correct with OpenGL render mode
CONS_Debug ( DBG_SETUP , " Precache level done: \n "
" flatmemory: %s k \n "
" texturememory: %s k \n "
" spritememory: %s k \n " , sizeu1 ( flatmemory > > 10 ) , sizeu2 ( texturememory > > 10 ) , sizeu3 ( spritememory > > 10 ) ) ;
}
2019-05-21 03:28:52 +00:00
// https://github.com/coelckers/prboom-plus/blob/master/prboom2/src/r_patch.c#L350
boolean R_CheckIfPatch ( lumpnum_t lump )
{
size_t size ;
INT16 width , height ;
patch_t * patch ;
boolean result ;
size = W_LumpLength ( lump ) ;
// minimum length of a valid Doom patch
if ( size < 13 )
return false ;
patch = ( patch_t * ) W_CacheLumpNum ( lump , PU_STATIC ) ;
width = SHORT ( patch - > width ) ;
height = SHORT ( patch - > height ) ;
2019-05-21 14:03:53 +00:00
result = ( height > 0 & & height < = 16384 & & width > 0 & & width < = 16384 & & width < ( INT16 ) ( size / 4 ) ) ;
2019-05-21 03:28:52 +00:00
if ( result )
{
// The dimensions seem like they might be valid for a patch, so
// check the column directory for extra security. All columns
// must begin after the column directory, and none of them must
// point past the end of the patch.
INT16 x ;
for ( x = 0 ; x < width ; x + + )
{
UINT32 ofs = LONG ( patch - > columnofs [ x ] ) ;
// Need one byte for an empty column (but there's patches that don't know that!)
if ( ofs < ( UINT32 ) width * 4 + 8 | | ofs > = ( UINT32 ) size )
{
result = false ;
break ;
}
}
}
return result ;
}
2019-05-27 02:37:23 +00:00
void R_PatchToFlat ( patch_t * patch , UINT8 * flat )
2019-05-21 03:28:52 +00:00
{
fixed_t col , ofs ;
column_t * column ;
UINT8 * desttop , * dest , * deststop ;
UINT8 * source ;
desttop = flat ;
2019-05-26 19:22:33 +00:00
deststop = desttop + ( SHORT ( patch - > width ) * SHORT ( patch - > height ) ) ;
2019-05-21 03:28:52 +00:00
for ( col = 0 ; col < SHORT ( patch - > width ) ; col + + , desttop + + )
{
INT32 topdelta , prevdelta = - 1 ;
column = ( column_t * ) ( ( UINT8 * ) patch + LONG ( patch - > columnofs [ col ] ) ) ;
while ( column - > topdelta ! = 0xff )
{
topdelta = column - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
2019-05-26 19:22:33 +00:00
dest = desttop + ( topdelta * SHORT ( patch - > width ) ) ;
2019-05-21 03:28:52 +00:00
source = ( UINT8 * ) ( column ) + 3 ;
for ( ofs = 0 ; dest < deststop & & ofs < column - > length ; ofs + + )
{
2019-09-07 19:54:26 +00:00
* dest = source [ ofs ] ;
2019-05-26 19:22:33 +00:00
dest + = SHORT ( patch - > width ) ;
2019-05-21 03:28:52 +00:00
}
column = ( column_t * ) ( ( UINT8 * ) column + column - > length + 4 ) ;
}
}
}
2019-05-27 02:37:23 +00:00
# ifndef NO_PNG_LUMPS
2019-09-17 20:18:54 +00:00
boolean R_IsLumpPNG ( const UINT8 * d , size_t s )
2019-05-27 02:37:23 +00:00
{
if ( s < 67 ) // http://garethrees.org/2007/11/14/pngcrush/
return false ;
// Check for PNG file signature using memcmp
// As it may be faster on CPUs with slow unaligned memory access
// Ref: http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature
return ( memcmp ( & d [ 0 ] , " \x89 \x50 \x4e \x47 \x0d \x0a \x1a \x0a " , 8 ) = = 0 ) ;
}
# ifdef HAVE_PNG
2019-09-17 20:18:54 +00:00
# if PNG_LIBPNG_VER_DLLNUM < 14
typedef PNG_CONST png_byte * png_const_bytep ;
# endif
2019-05-27 02:37:23 +00:00
typedef struct {
2019-09-17 20:18:54 +00:00
png_const_bytep buffer ;
2019-05-27 02:37:23 +00:00
png_uint_32 bufsize ;
png_uint_32 current_pos ;
2019-09-11 23:18:04 +00:00
} png_io_t ;
2019-05-27 02:37:23 +00:00
static void PNG_IOReader ( png_structp png_ptr , png_bytep data , png_size_t length )
{
2019-09-11 23:18:04 +00:00
png_io_t * f = png_get_io_ptr ( png_ptr ) ;
2019-05-27 02:37:23 +00:00
if ( length > ( f - > bufsize - f - > current_pos ) )
2019-09-07 21:56:08 +00:00
png_error ( png_ptr , " PNG_IOReader: buffer overrun " ) ;
2019-05-27 02:37:23 +00:00
memcpy ( data , f - > buffer + f - > current_pos , length ) ;
f - > current_pos + = length ;
}
2019-09-11 23:18:04 +00:00
typedef struct
{
char name [ 4 ] ;
void * data ;
size_t size ;
} png_chunk_t ;
static png_byte * chunkname = NULL ;
static png_chunk_t chunk ;
static int PNG_ChunkReader ( png_structp png_ptr , png_unknown_chunkp chonk )
{
2019-09-17 20:18:54 +00:00
( void ) png_ptr ;
2019-09-11 23:18:04 +00:00
if ( ! memcmp ( chonk - > name , chunkname , 4 ) )
{
memcpy ( chunk . name , chonk - > name , 4 ) ;
chunk . size = chonk - > size ;
chunk . data = Z_Malloc ( chunk . size , PU_STATIC , NULL ) ;
memcpy ( chunk . data , chonk - > data , chunk . size ) ;
return 1 ;
}
return 0 ;
}
2019-05-27 02:37:23 +00:00
static void PNG_error ( png_structp PNG , png_const_charp pngtext )
{
CONS_Debug ( DBG_RENDER , " libpng error at %p: %s " , PNG , pngtext ) ;
//I_Error("libpng error at %p: %s", PNG, pngtext);
}
static void PNG_warn ( png_structp PNG , png_const_charp pngtext )
{
CONS_Debug ( DBG_RENDER , " libpng warning at %p: %s " , PNG , pngtext ) ;
}
2019-09-17 20:18:54 +00:00
static png_bytep * PNG_Read ( const UINT8 * png , UINT16 * w , UINT16 * h , INT16 * topoffset , INT16 * leftoffset , size_t size )
2019-05-27 02:37:23 +00:00
{
png_structp png_ptr ;
png_infop png_info_ptr ;
png_uint_32 width , height ;
int bit_depth , color_type ;
png_uint_32 y ;
# ifdef PNG_SETJMP_SUPPORTED
# ifdef USE_FAR_KEYWORD
jmp_buf jmpbuf ;
# endif
# endif
2019-09-11 23:18:04 +00:00
png_io_t png_io ;
2019-05-27 02:37:23 +00:00
png_bytep * row_pointers ;
2019-09-11 23:18:04 +00:00
png_byte grAb_chunk [ 5 ] = { ' g ' , ' r ' , ' A ' , ' b ' , ( png_byte ) ' \0 ' } ;
png_voidp * user_chunk_ptr ;
png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING , NULL , PNG_error , PNG_warn ) ;
2019-05-27 02:37:23 +00:00
if ( ! png_ptr )
{
CONS_Debug ( DBG_RENDER , " PNG_Load: Error on initialize libpng \n " ) ;
return NULL ;
}
png_info_ptr = png_create_info_struct ( png_ptr ) ;
if ( ! png_info_ptr )
{
CONS_Debug ( DBG_RENDER , " PNG_Load: Error on allocate for libpng \n " ) ;
png_destroy_read_struct ( & png_ptr , NULL , NULL ) ;
return NULL ;
}
# ifdef USE_FAR_KEYWORD
if ( setjmp ( jmpbuf ) )
# else
if ( setjmp ( png_jmpbuf ( png_ptr ) ) )
# endif
{
//CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename);
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
return NULL ;
}
# ifdef USE_FAR_KEYWORD
png_memcpy ( png_jmpbuf ( png_ptr ) , jmpbuf , sizeof jmp_buf ) ;
# endif
2019-09-07 21:56:08 +00:00
// set our own read_function
2019-09-17 20:18:54 +00:00
png_io . buffer = ( png_const_bytep ) png ;
2019-05-27 02:37:23 +00:00
png_io . bufsize = size ;
png_io . current_pos = 0 ;
png_set_read_fn ( png_ptr , & png_io , PNG_IOReader ) ;
2019-09-11 23:18:04 +00:00
memset ( & chunk , 0x00 , sizeof ( png_chunk_t ) ) ;
chunkname = grAb_chunk ; // I want to read a grAb chunk
user_chunk_ptr = png_get_user_chunk_ptr ( png_ptr ) ;
png_set_read_user_chunk_fn ( png_ptr , user_chunk_ptr , PNG_ChunkReader ) ;
png_set_keep_unknown_chunks ( png_ptr , 2 , chunkname , 1 ) ;
2019-05-27 02:37:23 +00:00
# ifdef PNG_SET_USER_LIMITS_SUPPORTED
png_set_user_limits ( png_ptr , 2048 , 2048 ) ;
# endif
png_read_info ( png_ptr , png_info_ptr ) ;
2019-09-11 19:59:28 +00:00
png_get_IHDR ( png_ptr , png_info_ptr , & width , & height , & bit_depth , & color_type , NULL , NULL , NULL ) ;
2019-05-27 02:37:23 +00:00
if ( bit_depth = = 16 )
png_set_strip_16 ( png_ptr ) ;
if ( color_type = = PNG_COLOR_TYPE_GRAY | | color_type = = PNG_COLOR_TYPE_GRAY_ALPHA )
png_set_gray_to_rgb ( png_ptr ) ;
else if ( color_type = = PNG_COLOR_TYPE_PALETTE )
png_set_palette_to_rgb ( png_ptr ) ;
if ( png_get_valid ( png_ptr , png_info_ptr , PNG_INFO_tRNS ) )
png_set_tRNS_to_alpha ( png_ptr ) ;
else if ( color_type ! = PNG_COLOR_TYPE_RGB_ALPHA & & color_type ! = PNG_COLOR_TYPE_GRAY_ALPHA )
{
# if PNG_LIBPNG_VER < 10207
png_set_filler ( png_ptr , 0xFF , PNG_FILLER_AFTER ) ;
# else
png_set_add_alpha ( png_ptr , 0xFF , PNG_FILLER_AFTER ) ;
# endif
}
png_read_update_info ( png_ptr , png_info_ptr ) ;
// Read the image
row_pointers = ( png_bytep * ) malloc ( sizeof ( png_bytep ) * height ) ;
for ( y = 0 ; y < height ; y + + )
row_pointers [ y ] = ( png_byte * ) malloc ( png_get_rowbytes ( png_ptr , png_info_ptr ) ) ;
png_read_image ( png_ptr , row_pointers ) ;
2019-09-11 19:59:28 +00:00
// Read grAB chunk
2019-09-11 23:38:15 +00:00
if ( ( topoffset | | leftoffset ) & & ( chunk . data ! = NULL ) )
2019-09-11 19:59:28 +00:00
{
2019-09-11 23:18:04 +00:00
INT32 * offsets = ( INT32 * ) chunk . data ;
// read left offset
if ( leftoffset ! = NULL )
2019-09-12 19:03:44 +00:00
* leftoffset = ( INT16 ) BIGENDIAN_LONG ( * offsets ) ;
2019-09-11 23:18:04 +00:00
offsets + + ;
// read top offset
if ( topoffset ! = NULL )
2019-09-12 19:03:44 +00:00
* topoffset = ( INT16 ) BIGENDIAN_LONG ( * offsets ) ;
2019-09-11 19:59:28 +00:00
}
// bye
2019-05-27 02:37:23 +00:00
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
2019-09-11 23:18:04 +00:00
if ( chunk . data )
Z_Free ( chunk . data ) ;
2019-05-27 02:37:23 +00:00
* w = ( INT32 ) width ;
* h = ( INT32 ) height ;
return row_pointers ;
}
// Convert a PNG to a raw image.
2019-09-17 20:18:54 +00:00
static UINT8 * PNG_RawConvert ( const UINT8 * png , UINT16 * w , UINT16 * h , INT16 * topoffset , INT16 * leftoffset , size_t size )
2019-05-27 02:37:23 +00:00
{
UINT8 * flat ;
png_uint_32 x , y ;
2019-09-11 19:59:28 +00:00
png_bytep * row_pointers = PNG_Read ( png , w , h , topoffset , leftoffset , size ) ;
2019-05-27 02:37:23 +00:00
png_uint_32 width = * w , height = * h ;
if ( ! row_pointers )
2019-09-07 21:56:08 +00:00
I_Error ( " PNG_RawConvert: conversion failed " ) ;
2019-05-27 02:37:23 +00:00
// Convert the image to 8bpp
2019-06-28 22:43:37 +00:00
flat = Z_Malloc ( width * height , PU_LEVEL , NULL ) ;
2019-05-27 02:37:23 +00:00
memset ( flat , TRANSPARENTPIXEL , width * height ) ;
for ( y = 0 ; y < height ; y + + )
{
png_bytep row = row_pointers [ y ] ;
for ( x = 0 ; x < width ; x + + )
{
png_bytep px = & ( row [ x * 4 ] ) ;
2019-09-07 19:54:26 +00:00
if ( ( UINT8 ) px [ 3 ] )
flat [ ( ( y * width ) + x ) ] = NearestColor ( ( UINT8 ) px [ 0 ] , ( UINT8 ) px [ 1 ] , ( UINT8 ) px [ 2 ] ) ;
2019-05-27 02:37:23 +00:00
}
}
free ( row_pointers ) ;
return flat ;
}
// Convert a PNG to a flat.
UINT8 * R_PNGToFlat ( levelflat_t * levelflat , UINT8 * png , size_t size )
{
2019-09-11 19:59:28 +00:00
return PNG_RawConvert ( png , & levelflat - > width , & levelflat - > height , NULL , NULL , size ) ;
2019-05-27 02:37:23 +00:00
}
// Convert a PNG to a patch.
static unsigned char imgbuf [ 1 < < 26 ] ;
2019-09-17 20:18:54 +00:00
patch_t * R_PNGToPatch ( const UINT8 * png , size_t size , size_t * destsize , boolean transparency )
2019-05-27 02:37:23 +00:00
{
UINT16 width , height ;
2019-09-11 19:59:28 +00:00
INT16 topoffset = 0 , leftoffset = 0 ;
UINT8 * raw = PNG_RawConvert ( png , & width , & height , & topoffset , & leftoffset , size ) ;
2019-05-27 02:37:23 +00:00
UINT32 x , y ;
UINT8 * img ;
UINT8 * imgptr = imgbuf ;
UINT8 * colpointers , * startofspan ;
if ( ! raw )
2019-09-07 21:56:08 +00:00
I_Error ( " R_PNGToPatch: conversion failed " ) ;
2019-05-27 02:37:23 +00:00
// Write image size and offset
2019-09-20 01:40:23 +00:00
WRITEINT16 ( imgptr , width ) ;
WRITEINT16 ( imgptr , height ) ;
WRITEINT16 ( imgptr , leftoffset ) ;
WRITEINT16 ( imgptr , topoffset ) ;
2019-05-27 02:37:23 +00:00
// Leave placeholder to column pointers
colpointers = imgptr ;
imgptr + = width * 4 ;
// Write columns
for ( x = 0 ; x < width ; x + + )
{
int lastStartY = 0 ;
int spanSize = 0 ;
startofspan = NULL ;
//printf("%d ", x);
// Write column pointer (@TODO may be wrong)
2019-09-20 01:40:23 +00:00
WRITEINT32 ( colpointers , imgptr - imgbuf ) ;
2019-05-27 02:37:23 +00:00
// Write pixels
for ( y = 0 ; y < height ; y + + )
{
UINT8 paletteIndex = raw [ ( ( y * width ) + x ) ] ;
2019-09-11 20:03:50 +00:00
boolean opaque = transparency ? ( paletteIndex ! = TRANSPARENTPIXEL ) : true ;
2019-09-11 19:59:28 +00:00
// End span if we have a transparent pixel
if ( ! opaque )
{
if ( startofspan )
2019-09-20 01:40:23 +00:00
WRITEUINT8 ( imgptr , 0 ) ;
2019-09-11 19:59:28 +00:00
startofspan = NULL ;
continue ;
}
2019-05-27 02:37:23 +00:00
// Start new column if we need to
if ( ! startofspan | | spanSize = = 255 )
{
int writeY = y ;
// If we reached the span size limit, finish the previous span
if ( startofspan )
2019-09-20 01:40:23 +00:00
WRITEUINT8 ( imgptr , 0 ) ;
2019-05-27 02:37:23 +00:00
if ( y > 254 )
{
// Make sure we're aligned to 254
if ( lastStartY < 254 )
{
2019-09-20 01:40:23 +00:00
WRITEUINT8 ( imgptr , 254 ) ;
WRITEUINT8 ( imgptr , 0 ) ;
2019-05-27 02:37:23 +00:00
imgptr + = 2 ;
lastStartY = 254 ;
}
// Write stopgap empty spans if needed
writeY = y - lastStartY ;
while ( writeY > 254 )
{
2019-09-20 01:40:23 +00:00
WRITEUINT8 ( imgptr , 254 ) ;
WRITEUINT8 ( imgptr , 0 ) ;
2019-05-27 02:37:23 +00:00
imgptr + = 2 ;
writeY - = 254 ;
}
}
startofspan = imgptr ;
2019-09-20 01:40:23 +00:00
WRITEUINT8 ( imgptr , writeY ) ; ///@TODO calculate starting y pos
2019-05-27 02:37:23 +00:00
imgptr + = 2 ;
spanSize = 0 ;
lastStartY = y ;
}
// Write the pixel
2019-09-20 01:40:23 +00:00
WRITEUINT8 ( imgptr , paletteIndex ) ;
2019-05-27 02:37:23 +00:00
spanSize + + ;
startofspan [ 1 ] = spanSize ;
}
if ( startofspan )
2019-09-20 01:40:23 +00:00
WRITEUINT8 ( imgptr , 0 ) ;
2019-05-27 02:37:23 +00:00
2019-09-20 01:40:23 +00:00
WRITEUINT8 ( imgptr , 0xFF ) ;
2019-05-27 02:37:23 +00:00
}
size = imgptr - imgbuf ;
2019-09-11 19:59:28 +00:00
img = Z_Malloc ( size , PU_STATIC , NULL ) ;
2019-05-27 02:37:23 +00:00
memcpy ( img , imgbuf , size ) ;
2019-09-07 21:56:08 +00:00
Z_Free ( raw ) ;
2019-09-11 19:59:28 +00:00
if ( destsize ! = NULL )
* destsize = size ;
2019-05-27 02:37:23 +00:00
return ( patch_t * ) img ;
}
boolean R_PNGDimensions ( UINT8 * png , INT16 * width , INT16 * height , size_t size )
{
png_structp png_ptr ;
png_infop png_info_ptr ;
png_uint_32 w , h ;
int bit_depth , color_type ;
# ifdef PNG_SETJMP_SUPPORTED
# ifdef USE_FAR_KEYWORD
jmp_buf jmpbuf ;
# endif
# endif
2019-09-11 23:18:04 +00:00
png_io_t png_io ;
2019-05-27 02:37:23 +00:00
png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING , NULL ,
PNG_error , PNG_warn ) ;
if ( ! png_ptr )
{
CONS_Debug ( DBG_RENDER , " PNG_Load: Error on initialize libpng \n " ) ;
return false ;
}
png_info_ptr = png_create_info_struct ( png_ptr ) ;
if ( ! png_info_ptr )
{
CONS_Debug ( DBG_RENDER , " PNG_Load: Error on allocate for libpng \n " ) ;
png_destroy_read_struct ( & png_ptr , NULL , NULL ) ;
return false ;
}
# ifdef USE_FAR_KEYWORD
if ( setjmp ( jmpbuf ) )
# else
if ( setjmp ( png_jmpbuf ( png_ptr ) ) )
# endif
{
//CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename);
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
return false ;
}
# ifdef USE_FAR_KEYWORD
png_memcpy ( png_jmpbuf ( png_ptr ) , jmpbuf , sizeof jmp_buf ) ;
# endif
2019-09-07 21:56:08 +00:00
// set our own read_function
2019-05-27 02:37:23 +00:00
png_io . buffer = ( png_bytep ) png ;
png_io . bufsize = size ;
png_io . current_pos = 0 ;
png_set_read_fn ( png_ptr , & png_io , PNG_IOReader ) ;
# ifdef PNG_SET_USER_LIMITS_SUPPORTED
png_set_user_limits ( png_ptr , 2048 , 2048 ) ;
# endif
png_read_info ( png_ptr , png_info_ptr ) ;
png_get_IHDR ( png_ptr , png_info_ptr , & w , & h , & bit_depth , & color_type ,
NULL , NULL , NULL ) ;
// okay done. stop.
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
* width = ( INT32 ) w ;
* height = ( INT32 ) h ;
return true ;
}
# endif
# endif
void R_TextureToFlat ( size_t tex , UINT8 * flat )
2019-05-21 03:28:52 +00:00
{
texture_t * texture = textures [ tex ] ;
fixed_t col , ofs ;
column_t * column ;
UINT8 * desttop , * dest , * deststop ;
UINT8 * source ;
desttop = flat ;
deststop = desttop + ( texture - > width * texture - > height ) ;
2019-05-26 19:22:33 +00:00
for ( col = 0 ; col < texture - > width ; col + + , desttop + + )
2019-05-21 03:28:52 +00:00
{
column = ( column_t * ) R_GetColumn ( tex , col ) ;
if ( ! texture - > holes )
{
dest = desttop ;
source = ( UINT8 * ) ( column ) ;
for ( ofs = 0 ; dest < deststop & & ofs < texture - > height ; ofs + + )
{
if ( source [ ofs ] ! = TRANSPARENTPIXEL )
* dest = source [ ofs ] ;
dest + = texture - > width ;
}
}
else
{
2019-05-26 19:22:33 +00:00
INT32 topdelta , prevdelta = - 1 ;
2019-05-21 03:28:52 +00:00
while ( column - > topdelta ! = 0xff )
{
topdelta = column - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
dest = desttop + ( topdelta * texture - > width ) ;
source = ( UINT8 * ) ( column ) + 3 ;
for ( ofs = 0 ; dest < deststop & & ofs < column - > length ; ofs + + )
{
if ( source [ ofs ] ! = TRANSPARENTPIXEL )
* dest = source [ ofs ] ;
dest + = texture - > width ;
}
column = ( column_t * ) ( ( UINT8 * ) column + column - > length + 4 ) ;
}
}
}
}