2020-01-23 23:12:15 +00:00
// SONIC ROBO BLAST 2
2014-03-15 16:59:03 +00:00
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
2022-03-03 19:24:46 +00:00
// Copyright (C) 1999-2022 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
2020-01-23 23:12:15 +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.
2014-03-15 16:59:03 +00:00
//-----------------------------------------------------------------------------
2020-01-23 23:12:15 +00:00
/// \file hw_md2.c
/// \brief 3D Model Handling
2014-03-15 16:59:03 +00:00
/// Inspired from md2.c by Mete Ciragan (mete@swissquake.ch)
# ifdef __GNUC__
# include <unistd.h>
# endif
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <math.h>
2019-09-23 00:30:07 +00:00
# include "../d_main.h"
2014-03-15 16:59:03 +00:00
# include "../doomdef.h"
# include "../doomstat.h"
2017-08-26 17:56:23 +00:00
# include "../fastcmp.h"
2014-03-15 16:59:03 +00:00
# ifdef HWRENDER
# include "hw_drv.h"
# include "hw_light.h"
# include "hw_md2.h"
2019-08-05 01:49:31 +00:00
# include "../d_main.h"
2014-03-15 16:59:03 +00:00
# include "../r_bsp.h"
2022-04-13 04:06:14 +00:00
# include "../r_fps.h"
2014-03-15 16:59:03 +00:00
# include "../r_main.h"
# include "../m_misc.h"
# include "../w_wad.h"
# include "../z_zone.h"
# include "../r_things.h"
2019-06-18 16:55:57 +00:00
# include "../r_draw.h"
# include "../p_tick.h"
2018-12-16 01:57:11 +00:00
# include "hw_model.h"
2014-03-15 16:59:03 +00:00
# include "hw_main.h"
2015-07-26 19:14:47 +00:00
# include "../v_video.h"
2014-03-15 16:59:03 +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
# if PNG_LIBPNG_VER < 100207
//#undef HAVE_PNG
# endif
# endif
2019-09-23 00:30:07 +00:00
# ifndef errno
# include "errno.h"
# endif
2014-03-15 16:59:03 +00:00
md2_t md2_models [ NUMSPRITES ] ;
md2_t md2_playermodels [ MAXSKINS ] ;
/*
* free model
*/
2018-12-19 00:17:33 +00:00
#if 0
2018-12-16 01:57:11 +00:00
static void md2_freeModel ( model_t * model )
2014-03-15 16:59:03 +00:00
{
2018-12-16 01:57:11 +00:00
UnloadModel ( model ) ;
2014-03-15 16:59:03 +00:00
}
2018-12-19 00:17:33 +00:00
# endif
2014-03-15 16:59:03 +00:00
//
// load model
//
// Hurdler: the current path is the Legacy.exe path
2018-12-16 01:57:11 +00:00
static model_t * md2_readModel ( const char * filename )
2014-03-15 16:59:03 +00:00
{
2019-09-23 00:30:07 +00:00
//Filename checking fixed ~Monster Iestyn and Golden
2020-10-02 19:45:43 +00:00
if ( FIL_FileExists ( va ( " %s " PATHSEP " %s " , srb2home , filename ) ) )
return LoadModel ( va ( " %s " PATHSEP " %s " , srb2home , filename ) , PU_STATIC ) ;
if ( FIL_FileExists ( va ( " %s " PATHSEP " %s " , srb2path , filename ) ) )
return LoadModel ( va ( " %s " PATHSEP " %s " , srb2path , filename ) , PU_STATIC ) ;
return NULL ;
2014-03-15 16:59:03 +00:00
}
2018-12-16 01:57:11 +00:00
static inline void md2_printModelInfo ( model_t * model )
2014-03-15 16:59:03 +00:00
{
#if 0
INT32 i ;
CONS_Debug ( DBG_RENDER , " magic: \t \t \t %c%c%c%c \n " , model - > header . magic > > 24 ,
( model - > header . magic > > 16 ) & 0xff ,
( model - > header . magic > > 8 ) & 0xff ,
model - > header . magic & 0xff ) ;
CONS_Debug ( DBG_RENDER , " version: \t \t %d \n " , model - > header . version ) ;
CONS_Debug ( DBG_RENDER , " skinWidth: \t \t %d \n " , model - > header . skinWidth ) ;
CONS_Debug ( DBG_RENDER , " skinHeight: \t \t %d \n " , model - > header . skinHeight ) ;
CONS_Debug ( DBG_RENDER , " frameSize: \t \t %d \n " , model - > header . frameSize ) ;
CONS_Debug ( DBG_RENDER , " numSkins: \t \t %d \n " , model - > header . numSkins ) ;
CONS_Debug ( DBG_RENDER , " numVertices: \t \t %d \n " , model - > header . numVertices ) ;
CONS_Debug ( DBG_RENDER , " numTexCoords: \t \t %d \n " , model - > header . numTexCoords ) ;
CONS_Debug ( DBG_RENDER , " numTriangles: \t \t %d \n " , model - > header . numTriangles ) ;
CONS_Debug ( DBG_RENDER , " numGlCommands: \t \t %d \n " , model - > header . numGlCommands ) ;
CONS_Debug ( DBG_RENDER , " numFrames: \t \t %d \n " , model - > header . numFrames ) ;
CONS_Debug ( DBG_RENDER , " offsetSkins: \t \t %d \n " , model - > header . offsetSkins ) ;
CONS_Debug ( DBG_RENDER , " offsetTexCoords: \t %d \n " , model - > header . offsetTexCoords ) ;
CONS_Debug ( DBG_RENDER , " offsetTriangles: \t %d \n " , model - > header . offsetTriangles ) ;
CONS_Debug ( DBG_RENDER , " offsetFrames: \t \t %d \n " , model - > header . offsetFrames ) ;
CONS_Debug ( DBG_RENDER , " offsetGlCommands: \t %d \n " , model - > header . offsetGlCommands ) ;
CONS_Debug ( DBG_RENDER , " offsetEnd: \t \t %d \n " , model - > header . offsetEnd ) ;
for ( i = 0 ; i < model - > header . numFrames ; i + + )
CONS_Debug ( DBG_RENDER , " %s " , model - > frames [ i ] . name ) ;
CONS_Debug ( DBG_RENDER , " \n " ) ;
# else
( void ) model ;
# endif
}
# ifdef HAVE_PNG
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 ) ;
}
2020-07-06 03:52:10 +00:00
static GLTextureFormat_t PNG_Load ( const char * filename , int * w , int * h , GLPatch_t * grpatch )
2014-03-15 16:59:03 +00:00
{
png_structp png_ptr ;
png_infop png_info_ptr ;
png_uint_32 width , height ;
int bit_depth , color_type ;
# ifdef PNG_SETJMP_SUPPORTED
# ifdef USE_FAR_KEYWORD
jmp_buf jmpbuf ;
# endif
# endif
2021-03-21 19:49:32 +00:00
volatile png_FILE_p png_FILE ;
2019-09-23 00:30:07 +00:00
//Filename checking fixed ~Monster Iestyn and Golden
2019-09-03 02:29:16 +00:00
char * pngfilename = va ( " %s " PATHSEP " models " PATHSEP " %s " , srb2home , filename ) ;
2014-03-15 16:59:03 +00:00
FIL_ForceExtension ( pngfilename , " .png " ) ;
png_FILE = fopen ( pngfilename , " rb " ) ;
if ( ! png_FILE )
{
2020-10-02 19:45:43 +00:00
pngfilename = va ( " %s " PATHSEP " models " PATHSEP " %s " , srb2path , filename ) ;
FIL_ForceExtension ( pngfilename , " .png " ) ;
png_FILE = fopen ( pngfilename , " rb " ) ;
2014-03-15 16:59:03 +00:00
//CONS_Debug(DBG_RENDER, "M_SavePNG: Error on opening %s for loading\n", filename);
2020-10-02 19:45:43 +00:00
if ( ! png_FILE )
return 0 ;
2014-03-15 16:59:03 +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 " ) ;
fclose ( png_FILE ) ;
return 0 ;
}
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 ) ;
fclose ( png_FILE ) ;
return 0 ;
}
# 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 ) ;
fclose ( png_FILE ) ;
2020-07-06 03:35:48 +00:00
Z_Free ( grpatch - > mipmap - > data ) ;
2014-03-15 16:59:03 +00:00
return 0 ;
}
# ifdef USE_FAR_KEYWORD
png_memcpy ( png_jmpbuf ( png_ptr ) , jmpbuf , sizeof jmp_buf ) ;
# endif
png_init_io ( png_ptr , png_FILE ) ;
# 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 , & width , & height , & bit_depth , & color_type ,
NULL , NULL , NULL ) ;
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 ) ;
{
png_uint_32 i , pitch = png_get_rowbytes ( png_ptr , png_info_ptr ) ;
2020-07-06 03:35:48 +00:00
png_bytep PNG_image = Z_Malloc ( pitch * height , PU_HWRMODELTEXTURE , & grpatch - > mipmap - > data ) ;
2014-03-15 16:59:03 +00:00
png_bytepp row_pointers = png_malloc ( png_ptr , height * sizeof ( png_bytep ) ) ;
for ( i = 0 ; i < height ; i + + )
row_pointers [ i ] = PNG_image + i * pitch ;
png_read_image ( png_ptr , row_pointers ) ;
png_free ( png_ptr , ( png_voidp ) row_pointers ) ;
}
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
fclose ( png_FILE ) ;
* w = ( int ) width ;
* h = ( int ) height ;
2020-07-06 03:52:10 +00:00
return GL_TEXFMT_RGBA ;
2014-03-15 16:59:03 +00:00
}
# endif
typedef struct
{
UINT8 manufacturer ;
UINT8 version ;
UINT8 encoding ;
UINT8 bitsPerPixel ;
INT16 xmin ;
INT16 ymin ;
INT16 xmax ;
INT16 ymax ;
INT16 hDpi ;
INT16 vDpi ;
UINT8 colorMap [ 48 ] ;
UINT8 reserved ;
UINT8 numPlanes ;
INT16 bytesPerLine ;
INT16 paletteInfo ;
INT16 hScreenSize ;
INT16 vScreenSize ;
UINT8 filler [ 54 ] ;
} PcxHeader ;
2020-07-06 03:52:10 +00:00
static GLTextureFormat_t PCX_Load ( const char * filename , int * w , int * h ,
2014-03-15 16:59:03 +00:00
GLPatch_t * grpatch )
{
PcxHeader header ;
# define PALSIZE 768
UINT8 palette [ PALSIZE ] ;
const UINT8 * pal ;
RGBA_t * image ;
size_t pw , ph , size , ptr = 0 ;
INT32 ch , rep ;
FILE * file ;
2019-09-23 00:30:07 +00:00
//Filename checking fixed ~Monster Iestyn and Golden
2019-09-03 02:29:16 +00:00
char * pcxfilename = va ( " %s " PATHSEP " models " PATHSEP " %s " , srb2home , filename ) ;
2014-03-15 16:59:03 +00:00
FIL_ForceExtension ( pcxfilename , " .pcx " ) ;
file = fopen ( pcxfilename , " rb " ) ;
if ( ! file )
2020-10-02 19:45:43 +00:00
{
pcxfilename = va ( " %s " PATHSEP " models " PATHSEP " %s " , srb2path , filename ) ;
FIL_ForceExtension ( pcxfilename , " .pcx " ) ;
file = fopen ( pcxfilename , " rb " ) ;
if ( ! file )
return 0 ;
}
2014-03-15 16:59:03 +00:00
if ( fread ( & header , sizeof ( PcxHeader ) , 1 , file ) ! = 1 )
{
fclose ( file ) ;
return 0 ;
}
if ( header . bitsPerPixel ! = 8 )
{
fclose ( file ) ;
return 0 ;
}
fseek ( file , - PALSIZE , SEEK_END ) ;
pw = * w = header . xmax - header . xmin + 1 ;
ph = * h = header . ymax - header . ymin + 1 ;
2020-07-06 03:35:48 +00:00
image = Z_Malloc ( pw * ph * 4 , PU_HWRMODELTEXTURE , & grpatch - > mipmap - > data ) ;
2014-03-15 16:59:03 +00:00
if ( fread ( palette , sizeof ( UINT8 ) , PALSIZE , file ) ! = PALSIZE )
{
Z_Free ( image ) ;
fclose ( file ) ;
return 0 ;
}
fseek ( file , sizeof ( PcxHeader ) , SEEK_SET ) ;
size = pw * ph ;
while ( ptr < size )
{
ch = fgetc ( file ) ; //Hurdler: beurk
if ( ch > = 192 )
{
rep = ch - 192 ;
ch = fgetc ( file ) ;
}
else
{
rep = 1 ;
}
while ( rep - - )
{
pal = palette + ch * 3 ;
image [ ptr ] . s . red = * pal + + ;
image [ ptr ] . s . green = * pal + + ;
image [ ptr ] . s . blue = * pal + + ;
image [ ptr ] . s . alpha = 0xFF ;
ptr + + ;
}
}
fclose ( file ) ;
2020-07-06 03:52:10 +00:00
return GL_TEXFMT_RGBA ;
2014-03-15 16:59:03 +00:00
}
// -----------------+
2019-11-05 13:28:19 +00:00
// md2_loadTexture : Download a pcx or png texture for models
2014-03-15 16:59:03 +00:00
// -----------------+
static void md2_loadTexture ( md2_t * model )
{
2020-08-08 08:16:47 +00:00
patch_t * patch ;
GLPatch_t * grPatch = NULL ;
2014-03-15 16:59:03 +00:00
const char * filename = model - > filename ;
if ( model - > grpatch )
{
2020-08-08 08:16:47 +00:00
patch = model - > grpatch ;
grPatch = ( GLPatch_t * ) ( patch - > hardware ) ;
if ( grPatch )
Z_Free ( grPatch - > mipmap - > data ) ;
2014-03-15 16:59:03 +00:00
}
else
2020-08-08 08:16:47 +00:00
model - > grpatch = patch = Patch_Create ( NULL , 0 , NULL ) ;
if ( ! patch - > hardware )
Patch_AllocateHardwarePatch ( patch ) ;
if ( grPatch = = NULL )
grPatch = ( GLPatch_t * ) ( patch - > hardware ) ;
2014-03-15 16:59:03 +00:00
2020-08-08 08:16:47 +00:00
if ( ! grPatch - > mipmap - > downloaded & & ! grPatch - > mipmap - > data )
2014-03-15 16:59:03 +00:00
{
int w = 0 , h = 0 ;
2019-12-13 02:18:39 +00:00
UINT32 size ;
RGBA_t * image ;
2014-03-15 16:59:03 +00:00
# ifdef HAVE_PNG
2020-08-08 08:16:47 +00:00
grPatch - > mipmap - > format = PNG_Load ( filename , & w , & h , grPatch ) ;
if ( grPatch - > mipmap - > format = = 0 )
2014-03-15 16:59:03 +00:00
# endif
2020-08-08 08:16:47 +00:00
grPatch - > mipmap - > format = PCX_Load ( filename , & w , & h , grPatch ) ;
if ( grPatch - > mipmap - > format = = 0 )
2020-06-19 13:18:04 +00:00
{
model - > notexturefile = true ; // mark it so its not searched for again repeatedly
2014-03-15 16:59:03 +00:00
return ;
2020-06-19 13:18:04 +00:00
}
2014-03-15 16:59:03 +00:00
2020-08-08 08:16:47 +00:00
grPatch - > mipmap - > downloaded = 0 ;
grPatch - > mipmap - > flags = 0 ;
2014-03-15 16:59:03 +00:00
2020-08-08 08:16:47 +00:00
patch - > width = ( INT16 ) w ;
patch - > height = ( INT16 ) h ;
grPatch - > mipmap - > width = ( UINT16 ) w ;
grPatch - > mipmap - > height = ( UINT16 ) h ;
2014-03-15 16:59:03 +00:00
2019-12-13 02:18:39 +00:00
// Lactozilla: Apply colour cube
2020-08-08 08:16:47 +00:00
image = grPatch - > mipmap - > data ;
2019-12-13 02:18:39 +00:00
size = w * h ;
while ( size - - )
{
V_CubeApply ( & image - > s . red , & image - > s . green , & image - > s . blue ) ;
image + + ;
}
2014-03-15 16:59:03 +00:00
}
2020-08-08 08:16:47 +00:00
HWD . pfnSetTexture ( grPatch - > mipmap ) ;
2014-03-15 16:59:03 +00:00
}
2015-07-26 19:14:47 +00:00
// -----------------+
// md2_loadBlendTexture : Download a pcx or png texture for blending MD2 models
// -----------------+
static void md2_loadBlendTexture ( md2_t * model )
{
2020-08-08 08:16:47 +00:00
patch_t * patch ;
GLPatch_t * grPatch = NULL ;
2015-07-26 19:14:47 +00:00
char * filename = Z_Malloc ( strlen ( model - > filename ) + 7 , PU_STATIC , NULL ) ;
2020-08-08 08:16:47 +00:00
strcpy ( filename , model - > filename ) ;
2015-07-26 19:14:47 +00:00
FIL_ForceExtension ( filename , " _blend.png " ) ;
if ( model - > blendgrpatch )
{
2020-08-08 08:16:47 +00:00
patch = model - > blendgrpatch ;
grPatch = ( GLPatch_t * ) ( patch - > hardware ) ;
if ( grPatch )
Z_Free ( grPatch - > mipmap - > data ) ;
2015-07-26 19:14:47 +00:00
}
else
2020-08-08 08:16:47 +00:00
model - > blendgrpatch = patch = Patch_Create ( NULL , 0 , NULL ) ;
if ( ! patch - > hardware )
Patch_AllocateHardwarePatch ( patch ) ;
if ( grPatch = = NULL )
grPatch = ( GLPatch_t * ) ( patch - > hardware ) ;
2015-07-26 19:14:47 +00:00
2020-08-08 08:16:47 +00:00
if ( ! grPatch - > mipmap - > downloaded & & ! grPatch - > mipmap - > data )
2015-07-26 19:14:47 +00:00
{
int w = 0 , h = 0 ;
# ifdef HAVE_PNG
2020-08-08 08:16:47 +00:00
grPatch - > mipmap - > format = PNG_Load ( filename , & w , & h , grPatch ) ;
if ( grPatch - > mipmap - > format = = 0 )
2015-07-26 19:14:47 +00:00
# endif
2020-08-08 08:16:47 +00:00
grPatch - > mipmap - > format = PCX_Load ( filename , & w , & h , grPatch ) ;
if ( grPatch - > mipmap - > format = = 0 )
2015-07-26 19:14:47 +00:00
{
2020-06-19 13:18:04 +00:00
model - > noblendfile = true ; // mark it so its not searched for again repeatedly
2015-07-26 19:14:47 +00:00
Z_Free ( filename ) ;
return ;
}
2020-08-08 08:16:47 +00:00
grPatch - > mipmap - > downloaded = 0 ;
grPatch - > mipmap - > flags = 0 ;
2015-07-26 19:14:47 +00:00
2020-08-08 08:16:47 +00:00
patch - > width = ( INT16 ) w ;
patch - > height = ( INT16 ) h ;
grPatch - > mipmap - > width = ( UINT16 ) w ;
grPatch - > mipmap - > height = ( UINT16 ) h ;
2015-07-26 19:14:47 +00:00
}
2020-08-08 08:16:47 +00:00
HWD . pfnSetTexture ( grPatch - > mipmap ) ; // We do need to do this so that it can be cleared and knows to recreate it when necessary
2015-07-26 19:14:47 +00:00
Z_Free ( filename ) ;
}
2014-04-14 05:14:58 +00:00
// Don't spam the console, or the OS with fopen requests!
static boolean nomd2s = false ;
2019-11-05 13:28:19 +00:00
void HWR_InitModels ( void )
2014-03-15 16:59:03 +00:00
{
size_t i ;
INT32 s ;
FILE * f ;
2020-02-15 15:40:41 +00:00
char name [ 24 ] , filename [ 32 ] ;
2014-03-15 16:59:03 +00:00
float scale , offset ;
2020-02-15 15:40:41 +00:00
size_t prefixlen ;
2014-03-15 16:59:03 +00:00
2019-11-05 13:28:19 +00:00
CONS_Printf ( " HWR_InitModels()... \n " ) ;
2014-03-15 16:59:03 +00:00
for ( s = 0 ; s < MAXSKINS ; s + + )
{
md2_playermodels [ s ] . scale = - 1.0f ;
md2_playermodels [ s ] . model = NULL ;
md2_playermodels [ s ] . grpatch = NULL ;
2020-06-19 13:18:04 +00:00
md2_playermodels [ s ] . notexturefile = false ;
md2_playermodels [ s ] . noblendfile = false ;
2014-03-15 16:59:03 +00:00
md2_playermodels [ s ] . skin = - 1 ;
md2_playermodels [ s ] . notfound = true ;
2016-11-03 21:06:23 +00:00
md2_playermodels [ s ] . error = false ;
2014-03-15 16:59:03 +00:00
}
for ( i = 0 ; i < NUMSPRITES ; i + + )
{
md2_models [ i ] . scale = - 1.0f ;
md2_models [ i ] . model = NULL ;
md2_models [ i ] . grpatch = NULL ;
2020-06-19 13:18:04 +00:00
md2_models [ i ] . notexturefile = false ;
md2_models [ i ] . noblendfile = false ;
2014-03-15 16:59:03 +00:00
md2_models [ i ] . skin = - 1 ;
md2_models [ i ] . notfound = true ;
2016-11-06 17:50:04 +00:00
md2_models [ i ] . error = false ;
2014-03-15 16:59:03 +00:00
}
2019-09-03 02:29:16 +00:00
// read the models.dat file
2019-09-23 00:30:07 +00:00
//Filename checking fixed ~Monster Iestyn and Golden
2019-09-03 02:29:16 +00:00
f = fopen ( va ( " %s " PATHSEP " %s " , srb2home , " models.dat " ) , " rt " ) ;
2014-04-14 05:14:58 +00:00
2014-03-15 16:59:03 +00:00
if ( ! f )
{
2020-10-02 19:45:43 +00:00
f = fopen ( va ( " %s " PATHSEP " %s " , srb2path , " models.dat " ) , " rt " ) ;
if ( ! f )
{
CONS_Printf ( " %s %s \n " , M_GetText ( " Error while loading models.dat: " ) , strerror ( errno ) ) ;
nomd2s = true ;
return ;
}
2014-03-15 16:59:03 +00:00
}
2020-11-22 20:10:10 +00:00
2020-02-15 15:40:41 +00:00
// length of the player model prefix
prefixlen = strlen ( PLAYERMODELPREFIX ) ;
while ( fscanf ( f , " %25s %31s %f %f " , name , filename , & scale , & offset ) = = 4 )
2014-03-15 16:59:03 +00:00
{
2020-02-12 02:54:39 +00:00
char * skinname = name ;
size_t len = strlen ( name ) ;
2020-02-15 15:40:41 +00:00
// check for the player model prefix.
if ( ! strnicmp ( name , PLAYERMODELPREFIX , prefixlen ) & & ( len > prefixlen ) )
2015-01-10 20:54:17 +00:00
{
2020-02-15 15:40:41 +00:00
skinname + = prefixlen ;
2020-02-12 02:54:39 +00:00
goto addskinmodel ;
2015-01-10 20:54:17 +00:00
}
2015-06-18 14:01:57 +00:00
2020-02-12 02:54:39 +00:00
// add sprite model
if ( len = = 4 ) // must be 4 characters long exactly. otherwise it's not a sprite name.
2014-03-15 16:59:03 +00:00
{
2020-02-12 02:54:39 +00:00
for ( i = 0 ; i < NUMSPRITES ; i + + )
2014-03-15 16:59:03 +00:00
{
2020-02-12 02:54:39 +00:00
if ( stricmp ( name , sprnames [ i ] ) = = 0 )
{
md2_models [ i ] . scale = scale ;
md2_models [ i ] . offset = offset ;
md2_models [ i ] . notfound = false ;
strcpy ( md2_models [ i ] . filename , filename ) ;
goto modelfound ;
}
2014-03-15 16:59:03 +00:00
}
}
2020-02-12 02:54:39 +00:00
addskinmodel :
2020-02-15 15:40:41 +00:00
// add player model
2014-03-15 16:59:03 +00:00
for ( s = 0 ; s < MAXSKINS ; s + + )
{
2020-02-12 02:54:39 +00:00
if ( stricmp ( skinname , skins [ s ] . name ) = = 0 )
2014-03-15 16:59:03 +00:00
{
md2_playermodels [ s ] . skin = s ;
md2_playermodels [ s ] . scale = scale ;
md2_playermodels [ s ] . offset = offset ;
md2_playermodels [ s ] . notfound = false ;
strcpy ( md2_playermodels [ s ] . filename , filename ) ;
2020-02-12 02:54:39 +00:00
goto modelfound ;
2014-03-15 16:59:03 +00:00
}
}
2020-02-08 23:29:28 +00:00
2020-02-12 02:54:39 +00:00
modelfound :
2015-01-10 20:54:17 +00:00
// move on to next line...
continue ;
2014-03-15 16:59:03 +00:00
}
fclose ( f ) ;
}
2019-11-05 13:28:19 +00:00
void HWR_AddPlayerModel ( int skin ) // For skins that were added after startup
2014-03-15 16:59:03 +00:00
{
FILE * f ;
2020-02-15 15:40:41 +00:00
char name [ 24 ] , filename [ 32 ] ;
2014-03-15 16:59:03 +00:00
float scale , offset ;
2020-02-15 15:40:41 +00:00
size_t prefixlen ;
2014-03-15 16:59:03 +00:00
2014-04-14 05:14:58 +00:00
if ( nomd2s )
return ;
2019-11-05 13:28:19 +00:00
//CONS_Printf("HWR_AddPlayerModel()...\n");
2014-03-15 16:59:03 +00:00
2019-09-03 02:29:16 +00:00
// read the models.dat file
2019-09-23 00:30:07 +00:00
//Filename checking fixed ~Monster Iestyn and Golden
2019-09-03 02:29:16 +00:00
f = fopen ( va ( " %s " PATHSEP " %s " , srb2home , " models.dat " ) , " rt " ) ;
2014-04-14 05:14:58 +00:00
2014-03-15 16:59:03 +00:00
if ( ! f )
{
2020-10-02 19:45:43 +00:00
f = fopen ( va ( " %s " PATHSEP " %s " , srb2path , " models.dat " ) , " rt " ) ;
if ( ! f )
{
CONS_Printf ( " %s %s \n " , M_GetText ( " Error while loading models.dat: " ) , strerror ( errno ) ) ;
nomd2s = true ;
return ;
}
2014-03-15 16:59:03 +00:00
}
2020-02-15 15:40:41 +00:00
// length of the player model prefix
prefixlen = strlen ( PLAYERMODELPREFIX ) ;
2020-02-12 02:54:39 +00:00
// Check for any models that match the names of player skins!
2020-02-15 15:40:41 +00:00
while ( fscanf ( f , " %25s %31s %f %f " , name , filename , & scale , & offset ) = = 4 )
2014-03-15 16:59:03 +00:00
{
2020-02-12 02:54:39 +00:00
char * skinname = name ;
2020-02-15 15:40:41 +00:00
size_t len = strlen ( name ) ;
// ignore the player model prefix.
if ( ! strnicmp ( name , PLAYERMODELPREFIX , prefixlen ) & & ( len > prefixlen ) )
skinname + = prefixlen ;
2020-02-12 02:54:39 +00:00
if ( stricmp ( skinname , skins [ skin ] . name ) = = 0 )
2014-03-15 16:59:03 +00:00
{
md2_playermodels [ skin ] . skin = skin ;
md2_playermodels [ skin ] . scale = scale ;
md2_playermodels [ skin ] . offset = offset ;
md2_playermodels [ skin ] . notfound = false ;
strcpy ( md2_playermodels [ skin ] . filename , filename ) ;
2020-02-12 02:54:39 +00:00
goto playermodelfound ;
2014-03-15 16:59:03 +00:00
}
}
2015-01-10 20:54:17 +00:00
md2_playermodels [ skin ] . notfound = true ;
2020-02-12 02:54:39 +00:00
playermodelfound :
2014-03-15 16:59:03 +00:00
fclose ( f ) ;
}
2019-11-05 13:28:19 +00:00
void HWR_AddSpriteModel ( size_t spritenum ) // For sprites that were added after startup
2014-03-15 16:59:03 +00:00
{
FILE * f ;
2020-02-15 15:40:41 +00:00
// name[24] is used to check for names in the models.dat file that match with sprites or player skins
2014-03-15 16:59:03 +00:00
// sprite names are always 4 characters long, and names is for player skins can be up to 19 characters long
2020-02-15 15:40:41 +00:00
// PLAYERMODELPREFIX is 6 characters long
char name [ 24 ] , filename [ 32 ] ;
2014-03-15 16:59:03 +00:00
float scale , offset ;
2014-04-14 05:14:58 +00:00
if ( nomd2s )
return ;
2014-03-15 16:59:03 +00:00
2015-01-10 20:54:17 +00:00
if ( spritenum = = SPR_PLAY ) // Handled already NEWMD2: Per sprite, per-skin check
return ;
2019-09-03 02:29:16 +00:00
// Read the models.dat file
2019-09-23 00:30:07 +00:00
//Filename checking fixed ~Monster Iestyn and Golden
2019-09-03 02:29:16 +00:00
f = fopen ( va ( " %s " PATHSEP " %s " , srb2home , " models.dat " ) , " rt " ) ;
2014-03-15 16:59:03 +00:00
if ( ! f )
{
2020-10-02 19:45:43 +00:00
f = fopen ( va ( " %s " PATHSEP " %s " , srb2path , " models.dat " ) , " rt " ) ;
if ( ! f )
{
CONS_Printf ( " %s %s \n " , M_GetText ( " Error while loading models.dat: " ) , strerror ( errno ) ) ;
nomd2s = true ;
return ;
}
2014-03-15 16:59:03 +00:00
}
2020-02-12 02:54:39 +00:00
// Check for any models that match the names of sprite names!
2020-02-15 15:40:41 +00:00
while ( fscanf ( f , " %25s %31s %f %f " , name , filename , & scale , & offset ) = = 4 )
2014-03-15 16:59:03 +00:00
{
2020-02-12 02:54:39 +00:00
// length of the sprite name
size_t len = strlen ( name ) ;
2020-02-15 15:40:41 +00:00
if ( len ! = 4 ) // must be 4 characters long exactly. otherwise it's not a sprite name.
continue ;
2020-02-12 02:54:39 +00:00
2020-02-15 15:40:41 +00:00
// check for the player model prefix.
if ( ! strnicmp ( name , PLAYERMODELPREFIX , strlen ( PLAYERMODELPREFIX ) ) )
2020-02-12 02:54:39 +00:00
continue ; // that's not a sprite...
2015-01-10 20:54:17 +00:00
if ( stricmp ( name , sprnames [ spritenum ] ) = = 0 )
2014-03-15 16:59:03 +00:00
{
2015-01-10 20:54:17 +00:00
md2_models [ spritenum ] . scale = scale ;
md2_models [ spritenum ] . offset = offset ;
md2_models [ spritenum ] . notfound = false ;
strcpy ( md2_models [ spritenum ] . filename , filename ) ;
2020-02-12 02:54:39 +00:00
goto spritemodelfound ;
2014-03-15 16:59:03 +00:00
}
}
2015-01-10 20:54:17 +00:00
md2_models [ spritenum ] . notfound = true ;
2020-02-12 02:54:39 +00:00
spritemodelfound :
2014-03-15 16:59:03 +00:00
fclose ( f ) ;
}
2019-06-18 16:55:57 +00:00
// Define for getting accurate color brightness readings according to how the human eye sees them.
// https://en.wikipedia.org/wiki/Relative_luminance
// 0.2126 to red
// 0.7152 to green
// 0.0722 to blue
# define SETBRIGHTNESS(brightness,r,g,b) \
2019-12-13 00:22:36 +00:00
brightness = ( UINT8 ) ( ( ( 1063 * ( UINT16 ) ( r ) ) / 5000 ) + ( ( 3576 * ( UINT16 ) ( g ) ) / 5000 ) + ( ( 361 * ( UINT16 ) ( b ) ) / 5000 ) )
2019-06-18 16:55:57 +00:00
2020-08-08 08:16:47 +00:00
static void HWR_CreateBlendedTexture ( patch_t * gpatch , patch_t * blendgpatch , GLMipmap_t * grMipmap , INT32 skinnum , skincolornum_t color )
2015-07-26 19:14:47 +00:00
{
2020-08-08 08:16:47 +00:00
GLPatch_t * hwrPatch = gpatch - > hardware ;
GLPatch_t * hwrBlendPatch = blendgpatch - > hardware ;
2015-07-26 19:14:47 +00:00
UINT16 w = gpatch - > width , h = gpatch - > height ;
2016-01-20 15:55:32 +00:00
UINT32 size = w * h ;
RGBA_t * image , * blendimage , * cur , blendcolor ;
2020-05-24 00:29:07 +00:00
UINT16 translation [ 16 ] ; // First the color index
2019-12-25 23:46:30 +00:00
UINT8 cutoff [ 16 ] ; // Brightness cutoff before using the next color
UINT8 translen = 0 ;
2019-12-29 21:36:53 +00:00
UINT8 i ;
2015-07-26 19:14:47 +00:00
2019-12-29 21:36:53 +00:00
blendcolor = V_GetColor ( 0 ) ; // initialize
2019-12-13 00:22:36 +00:00
memset ( translation , 0 , sizeof ( translation ) ) ;
2019-12-25 23:46:30 +00:00
memset ( cutoff , 0 , sizeof ( cutoff ) ) ;
2019-12-13 00:22:36 +00:00
2020-08-08 08:16:47 +00:00
if ( grMipmap - > width = = 0 )
2015-07-26 19:14:47 +00:00
{
2020-08-08 08:16:47 +00:00
grMipmap - > width = gpatch - > width ;
grMipmap - > height = gpatch - > height ;
2015-07-26 19:14:47 +00:00
// no wrap around, no chroma key
2020-08-08 08:16:47 +00:00
grMipmap - > flags = 0 ;
2015-07-26 19:14:47 +00:00
// setup the texture info
2020-08-08 08:16:47 +00:00
grMipmap - > format = GL_TEXFMT_RGBA ;
2015-07-26 19:14:47 +00:00
}
2020-08-08 08:16:47 +00:00
if ( grMipmap - > data )
2019-12-13 00:22:36 +00:00
{
2020-08-08 08:16:47 +00:00
Z_Free ( grMipmap - > data ) ;
grMipmap - > data = NULL ;
2019-12-13 00:22:36 +00:00
}
2015-07-26 19:14:47 +00:00
2020-08-08 08:16:47 +00:00
cur = Z_Malloc ( size * 4 , PU_HWRMODELTEXTURE , & grMipmap - > data ) ;
2015-07-26 19:14:47 +00:00
memset ( cur , 0x00 , size * 4 ) ;
2020-08-08 08:16:47 +00:00
image = hwrPatch - > mipmap - > data ;
blendimage = hwrBlendPatch - > mipmap - > data ;
2019-12-29 21:36:53 +00:00
// TC_METALSONIC includes an actual skincolor translation, on top of its flashing.
if ( skinnum = = TC_METALSONIC )
color = SKINCOLOR_COBALT ;
2015-07-26 19:14:47 +00:00
2020-02-15 08:18:41 +00:00
if ( color ! = SKINCOLOR_NONE & & color < numskincolors )
2019-12-25 23:46:30 +00:00
{
UINT8 numdupes = 1 ;
2020-02-15 08:18:41 +00:00
translation [ translen ] = skincolors [ color ] . ramp [ 0 ] ;
2019-12-25 23:46:30 +00:00
cutoff [ translen ] = 255 ;
for ( i = 1 ; i < 16 ; i + + )
{
2020-02-15 08:18:41 +00:00
if ( translation [ translen ] = = skincolors [ color ] . ramp [ i ] )
2019-12-25 23:46:30 +00:00
{
numdupes + + ;
continue ;
}
if ( translen > 0 )
{
cutoff [ translen ] = cutoff [ translen - 1 ] - ( 256 / ( 16 / numdupes ) ) ;
}
numdupes = 1 ;
translen + + ;
2020-05-24 00:29:07 +00:00
translation [ translen ] = ( UINT16 ) skincolors [ color ] . ramp [ i ] ;
2019-12-25 23:46:30 +00:00
}
translen + + ;
}
2019-12-13 00:22:36 +00:00
while ( size - - )
2015-07-26 19:14:47 +00:00
{
2021-05-22 13:25:36 +00:00
if ( skinnum = = TC_ALLWHITE )
2015-07-26 19:14:47 +00:00
{
2019-12-13 00:22:36 +00:00
// Turn everything white
cur - > s . red = cur - > s . green = cur - > s . blue = 255 ;
cur - > s . alpha = image - > s . alpha ;
2015-07-26 19:14:47 +00:00
}
2019-12-13 00:22:36 +00:00
else
2015-07-26 19:14:47 +00:00
{
2019-12-29 21:36:53 +00:00
// Everything below requires a blend image
if ( blendimage = = NULL )
{
cur - > rgba = image - > rgba ;
goto skippixel ;
}
2019-12-25 23:46:30 +00:00
2019-12-29 21:36:53 +00:00
// Metal Sonic dash mode
if ( skinnum = = TC_DASHMODE )
2019-06-18 16:55:57 +00:00
{
2019-12-13 00:22:36 +00:00
if ( image - > s . alpha = = 0 & & blendimage - > s . alpha = = 0 )
{
2019-12-29 21:36:53 +00:00
// Don't bother with blending the pixel if the alpha of the blend pixel is 0
2019-12-13 00:22:36 +00:00
cur - > rgba = image - > rgba ;
}
else
{
2019-12-29 21:36:53 +00:00
UINT8 ialpha = 255 - blendimage - > s . alpha , balpha = blendimage - > s . alpha ;
RGBA_t icolor = * image , bcolor ;
memset ( & bcolor , 0x00 , sizeof ( RGBA_t ) ) ;
if ( blendimage - > s . alpha )
{
bcolor . s . blue = 0 ;
bcolor . s . red = 255 ;
bcolor . s . green = ( blendimage - > s . red + blendimage - > s . green + blendimage - > s . blue ) / 3 ;
}
if ( image - > s . alpha & & image - > s . red > image - > s . green < < 1 ) // this is pretty arbitrary, but it works well for Metal Sonic
{
icolor . s . red = image - > s . blue ;
icolor . s . blue = image - > s . red ;
}
cur - > s . red = ( ialpha * icolor . s . red + balpha * bcolor . s . red ) / 255 ;
cur - > s . green = ( ialpha * icolor . s . green + balpha * bcolor . s . green ) / 255 ;
cur - > s . blue = ( ialpha * icolor . s . blue + balpha * bcolor . s . blue ) / 255 ;
cur - > s . alpha = image - > s . alpha ;
2019-12-13 00:22:36 +00:00
}
2019-06-18 16:55:57 +00:00
}
else
2019-12-13 00:22:36 +00:00
{
2019-12-29 21:36:53 +00:00
// All settings that use skincolors!
UINT16 brightness ;
if ( translen < = 0 )
2019-12-13 00:22:36 +00:00
{
cur - > rgba = image - > rgba ;
2019-12-29 21:36:53 +00:00
goto skippixel ;
2019-12-13 00:22:36 +00:00
}
2019-12-29 21:36:53 +00:00
// Don't bother with blending the pixel if the alpha of the blend pixel is 0
2019-12-13 00:22:36 +00:00
if ( skinnum = = TC_RAINBOW )
{
2019-12-29 21:36:53 +00:00
if ( image - > s . alpha = = 0 & & blendimage - > s . alpha = = 0 )
2019-12-13 00:22:36 +00:00
{
cur - > rgba = image - > rgba ;
2019-12-29 21:36:53 +00:00
goto skippixel ;
2019-12-13 00:22:36 +00:00
}
2019-12-29 21:36:53 +00:00
else
{
UINT16 imagebright , blendbright ;
SETBRIGHTNESS ( imagebright , image - > s . red , image - > s . green , image - > s . blue ) ;
SETBRIGHTNESS ( blendbright , blendimage - > s . red , blendimage - > s . green , blendimage - > s . blue ) ;
// slightly dumb average between the blend image color and base image colour, usually one or the other will be fully opaque anyway
brightness = ( imagebright * ( 255 - blendimage - > s . alpha ) ) / 255 + ( blendbright * blendimage - > s . alpha ) / 255 ;
}
}
else
{
if ( blendimage - > s . alpha = = 0 )
{
cur - > rgba = image - > rgba ;
goto skippixel ; // for metal sonic blend
}
else
2019-12-13 00:22:36 +00:00
{
2019-12-29 21:36:53 +00:00
SETBRIGHTNESS ( brightness , blendimage - > s . red , blendimage - > s . green , blendimage - > s . blue ) ;
2019-12-13 00:22:36 +00:00
}
2019-12-29 21:36:53 +00:00
}
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
// Calculate a sort of "gradient" for the skincolor
// (Me splitting this into a function didn't work, so I had to ruin this entire function's groove...)
{
RGBA_t nextcolor ;
UINT8 firsti , secondi , mul , mulmax ;
INT32 r , g , b ;
// Rainbow needs to find the closest match to the textures themselves, instead of matching brightnesses to other colors.
// Ensue horrible mess.
if ( skinnum = = TC_RAINBOW )
2019-12-13 00:22:36 +00:00
{
2019-12-29 21:36:53 +00:00
UINT16 brightdif = 256 ;
UINT8 colorbrightnesses [ 16 ] ;
INT32 compare , m , d ;
// Ignore pure white & pitch black
if ( brightness > 253 | | brightness < 2 )
{
cur - > rgba = image - > rgba ;
cur + + ; image + + ; blendimage + + ;
2019-12-13 00:22:36 +00:00
continue ;
2019-12-29 21:36:53 +00:00
}
2019-12-25 23:46:30 +00:00
2019-12-29 21:36:53 +00:00
firsti = 0 ;
mul = 0 ;
mulmax = 1 ;
2019-12-25 23:46:30 +00:00
2019-12-29 21:36:53 +00:00
for ( i = 0 ; i < translen ; i + + )
2019-12-13 00:22:36 +00:00
{
2019-12-29 21:36:53 +00:00
RGBA_t tempc = V_GetColor ( translation [ i ] ) ;
SETBRIGHTNESS ( colorbrightnesses [ i ] , tempc . s . red , tempc . s . green , tempc . s . blue ) ; // store brightnesses for comparison
2019-12-13 00:22:36 +00:00
}
2019-12-29 21:36:53 +00:00
for ( i = 0 ; i < translen ; i + + )
{
if ( brightness > colorbrightnesses [ i ] ) // don't allow greater matches (because calculating a makeshift gradient for this is already a huge mess as is)
continue ;
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
compare = abs ( ( INT16 ) ( colorbrightnesses [ i ] ) - ( INT16 ) ( brightness ) ) ;
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
if ( compare < brightdif )
{
brightdif = ( UINT16 ) compare ;
firsti = i ; // best matching color that's equal brightness or darker
}
}
secondi = firsti + 1 ; // next color in line
if ( secondi > = translen )
{
m = ( INT16 ) brightness ; // - 0;
d = ( INT16 ) colorbrightnesses [ firsti ] ; // - 0;
}
else
{
m = ( INT16 ) brightness - ( INT16 ) colorbrightnesses [ secondi ] ;
d = ( INT16 ) colorbrightnesses [ firsti ] - ( INT16 ) colorbrightnesses [ secondi ] ;
}
if ( m > = d )
m = d - 1 ;
mulmax = 16 ;
// calculate the "gradient" multiplier based on how close this color is to the one next in line
if ( m < = 0 | | d < = 0 )
mul = 0 ;
else
mul = ( mulmax - 1 ) - ( ( m * mulmax ) / d ) ;
2019-12-25 23:46:30 +00:00
}
2019-12-13 00:22:36 +00:00
else
2019-12-25 23:46:30 +00:00
{
2019-12-29 21:36:53 +00:00
// Just convert brightness to a skincolor value, use distance to next position to find the gradient multipler
firsti = 0 ;
for ( i = 1 ; i < translen ; i + + )
{
if ( brightness > = cutoff [ i ] )
break ;
firsti = i ;
}
secondi = firsti + 1 ;
2019-12-25 23:46:30 +00:00
mulmax = cutoff [ firsti ] ;
if ( secondi < translen )
mulmax - = cutoff [ secondi ] ;
2019-12-29 21:36:53 +00:00
mul = cutoff [ firsti ] - brightness ;
2019-12-25 23:46:30 +00:00
}
2019-12-29 21:36:53 +00:00
blendcolor = V_GetColor ( translation [ firsti ] ) ;
2020-02-02 06:08:23 +00:00
if ( secondi > = translen )
mul = 0 ;
2019-12-29 21:36:53 +00:00
if ( mul > 0 ) // If it's 0, then we only need the first color.
2019-12-25 23:46:30 +00:00
{
2020-02-02 06:08:23 +00:00
#if 0
if ( secondi > = translen )
{
// blend to black
2019-12-29 21:36:53 +00:00
nextcolor = V_GetColor ( 31 ) ;
2020-02-02 06:08:23 +00:00
}
2019-12-29 21:36:53 +00:00
else
2020-02-02 06:08:23 +00:00
# endif
2019-12-29 21:36:53 +00:00
nextcolor = V_GetColor ( translation [ secondi ] ) ;
// Find difference between points
r = ( INT32 ) ( nextcolor . s . red - blendcolor . s . red ) ;
g = ( INT32 ) ( nextcolor . s . green - blendcolor . s . green ) ;
b = ( INT32 ) ( nextcolor . s . blue - blendcolor . s . blue ) ;
// Find the gradient of the two points
r = ( ( mul * r ) / mulmax ) ;
g = ( ( mul * g ) / mulmax ) ;
b = ( ( mul * b ) / mulmax ) ;
// Add gradient value to color
blendcolor . s . red + = r ;
blendcolor . s . green + = g ;
blendcolor . s . blue + = b ;
2019-12-25 23:46:30 +00:00
}
2019-12-29 21:36:53 +00:00
}
2019-12-25 23:46:30 +00:00
2019-12-29 21:36:53 +00:00
if ( skinnum = = TC_RAINBOW )
{
UINT32 tempcolor ;
UINT16 colorbright ;
2019-12-25 23:46:30 +00:00
2019-12-29 21:36:53 +00:00
SETBRIGHTNESS ( colorbright , blendcolor . s . red , blendcolor . s . green , blendcolor . s . blue ) ;
if ( colorbright = = 0 )
colorbright = 1 ; // no dividing by 0 please
2019-12-25 23:46:30 +00:00
2019-12-29 21:36:53 +00:00
tempcolor = ( brightness * blendcolor . s . red ) / colorbright ;
tempcolor = min ( 255 , tempcolor ) ;
cur - > s . red = ( UINT8 ) tempcolor ;
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
tempcolor = ( brightness * blendcolor . s . green ) / colorbright ;
tempcolor = min ( 255 , tempcolor ) ;
cur - > s . green = ( UINT8 ) tempcolor ;
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
tempcolor = ( brightness * blendcolor . s . blue ) / colorbright ;
tempcolor = min ( 255 , tempcolor ) ;
cur - > s . blue = ( UINT8 ) tempcolor ;
cur - > s . alpha = image - > s . alpha ;
2019-12-13 00:22:36 +00:00
}
2019-12-29 21:36:53 +00:00
else
{
// Color strength depends on image alpha
INT32 tempcolor ;
2019-06-18 16:55:57 +00:00
2019-12-29 21:36:53 +00:00
tempcolor = ( ( image - > s . red * ( 255 - blendimage - > s . alpha ) ) / 255 ) + ( ( blendcolor . s . red * blendimage - > s . alpha ) / 255 ) ;
tempcolor = min ( 255 , tempcolor ) ;
cur - > s . red = ( UINT8 ) tempcolor ;
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
tempcolor = ( ( image - > s . green * ( 255 - blendimage - > s . alpha ) ) / 255 ) + ( ( blendcolor . s . green * blendimage - > s . alpha ) / 255 ) ;
tempcolor = min ( 255 , tempcolor ) ;
cur - > s . green = ( UINT8 ) tempcolor ;
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
tempcolor = ( ( image - > s . blue * ( 255 - blendimage - > s . alpha ) ) / 255 ) + ( ( blendcolor . s . blue * blendimage - > s . alpha ) / 255 ) ;
tempcolor = min ( 255 , tempcolor ) ;
cur - > s . blue = ( UINT8 ) tempcolor ;
cur - > s . alpha = image - > s . alpha ;
}
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
skippixel :
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
// *Now* we can do Metal Sonic's flashing
if ( skinnum = = TC_METALSONIC )
{
// Blend dark blue into white
if ( cur - > s . alpha > 0 & & cur - > s . red = = 0 & & cur - > s . green = = 0 & & cur - > s . blue < 255 & & cur - > s . blue > 31 )
{
// Sal: Invert non-blue
cur - > s . red = cur - > s . green = ( 255 - cur - > s . blue ) ;
cur - > s . blue = 255 ;
}
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
cur - > s . alpha = image - > s . alpha ;
}
2021-05-22 13:25:36 +00:00
else if ( skinnum = = TC_BOSS )
{
// Turn everything below a certain threshold white
if ( ( image - > s . red = = image - > s . green ) & & ( image - > s . green = = image - > s . blue ) & & image - > s . blue < 127 )
{
// Lactozilla: Invert the colors
cur - > s . red = cur - > s . green = cur - > s . blue = ( 255 - image - > s . blue ) ;
}
}
2019-06-18 16:55:57 +00:00
}
}
2019-12-13 00:22:36 +00:00
2019-12-29 21:36:53 +00:00
cur + + ; image + + ;
if ( blendimage ! = NULL )
blendimage + + ;
2015-07-26 19:14:47 +00:00
}
return ;
}
2019-06-18 16:55:57 +00:00
# undef SETBRIGHTNESS
2020-08-08 08:16:47 +00:00
static void HWR_GetBlendedTexture ( patch_t * patch , patch_t * blendpatch , INT32 skinnum , const UINT8 * colormap , skincolornum_t color )
2015-07-26 19:14:47 +00:00
{
// mostly copied from HWR_GetMappedPatch, hence the similarities and comment
2020-08-08 08:16:47 +00:00
GLPatch_t * grPatch = patch - > hardware ;
GLPatch_t * grBlendPatch = NULL ;
GLMipmap_t * grMipmap , * newMipmap ;
2015-07-26 19:14:47 +00:00
2020-08-08 08:16:47 +00:00
if ( blendpatch = = NULL | | colormap = = colormaps | | colormap = = NULL )
2015-07-26 19:14:47 +00:00
{
// Don't do any blending
2020-08-08 08:16:47 +00:00
HWD . pfnSetTexture ( grPatch - > mipmap ) ;
2015-07-26 19:14:47 +00:00
return ;
}
2020-08-08 08:16:47 +00:00
if ( ( blendpatch & & ( grBlendPatch = blendpatch - > hardware ) & & grBlendPatch - > mipmap - > format )
& & ( patch - > width ! = blendpatch - > width | | patch - > height ! = blendpatch - > height ) )
2020-02-02 03:11:21 +00:00
{
// Blend image exists, but it's bad.
2020-08-08 08:16:47 +00:00
HWD . pfnSetTexture ( grPatch - > mipmap ) ;
2020-02-02 03:11:21 +00:00
return ;
}
2019-12-13 01:26:28 +00:00
// search for the mipmap
2015-07-26 19:14:47 +00:00
// skip the first (no colormap translated)
2020-08-08 08:16:47 +00:00
for ( grMipmap = grPatch - > mipmap ; grMipmap - > nextcolormap ; )
2015-07-26 19:14:47 +00:00
{
2020-08-08 08:16:47 +00:00
grMipmap = grMipmap - > nextcolormap ;
2020-12-06 23:17:14 +00:00
if ( grMipmap - > colormap & & grMipmap - > colormap - > source = = colormap )
2015-07-26 19:14:47 +00:00
{
2020-08-08 08:16:47 +00:00
if ( grMipmap - > downloaded & & grMipmap - > data )
2015-07-26 19:14:47 +00:00
{
2020-12-06 23:17:14 +00:00
if ( memcmp ( grMipmap - > colormap - > data , colormap , 256 * sizeof ( UINT8 ) ) )
{
M_Memcpy ( grMipmap - > colormap - > data , colormap , 256 * sizeof ( UINT8 ) ) ;
HWR_CreateBlendedTexture ( patch , blendpatch , grMipmap , skinnum , color ) ;
HWD . pfnUpdateTexture ( grMipmap ) ;
}
else
HWD . pfnSetTexture ( grMipmap ) ; // found the colormap, set it to the correct texture
2020-08-08 08:16:47 +00:00
Z_ChangeTag ( grMipmap - > data , PU_HWRMODELTEXTURE_UNLOCKED ) ;
2015-07-26 19:14:47 +00:00
return ;
}
}
}
// If here, the blended texture has not been created
// So we create it
//BP: WARNING: don't free it manually without clearing the cache of harware renderer
// (it have a liste of mipmap)
2020-08-15 01:27:16 +00:00
// this malloc is cleared in HWR_FreeColormapCache
2015-07-26 19:14:47 +00:00
// (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
2020-08-08 08:16:47 +00:00
newMipmap = calloc ( 1 , sizeof ( * newMipmap ) ) ;
if ( newMipmap = = NULL )
2020-02-02 03:11:21 +00:00
I_Error ( " %s: Out of memory " , " HWR_GetBlendedTexture " ) ;
2020-08-08 08:16:47 +00:00
grMipmap - > nextcolormap = newMipmap ;
2020-12-06 23:17:14 +00:00
newMipmap - > colormap = Z_Calloc ( sizeof ( * newMipmap - > colormap ) , PU_HWRPATCHCOLMIPMAP , NULL ) ;
newMipmap - > colormap - > source = colormap ;
M_Memcpy ( newMipmap - > colormap - > data , colormap , 256 * sizeof ( UINT8 ) ) ;
2015-07-26 19:14:47 +00:00
2020-08-08 08:16:47 +00:00
HWR_CreateBlendedTexture ( patch , blendpatch , newMipmap , skinnum , color ) ;
2015-07-26 19:14:47 +00:00
2020-08-08 08:16:47 +00:00
HWD . pfnSetTexture ( newMipmap ) ;
Z_ChangeTag ( newMipmap - > data , PU_HWRMODELTEXTURE_UNLOCKED ) ;
2015-07-26 19:14:47 +00:00
}
2019-09-03 21:27:22 +00:00
# define NORMALFOG 0x00000000
# define FADEFOG 0x19000000
2014-03-15 16:59:03 +00:00
2019-12-13 01:53:19 +00:00
static boolean HWR_AllowModel ( mobj_t * mobj )
{
// Signpost overlay. Not needed.
if ( mobj - > state - states = = S_PLAY_SIGN )
return false ;
// Otherwise, render the model.
return true ;
}
2019-09-04 01:57:54 +00:00
static boolean HWR_CanInterpolateModel ( mobj_t * mobj , model_t * model )
2019-09-03 02:29:16 +00:00
{
2020-07-06 04:15:08 +00:00
if ( cv_glmodelinterpolation . value = = 2 ) // Always interpolate
2019-11-04 15:21:53 +00:00
return true ;
2019-09-04 01:57:54 +00:00
return model - > interpolate [ ( mobj - > frame & FF_FRAMEMASK ) ] ;
}
2019-06-18 16:55:57 +00:00
2019-09-04 01:57:54 +00:00
static boolean HWR_CanInterpolateSprite2 ( modelspr2frames_t * spr2frame )
{
2020-07-06 04:15:08 +00:00
if ( cv_glmodelinterpolation . value = = 2 ) // Always interpolate
2019-11-04 15:21:53 +00:00
return true ;
2019-09-04 01:57:54 +00:00
return spr2frame - > interpolate ;
2017-08-26 17:56:23 +00:00
}
2014-03-15 16:59:03 +00:00
2019-11-02 17:52:54 +00:00
//
2019-11-05 13:28:19 +00:00
// HWR_GetModelSprite2 (see P_GetSkinSprite2)
2019-11-02 17:52:54 +00:00
// For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing.
// For super players, does the same as above - but tries the super equivalent for each sprite2 before the non-super version.
//
2019-06-18 16:55:57 +00:00
2019-11-05 13:28:19 +00:00
static UINT8 HWR_GetModelSprite2 ( md2_t * md2 , skin_t * skin , UINT8 spr2 , player_t * player )
2017-08-26 17:56:23 +00:00
{
UINT8 super = 0 , i = 0 ;
2019-11-02 17:52:54 +00:00
if ( ! md2 | | ! md2 - > model | | ! md2 - > model - > spr2frames | | ! skin )
2017-08-26 17:56:23 +00:00
return 0 ;
2019-09-20 01:42:59 +00:00
if ( ( playersprite_t ) ( spr2 & ~ FF_SPR2SUPER ) > = free_spr2 )
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
return 0 ;
2019-11-02 17:52:54 +00:00
while ( ! md2 - > model - > spr2frames [ spr2 ] . numframes
2017-08-26 17:56:23 +00:00
& & spr2 ! = SPR2_STND
& & + + i ! = 32 ) // recursion limiter
{
if ( spr2 & FF_SPR2SUPER )
{
super = FF_SPR2SUPER ;
spr2 & = ~ FF_SPR2SUPER ;
continue ;
}
switch ( spr2 )
{
// Normal special cases.
case SPR2_JUMP :
spr2 = ( ( player
? player - > charflags
: skin - > flags )
& SF_NOJUMPSPIN ) ? SPR2_SPNG : SPR2_ROLL ;
break ;
case SPR2_TIRE :
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
spr2 = ( ( player
? player - > charability
: skin - > ability )
= = CA_SWIM ) ? SPR2_SWIM : SPR2_FLY ;
2017-08-26 17:56:23 +00:00
break ;
// Use the handy list, that's what it's there for!
default :
spr2 = spr2defaults [ spr2 ] ;
break ;
}
spr2 | = super ;
}
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
if ( i > = 32 ) // probably an infinite loop...
return 0 ;
2017-08-26 17:56:23 +00:00
return spr2 ;
}
2020-07-12 15:04:56 +00:00
// Adjust texture coords of model to fit into a patch's max_s and max_t
2020-08-08 08:16:47 +00:00
static void adjustTextureCoords ( model_t * model , patch_t * patch )
2020-07-10 19:54:15 +00:00
{
int i ;
2020-08-08 08:16:47 +00:00
GLPatch_t * gpatch = ( ( GLPatch_t * ) patch - > hardware ) ;
2020-07-10 19:54:15 +00:00
for ( i = 0 ; i < model - > numMeshes ; i + + )
{
int j ;
mesh_t * mesh = & model - > meshes [ i ] ;
int numVertices ;
2020-07-12 15:04:56 +00:00
float * uvReadPtr = mesh - > originaluvs ;
float * uvWritePtr ;
2020-07-10 19:54:15 +00:00
// i dont know if this is actually possible, just logical conclusion of structure in CreateModelVBOs
2020-07-12 15:04:56 +00:00
if ( ! mesh - > frames & & ! mesh - > tinyframes ) continue ;
2020-07-10 19:54:15 +00:00
if ( mesh - > frames ) // again CreateModelVBO and CreateModelVBOTiny iterate like this so I'm gonna do that too
numVertices = mesh - > numTriangles * 3 ;
else
numVertices = mesh - > numVertices ;
2020-07-12 15:04:56 +00:00
// if originaluvs points to uvs, we need to allocate new memory for adjusted uvs
// the old uvs are kept around for use in possible readjustments
if ( mesh - > uvs = = mesh - > originaluvs )
mesh - > uvs = Z_Malloc ( numVertices * 2 * sizeof ( float ) , PU_STATIC , NULL ) ;
uvWritePtr = mesh - > uvs ;
2020-07-10 19:54:15 +00:00
// fix uvs (texture coordinates) to take into account that the actual texture
// has empty space added until the next power of two
for ( j = 0 ; j < numVertices ; j + + )
{
2020-07-12 15:04:56 +00:00
* uvWritePtr + + = * uvReadPtr + + * gpatch - > max_s ;
* uvWritePtr + + = * uvReadPtr + + * gpatch - > max_t ;
2020-07-10 19:54:15 +00:00
}
}
2020-07-12 15:04:56 +00:00
// Save the values we adjusted the uvs for
model - > max_s = gpatch - > max_s ;
model - > max_t = gpatch - > max_t ;
2020-07-10 19:54:15 +00:00
}
2019-11-05 13:28:19 +00:00
//
// HWR_DrawModel
//
2020-07-06 04:15:08 +00:00
boolean HWR_DrawModel ( gl_vissprite_t * spr )
2014-03-15 16:59:03 +00:00
{
2019-12-25 19:22:01 +00:00
md2_t * md2 ;
2014-03-15 16:59:03 +00:00
char filename [ 64 ] ;
2018-12-19 03:50:00 +00:00
INT32 frame = 0 ;
INT32 nextFrame = - 1 ;
2019-09-03 21:27:22 +00:00
UINT8 spr2 = 0 ;
2014-03-15 16:59:03 +00:00
FTransform p ;
2019-12-25 19:22:01 +00:00
FSurfaceInfo Surf ;
2014-03-15 16:59:03 +00:00
2020-07-06 04:15:08 +00:00
if ( ! cv_glmodels . value )
2019-12-13 01:53:19 +00:00
return false ;
2015-01-28 15:16:50 +00:00
2016-01-03 03:53:43 +00:00
if ( spr - > precip )
2019-12-13 01:53:19 +00:00
return false ;
2015-01-28 15:16:50 +00:00
2020-07-12 15:04:56 +00:00
// Lactozilla: Disallow certain models from rendering
if ( ! HWR_AllowModel ( spr - > mobj ) )
return false ;
2019-12-08 02:55:28 +00:00
memset ( & p , 0x00 , sizeof ( FTransform ) ) ;
2014-03-15 16:59:03 +00:00
// MD2 colormap fix
// colormap test
2019-10-13 10:59:38 +00:00
if ( spr - > mobj - > subsector )
2014-03-15 16:59:03 +00:00
{
sector_t * sector = spr - > mobj - > subsector - > sector ;
2015-01-10 20:54:17 +00:00
UINT8 lightlevel = 255 ;
2020-01-01 18:55:18 +00:00
extracolormap_t * colormap = NULL ;
2014-03-15 16:59:03 +00:00
if ( sector - > numlights )
{
2014-03-19 23:10:37 +00:00
INT32 light ;
2014-03-15 16:59:03 +00:00
2014-03-19 23:10:37 +00:00
light = R_GetPlaneLight ( sector , spr - > mobj - > z + spr - > mobj - > height , false ) ; // Always use the light at the top instead of whatever I was doing before
2014-03-15 16:59:03 +00:00
2021-03-02 05:27:14 +00:00
if ( ! R_ThingIsFullBright ( spr - > mobj ) )
2020-03-08 03:41:52 +00:00
lightlevel = * sector - > lightlist [ light ] . lightlevel > 255 ? 255 : * sector - > lightlist [ light ] . lightlevel ;
2014-03-15 16:59:03 +00:00
2018-09-12 20:28:55 +00:00
if ( * sector - > lightlist [ light ] . extra_colormap )
colormap = * sector - > lightlist [ light ] . extra_colormap ;
2014-03-15 16:59:03 +00:00
}
else
{
2021-03-02 05:27:14 +00:00
if ( ! R_ThingIsFullBright ( spr - > mobj ) )
2020-03-08 03:41:52 +00:00
lightlevel = sector - > lightlevel > 255 ? 255 : sector - > lightlevel ;
2014-03-15 16:59:03 +00:00
if ( sector - > extra_colormap )
colormap = sector - > extra_colormap ;
}
2019-12-25 19:22:01 +00:00
HWR_Lighting ( & Surf , lightlevel , colormap ) ;
2014-03-15 16:59:03 +00:00
}
2019-10-13 10:59:38 +00:00
else
2019-12-25 19:22:01 +00:00
Surf . PolyColor . rgba = 0xFFFFFFFF ;
2014-03-15 16:59:03 +00:00
2015-01-28 15:16:50 +00:00
// Look at HWR_ProjectSprite for more
2014-03-15 16:59:03 +00:00
{
2020-08-08 08:16:47 +00:00
patch_t * gpatch , * blendgpatch ;
GLPatch_t * hwrPatch = NULL , * hwrBlendPatch = NULL ;
2022-04-25 21:50:07 +00:00
float durs = ( float ) spr - > mobj - > state - > tics ;
float tics = ( float ) spr - > mobj - > tics ;
2021-03-02 05:27:14 +00:00
const boolean papersprite = ( R_ThingIsPaperSprite ( spr - > mobj ) & & ! R_ThingIsFloorSprite ( spr - > mobj ) ) ;
const UINT8 flip = ( UINT8 ) ( ! ( spr - > mobj - > eflags & MFE_VERTICALFLIP ) ! = ! R_ThingVerticallyFlipped ( spr - > mobj ) ) ;
const UINT8 hflip = ( UINT8 ) ( ! ( spr - > mobj - > mirrored ) ! = ! R_ThingHorizontallyFlipped ( spr - > mobj ) ) ;
2014-03-15 16:59:03 +00:00
spritedef_t * sprdef ;
spriteframe_t * sprframe ;
2019-11-13 15:36:44 +00:00
spriteinfo_t * sprinfo ;
angle_t ang ;
2019-11-02 17:52:54 +00:00
INT32 mod ;
2014-03-15 16:59:03 +00:00
float finalscale ;
2022-04-13 04:06:14 +00:00
interpmobjstate_t interp ;
2022-03-27 03:48:08 +00:00
if ( R_UsingFrameInterpolation ( ) & & ! paused )
2022-04-13 04:06:14 +00:00
{
R_InterpolateMobjState ( spr - > mobj , rendertimefrac , & interp ) ;
}
else
{
R_InterpolateMobjState ( spr - > mobj , FRACUNIT , & interp ) ;
}
2014-03-15 16:59:03 +00:00
2014-04-17 10:57:10 +00:00
// Apparently people don't like jump frames like that, so back it goes
//if (tics > durs)
//durs = tics;
2014-04-07 11:37:15 +00:00
2022-04-08 19:01:41 +00:00
INT32 blendmode ;
if ( spr - > mobj - > frame & FF_BLENDMASK )
blendmode = ( ( spr - > mobj - > frame & FF_BLENDMASK ) > > FF_BLENDSHIFT ) + 1 ;
else
blendmode = spr - > mobj - > blendmode ;
2020-10-27 20:02:35 +00:00
if ( spr - > mobj - > frame & FF_TRANSMASK )
2022-04-08 19:01:41 +00:00
Surf . PolyFlags = HWR_SurfaceBlend ( blendmode , ( spr - > mobj - > frame & FF_TRANSMASK ) > > FF_TRANSSHIFT , & Surf ) ;
2014-03-15 16:59:03 +00:00
else
2020-10-27 03:03:41 +00:00
{
2020-10-27 20:02:35 +00:00
Surf . PolyColor . s . alpha = ( spr - > mobj - > flags2 & MF2_SHADOW ) ? 0x40 : 0xff ;
2022-04-08 19:01:41 +00:00
Surf . PolyFlags = HWR_GetBlendModeFlag ( blendmode ) ;
2020-10-27 03:03:41 +00:00
}
2014-03-15 16:59:03 +00:00
2020-10-27 20:02:35 +00:00
// don't forget to enable the depth test because we can't do this
// like before: model polygons are not sorted
2014-03-15 16:59:03 +00:00
// 1. load model+texture if not already loaded
// 2. draw model with correct position, rotation,...
2014-03-19 23:10:37 +00:00
if ( spr - > mobj - > skin & & spr - > mobj - > sprite = = SPR_PLAY ) // Use the player MD2 list if the mobj has a skin and is using the player sprites
2014-03-15 16:59:03 +00:00
{
md2 = & md2_playermodels [ ( skin_t * ) spr - > mobj - > skin - skins ] ;
md2 - > skin = ( skin_t * ) spr - > mobj - > skin - skins ;
2019-11-13 15:36:44 +00:00
sprinfo = & ( ( skin_t * ) spr - > mobj - > skin ) - > sprinfo [ spr - > mobj - > sprite2 ] ;
2014-03-15 16:59:03 +00:00
}
else
2019-11-13 15:36:44 +00:00
{
2014-03-15 16:59:03 +00:00
md2 = & md2_models [ spr - > mobj - > sprite ] ;
2019-11-13 15:36:44 +00:00
sprinfo = & spriteinfo [ spr - > mobj - > sprite ] ;
}
2014-03-15 16:59:03 +00:00
2020-07-10 19:54:15 +00:00
// texture loading before model init, so it knows if sprite graphics are used, which
// means that texture coordinates have to be adjusted
gpatch = md2 - > grpatch ;
2020-08-08 08:16:47 +00:00
if ( gpatch )
hwrPatch = ( ( GLPatch_t * ) gpatch - > hardware ) ;
if ( ! gpatch | | ! hwrPatch
| | ( ( ! hwrPatch - > mipmap - > format | | ! hwrPatch - > mipmap - > downloaded ) & & ! md2 - > notexturefile ) )
2020-07-10 19:54:15 +00:00
md2_loadTexture ( md2 ) ;
2020-08-08 08:16:47 +00:00
// Load it again, because it isn't being loaded into gpatch after md2_loadtexture...
gpatch = md2 - > grpatch ;
if ( gpatch )
hwrPatch = ( ( GLPatch_t * ) gpatch - > hardware ) ;
// Load blend texture
blendgpatch = md2 - > blendgrpatch ;
if ( blendgpatch )
hwrBlendPatch = ( ( GLPatch_t * ) blendgpatch - > hardware ) ;
if ( ( gpatch & & hwrPatch & & hwrPatch - > mipmap - > format ) // don't load the blend texture if the base texture isn't available
& & ( ! blendgpatch | | ! hwrBlendPatch
| | ( ( ! hwrBlendPatch - > mipmap - > format | | ! hwrBlendPatch - > mipmap - > downloaded ) & & ! md2 - > noblendfile ) ) )
2020-07-10 19:54:15 +00:00
md2_loadBlendTexture ( md2 ) ;
2021-03-13 21:07:51 +00:00
// Load it again, because it isn't being loaded into blendgpatch after md2_loadblendtexture...
blendgpatch = md2 - > blendgrpatch ;
if ( blendgpatch )
hwrBlendPatch = ( ( GLPatch_t * ) blendgpatch - > hardware ) ;
2016-11-03 21:06:23 +00:00
if ( md2 - > error )
2019-12-13 01:53:19 +00:00
return false ; // we already failed loading this before :(
2014-03-15 16:59:03 +00:00
if ( ! md2 - > model )
{
2019-05-03 02:55:45 +00:00
//CONS_Debug(DBG_RENDER, "Loading model... (%s)", sprnames[spr->mobj->sprite]);
2019-09-03 02:29:16 +00:00
sprintf ( filename , " models/%s " , md2 - > filename ) ;
2014-03-15 16:59:03 +00:00
md2 - > model = md2_readModel ( filename ) ;
if ( md2 - > model )
{
md2_printModelInfo ( md2 - > model ) ;
2020-07-16 19:11:36 +00:00
// If model uses sprite patch as texture, then
2020-07-10 19:54:15 +00:00
// adjust texture coordinates to take power of two textures into account
2020-08-08 08:16:47 +00:00
if ( ! gpatch | | ! hwrPatch - > mipmap - > format )
2020-07-10 19:54:15 +00:00
adjustTextureCoords ( md2 - > model , spr - > gpatch ) ;
2020-07-16 19:11:36 +00:00
// note down the max_s and max_t that end up in the VBO
md2 - > model - > vbo_max_s = md2 - > model - > max_s ;
md2 - > model - > vbo_max_t = md2 - > model - > max_t ;
2018-12-26 15:58:37 +00:00
HWD . pfnCreateModelVBOs ( md2 - > model ) ;
2014-03-15 16:59:03 +00:00
}
else
{
//CONS_Debug(DBG_RENDER, " FAILED\n");
2016-11-03 21:06:23 +00:00
md2 - > error = true ; // prevent endless fail
2019-12-13 01:53:19 +00:00
return false ;
2014-03-15 16:59:03 +00:00
}
}
2019-12-13 01:53:19 +00:00
2014-03-19 23:10:37 +00:00
//HWD.pfnSetBlend(blend); // This seems to actually break translucency?
2014-03-15 16:59:03 +00:00
finalscale = md2 - > scale ;
//Hurdler: arf, I don't like that implementation at all... too much crappy
2015-07-26 19:14:47 +00:00
2020-08-08 08:16:47 +00:00
if ( gpatch & & hwrPatch & & hwrPatch - > mipmap - > format ) // else if meant that if a texture couldn't be loaded, it would just end up using something else's texture
2014-03-15 16:59:03 +00:00
{
2020-02-02 03:11:21 +00:00
INT32 skinnum = TC_DEFAULT ;
2019-12-29 21:36:53 +00:00
if ( ( spr - > mobj - > flags & ( MF_ENEMY | MF_BOSS ) ) & & ( spr - > mobj - > flags2 & MF2_FRET ) & & ! ( spr - > mobj - > flags & MF_GRENADEBOUNCE ) & & ( leveltime & 1 ) ) // Bosses "flash"
2015-07-26 19:14:47 +00:00
{
2019-12-29 21:36:53 +00:00
if ( spr - > mobj - > type = = MT_CYBRAKDEMON | | spr - > mobj - > colorized )
skinnum = TC_ALLWHITE ;
else if ( spr - > mobj - > type = = MT_METALSONIC_BATTLE )
skinnum = TC_METALSONIC ;
else
skinnum = TC_BOSS ;
}
2020-02-15 08:18:41 +00:00
else if ( ( skincolornum_t ) spr - > mobj - > color ! = SKINCOLOR_NONE )
2019-12-29 21:36:53 +00:00
{
if ( spr - > mobj - > colorized )
skinnum = TC_RAINBOW ;
else if ( spr - > mobj - > player & & spr - > mobj - > player - > dashmode > = DASHMODE_THRESHOLD
& & ( spr - > mobj - > player - > charflags & SF_DASHMODE )
& & ( ( leveltime / 2 ) & 1 ) )
2019-06-18 16:55:57 +00:00
{
2019-12-29 21:36:53 +00:00
if ( spr - > mobj - > player - > charflags & SF_MACHINE )
skinnum = TC_DASHMODE ;
2019-06-18 16:55:57 +00:00
else
2019-11-04 13:11:04 +00:00
skinnum = TC_RAINBOW ;
2019-06-18 16:55:57 +00:00
}
2019-12-29 21:36:53 +00:00
else if ( spr - > mobj - > skin & & spr - > mobj - > sprite = = SPR_PLAY )
skinnum = ( INT32 ) ( ( skin_t * ) spr - > mobj - > skin - skins ) ;
2019-12-13 01:26:28 +00:00
else
2019-12-29 21:36:53 +00:00
skinnum = TC_DEFAULT ;
}
// Translation or skin number found
2020-08-08 08:16:47 +00:00
HWR_GetBlendedTexture ( gpatch , blendgpatch , skinnum , spr - > colormap , ( skincolornum_t ) spr - > mobj - > color ) ;
2014-03-15 16:59:03 +00:00
}
2020-10-10 20:01:10 +00:00
else // Sprite
2014-03-15 16:59:03 +00:00
{
2020-07-12 15:04:56 +00:00
// Check if sprite dimensions are different from previously used sprite.
// If so, uvs need to be readjusted.
2020-07-16 19:11:36 +00:00
// Comparing floats with the != operator here should be okay because they
// are just copies of glpatches' max_s and max_t values.
2020-08-18 20:21:26 +00:00
// Instead of the != operator, memcmp is used to avoid a compiler warning.
2020-10-10 20:01:10 +00:00
if ( memcmp ( & ( hwrPatch - > max_s ) , & ( md2 - > model - > max_s ) , sizeof ( md2 - > model - > max_s ) ) ! = 0 | |
memcmp ( & ( hwrPatch - > max_t ) , & ( md2 - > model - > max_t ) , sizeof ( md2 - > model - > max_t ) ) ! = 0 )
2020-11-22 20:10:10 +00:00
adjustTextureCoords ( md2 - > model , spr - > gpatch ) ;
2020-08-08 08:16:47 +00:00
HWR_GetMappedPatch ( spr - > gpatch , spr - > colormap ) ;
2014-03-15 16:59:03 +00:00
}
2016-02-09 16:20:18 +00:00
if ( spr - > mobj - > frame & FF_ANIMATE )
{
// set duration and tics to be the correct values for FF_ANIMATE states
2022-04-25 21:50:07 +00:00
durs = ( float ) spr - > mobj - > state - > var2 ;
tics = ( float ) spr - > mobj - > anim_duration ;
2016-02-09 16:20:18 +00:00
}
2019-11-02 17:52:54 +00:00
frame = ( spr - > mobj - > frame & FF_FRAMEMASK ) ;
2017-08-27 13:56:07 +00:00
if ( spr - > mobj - > skin & & spr - > mobj - > sprite = = SPR_PLAY & & md2 - > model - > spr2frames )
2014-03-15 16:59:03 +00:00
{
2019-11-05 13:28:19 +00:00
spr2 = HWR_GetModelSprite2 ( md2 , spr - > mobj - > skin , spr - > mobj - > sprite2 , spr - > mobj - > player ) ;
2019-11-02 17:52:54 +00:00
mod = md2 - > model - > spr2frames [ spr2 ] . numframes ;
# ifndef DONTHIDEDIFFANIMLENGTH // by default, different anim length is masked by the mod
if ( mod > ( INT32 ) ( ( skin_t * ) spr - > mobj - > skin ) - > sprites [ spr2 ] . numframes )
2017-09-09 21:12:23 +00:00
mod = ( ( skin_t * ) spr - > mobj - > skin ) - > sprites [ spr2 ] . numframes ;
2019-11-02 17:52:54 +00:00
# endif
2019-11-02 18:12:12 +00:00
if ( ! mod )
mod = 1 ;
frame = md2 - > model - > spr2frames [ spr2 ] . frames [ frame % mod ] ;
2019-09-03 21:27:22 +00:00
}
else
2019-11-02 17:52:54 +00:00
{
mod = md2 - > model - > meshes [ 0 ] . numFrames ;
2019-11-02 18:12:12 +00:00
if ( ! mod )
mod = 1 ;
2019-11-02 17:52:54 +00:00
}
2017-08-26 17:56:23 +00:00
2019-09-03 02:29:16 +00:00
# ifdef USE_MODEL_NEXTFRAME
2022-04-25 21:50:07 +00:00
// Interpolate the model interpolation. (lol)
tics - = FixedToFloat ( rendertimefrac ) ;
# define INTERPOLERATION_LIMIT (TICRATE * 0.25f)
2020-07-06 04:15:08 +00:00
if ( cv_glmodelinterpolation . value & & tics < = durs & & tics < = INTERPOLERATION_LIMIT )
2018-12-19 03:50:00 +00:00
{
2019-11-02 17:52:54 +00:00
if ( durs > INTERPOLERATION_LIMIT )
durs = INTERPOLERATION_LIMIT ;
2017-08-26 17:56:23 +00:00
2019-11-02 17:52:54 +00:00
if ( spr - > mobj - > skin & & spr - > mobj - > sprite = = SPR_PLAY & & md2 - > model - > spr2frames )
2018-12-19 03:50:00 +00:00
{
2019-11-02 18:12:12 +00:00
if ( HWR_CanInterpolateSprite2 ( & md2 - > model - > spr2frames [ spr2 ] )
2019-11-02 17:52:54 +00:00
& & ( spr - > mobj - > frame & FF_ANIMATE
2017-08-26 17:56:23 +00:00
| | ( spr - > mobj - > state - > nextstate ! = S_NULL
2019-11-02 17:52:54 +00:00
& & states [ spr - > mobj - > state - > nextstate ] . sprite = = SPR_PLAY
& & ( ( P_GetSkinSprite2 ( spr - > mobj - > skin , ( ( ( spr - > mobj - > player & & spr - > mobj - > player - > powers [ pw_super ] ) ? FF_SPR2SUPER : 0 ) | states [ spr - > mobj - > state - > nextstate ] . frame ) & FF_FRAMEMASK , spr - > mobj - > player ) = = spr - > mobj - > sprite2 ) ) ) ) )
2017-08-26 17:56:23 +00:00
{
2019-09-03 02:29:16 +00:00
nextFrame = ( spr - > mobj - > frame & FF_FRAMEMASK ) + 1 ;
2019-11-02 17:52:54 +00:00
if ( nextFrame > = mod )
2021-05-22 08:17:48 +00:00
{
if ( spr - > mobj - > state - > frame & FF_SPR2ENDSTATE )
nextFrame - - ;
else
nextFrame = 0 ;
}
2017-09-09 21:12:23 +00:00
if ( frame | | ! ( spr - > mobj - > state - > frame & FF_SPR2ENDSTATE ) )
2019-09-03 21:27:22 +00:00
nextFrame = md2 - > model - > spr2frames [ spr2 ] . frames [ nextFrame ] ;
2019-11-02 17:52:54 +00:00
else
nextFrame = - 1 ;
2017-08-26 17:56:23 +00:00
}
2016-02-09 16:20:18 +00:00
}
2019-09-04 01:57:54 +00:00
else if ( HWR_CanInterpolateModel ( spr - > mobj , md2 - > model ) )
2016-02-09 16:20:18 +00:00
{
2017-08-26 17:56:23 +00:00
// frames are handled differently for states with FF_ANIMATE, so get the next frame differently for the interpolation
if ( spr - > mobj - > frame & FF_ANIMATE )
2016-02-09 16:20:18 +00:00
{
2019-08-05 01:32:41 +00:00
nextFrame = ( spr - > mobj - > frame & FF_FRAMEMASK ) + 1 ;
2019-11-02 17:52:54 +00:00
if ( nextFrame > = ( INT32 ) ( spr - > mobj - > state - > var1 + ( spr - > mobj - > state - > frame & FF_FRAMEMASK ) ) )
2019-11-02 18:12:12 +00:00
nextFrame = ( spr - > mobj - > state - > frame & FF_FRAMEMASK ) % mod ;
2016-02-09 16:20:18 +00:00
}
2017-08-26 17:56:23 +00:00
else
{
2019-08-05 01:32:41 +00:00
if ( spr - > mobj - > state - > nextstate ! = S_NULL & & states [ spr - > mobj - > state - > nextstate ] . sprite ! = SPR_NULL
2019-11-02 18:12:12 +00:00
& & ! ( spr - > mobj - > player & & ( spr - > mobj - > state - > nextstate = = S_PLAY_WAIT ) & & spr - > mobj - > state = = & states [ S_PLAY_STND ] ) )
nextFrame = ( states [ spr - > mobj - > state - > nextstate ] . frame & FF_FRAMEMASK ) % mod ;
2017-08-26 17:56:23 +00:00
}
2016-02-09 16:20:18 +00:00
}
2014-03-15 16:59:03 +00:00
}
2017-08-26 17:56:23 +00:00
# undef INTERPOLERATION_LIMIT
2019-09-03 02:29:16 +00:00
# endif
2017-08-26 17:56:23 +00:00
2014-03-15 16:59:03 +00:00
//Hurdler: it seems there is still a small problem with mobj angle
2022-04-13 04:06:14 +00:00
p . x = FIXED_TO_FLOAT ( interp . x ) ;
p . y = FIXED_TO_FLOAT ( interp . y ) + md2 - > offset ;
2014-03-15 16:59:03 +00:00
2016-09-24 13:23:00 +00:00
if ( flip )
2022-04-13 04:06:14 +00:00
p . z = FIXED_TO_FLOAT ( interp . z + spr - > mobj - > height ) ;
2014-03-15 16:59:03 +00:00
else
2022-04-13 04:06:14 +00:00
p . z = FIXED_TO_FLOAT ( interp . z ) ;
2014-03-15 16:59:03 +00:00
2014-09-09 23:22:39 +00:00
if ( spr - > mobj - > skin & & spr - > mobj - > sprite = = SPR_PLAY )
2015-01-22 15:23:45 +00:00
sprdef = & ( ( skin_t * ) spr - > mobj - > skin ) - > sprites [ spr - > mobj - > sprite2 ] ;
2014-03-15 16:59:03 +00:00
else
sprdef = & sprites [ spr - > mobj - > sprite ] ;
sprframe = & sprdef - > spriteframes [ spr - > mobj - > frame & FF_FRAMEMASK ] ;
2020-01-01 21:00:01 +00:00
if ( sprframe - > rotate | | papersprite )
2014-03-15 16:59:03 +00:00
{
2022-04-13 04:06:14 +00:00
fixed_t anglef = AngleFixed ( interp . angle ) ;
2019-08-05 01:32:41 +00:00
2014-03-15 16:59:03 +00:00
p . angley = FIXED_TO_FLOAT ( anglef ) ;
}
else
{
2022-04-13 04:06:14 +00:00
const fixed_t anglef = AngleFixed ( ( R_PointToAngle ( interp . x , interp . y ) ) - ANGLE_180 ) ;
2014-03-15 16:59:03 +00:00
p . angley = FIXED_TO_FLOAT ( anglef ) ;
}
2019-08-18 17:16:48 +00:00
2019-11-13 00:38:02 +00:00
p . rollangle = 0.0f ;
2019-12-17 16:48:48 +00:00
p . rollflip = 1 ;
2019-11-13 15:47:30 +00:00
p . rotaxis = 0 ;
2019-08-18 17:16:48 +00:00
if ( spr - > mobj - > rollangle )
{
fixed_t anglef = AngleFixed ( spr - > mobj - > rollangle ) ;
2019-11-13 00:38:02 +00:00
p . rollangle = FIXED_TO_FLOAT ( anglef ) ;
2019-11-13 15:36:44 +00:00
p . roll = true ;
// rotation pivot
2019-08-18 17:16:48 +00:00
p . centerx = FIXED_TO_FLOAT ( spr - > mobj - > radius / 2 ) ;
2020-02-24 00:44:22 +00:00
p . centery = FIXED_TO_FLOAT ( spr - > mobj - > height / ( flip ? - 2 : 2 ) ) ;
2019-11-13 15:36:44 +00:00
2019-11-13 15:47:30 +00:00
// rotation axis
2019-11-13 15:36:44 +00:00
if ( sprinfo - > available )
2019-11-13 15:47:30 +00:00
p . rotaxis = ( UINT8 ) ( sprinfo - > pivot [ ( spr - > mobj - > frame & FF_FRAMEMASK ) ] . rotaxis ) ;
2019-11-13 15:36:44 +00:00
// for NiGHTS specifically but should work everywhere else
2022-04-13 04:06:14 +00:00
ang = R_PointToAngle ( interp . x , interp . y ) - interp . angle ;
2019-11-13 15:36:44 +00:00
if ( ( sprframe - > rotate & SRF_RIGHT ) & & ( ang < ANGLE_180 ) ) // See from right
p . rollflip = 1 ;
else if ( ( sprframe - > rotate & SRF_LEFT ) & & ( ang > = ANGLE_180 ) ) // See from left
p . rollflip = - 1 ;
2020-02-24 00:44:22 +00:00
if ( flip )
p . rollflip * = - 1 ;
2019-11-13 00:38:02 +00:00
}
2014-03-15 16:59:03 +00:00
p . anglex = 0.0f ;
2019-11-13 00:38:02 +00:00
2018-12-19 05:17:51 +00:00
# ifdef USE_FTRANSFORM_ANGLEZ
// Slope rotation from Kart
2018-12-19 03:50:00 +00:00
p . anglez = 0.0f ;
if ( spr - > mobj - > standingslope )
{
fixed_t tempz = spr - > mobj - > standingslope - > normal . z ;
fixed_t tempy = spr - > mobj - > standingslope - > normal . y ;
fixed_t tempx = spr - > mobj - > standingslope - > normal . x ;
fixed_t tempangle = AngleFixed ( R_PointToAngle2 ( 0 , 0 , FixedSqrt ( FixedMul ( tempy , tempy ) + FixedMul ( tempz , tempz ) ) , tempx ) ) ;
p . anglez = FIXED_TO_FLOAT ( tempangle ) ;
tempangle = - AngleFixed ( R_PointToAngle2 ( 0 , 0 , tempz , tempy ) ) ;
p . anglex = FIXED_TO_FLOAT ( tempangle ) ;
2019-08-18 17:16:48 +00:00
}
# endif
2014-03-15 16:59:03 +00:00
// SRB2CBTODO: MD2 scaling support
finalscale * = FIXED_TO_FLOAT ( spr - > mobj - > scale ) ;
2016-05-27 13:53:36 +00:00
p . flip = atransform . flip ;
2018-12-19 05:17:51 +00:00
# ifdef USE_FTRANSFORM_MIRROR
p . mirror = atransform . mirror ; // from Kart
# endif
2014-04-07 16:08:04 +00:00
2020-08-07 21:17:05 +00:00
HWD . pfnSetShader ( SHADER_MODEL ) ; // model shader
2020-06-07 20:11:36 +00:00
HWD . pfnDrawModel ( md2 - > model , frame , durs , tics , nextFrame , & p , finalscale , flip , hflip , & Surf ) ;
2014-03-15 16:59:03 +00:00
}
2019-12-13 01:53:19 +00:00
return true ;
2014-03-15 16:59:03 +00:00
}
# endif //HWRENDER