diff --git a/.gitmodules b/.gitmodules index fc29b8c4..def188dd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "mainui"] path = mainui - url = https://github.com/FWGS/mainui_cpp + url = https://github.com/Crow-bar/mainui_cpp [submodule "ref_gl/nanogl"] path = ref_gl/nanogl url = https://github.com/FWGS/nanogl diff --git a/common/backends.h b/common/backends.h index 34c0f097..3896f8b1 100644 --- a/common/backends.h +++ b/common/backends.h @@ -21,6 +21,7 @@ GNU General Public License for more details. #define VIDEO_ANDROID 2 #define VIDEO_FBDEV 3 #define VIDEO_DOS 4 +#define VIDEO_PSP 5 // audio backends (XASH_SOUND) @@ -28,6 +29,7 @@ GNU General Public License for more details. #define SOUND_SDL 1 #define SOUND_OPENSLES 2 #define SOUND_ALSA 3 +#define SOUND_PSP 4 // crash handler (XASH_CRASHHANDLER) #define CRASHHANDLER_NULL 0 @@ -40,6 +42,7 @@ GNU General Public License for more details. #define INPUT_SDL 1 #define INPUT_ANDROID 2 #define INPUT_EVDEV 3 +#define INPUT_PSP 4 // timer (XASH_TIMER) #define TIMER_NULL 0 // not used @@ -47,19 +50,21 @@ GNU General Public License for more details. #define TIMER_LINUX 2 #define TIMER_WIN32 3 #define TIMER_DOS 4 +#define TIMER_PSP 5 // messageboxes (XASH_MESSAGEBOX) #define MSGBOX_STDERR 0 #define MSGBOX_SDL 1 #define MSGBOX_ANDROID 2 #define MSGBOX_WIN32 3 - +#define MSGBOX_PSP 4 // library loading (XASH_LIB) -#define LIB_NULL 0 -#define LIB_POSIX 1 -#define LIB_WIN32 2 +#define LIB_NULL 0 +#define LIB_POSIX 1 +#define LIB_WIN32 2 #define LIB_STATIC 3 +#define LIB_PSP 4 #endif /* BACKENDS_H */ diff --git a/common/com_model.h b/common/com_model.h index 1372cd07..dacbe525 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -126,6 +126,21 @@ typedef struct int flags; // sky or slime, no lightmap or 256 subdivision } mtexinfo_t; +#if XASH_PSP +typedef struct +{ + float uv[2]; + float xyz[3]; +}gu_vert_t; +typedef struct glpoly_s +{ + struct glpoly_s *next; + struct glpoly_s *chain; + int numverts; + int flags; // for SURF_UNDERWATER + gu_vert_t verts[1]; // variable sized (xyz s1t1 + lm(xyz s2t2)) +} glpoly_t; +#else typedef struct glpoly_s { struct glpoly_s *next; @@ -134,7 +149,7 @@ typedef struct glpoly_s int flags; // for SURF_UNDERWATER float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) } glpoly_t; - +#endif typedef struct mnode_s { // common with leaf diff --git a/common/defaults.h b/common/defaults.h index e31d093c..71d7e885 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -95,6 +95,29 @@ SETUP BACKENDS DEFINITIONS // usually only 10-20 fds availiable #define XASH_REDUCE_FD + #elif XASH_PSP + #ifndef XASH_VIDEO + #define XASH_VIDEO VIDEO_PSP + #endif + + #ifndef XASH_TIMER + #define XASH_TIMER TIMER_PSP + #endif + + #ifndef XASH_INPUT + #define XASH_INPUT INPUT_PSP + #endif + + #ifndef XASH_SOUND + #define XASH_SOUND SOUND_PSP + #endif // XASH_SOUND + + #ifndef XASH_MESSAGEBOX + #define XASH_MESSAGEBOX MSGBOX_PSP + #endif // XASH_MESSAGEBOX + + #define XASH_REDUCE_FD + #define XASH_NO_TOUCH #endif #endif // XASH_DEDICATED @@ -132,7 +155,7 @@ SETUP BACKENDS DEFINITIONS #endif // !XASH_WIN32 #endif -#ifdef XASH_STATIC_LIBS +#if defined(XASH_STATIC_LIBS) && !defined(XASH_PSP) #define XASH_LIB LIB_STATIC #define XASH_INTERNAL_GAMELIBS #define XASH_ALLOW_SAVERESTORE_OFFSETS @@ -140,6 +163,9 @@ SETUP BACKENDS DEFINITIONS #define XASH_LIB LIB_WIN32 #elif XASH_POSIX #define XASH_LIB LIB_POSIX +#elif XASH_PSP +#define XASH_ALLOW_SAVERESTORE_OFFSETS +#define XASH_LIB LIB_PSP #endif // @@ -193,11 +219,15 @@ Default build-depended cvar and constant values #endif // DEFAULT_FULLSCREEN #ifndef DEFAULT_ACCELERATED_RENDERER - #if XASH_MOBILE_PLATFORM - #define DEFAULT_ACCELERATED_RENDERER "gles1" - #else // !XASH_MOBILE_PLATFORM - #define DEFAULT_ACCELERATED_RENDERER "gl" - #endif // !XASH_MOBILE_PLATFORM + #ifdef XASH_PSP + #define DEFAULT_ACCELERATED_RENDERER "gu" + #else + #if XASH_MOBILE_PLATFORM + #define DEFAULT_ACCELERATED_RENDERER "gles1" + #else // !XASH_MOBILE_PLATFORM + #define DEFAULT_ACCELERATED_RENDERER "gl" + #endif // !XASH_MOBILE_PLATFORM + #endif #endif // DEFAULT_ACCELERATED_RENDERER #ifndef DEFAULT_SOFTWARE_RENDERER diff --git a/common/port.h b/common/port.h index 102b6dff..233335b9 100644 --- a/common/port.h +++ b/common/port.h @@ -26,7 +26,9 @@ GNU General Public License for more details. #include #define OS_LIB_EXT "dylib" #define OPEN_COMMAND "open" - #else + #elif XASH_PSP + #define OS_LIB_EXT "prx" + #else #define OS_LIB_EXT "so" #define OPEN_COMMAND "xdg-open" #endif @@ -74,6 +76,25 @@ GNU General Public License for more details. #define FreeLibrary( x ) (0) #define SetCurrentDirectory( x ) (!chdir( x )) #endif + +#if XASH_PSP + #include + #include + #include "../engine/platform/psp/dll_psp.h" + #define PATH_MAX 1024 + #define PATH_SPLITTER "/" + #define O_BINARY 0 + #define O_TEXT 0 + #define _mkdir( x ) sceIoMkdir( x, FIO_S_IRWXU | FIO_S_IRWXG | FIO_S_IROTH | FIO_S_IXOTH ) + #define SetCurrentDirectory( x ) ( !sceIoChdir( x ) ) + #define LoadLibrary( x ) ( 0 ) + #define GetProcAddress( x, y ) ( 0 ) + #define FreeLibrary( x ) ( 0 ) +// #define LoadLibrary( x ) dlopen( x, RTLD_NOW ) +// #define GetProcAddress( x, y ) dlsym( x, y ) +// #define FreeLibrary( x ) dlclose( x ) +#endif + //#define MAKEWORD( a, b ) ((short int)(((unsigned char)(a))|(((short int)((unsigned char)(b)))<<8))) #define max( a, b ) (((a) > (b)) ? (a) : (b)) #define min( a, b ) (((a) < (b)) ? (a) : (b)) diff --git a/common/xash3d_types.h b/common/xash3d_types.h index 7f979601..e6c9824c 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -136,6 +136,9 @@ typedef void (*setpair_t)( const char *key, const void *value, void *buffer, voi // config strings are a general means of communication from // the server to all connected clients. // each config string can be at most CS_SIZE characters. +#if XASH_PSP +#define MAX_QPATH 48 +#else #if XASH_LOW_MEMORY == 0 #define MAX_QPATH 64 // max length of a game pathname #elif XASH_LOW_MEMORY == 2 @@ -143,6 +146,7 @@ typedef void (*setpair_t)( const char *key, const void *value, void *buffer, voi #elif XASH_LOW_MEMORY == 1 #define MAX_QPATH 48 #endif +#endif #define MAX_OSPATH 260 // max length of a filesystem pathname #define CS_SIZE 64 // size of one config string #define CS_TIME 16 // size of time string diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 707bc27b..baa4b749 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -103,6 +103,12 @@ CL_GetEntityByIndex Render callback for studio models ==================== */ +#if XASH_OPT +cl_entity_t *pfnCL_GetEntityByIndex( int index ) +{ + return CL_GetEntityByIndex( index ); +} +#else cl_entity_t *CL_GetEntityByIndex( int index ) { if( !clgame.entities ) // not in game yet @@ -116,7 +122,7 @@ cl_entity_t *CL_GetEntityByIndex( int index ) return CL_EDICT_NUM( index ); } - +#endif /* ================ CL_ModelHandle @@ -124,13 +130,19 @@ CL_ModelHandle get model handle by index ================ */ +#if XASH_OPT +model_t *pfnCL_ModelHandle( int modelindex ) +{ + return CL_ModelHandle( modelindex ); +} +#else model_t *CL_ModelHandle( int modelindex ) { if( modelindex < 0 || modelindex >= MAX_MODELS ) return NULL; return cl.models[modelindex]; } - +#endif /* ==================== CL_IsThirdPerson @@ -3807,7 +3819,11 @@ static cl_enginefunc_t gEngfuncs = pfnIsNoClipping, CL_GetLocalPlayer, CL_GetViewModel, +#if XASH_OPT + pfnCL_GetEntityByIndex, +#else CL_GetEntityByIndex, +#endif pfnGetClientTime, pfnCalcShake, pfnApplyShake, diff --git a/engine/client/cl_netgraph.c b/engine/client/cl_netgraph.c index ed40ed5a..3aa51ce0 100644 --- a/engine/client/cl_netgraph.c +++ b/engine/client/cl_netgraph.c @@ -16,9 +16,11 @@ GNU General Public License for more details. #include "common.h" #include "client.h" -#if XASH_LOW_MEMORY +#if XASH_LOW_MEMORY == 0 #define NET_TIMINGS 1024 -#else +#elif XASH_LOW_MEMORY == 1 +#define NET_TIMINGS 256 +#elif XASH_LOW_MEMORY == 2 #define NET_TIMINGS 64 #endif #define NET_TIMINGS_MASK (NET_TIMINGS - 1) diff --git a/engine/client/cl_render.c b/engine/client/cl_render.c index 88a25f69..8c645f46 100644 --- a/engine/client/cl_render.c +++ b/engine/client/cl_render.c @@ -271,7 +271,11 @@ static render_api_t gRenderAPI = pfnFileBufferCRC32, COM_CompareFileTime, Host_Error, +#if XASH_OPT + (void*)pfnCL_ModelHandle, +#else (void*)CL_ModelHandle, +#endif pfnTime, Cvar_Set, S_FadeMusicVolume, diff --git a/engine/client/client.h b/engine/client/client.h index c219f70b..9de00b29 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -709,7 +709,11 @@ dlight_t *CL_GetEntityLight( int number ); // // cl_cmds.c // +#ifdef XASH_PSP +void CL_Quit_f( void ); +#else void CL_Quit_f( void ) NORETURN; +#endif void CL_ScreenShot_f( void ); void CL_SnapShot_f( void ); void CL_PlayCDTrack_f( void ); @@ -821,7 +825,12 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ); client_textmessage_t *CL_TextMessageGet( const char *pName ); int pfnDecalIndexFromName( const char *szDecalName ); int pfnIndexFromTrace( struct pmtrace_s *pTrace ); +#if XASH_OPT +#define CL_ModelHandle( modelindex ) ( ( ( modelindex ) < 0 || ( modelindex ) >= MAX_MODELS ) ? NULL : cl.models[( modelindex )] ) +model_t *pfnCL_ModelHandle( int modelindex ); +#else model_t *CL_ModelHandle( int modelindex ); +#endif void NetAPI_CancelAllRequests( void ); int CL_FindModelIndex( const char *m ); cl_entity_t *CL_GetLocalPlayer( void ); diff --git a/engine/client/console.c b/engine/client/console.c index 1d4f3177..1cfe1277 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1788,13 +1788,20 @@ void Key_Console( int key ) } // console scrolling +#if XASH_PSP + if( key == K_JOY2 ) +#else if( key == K_PGUP ) +#endif { Con_PageUp( 1 ); return; } - +#if XASH_PSP + if( key == K_JOY1 ) +#else if( key == K_PGDN ) +#endif { Con_PageDown( 1 ); return; diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index c42ea954..cf754f87 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -19,6 +19,24 @@ GNU General Public License for more details. #include "vgui_draw.h" #include "mobility_int.h" +#ifdef XASH_NO_TOUCH + +void Touch_WriteConfig( void ) {} +void Touch_SetClientOnly( qboolean state ) {} +void Touch_RemoveButton( const char *name ) {} +void Touch_HideButtons( const char *name, byte hide ) {} +void Touch_AddClientButton( const char *name, const char *texture, const char *command, float x1, float y1, float x2, float y2, byte *color, int round, float aspect, int flags ) {} +void Touch_AddDefaultButton( const char *name, const char *texturefile, const char *command, float x1, float y1, float x2, float y2, byte *color, int round, float aspect, int flags ) {} +void Touch_ResetDefaultButtons( void ){} +void Touch_Init( void ) {} +void Touch_Draw( void ) {} +int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy ) { return 0; } +void Touch_GetMove( float *forward, float *side, float *yaw, float *pitch ) {} +void Touch_KeyEvent( int key, int down ) {} +void Touch_Shutdown( void ) {} + +#else /* XASH_NO_TOUCH */ + typedef enum { touch_command, // just tap a button @@ -1973,3 +1991,5 @@ void Touch_Shutdown( void ) touch.initialized = false; Mem_FreePool( &touch.mempool ); } + +#endif /* XASH_NO_TOUCH */ diff --git a/engine/client/input.c b/engine/client/input.c index 744ba6e0..62874174 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -61,10 +61,10 @@ uint IN_CollectInputDevices( void ) if( !m_ignore->value ) // no way to check is mouse connected, so use cvar only ret |= INPUT_DEVICE_MOUSE; - +#ifndef XASH_NO_TOUCH if( CVAR_TO_BOOL(touch_enable) ) ret |= INPUT_DEVICE_TOUCH; - +#endif if( Joy_IsActive() ) // connected or enabled ret |= INPUT_DEVICE_JOYSTICK; @@ -93,13 +93,17 @@ void IN_LockInputDevices( qboolean lock ) { SetBits( m_ignore->flags, FCVAR_READ_ONLY ); SetBits( joy_enable->flags, FCVAR_READ_ONLY ); +#ifndef XASH_NO_TOUCH SetBits( touch_enable->flags, FCVAR_READ_ONLY ); +#endif } else { ClearBits( m_ignore->flags, FCVAR_READ_ONLY ); ClearBits( joy_enable->flags, FCVAR_READ_ONLY ); +#ifndef XASH_NO_TOUCH ClearBits( touch_enable->flags, FCVAR_READ_ONLY ); +#endif } } @@ -196,7 +200,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) else if( newstate == key_game ) { // reset mouse pos, so cancel effect in game -#if SDL_VERSION_ATLEAST( 2, 0, 0 ) +#if SDL_VERSION_ATLEAST( 2, 0, 0 ) && !defined( XASH_NO_TOUCH ) if( CVAR_TO_BOOL( touch_enable ) ) { SDL_SetRelativeMouseMode( SDL_FALSE ); @@ -277,7 +281,7 @@ void IN_ActivateMouse( qboolean force ) { if( in_mouse_suspended ) { -#ifdef XASH_SDL +#if defined( XASH_SDL ) && !defined( XASH_NO_TOUCH ) /// TODO: Platform_ShowCursor if( !touch_emulate ) SDL_ShowCursor( SDL_FALSE ); @@ -372,6 +376,7 @@ void IN_MouseEvent( void ) { int i; +#ifndef XASH_NO_TOUCH // touch emu: handle motion if( CVAR_TO_BOOL( touch_emulate )) { @@ -380,7 +385,7 @@ void IN_MouseEvent( void ) else Touch_KeyEvent( K_MOUSE1, 2 ); } - +#endif if( !in_mouseinitialized || !in_mouseactive ) return; @@ -396,9 +401,10 @@ void IN_MouseEvent( void ) Platform_GetMousePos(&x, &y); if( host.mouse_visible ) SDL_ShowCursor( SDL_TRUE ); +#ifndef XASH_NO_TOUCH else if( !CVAR_TO_BOOL( touch_emulate ) ) SDL_ShowCursor( SDL_FALSE ); - +#endif if( x < host.window_center_x / 2 || y < host.window_center_y / 2 || x > host.window_center_x + host.window_center_x / 2 || diff --git a/engine/client/keys.c b/engine/client/keys.c index 9172b97f..c941b286 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -519,8 +519,11 @@ void Key_Init( void ) // setup default binding. "unbindall" from config.cfg will be reset it for( kn = keynames; kn->name; kn++ ) Key_SetBinding( kn->keynum, kn->binding ); - +#if XASH_PSP + osk_enable = Cvar_Get( "osk_enable", "1", FCVAR_ARCHIVE, "enable built-in on-screen keyboard" ); +#else osk_enable = Cvar_Get( "osk_enable", "0", FCVAR_ARCHIVE, "enable built-in on-screen keyboard" ); +#endif key_rotate = Cvar_Get( "key_rotate", "0", FCVAR_ARCHIVE, "rotate arrow keys (0-3)" ); } diff --git a/engine/client/ref_common.c b/engine/client/ref_common.c index 4a63b438..93fd07ec 100644 --- a/engine/client/ref_common.c +++ b/engine/client/ref_common.c @@ -268,7 +268,11 @@ static ref_api_t gEngfuncs = CL_GetLocalPlayer, CL_GetViewModel, +#if XASH_OPT + pfnCL_GetEntityByIndex, +#else CL_GetEntityByIndex, +#endif R_BeamGetEntity, CL_GetWaterEntity, CL_AddVisibleEntity, @@ -295,7 +299,11 @@ static ref_api_t gEngfuncs = Mod_ForName, pfnMod_Extradata, +#if XASH_OPT + pfnCL_ModelHandle, +#else CL_ModelHandle, +#endif pfnMod_GetCurrentLoadingModel, pfnMod_SetCurrentLoadingModel, diff --git a/engine/client/s_vox.c b/engine/client/s_vox.c index 38047960..745b7cb0 100644 --- a/engine/client/s_vox.c +++ b/engine/client/s_vox.c @@ -466,8 +466,14 @@ void VOX_LoadSound( channel_t *pchan, const char *pszin ) // lookup actual string in g_Sentences, // set pointer to string data +#ifdef XASH_PSP + if( pszin[0] == '#' ) + psz = VOX_LookupString( pszin + 1, NULL ); + else + psz = VOX_LookupString( pszin, NULL ); +#else psz = VOX_LookupString( pszin, NULL ); - +#endif if( !psz ) { Con_DPrintf( S_ERROR "VOX_LoadSound: no such sentence %s\n", pszin ); diff --git a/engine/common/base_cmd.c b/engine/common/base_cmd.c index b1685626..745f6624 100644 --- a/engine/common/base_cmd.c +++ b/engine/common/base_cmd.c @@ -17,6 +17,7 @@ GNU General Public License for more details. #include "base_cmd.h" #include "cdll_int.h" +#ifdef XASH_HASHED_VARS // TODO: use another hash function, as COM_HashKey depends on string length #define HASH_SIZE 128 // 128 * 4 * 4 == 2048 bytes static base_command_hashmap_t *hashed_cmds[HASH_SIZE]; @@ -285,3 +286,5 @@ void BaseCmd_Test_f( void ) Con_Printf( "BaseCmd is valid\n" ); } } + +#endif // XASH_HASHED_VARS diff --git a/engine/common/build.c b/engine/common/build.c index 78f9c8f9..d9bcca82 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -93,6 +93,8 @@ const char *Q_buildos( void ) osname = "emscripten"; #elif XASH_DOS4GW osname = "DOS4GW"; +#elif XASH_PSP + osname = "psp"; #else #error "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug" #endif @@ -136,9 +138,9 @@ const char *Q_buildarch( void ) #else "l"; #endif -#elif XASH_MIPS && XASH_BIG_ENDIAN +#elif XASH_MIPS && defined XASH_BIG_ENDIAN archname = "mips"; -#elif XASH_MIPS && XASH_LITTLE_ENDIAN +#elif XASH_MIPS && defined XASH_LITTLE_ENDIAN archname = "mipsel"; #elif XASH_JS archname = "javascript"; diff --git a/engine/common/common.h b/engine/common/common.h index b23e3147..7a02284a 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -138,10 +138,13 @@ typedef enum #define CIN_MAIN 0 #define CIN_LOGO 1 +#if XASH_PSP +#define MAX_DECALS 256 // touching TE_DECAL messages, etc +#define MAX_STATIC_ENTITIES 256 // static entities that moved on the client when level is spawn +#else #if XASH_LOW_MEMORY == 0 #define MAX_DECALS 512 // touching TE_DECAL messages, etc #define MAX_STATIC_ENTITIES 3096 // static entities that moved on the client when level is spawn - #elif XASH_LOW_MEMORY == 2 #define MAX_DECALS 256 // touching TE_DECAL messages, etc #define MAX_STATIC_ENTITIES 32 // static entities that moved on the client when level is spawn @@ -149,6 +152,7 @@ typedef enum #define MAX_DECALS 512 // touching TE_DECAL messages, etc #define MAX_STATIC_ENTITIES 128 // static entities that moved on the client when level is spawn #endif +#endif // filesystem flags #define FS_STATIC_PATH ( 1U << 0 ) // FS_ClearSearchPath will be ignore this path @@ -240,7 +244,9 @@ typedef struct gameinfo_s int max_tents; // min temp ents is 300, max is 2048 int max_beams; // min beams is 64, max beams is 512 int max_particles; // min particles is 4096, max particles is 32768 - +#ifdef XASH_PSP + char game_dll_psp[64]; +#endif char game_dll_linux[64]; // custom path for game.dll char game_dll_osx[64]; // custom path for game.dll @@ -871,7 +877,12 @@ int R_CreateDecalList( struct decallist_s *pList ); void R_ClearAllDecals( void ); void CL_ClearStaticEntities( void ); qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position ); +#if XASH_OPT +#define CL_GetEntityByIndex( index ) ( ( !clgame.entities ) ? NULL : ( ( ( index ) < 0 || ( index ) >= clgame.maxEntities ) ? NULL : ( ( ( index ) == 0 ) ? clgame.entities : ( clgame.entities + ( index ) ) ) ) ) +struct cl_entity_s *pfnCL_GetEntityByIndex( int index ); +#else struct cl_entity_s *CL_GetEntityByIndex( int index ); +#endif struct player_info_s *CL_GetPlayerInfo( int playerIndex ); void CL_ServerCommand( qboolean reliable, char *fmt, ... ) _format( 2 ); void CL_HudMessage( const char *pMessage ); diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 946e2fed..c38919a9 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -23,6 +23,10 @@ GNU General Public License for more details. #elif XASH_DOS4GW #include #include +#elif XASH_PSP +#include +#include +#include #else #include #include @@ -80,16 +84,25 @@ typedef struct wadtype_s struct file_s { +#if XASH_PSP + SceUID handle; // psp file descriptor +#else int handle; // file descriptor +#endif fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode) fs_offset_t position; // current position in the file fs_offset_t offset; // offset into the package (0 if external file) int ungetc; // single stored character from ungetc, cleared to EOF when read +#if !XASH_PSP /* unused */ time_t filetime; // pak, wad or real filetime +#endif // contents buffer fs_offset_t buff_ind, buff_len; // buffer current index and length byte buff[FILE_BUFF_SIZE]; // intermediate buffer #ifdef XASH_REDUCE_FD +#if XASH_PSP + qboolean backup_hold; +#endif const char *backup_path; fs_offset_t backup_position; uint backup_options; @@ -110,7 +123,11 @@ struct wfile_s typedef struct pack_s { string filename; +#if XASH_PSP + SceUID handle; +#else int handle; +#endif int numfiles; time_t filetime; // common for all packed files dpackfile_t *files; @@ -128,7 +145,11 @@ typedef struct zipfile_s typedef struct zip_s { string filename; +#if XASH_PSP + SceUID handle; +#else int handle; +#endif int numfiles; time_t filetime; zipfile_t *files; @@ -168,7 +189,20 @@ static void FS_EnsureOpenFile( file_t *file ) if( file && !file->backup_path ) return; - +#if XASH_PSP + if( fs_last_readfile && (fs_last_readfile->handle != -1) && (fs_last_readfile->backup_hold == false) ) + { + fs_last_readfile->backup_position = sceIoLseek( fs_last_readfile->handle, 0, PSP_SEEK_CUR ); + sceIoClose( fs_last_readfile->handle ); + fs_last_readfile->handle = -1; + } + fs_last_readfile = file; + if( file && (file->handle == -1) ) + { + file->handle = sceIoOpen( file->backup_path, file->backup_options, 0666 ); + sceIoLseek( file->handle, file->backup_position, PSP_SEEK_SET ); + } +#else if( fs_last_readfile && (fs_last_readfile->handle != -1) ) { fs_last_readfile->backup_position = lseek( fs_last_readfile->handle, 0, SEEK_CUR ); @@ -181,13 +215,23 @@ static void FS_EnsureOpenFile( file_t *file ) file->handle = open( file->backup_path, file->backup_options ); lseek( file->handle, file->backup_position, SEEK_SET ); } +#endif } static void FS_EnsureOpenZip( zip_t *zip ) { if( fs_last_zip == zip ) return; - +#if XASH_PSP + if( fs_last_zip && (fs_last_zip->handle != -1) ) + { + sceIoClose( fs_last_zip->handle ); + fs_last_zip->handle = -1; + } + fs_last_zip = zip; + if( zip && (zip->handle == -1) ) + zip->handle = sceIoOpen( zip->filename, PSP_O_RDONLY, 0666 ); +#else if( fs_last_zip && (fs_last_zip->handle != -1) ) { close( fs_last_zip->handle ); @@ -196,6 +240,7 @@ static void FS_EnsureOpenZip( zip_t *zip ) fs_last_zip = zip; if( zip && (zip->handle == -1) ) zip->handle = open( zip->filename, O_RDONLY|O_BINARY ); +#endif } static void FS_BackupFileName( file_t *file, const char *path, uint options ) @@ -207,7 +252,11 @@ static void FS_BackupFileName( file_t *file, const char *path, uint options ) if( file == fs_last_readfile ) FS_EnsureOpenFile( NULL ); } +#if XASH_PSP + else if( options == PSP_O_RDONLY ) +#else else if( options == O_RDONLY || options == (O_RDONLY|O_BINARY) ) +#endif { file->backup_path = copystring( path ); file->backup_options = options; @@ -325,6 +374,9 @@ static void listdirectory( stringlist_t *list, const char *path, qboolean lowerc char pattern[4096]; struct _finddata_t n_file; int hFile; +#elif XASH_PSP + SceUID dir; + SceIoDirent entry; #else DIR *dir; struct dirent *entry; @@ -343,6 +395,20 @@ static void listdirectory( stringlist_t *list, const char *path, qboolean lowerc while( _findnext( hFile, &n_file ) == 0 ) stringlistappend( list, n_file.name ); _findclose( hFile ); +#elif XASH_PSP + if( ( dir = sceIoDopen( path ) ) < 0 ) + return; + + // iterate through the directory + while( 1 ) + { + // zero the dirent, to avoid possible problems with sceIoDread + memset( &entry, 0, sizeof( SceIoDirent ) ); + if( !sceIoDread( dir, &entry ) ) + break; + stringlistappend( list, entry.d_name ); + } + sceIoDclose( dir ); #else if( !( dir = opendir( path ) ) ) return; @@ -405,7 +471,7 @@ static const char *FS_FixFileCase( const char *path ) out[i] = 0; return out; -#elif !XASH_WIN32 && !XASH_IOS // assume case insensitive +#elif !XASH_WIN32 && !XASH_IOS && !XASH_PSP // assume case insensitive DIR *dir; struct dirent *entry; char path2[PATH_MAX], *fname; @@ -524,7 +590,11 @@ void FS_CreatePath( char *path ) // create the directory save = *ofs; *ofs = 0; +#if XASH_PSP + sceIoMkdir( path, 0777 ); +#else _mkdir( path ); +#endif *ofs = save; } } @@ -585,14 +655,20 @@ of the list so they override previous pack files. pack_t *FS_LoadPackPAK( const char *packfile, int *error ) { dpackheader_t header; +#if XASH_PSP + SceUID packhandle; +#else int packhandle; +#endif int i, numpackfiles; pack_t *pack; dpackfile_t *info; - +#if XASH_PSP + packhandle = sceIoOpen( packfile, PSP_O_RDONLY, 0666 ); +#else packhandle = open( packfile, O_RDONLY|O_BINARY ); - -#if !XASH_WIN32 +#endif +#if !XASH_WIN32 && !XASH_PSP if( packhandle < 0 ) { const char *fpackfile = FS_FixFileCase( packfile ); @@ -607,14 +683,20 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) if( error ) *error = PAK_LOAD_COULDNT_OPEN; return NULL; } - +#if XASH_PSP + sceIoRead( packhandle, (void *)&header, sizeof( header )); +#else read( packhandle, (void *)&header, sizeof( header )); - +#endif if( header.ident != IDPACKV1HEADER ) { Con_Reportf( "%s is not a packfile. Ignored.\n", packfile ); if( error ) *error = PAK_LOAD_BAD_HEADER; +#if XASH_PSP + sceIoClose( packhandle ); +#else close( packhandle ); +#endif return NULL; } @@ -622,7 +704,11 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) { Con_Reportf( S_ERROR "%s has an invalid directory size. Ignored.\n", packfile ); if( error ) *error = PAK_LOAD_BAD_FOLDERS; +#if XASH_PSP + sceIoClose( packhandle ); +#else close( packhandle ); +#endif return NULL; } @@ -632,7 +718,11 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) { Con_Reportf( S_ERROR "%s has too many files ( %i ). Ignored.\n", packfile, numpackfiles ); if( error ) *error = PAK_LOAD_TOO_MANY_FILES; +#if XASH_PSP + sceIoClose( packhandle ); +#else close( packhandle ); +#endif return NULL; } @@ -640,18 +730,33 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) { Con_Reportf( "%s has no files. Ignored.\n", packfile ); if( error ) *error = PAK_LOAD_NO_FILES; +#if XASH_PSP + sceIoClose( packhandle ); +#else close( packhandle ); +#endif return NULL; } info = (dpackfile_t *)Mem_Malloc( fs_mempool, sizeof( *info ) * numpackfiles ); +#if XASH_PSP + sceIoLseek( packhandle, header.dirofs, PSP_SEEK_SET ); +#else lseek( packhandle, header.dirofs, SEEK_SET ); - +#endif +#if XASH_PSP + if( header.dirlen != sceIoRead( packhandle, (void *)info, header.dirlen ) ) +#else if( header.dirlen != read( packhandle, (void *)info, header.dirlen )) +#endif { Con_Reportf( "%s is an incomplete PAK, not loading\n", packfile ); if( error ) *error = PAK_LOAD_CORRUPTED; +#if XASH_PSP + sceIoClose( packhandle ); +#else close( packhandle ); +#endif Mem_Free( info ); return NULL; } @@ -669,7 +774,11 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) #ifdef XASH_REDUCE_FD // will reopen when needed - close(pack->handle); +#if XASH_PSP + sceIoClose( packhandle ); +#else + close( packhandle ); +#endif pack->handle = -1; #endif @@ -689,10 +798,12 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) zipfile_t *info = NULL; char filename_buffer[MAX_SYSPATH]; zip_t *zip = (zip_t *)Mem_Calloc( fs_mempool, sizeof( zip_t ) ); - +#if XASH_PSP + zip->handle = sceIoOpen( zipfile, PSP_O_RDONLY, 0666 ); +#else zip->handle = open( zipfile, O_RDONLY|O_BINARY ); - -#if !XASH_WIN32 +#endif +#if !XASH_WIN32 && !XASH_PSP if( zip->handle < 0 ) { const char *fzipfile = FS_FixFileCase( zipfile ); @@ -711,9 +822,11 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) Zip_Close( zip ); return NULL; } - +#if XASH_PSP + length = sceIoLseek( zip->handle, 0, PSP_SEEK_END ); +#else length = lseek( zip->handle, 0, SEEK_END ); - +#endif if( length > UINT_MAX ) { Con_Reportf( S_ERROR "%s bigger than 4GB.\n", zipfile ); @@ -724,11 +837,13 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) Zip_Close( zip ); return NULL; } - +#if XASH_PSP + sceIoLseek( zip->handle, 0, PSP_SEEK_SET ); + sceIoRead( zip->handle, &signature, sizeof( signature ) ); +#else lseek( zip->handle, 0, SEEK_SET ); - read( zip->handle, &signature, sizeof( signature ) ); - +#endif if( signature == ZIP_HEADER_EOCD ) { Con_Reportf( S_WARN "%s has no files. Ignored.\n", zipfile ); @@ -752,14 +867,22 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) } // Find oecd +#if XASH_PSP + sceIoLseek( zip->handle, 0, PSP_SEEK_SET ); +#else lseek( zip->handle, 0, SEEK_SET ); +#endif filepos = length; while ( filepos > 0 ) { +#if XASH_PSP + sceIoLseek( zip->handle, filepos, PSP_SEEK_SET ); + sceIoRead( zip->handle, &signature, sizeof( signature ) ); +#else lseek( zip->handle, filepos, SEEK_SET ); read( zip->handle, &signature, sizeof( signature ) ); - +#endif if( signature == ZIP_HEADER_EOCD ) break; @@ -776,19 +899,27 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) Zip_Close( zip ); return NULL; } +#if XASH_PSP + sceIoRead( zip->handle, &header_eocd, sizeof( zip_header_eocd_t ) ); + // Move to CDF start + sceIoLseek( zip->handle, header_eocd.central_directory_offset, PSP_SEEK_SET ); +#else read( zip->handle, &header_eocd, sizeof( zip_header_eocd_t ) ); // Move to CDF start lseek( zip->handle, header_eocd.central_directory_offset, SEEK_SET ); - +#endif // Calc count of files in archive info = (zipfile_t *)Mem_Calloc( fs_mempool, sizeof( zipfile_t ) * header_eocd.total_central_directory_record ); for( i = 0; i < header_eocd.total_central_directory_record; i++ ) { +#if XASH_PSP + sceIoRead( zip->handle, &header_cdf, sizeof( header_cdf ) ); +#else read( zip->handle, &header_cdf, sizeof( header_cdf ) ); - +#endif if( header_cdf.signature != ZIP_HEADER_CDF ) { Con_Reportf( S_ERROR "CDF signature mismatch in %s. Zip file corrupted.\n", zipfile ); @@ -804,7 +935,11 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) if( header_cdf.uncompressed_size && header_cdf.filename_len && ( header_cdf.filename_len < MAX_SYSPATH ) ) { memset( &filename_buffer, '\0', MAX_SYSPATH ); +#if XASH_PSP + sceIoRead( zip->handle, &filename_buffer, header_cdf.filename_len ); +#else read( zip->handle, &filename_buffer, header_cdf.filename_len ); +#endif Q_strncpy( info[numpackfiles].name, filename_buffer, MAX_SYSPATH ); info[numpackfiles].size = header_cdf.uncompressed_size; @@ -813,6 +948,15 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) numpackfiles++; } else +#if XASH_PSP + sceIoLseek( zip->handle, header_cdf.filename_len, PSP_SEEK_CUR ); + + if( header_cdf.extrafield_len ) + sceIoLseek( zip->handle, header_cdf.extrafield_len, PSP_SEEK_CUR ); + + if( header_cdf.file_commentary_len ) + sceIoLseek( zip->handle, header_cdf.file_commentary_len, PSP_SEEK_CUR ); +#else lseek( zip->handle, header_cdf.filename_len, SEEK_CUR ); if( header_cdf.extrafield_len ) @@ -820,15 +964,20 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) if( header_cdf.file_commentary_len ) lseek( zip->handle, header_cdf.file_commentary_len, SEEK_CUR ); +#endif } // recalculate offsets for( i = 0; i < numpackfiles; i++ ) { zip_header_t header; - +#if XASH_PSP + sceIoLseek( zip->handle, info[i].offset, PSP_SEEK_SET ); + sceIoRead( zip->handle, &header, sizeof( header ) ); +#else lseek( zip->handle, info[i].offset, SEEK_SET ); read( zip->handle, &header, sizeof( header ) ); +#endif info[i].flags = header.compression_flags; info[i].offset = info[i].offset + header.filename_len + header.extrafield_len + sizeof( header ); } @@ -840,8 +989,13 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) #ifdef XASH_REDUCE_FD // will reopen when needed - close(zip->handle); +#if XASH_PSP + sceIoClose( zip->handle ); zip->handle = -1; +#else + close( zip->handle ); + zip->handle = -1; +#endif #endif if( error ) @@ -858,10 +1012,13 @@ void Zip_Close( zip_t *zip ) Mem_Free( zip->files ); FS_EnsureOpenZip( NULL ); - +#if XASH_PSP + if( zip->handle >= 0 ) + sceIoClose( zip->handle ); +#else if( zip->handle >= 0 ) close( zip->handle ); - +#endif Mem_Free( zip ); } @@ -885,10 +1042,13 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game file = &search->zip->files[index]; FS_EnsureOpenZip( search->zip ); - +#if XASH_PSP + if( sceIoLseek( search->zip->handle, file->offset, PSP_SEEK_SET ) == -1 ) + return NULL; +#else if( lseek( search->zip->handle, file->offset, SEEK_SET ) == -1 ) return NULL; - +#endif /*if( read( search->zip->handle, &header, sizeof( header ) ) < 0 ) return NULL; @@ -902,8 +1062,11 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game { decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 ); decompressed_buffer[file->size] = '\0'; - +#if XASH_PSP + sceIoRead( search->zip->handle, decompressed_buffer, file->size ); +#else read( search->zip->handle, decompressed_buffer, file->size ); +#endif #if 0 CRC32_Init( &test_crc ); CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size ); @@ -927,9 +1090,11 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game compressed_buffer = Mem_Malloc( fs_mempool, file->compressed_size + 1 ); decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 ); decompressed_buffer[file->size] = '\0'; - +#if XASH_PSP + sceIoRead( search->zip->handle, compressed_buffer, file->compressed_size ); +#else read( search->zip->handle, compressed_buffer, file->compressed_size ); - +#endif memset( &decompress_stream, 0, sizeof( decompress_stream ) ); decompress_stream.total_in = decompress_stream.avail_in = file->compressed_size; @@ -1268,12 +1433,15 @@ void FS_AddGameHierarchy( const char *dir, uint flags ) FS_AddGameDirectory( va( "%s/%s/", host.rodir, dir ), newFlags ); FS_AllowDirectPaths( false ); } - +#if !XASH_PSP if( isGameDir ) FS_AddGameDirectory( va( "%s/downloaded/", dir ), FS_NOWRITE_PATH | FS_CUSTOM_PATH ); +#endif FS_AddGameDirectory( va( "%s/", dir ), flags ); +#if !XASH_PSP if( isGameDir ) FS_AddGameDirectory( va( "%s/custom/", dir ), FS_NOWRITE_PATH | FS_CUSTOM_PATH ); +#endif } /* @@ -1302,8 +1470,13 @@ void FS_ClearSearchPath( void ) { if( search->pack->files ) Mem_Free( search->pack->files ); +#if XASH_PSP + if( search->pack->handle >= 0 ) + sceIoClose( search->pack->handle ); +#else if( search->pack->handle >= 0 ) close( search->pack->handle ); +#endif Mem_Free( search->pack ); } @@ -1477,8 +1650,10 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) if( Q_strlen( GameInfo->game_dll_osx )) FS_Printf( f, "gamedll_osx\t\t\"%s\"\n", GameInfo->game_dll_osx ); - - +#if XASH_PSP + if( Q_strlen( GameInfo->game_dll_psp )) + FS_Printf( f, "gamedll_psp\t\t\"%s\"\n", GameInfo->game_dll_psp ); +#endif if( Q_strlen( GameInfo->iconpath )) FS_Printf( f, "icon\t\t\"%s\"\n", GameInfo->iconpath ); @@ -1545,7 +1720,9 @@ void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir ) Q_strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll )); Q_strncpy( GameInfo->game_dll_linux, "dlls/hl.so", sizeof( GameInfo->game_dll_linux )); Q_strncpy( GameInfo->game_dll_osx, "dlls/hl.dylib", sizeof( GameInfo->game_dll_osx )); - +#if XASH_PSP + Q_strncpy( GameInfo->game_dll_psp, "dlls/server.prx", sizeof( GameInfo->game_dll_psp )); +#endif // .ico path Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath )); @@ -1562,6 +1739,9 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool { char *pfile = (char*) buf; qboolean found_linux = false, found_osx = false; +#if XASH_PSP + qboolean found_psp = false; +#endif string token; while(( pfile = COM_ParseFile( pfile, token )) != NULL ) @@ -1617,6 +1797,14 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool pfile = COM_ParseFile( pfile, GameInfo->game_dll_osx ); found_osx = true; } +#if XASH_PSP + // valid for both + else if( !Q_stricmp( token, "gamedll_psp" )) + { + pfile = COM_ParseFile( pfile, GameInfo->game_dll_psp ); + found_psp = true; + } +#endif // valid for both else if( !Q_stricmp( token, "icon" )) { @@ -1759,7 +1947,8 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool } } } - + +#if XASH_PSP if( !found_linux || !found_osx ) { // just replace extension from dll to so/dylib @@ -1773,7 +1962,10 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool if( !found_osx ) Q_snprintf( GameInfo->game_dll_osx, sizeof( GameInfo->game_dll_osx ), "%s.dylib", gamedll ); } - +#else + if( !found_psp ) + Q_snprintf( GameInfo->game_dll_psp, sizeof( GameInfo->game_dll_psp ), "dlls/server.prx" ); // force set +#endif // make sure what gamedir is really exist if( !FS_SysFolderExists( va( "%s"PATH_SPLITTER"%s", host.rootdir, GameInfo->falldir ))) GameInfo->falldir[0] = '\0'; @@ -2027,7 +2219,7 @@ void FS_Init( void ) Cmd_AddCommand( "fs_path", FS_Path_f, "show filesystem search pathes" ); Cmd_AddCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" ); -#if !XASH_WIN32 +#if !XASH_WIN32 && !XASH_PSP if( Sys_CheckParm( "-casesensitive" ) ) fs_caseinsensitive = false; @@ -2156,12 +2348,30 @@ Internal function used to determine filetime */ static int FS_SysFileTime( const char *filename ) { +#if XASH_PSP + SceIoStat buf; + struct tm libc_tm; + + if ( sceIoGetstat( filename, &buf ) < 0 ) + return -1; + + libc_tm.tm_year = buf.st_mtime.year; + libc_tm.tm_mon = buf.st_mtime.month; + libc_tm.tm_mday = buf.st_mtime.day; + libc_tm.tm_hour = buf.st_mtime.hour; + libc_tm.tm_min = buf.st_mtime.minute; + libc_tm.tm_sec = buf.st_mtime.second; + libc_tm.tm_isdst = -1; + + return mktime( &libc_tm ); +#else struct stat buf; if( stat( filename, &buf ) == -1 ) return -1; return buf.st_mtime; +#endif } /* @@ -2176,7 +2386,53 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode ) file_t *file; int mod, opt; uint ind; +#if XASH_PSP + qboolean backup_hold = false; + // Hold file in open state + if( mode[0] == '*' ) + { + backup_hold = true; + mode++; + } + + // Parse the mode string + switch( mode[0] ) + { + case 'r': // read + mod = PSP_O_RDONLY; + opt = 0; + break; + case 'w': // write + mod = PSP_O_WRONLY; + opt = PSP_O_CREAT | PSP_O_TRUNC; + break; + case 'a': // append + mod = PSP_O_WRONLY; + opt = PSP_O_CREAT | PSP_O_APPEND; + break; + case 'e': // edit + mod = PSP_O_WRONLY; + opt = PSP_O_CREAT; + break; + default: + return NULL; + } + + for( ind = 1; mode[ind] != '\0'; ind++ ) + { + switch( mode[ind] ) + { + case '+': + mod = PSP_O_RDWR; + break; + case 'b': // not used + break; + default: + break; + } + } +#else // Parse the mode string switch( mode[0] ) { @@ -2214,14 +2470,23 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode ) break; } } - +#endif file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file )); +#if !XASH_PSP file->filetime = FS_SysFileTime( filepath ); +#endif file->ungetc = EOF; - +#if XASH_PSP + file->handle = sceIoOpen( filepath, mod|opt, 0666 ); +#else file->handle = open( filepath, mod|opt, 0666 ); +#endif -#if !XASH_WIN32 +#if XASH_PSP + file->backup_hold = backup_hold; + if( file->handle >= 0 ) + FS_BackupFileName( file, filepath, mod|opt ); +#elif !XASH_WIN32 if( file->handle < 0 ) { const char *ffilepath = FS_FixFileCase( filepath ); @@ -2234,22 +2499,29 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode ) FS_BackupFileName( file, filepath, mod|opt ); #endif + if( file->handle < 0 ) { Mem_Free( file ); return NULL; } - +#if XASH_PSP + file->real_length = sceIoLseek( file->handle, 0, PSP_SEEK_END ); +#else file->real_length = lseek( file->handle, 0, SEEK_END ); - +#endif // uncomment do disable write //if( opt & O_CREAT ) // return NULL; // For files opened in append mode, we start at the end of the file if( opt & O_APPEND ) file->position = file->real_length; +#if XASH_PSP + else sceIoLseek( file->handle, 0, PSP_SEEK_SET ); +#else else lseek( file->handle, 0, SEEK_SET ); +#endif return file; } @@ -2270,6 +2542,15 @@ static file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offse { file_t *file = (file_t *)Mem_Calloc( fs_mempool, sizeof( file_t )); #ifndef XASH_REDUCE_FD +#if XASH_PSP + file->handle = sceIoOpen( syspath, PSP_O_RDONLY, 0666 ); + + if( sceIoLseek( file->handle, offset, SEEK_SET ) == -1 ) + { + Mem_Free( file ); + return NULL; + } +#else /* XASH_PSP */ #ifdef HAVE_DUP file->handle = dup( handle ); #else @@ -2281,13 +2562,18 @@ static file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offse Mem_Free( file ); return NULL; } - -#else +#endif /* XASH_PSP */ +#else /* XASH_REDUCE_FD */ file->backup_position = offset; file->backup_path = copystring( syspath ); +#if XASH_PSP + file->backup_hold = false; + file->backup_options = PSP_O_RDONLY; +#else file->backup_options = O_RDONLY|O_BINARY; - file->handle = -1; #endif + file->handle = -1; +#endif /* XASH_REDUCE_FD */ file->real_length = len; file->offset = offset; @@ -2349,6 +2635,11 @@ qboolean FS_SysFileExists( const char *path, qboolean caseinsensitive ) close( desc ); return true; +#elif XASH_PSP + SceIoStat buf; + if ( sceIoGetstat( path, &buf ) < 0 ) + return false; + return FIO_S_ISREG( buf.st_mode ); #else int ret; struct stat buf; @@ -2383,6 +2674,23 @@ qboolean FS_SysFolderExists( const char *path ) DWORD dwFlags = GetFileAttributes( path ); return ( dwFlags != -1 ) && ( dwFlags & FILE_ATTRIBUTE_DIRECTORY ); +#elif XASH_PSP + SceUID dir = sceIoDopen( path ); + + if( dir >= 0 ) + { + sceIoDclose( dir ); + return true; + } + else if( ( dir == 0x80010002 ) || ( dir == 0x80010014 ) ) + { + return false; + } + else + { + Con_Reportf( S_ERROR "FS_SysFolderExists: problem while opening dir: %#010x\n", dir ); + return false; + } #else DIR *dir = opendir( path ); @@ -2640,7 +2948,11 @@ file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ) char real_path[MAX_SYSPATH]; // open the file on disk directly +#if XASH_PSP /* Fixed double slashes */ + Q_sprintf( real_path, "%s%s", fs_writedir, filepath ); +#else Q_sprintf( real_path, "%s/%s", fs_writedir, filepath ); +#endif FS_CreatePath( real_path );// Create directories up to the file return FS_SysOpen( real_path, mode ); } @@ -2663,7 +2975,11 @@ int FS_Close( file_t *file ) FS_BackupFileName( file, NULL, 0 ); if( file->handle >= 0 ) +#if XASH_PSP + if( sceIoClose( file->handle ) ) +#else if( close( file->handle )) +#endif return EOF; Mem_Free( file ); @@ -2685,15 +3001,22 @@ fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize ) // if necessary, seek to the exact file position we're supposed to be if( file->buff_ind != file->buff_len ) +#if XASH_PSP + sceIoLseek( file->handle, file->buff_ind - file->buff_len, PSP_SEEK_CUR ); +#else lseek( file->handle, file->buff_ind - file->buff_len, SEEK_CUR ); - +#endif // purge cached data FS_Purge( file ); - +#if XASH_PSP + // write the buffer and update the position + result = sceIoWrite( file->handle, data, (fs_offset_t)datasize ); + file->position = sceIoLseek( file->handle, 0, PSP_SEEK_CUR ); +#else // write the buffer and update the position result = write( file->handle, data, (fs_offset_t)datasize ); file->position = lseek( file->handle, 0, SEEK_CUR ); - +#endif if( file->real_length < file->position ) file->real_length = file->position; @@ -2752,9 +3075,13 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize ) { if( count > (fs_offset_t)buffersize ) count = (fs_offset_t)buffersize; +#if XASH_PSP + sceIoLseek( file->handle, file->offset + file->position, PSP_SEEK_SET ); + nb = sceIoRead (file->handle, &((byte *)buffer)[done], count ); +#else lseek( file->handle, file->offset + file->position, SEEK_SET ); nb = read (file->handle, &((byte *)buffer)[done], count ); - +#endif if( nb > 0 ) { done += nb; @@ -2767,9 +3094,13 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize ) { if( count > (fs_offset_t)sizeof( file->buff )) count = (fs_offset_t)sizeof( file->buff ); +#if XASH_PSP + sceIoLseek( file->handle, file->offset + file->position, PSP_SEEK_SET ); + nb = sceIoRead( file->handle, file->buff, count ); +#else lseek( file->handle, file->offset + file->position, SEEK_SET ); nb = read( file->handle, file->buff, count ); - +#endif if( nb > 0 ) { file->buff_len = nb; @@ -2843,8 +3174,11 @@ int FS_VPrintf( file_t *file, const char *format, va_list ap ) Mem_Free( tempbuff ); buff_size *= 2; } - +#if XASH_PSP + len = sceIoWrite( file->handle, tempbuff, len ); +#else len = write( file->handle, tempbuff, len ); +#endif Mem_Free( tempbuff ); return len; @@ -2956,9 +3290,13 @@ int FS_Seek( file_t *file, fs_offset_t offset, int whence ) FS_EnsureOpenFile( file ); // Purge cached data FS_Purge( file ); - +#if XASH_PSP + if( sceIoLseek( file->handle, file->offset + offset, PSP_SEEK_SET ) == -1 ) + return -1; +#else if( lseek( file->handle, file->offset + offset, SEEK_SET ) == -1 ) return -1; +#endif file->position = offset; return 0; diff --git a/engine/common/host.c b/engine/common/host.c index aa6df9c6..1e8fe3c6 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -874,10 +874,10 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha // init host state machine COM_InitHostState(); - +#ifdef XASH_HASHED_VARS // init hashed commands BaseCmd_Init(); - +#endif // startup cmds and cvars subsystem Cmd_Init(); Cvar_Init(); @@ -908,6 +908,8 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha #if TARGET_OS_IOS const char *IOS_GetDocsDir(); Q_strncpy( host.rootdir, IOS_GetDocsDir(), sizeof(host.rootdir) ); +#elif XASH_PSP + COM_ExtractFilePath( argv[0], host.rootdir ); #elif XASH_SDL == 2 char *szBasePath; @@ -1009,7 +1011,11 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa host_serverstate = Cvar_Get( "host_serverstate", "0", FCVAR_READ_ONLY, "displays current server state" ); host_maxfps = Cvar_Get( "fps_max", "72", FCVAR_ARCHIVE, "host fps upper limit" ); host_framerate = Cvar_Get( "host_framerate", "0", 0, "locks frame timing to this value in seconds" ); +#if XASH_PSP + host_sleeptime = Cvar_Get( "sleeptime", "0", FCVAR_ARCHIVE, "milliseconds to sleep for each frame. higher values reduce fps accuracy" ); +#else host_sleeptime = Cvar_Get( "sleeptime", "1", FCVAR_ARCHIVE, "milliseconds to sleep for each frame. higher values reduce fps accuracy" ); +#endif host_gameloaded = Cvar_Get( "host_gameloaded", "0", FCVAR_READ_ONLY, "inidcates a loaded game.dll" ); host_clientloaded = Cvar_Get( "host_clientloaded", "0", FCVAR_READ_ONLY, "inidcates a loaded client.dll" ); host_limitlocal = Cvar_Get( "host_limitlocal", "0", 0, "apply cl_cmdrate and rate to loopback connection" ); diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index 358de8e3..08532b3d 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -101,7 +101,22 @@ static const loadpixformat_t load_null[] = { { NULL, NULL, NULL, IL_HINT_NO } }; - +#if XASH_PSP +static const loadpixformat_t load_game[] = +{ +{ "%s%s.%s", "mip", Image_LoadMIP, IL_HINT_NO }, // hl textures from wad or buffer +//{ "%s%s.%s", "dds", Image_LoadDDS, IL_HINT_NO }, // dds for world and studio models +{ "%s%s.%s", "tga", Image_LoadTGA, IL_HINT_NO }, // hl vgui menus +{ "%s%s.%s", "bmp", Image_LoadBMP, IL_HINT_NO }, // WON menu images +//{ "%s%s.%s", "png", Image_LoadPNG, IL_HINT_NO }, // NightFire 007 menus +{ "%s%s.%s", "mdl", Image_LoadMDL, IL_HINT_HL }, // hl studio model skins +{ "%s%s.%s", "spr", Image_LoadSPR, IL_HINT_HL }, // hl sprite frames +{ "%s%s.%s", "lmp", Image_LoadLMP, IL_HINT_NO }, // hl menu images (cached.wad etc) +{ "%s%s.%s", "fnt", Image_LoadFNT, IL_HINT_HL }, // hl console font (fonts.wad etc) +{ "%s%s.%s", "pal", Image_LoadPAL, IL_HINT_NO }, // install studio\sprite palette +{ NULL, NULL, NULL, IL_HINT_NO } +}; +#else static const loadpixformat_t load_game[] = { { "%s%s.%s", "dds", Image_LoadDDS, IL_HINT_NO }, // dds for world and studio models @@ -116,7 +131,7 @@ static const loadpixformat_t load_game[] = { "%s%s.%s", "pal", Image_LoadPAL, IL_HINT_NO }, // install studio\sprite palette { NULL, NULL, NULL, IL_HINT_NO } }; - +#endif /* ============================================================================= diff --git a/engine/common/launcher.c b/engine/common/launcher.c index 08fedf6b..b37968eb 100644 --- a/engine/common/launcher.c +++ b/engine/common/launcher.c @@ -34,6 +34,11 @@ GNU General Public License for more details. static char szGameDir[128]; // safe place to keep gamedir static int g_iArgc; static char **g_pszArgv; +#if XASH_PSP +#define MAX_NARGVS 50 +static int g_fArgc; +static char *g_fArgv[MAX_NARGVS]; +#endif void Launcher_ChangeGame( const char *progname ) { @@ -70,6 +75,7 @@ int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int n free( szArgv ); } #endif + int main( int argc, char** argv ) { char gamedir_buf[32] = ""; @@ -102,15 +108,38 @@ int main( int argc, char** argv ) #endif #endif +#if XASH_PSP + g_iArgc = argc; + g_pszArgv = argv; + if(argc <= 1) + { + g_fArgc = 0; + g_fArgv[g_fArgc++] = argv[0]; // for get root directory path + Platform_ReadCmd( "start.cmd", &g_fArgc, g_fArgv ); + if(g_fArgc > 1) + { + g_iArgc = g_fArgc; + g_pszArgv = g_fArgv; + } + } +#else g_iArgc = argc; g_pszArgv = argv; +#endif #if XASH_IOS { void IOS_LaunchDialog( void ); IOS_LaunchDialog(); } #endif + +#if XASH_PSP + Host_Main( g_iArgc, g_pszArgv, gamedir, 0, &Launcher_ChangeGame ); + Sys_Quit( ); + return 0; +#else return Host_Main( g_iArgc, g_pszArgv, gamedir, 0, &Launcher_ChangeGame ); +#endif } #endif diff --git a/engine/common/lib_common.c b/engine/common/lib_common.c index 76080657..9788053b 100644 --- a/engine/common/lib_common.c +++ b/engine/common/lib_common.c @@ -63,7 +63,7 @@ const char *COM_OffsetNameForFunction( void *function ) static void COM_GenerateCommonLibraryName( const char *name, const char *ext, char *out, size_t size ) { -#if ( XASH_WIN32 || XASH_LINUX || XASH_APPLE ) && XASH_X86 +#if (( XASH_WIN32 || XASH_LINUX || XASH_APPLE ) && XASH_X86) || XASH_PSP Q_snprintf( out, size, "%s.%s", name, ext ); #elif ( XASH_WIN32 || XASH_LINUX || XASH_APPLE ) Q_snprintf( out, size, "%s_%s.%s", name, Q_buildarch(), ext ); @@ -110,6 +110,8 @@ static void COM_GenerateServerLibraryPath( char *out, size_t size ) Q_strncpy( out, GI->game_dll, size ); #elif XASH_APPLE Q_strncpy( out, GI->game_dll_osx, size ); +#elif XASH_PSP + Q_strncpy( out, GI->game_dll_psp, size ); #else // XASH_LINUX Q_strncpy( out, GI->game_dll_linux, size ); #endif @@ -122,6 +124,8 @@ static void COM_GenerateServerLibraryPath( char *out, size_t size ) Q_strncpy( dllpath, GI->game_dll, sizeof( dllpath ) ); #elif XASH_APPLE Q_strncpy( dllpath, GI->game_dll_osx, sizeof( dllpath ) ); +#elif XASH_PSP + Q_strncpy( dllpath, GI->game_dll_psp, sizeof( dllpath ) ); #else // XASH_APPLE Q_strncpy( dllpath, GI->game_dll_linux, sizeof( dllpath ) ); #endif diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index 53a2f981..0fa02ed1 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -94,13 +94,14 @@ void MSG_Clear( sizebuf_t *sb ) sb->bOverflow = false; } +#if !XASH_PSP static qboolean MSG_Overflow( sizebuf_t *sb, int nBits ) { if( sb->iCurBit + nBits > sb->nDataBits ) sb->bOverflow = true; return sb->bOverflow; } - +#endif qboolean MSG_CheckOverflow( sizebuf_t *sb ) { return MSG_Overflow( sb, 0 ); @@ -135,7 +136,7 @@ void MSG_SeekToByte( sizebuf_t *sb, int bytePos ) { sb->iCurBit = bytePos << 3; } - +#if !XASH_PSP void MSG_WriteOneBit( sizebuf_t *sb, int nValue ) { if( !MSG_Overflow( sb, 1 )) @@ -146,7 +147,7 @@ void MSG_WriteOneBit( sizebuf_t *sb, int nValue ) sb->iCurBit++; } } - +#endif void MSG_WriteUBitLong( sizebuf_t *sb, uint curData, int numbits ) { Assert( numbits >= 0 && numbits <= 32 ); diff --git a/engine/common/net_buffer.h b/engine/common/net_buffer.h index 0d0af632..848c1d78 100644 --- a/engine/common/net_buffer.h +++ b/engine/common/net_buffer.h @@ -70,7 +70,26 @@ void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, in void MSG_Clear( sizebuf_t *sb ); // Bit-write functions +#if XASH_PSP +_inline qboolean MSG_Overflow( sizebuf_t *sb, int nBits ) +{ + if( sb->iCurBit + nBits > sb->nDataBits ) + sb->bOverflow = true; + return sb->bOverflow; +} +_inline void MSG_WriteOneBit( sizebuf_t *sb, int nValue ) +{ + if( !MSG_Overflow( sb, 1 )) + { + if( nValue ) sb->pData[sb->iCurBit>>3] |= BIT( sb->iCurBit & 7 ); + else sb->pData[sb->iCurBit>>3] &= ~BIT( sb->iCurBit & 7 ); + + sb->iCurBit++; + } +} +#else void MSG_WriteOneBit( sizebuf_t *sb, int nValue ); +#endif void MSG_WriteUBitLong( sizebuf_t *sb, uint curData, int numbits ); void MSG_WriteSBitLong( sizebuf_t *sb, int data, int numbits ); void MSG_WriteBitLong( sizebuf_t *sb, int data, int numbits, qboolean bSigned ); diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index c132b1bd..5f73818f 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -295,6 +295,17 @@ static const delta_field_t ent_fields[] = { NULL }, }; +#if XASH_OPT /* Direct access */ +#define D_event_t 0 +#define D_movevars_t 1 +#define D_usercmd_t 2 +#define D_clientdata_t 3 +#define D_weapon_data_t 4 +#define D_entity_state_t 5 +#define D_entity_state_player_t 6 +#define D_custom_entity_state_t 7 +#endif + static delta_info_t dt_info[] = { { "event_t", ev_fields, NUM_FIELDS( ev_fields ) }, @@ -799,9 +810,11 @@ void Delta_Init( void ) Delta_InitFields (); // initialize fields delta_init = true; - +#if XASH_OPT + dt = &dt_info[D_movevars_t]; +#else dt = Delta_FindStruct( "movevars_t" ); - +#endif Assert( dt != NULL ); if( dt->bInitialized ) return; // "movevars_t" already specified by user @@ -894,7 +907,11 @@ Delta_ClampIntegerField prevent data to out of range ===================== */ +#if XASH_OPT +_inline int Delta_ClampIntegerField( delta_t *pField, int iValue, qboolean bSigned, int numbits ) +#else int Delta_ClampIntegerField( delta_t *pField, int iValue, qboolean bSigned, int numbits ) +#endif { #ifdef _DEBUG if( numbits < 32 && abs( iValue ) >= (uint)BIT( numbits )) @@ -989,7 +1006,6 @@ qboolean Delta_CompareField( delta_t *pField, void *from, void *to, float timeba #if defined __GNUC__ && __GNUC_MAJOR < 9 && !defined __clang__ #pragma GCC diagnostic pop #endif - fromF = Delta_ClampIntegerField( pField, fromF, bSigned, pField->bits ); toF = Delta_ClampIntegerField( pField, toF, bSigned, pField->bits ); if( pField->multiplier != 1.0f ) fromF *= pField->multiplier; @@ -1065,13 +1081,20 @@ int Delta_TestBaseline( entity_state_t *from, entity_state_t *to, qboolean playe if( from == NULL ) return 0; return countBits; } - +#if XASH_OPT + dt = &dt_info[D_weapon_data_t]; + if( FBitSet( to->entityType, ENTITY_BEAM )) + dt = &dt_info[D_custom_entity_state_t]; + else if( player ) + dt = &dt_info[D_entity_state_player_t]; + else dt = &dt_info[D_entity_state_t]; +#else if( FBitSet( to->entityType, ENTITY_BEAM )) dt = Delta_FindStruct( "custom_entity_state_t" ); else if( player ) dt = Delta_FindStruct( "entity_state_player_t" ); else dt = Delta_FindStruct( "entity_state_t" ); - +#endif Assert( dt && dt->bInitialized ); countBits++; // entityType flag @@ -1109,7 +1132,11 @@ write fields by offsets assume from and to is valid ===================== */ +#if XASH_OPT +_inline qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, void *to, float timebase ) +#else qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, void *to, float timebase ) +#endif { qboolean bSigned = ( pField->flags & DT_SIGNED ) ? true : false; float flValue, flAngle, flTime; @@ -1332,8 +1359,11 @@ void MSG_WriteDeltaUsercmd( sizebuf_t *msg, usercmd_t *from, usercmd_t *to ) delta_t *pField; delta_info_t *dt; int i; - +#if XASH_OPT + dt = &dt_info[D_usercmd_t]; +#else dt = Delta_FindStruct( "usercmd_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1359,8 +1389,11 @@ void MSG_ReadDeltaUsercmd( sizebuf_t *msg, usercmd_t *from, usercmd_t *to ) delta_t *pField; delta_info_t *dt; int i; - +#if XASH_OPT + dt = &dt_info[D_usercmd_t]; +#else dt = Delta_FindStruct( "usercmd_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1394,8 +1427,11 @@ void MSG_WriteDeltaEvent( sizebuf_t *msg, event_args_t *from, event_args_t *to ) delta_t *pField; delta_info_t *dt; int i; - +#if XASH_OPT + dt = &dt_info[D_event_t]; +#else dt = Delta_FindStruct( "event_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1421,8 +1457,11 @@ void MSG_ReadDeltaEvent( sizebuf_t *msg, event_args_t *from, event_args_t *to ) delta_t *pField; delta_info_t *dt; int i; - +#if XASH_OPT + dt = &dt_info[D_event_t]; +#else dt = Delta_FindStruct( "event_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1450,8 +1489,11 @@ qboolean MSG_WriteDeltaMovevars( sizebuf_t *msg, movevars_t *from, movevars_t *t delta_info_t *dt; int i, startBit; int numChanges = 0; - +#if XASH_OPT + dt = &dt_info[D_movevars_t]; +#else dt = Delta_FindStruct( "movevars_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1485,8 +1527,11 @@ void MSG_ReadDeltaMovevars( sizebuf_t *msg, movevars_t *from, movevars_t *to ) delta_t *pField; delta_info_t *dt; int i; - +#if XASH_OPT + dt = &dt_info[D_movevars_t]; +#else dt = Delta_FindStruct( "movevars_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1522,8 +1567,11 @@ void MSG_WriteClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, delta_info_t *dt; int i, startBit; int numChanges = 0; - +#if XASH_OPT + dt = &dt_info[D_clientdata_t]; +#else dt = Delta_FindStruct( "clientdata_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1562,8 +1610,11 @@ void MSG_ReadClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, f delta_t *pField; delta_info_t *dt; int i; - +#if XASH_OPT + dt = &dt_info[D_clientdata_t]; +#else dt = Delta_FindStruct( "clientdata_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1603,8 +1654,11 @@ void MSG_WriteWeaponData( sizebuf_t *msg, weapon_data_t *from, weapon_data_t *to delta_info_t *dt; int i, startBit; int numChanges = 0; - +#if XASH_OPT + dt = &dt_info[D_weapon_data_t]; +#else dt = Delta_FindStruct( "weapon_data_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1641,8 +1695,11 @@ void MSG_ReadWeaponData( sizebuf_t *msg, weapon_data_t *from, weapon_data_t *to, delta_t *pField; delta_info_t *dt; int i; - +#if XASH_OPT + dt = &dt_info[D_weapon_data_t]; +#else dt = Delta_FindStruct( "weapon_data_t" ); +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1724,7 +1781,20 @@ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t * numChanges++; } else MSG_WriteOneBit( msg, 0 ); - +#if XASH_OPT + if( FBitSet( to->entityType, ENTITY_BEAM )) + { + dt = &dt_info[D_custom_entity_state_t]; + } + else if( delta_type == DELTA_PLAYER ) + { + dt = &dt_info[D_entity_state_player_t]; + } + else + { + dt = &dt_info[D_entity_state_t]; + } +#else if( FBitSet( to->entityType, ENTITY_BEAM )) { dt = Delta_FindStruct( "custom_entity_state_t" ); @@ -1737,7 +1807,7 @@ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t * { dt = Delta_FindStruct( "entity_state_t" ); } - +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; @@ -1843,6 +1913,20 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state to->entityType = MSG_ReadUBitLong( msg, 2 ); to->number = number; +#if XASH_OPT + if( cls.legacymode ? ( to->entityType == ENTITY_BEAM ) : FBitSet( to->entityType, ENTITY_BEAM )) + { + dt = &dt_info[D_custom_entity_state_t]; + } + else if( delta_type == DELTA_PLAYER ) + { + dt = &dt_info[D_entity_state_player_t]; + } + else + { + dt = &dt_info[D_entity_state_t]; + } +#else if( cls.legacymode ? ( to->entityType == ENTITY_BEAM ) : FBitSet( to->entityType, ENTITY_BEAM )) { dt = Delta_FindStruct( "custom_entity_state_t" ); @@ -1855,7 +1939,7 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state { dt = Delta_FindStruct( "entity_state_t" ); } - +#endif Assert( dt && dt->bInitialized ); pField = dt->pFields; diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index b372c133..719821cb 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -93,6 +93,7 @@ static int ioctl_stub( int d, unsigned long r, ... ) #define SOCKET int typedef int WSAsize_t; #else +#include //GSA #include "platform/stub/net_stub.h" #endif @@ -340,7 +341,7 @@ int NET_GetHostByName( const char *hostname ) #endif } -#if !defined XASH_NO_ASYNC_NS_RESOLVE && ( XASH_WIN32 || !(XASH_EMSCRIPTEN || XASH_DOS4GW) ) +#if !defined XASH_NO_ASYNC_NS_RESOLVE && ( XASH_WIN32 || !(XASH_EMSCRIPTEN || XASH_DOS4GW || XASH_PSP) ) #define CAN_ASYNC_NS_RESOLVE #endif diff --git a/engine/common/netchan.h b/engine/common/netchan.h index 0ccb4236..2d5b3278 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -87,7 +87,18 @@ GNU General Public License for more details. #define NETSPLIT_BACKUP 8 #define NETSPLIT_BACKUP_MASK (NETSPLIT_BACKUP - 1) #define NETSPLIT_HEADER_SIZE 18 - +#if XASH_PSP + #undef MULTIPLAYER_BACKUP + #undef SINGLEPLAYER_BACKUP + #undef NUM_PACKET_ENTITIES + #undef MAX_CUSTOM_BASELINES + #undef NET_MAX_FRAGMENT + #define MULTIPLAYER_BACKUP 16 + #define SINGLEPLAYER_BACKUP 16 + #define NUM_PACKET_ENTITIES 32 + #define MAX_CUSTOM_BASELINES 8 + #define NET_MAX_FRAGMENT 32768 +#else #if XASH_LOW_MEMORY == 2 #undef MULTIPLAYER_BACKUP #undef SINGLEPLAYER_BACKUP @@ -109,7 +120,7 @@ GNU General Public License for more details. #define MAX_CUSTOM_BASELINES 8 #define NET_MAX_FRAGMENT 32768 #endif - +#endif /* XASH_PSP */ typedef struct netsplit_chain_packet_s { // bool vector diff --git a/engine/common/protocol.h b/engine/common/protocol.h index d43e6aec..06336c30 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -182,6 +182,29 @@ GNU General Public License for more details. #define FRAGMENT_MAX_SIZE 64000 // maximal fragment size #define FRAGMENT_LOCAL_SIZE FRAGMENT_MAX_SIZE // local connection +#if XASH_PSP + +#undef MAX_VISIBLE_PACKET +#undef MAX_VISIBLE_PACKET_VIS_BYTES +#undef MAX_EVENTS +#undef MAX_SUPPORTED_MODELS +#undef MAX_MODELS +#undef MAX_SOUNDS +#undef MAX_CUSTOM +#undef MAX_RENDER_DECALS +#undef MAX_RESOURCES + +#define MAX_VISIBLE_PACKET 256 +#define MAX_VISIBLE_PACKET_VIS_BYTES ((MAX_VISIBLE_PACKET + 7) / 8) +#define MAX_EVENTS 256 +#define MAX_SUPPORTED_MODELS 512 +#define MAX_MODELS MAX_SUPPORTED_MODELS +#define MAX_SOUNDS 512 +#define MAX_CUSTOM 32 +#define MAX_RENDER_DECALS 256 +#define MAX_RESOURCES (MAX_MODELS+MAX_SOUNDS+MAX_CUSTOM+MAX_EVENTS) + +#else #if XASH_LOW_MEMORY == 2 #undef MAX_VISIBLE_PACKET #undef MAX_VISIBLE_PACKET_VIS_BYTES @@ -234,7 +257,7 @@ GNU General Public License for more details. #define MAX_RENDER_DECALS 128 #define MAX_RESOURCES 1024 #endif - +#endif /* XASH_PSP */ // Quake1 Protocol #define PROTOCOL_VERSION_QUAKE 15 diff --git a/engine/common/soundlib/snd_main.c b/engine/common/soundlib/snd_main.c index c68c14a1..704d5765 100644 --- a/engine/common/soundlib/snd_main.c +++ b/engine/common/soundlib/snd_main.c @@ -62,10 +62,10 @@ wavdata_t *FS_LoadSound( const char *filename, const byte *buffer, size_t size ) fs_offset_t filesize = 0; const loadwavfmt_t *format; byte *f; - + Sound_Reset(); // clear old sounddata Q_strncpy( loadname, filename, sizeof( loadname )); - + if( Q_stricmp( ext, "" )) { // we needs to compare file extension with list of supported formats diff --git a/engine/common/soundlib/snd_mp3.c b/engine/common/soundlib/snd_mp3.c index e8f44661..1f42bc32 100644 --- a/engine/common/soundlib/snd_mp3.c +++ b/engine/common/soundlib/snd_mp3.c @@ -117,8 +117,11 @@ stream_t *Stream_OpenMPG( const char *filename ) file_t *file; int ret; wavinfo_t sc; - +#if XASH_PSP // hold mode + file = FS_Open( filename, "*rb", false ); +#else file = FS_Open( filename, "rb", false ); +#endif if( !file ) return NULL; // at this point we have valid stream diff --git a/engine/common/soundlib/snd_wav.c b/engine/common/soundlib/snd_wav.c index 7b53d460..22fe6681 100644 --- a/engine/common/soundlib/snd_wav.c +++ b/engine/common/soundlib/snd_wav.c @@ -320,7 +320,11 @@ stream_t *Stream_OpenWAV( const char *filename ) return NULL; // open +#if XASH_PSP // hold mode + file = FS_Open( filename, "*rb", false ); +#else file = FS_Open( filename, "rb", false ); +#endif if( !file ) return NULL; // find "RIFF" chunk diff --git a/engine/common/system.c b/engine/common/system.c index 43f8201e..d61acfa1 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -32,6 +32,10 @@ GNU General Public License for more details. #endif #endif +#if XASH_PSP +#include +#endif + #include "menu_int.h" // _UPDATE_PAGE macro qboolean error_on_exit = false; // arg for exit(); @@ -472,7 +476,12 @@ Sys_Quit void Sys_Quit( void ) { Host_Shutdown(); +#if XASH_PSP + sceKernelDelayThread( 50 * 1000 ); + sceKernelExitGame(); +#else exit( error_on_exit ); +#endif } /* diff --git a/engine/common/system.h b/engine/common/system.h index 2096e6e0..48b8c198 100644 --- a/engine/common/system.h +++ b/engine/common/system.h @@ -68,7 +68,11 @@ void Sys_Print( const char *pMsg ); void Sys_PrintLog( const char *pMsg ); void Sys_InitLog( void ); void Sys_CloseLog( void ); +#ifdef XASH_PSP +void Sys_Quit( void ); +#else void Sys_Quit( void ) NORETURN; +#endif // // sys_con.c diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 134a2563..e40fe461 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -38,7 +38,11 @@ void Platform_MessageBox( const char *title, const char *message, qboolean paren // commented out, as this is an optional feature or maybe implemented in system API directly // see system.c // qboolean Sys_DebuggerPresent( void ); - +#if XASH_PSP +void Platform_ReadCmd( const char *fname, int *argc, char **argv ); +SceUID Platform_LoadModule( const char *filename, int mpid, SceSize argsize, void *argp ); +int Platform_UnloadModule( SceUID modid, int *sce_code ); +#endif #if XASH_ANDROID const char *Android_GetAndroidID( void ); const char *Android_LoadID( void ); diff --git a/engine/platform/psp/dll_psp.c b/engine/platform/psp/dll_psp.c new file mode 100644 index 00000000..7a78b279 --- /dev/null +++ b/engine/platform/psp/dll_psp.c @@ -0,0 +1,259 @@ +/* + From the vitaXash3D by fgsfdsfgs. +*/ +#include "common.h" +#include "dll_psp.h" +#include +#include +#include + +#define MAX_DLNAMELEN 256 + +#if 1 +typedef struct table_s +{ + const char *name; + void *pointer; +} table_t; + +#include "generated_library_tables.h" +#endif + +typedef struct dll_s +{ + SceUID handle; + char name[MAX_DLNAMELEN]; + int refcnt; + dllexport_t *exp; + struct dll_s *next; +} dll_t; + +static dll_t *dll_list; +static char *dll_err = NULL; +static char dll_err_buf[1024]; + +static void *dlfind( const char *name ) +{ + dll_t *d = NULL; + for( d = dll_list; d; d = d->next ) + if( !Q_strcmp( d->name, name ) ) + break; + return d; +} + +static const char *dlname( void *handle ) +{ + dll_t *d = NULL; + // iterate through all dll_ts to check if the handle is actually in the list + // and not some bogus pointer from god knows where + for( d = dll_list; d; d = d->next ) if( d == handle ) break; + return d ? d->name : NULL; +} + +void *dlopen( const char *name, int flag ) +{ + dll_t *old, *new; + char fullpath[MAX_SYSPATH]; + char static_name[16]; + void *static_exp; + void *dynamic_exp; + void *exp_p; + SceUID h; + + if( !name ) + return NULL; + + old = dlfind( name ); + if( old ) + { + old->refcnt++; + return old; + } + + Q_snprintf( fullpath, sizeof( fullpath ), "%s", name ); + + exp_p = &dynamic_exp; + static_exp = NULL; + memset( static_name, 0x00, sizeof( static_name ) ); + + h = Platform_LoadModule( fullpath, 0, sizeof( exp_p ), &exp_p ); + if( h < 0 ) + { + snprintf( dll_err_buf, sizeof( dll_err_buf ), "dlopen( %s ): error %#010x\n", name, h ); + dll_err = dll_err_buf; + + COM_FileBase( name, static_name ); + + if( !Q_strncmp( static_name, "lib", 3 ) ) + Q_sprintf( static_name, "%s", &static_name[3] ); + + table_t* tbl = ( table_t* )libs; + while( tbl->name ) + { + if( !Q_strcmp( tbl->name, static_name) ) + { + static_exp = tbl->pointer; + break; + } + tbl++; + } + + if( static_exp == NULL ) + return NULL; + + Con_Printf ( "dlopen( %s ): ( static ) success!\n", fullpath ); + } + else + { + Con_Printf ( "dlopen( %s ): ( dynamic ) success!\n", fullpath ); + } + + new = calloc( 1, sizeof( dll_t ) ); + if( !new ) + { + dll_err = "dlopen(): out of memory"; + return NULL; + } + snprintf( new->name, MAX_DLNAMELEN, name ); + + if( static_exp != NULL ) + { + new->handle = -1; + new->exp = static_exp; + } + else + { + new->handle = h; + new->exp = dynamic_exp; + } + + new->refcnt = 1; + + new->next = dll_list; + dll_list = new; + + return new; +} + +void *dlsym( void *handle, const char *symbol ) +{ + if( !handle || !symbol ) + { + dll_err = "dlsym(): NULL args"; + return NULL; + } + + if( !dlname( handle ) ) + { + dll_err = "dlsym(): unknown handle"; + return NULL; + } + + dll_t *d = handle; + if( !d->refcnt ) + { + dll_err = "dlsym(): call dlopen() first"; + return NULL; + } + + dllexport_t *f = NULL; + for( f = d->exp; f && f->func; f++ ) + { + if( !Q_strcmp( f->name, symbol ) ) + break; + } + + if( f && f->func ) + { + return f->func; + } + else + { + dll_err = "dlsym(): symbol not found in dll"; + return NULL; + } +} + +int dlclose( void *handle ) +{ + int result, sce_code; + dll_t *d; + + if( !handle ) + { + dll_err = "dlclose(): NULL arg"; + return -1; + } + + if( !dlname( handle ) ) + { + dll_err = "dlclose(): unknown handle"; + return -2; + } + + d = handle; + d->refcnt--; + if( d->refcnt <= 0 ) + { + if( d->handle != -1 ) + { + result = Platform_UnloadModule( d->handle, &sce_code ); + if( result < 0 ) + { + if( result == -1 ) + { + dll_err = "dlclose(): module doesn't want to stop"; + return -3; + } + + snprintf( dll_err_buf, sizeof( dll_err_buf ), "dlclose( %s ): error %#010x", d->name, sce_code ); + dll_err = dll_err_buf; + } + } + + if( d == dll_list ) + dll_list = NULL; + else + { + dll_t *pd; + for( pd = dll_list; pd; pd = pd->next ) + { + if( pd->next == d ) + { + pd->next = d->next; + break; + } + } + } + free( d ); + } + return 0; +} + +char *dlerror( void ) +{ + char *err = dll_err; + dll_err = NULL; + return err; +} + +int dladdr( const void *addr, Dl_info *info ) +{ + dll_t *d = NULL; + dllexport_t *f = NULL; + for( d = dll_list; d; d = d->next ) + for( f = d->exp; f && f->func; f++ ) + if( f->func == addr ) goto for_end; +for_end: + if( d && f && f->func ) + { + if( info ) + { + info->dli_fhandle = d; + info->dli_sname = f->name; + info->dli_saddr = addr; + } + return 1; + } + return 0; +} \ No newline at end of file diff --git a/engine/platform/psp/dll_psp.h b/engine/platform/psp/dll_psp.h new file mode 100644 index 00000000..4fc7766e --- /dev/null +++ b/engine/platform/psp/dll_psp.h @@ -0,0 +1,38 @@ +#ifndef DLL_PSP_H +#define DLL_PSP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define RTLD_LAZY 0x0001 +#define RTLD_NOW 0x0002 + +typedef struct dllexport_s +{ + const char *name; + void *func; +} dllexport_t; + +typedef struct Dl_info_s +{ + void *dli_fhandle; + const char *dli_sname; + const void *dli_saddr; +} Dl_info; + +void *dlsym(void *handle, const char *symbol ); +void *dlopen(const char *name, int flag ); +int dlclose(void *handle); +char *dlerror( void ); +int dladdr( const void *addr, Dl_info *info ); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/engine/platform/psp/in_psp.c b/engine/platform/psp/in_psp.c new file mode 100644 index 00000000..befa200e --- /dev/null +++ b/engine/platform/psp/in_psp.c @@ -0,0 +1,182 @@ +/* +in_psp.c - PSP input component +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include "common.h" +#include "keydefs.h" +#include "input.h" +#include "client.h" + +#if XASH_INPUT == INPUT_PSP +#include + +#define PSP_MAX_KEYS 12 + +extern convar_t *psp_deadzone_min; +extern convar_t *psp_deadzone_max; + +static struct psp_keymap_s +{ + unsigned int srckey; + int dstkey; +}psp_keymap[] = +{ +#if 1 + { PSP_CTRL_SELECT, '~' }, + { PSP_CTRL_START, K_ESCAPE }, + { PSP_CTRL_UP, K_UPARROW }, + { PSP_CTRL_RIGHT, K_RIGHTARROW }, + { PSP_CTRL_DOWN, K_DOWNARROW }, + { PSP_CTRL_LEFT, K_LEFTARROW }, + { PSP_CTRL_LTRIGGER, K_JOY1 }, + { PSP_CTRL_RTRIGGER, K_JOY2 }, + { PSP_CTRL_TRIANGLE, K_SHIFT }, + { PSP_CTRL_CIRCLE, K_SPACE }, + { PSP_CTRL_CROSS, K_ENTER }, + { PSP_CTRL_SQUARE, K_BACKSPACE }, +#else + { PSP_CTRL_SELECT, K_MODE_BUTTON }, + { PSP_CTRL_START, K_START_BUTTON }, + { PSP_CTRL_UP, K_UPARROW }, + { PSP_CTRL_RIGHT, K_RIGHTARROW }, + { PSP_CTRL_DOWN, K_DOWNARROW }, + { PSP_CTRL_LEFT, K_LEFTARROW }, + { PSP_CTRL_LTRIGGER, K_L1_BUTTON }, + { PSP_CTRL_RTRIGGER, K_R1_BUTTON }, + { PSP_CTRL_TRIANGLE, K_Y_BUTTON }, + { PSP_CTRL_CIRCLE, K_B_BUTTON }, + { PSP_CTRL_CROSS, K_A_BUTTON }, + { PSP_CTRL_SQUARE, K_X_BUTTON }, +#endif +#if 0 + PSP_CTRL_HOME, + PSP_CTRL_HOLD, + PSP_CTRL_NOTE, + PSP_CTRL_SCREEN, + PSP_CTRL_VOLUP, + PSP_CTRL_VOLDOWN, + PSP_CTRL_WLAN_UP, + PSP_CTRL_REMOTE, + PSP_CTRL_DISC, + PSP_CTRL_MS, +#endif +}; +static signed short psp_joymap[256]; /* -32768 <> 32767 */ + +static float Platform_JoyRescale( float axis, float deadzone_min, float deadzone_max ) +{ + float abs_axis, scale, r_deadzone_max; + int flip_axis = 0; + + // (-127) - (0) - (+127) + abs_axis = axis - 128.0f; + if( abs_axis < 0.0f ) + { + abs_axis = -abs_axis - 1.0f; + flip_axis = 1; + } + if( abs_axis <= deadzone_min ) return 0.0f; + r_deadzone_max = 127.0f - deadzone_max; + if( abs_axis >= r_deadzone_max ) return ( flip_axis ? -127.0f : 127.0f ); + + scale = 127.0f / ( r_deadzone_max - deadzone_min ); + abs_axis -= deadzone_min; + abs_axis *= scale; + + return ( flip_axis ? -abs_axis : abs_axis ); +} + +void Platform_RunEvents( void ) +{ + int i; + SceCtrlData buf; + signed short curr_X, curr_Y; + static unsigned int last_buttons; + static signed short last_X, last_Y; + + sceCtrlReadBufferPositive( &buf, 1 ); + + for( i = 0; i < PSP_MAX_KEYS; i++ ) + { + if( ( last_buttons ^ buf.Buttons ) & psp_keymap[i].srckey ) + Key_Event( psp_keymap[i].dstkey, buf.Buttons & psp_keymap[i].srckey ); + } + last_buttons = buf.Buttons; + + if( FBitSet( psp_deadzone_min->flags, FCVAR_CHANGED ) || FBitSet( psp_deadzone_max->flags, FCVAR_CHANGED ) ) + { + for ( i = 0; i < 256; i++ ) + { + psp_joymap[i] = Platform_JoyRescale( i, psp_deadzone_min->value, psp_deadzone_max->value ); + psp_joymap[i] *= 256; + } + + ClearBits( psp_deadzone_min->flags, FCVAR_CHANGED ); + ClearBits( psp_deadzone_max->flags, FCVAR_CHANGED ); + } + + curr_X = psp_joymap[buf.Lx]; + curr_Y = psp_joymap[buf.Ly]; + + if( last_X != curr_X ) + Joy_AxisMotionEvent( 2, -curr_X ); + + if( last_Y != curr_Y ) + Joy_AxisMotionEvent( 3, -curr_Y ); + + last_X = curr_X; + last_Y = curr_Y; +} + +void Platform_GetMousePos( int *x, int *y ) +{ + *x = *y = 0; +} + +void Platform_SetMousePos( int x, int y ) +{ + +} + +void Platform_EnableTextInput( qboolean enable ) +{ + +} + +int Platform_JoyInit( int numjoy ) +{ + int i; + + // set up the controller. + sceCtrlSetSamplingCycle( 0 ); + sceCtrlSetSamplingMode( PSP_CTRL_MODE_ANALOG ); + + // building a joystick map + for ( i = 0; i < 256; i++ ) + { + psp_joymap[i] = Platform_JoyRescale( i, psp_deadzone_min->value, psp_deadzone_max->value ); + psp_joymap[i] *= 256; + } + return 1; +} + +void Platform_MouseMove( float *x, float *y ) +{ + +} + +void Platform_PreCreateMove( void ) +{ + +} +#endif /* XASH_INPUT */ \ No newline at end of file diff --git a/engine/platform/psp/lib_psp.c b/engine/platform/psp/lib_psp.c new file mode 100644 index 00000000..9d343f8d --- /dev/null +++ b/engine/platform/psp/lib_psp.c @@ -0,0 +1,145 @@ +/* +lib_psp.c - dynamic library code for Sony PSP system +Copyright (C) 2018 Flying With Gauss + +This program is free software: you can redistribute it and/sor modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include "platform/platform.h" +#if XASH_LIB == LIB_PSP + + +#include "common.h" +#include "library.h" +#include "filesystem.h" +#include "server.h" +#include "platform/psp/lib_psp.h" + +#ifdef XASH_NO_LIBDL +#ifndef XASH_DLL_LOADER +#error Enable at least one dll backend!!! +#endif // XASH_DLL_LOADER + +void *dlsym(void *handle, const char *symbol ) +{ + Con_DPrintf( "dlsym( %p, \"%s\" ): stub\n", handle, symbol ); + return NULL; +} + +void *dlopen(const char *name, int flag ) +{ + Con_DPrintf( "dlopen( \"%s\", %d ): stub\n", name, flag ); + return NULL; +} + +int dlclose(void *handle) +{ + Con_DPrintf( "dlsym( %p ): stub\n", handle ); + return 0; +} + +char *dlerror( void ) +{ + return "Loading ELF libraries not supported in this build!\n"; +} + +int dladdr( const void *addr, Dl_info *info ) +{ + return 0; +} +#endif // XASH_NO_LIBDL + +qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname, qboolean directpath ) +{ + // TODO: implement + return true; +} + +void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean directpath ) +{ + dll_user_t *hInst = NULL; + void *pHandle = NULL; + + COM_ResetLibraryError(); + + // platforms where gameinfo mechanism is working goes here + // and use FS_FindLibrary + hInst = FS_FindLibrary( dllname, directpath ); + if( !hInst ) + { + // try to find by linker(LD_LIBRARY_PATH, DYLD_LIBRARY_PATH, LD_32_LIBRARY_PATH and so on...) + if( !pHandle ) + { + pHandle = dlopen( dllname, RTLD_LAZY ); + if( pHandle ) + return pHandle; + + COM_PushLibraryError( va( "Failed to find library %s", dllname )); + COM_PushLibraryError( dlerror() ); + return NULL; + } + } + + if( hInst->custom_loader ) + { + COM_PushLibraryError( va( "Custom library loader is not available. Extract library %s and fix gameinfo.txt!", hInst->fullPath )); + Mem_Free( hInst ); + return NULL; + } + + if( !( hInst->hInstance = dlopen( hInst->fullPath, RTLD_LAZY ) ) ) + { + COM_PushLibraryError( dlerror() ); + Mem_Free( hInst ); + return NULL; + } + + pHandle = hInst->hInstance; + + Mem_Free( hInst ); + + return pHandle; +} + +void COM_FreeLibrary( void *hInstance ) +{ + dlclose( hInstance ); +} + +void *COM_GetProcAddress( void *hInstance, const char *name ) +{ + return dlsym( hInstance, name ); +} + +void *COM_FunctionFromName( void *hInstance, const char *pName ) +{ + void *function; + if( !( function = COM_GetProcAddress( hInstance, pName ) ) ) + { + Con_Reportf( S_ERROR "FunctionFromName: Can't get symbol %s: %s\n", pName, dlerror()); + } + return function; +} + +const char *COM_NameForFunction( void *hInstance, void *function ) +{ + // NOTE: dladdr() is a glibc extension + Dl_info info = {0}; + dladdr((void*)function, &info); + if(info.dli_sname) + return info.dli_sname; +#ifdef XASH_ALLOW_SAVERESTORE_OFFSETS + return COM_OffsetNameForFunction( function ); +#else + return NULL; +#endif +} + +#endif // _WIN32 diff --git a/engine/platform/psp/lib_psp.h b/engine/platform/psp/lib_psp.h new file mode 100644 index 00000000..e69de29b diff --git a/engine/platform/psp/s_psp.c b/engine/platform/psp/s_psp.c new file mode 100644 index 00000000..eaf3f82f --- /dev/null +++ b/engine/platform/psp/s_psp.c @@ -0,0 +1,252 @@ +/* +s_psp.c - sound hardware output +Copyright (C) 2020 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + + +#ifndef XASH_DEDICATED +#include "common.h" +#if XASH_SOUND == SOUND_PSP + +#include +#include +#include + +#include "sound.h" + +#define SAMPLE_16BIT_SHIFT 1 +#define SECONDARY_BUFFER_SIZE 0x10000 + +#define SND_CHANELS 2 +#define SND_FREQ 44100 +#define SND_BUF_SIZE 16384 + +typedef struct +{ + short left; + short right; +}sample_t; + +static sample_t snd_buffer[SND_BUF_SIZE] __attribute__( ( aligned( 64 ) ) ); +static volatile unsigned int snd_sampleread; + +/* +======================================================================= +Global variables. Must be visible to window-procedure function +so it can unlock and free the data block after it has been played. +======================================================================= +*/ + +dma_t dma; + +void S_Activate( qboolean active ) +{ + +} + +/* From PSP Quake */ +static void PSP_SoundCallback( void* buffer, unsigned int samplesToWrite, void* userData ) +{ + // Where are we writing to? + sample_t* const dest = ( sample_t* )buffer; + + // Where are we reading from? + const sample_t* const src = &snd_buffer[snd_sampleread]; + + // How many samples to read? + const unsigned int samplesToRead = samplesToWrite; // 44100 + + // Going to wrap past the end of the input buffer? + const unsigned int samplesBeforeEndOfInput = SND_BUF_SIZE - snd_sampleread; + if ( samplesToRead > samplesBeforeEndOfInput ) + { + // Yes, so write the first chunk from the end of the input buffer. + memcpy( dest, src, samplesBeforeEndOfInput * sizeof( sample_t ) ); + + // Write the second chunk from the start of the input buffer. + const unsigned int samplesToReadFromBeginning = samplesToRead - samplesBeforeEndOfInput; + + memcpy( &dest[samplesBeforeEndOfInput], src, samplesToReadFromBeginning * sizeof( sample_t ) ); + } + else + { + // No wrapping, just copy. + memcpy( &dest[0], src, samplesToRead * sizeof( sample_t ) ); + } + + // Update the read offset. + snd_sampleread = ( snd_sampleread + samplesToRead ) % SND_BUF_SIZE; +} + +/* +================== +SNDDMA_Init + +Try to find a sound device to mix for. +Returns false if nothing is found. +================== +*/ +qboolean SNDDMA_Init( void ) +{ + dma.format.speed = SND_FREQ; + dma.format.channels = SND_CHANELS; + dma.format.width = 2; + dma.samples = SND_BUF_SIZE * SND_CHANELS; + dma.buffer = ( unsigned char * )snd_buffer; + dma.samplepos = 0; + + // Initialise the audio system. This initialises it for the CD audio module + // too. + pspAudioInit(); + + // Set the channel callback. + // Sound effects use channel 0, CD audio uses channel 1. + pspAudioSetChannelCallback( 0, PSP_SoundCallback, 0 ); + + Con_Printf( "Using PSP audio driver: %d Hz\n", dma.format.speed ); + + //SNDDMA_Activate( true ); + + dma.initialized = true; +#if 0 /* libmp3 */ + // load modules + int status = sceUtilityLoadModule( PSP_MODULE_AV_AVCODEC ); + if ( status < 0 ) + { + Con_Printf( "Error - PSP_MODULE_AV_AVCODEC returned 0x%08X\n", status ); + }else printf("PSP_MODULE_AV_AVCODEC - OK\n"); + + status = sceUtilityLoadModule( PSP_MODULE_AV_MP3 ); + if ( status < 0 ) + { + Con_Printf( "Error - PSP_MODULE_AV_MP3 returned 0x%08X\n", status ); + }else printf("PSP_MODULE_AV_MP3 - OK\n"); + + // init mp3 resources + status = sceMp3InitResource(); + if ( status < 0 ) + { + Con_Printf("Error - sceMp3InitResource returned 0x%08X\n", status); + }else printf("sceMp3InitResource - OK\n"); +#endif + return true; +} + +/* +============== +SNDDMA_GetDMAPos + +return the current sample position (in mono samples read) +inside the recirculating dma buffer, so the mixing code will know +how many sample are required to fill it up. +=============== +*/ +int SNDDMA_GetDMAPos( void ) +{ + dma.samplepos = snd_sampleread * SND_CHANELS; + return dma.samplepos; +} + +/* +============== +SNDDMA_GetSoundtime + +update global soundtime +=============== +*/ +int SNDDMA_GetSoundtime( void ) +{ + static int buffers, oldsamplepos; + int samplepos, fullsamples; + + fullsamples = dma.samples / 2; + + // it is possible to miscount buffers + // if it has wrapped twice between + // calls to S_Update. Oh well. + samplepos = SNDDMA_GetDMAPos(); + + if( samplepos < oldsamplepos ) + { + buffers++; // buffer wrapped + + if( paintedtime > 0x40000000 ) + { + // time to chop things off to avoid 32 bit limits + buffers = 0; + paintedtime = fullsamples; + S_StopAllSounds( true ); + } + } + + oldsamplepos = samplepos; + + return (buffers * fullsamples + samplepos / 2); +} + +/* +============== +SNDDMA_BeginPainting + +Makes sure dma.buffer is valid +=============== +*/ +void SNDDMA_BeginPainting( void ) +{ + +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +Also unlocks the dsound buffer +=============== +*/ +void SNDDMA_Submit( void ) +{ + +} + +/* +============== +SNDDMA_Shutdown + +Reset the sound device for exiting +=============== +*/ +void SNDDMA_Shutdown( void ) +{ + Con_Printf("Shutting down audio.\n"); + + // Clear the mixing buffer so we don't get any noise during cleanup. + memset(snd_buffer, 0, sizeof(snd_buffer)); + + // Clear the channel callback. + pspAudioSetChannelCallback(0, 0, 0); + + // Stop the audio system? + pspAudioEndPre(); + + // Insert a false delay so the thread can be cleaned up. + sceKernelDelayThread(50 * 1000); + + // Shut down the audio system. + pspAudioEnd(); + + dma.initialized = false; +} + +#endif +#endif diff --git a/engine/platform/psp/sys_psp.c b/engine/platform/psp/sys_psp.c new file mode 100644 index 00000000..8c835496 --- /dev/null +++ b/engine/platform/psp/sys_psp.c @@ -0,0 +1,281 @@ +/* +sys_psp.c - PSP System utils +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "platform/platform.h" +#include +#include +#include +#include +#include +#include + +#include + + +PSP_MODULE_INFO( "Xash3D", PSP_MODULE_USER, 1, 0 ); +PSP_MAIN_THREAD_ATTR( PSP_THREAD_ATTR_USER | PSP_THREAD_ATTR_VFPU ); +PSP_MAIN_THREAD_STACK_SIZE_KB( 512 ); +PSP_HEAP_SIZE_KB( -3 * 1024 ); /* 3 MB for prx modules */ + +convar_t *psp_deadzone_min; +convar_t *psp_deadzone_max; + +/* Exit callback */ +static int exit_callback( int arg1, int arg2, void *common ) +{ + host.crashed = true; + return 0; +} + +/* Callback thread */ +static int CallbackThread( SceSize args, void *argp ) +{ + int cbid; + + cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL); + sceKernelRegisterExitCallback(cbid); + sceKernelSleepThreadCB(); + + return 0; +} + +/* Sets up the callback thread and returns its thread id */ +static int SetupCallbacks( void ) +{ + int thid = 0; + + thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0); + if(thid >= 0) + { + sceKernelStartThread(thid, 0, 0); + } + return thid; +} + +#if XASH_TIMER == TIMER_PSP +double Platform_DoubleTime( void ) +{ +/* + static u64 start_ticks; + static qboolean timer_init = false; + u64 current_ticks; + + if ( !timer_init ) + { + sceRtcGetCurrentTick( &start_ticks ); + timer_init = true; + } + sceRtcGetCurrentTick( ¤t_ticks ); + + return ( current_ticks - start_ticks ) * 0.000001; +*/ + u64 current_ticks; + sceRtcGetCurrentTick( ¤t_ticks ); + + return ( double )current_ticks * 0.000001; +} + +void Platform_Sleep( int msec ) +{ + sceKernelDelayThread( msec * 1000 ); +} +#endif + +void *Platform_GetNativeObject( const char *name ) +{ + return NULL; +} + +void Platform_Vibrate( float life, char flags ) +{ + +} + +void Platform_GetClipboardText( char *buffer, size_t size ) +{ + +} + +void Platform_SetClipboardText( const char *buffer, size_t size ) +{ + +} + +void Platform_ShellExecute( const char *path, const char *parms ) +{ + +} + +#if XASH_MESSAGEBOX == MSGBOX_PSP +void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow ) +{ + static qboolean g_dbgscreen = false; + + // Clear the sound buffer. + S_StopAllSounds( true ); + + // Print message to the debug screen. + if ( !g_dbgscreen ) + { + pspDebugScreenInit(); + g_dbgscreen = true; + } + pspDebugScreenSetTextColor( 0x0080ff ); + pspDebugScreenPrintf( "\n\n\n\n%s:\n", title ); + pspDebugScreenSetTextColor( 0x0000ff ); + pspDebugScreenPrintData( message, Q_strlen( message ) ); + pspDebugScreenSetTextColor( 0xffffff ); + pspDebugScreenPrintf( "\n\nPress X to continue.\n" ); + + // Wait for a X button press. + SceCtrlData pad; + do + { + sceCtrlReadBufferPositive( &pad, 1 ); + } + while( !( pad.Buttons & PSP_CTRL_CROSS ) ); + pspDebugScreenClear(); +} +#endif // XASH_MESSAGEBOX == MSGBOX_PSP + +void Platform_ReadCmd( const char *fname, int *argc, char **argv ) +{ + int cmd_fd; + size_t cmd_fsize; + byte *cmd_buff; + int i, j = 0; + + cmd_fd = sceIoOpen( fname, PSP_O_RDONLY, 0777 ); + if( cmd_fd > 0 ) + { + cmd_fsize = sceIoLseek( cmd_fd, 0, PSP_SEEK_END ); + sceIoLseek( cmd_fd, 0, PSP_SEEK_SET ); + + printf( "CMD FILE(%s) Size: %i\n", fname, cmd_fsize ); + + if( cmd_fsize == 0 ) return; + + cmd_buff = malloc( cmd_fsize ); + if( !cmd_buff ) + { + printf( "CMD FILE(%s) Memory allocation error!\n", fname ); + sceIoClose(cmd_fd); + return; + } + + if( sceIoRead(cmd_fd, cmd_buff, cmd_fsize) >= 0 ) + { + for( i = 0; i < cmd_fsize; i++ ) + { + if( isspace( cmd_buff[i] ) != 0 ) + { + if( j == 0 ) continue; + + argv[*argc] = malloc( j + 1 ); + if( !argv[*argc] ) + { + printf( "CMD FILE(%s) Memory allocation error!\n", fname ); + free( cmd_buff ); + sceIoClose( cmd_fd ); + return; + } + memcpy( argv[*argc], &cmd_buff[i - j], j ); + argv[*argc][j] = 0x00; + ( int )( *argc )++; + j = 0; + } + else j++; + + } + if( j != 0 ) + { + argv[*argc] = malloc( j + 1 ); + if( !argv[*argc] ) + { + printf( "CMD FILE(%s) Memory allocation error!\n", fname ); + free( cmd_buff ); + sceIoClose( cmd_fd ); + return; + } + memcpy( argv[*argc], &cmd_buff[i - j], j ); + argv[*argc][j] = 0x00; + ( int )( *argc )++; + j = 0; + } + } + else printf( "CMD FILE(%s) Read error!\n", fname ); + + free( cmd_buff ); + sceIoClose(cmd_fd); + } +} + +SceUID Platform_LoadModule( const char *filename, int mpid, SceSize argsize, void *argp ) +{ + SceKernelLMOption option; + SceUID modid = 0; + int retVal = 0, mresult; + + memset( &option, 0, sizeof( option ) ); + option.size = sizeof( option ); + option.mpidtext = mpid; + option.mpiddata = mpid; + option.position = 0; + option.access = 1; + + retVal = sceKernelLoadModule( filename, 0, &option ); + if(retVal < 0) + return retVal; + + modid = retVal; + + retVal = sceKernelStartModule( modid, argsize, argp, &mresult, NULL ); + if( retVal < 0 ) + return retVal; + + return modid; +} + +int Platform_UnloadModule( SceUID modid, int *sce_code ) +{ + int status; + *sce_code = sceKernelStopModule( modid, 0, NULL, &status, NULL); + + if( ( *sce_code ) < 0 ) + return -2; + else if( status == SCE_KERNEL_ERROR_NOT_STOPPED ) + return -1; + + *sce_code = sceKernelUnloadModule( modid ); + return ( ( ( *sce_code ) < 0 ) ? -2 : 0 ); +} + +void Platform_Init( void ) +{ + pspSdkDisableFPUExceptions(); + + SetupCallbacks(); + scePowerSetClockFrequency( 333, 333, 166 ); + + psp_deadzone_min = Cvar_Get( "psp_deadzone_min", "20", FCVAR_ARCHIVE, "Joy deadzone min (0 - 127)" ); + psp_deadzone_max = Cvar_Get( "psp_deadzone_max", "0", FCVAR_ARCHIVE, "Joy deadzone max (0 - 127)" ); +} + +void Platform_Shutdown( void ) +{ +#if XASH_PROFILING + gprof_cleanup(); +#endif +} \ No newline at end of file diff --git a/engine/platform/psp/vid_psp.c b/engine/platform/psp/vid_psp.c new file mode 100644 index 00000000..e29eeab5 --- /dev/null +++ b/engine/platform/psp/vid_psp.c @@ -0,0 +1,538 @@ +/* +vid_psp.c - PSP video component +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#if XASH_VIDEO == VIDEO_PSP + +#if !XASH_DEDICATED +#include +#include +#include +#include +#include "vram_psp.h" + +#include "common.h" +#include "client.h" +#include "mod_local.h" +#include "input.h" +#include "vid_common.h" + +// Set gu list +#define PSP_GU_STATIC_LIST 1 +#define PSP_GU_LIST_SIZE 0x40000 + +// Set frame buffer +#define PSP_FB_WIDTH 480 +#define PSP_FB_HEIGHT 272 +#define PSP_FB_BWIDTH 512 +#define PSP_FB_FORMAT 5650 //4444,5551,5650,8888 + +#if PSP_FB_FORMAT == 4444 +#define PSP_FB_HW_FORMAT GU_PSM_4444 +#define PSP_FB_SW_FORMAT PSP_DISPLAY_PIXEL_FORMAT_4444 +#define PSP_FB_BPP 2 +#elif PSP_FB_FORMAT == 5551 +#define PSP_FB_HW_FORMAT GU_PSM_5551 +#define PSP_FB_SW_FORMAT PSP_DISPLAY_PIXEL_FORMAT_5551 +#define PSP_FB_BPP 2 +#elif PSP_FB_FORMAT == 5650 +#define PSP_FB_HW_FORMAT GU_PSM_5650 +#define PSP_FB_SW_FORMAT PSP_DISPLAY_PIXEL_FORMAT_565 +#define PSP_FB_BPP 2 +#elif PSP_FB_FORMAT == 8888 +#define PSP_FB_HW_FORMAT GU_PSM_8888 +#define PSP_FB_SW_FORMAT PSP_DISPLAY_PIXEL_FORMAT_8888 +#define PSP_FB_BPP 4 +#endif + +#if 0 +// Set up screen scaling +#define PSP_WIDTH_AR 30 +#define PSP_HEIGHT_AR 17 +#define PSP_MIN_MAR 12 +#define PSP_MAX_MAR 16 +#define PSP_MIN_MAR_W (PSP_WIDTH_AR * PSP_MIN_MAR) +#define PSP_MIN_MAR_H (PSP_HEIGHT_AR * PSP_MIN_MAR) +#define PSP_MAX_MAR_W (PSP_WIDTH_AR * PSP_MAX_MAR) +#define PSP_MAX_MAR_H (PSP_HEIGHT_AR * PSP_MAX_MAR) +#endif + + +static qboolean vsync; +#if 1 +static vidmode_t vidmodes[] = { "480x272", PSP_FB_WIDTH, PSP_FB_HEIGHT}; +static int num_vidmodes = 1; +#else +static vidmode_t *vidmodes = NULL; +static int num_vidmodes = 0; +#endif +static struct +{ +#if 0 + int in_width, in_height; + int out_width, out_height; +#endif + void *draw_buffer; + void *disp_buffer; + void *depth_buffer; +#if PSP_GU_STATIC_LIST + unsigned int gu_list[PSP_GU_LIST_SIZE] __attribute__( ( aligned( 64 ) ) ); +#else + unsigned int *gu_list; +#endif +}vid_psp; + +qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b ) +{ + *stride = PSP_FB_BWIDTH; + *r = 31; + *g = 63 << 5; + *b = 31 << 11; + *bpp = 2; + + vid_psp.draw_buffer = (void*)valloc( PSP_FB_HEIGHT * PSP_FB_BWIDTH * PSP_FB_BPP ); + if( !vid_psp.draw_buffer ) + Host_Error( "Memory allocation failled! (vid_psp.draw_buffer)\n"); + + vid_psp.disp_buffer = (void*)valloc( PSP_FB_HEIGHT * PSP_FB_BWIDTH * PSP_FB_BPP ); + if( !vid_psp.disp_buffer ) + Host_Error( "Memory allocation failled! (vid_psp.disp_buffer)\n"); + + sceDisplaySetMode(0, PSP_FB_WIDTH, PSP_FB_HEIGHT); + sceDisplaySetFrameBuf(vid_psp.disp_buffer, PSP_FB_WIDTH, PSP_FB_SW_FORMAT, 1); + + return true; +} + +void *SW_LockBuffer( void ) +{ + return vid_psp.draw_buffer; +} + +void SW_UnlockBuffer( void ) +{ + void* p_swap = vid_psp.disp_buffer; + vid_psp.disp_buffer = vid_psp.draw_buffer; + vid_psp.draw_buffer = p_swap; + + sceKernelDcacheWritebackInvalidateAll(); + sceDisplaySetFrameBuf(vid_psp.disp_buffer, PSP_FB_BWIDTH, PSP_FB_SW_FORMAT, PSP_DISPLAY_SETBUF_NEXTFRAME); + + if ( vsync ) + { + sceDisplayWaitVblankStart(); + } +} + +_inline void GU_Init( void ) +{ + vid_psp.draw_buffer = ( void* )valloc( PSP_FB_HEIGHT * PSP_FB_BWIDTH * PSP_FB_BPP ); + if( !vid_psp.draw_buffer ) + Host_Error( "Memory allocation failled! (vid_psp.draw_buffer)\n"); + + vid_psp.disp_buffer = ( void* )valloc( PSP_FB_HEIGHT * PSP_FB_BWIDTH * PSP_FB_BPP ); + if( !vid_psp.disp_buffer ) + Host_Error( "Memory allocation failled! (vid_psp.disp_buffer)\n"); + + vid_psp.depth_buffer = ( void* )valloc( PSP_FB_HEIGHT * PSP_FB_BWIDTH * PSP_FB_BPP ); + if( !vid_psp.depth_buffer ) + Host_Error( "Memory allocation failled! (vid_psp.depth_buffer)\n"); + +#if !PSP_GU_STATIC_LIST + vid_psp.gu_list = ( unsigned int* )valloc( PSP_GU_LIST_SIZE * sizeof ( unsigned int ) ); + if( !vid_psp.gu_list ) + Host_Error( "Memory allocation failled! (vid_psp.gu_list)\n"); +#endif + + // Initialise the GU. + sceGuInit(); + + // Set up the GU. + sceGuStart( GU_DIRECT, vid_psp.gu_list ); + + sceGuDrawBuffer( PSP_FB_HW_FORMAT, vrelptr( vid_psp.draw_buffer ), PSP_FB_BWIDTH); + sceGuDispBuffer( PSP_FB_WIDTH, PSP_FB_HEIGHT, vrelptr( vid_psp.disp_buffer ), PSP_FB_BWIDTH); + sceGuDepthBuffer( vrelptr( vid_psp.depth_buffer ), PSP_FB_BWIDTH); + + // Set the rendering offset and viewport. + sceGuOffset( 2048 - ( PSP_FB_WIDTH / 2 ), 2048 - ( PSP_FB_HEIGHT / 2 ) ); + sceGuViewport( 2048, 2048, PSP_FB_WIDTH, PSP_FB_HEIGHT ); + + // Set up scissoring. + sceGuEnable( GU_SCISSOR_TEST ); + sceGuScissor( 0, 0, PSP_FB_WIDTH, PSP_FB_HEIGHT ); + + // Set up texturing. + sceGuEnable( GU_TEXTURE_2D ); + + // Set up clearing. + sceGuClearDepth( 65535 ); + sceGuClearColor( GU_RGBA( 0x10, 0x20, 0x40, 0xff ) ); + + // Set up depth. + sceGuDepthOffset( 0 ); + sceGuDepthRange( 0, 65535 ); + sceGuDepthFunc( GU_LEQUAL ); + sceGuEnable( GU_DEPTH_TEST ); + + // Set up fog +// sceGuFog( 100.0f, 500.0f, 0xffffffff ); +// sceGuDisable( GU_FOG ); + + // Set the default matrices. + sceGumMatrixMode( GU_PROJECTION ); + sceGumLoadIdentity(); + sceGumMatrixMode( GU_VIEW ); + sceGumLoadIdentity(); + sceGumMatrixMode( GU_MODEL ); + sceGumLoadIdentity(); + sceGumMatrixMode( GU_TEXTURE ); + sceGumLoadIdentity(); + + sceGumUpdateMatrix(); + + // Set up culling. + sceGuFrontFace( GU_CW ); + sceGuEnable( GU_CULL_FACE ); + sceGuEnable( GU_CLIP_PLANES ); + + // Set up blending. + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + + sceGuFinish(); + sceGuSync( GU_SYNC_FINISH, GU_SYNC_WHAT_DONE ); + + // Turn on the display. + sceDisplayWaitVblankStart(); + sceGuDisplay(GU_TRUE); + + // Start a new render. + sceGuStart( GU_DIRECT, vid_psp.gu_list ); + + Con_Printf( "Vram available: %i\n", vmemavail() ); +} + +_inline void GU_Shutdown( void ) +{ + // Finish rendering. + sceGuFinish(); + sceGuSync( GU_SYNC_FINISH, GU_SYNC_WHAT_DONE ); + + // Shut down the display. + sceGuTerm(); + + // Free the buffers. + vfree( vid_psp.draw_buffer ); + vid_psp.draw_buffer = NULL; + vfree( vid_psp.disp_buffer ); + vid_psp.disp_buffer = NULL; + vfree( vid_psp.depth_buffer ); + vid_psp.depth_buffer = NULL; +#if !PSP_GU_STATIC_LIST + vfree( vid_psp.gu_list ); + vid_psp.gu_list = NULL; +#endif +} + +_inline void GU_SwapBuffers( void ) +{ +/* + // Don't forget writeback the cache + sceKernelDcacheWritebackAll(); +*/ + // Finish rendering. + size_t list_size = sceGuFinish(); +/* + if( list_size > sizeof( gu_list ) - 1000 ) + printf( "Display list size (%i) %i!\n", list_size, sizeof( gu_list ) ); +*/ + sceGuSync( GU_SYNC_FINISH, GU_SYNC_WHAT_DONE ); + + if ( vsync ) + { + sceDisplayWaitVblankStart(); + } + + // Swap the buffers. + sceGuSwapBuffers(); + + void* p_swap = vid_psp.disp_buffer; + vid_psp.disp_buffer = vid_psp.draw_buffer; + vid_psp.draw_buffer = p_swap; + + // Start a new render. + sceGuStart( GU_DIRECT, vid_psp.gu_list ); +} + +int R_MaxVideoModes( void ) +{ + return num_vidmodes; +} + +vidmode_t *R_GetVideoMode( int num ) +{ + if( !vidmodes || num < 0 || num >= R_MaxVideoModes() ) + { + return NULL; + } + + return vidmodes + num; +} +#if 0 +static void R_InitVideoModes( void ) +{ + int i; + + vidmodes = Mem_Malloc( host.mempool, (PSP_MAX_MAR - PSP_MIN_MAR) * sizeof( vidmode_t ) ); + + // from smallest to largest + for(i = PSP_MIN_MAR ; i <= PSP_MAX_MAR; i++ ) + { + vidmodes[num_vidmodes].width = PSP_WIDTH_AR * i; + vidmodes[num_vidmodes].height = PSP_HEIGHT_AR * i; + vidmodes[num_vidmodes].desc = + copystring( va( "%ix%i", vidmodes[num_vidmodes].width, vidmodes[num_vidmodes].height )); + + num_vidmodes++; + } +} + +static void R_FreeVideoModes( void ) +{ + int i; + + if(!vidmodes) + return; + + for( i = 0; i < num_vidmodes; i++ ) + Mem_Free( (char*)vidmodes[i].desc ); + Mem_Free( vidmodes ); + + vidmodes = NULL; +} +#endif +/* +================= +GL_GetProcAddress +================= +*/ +void *GL_GetProcAddress( const char *name ) +{ + return NULL; +} + +/* +=============== +GL_UpdateSwapInterval +=============== +*/ +void GL_UpdateSwapInterval( void ) +{ + // disable VSync while level is loading + if( cls.state < ca_active ) + { + // setup vsync here + vsync = false; + SetBits( gl_vsync->flags, FCVAR_CHANGED ); + } + else if( FBitSet( gl_vsync->flags, FCVAR_CHANGED )) + { + ClearBits( gl_vsync->flags, FCVAR_CHANGED ); + vsync = (gl_vsync->value > 0) ? true : false; + } +} + +static qboolean VID_SetScreenResolution( int width, int height ) +{ + int render_w = width, render_h = height; + uint rotate = vid_rotate->value; + + if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, vid_scale->value, vid_scale->value ) ) + { + if( rotate & 1 ) + { + int swap = render_w; + + render_w = render_h; + render_h = swap; + } + + render_h /= vid_scale->value; + render_w /= vid_scale->value; + } + else + { + Con_Printf( S_WARN "failed to setup screen transform\n" ); + } + + + + R_SaveVideoMode( width, height, render_w, render_h ); + + return true; +} + +void GL_SwapBuffers( void ) +{ + GU_SwapBuffers(); +} + +int GL_SetAttribute( int attr, int val ) +{ + return 0; +} + +int GL_GetAttribute( int attr, int *val ) +{ + return 0; +} + +/* +================== +R_Init_Video +================== +*/ +qboolean R_Init_Video( const int type ) +{ + string safe; + qboolean retval; + + refState.desktopBitsPixel = ( PSP_FB_BPP * 8 ); + + VID_StartupGamma(); + + switch( type ) + { + case REF_SOFTWARE: + glw_state.software = true; + break; + case REF_GL: + if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", safe ) ) + glw_state.safe = bound( SAFE_NO, Q_atoi( safe ), SAFE_DONTCARE ); + break; + default: + Host_Error( "Can't initialize unknown context type %d!\n", type ); + break; + } + + if( !(retval = VID_SetMode()) ) + { + return retval; + } + + switch( type ) + { + case REF_GL: + // refdll also can check extensions + ref.dllFuncs.GL_InitExtensions(); + GU_Init(); + break; + case REF_SOFTWARE: + default: + break; + } +#if 0 + R_InitVideoModes(); +#endif + host.renderinfo_changed = false; + + return true; +} + +rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) +{ + Con_Reportf( "R_ChangeDisplaySettings: Setting video mode to %dx%d %s\n", width, height, fullscreen ? "fullscreen" : "windowed" ); + + refState.fullScreen = fullscreen; + + if( !VID_SetScreenResolution( width, height ) ) + return rserr_invalid_fullscreen; + + return rserr_ok; +} + +/* +================== +VID_SetMode + +Set the described video mode +================== +*/ +qboolean VID_SetMode( void ) +{ + qboolean fullscreen = false; + int iScreenWidth, iScreenHeight; + rserr_t err; + +#if 1 + iScreenWidth = PSP_FB_WIDTH; + iScreenHeight = PSP_FB_HEIGHT; +#else + vid_psp.out_width = PSP_FB_WIDTH; + vid_psp.out_height = PSP_FB_HEIGHT; + + iScreenWidth = Cvar_VariableInteger( "width" ); + iScreenHeight = Cvar_VariableInteger( "height" ); + + if( iScreenWidth < PSP_MIN_MAR_W || + iScreenHeight < PSP_MIN_MAR_H ) // trying to get resolution automatically by default + { + iScreenWidth = PSP_MIN_MAR_W; + iScreenHeight = PSP_MIN_MAR_H; + } + + if( iScreenWidth > PSP_MAX_MAR_W || + iScreenHeight > PSP_MAX_MAR_H ) // trying to get resolution automatically by default + { + iScreenWidth = PSP_MAX_MAR_W; + iScreenHeight = PSP_MAX_MAR_H; + } +#endif + if( !FBitSet( vid_fullscreen->flags, FCVAR_CHANGED ) ) + Cvar_SetValue( "fullscreen", DEFAULT_FULLSCREEN ); + else + ClearBits( vid_fullscreen->flags, FCVAR_CHANGED ); + + SetBits( gl_vsync->flags, FCVAR_CHANGED ); + fullscreen = true;//Cvar_VariableInteger("fullscreen") != 0; + + if(( err = R_ChangeDisplaySettings( iScreenWidth, iScreenHeight, fullscreen )) == rserr_ok ) + { +#if 0 + vid_psp.in_width = iScreenWidth; + vid_psp.in_height = iScreenHeight; +#endif + } + else + return false; + + return true; +} + +/* +================== +R_Free_Video +================== +*/ +void R_Free_Video( void ) +{ +#if 0 + R_FreeVideoModes(); +#endif + ref.dllFuncs.GL_ClearExtensions(); + if( !glw_state.software ) + GU_Shutdown(); +} + +#endif // XASH_DEDICATED +#endif // XASH_VIDEO \ No newline at end of file diff --git a/engine/platform/psp/vram_psp.c b/engine/platform/psp/vram_psp.c new file mode 100644 index 00000000..a9a520dd --- /dev/null +++ b/engine/platform/psp/vram_psp.c @@ -0,0 +1,259 @@ +/* + * Helper for use with the PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed as 'free to use and modify as long as credited appropriately' + * + * vram.c - Standard C high performance VRAM allocation routines. + * + * Copyright (c) 2007 Alexander Berl 'Raphael' + * http://wordpress.fx-world.org + */ +#include "vram.h" +#include + +// Configure the memory to be managed +#define __MEM_SIZE 0x00200000 +#define __MEM_START 0x04000000 + +// Configure the block size the memory gets subdivided into (page size) +// __MEM_SIZE/__BLOCK_SIZE may not exceed 2^16-1 = 65535 +// The block size also defines the alignment of allocations +// Larger block sizes perform better, because the blocktable is smaller and therefore fits better into cache +// however the overhead is also bigger and more memory is wasted +#define __BLOCK_SIZE 512 +#define __MEM_BLOCKS (__MEM_SIZE/__BLOCK_SIZE) +#define __BLOCKS(x) ((x+__BLOCK_SIZE-1)/__BLOCK_SIZE) +#define __BLOCKSIZE(x) ((x+__BLOCK_SIZE-1)&~(__BLOCK_SIZE-1)) + + +// A MEMORY BLOCK ENTRY IS MADE UP LIKE THAT: +// bit: 31 30 29 - 15 14-0 +// free block prev size +// +// bit 31: free bit, indicating if block is allocated or not +// bit 30: blocked bit, indicating if block is part of a larger block (0) - used for error resilience +// bit 29-15: block index of previous block +// bit 14- 0: size of current block +// +// This management can handle a max amount of 2^16-1 = 65535 blocks, which resolves to 64MB at blocksize of 1024 bytes +// +#define __BLOCK_GET_SIZE(x) ((x & 0x7FFF)) +#define __BLOCK_GET_PREV(x) ((x >> 15) & 0x7FFF) +#define __BLOCK_GET_FREE(x) ((x >> 31)) +#define __BLOCK_GET_BLOCK(x) ((x >> 30) & 0x1) +#define __BLOCK_SET_SIZE(x,y) x=((x & ~0x7FFF) | ((y) & 0x7FFF)) +#define __BLOCK_ADD_SIZE(x,y) x=((x & ~0x7FFF) | (((x & 0x7FFF)+((y) & 0x7FFF)) & 0x7FFF)) +#define __BLOCK_SET_PREV(x,y) x=((x & ~0x3FFF8000) | (((y) & 0x7FFF)<<15)) +#define __BLOCK_SET_FREE(x,y) x=((x & 0x7FFFFFFF) | (((y) & 0x1)<<31)) +#define __BLOCK_SET_BLOCK(x,y) x=((x & 0xBFFFFFFF) | (((y) & 0x1)<<30)) +#define __BLOCK_MAKE(s,p,f,n) (((f & 0x1)<<31) | ((n & 0x1)<<30) | (((p) & 0x7FFF)<<15) | ((s) & 0x7FFF)) +#define __BLOCK_GET_FREEBLOCK(x) ((x>>30) & 0x3) // returns 11b if block is a starting block and free, 10b if block is a starting block and allocated, 0xb if it is a non-starting block (don't change) +#define __BLOCK0 ((__MEM_BLOCKS) | (1<<31) | (1<<30)) + + +unsigned int __mem_blocks[__MEM_BLOCKS] = { 0 }; + + +static int __largest_update = 0; +static int __largest_block = __MEM_BLOCKS; +static int __mem_free = __MEM_BLOCKS; + + +inline void* vrelptr( void *ptr ) +{ + return (void*)((unsigned int)ptr & ~__MEM_START); +} + +inline void* vabsptr( void *ptr ) +{ + return (void*)((unsigned int)ptr | __MEM_START); +} + + +static void __find_largest_block( void ) +{ + int i = 0; + __largest_block = 0; + while (i<__MEM_BLOCKS) + { + int csize = __BLOCK_GET_SIZE(__mem_blocks[i]); + if (__BLOCK_GET_FREEBLOCK(__mem_blocks[i])==3 && csize>__largest_block) + __largest_block = csize; + i += csize; + } + __largest_update = 0; +} + +#ifdef _DEBUG +void __memwalk( void ) +{ + int i = 0; + if (__mem_blocks[0]==0) __mem_blocks[0] = __BLOCK0; + while (i<__MEM_BLOCKS) + { + printf("BLOCK %i:\n", i); + printf(" free: %i\n", __BLOCK_GET_FREEBLOCK(__mem_blocks[i])); + printf(" size: %i\n", __BLOCK_GET_SIZE(__mem_blocks[i])); + printf(" prev: %i\n", __BLOCK_GET_PREV(__mem_blocks[i])); + i+=__BLOCK_GET_SIZE(__mem_blocks[i]); + } +} +#endif + +void* valloc( size_t size ) +{ + // Initialize memory block, if not yet done + if (__mem_blocks[0]==0) __mem_blocks[0] = __BLOCK0; + + int i = 0; + int j = 0; + int bsize = __BLOCKS(size); + + if (__largest_update==0 && __largest_block=bsize) + { + if (csizebsize && next<__MEM_BLOCKS) + { + __mem_blocks[next] = __BLOCK_MAKE(csize-bsize,i,1,1); + int nextnext = i+csize; + if (nextnext<__MEM_BLOCKS) + { + __BLOCK_SET_PREV(__mem_blocks[nextnext], next); + } + } + + __mem_free -= bsize; + if (__largest_block==csize) // if we just allocated from one of the largest blocks + { + if ((csize-bsize)>(__mem_free/2)) + __largest_block = (csize-bsize); // there can't be another largest block + else + __largest_update = 1; + } + return ((void*)(__MEM_START + (i*__BLOCK_SIZE))); +} + + +void vfree( void* ptr ) +{ + if (ptr==0) return; + + int block = ((unsigned int)ptr - __MEM_START)/__BLOCK_SIZE; + if (block<0 || block>__MEM_BLOCKS) + { + #ifdef _DEBUG + printf("Block is out of range: %i (0x%x)\n", block, (int)ptr); + #endif + return; + } + int csize = __BLOCK_GET_SIZE(__mem_blocks[block]); + #ifdef _DEBUG + printf("freeing block %i (0x%x), size: %i\n", block, (int)ptr, csize); + #endif + + if (__BLOCK_GET_FREEBLOCK(__mem_blocks[block])!=1 || csize==0) + { + #ifdef _DEBUG + printf("Block was not allocated!\n"); + #endif + return; + } + + // Mark block as free + __BLOCK_SET_FREE(__mem_blocks[block],1); + __mem_free += csize; + + int next = block+csize; + // Merge with previous block if possible + int prev = __BLOCK_GET_PREV(__mem_blocks[block]); + if (prev + * http://wordpress.fx-world.org + */ +#ifndef vram_h__ +#define vram_h__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void* vrelptr( void *ptr ); // make a pointer relative to memory base address (ATTENTION: A NULL rel ptr is not illegal/invalid!) +void* vabsptr( void *ptr ); // make a pointer absolute (default return type of valloc) + +void* valloc( size_t size ); +void vfree( void* ptr ); +size_t vmemavail(); +size_t vlargestblock(); + + +#ifdef _DEBUG +// Debug printf (to stdout) a trace of the current Memblocks +void __memwalk(); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // ifdef vram_h__ diff --git a/engine/server/server.h b/engine/server/server.h index 47fbf173..7098b5b4 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -54,13 +54,17 @@ extern int SV_UPDATE_BACKUP; #define GROUP_OP_AND 0 #define GROUP_OP_NAND 1 -#ifdef NDEBUG +#if defined( NDEBUG ) || XASH_OPT #define SV_IsValidEdict( e ) ( e && !e->free ) #else #define SV_IsValidEdict( e ) SV_CheckEdict( e, __FILE__, __LINE__ ) #endif #define NUM_FOR_EDICT(e) ((int)((edict_t *)(e) - svgame.edicts)) +#if XASH_OPT +#define EDICT_NUM( num ) ( ( ( ( num ) >= 0 ) && ( ( num ) < GI->max_edicts) ) ? svgame.edicts + ( num ) : NULL ) +#else #define EDICT_NUM( num ) SV_EdictNum( num ) +#endif #define STRING( offset ) SV_GetString( offset ) #define ALLOC_STRING(str) SV_AllocString( str ) #define MAKE_STRING(str) SV_MakeString( str ) @@ -476,7 +480,12 @@ void Master_Packet( void ); // void SV_ActivateServer( int runPhysics ); qboolean SV_SpawnServer( const char *server, const char *startspot, qboolean background ); +#if XASH_OPT +#define SV_ModelHandle( modelindex ) ( ( modelindex < 0 || modelindex >= MAX_MODELS ) ? NULL : sv.models[( modelindex )] ) +model_t *pfnSV_ModelHandle( int modelindex ); +#else model_t *SV_ModelHandle( int modelindex ); +#endif void SV_DeactivateServer( void ); // @@ -490,7 +499,17 @@ qboolean SV_RunThink( edict_t *ent ); qboolean SV_PlayerRunThink( edict_t *ent, float frametime, double time ); qboolean SV_TestEntityPosition( edict_t *ent, edict_t *blocker ); void SV_Impact( edict_t *e1, edict_t *e2, trace_t *trace ); +#if XASH_OPT +// filter movetypes to collide with +#define SV_CanPushed( ent ) ( ( ent )->v.movetype == MOVETYPE_NONE || \ + ( ent )->v.movetype == MOVETYPE_PUSH || \ + ( ent )->v.movetype == MOVETYPE_FOLLOW || \ + ( ent )->v.movetype == MOVETYPE_NOCLIP || \ + ( ent )->v.movetype == MOVETYPE_COMPOUND ) ? false : true +qboolean pfnSV_CanPushed( edict_t *ent ); +#else qboolean SV_CanPushed( edict_t *ent ); +#endif void SV_FreeOldEntities( void ); void SV_CheckAllEnts( void ); @@ -633,7 +652,9 @@ void SV_RestartAmbientSounds( void ); void SV_RestartDecals( void ); void SV_RestartStaticEnts( void ); int pfnGetCurrentPlayer( void ); +#if !XASH_OPT edict_t *SV_EdictNum( int n ); +#endif char *SV_Localinfo( void ); // // sv_log.c diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index c6d41c42..0a72ed88 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -35,7 +35,7 @@ static vec3_t viewPoint[MAX_CLIENTS]; // exports typedef void (__cdecl *LINK_ENTITY_FUNC)( entvars_t *pev ); typedef void (__stdcall *GIVEFNPTRSTODLL)( enginefuncs_t* engfuncs, globalvars_t *pGlobals ); - +#if !XASH_OPT edict_t *SV_EdictNum( int n ) { if(( n >= 0 ) && ( n < GI->max_edicts )) @@ -59,7 +59,7 @@ qboolean SV_CheckEdict( const edict_t *e, const char *file, const int line ) return false; } #endif - +#endif /* ============= EntvarsDescription @@ -2679,7 +2679,8 @@ void GAME_EXPORT pfnMessageEnd( void ) return; } - *(word *)&sv.multicast.pData[svgame.msg_size_index] = svgame.msg_realsize; + /**(word *)&*/ // GSA + sv.multicast.pData[svgame.msg_size_index] = svgame.msg_realsize; } else { diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index e4b05a1c..497aaa2f 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -266,12 +266,19 @@ SV_ModelHandle get model by handle ================ */ +#if XASH_OPT +model_t *pfnSV_ModelHandle( int modelindex ) +{ + return SV_ModelHandle( modelindex ); +} +#else model_t *SV_ModelHandle( int modelindex ) { if( modelindex < 0 || modelindex >= MAX_MODELS ) return NULL; return sv.models[modelindex]; } +#endif void SV_ReadResourceList( const char *filename ) { diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 62efc1de..602b0458 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -842,6 +842,12 @@ SV_CanPushed filter entities for push ============ */ +#if XASH_OPT +qboolean pfnSV_CanPushed( edict_t *ent ) +{ + return SV_CanPushed( ent ); +} +#else qboolean SV_CanPushed( edict_t *ent ) { // filter movetypes to collide with @@ -856,6 +862,7 @@ qboolean SV_CanPushed( edict_t *ent ) } return true; } +#endif /* ============ @@ -864,7 +871,11 @@ SV_CanBlock allow entity to block pusher? ============ */ +#if XASH_PSP +static inline qboolean SV_CanBlock( edict_t *ent ) +#else static qboolean SV_CanBlock( edict_t *ent ) +#endif { if( ent->v.mins[0] == ent->v.maxs[0] ) return false; @@ -936,12 +947,12 @@ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) // filter movetypes to collide with if( !SV_CanPushed( check )) continue; - +#if !XASH_OPT pusher->v.solid = SOLID_NOT; block = SV_TestEntityPosition( check, pusher ); pusher->v.solid = oldsolid; if( block ) continue; - +#endif // if the entity is standing on the pusher, it will definately be moved if( !( FBitSet( check->v.flags, FL_ONGROUND ) && check->v.groundentity == pusher )) { @@ -957,7 +968,12 @@ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) if( !SV_TestEntityPosition( check, NULL )) continue; } - +#if XASH_OPT + pusher->v.solid = SOLID_NOT; + block = SV_TestEntityPosition( check, pusher ); + pusher->v.solid = oldsolid; + if( block ) continue; +#endif // remove the onground flag for non-players if( check->v.movetype != MOVETYPE_WALK ) check->v.flags &= ~FL_ONGROUND; @@ -1049,18 +1065,19 @@ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); + if( !SV_IsValidEdict( check )) continue; // filter movetypes to collide with if( !SV_CanPushed( check )) continue; - +#if !XASH_OPT pusher->v.solid = SOLID_NOT; block = SV_TestEntityPosition( check, pusher ); pusher->v.solid = oldsolid; if( block ) continue; - +#endif // if the entity is standing on the pusher, it will definately be moved if( !(( check->v.flags & FL_ONGROUND ) && check->v.groundentity == pusher )) { @@ -1076,7 +1093,12 @@ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) if( !SV_TestEntityPosition( check, NULL )) continue; } - +#if XASH_OPT + pusher->v.solid = SOLID_NOT; + block = SV_TestEntityPosition( check, pusher ); + pusher->v.solid = oldsolid; + if( block ) continue; +#endif // save original position of contacted entity pushed_p->ent = check; VectorCopy( check->v.origin, pushed_p->origin ); @@ -1092,7 +1114,7 @@ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) Matrix4x4_VectorITransform( start_l, org, temp ); Matrix4x4_VectorTransform( end_l, temp, org2 ); VectorSubtract( org2, org, lmove ); - + // i can't clear FL_ONGROUND in all cases because many bad things may be happen if( check->v.movetype != MOVETYPE_WALK ) { @@ -1131,7 +1153,6 @@ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) return check; } } - return NULL; } @@ -2018,7 +2039,11 @@ static server_physics_api_t gPhysicsAPI = SV_LinkEdict, SV_GetServerTime, SV_GetFrameTime, +#if XASH_OPT + (void*)pfnSV_ModelHandle, +#else (void*)SV_ModelHandle, +#endif SV_GetHeadNode, SV_ServerState, Host_Error, diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 6dff96c8..6bfba80a 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -32,8 +32,11 @@ half-life implementation of saverestore system #define SAVEGAME_HEADER (('V'<<24)+('A'<<16)+('S'<<8)+'J') // little-endian "JSAV" #define SAVEGAME_VERSION 0x0071 // Version 0.71 GoldSrc compatible #define CLIENT_SAVEGAME_VERSION 0x0067 // Version 0.67 - +#ifdef XASH_PSP +#define SAVE_HEAPSIZE 0x200000 // reserve 2Mb for now +#else #define SAVE_HEAPSIZE 0x400000 // reserve 4Mb for now +#endif #define SAVE_HASHSTRINGS 0xFFF // 4095 unique strings #define SAVE_AGED_COUNT 2 @@ -2255,10 +2258,19 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment ) else pTokenList = NULL; // short, short (size, index of field name) +#if XASH_PSP /* FIX Unaligned access! */ + short offpd; + memcpy(&offpd, pData, sizeof( short ) ); + nFieldSize = offpd; + pData += sizeof( short ); + + memcpy(&offpd, pData, sizeof( short )); + pFieldName = pTokenList[offpd]; +#else nFieldSize = *(short *)pData; pData += sizeof( short ); pFieldName = pTokenList[*(short *)pData]; - +#endif if( Q_stricmp( pFieldName, "GameHeader" )) { Q_strncpy( comment, "", MAX_STRING ); @@ -2270,7 +2282,11 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment ) // int (fieldcount) pData += sizeof( short ); +#if XASH_PSP /* FIX Unaligned access! */ + memcpy(&nNumberOfFields, pData, sizeof(int)); +#else nNumberOfFields = (int)*pData; +#endif pData += nFieldSize; // each field is a short (size), short (index of name), binary string of "size" bytes (data) @@ -2280,12 +2296,21 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment ) // Size // szName // Actual Data +#if XASH_PSP /* FIX Unaligned access! */ + memcpy(&offpd, pData, sizeof( short ) ); + nFieldSize = offpd; + pData += sizeof( short ); + + memcpy(&offpd, pData, sizeof( short )); + pFieldName = pTokenList[offpd]; + pData += sizeof( short ); +#else nFieldSize = *(short *)pData; pData += sizeof( short ); pFieldName = pTokenList[*(short *)pData]; pData += sizeof( short ); - +#endif if( !Q_stricmp( pFieldName, "comment" )) { Q_strncpy( description, pData, nFieldSize ); diff --git a/engine/sprite.h b/engine/sprite.h index a62a14ed..da0964e3 100644 --- a/engine/sprite.h +++ b/engine/sprite.h @@ -15,6 +15,9 @@ #ifndef SPRITE_H #define SPRITE_H +#ifdef __psp__ // GSA +#pragma pack(push, 1) +#endif /* ============================================================================== @@ -125,4 +128,7 @@ typedef struct frametype_t type; } dframetype_t; +#ifdef __psp__ // GSA +#pragma pack(pop) +#endif #endif//SPRITE_H diff --git a/engine/wscript b/engine/wscript index 847919ab..c230a68b 100644 --- a/engine/wscript +++ b/engine/wscript @@ -44,6 +44,8 @@ def configure(conf): elif conf.env.DEST_OS == 'android': # Android doesn't need SDL2 for i in ['log']: conf.check_cc(lib = i) + elif conf.env.DEST_OS == 'psp': + conf.define('XASH_NO_NETWORK', 1) elif conf.options.FBDEV_SW: # unused, XASH_LINUX without XASH_SDL gives fbdev & alsa support # conf.define('XASH_FBDEV', 1) @@ -66,7 +68,8 @@ def configure(conf): if conf.options.STATIC: conf.env.STATIC = True - conf.define('XASH_NO_LIBDL',1) + if conf.env.DEST_OS != 'psp': + conf.define('XASH_NO_LIBDL',1) if not conf.env.DEST_OS in ['win32', 'android'] and not conf.options.NO_ASYNC_RESOLVE: conf.load('pthreads') @@ -99,7 +102,7 @@ def build(bld): if bld.env.DEST_OS == 'win32': libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI', 'WS2_32' ] source += bld.path.ant_glob(['platform/win32/*.c']) - elif bld.env.DEST_OS != 'dos': #posix + elif bld.env.DEST_OS not in ['dos', 'psp']: #posix libs += [ 'M', 'RT', 'PTHREAD', 'ASOUND'] if not bld.env.STATIC: libs += ['DL'] @@ -112,7 +115,6 @@ def build(bld): source += bld.path.ant_glob(['platform/dos/*.c']) source += bld.path.ant_glob(['platform/stub/s_stub.c']) - if bld.get_define('XASH_CUSTOM_SWAP'): source += bld.path.ant_glob(['platform/misc/kmalloc.c', 'platform/misc/sbrk.c']) @@ -133,6 +135,9 @@ def build(bld): libs += ['LOG'] source += bld.path.ant_glob(['platform/android/*.cpp', 'platform/android/*.c', 'platform/linux/*.c']) + if bld.env.DEST_OS == 'psp': + source += bld.path.ant_glob(['platform/psp/*.c']) + # add client files if not bld.env.DEDICATED: source += bld.path.ant_glob([ diff --git a/mainui b/mainui index 6e913551..d3c61e2e 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 6e9135516a9e1c9bcd7e0b1b6a923fc55d4f8eaa +Subproject commit d3c61e2e1486bcac3af9e8d31a7c6b0186d1968a diff --git a/public/build.h b/public/build.h index 043a11cd..2b7ee030 100644 --- a/public/build.h +++ b/public/build.h @@ -70,6 +70,8 @@ For more information, please refer to #undef XASH_WIN32 #undef XASH_WIN64 #undef XASH_X86 +#undef XASH_PSP +#undef XASH_OPT //================================================================ // @@ -115,11 +117,15 @@ For more information, please refer to #elif defined __WATCOMC__ && defined __DOS__ #define XASH_DOS4GW 1 #define XASH_LITTLE_ENDIAN +#elif defined __psp__ + #define XASH_PSP 1 + #define XASH_LITTLE_ENDIAN + #define XASH_OPT 1 #else #error "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug" #endif -#if defined XASH_ANDROID || defined XASH_IOS +#if defined XASH_ANDROID || defined XASH_IOS || defined XASH_PSP #define XASH_MOBILE_PLATFORM 1 #endif diff --git a/public/matrixlib.c b/public/matrixlib.c index f536e4f9..ef4c1415 100644 --- a/public/matrixlib.c +++ b/public/matrixlib.c @@ -35,13 +35,57 @@ const matrix3x4 matrix3x4_identity = */ void Matrix3x4_VectorTransform( const matrix3x4 in, const float v[3], float out[3] ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %1\n" // C100 = in[0] + "ulv.q C110, 16 + %1\n" // C110 = in[1] + "ulv.q C120, 32 + %1\n" // C120 = in[2] + "lv.s S130, 0 + %2\n" // S130 = v[0] + "lv.s S131, 4 + %2\n" // S131 = v[1] + "lv.s S132, 8 + %2\n" // S132 = v[2] + "vhdp.q S000, C130, C100\n" // S000 = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3] + "vhdp.q S001, C130, C110\n" // S001 = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3] + "vhdp.q S002, C130, C120\n" // S002 = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3] + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *in ), "m"( *v ) + ); +#else out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3]; out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3]; out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3]; +#endif } void Matrix3x4_VectorITransform( const matrix3x4 in, const float v[3], float out[3] ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %1\n" // C100 = in[0] + "ulv.q C110, 16 + %1\n" // C110 = in[1] + "ulv.q C120, 32 + %1\n" // C120 = in[2] + "lv.s S130, 0 + %2\n" // S130 = v[0] + "lv.s S131, 4 + %2\n" // S131 = v[1] + "lv.s S132, 8 + %2\n" // S132 = v[2] + "vsub.t C130, C130, R103\n" // C130 = v - in[][3] + "vdot.t S000, C130, R100\n" // S000 = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0] + "vdot.t S001, C130, R101\n" // S001 = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1] + "vdot.t S002, C130, R102\n" // S002 = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2] + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *in ), "m"( *v ) + ); +#else vec3_t dir; dir[0] = v[0] - in[0][3]; @@ -51,20 +95,74 @@ void Matrix3x4_VectorITransform( const matrix3x4 in, const float v[3], float out out[0] = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0]; out[1] = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1]; out[2] = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2]; +#endif } void Matrix3x4_VectorRotate( const matrix3x4 in, const float v[3], float out[3] ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %1\n" // C100 = in[0] + "ulv.q C110, 16 + %1\n" // C110 = in[1] + "ulv.q C120, 32 + %1\n" // C120 = in[2] + "lv.s S130, 0 + %2\n" // S130 = v[0] + "lv.s S131, 4 + %2\n" // S131 = v[1] + "lv.s S132, 8 + %2\n" // S132 = v[2] +#if 0 + "vtfm3.t C000, M100, C130\n" // c000 = M100 * C130 +#else + "vdot.t S000, C130, C100\n" // S000 = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + "vdot.t S001, C130, C110\n" // S001 = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + "vdot.t S002, C130, C120\n" // S002 = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] +#endif + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *in ), "m"( *v ) + ); + +#else out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2]; out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2]; out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2]; +#endif } void Matrix3x4_VectorIRotate( const matrix3x4 in, const float v[3], float out[3] ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %1\n" // C100 = in[0] + "ulv.q C110, 16 + %1\n" // C110 = in[1] + "ulv.q C120, 32 + %1\n" // C120 = in[2] + "lv.s S130, 0 + %2\n" // S130 = v[0] + "lv.s S131, 4 + %2\n" // S131 = v[1] + "lv.s S132, 8 + %2\n" // S132 = v[2] +#if 0 + "vtfm3.t C000, E100, C130\n" // c000 = E100 * C130 +#else + "vdot.t S000, C130, R100\n" // S000 = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0] + "vdot.t S001, C130, R101\n" // S001 = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1] + "vdot.t S002, C130, R102\n" // S002 = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2] +#endif + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *in ), "m"( *v ) + ); +#else out[0] = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0]; out[1] = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1]; out[2] = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2]; +#endif } void Matrix3x4_ConcatTransforms( matrix3x4 out, const matrix3x4 in1, const matrix3x4 in2 ) @@ -119,6 +217,30 @@ void Matrix3x4_AnglesFromMatrix( const matrix3x4 in, vec3_t out ) void Matrix3x4_FromOriginQuat( matrix3x4 out, const vec4_t quaternion, const vec3_t origin ) { +#if 0//XASH_PSP /* not tested */ + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C130, %1\n" // C130 = quaternion + "lv.s S003, 0 + %2\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %2\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %2\n" // S023 = out[2][3] = origin[2] + "vmov.q C100, C130[ W, Z, -Y, -X]\n" // C100 = ( w, z, -y, -x) + "vmov.q C110, C130[-Z, W, X, -Y]\n" // C110 = (-z, w, x, -y) + "vmov.q C120, C130[ Y, -X, W, -Z]\n" // C120 = ( y, -x, w, -z) + "vmov.q C200, C130[ W, Z, -Y, X]\n" // C200 = ( w, z, -y, x) + "vmov.q C210, C130[-Z, W, X, Y]\n" // C210 = (-z, w, x, y) + "vmov.q C220, C130[ Y, -X, W, Z]\n" // C220 = ( y, -x, w, z) + "vmov.q C230, C130[-X, -Y, -Z, W]\n" // C230 = (-x, -y, -z, w) + "vmmul.q E000, E100, E200\n" // E000 = E100 * E200 + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *quaternion ), "m"( *origin ) + ); +#else out[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2]; out[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2]; out[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1]; @@ -134,10 +256,166 @@ void Matrix3x4_FromOriginQuat( matrix3x4 out, const vec4_t quaternion, const vec out[0][3] = origin[0]; out[1][3] = origin[1]; out[2][3] = origin[2]; +#endif } void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_t origin, float scale ) { +#if XASH_PSP /* performance not tested */ + if( angles[ROLL] ) + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S100, 0 + %1\n" // S100 = angles[PITCH] + "lv.s S101, 4 + %1\n" // S101 = angles[YAW] + "lv.s S102, 8 + %1\n" // S102 = angles[ROLL] + "lv.s S003, 0 + %2\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %2\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %2\n" // S023 = out[2][3] = origin[2] + "lv.s S130, %3\n" // S130 = scale + /**/ + "vfim.s S120, 0.0111111111111111\n" // S121 = 0.0111111111111111 const ( 2 / 180 ) + "vscl.t C100, C100, S120\n" // C100 = C100 * S120 = angles * ( 2 / 180 ) + /**/ + "vsin.t C110, C100\n" // C110 = sin( C100 ) P Y R + "vcos.t C120, C100\n" // C120 = cos( C100 ) P Y R + "vneg.t C100, C110\n" // C100 = -C110 = -sin( C100 ) + /**/ + "vmul.s S000, S120, S121\n" // S000 = S120 * S121 = out[0][0] = ( cp * cy ) + "vmul.s S011, S112, S110\n" // S011 = S112 * S110 = ( sr * sp ) + "vmul.s S001, S011, S121\n" // S001 = S011 * S121 = ( sr * sp * cy ) + "vmul.s S103, S122, S101\n" // S001 = S122 * S101 = ( cr * -sy ) + "vadd.s S001, S001, S103\n" // S001 = S001 + S103 = out[0][1] = ( sr * sp * cy + cr * -sy ) + "vmul.s S012, S122, S110\n" // S002 = S122 * S110 = ( cr * sp ) + "vmul.s S002, S012, S121\n" // S002 = S012 * S121 = ( cr * sp * cy ) + "vmul.s S103, S102, S101\n" // S002 = S102 * S101 = ( -sr * -sy ) + "vadd.s S002, S002, S103\n" // S002 = S002 + S103 = out[0][2] = ( cr * sp * cy + -sr * -sy ) + /**/ + "vmul.s S010, S120, S111\n" // S010 = S120 * S111 = out[1][0] = ( cp * sy ) + "vmul.s S011, S011, S111\n" // S001 = S011 * S111 = ( sr * sp * sy ) + "vmul.s S103, S122, S121\n" // S001 = S122 * S121 = ( cr * cy ) + "vadd.s S011, S011, S103\n" // S011 = S011 + S103 = out[1][1] = ( sr * sp * sy + cr * cy ) + "vmul.s S012, S012, S111\n" // S012 = S012 * S111 = ( cr * sp * sy ) + "vmul.s S103, S102, S121\n" // S103 = S102 * S121 = ( -sr * cy ) + "vadd.s S012, S012, S103\n" // S012 = S012 + S103 = out[1][2] = ( cr * sp * sy + -sr * cy ) + /**/ + "vmov.s S020, S101\n" // S020 = S101 = out[2][0] = ( -sp ) + "vmul.s S021, S112, S120\n" // S021 = S112 * S120 = out[2][1] = ( sr * cp ) + "vmul.s S022, S122, S120\n" // S021 = S122 * S120 = out[2][2] = ( cr * cp ) + /**/ + "vmscl.t E000, E000, S130\n" // E000 = E000 * S103 = out(3) * scale + /**/ + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *angles ), "m"( *origin ), "m"( scale ) + ); + } + else if( angles[PITCH] ) + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S100, 0 + %1\n" // S100 = angles[PITCH] + "lv.s S101, 4 + %1\n" // S101 = angles[YAW] + "lv.s S003, 0 + %2\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %2\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %2\n" // S023 = out[2][3] = origin[2] + "lv.s S130, %3\n" // S130 = scale + /**/ + "vfim.s S120, 0.0111111111111111\n" // S121 = 0.0111111111111111 const ( 2 / 180 ) + "vscl.p C100, C100, S120\n" // C100 = C100 * S120 = angles * ( 2 / 180 ) + /**/ + "vsin.p C110, C100\n" // C110 = sin( C100 ) P Y + "vcos.p C120, C100\n" // C120 = cos( C100 ) P Y + "vneg.p C100, C110\n" // C100 = -C110 = -sin( C100 ) + /**/ + "vmul.s S000, S120, S121\n" // S000 = S120 * S121 = out[0][0] = ( cp * cy ) + "vmov.s S001, S101\n" // S001 = S101 = out[0][1] = ( -sy ) + "vmul.s S002, S110, S121\n" // S001 = S110 * S121 = out[0][2] = ( sp * cy ) + /**/ + "vmul.s S010, S120, S111\n" // S010 = S120 * S111 = out[1][0] = ( cp * sy ) + "vmov.s S011, S121\n" // S011 = S121 = out[1][1] = ( cy ) + "vmul.s S012, S110, S111\n" // S012 = S110 * S111 = out[1][2] = ( sp * sy ) + /**/ + "vmov.s S020, S100\n" // S020 = S100 = out[2][0] = ( -sp ) + "vzero.s S021\n" // S021 = out[2][1] = 0.0f + "vmov.s S022, S120\n" // S022 = S120 = out[2][2] = ( cp ) + /**/ + "vmscl.t E000, E000, S130\n" // E000 = E000 * S103 = out(3) * scale + /**/ + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *angles ), "m"( *origin ), "m"( scale ) + ); + } + else if( angles[YAW] ) + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S101, 4 + %1\n" // S101 = angles[YAW] + "lv.s S003, 0 + %2\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %2\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %2\n" // S023 = out[2][3] = origin[2] + "lv.s S130, %3\n" // S130 = scale + /**/ + "vfim.s S120, 0.0111111111111111\n" // S121 = 0.0111111111111111 const ( 2 / 180 ) + "vmul.s S101, S101, S120\n" // S101 = S101 * S120 = angles[YAW] * ( 2 / 180 ) + /**/ + "vsin.s S111, S101\n" // S111 = sin( S101 ) Y + "vcos.s S121, S101\n" // S121 = cos( S101 ) Y + /**/ + "vzero.p R002\n" // S002 = 0.0f S012 = 0.0f + "vzero.p C020\n" // S020 = 0.0f S021 = 0.0f + "vmov.s S000, S121\n" // S000 = S121 = out[0][0] = ( cy ) + "vneg.s S001, S111\n" // S001 = S111 = out[0][1] = ( -sy ) + "vmov.s S010, S111\n" // S010 = S111 = out[1][0] = ( sy ) + "vmov.s S011, S121\n" // S011 = S121 = out[1][1] = ( cy ) + "vone.s S022\n" // S022 = out[2][2] = 1.0f + /**/ + "vmscl.t E000, E000, S130\n" // E000 = E000 * S103 = out(3) * scale + /**/ + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *angles ), "m"( *origin ), "m"( scale ) + ); + } + else + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S003, 0 + %1\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %1\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %1\n" // S023 = out[2][3] = origin[2] + "lv.s S130, %2\n" // S130 = scale + /**/ + "vzero.t C000\n" // C000 = [0.0f, 0.0f, 0.0f] + "vzero.t C010\n" // C010 = [0.0f, 0.0f, 0.0f] + "vzero.t C020\n" // C020 = [0.0f, 0.0f, 0.0f] + "vmov.s S000, S130\n" // S000 = S130 = out[0][0] = scale + "vmov.s S011, S130\n" // S011 = S130 = out[1][1] = scale + "vmov.s S022, S130\n" // S022 = S130 = out[2][2] = scale + /**/ + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *origin ), "m"( scale ) + ); + } +#else float angle, sr, sp, sy, cr, cp, cy; if( angles[ROLL] ) @@ -215,10 +493,39 @@ void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_ out[2][2] = scale; out[2][3] = origin[2]; } +#endif } void Matrix3x4_TransformPositivePlane( const matrix3x4 in, const vec3_t normal, float d, vec3_t out, float *dist ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %2\n" // C100 = in[0] + "ulv.q C110, 16 + %2\n" // C110 = in[1] + "ulv.q C120, 32 + %2\n" // C120 = in[2] + "lv.s S200, 0 + %3\n" // S200 = normal[0] + "lv.s S201, 4 + %3\n" // S201 = normal[1] + "lv.s S202, 8 + %3\n" // S202 = normal[2] + "lv.s S210, %4\n" // S210 = d + "vdot.t S211, C100, C100\n" // S211 = C100 * C100 + "vsqrt.s S211, S211\n" // S211 = sqrt( S211 ) + "vrcp.s S212, S211\n" // S212 = 1 / S211 + "vtfm3.t C000, M100, C200\n" // C000 = M100 * C200 + "vscl.t C000, C000, S212\n" // C000 = C000 * S211 + "vmul.s S003, S210, S211\n" // S003 = S210 * S211 + "vdot.t S010, R103, C000\n" // S010 = R103 * C000 + "vadd.s S003, S003, S010\n" // S003 = S003 + S010 + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + "sv.s S003, %1\n" // dist = S003 + ".set pop\n" // restore assembler option + : "=m"( *out ), "=m"( *dist ) + : "m"( *in ), "m"( *normal ), "m"( d ) + ); +#else float scale = sqrt( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); float iscale = 1.0f / scale; @@ -226,6 +533,7 @@ void Matrix3x4_TransformPositivePlane( const matrix3x4 in, const vec3_t normal, out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale; out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale; *dist = d * scale + ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] ); +#endif } void Matrix3x4_Invert_Simple( matrix3x4 out, const matrix3x4 in1 ) @@ -312,13 +620,57 @@ const matrix4x4 matrix4x4_identity = */ void Matrix4x4_VectorTransform( const matrix4x4 in, const float v[3], float out[3] ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %1\n" // C100 = in[0] + "ulv.q C110, 16 + %1\n" // C110 = in[1] + "ulv.q C120, 32 + %1\n" // C120 = in[2] + "lv.s S130, 0 + %2\n" // S130 = v[0] + "lv.s S131, 4 + %2\n" // S131 = v[1] + "lv.s S132, 8 + %2\n" // S132 = v[2] + "vhdp.q S000, C130, C100\n" // S000 = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3] + "vhdp.q S001, C130, C110\n" // S001 = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3] + "vhdp.q S002, C130, C120\n" // S002 = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3] + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *in ), "m"( *v ) + ); +#else out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3]; out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3]; out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3]; +#endif } void Matrix4x4_VectorITransform( const matrix4x4 in, const float v[3], float out[3] ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %1\n" // C100 = in[0] + "ulv.q C110, 16 + %1\n" // C110 = in[1] + "ulv.q C120, 32 + %1\n" // C120 = in[2] + "lv.s S130, 0 + %2\n" // S130 = v[0] + "lv.s S131, 4 + %2\n" // S131 = v[1] + "lv.s S132, 8 + %2\n" // S132 = v[2] + "vsub.t C200, C130, R103\n" // C130 = v - in[][3] + "vdot.t S000, C200, R100\n" // S000 = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0] + "vdot.t S001, C200, R101\n" // S001 = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1] + "vdot.t S002, C200, R102\n" // S002 = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2] + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *in ), "m"( *v ) + ); +#else vec3_t dir; dir[0] = v[0] - in[0][3]; @@ -328,20 +680,74 @@ void Matrix4x4_VectorITransform( const matrix4x4 in, const float v[3], float out out[0] = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0]; out[1] = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1]; out[2] = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2]; +#endif } void Matrix4x4_VectorRotate( const matrix4x4 in, const float v[3], float out[3] ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %1\n" // C100 = in[0] + "ulv.q C110, 16 + %1\n" // C110 = in[1] + "ulv.q C120, 32 + %1\n" // C120 = in[2] + "lv.s S130, 0 + %2\n" // S130 = v[0] + "lv.s S131, 4 + %2\n" // S131 = v[1] + "lv.s S132, 8 + %2\n" // S132 = v[2] +#if 0 + "vtfm3.t C000, M100, C130\n" // c000 = M100 * C130 +#else + "vdot.t S000, C130, C100\n" // S000 = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + "vdot.t S001, C130, C110\n" // S001 = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + "vdot.t S002, C130, C120\n" // S002 = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] +#endif + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *in ), "m"( *v ) + ); +#else out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2]; out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2]; out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2]; +#endif } void Matrix4x4_VectorIRotate( const matrix4x4 in, const float v[3], float out[3] ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %1\n" // C100 = in[0] + "ulv.q C110, 16 + %1\n" // C110 = in[1] + "ulv.q C120, 32 + %1\n" // C120 = in[2] + "lv.s S130, 0 + %2\n" // S130 = v[0] + "lv.s S131, 4 + %2\n" // S131 = v[1] + "lv.s S132, 8 + %2\n" // S132 = v[2] +#if 0 + "vtfm3.t C000, E100, C130\n" // c000 = E100 * C130 +#else + "vdot.t S000, C130, R100\n" // S000 = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0] + "vdot.t S001, C130, R101\n" // S001 = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1] + "vdot.t S002, C130, R102\n" // S002 = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2] +#endif + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *in ), "m"( *v ) + ); +#else out[0] = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0]; out[1] = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1]; out[2] = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2]; +#endif + } void Matrix4x4_ConcatTransforms( matrix4x4 out, const matrix4x4 in1, const matrix4x4 in2 ) @@ -384,6 +790,7 @@ void Matrix4x4_FromOriginQuat( matrix4x4 out, const vec4_t quaternion, const vec out[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2]; out[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0]; out[1][3] = origin[1]; + out[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1]; out[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0]; out[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1]; @@ -396,6 +803,169 @@ void Matrix4x4_FromOriginQuat( matrix4x4 out, const vec4_t quaternion, const vec void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_t origin, float scale ) { +#if XASH_PSP /* performance not tested */ + if( angles[ROLL] ) + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S100, 0 + %1\n" // S100 = angles[PITCH] + "lv.s S101, 4 + %1\n" // S101 = angles[YAW] + "lv.s S102, 8 + %1\n" // S102 = angles[ROLL] + "lv.s S003, 0 + %2\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %2\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %2\n" // S023 = out[2][3] = origin[2] + "lv.s S130, %3\n" // S130 = scale + /**/ + "vfim.s S120, 0.0111111111111111\n" // S121 = 0.0111111111111111 const ( 2 / 180 ) + "vscl.t C100, C100, S120\n" // C100 = C100 * S120 = angles * ( 2 / 180 ) + /**/ + "vsin.t C110, C100\n" // C110 = sin( C100 ) P Y R + "vcos.t C120, C100\n" // C120 = cos( C100 ) P Y R + "vneg.t C100, C110\n" // C100 = -C110 = -sin( C100 ) + /**/ + "vmul.s S000, S120, S121\n" // S000 = S120 * S121 = out[0][0] = ( cp * cy ) + "vmul.s S011, S112, S110\n" // S011 = S112 * S110 = ( sr * sp ) + "vmul.s S001, S011, S121\n" // S001 = S011 * S121 = ( sr * sp * cy ) + "vmul.s S103, S122, S101\n" // S001 = S122 * S101 = ( cr * -sy ) + "vadd.s S001, S001, S103\n" // S001 = S001 + S103 = out[0][1] = ( sr * sp * cy + cr * -sy ) + "vmul.s S012, S122, S110\n" // S002 = S122 * S110 = ( cr * sp ) + "vmul.s S002, S012, S121\n" // S002 = S012 * S121 = ( cr * sp * cy ) + "vmul.s S103, S102, S101\n" // S002 = S102 * S101 = ( -sr * -sy ) + "vadd.s S002, S002, S103\n" // S002 = S002 + S103 = out[0][2] = ( cr * sp * cy + -sr * -sy ) + /**/ + "vmul.s S010, S120, S111\n" // S010 = S120 * S111 = out[1][0] = ( cp * sy ) + "vmul.s S011, S011, S111\n" // S001 = S011 * S111 = ( sr * sp * sy ) + "vmul.s S103, S122, S121\n" // S001 = S122 * S121 = ( cr * cy ) + "vadd.s S011, S011, S103\n" // S011 = S011 + S103 = out[1][1] = ( sr * sp * sy + cr * cy ) + "vmul.s S012, S012, S111\n" // S012 = S012 * S111 = ( cr * sp * sy ) + "vmul.s S103, S102, S121\n" // S103 = S102 * S121 = ( -sr * cy ) + "vadd.s S012, S012, S103\n" // S012 = S012 + S103 = out[1][2] = ( cr * sp * sy + -sr * cy ) + /**/ + "vmov.s S020, S101\n" // S020 = S101 = out[2][0] = ( -sp ) + "vmul.s S021, S112, S120\n" // S021 = S112 * S120 = out[2][1] = ( sr * cp ) + "vmul.s S022, S122, S120\n" // S021 = S122 * S120 = out[2][2] = ( cr * cp ) + /**/ + "vmscl.t E000, E000, S130\n" // E000 = E000 * S103 = out(3) * scale + "vidt.q C030\n" // C030 = [0.0f, 0.0f, 0.0f, 1.0f] + /**/ + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + "usv.q C030, 48 + %0\n" // out[3] = C030 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *angles ), "m"( *origin ), "m"( scale ) + ); + } + else if( angles[PITCH] ) + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S100, 0 + %1\n" // S100 = angles[PITCH] + "lv.s S101, 4 + %1\n" // S101 = angles[YAW] + "lv.s S003, 0 + %2\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %2\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %2\n" // S023 = out[2][3] = origin[2] + "lv.s S130, %3\n" // S130 = scale + /**/ + "vfim.s S120, 0.0111111111111111\n" // S121 = 0.0111111111111111 const ( 2 / 180 ) + "vscl.p C100, C100, S120\n" // C100 = C100 * S120 = angles * ( 2 / 180 ) + /**/ + "vsin.p C110, C100\n" // C110 = sin( C100 ) P Y + "vcos.p C120, C100\n" // C120 = cos( C100 ) P Y + "vneg.p C100, C110\n" // C100 = -C110 = -sin( C100 ) + /**/ + "vmul.s S000, S120, S121\n" // S000 = S120 * S121 = out[0][0] = ( cp * cy ) + "vmov.s S001, S101\n" // S001 = S101 = out[0][1] = ( -sy ) + "vmul.s S002, S110, S121\n" // S001 = S110 * S121 = out[0][2] = ( sp * cy ) + /**/ + "vmul.s S010, S120, S111\n" // S010 = S120 * S111 = out[1][0] = ( cp * sy ) + "vmov.s S011, S121\n" // S011 = S121 = out[1][1] = ( cy ) + "vmul.s S012, S110, S111\n" // S012 = S110 * S111 = out[1][2] = ( sp * sy ) + /**/ + "vmov.s S020, S100\n" // S020 = S100 = out[2][0] = ( -sp ) + "vzero.s S021\n" // S021 = out[2][1] = 0.0f + "vmov.s S022, S120\n" // S022 = S120 = out[2][2] = ( cp ) + /**/ + "vmscl.t E000, E000, S130\n" // E000 = E000 * S103 = out(3) * scale + "vidt.q C030\n" // C030 = [0.0f, 0.0f, 0.0f, 1.0f] + /**/ + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + "usv.q C030, 48 + %0\n" // out[3] = C030 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *angles ), "m"( *origin ), "m"( scale ) + ); + } + else if( angles[YAW] ) + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S101, 4 + %1\n" // S101 = angles[YAW] + "lv.s S003, 0 + %2\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %2\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %2\n" // S023 = out[2][3] = origin[2] + "lv.s S130, %3\n" // S130 = scale + /**/ + "vfim.s S120, 0.0111111111111111\n" // S121 = 0.0111111111111111 const ( 2 / 180 ) + "vmul.s S101, S101, S120\n" // S101 = S101 * S120 = angles[YAW] * ( 2 / 180 ) + /**/ + "vsin.s S111, S101\n" // S111 = sin( S101 ) Y + "vcos.s S121, S101\n" // S121 = cos( S101 ) Y + /**/ + "vzero.p R002\n" // S002 = 0.0f S012 = 0.0f + "vzero.p C020\n" // S020 = 0.0f S021 = 0.0f + "vmov.s S000, S121\n" // S000 = S121 = out[0][0] = ( cy ) + "vneg.s S001, S111\n" // S001 = S111 = out[0][1] = ( -sy ) + "vmov.s S010, S111\n" // S010 = S111 = out[1][0] = ( sy ) + "vmov.s S011, S121\n" // S011 = S121 = out[1][1] = ( cy ) + "vone.s S022\n" // S022 = out[2][2] = 1.0f + /**/ + "vmscl.t E000, E000, S130\n" // E000 = E000 * S103 = out(3) * scale + "vidt.q C030\n" // C030 = [0.0f, 0.0f, 0.0f, 1.0f] + /**/ + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + "usv.q C030, 48 + %0\n" // out[3] = C030 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *angles ), "m"( *origin ), "m"( scale ) + ); + } + else + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S003, 0 + %1\n" // S003 = out[0][3] = origin[0] + "lv.s S013, 4 + %1\n" // S013 = out[1][3] = origin[1] + "lv.s S023, 8 + %1\n" // S023 = out[2][3] = origin[2] + "lv.s S130, %2\n" // S130 = scale + /**/ + "vzero.t C000\n" // C000 = [0.0f, 0.0f, 0.0f] + "vzero.t C010\n" // C010 = [0.0f, 0.0f, 0.0f] + "vzero.t C020\n" // C020 = [0.0f, 0.0f, 0.0f] + "vidt.q C030\n" // C030 = [0.0f, 0.0f, 0.0f, 1.0f] + "vmov.s S000, S130\n" // S000 = S130 = out[0][0] = scale + "vmov.s S011, S130\n" // S011 = S130 = out[1][1] = scale + "vmov.s S022, S130\n" // S022 = S130 = out[2][2] = scale + /**/ + "usv.q C000, 0 + %0\n" // out[0] = C000 + "usv.q C010, 16 + %0\n" // out[1] = C010 + "usv.q C020, 32 + %0\n" // out[2] = C020 + "usv.q C030, 48 + %0\n" // out[3] = C030 + ".set pop\n" // restore assembler option + : "=m"( *out ) + : "m"( *origin ), "m"( scale ) + ); + } +#else float angle, sr, sp, sy, cr, cp, cy; if( angles[ROLL] ) @@ -489,6 +1059,7 @@ void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_ out[3][2] = 0.0f; out[3][3] = 1.0f; } +#endif } void Matrix4x4_ConvertToEntity( const matrix4x4 in, vec3_t angles, vec3_t origin ) @@ -516,6 +1087,34 @@ void Matrix4x4_ConvertToEntity( const matrix4x4 in, vec3_t angles, vec3_t origin void Matrix4x4_TransformPositivePlane( const matrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %2\n" // C100 = in[0] + "ulv.q C110, 16 + %2\n" // C110 = in[1] + "ulv.q C120, 32 + %2\n" // C120 = in[2] + "lv.s S200, 0 + %3\n" // S200 = normal[0] + "lv.s S201, 4 + %3\n" // S201 = normal[1] + "lv.s S202, 8 + %3\n" // S202 = normal[2] + "lv.s S210, %4\n" // S210 = d + "vdot.t S211, C100, C100\n" // S211 = C100 * C100 + "vsqrt.s S211, S211\n" // S211 = sqrt( S211 ) + "vrcp.s S212, S211\n" // S212 = 1 / S211 + "vtfm3.t C000, M100, C200\n" // C000 = M100 * C200 + "vscl.t C000, C000, S212\n" // C000 = C000 * S211 + "vmul.s S003, S210, S211\n" // S003 = S210 * S211 + "vdot.t S010, R103, C000\n" // S010 = R103 * C000 + "vadd.s S003, S003, S010\n" // S003 = S003 + S010 + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + "sv.s S003, %1\n" // dist = S003 + ".set pop\n" // restore assembler option + : "=m"( *out ), "=m"( *dist ) + : "m"( *in ), "m"( *normal ), "m"( d ) + ); +#else float scale = sqrt( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); float iscale = 1.0f / scale; @@ -523,10 +1122,39 @@ void Matrix4x4_TransformPositivePlane( const matrix4x4 in, const vec3_t normal, out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale; out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale; *dist = d * scale + ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] ); +#endif } void Matrix4x4_TransformStandardPlane( const matrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C100, 0 + %2\n" // C100 = in[0] + "ulv.q C110, 16 + %2\n" // C110 = in[1] + "ulv.q C120, 32 + %2\n" // C120 = in[2] + "lv.s S200, 0 + %3\n" // S200 = normal[0] + "lv.s S201, 4 + %3\n" // S201 = normal[1] + "lv.s S202, 8 + %3\n" // S202 = normal[2] + "lv.s S210, %4\n" // S210 = d + "vdot.t S211, C100, C100\n" // S211 = C100 * C100 + "vsqrt.s S211, S211\n" // S211 = sqrt( S211 ) + "vrcp.s S212, S211\n" // S212 = 1 / S211 + "vtfm3.t C000, M100, C200\n" // C000 = M100 * C200 + "vscl.t C000, C000, S212\n" // C000 = C000 * S211 + "vmul.s S003, S210, S211\n" // S003 = S210 * S211 + "vdot.t S010, R103, C000\n" // S010 = R103 * C000 + "vsub.s S003, S003, S010\n" // S003 = S003 - S010 + "sv.s S000, 0 + %0\n" // out[0] = S000 + "sv.s S001, 4 + %0\n" // out[1] = S001 + "sv.s S002, 8 + %0\n" // out[2] = S002 + "sv.s S003, %1\n" // dist = S003 + ".set pop\n" // restore assembler option + : "=m"( *out ), "=m"( *dist ) + : "m"( *in ), "m"( *normal ), "m"( d ) + ); +#else float scale = sqrt( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); float iscale = 1.0f / scale; @@ -534,6 +1162,7 @@ void Matrix4x4_TransformStandardPlane( const matrix4x4 in, const vec3_t normal, out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale; out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale; *dist = d * scale - ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] ); +#endif } void Matrix4x4_Invert_Simple( matrix4x4 out, const matrix4x4 in1 ) @@ -635,7 +1264,7 @@ qboolean Matrix4x4_Invert_Full( matrix4x4 out, const matrix4x4 in1 ) r[3][4] = 0.0f; r[3][5] = 0.0f; r[3][6] = 0.0f; - r[3][7] = 1.0f; + r[3][7] = 1.0f; if( fabs( r[3][0] ) > fabs( r[2][0] )) { diff --git a/public/xash3d_mathlib.c b/public/xash3d_mathlib.c index 39f5d1ba..973bf692 100644 --- a/public/xash3d_mathlib.c +++ b/public/xash3d_mathlib.c @@ -34,7 +34,7 @@ int boxpnt[6][4] = { 7, 5, 1, 3 }, // -X { 7, 3, 2, 6 }, // -Y { 7, 6, 4, 5 }, // -Z -}; +}; // pre-quantized table normals from Quake1 const float m_bytenormals[NUMVERTEXNORMALS][3] = @@ -128,7 +128,7 @@ round the hullsize to nearest 'right' value void RoundUpHullSize( vec3_t size ) { int i, j; - + for( i = 0; i < 3; i++) { qboolean negative = false; @@ -149,7 +149,7 @@ void RoundUpHullSize( vec3_t size ) { result = ( value - hull_table[j] ); if( result <= HULL_PRECISION ) - { + { result = -hull_table[j]; break; } @@ -158,7 +158,7 @@ void RoundUpHullSize( vec3_t size ) { result = ( value - hull_table[j] ); if( result <= HULL_PRECISION ) - { + { result = hull_table[j]; break; } @@ -244,6 +244,23 @@ qboolean PlanesGetIntersectionPoint( const mplane_t *plane1, const mplane_t *pla return true; } +/* +================= +PlaneIntersect + +find point where ray +was intersect with plane +================= +*/ +void PlaneIntersect( const mplane_t *plane, const vec3_t p0, const vec3_t p1, vec3_t out ) +{ + float distToPlane = PlaneDiff( p0, plane ); + float planeDotRay = DotProduct( plane->normal, p1 ); + float sect = -(distToPlane) / planeDotRay; + + VectorMA( p0, sect, p1, out ); +} + /* ================= NearestPOW @@ -289,6 +306,23 @@ rsqrt */ float rsqrt( float number ) { +#if XASH_PSP + float result; + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S000, %1\n" // S000 = number + "vzero.s S001\n" // S111 = 0 + "vcmp.s EZ, S000\n" // CC[0] = ( S000 == 0.0f ) + "vrsq.s S000, S000\n" // S000 = 1.0 / sqrt( S000 ) + "vcmovt.s S000, S001, 0\n" // if ( CC[0] ) S000 = S001 + "sv.s S000, %0\n" // result = S001 + ".set pop\n" // restore assembler option + : "=m"( result ) + : "m"( number ) + ); + return result; +#else int i; float x, y; @@ -302,6 +336,7 @@ float rsqrt( float number ) y = y * (1.5f - (x * y * y)); // first iteration return y; +#endif } /* @@ -311,6 +346,21 @@ SinCos */ void SinCos( float radians, float *sine, float *cosine ) { +#if XASH_PSP + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S000, %2\n" // S000 = radians + "vcst.s S001, VFPU_2_PI\n" // S001 = VFPU_2_PI = 2 / PI + "vmul.s S000, S000, S001\n" // S000 = S000 * S001 + "vrot.p C002, S000, [S, C]\n" // S002 = sin( radians ), S003 = cos( radians ) + "sv.s S002, %0\n" // sine = S002 + "sv.s S003, %1\n" // cosine = S003 + ".set pop\n" // restore assembler option + : "=m"( *sine), "=m"( *cosine ) + : "m"( radians ) + ); +#else #if _MSC_VER == 1200 _asm { @@ -327,6 +377,7 @@ void SinCos( float radians, float *sine, float *cosine ) *sine = sin(radians); *cosine = cos(radians); #endif +#endif } /* @@ -451,7 +502,7 @@ void GAME_EXPORT VectorAngles( const float *forward, float *angles ) if( pitch < 0 ) pitch += 360; } - VectorSet( angles, pitch, yaw, 0 ); + VectorSet( angles, pitch, yaw, 0 ); } /* @@ -489,6 +540,7 @@ void VectorsAngles( const vec3_t forward, const vec3_t right, const vec3_t up, v // // bounds operations // + /* ================= ClearBounds @@ -585,23 +637,6 @@ qboolean SphereIntersect( const vec3_t vSphereCenter, float fSphereRadiusSquared return true; } -/* -================= -PlaneIntersect - -find point where ray -was intersect with plane -================= -*/ -void PlaneIntersect( const mplane_t *plane, const vec3_t p0, const vec3_t p1, vec3_t out ) -{ - float distToPlane = PlaneDiff( p0, plane ); - float planeDotRay = DotProduct( plane->normal, p1 ); - float sect = -(distToPlane) / planeDotRay; - - VectorMA( p0, sect, p1, out ); -} - /* ================= RadiusFromBounds @@ -630,6 +665,42 @@ AngleQuaternion */ void AngleQuaternion( const vec3_t angles, vec4_t q, qboolean studio ) { +#if 0//XASH_PSP /* performance not tested */ + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + + + + /**/ + "lv.s S000, 8 + %1\n" // S000 = angles[ROLL] + "lv.s S001, 0 + %1\n" // S000 = angles[PITCH] + "lv.s S002, 4 + %1\n" // S000 = angles[YAW] + /**/ + "vcst.s S010, VFPU_1_PI\n" // S010 = VFPU_1_PI = 1 / PI + "vscl.t C000, C000, S010\n" // C000 = C000 * S010 = C000 * 0.5f * ( 2 / PI ) + + /**/ + "vcos.t C010, C000\n" // C010 = cos( *angles * 0.5f ) + "vsin.t C000, C000\n" // C000 = sin( *angles * 0.5f ) + "vcrs.t C020, C010, C010\n" // C020 = ( cp*cy, cy*cr, cr*cp ) + "vcrs.t C030, C000, C000\n" // C030 = ( sp*sy, sy*sr, sr*sp ) + "vmul.s S003, S020, S010\n" // S003 = S020 * S010 = cp*cy*cr + "vmul.s S013, S030, S000\n" // S013 = S030 * S000 = sp*sy*sr + "vmul.t C020, C020, C000\n" // C020 = C020 * C000 = ( cp*cy*sr, cy*cr*sp, cr*cp*sy ) + "vmul.t C030, C030, C010\n" // C030 = C030 * C010 = ( sp*sy*cr, sy*sr*cp, sr*sp*cy ) + "vadd.s S003, S003, S013\n" // S003 = S003 + S013 = cr*cp*cy + sr*sp*sy + "vadd.t C000, C020, C030[-X, Y, -Z]\n" + // S000 = S020 - C030 = cp*cy*sr - sp*sy*cr + // S001 = S021 + C031 = cy*cr*sp + sy*sr*cp + // S002 = S022 - C032 = cr*cp*sy - sr*sp*cy + "usv.q C000, %0\n" // *q = C000 + ".set pop\n" // restore assembler option + : "=m"( *q ) + : "m"( *angles ), "r"( studio ) + ); + + printf("ASM [%f %f %f %f]\n", q[0], q[1], q[2], q[3] ); float sr, sp, sy, cr, cp, cy; if( studio ) @@ -649,6 +720,28 @@ void AngleQuaternion( const vec3_t angles, vec4_t q, qboolean studio ) q[1] = cr * sp * cy + sr * cp * sy; // Y q[2] = cr * cp * sy - sr * sp * cy; // Z q[3] = cr * cp * cy + sr * sp * sy; // W + printf("C [%f %f %f %f]\n", q[0], q[1], q[2], q[3] ); +#else + float sr, sp, sy, cr, cp, cy; + + if( studio ) + { + SinCos( angles[ROLL] * 0.5f, &sy, &cy ); + SinCos( angles[YAW] * 0.5f, &sp, &cp ); + SinCos( angles[PITCH] * 0.5f, &sr, &cr ); + } + else + { + SinCos( DEG2RAD( angles[YAW] ) * 0.5f, &sy, &cy ); + SinCos( DEG2RAD( angles[PITCH] ) * 0.5f, &sp, &cp ); + SinCos( DEG2RAD( angles[ROLL] ) * 0.5f, &sr, &cr ); + } + + q[0] = sr * cp * cy - cr * sp * sy; // X + q[1] = cr * sp * cy + sr * cp * sy; // Y + q[2] = cr * cp * sy - sr * sp * cy; // Z + q[3] = cr * cp * cy + sr * sp * sy; // W +#endif } /* @@ -679,20 +772,20 @@ void QuaternionAlign( const vec4_t p, const vec4_t q, vec4_t qt ) float b = 0.0f; int i; - for( i = 0; i < 4; i++ ) + for( i = 0; i < 4; i++ ) { a += (p[i] - q[i]) * (p[i] - q[i]); b += (p[i] + q[i]) * (p[i] + q[i]); } - if( a > b ) + if( a > b ) { - for( i = 0; i < 4; i++ ) + for( i = 0; i < 4; i++ ) qt[i] = -q[i]; } else { - for( i = 0; i < 4; i++ ) + for( i = 0; i < 4; i++ ) qt[i] = q[i]; } } @@ -820,8 +913,184 @@ Returns 1, 2, or 1 + 2 */ int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p ) { - float dist1, dist2; +#if XASH_PSP + int sides; + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S000, 0 + %[normal]\n" // S000 = p->normal[0] + "lv.s S001, 4 + %[normal]\n" // S001 = p->normal[1] + "lv.s S002, 8 + %[normal]\n" // S002 = p->normal[2] + "vzero.p C030\n" // C030 = [0.0f, 0.0f] + "lv.s S032, %[dist]\n" // S032 = p->dist + "move $8, $0\n" // $8 = 0 + "beq %[signbits], $8, 0f\n" // jump to 0 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 1f\n" // jump to 1 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 2f\n" // jump to 2 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 3f\n" // jump to 3 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 4f\n" // jump to 4 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 5f\n" // jump to 5 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 6f\n" // jump to 6 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 7f\n" // jump to 7 + "nop\n" // ( delay slot ) + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "0:\n" +/* + dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +*/ + "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0] + "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1] + "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2] + "lv.s S020, 0 + %[emins]\n" // S020 = emins[0] + "lv.s S021, 4 + %[emins]\n" // S021 = emins[1] + "lv.s S022, 8 + %[emins]\n" // S022 = emins[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "1:\n" +/* + dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +*/ + "lv.s S010, 0 + %[emins]\n" // S010 = emins[0] + "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1] + "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2] + "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0] + "lv.s S021, 4 + %[emins]\n" // S021 = emins[1] + "lv.s S022, 8 + %[emins]\n" // S022 = emins[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "2:\n" +/* + dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +*/ + "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0] + "lv.s S011, 4 + %[emins]\n" // S011 = emins[1] + "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2] + "lv.s S020, 0 + %[emins]\n" // S020 = emins[0] + "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1] + "lv.s S022, 8 + %[emins]\n" // S022 = emins[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "3:\n" +/* + dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +*/ + "lv.s S010, 0 + %[emins]\n" // S010 = emins[0] + "lv.s S011, 4 + %[emins]\n" // S011 = emins[1] + "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2] + "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0] + "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1] + "lv.s S022, 8 + %[emins]\n" // S022 = emins[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "4:\n" +/* + dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +*/ + "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0] + "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1] + "lv.s S012, 8 + %[emins]\n" // S012 = emins[2] + "lv.s S020, 0 + %[emins]\n" // S020 = emins[0] + "lv.s S021, 4 + %[emins]\n" // S021 = emins[1] + "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "5:\n" +/* + dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +*/ + "lv.s S010, 0 + %[emins]\n" // S010 = emins[0] + "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1] + "lv.s S012, 8 + %[emins]\n" // S012 = emins[2] + "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0] + "lv.s S021, 4 + %[emins]\n" // S021 = emins[1] + "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "6:\n" +/* + dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +*/ + "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0] + "lv.s S011, 4 + %[emins]\n" // S011 = emins[1] + "lv.s S012, 8 + %[emins]\n" // S012 = emins[2] + "lv.s S020, 0 + %[emins]\n" // S020 = emins[0] + "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1] + "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "7:\n" +/* + dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +*/ + "lv.s S010, 0 + %[emins]\n" // S010 = emins[0] + "lv.s S011, 4 + %[emins]\n" // S011 = emins[1] + "lv.s S012, 8 + %[emins]\n" // S012 = emins[2] + "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0] + "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1] + "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "8:\n" // SetSides +/* + if( dist1 >= p->dist ) + sides = 1; + if( dist2 < p->dist ) + sides |= 2; +*/ + "addiu %[sides], $0, 0\n" // sides = 0 + "vcmp.s LT, S030, S032\n" // S030 < S032 + "bvt 0, 9f\n" // if ( CC[0] == 1 ) jump to 9 + "nop\n" // ( delay slot ) + "addiu %[sides], %[sides], 1\n"// sides = 1 + "9:\n" + "vcmp.s GE, S031, S032\n" // S031 >= S032 + "bvt 0, 10f\n" // if ( CC[0] == 1 ) jump to 10 + "nop\n" // ( delay slot ) + "addiu %[sides], %[sides], 2\n"// sides = sides + 2 + "10:\n" + ".set pop\n" // restore assembler option + : [sides] "=r" ( sides ) + : [normal] "m" ( p->normal ), + [emaxs] "m" ( *emaxs ), + [emins] "m" ( *emins ), + [signbits] "r" ( p->signbits ), + [dist] "m" ( p->dist ) + : "$8" + ); + return sides; +#else int sides = 0; + float dist1, dist2; // general case switch( p->signbits ) @@ -870,5 +1139,6 @@ int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p ) sides |= 2; return sides; +#endif } diff --git a/ref_gu/exports.txt b/ref_gu/exports.txt new file mode 100644 index 00000000..b79f9600 --- /dev/null +++ b/ref_gu/exports.txt @@ -0,0 +1,2 @@ +GetRefAPI +GetRefHumanReadableName \ No newline at end of file diff --git a/ref_gu/gu_alias.c b/ref_gu/gu_alias.c new file mode 100644 index 00000000..69271ba8 --- /dev/null +++ b/ref_gu/gu_alias.c @@ -0,0 +1,1545 @@ +/* +gu_alias.c - alias model renderer +Copyright (C) 2017 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include "gu_local.h" +#include "xash3d_mathlib.h" +#include "const.h" +#include "r_studioint.h" +#include "triangleapi.h" +#include "alias.h" +#include "pm_local.h" +#include "pmtrace.h" + +extern cvar_t r_shadows; + +typedef struct +{ + double time; + double frametime; + int framecount; // alias framecount + qboolean interpolate; + + float ambientlight; + float shadelight; + vec3_t lightvec; // averaging light direction + vec3_t lightvec_local; // light direction in local space + vec3_t lightspot; // shadow spot + vec3_t lightcolor; // averaging lightcolor + int oldpose; // shadow used + int newpose; // shadow used + float lerpfrac; // lerp frames +} alias_draw_state_t; + +static alias_draw_state_t g_alias; // global alias state + +/* +================================================================= + +ALIAS MODEL DISPLAY LIST GENERATION + +================================================================= +*/ +static qboolean m_fDoRemap; +static aliashdr_t *m_pAliasHeader; +static trivertex_t *g_poseverts[MAXALIASFRAMES]; +static dtriangle_t g_triangles[MAXALIASTRIS]; +static stvert_t g_stverts[MAXALIASVERTS]; +static int g_used[8192]; + +// a pose is a single set of vertexes. a frame may be +// an animating sequence of poses +int g_posenum; + +// the command list holds counts and s/t values that are valid for +// every frame +static int g_commands[8192]; +static int g_numcommands; + +// all frames will have their vertexes rearranged and expanded +// so they are in the order expected by the command list +static int g_vertexorder[8192]; +static int g_numorder; + +static int g_stripverts[128]; +static int g_striptris[128]; +static int g_stripcount; + +/* +==================== +R_StudioInit + +==================== +*/ +void R_AliasInit( void ) +{ + g_alias.interpolate = true; + m_fDoRemap = false; +} + +/* +================ +StripLength +================ +*/ +static int StripLength( int starttri, int startv ) +{ + int m1, m2, j, k; + dtriangle_t *last, *check; + + g_used[starttri] = 2; + + last = &g_triangles[starttri]; + + g_stripverts[0] = last->vertindex[(startv+0) % 3]; + g_stripverts[1] = last->vertindex[(startv+1) % 3]; + g_stripverts[2] = last->vertindex[(startv+2) % 3]; + + g_striptris[0] = starttri; + g_stripcount = 1; + + m1 = last->vertindex[(startv+2)%3]; + m2 = last->vertindex[(startv+1)%3]; +nexttri: + // look for a matching triangle + for( j = starttri + 1, check = &g_triangles[starttri + 1]; j < m_pAliasHeader->numtris; j++, check++ ) + { + if( check->facesfront != last->facesfront ) + continue; + + for( k = 0; k < 3; k++ ) + { + if( check->vertindex[k] != m1 ) + continue; + if( check->vertindex[(k+1) % 3] != m2 ) + continue; + + // this is the next part of the fan + + // if we can't use this triangle, this tristrip is done + if( g_used[j] ) goto done; + + // the new edge + if( g_stripcount & 1 ) + m2 = check->vertindex[(k+2) % 3]; + else m1 = check->vertindex[(k+2) % 3]; + + g_stripverts[g_stripcount+2] = check->vertindex[(k+2) % 3]; + g_striptris[g_stripcount] = j; + g_stripcount++; + + g_used[j] = 2; + goto nexttri; + } + } +done: + // clear the temp used flags + for( j = starttri + 1; j < m_pAliasHeader->numtris; j++ ) + { + if( g_used[j] == 2 ) + g_used[j] = 0; + } + + return g_stripcount; +} + +/* +=========== +FanLength +=========== +*/ +static int FanLength( int starttri, int startv ) +{ + int m1, m2, j, k; + dtriangle_t *last, *check; + + g_used[starttri] = 2; + + last = &g_triangles[starttri]; + + g_stripverts[0] = last->vertindex[(startv+0) % 3]; + g_stripverts[1] = last->vertindex[(startv+1) % 3]; + g_stripverts[2] = last->vertindex[(startv+2) % 3]; + + g_striptris[0] = starttri; + g_stripcount = 1; + + m1 = last->vertindex[(startv+0) % 3]; + m2 = last->vertindex[(startv+2) % 3]; + +nexttri: + // look for a matching triangle + for( j = starttri + 1, check = &g_triangles[starttri + 1]; j < m_pAliasHeader->numtris; j++, check++ ) + { + if( check->facesfront != last->facesfront ) + continue; + + for( k = 0; k < 3; k++ ) + { + if( check->vertindex[k] != m1 ) + continue; + if( check->vertindex[(k+1) % 3] != m2 ) + continue; + + // this is the next part of the fan + // if we can't use this triangle, this tristrip is done + if( g_used[j] ) goto done; + + // the new edge + m2 = check->vertindex[(k+2) % 3]; + + g_stripverts[g_stripcount + 2] = m2; + g_striptris[g_stripcount] = j; + g_stripcount++; + + g_used[j] = 2; + goto nexttri; + } + } +done: + // clear the temp used flags + for( j = starttri + 1; j < m_pAliasHeader->numtris; j++ ) + { + if( g_used[j] == 2 ) + g_used[j] = 0; + } + + return g_stripcount; +} + +/* +================ +BuildTris + +Generate a list of trifans or strips +for the model, which holds for all frames +================ +*/ +void BuildTris( void ) +{ + int len, bestlen, besttype = 0; + int bestverts[1024]; + int besttris[1024]; + int type, startv; + int i, j, k; + float s, t; + + // + // build tristrips + // + memset( g_used, 0, sizeof( g_used )); + g_numcommands = 0; + g_numorder = 0; + + for( i = 0; i < m_pAliasHeader->numtris; i++ ) + { + // pick an unused triangle and start the trifan + if( g_used[i] ) continue; + + bestlen = 0; + for( type = 0; type < 2; type++ ) + { + for( startv = 0; startv < 3; startv++ ) + { + if( type == 1 ) len = StripLength( i, startv ); + else len = FanLength( i, startv ); + + if( len > bestlen ) + { + besttype = type; + bestlen = len; + + for( j = 0; j < bestlen + 2; j++ ) + bestverts[j] = g_stripverts[j]; + + for( j = 0; j < bestlen; j++ ) + besttris[j] = g_striptris[j]; + } + } + } + + // mark the tris on the best strip as used + for( j = 0; j < bestlen; j++ ) + g_used[besttris[j]] = 1; + + if( besttype == 1 ) + g_commands[g_numcommands++] = (bestlen + 2); + else g_commands[g_numcommands++] = -(bestlen + 2); + + for( j = 0; j < bestlen + 2; j++ ) + { + // emit a vertex into the reorder buffer + k = bestverts[j]; + g_vertexorder[g_numorder++] = k; + + // emit s/t coords into the commands stream + s = g_stverts[k].s; + t = g_stverts[k].t; + + if( !g_triangles[besttris[0]].facesfront && g_stverts[k].onseam ) + s += m_pAliasHeader->skinwidth / 2; // on back side + s = (s + 0.5f) / m_pAliasHeader->skinwidth; + t = (t + 0.5f) / m_pAliasHeader->skinheight; + + // Carmack use floats and Valve use shorts here... + *(float *)&g_commands[g_numcommands++] = s; + *(float *)&g_commands[g_numcommands++] = t; + } + } + + g_commands[g_numcommands++] = 0; // end of list marker +} + +/* +================ +GL_MakeAliasModelDisplayLists +================ +*/ +void GL_MakeAliasModelDisplayLists( model_t *m ) +{ + trivertex_t *verts; + int i, j; + + BuildTris( ); + + // save the data out + m_pAliasHeader->poseverts = g_numorder; + + m_pAliasHeader->commands = Mem_Malloc( m->mempool, g_numcommands * 4 ); + memcpy( m_pAliasHeader->commands, g_commands, g_numcommands * 4 ); + + m_pAliasHeader->posedata = Mem_Malloc( m->mempool, m_pAliasHeader->numposes * m_pAliasHeader->poseverts * sizeof( trivertex_t )); + verts = m_pAliasHeader->posedata; + + for( i = 0; i < m_pAliasHeader->numposes; i++ ) + { + for( j = 0; j < g_numorder; j++ ) + *verts++ = g_poseverts[i][g_vertexorder[j]]; + } +} + +/* +============================================================================== + +ALIAS MODELS + +============================================================================== +*/ +/* +================= +Mod_LoadAliasFrame +================= +*/ +void *Mod_LoadAliasFrame( void *pin, maliasframedesc_t *frame ) +{ + daliasframe_t *pdaliasframe; + trivertex_t *pinframe; + int i; + + pdaliasframe = (daliasframe_t *)pin; + + Q_strncpy( frame->name, pdaliasframe->name, sizeof( frame->name )); + frame->firstpose = g_posenum; + frame->numposes = 1; + + for( i = 0; i < 3; i++ ) + { + frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; + frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i]; + } + + pinframe = (trivertex_t *)(pdaliasframe + 1); + + g_poseverts[g_posenum] = (trivertex_t *)pinframe; + pinframe += m_pAliasHeader->numverts; + g_posenum++; + + return (void *)pinframe; +} + +/* +================= +Mod_LoadAliasGroup +================= +*/ +void *Mod_LoadAliasGroup( void *pin, maliasframedesc_t *frame ) +{ + daliasgroup_t *pingroup; + int i, numframes; + daliasinterval_t *pin_intervals; + void *ptemp; + + pingroup = (daliasgroup_t *)pin; + numframes = pingroup->numframes; + + frame->firstpose = g_posenum; + frame->numposes = numframes; + + for( i = 0; i < 3; i++ ) + { + frame->bboxmin.v[i] = pingroup->bboxmin.v[i]; + frame->bboxmax.v[i] = pingroup->bboxmax.v[i]; + } + + pin_intervals = (daliasinterval_t *)(pingroup + 1); + + // all the intervals are always equal 0.1 so we don't care about them + frame->interval = pin_intervals->interval; + pin_intervals += numframes; + ptemp = (void *)pin_intervals; + + for( i = 0; i < numframes; i++ ) + { + g_poseverts[g_posenum] = (trivertex_t *)((daliasframe_t *)ptemp + 1); + ptemp = (trivertex_t *)((daliasframe_t *)ptemp + 1) + m_pAliasHeader->numverts; + g_posenum++; + } + + return ptemp; +} + +/* +=============== +Mod_CreateSkinData +=============== +*/ +rgbdata_t *Mod_CreateSkinData( model_t *mod, byte *data, int width, int height ) +{ + static rgbdata_t skin; + char name[MAX_QPATH]; + int i; + model_t *loadmodel = gEngfuncs.Mod_GetCurrentLoadingModel(); + + skin.width = width; + skin.height = height; + skin.depth = 1; + skin.type = PF_INDEXED_24; + skin.flags = IMAGE_HAS_COLOR|IMAGE_QUAKEPAL; + skin.encode = DXT_ENCODE_DEFAULT; + skin.numMips = 1; + skin.buffer = data; + skin.palette = (byte *)gEngfuncs.CL_GetPaletteColor( 0 ); + skin.size = width * height; + + if( !gEngfuncs.Image_CustomPalette() ) + { + for( i = 0; i < skin.width * skin.height; i++ ) + { + if( data[i] > 224 && data[i] != 255 ) + { + SetBits( skin.flags, IMAGE_HAS_LUMA ); + break; + } + } + } + + COM_FileBase( loadmodel->name, name ); + + // for alias models only player can have remap textures + if( mod != NULL && !Q_stricmp( name, "player" )) + { + texture_t *tx = NULL; + int i, size; + + i = mod->numtextures; + mod->textures = (texture_t **)Mem_Realloc( mod->mempool, mod->textures, ( i + 1 ) * sizeof( texture_t* )); + size = width * height + 768; + tx = Mem_Calloc( mod->mempool, sizeof( *tx ) + size ); + mod->textures[i] = tx; + + Q_strncpy( tx->name, "DM_Skin", sizeof( tx->name )); + tx->anim_min = SHIRT_HUE_START; // topcolor start + tx->anim_max = SHIRT_HUE_END; // topcolor end + // bottomcolor start always equal is (topcolor end + 1) + tx->anim_total = PANTS_HUE_END;// bottomcolor end + + tx->width = width; + tx->height = height; + + // the pixels immediately follow the structures + memcpy( (tx+1), data, width * height ); + memcpy( ((byte *)(tx+1)+(width * height)), skin.palette, 768 ); + mod->numtextures++; // done + } + + // make an copy + return gEngfuncs.FS_CopyImage( &skin ); +} + +void *Mod_LoadSingleSkin( daliasskintype_t *pskintype, int skinnum, int size ) +{ + string name, lumaname; + string checkname; + rgbdata_t *pic; + model_t *loadmodel = gEngfuncs.Mod_GetCurrentLoadingModel(); + + Q_snprintf( name, sizeof( name ), "%s:frame%i", loadmodel->name, skinnum ); + Q_snprintf( lumaname, sizeof( lumaname ), "%s:luma%i", loadmodel->name, skinnum ); + Q_snprintf( checkname, sizeof( checkname ), "%s_%i.tga", loadmodel->name, skinnum ); + if( !gEngfuncs.FS_FileExists( checkname, false ) || ( pic = gEngfuncs.FS_LoadImage( checkname, NULL, 0 )) == NULL ) + pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); + + m_pAliasHeader->gl_texturenum[skinnum][0] = + m_pAliasHeader->gl_texturenum[skinnum][1] = + m_pAliasHeader->gl_texturenum[skinnum][2] = + m_pAliasHeader->gl_texturenum[skinnum][3] = GL_LoadTextureInternal( name, pic, 0 ); + gEngfuncs.FS_FreeImage( pic ); + + if( R_GetTexture( m_pAliasHeader->gl_texturenum[skinnum][0] )->flags & TF_HAS_LUMA ) + { + pic = Mod_CreateSkinData( NULL, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); + m_pAliasHeader->fb_texturenum[skinnum][0] = + m_pAliasHeader->fb_texturenum[skinnum][1] = + m_pAliasHeader->fb_texturenum[skinnum][2] = + m_pAliasHeader->fb_texturenum[skinnum][3] = GL_LoadTextureInternal( lumaname, pic, TF_MAKELUMA ); + gEngfuncs.FS_FreeImage( pic ); + } + + return ((byte *)(pskintype + 1) + size); +} + +void *Mod_LoadGroupSkin( daliasskintype_t *pskintype, int skinnum, int size ) +{ + daliasskininterval_t *pinskinintervals; + daliasskingroup_t *pinskingroup; + string name, lumaname; + rgbdata_t *pic; + int i, j; + model_t *loadmodel = gEngfuncs.Mod_GetCurrentLoadingModel(); + + // animating skin group. yuck. + pskintype++; + pinskingroup = (daliasskingroup_t *)pskintype; + pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); + pskintype = (void *)(pinskinintervals + pinskingroup->numskins); + + for( i = 0; i < pinskingroup->numskins; i++ ) + { + Q_snprintf( name, sizeof( name ), "%s_%i_%i", loadmodel->name, skinnum, i ); + pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); + m_pAliasHeader->gl_texturenum[skinnum][i & 3] = GL_LoadTextureInternal( name, pic, 0 ); + gEngfuncs.FS_FreeImage( pic ); + + if( R_GetTexture( m_pAliasHeader->gl_texturenum[skinnum][i & 3] )->flags & TF_HAS_LUMA ) + { + Q_snprintf( lumaname, sizeof( lumaname ), "%s_%i_%i_luma", loadmodel->name, skinnum, i ); + pic = Mod_CreateSkinData( NULL, (byte *)(pskintype), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); + m_pAliasHeader->fb_texturenum[skinnum][i & 3] = GL_LoadTextureInternal( lumaname, pic, TF_MAKELUMA ); + gEngfuncs.FS_FreeImage( pic ); + } + + pskintype = (daliasskintype_t *)((byte *)(pskintype) + size); + } + + for( j = i; i < 4; i++ ) + { + m_pAliasHeader->gl_texturenum[skinnum][i & 3] = m_pAliasHeader->gl_texturenum[skinnum][i - j]; + m_pAliasHeader->fb_texturenum[skinnum][i & 3] = m_pAliasHeader->fb_texturenum[skinnum][i - j]; + } + + return pskintype; +} + +/* +=============== +Mod_LoadAllSkins +=============== +*/ +void *Mod_LoadAllSkins( int numskins, daliasskintype_t *pskintype ) +{ + int i, size; + + if( numskins < 1 || numskins > MAX_SKINS ) + gEngfuncs.Host_Error( "Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins ); + + size = m_pAliasHeader->skinwidth * m_pAliasHeader->skinheight; + + for( i = 0; i < numskins; i++ ) + { + if( pskintype->type == ALIAS_SKIN_SINGLE ) + { + pskintype = (daliasskintype_t *)Mod_LoadSingleSkin( pskintype, i, size ); + } + else + { + pskintype = (daliasskintype_t *)Mod_LoadGroupSkin( pskintype, i, size ); + } + } + + return (void *)pskintype; +} + +//========================================================================= +/* +================= +Mod_CalcAliasBounds +================= +*/ +void Mod_CalcAliasBounds( model_t *mod ) +{ + int i, j, k; + float radius; + float dist; + vec3_t v; + + ClearBounds( mod->mins, mod->maxs ); + radius = 0.0f; + + // process verts + for( i = 0; i < m_pAliasHeader->numposes; i++ ) + { + for( j = 0; j < m_pAliasHeader->numverts; j++ ) + { + for( k = 0; k < 3; k++ ) + v[k] = g_poseverts[i][j].v[k] * m_pAliasHeader->scale[k] + m_pAliasHeader->scale_origin[k]; + + AddPointToBounds( v, mod->mins, mod->maxs ); + dist = DotProduct( v, v ); + + if( radius < dist ) + radius = dist; + } + } + + mod->radius = sqrt( radius ); +} + +/* +================= +Mod_LoadAliasModel +================= +*/ +void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded ) +{ + daliashdr_t *pinmodel; + stvert_t *pinstverts; + dtriangle_t *pintriangles; + daliasframetype_t *pframetype; + daliasskintype_t *pskintype; + int i, j, size; + + if( loaded ) *loaded = false; + pinmodel = (daliashdr_t *)buffer; + i = pinmodel->version; + + if( i != ALIAS_VERSION ) + { + gEngfuncs.Con_DPrintf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, i, ALIAS_VERSION ); + return; + } + + if( pinmodel->numverts <= 0 || pinmodel->numtris <= 0 || pinmodel->numframes <= 0 ) + return; // how to possible is make that? + + mod->mempool = Mem_AllocPool( va( "^2%s^7", mod->name )); + + // allocate space for a working header, plus all the data except the frames, + // skin and group info + size = sizeof( aliashdr_t ) + (pinmodel->numframes - 1) * sizeof( maliasframedesc_t ); + + m_pAliasHeader = Mem_Calloc( mod->mempool, size ); + mod->flags = pinmodel->flags; // share effects flags + + // endian-adjust and copy the data, starting with the alias model header + m_pAliasHeader->boundingradius = pinmodel->boundingradius; + m_pAliasHeader->numskins = pinmodel->numskins; + m_pAliasHeader->skinwidth = pinmodel->skinwidth; + m_pAliasHeader->skinheight = pinmodel->skinheight; + m_pAliasHeader->numverts = pinmodel->numverts; + m_pAliasHeader->numtris = pinmodel->numtris; + m_pAliasHeader->numframes = pinmodel->numframes; + + if( m_pAliasHeader->numverts > MAXALIASVERTS ) + { + gEngfuncs.Con_DPrintf( S_ERROR "model %s has too many vertices\n", mod->name ); + return; + } + + m_pAliasHeader->size = pinmodel->size; +// mod->synctype = pinmodel->synctype; + mod->numframes = m_pAliasHeader->numframes; + + for( i = 0; i < 3; i++ ) + { + m_pAliasHeader->scale[i] = pinmodel->scale[i]; + m_pAliasHeader->scale_origin[i] = pinmodel->scale_origin[i]; + m_pAliasHeader->eyeposition[i] = pinmodel->eyeposition[i]; + } + + // load the skins + pskintype = (daliasskintype_t *)&pinmodel[1]; + pskintype = Mod_LoadAllSkins( m_pAliasHeader->numskins, pskintype ); + + // load base s and t vertices + pinstverts = (stvert_t *)pskintype; + + for( i = 0; i < m_pAliasHeader->numverts; i++ ) + { + g_stverts[i].onseam = pinstverts[i].onseam; + g_stverts[i].s = pinstverts[i].s; + g_stverts[i].t = pinstverts[i].t; + } + + // load triangle lists + pintriangles = (dtriangle_t *)&pinstverts[m_pAliasHeader->numverts]; + + for( i = 0; i < m_pAliasHeader->numtris; i++ ) + { + g_triangles[i].facesfront = pintriangles[i].facesfront; + + for( j = 0; j < 3; j++ ) + g_triangles[i].vertindex[j] = pintriangles[i].vertindex[j]; + } + + // load the frames + pframetype = (daliasframetype_t *)&pintriangles[m_pAliasHeader->numtris]; + g_posenum = 0; + + for( i = 0; i < m_pAliasHeader->numframes; i++ ) + { + aliasframetype_t frametype = pframetype->type; + + if( frametype == ALIAS_SINGLE ) + pframetype = (daliasframetype_t *)Mod_LoadAliasFrame( pframetype + 1, &m_pAliasHeader->frames[i] ); + else pframetype = (daliasframetype_t *)Mod_LoadAliasGroup( pframetype + 1, &m_pAliasHeader->frames[i] ); + } + + m_pAliasHeader->numposes = g_posenum; + + Mod_CalcAliasBounds( mod ); + mod->type = mod_alias; + + // build the draw lists + GL_MakeAliasModelDisplayLists( mod ); + + // move the complete, relocatable alias model to the cache + gEngfuncs.Mod_GetCurrentLoadingModel()->cache.data = m_pAliasHeader; + + if( loaded ) *loaded = true; // done +} + +void Mod_AliasUnloadTextures( void *data ) +{ + aliashdr_t *palias; + int i, j; + + palias = data; + if( !palias ) return; // already freed + + for( i = 0; i < MAX_SKINS; i++ ) + { + if( !palias->gl_texturenum[i][0] ) + break; + + for( j = 0; j < 4; j++ ) + { + GL_FreeTexture( palias->gl_texturenum[i][j] ); + GL_FreeTexture( palias->fb_texturenum[i][j] ); + } + } +} + +/* +============================================================= + + ALIAS MODELS + +============================================================= +*/ + +/* +=============== +R_AliasDynamicLight + +similar to R_StudioDynamicLight +=============== +*/ +void R_AliasDynamicLight( cl_entity_t *ent, alight_t *plight ) +{ + movevars_t *mv = gEngfuncs.pfnGetMoveVars(); + vec3_t lightDir, vecSrc, vecEnd; + vec3_t origin, dist, finalLight; + float add, radius, total; + colorVec light; + uint lnum; + dlight_t *dl; + + if( !plight || !ent ) + return; + + if( !RI.drawWorld || r_fullbright->value || FBitSet( ent->curstate.effects, EF_FULLBRIGHT )) + { + plight->shadelight = 0; + plight->ambientlight = 192; + + VectorSet( plight->plightvec, 0.0f, 0.0f, -1.0f ); + VectorSet( plight->color, 1.0f, 1.0f, 1.0f ); + return; + } + + // determine plane to get lightvalues from: ceil or floor + if( FBitSet( ent->curstate.effects, EF_INVLIGHT )) + VectorSet( lightDir, 0.0f, 0.0f, 1.0f ); + else VectorSet( lightDir, 0.0f, 0.0f, -1.0f ); + + VectorCopy( ent->origin, origin ); + + VectorSet( vecSrc, origin[0], origin[1], origin[2] - lightDir[2] * 8.0f ); + light.r = light.g = light.b = light.a = 0; + + if(( mv->skycolor_r + mv->skycolor_g + mv->skycolor_b ) != 0 ) + { + msurface_t *psurf = NULL; + pmtrace_t trace; + + if( FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_WRITE_LARGE_COORD )) + { + vecEnd[0] = origin[0] - mv->skyvec_x * 65536.0f; + vecEnd[1] = origin[1] - mv->skyvec_y * 65536.0f; + vecEnd[2] = origin[2] - mv->skyvec_z * 65536.0f; + } + else + { + vecEnd[0] = origin[0] - mv->skyvec_x * 8192.0f; + vecEnd[1] = origin[1] - mv->skyvec_y * 8192.0f; + vecEnd[2] = origin[2] - mv->skyvec_z * 8192.0f; + } + + trace = gEngfuncs.CL_TraceLine( vecSrc, vecEnd, PM_STUDIO_IGNORE ); + if( trace.ent > 0 ) psurf = gEngfuncs.EV_TraceSurface( trace.ent, vecSrc, vecEnd ); + else psurf = gEngfuncs.EV_TraceSurface( 0, vecSrc, vecEnd ); + + if( psurf && FBitSet( psurf->flags, SURF_DRAWSKY )) + { + VectorSet( lightDir, mv->skyvec_x, mv->skyvec_y, mv->skyvec_z ); + + light.r = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_r, 255 )); + light.g = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_g, 255 )); + light.b = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_b, 255 )); + } + } + + if(( light.r + light.g + light.b ) == 0 ) + { + colorVec gcolor; + float grad[4]; + + VectorScale( lightDir, 2048.0f, vecEnd ); + VectorAdd( vecEnd, vecSrc, vecEnd ); + + light = R_LightVec( vecSrc, vecEnd, g_alias.lightspot, g_alias.lightvec ); + + if( VectorIsNull( g_alias.lightvec )) + { + vecSrc[0] -= 16.0f; + vecSrc[1] -= 16.0f; + vecEnd[0] -= 16.0f; + vecEnd[1] -= 16.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[0] += 32.0f; + vecEnd[0] += 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[1] += 32.0f; + vecEnd[1] += 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[0] -= 32.0f; + vecEnd[0] -= 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3]; + lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3]; + VectorNormalize( lightDir ); + } + else + { + VectorCopy( g_alias.lightvec, lightDir ); + } + } + + VectorSet( finalLight, light.r, light.g, light.b ); + ent->cvFloorColor = light; + + total = Q_max( Q_max( light.r, light.g ), light.b ); + if( total == 0.0f ) total = 1.0f; + + // scale lightdir by light intentsity + VectorScale( lightDir, total, lightDir ); + + for( lnum = 0; lnum < MAX_DLIGHTS; lnum++ ) + { + dl = gEngfuncs.GetDynamicLight( lnum ); + + if( dl->die < g_alias.time || !r_dynamic->value ) + continue; + + VectorSubtract( origin, dl->origin, dist ); + + radius = VectorLength( dist ); + add = dl->radius - radius; + + if( add > 0.0f ) + { + total += add; + + if( radius > 1.0f ) + VectorScale( dist, ( add / radius ), dist ); + else VectorScale( dist, add, dist ); + + VectorAdd( lightDir, dist, lightDir ); + + finalLight[0] += gEngfuncs.LightToTexGamma( dl->color.r ) * ( add / 256.0f ) * 2.0f; + finalLight[1] += gEngfuncs.LightToTexGamma( dl->color.g ) * ( add / 256.0f ) * 2.0f; + finalLight[2] += gEngfuncs.LightToTexGamma( dl->color.b ) * ( add / 256.0f ) * 2.0f; + } + } + + VectorScale( lightDir, 0.9f, lightDir ); + + plight->shadelight = VectorLength( lightDir ); + plight->ambientlight = total - plight->shadelight; + + total = Q_max( Q_max( finalLight[0], finalLight[1] ), finalLight[2] ); + + if( total > 0.0f ) + { + plight->color[0] = finalLight[0] * ( 1.0f / total ); + plight->color[1] = finalLight[1] * ( 1.0f / total ); + plight->color[2] = finalLight[2] * ( 1.0f / total ); + } + else VectorSet( plight->color, 1.0f, 1.0f, 1.0f ); + + if( plight->ambientlight > 128 ) + plight->ambientlight = 128; + + if( plight->ambientlight + plight->shadelight > 255 ) + plight->shadelight = 255 - plight->ambientlight; + + VectorNormalize2( lightDir, plight->plightvec ); +} + +/* +=============== +R_AliasSetupLighting + +=============== +*/ +void R_AliasSetupLighting( alight_t *plight ) +{ + if( !m_pAliasHeader || !plight ) + return; + + g_alias.ambientlight = plight->ambientlight; + g_alias.shadelight = plight->shadelight; + VectorCopy( plight->plightvec, g_alias.lightvec ); + VectorCopy( plight->color, g_alias.lightcolor ); + + // transform back to local space + Matrix4x4_VectorIRotate( RI.objectMatrix, g_alias.lightvec, g_alias.lightvec_local ); + VectorNormalize( g_alias.lightvec_local ); +} + +/* +=============== +R_AliasLighting + +=============== +*/ +void R_AliasLighting( float *lv, const vec3_t normal ) +{ + float illum = g_alias.ambientlight; + float r, lightcos; + + lightcos = DotProduct( normal, g_alias.lightvec_local ); // -1 colinear, 1 opposite + if( lightcos > 1.0f ) lightcos = 1.0f; + + illum += g_alias.shadelight; + + r = SHADE_LAMBERT; + + // do modified hemispherical lighting + if( r <= 1.0f ) + { + r += 1.0f; + lightcos = (( r - 1.0f ) - lightcos) / r; + if( lightcos > 0.0f ) + illum += g_alias.shadelight * lightcos; + } + else + { + lightcos = (lightcos + ( r - 1.0f )) / r; + if( lightcos > 0.0f ) + illum -= g_alias.shadelight * lightcos; + } + + illum = Q_max( illum, 0.0f ); + illum = Q_min( illum, 255.0f ); + *lv = illum * (1.0f / 255.0f); +} + +/* +=============== +R_AliasSetRemapColors + +=============== +*/ +void R_AliasSetRemapColors( int newTop, int newBottom ) +{ + gEngfuncs.CL_AllocRemapInfo( RI.currententity, newTop, newBottom ); + + if( gEngfuncs.CL_GetRemapInfoForEntity( RI.currententity )) + { + gEngfuncs.CL_UpdateRemapInfo( RI.currententity, newTop, newBottom ); + m_fDoRemap = true; + } +} + +/* +============= +GL_DrawAliasFrame +============= +*/ +void GL_DrawAliasFrame( aliashdr_t *paliashdr ) +{ + float lv_tmp; + trivertex_t *verts0; + trivertex_t *verts1; + vec3_t vert, norm; + int *order; + int count; + + verts0 = verts1 = paliashdr->posedata; + verts0 += g_alias.oldpose * paliashdr->poseverts; + verts1 += g_alias.newpose * paliashdr->poseverts; + order = paliashdr->commands; + + while( 1 ) + { + // get the vertex count and primitive type + count = *order++; + if( !count ) break; // done + + int prim; + if( count < 0 ) + { + prim = GU_TRIANGLE_FAN; + count = -count; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + gu_vert_ftcv_t* const out = ( gu_vert_ftcv_t* )sceGuGetMemory( sizeof( gu_vert_ftcv_t ) * count ); + for( int index = 0; index < count; index++ ) + { + out[index].u = ((float *)order)[0]; + out[index].v = ((float *)order)[1]; + order += 2; + + VectorLerp( m_bytenormals[verts0->lightnormalindex], g_alias.lerpfrac, m_bytenormals[verts1->lightnormalindex], norm ); + VectorNormalize( norm ); + R_AliasLighting( &lv_tmp, norm ); + out[index].c = GUCOLOR4F( g_alias.lightcolor[0] * lv_tmp, g_alias.lightcolor[1] * lv_tmp, g_alias.lightcolor[2] * lv_tmp, tr.blend ); + VectorLerp( verts0->v, g_alias.lerpfrac, verts1->v, vert ); + out[index].x = vert[0]; + out[index].y = vert[1]; + out[index].z = vert[2]; + verts0++, verts1++; + } + sceGuDrawArray( prim, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF, count, 0, out ); + } +} + +/* +============= +GL_DrawAliasShadow +============= +*/ +void GL_DrawAliasShadow( aliashdr_t *paliashdr ) +{ + trivertex_t *verts0; + trivertex_t *verts1; + float vec_x, vec_y; + vec3_t av, point; + int *order; + float height; + int count; + + if( FBitSet( RI.currententity->curstate.effects, EF_NOSHADOW )) + return; + + if( glState.stencilEnabled ) + sceGuEnable( GU_STENCIL_TEST ); + + height = g_alias.lightspot[2] + 1.0f; + vec_x = -g_alias.lightvec[0] * 8.0f; + vec_y = -g_alias.lightvec[1] * 8.0f; + + r_stats.c_alias_polys += paliashdr->numtris; + + verts0 = verts1 = paliashdr->posedata; + verts0 += g_alias.oldpose * paliashdr->poseverts; + verts1 += g_alias.newpose * paliashdr->poseverts; + order = paliashdr->commands; + + while( 1 ) + { + // get the vertex count and primitive type + count = *order++; + if( !count ) break; // done + + int prim; + if( count < 0 ) + { + prim = GU_TRIANGLE_FAN; + count = -count; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + gu_vert_fv_t* const out = ( gu_vert_fv_t* )sceGuGetMemory( sizeof( gu_vert_fv_t ) * count ); + for( int index = 0; index < count; index++ ) + { + // texture coordinates come from the draw list + // (skipped for shadows) pglTexCoord2fv ((float *)order); + order += 2; + + // normals and vertexes come from the frame list + VectorLerp( verts0->v, g_alias.lerpfrac, verts1->v, av ); + point[0] = av[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; + point[1] = av[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; + point[2] = av[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; + Matrix3x4_VectorTransform( RI.objectMatrix, point, av ); + + point[0] = av[0] - (vec_x * ( av[2] - g_alias.lightspot[2] )); + point[1] = av[1] - (vec_y * ( av[2] - g_alias.lightspot[2] )); + point[2] = g_alias.lightspot[2] + 1.0f; + + out[index].x = point[0]; + out[index].y = point[1]; + out[index].z = point[2]; + + verts0++, verts1++; + } + sceGuDrawArray( prim, GU_VERTEX_32BITF, count, 0, out ); + } + + if( glState.stencilEnabled ) + sceGuDisable( GU_STENCIL_TEST ); +} + +/* +==================== +R_AliasLerpMovement + +==================== +*/ +void R_AliasLerpMovement( cl_entity_t *e ) +{ + float f = 1.0f; + + // don't do it if the goalstarttime hasn't updated in a while. + // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit + // was increased to 1.0 s., which is 2x the max lag we are accounting for. + if( g_alias.interpolate && ( g_alias.time < e->curstate.animtime + 1.0f ) && ( e->curstate.animtime != e->latched.prevanimtime )) + f = ( g_alias.time - e->curstate.animtime ) / ( e->curstate.animtime - e->latched.prevanimtime ); + + if( ENGINE_GET_PARM( PARM_PLAYING_DEMO ) == DEMO_QUAKE1 ) + f = f + 1.0f; + + g_alias.lerpfrac = bound( 0.0f, f, 1.0f ); + + if( e->player || e->curstate.movetype != MOVETYPE_STEP ) + return; // monsters only + + // Con_Printf( "%4.2f %.2f %.2f\n", f, e->curstate.animtime, g_alias.time ); + VectorLerp( e->latched.prevorigin, f, e->curstate.origin, e->origin ); + + if( !VectorCompareEpsilon( e->curstate.angles, e->latched.prevangles, ON_EPSILON )) + { + vec4_t q, q1, q2; + + AngleQuaternion( e->curstate.angles, q1, false ); + AngleQuaternion( e->latched.prevangles, q2, false ); + QuaternionSlerp( q2, q1, f, q ); + QuaternionAngle( q, e->angles ); + } + else VectorCopy( e->curstate.angles, e->angles ); + + // NOTE: this completely over control about angles and don't broke interpolation + if( FBitSet( e->model->flags, ALIAS_ROTATE )) + e->angles[1] = anglemod( 100.0f * g_alias.time ); +} + +/* +================= +R_SetupAliasFrame + +================= +*/ +void R_SetupAliasFrame( cl_entity_t *e, aliashdr_t *paliashdr ) +{ + int newpose, oldpose; + int newframe, oldframe; + int numposes, cycle; + float interval; + + oldframe = e->latched.prevframe; + newframe = e->curstate.frame; + + if( newframe < 0 ) + { + newframe = 0; + } + else if( newframe >= paliashdr->numframes ) + { + if( newframe > paliashdr->numframes ) + gEngfuncs.Con_Reportf( S_WARN "R_GetAliasFrame: no such frame %d (%s)\n", newframe, e->model->name ); + newframe = paliashdr->numframes - 1; + } + + if(( oldframe >= paliashdr->numframes ) || ( oldframe < 0 )) + oldframe = newframe; + + numposes = paliashdr->frames[newframe].numposes; + + if( numposes > 1 ) + { + oldpose = newpose = paliashdr->frames[newframe].firstpose; + interval = 1.0f / paliashdr->frames[newframe].interval; + cycle = (int)(g_alias.time * interval); + oldpose += (cycle + 0) % numposes; // lerpframe from + newpose += (cycle + 1) % numposes; // lerpframe to + g_alias.lerpfrac = ( g_alias.time * interval ); + g_alias.lerpfrac -= (int)g_alias.lerpfrac; + } + else + { + oldpose = paliashdr->frames[oldframe].firstpose; + newpose = paliashdr->frames[newframe].firstpose; + } + + g_alias.oldpose = oldpose; + g_alias.newpose = newpose; + + GL_DrawAliasFrame( paliashdr ); +} + +/* +=============== +R_StudioDrawAbsBBox + +=============== +*/ +static void R_AliasDrawAbsBBox( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ) +{ + vec3_t p[8]; + int i; + + // looks ugly, skip + if( r_drawentities->value != 5 || e == gEngfuncs.GetViewModel() ) + return; + + // compute a full bounding box + for( i = 0; i < 8; i++ ) + { + p[i][0] = ( i & 1 ) ? absmin[0] : absmax[0]; + p[i][1] = ( i & 2 ) ? absmin[1] : absmax[1]; + p[i][2] = ( i & 4 ) ? absmin[2] : absmax[2]; + } + + GL_Bind( XASH_TEXTURE0, tr.whiteTexture ); + sceGuColor( GUCOLOR4F( 0.5f, 0.5f, 1.0f, 0.5f ) ); + TriRenderMode( kRenderTransAdd ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + + unsigned int color = getTriBrightness( g_alias.shadelight / 255.0f ); + + gu_vert_fcv_t* const out = ( gu_vert_fcv_t* )sceGuGetMemory( sizeof( gu_vert_fcv_t ) * 24 ); + for( i = 0; i < 6; i++ ) + { + out[i * 4 + 0].c = color; + out[i * 4 + 0].x = p[boxpnt[i][0]][0]; + out[i * 4 + 0].y = p[boxpnt[i][0]][1]; + out[i * 4 + 0].z = p[boxpnt[i][0]][2]; + out[i * 4 + 1].c = color; + out[i * 4 + 1].x = p[boxpnt[i][1]][0]; + out[i * 4 + 1].y = p[boxpnt[i][1]][1]; + out[i * 4 + 1].z = p[boxpnt[i][1]][2]; + out[i * 4 + 2].c = color; + out[i * 4 + 2].x = p[boxpnt[i][2]][0]; + out[i * 4 + 2].y = p[boxpnt[i][2]][1]; + out[i * 4 + 2].z = p[boxpnt[i][2]][2]; + out[i * 4 + 3].c = color; + out[i * 4 + 3].x = p[boxpnt[i][3]][0]; + out[i * 4 + 3].y = p[boxpnt[i][3]][1]; + out[i * 4 + 3].z = p[boxpnt[i][3]][2]; + } + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 24, 0, out ); + + TriRenderMode( kRenderNormal ); +} + +static void R_AliasDrawLightTrace( cl_entity_t *e ) +{ + if( r_drawentities->value == 7 ) + { + vec3_t origin; + + sceGuDisable( GU_TEXTURE_2D ); + sceGuDisable( GU_DEPTH_TEST ); + + gu_vert_fcv_t* out = ( gu_vert_fcv_t* )sceGuGetMemory( sizeof( gu_vert_fcv_t ) * 4 ); + out[0].c = GU_COLOR( 1.0f, 0.5f, 0.0f, 1.0f ); + out[0].x = e->origin[0]; + out[0].y = e->origin[1]; + out[0].z = e->origin[2]; + out[1].c = out[0].c; + out[1].x = g_alias.lightspot[0]; + out[1].y = g_alias.lightspot[1]; + out[1].z = g_alias.lightspot[2]; + VectorMA( g_alias.lightspot, -64.0f, g_alias.lightvec, origin ); + out[2].c = GU_COLOR( 0.0f, 0.5f, 1.0f, 1.0f ); + out[2].x = g_alias.lightspot[0]; + out[2].y = g_alias.lightspot[1]; + out[2].z = g_alias.lightspot[2]; + out[3].c = out[2].c; + out[3].x = origin[0]; + out[3].y = origin[1]; + out[3].z = origin[2]; + sceGuDrawArray( GU_LINES, GU_COLOR_8888 | GU_VERTEX_32BITF, 4, 0, out ); + + out = ( gu_vert_fcv_t* )sceGuGetMemory( sizeof( gu_vert_fcv_t ) ); + out->c = GU_COLOR( 1.0f, 0.0f, 0.0f, 1.0f ); + out->x = g_alias.lightspot[0]; + out->y = g_alias.lightspot[1]; + out->z = g_alias.lightspot[2]; + sceGuDrawArray( GU_POINTS, GU_COLOR_8888 | GU_VERTEX_32BITF, 1, 0, out ); + + sceGuEnable( GU_DEPTH_TEST ); + sceGuEnable( GU_TEXTURE_2D ); + } +} + +/* +================ +R_AliasSetupTimings + +init current time for a given model +================ +*/ +static void R_AliasSetupTimings( void ) +{ + if( RI.drawWorld ) + { + // synchronize with server time + g_alias.time = gpGlobals->time; + } + else + { + // menu stuff + g_alias.time = gpGlobals->realtime; + } + + m_fDoRemap = false; +} + +/* +================= +R_DrawAliasModel + +================= +*/ +void R_DrawAliasModel( cl_entity_t *e ) +{ + model_t *clmodel; + vec3_t absmin, absmax; + remap_info_t *pinfo = NULL; + int anim, skin; + alight_t lighting; + player_info_t *playerinfo; + vec3_t dir, angles; + + clmodel = RI.currententity->model; + + VectorAdd( e->origin, clmodel->mins, absmin ); + VectorAdd( e->origin, clmodel->maxs, absmax ); + + if( R_CullModel( e, absmin, absmax )) + return; + + // + // locate the proper data + // + m_pAliasHeader = (aliashdr_t *)gEngfuncs.Mod_Extradata( mod_alias, RI.currententity->model ); + if( !m_pAliasHeader ) return; + + // init time + R_AliasSetupTimings(); + + // angles will be modify below keep original + VectorCopy( e->angles, angles ); + + R_AliasLerpMovement( e ); + + if( !FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_COMPENSATE_QUAKE_BUG )) + e->angles[PITCH] = -e->angles[PITCH]; // stupid quake bug + + // don't rotate clients, only aim + if( e->player ) e->angles[PITCH] = 0.0f; + + // + // get lighting information + // + lighting.plightvec = dir; + R_AliasDynamicLight( e, &lighting ); + + r_stats.c_alias_polys += m_pAliasHeader->numtris; + r_stats.c_alias_models_drawn++; + + // + // draw all the triangles + // + + R_RotateForEntity( e ); + + // model and frame independant + R_AliasSetupLighting( &lighting ); + GL_SetRenderMode( e->curstate.rendermode ); + + // setup remapping only for players + if( e->player && ( playerinfo = pfnPlayerInfo( e->curstate.number - 1 )) != NULL ) + { + // get remap colors + int topcolor = bound( 0, playerinfo->topcolor, 13 ); + int bottomcolor = bound( 0, playerinfo->bottomcolor, 13 ); + R_AliasSetRemapColors( topcolor, bottomcolor ); + } + + if( tr.fFlipViewModel ) + { + const ScePspFVector3 translation = + { + m_pAliasHeader->scale_origin[0], + -m_pAliasHeader->scale_origin[1], + m_pAliasHeader->scale_origin[2] + }; + sceGumTranslate( &translation ); + + const ScePspFVector3 scaling = + { + m_pAliasHeader->scale[0], + -m_pAliasHeader->scale[1], + m_pAliasHeader->scale[2] + }; + sceGumScale( &scaling ); + } + else + { + const ScePspFVector3 translation = + { + m_pAliasHeader->scale_origin[0], + m_pAliasHeader->scale_origin[1], + m_pAliasHeader->scale_origin[2] + }; + sceGumTranslate( &translation ); + + const ScePspFVector3 scaling = + { + m_pAliasHeader->scale[0], + m_pAliasHeader->scale[1], + m_pAliasHeader->scale[2] + }; + sceGumScale( &scaling ); + } + + + anim = (int)( g_alias.time * 10 ) & 3; + skin = bound( 0, RI.currententity->curstate.skin, m_pAliasHeader->numskins - 1 ); + if( m_fDoRemap ) pinfo = gEngfuncs.CL_GetRemapInfoForEntity( e ); + + if( r_lightmap->value && !r_fullbright->value ) + GL_Bind( XASH_TEXTURE0, tr.whiteTexture ); + else if( pinfo != NULL && pinfo->textures[skin] != 0 ) + GL_Bind( XASH_TEXTURE0, pinfo->textures[skin] ); // FIXME: allow remapping for skingroups someday + else + { + GL_Bind( XASH_TEXTURE0, m_pAliasHeader->gl_texturenum[skin][anim] ); + + if( FBitSet( R_GetTexture( m_pAliasHeader->gl_texturenum[skin][anim] )->flags, TF_HAS_ALPHA )) + { + sceGuEnable( GU_ALPHA_TEST ); + sceGuAlphaFunc( GU_GREATER, 0x00, 0xff ); + tr.blend = 1.0f; + } + } + + sceGumUpdateMatrix(); //Apply a matrix transformation + + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + + sceGuShadeModel( GU_SMOOTH ); + R_SetupAliasFrame( e, m_pAliasHeader ); + + sceGuShadeModel( GU_FLAT ); + + R_LoadIdentity(); + + // get lerped origin + VectorAdd( e->origin, clmodel->mins, absmin ); + VectorAdd( e->origin, clmodel->maxs, absmax ); + + R_AliasDrawAbsBBox( e, absmin, absmax ); + R_AliasDrawLightTrace( e ); + + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); + sceGuAlphaFunc( GU_GREATER, DEFAULT_ALPHATEST, 0xff ); + sceGuDisable( GU_ALPHA_TEST ); + if( r_shadows.value ) + { + // need to compute transformation matrix + Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, 1.0f ); + sceGuDisable( GU_TEXTURE_2D ); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuEnable(GU_BLEND); + sceGuColor( GUCOLOR4F( 0.0f, 0.0f, 0.0f, 0.5f ) ); + sceGuDepthFunc( GU_LESS ); + + GL_DrawAliasShadow( m_pAliasHeader ); + + sceGuDepthFunc( GU_LEQUAL ); + sceGuEnable( GU_TEXTURE_2D ); + sceGuDisable( GU_BLEND ); + sceGuColor( GUCOLOR4F( 1.0f, 1.0f, 1.0f, 1.0f ) ); + R_LoadIdentity(); + } + + // restore original angles + VectorCopy( angles, e->angles ); +} + +//================================================================================== diff --git a/ref_gu/gu_backend.c b/ref_gu/gu_backend.c new file mode 100644 index 00000000..b8c34cea --- /dev/null +++ b/ref_gu/gu_backend.c @@ -0,0 +1,663 @@ +/* +gl_backend.c - rendering backend +Copyright (C) 2010 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + + +#include "gu_local.h" +#include "xash3d_mathlib.h" + +char r_speeds_msg[MAX_SYSPATH]; +ref_speeds_t r_stats; // r_speeds counters + +/* +=============== +R_SpeedsMessage +=============== +*/ +qboolean R_SpeedsMessage( char *out, size_t size ) +{ + if( gEngfuncs.drawFuncs->R_SpeedsMessage != NULL ) + { + if( gEngfuncs.drawFuncs->R_SpeedsMessage( out, size )) + return true; + // otherwise pass to default handler + } + + if( r_speeds->value <= 0 ) return false; + if( !out || !size ) return false; + + Q_strncpy( out, r_speeds_msg, size ); + + return true; +} + +/* +============== +R_Speeds_Printf + +helper to print into r_speeds message +============== +*/ +void R_Speeds_Printf( const char *msg, ... ) +{ + va_list argptr; + char text[2048]; + + va_start( argptr, msg ); + Q_vsprintf( text, msg, argptr ); + va_end( argptr ); + + Q_strncat( r_speeds_msg, text, sizeof( r_speeds_msg )); +} + +/* +============== +GL_BackendStartFrame +============== +*/ +void GL_BackendStartFrame( void ) +{ + r_speeds_msg[0] = '\0'; +} + +/* +============== +GL_BackendEndFrame +============== +*/ +void GL_BackendEndFrame( void ) +{ + mleaf_t *curleaf; + + if( r_speeds->value <= 0 || !RI.drawWorld ) + return; + + if( !RI.viewleaf ) + curleaf = WORLDMODEL->leafs; + else curleaf = RI.viewleaf; + + R_Speeds_Printf( "Renderer: ^1Engine^7\n\n" ); + + switch( (int)r_speeds->value ) + { + case 1: + Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i wpoly, %3i apoly\n%3i epoly, %3i spoly", + r_stats.c_world_polys, r_stats.c_alias_polys, r_stats.c_studio_polys, r_stats.c_sprite_polys ); + break; + case 2: + R_Speeds_Printf( "visible leafs:\n%3i leafs\ncurrent leaf %3i\n", r_stats.c_world_leafs, curleaf - WORLDMODEL->leafs ); + R_Speeds_Printf( "ReciusiveWorldNode: %3lf secs\nDrawTextureChains %lf\n", r_stats.t_world_node, r_stats.t_world_draw ); + break; + case 3: + Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i alias models drawn\n%3i studio models drawn\n%3i sprites drawn", + r_stats.c_alias_models_drawn, r_stats.c_studio_models_drawn, r_stats.c_sprite_models_drawn ); + break; + case 4: + Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i static entities\n%3i normal entities\n%3i server entities", + r_numStatics, r_numEntities - r_numStatics, ENGINE_GET_PARM( PARM_NUMENTITIES )); + break; + case 5: + Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i tempents\n%3i viewbeams\n%3i particles", + r_stats.c_active_tents_count, r_stats.c_view_beams_count, r_stats.c_particle_count ); + break; + } + + memset( &r_stats, 0, sizeof( r_stats )); +} + +/* +================= +GL_LoadTexMatrix +================= +*/ +void GL_LoadTexMatrix( const matrix4x4 m ) +{ + sceGumMatrixMode( GU_TEXTURE ); + GL_LoadMatrix( m ); + sceGumUpdateMatrix(); + + glState.texIdentityMatrix = false; +} + +/* +================= +GL_LoadTexMatrixExt +================= +*/ +void GL_LoadTexMatrixExt( const float *glmatrix ) +{ + Assert( glmatrix != NULL ); + + sceGumMatrixMode( GU_TEXTURE ); + sceGumLoadMatrix( ( const ScePspFMatrix4 * ) glmatrix ); + sceGumUpdateMatrix(); + + glState.texIdentityMatrix = false; +} + +/* +================= +GL_LoadMatrix +================= +*/ +void GL_LoadMatrix( const matrix4x4 source ) +{ + float dest[16]; + + Matrix4x4_ToArrayFloatGL( source, dest ); + sceGumLoadMatrix( ( const ScePspFMatrix4 * ) dest ); +} + +/* +================= +GL_LoadIdentityTexMatrix +================= +*/ +void GL_LoadIdentityTexMatrix( void ) +{ + if( glState.texIdentityMatrix ) + return; + + sceGumMatrixMode( GU_TEXTURE ); + sceGumLoadIdentity(); + sceGumUpdateMatrix(); + + glState.texIdentityMatrix = true; +} + +/* +================= +GL_SelectTexture +================= +*/ +void GL_SelectTexture( GLint tmu ) +{ + +} + +/* +============== +GL_DisableAllTexGens +============== +*/ +void GL_DisableAllTexGens( void ) +{ + +} + +/* +============== +GL_CleanUpTextureUnits +============== +*/ +void GL_CleanUpTextureUnits( int last ) +{ + +} + +/* +============== +GL_CleanupAllTextureUnits +============== +*/ +void GL_CleanupAllTextureUnits( void ) +{ + +} + +/* +================= +GL_MultiTexCoord2f +================= +*/ +void GL_MultiTexCoord2f( GLenum texture, GLfloat s, GLfloat t ) +{ + +} + +/* +================= +GL_TextureTarget +================= +*/ +void GL_TextureTarget( uint target ) +{ + +} + +/* +================= +GL_TexGen +================= +*/ +void GL_TexGen( GLenum coord, GLenum mode ) +{ + +} + +/* +================= +GL_SetTexCoordArrayMode +================= +*/ +void GL_SetTexCoordArrayMode( GLenum mode ) +{ + +} + +/* +================= +GL_Cull +================= +*/ +void GL_Cull( GLenum cull ) +{ + if( glState.faceCull == cull ) + return; + + if( !cull ) + { + sceGuDisable( GU_CULL_FACE ); + glState.faceCull = 0; + return; + } + + sceGuEnable( GU_CULL_FACE ); + sceGuFrontFace( cull - 1 ); + glState.faceCull = cull; +} + +void GL_SetRenderMode( int mode ) +{ + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + + switch( mode ) + { + case kRenderNormal: + default: + sceGuDisable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + break; + case kRenderTransColor: + case kRenderTransTexture: + sceGuEnable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + break; + case kRenderTransAlpha: + sceGuDisable( GU_BLEND ); + sceGuEnable( GU_ALPHA_TEST ); + break; + case kRenderGlow: + case kRenderTransAdd: + sceGuEnable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, GUBLEND1 ); + break; + } +} + +/* +============================================================================== + +SCREEN SHOTS + +============================================================================== +*/ +// used for 'env' and 'sky' shots +typedef struct envmap_s +{ + vec3_t angles; + int flags; +} envmap_t; + +const envmap_t r_skyBoxInfo[6] = +{ +{{ 0, 270, 180}, IMAGE_FLIP_X }, +{{ 0, 90, 180}, IMAGE_FLIP_X }, +{{ -90, 0, 180}, IMAGE_FLIP_X }, +{{ 90, 0, 180}, IMAGE_FLIP_X }, +{{ 0, 0, 180}, IMAGE_FLIP_X }, +{{ 0, 180, 180}, IMAGE_FLIP_X }, +}; + +const envmap_t r_envMapInfo[6] = +{ +{{ 0, 0, 90}, 0 }, +{{ 0, 180, -90}, 0 }, +{{ 0, 90, 0}, 0 }, +{{ 0, 270, 180}, 0 }, +{{-90, 180, -90}, 0 }, +{{ 90, 0, 90}, 0 } +}; + +qboolean VID_ScreenShot( const char *filename, int shot_type ) +{ +#if 0 + rgbdata_t *r_shot; + uint flags = IMAGE_FLIP_Y; + int width = 0, height = 0; + qboolean result; + + r_shot = Mem_Calloc( r_temppool, sizeof( rgbdata_t )); + r_shot->width = (gpGlobals->width + 3) & ~3; + r_shot->height = (gpGlobals->height + 3) & ~3; + r_shot->flags = IMAGE_HAS_COLOR; + r_shot->type = PF_RGB_24; + r_shot->size = r_shot->width * r_shot->height * gEngfuncs.Image_GetPFDesc( r_shot->type )->bpp; + r_shot->palette = NULL; + r_shot->buffer = Mem_Malloc( r_temppool, r_shot->size ); + + // get screen frame + pglReadPixels( 0, 0, r_shot->width, r_shot->height, GL_RGB, GL_UNSIGNED_BYTE, r_shot->buffer ); + + switch( shot_type ) + { + case VID_SCREENSHOT: + break; + case VID_SNAPSHOT: + gEngfuncs.FS_AllowDirectPaths( true ); + break; + case VID_LEVELSHOT: + flags |= IMAGE_RESAMPLE; + if( gpGlobals->wideScreen ) + { + height = 480; + width = 800; + } + else + { + height = 480; + width = 640; + } + break; + case VID_MINISHOT: + flags |= IMAGE_RESAMPLE; + height = 200; + width = 320; + break; + case VID_MAPSHOT: + flags |= IMAGE_RESAMPLE|IMAGE_QUANTIZE; // GoldSrc request overviews in 8-bit format + height = 768; + width = 1024; + break; + } + + gEngfuncs.Image_Process( &r_shot, width, height, flags, 0.0f ); + + // write image + result = gEngfuncs.FS_SaveImage( filename, r_shot ); + gEngfuncs.FS_AllowDirectPaths( false ); // always reset after store screenshot + gEngfuncs.FS_FreeImage( r_shot ); + + return result; +#else + return 0; +#endif +} + +/* +================= +VID_CubemapShot +================= +*/ +qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot ) +{ +#if 0 + rgbdata_t *r_shot, *r_side; + byte *temp = NULL; + byte *buffer = NULL; + string basename; + int i = 1, flags, result; + + if( !RI.drawWorld || !WORLDMODEL ) + return false; + + // make sure the specified size is valid + while( i < size ) i<<=1; + + if( i != size ) return false; + if( size > gpGlobals->width || size > gpGlobals->height ) + return false; + + // alloc space + temp = Mem_Malloc( r_temppool, size * size * 3 ); + buffer = Mem_Malloc( r_temppool, size * size * 3 * 6 ); + r_shot = Mem_Calloc( r_temppool, sizeof( rgbdata_t )); + r_side = Mem_Calloc( r_temppool, sizeof( rgbdata_t )); + + // use client vieworg + if( !vieworg ) vieworg = RI.vieworg; + + R_CheckGamma(); + + for( i = 0; i < 6; i++ ) + { + // go into 3d mode + R_Set2DMode( false ); + + if( skyshot ) + { + R_DrawCubemapView( vieworg, r_skyBoxInfo[i].angles, size ); + flags = r_skyBoxInfo[i].flags; + } + else + { + R_DrawCubemapView( vieworg, r_envMapInfo[i].angles, size ); + flags = r_envMapInfo[i].flags; + } + + pglReadPixels( 0, 0, size, size, GL_RGB, GL_UNSIGNED_BYTE, temp ); + r_side->flags = IMAGE_HAS_COLOR; + r_side->width = r_side->height = size; + r_side->type = PF_RGB_24; + r_side->size = r_side->width * r_side->height * 3; + r_side->buffer = temp; + + if( flags ) gEngfuncs.Image_Process( &r_side, 0, 0, flags, 0.0f ); + memcpy( buffer + (size * size * 3 * i), r_side->buffer, size * size * 3 ); + } + + r_shot->flags = IMAGE_HAS_COLOR; + r_shot->flags |= (skyshot) ? IMAGE_SKYBOX : IMAGE_CUBEMAP; + r_shot->width = size; + r_shot->height = size; + r_shot->type = PF_RGB_24; + r_shot->size = r_shot->width * r_shot->height * 3 * 6; + r_shot->palette = NULL; + r_shot->buffer = buffer; + + // make sure what we have right extension + Q_strncpy( basename, base, MAX_STRING ); + COM_StripExtension( basename ); + COM_DefaultExtension( basename, ".tga" ); + + // write image as 6 sides + result = gEngfuncs.FS_SaveImage( basename, r_shot ); + gEngfuncs.FS_FreeImage( r_shot ); + gEngfuncs.FS_FreeImage( r_side ); + + return result; +#else + return 0; +#endif +} + +//======================================================= + +/* +=============== +R_ShowTextures + +Draw all the images to the screen, on top of whatever +was there. This is used to test for texture thrashing. +=============== +*/ +void R_ShowTextures( void ) +{ +#if 0 + gl_texture_t *image; + float x, y, w, h; + int total, start, end; + int i, j, k, base_w, base_h; + rgba_t color = { 192, 192, 192, 255 }; + int charHeight, numTries = 0; + static qboolean showHelp = true; + string shortname; + + if( !CVAR_TO_BOOL( gl_showtextures )) + return; + + if( showHelp ) + { + gEngfuncs.CL_CenterPrint( "use '<-' and '->' keys to change atlas page, ESC to quit", 0.25f ); + showHelp = false; + } + + GL_SetRenderMode( kRenderNormal ); + pglClear( GL_COLOR_BUFFER_BIT ); + pglFinish(); + + base_w = 8; // textures view by horizontal + base_h = 6; // textures view by vertical + +rebuild_page: + total = base_w * base_h; + start = total * (gl_showtextures->value - 1); + end = total * gl_showtextures->value; + if( end > MAX_TEXTURES ) end = MAX_TEXTURES; + + w = gpGlobals->width / base_w; + h = gpGlobals->height / base_h; + + gEngfuncs.Con_DrawStringLen( NULL, NULL, &charHeight ); + + for( i = j = 0; i < MAX_TEXTURES; i++ ) + { + image = R_GetTexture( i ); + if( j == start ) break; // found start + if( pglIsTexture( image->texnum )) j++; + } + + if( i == MAX_TEXTURES && gl_showtextures->value != 1 ) + { + // bad case, rewind to one and try again + gEngfuncs.Cvar_SetValue( "r_showtextures", max( 1, gl_showtextures->value - 1 )); + if( ++numTries < 2 ) goto rebuild_page; // to prevent infinite loop + } + + for( k = 0; i < MAX_TEXTURES; i++ ) + { + if( j == end ) break; // page is full + + image = R_GetTexture( i ); + if( !pglIsTexture( image->texnum )) + continue; + + x = k % base_w * w; + y = k / base_w * h; + + pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + GL_Bind( XASH_TEXTURE0, i ); // NOTE: don't use image->texnum here, because skybox has a 'wrong' indexes + + if( FBitSet( image->flags, TF_DEPTHMAP ) && !FBitSet( image->flags, TF_NOCOMPARE )) + pglTexParameteri( image->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE ); + + pglBegin( GL_QUADS ); + pglTexCoord2f( 0, 0 ); + pglVertex2f( x, y ); + if( image->target == GL_TEXTURE_RECTANGLE_EXT ) + pglTexCoord2f( image->width, 0 ); + else pglTexCoord2f( 1, 0 ); + pglVertex2f( x + w, y ); + if( image->target == GL_TEXTURE_RECTANGLE_EXT ) + pglTexCoord2f( image->width, image->height ); + else pglTexCoord2f( 1, 1 ); + pglVertex2f( x + w, y + h ); + if( image->target == GL_TEXTURE_RECTANGLE_EXT ) + pglTexCoord2f( 0, image->height ); + else pglTexCoord2f( 0, 1 ); + pglVertex2f( x, y + h ); + pglEnd(); + + if( FBitSet( image->flags, TF_DEPTHMAP ) && !FBitSet( image->flags, TF_NOCOMPARE )) + pglTexParameteri( image->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB ); + + COM_FileBase( image->name, shortname ); + if( Q_strlen( shortname ) > 18 ) + { + // cutoff too long names, it looks ugly + shortname[16] = '.'; + shortname[17] = '.'; + shortname[18] = '\0'; + } + gEngfuncs.Con_DrawString( x + 1, y + h - charHeight, shortname, color ); + j++, k++; + } + + gEngfuncs.CL_DrawCenterPrint (); + pglFinish(); +#endif +} + +/* +================ +SCR_TimeRefresh_f + +timerefresh [noflip] +================ +*/ +void SCR_TimeRefresh_f( void ) +{ + int i; + double start, stop; + double time; + + if( ENGINE_GET_PARM( PARM_CONNSTATE ) != ca_active ) + return; + + start = gEngfuncs.pfnTime(); + + // run without page flipping like GoldSrc + if( gEngfuncs.Cmd_Argc() == 1 ) + { +#if 0 + pglDrawBuffer( GL_FRONT ); +#endif + for( i = 0; i < 128; i++ ) + { + gpGlobals->viewangles[1] = i / 128.0f * 360.0f; + R_RenderScene(); + } +#if 0 + pglFinish(); +#endif + R_EndFrame(); + } + else + { + for( i = 0; i < 128; i++ ) + { + R_BeginFrame( true ); + gpGlobals->viewangles[1] = i / 128.0f * 360.0f; + R_RenderScene(); + R_EndFrame(); + } + } + + stop = gEngfuncs.pfnTime (); + time = (stop - start); + gEngfuncs.Con_Printf( "%f seconds (%f fps)\n", time, 128 / time ); +} diff --git a/ref_gu/gu_beams.c b/ref_gu/gu_beams.c new file mode 100644 index 00000000..aedbde39 --- /dev/null +++ b/ref_gu/gu_beams.c @@ -0,0 +1,1544 @@ +/* +gl_beams.c - beams rendering +Copyright (C) 2009 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "r_efx.h" +#include "event_flags.h" +#include "entity_types.h" +#include "triangleapi.h" +#include "customentity.h" +#include "cl_tent.h" +#include "pm_local.h" +#include "studio.h" + +#define NOISE_DIVISIONS 64 // don't touch - many tripmines cause the crash when it equal 128 + +typedef struct +{ + vec3_t pos; + float texcoord; // Y texture coordinate + float width; +} beamseg_t; + +/* +============================================================== + +FRACTAL NOISE + +============================================================== +*/ +static float rgNoise[NOISE_DIVISIONS+1]; // global noise array + +// freq2 += step * 0.1; +// Fractal noise generator, power of 2 wavelength +static void FracNoise( float *noise, int divs ) +{ + int div2; + + div2 = divs >> 1; + if( divs < 2 ) return; + + // noise is normalized to +/- scale + noise[div2] = ( noise[0] + noise[divs] ) * 0.5f + divs * gEngfuncs.COM_RandomFloat( -0.125f, 0.125f ); + + if( div2 > 1 ) + { + FracNoise( &noise[div2], div2 ); + FracNoise( noise, div2 ); + } +} + +static void SineNoise( float *noise, int divs ) +{ + float freq = 0; + float step = M_PI_F / (float)divs; + int i; + + for( i = 0; i < divs; i++ ) + { + noise[i] = sin( freq ); + freq += step; + } +} + + +/* +============================================================== + +BEAM MATHLIB + +============================================================== +*/ +static void R_BeamComputePerpendicular( const vec3_t vecBeamDelta, vec3_t pPerp ) +{ + // direction in worldspace of the center of the beam + vec3_t vecBeamCenter; + + VectorNormalize2( vecBeamDelta, vecBeamCenter ); + CrossProduct( RI.vforward, vecBeamCenter, pPerp ); + VectorNormalize( pPerp ); +} + +static void R_BeamComputeNormal( const vec3_t vStartPos, const vec3_t vNextPos, vec3_t pNormal ) +{ + // vTangentY = line vector for beam + vec3_t vTangentY, vDirToBeam; + + VectorSubtract( vStartPos, vNextPos, vTangentY ); + + // vDirToBeam = vector from viewer origin to beam + VectorSubtract( vStartPos, RI.vieworg, vDirToBeam ); + + // get a vector that is perpendicular to us and perpendicular to the beam. + // this is used to fatten the beam. + CrossProduct( vTangentY, vDirToBeam, pNormal ); + VectorNormalizeFast( pNormal ); +} + + +/* +============== +R_BeamCull + +Cull the beam by bbox +============== +*/ +qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly ) +{ + vec3_t mins, maxs; + int i; + + for( i = 0; i < 3; i++ ) + { + if( start[i] < end[i] ) + { + mins[i] = start[i]; + maxs[i] = end[i]; + } + else + { + mins[i] = end[i]; + maxs[i] = start[i]; + } + + // don't let it be zero sized + if( mins[i] == maxs[i] ) + maxs[i] += 1.0f; + } + + // check bbox + if( gEngfuncs.Mod_BoxVisible( mins, maxs, Mod_GetCurrentVis( ))) + { + if( pvsOnly || !R_CullBox( mins, maxs )) + { + // beam is visible + return false; + } + } + + // beam is culled + return true; +} + +/* +================ +CL_AddCustomBeam + +Add the beam that encoded as custom entity +================ +*/ +void CL_AddCustomBeam( cl_entity_t *pEnvBeam ) +{ + if( tr.draw_list->num_beam_entities >= MAX_VISIBLE_PACKET ) + { + gEngfuncs.Con_Printf( S_ERROR "Too many beams %d!\n", tr.draw_list->num_beam_entities ); + return; + } + + if( pEnvBeam ) + { + tr.draw_list->beam_entities[tr.draw_list->num_beam_entities] = pEnvBeam; + tr.draw_list->num_beam_entities++; + } +} + + +/* +============================================================== + +BEAM DRAW METHODS + +============================================================== +*/ +/* +================ +R_DrawSegs + +general code for drawing beams +================ +*/ +static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments, int flags ) +{ + int noiseIndex, noiseStep; + int i, total_segs, segs_drawn; + float div, length, fraction, factor; + float flMaxWidth, vLast, vStep, brightness; + vec3_t perp1, vLastNormal; + beamseg_t curSeg; + + if( segments < 2 ) return; + + length = VectorLength( delta ); + flMaxWidth = width * 0.5f; + div = 1.0f / ( segments - 1 ); + + if( length * div < flMaxWidth * 1.414f ) + { + // here, we have too many segments; we could get overlap... so lets have less segments + segments = (int)( length / ( flMaxWidth * 1.414f )) + 1.0f; + if( segments < 2 ) segments = 2; + } + + if( segments > NOISE_DIVISIONS ) + segments = NOISE_DIVISIONS; + + div = 1.0f / (segments - 1); + length *= 0.01f; + vStep = length * div; // Texture length texels per space pixel + + // Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture) + vLast = fmod( freq * speed, 1 ); + + if( flags & FBEAM_SINENOISE ) + { + if( segments < 16 ) + { + segments = 16; + div = 1.0f / ( segments - 1 ); + } + scale *= 100.0f; + length = segments * 0.1f; + } + else + { + scale *= length * 2.0f; + } + + // Iterator to resample noise waveform (it needs to be generated in powers of 2) + noiseStep = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f ); + brightness = 1.0f; + noiseIndex = 0; + + if( FBitSet( flags, FBEAM_SHADEIN )) + brightness = 0; + + // Choose two vectors that are perpendicular to the beam + R_BeamComputePerpendicular( delta, perp1 ); + + total_segs = segments; + segs_drawn = 0; +#if 1 + gu_vert_ftcnv_t* const out = ( gu_vert_ftcnv_t* )extGuBeginPacket( NULL ); + unsigned int vert_size = 0; + unsigned int vert_color = 0; +#endif + // specify all the segments. + for( i = 0; i < segments; i++ ) + { + beamseg_t nextSeg; + vec3_t vPoint1, vPoint2; + + Assert( noiseIndex < ( NOISE_DIVISIONS << 16 )); + + fraction = i * div; + + VectorMA( source, fraction, delta, nextSeg.pos ); + + // distort using noise + if( scale != 0 ) + { + factor = rgNoise[noiseIndex>>16] * scale; + + if( FBitSet( flags, FBEAM_SINENOISE )) + { + float s, c; + + SinCos( fraction * M_PI_F * length + freq, &s, &c ); + VectorMA( nextSeg.pos, (factor * s), RI.vup, nextSeg.pos ); + + // rotate the noise along the perpendicluar axis a bit to keep the bolt from looking diagonal + VectorMA( nextSeg.pos, (factor * c), RI.vright, nextSeg.pos ); + } + else + { + VectorMA( nextSeg.pos, factor, perp1, nextSeg.pos ); + } + } + + // specify the next segment. + nextSeg.width = width * 2.0f; + nextSeg.texcoord = vLast; + + if( segs_drawn > 0 ) + { + // Get a vector that is perpendicular to us and perpendicular to the beam. + // This is used to fatten the beam. + vec3_t vNormal, vAveNormal; + + R_BeamComputeNormal( curSeg.pos, nextSeg.pos, vNormal ); + + if( segs_drawn > 1 ) + { + // Average this with the previous normal + VectorAdd( vNormal, vLastNormal, vAveNormal ); + VectorScale( vAveNormal, 0.5f, vAveNormal ); + VectorNormalizeFast( vAveNormal ); + } + else + { + VectorCopy( vNormal, vAveNormal ); + } + + VectorCopy( vNormal, vLastNormal ); + + // draw regular segment + VectorMA( curSeg.pos, ( curSeg.width * 0.5f ), vAveNormal, vPoint1 ); + VectorMA( curSeg.pos, (-curSeg.width * 0.5f ), vAveNormal, vPoint2 ); +#if 1 + vert_color = getTriBrightness( brightness ); + out[vert_size].u = 0.0f; + out[vert_size].v = curSeg.texcoord; + out[vert_size].c = vert_color; + out[vert_size].nx = vAveNormal[0]; + out[vert_size].ny = vAveNormal[1]; + out[vert_size].nz = vAveNormal[2]; + out[vert_size].x = vPoint1[0]; + out[vert_size].y = vPoint1[1]; + out[vert_size].z = vPoint1[2]; + vert_size++; + out[vert_size].u = 1.0f; + out[vert_size].v = curSeg.texcoord; + out[vert_size].c = vert_color; + out[vert_size].nx = vAveNormal[0]; + out[vert_size].ny = vAveNormal[1]; + out[vert_size].nz = vAveNormal[2]; + out[vert_size].x = vPoint2[0]; + out[vert_size].y = vPoint2[1]; + out[vert_size].z = vPoint2[2]; + vert_size++; +#else + pglTexCoord2f( 0.0f, curSeg.texcoord ); + TriBrightness( brightness ); + pglNormal3fv( vAveNormal ); + pglVertex3fv( vPoint1 ); + + pglTexCoord2f( 1.0f, curSeg.texcoord ); + TriBrightness( brightness ); + pglNormal3fv( vAveNormal ); + pglVertex3fv( vPoint2 ); +#endif + } + + curSeg = nextSeg; + segs_drawn++; + + if( FBitSet( flags, FBEAM_SHADEIN ) && FBitSet( flags, FBEAM_SHADEOUT )) + { + if( fraction < 0.5f ) brightness = fraction; + else brightness = ( 1.0f - fraction ); + } + else if( FBitSet( flags, FBEAM_SHADEIN )) + { + brightness = fraction; + } + else if( FBitSet( flags, FBEAM_SHADEOUT )) + { + brightness = 1.0f - fraction; + } + + if( segs_drawn == total_segs ) + { + // draw the last segment + VectorMA( curSeg.pos, ( curSeg.width * 0.5f ), vLastNormal, vPoint1 ); + VectorMA( curSeg.pos, (-curSeg.width * 0.5f ), vLastNormal, vPoint2 ); + + // specify the points. +#if 1 + vert_color = getTriBrightness( brightness ); + out[vert_size].u = 0.0f; + out[vert_size].v = curSeg.texcoord; + out[vert_size].c = vert_color; + out[vert_size].nx = vLastNormal[0]; + out[vert_size].ny = vLastNormal[1]; + out[vert_size].nz = vLastNormal[2]; + out[vert_size].x = vPoint1[0]; + out[vert_size].y = vPoint1[1]; + out[vert_size].z = vPoint1[2]; + vert_size++; + out[vert_size].u = 1.0f; + out[vert_size].v = curSeg.texcoord; + out[vert_size].c = vert_color; + out[vert_size].nx = vLastNormal[0]; + out[vert_size].ny = vLastNormal[1]; + out[vert_size].nz = vLastNormal[2]; + out[vert_size].x = vPoint2[0]; + out[vert_size].y = vPoint2[1]; + out[vert_size].z = vPoint2[2]; + vert_size++; +#else + pglTexCoord2f( 0.0f, curSeg.texcoord ); + TriBrightness( brightness ); + pglNormal3fv( vLastNormal ); + pglVertex3fv( vPoint1 ); + + pglTexCoord2f( 1.0f, curSeg.texcoord ); + TriBrightness( brightness ); + pglNormal3fv( vLastNormal ); + pglVertex3fv( vPoint2 ); +#endif + } + + vLast += vStep; // Advance texture scroll (v axis only) + noiseIndex += noiseStep; + } +#if 1 + extGuEndPacket( ( void * )( out + vert_size ) ); + sceGuDrawArray( GU_TRIANGLE_STRIP, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_NORMAL_32BITF | GU_VERTEX_32BITF, vert_size, 0, out ); +#endif +} + +/* +================ +R_DrawTorus + +Draw beamtours +================ +*/ +void R_DrawTorus( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments ) +{ + int i, noiseIndex, noiseStep; + float div, length, fraction, factor, vLast, vStep; + vec3_t last1, last2, point, screen, screenLast, tmp, normal; + + if( segments < 2 ) + return; + + if( segments > NOISE_DIVISIONS ) + segments = NOISE_DIVISIONS; + + length = VectorLength( delta ) * 0.01f; + if( length < 0.5f ) length = 0.5f; // don't lose all of the noise/texture on short beams + + div = 1.0f / (segments - 1); + + vStep = length * div; // Texture length texels per space pixel + + // Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture) + vLast = fmod( freq * speed, 1 ); + scale = scale * length; + + // Iterator to resample noise waveform (it needs to be generated in powers of 2) + noiseStep = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f ); + noiseIndex = 0; +#if 1 + gu_vert_ftv_t* const out = ( gu_vert_ftv_t* )extGuBeginPacket( NULL ); + unsigned int vert_size = 0; +#endif + for( i = 0; i < segments; i++ ) + { + float s, c; + + fraction = i * div; + SinCos( fraction * M_PI2_F, &s, &c ); + + point[0] = s * freq * delta[2] + source[0]; + point[1] = c * freq * delta[2] + source[1]; + point[2] = source[2]; + + // distort using noise + if( scale != 0 ) + { + if(( noiseIndex >> 16 ) < NOISE_DIVISIONS ) + { + factor = rgNoise[noiseIndex>>16] * scale; + VectorMA( point, factor, RI.vup, point ); + + // rotate the noise along the perpendicluar axis a bit to keep the bolt from looking diagonal + factor = rgNoise[noiseIndex>>16] * scale * cos( fraction * M_PI_F * 3 + freq ); + VectorMA( point, factor, RI.vright, point ); + } + } + + // Transform point into screen space + TriWorldToScreen( point, screen ); + + if( i != 0 ) + { + // build world-space normal to screen-space direction vector + VectorSubtract( screen, screenLast, tmp ); + + // we don't need Z, we're in screen space + tmp[2] = 0; + VectorNormalize( tmp ); + VectorScale( RI.vup, -tmp[0], normal ); // Build point along noraml line (normal is -y, x) + VectorMA( normal, tmp[1], RI.vright, normal ); + + // Make a wide line + VectorMA( point, width, normal, last1 ); + VectorMA( point, -width, normal, last2 ); + + vLast += vStep; // advance texture scroll (v axis only) +#if 1 + out[vert_size].u = 1.0f; + out[vert_size].v = vLast; + out[vert_size].x = last2[0]; + out[vert_size].y = last2[1]; + out[vert_size].z = last2[2]; + vert_size++; + out[vert_size].u = 0.0f; + out[vert_size].v = vLast; + out[vert_size].x = last1[0]; + out[vert_size].y = last1[1]; + out[vert_size].z = last1[2]; + vert_size++; +#else + TriTexCoord2f( 1, vLast ); + TriVertex3fv( last2 ); + TriTexCoord2f( 0, vLast ); + TriVertex3fv( last1 ); +#endif + } + + VectorCopy( screen, screenLast ); + noiseIndex += noiseStep; + } +#if 1 + extGuEndPacket( ( void * )( out + vert_size ) ); + sceGuDrawArray( GU_TRIANGLE_STRIP, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, vert_size, 0, out ); +#endif +} + +/* +================ +R_DrawDisk + +Draw beamdisk +================ +*/ +void R_DrawDisk( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments ) +{ + float div, length, fraction; + float w, vLast, vStep; + vec3_t point; + int i; + + if( segments < 2 ) + return; + + if( segments > NOISE_DIVISIONS ) // UNDONE: Allow more segments? + segments = NOISE_DIVISIONS; + + length = VectorLength( delta ) * 0.01f; + if( length < 0.5f ) length = 0.5f; // don't lose all of the noise/texture on short beams + + div = 1.0f / (segments - 1); + vStep = length * div; // Texture length texels per space pixel + + // scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture) + vLast = fmod( freq * speed, 1 ); + scale = scale * length; + + // clamp the beam width + w = fmod( freq, width * 0.1f ) * delta[2]; +#if 1 + gu_vert_ftcv_t* const out = ( gu_vert_ftcv_t* )extGuBeginPacket( NULL ); + unsigned int vert_size = 0; + unsigned int vert_color = getTriBrightness( 1.0f ); +#endif + // NOTE: we must force the degenerate triangles to be on the edge + for( i = 0; i < segments; i++ ) + { + float s, c; + + fraction = i * div; + VectorCopy( source, point ); +#if 1 + out[vert_size].u = 1.0f; + out[vert_size].v = vLast; + out[vert_size].c = vert_color; + out[vert_size].x = point[0]; + out[vert_size].y = point[1]; + out[vert_size].z = point[2]; + vert_size++; + + SinCos( fraction * M_PI2_F, &s, &c ); + point[0] = s * w + source[0]; + point[1] = c * w + source[1]; + point[2] = source[2]; + + out[vert_size].u = 0.0f; + out[vert_size].v = vLast; + out[vert_size].c = vert_color; + out[vert_size].x = point[0]; + out[vert_size].y = point[1]; + out[vert_size].z = point[2]; + vert_size++; +#else + TriBrightness( 1.0f ); + TriTexCoord2f( 1.0f, vLast ); + TriVertex3fv( point ); + + SinCos( fraction * M_PI2_F, &s, &c ); + point[0] = s * w + source[0]; + point[1] = c * w + source[1]; + point[2] = source[2]; + + TriBrightness( 1.0f ); + TriTexCoord2f( 0.0f, vLast ); + TriVertex3fv( point ); +#endif + vLast += vStep; // advance texture scroll (v axis only) + } +#if 1 + extGuEndPacket( ( void * )( out + vert_size ) ); + sceGuDrawArray( GU_TRIANGLE_STRIP, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF, vert_size, 0, out ); +#endif +} + +/* +================ +R_DrawCylinder + +Draw beam cylinder +================ +*/ +void R_DrawCylinder( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments ) +{ + float div, length, fraction; + float vLast, vStep; + vec3_t point; + int i; + + if( segments < 2 ) + return; + + if( segments > NOISE_DIVISIONS ) + segments = NOISE_DIVISIONS; + + length = VectorLength( delta ) * 0.01f; + if( length < 0.5f ) length = 0.5f; // don't lose all of the noise/texture on short beams + + div = 1.0f / (segments - 1); + vStep = length * div; // texture length texels per space pixel + + // Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture) + vLast = fmod( freq * speed, 1 ); + scale = scale * length; +#if 1 + gu_vert_ftcv_t* const out = ( gu_vert_ftcv_t* )extGuBeginPacket( NULL ); + unsigned int vert_size = 0; + unsigned int vert_color0 = getTriBrightness( 0.0f ); + unsigned int vert_color1 = getTriBrightness( 1.0f ); +#endif + for ( i = 0; i < segments; i++ ) + { + float s, c; + + fraction = i * div; + SinCos( fraction * M_PI2_F, &s, &c ); + + point[0] = s * freq * delta[2] + source[0]; + point[1] = c * freq * delta[2] + source[1]; + point[2] = source[2] + width; +#if 1 + out[vert_size].u = 1.0f; + out[vert_size].v = vLast; + out[vert_size].c = vert_color0; + out[vert_size].x = point[0]; + out[vert_size].y = point[1]; + out[vert_size].z = point[2]; + vert_size++; + + point[0] = s * freq * ( delta[2] + width ) + source[0]; + point[1] = c * freq * ( delta[2] + width ) + source[1]; + point[2] = source[2] - width; + + out[vert_size].u = 0.0f; + out[vert_size].v = vLast; + out[vert_size].c = vert_color1; + out[vert_size].x = point[0]; + out[vert_size].y = point[1]; + out[vert_size].z = point[2]; + vert_size++; +#else + TriBrightness( 0 ); + TriTexCoord2f( 1, vLast ); + TriVertex3fv( point ); + + point[0] = s * freq * ( delta[2] + width ) + source[0]; + point[1] = c * freq * ( delta[2] + width ) + source[1]; + point[2] = source[2] - width; + + TriBrightness( 1 ); + TriTexCoord2f( 0, vLast ); + TriVertex3fv( point ); +#endif + vLast += vStep; // Advance texture scroll (v axis only) + } +#if 1 + extGuEndPacket( ( void * )( out + vert_size ) ); + sceGuDrawArray( GU_TRIANGLE_STRIP, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF, vert_size, 0, out ); +#endif +} + +/* +============== +R_DrawBeamFollow + +drawi followed beam +============== +*/ +void R_DrawBeamFollow( BEAM *pbeam, float frametime ) +{ + particle_t *pnew, *particles; + float fraction, div, vLast, vStep; + vec3_t last1, last2, tmp, screen; + vec3_t delta, screenLast, normal; + + gEngfuncs.R_FreeDeadParticles( &pbeam->particles ); + + particles = pbeam->particles; + pnew = NULL; + + div = 0; + if( FBitSet( pbeam->flags, FBEAM_STARTENTITY )) + { + if( particles ) + { + VectorSubtract( particles->org, pbeam->source, delta ); + div = VectorLength( delta ); + + if( div >= 32 ) + { + pnew = gEngfuncs.CL_AllocParticleFast(); + } + } + else + { + pnew = gEngfuncs.CL_AllocParticleFast(); + } + } + + if( pnew ) + { + VectorCopy( pbeam->source, pnew->org ); + pnew->die = gpGlobals->time + pbeam->amplitude; + VectorClear( pnew->vel ); + + pnew->next = particles; + pbeam->particles = pnew; + particles = pnew; + } + + // nothing to draw + if( !particles ) return; + + if( !pnew && div != 0 ) + { + VectorCopy( pbeam->source, delta ); + TriWorldToScreen( pbeam->source, screenLast ); + TriWorldToScreen( particles->org, screen ); + } + else if( particles && particles->next ) + { + VectorCopy( particles->org, delta ); + TriWorldToScreen( particles->org, screenLast ); + TriWorldToScreen( particles->next->org, screen ); + particles = particles->next; + } + else + { + return; + } + + // UNDONE: This won't work, screen and screenLast must be extrapolated here to fix the + // first beam segment for this trail + + // build world-space normal to screen-space direction vector + VectorSubtract( screen, screenLast, tmp ); + // we don't need Z, we're in screen space + tmp[2] = 0; + VectorNormalize( tmp ); + + // Build point along noraml line (normal is -y, x) + VectorScale( RI.vup, tmp[0], normal ); // Build point along normal line (normal is -y, x) + VectorMA( normal, tmp[1], RI.vright, normal ); + + // Make a wide line + VectorMA( delta, pbeam->width, normal, last1 ); + VectorMA( delta, -pbeam->width, normal, last2 ); + + div = 1.0f / pbeam->amplitude; + fraction = ( pbeam->die - gpGlobals->time ) * div; + + vLast = 0.0f; + vStep = 1.0f; + +#if 1 + gu_vert_ftcv_t* const out = ( gu_vert_ftcv_t* )extGuBeginPacket( NULL ); + unsigned int vert_size = 0; + unsigned int vert_color = 0; + gu_vert_ftcv_t* saved_ptr; +#endif + + while( particles ) + { +#if 1 + vert_color = getTriBrightness( fraction ); + out[vert_size].u = 1.0f; + out[vert_size].v = 1.0f; + out[vert_size].c = vert_color; + out[vert_size].x = last2[0]; + out[vert_size].y = last2[1]; + out[vert_size].z = last2[2]; + saved_ptr = &out[vert_size]; + vert_size++; + out[vert_size].u = 0.0f; + out[vert_size].v = 1.0f; + out[vert_size].c = vert_color; + out[vert_size].x = last1[0]; + out[vert_size].y = last1[1]; + out[vert_size].z = last1[2]; + vert_size++; +#else + TriBrightness( fraction ); + TriTexCoord2f( 1, 1 ); + TriVertex3fv( last2 ); + TriBrightness( fraction ); + TriTexCoord2f( 0, 1 ); + TriVertex3fv( last1 ); +#endif + // Transform point into screen space + TriWorldToScreen( particles->org, screen ); + // Build world-space normal to screen-space direction vector + VectorSubtract( screen, screenLast, tmp ); + + // we don't need Z, we're in screen space + tmp[2] = 0; + VectorNormalize( tmp ); + VectorScale( RI.vup, tmp[0], normal ); // Build point along noraml line (normal is -y, x) + VectorMA( normal, tmp[1], RI.vright, normal ); + + // Make a wide line + VectorMA( particles->org, pbeam->width, normal, last1 ); + VectorMA( particles->org, -pbeam->width, normal, last2 ); + + vLast += vStep; // Advance texture scroll (v axis only) + + if( particles->next != NULL ) + { + fraction = (particles->die - gpGlobals->time) * div; + } + else + { + fraction = 0.0; + } +#if 1 + vert_color = getTriBrightness( fraction ); + out[vert_size].u = 0.0f; + out[vert_size].v = 0.0f; + out[vert_size].c = vert_color; + out[vert_size].x = last1[0]; + out[vert_size].y = last1[1]; + out[vert_size].z = last1[2]; + vert_size++; + + out[vert_size].u = saved_ptr->u; + out[vert_size].v = saved_ptr->v; + out[vert_size].c = saved_ptr->c; + out[vert_size].x = saved_ptr->x; + out[vert_size].y = saved_ptr->y; + out[vert_size].z = saved_ptr->z; + vert_size++; + + out[vert_size].u = 0.0f; + out[vert_size].v = 0.0f; + out[vert_size].c = vert_color; + out[vert_size].x = last1[0]; + out[vert_size].y = last1[1]; + out[vert_size].z = last1[2]; + vert_size++; + + out[vert_size].u = 1.0f; + out[vert_size].v = 0.0f; + out[vert_size].c = vert_color; + out[vert_size].x = last2[0]; + out[vert_size].y = last2[1]; + out[vert_size].z = last2[2]; + vert_size++; +#else + TriBrightness( fraction ); + TriTexCoord2f( 0, 0 ); + TriVertex3fv( last1 ); + TriBrightness( fraction ); + TriTexCoord2f( 1, 0 ); + TriVertex3fv( last2 ); +#endif + VectorCopy( screen, screenLast ); + + particles = particles->next; + } +#if 1 + extGuEndPacket( ( void * )( out + vert_size ) ); + sceGuDrawArray( GU_TRIANGLES, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF, vert_size, 0, out ); +#endif + // drift popcorn trail if there is a velocity + particles = pbeam->particles; + + while( particles ) + { + VectorMA( particles->org, frametime, particles->vel, particles->org ); + particles = particles->next; + } +} + +/* +================ +R_DrawRing + +Draw beamring +================ +*/ +void R_DrawRing( vec3_t source, vec3_t delta, float width, float amplitude, float freq, float speed, int segments ) +{ + int i, j, noiseIndex, noiseStep; + float div, length, fraction, factor, vLast, vStep; + vec3_t last1, last2, point, screen, screenLast; + vec3_t tmp, normal, center, xaxis, yaxis; + float radius, x, y, scale; + + if( segments < 2 ) + return; + + VectorClear( screenLast ); + segments = segments * M_PI_F; + + if( segments > NOISE_DIVISIONS * 8 ) + segments = NOISE_DIVISIONS * 8; + + length = VectorLength( delta ) * 0.01f * M_PI_F; + if( length < 0.5f ) length = 0.5f; // Don't lose all of the noise/texture on short beams + + div = 1.0f / ( segments - 1 ); + + vStep = length * div / 8.0f; // texture length texels per space pixel + + // Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture) + vLast = fmod( freq * speed, 1.0f ); + scale = amplitude * length / 8.0f; + + // Iterator to resample noise waveform (it needs to be generated in powers of 2) + noiseStep = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f ) * 8; + noiseIndex = 0; + + VectorScale( delta, 0.5f, delta ); + VectorAdd( source, delta, center ); + + VectorCopy( delta, xaxis ); + radius = VectorLength( xaxis ); + + // cull beamring + // -------------------------------- + // Compute box center +/- radius + VectorSet( last1, radius, radius, scale ); + VectorAdd( center, last1, tmp ); // maxs + VectorSubtract( center, last1, screen ); // mins + + if( !WORLDMODEL ) + return; + + // is that box in PVS && frustum? + if( !gEngfuncs.Mod_BoxVisible( screen, tmp, Mod_GetCurrentVis( )) || R_CullBox( screen, tmp )) + { + return; + } + + VectorSet( yaxis, xaxis[1], -xaxis[0], 0.0f ); + VectorNormalize( yaxis ); + VectorScale( yaxis, radius, yaxis ); + + j = segments / 8; +#if 1 + gu_vert_ftv_t* const out = ( gu_vert_ftv_t* )extGuBeginPacket( NULL ); + unsigned int vert_size = 0; +#endif + for( i = 0; i < segments + 1; i++ ) + { + fraction = i * div; + SinCos( fraction * M_PI2_F, &x, &y ); + + VectorMAMAM( x, xaxis, y, yaxis, 1.0f, center, point ); + + // distort using noise + factor = rgNoise[(noiseIndex >> 16) & (NOISE_DIVISIONS - 1)] * scale; + VectorMA( point, factor, RI.vup, point ); + + // Rotate the noise along the perpendicluar axis a bit to keep the bolt from looking diagonal + factor = rgNoise[(noiseIndex >> 16) & (NOISE_DIVISIONS - 1)] * scale; + factor *= cos( fraction * M_PI_F * 24 + freq ); + VectorMA( point, factor, RI.vright, point ); + + // Transform point into screen space + TriWorldToScreen( point, screen ); + + if( i != 0 ) + { + // build world-space normal to screen-space direction vector + VectorSubtract( screen, screenLast, tmp ); + + // we don't need Z, we're in screen space + tmp[2] = 0; + VectorNormalize( tmp ); + + // Build point along normal line (normal is -y, x) + VectorScale( RI.vup, tmp[0], normal ); + VectorMA( normal, tmp[1], RI.vright, normal ); + + // Make a wide line + VectorMA( point, width, normal, last1 ); + VectorMA( point, -width, normal, last2 ); + + vLast += vStep; // Advance texture scroll (v axis only) +#if 1 + out[vert_size].u = 1.0f; + out[vert_size].v = vLast; + out[vert_size].x = last2[0]; + out[vert_size].y = last2[1]; + out[vert_size].z = last2[2]; + vert_size++; + out[vert_size].u = 0.0f; + out[vert_size].v = vLast; + out[vert_size].x = last1[0]; + out[vert_size].y = last1[1]; + out[vert_size].z = last1[2]; + vert_size++; +#else + TriTexCoord2f( 1.0f, vLast ); + TriVertex3fv( last2 ); + TriTexCoord2f( 0.0f, vLast ); + TriVertex3fv( last1 ); +#endif + } + + VectorCopy( screen, screenLast ); + noiseIndex += noiseStep; + j--; + + if( j == 0 && amplitude != 0 ) + { + j = segments / 8; + FracNoise( rgNoise, NOISE_DIVISIONS ); + } + } +#if 1 + extGuEndPacket( ( void * )( out + vert_size ) ); + sceGuDrawArray( GU_TRIANGLE_STRIP, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, vert_size, 0, out ); +#endif +} + +/* +============== +R_BeamComputePoint + +compute attachment point for beam +============== +*/ +static qboolean R_BeamComputePoint( int beamEnt, vec3_t pt ) +{ + cl_entity_t *ent; + int attach; + + ent = gEngfuncs.R_BeamGetEntity( beamEnt ); + + if( beamEnt < 0 ) + attach = BEAMENT_ATTACHMENT( -beamEnt ); + else attach = BEAMENT_ATTACHMENT( beamEnt ); + + if( !ent ) + { + gEngfuncs.Con_DPrintf( S_ERROR "R_BeamComputePoint: invalid entity %i\n", BEAMENT_ENTITY( beamEnt )); + VectorClear( pt ); + return false; + } + + // get attachment + if( attach > 0 ) + VectorCopy( ent->attachment[attach - 1], pt ); + else if( ent->index == ENGINE_GET_PARM( PARM_PLAYER_INDEX ) ) + { + vec3_t simorg; + gEngfuncs.GetPredictedOrigin( simorg ); + VectorCopy( simorg, pt ); + } + else VectorCopy( ent->origin, pt ); + + return true; +} + +/* +============== +R_BeamRecomputeEndpoints + +Recomputes beam endpoints.. +============== +*/ +qboolean R_BeamRecomputeEndpoints( BEAM *pbeam ) +{ + if( FBitSet( pbeam->flags, FBEAM_STARTENTITY )) + { + cl_entity_t *start = gEngfuncs.R_BeamGetEntity( pbeam->startEntity ); + + if( R_BeamComputePoint( pbeam->startEntity, pbeam->source )) + { + if( !pbeam->pFollowModel ) + pbeam->pFollowModel = start->model; + SetBits( pbeam->flags, FBEAM_STARTVISIBLE ); + } + else if( !FBitSet( pbeam->flags, FBEAM_FOREVER )) + { + ClearBits( pbeam->flags, FBEAM_STARTENTITY ); + } + } + + if( FBitSet( pbeam->flags, FBEAM_ENDENTITY )) + { + cl_entity_t *end = gEngfuncs.R_BeamGetEntity( pbeam->endEntity ); + + if( R_BeamComputePoint( pbeam->endEntity, pbeam->target )) + { + if( !pbeam->pFollowModel ) + pbeam->pFollowModel = end->model; + SetBits( pbeam->flags, FBEAM_ENDVISIBLE ); + } + else if( !FBitSet( pbeam->flags, FBEAM_FOREVER )) + { + ClearBits( pbeam->flags, FBEAM_ENDENTITY ); + pbeam->die = gpGlobals->time; + return false; + } + else + { + return false; + } + } + + if( FBitSet( pbeam->flags, FBEAM_STARTENTITY ) && !FBitSet( pbeam->flags, FBEAM_STARTVISIBLE )) + return false; + return true; +} + + +/* +============== +R_BeamDraw + +Update beam vars and draw it +============== +*/ +void R_BeamDraw( BEAM *pbeam, float frametime ) +{ + model_t *model; + vec3_t delta; + + model = gEngfuncs.pfnGetModelByIndex( pbeam->modelIndex ); + SetBits( pbeam->flags, FBEAM_ISACTIVE ); + + if( !model || model->type != mod_sprite ) + { + pbeam->flags &= ~FBEAM_ISACTIVE; // force to ignore + pbeam->die = gpGlobals->time; + return; + } + + // update frequency + pbeam->freq += frametime; + + // generate fractal noise + if( frametime != 0.0f ) + { + rgNoise[0] = 0; + rgNoise[NOISE_DIVISIONS] = 0; + } + + if( pbeam->amplitude != 0 && frametime != 0.0f ) + { + if( FBitSet( pbeam->flags, FBEAM_SINENOISE )) + SineNoise( rgNoise, NOISE_DIVISIONS ); + else FracNoise( rgNoise, NOISE_DIVISIONS ); + } + + // update end points + if( FBitSet( pbeam->flags, FBEAM_STARTENTITY|FBEAM_ENDENTITY )) + { + // makes sure attachment[0] + attachment[1] are valid + if( !R_BeamRecomputeEndpoints( pbeam )) + { + ClearBits( pbeam->flags, FBEAM_ISACTIVE ); // force to ignore + return; + } + + // compute segments from the new endpoints + VectorSubtract( pbeam->target, pbeam->source, delta ); + VectorClear( pbeam->delta ); + + if( VectorLength( delta ) > 0.0000001f ) + VectorCopy( delta, pbeam->delta ); + + if( pbeam->amplitude >= 0.50f ) + pbeam->segments = VectorLength( pbeam->delta ) * 0.25f + 3.0f; // one per 4 pixels + else pbeam->segments = VectorLength( pbeam->delta ) * 0.075f + 3.0f; // one per 16 pixels + } + + if( pbeam->type == TE_BEAMPOINTS && R_BeamCull( pbeam->source, pbeam->target, 0 )) + { + ClearBits( pbeam->flags, FBEAM_ISACTIVE ); + return; + } + + // don't draw really short or inactive beams + if( !FBitSet( pbeam->flags, FBEAM_ISACTIVE ) || VectorLength( pbeam->delta ) < 0.1f ) + { + return; + } + + if( pbeam->flags & ( FBEAM_FADEIN|FBEAM_FADEOUT )) + { + // update life cycle + pbeam->t = pbeam->freq + ( pbeam->die - gpGlobals->time ); + if( pbeam->t != 0.0f ) pbeam->t = 1.0f - pbeam->freq / pbeam->t; + } + + if( pbeam->type == TE_BEAMHOSE ) + { + float flDot; + + VectorSubtract( pbeam->target, pbeam->source, delta ); + VectorNormalize( delta ); + + flDot = DotProduct( delta, RI.vforward ); + + // abort if the player's looking along it away from the source + if( flDot > 0 ) + { + return; + } + else + { + float flFade = pow( flDot, 10 ); + vec3_t localDir, vecProjection, tmp; + float flDistance; + + // fade the beam if the player's not looking at the source + VectorSubtract( RI.vieworg, pbeam->source, localDir ); + flDot = DotProduct( delta, localDir ); + VectorScale( delta, flDot, vecProjection ); + VectorSubtract( localDir, vecProjection, tmp ); + flDistance = VectorLength( tmp ); + + if( flDistance > 30 ) + { + flDistance = 1.0f - (( flDistance - 30.0f ) / 64.0f ); + if( flDistance <= 0 ) flFade = 0; + else flFade *= pow( flDistance, 3 ); + } + + if( flFade < ( 1.0f / 255.0f )) + return; + + // FIXME: needs to be testing + pbeam->brightness *= flFade; + } + } + + TriRenderMode( FBitSet( pbeam->flags, FBEAM_SOLID ) ? kRenderNormal : kRenderTransAdd ); + + if( !TriSpriteTexture( model, (int)(pbeam->frame + pbeam->frameRate * gpGlobals->time) % pbeam->frameCount )) + { + ClearBits( pbeam->flags, FBEAM_ISACTIVE ); + return; + } + + if( pbeam->type == TE_BEAMFOLLOW ) + { + cl_entity_t *pStart; + + // XASH SPECIFIC: get brightness from head entity + pStart = gEngfuncs.R_BeamGetEntity( pbeam->startEntity ); + if( pStart && pStart->curstate.rendermode != kRenderNormal ) + pbeam->brightness = CL_FxBlend( pStart ) / 255.0f; + } + + if( FBitSet( pbeam->flags, FBEAM_FADEIN )) + TriColor4f( pbeam->r, pbeam->g, pbeam->b, pbeam->t * pbeam->brightness ); + else if( FBitSet( pbeam->flags, FBEAM_FADEOUT )) + TriColor4f( pbeam->r, pbeam->g, pbeam->b, ( 1.0f - pbeam->t ) * pbeam->brightness ); + else TriColor4f( pbeam->r, pbeam->g, pbeam->b, pbeam->brightness ); + + switch( pbeam->type ) + { + case TE_BEAMTORUS: + GL_Cull( GL_NONE ); +#if 0 + TriBegin( TRI_TRIANGLE_STRIP ); +#endif + R_DrawTorus( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments ); +#if 0 + TriEnd(); +#endif + break; + case TE_BEAMDISK: + GL_Cull( GL_NONE ); +#if 0 + TriBegin( TRI_TRIANGLE_STRIP ); +#endif + R_DrawDisk( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments ); +#if 0 + TriEnd(); +#endif + break; + case TE_BEAMCYLINDER: + GL_Cull( GL_NONE ); +#if 0 + TriBegin( TRI_TRIANGLE_STRIP ); +#endif + R_DrawCylinder( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments ); +#if 0 + TriEnd(); +#endif + break; + case TE_BEAMPOINTS: + case TE_BEAMHOSE: +#if 0 + TriBegin( TRI_TRIANGLE_STRIP ); +#endif + R_DrawSegs( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments, pbeam->flags ); +#if 0 + TriEnd(); +#endif + break; + case TE_BEAMFOLLOW: +#if 0 + TriBegin( TRI_QUADS ); +#endif + R_DrawBeamFollow( pbeam, frametime ); +#if 0 + TriEnd(); +#endif + break; + case TE_BEAMRING: + GL_Cull( GL_NONE ); +#if 0 + TriBegin( TRI_TRIANGLE_STRIP ); +#endif + R_DrawRing( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments ); +#if 0 + TriEnd(); +#endif + break; + } + + GL_Cull( GL_FRONT ); + r_stats.c_view_beams_count++; +} + +/* +============== +R_BeamSetAttributes + +set beam attributes +============== +*/ +static void R_BeamSetAttributes( BEAM *pbeam, float r, float g, float b, float framerate, int startFrame ) +{ + pbeam->frame = (float)startFrame; + pbeam->frameRate = framerate; + pbeam->r = r; + pbeam->g = g; + pbeam->b = b; +} + +/* +============== +R_BeamSetup + +generic function. all beams must be +passed through this +============== +*/ +static void R_BeamSetup( BEAM *pbeam, vec3_t start, vec3_t end, int modelIndex, float life, float width, float amplitude, float brightness, float speed ) +{ + model_t *sprite = gEngfuncs.pfnGetModelByIndex( modelIndex ); + + if( !sprite ) return; + + pbeam->type = BEAM_POINTS; + pbeam->modelIndex = modelIndex; + pbeam->frame = 0; + pbeam->frameRate = 0; + pbeam->frameCount = sprite->numframes; + + VectorCopy( start, pbeam->source ); + VectorCopy( end, pbeam->target ); + VectorSubtract( end, start, pbeam->delta ); + + pbeam->freq = speed * gpGlobals->time; + pbeam->die = life + gpGlobals->time; + pbeam->amplitude = amplitude; + pbeam->brightness = brightness; + pbeam->width = width; + pbeam->speed = speed; + + if( amplitude >= 0.50f ) + pbeam->segments = VectorLength( pbeam->delta ) * 0.25f + 3.0f; // one per 4 pixels + else pbeam->segments = VectorLength( pbeam->delta ) * 0.075f + 3.0f; // one per 16 pixels + + pbeam->pFollowModel = NULL; + pbeam->flags = 0; +} + + + +/* +============== +R_BeamDrawCustomEntity + +initialize beam from server entity +============== +*/ +void R_BeamDrawCustomEntity( cl_entity_t *ent ) +{ + BEAM beam; + float amp = ent->curstate.body / 100.0f; + float blend = CL_FxBlend( ent ) / 255.0f; + float r, g, b; + int beamFlags; + + r = ent->curstate.rendercolor.r / 255.0f; + g = ent->curstate.rendercolor.g / 255.0f; + b = ent->curstate.rendercolor.b / 255.0f; + + R_BeamSetup( &beam, ent->origin, ent->angles, ent->curstate.modelindex, 0, ent->curstate.scale, amp, blend, ent->curstate.animtime ); + R_BeamSetAttributes( &beam, r, g, b, ent->curstate.framerate, ent->curstate.frame ); + beam.pFollowModel = NULL; + + switch( ent->curstate.rendermode & 0x0F ) + { + case BEAM_ENTPOINT: + beam.type = TE_BEAMPOINTS; + if( ent->curstate.sequence ) + { + SetBits( beam.flags, FBEAM_STARTENTITY ); + beam.startEntity = ent->curstate.sequence; + } + if( ent->curstate.skin ) + { + SetBits( beam.flags, FBEAM_ENDENTITY ); + beam.endEntity = ent->curstate.skin; + } + break; + case BEAM_ENTS: + beam.type = TE_BEAMPOINTS; + SetBits( beam.flags, FBEAM_STARTENTITY | FBEAM_ENDENTITY ); + beam.startEntity = ent->curstate.sequence; + beam.endEntity = ent->curstate.skin; + break; + case BEAM_HOSE: + beam.type = TE_BEAMHOSE; + break; + case BEAM_POINTS: + // already set up + break; + } + + beamFlags = ( ent->curstate.rendermode & 0xF0 ); + + if( FBitSet( beamFlags, BEAM_FSINE )) + SetBits( beam.flags, FBEAM_SINENOISE ); + + if( FBitSet( beamFlags, BEAM_FSOLID )) + SetBits( beam.flags, FBEAM_SOLID ); + + if( FBitSet( beamFlags, BEAM_FSHADEIN )) + SetBits( beam.flags, FBEAM_SHADEIN ); + + if( FBitSet( beamFlags, BEAM_FSHADEOUT )) + SetBits( beam.flags, FBEAM_SHADEOUT ); + + // draw it + R_BeamDraw( &beam, tr.frametime ); +} + + +/* +============== +CL_DrawBeams + +draw beam loop +============== +*/ +void CL_DrawBeams( int fTrans, BEAM *active_beams ) +{ + BEAM *pBeam; + int i, flags; + + sceGuShadeModel( GU_SMOOTH ); + sceGuDepthMask( fTrans ? GU_TRUE : GU_FALSE ); + + // server beams don't allocate beam chains + // all params are stored in cl_entity_t + for( i = 0; i < tr.draw_list->num_beam_entities; i++ ) + { + RI.currentbeam = tr.draw_list->beam_entities[i]; + flags = RI.currentbeam->curstate.rendermode & 0xF0; + + if( fTrans && FBitSet( flags, FBEAM_SOLID )) + continue; + + if( !fTrans && !FBitSet( flags, FBEAM_SOLID )) + continue; + + R_BeamDrawCustomEntity( RI.currentbeam ); + r_stats.c_view_beams_count++; + } + + RI.currentbeam = NULL; + + // draw temporary entity beams + for( pBeam = active_beams; pBeam; pBeam = pBeam->next ) + { + if( fTrans && FBitSet( pBeam->flags, FBEAM_SOLID )) + continue; + + if( !fTrans && !FBitSet( pBeam->flags, FBEAM_SOLID )) + continue; + + R_BeamDraw( pBeam, gpGlobals->time - gpGlobals->oldtime ); + } + + sceGuShadeModel( GU_FLAT ); + sceGuDepthMask( GU_FALSE ); + +} diff --git a/ref_gu/gu_clipping.c b/ref_gu/gu_clipping.c new file mode 100644 index 00000000..536a8665 --- /dev/null +++ b/ref_gu/gu_clipping.c @@ -0,0 +1,416 @@ +/* +gu_clipping.c - software clipping implimentation +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +/* + +Based on clipping system from PSP Quake by Peter Mackay and Chris Swindle. + +*/ + +#include "gu_local.h" + +#define MAX_CLIPPED_VERTICES 32 + +#define CPLANE_BOTTOM 0 +#define CPLANE_LEFT 1 +#define CPLANE_RIGHT 2 +#define CPLANE_TOP 3 +#define MAX_CPLANES 4 + +// Types. +typedef ScePspFVector4 plane_type; +typedef plane_type frustum_t[MAX_CPLANES]; + +// Transformed frustum. +static ScePspFMatrix4 projection_view_matrix __attribute__( ( aligned( 16 ) ) ); +static frustum_t projection_view_frustum __attribute__( ( aligned( 16 ) ) ); +static frustum_t clipping_frustum __attribute__( ( aligned( 16 ) ) ); + +// The temporary working buffers. +static gu_vert_t work_buffer[2][MAX_CLIPPED_VERTICES] __attribute__( ( aligned( 16 ) ) ); + +/* +================= +GU_ClipGetFrustum +================= +*/ +_inline void GU_ClipGetFrustum(const ScePspFMatrix4 *matrix, frustum_t* frustum) +{ + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.q C100, 0(%4)\n" // C000 = matrix->x + "lv.q C110, 16(%4)\n" // C010 = matrix->y + "lv.q C120, 32(%4)\n" // C020 = matrix->z + "lv.q C130, 48(%4)\n" // C030 = matrix->w + "vzero.q C210\n" // C000 = 0 + "vadd.q C000, R103, R101\n" // C000 = R103 + R101 ( BOTTOM ) + "vdot.q S200, C000, C000\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 ( BOTTOM ) + "vadd.q C010, R103, R100\n" // C010 = R103 + R100 ( LEFT ) + "vdot.q S201, C010, C010\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 ( LEFT ) + "vsub.q C020, R103, R100\n" // C020 = R103 - R100 ( RIGHT ) + "vdot.q S202, C020, C020\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 ( RIGHT ) + "vsub.q C030, R103, R101\n" // C030 = R103 - R101 ( TOP ) + "vdot.q S203, C030, C030\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 ( TOP ) + "vcmp.q EZ, C200\n" // CC[0] = ( C200 == 0.0f ) + "vrsq.q C200, C200\n" // C200 = 1.0 / sqrt( C200 ) + "vcmovt.q C200, C210, 0\n" // if ( CC[*] ) C200 = C210 + "vscl.q C000, C000, S200\n" // C000 = C000 * S200 ( BOTTOM ) + "vscl.q C010, C010, S201\n" // C010 = C010 * S201 ( LEFT ) + "vscl.q C020, C020, S202\n" // C020 = C020 * S202 ( RIGHT ) + "vscl.q C030, C030, S203\n" // C030 = C030 * S203 ( TOP ) + "sv.q C000, %0\n" // Store plane from register + "sv.q C010, %1\n" // Store plane from register + "sv.q C020, %2\n" // Store plane from register + "sv.q C030, %3\n" // Store plane from register + ".set pop\n" // Restore assembler option + : "=m"( ( *frustum )[CPLANE_BOTTOM] ), + "=m"( ( *frustum )[CPLANE_LEFT] ), + "=m"( ( *frustum )[CPLANE_RIGHT] ), + "=m"( ( *frustum )[CPLANE_TOP] ) + : "r"( matrix ) /* addr */ + ); +} + +/* +================= +GU_ClipBeginFrame + +Calculate the clipping frustum for static objects +================= +*/ +void GU_ClipBeginFrame( void ) +{ + // Get the projection matrix. + sceGumMatrixMode( GU_PROJECTION ); + ScePspFMatrix4 proj; + sceGumStoreMatrix( &proj ); + + // Get the view matrix. + sceGumMatrixMode( GU_VIEW ); + ScePspFMatrix4 view; + sceGumStoreMatrix( &view ); + + // Restore matrix mode. + sceGumMatrixMode( GU_MODEL ); + + // Combine the two matrices (multiply projection by view). + gumMultMatrix( &projection_view_matrix, &proj, &view ); + + // Calculate and cache the clipping frustum. + GU_ClipGetFrustum( &projection_view_matrix, &projection_view_frustum ); + + __asm__ volatile( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.q C700, %4\n" // Load plane into register + "lv.q C710, %5\n" // Load plane into register + "lv.q C720, %6\n" // Load plane into register + "lv.q C730, %7\n" // Load plane into register + "sv.q C700, %0\n" // Store plane from register + "sv.q C710, %1\n" // Store plane from register + "sv.q C720, %2\n" // Store plane from register + "sv.q C730, %3\n" // Store plane from register + ".set pop\n" // Restore assembler option + : "=m"( clipping_frustum[CPLANE_BOTTOM] ), + "=m"( clipping_frustum[CPLANE_LEFT] ), + "=m"( clipping_frustum[CPLANE_RIGHT] ), + "=m"( clipping_frustum[CPLANE_TOP] ) + : "m"( projection_view_frustum[CPLANE_BOTTOM] ), + "m"( projection_view_frustum[CPLANE_LEFT] ), + "m"( projection_view_frustum[CPLANE_RIGHT] ), + "m"( projection_view_frustum[CPLANE_TOP] ) + ); +} + +/* +================= +GU_ClipBeginBrush + +Calculate the clipping frustum for dynamic objects +================= +*/ +void GU_ClipBeginBrush( void ) +{ + // Get the model matrix. + ScePspFMatrix4 model_matrix; + sceGumStoreMatrix( &model_matrix ); + + // Combine the matrices (multiply projection-view by model). + ScePspFMatrix4 projection_view_model_matrix; + gumMultMatrix( &projection_view_model_matrix, &projection_view_matrix, &model_matrix ); + + // Calculate the clipping frustum. + GU_ClipGetFrustum( &projection_view_model_matrix, &clipping_frustum ); + + __asm__ volatile( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.q C700, %0\n" // Load plane into register + "lv.q C710, %1\n" // Load plane into register + "lv.q C720, %2\n" // Load plane into register + "lv.q C730, %3\n" // Load plane into register + ".set pop\n" // Restore assembler option + :: "m"( clipping_frustum[CPLANE_BOTTOM] ), + "m"( clipping_frustum[CPLANE_LEFT] ), + "m"( clipping_frustum[CPLANE_RIGHT] ), + "m"( clipping_frustum[CPLANE_TOP] ) + ); +} + +/* +================= +GU_ClipEndBrush + +Restore the clipping frustum +================= +*/ +void GU_ClipEndBrush( void ) +{ + __asm__ volatile( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.q C700, %4\n" // Load plane into register + "lv.q C710, %5\n" // Load plane into register + "lv.q C720, %6\n" // Load plane into register + "lv.q C730, %7\n" // Load plane into register + "sv.q C700, %0\n" // Store plane from register + "sv.q C710, %1\n" // Store plane from register + "sv.q C720, %2\n" // Store plane from register + "sv.q C730, %3\n" // Store plane from register + ".set pop\n" // Restore assembler option + : "=m"( clipping_frustum[CPLANE_BOTTOM] ), + "=m"( clipping_frustum[CPLANE_LEFT] ), + "=m"( clipping_frustum[CPLANE_RIGHT] ), + "=m"( clipping_frustum[CPLANE_TOP] ) + : "m"( projection_view_frustum[CPLANE_BOTTOM] ), + "m"( projection_view_frustum[CPLANE_LEFT] ), + "m"( projection_view_frustum[CPLANE_RIGHT] ), + "m"( projection_view_frustum[CPLANE_TOP] ) + ); +} + +/* +================= +GU_ClipLoadFrustum + +Load the clipping frustum ( native ) +================= +*/ +#if 0 +void GU_ClipLoadFrustum( const mplane_t *plane ) +{ + __asm__ volatile( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "ulv.q C700, %4\n" // Load plane into register + "ulv.q C710, %5\n" // Load plane into register + "ulv.q C720, %6\n" // Load plane into register + "ulv.q C730, %7\n" // Load plane into register + "vneg.q R703, R703\n" // R703 = -R703 = -dist + "sv.q C700, %0\n" // Store plane from register + "sv.q C710, %1\n" // Store plane from register + "sv.q C720, %2\n" // Store plane from register + "sv.q C730, %3\n" // Store plane from register + ".set pop\n" // Restore assembler option + : "=m"( clipping_frustum[CPLANE_BOTTOM] ), + "=m"( clipping_frustum[CPLANE_LEFT] ), + "=m"( clipping_frustum[CPLANE_RIGHT] ), + "=m"( clipping_frustum[CPLANE_TOP] ) + : "m"( plane[FRUSTUM_BOTTOM] ), + "m"( plane[FRUSTUM_LEFT] ), + "m"( plane[FRUSTUM_RIGHT] ), + "m"( plane[FRUSTUM_TOP] ) + + ); +} +#endif + +/* +================= +GU_ClipIsRequired + +Is clipping required? +================= +*/ +qboolean GU_ClipIsRequired( const gu_vert_t* v, size_t vc ) +{ + int res; + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "move $8, %1\n" // $8 = &v[0] + "move $9, %2\n" // $9 = vc + "li $10, 20\n" // $10 = 20( sizeof( gu_vert_t ) ) + "mul $10, $10, $9\n" // $10 = $10 * $9 + "addu $10, $10, $8\n" // $10 = $10 + $8 + "addiu %0, $0, 1\n" // res = 1 + "vzero.q C600\n" // C600 = [0.0f, 0.0f, 0.0f. 0.0f] + "0:\n" // loop + "lv.s S610, 8($8)\n" // S610 = v[i].xyz[0] + "lv.s S611, 12($8)\n" // S611 = v[i].xyz[1] + "lv.s S612, 16($8)\n" // S612 = v[i].xyz[2] + "vhtfm4.q C620, M700, C610\n" // C620 = frustrum * v[i].xyz + "vcmp.q LT, C620, C600\n" // S620 < 0.0f || S621 < 0.0f || S622 < 0.0f || S623 < 0.0f + "bvt 4, 1f\n" // if ( CC[4] == 1 ) jump to exit + "addiu $8, $8, 20\n" // $8 = $8 + 20( sizeof( gu_vert_t ) ) ( delay slot ) + "bne $10, $8, 0b\n" // if ( $10 != $8 ) jump to loop + "nop\n" // ( delay slot ) + "move %0, $0\n" // res = 0 + "1:\n" // exit + ".set pop\n" // Restore assembler option + : "=r"( res ) + : "r"( v ), "r"( vc ) + : "$8", "$9", "$10" + ); + return res; +} + +/* +================= +GU_Clip2Plane + +Clips a polygon against a plane. +================= +*/ +_inline void GU_Clip2Plane( plane_type *plane, gu_vert_t *uv, size_t uvc, gu_vert_t *cv, size_t *cvc ) +{ + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "move $8, %1\n" // $8 = uv[0] is S + "addiu $9, $8, 20\n" // $9 = uv[1] is P + "move $10, %2\n" // $10 = uvc + "li $11, 20\n" // $11 = 20( sizeof( gu_vert_t ) ) + "mul $11, $11, $10\n" // $11 = $11 * $10 + "addu $11, $11, $8\n" // $11 = $11 + $8 + "move $12, %3\n" // $12 = &cv[0] + "move %0, $0\n" // cvc = 0 + "ulv.q C100, %4\n" // Load plane into register + "vzero.s S110\n" // Set zero for cmp + "0:\n" // loop + "lv.s S000, 0($8)\n" // Load vertex S TEX(4b) U into register + "lv.s S001, 4($8)\n" // Load vertex S TEX(4b) V into register + "lv.s S010, 8($8)\n" // Load vertex S XYZ(4b) X into register + "lv.s S011, 12($8)\n" // Load vertex S XYZ(4b) Y into register + "lv.s S012, 16($8)\n" // Load vertex S XYZ(4b) Z into register + "lv.s S020, 0($9)\n" // Load vertex P TEX(4b) U into register + "lv.s S021, 4($9)\n" // Load vertex P TEX(4b) V into register + "lv.s S030, 8($9)\n" // Load vertex P XYZ(4b) X into register + "lv.s S031, 12($9)\n" // Load vertex P XYZ(4b) Y into register + "lv.s S032, 16($9)\n" // Load vertex P XYZ(4b) Z into register + "vhdp.q S111, C010, C100\n" // S111 = plane * S (S DIST) + "vhdp.q S112, C030, C100\n" // S112 = plane * P (P DIST) + "vcmp.s LE, S111, S110\n" // (S dist <= 0.0f) + "bvt 0, 2f\n" // if ( CC[0] == 1 ) jump to 2f + "nop\n" // ( delay slot ) + "vcmp.s LE, S112, S110\n" // (P dist <= 0.0f) + "bvt 0, 1f\n" // if ( CC[0] == 1 ) jump to 1f + "nop\n" // ( delay slot ) + "sv.s S020, 0($12)\n" // cv->uv[0] = C020 TEX U + "sv.s S021, 4($12)\n" // cv->uv[1] = C021 TEX V + "sv.s S030, 8($12)\n" // cv->xyz[0] = C030 XYZ X + "sv.s S031, 12($12)\n" // cv->xyz[1] = C031 XYZ Y + "sv.s S032, 16($12)\n" // cv->xyz[2] = C032 XYZ Z + "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) ) + "j 3f\n" // jump to next + "addiu %0, %0, 1\n" // cvc += 1 ( delay slot ) + "1:\n" + "vsub.s S112, S111, S112\n" // S112 = ( S dist - P dist ) + "vdiv.s S112, S111, S112\n" // S112 = S111 / S112 + "vsub.p C120, C020, C000\n" // C120 = C020(P TEX) - C000(S TEX) + "vsub.t C130, C030, C010\n" // C130 = C030(P XYZ) - C010(S XYZ) + "vscl.p C120, C120, S112\n" // C120 = C020 * S112 + "vscl.t C130, C130, S112\n" // C130 = C030 * S112 + "vadd.p C120, C120, C000\n" // C120 = C120 + C000(S TEX) + "vadd.t C130, C130, C010\n" // C130 = C130 + C010(S XYZ) + "sv.s S120, 0($12)\n" // cv->uv[0] = S120 TEX U + "sv.s S121, 4($12)\n" // cv->uv[1] = S121 TEX V + "sv.s S130, 8($12)\n" // cv->xyz[0] = S130 XYZ X + "sv.s S131, 12($12)\n" // cv->xyz[1] = S131 XYZ Y + "sv.s S132, 16($12)\n" // cv->xyz[2] = S132 XYZ Z + "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) ) + "j 3f\n" // jump to next + "addiu %0, %0, 1\n" // cvc += 1 ( delay slot ) + "2:\n" + "vcmp.s LE, S112, S110\n" // (P dist <= 0.0f) + "bvt 0, 3f\n" // if ( CC[0] == 1 ) jump to next + "nop\n" // ( delay slot ) + "vsub.s S112, S111, S112\n" // S112 = ( S dist - P dist ) + "vdiv.s S112, S111, S112\n" // S112 = S111 / S112 + "vsub.p C120, C020, C000\n" // C120 = C020(P TEX) - C000(S TEX) + "vsub.t C130, C030, C010\n" // C130 = C030(P XYZ) - C010(S XYZ) + "vscl.p C120, C120, S112\n" // C120 = C020 * S112 + "vscl.t C130, C130, S112\n" // C130 = C030 * S112 + "vadd.p C120, C120, C000\n" // C120 = C120 + C000(S TEX) + "vadd.t C130, C130, C010\n" // C130 = C130 + C010(S XYZ) + "sv.s S120, 0($12)\n" // cv->uv[0] = S120 TEX U + "sv.s S121, 4($12)\n" // cv->uv[1] = S121 TEX V + "sv.s S130, 8($12)\n" // cv->xyz[0] = S130 XYZ X + "sv.s S131, 12($12)\n" // cv->xyz[1] = S131 XYZ Y + "sv.s S132, 16($12)\n" // cv->xyz[2] = S132 XYZ Z + "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) ) + "addiu %0, %0, 1\n" // cvc += 1 + "sv.s S020, 0($12)\n" // cv->uv[0] = S020 TEX U + "sv.s S021, 4($12)\n" // cv->uv[1] = S021 TEX V + "sv.s S030, 8($12)\n" // cv->xyz[0] = S030 XYZ X + "sv.s S031, 12($12)\n" // cv->xyz[1] = S031 XYZ Y + "sv.s S032, 16($12)\n" // cv->xyz[2] = S032 XYZ Z + "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) ) + "addiu %0, %0, 1\n" // cvc += 1 + "3:\n" // next + "addiu $8, $8, 20\n" // $8 = $8 + 20( sizeof( gu_vert_t ) ) + "addiu $9, $8, 20\n" // $9 = $8 + 20( sizeof( gu_vert_t ) ) + "bne $9, $11, 4f\n" // if ( $11 != $9 ) jump to next + "nop\n" + "move $9, %1\n" // $9 = &uv[0] set P + "4:\n" + "bne $11, $8, 0b\n" // if ( $11 != $8 ) jump to loop + "nop\n" // ( delay slot ) + ".set pop\n" // Restore assembler option + : "+r"( *cvc ) + : "r"( uv ), "r"( uvc ), "r"( cv ), "m"( *plane ) + : "$8", "$9", "$10", "$11", "$12" + ); +} + +/* +================= +GU_Clip + +Clips a polygon against the frustum +================= +*/ +void GU_Clip( gu_vert_t *uv, size_t uvc, gu_vert_t **cv, size_t* cvc ) +{ + size_t vc; + + if ( !uvc ) // no vertices to clip? + gEngfuncs.Host_Error( "GU_Clip: calling clip with zero vertices!" ); + + vc = uvc; + *cvc = 0; + + GU_Clip2Plane( &clipping_frustum[CPLANE_BOTTOM], uv, vc, work_buffer[0], &vc ); + if ( !vc ) return; + GU_Clip2Plane( &clipping_frustum[CPLANE_LEFT], work_buffer[0], vc, work_buffer[1], &vc ); + if ( !vc ) return; + GU_Clip2Plane( &clipping_frustum[CPLANE_RIGHT], work_buffer[1], vc, work_buffer[0], &vc ); + if ( !vc ) return; + *cv = extGuBeginPacket( NULL ); // uncached + GU_Clip2Plane( &clipping_frustum[CPLANE_TOP], work_buffer[0], vc, *cv, cvc ); + if ( !cvc ) return; + extGuEndPacket( ( void * )( *cv + *cvc ) ); +} \ No newline at end of file diff --git a/ref_gu/gu_context.c b/ref_gu/gu_context.c new file mode 100644 index 00000000..32d3a1fc --- /dev/null +++ b/ref_gu/gu_context.c @@ -0,0 +1,515 @@ +/* +vid_sdl.c - SDL vid component +Copyright (C) 2018 a1batross +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +// GL API function pointers, if any, reside in this translation unit +#define APIENTRY_LINKAGE +#include "gu_local.h" + +ref_api_t gEngfuncs; +ref_globals_t *gpGlobals; + +static void R_ClearScreen( void ) +{ + sceGuClearColor( 0x00000000 ); + sceGuClear( GU_COLOR_BUFFER_BIT ); +} + +static const byte *R_GetTextureOriginalBuffer( unsigned int idx ) +{ + gl_texture_t *glt = R_GetTexture( idx ); + + if( !glt || !glt->original || !glt->original->buffer ) + return NULL; + + return glt->original->buffer; +} + +/* +============= +CL_FillRGBA + +============= +*/ +static void CL_FillRGBA( float _x, float _y, float _w, float _h, int r, int g, int b, int a ) +{ + sceGuDisable( GU_TEXTURE_2D ); + sceGuEnable( GU_BLEND ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, GUBLEND1 ); + sceGuColor( GUCOLOR4UB( r, g, b, a ) ); + + gu_vert_hv_t* const out = ( gu_vert_hv_t* )sceGuGetMemory( sizeof( gu_vert_hv_t ) * 2 ); + out[0].x = _x; + out[0].y = _y; + out[0].z = 0; + out[1].x = _x + _w; + out[1].y = _y + _h; + out[1].z = 0; + sceGuDrawArray( GU_SPRITES, GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, out ); + + sceGuColor( 0xffffffff ); + sceGuEnable( GU_TEXTURE_2D ); + sceGuDisable( GU_BLEND ); +} + +/* +============= +pfnFillRGBABlend + +============= +*/ +static void GAME_EXPORT CL_FillRGBABlend( float _x, float _y, float _w, float _h, int r, int g, int b, int a ) +{ + sceGuDisable( GU_TEXTURE_2D ); + sceGuEnable( GU_BLEND ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, 0, 0 ); + sceGuColor( GUCOLOR4UB( r, g, b, a ) ); + + gu_vert_hv_t* const out = ( gu_vert_hv_t* )sceGuGetMemory( sizeof( gu_vert_hv_t ) * 2 ); + out[0].x = _x; + out[0].y = _y; + out[0].z = 0; + out[1].x = _x + _w; + out[1].y = _y + _h; + out[1].z = 0; + sceGuDrawArray( GU_SPRITES, GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, out ); + + sceGuColor( 0xffffffff ); + sceGuEnable( GU_TEXTURE_2D ); + sceGuDisable( GU_BLEND ); +} + +void Mod_BrushUnloadTextures( model_t *mod ) +{ + int i; + + for( i = 0; i < mod->numtextures; i++ ) + { + texture_t *tx = mod->textures[i]; + if( !tx || tx->gl_texturenum == tr.defaultTexture ) + continue; // free slot + + GL_FreeTexture( tx->gl_texturenum ); // main texture + GL_FreeTexture( tx->fb_texturenum ); // luma texture + } +} + +void Mod_UnloadTextures( model_t *mod ) +{ + Assert( mod != NULL ); + + switch( mod->type ) + { + case mod_studio: + Mod_StudioUnloadTextures( mod->cache.data ); + break; + case mod_alias: + Mod_AliasUnloadTextures( mod->cache.data ); + break; + case mod_brush: + Mod_BrushUnloadTextures( mod ); + break; + case mod_sprite: + Mod_SpriteUnloadTextures( mod->cache.data ); + break; + default: + ASSERT( 0 ); + break; + } +} + +qboolean Mod_ProcessRenderData( model_t *mod, qboolean create, const byte *buf ) +{ + qboolean loaded = true; + + if( create ) + { + switch( mod->type ) + { + case mod_studio: + // Mod_LoadStudioModel( mod, buf, loaded ); + break; + case mod_sprite: + Mod_LoadSpriteModel( mod, buf, &loaded, mod->numtexinfo ); + break; + case mod_alias: + Mod_LoadAliasModel( mod, buf, &loaded ); + break; + case mod_brush: + // Mod_LoadBrushModel( mod, buf, loaded ); + break; + default: gEngfuncs.Host_Error( "Mod_LoadModel: unsupported type %d\n", mod->type ); + } + } + + if( loaded && gEngfuncs.drawFuncs->Mod_ProcessUserData ) + gEngfuncs.drawFuncs->Mod_ProcessUserData( mod, create, buf ); + + if( !create ) + Mod_UnloadTextures( mod ); + + return loaded; +} + +static int GL_RefGetParm( int parm, int arg ) +{ + gl_texture_t *glt; + + switch( parm ) + { + case PARM_TEX_WIDTH: + glt = R_GetTexture( arg ); + return glt->width; + case PARM_TEX_HEIGHT: + glt = R_GetTexture( arg ); + return glt->height; + case PARM_TEX_SRC_WIDTH: + glt = R_GetTexture( arg ); + return glt->srcWidth; + case PARM_TEX_SRC_HEIGHT: + glt = R_GetTexture( arg ); + return glt->srcHeight; + case PARM_TEX_GLFORMAT: + glt = R_GetTexture( arg ); + return glt->format; + case PARM_TEX_ENCODE: + glt = R_GetTexture( arg ); + return glt->encode; + case PARM_TEX_MIPCOUNT: + glt = R_GetTexture( arg ); + return glt->numMips; + case PARM_TEX_DEPTH: + glt = R_GetTexture( arg ); + return glt->depth; + case PARM_TEX_SKYBOX: + Assert( arg >= 0 && arg < 6 ); + return tr.skyboxTextures[arg]; + case PARM_TEX_SKYTEXNUM: + return tr.skytexturenum; + case PARM_TEX_LIGHTMAP: + arg = bound( 0, arg, MAX_LIGHTMAPS - 1 ); + return tr.lightmapTextures[arg]; + case PARM_WIDESCREEN: + return gpGlobals->wideScreen; + case PARM_FULLSCREEN: + return gpGlobals->fullScreen; + case PARM_SCREEN_WIDTH: + return gpGlobals->width; + case PARM_SCREEN_HEIGHT: + return gpGlobals->height; + case PARM_TEX_TARGET: + /*glt = R_GetTexture( arg );*/ + return /*glt->target*/0; + case PARM_TEX_TEXNUM: + /*glt = R_GetTexture( arg );*/ + return /*glt->texnum*/0; + case PARM_TEX_FLAGS: + glt = R_GetTexture( arg ); + return glt->flags; + case PARM_ACTIVE_TMU: + return 0; + case PARM_LIGHTSTYLEVALUE: + arg = bound( 0, arg, MAX_LIGHTSTYLES - 1 ); + return tr.lightstylevalue[arg]; + case PARM_MAX_IMAGE_UNITS: + return 1; + case PARM_REBUILD_GAMMA: + return glConfig.softwareGammaUpdate; + case PARM_SURF_SAMPLESIZE: + if( arg >= 0 && arg < WORLDMODEL->numsurfaces ) + return gEngfuncs.Mod_SampleSizeForFace( &WORLDMODEL->surfaces[arg] ); + return LM_SAMPLE_SIZE; + case PARM_GL_CONTEXT_TYPE: + return CONTEXT_TYPE_GL; + case PARM_GLES_WRAPPER: + return GLES_WRAPPER_NONE; + case PARM_STENCIL_ACTIVE: + return glState.stencilEnabled; + case PARM_SKY_SPHERE: + return ENGINE_GET_PARM_( parm, arg ) && !tr.fCustomSkybox; + default: + return ENGINE_GET_PARM_( parm, arg ); + } + return 0; +} + +static void R_GetDetailScaleForTexture( int texture, float *xScale, float *yScale ) +{ + gl_texture_t *glt = R_GetTexture( texture ); + + if( xScale ) *xScale = glt->xscale; + if( yScale ) *yScale = glt->yscale; +} + +static void R_GetExtraParmsForTexture( int texture, byte *red, byte *green, byte *blue, byte *density ) +{ + gl_texture_t *glt = R_GetTexture( texture ); + + if( red ) *red = glt->fogParams[0]; + if( green ) *green = glt->fogParams[1]; + if( blue ) *blue = glt->fogParams[2]; + if( density ) *density = glt->fogParams[3]; +} + + +static void R_SetCurrentEntity( cl_entity_t *ent ) +{ + RI.currententity = ent; + + // set model also + if( RI.currententity != NULL ) + { + RI.currentmodel = RI.currententity->model; + } +} + +static void R_SetCurrentModel( model_t *mod ) +{ + RI.currentmodel = mod; +} + +static float R_GetFrameTime( void ) +{ + return tr.frametime; +} + +static const char *GL_TextureName( unsigned int texnum ) +{ + return R_GetTexture( texnum )->name; +} + +const byte *GL_TextureData( unsigned int texnum ) +{ + rgbdata_t *pic = R_GetTexture( texnum )->original; + + if( pic != NULL ) + return pic->buffer; + return NULL; +} + +void R_ProcessEntData( qboolean allocate ) +{ + if( !allocate ) + { + tr.draw_list->num_solid_entities = 0; + tr.draw_list->num_trans_entities = 0; + tr.draw_list->num_beam_entities = 0; + } + + if( gEngfuncs.drawFuncs->R_ProcessEntData ) + gEngfuncs.drawFuncs->R_ProcessEntData( allocate ); +} + +qboolean R_SetDisplayTransform( ref_screen_rotation_t rotate, int offset_x, int offset_y, float scale_x, float scale_y ) +{ + qboolean ret = true; + if( rotate > 0 ) + { + gEngfuncs.Con_Printf("rotation transform not supported\n"); + ret = false; + } + + if( offset_x || offset_y ) + { + gEngfuncs.Con_Printf("offset transform not supported\n"); + ret = false; + } + + if( scale_x != 1.0f || scale_y != 1.0f ) + { + gEngfuncs.Con_Printf("scale transform not supported\n"); + ret = false; + } + + return ret; +} + +static void* GAME_EXPORT R_GetProcAddress( const char *name ) +{ + return gEngfuncs.GL_GetProcAddress( name ); +} + +static const char *R_GetConfigName( void ) +{ + return "ref_gu"; +} + +ref_interface_t gReffuncs = +{ + R_Init, + R_Shutdown, + R_GetConfigName, + R_SetDisplayTransform, + + GL_SetupAttributes, + GL_InitExtensions, + GL_ClearExtensions, + + R_BeginFrame, + R_RenderScene, + R_EndFrame, + R_PushScene, + R_PopScene, + GL_BackendStartFrame, + GL_BackendEndFrame, + + R_ClearScreen, + R_AllowFog, + GL_SetRenderMode, + + R_AddEntity, + CL_AddCustomBeam, + R_ProcessEntData, + + R_ShowTextures, + + R_GetTextureOriginalBuffer, + GL_LoadTextureFromBuffer, + GL_ProcessTexture, + R_SetupSky, + + R_Set2DMode, + R_DrawStretchRaw, + R_DrawStretchPic, + R_DrawTileClear, + CL_FillRGBA, + CL_FillRGBABlend, + + VID_ScreenShot, + VID_CubemapShot, + + R_LightPoint, + + R_DecalShoot, + R_DecalRemoveAll, + R_CreateDecalList, + R_ClearAllDecals, + + R_StudioEstimateFrame, + R_StudioLerpMovement, + CL_InitStudioAPI, + + R_InitSkyClouds, + GL_SubdivideSurface, + CL_RunLightStyles, + + R_GetSpriteParms, + R_GetSpriteTexture, + + Mod_LoadMapSprite, + Mod_ProcessRenderData, + Mod_StudioLoadTextures, + + CL_DrawParticles, + CL_DrawTracers, + CL_DrawBeams, + R_BeamCull, + + GL_RefGetParm, + R_GetDetailScaleForTexture, + R_GetExtraParmsForTexture, + R_GetFrameTime, + + R_SetCurrentEntity, + R_SetCurrentModel, + + GL_FindTexture, + GL_TextureName, + GL_TextureData, + GL_LoadTexture, + GL_CreateTexture, + GL_LoadTextureArray, + GL_CreateTextureArray, + GL_FreeTexture, + + DrawSingleDecal, + R_DecalSetupVerts, + R_EntityRemoveDecals, + + R_UploadStretchRaw, + + GL_Bind, + GL_SelectTexture, + GL_LoadTexMatrixExt, + GL_LoadIdentityTexMatrix, + GL_CleanUpTextureUnits, + GL_TexGen, + GL_TextureTarget, + GL_SetTexCoordArrayMode, + GL_UpdateTexSize, + NULL, + NULL, + + CL_DrawParticlesExternal, + R_LightVec, + R_StudioGetTexture, + + R_RenderFrame, + Mod_SetOrthoBounds, + R_SpeedsMessage, + Mod_GetCurrentVis, + R_NewMap, + R_ClearScene, + R_GetProcAddress, + + TriRenderMode, + TriBegin, + TriEnd, + _TriColor4f, + _TriColor4ub, + TriTexCoord2f, + TriVertex3fv, + TriVertex3f, + TriWorldToScreen, + TriFog, + R_ScreenToWorld, + TriGetMatrix, + TriFogParams, + TriCullFace, + + VGUI_DrawInit, + VGUI_DrawShutdown, + VGUI_SetupDrawingText, + VGUI_SetupDrawingRect, + VGUI_SetupDrawingImage, + VGUI_BindTexture, + VGUI_EnableTexture, + VGUI_CreateTexture, + VGUI_UploadTexture, + VGUI_UploadTextureBlock, + VGUI_DrawQuad, + VGUI_GetTextureSizes, + VGUI_GenerateTexture, +}; + +int EXPORT GetRefAPI( int version, ref_interface_t *funcs, ref_api_t *engfuncs, ref_globals_t *globals ) +{ + if( version != REF_API_VERSION ) + return 0; + + // fill in our callbacks + memcpy( funcs, &gReffuncs, sizeof( ref_interface_t )); + memcpy( &gEngfuncs, engfuncs, sizeof( ref_api_t )); + gpGlobals = globals; + + return REF_API_VERSION; +} + +void EXPORT GetRefHumanReadableName( char *out, size_t size ) +{ + Q_strncpy( out, "sceGu", size ); +} diff --git a/ref_gu/gu_cull.c b/ref_gu/gu_cull.c new file mode 100644 index 00000000..782ba4e5 --- /dev/null +++ b/ref_gu/gu_cull.c @@ -0,0 +1,149 @@ +/* +gl_cull.c - render culling routines +Copyright (C) 2010 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "entity_types.h" + +/* +============================================================= + +FRUSTUM AND PVS CULLING + +============================================================= +*/ +/* +================= +R_CullBox + +Returns true if the box is completely outside the frustum +================= +*/ +qboolean R_CullBox( const vec3_t mins, const vec3_t maxs ) +{ + return GL_FrustumCullBox( &RI.frustum, mins, maxs, 0 ); +} + +/* +================= +R_CullSphere + +Returns true if the sphere is completely outside the frustum +================= +*/ +qboolean R_CullSphere( const vec3_t centre, const float radius ) +{ + return GL_FrustumCullSphere( &RI.frustum, centre, radius, 0 ); +} + +/* +============= +R_CullModel +============= +*/ +int R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ) +{ + if( e == gEngfuncs.GetViewModel() ) + { + if( ENGINE_GET_PARM( PARM_DEV_OVERVIEW )) + return 1; + + if( RP_NORMALPASS() && !ENGINE_GET_PARM( PARM_THIRDPERSON ) && CL_IsViewEntityLocalPlayer()) + return 0; + + return 1; + } + + // local client can't view himself if camera or thirdperson is not active + if( RP_LOCALCLIENT( e ) && !ENGINE_GET_PARM( PARM_THIRDPERSON ) && CL_IsViewEntityLocalPlayer()) + return 1; + + if( R_CullBox( absmin, absmax )) + return 1; + + return 0; +} + +/* +================= +R_CullSurface + +cull invisible surfaces +================= +*/ +int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags ) +{ + cl_entity_t *e = RI.currententity; + + if( !surf || !surf->texinfo || !surf->texinfo->texture ) + return CULL_OTHER; + + if( r_nocull->value ) + return CULL_VISIBLE; + + // world surfaces can be culled by vis frame too + if( RI.currententity == gEngfuncs.GetEntityByIndex( 0 ) && surf->visframe != tr.framecount ) + return CULL_VISFRAME; + + // only static ents can be culled by frustum + if( !R_StaticEntity( e )) frustum = NULL; + + if( !VectorIsNull( surf->plane->normal )) + { + float dist; + + // can use normal.z for world (optimisation) + if( RI.drawOrtho ) + { + vec3_t orthonormal; + + if( e == gEngfuncs.GetEntityByIndex( 0 ) ) orthonormal[2] = surf->plane->normal[2]; + else Matrix4x4_VectorRotate( RI.objectMatrix, surf->plane->normal, orthonormal ); + dist = orthonormal[2]; + } + else dist = PlaneDiff( tr.modelorg, surf->plane ); + + if( glState.faceCull == GL_FRONT ) + { + if( FBitSet( surf->flags, SURF_PLANEBACK )) + { + if( dist >= -BACKFACE_EPSILON ) + return CULL_BACKSIDE; // wrong side + } + else + { + if( dist <= BACKFACE_EPSILON ) + return CULL_BACKSIDE; // wrong side + } + } + else if( glState.faceCull == GL_BACK ) + { + if( FBitSet( surf->flags, SURF_PLANEBACK )) + { + if( dist <= BACKFACE_EPSILON ) + return CULL_BACKSIDE; // wrong side + } + else + { + if( dist >= -BACKFACE_EPSILON ) + return CULL_BACKSIDE; // wrong side + } + } + } + + if( frustum && GL_FrustumCullBox( frustum, surf->info->mins, surf->info->maxs, clipflags )) + return CULL_FRUSTUM; + + return CULL_VISIBLE; +} diff --git a/ref_gu/gu_dbghulls.c b/ref_gu/gu_dbghulls.c new file mode 100644 index 00000000..5d340ff7 --- /dev/null +++ b/ref_gu/gu_dbghulls.c @@ -0,0 +1,124 @@ +/* +gl_dbghulls.c - loading & handling world and brushmodels +Copyright (C) 2016 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "mod_local.h" + +#define list_entry( ptr, type, member ) \ + ((type *)((char *)(ptr) - (size_t)(&((type *)0)->member))) + +// iterate over each entry in the list +#define list_for_each_entry( pos, head, member ) \ + for( pos = list_entry( (head)->next, winding_t, member ); \ + &pos->member != (head); \ + pos = list_entry( pos->member.next, winding_t, member )) + +// REFTODO: rewrite in triapi +void R_DrawWorldHull( void ) +{ + hull_model_t *hull = &WORLD->hull_models[0]; + winding_t *poly; + int i; + + if( FBitSet( r_showhull->flags, FCVAR_CHANGED )) + { + int val = bound( 0, (int)r_showhull->value, 3 ); + if( val ) gEngfuncs.Mod_CreatePolygonsForHull( val ); + ClearBits( r_showhull->flags, FCVAR_CHANGED ); + } + + if( !CVAR_TO_BOOL( r_showhull )) + return; +#if 1 + sceGuDisable( GU_TEXTURE_2D ); + list_for_each_entry( poly, &hull->polys, chain ) + { + srand((unsigned int)poly); + sceGuColor( GU_RGBA(rand() % 256, rand() % 256, rand() % 256, rand() % 256) ); + gu_vert_fv_t* const out = ( gu_vert_fv_t* )sceGuGetMemory( sizeof( gu_vert_fv_t ) * poly->numpoints ); + for( i = 0; i < poly->numpoints; i++ ) + { + out[i].x = poly->p[i][0]; + out[i].y = poly->p[i][1]; + out[i].z = poly->p[i][2]; + } + sceGuDrawArray( GU_TRIANGLE_FAN, GU_VERTEX_32BITF, poly->numpoints, 0, out ); + } + sceGuEnable( GU_TEXTURE_2D ); +#else + pglDisable( GL_TEXTURE_2D ); + list_for_each_entry( poly, &hull->polys, chain ) + { + srand((unsigned int)poly); + pglColor3f( rand() % 256 / 255.0, rand() % 256 / 255.0, rand() % 256 / 255.0 ); + pglBegin( GL_POLYGON ); + for( i = 0; i < poly->numpoints; i++ ) + pglVertex3fv( poly->p[i] ); + pglEnd(); + } + pglEnable( GL_TEXTURE_2D ); +#endif +} + +void R_DrawModelHull( void ) +{ + hull_model_t *hull; + winding_t *poly; + int i; + + if( !CVAR_TO_BOOL( r_showhull )) + return; + + if( !RI.currentmodel || RI.currentmodel->name[0] != '*' ) + return; + + i = atoi( RI.currentmodel->name + 1 ); + if( i < 1 || i >= WORLD->num_hull_models ) + return; + + hull = &WORLD->hull_models[i]; +#if 1 + sceGuDisable( GU_TEXTURE_2D ); + list_for_each_entry( poly, &hull->polys, chain ) + { + srand((unsigned int)poly); + sceGuColor( GU_RGBA(rand() % 256, rand() % 256, rand() % 256, rand() % 256) ); + gu_vert_fv_t* const out = ( gu_vert_fv_t* )sceGuGetMemory( sizeof( gu_vert_fv_t ) * poly->numpoints ); + for( i = 0; i < poly->numpoints; i++ ) + { + out[i].x = poly->p[i][0]; + out[i].y = poly->p[i][1]; + out[i].z = poly->p[i][2]; + } + sceGuDrawArray( GU_TRIANGLE_FAN, GU_VERTEX_32BITF, poly->numpoints, 0, out ); + } + sceGuEnable( GU_TEXTURE_2D ); +#else + pglPolygonOffset( 1.0f, 2.0 ); + pglEnable( GL_POLYGON_OFFSET_FILL ); + pglDisable( GL_TEXTURE_2D ); + list_for_each_entry( poly, &hull->polys, chain ) + { + srand((unsigned int)poly); + pglColor3f( rand() % 256 / 255.0, rand() % 256 / 255.0, rand() % 256 / 255.0 ); + pglBegin( GL_POLYGON ); + for( i = 0; i < poly->numpoints; i++ ) + pglVertex3fv( poly->p[i] ); + pglEnd(); + } + pglEnable( GL_TEXTURE_2D ); + pglDisable( GL_POLYGON_OFFSET_FILL ); +#endif +} diff --git a/ref_gu/gu_decals.c b/ref_gu/gu_decals.c new file mode 100644 index 00000000..a3e52985 --- /dev/null +++ b/ref_gu/gu_decals.c @@ -0,0 +1,1268 @@ +/* +gl_decals.c - decal paste and rendering +Copyright (C) 2010 Uncle Mike +Copyright (C) 2020 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "cl_tent.h" + +#define DECAL_OVERLAP_DISTANCE 2 +#define DECAL_DISTANCE 4 // too big values produce more clipped polygons +#define MAX_DECALCLIPVERT 32 // produced vertexes of fragmented decal +#define DECAL_CACHEENTRY 256 // MUST BE POWER OF 2 or code below needs to change! +#define DECAL_TRANSPARENT_THRESHOLD 230 // transparent decals draw with GL_MODULATE + +// empirically determined constants for minimizing overalpping decals +#define MAX_OVERLAP_DECALS 6 +#define DECAL_OVERLAP_DIST 8 +#define MIN_DECAL_SCALE 0.01f +#define MAX_DECAL_SCALE 16.0f + +// clip edges +#define LEFT_EDGE 0 +#define RIGHT_EDGE 1 +#define TOP_EDGE 2 +#define BOTTOM_EDGE 3 + +// This structure contains the information used to create new decals +typedef struct +{ + vec3_t m_Position; // world coordinates of the decal center + model_t *m_pModel; // the model the decal is going to be applied in + int m_iTexture; // The decal material + int m_Size; // Size of the decal (in world coords) + int m_Flags; + int m_Entity; // Entity the decal is applied to. + float m_scale; + int m_decalWidth; + int m_decalHeight; + vec3_t m_Basis[3]; +} decalinfo_t; + +static float g_DecalClipVerts[MAX_DECALCLIPVERT][VERTEXSIZE]; +static float g_DecalClipVerts2[MAX_DECALCLIPVERT][VERTEXSIZE]; + +decal_t gDecalPool[MAX_RENDER_DECALS]; +static int gDecalCount; + +void R_ClearDecals( void ) +{ + memset( gDecalPool, 0, sizeof( gDecalPool )); + gDecalCount = 0; +} + +// unlink pdecal from any surface it's attached to +static void R_DecalUnlink( decal_t *pdecal ) +{ + decal_t *tmp; + + if( pdecal->psurface ) + { + if( pdecal->psurface->pdecals == pdecal ) + { + pdecal->psurface->pdecals = pdecal->pnext; + } + else + { + tmp = pdecal->psurface->pdecals; + if( !tmp ) gEngfuncs.Host_Error( "R_DecalUnlink: bad decal list\n" ); + + while( tmp->pnext ) + { + if( tmp->pnext == pdecal ) + { + tmp->pnext = pdecal->pnext; + break; + } + tmp = tmp->pnext; + } + } + } + + if( pdecal->polys ) + Mem_Free( pdecal->polys ); + + pdecal->psurface = NULL; + pdecal->polys = NULL; +} + +// Just reuse next decal in list +// A decal that spans multiple surfaces will use multiple decal_t pool entries, +// as each surface needs it's own. +static decal_t *R_DecalAlloc( decal_t *pdecal ) +{ + int limit = MAX_RENDER_DECALS; + + if( r_decals->value < limit ) + limit = r_decals->value; + + if( !limit ) return NULL; + + if( !pdecal ) + { + int count = 0; + + // check for the odd possiblity of infinte loop + do + { + if( gDecalCount >= limit ) + gDecalCount = 0; + + pdecal = &gDecalPool[gDecalCount]; // reuse next decal + gDecalCount++; + count++; + } while( FBitSet( pdecal->flags, FDECAL_PERMANENT ) && count < limit ); + } + + // if decal is already linked to a surface, unlink it. + R_DecalUnlink( pdecal ); + + return pdecal; +} + +//----------------------------------------------------------------------------- +// find decal image and grab size from it +//----------------------------------------------------------------------------- +static void R_GetDecalDimensions( int texture, int *width, int *height ) +{ + if( width ) *width = 1; // to avoid divide by zero + if( height ) *height = 1; + + R_GetTextureParms( width, height, texture ); +} + +//----------------------------------------------------------------------------- +// compute the decal basis based on surface normal +//----------------------------------------------------------------------------- +void R_DecalComputeBasis( msurface_t *surf, int flags, vec3_t textureSpaceBasis[3] ) +{ + vec3_t surfaceNormal; + + // setup normal + if( surf->flags & SURF_PLANEBACK ) + VectorNegate( surf->plane->normal, surfaceNormal ); + else VectorCopy( surf->plane->normal, surfaceNormal ); + + VectorNormalize2( surfaceNormal, textureSpaceBasis[2] ); +#if 0 + if( FBitSet( flags, FDECAL_CUSTOM )) + { + vec3_t pSAxis = { 1, 0, 0 }; + + // T = S cross N + CrossProduct( pSAxis, textureSpaceBasis[2], textureSpaceBasis[1] ); + + // Name sure they aren't parallel or antiparallel + // In that case, fall back to the normal algorithm. + if( DotProduct( textureSpaceBasis[1], textureSpaceBasis[1] ) > 1e-6 ) + { + // S = N cross T + CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] ); + + VectorNormalizeFast( textureSpaceBasis[0] ); + VectorNormalizeFast( textureSpaceBasis[1] ); + return; + } + // Fall through to the standard algorithm for parallel or antiparallel + } +#endif + VectorNormalize2( surf->texinfo->vecs[0], textureSpaceBasis[0] ); + VectorNormalize2( surf->texinfo->vecs[1], textureSpaceBasis[1] ); +} + +void R_SetupDecalTextureSpaceBasis( decal_t *pDecal, msurface_t *surf, int texture, vec3_t textureSpaceBasis[3], float decalWorldScale[2] ) +{ + int width, height; + + // Compute the non-scaled decal basis + R_DecalComputeBasis( surf, pDecal->flags, textureSpaceBasis ); + R_GetDecalDimensions( texture, &width, &height ); + + // world width of decal = ptexture->width / pDecal->scale + // world height of decal = ptexture->height / pDecal->scale + // scale is inverse, scales world space to decal u/v space [0,1] + // OPTIMIZE: Get rid of these divides + decalWorldScale[0] = (float)pDecal->scale / width; + decalWorldScale[1] = (float)pDecal->scale / height; + + VectorScale( textureSpaceBasis[0], decalWorldScale[0], textureSpaceBasis[0] ); + VectorScale( textureSpaceBasis[1], decalWorldScale[1], textureSpaceBasis[1] ); +} + +// Build the initial list of vertices from the surface verts into the global array, 'verts'. +void R_SetupDecalVertsForMSurface( decal_t *pDecal, msurface_t *surf, vec3_t textureSpaceBasis[3], float *verts ) +{ + float *v; + int i; + + for( i = 0; i < surf->polys->numverts; i++, verts += VERTEXSIZE ) + { + VectorCopy( surf->polys->verts[i].xyz, verts ); // copy model space coordinates + verts[3] = DotProduct( verts, textureSpaceBasis[0] ) - pDecal->dx + 0.5f; + verts[4] = DotProduct( verts, textureSpaceBasis[1] ) - pDecal->dy + 0.5f; + verts[5] = verts[6] = 0.0f; + } +} + +// Figure out where the decal maps onto the surface. +void R_SetupDecalClip( decal_t *pDecal, msurface_t *surf, int texture, vec3_t textureSpaceBasis[3], float decalWorldScale[2] ) +{ + R_SetupDecalTextureSpaceBasis( pDecal, surf, texture, textureSpaceBasis, decalWorldScale ); + + // Generate texture coordinates for each vertex in decal s,t space + // probably should pre-generate this, store it and use it for decal-decal collisions + // as in R_DecalsIntersect() + pDecal->dx = DotProduct( pDecal->position, textureSpaceBasis[0] ); + pDecal->dy = DotProduct( pDecal->position, textureSpaceBasis[1] ); +} + +// Quick and dirty sutherland Hodgman clipper +// Clip polygon to decal in texture space +// JAY: This code is lame, change it later. It does way too much work per frame +// It can be made to recursively call the clipping code and only copy the vertex list once +int R_ClipInside( float *vert, int edge ) +{ + switch( edge ) + { + case LEFT_EDGE: + if( vert[3] > 0.0f ) + return 1; + return 0; + case RIGHT_EDGE: + if( vert[3] < 1.0f ) + return 1; + return 0; + case TOP_EDGE: + if( vert[4] > 0.0f ) + return 1; + return 0; + case BOTTOM_EDGE: + if( vert[4] < 1.0f ) + return 1; + return 0; + } + return 0; +} + +void R_ClipIntersect( float *one, float *two, float *out, int edge ) +{ + float t; + + // t is the parameter of the line between one and two clipped to the edge + // or the fraction of the clipped point between one & two + // vert[0], vert[1], vert[2] is X, Y, Z + // vert[3] is u + // vert[4] is v + // vert[5] is lightmap u + // vert[6] is lightmap v + + if( edge < TOP_EDGE ) + { + if( edge == LEFT_EDGE ) + { + // left + t = ((one[3] - 0.0f) / (one[3] - two[3])); + out[3] = out[5] = 0.0f; + } + else + { + // right + t = ((one[3] - 1.0f) / (one[3] - two[3])); + out[3] = out[5] = 1.0f; + } + + out[4] = one[4] + (two[4] - one[4]) * t; + out[6] = one[6] + (two[6] - one[6]) * t; + } + else + { + if( edge == TOP_EDGE ) + { + // top + t = ((one[4] - 0.0f) / (one[4] - two[4])); + out[4] = out[6] = 0.0f; + } + else + { + // bottom + t = ((one[4] - 1.0f) / (one[4] - two[4])); + out[4] = out[6] = 1.0f; + } + + out[3] = one[3] + (two[3] - one[3]) * t; + out[5] = one[5] + (two[4] - one[5]) * t; + } + + VectorLerp( one, t, two, out ); +} + +static int SHClip( float *vert, int vertCount, float *out, int edge ) +{ + int j, outCount; + float *s, *p; + + outCount = 0; + + s = &vert[(vertCount - 1) * VERTEXSIZE]; + + for( j = 0; j < vertCount; j++ ) + { + p = &vert[j * VERTEXSIZE]; + + if( R_ClipInside( p, edge )) + { + if( R_ClipInside( s, edge )) + { + // Add a vertex and advance out to next vertex + memcpy( out, p, sizeof( float ) * VERTEXSIZE ); + out += VERTEXSIZE; + outCount++; + } + else + { + R_ClipIntersect( s, p, out, edge ); + out += VERTEXSIZE; + outCount++; + + memcpy( out, p, sizeof( float ) * VERTEXSIZE ); + out += VERTEXSIZE; + outCount++; + } + } + else + { + if( R_ClipInside( s, edge )) + { + R_ClipIntersect( p, s, out, edge ); + out += VERTEXSIZE; + outCount++; + } + } + + s = p; + } + + return outCount; +} + +float *R_DoDecalSHClip( float *pInVerts, decal_t *pDecal, int nStartVerts, int *pVertCount ) +{ + float *pOutVerts = g_DecalClipVerts[0]; + int outCount; + + // clip the polygon to the decal texture space + outCount = SHClip( pInVerts, nStartVerts, g_DecalClipVerts2[0], LEFT_EDGE ); + outCount = SHClip( g_DecalClipVerts2[0], outCount, g_DecalClipVerts[0], RIGHT_EDGE ); + outCount = SHClip( g_DecalClipVerts[0], outCount, g_DecalClipVerts2[0], TOP_EDGE ); + outCount = SHClip( g_DecalClipVerts2[0], outCount, pOutVerts, BOTTOM_EDGE ); + + if( pVertCount ) + *pVertCount = outCount; + + return pOutVerts; +} + +//----------------------------------------------------------------------------- +// Generate clipped vertex list for decal pdecal projected onto polygon psurf +//----------------------------------------------------------------------------- +float *R_DecalVertsClip( decal_t *pDecal, msurface_t *surf, int texture, int *pVertCount ) +{ + float decalWorldScale[2]; + vec3_t textureSpaceBasis[3]; + + // figure out where the decal maps onto the surface. + R_SetupDecalClip( pDecal, surf, texture, textureSpaceBasis, decalWorldScale ); + + // build the initial list of vertices from the surface verts. + R_SetupDecalVertsForMSurface( pDecal, surf, textureSpaceBasis, g_DecalClipVerts[0] ); + + return R_DoDecalSHClip( g_DecalClipVerts[0], pDecal, surf->polys->numverts, pVertCount ); +} + +// Generate lighting coordinates at each vertex for decal vertices v[] on surface psurf +static void R_DecalVertsLight( float *v, msurface_t *surf, int vertCount ) +{ + float s, t; + mtexinfo_t *tex; + mextrasurf_t *info = surf->info; + float sample_size; + int j; + + sample_size = gEngfuncs.Mod_SampleSizeForFace( surf ); + tex = surf->texinfo; + + for( j = 0; j < vertCount; j++, v += VERTEXSIZE ) + { + // lightmap texture coordinates + s = DotProduct( v, info->lmvecs[0] ) + info->lmvecs[0][3] - info->lightmapmins[0]; + s += surf->light_s * sample_size; + s += sample_size * 0.5f; + s /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->width; + + t = DotProduct( v, info->lmvecs[1] ) + info->lmvecs[1][3] - info->lightmapmins[1]; + t += surf->light_t * sample_size; + t += sample_size * 0.5f; + t /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->height; + + v[5] = s; + v[6] = t; + } +} + +// Check for intersecting decals on this surface +static decal_t *R_DecalIntersect( decalinfo_t *decalinfo, msurface_t *surf, int *pcount ) +{ + int texture; + decal_t *plast, *pDecal; + vec3_t decalExtents[2]; + float lastArea = 2; + int mapSize[2]; + + plast = NULL; + *pcount = 0; + + // (Same as R_SetupDecalClip). + texture = decalinfo->m_iTexture; + + // precalculate the extents of decalinfo's decal in world space. + R_GetDecalDimensions( texture, &mapSize[0], &mapSize[1] ); + VectorScale( decalinfo->m_Basis[0], ((mapSize[0] / decalinfo->m_scale) * 0.5f), decalExtents[0] ); + VectorScale( decalinfo->m_Basis[1], ((mapSize[1] / decalinfo->m_scale) * 0.5f), decalExtents[1] ); + + pDecal = surf->pdecals; + + while( pDecal ) + { + texture = pDecal->texture; + + // Don't steal bigger decals and replace them with smaller decals + // Don't steal permanent decals + if( !FBitSet( pDecal->flags, FDECAL_PERMANENT )) + { + vec3_t testBasis[3]; + vec3_t testPosition[2]; + float testWorldScale[2]; + vec2_t vDecalMin, vDecalMax; + vec2_t vUnionMin, vUnionMax; + + R_SetupDecalTextureSpaceBasis( pDecal, surf, texture, testBasis, testWorldScale ); + + VectorSubtract( decalinfo->m_Position, decalExtents[0], testPosition[0] ); + VectorSubtract( decalinfo->m_Position, decalExtents[1], testPosition[1] ); + + // Here, we project the min and max extents of the decal that got passed in into + // this decal's (pDecal's) [0,0,1,1] clip space, just like we would if we were + // clipping a triangle into pDecal's clip space. + Vector2Set( vDecalMin, + DotProduct( testPosition[0], testBasis[0] ) - pDecal->dx + 0.5f, + DotProduct( testPosition[1], testBasis[1] ) - pDecal->dy + 0.5f ); + + VectorAdd( decalinfo->m_Position, decalExtents[0], testPosition[0] ); + VectorAdd( decalinfo->m_Position, decalExtents[1], testPosition[1] ); + + Vector2Set( vDecalMax, + DotProduct( testPosition[0], testBasis[0] ) - pDecal->dx + 0.5f, + DotProduct( testPosition[1], testBasis[1] ) - pDecal->dy + 0.5f ); + + // Now figure out the part of the projection that intersects pDecal's + // clip box [0,0,1,1]. + Vector2Set( vUnionMin, max( vDecalMin[0], 0 ), max( vDecalMin[1], 0 )); + Vector2Set( vUnionMax, min( vDecalMax[0], 1 ), min( vDecalMax[1], 1 )); + + if( vUnionMin[0] < 1 && vUnionMin[1] < 1 && vUnionMax[0] > 0 && vUnionMax[1] > 0 ) + { + // Figure out how much of this intersects the (0,0) - (1,1) bbox. + float flArea = (vUnionMax[0] - vUnionMin[1]) * (vUnionMax[1] - vUnionMin[1]); + + if( flArea > 0.6f ) + { + *pcount += 1; + + if( !plast || flArea <= lastArea ) + { + plast = pDecal; + lastArea = flArea; + } + } + } + } + pDecal = pDecal->pnext; + } + return plast; +} + +/* +==================== +R_DecalCreatePoly + +creates mesh for decal on first rendering +==================== +*/ +glpoly_t *R_DecalCreatePoly( decalinfo_t *decalinfo, decal_t *pdecal, msurface_t *surf ) +{ + int lnumverts; + glpoly_t *poly; + float *v; + int i; + + if( pdecal->polys ) // already created? + return pdecal->polys; + + v = R_DecalSetupVerts( pdecal, surf, pdecal->texture, &lnumverts ); + if( !lnumverts ) return NULL; // probably this never happens + + // allocate glpoly + // REFTODO: com_studiocache pool! + poly = Mem_Calloc( r_temppool, sizeof( glpoly_t ) + ( lnumverts * 2 - 1 ) * sizeof( gu_vert_t )); + poly->next = pdecal->polys; + poly->flags = surf->flags; + pdecal->polys = poly; + poly->numverts = lnumverts; + + for( i = 0; i < lnumverts; i++, v += VERTEXSIZE ) + { + poly->verts[i].uv[0] = v[3]; + poly->verts[i].uv[1] = v[4]; + VectorCopy( v, poly->verts[i].xyz ); + + poly->verts[i + lnumverts].uv[0] = v[5]; + poly->verts[i + lnumverts].uv[1] = v[6]; + VectorCopy( v, poly->verts[i + lnumverts].xyz ); + } + + return poly; +} + +// Add the decal to the surface's list of decals. +static void R_AddDecalToSurface( decal_t *pdecal, msurface_t *surf, decalinfo_t *decalinfo ) +{ + decal_t *pold; + + pdecal->pnext = NULL; + pold = surf->pdecals; + + if( pold ) + { + while( pold->pnext ) + pold = pold->pnext; + pold->pnext = pdecal; + } + else + { + surf->pdecals = pdecal; + } + + // tag surface + pdecal->psurface = surf; + + // at this point decal are linked with surface + // and will be culled, drawing and sorting + // together with surface + + // alloc clipped poly for decal + R_DecalCreatePoly( decalinfo, pdecal, surf ); +} + +static void R_DecalCreate( decalinfo_t *decalinfo, msurface_t *surf, float x, float y ) +{ + decal_t *pdecal, *pold; + int count, vertCount; + + if( !surf ) return; // ??? + + pold = R_DecalIntersect( decalinfo, surf, &count ); + if( count < MAX_OVERLAP_DECALS ) pold = NULL; + + pdecal = R_DecalAlloc( pold ); + if( !pdecal ) return; // r_decals == 0 ??? + + pdecal->flags = decalinfo->m_Flags; + + VectorCopy( decalinfo->m_Position, pdecal->position ); + + pdecal->dx = x; + pdecal->dy = y; + + // set scaling + pdecal->scale = decalinfo->m_scale; + pdecal->entityIndex = decalinfo->m_Entity; + pdecal->texture = decalinfo->m_iTexture; + + // check to see if the decal actually intersects the surface + // if not, then remove the decal + R_DecalVertsClip( pdecal, surf, decalinfo->m_iTexture, &vertCount ); + + if( !vertCount ) + { + R_DecalUnlink( pdecal ); + return; + } + + // add to the surface's list + R_AddDecalToSurface( pdecal, surf, decalinfo ); +} + +void R_DecalSurface( msurface_t *surf, decalinfo_t *decalinfo ) +{ + // get the texture associated with this surface + mtexinfo_t *tex = surf->texinfo; + decal_t *decal = surf->pdecals; + vec4_t textureU, textureV; + float s, t, w, h; + connstate_t state = ENGINE_GET_PARM( PARM_CONNSTATE ); + + // we in restore mode + if( state == ca_connected || state == ca_validate ) + { + // NOTE: we may have the decal on this surface that come from another level. + // check duplicate with same position and texture + while( decal != NULL ) + { + if( VectorCompare( decal->position, decalinfo->m_Position ) && decal->texture == decalinfo->m_iTexture ) + return; // decal already exists, don't place it again + decal = decal->pnext; + } + } + + Vector4Copy( tex->vecs[0], textureU ); + Vector4Copy( tex->vecs[1], textureV ); + + // project decal center into the texture space of the surface + s = DotProduct( decalinfo->m_Position, textureU ) + textureU[3] - surf->texturemins[0]; + t = DotProduct( decalinfo->m_Position, textureV ) + textureV[3] - surf->texturemins[1]; + + // Determine the decal basis (measured in world space) + // Note that the decal basis vectors 0 and 1 will always lie in the same + // plane as the texture space basis vectorstextureVecsTexelsPerWorldUnits. + R_DecalComputeBasis( surf, decalinfo->m_Flags, decalinfo->m_Basis ); + + // Compute an effective width and height (axis aligned) in the parent texture space + // How does this work? decalBasis[0] represents the u-direction (width) + // of the decal measured in world space, decalBasis[1] represents the + // v-direction (height) measured in world space. + // textureVecsTexelsPerWorldUnits[0] represents the u direction of + // the surface's texture space measured in world space (with the appropriate + // scale factor folded in), and textureVecsTexelsPerWorldUnits[1] + // represents the texture space v direction. We want to find the dimensions (w,h) + // of a square measured in texture space, axis aligned to that coordinate system. + // All we need to do is to find the components of the decal edge vectors + // (decalWidth * decalBasis[0], decalHeight * decalBasis[1]) + // in texture coordinates: + + w = fabs( decalinfo->m_decalWidth * DotProduct( textureU, decalinfo->m_Basis[0] )) + + fabs( decalinfo->m_decalHeight * DotProduct( textureU, decalinfo->m_Basis[1] )); + + h = fabs( decalinfo->m_decalWidth * DotProduct( textureV, decalinfo->m_Basis[0] )) + + fabs( decalinfo->m_decalHeight * DotProduct( textureV, decalinfo->m_Basis[1] )); + + // move s,t to upper left corner + s -= ( w * 0.5f ); + t -= ( h * 0.5f ); + + // Is this rect within the surface? -- tex width & height are unsigned + if( s <= -w || t <= -h || s > (surf->extents[0] + w) || t > (surf->extents[1] + h)) + { + return; // nope + } + + // stamp it + R_DecalCreate( decalinfo, surf, s, t ); +} + +//----------------------------------------------------------------------------- +// iterate over all surfaces on a node, looking for surfaces to decal +//----------------------------------------------------------------------------- +static void R_DecalNodeSurfaces( model_t *model, mnode_t *node, decalinfo_t *decalinfo ) +{ + // iterate over all surfaces in the node + msurface_t *surf; + int i; + + surf = model->surfaces + node->firstsurface; + + for( i = 0; i < node->numsurfaces; i++, surf++ ) + { + // never apply decals on the water or sky surfaces + if( surf->flags & (SURF_DRAWTURB|SURF_DRAWSKY|SURF_CONVEYOR)) + continue; + + if( surf->flags & SURF_TRANSPARENT && !glState.stencilEnabled ) + continue; + + R_DecalSurface( surf, decalinfo ); + } +} + +//----------------------------------------------------------------------------- +// Recursive routine to find surface to apply a decal to. World coordinates of +// the decal are passed in r_recalpos like the rest of the engine. This should +// be called through R_DecalShoot() +//----------------------------------------------------------------------------- +static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo ) +{ + mplane_t *splitplane; + float dist; + + Assert( node != NULL ); + + if( node->contents < 0 ) + { + // hit a leaf + return; + } + + splitplane = node->plane; + dist = DotProduct( decalinfo->m_Position, splitplane->normal ) - splitplane->dist; + + // This is arbitrarily set to 10 right now. In an ideal world we'd have the + // exact surface but we don't so, this tells me which planes are "sort of + // close" to the gunshot -- the gunshot is actually 4 units in front of the + // wall (see dlls\weapons.cpp). We also need to check to see if the decal + // actually intersects the texture space of the surface, as this method tags + // parallel surfaces in the same node always. + // JAY: This still tags faces that aren't correct at edges because we don't + // have a surface normal + if( dist > decalinfo->m_Size ) + { + R_DecalNode( model, node->children[0], decalinfo ); + } + else if( dist < -decalinfo->m_Size ) + { + R_DecalNode( model, node->children[1], decalinfo ); + } + else + { + if( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE ) + R_DecalNodeSurfaces( model, node, decalinfo ); + + R_DecalNode( model, node->children[0], decalinfo ); + R_DecalNode( model, node->children[1], decalinfo ); + } +} + +// Shoots a decal onto the surface of the BSP. position is the center of the decal in world coords +void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, float scale ) +{ + decalinfo_t decalInfo; + cl_entity_t *ent = NULL; + model_t *model = NULL; + int width, height; + hull_t *hull; + + if( textureIndex <= 0 || textureIndex >= MAX_TEXTURES ) + { + gEngfuncs.Con_Printf( S_ERROR "Decal has invalid texture!\n" ); + return; + } + + if( entityIndex > 0 ) + { + ent = gEngfuncs.GetEntityByIndex( entityIndex ); + + if( modelIndex > 0 ) model = gEngfuncs.pfnGetModelByIndex( modelIndex ); + else if( ent != NULL ) model = gEngfuncs.pfnGetModelByIndex( ent->curstate.modelindex ); + else return; + } + else if( modelIndex > 0 ) + model = gEngfuncs.pfnGetModelByIndex( modelIndex ); + else model = WORLDMODEL; + + if( !model ) return; + + if( model->type != mod_brush ) + { + gEngfuncs.Con_Printf( S_ERROR "Decals must hit mod_brush!\n" ); + return; + } + + decalInfo.m_pModel = model; + hull = &model->hulls[0]; // always use #0 hull + + // NOTE: all the decals at 'first shoot' placed into local space of parent entity + // and won't transform again on a next restore, levelchange etc + if( ent && !FBitSet( flags, FDECAL_LOCAL_SPACE )) + { + vec3_t pos_l; + + // transform decal position in local bmodel space + if( !VectorIsNull( ent->angles )) + { + matrix4x4 matrix; + + Matrix4x4_CreateFromEntity( matrix, ent->angles, ent->origin, 1.0f ); + Matrix4x4_VectorITransform( matrix, pos, pos_l ); + } + else + { + VectorSubtract( pos, ent->origin, pos_l ); + } + + VectorCopy( pos_l, decalInfo.m_Position ); + // decal position moved into local space + SetBits( flags, FDECAL_LOCAL_SPACE ); + } + else + { + // already in local space + VectorCopy( pos, decalInfo.m_Position ); + } + + // this decal must use landmark for correct transition + // because their model exist only in world-space + if( !FBitSet( model->flags, MODEL_HAS_ORIGIN )) + SetBits( flags, FDECAL_USE_LANDMARK ); + + // more state used by R_DecalNode() + decalInfo.m_iTexture = textureIndex; + decalInfo.m_Entity = entityIndex; + decalInfo.m_Flags = flags; + + R_GetDecalDimensions( textureIndex, &width, &height ); + decalInfo.m_Size = width >> 1; + if(( height >> 1 ) > decalInfo.m_Size ) + decalInfo.m_Size = height >> 1; + + decalInfo.m_scale = bound( MIN_DECAL_SCALE, scale, MAX_DECAL_SCALE ); + + // compute the decal dimensions in world space + decalInfo.m_decalWidth = width / decalInfo.m_scale; + decalInfo.m_decalHeight = height / decalInfo.m_scale; + + R_DecalNode( model, &model->nodes[hull->firstclipnode], &decalInfo ); +} + +// Build the vertex list for a decal on a surface and clip it to the surface. +// This is a template so it can work on world surfaces and dynamic displacement +// triangles the same way. +float *R_DecalSetupVerts( decal_t *pDecal, msurface_t *surf, int texture, int *outCount ) +{ + glpoly_t *p = pDecal->polys; + int i, count; + float *v; + + if( p ) + { + v = g_DecalClipVerts[0]; + count = p->numverts; + + // if we have mesh so skip clipping and just copy vertexes out (perf) + for( i = 0; i < count; i++, v += VERTEXSIZE ) + { + VectorCopy( p->verts[i].xyz, v ); + v[3] = p->verts[i].uv[0]; + v[4] = p->verts[i].uv[1]; + v[5] = p->verts[i + count].uv[0]; + v[6] = p->verts[i + count].uv[1]; + } + + // restore pointer + v = g_DecalClipVerts[0]; + } + else + { + v = R_DecalVertsClip( pDecal, surf, texture, &count ); + R_DecalVertsLight( v, surf, count ); + } + + if( outCount ) + *outCount = count; + + return v; +} + +void DrawSingleDecal( decal_t *pDecal, msurface_t *fa ) +{ + float *v; + int i, numVerts; + + v = R_DecalSetupVerts( pDecal, fa, pDecal->texture, &numVerts ); + if( !numVerts ) return; + + GL_Bind( XASH_TEXTURE0, pDecal->texture ); + + gu_vert_t* const out = ( gu_vert_t* )sceGuGetMemory( sizeof( gu_vert_t ) * numVerts ); + for( i = 0; i < numVerts; i++, v += VERTEXSIZE ) + { + out[i].uv[0] = v[3]; + out[i].uv[1] = v[4]; + VectorCopy( v, out[i].xyz ); + } + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, numVerts, 0, out ); +} + +void DrawSurfaceDecals( msurface_t *fa, qboolean single, qboolean reverse ) +{ + decal_t *p; + cl_entity_t *e; + + if( !fa->pdecals ) return; + + e = RI.currententity; + Assert( e != NULL ); + + if( single ) + { + if( e->curstate.rendermode == kRenderNormal || e->curstate.rendermode == kRenderTransAlpha ) + { + sceGuDepthMask( GU_TRUE ); + sceGuEnable( GU_BLEND ); + + if( e->curstate.rendermode == kRenderTransAlpha ) + sceGuDisable( GU_ALPHA_TEST ); + } + + if( e->curstate.rendermode == kRenderTransColor ) + sceGuEnable( GU_TEXTURE_2D ); + + if( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd ) + GL_Cull( GL_NONE ); + + if( gl_depthoffset->value ) + sceGuDepthOffset( ( int )-gl_depthoffset->value ); + } + + if( FBitSet( fa->flags, SURF_TRANSPARENT ) && glState.stencilEnabled ) + { + mtexinfo_t *tex = fa->texinfo; + + for( p = fa->pdecals; p; p = p->pnext ) + { + if( p->texture ) + { + float *o, *v; + int i, numVerts; + o = R_DecalSetupVerts( p, fa, p->texture, &numVerts ); + + sceGuEnable( GU_STENCIL_TEST ); + sceGuStencilFunc( GU_ALWAYS, 1, 0xffffffff ); + sceGuPixelMask( 0xffffffff ); + + sceGuStencilOp( GU_KEEP, GU_KEEP, GU_REPLACE ); + gu_vert_t* const out = ( gu_vert_t* )sceGuGetMemory( sizeof( gu_vert_t ) * numVerts ); + for( i = 0, v = o; i < numVerts; i++, v += VERTEXSIZE ) + { + out[i].uv[0] = ( DotProduct( v, tex->vecs[0] ) + tex->vecs[0][3] ) / tex->texture->width; + out[i].uv[1] = ( DotProduct( v, tex->vecs[1] ) + tex->vecs[1][3] ) / tex->texture->height; + VectorCopy( v, out[i].xyz ); + } + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, numVerts, 0, out ); +#if 0 /* two layer ??? */ + sceGuStencilOp( GU_KEEP, GU_KEEP, GU_DECR ); + sceGuEnable( GU_ALPHA_TEST ); + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, numVerts, 0, out ); + sceGuDisable( GU_ALPHA_TEST ); +#endif + sceGuPixelMask( 0x00000000 ); + sceGuStencilFunc( GU_EQUAL, 0, 0xffffffff ); + sceGuStencilOp( GU_KEEP, GU_KEEP, GU_KEEP ); + } + } + } + + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + + if( reverse && e->curstate.rendermode == kRenderTransTexture ) + { + decal_t *list[1024]; + int i, count; + + for( p = fa->pdecals, count = 0; p && count < 1024; p = p->pnext ) + if( p->texture ) list[count++] = p; + + for( i = count - 1; i >= 0; i-- ) + DrawSingleDecal( list[i], fa ); + } + else + { + for( p = fa->pdecals; p; p = p->pnext ) + { + if( !p->texture ) continue; + DrawSingleDecal( p, fa ); + } + } + + if( FBitSet( fa->flags, SURF_TRANSPARENT ) && glState.stencilEnabled ) + sceGuDisable( GU_STENCIL_TEST ); + + if( single ) + { + if( e->curstate.rendermode == kRenderNormal || e->curstate.rendermode == kRenderTransAlpha ) + { + sceGuDepthMask( GU_FALSE ); + sceGuDisable( GU_BLEND ); + + if( e->curstate.rendermode == kRenderTransAlpha ) + sceGuEnable( GU_ALPHA_TEST ); + } + + if( gl_depthoffset->value ) + sceGuDepthOffset( 0 ); + + if( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd ) + GL_Cull( GL_FRONT ); + + if( e->curstate.rendermode == kRenderTransColor ) + sceGuDisable( GU_TEXTURE_2D ); + + // restore blendfunc here + if( e->curstate.rendermode == kRenderTransAdd || e->curstate.rendermode == kRenderGlow ) + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, GUBLEND1 ); + + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + } +} + +void DrawDecalsBatch( void ) +{ + cl_entity_t *e; + int i; + + if( !tr.num_draw_decals ) + return; + + e = RI.currententity; + Assert( e != NULL ); + + if( e->curstate.rendermode != kRenderTransTexture ) + { + sceGuEnable( GU_BLEND ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuDepthMask( GU_TRUE ); + } + + if( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd ) + GL_Cull( GL_NONE ); + + if( gl_depthoffset->value ) + sceGuDepthOffset( ( int )-gl_depthoffset->value ); + + for( i = 0; i < tr.num_draw_decals; i++ ) + { + DrawSurfaceDecals( tr.draw_decals[i], false, false ); + } + + if( e->curstate.rendermode != kRenderTransTexture ) + { + sceGuDepthMask( GU_FALSE ); + sceGuDisable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + } + + if( gl_depthoffset->value ) + sceGuDepthOffset( 0 ); + + if( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd ) + GL_Cull( GL_FRONT ); + + tr.num_draw_decals = 0; +} + +/* +============================================================= + + DECALS SERIALIZATION + +============================================================= +*/ +static qboolean R_DecalUnProject( decal_t *pdecal, decallist_t *entry ) +{ + if( !pdecal || !( pdecal->psurface )) + return false; + + VectorCopy( pdecal->position, entry->position ); + entry->entityIndex = pdecal->entityIndex; + + // Grab surface plane equation + if( pdecal->psurface->flags & SURF_PLANEBACK ) + VectorNegate( pdecal->psurface->plane->normal, entry->impactPlaneNormal ); + else VectorCopy( pdecal->psurface->plane->normal, entry->impactPlaneNormal ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pList - +// count - +// Output : static int +//----------------------------------------------------------------------------- +static int DecalListAdd( decallist_t *pList, int count ) +{ + vec3_t tmp; + decallist_t *pdecal; + int i; + + pdecal = pList + count; + + for( i = 0; i < count; i++ ) + { + if( !Q_strcmp( pdecal->name, pList[i].name ) && pdecal->entityIndex == pList[i].entityIndex ) + { + VectorSubtract( pdecal->position, pList[i].position, tmp ); // Merge + + if( VectorLength( tmp ) < DECAL_OVERLAP_DISTANCE ) + return count; + } + } + + // this is a new decal + return count + 1; +} + +static int DecalDepthCompare( const void *a, const void *b ) +{ + const decallist_t *elem1, *elem2; + + elem1 = (const decallist_t *)a; + elem2 = (const decallist_t *)b; + + if( elem1->depth > elem2->depth ) + return 1; + if( elem1->depth < elem2->depth ) + return -1; + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Called by CSaveRestore::SaveClientState +// Input : *pList - +// Output : int +//----------------------------------------------------------------------------- +int R_CreateDecalList( decallist_t *pList ) +{ + int total = 0; + int i, depth; + + if( WORLDMODEL ) + { + for( i = 0; i < MAX_RENDER_DECALS; i++ ) + { + decal_t *decal = &gDecalPool[i]; + decal_t *pdecals; + + // decal is in use and is not a custom decal + if( decal->psurface == NULL || FBitSet( decal->flags, FDECAL_DONTSAVE )) + continue; + + // compute depth + depth = 0; + pdecals = decal->psurface->pdecals; + + while( pdecals && pdecals != decal ) + { + depth++; + pdecals = pdecals->pnext; + } + + pList[total].depth = depth; + pList[total].flags = decal->flags; + pList[total].scale = decal->scale; + + R_DecalUnProject( decal, &pList[total] ); + COM_FileBase( R_GetTexture( decal->texture )->name, pList[total].name ); + + // check to see if the decal should be added + total = DecalListAdd( pList, total ); + } + + if( gEngfuncs.drawFuncs->R_CreateStudioDecalList ) + { + total += gEngfuncs.drawFuncs->R_CreateStudioDecalList( pList, total ); + } + } + + // sort the decals lowest depth first, so they can be re-applied in order + qsort( pList, total, sizeof( decallist_t ), DecalDepthCompare ); + + return total; +} + +/* +=============== +R_DecalRemoveAll + +remove all decals with specified texture +=============== +*/ +void R_DecalRemoveAll( int textureIndex ) +{ + decal_t *pdecal; + int i; + + if( textureIndex < 0 || textureIndex >= MAX_TEXTURES ) + return; // out of bounds + + for( i = 0; i < gDecalCount; i++ ) + { + pdecal = &gDecalPool[i]; + + // don't remove permanent decals + if( !textureIndex && FBitSet( pdecal->flags, FDECAL_PERMANENT )) + continue; + + if( !textureIndex || ( pdecal->texture == textureIndex )) + R_DecalUnlink( pdecal ); + } +} + +/* +=============== +R_EntityRemoveDecals + +remove all decals from specified entity +=============== +*/ +void R_EntityRemoveDecals( model_t *mod ) +{ + msurface_t *psurf; + decal_t *p; + int i; + + if( !mod || mod->type != mod_brush ) + return; + + psurf = &mod->surfaces[mod->firstmodelsurface]; + for( i = 0; i < mod->nummodelsurfaces; i++, psurf++ ) + { + for( p = psurf->pdecals; p; p = p->pnext ) + R_DecalUnlink( p ); + } +} + +/* +=============== +R_ClearAllDecals + +remove all decals from anything +used for full decals restart +=============== +*/ +void R_ClearAllDecals( void ) +{ + decal_t *pdecal; + int i; + + // because gDecalCount may be zeroed after recach the decal limit + for( i = 0; i < MAX_RENDER_DECALS; i++ ) + { + pdecal = &gDecalPool[i]; + R_DecalUnlink( pdecal ); + } + + if( gEngfuncs.drawFuncs->R_ClearStudioDecals ) + { + gEngfuncs.drawFuncs->R_ClearStudioDecals(); + } +} diff --git a/ref_gu/gu_draw.c b/ref_gu/gu_draw.c new file mode 100644 index 00000000..1646f37c --- /dev/null +++ b/ref_gu/gu_draw.c @@ -0,0 +1,308 @@ +/* +gu_draw.c - orthogonal drawing stuff +Copyright (C) 2010 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" + +/* +============= +R_GetImageParms +============= +*/ +void R_GetTextureParms( int *w, int *h, int texnum ) +{ + gl_texture_t *glt; + + glt = R_GetTexture( texnum ); + if( w ) *w = glt->srcWidth; + if( h ) *h = glt->srcHeight; +} + +/* +============= +R_GetSpriteParms + +same as GetImageParms but used +for sprite models +============= +*/ +void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite ) +{ + mspriteframe_t *pFrame; + + if( !pSprite || pSprite->type != mod_sprite ) return; // bad model ? + pFrame = R_GetSpriteFrame( pSprite, currentFrame, 0.0f ); + + if( frameWidth ) *frameWidth = pFrame->width; + if( frameHeight ) *frameHeight = pFrame->height; + if( numFrames ) *numFrames = pSprite->numframes; +} + +int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame ) +{ + if( !m_pSpriteModel || m_pSpriteModel->type != mod_sprite || !m_pSpriteModel->cache.data ) + return 0; + + return R_GetSpriteFrame( m_pSpriteModel, frame, 0.0f )->gl_texturenum; +} + +/* +============= +R_DrawStretchPic +============= +*/ +void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, int texnum ) +{ + float uw, uh; + gl_texture_t *glt; + + GL_Bind( XASH_TEXTURE0, texnum ); + + glt = R_GetTexture( texnum ); + + gu_vert_htv_t* const out = ( gu_vert_htv_t* )sceGuGetMemory( sizeof( gu_vert_htv_t ) * 2 ); + out[0].u = s1 * glt->width; + out[0].v = t1 * glt->height; + out[0].x = x; + out[0].y = y; + out[0].z = 0; + out[1].u = s2 * glt->width; + out[1].v = t2 * glt->height; + out[1].x = x + w; + out[1].y = y + h; + out[1].z = 0; + sceGuDrawArray( GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, out ); +} + +/* +============= +Draw_TileClear + +This repeats a 64*64 tile graphic to fill the screen around a sized down +refresh window. +============= +*/ +void R_DrawTileClear( int texnum, int x, int y, int w, int h ) +{ + float tw, th; + gl_texture_t *glt; + + GL_SetRenderMode( kRenderNormal ); + + sceGuColor( 0xffffffff ); + + GL_Bind( XASH_TEXTURE0, texnum ); + + glt = R_GetTexture( texnum ); + tw = glt->srcWidth; + th = glt->srcHeight; + + gu_vert_htv_t* const out = ( gu_vert_htv_t* )sceGuGetMemory( sizeof( gu_vert_ftv_t ) * 2 ); + out[0].u = x / tw * glt->width; + out[0].v = y / th * glt->height; + out[0].x = x; + out[0].y = y; + out[0].z = 0; + out[1].u = ( x + w ) / tw * glt->width; + out[1].v = ( y + h ) / th * glt->height; + out[1].x = x + w; + out[1].y = y + h; + out[1].z = 0; + sceGuDrawArray( GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, out ); +} + +/* +============= +R_DrawStretchRaw +============= +*/ +void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, const byte *data, qboolean dirty ) +{ +#if 0 + byte *raw = NULL; + gl_texture_t *tex; + + if( !GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) + { + int width = 1, height = 1; + + // check the dimensions + width = NearestPOW( cols, true ); + height = NearestPOW( rows, false ); + + if( cols != width || rows != height ) + { + raw = GL_ResampleTexture( data, cols, rows, width, height, false ); + cols = width; + rows = height; + } + } + else + { + raw = (byte *)data; + } + + if( cols > glConfig.max_2d_texture_size ) + gEngfuncs.Host_Error( "R_DrawStretchRaw: size %i exceeds hardware limits\n", cols ); + if( rows > glConfig.max_2d_texture_size ) + gEngfuncs.Host_Error( "R_DrawStretchRaw: size %i exceeds hardware limits\n", rows ); + + pglDisable( GL_BLEND ); + pglDisable( GL_ALPHA_TEST ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + + tex = R_GetTexture( tr.cinTexture ); + GL_Bind( XASH_TEXTURE0, tr.cinTexture ); + + if( cols == tex->width && rows == tex->height ) + { + if( dirty ) + { + pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_BGRA, GL_UNSIGNED_BYTE, raw ); + } + } + else + { + tex->size = cols * rows * 4; + tex->width = cols; + tex->height = rows; + if( dirty ) + { + pglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, cols, rows, 0, GL_BGRA, GL_UNSIGNED_BYTE, raw ); + } + } + + pglBegin( GL_QUADS ); + pglTexCoord2f( 0, 0 ); + pglVertex2f( x, y ); + pglTexCoord2f( 1, 0 ); + pglVertex2f( x + w, y ); + pglTexCoord2f( 1, 1 ); + pglVertex2f( x + w, y + h ); + pglTexCoord2f( 0, 1 ); + pglVertex2f( x, y + h ); + pglEnd(); +#endif +} + +/* +============= +R_UploadStretchRaw +============= +*/ +void R_UploadStretchRaw( int texture, int cols, int rows, int width, int height, const byte *data ) +{ +#if 0 + byte *raw = NULL; + gl_texture_t *tex; + + if( !GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) + { + // check the dimensions + width = NearestPOW( width, true ); + height = NearestPOW( height, false ); + } + else + { + width = bound( 128, width, glConfig.max_2d_texture_size ); + height = bound( 128, height, glConfig.max_2d_texture_size ); + } + + if( cols != width || rows != height ) + { + raw = GL_ResampleTexture( data, cols, rows, width, height, false ); + cols = width; + rows = height; + } + else + { + raw = (byte *)data; + } + + if( cols > glConfig.max_2d_texture_size ) + gEngfuncs.Host_Error( "R_UploadStretchRaw: size %i exceeds hardware limits\n", cols ); + if( rows > glConfig.max_2d_texture_size ) + gEngfuncs.Host_Error( "R_UploadStretchRaw: size %i exceeds hardware limits\n", rows ); + + tex = R_GetTexture( texture ); + GL_Bind( GL_KEEP_UNIT, texture ); + tex->width = cols; + tex->height = rows; + + pglTexImage2D( GL_TEXTURE_2D, 0, tex->format, cols, rows, 0, GL_BGRA, GL_UNSIGNED_BYTE, raw ); + GL_ApplyTextureParams( tex ); +#endif +} + +/* +=============== +R_Set2DMode +=============== +*/ +void R_Set2DMode( qboolean enable ) +{ + if( enable ) + { + if( glState.in2DMode ) + return; + + // set 2D virtual screen size +#if 1 // through mode + sceGuViewport( 2048, 2048, gpGlobals->width, gpGlobals->width ); + sceGuScissor(0, 0, gpGlobals->width, gpGlobals->height); +#else + pglViewport( 0, 0, gpGlobals->width, gpGlobals->height ); + pglMatrixMode( GL_PROJECTION ); + pglLoadIdentity(); + pglOrtho( 0, gpGlobals->width, gpGlobals->height, 0, -99999, 99999 ); + pglMatrixMode( GL_MODELVIEW ); + pglLoadIdentity(); +#endif + GL_Cull( GL_NONE ); +#if 1 + sceGuDepthMask( GU_TRUE ); + sceGuDisable( GU_DEPTH_TEST ); + sceGuEnable( GU_ALPHA_TEST ); + sceGuColor( 0xffffffff ); +#else + pglDepthMask( GL_FALSE ); + pglDisable( GL_DEPTH_TEST ); + pglEnable( GL_ALPHA_TEST ); + pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); +#endif + glState.in2DMode = true; + RI.currententity = NULL; + RI.currentmodel = NULL; + } + else + { +#if 1 + sceGuDepthMask( GU_FALSE ); + sceGuEnable( GU_DEPTH_TEST ); + glState.in2DMode = false; +#else + pglDepthMask( GL_TRUE ); + pglEnable( GL_DEPTH_TEST ); + glState.in2DMode = false; + + pglMatrixMode( GL_PROJECTION ); + GL_LoadMatrix( RI.projectionMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.worldviewMatrix ); +#endif + GL_Cull( GL_FRONT ); + } +} diff --git a/ref_gu/gu_extension.c b/ref_gu/gu_extension.c new file mode 100644 index 00000000..260783dc --- /dev/null +++ b/ref_gu/gu_extension.c @@ -0,0 +1,79 @@ +/* +gu_extension.c - PSP Graphic Unit extensions +Copyright (C) 2020 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include "gu_extension.h" + +/* from guInternal.h */ +typedef struct +{ + unsigned int* start; + unsigned int* current; + int parent_context; +} GuDisplayList; +extern GuDisplayList* gu_list; +extern int gu_curr_context; +extern int ge_list_executed[]; + +static unsigned int gu_list_size; + + +void extGuStart(int cid, void* list, int size) +{ + gu_list_size = size; + sceGuStart( cid, list ); +} + +/* Begin user packet */ +void *extGuBeginPacket( unsigned int *maxsize ) +{ + unsigned int* current_ptr = gu_list->current; + if( maxsize != NULL ) + { + unsigned int size = ( ( unsigned int )gu_list->current ) - ( ( unsigned int )gu_list->start ); + *maxsize = ( ( unsigned int )gu_list_size ) - ( ( unsigned int )size ); + } + return current_ptr + 2; +} + +/* End user packet */ +void extGuEndPacket( void *eaddr ) +{ + unsigned int* current_ptr = gu_list->current; + unsigned int size = ( ( unsigned int ) eaddr ) - ( ( unsigned int )current_ptr ); + + if( size > 0 ) + { + size += 3; + size += ( ( unsigned int )( size >> 31 ) ) >> 30; + size = ( size >> 2 ) << 2; + + unsigned int* new_ptr = ( unsigned int* )( ( ( unsigned int ) current_ptr ) + size + 8 ); + + int lo = ( 8 << 24 ) | ( ( ( unsigned int )new_ptr ) & 0xffffff ); + int hi = ( 16 << 24 ) | ( ( ( ( unsigned int )new_ptr ) >> 8 ) & 0xf0000 ); + + current_ptr[0] = hi; + current_ptr[1] = lo; + + gu_list->current = new_ptr; + + if ( !gu_curr_context ) + sceGeListUpdateStallAddr( ge_list_executed[0], new_ptr ); + } +} \ No newline at end of file diff --git a/ref_gu/gu_extension.h b/ref_gu/gu_extension.h new file mode 100644 index 00000000..7273870e --- /dev/null +++ b/ref_gu/gu_extension.h @@ -0,0 +1,23 @@ +/* +gu_extension.h - PSP Graphic Unit extensions header +Copyright (C) 2020 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef GU_EXTENSION_H +#define GU_EXTENSION_H + +void extGuStart(int cid, void* list, int size); +void *extGuBeginPacket( unsigned int *maxsize ); +void extGuEndPacket( void *eaddr ); + +#endif//GU_EXTENSION_H diff --git a/ref_gu/gu_frustum.c b/ref_gu/gu_frustum.c new file mode 100644 index 00000000..a52a8fb1 --- /dev/null +++ b/ref_gu/gu_frustum.c @@ -0,0 +1,355 @@ +/* +gu_frustum.cpp - frustum test implementation +Copyright (C) 2016 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "xash3d_mathlib.h" + +void GL_FrustumEnablePlane( gl_frustum_t *out, int side ) +{ + Assert( side >= 0 && side < FRUSTUM_PLANES ); + + // make sure what plane is ready + if( !VectorIsNull( out->planes[side].normal )) + SetBits( out->clipFlags, BIT( side )); +} + +void GL_FrustumDisablePlane( gl_frustum_t *out, int side ) +{ + Assert( side >= 0 && side < FRUSTUM_PLANES ); + ClearBits( out->clipFlags, BIT( side )); +} + +void GL_FrustumSetPlane( gl_frustum_t *out, int side, const vec3_t vecNormal, float flDist ) +{ + Assert( side >= 0 && side < FRUSTUM_PLANES ); + + out->planes[side].type = PlaneTypeForNormal( vecNormal ); + out->planes[side].signbits = SignbitsForPlane( vecNormal ); + VectorCopy( vecNormal, out->planes[side].normal ); + out->planes[side].dist = flDist; + + SetBits( out->clipFlags, BIT( side )); +} + +void GL_FrustumNormalizePlane( gl_frustum_t *out, int side ) +{ + float length; + + Assert( side >= 0 && side < FRUSTUM_PLANES ); + + // normalize + length = VectorLength( out->planes[side].normal ); + + if( length ) + { + float ilength = (1.0f / length); + out->planes[side].normal[0] *= ilength; + out->planes[side].normal[1] *= ilength; + out->planes[side].normal[2] *= ilength; + out->planes[side].dist *= ilength; + } + + out->planes[side].type = PlaneTypeForNormal( out->planes[side].normal ); + out->planes[side].signbits = SignbitsForPlane( out->planes[side].normal ); + + SetBits( out->clipFlags, BIT( side )); +} + +void GL_FrustumInitProj( gl_frustum_t *out, float flZNear, float flZFar, float flFovX, float flFovY ) +{ + float xs, xc; + vec3_t farpoint, nearpoint; + vec3_t normal, iforward; + + // horizontal fov used for left and right planes + SinCos( DEG2RAD( flFovX ) * 0.5f, &xs, &xc ); + + // setup left plane + VectorMAM( xs, RI.cull_vforward, -xc, RI.cull_vright, normal ); + GL_FrustumSetPlane( out, FRUSTUM_LEFT, normal, DotProduct( RI.cullorigin, normal )); + + // setup right plane + VectorMAM( xs, RI.cull_vforward, xc, RI.cull_vright, normal ); + GL_FrustumSetPlane( out, FRUSTUM_RIGHT, normal, DotProduct( RI.cullorigin, normal )); + + // vertical fov used for top and bottom planes + SinCos( DEG2RAD( flFovY ) * 0.5f, &xs, &xc ); + VectorNegate( RI.cull_vforward, iforward ); + + // setup bottom plane + VectorMAM( xs, RI.cull_vforward, -xc, RI.cull_vup, normal ); + GL_FrustumSetPlane( out, FRUSTUM_BOTTOM, normal, DotProduct( RI.cullorigin, normal )); + + // setup top plane + VectorMAM( xs, RI.cull_vforward, xc, RI.cull_vup, normal ); + GL_FrustumSetPlane( out, FRUSTUM_TOP, normal, DotProduct( RI.cullorigin, normal )); + + // setup far plane + VectorMA( RI.cullorigin, flZFar, RI.cull_vforward, farpoint ); + GL_FrustumSetPlane( out, FRUSTUM_FAR, iforward, DotProduct( iforward, farpoint )); + + // no need to setup backplane for general view. + if( flZNear == 0.0f ) return; + + // setup near plane + VectorMA( RI.cullorigin, flZNear, RI.cull_vforward, nearpoint ); + GL_FrustumSetPlane( out, FRUSTUM_NEAR, RI.cull_vforward, DotProduct( RI.cull_vforward, nearpoint )); +} + +void GL_FrustumInitOrtho( gl_frustum_t *out, float xLeft, float xRight, float yTop, float yBottom, float flZNear, float flZFar ) +{ + vec3_t iforward, iright, iup; + + // setup the near and far planes + float orgOffset = DotProduct( RI.cullorigin, RI.cull_vforward ); + VectorNegate( RI.cull_vforward, iforward ); + + // because quake ortho is inverted and far and near should be swaped + GL_FrustumSetPlane( out, FRUSTUM_FAR, iforward, -flZNear - orgOffset ); + GL_FrustumSetPlane( out, FRUSTUM_NEAR, RI.cull_vforward, flZFar + orgOffset ); + + // setup left and right planes + orgOffset = DotProduct( RI.cullorigin, RI.cull_vright ); + VectorNegate( RI.cull_vright, iright ); + + GL_FrustumSetPlane( out, FRUSTUM_LEFT, RI.cull_vright, xLeft + orgOffset ); + GL_FrustumSetPlane( out, FRUSTUM_RIGHT, iright, -xRight - orgOffset ); + + // setup top and buttom planes + orgOffset = DotProduct( RI.cullorigin, RI.cull_vup ); + VectorNegate( RI.cull_vup, iup ); + + GL_FrustumSetPlane( out, FRUSTUM_TOP, RI.cull_vup, yTop + orgOffset ); + GL_FrustumSetPlane( out, FRUSTUM_BOTTOM, iup, -yBottom - orgOffset ); +} + +void GL_FrustumInitBox( gl_frustum_t *out, const vec3_t org, float radius ) +{ + vec3_t normal; + int i; + + for( i = 0; i < FRUSTUM_PLANES; i++ ) + { + // setup normal for each direction + VectorClear( normal ); + normal[((i >> 1) + 1) % 3] = (i & 1) ? 1.0f : -1.0f; + GL_FrustumSetPlane( out, i, normal, DotProduct( org, normal ) - radius ); + } +} + +void GL_FrustumInitProjFromMatrix( gl_frustum_t *out, const matrix4x4 projection ) +{ + int i; + + // left + out->planes[FRUSTUM_LEFT].normal[0] = projection[0][3] + projection[0][0]; + out->planes[FRUSTUM_LEFT].normal[1] = projection[1][3] + projection[1][0]; + out->planes[FRUSTUM_LEFT].normal[2] = projection[2][3] + projection[2][0]; + out->planes[FRUSTUM_LEFT].dist = -(projection[3][3] + projection[3][0]); + + // right + out->planes[FRUSTUM_RIGHT].normal[0] = projection[0][3] - projection[0][0]; + out->planes[FRUSTUM_RIGHT].normal[1] = projection[1][3] - projection[1][0]; + out->planes[FRUSTUM_RIGHT].normal[2] = projection[2][3] - projection[2][0]; + out->planes[FRUSTUM_RIGHT].dist = -(projection[3][3] - projection[3][0]); + + // bottom + out->planes[FRUSTUM_BOTTOM].normal[0] = projection[0][3] + projection[0][1]; + out->planes[FRUSTUM_BOTTOM].normal[1] = projection[1][3] + projection[1][1]; + out->planes[FRUSTUM_BOTTOM].normal[2] = projection[2][3] + projection[2][1]; + out->planes[FRUSTUM_BOTTOM].dist = -(projection[3][3] + projection[3][1]); + + // top + out->planes[FRUSTUM_TOP].normal[0] = projection[0][3] - projection[0][1]; + out->planes[FRUSTUM_TOP].normal[1] = projection[1][3] - projection[1][1]; + out->planes[FRUSTUM_TOP].normal[2] = projection[2][3] - projection[2][1]; + out->planes[FRUSTUM_TOP].dist = -(projection[3][3] - projection[3][1]); + + // near + out->planes[FRUSTUM_NEAR].normal[0] = projection[0][3] + projection[0][2]; + out->planes[FRUSTUM_NEAR].normal[1] = projection[1][3] + projection[1][2]; + out->planes[FRUSTUM_NEAR].normal[2] = projection[2][3] + projection[2][2]; + out->planes[FRUSTUM_NEAR].dist = -(projection[3][3] + projection[3][2]); + + // far + out->planes[FRUSTUM_FAR].normal[0] = projection[0][3] - projection[0][2]; + out->planes[FRUSTUM_FAR].normal[1] = projection[1][3] - projection[1][2]; + out->planes[FRUSTUM_FAR].normal[2] = projection[2][3] - projection[2][2]; + out->planes[FRUSTUM_FAR].dist = -(projection[3][3] - projection[3][2]); + + for( i = 0; i < FRUSTUM_PLANES; i++ ) + { + GL_FrustumNormalizePlane( out, i ); + } +} + +void GL_FrustumComputeCorners( gl_frustum_t *out, vec3_t corners[8] ) +{ + memset( corners, 0, sizeof( vec3_t ) * 8 ); + + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_LEFT], &out->planes[FRUSTUM_TOP], &out->planes[FRUSTUM_FAR], corners[0] ); + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_RIGHT], &out->planes[FRUSTUM_TOP], &out->planes[FRUSTUM_FAR], corners[1] ); + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_LEFT], &out->planes[FRUSTUM_BOTTOM], &out->planes[FRUSTUM_FAR], corners[2] ); + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_RIGHT], &out->planes[FRUSTUM_BOTTOM], &out->planes[FRUSTUM_FAR], corners[3] ); + + if( FBitSet( out->clipFlags, BIT( FRUSTUM_NEAR ))) + { + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_LEFT], &out->planes[FRUSTUM_TOP], &out->planes[FRUSTUM_NEAR], corners[4] ); + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_RIGHT], &out->planes[FRUSTUM_TOP], &out->planes[FRUSTUM_NEAR], corners[5] ); + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_LEFT], &out->planes[FRUSTUM_BOTTOM], &out->planes[FRUSTUM_NEAR], corners[6] ); + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_RIGHT], &out->planes[FRUSTUM_BOTTOM], &out->planes[FRUSTUM_NEAR], corners[7] ); + } + else + { + PlanesGetIntersectionPoint( &out->planes[FRUSTUM_LEFT], &out->planes[FRUSTUM_RIGHT], &out->planes[FRUSTUM_TOP], corners[4] ); + VectorCopy( corners[4], corners[5] ); + VectorCopy( corners[4], corners[6] ); + VectorCopy( corners[4], corners[7] ); + } +} + +void GL_FrustumComputeBounds( gl_frustum_t *out, vec3_t mins, vec3_t maxs ) +{ + vec3_t corners[8]; + int i; + + GL_FrustumComputeCorners( out, corners ); + + ClearBounds( mins, maxs ); + + for( i = 0; i < 8; i++ ) + AddPointToBounds( corners[i], mins, maxs ); +} + +void GL_FrustumDrawDebug( gl_frustum_t *out ) +{ + vec3_t bbox[8]; + int i; + + GL_FrustumComputeCorners( out, bbox ); +/* + // g-cont. frustum must be yellow :-) + pglColor4f( 1.0f, 1.0f, 0.0f, 1.0f ); + pglDisable( GL_TEXTURE_2D ); + pglBegin( GL_LINES ); + + for( i = 0; i < 2; i += 1 ) + { + pglVertex3fv( bbox[i+0] ); + pglVertex3fv( bbox[i+2] ); + pglVertex3fv( bbox[i+4] ); + pglVertex3fv( bbox[i+6] ); + pglVertex3fv( bbox[i+0] ); + pglVertex3fv( bbox[i+4] ); + pglVertex3fv( bbox[i+2] ); + pglVertex3fv( bbox[i+6] ); + pglVertex3fv( bbox[i*2+0] ); + pglVertex3fv( bbox[i*2+1] ); + pglVertex3fv( bbox[i*2+4] ); + pglVertex3fv( bbox[i*2+5] ); + } + + pglEnd(); + pglEnable( GL_TEXTURE_2D ); +*/ +} + +// cull methods +qboolean GL_FrustumCullBox( gl_frustum_t *out, const vec3_t mins, const vec3_t maxs, int userClipFlags ) +{ + int iClipFlags; + int i, bit; + + if( r_nocull->value ) + return false; + + if( userClipFlags != 0 ) + iClipFlags = userClipFlags; + else iClipFlags = out->clipFlags; + + for( i = FRUSTUM_PLANES, bit = 1; i > 0; i--, bit <<= 1 ) + { + const mplane_t *p = &out->planes[FRUSTUM_PLANES - i]; + + if( !FBitSet( iClipFlags, bit )) + continue; + + switch( p->signbits ) + { + case 0: + if( p->normal[0] * maxs[0] + p->normal[1] * maxs[1] + p->normal[2] * maxs[2] < p->dist ) + return true; + break; + case 1: + if( p->normal[0] * mins[0] + p->normal[1] * maxs[1] + p->normal[2] * maxs[2] < p->dist ) + return true; + break; + case 2: + if( p->normal[0] * maxs[0] + p->normal[1] * mins[1] + p->normal[2] * maxs[2] < p->dist ) + return true; + break; + case 3: + if( p->normal[0] * mins[0] + p->normal[1] * mins[1] + p->normal[2] * maxs[2] < p->dist ) + return true; + break; + case 4: + if( p->normal[0] * maxs[0] + p->normal[1] * maxs[1] + p->normal[2] * mins[2] < p->dist ) + return true; + break; + case 5: + if( p->normal[0] * mins[0] + p->normal[1] * maxs[1] + p->normal[2] * mins[2] < p->dist ) + return true; + break; + case 6: + if( p->normal[0] * maxs[0] + p->normal[1] * mins[1] + p->normal[2] * mins[2] < p->dist ) + return true; + break; + case 7: + if( p->normal[0] * mins[0] + p->normal[1] * mins[1] + p->normal[2] * mins[2] < p->dist ) + return true; + break; + default: + return false; + } + } + + return false; +} + +qboolean GL_FrustumCullSphere( gl_frustum_t *out, const vec3_t center, float radius, int userClipFlags ) +{ + int iClipFlags; + int i, bit; + + if( r_nocull->value ) + return false; + + if( userClipFlags != 0 ) + iClipFlags = userClipFlags; + else iClipFlags = out->clipFlags; + + for( i = FRUSTUM_PLANES, bit = 1; i > 0; i--, bit <<= 1 ) + { + const mplane_t *p = &out->planes[FRUSTUM_PLANES - i]; + + if( !FBitSet( iClipFlags, bit )) + continue; + + if( DotProduct( center, p->normal ) - p->dist <= -radius ) + return true; + } + + return false; +} diff --git a/ref_gu/gu_frustum.h b/ref_gu/gu_frustum.h new file mode 100644 index 00000000..f48793b5 --- /dev/null +++ b/ref_gu/gu_frustum.h @@ -0,0 +1,52 @@ +/* +gl_frustum.cpp - frustum test implementation +Copyright (C) 2016 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef GL_FRUSTUM_H +#define GL_FRUSTUM_H + +// don't change this order +#define FRUSTUM_LEFT 0 +#define FRUSTUM_RIGHT 1 +#define FRUSTUM_BOTTOM 2 +#define FRUSTUM_TOP 3 +#define FRUSTUM_FAR 4 +#define FRUSTUM_NEAR 5 +#define FRUSTUM_PLANES 6 + +typedef struct gl_frustum_s +{ + mplane_t planes[FRUSTUM_PLANES]; + unsigned int clipFlags; +} gl_frustum_t; + +void GL_FrustumInitProj( gl_frustum_t *out, float flZNear, float flZFar, float flFovX, float flFovY ); +void GL_FrustumInitOrtho( gl_frustum_t *out, float xLeft, float xRight, float yTop, float yBottom, float flZNear, float flZFar ); +void GL_FrustumInitBox( gl_frustum_t *out, const vec3_t org, float radius ); // used for pointlights +void GL_FrustumInitProjFromMatrix( gl_frustum_t *out, const matrix4x4 projection ); +void GL_FrustumSetPlane( gl_frustum_t *out, int side, const vec3_t vecNormal, float flDist ); +void GL_FrustumNormalizePlane( gl_frustum_t *out, int side ); +void GL_FrustumComputeBounds( gl_frustum_t *out, vec3_t mins, vec3_t maxs ); +void GL_FrustumComputeCorners( gl_frustum_t *out, vec3_t bbox[8] ); +void GL_FrustumDrawDebug( gl_frustum_t *out ); + +// cull methods +qboolean GL_FrustumCullBox( gl_frustum_t *out, const vec3_t mins, const vec3_t maxs, int userClipFlags ); +qboolean GL_FrustumCullSphere( gl_frustum_t *out, const vec3_t centre, float radius, int userClipFlags ); + +// plane manipulating +void GL_FrustumEnablePlane( gl_frustum_t *out, int side ); +void GL_FrustumDisablePlane( gl_frustum_t *out, int side ); + +#endif//GL_FRUSTUM_H diff --git a/ref_gu/gu_helper.h b/ref_gu/gu_helper.h new file mode 100644 index 00000000..588a6693 --- /dev/null +++ b/ref_gu/gu_helper.h @@ -0,0 +1,74 @@ +/* +gl_helper.h - gu helper +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef GU_HELPER_H +#define GU_HELPER_H +#ifndef APIENTRY +#define APIENTRY +#endif + +#ifndef APIENTRY_LINKAGE +#define APIENTRY_LINKAGE extern +#endif + +// GL types wrapping +typedef uint GLenum; +typedef byte GLboolean; +typedef uint GLbitfield; +typedef void GLvoid; +typedef signed char GLbyte; +typedef short GLshort; +typedef int GLint; +typedef byte GLubyte; +typedef word GLushort; +typedef uint GLuint; +typedef int GLsizei; +typedef float GLfloat; +typedef float GLclampf; +typedef double GLdouble; +typedef double GLclampd; +typedef int GLintptrARB; +typedef int GLsizeiptrARB; +typedef char GLcharARB; +typedef uint GLhandleARB; +typedef float GLmatrix[16]; + +// GL_Cull wrapping +#define GL_NONE 0 +#define GL_FRONT GU_CW + 1 +#define GL_BACK GU_CCW + 1 + +// color functions wrapping +#define GUCOLOR4F( r, g, b, a ) GU_COLOR( ( r ), ( g ), ( b ), ( a ) ) +#define GUCOLOR4FV( v ) GU_COLOR( ( v )[0], ( v )[1], ( v )[2], ( v )[3] ) +#define GUCOLOR4UB( r, g, b, a ) GU_RGBA( ( r ), ( g ), ( b ), ( a ) ) +#define GUCOLOR4UBV( v ) GU_RGBA( ( v )[0], ( v )[1], ( v )[2], ( v )[3] ) +#define GUCOLOR3F( r, g, b ) GU_COLOR( ( r ), ( g ), ( b ), 1.0f ) +#define GUCOLOR3FV( v ) GU_COLOR( ( v )[0], ( v )[1], ( v )[2], 1.0f ) +#define GUCOLOR3UB( r, g, b ) GU_RGBA( ( r ), ( g ), ( b ), 255 ) +#define GUCOLOR3UBV( v ) GU_RGBA( ( v )[0], ( v )[1], ( v )[2], 255 ) + +// blend function wrapping +#define GUBLEND1 0xffffff +#define GUBLEND0 0x000000 + +// texture flags +#define GUFLAGTEXNONE 0x00 +#define GUFLAGTEXUSED 0x01 +#define GUFLAGTEXSWIZZLED 0x02 +#define GUFLAGTEXINVRAM 0x04 +/*#define GUFLAGMIP 0x08*/ + +#endif // GU_HELPER_H diff --git a/ref_gu/gu_image.c b/ref_gu/gu_image.c new file mode 100644 index 00000000..46296beb --- /dev/null +++ b/ref_gu/gu_image.c @@ -0,0 +1,2499 @@ +/* +gu_image.c - texture uploading and processing +Copyright (C) 2010 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include +#include +#include "vram_psp.h" +#include "gu_local.h" +#include "crclib.h" + +#define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2) + +static gl_texture_t gl_textures[MAX_TEXTURES]; +static gl_texture_t* gl_texturesHashTable[TEXTURES_HASH_SIZE]; +static uint gl_numTextures; + + +/* + num_colors = PALETTE_BLOCKS x 16 (16-bit CLUT) + num_colors = PALETTE_BLOCKS x 8 (32-bit CLUT) +*/ +#define PALETTE_FORMAT GU_PSM_4444 +#if (PALETTE_FORMAT == GU_PSM_4444) || (PALETTE_FORMAT == GU_PSM_5551) || (PALETTE_FORMAT == GU_PSM_5650) +#define PALETTE_SIZE 2 * 256 +#define PALETTE_BLOCKS 16 +#elif PALETTE_FORMAT == GU_PSM_8888 +#define PALETTE_SIZE 4 * 256 +#define PALETTE_BLOCKS 32 +#else +#error Current PALETTE_FORMAT not supported! +#endif + + + +#define IsLightMap( tex ) ( FBitSet(( tex )->flags, TF_ATLAS_PAGE )) +/* +================= +R_GetTexture + +acess to array elem +================= +*/ +gl_texture_t *R_GetTexture( GLenum texnum ) +{ + ASSERT( texnum >= 0 && texnum < MAX_TEXTURES ); + return &gl_textures[texnum]; +} + +/* +================= +GL_Bind +================= +*/ +void GL_Bind( GLint tmu, GLenum texnum ) +{ + gl_texture_t *texture; +#if 1 + int i; +#else + GLuint glTarget; +#endif + // missed or invalid texture? + if( texnum <= 0 || texnum >= MAX_TEXTURES ) + { + if( texnum != 0 ) + gEngfuncs.Con_DPrintf( S_ERROR "GL_Bind: invalid texturenum %d\n", texnum ); + texnum = tr.defaultTexture; + } + + texture = &gl_textures[texnum]; +#if 1 + if( glState.currentTexture == texnum ) + return; + + // Set palette + if(texture->format == GU_PSM_T8 && texture->dstPalette) + { + sceGuClutMode( PALETTE_FORMAT, 0, 0xff, 0 ); + sceGuClutLoad( PALETTE_BLOCKS, texture->dstPalette ); + } + + // Set texture parameters + sceGuTexMode( texture->format, texture->numMips, 0, FBitSet(texture->guFlags, GUFLAGTEXSWIZZLED) ? GU_TRUE : GU_FALSE ); + if(texture->numMips > 0) + { + sceGuTexFilter( GU_LINEAR_MIPMAP_LINEAR, GU_LINEAR_MIPMAP_LINEAR ); + sceGuTexLevelMode( gl_texture_lodfunc->value, gl_texture_lodbias->value ); + sceGuTexSlope( gl_texture_lodslope->value ); + } + else + { + sceGuTexFilter( GU_LINEAR, GU_LINEAR ); + } + + // Set base texture + sceGuTexImage(0, texture->width, texture->height, texture->width, texture->dstTexture); + + // Set mip textures + if( texture->numMips > 0 ) + { + int offset = texture->width * texture->height; + int width, height; + for ( i = 1; i <= texture->numMips; i++ ) + { + width = Q_max( 1, ( texture->width >> i ) ); + height = Q_max( 1, ( texture->height >> i ) ); + sceGuTexImage( i, width, height, width, &texture->dstTexture[offset] ); + offset += width * height; + } + } + + glState.currentTexture = texnum; +#else + glTarget = texture->target; + + if( glTarget == GL_TEXTURE_2D_ARRAY_EXT ) + glTarget = GL_TEXTURE_2D; + + if( glState.currentTextureTargets[tmu] != glTarget ) + { + if( glState.currentTextureTargets[tmu] != GL_NONE ) + pglDisable( glState.currentTextureTargets[tmu] ); + glState.currentTextureTargets[tmu] = glTarget; + pglEnable( glState.currentTextureTargets[tmu] ); + } + + if( glState.currentTextures[tmu] == texture->texnum ) + return; + + pglBindTexture( texture->target, texture->texnum ); + + glState.currentTextures[tmu] = texture->texnum; +#endif +} + +/* +================= +GL_ApplyTextureParams +================= +*/ +void GL_ApplyTextureParams( gl_texture_t *tex ) +{ +#if 0 + vec4_t border = { 0.0f, 0.0f, 0.0f, 1.0f }; + + if( !glw_state.initialized ) + return; + + Assert( tex != NULL ); + + // set texture filter + if( FBitSet( tex->flags, TF_DEPTHMAP )) + { + if( !FBitSet( tex->flags, TF_NOCOMPARE )) + { + pglTexParameteri( tex->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB ); + pglTexParameteri( tex->target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL ); + } + + if( FBitSet( tex->flags, TF_LUMINANCE )) + pglTexParameteri( tex->target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE ); + else pglTexParameteri( tex->target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY ); + + if( FBitSet( tex->flags, TF_NEAREST )) + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } + else + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + // allow max anisotropy as 1.0f on depth textures + if( GL_Support( GL_ANISOTROPY_EXT )) + pglTexParameterf( tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f ); + } + else if( FBitSet( tex->flags, TF_NOMIPMAP ) || tex->numMips <= 1 ) + { + if( FBitSet( tex->flags, TF_NEAREST ) || ( IsLightMap( tex ) && gl_lightmap_nearest->value )) + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } + else + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + } + else + { + if( FBitSet( tex->flags, TF_NEAREST ) || gl_texture_nearest->value ) + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } + else + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + // set texture anisotropy if available + if( GL_Support( GL_ANISOTROPY_EXT ) && ( tex->numMips > 1 ) && !FBitSet( tex->flags, TF_ALPHACONTRAST )) + pglTexParameterf( tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy->value ); + + // set texture LOD bias if available + if( GL_Support( GL_TEXTURE_LOD_BIAS ) && ( tex->numMips > 1 )) + pglTexParameterf( tex->target, GL_TEXTURE_LOD_BIAS_EXT, gl_texture_lodbias->value ); + } + + // check if border is not supported + if( FBitSet( tex->flags, TF_BORDER ) && !GL_Support( GL_CLAMP_TEXBORDER_EXT )) + { + ClearBits( tex->flags, TF_BORDER ); + SetBits( tex->flags, TF_CLAMP ); + } + + // only seamless cubemaps allows wrap 'clamp_to_border" + if( tex->target == GL_TEXTURE_CUBE_MAP_ARB && !GL_Support( GL_ARB_SEAMLESS_CUBEMAP ) && FBitSet( tex->flags, TF_BORDER )) + ClearBits( tex->flags, TF_BORDER ); + + // set texture wrap + if( FBitSet( tex->flags, TF_BORDER )) + { + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); + + if( tex->target != GL_TEXTURE_1D ) + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + + if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_CUBE_MAP_ARB ) + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER ); + + pglTexParameterfv( tex->target, GL_TEXTURE_BORDER_COLOR, border ); + } + else if( FBitSet( tex->flags, TF_CLAMP )) + { + if( GL_Support( GL_CLAMPTOEDGE_EXT )) + { + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + + if( tex->target != GL_TEXTURE_1D ) + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + + if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_CUBE_MAP_ARB ) + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + } + else + { + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP ); + + if( tex->target != GL_TEXTURE_1D ) + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP ); + + if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_CUBE_MAP_ARB ) + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP ); + } + } + else + { + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_S, GL_REPEAT ); + + if( tex->target != GL_TEXTURE_1D ) + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_T, GL_REPEAT ); + + if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_CUBE_MAP_ARB ) + pglTexParameteri( tex->target, GL_TEXTURE_WRAP_R, GL_REPEAT ); + } +#endif +} + +/* +================= +GL_UpdateTextureParams +================= +*/ +static void GL_UpdateTextureParams( int iTexture ) +{ +#if 0 + gl_texture_t *tex = &gl_textures[iTexture]; + + Assert( tex != NULL ); + + if( !tex->texnum ) return; // free slot + + GL_Bind( XASH_TEXTURE0, iTexture ); + + // set texture anisotropy if available + if( GL_Support( GL_ANISOTROPY_EXT ) && ( tex->numMips > 1 ) && !FBitSet( tex->flags, TF_DEPTHMAP|TF_ALPHACONTRAST )) + pglTexParameterf( tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy->value ); + + // set texture LOD bias if available + if( GL_Support( GL_TEXTURE_LOD_BIAS ) && ( tex->numMips > 1 ) && !FBitSet( tex->flags, TF_DEPTHMAP )) + pglTexParameterf( tex->target, GL_TEXTURE_LOD_BIAS_EXT, gl_texture_lodbias->value ); + + if( IsLightMap( tex )) + { + if( gl_lightmap_nearest->value ) + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } + else + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + } + + if( tex->numMips <= 1 ) return; + + if( FBitSet( tex->flags, TF_NEAREST ) || gl_texture_nearest->value ) + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } + else + { + pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } +#endif +} + +/* +================= +R_SetTextureParameters +================= +*/ +void R_SetTextureParameters( void ) +{ +#if 0 + int i; + + if( GL_Support( GL_ANISOTROPY_EXT )) + { + if( gl_texture_anisotropy->value > glConfig.max_texture_anisotropy ) + gEngfuncs.Cvar_SetValue( "gl_anisotropy", glConfig.max_texture_anisotropy ); + else if( gl_texture_anisotropy->value < 1.0f ) + gEngfuncs.Cvar_SetValue( "gl_anisotropy", 1.0f ); + } + + if( GL_Support( GL_TEXTURE_LOD_BIAS )) + { + if( gl_texture_lodbias->value < -glConfig.max_texture_lod_bias ) + gEngfuncs.Cvar_SetValue( "gl_texture_lodbias", -glConfig.max_texture_lod_bias ); + else if( gl_texture_lodbias->value > glConfig.max_texture_lod_bias ) + gEngfuncs.Cvar_SetValue( "gl_texture_lodbias", glConfig.max_texture_lod_bias ); + } + + ClearBits( gl_texture_anisotropy->flags, FCVAR_CHANGED ); + ClearBits( gl_texture_lodbias->flags, FCVAR_CHANGED ); + ClearBits( gl_texture_nearest->flags, FCVAR_CHANGED ); + ClearBits( gl_lightmap_nearest->flags, FCVAR_CHANGED ); + + // change all the existing mipmapped texture objects + for( i = 0; i < gl_numTextures; i++ ) + GL_UpdateTextureParams( i ); +#endif +} + +/* +================ +GL_CalcTextureSamples +================ +*/ +static int GL_CalcTextureSamples( int flags ) +{ + if( FBitSet( flags, IMAGE_HAS_COLOR )) + return FBitSet( flags, IMAGE_HAS_ALPHA ) ? 4 : 3; + return FBitSet( flags, IMAGE_HAS_ALPHA ) ? 2 : 1; +} + +/* +================== +GL_CalcImageSize +================== +*/ +static size_t GL_CalcImageSize( pixformat_t format, int width, int height, int depth ) +{ + size_t size = 0; + + // check the depth error + depth = Q_max( 1, depth ); + + switch( format ) + { + case PF_INDEXED_24: + case PF_INDEXED_32: + case PF_LUMINANCE: + size = width * height * depth; + break; + case PF_RGB_24: + case PF_BGR_24: + size = width * height * depth * 3; + break; + case PF_BGRA_32: + case PF_RGBA_32: + size = width * height * depth * 4; + break; + case PF_DXT1: + size = (((width + 3) >> 2) * ((height + 3) >> 2) * 8) * depth; + break; + case PF_DXT3: + case PF_DXT5: + case PF_ATI2: + size = (((width + 3) >> 2) * ((height + 3) >> 2) * 16) * depth; + break; + } + + return size; +} + +/* +================== +GL_CalcTextureSize +================== +*/ +static size_t GL_CalcTextureSize( int format, int width, int height ) +{ + size_t size = 0; + + switch( format ) + { + case GU_PSM_T4: + case GU_PSM_DXT1: + size = ( ( width * height ) >> 1 ); + break; + case GU_PSM_T8: + case GU_PSM_DXT3: + case GU_PSM_DXT5: + size = width * height; + break; + case GU_PSM_T16: + case GU_PSM_4444: + case GU_PSM_5551: + case GU_PSM_5650: + size = width * height * 2; + break; + case GU_PSM_T32: + case GU_PSM_8888: + size = width * height * 4; + break; + default: + gEngfuncs.Host_Error( "GL_CalcTextureSize: bad texture internal format (%u)\n", format ); + break; + } + return size; +} + +static int GL_CalcMipmapCount( gl_texture_t *tex, qboolean haveBuffer ) +{ + int width, height; +#if 1 + int i, mipcount; +#endif + Assert( tex != NULL ); + + if( !haveBuffer ) + return 1; +#if 1 // brush model only + if( !FBitSet( tex->flags, TF_ALLOW_EMBOSS ) ) + return 1; +#endif + // generate mip-levels by user request + if( FBitSet( tex->flags, TF_NOMIPMAP )) + return 1; + + // mip-maps can't exceeds 16 +#if 1 // 8 mipmaps for GU + for( i = 0; i < 8; i++ ) +#else + for( mipcount = 0; mipcount < 16; mipcount++ ) +#endif + { + width = Q_max( 1, ( tex->width >> i )); + height = Q_max( 1, ( tex->height >> i )); +#if 1 + if( ( width > 16 ) && ( height > 16 ) ) + mipcount = i; + else break; +#else + if( width == 1 && height == 1 ) + break; +#endif + } + + return mipcount + 1; +} + +/* +================ +GL_SetTextureDimensions +================ +*/ +static void GL_SetTextureDimensions( gl_texture_t *tex, int width, int height, int depth ) +{ +#if 1 + int maxTextureSize = glConfig.max_texture_size; + + Assert( tex != NULL ); + + // store original sizes + tex->srcWidth = width; + tex->srcHeight = height; + + if( width > maxTextureSize || height > maxTextureSize ) + { + while( width > maxTextureSize || height > maxTextureSize ) + { + width >>= 1; + height >>= 1; + } + } + + float calc_w = ceilf( logf( width ) * 1.0f / logf( 2.0f ) ); + float calc_h = ceilf( logf( height ) * 1.0f / logf( 2.0f ) ); + width = ( 1 << ( int ) calc_w ); + height = ( 1 << ( int ) calc_h ); + + // set the texture dimensions + tex->width = Q_max( 32, width ); + tex->height = Q_max( 32, height ); + tex->depth = Q_max( 1, depth ); +#else + int maxTextureSize = 0; + int maxDepthSize = 1; + + Assert( tex != NULL ); + + switch( tex->target ) + { + case GL_TEXTURE_1D: + case GL_TEXTURE_2D: + maxTextureSize = glConfig.max_2d_texture_size; + break; + case GL_TEXTURE_2D_ARRAY_EXT: + maxDepthSize = glConfig.max_2d_texture_layers; + maxTextureSize = glConfig.max_2d_texture_size; + break; + case GL_TEXTURE_RECTANGLE_EXT: + maxTextureSize = glConfig.max_2d_rectangle_size; + break; + case GL_TEXTURE_CUBE_MAP_ARB: + maxTextureSize = glConfig.max_cubemap_size; + break; + case GL_TEXTURE_3D: + maxDepthSize = glConfig.max_3d_texture_size; + maxTextureSize = glConfig.max_3d_texture_size; + break; + default: + Assert( false ); + } + + // store original sizes + tex->srcWidth = width; + tex->srcHeight = height; + + if( !GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) + { + int step = (int)gl_round_down->value; + int scaled_width, scaled_height; + + for( scaled_width = 1; scaled_width < width; scaled_width <<= 1 ); + + if( step > 0 && width < scaled_width && ( step == 1 || ( scaled_width - width ) > ( scaled_width >> step ))) + scaled_width >>= 1; + + for( scaled_height = 1; scaled_height < height; scaled_height <<= 1 ); + + if( step > 0 && height < scaled_height && ( step == 1 || ( scaled_height - height ) > ( scaled_height >> step ))) + scaled_height >>= 1; + + width = scaled_width; + height = scaled_height; + } + + if( width > maxTextureSize || height > maxTextureSize || depth > maxDepthSize ) + { + if( tex->target == GL_TEXTURE_1D ) + { + while( width > maxTextureSize ) + width >>= 1; + } + else if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_2D_ARRAY_EXT ) + { + while( width > maxTextureSize || height > maxTextureSize || depth > maxDepthSize ) + { + width >>= 1; + height >>= 1; + depth >>= 1; + } + } + else // all remaining cases + { + while( width > maxTextureSize || height > maxTextureSize ) + { + width >>= 1; + height >>= 1; + } + } + } + + // set the texture dimensions + tex->width = Q_max( 1, width ); + tex->height = Q_max( 1, height ); + tex->depth = Q_max( 1, depth ); +#endif +} + +/* +=============== +GL_SetTextureFormat +=============== +*/ +static void GL_SetTextureFormat( gl_texture_t *tex, pixformat_t format, int channelMask ) +{ + qboolean haveColor = ( channelMask & IMAGE_HAS_COLOR ); + qboolean haveAlpha = ( channelMask & IMAGE_HAS_ALPHA ); + + Assert( tex != NULL ); +#if 1 + if( ImageDXT( format )) + { + switch( format ) + { + case PF_DXT1: tex->format = GU_PSM_DXT1; break; // never use DXT1 with 1-bit alpha + case PF_DXT3: tex->format = GU_PSM_DXT3; break; + case PF_DXT5: tex->format = GU_PSM_DXT5; break; + } + } + /* + else if( FBitSet( tex->flags, TF_DEPTHMAP )) + { + if( FBitSet( tex->flags, TF_ARB_16BIT )) + tex->format = GL_DEPTH_COMPONENT16; + else if( FBitSet( tex->flags, TF_ARB_FLOAT )) + tex->format = GL_DEPTH_COMPONENT32F; + else tex->format = GL_DEPTH_COMPONENT24; + }*/ + else + { + if( format == PF_INDEXED_24 || format == PF_INDEXED_32 ) + { + tex->format = GU_PSM_T8; + } + else + { + if ( IsLightMap( tex ) ) + tex->format = GU_PSM_5650; + else if( haveAlpha ) + tex->format = GU_PSM_4444; + else + tex->format = GU_PSM_5650; + } + } +#endif +#if 0 + qboolean haveColor = ( channelMask & IMAGE_HAS_COLOR ); + qboolean haveAlpha = ( channelMask & IMAGE_HAS_ALPHA ); + + Assert( tex != NULL ); + + if( ImageDXT( format )) + { + switch( format ) + { + case PF_DXT1: tex->format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; // never use DXT1 with 1-bit alpha + case PF_DXT3: tex->format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case PF_DXT5: tex->format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case PF_ATI2: + if( glConfig.hardware_type == GLHW_RADEON ) + tex->format = GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI; + else tex->format = GL_COMPRESSED_RED_GREEN_RGTC2_EXT; + break; + } + return; + } + else if( FBitSet( tex->flags, TF_DEPTHMAP )) + { + if( FBitSet( tex->flags, TF_ARB_16BIT )) + tex->format = GL_DEPTH_COMPONENT16; + else if( FBitSet( tex->flags, TF_ARB_FLOAT ) && GL_Support( GL_ARB_DEPTH_FLOAT_EXT )) + tex->format = GL_DEPTH_COMPONENT32F; + else tex->format = GL_DEPTH_COMPONENT24; + } + else if( FBitSet( tex->flags, TF_ARB_FLOAT|TF_ARB_16BIT ) && GL_Support( GL_ARB_TEXTURE_FLOAT_EXT )) + { + if( haveColor && haveAlpha ) + { + if( FBitSet( tex->flags, TF_ARB_16BIT ) || gpGlobals->desktopBitsPixel == 16 ) + tex->format = GL_RGBA16F_ARB; + else tex->format = GL_RGBA32F_ARB; + } + else if( haveColor ) + { + if( FBitSet( tex->flags, TF_ARB_16BIT ) || gpGlobals->desktopBitsPixel == 16 ) + tex->format = GL_RGB16F_ARB; + else tex->format = GL_RGB32F_ARB; + } + else if( haveAlpha ) + { + if( FBitSet( tex->flags, TF_ARB_16BIT ) || gpGlobals->desktopBitsPixel == 16 ) + tex->format = GL_RG16F; + else tex->format = GL_RG32F; + } + else + { + if( FBitSet( tex->flags, TF_ARB_16BIT ) || gpGlobals->desktopBitsPixel == 16 ) + tex->format = GL_LUMINANCE16F_ARB; + else tex->format = GL_LUMINANCE32F_ARB; + } + } + else + { + // NOTE: not all the types will be compressed + int bits = gpGlobals->desktopBitsPixel; + + switch( GL_CalcTextureSamples( channelMask )) + { + case 1: + if( FBitSet( tex->flags, TF_ALPHACONTRAST )) + tex->format = GL_INTENSITY8; + else tex->format = GL_LUMINANCE8; + break; + case 2: tex->format = GL_LUMINANCE8_ALPHA8; break; + case 3: + switch( bits ) + { + case 16: tex->format = GL_RGB5; break; + case 32: tex->format = GL_RGB8; break; + default: tex->format = GL_RGB; break; + } + break; + case 4: + default: + switch( bits ) + { + case 16: tex->format = GL_RGBA4; break; + case 32: tex->format = GL_RGBA8; break; + default: tex->format = GL_RGBA; break; + } + break; + } + } +#endif +} + +/* +================= +GL_ResampleTexture32 + +Assume input buffer is RGBA +================= +*/ +void GL_ResampleTexture32( const byte *source, int inWidth, int inHeight, byte *dest, int outWidth, int outHeight, qboolean isNormalMap ) +{ + uint frac, fracStep; + uint *in = (uint *)source; + uint p1[0x1000], p2[0x1000]; + byte *pix1, *pix2, *pix3, *pix4; + uint *out, *inRow1, *inRow2; + vec3_t normal; + int i, x, y; + + if( !source ) + return; + + fracStep = inWidth * 0x10000 / outWidth; + out = (uint *)dest; + + frac = fracStep >> 2; + for( i = 0; i < outWidth; i++ ) + { + p1[i] = 4 * (frac >> 16); + frac += fracStep; + } + + frac = (fracStep >> 2) * 3; + for( i = 0; i < outWidth; i++ ) + { + p2[i] = 4 * (frac >> 16); + frac += fracStep; + } + + if( isNormalMap ) + { + for( y = 0; y < outHeight; y++, out += outWidth ) + { + inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight); + inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight); + + for( x = 0; x < outWidth; x++ ) + { + pix1 = (byte *)inRow1 + p1[x]; + pix2 = (byte *)inRow1 + p2[x]; + pix3 = (byte *)inRow2 + p1[x]; + pix4 = (byte *)inRow2 + p2[x]; + + normal[0] = MAKE_SIGNED( pix1[0] ) + MAKE_SIGNED( pix2[0] ) + MAKE_SIGNED( pix3[0] ) + MAKE_SIGNED( pix4[0] ); + normal[1] = MAKE_SIGNED( pix1[1] ) + MAKE_SIGNED( pix2[1] ) + MAKE_SIGNED( pix3[1] ) + MAKE_SIGNED( pix4[1] ); + normal[2] = MAKE_SIGNED( pix1[2] ) + MAKE_SIGNED( pix2[2] ) + MAKE_SIGNED( pix3[2] ) + MAKE_SIGNED( pix4[2] ); + + if( !VectorNormalizeLength( normal )) + VectorSet( normal, 0.5f, 0.5f, 1.0f ); + + ((byte *)(out+x))[0] = 128 + (byte)(127.0f * normal[0]); + ((byte *)(out+x))[1] = 128 + (byte)(127.0f * normal[1]); + ((byte *)(out+x))[2] = 128 + (byte)(127.0f * normal[2]); + ((byte *)(out+x))[3] = 255; + } + } + } + else + { + for( y = 0; y < outHeight; y++, out += outWidth ) + { + inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight); + inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight); + + for( x = 0; x < outWidth; x++ ) + { + pix1 = (byte *)inRow1 + p1[x]; + pix2 = (byte *)inRow1 + p2[x]; + pix3 = (byte *)inRow2 + p1[x]; + pix4 = (byte *)inRow2 + p2[x]; + + ((byte *)(out+x))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2; + ((byte *)(out+x))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2; + ((byte *)(out+x))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2; + ((byte *)(out+x))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3]) >> 2; + } + } + } +} + +/* +================= +GL_ResampleTexture8 + +For indexed textures +================= +*/ +void GL_ResampleTexture8(const byte *source, int inWidth, int inHeight, byte *dest, int outWidth, int outHeight) +{ + uint fracstep; + byte *out; + const byte *inrow; + uint frac; + int i, j; + + if( !source ) + return; + + fracstep = inWidth * 0x10000 / outWidth; + out = dest; + + for (i = 0; i < outHeight ; ++i, out += outWidth) + { + inrow = source + inWidth * (i * inHeight / outHeight); + frac = fracstep >> 1; + for (j = 0; j < outWidth; ++j, frac += fracstep) + { + out[j] = inrow[frac >> 16]; + } + } +} +/* +================= +GL_BoxFilter3x3 + +box filter 3x3 +================= +*/ +void GL_BoxFilter3x3( byte *out, const byte *in, int w, int h, int x, int y ) +{ + int r = 0, g = 0, b = 0, a = 0; + int count = 0, acount = 0; + int i, j, u, v; + const byte *pixel; + + for( i = 0; i < 3; i++ ) + { + u = ( i - 1 ) + x; + + for( j = 0; j < 3; j++ ) + { + v = ( j - 1 ) + y; + + if( u >= 0 && u < w && v >= 0 && v < h ) + { + pixel = &in[( u + v * w ) * 4]; + + if( pixel[3] != 0 ) + { + r += pixel[0]; + g += pixel[1]; + b += pixel[2]; + a += pixel[3]; + acount++; + } + } + } + } + + if( acount == 0 ) + acount = 1; + + out[0] = r / acount; + out[1] = g / acount; + out[2] = b / acount; +// out[3] = (int)( SimpleSpline( ( a / 12.0f ) / 255.0f ) * 255 ); +} + +/* +================= +GL_ApplyFilter + +Apply box-filter to 1-bit alpha +================= +*/ +byte *GL_ApplyFilter( const byte *source, int width, int height ) +{ + byte *in = (byte *)source; + byte *out = (byte *)source; + int i; + + if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) ) + return in; + + for( i = 0; source && i < width * height; i++, in += 4 ) + { + if( in[0] == 0 && in[1] == 0 && in[2] == 0 && in[3] == 0 ) + GL_BoxFilter3x3( in, source, width, height, i % width, i / width ); + } + + return out; +} + +/* +================= +GL_BuildMipMap + +Operates in place, quartering the size of the texture +================= +*/ +static void GL_BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, int flags ) +{ + byte *out = in; + int instride = ALIGN( srcWidth * 4, 1 ); + int mipWidth, mipHeight, outpadding; + int row, x, y, z; + vec3_t normal; + + if( !in ) return; + + mipWidth = Q_max( 1, ( srcWidth >> 1 )); + mipHeight = Q_max( 1, ( srcHeight >> 1 )); + outpadding = ALIGN( mipWidth * 4, 1 ) - mipWidth * 4; + row = srcWidth << 2; + + if( FBitSet( flags, TF_ALPHACONTRAST )) + { + memset( in, mipWidth, mipWidth * mipHeight * 4 ); + return; + } + + // move through all layers + for( z = 0; z < srcDepth; z++ ) + { + if( FBitSet( flags, TF_NORMALMAP )) + { + for( y = 0; y < mipHeight; y++, in += instride * 2, out += outpadding ) + { + byte *next = ((( y << 1 ) + 1 ) < srcHeight ) ? ( in + instride ) : in; + for( x = 0, row = 0; x < mipWidth; x++, row += 8, out += 4 ) + { + if((( x << 1 ) + 1 ) < srcWidth ) + { + normal[0] = MAKE_SIGNED( in[row+0] ) + MAKE_SIGNED( in[row+4] ) + + MAKE_SIGNED( next[row+0] ) + MAKE_SIGNED( next[row+4] ); + normal[1] = MAKE_SIGNED( in[row+1] ) + MAKE_SIGNED( in[row+5] ) + + MAKE_SIGNED( next[row+1] ) + MAKE_SIGNED( next[row+5] ); + normal[2] = MAKE_SIGNED( in[row+2] ) + MAKE_SIGNED( in[row+6] ) + + MAKE_SIGNED( next[row+2] ) + MAKE_SIGNED( next[row+6] ); + } + else + { + normal[0] = MAKE_SIGNED( in[row+0] ) + MAKE_SIGNED( next[row+0] ); + normal[1] = MAKE_SIGNED( in[row+1] ) + MAKE_SIGNED( next[row+1] ); + normal[2] = MAKE_SIGNED( in[row+2] ) + MAKE_SIGNED( next[row+2] ); + } + + if( !VectorNormalizeLength( normal )) + VectorSet( normal, 0.5f, 0.5f, 1.0f ); + + out[0] = 128 + (byte)(127.0f * normal[0]); + out[1] = 128 + (byte)(127.0f * normal[1]); + out[2] = 128 + (byte)(127.0f * normal[2]); + out[3] = 255; + } + } + } + else + { + for( y = 0; y < mipHeight; y++, in += instride * 2, out += outpadding ) + { + byte *next = ((( y << 1 ) + 1 ) < srcHeight ) ? ( in + instride ) : in; + for( x = 0, row = 0; x < mipWidth; x++, row += 8, out += 4 ) + { + if((( x << 1 ) + 1 ) < srcWidth ) + { + out[0] = (in[row+0] + in[row+4] + next[row+0] + next[row+4]) >> 2; + out[1] = (in[row+1] + in[row+5] + next[row+1] + next[row+5]) >> 2; + out[2] = (in[row+2] + in[row+6] + next[row+2] + next[row+6]) >> 2; + out[3] = (in[row+3] + in[row+7] + next[row+3] + next[row+7]) >> 2; + } + else + { + out[0] = (in[row+0] + next[row+0]) >> 1; + out[1] = (in[row+1] + next[row+1]) >> 1; + out[2] = (in[row+2] + next[row+2]) >> 1; + out[3] = (in[row+3] + next[row+3]) >> 1; + } + } + } + } + } +} + +static void GL_TextureImageRAW( gl_texture_t *tex, GLint side, GLint level, GLint width, GLint height, GLint depth, GLint type, const void *data ) +{ +#if 0 + GLuint cubeTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB; + qboolean subImage = FBitSet( tex->flags, TF_IMG_UPLOADED ); + GLenum inFormat = gEngfuncs.Image_GetPFDesc(type)->glFormat; + GLint dataType = GL_UNSIGNED_BYTE; + + Assert( tex != NULL ); +#endif +} +#if 0 +static void GL_TextureImageDXT( gl_texture_t *tex, GLint side, GLint level, GLint width, GLint height, GLint depth, size_t size, const void *data ) +{ + GLuint cubeTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB; + qboolean subImage = FBitSet( tex->flags, TF_IMG_UPLOADED ); + + Assert( tex != NULL ); + +#ifndef XASH_GLES + if( tex->target == GL_TEXTURE_1D ) + { + if( subImage ) pglCompressedTexSubImage1DARB( tex->target, level, 0, width, tex->format, size, data ); + else pglCompressedTexImage1DARB( tex->target, level, tex->format, width, 0, size, data ); + } + else if( tex->target == GL_TEXTURE_CUBE_MAP_ARB ) + { + if( subImage ) pglCompressedTexSubImage2DARB( cubeTarget + side, level, 0, 0, width, height, tex->format, size, data ); + else pglCompressedTexImage2DARB( cubeTarget + side, level, tex->format, width, height, 0, size, data ); + } + else if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_2D_ARRAY_EXT ) + { + if( subImage ) pglCompressedTexSubImage3DARB( tex->target, level, 0, 0, 0, width, height, depth, tex->format, size, data ); + else pglCompressedTexImage3DARB( tex->target, level, tex->format, width, height, depth, 0, size, data ); + } + else // 2D or RECT + { + if( subImage ) pglCompressedTexSubImage2DARB( tex->target, level, 0, 0, width, height, tex->format, size, data ); + else pglCompressedTexImage2DARB( tex->target, level, tex->format, width, height, 0, size, data ); + } +#endif +} +#endif +/* +=============== +GL_PixelConverter +=============== +*/ +static void GL_PixelConverter( byte *dst, const byte *src, int size, qboolean alpha, int format ) +{ +#if 0 // undone + if(alpha) + { + __asm__ volatile ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "move $8, %0\n" // $8 = &dst[0] + "move $9, %1\n" // $9 = &src[0] + "move $10, %2\n" // $10 = size + "0:\n" + + "ulv.q C000, 0($8)\n" // Load 4 pixels from src + + "vt4444.q C010, C000\n" // Convert 4 -> 2 + + "sv.s S010, 0($9)\n" + "sv.s S010, 8($9)\n" + "addiu $8, $8, 16\n" // $8 = $8 + 16 + "addiu $9, $9, 8\n" // $9 = $9 + 8 + + "bne $10, $8, 0b\n" // if ( $11 != $8 ) jump to loop + "addiu $10, $10, -4\n" // $10 = $10 - 4 ( delay slot ) + ".set pop\n" // Restore assembler option + : "r"( dst ), "r"( src ), "r"( size ), "r"( format ) + : "$8", "$9", "$10" + ); + } + else + { + + } +#else + int i; + byte color[4]; + + for( i = 0; i < size; i++ ) + { + switch( format ) + { + case GU_PSM_4444: + color[0] = *src; src++; + color[1] = *src; src++; + color[2] = *src; src++; + color[3] = alpha ? *src : 0xff; if( alpha ) src++; + *dst = ( color[0] >> 4 ) & 0x0f; + *dst |= ( color[1] ) & 0xf0; dst++; + *dst = ( color[2] >> 4 ) & 0x0f; + *dst |= ( color[3] ) & 0xf0; dst++; + break; + case GU_PSM_5551: + color[0] = ( *src >> 3 ) & 0x1f; src++; + color[1] = ( *src >> 3 ) & 0x1f; src++; + color[2] = ( *src >> 3 ) & 0x1f; src++; + color[3] = alpha ? ( ( *src >> 7 ) & 0x01 ) : 0xff; if( alpha ) src++; + *dst = color[0]; + *dst |= ( color[1] << 5 ) & 0xe0; dst++; + *dst = ( color[1] >> 3 ) & 0x03; + *dst |= ( color[2] << 2 ) & 0x7c; + *dst |= ( color[3] << 7 ) & 0x80; dst++; + break; + case GU_PSM_5650: + color[0] = ( *src >> 3 ) & 0x1f; src++; + color[1] = ( *src >> 2 ) & 0x3f; src++; + color[2] = ( *src >> 3 ) & 0x1f; src++; if( alpha ) src++; + *dst = color[0]; + *dst |= ( color[1] << 5 ) & 0xe0; dst++; + *dst = ( color[1] >> 3 ) & 0x07; + *dst |= ( color[2] << 3 ) & 0xf8; dst++; + break; + case GU_PSM_8888: + *dst = *src; src++; dst++; + *dst = *src; src++; dst++; + *dst = *src; src++; dst++; + *dst = alpha ? *src : 0xff; if( alpha ) src++; dst++; + break; + default: + break; + } + } +#endif +} +/* +=============== +GL_TextureSwizzle + +fast swizzling +=============== +*/ +static void GL_TextureSwizzle(byte *dst, const byte *src, uint width, uint height) +{ + uint blockx, blocky; + uint j; + uint width_blocks = ( width / 16 ); + uint height_blocks = ( height / 8 ); + uint src_pitch = ( width - 16 ) / 4; + uint src_row = width * 8; + const byte* ysrc = src; + uint* dst32 = ( uint* )dst; + + for (blocky = 0; blocky < height_blocks; ++blocky) + { + const byte* xsrc = ysrc; + for (blockx = 0; blockx < width_blocks; ++blockx) + { + const int* src32 = ( uint* )xsrc; + for (j = 0; j < 8; ++j) + { + *(dst32++) = *(src32++); + *(dst32++) = *(src32++); + *(dst32++) = *(src32++); + *(dst32++) = *(src32++); + src32 += src_pitch; + } + xsrc += 16; + } + ysrc += src_row; + } +} + +/* +=============== +GL_UploadTexture + +upload texture into memory +=============== +*/ +static qboolean GL_UploadTexture( gl_texture_t *tex, rgbdata_t *pic ) +{ + byte *buf, *data; + size_t texsize, size; + uint width, height; + uint i, j, numSides; + uint offset = 0; + qboolean normalMap; + const byte *bufend; + void *volBuff; + unsigned int volSize; + int volUid; + + // dedicated server + if( !glw_state.initialized ) + return true; + + Assert( pic != NULL ); + Assert( tex != NULL ); + + GL_SetTextureDimensions( tex, pic->width, pic->height, pic->depth ); + GL_SetTextureFormat( tex, pic->type, pic->flags ); + + tex->fogParams[0] = pic->fogParams[0]; + tex->fogParams[1] = pic->fogParams[1]; + tex->fogParams[2] = pic->fogParams[2]; + tex->fogParams[3] = pic->fogParams[3]; + + if(( pic->width * pic->height ) & 3 ) + { + // will be resampled, just tell me for debug targets + gEngfuncs.Con_Reportf( "GL_UploadTexture: %s s&3 [%d x %d]\n", tex->name, pic->width, pic->height ); + } + + if( !pic->buffer ) + return true; + + buf = pic->buffer; + bufend = pic->buffer + pic->size; // total image size include all the layers, cube sides, mipmaps + + offset = GL_CalcImageSize( pic->type, pic->width, pic->height, pic->depth ); + texsize = GL_CalcTextureSize( tex->format, tex->width, tex->height ); + normalMap = FBitSet( tex->flags, TF_NORMALMAP ) ? true : false; + tex->numMips = 0; + + volUid = sceKernelVolatileMemLock( 0, &volBuff, &volSize ); + if( volUid != 0) + gEngfuncs.Host_Error( "GL_AllocTexture: volatile memory lock error 0x%08x ! \n", volUid); + + if( ImageDXT( pic->type ) ) + { + memcpy(tex->dstTexture, buf, texsize); + } + else if( pic->type == PF_INDEXED_32 ) + { + int mipCount = GL_CalcMipmapCount( tex, ( buf != NULL )); + + // Calc mip size + texsize = 0; + for(j = 0; j < mipCount; j++ ) + { + width = Q_max( 1, ( tex->width >> j ) ); + height = Q_max( 1, ( tex->height >> j ) ); + texsize += GL_CalcTextureSize( tex->format, width, height ); + } + + // Check allocation size + if( tex->dstTexture && ( tex->size != texsize ) ) + { + if( FBitSet( tex->guFlags, GUFLAGTEXINVRAM ) ) + vfree( tex->dstTexture ); + else + free( tex->dstTexture ); + ClearBits( tex->guFlags, GUFLAGTEXINVRAM ); + } + + // Already allocated? + if( !tex->dstTexture ) + { + tex->dstTexture = ( byte* )valloc( texsize ); + if( !tex->dstTexture ) + { + tex->dstTexture = ( byte* )memalign( 16, texsize ); + if ( !tex->dstTexture ) + { + sceKernelVolatileMemUnlock( 0 ); + gEngfuncs.Host_Error( "GL_AllocTexture: out of memory! ( texture: %i %s )\n", texsize, tex->name); + } + } + else + { + SetBits( tex->guFlags, GUFLAGTEXINVRAM ); + } + } + + // Load palette + tex->dstPalette = ( byte* )memalign( 16, PALETTE_SIZE ); + if ( !tex->dstPalette ) + { + sceKernelVolatileMemUnlock( 0 ); + gEngfuncs.Host_Error( "GL_AllocTexture: out of memory! ( palette: %s )\n", tex->name); + } + GL_PixelConverter( tex->dstPalette, pic->palette, 256, true, PALETTE_FORMAT ); + sceKernelDcacheWritebackRange( tex->dstPalette, PALETTE_SIZE ); + + int mip_offset = 0; + for( j = 0; j < mipCount; j++ ) + { + width = Q_max( 1, ( tex->width >> j ) ); + height = Q_max( 1, ( tex->height >> j ) ); + + if( ( pic->width != width ) || ( pic->height != height ) ) + GL_ResampleTexture8( buf, pic->width, pic->height, volBuff, width, height ); + else memcpy(volBuff, buf, offset); + GL_TextureSwizzle( tex->dstTexture + mip_offset, volBuff, width, height ); + mip_offset += GL_CalcTextureSize( tex->format, width, height ); + tex->numMips = j; + } + SetBits( tex->guFlags, GUFLAGTEXSWIZZLED ); + } + else if( pic->type == PF_INDEXED_24 ) + { + // Check allocation + if( tex->dstTexture && ( tex->size != texsize ) ) + { + if( FBitSet( tex->guFlags, GUFLAGTEXINVRAM ) ) + vfree( tex->dstTexture ); + else + free( tex->dstTexture ); + ClearBits( tex->guFlags, GUFLAGTEXINVRAM ); + } + // Already allocated + if( !tex->dstTexture ) + { + tex->dstTexture = ( byte* )valloc( texsize ); + if( !tex->dstTexture ) + { + tex->dstTexture = ( byte* )memalign( 16, texsize ); + if ( !tex->dstTexture ) + { + sceKernelVolatileMemUnlock( 0 ); + gEngfuncs.Host_Error( "GL_AllocTexture: out of memory! ( texture: %i %s )\n", texsize, tex->name); + } + } + else + { + SetBits( tex->guFlags, GUFLAGTEXINVRAM ); + } + } + + if( ( pic->width != tex->width ) || ( pic->height != tex->height ) ) + GL_ResampleTexture8( buf, pic->width, pic->height, volBuff, tex->width, tex->height ); + else + memcpy(volBuff, buf, offset); + + tex->dstPalette = ( byte* )memalign( 16, PALETTE_SIZE ); + if ( !tex->dstPalette ) + { + sceKernelVolatileMemUnlock( 0 ); + gEngfuncs.Host_Error( "GL_AllocTexture: out of memory! ( palette: %s )\n", tex->name); + } + GL_PixelConverter( tex->dstPalette, pic->palette, 256, false, PALETTE_FORMAT ); + sceKernelDcacheWritebackRange( tex->dstPalette, PALETTE_SIZE ); + GL_TextureSwizzle( tex->dstTexture, volBuff, tex->width, tex->height ); + SetBits( tex->guFlags, GUFLAGTEXSWIZZLED ); + } + else // RGBA32 + { + // Check allocation + if( tex->dstTexture && ( tex->size != texsize ) ) + { + if( FBitSet( tex->guFlags, GUFLAGTEXINVRAM ) ) + vfree( tex->dstTexture ); + else + free( tex->dstTexture ); + ClearBits( tex->guFlags, GUFLAGTEXINVRAM ); + } + // Already allocated + if( !tex->dstTexture ) + { + tex->dstTexture = ( byte* )valloc( texsize ); + if( !tex->dstTexture ) + { + tex->dstTexture = ( byte* )memalign( 16, texsize ); + if ( !tex->dstTexture ) + { + sceKernelVolatileMemUnlock( 0 ); + gEngfuncs.Host_Error( "GL_AllocTexture: out of memory! ( texture: %i %s )\n", texsize, tex->name); + } + } + else + { + SetBits( tex->guFlags, GUFLAGTEXINVRAM ); + } + } + + if( ( pic->width != tex->width ) || ( pic->height != tex->height ) ) + { + GL_ResampleTexture32( buf, pic->width, pic->height, volBuff, tex->width, tex->height, normalMap ); + offset = tex->width * tex->height * 4; + } + else + { + memcpy( volBuff, buf, offset ); + } + + if( tex->format == GU_PSM_8888 ) + { + memcpy( tex->dstTexture, volBuff, texsize ); + } + else + { + if ( IsLightMap( tex ) ) + { + GL_PixelConverter( tex->dstTexture, volBuff, tex->width * tex->height, true, tex->format ); + } + else + { +/* + if( !FBitSet( tex->flags, TF_NOMIPMAP ) && FBitSet( pic->flags, IMAGE_ONEBIT_ALPHA )) + volBuff = GL_ApplyFilter( volBuff, tex->width, tex->height ); +*/ + GL_PixelConverter( volBuff + offset, volBuff, tex->width * tex->height, true, tex->format ); + GL_TextureSwizzle( tex->dstTexture, volBuff + offset, tex->width * 2, tex->height ); + SetBits( tex->guFlags, GUFLAGTEXSWIZZLED ); + } + } + } + sceKernelVolatileMemUnlock( 0 ); + sceKernelDcacheWritebackRange( tex->dstTexture, texsize ); + tex->size = texsize; + + SetBits( tex->flags, TF_IMG_UPLOADED ); // done + + return true; +} + +/* +=============== +GL_UpdateDlightTexture + +=============== +*/ +qboolean GL_UpdateDlightTexture( int texnum, int xoff, int yoff, int width, int height, const void *buffer ) +{ + gl_texture_t *tex; + size_t texsize; + + // missed or invalid texture? + if( texnum <= 0 || texnum >= MAX_TEXTURES ) + { + if( texnum != 0 ) + { + gEngfuncs.Con_DPrintf( S_ERROR "GL_UpdateDlightTexture: invalid texturenum %d\n", texnum ); + return false; + } + } + tex = &gl_textures[texnum]; + + if(tex->width < width || tex->height < height ) + { + + gEngfuncs.Con_DPrintf( S_ERROR "GL_UpdateDlightTexture: invalid update area size %s\n", tex->name ); + return false; + } + + if(tex->width < xoff || tex->height < yoff ) + { + gEngfuncs.Con_DPrintf( S_ERROR "GL_UpdateDlightTexture: invalid position cord %s\n", tex->name ); + return false; + } + tex->numMips = 0; + tex->dstPalette = NULL; + tex->format = GU_PSM_5650; + + texsize = tex->width * tex->height * 2; + if( tex->size != texsize ) + { + if( tex->dstTexture ) + { + if( FBitSet( tex->guFlags, GUFLAGTEXINVRAM ) ) + vfree( tex->dstTexture ); + else + free( tex->dstTexture ); + + tex->dstTexture = NULL; + ClearBits( tex->guFlags, GUFLAGTEXINVRAM ); + } + } + + if( !tex->dstTexture ) + { + tex->dstTexture = ( byte* )valloc( texsize ); + if( !tex->dstTexture ) + { + tex->dstTexture = ( byte* )memalign( 16, texsize ); + if ( !tex->dstTexture ) + gEngfuncs.Host_Error( "GL_UpdateDlightTexture: out of memory! ( texture: %i %s )\n", texsize, tex->name); + } + else + { + SetBits( tex->guFlags, GUFLAGTEXINVRAM ); + } + memset( tex->dstTexture, 0x00, texsize ); + SetBits( tex->flags, TF_IMG_UPLOADED ); // done + } + + tex->size = texsize; + byte *src = ( byte* )buffer; + byte *dst = tex->dstTexture; + + dst += ( yoff * tex->width + xoff ) * 2; + while( height-- ) + { + GL_PixelConverter( dst, src, width, true, tex->format ); + dst += tex->width * 2; + src += width * 4; + } + sceKernelDcacheWritebackRange( tex->dstTexture, texsize ); + return true; +} + +/* +=============== +GL_ProcessImage + +do specified actions on pixels +=============== +*/ +static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic ) +{ + float emboss_scale = 0.0f; + uint img_flags = 0; + + // force upload texture as RGB or RGBA (detail textures requires this) + if( tex->flags & TF_FORCE_COLOR ) pic->flags |= IMAGE_HAS_COLOR; + if( pic->flags & IMAGE_HAS_ALPHA ) tex->flags |= TF_HAS_ALPHA; + + tex->encode = pic->encode; // share encode method + + if( ImageDXT( pic->type )) + { + if( !pic->numMips ) + tex->flags |= TF_NOMIPMAP; // disable mipmapping by user request + + // clear all the unsupported flags + tex->flags &= ~TF_KEEP_SOURCE; + } + else + { + // copy flag about luma pixels + if( pic->flags & IMAGE_HAS_LUMA ) + tex->flags |= TF_HAS_LUMA; + + if( pic->flags & IMAGE_QUAKEPAL ) + tex->flags |= TF_QUAKEPAL; + + // create luma texture from quake texture + if( tex->flags & TF_MAKELUMA ) + { + img_flags |= IMAGE_MAKE_LUMA; + tex->flags &= ~TF_MAKELUMA; + } + + if( tex->flags & TF_ALLOW_EMBOSS ) + { + img_flags |= IMAGE_EMBOSS; + //tex->flags &= ~TF_ALLOW_EMBOSS; + } + + if( !FBitSet( tex->flags, TF_IMG_UPLOADED ) && FBitSet( tex->flags, TF_KEEP_SOURCE )) + tex->original = gEngfuncs.FS_CopyImage( pic ); // because current pic will be expanded to rgba +#if 0 // forced indexed textures + // we need to expand image into RGBA buffer + if( pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32 ) + img_flags |= IMAGE_FORCE_RGBA; +#endif + // dedicated server doesn't register this variable + if( gl_emboss_scale != NULL ) + emboss_scale = gl_emboss_scale->value; + + // processing image before uploading (force to rgba, make luma etc) + if( pic->buffer ) gEngfuncs.Image_Process( &pic, 0, 0, img_flags, emboss_scale ); + + if( FBitSet( tex->flags, TF_LUMINANCE )) + ClearBits( pic->flags, IMAGE_HAS_COLOR ); + } +} + +/* +================ +GL_CheckTexName +================ +*/ +qboolean GL_CheckTexName( const char *name ) +{ + if( !COM_CheckString( name )) + return false; + + // because multi-layered textures can exceed name string + if( Q_strlen( name ) >= sizeof( gl_textures->name )) + { + gEngfuncs.Con_Printf( S_ERROR "LoadTexture: too long name %s (%d)\n", name, Q_strlen( name )); + return false; + } + + return true; +} + +/* +================ +GL_TextureForName +================ +*/ +static gl_texture_t *GL_TextureForName( const char *name ) +{ + gl_texture_t *tex; + uint hash; + + // find the texture in array + hash = COM_HashKey( name, TEXTURES_HASH_SIZE ); + + for( tex = gl_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash ) + { + if( !Q_stricmp( tex->name, name )) + return tex; + } + + return NULL; +} + +/* +================ +GL_AllocTexture +================ +*/ +static gl_texture_t *GL_AllocTexture( const char *name, texFlags_t flags ) +{ + gl_texture_t *tex; + uint i; + + // find a free texture_t slot + for( i = 0, tex = gl_textures; i < gl_numTextures; i++, tex++ ) + if( !tex->name[0] ) break; + + if( i == gl_numTextures ) + { + if( gl_numTextures == MAX_TEXTURES ) + gEngfuncs.Host_Error( "GL_AllocTexture: MAX_TEXTURES limit exceeds\n" ); + gl_numTextures++; + } + + tex = &gl_textures[i]; + + // copy initial params + Q_strncpy( tex->name, name, sizeof( tex->name )); +#if 0 + if( FBitSet( flags, TF_SKYSIDE )) + tex->texnum = tr.skyboxbasenum++; + else tex->texnum = i; // texnum is used for fast acess into gl_textures array too +#endif + tex->flags = flags; + + // add to hash table + tex->hashValue = COM_HashKey( name, TEXTURES_HASH_SIZE ); + tex->nextHash = gl_texturesHashTable[tex->hashValue]; + gl_texturesHashTable[tex->hashValue] = tex; + + return tex; +} + +/* +================ +GL_DeleteTexture +================ +*/ +static void GL_DeleteTexture( gl_texture_t *tex ) +{ + gl_texture_t **prev; + gl_texture_t *cur; + + ASSERT( tex != NULL ); + + // already freed? + if( !tex->dstTexture && !tex->name[0] ) return; + + // debug + if( !tex->name[0] ) + { + gEngfuncs.Con_Printf( S_ERROR "GL_DeleteTexture: trying to free unnamed texture\n"); + return; + } + + // remove from hash table + prev = &gl_texturesHashTable[tex->hashValue]; + while( 1 ) + { + cur = *prev; + if( !cur ) break; + + if( cur == tex ) + { + *prev = cur->nextHash; + break; + } + prev = &cur->nextHash; + } + + // release source + if( tex->original ) + gEngfuncs.FS_FreeImage( tex->original ); + + if( tex->dstPalette ) + free( tex->dstPalette ); + + if( tex->dstTexture ) + { + if( FBitSet( tex->guFlags, GUFLAGTEXINVRAM ) ) + vfree( tex->dstTexture ); + else + free( tex->dstTexture ); + } + memset( tex, 0, sizeof( *tex )); +} + +/* +================ +GL_UpdateTexSize + +recalc image room +================ +*/ +void GL_UpdateTexSize( int texnum, int width, int height, int depth ) +{ + int i, j, texsize; + int numSides; + gl_texture_t *tex; + + if( texnum <= 0 || texnum >= MAX_TEXTURES ) + return; + + tex = &gl_textures[texnum]; + numSides = FBitSet( tex->flags, TF_CUBEMAP ) ? 6 : 1; + GL_SetTextureDimensions( tex, width, height, depth ); + tex->size = 0; // recompute now + + for( i = 0; i < numSides; i++ ) + { + for( j = 0; j < Q_max( 1, tex->numMips ); j++ ) + { + width = Q_max( 1, ( tex->width >> j )); + height = Q_max( 1, ( tex->height >> j )); +#if 1 + texsize = GL_CalcTextureSize( tex->format, width, height ); +#else + texsize = GL_CalcTextureSize( tex->format, width, height, tex->depth ); +#endif + tex->size += texsize; + } + } +} + +/* +================ +GL_LoadTexture +================ +*/ +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) +{ + gl_texture_t *tex; + rgbdata_t *pic; + uint picFlags = 0; + + if( !GL_CheckTexName( name )) + return 0; + + // see if already loaded + if(( tex = GL_TextureForName( name ))) + return (tex - gl_textures); + + if( FBitSet( flags, TF_NOFLIP_TGA )) + SetBits( picFlags, IL_DONTFLIP_TGA ); +#if 1 + SetBits( picFlags, IL_KEEP_8BIT ); +#else + if( FBitSet( flags, TF_KEEP_SOURCE ) && !FBitSet( flags, TF_EXPAND_SOURCE )) + SetBits( picFlags, IL_KEEP_8BIT ); +#endif + // set some image flags + gEngfuncs.Image_SetForceFlags( picFlags ); + + pic = gEngfuncs.FS_LoadImage( name, buf, size ); + if( !pic ) return 0; // couldn't loading image + + // allocate the new one + tex = GL_AllocTexture( name, flags ); + GL_ProcessImage( tex, pic ); + + if( !GL_UploadTexture( tex, pic )) + { + memset( tex, 0, sizeof( gl_texture_t )); + gEngfuncs.FS_FreeImage( pic ); // release source texture + return 0; + } + + GL_ApplyTextureParams( tex ); // update texture filter, wrap etc + gEngfuncs.FS_FreeImage( pic ); // release source texture + + // NOTE: always return texnum as index in array or engine will stop work !!! + return tex - gl_textures; +} + +/* +================ +GL_LoadTextureArray +================ +*/ +int GL_LoadTextureArray( const char **names, int flags ) +{ +#if 1 + return 0; +#else + rgbdata_t *pic, *src; + char basename[256]; + uint numLayers = 0; + uint picFlags = 0; + char name[256]; + gl_texture_t *tex; + uint i, j; + + if( !names || !names[0] || !glw_state.initialized ) + return 0; + + // count layers (g-cont. this is pontentially unsafe loop) + for( i = 0; i < glConfig.max_2d_texture_layers && ( *names[i] != '\0' ); i++ ) + numLayers++; + name[0] = '\0'; + + if( numLayers <= 0 ) return 0; + + // create complexname from layer names + for( i = 0; i < numLayers; i++ ) + { + COM_FileBase( names[i], basename ); + Q_strncat( name, va( "%s", basename ), sizeof( name )); + if( i != ( numLayers - 1 )) Q_strncat( name, "|", sizeof( name )); + } + + Q_strncat( name, va( "[%i]", numLayers ), sizeof( name )); + + if( !GL_CheckTexName( name )) + return 0; + + // see if already loaded + if(( tex = GL_TextureForName( name ))) + return (tex - gl_textures); + + // load all the images and pack it into single image + for( i = 0, pic = NULL; i < numLayers; i++ ) + { + size_t srcsize, dstsize, mipsize; + + src = gEngfuncs.FS_LoadImage( names[i], NULL, 0 ); + if( !src ) break; // coldn't find layer + + if( pic ) + { + // mixed mode: DXT + RGB + if( pic->type != src->type ) + { + gEngfuncs.Con_Printf( S_ERROR "GL_LoadTextureArray: mismatch image format for %s and %s\n", names[0], names[i] ); + break; + } + + // different mipcount + if( pic->numMips != src->numMips ) + { + gEngfuncs.Con_Printf( S_ERROR "GL_LoadTextureArray: mismatch mip count for %s and %s\n", names[0], names[i] ); + break; + } + + if( pic->encode != src->encode ) + { + gEngfuncs.Con_Printf( S_ERROR "GL_LoadTextureArray: mismatch custom encoding for %s and %s\n", names[0], names[i] ); + break; + } + + // but allow to rescale raw images + if( ImageRAW( pic->type ) && ImageRAW( src->type ) && ( pic->width != src->width || pic->height != src->height )) + gEngfuncs.Image_Process( &src, pic->width, pic->height, IMAGE_RESAMPLE, 0.0f ); + + if( pic->size != src->size ) + { + gEngfuncs.Con_Printf( S_ERROR "GL_LoadTextureArray: mismatch image size for %s and %s\n", names[0], names[i] ); + break; + } + } + else + { + // create new image + pic = Mem_Malloc( gEngfuncs.Image_GetPool(), sizeof( rgbdata_t )); + memcpy( pic, src, sizeof( rgbdata_t )); + + // expand pic buffer for all layers + pic->buffer = Mem_Malloc( gEngfuncs.Image_GetPool(), pic->size * numLayers ); + pic->depth = 0; + } + + mipsize = srcsize = dstsize = 0; + + for( j = 0; j < max( 1, pic->numMips ); j++ ) + { + int width = Q_max( 1, ( pic->width >> j )); + int height = Q_max( 1, ( pic->height >> j )); + mipsize = GL_CalcImageSize( pic->type, width, height, 1 ); + memcpy( pic->buffer + dstsize + mipsize * i, src->buffer + srcsize, mipsize ); + dstsize += mipsize * numLayers; + srcsize += mipsize; + } + + gEngfuncs.FS_FreeImage( src ); + + // increase layers + pic->depth++; + } + + // there were errors + if( !pic || ( pic->depth != numLayers )) + { + gEngfuncs.Con_Printf( S_ERROR "GL_LoadTextureArray: not all layers were loaded. Texture array is not created\n" ); + if( pic ) gEngfuncs.FS_FreeImage( pic ); + return 0; + } + + // it's multilayer image! + SetBits( pic->flags, IMAGE_MULTILAYER ); + pic->size *= numLayers; + + // allocate the new one + tex = GL_AllocTexture( name, flags ); + GL_ProcessImage( tex, pic ); + + if( !GL_UploadTexture( tex, pic )) + { + memset( tex, 0, sizeof( gl_texture_t )); + gEngfuncs.FS_FreeImage( pic ); // release source texture + return 0; + } + + GL_ApplyTextureParams( tex ); // update texture filter, wrap etc + gEngfuncs.FS_FreeImage( pic ); // release source texture + + // NOTE: always return texnum as index in array or engine will stop work !!! + return tex - gl_textures; +#endif +} + +/* +================ +GL_LoadTextureFromBuffer +================ +*/ +int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ) +{ + gl_texture_t *tex; + + if( !GL_CheckTexName( name )) + return 0; + + // see if already loaded + if(( tex = GL_TextureForName( name )) && !update ) + return (tex - gl_textures); + + // couldn't loading image + if( !pic ) return 0; + + if( update ) + { + if( tex == NULL ) + gEngfuncs.Host_Error( "GL_LoadTextureFromBuffer: couldn't find texture %s for update\n", name ); + SetBits( tex->flags, flags ); + } + else + { + // allocate the new one + tex = GL_AllocTexture( name, flags ); + } + + GL_ProcessImage( tex, pic ); + + if( !GL_UploadTexture( tex, pic )) + { + memset( tex, 0, sizeof( gl_texture_t )); + return 0; + } + + GL_ApplyTextureParams( tex ); // update texture filter, wrap etc + return (tex - gl_textures); +} + +/* +================ +GL_CreateTexture + +creates texture from buffer +================ +*/ +int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags ) +{ + qboolean update = FBitSet( flags, TF_UPDATE ) ? true : false; + int datasize = 1; + rgbdata_t r_empty; + + if( FBitSet( flags, TF_ARB_16BIT )) + datasize = 2; + else if( FBitSet( flags, TF_ARB_FLOAT )) + datasize = 4; + + ClearBits( flags, TF_UPDATE ); + memset( &r_empty, 0, sizeof( r_empty )); + r_empty.width = width; + r_empty.height = height; + r_empty.type = PF_RGBA_32; + r_empty.size = r_empty.width * r_empty.height * datasize * 4; + r_empty.buffer = (byte *)buffer; + + // clear invalid combinations + ClearBits( flags, TF_TEXTURE_3D ); + + // if image not luminance and not alphacontrast it will have color + if( !FBitSet( flags, TF_LUMINANCE ) && !FBitSet( flags, TF_ALPHACONTRAST )) + SetBits( r_empty.flags, IMAGE_HAS_COLOR ); + + if( FBitSet( flags, TF_HAS_ALPHA )) + SetBits( r_empty.flags, IMAGE_HAS_ALPHA ); +#if 0 + if( FBitSet( flags, TF_CUBEMAP )) + { + if( !GL_Support( GL_TEXTURE_CUBEMAP_EXT )) + return 0; + SetBits( r_empty.flags, IMAGE_CUBEMAP ); + r_empty.size *= 6; + } +#endif + return GL_LoadTextureFromBuffer( name, &r_empty, flags, update ); +} + +/* +================ +GL_CreateTextureArray + +creates texture array from buffer +================ +*/ +int GL_CreateTextureArray( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags ) +{ +#if 1 + return 0; +#else + rgbdata_t r_empty; + + memset( &r_empty, 0, sizeof( r_empty )); + r_empty.width = Q_max( width, 1 ); + r_empty.height = Q_max( height, 1 ); + r_empty.depth = Q_max( depth, 1 ); + r_empty.type = PF_RGBA_32; + r_empty.size = r_empty.width * r_empty.height * r_empty.depth * 4; + r_empty.buffer = (byte *)buffer; + + // clear invalid combinations + ClearBits( flags, TF_CUBEMAP|TF_SKYSIDE|TF_HAS_LUMA|TF_MAKELUMA|TF_ALPHACONTRAST ); + + // if image not luminance it will have color + if( !FBitSet( flags, TF_LUMINANCE )) + SetBits( r_empty.flags, IMAGE_HAS_COLOR ); + + if( FBitSet( flags, TF_HAS_ALPHA )) + SetBits( r_empty.flags, IMAGE_HAS_ALPHA ); + + if( FBitSet( flags, TF_TEXTURE_3D )) + { + if( !GL_Support( GL_TEXTURE_3D_EXT )) + return 0; + } + else + { + if( !GL_Support( GL_TEXTURE_ARRAY_EXT )) + return 0; + SetBits( r_empty.flags, IMAGE_MULTILAYER ); + } + + return GL_LoadTextureInternal( name, &r_empty, flags ); +#endif +} + +/* +================ +GL_FindTexture +================ +*/ +int GL_FindTexture( const char *name ) +{ + gl_texture_t *tex; + + if( !GL_CheckTexName( name )) + return 0; + + // see if already loaded + if(( tex = GL_TextureForName( name ))) + return (tex - gl_textures); + + return 0; +} + +/* +================ +GL_FreeTexture +================ +*/ +void GL_FreeTexture( GLenum texnum ) +{ + // number 0 it's already freed + if( texnum <= 0 ) return; + + GL_DeleteTexture( &gl_textures[texnum] ); +} + +/* +================ +GL_ProcessTexture +================ +*/ +void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ) +{ + gl_texture_t *image; + rgbdata_t *pic; + int flags = 0; + + if( texnum <= 0 || texnum >= MAX_TEXTURES ) + return; // missed image + image = &gl_textures[texnum]; + + // select mode + if( gamma != -1.0f ) + { + flags = IMAGE_LIGHTGAMMA; + } + else if( topColor != -1 && bottomColor != -1 ) + { + flags = IMAGE_REMAP; + } + else + { + gEngfuncs.Con_Printf( S_ERROR "GL_ProcessTexture: bad operation for %s\n", image->name ); + return; + } + + if( !image->original ) + { + gEngfuncs.Con_Printf( S_ERROR "GL_ProcessTexture: no input data for %s\n", image->name ); + return; + } + + if( ImageDXT( image->original->type )) + { + gEngfuncs.Con_Printf( S_ERROR "GL_ProcessTexture: can't process compressed texture %s\n", image->name ); + return; + } + + // all the operations makes over the image copy not an original + pic = gEngfuncs.FS_CopyImage( image->original ); + gEngfuncs.Image_Process( &pic, topColor, bottomColor, flags, 0.0f ); + + GL_UploadTexture( image, pic ); + GL_ApplyTextureParams( image ); // update texture filter, wrap etc + + gEngfuncs.FS_FreeImage( pic ); +} + +/* +================ +GL_TexMemory + +return size of all uploaded textures +================ +*/ +int GL_TexMemory( void ) +{ + int i, total = 0; + + for( i = 0; i < gl_numTextures; i++ ) + total += gl_textures[i].size; + + return total; +} + +/* +============================================================================== + +INTERNAL TEXTURES + +============================================================================== +*/ +/* +================== +GL_FakeImage +================== +*/ +static rgbdata_t *GL_FakeImage( int width, int height, int depth, int flags ) +{ + static byte data2D[1024]; // 16x16x4 + static rgbdata_t r_image; + + // also use this for bad textures, but without alpha + r_image.width = Q_max( 1, width ); + r_image.height = Q_max( 1, height ); + r_image.depth = Q_max( 1, depth ); + r_image.flags = flags; + r_image.type = PF_RGBA_32; + r_image.size = r_image.width * r_image.height * r_image.depth * 4; + r_image.buffer = (r_image.size > sizeof( data2D )) ? NULL : data2D; + r_image.palette = NULL; + r_image.numMips = 1; + r_image.encode = 0; + + if( FBitSet( r_image.flags, IMAGE_CUBEMAP )) + r_image.size *= 6; + memset( data2D, 0xFF, sizeof( data2D )); + + return &r_image; +} + +/* +================== +R_InitDlightTexture +================== +*/ +void R_InitDlightTexture( void ) +{ + rgbdata_t r_image; + + if( tr.dlightTexture != 0 ) + return; // already initialized + + memset( &r_image, 0, sizeof( r_image )); + r_image.width = BLOCK_SIZE; + r_image.height = BLOCK_SIZE; + r_image.flags = IMAGE_HAS_COLOR; + r_image.type = PF_RGBA_32; + r_image.size = r_image.width * r_image.height * 4; + + tr.dlightTexture = GL_LoadTextureInternal( "*dlight", &r_image, TF_NOMIPMAP|TF_CLAMP|TF_ATLAS_PAGE ); +} + +/* +================== +GL_CreateInternalTextures +================== +*/ +static void GL_CreateInternalTextures( void ) +{ + int dx2, dy, d; + int x, y; + rgbdata_t *pic; + + // emo-texture from quake1 + pic = GL_FakeImage( 16, 16, 1, IMAGE_HAS_COLOR ); + + for( y = 0; y < 16; y++ ) + { + for( x = 0; x < 16; x++ ) + { + if(( y < 8 ) ^ ( x < 8 )) + ((uint *)pic->buffer)[y*16+x] = 0xFFFF00FF; + else ((uint *)pic->buffer)[y*16+x] = 0xFF000000; + } + } + + tr.defaultTexture = GL_LoadTextureInternal( REF_DEFAULT_TEXTURE, pic, TF_COLORMAP ); + + // particle texture from quake1 + pic = GL_FakeImage( 16, 16, 1, IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA ); + + for( x = 0; x < 16; x++ ) + { + dx2 = x - 8; + dx2 = dx2 * dx2; + + for( y = 0; y < 16; y++ ) + { + dy = y - 8; + d = 255 - 35 * sqrt( dx2 + dy * dy ); + pic->buffer[( y * 16 + x ) * 4 + 3] = bound( 0, d, 255 ); + } + } + + tr.particleTexture = GL_LoadTextureInternal( REF_PARTICLE_TEXTURE, pic, TF_CLAMP ); + + // white texture + pic = GL_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR ); + for( x = 0; x < 16; x++ ) + ((uint *)pic->buffer)[x] = 0xFFFFFFFF; + tr.whiteTexture = GL_LoadTextureInternal( REF_WHITE_TEXTURE, pic, TF_COLORMAP ); + + // gray texture + pic = GL_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR ); + for( x = 0; x < 16; x++ ) + ((uint *)pic->buffer)[x] = 0xFF7F7F7F; + tr.grayTexture = GL_LoadTextureInternal( REF_GRAY_TEXTURE, pic, TF_COLORMAP ); + + // black texture + pic = GL_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR ); + for( x = 0; x < 16; x++ ) + ((uint *)pic->buffer)[x] = 0xFF000000; + tr.blackTexture = GL_LoadTextureInternal( REF_BLACK_TEXTURE, pic, TF_COLORMAP ); + + // cinematic dummy + pic = GL_FakeImage( 640, 100, 1, IMAGE_HAS_COLOR ); + tr.cinTexture = GL_LoadTextureInternal( "*cintexture", pic, TF_NOMIPMAP|TF_CLAMP ); +} + +/* +=============== +R_TextureList_f +=============== +*/ +void R_TextureList_f( void ) +{ + gl_texture_t *image; + int i, texCount, ram_bytes = 0, vram_bytes = 0; + + gEngfuncs.Con_Printf( "\n" ); + gEngfuncs.Con_Printf( " -id- -w- -h- -size- -fmt- -type- -encode- -wrap- -depth- -name--------\n" ); + + for( i = texCount = 0, image = gl_textures; i < gl_numTextures; i++, image++ ) + { + if( !image->dstTexture ) continue; + + if( image->dstPalette ) ram_bytes += PALETTE_SIZE; + + vram_bytes += ( FBitSet( image->guFlags, GUFLAGTEXINVRAM ) ) ? image->size : 0; + ram_bytes += ( FBitSet( image->guFlags, GUFLAGTEXINVRAM ) ) ? 0 : image->size; + texCount++; + + gEngfuncs.Con_Printf( "%4i: ", i ); + gEngfuncs.Con_Printf( "%4i %4i ", image->width, image->height ); + gEngfuncs.Con_Printf( "%12s ", Q_memprint( image->size )); + + switch( image->format ) + { + case GU_PSM_T4: + gEngfuncs.Con_Printf( "T4 " ); + break; + case GU_PSM_T8: + gEngfuncs.Con_Printf( "T8 " ); + break; + case GU_PSM_T16: + gEngfuncs.Con_Printf( "T16 " ); + break; + case GU_PSM_T32: + gEngfuncs.Con_Printf( "T32 " ); + break; + case GU_PSM_DXT1: + gEngfuncs.Con_Printf( "DXT1 " ); + break; + case GU_PSM_DXT3: + gEngfuncs.Con_Printf( "DXT3 " ); + break; + case GU_PSM_DXT5: + gEngfuncs.Con_Printf( "DXT5 " ); + break; + case GU_PSM_4444: + gEngfuncs.Con_Printf( "4444 " ); + break; + case GU_PSM_5551: + gEngfuncs.Con_Printf( "5551 " ); + break; + case GU_PSM_5650: + gEngfuncs.Con_Printf( "5650 " ); + break; + case GU_PSM_8888: + gEngfuncs.Con_Printf( "8888 " ); + break; + default: + gEngfuncs.Con_Printf( " ^1ERROR^7 " ); + break; + } + + if( image->flags & TF_NORMALMAP ) + gEngfuncs.Con_Printf( "normal " ); + else gEngfuncs.Con_Printf( "diffuse " ); + + switch( image->encode ) + { + case DXT_ENCODE_COLOR_YCoCg: + gEngfuncs.Con_Printf( "YCoCg " ); + break; + case DXT_ENCODE_NORMAL_AG_ORTHO: + gEngfuncs.Con_Printf( "ortho " ); + break; + case DXT_ENCODE_NORMAL_AG_STEREO: + gEngfuncs.Con_Printf( "stereo " ); + break; + case DXT_ENCODE_NORMAL_AG_PARABOLOID: + gEngfuncs.Con_Printf( "parabolic " ); + break; + case DXT_ENCODE_NORMAL_AG_QUARTIC: + gEngfuncs.Con_Printf( "quartic " ); + break; + case DXT_ENCODE_NORMAL_AG_AZIMUTHAL: + gEngfuncs.Con_Printf( "azimuthal " ); + break; + default: + gEngfuncs.Con_Printf( "default " ); + break; + } + + if( image->flags & TF_CLAMP ) + gEngfuncs.Con_Printf( "clamp " ); + else if( image->flags & TF_BORDER ) + gEngfuncs.Con_Printf( "border " ); + else gEngfuncs.Con_Printf( "repeat " ); + gEngfuncs.Con_Printf( " %d ", image->depth ); + gEngfuncs.Con_Printf( " %s\n", image->name ); + } + + gEngfuncs.Con_Printf( "---------------------------------------------------------\n" ); + gEngfuncs.Con_Printf( "%i total textures\n", texCount ); + gEngfuncs.Con_Printf( "%i max index\n", gl_numTextures ); + gEngfuncs.Con_Printf( "%s total ram memory used\n", Q_memprint( ram_bytes )); + gEngfuncs.Con_Printf( "%s total vram memory used\n", Q_memprint( vram_bytes )); + gEngfuncs.Con_Printf( "\n" ); +} + +/* +=============== +R_InitImages +=============== +*/ +void R_InitImages( void ) +{ + memset( gl_textures, 0, sizeof( gl_textures )); + memset( gl_texturesHashTable, 0, sizeof( gl_texturesHashTable )); + gl_numTextures = 0; + + // create unused 0-entry + Q_strncpy( gl_textures->name, "*unused*", sizeof( gl_textures->name )); + gl_textures->hashValue = COM_HashKey( gl_textures->name, TEXTURES_HASH_SIZE ); + gl_textures->nextHash = gl_texturesHashTable[gl_textures->hashValue]; + gl_texturesHashTable[gl_textures->hashValue] = gl_textures; + gl_numTextures = 1; + + // validate cvars + R_SetTextureParameters(); + GL_CreateInternalTextures(); + + gEngfuncs.Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" ); +} + +/* +=============== +R_ShutdownImages +=============== +*/ +void R_ShutdownImages( void ) +{ + gl_texture_t *tex; + int i; + + gEngfuncs.Cmd_RemoveCommand( "texturelist" ); + GL_CleanupAllTextureUnits(); + + for( i = 0, tex = gl_textures; i < gl_numTextures; i++, tex++ ) + GL_DeleteTexture( tex ); + + memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures )); + memset( gl_texturesHashTable, 0, sizeof( gl_texturesHashTable )); + memset( gl_textures, 0, sizeof( gl_textures )); + gl_numTextures = 0; +} diff --git a/ref_gu/gu_init.c b/ref_gu/gu_init.c new file mode 100644 index 00000000..d30b8481 --- /dev/null +++ b/ref_gu/gu_init.c @@ -0,0 +1,349 @@ + +#include "gu_local.h" + +cvar_t *gl_texture_lodfunc; +cvar_t *gl_texture_lodbias; +cvar_t *gl_texture_lodslope; + +cvar_t *gl_texture_nearest; +cvar_t *gl_lightmap_nearest; +cvar_t *gl_keeptjunctions; +cvar_t *gl_emboss_scale; +cvar_t *gl_detailscale; +cvar_t *gl_depthoffset; +cvar_t *gl_wireframe; +cvar_t *gl_finish; +cvar_t *gl_nosort; +cvar_t *gl_vsync; +cvar_t *gl_clear; +cvar_t *gl_test; +cvar_t *gl_stencilbits; +cvar_t *gl_subdivide_size; +cvar_t *r_speeds; +cvar_t *r_fullbright; +cvar_t *r_norefresh; +cvar_t *r_lighting_extended; +cvar_t *r_lighting_modulate; +cvar_t *r_lighting_ambient; +cvar_t *r_detailtextures; +cvar_t *r_drawentities; +cvar_t *r_adjust_fov; +cvar_t *r_decals; +cvar_t *r_novis; +cvar_t *r_nocull; +cvar_t *r_lockpvs; +cvar_t *r_lockfrustum; +cvar_t *r_traceglow; +cvar_t *r_dynamic; +cvar_t *r_lightmap; +cvar_t *r_showhull; +cvar_t *gl_round_down; +cvar_t *gl_showtextures; +cvar_t *cl_lightstyle_lerping; + +cvar_t *vid_brightness; +cvar_t *vid_gamma; +cvar_t *tracerred; +cvar_t *tracergreen; +cvar_t *tracerblue; +cvar_t *traceralpha; + +byte *r_temppool; + +gl_globals_t tr; +glconfig_t glConfig; +glstate_t glState; +glwstate_t glw_state; + +/* +============== +GL_GetProcAddress + +defined just for nanogl/glwes, so it don't link to SDL2 directly, nor use dlsym +============== +*/ +void GAME_EXPORT *GL_GetProcAddress( const char *name ) +{ + return gEngfuncs.GL_GetProcAddress( name ); +} + +/* +=============== +GL_SetDefaultState +=============== +*/ +static void GL_SetDefaultState( void ) +{ + memset( &glState, 0, sizeof( glState )); + + // init draw stack + tr.draw_list = &tr.draw_stack[0]; + tr.draw_stack_pos = 0; + + // init glState struct + glState.currentTexture = -1; + glState.texIdentityMatrix = true; + glState.fogColor = 0; + glState.fogDensity = 0; + glState.fogStart = 100.0f; + glState.fogEnd = 1000.0f; +} + +/* +=============== +GL_SetDefaults +=============== +*/ +static void GL_SetDefaults( void ) +{ +#if 1 +/* + sceGuFinish(); + sceGuSync(0,0); +*/ + sceGuClearColor( GU_COLOR( 0.5f, 0.5f, 0.5f, 1.0f ) ); + + sceGuDisable( GU_DEPTH_TEST ); + sceGuDisable( GU_CULL_FACE ); + //sceGuDisable( GU_SCISSOR_TEST ); + sceGuDepthFunc( GU_LEQUAL ); + sceGuColor( 0xffffffff ); + + if( glState.stencilEnabled ) + { + sceGuDisable( GU_STENCIL_TEST ); + /*pglStencilMask( ( GLuint ) ~0 );*/ + sceGuStencilFunc( GU_EQUAL, 0, ~0 ); + sceGuStencilOp( GU_KEEP, GU_INCR, GU_INCR ); + } + sceGuDepthOffset( 0 ); + + GL_CleanupAllTextureUnits(); + + sceGuDisable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + + sceGuAlphaFunc( GU_GREATER, DEFAULT_ALPHATEST, 0xff ); + sceGuEnable( GU_TEXTURE_2D ); + sceGuShadeModel( GU_SMOOTH ); + sceGuFrontFace( GU_CCW ); +#else + pglFinish(); + + pglClearColor( 0.5f, 0.5f, 0.5f, 1.0f ); + + pglDisable( GL_DEPTH_TEST ); + pglDisable( GL_CULL_FACE ); + pglDisable( GL_SCISSOR_TEST ); + pglDepthFunc( GL_LEQUAL ); + pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + + if( glState.stencilEnabled ) + { + pglDisable( GL_STENCIL_TEST ); + pglStencilMask( ( GLuint ) ~0 ); + pglStencilFunc( GL_EQUAL, 0, ~0 ); + pglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); + } + + pglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + pglPolygonOffset( -1.0f, -2.0f ); + + GL_CleanupAllTextureUnits(); + + pglDisable( GL_BLEND ); + pglDisable( GL_ALPHA_TEST ); + pglDisable( GL_POLYGON_OFFSET_FILL ); + pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST ); + pglEnable( GL_TEXTURE_2D ); + pglShadeModel( GL_SMOOTH ); + pglFrontFace( GL_CCW ); + + pglPointSize( 1.2f ); + pglLineWidth( 1.2f ); +#endif + GL_Cull( GL_NONE ); +} + + +/* +================= +R_RenderInfo_f +================= +*/ +void R_RenderInfo_f( void ) +{ + gEngfuncs.Con_Printf( "\n" ); + gEngfuncs.Con_Printf( "HARDWARE RENDER\n"); + gEngfuncs.Con_Printf( "MAX_TEXTURE_SIZE: %i\n", glConfig.max_texture_size ); + gEngfuncs.Con_Printf( "MODE: %ix%i\n", gpGlobals->width, gpGlobals->height ); + gEngfuncs.Con_Printf( "VERTICAL SYNC: %s\n", gl_vsync->value ? "enabled" : "disabled" ); + gEngfuncs.Con_Printf( "Color %d bits, Alpha %d bits, Depth %d bits, Stencil %d bits\n", glConfig.color_bits, + glConfig.alpha_bits, glConfig.depth_bits, glConfig.stencil_bits ); +} + +void GL_InitExtensions( void ) +{ +// int colorBits[3]; + + glConfig.color_bits = 5 + 5 + 5; + glConfig.alpha_bits = 1; + glConfig.stencil_bits = ( int )gl_stencilbits->value; + glState.stencilEnabled = true; + glConfig.max_texture_size = 256; + + // get our various GL strings + gEngfuncs.Con_Reportf( "^3Video^7: PSP HW\n" ); + + gEngfuncs.Cvar_Get( "gl_max_size", va( "%i", glConfig.max_texture_size ), 0, "opengl texture max dims" ); +/* + gEngfuncs.Image_AddCmdFlags( IL_DDS_HARDWARE ); +*/ + R_RenderInfo_f(); + + tr.framecount = tr.visframecount = 1; + glw_state.initialized = true; +} + +void GL_ClearExtensions( void ) +{ + // now all extensions are disabled + glw_state.initialized = false; +} + +//======================================================================= + +/* +================= +GL_InitCommands +================= +*/ +void GL_InitCommands( void ) +{ + r_speeds = gEngfuncs.Cvar_Get( "r_speeds", "0", FCVAR_ARCHIVE, "shows renderer speeds" ); + r_fullbright = gEngfuncs.Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); + r_norefresh = gEngfuncs.Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" ); + r_lighting_extended = gEngfuncs.Cvar_Get( "r_lighting_extended", "0", FCVAR_ARCHIVE, "allow to get lighting from world and bmodels" ); // disabled + r_lighting_modulate = gEngfuncs.Cvar_Get( "r_lighting_modulate", "0.6", FCVAR_ARCHIVE, "lightstyles modulate scale" ); + r_lighting_ambient = gEngfuncs.Cvar_Get( "r_lighting_ambient", "0.3", FCVAR_ARCHIVE, "map ambient lighting scale" ); + r_novis = gEngfuncs.Cvar_Get( "r_novis", "0", 0, "ignore vis information (perfomance test)" ); + r_nocull = gEngfuncs.Cvar_Get( "r_nocull", "0", 0, "ignore frustrum culling (perfomance test)" ); + r_detailtextures = gEngfuncs.Cvar_Get( "r_detailtextures", "0", FCVAR_ARCHIVE, "enable detail textures support, use '2' for autogenerate detail.txt" ); // disabled + r_lockpvs = gEngfuncs.Cvar_Get( "r_lockpvs", "0", FCVAR_CHEAT, "lockpvs area at current point (pvs test)" ); + r_lockfrustum = gEngfuncs.Cvar_Get( "r_lockfrustum", "0", FCVAR_CHEAT, "lock frustrum area at current point (cull test)" ); + r_dynamic = gEngfuncs.Cvar_Get( "r_dynamic", "1", FCVAR_ARCHIVE, "allow dynamic lighting (dlights, lightstyles)" ); + r_traceglow = gEngfuncs.Cvar_Get( "r_traceglow", "0", FCVAR_ARCHIVE, "cull flares behind models" ); // disabled + r_lightmap = gEngfuncs.Cvar_Get( "r_lightmap", "0", FCVAR_CHEAT, "lightmap debugging tool" ); + r_drawentities = gEngfuncs.Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT, "render entities" ); + r_decals = gEngfuncs.pfnGetCvarPointer( "r_decals", 0 ); + r_showhull = gEngfuncs.pfnGetCvarPointer( "r_showhull", 0 ); + + gl_texture_nearest = gEngfuncs.Cvar_Get( "gl_texture_nearest", "0", FCVAR_GLCONFIG, "disable texture filter" ); + gl_lightmap_nearest = gEngfuncs.Cvar_Get( "gl_lightmap_nearest", "0", FCVAR_GLCONFIG, "disable lightmap filter" ); + gl_vsync = gEngfuncs.pfnGetCvarPointer( "gl_vsync", 0 ); + gl_detailscale = gEngfuncs.Cvar_Get( "gl_detailscale", "4.0", FCVAR_GLCONFIG, "default scale applies while auto-generate list of detail textures" ); + gl_texture_lodfunc = gEngfuncs.Cvar_Get( "gl_texture_lodfunc", "2", FCVAR_GLCONFIG, "LOD func for mipmapped textures" ); + gl_texture_lodbias = gEngfuncs.Cvar_Get( "gl_texture_lodbias", "-5.0", FCVAR_GLCONFIG, "LOD bias for mipmapped textures (perfomance|quality)" ); + gl_texture_lodslope = gEngfuncs.Cvar_Get( "gl_texture_lodslope", "0.3", FCVAR_GLCONFIG, "LOD slope for mipmapped textures" ); + gl_keeptjunctions = gEngfuncs.Cvar_Get( "gl_keeptjunctions", "1", FCVAR_GLCONFIG, "removing tjuncs causes blinking pixels" ); + gl_emboss_scale = gEngfuncs.Cvar_Get( "gl_emboss_scale", "0", FCVAR_GLCONFIG|FCVAR_LATCH, "fake bumpmapping scale" ); + gl_showtextures = gEngfuncs.pfnGetCvarPointer( "r_showtextures", 0 ); + gl_finish = gEngfuncs.Cvar_Get( "gl_finish", "0", FCVAR_GLCONFIG, "use glFinish instead of glFlush" ); + gl_nosort = gEngfuncs.Cvar_Get( "gl_nosort", "1", FCVAR_GLCONFIG, "disable sorting of translucent surfaces" ); + gl_clear = gEngfuncs.pfnGetCvarPointer( "gl_clear", 0 ); + gl_test = gEngfuncs.Cvar_Get( "gl_test", "0", 0, "engine developer cvar for quick testing new features" ); + gl_wireframe = gEngfuncs.Cvar_Get( "gl_wireframe", "0", FCVAR_GLCONFIG|FCVAR_SPONLY, "show wireframe overlay" ); + gl_stencilbits = gEngfuncs.Cvar_Get( "gl_stencilbits", "8", FCVAR_GLCONFIG, "pixelformat stencil bits (0 - auto)" ); + gl_subdivide_size = gEngfuncs.Cvar_Get( "gl_subdivide_size", "256.0", FCVAR_GLCONFIG, "the division value for the sky brushes" ); + gl_round_down = gEngfuncs.Cvar_Get( "gl_round_down", "2", FCVAR_GLCONFIG, "round texture sizes to nearest POT value" ); + // these cvar not used by engine but some mods requires this + gl_depthoffset = gEngfuncs.Cvar_Get( "gl_depthoffset", "256.0", FCVAR_GLCONFIG, "depth offset for decals" ); + + // make sure gl_vsync is checked after vid_restart + SetBits( gl_vsync->flags, FCVAR_CHANGED ); + + vid_gamma = gEngfuncs.pfnGetCvarPointer( "gamma", 0 ); + vid_brightness = gEngfuncs.pfnGetCvarPointer( "brightness", 0 ); + + tracerred = gEngfuncs.Cvar_Get( "tracerred", "0.8", 0, "tracer red component weight ( 0 - 1.0 )" ); + tracergreen = gEngfuncs.Cvar_Get( "tracergreen", "0.8", 0, "tracer green component weight ( 0 - 1.0 )" ); + tracerblue = gEngfuncs.Cvar_Get( "tracerblue", "0.4", 0, "tracer blue component weight ( 0 - 1.0 )" ); + traceralpha = gEngfuncs.Cvar_Get( "traceralpha", "0.5", 0, "tracer alpha amount ( 0 - 1.0 )" ); + + cl_lightstyle_lerping = gEngfuncs.pfnGetCvarPointer( "cl_lightstyle_lerping", 0 ); + + gEngfuncs.Cmd_AddCommand( "r_info", R_RenderInfo_f, "display renderer info" ); + gEngfuncs.Cmd_AddCommand( "timerefresh", SCR_TimeRefresh_f, "turn quickly and print rendering statistcs" ); +} + +/* +================= +GL_RemoveCommands +================= +*/ +void GL_RemoveCommands( void ) +{ + gEngfuncs.Cmd_RemoveCommand( "r_info" ); +} + +/* +=============== +R_Init +=============== +*/ +qboolean R_Init( void ) +{ + if( glw_state.initialized ) + return true; + + GL_InitCommands(); + GL_InitRandomTable(); + + GL_SetDefaultState(); + + // create the window and set up the context + if( !gEngfuncs.R_Init_Video( REF_GL )) // request GL context + { + GL_RemoveCommands(); + gEngfuncs.R_Free_Video(); +// Why? Host_Error again??? +// gEngfuncs.Host_Error( "Can't initialize video subsystem\nProbably driver was not installed" ); + return false; + } + + r_temppool = Mem_AllocPool( "Render Zone" ); + + GL_SetDefaults(); + R_InitImages(); + R_SpriteInit(); + R_StudioInit(); + R_AliasInit(); + R_ClearDecals(); + R_ClearScene(); + + return true; +} + +/* +=============== +R_Shutdown +=============== +*/ +void R_Shutdown( void ) +{ + if( !glw_state.initialized ) + return; + + GL_RemoveCommands(); + R_ShutdownImages(); + + Mem_FreePool( &r_temppool ); + + // shut down OS specific OpenGL stuff like contexts, etc. + gEngfuncs.R_Free_Video(); +} + +void GL_SetupAttributes( int safegl ) +{ + +} diff --git a/ref_gu/gu_local.h b/ref_gu/gu_local.h new file mode 100644 index 00000000..6355af0b --- /dev/null +++ b/ref_gu/gu_local.h @@ -0,0 +1,809 @@ +/* +gu_local.h - renderer local declarations +Copyright (C) 2010 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef GL_LOCAL_H +#define GL_LOCAL_H +#if 1 +#include +#include +#include +#endif +#include "port.h" +#include "xash3d_types.h" +#include "cvardef.h" +#include "const.h" +#include "com_model.h" +#include "cl_entity.h" +#include "render_api.h" +#include "protocol.h" +#include "dlight.h" +#include "gu_frustum.h" +#include "ref_api.h" +#include "xash3d_mathlib.h" +#include "ref_params.h" +#include "enginefeatures.h" +#include "com_strings.h" +#include "pm_movevars.h" +//#include "cvar.h" +#include "gu_helper.h" +#include "gu_extension.h" +#include "wadfile.h" + +#ifndef offsetof +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif // offsetof + +#define ASSERT(x) if(!( x )) gEngfuncs.Host_Error( "assert failed at %s:%i\n", __FILE__, __LINE__ ) +#define Assert(x) if(!( x )) gEngfuncs.Host_Error( "assert failed at %s:%i\n", __FILE__, __LINE__ ) + +#include + +#define CVAR_DEFINE( cv, cvname, cvstr, cvflags, cvdesc ) cvar_t cv = { cvname, cvstr, cvflags, 0.0f, (void *)CVAR_SENTINEL, cvdesc } +#define CVAR_DEFINE_AUTO( cv, cvstr, cvflags, cvdesc ) cvar_t cv = { #cv, cvstr, cvflags, 0.0f, (void *)CVAR_SENTINEL, cvdesc } +#define CVAR_TO_BOOL( x ) ((x) && ((x)->value != 0.0f) ? true : false ) + +#define WORLD (gEngfuncs.GetWorld()) +#define WORLDMODEL (gEngfuncs.pfnGetModelByIndex( 1 )) +#define MOVEVARS (gEngfuncs.pfnGetMoveVars()) + +// make mod_ref.h? +#define LM_SAMPLE_SIZE 16 + + +extern byte *r_temppool; +#if 1 + +#define BLOCK_SIZE tr.block_size // lightmap blocksize +#define BLOCK_SIZE_DEFAULT 128 // for keep backward compatibility +#define BLOCK_SIZE_MAX 128 + +#define MAX_TEXTURES 1536 +#define MAX_DETAIL_TEXTURES 64 +#define MAX_LIGHTMAPS 64 +#define SUBDIVIDE_SIZE 64 +#define MAX_DECAL_SURFS 256 +#define MAX_DRAW_STACK 2 // normal view and menu view + +#else + +#define BLOCK_SIZE tr.block_size // lightmap blocksize +#define BLOCK_SIZE_DEFAULT 128 // for keep backward compatibility +#define BLOCK_SIZE_MAX 1024 + +#define MAX_TEXTURES 4096 +#define MAX_DETAIL_TEXTURES 256 +#define MAX_LIGHTMAPS 256 +#define SUBDIVIDE_SIZE 64 +#define MAX_DECAL_SURFS 4096 +#define MAX_DRAW_STACK 2 // normal view and menu view + +#endif + +#define SHADEDOT_QUANT 16 // precalculated dot products for quantized angles +#define SHADE_LAMBERT 1.495f + +#if 1 +#define DEFAULT_ALPHATEST 0x00 +#else +#define DEFAULT_ALPHATEST 0.0f +#endif + +// refparams +#define RP_NONE 0 +#define RP_ENVVIEW BIT( 0 ) // used for cubemapshot +#define RP_OLDVIEWLEAF BIT( 1 ) +#define RP_CLIPPLANE BIT( 2 ) + +#define RP_NONVIEWERREF (RP_ENVVIEW) +#define R_ModelOpaque( rm ) ( rm == kRenderNormal ) +#define R_StaticEntity( ent ) ( VectorIsNull( ent->origin ) && VectorIsNull( ent->angles )) +#define RP_LOCALCLIENT( e ) ((e) != NULL && (e)->index == ENGINE_GET_PARM( PARM_PLAYER_INDEX ) && e->player ) +#define RP_NORMALPASS() ( FBitSet( RI.params, RP_NONVIEWERREF ) == 0 ) + +#define CL_IsViewEntityLocalPlayer() ( ENGINE_GET_PARM( PARM_VIEWENT_INDEX ) == ENGINE_GET_PARM( PARM_PLAYER_INDEX ) ) + +#define CULL_VISIBLE 0 // not culled +#define CULL_BACKSIDE 1 // backside of transparent wall +#define CULL_FRUSTUM 2 // culled by frustum +#define CULL_VISFRAME 3 // culled by PVS +#define CULL_OTHER 4 // culled by other reason + +typedef struct gltexture_s +{ + + char name[128]; // game path, including extension (can be store image programs) + short srcWidth; // keep unscaled sizes + short srcHeight; + short width; // upload width\height + short height; + short depth; // texture depth or count of layers for 2D_ARRAY + byte numMips; // mipmap count + byte *dstTexture; // texture pointer + byte *dstPalette; + byte guFlags; + int format; // uploaded format + GLint encode; // using GLSL decoder + texFlags_t flags; + + rgba_t fogParams; // some water textures + // contain info about underwater fog + + rgbdata_t *original; // keep original image + + // debug info + size_t size; // upload size for debug targets + + // detail textures stuff + float xscale; + float yscale; +#if 0 + int servercount; +#endif + uint hashValue; + struct gltexture_s *nextHash; +} gl_texture_t; + +#if 1 +typedef struct +{ + float x, y, z; +}gu_vert_fv_t; + +typedef struct +{ + unsigned int c; + float x, y, z; +}gu_vert_fcv_t; + +typedef struct +{ + float u, v; + float x, y, z; +}gu_vert_ftv_t; + +typedef struct +{ + float u, v; + unsigned int c; + float x, y, z; +}gu_vert_ftcv_t; + +typedef struct +{ + float u, v; + unsigned int c; + float nx, ny, nz; + float x, y, z; +}gu_vert_ftcnv_t; + +typedef struct +{ + short x, y, z; +}gu_vert_hv_t; + +typedef struct +{ + short u, v; + short x, y, z; +}gu_vert_htv_t; + +typedef struct +{ + short u, v; + unsigned int c; + short x, y, z; +}gu_vert_htcv_t; +#endif + +typedef struct +{ + int params; // rendering parameters + + qboolean drawWorld; // ignore world for drawing PlayerModel + qboolean isSkyVisible; // sky is visible + qboolean onlyClientDraw; // disabled by client request + qboolean drawOrtho; // draw world as orthogonal projection + + float fov_x, fov_y; // current view fov + + cl_entity_t *currententity; + model_t *currentmodel; + cl_entity_t *currentbeam; // same as above but for beams + + int viewport[4]; + gl_frustum_t frustum; + + mleaf_t *viewleaf; + mleaf_t *oldviewleaf; + vec3_t pvsorigin; + vec3_t vieworg; // locked vieworigin + vec3_t viewangles; + vec3_t vforward; + vec3_t vright; + vec3_t vup; + + vec3_t cullorigin; + vec3_t cull_vforward; + vec3_t cull_vright; + vec3_t cull_vup; + + float farClip; + + qboolean fogCustom; + qboolean fogEnabled; + qboolean fogSkybox; + vec4_t fogColor; + float fogDensity; + float fogStart; + float fogEnd; + int cached_contents; // in water + int cached_waterlevel; // was in water + + float skyMins[2][6]; + float skyMaxs[2][6]; + + matrix4x4 objectMatrix; // currententity matrix + matrix4x4 worldviewMatrix; // modelview for world + matrix4x4 modelviewMatrix; // worldviewMatrix * objectMatrix + + matrix4x4 projectionMatrix; + matrix4x4 worldviewProjectionMatrix; // worldviewMatrix * projectionMatrix + byte visbytes[(MAX_MAP_LEAFS+7)/8];// actual PVS for current frame + + float viewplanedist; + mplane_t clipPlane; +} ref_instance_t; + +typedef struct +{ + cl_entity_t *solid_entities[MAX_VISIBLE_PACKET]; // opaque moving or alpha brushes + cl_entity_t *trans_entities[MAX_VISIBLE_PACKET]; // translucent brushes + cl_entity_t *beam_entities[MAX_VISIBLE_PACKET]; + uint num_solid_entities; + uint num_trans_entities; + uint num_beam_entities; +} draw_list_t; + +typedef struct +{ + int defaultTexture; // use for bad textures + int particleTexture; + int whiteTexture; + int grayTexture; + int blackTexture; + int solidskyTexture; // quake1 solid-sky layer + int alphaskyTexture; // quake1 alpha-sky layer + int lightmapTextures[MAX_LIGHTMAPS]; + int dlightTexture; // custom dlight texture + int skyboxTextures[6]; // skybox sides + int cinTexture; // cinematic texture + + int skytexturenum; // this not a gl_texturenum! +#if 0 + int skyboxbasenum; // start with 5800 +#endif + // entity lists + draw_list_t draw_stack[MAX_DRAW_STACK]; + int draw_stack_pos; + draw_list_t *draw_list; + + msurface_t *draw_decals[MAX_DECAL_SURFS]; + int num_draw_decals; + + // OpenGL matrix states + qboolean modelviewIdentity; + + int visframecount; // PVS frame + int dlightframecount; // dynamic light frame + int realframecount; // not including viewpasses + int framecount; + + qboolean ignore_lightgamma; + qboolean fCustomRendering; + qboolean fResetVis; + qboolean fFlipViewModel; + + byte visbytes[(MAX_MAP_LEAFS+7)/8]; // member custom PVS + int lightstylevalue[MAX_LIGHTSTYLES]; // value 0 - 65536 + int block_size; // lightmap blocksize + + double frametime; // special frametime for multipass rendering (will set to 0 on a nextview) + float blend; // global blend value + + // cull info + vec3_t modelorg; // relative to viewpoint + + qboolean fCustomSkybox; +} gl_globals_t; + +typedef struct +{ + uint c_world_polys; + uint c_studio_polys; + uint c_sprite_polys; + uint c_alias_polys; + uint c_world_leafs; + + uint c_view_beams_count; + uint c_active_tents_count; + uint c_alias_models_drawn; + uint c_studio_models_drawn; + uint c_sprite_models_drawn; + uint c_particle_count; + + uint c_client_ents; // entities that moved to client + double t_world_node; + double t_world_draw; +} ref_speeds_t; + +extern ref_speeds_t r_stats; +extern ref_instance_t RI; +extern gl_globals_t tr; + +extern float gldepthmin, gldepthmax; +#define r_numEntities (tr.draw_list->num_solid_entities + tr.draw_list->num_trans_entities) +#define r_numStatics (r_stats.c_client_ents) + +// +// gu_backend.c +// +void GL_BackendStartFrame( void ); +void GL_BackendEndFrame( void ); +void GL_CleanUpTextureUnits( int last ); +void GL_Bind( GLint tmu, GLenum texnum ); +void GL_MultiTexCoord2f( GLenum texture, GLfloat s, GLfloat t ); +void GL_SetTexCoordArrayMode( GLenum mode ); +void GL_LoadTexMatrix( const matrix4x4 m ); +void GL_LoadTexMatrixExt( const float *glmatrix ); +void GL_LoadMatrix( const matrix4x4 source ); +void GL_TexGen( GLenum coord, GLenum mode ); +void GL_SelectTexture( GLint texture ); +void GL_CleanupAllTextureUnits( void ); +void GL_LoadIdentityTexMatrix( void ); +void GL_DisableAllTexGens( void ); +void GL_SetRenderMode( int mode ); +void GL_TextureTarget( uint target ); +void GL_Cull( GLenum cull ); +void R_ShowTextures( void ); +void SCR_TimeRefresh_f( void ); + +// +// gu_beams.c +// +void CL_DrawBeams( int fTrans, BEAM *active_beams ); +qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly ); + +// +// gu_cull.c +// +int R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ); +qboolean R_CullBox( const vec3_t mins, const vec3_t maxs ); +qboolean R_CullSphere( const vec3_t centre, const float radius ); +int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags ); + +// +// gu_decals.c +// +void DrawSurfaceDecals( msurface_t *fa, qboolean single, qboolean reverse ); +float *R_DecalSetupVerts( decal_t *pDecal, msurface_t *surf, int texture, int *outCount ); +void DrawSingleDecal( decal_t *pDecal, msurface_t *fa ); +void R_EntityRemoveDecals( model_t *mod ); +void DrawDecalsBatch( void ); +void R_ClearDecals( void ); + +// +// gu_draw.c +// +void R_Set2DMode( qboolean enable ); +void R_DrawTileClear( int texnum, int x, int y, int w, int h ); +void R_UploadStretchRaw( int texture, int cols, int rows, int width, int height, const byte *data ); + +// +// gu_drawhulls.c +// +void R_DrawWorldHull( void ); +void R_DrawModelHull( void ); + +// +// gu_image.c +// +void R_SetTextureParameters( void ); +gl_texture_t *R_GetTexture( GLenum texnum ); +#define GL_LoadTextureInternal( name, pic, flags ) GL_LoadTextureFromBuffer( name, pic, flags, false ) +#define GL_UpdateTextureInternal( name, pic, flags ) GL_LoadTextureFromBuffer( name, pic, flags, true ) +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ); +int GL_LoadTextureArray( const char **names, int flags ); +int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ); +byte *GL_ResampleTexture( const byte *source, int in_w, int in_h, int out_w, int out_h, qboolean isNormalMap ); +int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags ); +int GL_CreateTextureArray( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags ); +void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ); +qboolean GL_UpdateDlightTexture( int texnum, int xoff, int yoff, int width, int height, const void *buffer ); +void GL_UpdateTexSize( int texnum, int width, int height, int depth ); +void GL_ApplyTextureParams( gl_texture_t *tex ); +int GL_FindTexture( const char *name ); +void GL_FreeTexture( GLenum texnum ); +const char *GL_Target( GLenum target ); +void R_InitDlightTexture( void ); +void R_TextureList_f( void ); +void R_InitImages( void ); +void R_ShutdownImages( void ); +int GL_TexMemory( void ); + +// +// gu_rlight.c +// +void CL_RunLightStyles( void ); +void R_PushDlights( void ); +void R_AnimateLight( void ); +void R_GetLightSpot( vec3_t lightspot ); +void R_MarkLights( dlight_t *light, int bit, mnode_t *node ); +colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lightspot, vec3_t lightvec ); +int R_CountSurfaceDlights( msurface_t *surf ); +colorVec R_LightPoint( const vec3_t p0 ); +int R_CountDlights( void ); + +// +// gu_rmain.c +// +void R_ClearScene( void ); +void R_LoadIdentity( void ); +void R_RenderScene( void ); +void R_DrawCubemapView( const vec3_t origin, const vec3_t angles, int size ); +void R_SetupRefParams( const struct ref_viewpass_s *rvp ); +void R_TranslateForEntity( cl_entity_t *e ); +void R_RotateForEntity( cl_entity_t *e ); +void R_SetupGL( qboolean set_gl_state ); +void R_AllowFog( qboolean allowed ); +void R_SetupFrustum( void ); +void R_FindViewLeaf( void ); +void R_CheckGamma( void ); +void R_PushScene( void ); +void R_PopScene( void ); +void R_DrawFog( void ); +int CL_FxBlend( cl_entity_t *e ); + +// +// gu_rmath.c +// +void Matrix4x4_ToArrayFloatGL( const matrix4x4 in, float out[16] ); +void Matrix4x4_FromArrayFloatGL( matrix4x4 out, const float in[16] ); +void Matrix4x4_Concat( matrix4x4 out, const matrix4x4 in1, const matrix4x4 in2 ); +void Matrix4x4_ConcatTranslate( matrix4x4 out, float x, float y, float z ); +void Matrix4x4_ConcatRotate( matrix4x4 out, float angle, float x, float y, float z ); +void Matrix4x4_ConcatScale( matrix4x4 out, float x ); +void Matrix4x4_ConcatScale3( matrix4x4 out, float x, float y, float z ); +void Matrix4x4_CreateTranslate( matrix4x4 out, float x, float y, float z ); +void Matrix4x4_CreateRotate( matrix4x4 out, float angle, float x, float y, float z ); +void Matrix4x4_CreateScale( matrix4x4 out, float x ); +void Matrix4x4_CreateScale3( matrix4x4 out, float x, float y, float z ); +void Matrix4x4_CreateProjection(matrix4x4 out, float xMax, float xMin, float yMax, float yMin, float zNear, float zFar); +void Matrix4x4_CreateOrtho(matrix4x4 m, float xLeft, float xRight, float yBottom, float yTop, float zNear, float zFar); +void Matrix4x4_CreateModelview( matrix4x4 out ); + +// +// gu_rmisc.c +// +void R_ClearStaticEntities( void ); + +// +// gu_rsurf.c +// +void R_MarkLeaves( void ); +void R_DrawWorld( void ); +void R_DrawWaterSurfaces( void ); +void R_DrawBrushModel( cl_entity_t *e ); +void GL_SubdivideSurface( msurface_t *fa ); +void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa ); +void DrawGLPoly( glpoly_t *p, float xScale, float yScale ); +texture_t *R_TextureAnimation( msurface_t *s ); +void GL_SetupFogColorForSurfaces( void ); +void R_DrawAlphaTextureChains( void ); +void GL_RebuildLightmaps( void ); +void GL_InitRandomTable( void ); +void GL_BuildLightmaps( void ); +void GL_ResetFogColor( void ); +void R_GenerateVBO( void ); +void R_ClearVBO( void ); +void R_AddDecalVBO( decal_t *pdecal, msurface_t *surf ); + +// +// gu_rpart.c +// +void CL_DrawParticlesExternal( const ref_viewpass_t *rvp, qboolean trans_pass, float frametime ); +void CL_DrawParticles( double frametime, particle_t *cl_active_particles, float partsize ); +void CL_DrawTracers( double frametime, particle_t *cl_active_tracers ); + + +// +// gu_sprite.c +// +void R_SpriteInit( void ); +void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, uint texFlags ); +mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw ); +void R_DrawSpriteModel( cl_entity_t *e ); + +// +// gu_studio.c +// +void R_StudioInit( void ); +void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ); +void R_StudioLerpMovement( cl_entity_t *e, double time, vec3_t origin, vec3_t angles ); +float CL_GetSequenceDuration( cl_entity_t *ent, int sequence ); +struct mstudiotex_s *R_StudioGetTexture( cl_entity_t *e ); +float CL_GetStudioEstimatedFrame( cl_entity_t *ent ); +int R_GetEntityRenderMode( cl_entity_t *ent ); +void R_DrawStudioModel( cl_entity_t *e ); +player_info_t *pfnPlayerInfo( int index ); +void R_GatherPlayerLight( void ); +float R_StudioEstimateFrame( cl_entity_t *e, mstudioseqdesc_t *pseqdesc ); +void R_StudioLerpMovement( cl_entity_t *e, double time, vec3_t origin, vec3_t angles ); +void R_StudioResetPlayerModels( void ); +void CL_InitStudioAPI( void ); +void Mod_StudioLoadTextures( model_t *mod, void *data ); +void Mod_StudioUnloadTextures( void *data ); + +// +// gu_alias.c +// +void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded ); +void R_DrawAliasModel( cl_entity_t *e ); +void R_AliasInit( void ); + +// +// gu_warp.c +// +void R_InitSkyClouds( mip_t *mt, struct texture_s *tx, qboolean custom_palette ); +void R_AddSkyBoxSurface( msurface_t *fa ); +void R_ClearSkyBox( void ); +void R_DrawSkyBox( void ); +void R_DrawClouds( void ); +void EmitWaterPolys( msurface_t *warp, qboolean reverse ); + +// +// gu_vgui.c +// +void VGUI_DrawInit( void ); +void VGUI_DrawShutdown( void ); +void VGUI_SetupDrawingText( int *pColor ); +void VGUI_SetupDrawingRect( int *pColor ); +void VGUI_SetupDrawingImage( int *pColor ); +void VGUI_BindTexture( int id ); +void VGUI_EnableTexture( qboolean enable ); +void VGUI_CreateTexture( int id, int width, int height ); +void VGUI_UploadTexture( int id, const char *buffer, int width, int height ); +void VGUI_UploadTextureBlock( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight ); +void VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr ); +void VGUI_GetTextureSizes( int *width, int *height ); +int VGUI_GenerateTexture( void ); + +//#include "vid_common.h" + +// +// renderer exports +// +qboolean R_Init( void ); +void R_Shutdown( void ); +void GL_SetupAttributes( int safegl ); +void GL_OnContextCreated( void ); +void GL_InitExtensions( void ); +void GL_ClearExtensions( void ); +void VID_CheckChanges( void ); +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ); +void GL_FreeImage( const char *name ); +qboolean VID_ScreenShot( const char *filename, int shot_type ); +qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot ); +void R_BeginFrame( qboolean clearScene ); +void R_RenderFrame( const struct ref_viewpass_s *vp ); +void R_EndFrame( void ); +void R_ClearScene( void ); +void R_GetTextureParms( int *w, int *h, int texnum ); +void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int curFrame, const struct model_s *pSprite ); +void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, const byte *data, qboolean dirty ); +void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, int texnum ); +qboolean R_SpeedsMessage( char *out, size_t size ); +void R_SetupSky( const char *skyboxname ); +qboolean R_CullBox( const vec3_t mins, const vec3_t maxs ); +int R_WorldToScreen( const vec3_t point, vec3_t screen ); +void R_ScreenToWorld( const vec3_t screen, vec3_t point ); +qboolean R_AddEntity( struct cl_entity_s *pRefEntity, int entityType ); +void Mod_LoadMapSprite( struct model_s *mod, const void *buffer, size_t size, qboolean *loaded ); +void Mod_SpriteUnloadTextures( void *data ); +void Mod_UnloadAliasModel( struct model_s *mod ); +void Mod_AliasUnloadTextures( void *data ); +void GL_SetRenderMode( int mode ); +void R_RunViewmodelEvents( void ); +void R_DrawViewModel( void ); +int R_GetSpriteTexture( const struct model_s *m_pSpriteModel, int frame ); +void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, float scale ); +void R_RemoveEfrags( struct cl_entity_s *ent ); +void R_AddEfrags( struct cl_entity_s *ent ); +void R_DecalRemoveAll( int texture ); +int R_CreateDecalList( decallist_t *pList ); +void R_ClearAllDecals( void ); +byte *Mod_GetCurrentVis( void ); +void Mod_SetOrthoBounds( const float *mins, const float *maxs ); +void R_NewMap( void ); +void CL_AddCustomBeam( cl_entity_t *pEnvBeam ); + +// +// gu_opengl.c +// + +// +// gu_triapi.c +// +void TriRenderMode( int mode ); +void TriBegin( int mode ); +void TriEnd( void ); +void TriTexCoord2f( float u, float v ); +void TriVertex3fv( const float *v ); +void TriVertex3f( float x, float y, float z ); +void _TriColor4f( float r, float g, float b, float a ); +void _TriColor4ub( byte r, byte g, byte b, byte a ); +void TriColor4f( float r, float g, float b, float a ); +void TriColor4ub( byte r, byte g, byte b, byte a ); +void TriBrightness( float brightness ); +int TriWorldToScreen( const float *world, float *screen ); +int TriSpriteTexture( model_t *pSpriteModel, int frame ); +void TriFog( float flFogColor[3], float flStart, float flEnd, int bOn ); +void TriGetMatrix( const int pname, float *matrix ); +void TriFogParams( float flDensity, int iFogSkybox ); +void TriCullFace( TRICULLSTYLE mode ); +#if 1 +/* Needs for psp */ +unsigned int getTriBrightness( float brightness ); +#endif + +// +// gu_clipping.c +// +#define CLIPPING_DEBUGGING 0 +void GU_ClipBeginFrame( void ); +void GU_ClipBeginBrush( void ); +void GU_ClipEndBrush( void ); +void GU_ClipLoadFrustum( const mplane_t *plane ); // Experimental +qboolean GU_ClipIsRequired( const gu_vert_t* v, size_t vc ); +void GU_Clip( gu_vert_t *uv, size_t uvc, gu_vert_t **cv, size_t* cvc ); + +/* +======================================================================= + + GL STATE MACHINE + +======================================================================= +*/ +typedef struct +{ + int max_texture_size; + int color_bits; + int alpha_bits; + int depth_bits; + int stencil_bits; + + gl_context_type_t context; + gles_wrapper_t wrapper; + + qboolean softwareGammaUpdate; + qboolean fCustomRenderer; + int prev_width; + int prev_height; +} glconfig_t; + +typedef struct +{ + + int width, height; + int activeTMU; + GLint currentTexture; + GLboolean texIdentityMatrix; + GLint isFogEnabled; + + int faceCull; + + qboolean stencilEnabled; + qboolean in2DMode; + + uint fogColor; + float fogDensity; + float fogStart; + float fogEnd; +} glstate_t; + + +typedef struct +{ + qboolean initialized; // OpenGL subsystem started + qboolean extended; // extended context allows to GL_Debug +} glwstate_t; + +extern glconfig_t glConfig; +extern glstate_t glState; +// move to engine +extern glwstate_t glw_state; +extern ref_api_t gEngfuncs; +extern ref_globals_t *gpGlobals; + +#define ENGINE_GET_PARM_ (*gEngfuncs.EngineGetParm) +#define ENGINE_GET_PARM( parm ) ENGINE_GET_PARM_( ( parm ), 0 ) + +// +// renderer cvars +// +extern cvar_t *gl_texture_lodfunc; +extern cvar_t *gl_texture_lodbias; +extern cvar_t *gl_texture_lodslope; +extern cvar_t *gl_texture_nearest; +extern cvar_t *gl_lightmap_nearest; +extern cvar_t *gl_keeptjunctions; +extern cvar_t *gl_emboss_scale; +extern cvar_t *gl_round_down; +extern cvar_t *gl_detailscale; +extern cvar_t *gl_wireframe; +extern cvar_t *gl_depthoffset; +extern cvar_t *gl_finish; +extern cvar_t *gl_nosort; +extern cvar_t *gl_clear; +extern cvar_t *gl_test; // cvar to testify new effects +extern cvar_t *gl_stencilbits; +extern cvar_t *gl_subdivide_size; + +extern cvar_t *r_speeds; +extern cvar_t *r_fullbright; +extern cvar_t *r_norefresh; +extern cvar_t *r_lighting_extended; +extern cvar_t *r_lighting_modulate; +extern cvar_t *r_lighting_ambient; +extern cvar_t *r_studio_lambert; +extern cvar_t *r_detailtextures; +extern cvar_t *r_drawentities; +extern cvar_t *r_decals; +extern cvar_t *r_novis; +extern cvar_t *r_nocull; +extern cvar_t *r_lockpvs; +extern cvar_t *r_lockfrustum; +extern cvar_t *r_traceglow; +extern cvar_t *r_dynamic; +extern cvar_t *r_lightmap; + +extern cvar_t *vid_brightness; +extern cvar_t *vid_gamma; + +// +// engine shared convars +// +extern cvar_t *gl_showtextures; +extern cvar_t *tracerred; +extern cvar_t *tracergreen; +extern cvar_t *tracerblue; +extern cvar_t *traceralpha; +extern cvar_t *cl_lightstyle_lerping; +extern cvar_t *r_showhull; + +// +// engine callbacks +// +#include "crtlib.h" + +#define Mem_Malloc( pool, size ) gEngfuncs._Mem_Alloc( pool, size, false, __FILE__, __LINE__ ) +#define Mem_Calloc( pool, size ) gEngfuncs._Mem_Alloc( pool, size, true, __FILE__, __LINE__ ) +#define Mem_Realloc( pool, ptr, size ) gEngfuncs._Mem_Realloc( pool, ptr, size, true, __FILE__, __LINE__ ) +#define Mem_Free( mem ) gEngfuncs._Mem_Free( mem, __FILE__, __LINE__ ) +#define Mem_AllocPool( name ) gEngfuncs._Mem_AllocPool( name, __FILE__, __LINE__ ) +#define Mem_FreePool( pool ) gEngfuncs._Mem_FreePool( pool, __FILE__, __LINE__ ) +#define Mem_EmptyPool( pool ) gEngfuncs._Mem_EmptyPool( pool, __FILE__, __LINE__ ) + +#endif // GL_LOCAL_H diff --git a/ref_gu/gu_rlight.c b/ref_gu/gu_rlight.c new file mode 100644 index 00000000..b212c328 --- /dev/null +++ b/ref_gu/gu_rlight.c @@ -0,0 +1,496 @@ +/* +gl_rlight.c - dynamic and static lights +Copyright (C) 2010 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "pm_local.h" +#include "studio.h" +#include "xash3d_mathlib.h" +#include "ref_params.h" + +/* +============================================================================= + +DYNAMIC LIGHTS + +============================================================================= +*/ +/* +================== +CL_RunLightStyles + +================== +*/ +void CL_RunLightStyles( void ) +{ + int i, k, flight, clight; + float l, lerpfrac, backlerp; + float frametime = (gpGlobals->time - gpGlobals->oldtime); + float scale; + lightstyle_t *ls; + + if( !WORLDMODEL ) return; + + scale = r_lighting_modulate->value; + + // light animations + // 'm' is normal light, 'a' is no light, 'z' is double bright + for( i = 0; i < MAX_LIGHTSTYLES; i++ ) + { + ls = gEngfuncs.GetLightStyle( i ); + if( !WORLDMODEL->lightdata ) + { + tr.lightstylevalue[i] = 256 * 256; + continue; + } + + if( !ENGINE_GET_PARM( PARAM_GAMEPAUSED ) && frametime <= 0.1f ) + ls->time += frametime; // evaluate local time + + flight = (int)Q_floor( ls->time * 10 ); + clight = (int)Q_ceil( ls->time * 10 ); + lerpfrac = ( ls->time * 10 ) - flight; + backlerp = 1.0f - lerpfrac; + + if( !ls->length ) + { + tr.lightstylevalue[i] = 256 * scale; + continue; + } + else if( ls->length == 1 ) + { + // single length style so don't bother interpolating + tr.lightstylevalue[i] = ls->map[0] * 22 * scale; + continue; + } + else if( !ls->interp || !CVAR_TO_BOOL( cl_lightstyle_lerping )) + { + tr.lightstylevalue[i] = ls->map[flight%ls->length] * 22 * scale; + continue; + } + + // interpolate animating light + // frame just gone + k = ls->map[flight % ls->length]; + l = (float)( k * 22.0f ) * backlerp; + + // upcoming frame + k = ls->map[clight % ls->length]; + l += (float)( k * 22.0f ) * lerpfrac; + + tr.lightstylevalue[i] = (int)l * scale; + } +} + +/* +============= +R_MarkLights +============= +*/ +void R_MarkLights( dlight_t *light, int bit, mnode_t *node ) +{ + float dist; + msurface_t *surf; + int i; + + if( !node || node->contents < 0 ) + return; + + dist = PlaneDiff( light->origin, node->plane ); + + if( dist > light->radius ) + { + R_MarkLights( light, bit, node->children[0] ); + return; + } + if( dist < -light->radius ) + { + R_MarkLights( light, bit, node->children[1] ); + return; + } + + // mark the polygons + surf = RI.currentmodel->surfaces + node->firstsurface; + + for( i = 0; i < node->numsurfaces; i++, surf++ ) + { + if( !BoundsAndSphereIntersect( surf->info->mins, surf->info->maxs, light->origin, light->radius )) + continue; // no intersection + + if( surf->dlightframe != tr.dlightframecount ) + { + surf->dlightbits = 0; + surf->dlightframe = tr.dlightframecount; + } + surf->dlightbits |= bit; + } + + R_MarkLights( light, bit, node->children[0] ); + R_MarkLights( light, bit, node->children[1] ); +} + +/* +============= +R_PushDlights +============= +*/ +void R_PushDlights( void ) +{ + dlight_t *l; + int i; + + tr.dlightframecount = tr.framecount; + + RI.currententity = gEngfuncs.GetEntityByIndex( 0 ); + RI.currentmodel = RI.currententity->model; + + for( i = 0; i < MAX_DLIGHTS; i++, l++ ) + { + l = gEngfuncs.GetDynamicLight( i ); + + if( l->die < gpGlobals->time || !l->radius ) + continue; + + if( GL_FrustumCullSphere( &RI.frustum, l->origin, l->radius, 15 )) + continue; + + R_MarkLights( l, 1<nodes ); + } +} + +/* +============= +R_CountDlights +============= +*/ +int R_CountDlights( void ) +{ + dlight_t *l; + int i, numDlights = 0; + + for( i = 0; i < MAX_DLIGHTS; i++ ) + { + l = gEngfuncs.GetDynamicLight( i ); + + if( l->die < gpGlobals->time || !l->radius ) + continue; + + numDlights++; + } + + return numDlights; +} + +/* +============= +R_CountSurfaceDlights +============= +*/ +int R_CountSurfaceDlights( msurface_t *surf ) +{ + int i, numDlights = 0; + + for( i = 0; i < MAX_DLIGHTS; i++ ) + { + if(!( surf->dlightbits & BIT( i ))) + continue; // not lit by this light + + numDlights++; + } + + return numDlights; +} + +/* +======================================================================= + + AMBIENT LIGHTING + +======================================================================= +*/ +static vec3_t g_trace_lightspot; +static vec3_t g_trace_lightvec; +static float g_trace_fraction; + +/* +================= +R_RecursiveLightPoint +================= +*/ +static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, float p2f, colorVec *cv, const vec3_t start, const vec3_t end ) +{ + float front, back, frac, midf; + int i, map, side, size; + float ds, dt, s, t; + int sample_size; + color24 *lm, *dm; + mextrasurf_t *info; + msurface_t *surf; + mtexinfo_t *tex; + matrix3x4 tbn; + vec3_t mid; + + // didn't hit anything + if( !node || node->contents < 0 ) + { + cv->r = cv->g = cv->b = cv->a = 0; + return false; + } + + // calculate mid point + front = PlaneDiff( start, node->plane ); + back = PlaneDiff( end, node->plane ); + + side = front < 0; + if(( back < 0 ) == side ) + return R_RecursiveLightPoint( model, node->children[side], p1f, p2f, cv, start, end ); + + frac = front / ( front - back ); + + VectorLerp( start, frac, end, mid ); + midf = p1f + ( p2f - p1f ) * frac; + + // co down front side + if( R_RecursiveLightPoint( model, node->children[side], p1f, midf, cv, start, mid )) + return true; // hit something + + if(( back < 0 ) == side ) + { + cv->r = cv->g = cv->b = cv->a = 0; + return false; // didn't hit anything + } + + // check for impact on this node + surf = model->surfaces + node->firstsurface; + VectorCopy( mid, g_trace_lightspot ); + + for( i = 0; i < node->numsurfaces; i++, surf++ ) + { + int smax, tmax; + + tex = surf->texinfo; + info = surf->info; + + if( FBitSet( surf->flags, SURF_DRAWTILED )) + continue; // no lightmaps + + s = DotProduct( mid, info->lmvecs[0] ) + info->lmvecs[0][3]; + t = DotProduct( mid, info->lmvecs[1] ) + info->lmvecs[1][3]; + + if( s < info->lightmapmins[0] || t < info->lightmapmins[1] ) + continue; + + ds = s - info->lightmapmins[0]; + dt = t - info->lightmapmins[1]; + + if ( ds > info->lightextents[0] || dt > info->lightextents[1] ) + continue; + + cv->r = cv->g = cv->b = cv->a = 0; + + if( !surf->samples ) + return true; + + sample_size = gEngfuncs.Mod_SampleSizeForFace( surf ); + smax = (info->lightextents[0] / sample_size) + 1; + tmax = (info->lightextents[1] / sample_size) + 1; + ds /= sample_size; + dt /= sample_size; + + lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds ); + g_trace_fraction = midf; + size = smax * tmax; + dm = NULL; + + if( surf->info->deluxemap ) + { + vec3_t faceNormal; + + if( FBitSet( surf->flags, SURF_PLANEBACK )) + VectorNegate( surf->plane->normal, faceNormal ); + else VectorCopy( surf->plane->normal, faceNormal ); + + // compute face TBN +#if 1 + Vector4Set( tbn[0], surf->info->lmvecs[0][0], surf->info->lmvecs[0][1], surf->info->lmvecs[0][2], 0.0f ); + Vector4Set( tbn[1], -surf->info->lmvecs[1][0], -surf->info->lmvecs[1][1], -surf->info->lmvecs[1][2], 0.0f ); + Vector4Set( tbn[2], faceNormal[0], faceNormal[1], faceNormal[2], 0.0f ); +#else + Vector4Set( tbn[0], surf->info->lmvecs[0][0], -surf->info->lmvecs[1][0], faceNormal[0], 0.0f ); + Vector4Set( tbn[1], surf->info->lmvecs[0][1], -surf->info->lmvecs[1][1], faceNormal[1], 0.0f ); + Vector4Set( tbn[2], surf->info->lmvecs[0][2], -surf->info->lmvecs[1][2], faceNormal[2], 0.0f ); +#endif + VectorNormalize( tbn[0] ); + VectorNormalize( tbn[1] ); + VectorNormalize( tbn[2] ); + dm = surf->info->deluxemap + Q_rint( dt ) * smax + Q_rint( ds ); + } + + for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ ) + { + uint scale = tr.lightstylevalue[surf->styles[map]]; + + if( tr.ignore_lightgamma ) + { + cv->r += lm->r * scale; + cv->g += lm->g * scale; + cv->b += lm->b * scale; + } + else + { + cv->r += gEngfuncs.LightToTexGamma( lm->r ) * scale; + cv->g += gEngfuncs.LightToTexGamma( lm->g ) * scale; + cv->b += gEngfuncs.LightToTexGamma( lm->b ) * scale; + } + lm += size; // skip to next lightmap + + if( dm != NULL ) + { + vec3_t srcNormal, lightNormal; + float f = (1.0f / 128.0f); + + VectorSet( srcNormal, ((float)dm->r - 128.0f) * f, ((float)dm->g - 128.0f) * f, ((float)dm->b - 128.0f) * f ); + Matrix3x4_VectorIRotate( tbn, srcNormal, lightNormal ); // turn to world space + VectorScale( lightNormal, (float)scale * -1.0f, lightNormal ); // turn direction from light + VectorAdd( g_trace_lightvec, lightNormal, g_trace_lightvec ); + dm += size; // skip to next deluxmap + } + } + + return true; + } + + // go down back side + return R_RecursiveLightPoint( model, node->children[!side], midf, p2f, cv, mid, end ); +} + +/* +================= +R_LightVec + +check bspmodels to get light from +================= +*/ +colorVec R_LightVecInternal( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) +{ + float last_fraction; + int i, maxEnts = 1; + colorVec light, cv; + + if( lspot ) VectorClear( lspot ); + if( lvec ) VectorClear( lvec ); + + if( WORLDMODEL && WORLDMODEL->lightdata ) + { + light.r = light.g = light.b = light.a = 0; + last_fraction = 1.0f; + + // get light from bmodels too + if( CVAR_TO_BOOL( r_lighting_extended )) + maxEnts = MAX_PHYSENTS; + + // check all the bsp-models + for( i = 0; i < maxEnts; i++ ) + { + physent_t *pe = gEngfuncs.EV_GetPhysent( i ); + vec3_t offset, start_l, end_l; + mnode_t *pnodes; + matrix4x4 matrix; + + if( !pe ) + break; + + if( !pe->model || pe->model->type != mod_brush ) + continue; // skip non-bsp models + + pnodes = &pe->model->nodes[pe->model->hulls[0].firstclipnode]; + VectorSubtract( pe->model->hulls[0].clip_mins, vec3_origin, offset ); + VectorAdd( offset, pe->origin, offset ); + VectorSubtract( start, offset, start_l ); + VectorSubtract( end, offset, end_l ); + + // rotate start and end into the models frame of reference + if( !VectorIsNull( pe->angles )) + { + Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); + Matrix4x4_VectorITransform( matrix, start, start_l ); + Matrix4x4_VectorITransform( matrix, end, end_l ); + } + + VectorClear( g_trace_lightspot ); + VectorClear( g_trace_lightvec ); + g_trace_fraction = 1.0f; + + if( !R_RecursiveLightPoint( pe->model, pnodes, 0.0f, 1.0f, &cv, start_l, end_l )) + continue; // didn't hit anything + + if( g_trace_fraction < last_fraction ) + { + if( lspot ) VectorCopy( g_trace_lightspot, lspot ); + if( lvec ) VectorNormalize2( g_trace_lightvec, lvec ); + light.r = Q_min(( cv.r >> 7 ), 255 ); + light.g = Q_min(( cv.g >> 7 ), 255 ); + light.b = Q_min(( cv.b >> 7 ), 255 ); + last_fraction = g_trace_fraction; + + if(( light.r + light.g + light.b ) != 0 ) + break; // we get light now + } + } + } + else + { + light.r = light.g = light.b = 255; + light.a = 0; + } + + return light; +} + +/* +================= +R_LightVec + +check bspmodels to get light from +================= +*/ +colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) +{ + colorVec light = R_LightVecInternal( start, end, lspot, lvec ); + + if( CVAR_TO_BOOL( r_lighting_extended ) && lspot != NULL && lvec != NULL ) + { + // trying to get light from ceiling (but ignore gradient analyze) + if(( light.r + light.g + light.b ) == 0 ) + return R_LightVecInternal( end, start, lspot, lvec ); + } + + return light; +} + +/* +================= +R_LightPoint + +light from floor +================= +*/ +colorVec R_LightPoint( const vec3_t p0 ) +{ + vec3_t p1; + + VectorSet( p1, p0[0], p0[1], p0[2] - 2048.0f ); + + return R_LightVec( p0, p1, NULL, NULL ); +} diff --git a/ref_gu/gu_rmain.c b/ref_gu/gu_rmain.c new file mode 100644 index 00000000..ca2bdd3b --- /dev/null +++ b/ref_gu/gu_rmain.c @@ -0,0 +1,1361 @@ +/* +gu_rmain.c - renderer main loop +Copyright (C) 2010 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "xash3d_mathlib.h" +#include "library.h" +#include "beamdef.h" +#include "particledef.h" +#include "entity_types.h" + +#define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA ) +float gldepthmin, gldepthmax; + +ref_instance_t RI; + +static int R_RankForRenderMode( int rendermode ) +{ + switch( rendermode ) + { + case kRenderTransTexture: + return 1; // draw second + case kRenderTransAdd: + return 2; // draw third + case kRenderGlow: + return 3; // must be last! + } + return 0; +} + +void R_AllowFog( qboolean allowed ) +{ +#if 1 + if( allowed ) + { + if( glState.isFogEnabled ) + sceGuEnable( GU_FOG ); + } + else + { + if( glState.isFogEnabled ) + sceGuDisable( GU_FOG ); + } +#else + if( allowed ) + { + if( glState.isFogEnabled ) + pglEnable( GL_FOG ); + } + else + { + if( glState.isFogEnabled ) + pglDisable( GL_FOG ); + } +#endif +} + +/* +=============== +R_OpaqueEntity + +Opaque entity can be brush or studio model but sprite +=============== +*/ +static qboolean R_OpaqueEntity( cl_entity_t *ent ) +{ + if( R_GetEntityRenderMode( ent ) == kRenderNormal ) + return true; + return false; +} + +/* +=============== +R_TransEntityCompare + +Sorting translucent entities by rendermode then by distance +=============== +*/ +static int R_TransEntityCompare( const void *a, const void *b ) +{ + cl_entity_t *ent1, *ent2; + vec3_t vecLen, org; + float dist1, dist2; + int rendermode1; + int rendermode2; + + ent1 = *(cl_entity_t **)a; + ent2 = *(cl_entity_t **)b; + rendermode1 = R_GetEntityRenderMode( ent1 ); + rendermode2 = R_GetEntityRenderMode( ent2 ); + + // sort by distance + if( ent1->model->type != mod_brush || rendermode1 != kRenderTransAlpha ) + { + VectorAverage( ent1->model->mins, ent1->model->maxs, org ); + VectorAdd( ent1->origin, org, org ); + VectorSubtract( RI.vieworg, org, vecLen ); + dist1 = DotProduct( vecLen, vecLen ); + } + else dist1 = 1000000000; + + if( ent2->model->type != mod_brush || rendermode2 != kRenderTransAlpha ) + { + VectorAverage( ent2->model->mins, ent2->model->maxs, org ); + VectorAdd( ent2->origin, org, org ); + VectorSubtract( RI.vieworg, org, vecLen ); + dist2 = DotProduct( vecLen, vecLen ); + } + else dist2 = 1000000000; + + if( dist1 > dist2 ) + return -1; + if( dist1 < dist2 ) + return 1; + + // then sort by rendermode + if( R_RankForRenderMode( rendermode1 ) > R_RankForRenderMode( rendermode2 )) + return 1; + if( R_RankForRenderMode( rendermode1 ) < R_RankForRenderMode( rendermode2 )) + return -1; + + return 0; +} + +/* +=============== +R_WorldToScreen + +Convert a given point from world into screen space +Returns true if we behind to screen +=============== +*/ +int R_WorldToScreen( const vec3_t point, vec3_t screen ) +{ + matrix4x4 worldToScreen; + qboolean behind; + float w; + + if( !point || !screen ) + return true; + + Matrix4x4_Copy( worldToScreen, RI.worldviewProjectionMatrix ); + screen[0] = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3]; + screen[1] = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3]; + w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3]; + screen[2] = 0.0f; // just so we have something valid here + + if( w < 0.001f ) + { + screen[0] *= 100000; + screen[1] *= 100000; + behind = true; + } + else + { + float invw = 1.0f / w; + screen[0] *= invw; + screen[1] *= invw; + behind = false; + } + + return behind; +} + +/* +=============== +R_ScreenToWorld + +Convert a given point from screen into world space +=============== +*/ +void R_ScreenToWorld( const vec3_t screen, vec3_t point ) +{ + matrix4x4 screenToWorld; + float w; + + if( !point || !screen ) + return; + + Matrix4x4_Invert_Full( screenToWorld, RI.worldviewProjectionMatrix ); + + point[0] = screen[0] * screenToWorld[0][0] + screen[1] * screenToWorld[0][1] + screen[2] * screenToWorld[0][2] + screenToWorld[0][3]; + point[1] = screen[0] * screenToWorld[1][0] + screen[1] * screenToWorld[1][1] + screen[2] * screenToWorld[1][2] + screenToWorld[1][3]; + point[2] = screen[0] * screenToWorld[2][0] + screen[1] * screenToWorld[2][1] + screen[2] * screenToWorld[2][2] + screenToWorld[2][3]; + w = screen[0] * screenToWorld[3][0] + screen[1] * screenToWorld[3][1] + screen[2] * screenToWorld[3][2] + screenToWorld[3][3]; + if( w != 0.0f ) VectorScale( point, ( 1.0f / w ), point ); +} + +/* +=============== +R_PushScene +=============== +*/ +void R_PushScene( void ) +{ + if( ++tr.draw_stack_pos >= MAX_DRAW_STACK ) + gEngfuncs.Host_Error( "draw stack overflow\n" ); + + tr.draw_list = &tr.draw_stack[tr.draw_stack_pos]; +} + +/* +=============== +R_PopScene +=============== +*/ +void R_PopScene( void ) +{ + if( --tr.draw_stack_pos < 0 ) + gEngfuncs.Host_Error( "draw stack underflow\n" ); + tr.draw_list = &tr.draw_stack[tr.draw_stack_pos]; +} + +/* +=============== +R_ClearScene +=============== +*/ +void R_ClearScene( void ) +{ + tr.draw_list->num_solid_entities = 0; + tr.draw_list->num_trans_entities = 0; + tr.draw_list->num_beam_entities = 0; + + // clear the scene befor start new frame + if( gEngfuncs.drawFuncs->R_ClearScene != NULL ) + gEngfuncs.drawFuncs->R_ClearScene(); + +} + +/* +=============== +R_AddEntity +=============== +*/ +qboolean R_AddEntity( struct cl_entity_s *clent, int type ) +{ + if( !r_drawentities->value ) + return false; // not allow to drawing + + if( !clent || !clent->model ) + return false; // if set to invisible, skip + + if( FBitSet( clent->curstate.effects, EF_NODRAW )) + return false; // done + + if( !R_ModelOpaque( clent->curstate.rendermode ) && CL_FxBlend( clent ) <= 0 ) + return true; // invisible + + switch( type ) + { + case ET_FRAGMENTED: + r_stats.c_client_ents++; + break; + case ET_TEMPENTITY: + r_stats.c_active_tents_count++; + break; + default: break; + } + + if( R_OpaqueEntity( clent )) + { + // opaque + if( tr.draw_list->num_solid_entities >= MAX_VISIBLE_PACKET ) + return false; + + tr.draw_list->solid_entities[tr.draw_list->num_solid_entities] = clent; + tr.draw_list->num_solid_entities++; + } + else + { + // translucent + if( tr.draw_list->num_trans_entities >= MAX_VISIBLE_PACKET ) + return false; + + tr.draw_list->trans_entities[tr.draw_list->num_trans_entities] = clent; + tr.draw_list->num_trans_entities++; + } + + return true; +} + +/* +============= +R_Clear +============= +*/ +static void R_Clear( int bitMask ) +{ + int bits; + + if( ENGINE_GET_PARM( PARM_DEV_OVERVIEW )) + sceGuClearColor( 0xff00ff00 ); // green background (Valve rules) + else sceGuClearColor( 0xff7f7f7f ); + + bits = GU_DEPTH_BUFFER_BIT; + + if( glState.stencilEnabled ) + bits |= GU_STENCIL_BUFFER_BIT; + + bits &= bitMask; + + sceGuClear( bits ); + + // change ordering for overview + if( RI.drawOrtho ) + { + gldepthmin = 65535.0f; + gldepthmax = 0.0f; + } + else + { + gldepthmin = 0.0f; + gldepthmax = 65535.0f; + } + + sceGuDepthFunc( GU_LEQUAL ); + sceGuDepthRange( ( int )gldepthmin, ( int )gldepthmax ); +} + +//============================================================================= +/* +=============== +R_GetFarClip +=============== +*/ +static float R_GetFarClip( void ) +{ + if( WORLDMODEL && RI.drawWorld ) + return MOVEVARS->zmax * 1.73f; + return 2048.0f; +} + +/* +=============== +R_SetupFrustum +=============== +*/ +void R_SetupFrustum( void ) +{ + const ref_overview_t *ov = gEngfuncs.GetOverviewParms(); + + if( RP_NORMALPASS() && ( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 )) + { + RI.fov_x = atan( tan( DEG2RAD( RI.fov_x ) / 2 ) * ( 0.97f + sin( gpGlobals->time * 1.5f ) * 0.03f )) * 2 / (M_PI_F / 180.0f); + RI.fov_y = atan( tan( DEG2RAD( RI.fov_y ) / 2 ) * ( 1.03f - sin( gpGlobals->time * 1.5f ) * 0.03f )) * 2 / (M_PI_F / 180.0f); + } + + // build the transformation matrix for the given view angles + AngleVectors( RI.viewangles, RI.vforward, RI.vright, RI.vup ); + + if( !r_lockfrustum->value ) + { + VectorCopy( RI.vieworg, RI.cullorigin ); + VectorCopy( RI.vforward, RI.cull_vforward ); + VectorCopy( RI.vright, RI.cull_vright ); + VectorCopy( RI.vup, RI.cull_vup ); + } + + if( RI.drawOrtho ) + GL_FrustumInitOrtho( &RI.frustum, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar ); + else GL_FrustumInitProj( &RI.frustum, 0.0f, R_GetFarClip(), RI.fov_x, RI.fov_y ); // NOTE: we ignore nearplane here (mirrors only) +} + +/* +============= +R_SetupProjectionMatrix +============= +*/ +static void R_SetupProjectionMatrix( matrix4x4 m ) +{ + GLfloat xMin, xMax, yMin, yMax, zNear, zFar; + + if( RI.drawOrtho ) + { + const ref_overview_t *ov = gEngfuncs.GetOverviewParms(); + Matrix4x4_CreateOrtho( m, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar ); + return; + } + + RI.farClip = R_GetFarClip(); + + zNear = 4.0f; + zFar = max( 256.0f, RI.farClip ); + + yMax = zNear * tan( RI.fov_y * M_PI_F / 360.0f ); + yMin = -yMax; + + xMax = zNear * tan( RI.fov_x * M_PI_F / 360.0f ); + xMin = -xMax; + + Matrix4x4_CreateProjection( m, xMax, xMin, yMax, yMin, zNear, zFar ); +} + +/* +============= +R_SetupModelviewMatrix +============= +*/ +static void R_SetupModelviewMatrix( matrix4x4 m ) +{ + Matrix4x4_CreateModelview( m ); + Matrix4x4_ConcatRotate( m, -RI.viewangles[2], 1, 0, 0 ); + Matrix4x4_ConcatRotate( m, -RI.viewangles[0], 0, 1, 0 ); + Matrix4x4_ConcatRotate( m, -RI.viewangles[1], 0, 0, 1 ); + Matrix4x4_ConcatTranslate( m, -RI.vieworg[0], -RI.vieworg[1], -RI.vieworg[2] ); +} + +/* +============= +R_LoadIdentity +============= +*/ +void R_LoadIdentity( void ) +{ + if( tr.modelviewIdentity ) + return; + + Matrix4x4_LoadIdentity( RI.objectMatrix ); +#if 1 + sceGumMatrixMode( GU_MODEL ); + GL_LoadMatrix( RI.objectMatrix ); + sceGumUpdateMatrix(); +#else + Matrix4x4_Copy( RI.modelviewMatrix, RI.worldviewMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.modelviewMatrix ); +#endif + tr.modelviewIdentity = true; +} + +/* +============= +R_RotateForEntity +============= +*/ +void R_RotateForEntity( cl_entity_t *e ) +{ + float scale = 1.0f; + + if( e == gEngfuncs.GetEntityByIndex( 0 ) ) + { + R_LoadIdentity(); + return; + } + + if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) + scale = e->curstate.scale; + + Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, scale ); +#if 1 + sceGumMatrixMode( GU_MODEL ); + GL_LoadMatrix( RI.objectMatrix ); + sceGumUpdateMatrix(); +#else + Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.modelviewMatrix ); +#endif + tr.modelviewIdentity = false; +} + +/* +============= +R_TranslateForEntity +============= +*/ +void R_TranslateForEntity( cl_entity_t *e ) +{ + float scale = 1.0f; + + if( e == gEngfuncs.GetEntityByIndex( 0 ) ) + { + printf("e == gEngfuncs.GetEntityByIndex\n"); + R_LoadIdentity(); + return; + } + + if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) + scale = e->curstate.scale; + + Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, scale ); +#if 1 + sceGumMatrixMode( GU_MODEL ); + GL_LoadMatrix( RI.objectMatrix ); + sceGumUpdateMatrix(); +#else + Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.modelviewMatrix ); +#endif + tr.modelviewIdentity = false; +} + +/* +=============== +R_FindViewLeaf +=============== +*/ +void R_FindViewLeaf( void ) +{ + RI.oldviewleaf = RI.viewleaf; + RI.viewleaf = gEngfuncs.Mod_PointInLeaf( RI.pvsorigin, WORLDMODEL->nodes ); +} + +/* +=============== +R_SetupFrame +=============== +*/ +static void R_SetupFrame( void ) +{ + // setup viewplane dist + RI.viewplanedist = DotProduct( RI.vieworg, RI.vforward ); + + // NOTE: this request is the fps-killer on some NVidia drivers +#if 1 + glState.isFogEnabled = sceGuGetStatus( GU_FOG ); +#else + glState.isFogEnabled = pglIsEnabled( GL_FOG ); +#endif + + if( !gl_nosort->value ) + { + // sort translucents entities by rendermode and distance + qsort( tr.draw_list->trans_entities, tr.draw_list->num_trans_entities, sizeof( cl_entity_t* ), R_TransEntityCompare ); + } + + // current viewleaf + if( RI.drawWorld ) + { + RI.isSkyVisible = false; // unknown at this moment + R_FindViewLeaf(); + } +} + +/* +============= +R_SetupGL +============= +*/ +void R_SetupGL( qboolean set_gl_state ) +{ + R_SetupModelviewMatrix( RI.worldviewMatrix ); + R_SetupProjectionMatrix( RI.projectionMatrix ); + + Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix ); + + if( !set_gl_state ) return; + + if( RP_NORMALPASS( )) + { + int x, x2, y, y2; + + // set up viewport (main, playersetup) + x = floor( RI.viewport[0] * gpGlobals->width / gpGlobals->width ); + x2 = ceil(( RI.viewport[0] + RI.viewport[2] ) * gpGlobals->width / gpGlobals->width ); + y = floor( gpGlobals->height - RI.viewport[1] * gpGlobals->height / gpGlobals->height ); + y2 = ceil( gpGlobals->height - ( RI.viewport[1] + RI.viewport[3] ) * gpGlobals->height / gpGlobals->height ); +#if 1 + sceGuViewport( 2048, 2048, x2 - x, y - y2 ); /* psp center viewport 2048x2048 PSPFIXME*/ + sceGuScissor(0, 0, gpGlobals->width, gpGlobals->height); + /*sceGuViewport( + 2048, + 2048 + (gpGlobals->height / 2) - y2 - ((y - y2) / 2), + x2 - x, y - y2); + sceGuScissor(x, gpGlobals->height - y2 - (y - y2), x + (x2 - x), gpGlobals->height - y2);*/ +#else + pglViewport( x, y2, x2 - x, y - y2 ); +#endif + } + else + { + // envpass, mirrorpass +#if 1 + sceGuViewport( 2048, 2048, RI.viewport[2], RI.viewport[3] ); /* psp center viewport 2048x2048 PSPFIXME */ + sceGuScissor(0, 0, gpGlobals->width, gpGlobals->height); +#else + pglViewport( RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3] ); +#endif + } +#if 1 + sceGumMatrixMode( GU_PROJECTION ); + GL_LoadMatrix( RI.projectionMatrix ); + + sceGumMatrixMode( GU_VIEW ); + GL_LoadMatrix( RI.worldviewMatrix ); + + sceGumUpdateMatrix(); //Apply a matrix transformation + sceGumMatrixMode( GU_MODEL ); + +/* not supported + if( FBitSet( RI.params, RP_CLIPPLANE )) + { + GLdouble clip[4]; + mplane_t *p = &RI.clipPlane; + + clip[0] = p->normal[0]; + clip[1] = p->normal[1]; + clip[2] = p->normal[2]; + clip[3] = -p->dist; + + pglClipPlane( GL_CLIP_PLANE0, clip ); + pglEnable( GL_CLIP_PLANE0 ); + } +*/ + GL_Cull( GL_FRONT ); + + sceGuDisable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuColor( GUCOLOR4F( 1.0f, 1.0f, 1.0f, 1.0f ) ); + + GU_ClipBeginFrame(); +#else + pglMatrixMode( GL_PROJECTION ); + GL_LoadMatrix( RI.projectionMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.worldviewMatrix ); + + if( FBitSet( RI.params, RP_CLIPPLANE )) + { + GLdouble clip[4]; + mplane_t *p = &RI.clipPlane; + + clip[0] = p->normal[0]; + clip[1] = p->normal[1]; + clip[2] = p->normal[2]; + clip[3] = -p->dist; + + pglClipPlane( GL_CLIP_PLANE0, clip ); + pglEnable( GL_CLIP_PLANE0 ); + } + + GL_Cull( GL_FRONT ); + + pglDisable( GL_BLEND ); + pglDisable( GL_ALPHA_TEST ); + pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); +#endif +} + +/* +============= +R_EndGL +============= +*/ +static void R_EndGL( void ) +{ +#if 0 + if( RI.params & RP_CLIPPLANE ) + pglDisable( GL_CLIP_PLANE0 ); +#endif +} + +/* +============= +R_RecursiveFindWaterTexture + +using to find source waterleaf with +watertexture to grab fog values from it +============= +*/ +static gl_texture_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down ) +{ + gl_texture_t *tex = NULL; + + // assure the initial node is not null + // we could check it here, but we would rather check it + // outside the call to get rid of one additional recursion level + Assert( node != NULL ); + + // ignore solid nodes + if( node->contents == CONTENTS_SOLID ) + return NULL; + + if( node->contents < 0 ) + { + mleaf_t *pleaf; + msurface_t **mark; + int i, c; + + // ignore non-liquid leaves + if( node->contents != CONTENTS_WATER && node->contents != CONTENTS_LAVA && node->contents != CONTENTS_SLIME ) + return NULL; + + // find texture + pleaf = (mleaf_t *)node; + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + for( i = 0; i < c; i++, mark++ ) + { + if( (*mark)->flags & SURF_DRAWTURB && (*mark)->texinfo && (*mark)->texinfo->texture ) + return R_GetTexture( (*mark)->texinfo->texture->gl_texturenum ); + } + + // texture not found + return NULL; + } + + // this is a regular node + // traverse children + if( node->children[0] && ( node->children[0] != ignore )) + { + tex = R_RecursiveFindWaterTexture( node->children[0], node, true ); + if( tex ) return tex; + } + + if( node->children[1] && ( node->children[1] != ignore )) + { + tex = R_RecursiveFindWaterTexture( node->children[1], node, true ); + if( tex ) return tex; + } + + // for down recursion, return immediately + if( down ) return NULL; + + // texture not found, step up if any + if( node->parent ) + return R_RecursiveFindWaterTexture( node->parent, node, false ); + + // top-level node, bail out + return NULL; +} + +/* +============= +R_CheckFog + +check for underwater fog +Using backward recursion to find waterline leaf +from underwater leaf (idea: XaeroX) +============= +*/ +static void R_CheckFog( void ) +{ + cl_entity_t *ent; + gl_texture_t *tex; + int i, cnt, count; + + // quake global fog + if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) + { + if( !MOVEVARS->fog_settings ) + { +#if 1 + if( sceGuGetStatus( GU_FOG ) ) + sceGuDisable( GU_FOG ); +#else + if( pglIsEnabled( GL_FOG ) ) + pglDisable( GL_FOG ); +#endif + RI.fogEnabled = false; + return; + } + + // quake-style global fog + RI.fogColor[0] = ((MOVEVARS->fog_settings & 0xFF000000) >> 24) / 255.0f; + RI.fogColor[1] = ((MOVEVARS->fog_settings & 0xFF0000) >> 16) / 255.0f; + RI.fogColor[2] = ((MOVEVARS->fog_settings & 0xFF00) >> 8) / 255.0f; + RI.fogDensity = ((MOVEVARS->fog_settings & 0xFF) / 255.0f) * 0.01f; + RI.fogStart = RI.fogEnd = 0.0f; + RI.fogColor[3] = 1.0f; + RI.fogCustom = false; + RI.fogEnabled = true; + RI.fogSkybox = true; + return; + } + + RI.fogEnabled = false; + + if( RI.onlyClientDraw || ENGINE_GET_PARM( PARM_WATER_LEVEL ) < 3 || !RI.drawWorld || !RI.viewleaf ) + { + if( RI.cached_waterlevel == 3 ) + { + // in some cases waterlevel jumps from 3 to 1. Catch it + RI.cached_waterlevel = ENGINE_GET_PARM( PARM_WATER_LEVEL ); + RI.cached_contents = CONTENTS_EMPTY; + if( !RI.fogCustom ) + { + glState.isFogEnabled = false; +#if 1 + sceGuDisable( GU_FOG ); +#else + pglDisable( GL_FOG ); +#endif + } + } + return; + } + + ent = gEngfuncs.CL_GetWaterEntity( RI.vieworg ); + if( ent && ent->model && ent->model->type == mod_brush && ent->curstate.skin < 0 ) + cnt = ent->curstate.skin; + else cnt = RI.viewleaf->contents; + + RI.cached_waterlevel = ENGINE_GET_PARM( PARM_WATER_LEVEL ); + + if( !IsLiquidContents( RI.cached_contents ) && IsLiquidContents( cnt )) + { + tex = NULL; + + // check for water texture + if( ent && ent->model && ent->model->type == mod_brush ) + { + msurface_t *surf; + + count = ent->model->nummodelsurfaces; + + for( i = 0, surf = &ent->model->surfaces[ent->model->firstmodelsurface]; i < count; i++, surf++ ) + { + if( surf->flags & SURF_DRAWTURB && surf->texinfo && surf->texinfo->texture ) + { + tex = R_GetTexture( surf->texinfo->texture->gl_texturenum ); + RI.cached_contents = ent->curstate.skin; + break; + } + } + } + else + { + tex = R_RecursiveFindWaterTexture( RI.viewleaf->parent, NULL, false ); + if( tex ) RI.cached_contents = RI.viewleaf->contents; + } + + if( !tex ) return; // no valid fogs + + // copy fog params + RI.fogColor[0] = tex->fogParams[0] / 255.0f; + RI.fogColor[1] = tex->fogParams[1] / 255.0f; + RI.fogColor[2] = tex->fogParams[2] / 255.0f; + RI.fogDensity = tex->fogParams[3] * 0.000025f; + RI.fogStart = RI.fogEnd = 0.0f; + RI.fogColor[3] = 1.0f; + RI.fogCustom = false; + RI.fogEnabled = true; + RI.fogSkybox = true; + } + else + { + RI.fogCustom = false; + RI.fogEnabled = true; + RI.fogSkybox = true; + } +} + +/* +============= +R_CheckGLFog + +special condition for Spirit 1.9 +that used direct calls of glFog-functions +============= +*/ +static void R_CheckGLFog( void ) +{ +#ifdef HACKS_RELATED_HLMODS + if(( !RI.fogEnabled && !RI.fogCustom ) && pglIsEnabled( GL_FOG ) && VectorIsNull( RI.fogColor )) + { + // fill the fog color from GL-state machine +#if 0 + pglGetFloatv( GL_FOG_COLOR, RI.fogColor ); +#endif + RI.fogSkybox = true; + } +#endif +} + +/* +============= +R_DrawFog + +============= +*/ +void R_DrawFog( void ) +{ + if( !RI.fogEnabled ) return; +#if 1 + sceGuEnable( GU_FOG ); + glState.fogDensity = RI.fogDensity; + glState.fogColor = GUCOLOR4F( RI.fogColor[0], RI.fogColor[1], RI.fogColor[2], glState.fogDensity ); + sceGuFog( glState.fogStart, glState.fogEnd, glState.fogColor ); +#else + pglEnable( GL_FOG ); + if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) + pglFogi( GL_FOG_MODE, GL_EXP2 ); + else pglFogi( GL_FOG_MODE, GL_EXP ); + pglFogf( GL_FOG_DENSITY, RI.fogDensity ); + pglFogfv( GL_FOG_COLOR, RI.fogColor ); + pglHint( GL_FOG_HINT, GL_NICEST ); +#endif +} + +/* +============= +R_DrawEntitiesOnList +============= +*/ +void R_DrawEntitiesOnList( void ) +{ + int i; + + tr.blend = 1.0f; + + // first draw solid entities + for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ ) + { + RI.currententity = tr.draw_list->solid_entities[i]; + RI.currentmodel = RI.currententity->model; + + Assert( RI.currententity != NULL ); + Assert( RI.currentmodel != NULL ); + + switch( RI.currentmodel->type ) + { + case mod_brush: + R_DrawBrushModel( RI.currententity ); + break; + case mod_alias: + R_DrawAliasModel( RI.currententity ); + break; + case mod_studio: + R_DrawStudioModel( RI.currententity ); + break; + default: + break; + } + } + + // quake-specific feature + R_DrawAlphaTextureChains(); + + // draw sprites seperately, because of alpha blending + for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ ) + { + RI.currententity = tr.draw_list->solid_entities[i]; + RI.currentmodel = RI.currententity->model; + + Assert( RI.currententity != NULL ); + Assert( RI.currentmodel != NULL ); + + switch( RI.currentmodel->type ) + { + case mod_sprite: + R_DrawSpriteModel( RI.currententity ); + break; + } + } + + if( !RI.onlyClientDraw ) + { + gEngfuncs.CL_DrawEFX( tr.frametime, false ); + } + + if( RI.drawWorld ) + gEngfuncs.pfnDrawNormalTriangles(); + + // then draw translucent entities + for( i = 0; i < tr.draw_list->num_trans_entities && !RI.onlyClientDraw; i++ ) + { + RI.currententity = tr.draw_list->trans_entities[i]; + RI.currentmodel = RI.currententity->model; + + // handle studiomodels with custom rendermodes on texture + if( RI.currententity->curstate.rendermode != kRenderNormal ) + tr.blend = CL_FxBlend( RI.currententity ) / 255.0f; + else tr.blend = 1.0f; // draw as solid but sorted by distance + + if( tr.blend <= 0.0f ) continue; + + Assert( RI.currententity != NULL ); + Assert( RI.currentmodel != NULL ); + + switch( RI.currentmodel->type ) + { + case mod_brush: + R_DrawBrushModel( RI.currententity ); + break; + case mod_alias: + R_DrawAliasModel( RI.currententity ); + break; + case mod_studio: + R_DrawStudioModel( RI.currententity ); + break; + case mod_sprite: + R_DrawSpriteModel( RI.currententity ); + break; + default: + break; + } + } + + if( RI.drawWorld ) + { + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + gEngfuncs.pfnDrawTransparentTriangles (); + } + + if( !RI.onlyClientDraw ) + { + R_AllowFog( false ); + gEngfuncs.CL_DrawEFX( tr.frametime, true ); + R_AllowFog( true ); + } + + sceGuDisable( GU_BLEND ); // Trinity Render issues + + if( !RI.onlyClientDraw ) + R_DrawViewModel(); + gEngfuncs.CL_ExtraUpdate(); +} + +/* +================ +R_RenderScene + +R_SetupRefParams must be called right before +================ +*/ +void R_RenderScene( void ) +{ + if( !WORLDMODEL && RI.drawWorld ) + gEngfuncs.Host_Error( "R_RenderView: NULL worldmodel\n" ); + + // frametime is valid only for normal pass + if( RP_NORMALPASS( )) + tr.frametime = gpGlobals->time - gpGlobals->oldtime; + else tr.frametime = 0.0; + + // begin a new frame + tr.framecount++; + + R_PushDlights(); + + R_SetupFrustum(); + R_SetupFrame(); + R_SetupGL( true ); + R_Clear( ~0 ); + + R_MarkLeaves(); + R_DrawFog (); + + R_CheckGLFog(); + R_DrawWorld(); + R_CheckFog(); + + gEngfuncs.CL_ExtraUpdate (); // don't let sound get messed up if going slow + + R_DrawEntitiesOnList(); + + R_DrawWaterSurfaces(); + + R_EndGL(); +} + +/* +=============== +R_CheckGamma +=============== +*/ +void R_CheckGamma( void ) +{ + if( gEngfuncs.R_DoResetGamma( )) + { + // paranoia cubemaps uses this + gEngfuncs.BuildGammaTable( 1.8f, 0.0f ); + + // paranoia cubemap rendering + if( gEngfuncs.drawFuncs->GL_BuildLightmaps ) + gEngfuncs.drawFuncs->GL_BuildLightmaps( ); + } + else if( FBitSet( vid_gamma->flags, FCVAR_CHANGED ) || FBitSet( vid_brightness->flags, FCVAR_CHANGED )) + { + gEngfuncs.BuildGammaTable( vid_gamma->value, vid_brightness->value ); + glConfig.softwareGammaUpdate = true; + GL_RebuildLightmaps(); + glConfig.softwareGammaUpdate = false; + } +} + +/* +=============== +R_BeginFrame +=============== +*/ +void R_BeginFrame( qboolean clearScene ) +{ + glConfig.softwareGammaUpdate = false; // in case of possible fails + + if(( gl_clear->value || ENGINE_GET_PARM( PARM_DEV_OVERVIEW )) && + clearScene && ENGINE_GET_PARM( PARM_CONNSTATE ) != ca_cinematic ) + { +#if 1 + sceGuClear( GU_COLOR_BUFFER_BIT ); +#else + pglClear( GL_COLOR_BUFFER_BIT ); +#endif + } + + R_CheckGamma(); + + R_Set2DMode( true ); + // draw buffer stuff +#if 0 + pglDrawBuffer( GL_BACK ); + + // update texture parameters + if( FBitSet( gl_texture_nearest->flags|gl_lightmap_nearest->flags|gl_texture_anisotropy->flags|gl_texture_lodbias->flags, FCVAR_CHANGED )) + R_SetTextureParameters(); +#endif + gEngfuncs.CL_ExtraUpdate (); +} + +/* +=============== +R_SetupRefParams + +set initial params for renderer +=============== +*/ +void R_SetupRefParams( const ref_viewpass_t *rvp ) +{ + RI.params = RP_NONE; + RI.drawWorld = FBitSet( rvp->flags, RF_DRAW_WORLD ); + RI.onlyClientDraw = FBitSet( rvp->flags, RF_ONLY_CLIENTDRAW ); + RI.farClip = 0; + + if( !FBitSet( rvp->flags, RF_DRAW_CUBEMAP )) + RI.drawOrtho = FBitSet( rvp->flags, RF_DRAW_OVERVIEW ); + else RI.drawOrtho = false; + + // setup viewport + RI.viewport[0] = rvp->viewport[0]; + RI.viewport[1] = rvp->viewport[1]; + RI.viewport[2] = rvp->viewport[2]; + RI.viewport[3] = rvp->viewport[3]; + + // calc FOV + RI.fov_x = rvp->fov_x; + RI.fov_y = rvp->fov_y; + + VectorCopy( rvp->vieworigin, RI.vieworg ); + VectorCopy( rvp->viewangles, RI.viewangles ); + VectorCopy( rvp->vieworigin, RI.pvsorigin ); +} + +/* +=============== +R_RenderFrame +=============== +*/ +void R_RenderFrame( const ref_viewpass_t *rvp ) +{ + if( r_norefresh->value ) + return; + + // setup the initial render params + R_SetupRefParams( rvp ); +#if 0 + if( gl_finish->value && RI.drawWorld ) + pglFinish(); +#endif + + // completely override rendering + if( gEngfuncs.drawFuncs->GL_RenderFrame != NULL ) + { + tr.fCustomRendering = true; + + if( gEngfuncs.drawFuncs->GL_RenderFrame( rvp )) + { + R_GatherPlayerLight(); + tr.realframecount++; + tr.fResetVis = true; + return; + } + } + + tr.fCustomRendering = false; + if( !RI.onlyClientDraw ) + R_RunViewmodelEvents(); + + tr.realframecount++; // right called after viewmodel events + R_RenderScene(); + + return; +} + +/* +=============== +R_EndFrame +=============== +*/ +void R_EndFrame( void ) +{ + // flush any remaining 2D bits + R_Set2DMode( false ); + gEngfuncs.GL_SwapBuffers(); +} + +/* +=============== +R_DrawCubemapView +=============== +*/ +void R_DrawCubemapView( const vec3_t origin, const vec3_t angles, int size ) +{ + ref_viewpass_t rvp; + + // basic params + rvp.flags = rvp.viewentity = 0; + SetBits( rvp.flags, RF_DRAW_WORLD ); + SetBits( rvp.flags, RF_DRAW_CUBEMAP ); + + rvp.viewport[0] = rvp.viewport[1] = 0; + rvp.viewport[2] = rvp.viewport[3] = size; + rvp.fov_x = rvp.fov_y = 90.0f; // this is a final fov value + + // setup origin & angles + VectorCopy( origin, rvp.vieworigin ); + VectorCopy( angles, rvp.viewangles ); + + R_RenderFrame( &rvp ); + + RI.viewleaf = NULL; // force markleafs next frame +} + +/* +=============== +CL_FxBlend +=============== +*/ +int CL_FxBlend( cl_entity_t *e ) +{ + int blend = 0; + float offset, dist; + vec3_t tmp; + + offset = ((int)e->index ) * 363.0f; // Use ent index to de-sync these fx + + switch( e->curstate.renderfx ) + { + case kRenderFxPulseSlowWide: + blend = e->curstate.renderamt + 0x40 * sin( gpGlobals->time * 2 + offset ); + break; + case kRenderFxPulseFastWide: + blend = e->curstate.renderamt + 0x40 * sin( gpGlobals->time * 8 + offset ); + break; + case kRenderFxPulseSlow: + blend = e->curstate.renderamt + 0x10 * sin( gpGlobals->time * 2 + offset ); + break; + case kRenderFxPulseFast: + blend = e->curstate.renderamt + 0x10 * sin( gpGlobals->time * 8 + offset ); + break; + case kRenderFxFadeSlow: + if( RP_NORMALPASS( )) + { + if( e->curstate.renderamt > 0 ) + e->curstate.renderamt -= 1; + else e->curstate.renderamt = 0; + } + blend = e->curstate.renderamt; + break; + case kRenderFxFadeFast: + if( RP_NORMALPASS( )) + { + if( e->curstate.renderamt > 3 ) + e->curstate.renderamt -= 4; + else e->curstate.renderamt = 0; + } + blend = e->curstate.renderamt; + break; + case kRenderFxSolidSlow: + if( RP_NORMALPASS( )) + { + if( e->curstate.renderamt < 255 ) + e->curstate.renderamt += 1; + else e->curstate.renderamt = 255; + } + blend = e->curstate.renderamt; + break; + case kRenderFxSolidFast: + if( RP_NORMALPASS( )) + { + if( e->curstate.renderamt < 252 ) + e->curstate.renderamt += 4; + else e->curstate.renderamt = 255; + } + blend = e->curstate.renderamt; + break; + case kRenderFxStrobeSlow: + blend = 20 * sin( gpGlobals->time * 4 + offset ); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxStrobeFast: + blend = 20 * sin( gpGlobals->time * 16 + offset ); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxStrobeFaster: + blend = 20 * sin( gpGlobals->time * 36 + offset ); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxFlickerSlow: + blend = 20 * (sin( gpGlobals->time * 2 ) + sin( gpGlobals->time * 17 + offset )); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxFlickerFast: + blend = 20 * (sin( gpGlobals->time * 16 ) + sin( gpGlobals->time * 23 + offset )); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxHologram: + case kRenderFxDistort: + VectorCopy( e->origin, tmp ); + VectorSubtract( tmp, RI.vieworg, tmp ); + dist = DotProduct( tmp, RI.vforward ); + + // turn off distance fade + if( e->curstate.renderfx == kRenderFxDistort ) + dist = 1; + + if( dist <= 0 ) + { + blend = 0; + } + else + { + e->curstate.renderamt = 180; + if( dist <= 100 ) blend = e->curstate.renderamt; + else blend = (int) ((1.0f - ( dist - 100 ) * ( 1.0f / 400.0f )) * e->curstate.renderamt ); + blend += gEngfuncs.COM_RandomLong( -32, 31 ); + } + break; + default: + blend = e->curstate.renderamt; + break; + } + + blend = bound( 0, blend, 255 ); + + return blend; +} diff --git a/ref_gu/gu_rmath.c b/ref_gu/gu_rmath.c new file mode 100644 index 00000000..c3a3db24 --- /dev/null +++ b/ref_gu/gu_rmath.c @@ -0,0 +1,267 @@ +/* +gl_rmath.c - renderer mathlib +Copyright (C) 2010 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "xash3d_mathlib.h" + +/* +======================================================================== + + Matrix4x4 operations (private to renderer) + +======================================================================== +*/ +void Matrix4x4_Concat( matrix4x4 out, const matrix4x4 in1, const matrix4x4 in2 ) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0] + in1[0][3] * in2[3][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1] + in1[0][3] * in2[3][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2] + in1[0][3] * in2[3][2]; + out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3] * in2[3][3]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0] + in1[1][3] * in2[3][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1] + in1[1][3] * in2[3][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2] + in1[1][3] * in2[3][2]; + out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3] * in2[3][3]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0] + in1[2][3] * in2[3][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1] + in1[2][3] * in2[3][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2] + in1[2][3] * in2[3][2]; + out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3] * in2[3][3]; + out[3][0] = in1[3][0] * in2[0][0] + in1[3][1] * in2[1][0] + in1[3][2] * in2[2][0] + in1[3][3] * in2[3][0]; + out[3][1] = in1[3][0] * in2[0][1] + in1[3][1] * in2[1][1] + in1[3][2] * in2[2][1] + in1[3][3] * in2[3][1]; + out[3][2] = in1[3][0] * in2[0][2] + in1[3][1] * in2[1][2] + in1[3][2] * in2[2][2] + in1[3][3] * in2[3][2]; + out[3][3] = in1[3][0] * in2[0][3] + in1[3][1] * in2[1][3] + in1[3][2] * in2[2][3] + in1[3][3] * in2[3][3]; +} + +/* +================ +Matrix4x4_CreateProjection + +NOTE: produce quake style world orientation +================ +*/ +void Matrix4x4_CreateProjection( matrix4x4 out, float xMax, float xMin, float yMax, float yMin, float zNear, float zFar ) +{ + out[0][0] = ( 2.0f * zNear ) / ( xMax - xMin ); + out[1][1] = ( 2.0f * zNear ) / ( yMax - yMin ); + out[2][2] = -( zFar + zNear ) / ( zFar - zNear ); + out[3][3] = out[0][1] = out[1][0] = out[3][0] = out[0][3] = out[3][1] = out[1][3] = 0.0f; + + out[2][0] = 0.0f; + out[2][1] = 0.0f; + out[0][2] = ( xMax + xMin ) / ( xMax - xMin ); + out[1][2] = ( yMax + yMin ) / ( yMax - yMin ); + out[3][2] = -1.0f; + out[2][3] = -( 2.0f * zFar * zNear ) / ( zFar - zNear ); +} + +void Matrix4x4_CreateOrtho( matrix4x4 out, float xLeft, float xRight, float yBottom, float yTop, float zNear, float zFar ) +{ + out[0][0] = 2.0f / (xRight - xLeft); + out[1][1] = 2.0f / (yTop - yBottom); + out[2][2] = -2.0f / (zFar - zNear); + out[3][3] = 1.0f; + out[0][1] = out[0][2] = out[1][0] = out[1][2] = out[3][0] = out[3][1] = out[3][2] = 0.0f; + + out[2][0] = 0.0f; + out[2][1] = 0.0f; + out[0][3] = -(xRight + xLeft) / (xRight - xLeft); + out[1][3] = -(yTop + yBottom) / (yTop - yBottom); + out[2][3] = -(zFar + zNear) / (zFar - zNear); +} + +/* +================ +Matrix4x4_CreateModelview + +NOTE: produce quake style world orientation +================ +*/ +void Matrix4x4_CreateModelview( matrix4x4 out ) +{ + out[0][0] = out[1][1] = out[2][2] = 0.0f; + out[3][0] = out[0][3] = 0.0f; + out[3][1] = out[1][3] = 0.0f; + out[3][2] = out[2][3] = 0.0f; + out[3][3] = 1.0f; + out[1][0] = out[0][2] = out[2][1] = 0.0f; + out[2][0] = out[0][1] = -1.0f; + out[1][2] = 1.0f; +} + +void Matrix4x4_ToArrayFloatGL( const matrix4x4 in, float out[16] ) +{ + out[ 0] = in[0][0]; + out[ 1] = in[1][0]; + out[ 2] = in[2][0]; + out[ 3] = in[3][0]; + out[ 4] = in[0][1]; + out[ 5] = in[1][1]; + out[ 6] = in[2][1]; + out[ 7] = in[3][1]; + out[ 8] = in[0][2]; + out[ 9] = in[1][2]; + out[10] = in[2][2]; + out[11] = in[3][2]; + out[12] = in[0][3]; + out[13] = in[1][3]; + out[14] = in[2][3]; + out[15] = in[3][3]; +} + +void Matrix4x4_FromArrayFloatGL( matrix4x4 out, const float in[16] ) +{ + out[0][0] = in[0]; + out[1][0] = in[1]; + out[2][0] = in[2]; + out[3][0] = in[3]; + out[0][1] = in[4]; + out[1][1] = in[5]; + out[2][1] = in[6]; + out[3][1] = in[7]; + out[0][2] = in[8]; + out[1][2] = in[9]; + out[2][2] = in[10]; + out[3][2] = in[11]; + out[0][3] = in[12]; + out[1][3] = in[13]; + out[2][3] = in[14]; + out[3][3] = in[15]; +} + +void Matrix4x4_CreateTranslate( matrix4x4 out, float x, float y, float z ) +{ + out[0][0] = 1.0f; + out[0][1] = 0.0f; + out[0][2] = 0.0f; + out[0][3] = x; + out[1][0] = 0.0f; + out[1][1] = 1.0f; + out[1][2] = 0.0f; + out[1][3] = y; + out[2][0] = 0.0f; + out[2][1] = 0.0f; + out[2][2] = 1.0f; + out[2][3] = z; + out[3][0] = 0.0f; + out[3][1] = 0.0f; + out[3][2] = 0.0f; + out[3][3] = 1.0f; +} + +void Matrix4x4_CreateRotate( matrix4x4 out, float angle, float x, float y, float z ) +{ + float len, c, s; + + len = x * x + y * y + z * z; + if( len != 0.0f ) len = 1.0f / sqrt( len ); + x *= len; + y *= len; + z *= len; + + angle *= (-M_PI_F / 180.0f); + SinCos( angle, &s, &c ); + + out[0][0]=x * x + c * (1 - x * x); + out[0][1]=x * y * (1 - c) + z * s; + out[0][2]=z * x * (1 - c) - y * s; + out[0][3]=0.0f; + out[1][0]=x * y * (1 - c) - z * s; + out[1][1]=y * y + c * (1 - y * y); + out[1][2]=y * z * (1 - c) + x * s; + out[1][3]=0.0f; + out[2][0]=z * x * (1 - c) + y * s; + out[2][1]=y * z * (1 - c) - x * s; + out[2][2]=z * z + c * (1 - z * z); + out[2][3]=0.0f; + out[3][0]=0.0f; + out[3][1]=0.0f; + out[3][2]=0.0f; + out[3][3]=1.0f; +} + +void Matrix4x4_CreateScale( matrix4x4 out, float x ) +{ + out[0][0] = x; + out[0][1] = 0.0f; + out[0][2] = 0.0f; + out[0][3] = 0.0f; + out[1][0] = 0.0f; + out[1][1] = x; + out[1][2] = 0.0f; + out[1][3] = 0.0f; + out[2][0] = 0.0f; + out[2][1] = 0.0f; + out[2][2] = x; + out[2][3] = 0.0f; + out[3][0] = 0.0f; + out[3][1] = 0.0f; + out[3][2] = 0.0f; + out[3][3] = 1.0f; +} + +void Matrix4x4_CreateScale3( matrix4x4 out, float x, float y, float z ) +{ + out[0][0] = x; + out[0][1] = 0.0f; + out[0][2] = 0.0f; + out[0][3] = 0.0f; + out[1][0] = 0.0f; + out[1][1] = y; + out[1][2] = 0.0f; + out[1][3] = 0.0f; + out[2][0] = 0.0f; + out[2][1] = 0.0f; + out[2][2] = z; + out[2][3] = 0.0f; + out[3][0] = 0.0f; + out[3][1] = 0.0f; + out[3][2] = 0.0f; + out[3][3] = 1.0f; +} + +void Matrix4x4_ConcatTranslate( matrix4x4 out, float x, float y, float z ) +{ + matrix4x4 base, temp; + + Matrix4x4_Copy( base, out ); + Matrix4x4_CreateTranslate( temp, x, y, z ); + Matrix4x4_Concat( out, base, temp ); +} + +void Matrix4x4_ConcatRotate( matrix4x4 out, float angle, float x, float y, float z ) +{ + matrix4x4 base, temp; + + Matrix4x4_Copy( base, out ); + Matrix4x4_CreateRotate( temp, angle, x, y, z ); + Matrix4x4_Concat( out, base, temp ); +} + +void Matrix4x4_ConcatScale( matrix4x4 out, float x ) +{ + matrix4x4 base, temp; + + Matrix4x4_Copy( base, out ); + Matrix4x4_CreateScale( temp, x ); + Matrix4x4_Concat( out, base, temp ); +} + +void Matrix4x4_ConcatScale3( matrix4x4 out, float x, float y, float z ) +{ + matrix4x4 base, temp; + + Matrix4x4_Copy( base, out ); + Matrix4x4_CreateScale3( temp, x, y, z ); + Matrix4x4_Concat( out, base, temp ); +} diff --git a/ref_gu/gu_rmisc.c b/ref_gu/gu_rmisc.c new file mode 100644 index 00000000..3c2bc692 --- /dev/null +++ b/ref_gu/gu_rmisc.c @@ -0,0 +1,190 @@ +/* +gl_rmisc.c - renderer misceallaneous +Copyright (C) 2010 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "shake.h" +#include "screenfade.h" +#include "cdll_int.h" + +static void R_ParseDetailTextures( const char *filename ) +{ + byte *afile; + char *pfile; + string token, texname; + string detail_texname; + string detail_path; + float xScale, yScale; + texture_t *tex; + int i; + + afile = gEngfuncs.COM_LoadFile( filename, NULL, false ); + if( !afile ) return; + + pfile = (char *)afile; + + // format: 'texturename' 'detailtexture' 'xScale' 'yScale' + while(( pfile = gEngfuncs.COM_ParseFile( pfile, token )) != NULL ) + { + texname[0] = '\0'; + detail_texname[0] = '\0'; + + // read texname + if( token[0] == '{' ) + { + // NOTE: COM_ParseFile handled some symbols seperately + // this code will be fix it + pfile = gEngfuncs.COM_ParseFile( pfile, token ); + Q_strncat( texname, "{", sizeof( texname )); + Q_strncat( texname, token, sizeof( texname )); + } + else Q_strncpy( texname, token, sizeof( texname )); + + // read detailtexture name + pfile = gEngfuncs.COM_ParseFile( pfile, token ); + Q_strncat( detail_texname, token, sizeof( detail_texname )); + + // trying the scales or '{' + pfile = gEngfuncs.COM_ParseFile( pfile, token ); + + // read second part of detailtexture name + if( token[0] == '{' ) + { + Q_strncat( detail_texname, token, sizeof( detail_texname )); + pfile = gEngfuncs.COM_ParseFile( pfile, token ); // read scales + Q_strncat( detail_texname, token, sizeof( detail_texname )); + pfile = gEngfuncs.COM_ParseFile( pfile, token ); // parse scales + } + + Q_snprintf( detail_path, sizeof( detail_path ), "gfx/%s", detail_texname ); + + // read scales + xScale = Q_atof( token ); + + pfile = gEngfuncs.COM_ParseFile( pfile, token ); + yScale = Q_atof( token ); + + if( xScale <= 0.0f || yScale <= 0.0f ) + continue; + + // search for existing texture and uploading detail texture + for( i = 0; i < WORLDMODEL->numtextures; i++ ) + { + tex = WORLDMODEL->textures[i]; + + if( Q_stricmp( tex->name, texname )) + continue; + + tex->dt_texturenum = GL_LoadTexture( detail_path, NULL, 0, TF_FORCE_COLOR ); + + // texture is loaded + if( tex->dt_texturenum ) + { + gl_texture_t *glt; + + glt = R_GetTexture( tex->gl_texturenum ); + glt->xscale = xScale; + glt->yscale = yScale; + } + break; + } + } + + Mem_Free( afile ); +} + +void R_NewMap( void ) +{ + texture_t *tx; + int i; + + R_ClearDecals(); // clear all level decals + + R_StudioResetPlayerModels(); + + // upload detailtextures + if( CVAR_TO_BOOL( r_detailtextures )) + { + string mapname, filepath; + + Q_strncpy( mapname, WORLDMODEL->name, sizeof( mapname )); + COM_StripExtension( mapname ); + Q_sprintf( filepath, "%s_detail.txt", mapname ); + + R_ParseDetailTextures( filepath ); + } + + if( gEngfuncs.pfnGetCvarFloat( "v_dark" )) + { + screenfade_t *sf = gEngfuncs.GetScreenFade(); + float fadetime = 5.0f; + client_textmessage_t *title; + + title = gEngfuncs.pfnTextMessageGet( "GAMETITLE" ); + if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) + fadetime = 1.0f; + + if( title ) + { + // get settings from titles.txt + sf->fadeEnd = title->holdtime + title->fadeout; + sf->fadeReset = title->fadeout; + } + else sf->fadeEnd = sf->fadeReset = fadetime; + + sf->fadeFlags = FFADE_IN; + sf->fader = sf->fadeg = sf->fadeb = 0; + sf->fadealpha = 255; + sf->fadeSpeed = (float)sf->fadealpha / sf->fadeReset; + sf->fadeReset += gpGlobals->time; + sf->fadeEnd += sf->fadeReset; + + gEngfuncs.Cvar_SetValue( "v_dark", 0.0f ); + } + + // clear out efrags in case the level hasn't been reloaded + for( i = 0; i < WORLDMODEL->numleafs; i++ ) + WORLDMODEL->leafs[i+1].efrags = NULL; + + glState.isFogEnabled = false; + tr.skytexturenum = -1; +#if 1 + sceGuDisable( GU_FOG ); +#else + pglDisable( GL_FOG ); +#endif + // clearing texture chains + for( i = 0; i < WORLDMODEL->numtextures; i++ ) + { + if( !WORLDMODEL->textures[i] ) + continue; + + tx = WORLDMODEL->textures[i]; + + if( !Q_strncmp( tx->name, "sky", 3 ) && tx->width == ( tx->height * 2 )) + tr.skytexturenum = i; + + tx->texturechain = NULL; + } + + R_SetupSky( MOVEVARS->skyName ); + + GL_BuildLightmaps (); +#if 0 + R_GenerateVBO(); +#endif + if( gEngfuncs.drawFuncs->R_NewMap != NULL ) + gEngfuncs.drawFuncs->R_NewMap(); + +} diff --git a/ref_gu/gu_rpart.c b/ref_gu/gu_rpart.c new file mode 100644 index 00000000..095ebea2 --- /dev/null +++ b/ref_gu/gu_rpart.c @@ -0,0 +1,405 @@ +/* +gu_rpart.c - particles and tracers +Copyright (C) 2010 Uncle Mike +Copyright (C) 2020 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "r_efx.h" +#include "event_flags.h" +#include "entity_types.h" +#include "triangleapi.h" +#include "pm_local.h" +#include "cl_tent.h" +#include "studio.h" + +static float gTracerSize[11] = { 1.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; +static color24 gTracerColors[] = +{ +{ 255, 255, 255 }, // White +{ 255, 0, 0 }, // Red +{ 0, 255, 0 }, // Green +{ 0, 0, 255 }, // Blue +{ 0, 0, 0 }, // Tracer default, filled in from cvars, etc. +{ 255, 167, 17 }, // Yellow-orange sparks +{ 255, 130, 90 }, // Yellowish streaks (garg) +{ 55, 60, 144 }, // Blue egon streak +{ 255, 130, 90 }, // More Yellowish streaks (garg) +{ 255, 140, 90 }, // More Yellowish streaks (garg) +{ 200, 130, 90 }, // More red streaks (garg) +{ 255, 120, 70 }, // Darker red streaks (garg) +}; + +/* +================ +CL_DrawParticles + +update particle color, position, free expired and draw it +================ +*/ +void CL_DrawParticles( double frametime, particle_t *cl_active_particles, float partsize ) +{ + particle_t *p; + vec3_t right, up; + color24 *pColor; + int alpha; + float size; + + if( !cl_active_particles ) + return; // nothing to draw? +#if 1 + sceGuEnable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + + GL_Bind( XASH_TEXTURE0, tr.particleTexture ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuDepthMask( GU_TRUE ); + + for( p = cl_active_particles; p; p = p->next ) + { + if(( p->type != pt_blob ) || ( p->packedColor == 255 )) + { + size = partsize; // get initial size of particle + + // scale up to keep particles from disappearing + size += (p->org[0] - RI.vieworg[0]) * RI.cull_vforward[0]; + size += (p->org[1] - RI.vieworg[1]) * RI.cull_vforward[1]; + size += (p->org[2] - RI.vieworg[2]) * RI.cull_vforward[2]; + + if( size < 20.0f ) size = partsize; + else size = partsize + size * 0.002f; + + // scale the axes by radius + VectorScale( RI.cull_vright, size, right ); + VectorScale( RI.cull_vup, size, up ); + + p->color = bound( 0, p->color, 255 ); + pColor = gEngfuncs.CL_GetPaletteColor( p->color ); + + alpha = 255 * (p->die - gpGlobals->time) * 16.0f; + if( alpha > 255 || p->type == pt_static ) + alpha = 255; + + sceGuColor( GUCOLOR4UB( gEngfuncs.LightToTexGamma( pColor->r ), + gEngfuncs.LightToTexGamma( pColor->g ), + gEngfuncs.LightToTexGamma( pColor->b ), alpha ) ); + + gu_vert_ftv_t* const out = ( gu_vert_ftv_t* )sceGuGetMemory( sizeof( gu_vert_ftv_t ) * 4 ); + + out[0].u = 0.0f; + out[0].v = 1.0f; + out[0].x = p->org[0] - right[0] + up[0]; + out[0].y = p->org[1] - right[1] + up[1]; + out[0].z = p->org[2] - right[2] + up[2]; + out[1].u = 0.0f; + out[1].v = 0.0f; + out[1].x = p->org[0] + right[0] + up[0]; + out[1].y = p->org[1] + right[1] + up[1]; + out[1].z = p->org[2] + right[2] + up[2]; + out[2].u = 1.0f; + out[2].v = 0.0f; + out[2].x = p->org[0] + right[0] - up[0]; + out[2].y = p->org[1] + right[1] - up[1]; + out[2].z = p->org[2] + right[2] - up[2]; + out[3].u = 1.0f; + out[3].v = 1.0f; + out[3].x = p->org[0] - right[0] - up[0]; + out[3].y = p->org[1] - right[1] - up[1]; + out[3].z = p->org[2] - right[2] - up[2]; + + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, out ); + + r_stats.c_particle_count++; + } + + gEngfuncs.CL_ThinkParticle( frametime, p ); + } + + sceGuDepthMask( GU_FALSE ); +#else + pglEnable( GL_BLEND ); + pglDisable( GL_ALPHA_TEST ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + GL_Bind( XASH_TEXTURE0, tr.particleTexture ); + pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + pglDepthMask( GL_FALSE ); + + pglBegin( GL_QUADS ); + + for( p = cl_active_particles; p; p = p->next ) + { + if(( p->type != pt_blob ) || ( p->packedColor == 255 )) + { + size = partsize; // get initial size of particle + + // scale up to keep particles from disappearing + size += (p->org[0] - RI.vieworg[0]) * RI.cull_vforward[0]; + size += (p->org[1] - RI.vieworg[1]) * RI.cull_vforward[1]; + size += (p->org[2] - RI.vieworg[2]) * RI.cull_vforward[2]; + + if( size < 20.0f ) size = partsize; + else size = partsize + size * 0.002f; + + // scale the axes by radius + VectorScale( RI.cull_vright, size, right ); + VectorScale( RI.cull_vup, size, up ); + + p->color = bound( 0, p->color, 255 ); + pColor = gEngfuncs.CL_GetPaletteColor( p->color ); + + alpha = 255 * (p->die - gpGlobals->time) * 16.0f; + if( alpha > 255 || p->type == pt_static ) + alpha = 255; + + pglColor4ub( gEngfuncs.LightToTexGamma( pColor->r ), + gEngfuncs.LightToTexGamma( pColor->g ), + gEngfuncs.LightToTexGamma( pColor->b ), alpha ); + + pglTexCoord2f( 0.0f, 1.0f ); + pglVertex3f( p->org[0] - right[0] + up[0], p->org[1] - right[1] + up[1], p->org[2] - right[2] + up[2] ); + pglTexCoord2f( 0.0f, 0.0f ); + pglVertex3f( p->org[0] + right[0] + up[0], p->org[1] + right[1] + up[1], p->org[2] + right[2] + up[2] ); + pglTexCoord2f( 1.0f, 0.0f ); + pglVertex3f( p->org[0] + right[0] - up[0], p->org[1] + right[1] - up[1], p->org[2] + right[2] - up[2] ); + pglTexCoord2f( 1.0f, 1.0f ); + pglVertex3f( p->org[0] - right[0] - up[0], p->org[1] - right[1] - up[1], p->org[2] - right[2] - up[2] ); + r_stats.c_particle_count++; + } + + gEngfuncs.CL_ThinkParticle( frametime, p ); + } + + pglEnd(); + pglDepthMask( GL_TRUE ); +#endif +} + +/* +================ +CL_CullTracer + +check tracer bbox +================ +*/ +static qboolean CL_CullTracer( particle_t *p, const vec3_t start, const vec3_t end ) +{ + vec3_t mins, maxs; + int i; + + // compute the bounding box + for( i = 0; i < 3; i++ ) + { + if( start[i] < end[i] ) + { + mins[i] = start[i]; + maxs[i] = end[i]; + } + else + { + mins[i] = end[i]; + maxs[i] = start[i]; + } + + // don't let it be zero sized + if( mins[i] == maxs[i] ) + { + maxs[i] += gTracerSize[p->type] * 2.0f; + } + } + + // check bbox + return R_CullBox( mins, maxs ); +} + +/* +================ +CL_DrawTracers + +update tracer color, position, free expired and draw it +================ +*/ +void CL_DrawTracers( double frametime, particle_t *cl_active_tracers ) +{ + float scale, atten, gravity; + vec3_t screenLast, screen; + vec3_t start, end, delta; + particle_t *p; + + // update tracer color if this is changed + if( FBitSet( tracerred->flags|tracergreen->flags|tracerblue->flags|traceralpha->flags, FCVAR_CHANGED )) + { + color24 *customColors = &gTracerColors[4]; + customColors->r = (byte)(tracerred->value * traceralpha->value * 255); + customColors->g = (byte)(tracergreen->value * traceralpha->value * 255); + customColors->b = (byte)(tracerblue->value * traceralpha->value * 255); + ClearBits( tracerred->flags, FCVAR_CHANGED ); + ClearBits( tracergreen->flags, FCVAR_CHANGED ); + ClearBits( tracerblue->flags, FCVAR_CHANGED ); + ClearBits( traceralpha->flags, FCVAR_CHANGED ); + } + + if( !cl_active_tracers ) + return; // nothing to draw? + + if( !TriSpriteTexture( gEngfuncs.GetDefaultSprite( REF_DOT_SPRITE ), 0 )) + return; +#if 1 + sceGuEnable( GU_BLEND ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, GUBLEND1 ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuDepthMask( GU_TRUE ); +#else + pglEnable( GL_BLEND ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE ); + pglDisable( GL_ALPHA_TEST ); + pglDepthMask( GL_FALSE ); +#endif + gravity = frametime * MOVEVARS->gravity; + scale = 1.0 - (frametime * 0.9); + if( scale < 0.0f ) scale = 0.0f; + + for( p = cl_active_tracers; p; p = p->next ) + { + atten = (p->die - gpGlobals->time); + if( atten > 0.1f ) atten = 0.1f; + + VectorScale( p->vel, ( p->ramp * atten ), delta ); + VectorAdd( p->org, delta, end ); + VectorCopy( p->org, start ); + + if( !CL_CullTracer( p, start, end )) + { + vec3_t verts[4], tmp2; + vec3_t tmp, normal; + color24 *pColor; + + // Transform point into screen space + TriWorldToScreen( start, screen ); + TriWorldToScreen( end, screenLast ); + + // build world-space normal to screen-space direction vector + VectorSubtract( screen, screenLast, tmp ); + + // we don't need Z, we're in screen space + tmp[2] = 0; + VectorNormalize( tmp ); + + // build point along noraml line (normal is -y, x) + VectorScale( RI.cull_vup, tmp[0] * gTracerSize[p->type], normal ); + VectorScale( RI.cull_vright, -tmp[1] * gTracerSize[p->type], tmp2 ); + VectorSubtract( normal, tmp2, normal ); + + // compute four vertexes + VectorSubtract( start, normal, verts[0] ); + VectorAdd( start, normal, verts[1] ); + VectorAdd( verts[0], delta, verts[2] ); + VectorAdd( verts[1], delta, verts[3] ); + + if( p->color > sizeof( gTracerColors ) / sizeof( color24 ) ) + { + gEngfuncs.Con_Printf( S_ERROR "UserTracer with color > %d\n", sizeof( gTracerColors ) / sizeof( color24 )); + p->color = 0; + } + + pColor = &gTracerColors[p->color]; +#if 1 + sceGuColor( GUCOLOR4UB( pColor->r, pColor->g, pColor->b, p->packedColor ) ); + + gu_vert_ftv_t* const out = ( gu_vert_ftv_t* )sceGuGetMemory( sizeof( gu_vert_ftv_t ) * 4 ); + + out[0].u = 0.0f; + out[0].v = 0.8f; + out[0].x = verts[2][0]; + out[0].y = verts[2][1]; + out[0].z = verts[2][2]; + out[1].u = 1.0f; + out[1].v = 0.8f; + out[1].x = verts[3][0]; + out[1].y = verts[3][1]; + out[1].z = verts[3][2]; + out[2].u = 1.0f; + out[2].v = 0.0f; + out[2].x = verts[1][0]; + out[2].y = verts[1][1]; + out[2].z = verts[1][2]; + out[3].u = 0.0f; + out[3].v = 0.0f; + out[3].x = verts[0][0]; + out[3].y = verts[0][1]; + out[3].z = verts[0][2]; + + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, out ); +#else + pglColor4ub( pColor->r, pColor->g, pColor->b, p->packedColor ); + + pglBegin( GL_QUADS ); + pglTexCoord2f( 0.0f, 0.8f ); + pglVertex3fv( verts[2] ); + pglTexCoord2f( 1.0f, 0.8f ); + pglVertex3fv( verts[3] ); + pglTexCoord2f( 1.0f, 0.0f ); + pglVertex3fv( verts[1] ); + pglTexCoord2f( 0.0f, 0.0f ); + pglVertex3fv( verts[0] ); + pglEnd(); +#endif + } + + // evaluate position + VectorMA( p->org, frametime, p->vel, p->org ); + + if( p->type == pt_grav ) + { + p->vel[0] *= scale; + p->vel[1] *= scale; + p->vel[2] -= gravity; + + p->packedColor = 255 * (p->die - gpGlobals->time) * 2; + if( p->packedColor > 255 ) p->packedColor = 255; + } + else if( p->type == pt_slowgrav ) + { + p->vel[2] = gravity * 0.05f; + } + } +#if 1 + sceGuDepthMask( GU_FALSE ); +#else + pglDepthMask( GL_TRUE ); +#endif +} + +/* +=============== +CL_DrawParticlesExternal + +allow to draw effects from custom renderer +=============== +*/ +void CL_DrawParticlesExternal( const ref_viewpass_t *rvp, qboolean trans_pass, float frametime ) +{ + ref_instance_t oldRI = RI; + + memcpy( &oldRI, &RI, sizeof( ref_instance_t )); + R_SetupRefParams( rvp ); + R_SetupFrustum(); + R_SetupGL( false ); // don't touch GL-states + tr.frametime = frametime; + + gEngfuncs.CL_DrawEFX( frametime, trans_pass ); + + // restore internal state + memcpy( &RI, &oldRI, sizeof( ref_instance_t )); +} diff --git a/ref_gu/gu_rsurf.c b/ref_gu/gu_rsurf.c new file mode 100644 index 00000000..c546a707 --- /dev/null +++ b/ref_gu/gu_rsurf.c @@ -0,0 +1,2352 @@ +/* +gu_rsurf.c - surface-related refresh code +Copyright (C) 2010 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "xash3d_mathlib.h" +#include "mod_local.h" + +typedef struct +{ + int allocated[BLOCK_SIZE_MAX]; + int current_lightmap_texture; + msurface_t *dynamic_surfaces; + msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; + byte lightmap_buffer[BLOCK_SIZE_MAX*BLOCK_SIZE_MAX*4]; +} gllightmapstate_t; + +static int nColinElim; // stats +static vec2_t world_orthocenter; +static vec2_t world_orthohalf; +static uint r_blocklights[BLOCK_SIZE_MAX*BLOCK_SIZE_MAX*3]; +static mextrasurf_t *fullbright_surfaces[MAX_TEXTURES]; +static mextrasurf_t *detail_surfaces[MAX_TEXTURES]; +static int rtable[MOD_FRAMES][MOD_FRAMES]; +static qboolean draw_alpha_surfaces = false; +static qboolean draw_fullbrights = false; +static qboolean draw_details = false; +static msurface_t *skychain = NULL; +static gllightmapstate_t gl_lms; +static void LM_UploadBlock( qboolean dynamic ); + +byte *Mod_GetCurrentVis( void ) +{ + if( gEngfuncs.drawFuncs->Mod_GetCurrentVis && tr.fCustomRendering ) + return gEngfuncs.drawFuncs->Mod_GetCurrentVis(); + return RI.visbytes; +} + +void Mod_SetOrthoBounds( const float *mins, const float *maxs ) +{ + if( gEngfuncs.drawFuncs->GL_OrthoBounds ) + { + gEngfuncs.drawFuncs->GL_OrthoBounds( mins, maxs ); + } + + Vector2Average( maxs, mins, world_orthocenter ); + Vector2Subtract( maxs, world_orthocenter, world_orthohalf ); +} + +static void BoundPoly( int numverts, float *verts, vec3_t mins, vec3_t maxs ) +{ + int i, j; + float *v; + + ClearBounds( mins, maxs ); + + for( i = 0, v = verts; i < numverts; i++ ) + { + for( j = 0; j < 3; j++, v++ ) + { + if( *v < mins[j] ) mins[j] = *v; + if( *v > maxs[j] ) maxs[j] = *v; + } + } +} + +static void SubdividePolygon_r( msurface_t *warpface, int numverts, float *verts ) +{ + vec3_t front[SUBDIVIDE_SIZE], back[SUBDIVIDE_SIZE]; + mextrasurf_t *warpinfo = warpface->info; + float dist[SUBDIVIDE_SIZE]; + float m, frac, s, t, *v; + int i, j, k, f, b; + float sample_size; + vec3_t mins, maxs; + glpoly_t *poly; + model_t *loadmodel = gEngfuncs.Mod_GetCurrentLoadingModel(); + + if( numverts > ( SUBDIVIDE_SIZE - 4 )) + gEngfuncs.Host_Error( "Mod_SubdividePolygon: too many vertexes on face ( %i )\n", numverts ); + + sample_size = gEngfuncs.Mod_SampleSizeForFace( warpface ); + BoundPoly( numverts, verts, mins, maxs ); + + for( i = 0; i < 3; i++ ) + { + m = ( mins[i] + maxs[i] ) * 0.5f; + m = gl_subdivide_size->value * floor( m / gl_subdivide_size->value + 0.5f ); + if( maxs[i] - m < 8 ) continue; + if( m - mins[i] < 8 ) continue; + + // cut it + v = verts + i; + for( j = 0; j < numverts; j++, v += 3 ) + dist[j] = *v - m; + + // wrap cases + dist[j] = dist[0]; + v -= i; + VectorCopy( verts, v ); + + f = b = 0; + v = verts; + for( j = 0; j < numverts; j++, v += 3 ) + { + if( dist[j] >= 0 ) + { + VectorCopy( v, front[f] ); + f++; + } + + if( dist[j] <= 0 ) + { + VectorCopy (v, back[b]); + b++; + } + + if( dist[j] == 0 || dist[j+1] == 0 ) + continue; + + if(( dist[j] > 0 ) != ( dist[j+1] > 0 )) + { + // clip point + frac = dist[j] / ( dist[j] - dist[j+1] ); + for( k = 0; k < 3; k++ ) + front[f][k] = back[b][k] = v[k] + frac * (v[3+k] - v[k]); + f++; + b++; + } + } + + SubdividePolygon_r( warpface, f, front[0] ); + SubdividePolygon_r( warpface, b, back[0] ); + return; + } + + if( numverts != 4 ) + ClearBits( warpface->flags, SURF_DRAWTURB_QUADS ); + + // add a point in the center to help keep warp valid + poly = Mem_Calloc( loadmodel->mempool, sizeof( glpoly_t ) + ( numverts * 2 - 1 ) * sizeof( gu_vert_t ) ); + poly->next = warpface->polys; + poly->flags = warpface->flags; + warpface->polys = poly; + poly->numverts = numverts; + + for( i = 0; i < numverts; i++, verts += 3 ) + { + if( FBitSet( warpface->flags, SURF_DRAWTURB )) + { + s = DotProduct( verts, warpface->texinfo->vecs[0] ); + t = DotProduct( verts, warpface->texinfo->vecs[1] ); + } + else + { + s = DotProduct( verts, warpface->texinfo->vecs[0] ) + warpface->texinfo->vecs[0][3]; + t = DotProduct( verts, warpface->texinfo->vecs[1] ) + warpface->texinfo->vecs[1][3]; + s /= warpface->texinfo->texture->width; + t /= warpface->texinfo->texture->height; + } + + poly->verts[i].uv[0] = s; + poly->verts[i].uv[1] = t; + VectorCopy( verts, poly->verts[i].xyz ); + + // for speed reasons + if( !FBitSet( warpface->flags, SURF_DRAWTURB )) + { + // lightmap texture coordinates + s = DotProduct( verts, warpinfo->lmvecs[0] ) + warpinfo->lmvecs[0][3]; + s -= warpinfo->lightmapmins[0]; + s += warpface->light_s * sample_size; + s += sample_size * 0.5f; + s /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->width; + + t = DotProduct( verts, warpinfo->lmvecs[1] ) + warpinfo->lmvecs[1][3]; + t -= warpinfo->lightmapmins[1]; + t += warpface->light_t * sample_size; + t += sample_size * 0.5f; + t /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->height; + + poly->verts[i + numverts].uv[0] = s; + poly->verts[i + numverts].uv[1] = t; + VectorCopy( verts, poly->verts[i + numverts].xyz ); + } + } +} + +void GL_SetupFogColorForSurfaces( void ) +{ + vec3_t fogColor; + float factor, div; + + if( !glState.isFogEnabled) + return; + + if( RI.currententity && RI.currententity->curstate.rendermode == kRenderTransTexture ) + { +#if 1 + glState.fogColor = GUCOLOR4F( RI.fogColor[0], RI.fogColor[1], RI.fogColor[2], glState.fogDensity ); + sceGuFog( glState.fogStart, glState.fogEnd, glState.fogColor ); +#else + pglFogfv( GL_FOG_COLOR, RI.fogColor ); +#endif + return; + } + + div = (r_detailtextures->value) ? 2.0f : 1.0f; + factor = (r_detailtextures->value) ? 3.0f : 2.0f; + fogColor[0] = pow( RI.fogColor[0] / div, ( 1.0f / factor )); + fogColor[1] = pow( RI.fogColor[1] / div, ( 1.0f / factor )); + fogColor[2] = pow( RI.fogColor[2] / div, ( 1.0f / factor )); +#if 1 + glState.fogColor = GUCOLOR4F( fogColor[0], fogColor[1], fogColor[2], glState.fogDensity ); + sceGuFog( glState.fogStart, glState.fogEnd, glState.fogColor ); +#else + pglFogfv( GL_FOG_COLOR, fogColor ); +#endif +} + +void GL_ResetFogColor( void ) +{ + // restore fog here +#if 1 + if( !glState.isFogEnabled ) + return; + + glState.fogColor = GUCOLOR4F( RI.fogColor[0], RI.fogColor[1], RI.fogColor[2], glState.fogDensity ); + sceGuFog( glState.fogStart, glState.fogEnd, glState.fogColor ); +#else + if( glState.isFogEnabled ) + pglFogfv( GL_FOG_COLOR, RI.fogColor ); +#endif +} + +/* +================ +GL_SubdivideSurface + +Breaks a polygon up along axial 64 unit +boundaries so that turbulent and sky warps +can be done reasonably. +================ +*/ +void GL_SubdivideSurface( msurface_t *fa ) +{ + vec3_t verts[SUBDIVIDE_SIZE]; + int numverts; + int i, lindex; + float *vec; + model_t *loadmodel = gEngfuncs.Mod_GetCurrentLoadingModel(); + + // convert edges back to a normal polygon + numverts = 0; + for( i = 0; i < fa->numedges; i++ ) + { + lindex = loadmodel->surfedges[fa->firstedge + i]; + + if( lindex > 0 ) vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; + else vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; + VectorCopy( vec, verts[numverts] ); + numverts++; + } + + SetBits( fa->flags, SURF_DRAWTURB_QUADS ); // predict state + + // do subdivide + SubdividePolygon_r( fa, numverts, verts[0] ); +} + +/* +================ +GL_BuildPolygonFromSurface +================ +*/ +void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa ) +{ + int i, lindex, lnumverts; + medge_t *pedges, *r_pedge; + mextrasurf_t *info = fa->info; + float sample_size; + texture_t *tex; + gl_texture_t *glt; + float *vec; + float s, t; + glpoly_t *poly; + int num_removed = 0; + + if( !mod || !fa->texinfo || !fa->texinfo->texture ) + return; // bad polygon ? + + if( FBitSet( fa->flags, SURF_CONVEYOR ) && fa->texinfo->texture->gl_texturenum != 0 ) + { + glt = R_GetTexture( fa->texinfo->texture->gl_texturenum ); + tex = fa->texinfo->texture; + Assert( glt != NULL && tex != NULL ); + + // update conveyor widths for keep properly speed of scrolling + glt->srcWidth = tex->width; + glt->srcHeight = tex->height; + } + + sample_size = gEngfuncs.Mod_SampleSizeForFace( fa ); + + // reconstruct the polygon + pedges = mod->edges; + lnumverts = fa->numedges; + + // detach if already created, reconstruct again + poly = fa->polys; + fa->polys = NULL; + + // quake simple models (healthkits etc) need to be reconstructed their polys because LM coords has changed after the map change + // PSP GU-friendly format s1t1 xyz s2t2 xyz (Idea from PSP Quake) + poly = Mem_Realloc( mod->mempool, poly, sizeof( glpoly_t ) + ( lnumverts * 2 - 1 ) * sizeof( gu_vert_t )); + poly->next = fa->polys; + poly->flags = fa->flags; + fa->polys = poly; + poly->numverts = lnumverts; + + for( i = 0; i < lnumverts; i++ ) + { + lindex = mod->surfedges[fa->firstedge + i]; + + if( lindex > 0 ) + { + r_pedge = &pedges[lindex]; + vec = mod->vertexes[r_pedge->v[0]].position; + } + else + { + r_pedge = &pedges[-lindex]; + vec = mod->vertexes[r_pedge->v[1]].position; + } + + s = DotProduct( vec, fa->texinfo->vecs[0] ) + fa->texinfo->vecs[0][3]; + s /= fa->texinfo->texture->width; + + t = DotProduct( vec, fa->texinfo->vecs[1] ) + fa->texinfo->vecs[1][3]; + t /= fa->texinfo->texture->height; + + poly->verts[i].uv[0] = s; + poly->verts[i].uv[1] = t; + VectorCopy( vec, poly->verts[i].xyz ); + + // lightmap texture coordinates + s = DotProduct( vec, info->lmvecs[0] ) + info->lmvecs[0][3]; + s -= info->lightmapmins[0]; + s += fa->light_s * sample_size; + s += sample_size * 0.5f; + s /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->width; + + t = DotProduct( vec, info->lmvecs[1] ) + info->lmvecs[1][3]; + t -= info->lightmapmins[1]; + t += fa->light_t * sample_size; + t += sample_size * 0.5f; + t /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->height; + + poly->verts[i + poly->numverts].uv[0] = s; + poly->verts[i + poly->numverts].uv[1] = t; + VectorCopy( vec, poly->verts[i + poly->numverts].xyz ); + } + + // remove co-linear points - Ed + if( !CVAR_TO_BOOL( gl_keeptjunctions ) && !FBitSet( fa->flags, SURF_UNDERWATER )) + { + for( i = 0; i < lnumverts; i++ ) + { + vec3_t v1, v2; + gu_vert_t *prev, *this, *next; + + prev = &poly->verts[(i + lnumverts - 1) % lnumverts]; + next = &poly->verts[(i + 1) % lnumverts]; + this = &poly->verts[i]; + + VectorSubtract( this->xyz, prev->xyz, v1 ); + VectorNormalize( v1 ); + VectorSubtract( next->xyz, prev->xyz, v2 ); + VectorNormalize( v2 ); + + // skip co-linear points + if(( fabs( v1[0] - v2[0] ) <= 0.001f) && (fabs( v1[1] - v2[1] ) <= 0.001f) && (fabs( v1[2] - v2[2] ) <= 0.001f)) + { + int j, k; + + for( j = i + 1; j < lnumverts; j++ ) + { + poly->verts[j - 1] = poly->verts[j]; + poly->verts[poly->numverts + j - 1] = poly->verts[poly->numverts + j]; + } + + // retry next vertex next time, which is now current vertex + lnumverts--; + nColinElim++; + num_removed++; + i--; + } + } + + if (num_removed > 0) + { + for (i = poly->numverts; i < poly->numverts + lnumverts; i++) + poly->verts[i - num_removed] = poly->verts[i]; + } + } + + poly->numverts = lnumverts; +} + + +/* +=============== +R_TextureAnim + +Returns the proper texture for a given time and base texture, do not process random tiling +=============== +*/ +texture_t *R_TextureAnim( texture_t *b ) +{ + texture_t *base = b; + int count, reletive; + + if( RI.currententity->curstate.frame ) + { + if( base->alternate_anims ) + base = base->alternate_anims; + } + + if( !base->anim_total ) + return base; + if( base->name[0] == '-' ) + { + return b; // already tiled + } + else + { + int speed; + + // Quake1 textures uses 10 frames per second + if( FBitSet( R_GetTexture( base->gl_texturenum )->flags, TF_QUAKEPAL )) + speed = 10; + else speed = 20; + + reletive = (int)(gpGlobals->time * speed) % base->anim_total; + } + + + count = 0; + + while( base->anim_min > reletive || base->anim_max <= reletive ) + { + base = base->anim_next; + + if( !base || ++count > MOD_FRAMES ) + return b; + } + + return base; +} + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and surface +=============== +*/ +texture_t *R_TextureAnimation( msurface_t *s ) +{ + texture_t *base = s->texinfo->texture; + int count, reletive; + + if( RI.currententity && RI.currententity->curstate.frame ) + { + if( base->alternate_anims ) + base = base->alternate_anims; + } + + if( !base->anim_total ) + return base; + + if( base->name[0] == '-' ) + { + int tx = (int)((s->texturemins[0] + (base->width << 16)) / base->width) % MOD_FRAMES; + int ty = (int)((s->texturemins[1] + (base->height << 16)) / base->height) % MOD_FRAMES; + + reletive = rtable[tx][ty] % base->anim_total; + } + else + { + int speed; + + // Quake1 textures uses 10 frames per second + if( FBitSet( R_GetTexture( base->gl_texturenum )->flags, TF_QUAKEPAL )) + speed = 10; + else speed = 20; + + reletive = (int)(gpGlobals->time * speed) % base->anim_total; + } + + count = 0; + + while( base->anim_min > reletive || base->anim_max <= reletive ) + { + base = base->anim_next; + + if( !base || ++count > MOD_FRAMES ) + return s->texinfo->texture; + } + + return base; +} + +/* +=============== +R_AddDynamicLights +=============== +*/ +void R_AddDynamicLights( msurface_t *surf ) +{ + float dist, rad, minlight; + int lnum, s, t, sd, td, smax, tmax; + float sl, tl, sacc, tacc; + vec3_t impact, origin_l; + mextrasurf_t *info = surf->info; + int sample_frac = 1.0; + float sample_size; + mtexinfo_t *tex; + dlight_t *dl; + uint *bl; + + // no dlighted surfaces here + if( !R_CountSurfaceDlights( surf )) return; + + sample_size = gEngfuncs.Mod_SampleSizeForFace( surf ); + smax = (info->lightextents[0] / sample_size) + 1; + tmax = (info->lightextents[1] / sample_size) + 1; + tex = surf->texinfo; + + if( FBitSet( tex->flags, TEX_WORLD_LUXELS )) + { + if( surf->texinfo->faceinfo ) + sample_frac = surf->texinfo->faceinfo->texture_step; + else if( FBitSet( surf->texinfo->flags, TEX_EXTRA_LIGHTMAP )) + sample_frac = LM_SAMPLE_EXTRASIZE; + else sample_frac = LM_SAMPLE_SIZE; + } + + for( lnum = 0; lnum < MAX_DLIGHTS; lnum++ ) + { + if( !FBitSet( surf->dlightbits, BIT( lnum ))) + continue; // not lit by this light + + dl = gEngfuncs.GetDynamicLight( lnum ); + + // transform light origin to local bmodel space + if( !tr.modelviewIdentity ) + Matrix4x4_VectorITransform( RI.objectMatrix, dl->origin, origin_l ); + else VectorCopy( dl->origin, origin_l ); + + rad = dl->radius; + dist = PlaneDiff( origin_l, surf->plane ); + rad -= fabs( dist ); + + // rad is now the highest intensity on the plane + minlight = dl->minlight; + if( rad < minlight ) + continue; + + minlight = rad - minlight; + + if( surf->plane->type < 3 ) + { + VectorCopy( origin_l, impact ); + impact[surf->plane->type] -= dist; + } + else VectorMA( origin_l, -dist, surf->plane->normal, impact ); + + sl = DotProduct( impact, info->lmvecs[0] ) + info->lmvecs[0][3] - info->lightmapmins[0]; + tl = DotProduct( impact, info->lmvecs[1] ) + info->lmvecs[1][3] - info->lightmapmins[1]; + bl = r_blocklights; + + for( t = 0, tacc = 0; t < tmax; t++, tacc += sample_size ) + { + td = (tl - tacc) * sample_frac; + if( td < 0 ) td = -td; + + for( s = 0, sacc = 0; s < smax; s++, sacc += sample_size, bl += 3 ) + { + sd = (sl - sacc) * sample_frac; + if( sd < 0 ) sd = -sd; + + if( sd > td ) dist = sd + (td >> 1); + else dist = td + (sd >> 1); + + if( dist < minlight ) + { + bl[0] += ((int)((rad - dist) * 256) * gEngfuncs.LightToTexGamma( dl->color.r )) / 256; + bl[1] += ((int)((rad - dist) * 256) * gEngfuncs.LightToTexGamma( dl->color.g )) / 256; + bl[2] += ((int)((rad - dist) * 256) * gEngfuncs.LightToTexGamma( dl->color.b )) / 256; + } + } + } + } +} + +/* +================ +R_SetCacheState +================ +*/ +void R_SetCacheState( msurface_t *surf ) +{ + int maps; + + for( maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++ ) + { + surf->cached_light[maps] = tr.lightstylevalue[surf->styles[maps]]; + } +} + +/* +============================================================================= + + LIGHTMAP ALLOCATION + +============================================================================= +*/ +static void LM_InitBlock( void ) +{ + memset( gl_lms.allocated, 0, sizeof( gl_lms.allocated )); +} + +static int LM_AllocBlock( int w, int h, int *x, int *y ) +{ + int i, j; + int best, best2; + + best = BLOCK_SIZE; + + for( i = 0; i < BLOCK_SIZE - w; i++ ) + { + best2 = 0; + + for( j = 0; j < w; j++ ) + { + if( gl_lms.allocated[i+j] >= best ) + break; + if( gl_lms.allocated[i+j] > best2 ) + best2 = gl_lms.allocated[i+j]; + } + + if( j == w ) + { + // this is a valid spot + *x = i; + *y = best = best2; + } + } + + if( best + h > BLOCK_SIZE ) + return false; + + for( i = 0; i < w; i++ ) + gl_lms.allocated[*x + i] = best + h; + + return true; +} + +static void LM_UploadBlock( qboolean dynamic ) +{ + int i; + + if( dynamic ) + { + int height = 0; + + for( i = 0; i < BLOCK_SIZE; i++ ) + { + if( gl_lms.allocated[i] > height ) + height = gl_lms.allocated[i]; + } + + GL_UpdateDlightTexture( tr.dlightTexture, 0, 0, BLOCK_SIZE, height, gl_lms.lightmap_buffer ); + GL_Bind( XASH_TEXTURE0, tr.dlightTexture ); + } + else + { + rgbdata_t r_lightmap; + char lmName[16]; + + i = gl_lms.current_lightmap_texture; + + // upload static lightmaps only during loading + memset( &r_lightmap, 0, sizeof( r_lightmap )); + Q_snprintf( lmName, sizeof( lmName ), "*lightmap%i", i ); + + r_lightmap.width = BLOCK_SIZE; + r_lightmap.height = BLOCK_SIZE; + r_lightmap.type = PF_RGBA_32; + r_lightmap.size = r_lightmap.width * r_lightmap.height * 4; + r_lightmap.flags = IMAGE_HAS_COLOR; + r_lightmap.buffer = gl_lms.lightmap_buffer; + tr.lightmapTextures[i] = GL_LoadTextureInternal( lmName, &r_lightmap, TF_FONT|TF_ATLAS_PAGE ); + + if( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS ) + gEngfuncs.Host_Error( "AllocBlock: full\n" ); + } +} + +/* +================= +R_BuildLightmap + +Combine and scale multiple lightmaps into the floating +format in r_blocklights +================= +*/ +static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride, qboolean dynamic ) +{ + int smax, tmax; + uint *bl, scale; + int i, map, size, s, t; + int sample_size; + mextrasurf_t *info = surf->info; + color24 *lm; + + sample_size = gEngfuncs.Mod_SampleSizeForFace( surf ); + smax = ( info->lightextents[0] / sample_size ) + 1; + tmax = ( info->lightextents[1] / sample_size ) + 1; + size = smax * tmax; + + lm = surf->samples; + + memset( r_blocklights, 0, sizeof( uint ) * size * 3 ); + + // add all the lightmaps + for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255 && lm; map++ ) + { + scale = tr.lightstylevalue[surf->styles[map]]; + + for( i = 0, bl = r_blocklights; i < size; i++, bl += 3, lm++ ) + { + bl[0] += gEngfuncs.LightToTexGamma( lm->r ) * scale; + bl[1] += gEngfuncs.LightToTexGamma( lm->g ) * scale; + bl[2] += gEngfuncs.LightToTexGamma( lm->b ) * scale; + } + } + + // add all the dynamic lights + if( surf->dlightframe == tr.framecount && dynamic ) + R_AddDynamicLights( surf ); + + // Put into texture format + stride -= (smax << 2); + bl = r_blocklights; + + for( t = 0; t < tmax; t++, dest += stride ) + { + for( s = 0; s < smax; s++ ) + { + dest[0] = Q_min((bl[0] >> 7), 255 ); + dest[1] = Q_min((bl[1] >> 7), 255 ); + dest[2] = Q_min((bl[2] >> 7), 255 ); + dest[3] = 255; + + bl += 3; + dest += 4; + } + } +} + +/* +================ +DrawGLPoly +================ +*/ +void DrawGLPoly( glpoly_t *p, float xScale, float yScale ) +{ + float *v; + float sOffset, sy; + float tOffset, cy; + cl_entity_t *e = RI.currententity; + int i; + qboolean hasScale = false; + qboolean hasOffset = false; + + if( !p ) return; + + if( FBitSet( p->flags, SURF_DRAWTILED )) + GL_ResetFogColor(); + + if( p->flags & SURF_CONVEYOR ) + { + float flConveyorSpeed = 0.0f; + float flRate, flAngle; + gl_texture_t *texture; + + if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) && RI.currententity == gEngfuncs.GetEntityByIndex( 0 ) ) + { + // same as doom speed + flConveyorSpeed = -35.0f; + } + else + { + flConveyorSpeed = (e->curstate.rendercolor.g<<8|e->curstate.rendercolor.b) / 16.0f; + if( e->curstate.rendercolor.r ) flConveyorSpeed = -flConveyorSpeed; + } + + texture = R_GetTexture( glState.currentTexture ); + + flRate = fabs( flConveyorSpeed ) / (float)texture->srcWidth; + flAngle = ( flConveyorSpeed >= 0 ) ? 180 : 0; + + SinCos( flAngle * ( M_PI_F / 180.0f ), &sy, &cy ); + sOffset = gpGlobals->time * cy * flRate; + tOffset = gpGlobals->time * sy * flRate; + + // make sure that we are positive + if( sOffset < 0.0f ) sOffset += 1.0f + -(int)sOffset; + if( tOffset < 0.0f ) tOffset += 1.0f + -(int)tOffset; + + // make sure that we are in a [0,1] range + sOffset = sOffset - (int)sOffset; + tOffset = tOffset - (int)tOffset; + + hasOffset = true; + } + + if( xScale != 0.0f && yScale != 0.0f ) + hasScale = true; + + if( hasScale ) sceGuTexScale( xScale, yScale ); + if( hasOffset ) sceGuTexOffset( sOffset, tOffset ); + + if ( GU_ClipIsRequired( &p->verts[0], p->numverts ) ) + { + // clip the polygon. + gu_vert_t* cv; + size_t cvc; + + GU_Clip( &p->verts[0], p->numverts, &cv, &cvc ); + if( cvc ) + { +#if CLIPPING_DEBUGGING + sceGuDisable(GU_TEXTURE_2D); + sceGuColor( 0xff0000ff ); +#endif + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, cvc, 0, cv ); +#if CLIPPING_DEBUGGING + sceGuEnable(GU_TEXTURE_2D); + sceGuColor( 0xffffffff ); +#endif + } + } + else sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, p->numverts, 0, &p->verts[0] ); + + if( hasScale ) sceGuTexScale( 1.0f, 1.0f ); + if( hasOffset ) sceGuTexOffset( 0.0f, 0.0f ); + + if( FBitSet( p->flags, SURF_DRAWTILED )) + GL_SetupFogColorForSurfaces(); +} + +/* +================ +DrawGLPolyChain + +Render lightmaps +================ +*/ +void DrawGLPolyChain( glpoly_t *p, float soffset, float toffset ) +{ + qboolean dynamic = true; + + if( soffset == 0.0f && toffset == 0.0f ) + dynamic = false; + + if( dynamic ) sceGuTexOffset( -soffset, -toffset ); + + for( ; p != NULL; p = p->chain ) + { + float *v; + int i; + + if ( GU_ClipIsRequired( &p->verts[p->numverts], p->numverts ) ) + { + // clip the polygon. + gu_vert_t* cv; + size_t cvc; + + GU_Clip( &p->verts[p->numverts], p->numverts, &cv, &cvc ); + if( cvc ) sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, cvc, 0, cv ); + } + else sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, p->numverts, 0, &p->verts[p->numverts] ); + } + + if( dynamic ) sceGuTexOffset( 0.0f, 0.0f ); +} + +_inline qboolean R_HasLightmap( void ) +{ + if( CVAR_TO_BOOL( r_fullbright ) || !WORLDMODEL->lightdata ) + return false; + + if( RI.currententity ) + { + if( RI.currententity->curstate.effects & EF_FULLBRIGHT ) + return false; // disabled by user + + // check for rendermode + switch( RI.currententity->curstate.rendermode ) + { + case kRenderTransTexture: + case kRenderTransColor: + case kRenderTransAdd: + case kRenderGlow: + return false; // no lightmaps + } + } + + return true; +} + +/* +================ +R_BlendLightmaps +================ +*/ +void R_BlendLightmaps( void ) +{ + msurface_t *surf, *newsurf = NULL; + int i; + + if( !R_HasLightmap() ) + return; + + GL_SetupFogColorForSurfaces (); + + if( !CVAR_TO_BOOL( r_lightmap )) + sceGuEnable( GU_BLEND ); + else sceGuDisable( GU_BLEND ); + + // lightmapped solid surfaces + sceGuDepthMask( GU_TRUE ); + sceGuDepthFunc( GU_EQUAL ); + + sceGuDisable( GU_ALPHA_TEST ); + sceGuBlendFunc( GU_ADD, GU_FIX, GU_SRC_COLOR, GUBLEND0, 0 ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + + // render static lightmaps first + for( i = 0; i < MAX_LIGHTMAPS; i++ ) + { + if( gl_lms.lightmap_surfaces[i] ) + { + GL_Bind( XASH_TEXTURE0, tr.lightmapTextures[i] ); + + for( surf = gl_lms.lightmap_surfaces[i]; surf != NULL; surf = surf->info->lightmapchain ) + { + if( surf->polys ) DrawGLPolyChain( surf->polys, 0.0f, 0.0f ); + } + } + } + + // render dynamic lightmaps + if( CVAR_TO_BOOL( r_dynamic )) + { + LM_InitBlock(); +#if 0 + GL_Bind( XASH_TEXTURE0, tr.dlightTexture ); +#endif + newsurf = gl_lms.dynamic_surfaces; + + for( surf = gl_lms.dynamic_surfaces; surf != NULL; surf = surf->info->lightmapchain ) + { + int smax, tmax; + int sample_size; + mextrasurf_t *info = surf->info; + byte *base; + + sample_size = gEngfuncs.Mod_SampleSizeForFace( surf ); + smax = ( info->lightextents[0] / sample_size ) + 1; + tmax = ( info->lightextents[1] / sample_size ) + 1; + + if( LM_AllocBlock( smax, tmax, &surf->info->dlight_s, &surf->info->dlight_t )) + { + base = gl_lms.lightmap_buffer; + base += ( surf->info->dlight_t * BLOCK_SIZE + surf->info->dlight_s ) * 4; + + R_BuildLightMap( surf, base, BLOCK_SIZE * 4, true ); + } + else + { + msurface_t *drawsurf; + + // upload what we have so far + LM_UploadBlock( true ); + + // draw all surfaces that use this lightmap + for( drawsurf = newsurf; drawsurf != surf; drawsurf = drawsurf->info->lightmapchain ) + { + if( drawsurf->polys ) + { + DrawGLPolyChain( drawsurf->polys, + ( drawsurf->light_s - drawsurf->info->dlight_s ) * ( 1.0f / (float)BLOCK_SIZE ), + ( drawsurf->light_t - drawsurf->info->dlight_t ) * ( 1.0f / (float)BLOCK_SIZE )); + } + } + + newsurf = drawsurf; + + // clear the block + LM_InitBlock(); + + // try uploading the block now + if( !LM_AllocBlock( smax, tmax, &surf->info->dlight_s, &surf->info->dlight_t )) + gEngfuncs.Host_Error( "AllocBlock: full\n" ); + + base = gl_lms.lightmap_buffer; + base += ( surf->info->dlight_t * BLOCK_SIZE + surf->info->dlight_s ) * 4; + + R_BuildLightMap( surf, base, BLOCK_SIZE * 4, true ); + } + } + + // draw remainder of dynamic lightmaps that haven't been uploaded yet + if( newsurf ) LM_UploadBlock( true ); + + for( surf = newsurf; surf != NULL; surf = surf->info->lightmapchain ) + { + if( surf->polys ) + { + DrawGLPolyChain( surf->polys, + ( surf->light_s - surf->info->dlight_s ) * ( 1.0f / (float)BLOCK_SIZE ), + ( surf->light_t - surf->info->dlight_t ) * ( 1.0f / (float)BLOCK_SIZE )); + } + } + } + + sceGuDisable( GU_BLEND ); + sceGuDepthMask( GU_FALSE ); + sceGuDepthFunc( GU_LEQUAL ); + sceGuTexFunc( GU_TFX_REPLACE, GU_TCC_RGBA ); + sceGuColor( 0xffffffff ); + + // restore fog here + GL_ResetFogColor(); +} + +/* +================ +R_RenderFullbrights +================ +*/ +void R_RenderFullbrights( void ) +{ + mextrasurf_t *es, *p; + int i; + + if( !draw_fullbrights ) + return; + + R_AllowFog( false ); + + sceGuEnable( GU_BLEND ); + sceGuDepthMask( GU_TRUE ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuBlendFunc( GU_ADD, GU_FIX, GU_FIX, GUBLEND1, GUBLEND1 ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + + for( i = 1; i < MAX_TEXTURES; i++ ) + { + es = fullbright_surfaces[i]; + if( !es ) continue; + + GL_Bind( XASH_TEXTURE0, i ); + + for( p = es; p; p = p->lumachain ) + DrawGLPoly( p->surf->polys, 0.0f, 0.0f ); + + fullbright_surfaces[i] = NULL; + es->lumachain = NULL; + } + + sceGuDisable( GU_BLEND ); + sceGuDepthMask( GU_FALSE ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuTexFunc( GU_TFX_REPLACE, GU_TCC_RGBA ); + + draw_fullbrights = false; + R_AllowFog( true ); +} + +/* +================ +R_RenderDetails +================ +*/ +void R_RenderDetails( void ) +{ + gl_texture_t *glt; + mextrasurf_t *es, *p; + msurface_t *fa; + int i; + + if( !draw_details ) + return; + + GL_SetupFogColorForSurfaces(); + + sceGuEnable( GU_BLEND ); + sceGuBlendFunc( GU_ADD, GU_DST_COLOR, GU_SRC_COLOR, 0, 0 ); + sceGuTexFunc( GU_TFX_DECAL, GU_TCC_RGBA ); + sceGuDepthFunc( GU_EQUAL ); + + for( i = 1; i < MAX_TEXTURES; i++ ) + { + es = detail_surfaces[i]; + if( !es ) continue; + + GL_Bind( XASH_TEXTURE0, i ); + + for( p = es; p; p = p->detailchain ) + { + fa = p->surf; + glt = R_GetTexture( fa->texinfo->texture->gl_texturenum ); // get texture scale + + DrawGLPoly( fa->polys, glt->xscale, glt->yscale ); + } + + detail_surfaces[i] = NULL; + es->detailchain = NULL; + } + + sceGuDisable( GU_BLEND ); + sceGuTexFunc( GU_TFX_REPLACE, GU_TCC_RGBA ); + sceGuDepthFunc( GU_LEQUAL ); + + draw_details = false; + + // restore fog here + GL_ResetFogColor(); +} + +/* +================ +R_RenderBrushPoly +================ +*/ +void R_RenderBrushPoly( msurface_t *fa, int cull_type ) +{ + qboolean is_dynamic = false; + int maps; + texture_t *t; + + r_stats.c_world_polys++; + + if( fa->flags & SURF_DRAWSKY ) + return; // already handled + + t = R_TextureAnimation( fa ); + + GL_Bind( XASH_TEXTURE0, t->gl_texturenum ); + + if( FBitSet( fa->flags, SURF_DRAWTURB )) + { + // warp texture, no lightmaps + EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE)); + return; + } + + if( t->fb_texturenum ) + { + fa->info->lumachain = fullbright_surfaces[t->fb_texturenum]; + fullbright_surfaces[t->fb_texturenum] = fa->info; + draw_fullbrights = true; + } + + if( CVAR_TO_BOOL( r_detailtextures )) + { + if( glState.isFogEnabled ) + { + // don't apply detail textures for windows in the fog + if( RI.currententity->curstate.rendermode != kRenderTransTexture ) + { + if( t->dt_texturenum ) + { + fa->info->detailchain = detail_surfaces[t->dt_texturenum]; + detail_surfaces[t->dt_texturenum] = fa->info; + } + else + { + // draw stub detail texture for underwater surfaces + fa->info->detailchain = detail_surfaces[tr.grayTexture]; + detail_surfaces[tr.grayTexture] = fa->info; + } + draw_details = true; + } + } + else if( t->dt_texturenum ) + { + fa->info->detailchain = detail_surfaces[t->dt_texturenum]; + detail_surfaces[t->dt_texturenum] = fa->info; + draw_details = true; + } + } + + DrawGLPoly( fa->polys, 0.0f, 0.0f ); + + if( RI.currententity->curstate.rendermode == kRenderNormal ) + { + // batch decals to draw later + if( tr.num_draw_decals < MAX_DECAL_SURFS && fa->pdecals ) + tr.draw_decals[tr.num_draw_decals++] = fa; + } + else + { + // if rendermode != kRenderNormal draw decals sequentially + DrawSurfaceDecals( fa, true, (cull_type == CULL_BACKSIDE)); + } + + if( FBitSet( fa->flags, SURF_DRAWTILED )) + return; // no lightmaps anyway + + // check for lightmap modification + for( maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++ ) + { + if( tr.lightstylevalue[fa->styles[maps]] != fa->cached_light[maps] ) + goto dynamic; + } + + // dynamic this frame or dynamic previously + if( fa->dlightframe == tr.framecount ) + { +dynamic: + // NOTE: at this point we have only valid textures + if( r_dynamic->value ) is_dynamic = true; + } + + if( is_dynamic ) + { + if(( fa->styles[maps] >= 32 || fa->styles[maps] == 0 || fa->styles[maps] == 20 ) && ( fa->dlightframe != tr.framecount )) + { + byte temp[132*132*4]; + mextrasurf_t *info = fa->info; + int sample_size; + int smax, tmax; + + sample_size = gEngfuncs.Mod_SampleSizeForFace( fa ); + smax = ( info->lightextents[0] / sample_size ) + 1; + tmax = ( info->lightextents[1] / sample_size ) + 1; + + R_BuildLightMap( fa, temp, smax * 4, true ); + R_SetCacheState( fa ); + +#if 1 + GL_UpdateDlightTexture( tr.lightmapTextures[fa->lightmaptexturenum], fa->light_s, fa->light_t, smax, tmax, temp ); +#else + GL_Bind( XASH_TEXTURE0, tr.lightmapTextures[fa->lightmaptexturenum] ); + + pglTexSubImage2D( GL_TEXTURE_2D, 0, fa->light_s, fa->light_t, smax, tmax, + GL_RGBA, GL_UNSIGNED_BYTE, temp ); +#endif + fa->info->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum]; + gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; + } + else + { + fa->info->lightmapchain = gl_lms.dynamic_surfaces; + gl_lms.dynamic_surfaces = fa; + } + } + else + { + fa->info->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum]; + gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; + } +} + +/* +================ +R_DrawTextureChains +================ +*/ +void R_DrawTextureChains( void ) +{ + int i; + msurface_t *s; + texture_t *t; + + // make sure what color is reset + sceGuColor( 0xffffffff ); + + R_LoadIdentity(); // set identity matrix + + GL_SetupFogColorForSurfaces(); + + // restore worldmodel + RI.currententity = gEngfuncs.GetEntityByIndex( 0 ); + RI.currentmodel = RI.currententity->model; + + if( ENGINE_GET_PARM( PARM_SKY_SPHERE ) ) + { + sceGuDisable( GU_TEXTURE_2D ); + sceGuColor( 0xffffffff ); + } + + // clip skybox surfaces + for( s = skychain; s != NULL; s = s->texturechain ) + R_AddSkyBoxSurface( s ); + + if( ENGINE_GET_PARM( PARM_SKY_SPHERE ) ) + { + sceGuEnable( GU_TEXTURE_2D ); + if( skychain ) + R_DrawClouds(); + skychain = NULL; + } + + for( i = 0; i < WORLDMODEL->numtextures; i++ ) + { + t = WORLDMODEL->textures[i]; + if( !t ) continue; + + s = t->texturechain; + + if( !s || ( i == tr.skytexturenum )) + continue; + + if(( s->flags & SURF_DRAWTURB ) && MOVEVARS->wateralpha < 1.0f ) + continue; // draw translucent water later + + if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) && FBitSet( s->flags, SURF_TRANSPARENT )) + { + draw_alpha_surfaces = true; + continue; // draw transparent surfaces later + } + + for( ; s != NULL; s = s->texturechain ) + R_RenderBrushPoly( s, CULL_VISIBLE ); + t->texturechain = NULL; + } +} + +/* +================ +R_DrawAlphaTextureChains +================ +*/ +void R_DrawAlphaTextureChains( void ) +{ + int i; + msurface_t *s; + texture_t *t; + + if( !draw_alpha_surfaces ) + return; + + memset( gl_lms.lightmap_surfaces, 0, sizeof( gl_lms.lightmap_surfaces )); + gl_lms.dynamic_surfaces = NULL; + + // make sure what color is reset + sceGuColor( 0xffffffff ); + + R_LoadIdentity(); // set identity matrix + + sceGuDisable( GU_BLEND ); + sceGuEnable( GU_ALPHA_TEST ); + sceGuAlphaFunc( GU_GREATER, 0x40, 0xff ); + + GL_SetupFogColorForSurfaces(); + + // restore worldmodel + RI.currententity = gEngfuncs.GetEntityByIndex( 0 ); + RI.currentmodel = RI.currententity->model; + RI.currententity->curstate.rendermode = kRenderTransAlpha; + draw_alpha_surfaces = false; + + for( i = 0; i < WORLDMODEL->numtextures; i++ ) + { + t = WORLDMODEL->textures[i]; + if( !t ) continue; + + s = t->texturechain; + + if( !s || !FBitSet( s->flags, SURF_TRANSPARENT )) + continue; + + for( ; s != NULL; s = s->texturechain ) + R_RenderBrushPoly( s, CULL_VISIBLE ); + t->texturechain = NULL; + } + + GL_ResetFogColor(); + R_BlendLightmaps(); + RI.currententity->curstate.rendermode = kRenderNormal; // restore world rendermode + + sceGuAlphaFunc( GU_GREATER, DEFAULT_ALPHATEST, 0xff ); +} + +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces( void ) +{ + int i; + msurface_t *s; + texture_t *t; + + if( !RI.drawWorld || RI.onlyClientDraw ) + return; + + // non-transparent water is already drawed + if( MOVEVARS->wateralpha >= 1.0f ) + return; + + // restore worldmodel + RI.currententity = gEngfuncs.GetEntityByIndex( 0 ); + RI.currentmodel = RI.currententity->model; + + // go back to the world matrix + R_LoadIdentity(); + + sceGuEnable( GU_BLEND ); + sceGuDepthMask( GU_TRUE ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuColor( GUCOLOR4F( 1.0f, 1.0f, 1.0f, MOVEVARS->wateralpha ) ); + + for( i = 0; i < WORLDMODEL->numtextures; i++ ) + { + t = WORLDMODEL->textures[i]; + if( !t ) continue; + + s = t->texturechain; + if( !s ) continue; + + if( !FBitSet( s->flags, SURF_DRAWTURB )) + continue; + + // set modulate mode explicitly + GL_Bind( XASH_TEXTURE0, t->gl_texturenum ); + + for( ; s; s = s->texturechain ) + EmitWaterPolys( s, false ); + + t->texturechain = NULL; + } + + sceGuDisable( GU_BLEND ); + sceGuDepthMask( GU_FALSE ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuTexFunc( GU_TFX_REPLACE, GU_TCC_RGBA ); + sceGuColor( 0xffffffff ); +} + +/* +================= +R_SurfaceCompare + +compare translucent surfaces +================= +*/ +static int R_SurfaceCompare( const void *a, const void *b ) +{ + msurface_t *surf1, *surf2; + vec3_t org1, org2; + float len1, len2; + + surf1 = (msurface_t *)((sortedface_t *)a)->surf; + surf2 = (msurface_t *)((sortedface_t *)b)->surf; + + VectorAdd( RI.currententity->origin, surf1->info->origin, org1 ); + VectorAdd( RI.currententity->origin, surf2->info->origin, org2 ); + + // compare by plane dists + len1 = DotProduct( org1, RI.vforward ) - RI.viewplanedist; + len2 = DotProduct( org2, RI.vforward ) - RI.viewplanedist; + + if( len1 > len2 ) + return -1; + if( len1 < len2 ) + return 1; + + return 0; +} + +void R_SetRenderMode( cl_entity_t *e ) +{ + switch( e->curstate.rendermode ) + { + case kRenderNormal: + sceGuColor( 0xffffffff ); + break; + case kRenderTransColor: + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuColor( GUCOLOR4UB( e->curstate.rendercolor.r, e->curstate.rendercolor.g, e->curstate.rendercolor.b, e->curstate.renderamt ) ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuDisable( GU_TEXTURE_2D ); + sceGuEnable( GU_BLEND ); + break; + case kRenderTransAdd: + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuColor( GUCOLOR4F( tr.blend, tr.blend, tr.blend, 1.0f ) ); + sceGuBlendFunc( GU_ADD, GU_FIX, GU_FIX, GUBLEND1, GUBLEND1 ); + sceGuDepthMask( GU_TRUE ); + sceGuEnable( GU_BLEND ); + break; + case kRenderTransAlpha: + sceGuEnable( GU_ALPHA_TEST ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) + { + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuColor( GUCOLOR4F( 1.0f, 1.0f, 1.0f, tr.blend ) ); + sceGuEnable( GU_BLEND ); + } + else + { + sceGuColor( 0xffffffff ); + sceGuDisable( GU_BLEND ); + } + sceGuAlphaFunc( GU_GREATER, 0x40, 0xff ); + break; + default: + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuColor( GUCOLOR4F( 1.0f, 1.0f, 1.0f, tr.blend ) ); + sceGuDepthMask( GU_TRUE ); + sceGuEnable( GU_BLEND ); + break; + } +} + +/* +================= +R_DrawBrushModel +================= +*/ +void R_DrawBrushModel( cl_entity_t *e ) +{ + int i, k, num_sorted; + vec3_t origin_l, oldorigin; + int old_rendermode; + vec3_t mins, maxs; + int cull_type; + msurface_t *psurf; + model_t *clmodel; + qboolean rotated; + dlight_t *l; + + if( !RI.drawWorld ) return; + + clmodel = e->model; + + if( !VectorIsNull( e->angles )) + { + for( i = 0; i < 3; i++ ) + { + mins[i] = e->origin[i] - clmodel->radius; + maxs[i] = e->origin[i] + clmodel->radius; + } + rotated = true; + } + else + { + VectorAdd( e->origin, clmodel->mins, mins ); + VectorAdd( e->origin, clmodel->maxs, maxs ); + rotated = false; + } + + if( R_CullBox( mins, maxs )) + return; + + memset( gl_lms.lightmap_surfaces, 0, sizeof( gl_lms.lightmap_surfaces )); + old_rendermode = e->curstate.rendermode; + gl_lms.dynamic_surfaces = NULL; + + if( rotated ) R_RotateForEntity( e ); + else R_TranslateForEntity( e ); + + if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) && FBitSet( clmodel->flags, MODEL_TRANSPARENT )) + e->curstate.rendermode = kRenderTransAlpha; + + e->visframe = tr.realframecount; // visible + + if( rotated ) Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg ); + else VectorSubtract( RI.cullorigin, e->origin, tr.modelorg ); + + // calculate dynamic lighting for bmodel + for( k = 0; k < MAX_DLIGHTS; k++ ) + { + l = gEngfuncs.GetDynamicLight( k ); + + if( l->die < gpGlobals->time || !l->radius ) + continue; + + VectorCopy( l->origin, oldorigin ); // save lightorigin + Matrix4x4_VectorITransform( RI.objectMatrix, l->origin, origin_l ); + VectorCopy( origin_l, l->origin ); // move light in bmodel space + R_MarkLights( l, 1<nodes + clmodel->hulls[0].firstclipnode ); + VectorCopy( oldorigin, l->origin ); // restore lightorigin + } + + // setup the rendermode + R_SetRenderMode( e ); + GL_SetupFogColorForSurfaces (); + + if( e->curstate.rendermode == kRenderTransAdd ) + { + R_AllowFog( false ); + } + + psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; +#if 1 /* no sorting */ + GU_ClipBeginBrush(); + for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) + { + if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) + { + if( psurf->plane->type != PLANE_Z && !FBitSet( e->curstate.effects, EF_WATERSIDES )) + continue; + if( mins[2] + 1.0f >= psurf->plane->dist ) + continue; + } + + cull_type = R_CullSurface( psurf, &RI.frustum, RI.frustum.clipFlags ); + + if( cull_type >= CULL_FRUSTUM ) + continue; + + if( cull_type == CULL_BACKSIDE ) + { + if( !FBitSet( psurf->flags, SURF_DRAWTURB ) && !( psurf->pdecals && e->curstate.rendermode == kRenderTransTexture )) + continue; + } + R_RenderBrushPoly( psurf, cull_type ); + } + + if( e->curstate.rendermode == kRenderTransColor ) + sceGuEnable( GU_TEXTURE_2D ); +#else + num_sorted = 0; + for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) + { + if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) + { + if( psurf->plane->type != PLANE_Z && !FBitSet( e->curstate.effects, EF_WATERSIDES )) + continue; + if( mins[2] + 1.0f >= psurf->plane->dist ) + continue; + } + + cull_type = R_CullSurface( psurf, &RI.frustum, RI.frustum.clipFlags ); + + if( cull_type >= CULL_FRUSTUM ) + continue; + + if( cull_type == CULL_BACKSIDE ) + { + if( !FBitSet( psurf->flags, SURF_DRAWTURB ) && !( psurf->pdecals && e->curstate.rendermode == kRenderTransTexture )) + continue; + } + + if( num_sorted < gpGlobals->max_surfaces ) + { + gpGlobals->draw_surfaces[num_sorted].surf = psurf; + gpGlobals->draw_surfaces[num_sorted].cull = cull_type; + num_sorted++; + } + } + + // sort faces if needs + if( !FBitSet( clmodel->flags, MODEL_LIQUID ) && e->curstate.rendermode == kRenderTransTexture && !CVAR_TO_BOOL( gl_nosort )) + qsort( gpGlobals->draw_surfaces, num_sorted, sizeof( sortedface_t ), R_SurfaceCompare ); + + // draw sorted translucent surfaces + GU_ClipBeginBrush(); + for( i = 0; i < num_sorted; i++ ) + R_RenderBrushPoly( gpGlobals->draw_surfaces[i].surf, gpGlobals->draw_surfaces[i].cull ); + if( e->curstate.rendermode == kRenderTransColor ) + sceGuEnable( GU_TEXTURE_2D ); +#endif + DrawDecalsBatch(); + GL_ResetFogColor(); + R_BlendLightmaps(); + R_RenderFullbrights(); + R_RenderDetails(); + GU_ClipEndBrush(); + + // restore fog here + if( e->curstate.rendermode == kRenderTransAdd ) + R_AllowFog( true ); + + e->curstate.rendermode = old_rendermode; + + sceGuDisable( GU_ALPHA_TEST ); + sceGuAlphaFunc( GU_GREATER, DEFAULT_ALPHATEST, 0xff ); + sceGuDisable( GU_BLEND ); + sceGuDepthMask( GU_FALSE ); + + R_DrawModelHull(); // draw before restore + R_LoadIdentity(); // restore worldmatrix +} + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode( mnode_t *node, uint clipflags ) +{ + int i, clipped; + msurface_t *surf, **mark; + mleaf_t *pleaf; + int c, side; + float dot; +loc0: + if( node->contents == CONTENTS_SOLID ) + return; // hit a solid leaf + + if( node->visframe != tr.visframecount ) + return; + + if( clipflags && !CVAR_TO_BOOL( r_nocull )) + { + for( i = 0; i < 6; i++ ) + { + const mplane_t *p = &RI.frustum.planes[i]; + + if( !FBitSet( clipflags, BIT( i ))) + continue; + + clipped = BoxOnPlaneSide( node->minmaxs, node->minmaxs + 3, p ); + if( clipped == 2 ) return; + if( clipped == 1 ) ClearBits( clipflags, BIT( i )); + } + } + + // if a leaf node, draw stuff + if( node->contents < 0 ) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if( c ) + { + do + { + (*mark)->visframe = tr.framecount; + mark++; + } while( --c ); + } + + // deal with model fragments in this leaf + if( pleaf->efrags ) + gEngfuncs.R_StoreEfrags( &pleaf->efrags, tr.realframecount ); + + r_stats.c_world_leafs++; + return; + } + + // node is just a decision point, so go down the apropriate sides + + // find which side of the node we are on + dot = PlaneDiff( tr.modelorg, node->plane ); + side = (dot >= 0.0f) ? 0 : 1; + + // recurse down the children, front side first + R_RecursiveWorldNode( node->children[side], clipflags ); + + // draw stuff + for( c = node->numsurfaces, surf = WORLDMODEL->surfaces + node->firstsurface; c; c--, surf++ ) + { + if( R_CullSurface( surf, &RI.frustum, clipflags )) + continue; + + if( surf->flags & SURF_DRAWSKY ) + { + // make sky chain to right clip the skybox + surf->texturechain = skychain; + skychain = surf; + } + else + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } + + // recurse down the back side + node = node->children[!side]; + goto loc0; +} + +/* +================ +R_CullNodeTopView + +cull node by user rectangle (simple scissor) +================ +*/ +qboolean R_CullNodeTopView( mnode_t *node ) +{ + vec2_t delta, size; + vec3_t center, half; + + // build the node center and half-diagonal + VectorAverage( node->minmaxs, node->minmaxs + 3, center ); + VectorSubtract( node->minmaxs + 3, center, half ); + + // cull against the screen frustum or the appropriate area's frustum. + Vector2Subtract( center, world_orthocenter, delta ); + Vector2Add( half, world_orthohalf, size ); + + return ( fabs( delta[0] ) > size[0] ) || ( fabs( delta[1] ) > size[1] ); +} + +/* +================ +R_DrawTopViewLeaf +================ +*/ +static void R_DrawTopViewLeaf( mleaf_t *pleaf, uint clipflags ) +{ + msurface_t **mark, *surf; + int i; + + for( i = 0, mark = pleaf->firstmarksurface; i < pleaf->nummarksurfaces; i++, mark++ ) + { + surf = *mark; + + // don't process the same surface twice + if( surf->visframe == tr.framecount ) + continue; + + surf->visframe = tr.framecount; + + if( R_CullSurface( surf, &RI.frustum, clipflags )) + continue; + + if(!( surf->flags & SURF_DRAWSKY )) + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } + + // deal with model fragments in this leaf + if( pleaf->efrags ) + gEngfuncs.R_StoreEfrags( &pleaf->efrags, tr.realframecount ); + + r_stats.c_world_leafs++; +} + +/* +================ +R_DrawWorldTopView +================ +*/ +void R_DrawWorldTopView( mnode_t *node, uint clipflags ) +{ + int i, c, clipped; + msurface_t *surf; + + do + { + if( node->contents == CONTENTS_SOLID ) + return; // hit a solid leaf + + if( node->visframe != tr.visframecount ) + return; + + if( clipflags && !r_nocull->value ) + { + for( i = 0; i < 6; i++ ) + { + const mplane_t *p = &RI.frustum.planes[i]; + + if( !FBitSet( clipflags, BIT( i ))) + continue; + + clipped = BoxOnPlaneSide( node->minmaxs, node->minmaxs + 3, p ); + if( clipped == 2 ) return; + if( clipped == 1 ) ClearBits( clipflags, BIT( i )); + } + } + + // cull against the screen frustum or the appropriate area's frustum. + if( R_CullNodeTopView( node )) + return; + + // if a leaf node, draw stuff + if( node->contents < 0 ) + { + R_DrawTopViewLeaf( (mleaf_t *)node, clipflags ); + return; + } + + // draw stuff + for( c = node->numsurfaces, surf = WORLDMODEL->surfaces + node->firstsurface; c; c--, surf++ ) + { + // don't process the same surface twice + if( surf->visframe == tr.framecount ) + continue; + + surf->visframe = tr.framecount; + + if( R_CullSurface( surf, &RI.frustum, clipflags )) + continue; + + if(!( surf->flags & SURF_DRAWSKY )) + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } + + // recurse down both children, we don't care the order... + R_DrawWorldTopView( node->children[0], clipflags ); + node = node->children[1]; + + } while( node ); +} + +/* +============= +R_DrawTriangleOutlines +============= +*/ +void R_DrawTriangleOutlines( void ) +{ +#if 0 + int i, j; + msurface_t *surf; + glpoly_t *p; + float *v; + + if( !gl_wireframe->value ) + return; + + pglDisable( GL_TEXTURE_2D ); + pglDisable( GL_DEPTH_TEST ); + pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + pglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + + // render static surfaces first + for( i = 0; i < MAX_LIGHTMAPS; i++ ) + { + for( surf = gl_lms.lightmap_surfaces[i]; surf != NULL; surf = surf->info->lightmapchain ) + { + p = surf->polys; + for( ; p != NULL; p = p->chain ) + { + pglBegin( GL_POLYGON ); + v = p->verts[0]; + for( j = 0; j < p->numverts; j++, v += VERTEXSIZE ) + pglVertex3fv( v ); + pglEnd (); + } + } + } + + // render surfaces with dynamic lightmaps + for( surf = gl_lms.dynamic_surfaces; surf != NULL; surf = surf->info->lightmapchain ) + { + p = surf->polys; + + for( ; p != NULL; p = p->chain ) + { + pglBegin( GL_POLYGON ); + v = p->verts[0]; + for( j = 0; j < p->numverts; j++, v += VERTEXSIZE ) + pglVertex3fv( v ); + pglEnd (); + } + } + + pglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + pglEnable( GL_DEPTH_TEST ); + pglEnable( GL_TEXTURE_2D ); +#endif +} + +/* +============= +R_DrawWorld +============= +*/ +void R_DrawWorld( void ) +{ + double start, end; + + // paranoia issues: when gl_renderer is "0" we need have something valid for currententity + // to prevent crashing until HeadShield drawing. + RI.currententity = gEngfuncs.GetEntityByIndex( 0 ); + RI.currentmodel = RI.currententity->model; + + if( !RI.drawWorld || RI.onlyClientDraw ) + return; + + VectorCopy( RI.cullorigin, tr.modelorg ); + memset( gl_lms.lightmap_surfaces, 0, sizeof( gl_lms.lightmap_surfaces )); + memset( fullbright_surfaces, 0, sizeof( fullbright_surfaces )); + memset( detail_surfaces, 0, sizeof( detail_surfaces )); + + gl_lms.dynamic_surfaces = NULL; + + sceGuDisable( GU_ALPHA_TEST ); + sceGuDisable( GU_BLEND ); + + tr.blend = 1.0f; + + R_ClearSkyBox (); + + start = gEngfuncs.pfnTime(); + if( RI.drawOrtho ) + R_DrawWorldTopView( WORLDMODEL->nodes, RI.frustum.clipFlags ); + else R_RecursiveWorldNode( WORLDMODEL->nodes, RI.frustum.clipFlags ); + end = gEngfuncs.pfnTime(); + + r_stats.t_world_node = end - start; + + start = gEngfuncs.pfnTime(); + + R_DrawTextureChains(); + + if( !ENGINE_GET_PARM( PARM_DEV_OVERVIEW )) + { + DrawDecalsBatch(); + GL_ResetFogColor(); + R_BlendLightmaps(); + R_RenderFullbrights(); + R_RenderDetails(); + + if( skychain ) + R_DrawSkyBox(); + } + + end = gEngfuncs.pfnTime(); + + r_stats.t_world_draw = end - start; + tr.num_draw_decals = 0; + skychain = NULL; + + R_DrawTriangleOutlines (); + + R_DrawWorldHull(); +} + +/* +=============== +R_MarkLeaves + +Mark the leaves and nodes that are in the PVS for the current leaf +=============== +*/ +void R_MarkLeaves( void ) +{ + qboolean novis = false; + qboolean force = false; + mleaf_t *leaf = NULL; + mnode_t *node; + vec3_t test; + int i; + + if( !RI.drawWorld ) return; + + if( FBitSet( r_novis->flags, FCVAR_CHANGED ) || tr.fResetVis ) + { + // force recalc viewleaf + ClearBits( r_novis->flags, FCVAR_CHANGED ); + tr.fResetVis = false; + RI.viewleaf = NULL; + } + + VectorCopy( RI.pvsorigin, test ); + + if( RI.viewleaf != NULL ) + { + // merge two leafs that can be a crossed-line contents + if( RI.viewleaf->contents == CONTENTS_EMPTY ) + { + VectorSet( test, RI.pvsorigin[0], RI.pvsorigin[1], RI.pvsorigin[2] - 16.0f ); + leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes ); + } + else + { + VectorSet( test, RI.pvsorigin[0], RI.pvsorigin[1], RI.pvsorigin[2] + 16.0f ); + leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes ); + } + + if(( leaf->contents != CONTENTS_SOLID ) && ( RI.viewleaf != leaf )) + force = true; + } + + if( RI.viewleaf == RI.oldviewleaf && RI.viewleaf != NULL && !force ) + return; + + // development aid to let you run around + // and see exactly where the pvs ends + if( r_lockpvs->value ) return; + + RI.oldviewleaf = RI.viewleaf; + tr.visframecount++; + + if( r_novis->value || RI.drawOrtho || !RI.viewleaf || !WORLDMODEL->visdata ) + novis = true; + + gEngfuncs.R_FatPVS( RI.pvsorigin, REFPVS_RADIUS, RI.visbytes, FBitSet( RI.params, RP_OLDVIEWLEAF ), novis ); + if( force && !novis ) gEngfuncs.R_FatPVS( test, REFPVS_RADIUS, RI.visbytes, true, novis ); + + for( i = 0; i < WORLDMODEL->numleafs; i++ ) + { + if( CHECKVISBIT( RI.visbytes, i )) + { + node = (mnode_t *)&WORLDMODEL->leafs[i+1]; + do + { + if( node->visframe == tr.visframecount ) + break; + node->visframe = tr.visframecount; + node = node->parent; + } while( node ); + } + } +} + +/* +======================== +GL_CreateSurfaceLightmap +======================== +*/ +void GL_CreateSurfaceLightmap( msurface_t *surf, model_t *loadmodel ) +{ + int smax, tmax; + int sample_size; + mextrasurf_t *info = surf->info; + byte *base; + + if( !loadmodel->lightdata ) + return; + + if( FBitSet( surf->flags, SURF_DRAWTILED ) ) + return; + + sample_size = gEngfuncs.Mod_SampleSizeForFace( surf ); + smax = ( info->lightextents[0] / sample_size ) + 1; + tmax = ( info->lightextents[1] / sample_size ) + 1; + + if( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ) ) + { + LM_UploadBlock( false ); + LM_InitBlock(); + + if( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ) ) + gEngfuncs.Host_Error( "AllocBlock: full\n" ); + } + + surf->lightmaptexturenum = gl_lms.current_lightmap_texture; + + base = gl_lms.lightmap_buffer; + base += ( surf->light_t * BLOCK_SIZE + surf->light_s ) * 4; + + R_SetCacheState( surf ); + R_BuildLightMap( surf, base, BLOCK_SIZE * 4, false ); +} + +/* +================== +GL_RebuildLightmaps + +Rebuilds the lightmap texture +when gamma is changed +================== +*/ +void GL_RebuildLightmaps( void ) +{ + int i, j; + model_t *m; + + if( !ENGINE_GET_PARM( PARM_CLIENT_ACTIVE ) ) + return; // wait for worldmodel + + ClearBits( vid_brightness->flags, FCVAR_CHANGED ); + ClearBits( vid_gamma->flags, FCVAR_CHANGED ); + + // release old lightmaps + for( i = 0; i < MAX_LIGHTMAPS; i++ ) + { + if( !tr.lightmapTextures[i] ) break; + GL_FreeTexture( tr.lightmapTextures[i] ); + } + + memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures ) ); + gl_lms.current_lightmap_texture = 0; + + // setup all the lightstyles + CL_RunLightStyles(); + + LM_InitBlock(); + + for( i = 0; i < ENGINE_GET_PARM( PARM_NUMMODELS ); i++ ) + { + if( ( m = gEngfuncs.pfnGetModelByIndex( i + 1 ) ) == NULL ) + continue; + + if( m->name[0] == '*' || m->type != mod_brush ) + continue; + + for( j = 0; j < m->numsurfaces; j++ ) + GL_CreateSurfaceLightmap( m->surfaces + j, m ); + } + LM_UploadBlock( false ); + + if( gEngfuncs.drawFuncs->GL_BuildLightmaps ) + { + // build lightmaps on the client-side + gEngfuncs.drawFuncs->GL_BuildLightmaps( ); + } +} + +/* +================== +GL_BuildLightmaps + +Builds the lightmap texture +with all the surfaces from all brush models +================== +*/ +void GL_BuildLightmaps( void ) +{ + int i, j; + model_t *m; + + // release old lightmaps + for( i = 0; i < MAX_LIGHTMAPS; i++ ) + { + if( !tr.lightmapTextures[i] ) break; + GL_FreeTexture( tr.lightmapTextures[i] ); + } + + memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures )); + memset( &RI, 0, sizeof( RI )); +#if 1 + tr.block_size = BLOCK_SIZE_DEFAULT; +#else + // update the lightmap blocksize + if( FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_LARGE_LIGHTMAPS )) + tr.block_size = BLOCK_SIZE_MAX; + else tr.block_size = BLOCK_SIZE_DEFAULT; +#endif + skychain = NULL; + + tr.framecount = tr.visframecount = 1; // no dlight cache + gl_lms.current_lightmap_texture = 0; + tr.modelviewIdentity = false; + tr.realframecount = 1; + nColinElim = 0; + + // setup the texture for dlights + R_InitDlightTexture(); + + // setup all the lightstyles + CL_RunLightStyles(); + + LM_InitBlock(); + + for( i = 0; i < ENGINE_GET_PARM( PARM_NUMMODELS ); i++ ) + { + if( ( m = gEngfuncs.pfnGetModelByIndex( i + 1 ) ) == NULL ) + continue; + + if( m->name[0] == '*' || m->type != mod_brush ) + continue; + + for( j = 0; j < m->numsurfaces; j++ ) + { + // clearing all decal chains + m->surfaces[j].pdecals = NULL; + m->surfaces[j].visframe = 0; + + GL_CreateSurfaceLightmap( m->surfaces + j, m ); + + if( m->surfaces[j].flags & SURF_DRAWTURB ) + continue; + + GL_BuildPolygonFromSurface( m, m->surfaces + j ); + } + + // clearing visframe + for( j = 0; j < m->numleafs; j++ ) + m->leafs[j+1].visframe = 0; + for( j = 0; j < m->numnodes; j++ ) + m->nodes[j].visframe = 0; + } + + LM_UploadBlock( false ); + + if( gEngfuncs.drawFuncs->GL_BuildLightmaps ) + { + // build lightmaps on the client-side + gEngfuncs.drawFuncs->GL_BuildLightmaps( ); + } + + // now gamma and brightness are valid + ClearBits( vid_brightness->flags, FCVAR_CHANGED ); + ClearBits( vid_gamma->flags, FCVAR_CHANGED ); +} + +void GL_InitRandomTable( void ) +{ + int tu, tv; + + // make random predictable + gEngfuncs.COM_SetRandomSeed( 255 ); + + for( tu = 0; tu < MOD_FRAMES; tu++ ) + { + for( tv = 0; tv < MOD_FRAMES; tv++ ) + { + rtable[tu][tv] = gEngfuncs.COM_RandomLong( 0, 0x7FFF ); + } + } + + gEngfuncs.COM_SetRandomSeed( 0 ); +} diff --git a/ref_gu/gu_sprite.c b/ref_gu/gu_sprite.c new file mode 100644 index 00000000..1958ebaa --- /dev/null +++ b/ref_gu/gu_sprite.c @@ -0,0 +1,1026 @@ +/* +gu_sprite.c - sprite rendering +Copyright (C) 2010 Uncle Mike +Copyright (C) 2020 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "pm_local.h" +#include "sprite.h" +#include "studio.h" +#include "entity_types.h" +#include "cl_tent.h" + +// it's a Valve default value for LoadMapSprite (probably must be power of two) +#define MAPSPRITE_SIZE 128 +#define GLARE_FALLOFF 19000.0f + +cvar_t *r_sprite_lerping; +cvar_t *r_sprite_lighting; +char sprite_name[MAX_QPATH]; +char group_suffix[8]; +static uint r_texFlags = 0; +static int sprite_version; +float sprite_radius; + +/* +==================== +R_SpriteInit + +==================== +*/ +void R_SpriteInit( void ) +{ + r_sprite_lerping = gEngfuncs.Cvar_Get( "r_sprite_lerping", "1", FCVAR_ARCHIVE, "enables sprite animation lerping" ); + r_sprite_lighting = gEngfuncs.Cvar_Get( "r_sprite_lighting", "1", FCVAR_ARCHIVE, "enables sprite lighting (blood etc)" ); +} + +/* +==================== +R_SpriteLoadFrame + +upload a single frame +==================== +*/ +static const dframetype_t *R_SpriteLoadFrame( model_t *mod, const void *pin, mspriteframe_t **ppframe, int num ) +{ + dspriteframe_t pinframe; + mspriteframe_t *pspriteframe; + int gl_texturenum = 0; + char texname[128]; + int bytes = 1; + + memcpy( &pinframe, pin, sizeof(dspriteframe_t)); + + if( sprite_version == SPRITE_VERSION_32 ) + bytes = 4; + + // build uinque frame name + if( FBitSet( mod->flags, MODEL_CLIENT )) // it's a HUD sprite + { + Q_snprintf( texname, sizeof( texname ), "#HUD/%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, r_texFlags ); + } + else + { + Q_snprintf( texname, sizeof( texname ), "#%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, r_texFlags ); + } + + // setup frame description + pspriteframe = Mem_Malloc( mod->mempool, sizeof( mspriteframe_t )); + pspriteframe->width = pinframe.width; + pspriteframe->height = pinframe.height; + pspriteframe->up = pinframe.origin[1]; + pspriteframe->left = pinframe.origin[0]; + pspriteframe->down = pinframe.origin[1] - pinframe.height; + pspriteframe->right = pinframe.width + pinframe.origin[0]; + pspriteframe->gl_texturenum = gl_texturenum; + *ppframe = pspriteframe; + + return ( const dframetype_t* )(( const byte* )pin + sizeof( dspriteframe_t ) + pinframe.width * pinframe.height * bytes ); +} + +/* +==================== +R_SpriteLoadGroup + +upload a group frames +==================== +*/ +static const dframetype_t *R_SpriteLoadGroup( model_t *mod, const void *pin, mspriteframe_t **ppframe, int framenum ) +{ + const dspritegroup_t *pingroup; + mspritegroup_t *pspritegroup; + const dspriteinterval_t *pin_intervals; + float *poutintervals; + int i, groupsize, numframes; + const void *ptemp; + + pingroup = (const dspritegroup_t *)pin; + numframes = pingroup->numframes; + + groupsize = sizeof( mspritegroup_t ) + (numframes - 1) * sizeof( pspritegroup->frames[0] ); + pspritegroup = Mem_Calloc( mod->mempool, groupsize ); + pspritegroup->numframes = numframes; + + *ppframe = (mspriteframe_t *)pspritegroup; + pin_intervals = (const dspriteinterval_t *)(pingroup + 1); + poutintervals = Mem_Calloc( mod->mempool, numframes * sizeof( float )); + pspritegroup->intervals = poutintervals; + + for( i = 0; i < numframes; i++ ) + { + *poutintervals = pin_intervals->interval; + if( *poutintervals <= 0.0f ) + *poutintervals = 1.0f; // set error value + poutintervals++; + pin_intervals++; + } + + ptemp = (const void *)pin_intervals; + for( i = 0; i < numframes; i++ ) + { + ptemp = R_SpriteLoadFrame( mod, ptemp, &pspritegroup->frames[i], framenum * 10 + i ); + } + + return (const dframetype_t *)ptemp; +} + +/* +==================== +Mod_LoadSpriteModel + +load sprite model +==================== +*/ +void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, uint texFlags ) +{ + const dsprite_t *pin; + const short *numi = NULL; + const dframetype_t *pframetype; + msprite_t *psprite; + int i; + + pin = buffer; + psprite = mod->cache.data; + + if( pin->version == SPRITE_VERSION_Q1 || pin->version == SPRITE_VERSION_32 ) + numi = NULL; + else if( pin->version == SPRITE_VERSION_HL ) + numi = (const short *)((const byte*)buffer + sizeof( dsprite_hl_t )); + + r_texFlags = texFlags; + sprite_version = pin->version; + Q_strncpy( sprite_name, mod->name, sizeof( sprite_name )); + COM_StripExtension( sprite_name ); + + if( numi == NULL ) + { + rgbdata_t *pal; + + pal = gEngfuncs.FS_LoadImage( "#id.pal", (byte *)&i, 768 ); + pframetype = (const dframetype_t *)((const byte*)buffer + sizeof( dsprite_q1_t )); // pinq1 + 1 + gEngfuncs.FS_FreeImage( pal ); // palette installed, no reason to keep this data + } + else if( *numi == 256 ) + { + const byte *src = (const byte *)(numi+1); + rgbdata_t *pal; + + // install palette + switch( psprite->texFormat ) + { + case SPR_INDEXALPHA: + pal = gEngfuncs.FS_LoadImage( "#gradient.pal", src, 768 ); + break; + case SPR_ALPHTEST: + pal = gEngfuncs.FS_LoadImage( "#masked.pal", src, 768 ); + break; + default: + pal = gEngfuncs.FS_LoadImage( "#normal.pal", src, 768 ); + break; + } + + pframetype = (const dframetype_t *)(src + 768); + gEngfuncs.FS_FreeImage( pal ); // palette installed, no reason to keep this data + } + else + { + gEngfuncs.Con_DPrintf( S_ERROR "%s has wrong number of palette colors %i (should be 256)\n", mod->name, *numi ); + return; + } + + if( mod->numframes < 1 ) + return; + + for( i = 0; i < mod->numframes; i++ ) + { + frametype_t frametype = pframetype->type; + psprite->frames[i].type = (spriteframetype_t)frametype; + + switch( frametype ) + { + case FRAME_SINGLE: + Q_strncpy( group_suffix, "frame", sizeof( group_suffix )); + pframetype = R_SpriteLoadFrame( mod, pframetype + 1, &psprite->frames[i].frameptr, i ); + break; + case FRAME_GROUP: + Q_strncpy( group_suffix, "group", sizeof( group_suffix )); + pframetype = R_SpriteLoadGroup( mod, pframetype + 1, &psprite->frames[i].frameptr, i ); + break; + case FRAME_ANGLED: + Q_strncpy( group_suffix, "angle", sizeof( group_suffix )); + pframetype = R_SpriteLoadGroup( mod, pframetype + 1, &psprite->frames[i].frameptr, i ); + break; + } + if( pframetype == NULL ) break; // technically an error + } + + if( loaded ) *loaded = true; // done +} + +/* +==================== +Mod_LoadMapSprite + +Loading a bitmap image as sprite with multiple frames +as pieces of input image +==================== +*/ +void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean *loaded ) +{ + byte *src, *dst; + rgbdata_t *pix, temp; + char texname[128]; + int i, j, x, y, w, h; + int xl, yl, xh, yh; + int linedelta, numframes; + mspriteframe_t *pspriteframe; + msprite_t *psprite; + + if( loaded ) *loaded = false; + Q_snprintf( texname, sizeof( texname ), "#%s", mod->name ); + gEngfuncs.Image_SetForceFlags( IL_OVERVIEW ); + pix = gEngfuncs.FS_LoadImage( texname, buffer, size ); + gEngfuncs.Image_ClearForceFlags(); + if( !pix ) return; // bad image or something else + + mod->type = mod_sprite; + r_texFlags = 0; // no custom flags for map sprites + + if( pix->width % MAPSPRITE_SIZE ) + w = pix->width - ( pix->width % MAPSPRITE_SIZE ); + else w = pix->width; + + if( pix->height % MAPSPRITE_SIZE ) + h = pix->height - ( pix->height % MAPSPRITE_SIZE ); + else h = pix->height; + + if( w < MAPSPRITE_SIZE ) w = MAPSPRITE_SIZE; + if( h < MAPSPRITE_SIZE ) h = MAPSPRITE_SIZE; + + // resample image if needed + gEngfuncs.Image_Process( &pix, w, h, IMAGE_FORCE_RGBA|IMAGE_RESAMPLE, 0.0f ); + + w = h = MAPSPRITE_SIZE; + + // check range + if( w > pix->width ) w = pix->width; + if( h > pix->height ) h = pix->height; + + // determine how many frames we needs + numframes = (pix->width * pix->height) / (w * h); + mod->mempool = Mem_AllocPool( va( "^2%s^7", mod->name )); + psprite = Mem_Calloc( mod->mempool, sizeof( msprite_t ) + ( numframes - 1 ) * sizeof( psprite->frames )); + mod->cache.data = psprite; // make link to extradata + + psprite->type = SPR_FWD_PARALLEL_ORIENTED; + psprite->texFormat = SPR_ALPHTEST; + psprite->numframes = mod->numframes = numframes; + psprite->radius = sqrt(((w >> 1) * (w >> 1)) + ((h >> 1) * (h >> 1))); + + mod->mins[0] = mod->mins[1] = -w / 2; + mod->maxs[0] = mod->maxs[1] = w / 2; + mod->mins[2] = -h / 2; + mod->maxs[2] = h / 2; + + // create a temporary pic + memset( &temp, 0, sizeof( temp )); + temp.width = w; + temp.height = h; + temp.type = pix->type; + temp.flags = pix->flags; + temp.size = w * h * gEngfuncs.Image_GetPFDesc(temp.type)->bpp; + temp.buffer = Mem_Malloc( r_temppool, temp.size ); + temp.palette = NULL; + + // chop the image and upload into video memory + for( i = xl = yl = 0; i < numframes; i++ ) + { + xh = xl + w; + yh = yl + h; + + src = pix->buffer + ( yl * pix->width + xl ) * 4; + linedelta = ( pix->width - w ) * 4; + dst = temp.buffer; + + // cut block from source + for( y = yl; y < yh; y++ ) + { + for( x = xl; x < xh; x++ ) + for( j = 0; j < 4; j++ ) + *dst++ = *src++; + src += linedelta; + } + + // build uinque frame name + Q_snprintf( texname, sizeof( texname ), "#MAP/%s_%i%i.spr", mod->name, i / 10, i % 10 ); + + psprite->frames[i].frameptr = Mem_Calloc( mod->mempool, sizeof( mspriteframe_t )); + pspriteframe = psprite->frames[i].frameptr; + pspriteframe->width = w; + pspriteframe->height = h; + pspriteframe->up = ( h >> 1 ); + pspriteframe->left = -( w >> 1 ); + pspriteframe->down = ( h >> 1 ) - h; + pspriteframe->right = w + -( w >> 1 ); + pspriteframe->gl_texturenum = GL_LoadTextureInternal( texname, &temp, TF_IMAGE ); + + xl += w; + if( xl >= pix->width ) + { + xl = 0; + yl += h; + } + } + + gEngfuncs.FS_FreeImage( pix ); + Mem_Free( temp.buffer ); + + if( loaded ) *loaded = true; +} + +/* +==================== +Mod_UnloadSpriteModel + +release sprite model and frames +==================== +*/ +void Mod_SpriteUnloadTextures( void *data ) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, j; + + psprite = data; + + if( psprite ) + { + // release all textures + for( i = 0; i < psprite->numframes; i++ ) + { + if( psprite->frames[i].type == SPR_SINGLE ) + { + pspriteframe = psprite->frames[i].frameptr; + GL_FreeTexture( pspriteframe->gl_texturenum ); + } + else + { + pspritegroup = (mspritegroup_t *)psprite->frames[i].frameptr; + + for( j = 0; j < pspritegroup->numframes; j++ ) + { + pspriteframe = pspritegroup->frames[i]; + GL_FreeTexture( pspriteframe->gl_texturenum ); + } + } + } + } +} + +/* +================ +R_GetSpriteFrame + +assume pModel is valid +================ +*/ +mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw ) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe = NULL; + float *pintervals, fullinterval; + int i, numframes; + float targettime; + + Assert( pModel != NULL ); + psprite = pModel->cache.data; + + if( frame < 0 ) + { + frame = 0; + } + else if( frame >= psprite->numframes ) + { + if( frame > psprite->numframes ) + gEngfuncs.Con_Printf( S_WARN "R_GetSpriteFrame: no such frame %d (%s)\n", frame, pModel->name ); + frame = psprite->numframes - 1; + } + + if( psprite->frames[frame].type == SPR_SINGLE ) + { + pspriteframe = psprite->frames[frame].frameptr; + } + else if( psprite->frames[frame].type == SPR_GROUP ) + { + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pintervals = pspritegroup->intervals; + numframes = pspritegroup->numframes; + fullinterval = pintervals[numframes-1]; + + // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values + // are positive, so we don't have to worry about division by zero + targettime = gpGlobals->time - ((int)( gpGlobals->time / fullinterval )) * fullinterval; + + for( i = 0; i < (numframes - 1); i++ ) + { + if( pintervals[i] > targettime ) + break; + } + pspriteframe = pspritegroup->frames[i]; + } + else if( psprite->frames[frame].type == FRAME_ANGLED ) + { + int angleframe = (int)(Q_rint(( RI.viewangles[1] - yaw + 45.0f ) / 360 * 8) - 4) & 7; + + // e.g. doom-style sprite monsters + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pspriteframe = pspritegroup->frames[angleframe]; + } + + return pspriteframe; +} + +/* +================ +R_GetSpriteFrameInterpolant + +NOTE: we using prevblending[0] and [1] for holds interval +between frames where are we lerping +================ +*/ +float R_GetSpriteFrameInterpolant( cl_entity_t *ent, mspriteframe_t **oldframe, mspriteframe_t **curframe ) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + int i, j, numframes, frame; + float lerpFrac, time, jtime, jinterval; + float *pintervals, fullinterval, targettime; + int m_fDoInterp; + + psprite = ent->model->cache.data; + frame = (int)ent->curstate.frame; + lerpFrac = 1.0f; + + // misc info + m_fDoInterp = (ent->curstate.effects & EF_NOINTERP) ? false : true; + + if( frame < 0 ) + { + frame = 0; + } + else if( frame >= psprite->numframes ) + { + gEngfuncs.Con_Reportf( S_WARN "R_GetSpriteFrameInterpolant: no such frame %d (%s)\n", frame, ent->model->name ); + frame = psprite->numframes - 1; + } + + if( psprite->frames[frame].type == FRAME_SINGLE ) + { + if( m_fDoInterp ) + { + if( ent->latched.prevblending[0] >= psprite->numframes || psprite->frames[ent->latched.prevblending[0]].type != FRAME_SINGLE ) + { + // this can be happens when rendering switched between single and angled frames + // or change model on replace delta-entity + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 1.0f; + } + + if( ent->latched.sequencetime < gpGlobals->time ) + { + if( frame != ent->latched.prevblending[1] ) + { + ent->latched.prevblending[0] = ent->latched.prevblending[1]; + ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + else lerpFrac = (gpGlobals->time - ent->latched.sequencetime) * 11.0f; + } + else + { + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + } + else + { + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + lerpFrac = 1.0f; + } + + if( ent->latched.prevblending[0] >= psprite->numframes ) + { + // reset interpolation on change model + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + + // get the interpolated frames + if( oldframe ) *oldframe = psprite->frames[ent->latched.prevblending[0]].frameptr; + if( curframe ) *curframe = psprite->frames[frame].frameptr; + } + else if( psprite->frames[frame].type == FRAME_GROUP ) + { + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pintervals = pspritegroup->intervals; + numframes = pspritegroup->numframes; + fullinterval = pintervals[numframes-1]; + jinterval = pintervals[1] - pintervals[0]; + time = gpGlobals->time; + jtime = 0.0f; + + // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values + // are positive, so we don't have to worry about division by zero + targettime = time - ((int)(time / fullinterval)) * fullinterval; + + // LordHavoc: since I can't measure the time properly when it loops from numframes - 1 to 0, + // i instead measure the time of the first frame, hoping it is consistent + for( i = 0, j = numframes - 1; i < (numframes - 1); i++ ) + { + if( pintervals[i] > targettime ) + break; + j = i; + jinterval = pintervals[i] - jtime; + jtime = pintervals[i]; + } + + if( m_fDoInterp ) + lerpFrac = (targettime - jtime) / jinterval; + else j = i; // no lerping + + // get the interpolated frames + if( oldframe ) *oldframe = pspritegroup->frames[j]; + if( curframe ) *curframe = pspritegroup->frames[i]; + } + else if( psprite->frames[frame].type == FRAME_ANGLED ) + { + // e.g. doom-style sprite monsters + float yaw = ent->angles[YAW]; + int angleframe = (int)(Q_rint(( RI.viewangles[1] - yaw + 45.0f ) / 360 * 8) - 4) & 7; + + if( m_fDoInterp ) + { + if( ent->latched.prevblending[0] >= psprite->numframes || psprite->frames[ent->latched.prevblending[0]].type != FRAME_ANGLED ) + { + // this can be happens when rendering switched between single and angled frames + // or change model on replace delta-entity + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 1.0f; + } + + if( ent->latched.sequencetime < gpGlobals->time ) + { + if( frame != ent->latched.prevblending[1] ) + { + ent->latched.prevblending[0] = ent->latched.prevblending[1]; + ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + else lerpFrac = (gpGlobals->time - ent->latched.sequencetime) * ent->curstate.framerate; + } + else + { + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + } + else + { + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + lerpFrac = 1.0f; + } + + pspritegroup = (mspritegroup_t *)psprite->frames[ent->latched.prevblending[0]].frameptr; + if( oldframe ) *oldframe = pspritegroup->frames[angleframe]; + + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + if( curframe ) *curframe = pspritegroup->frames[angleframe]; + } + + return lerpFrac; +} + +/* +================ +R_CullSpriteModel + +Cull sprite model by bbox +================ +*/ +qboolean R_CullSpriteModel( cl_entity_t *e, vec3_t origin ) +{ + vec3_t sprite_mins, sprite_maxs; + float scale = 1.0f; + + if( !e->model->cache.data ) + return true; + + if( e->curstate.scale > 0.0f ) + scale = e->curstate.scale; + + // scale original bbox (no rotation for sprites) + VectorScale( e->model->mins, scale, sprite_mins ); + VectorScale( e->model->maxs, scale, sprite_maxs ); + + sprite_radius = RadiusFromBounds( sprite_mins, sprite_maxs ); + + VectorAdd( sprite_mins, origin, sprite_mins ); + VectorAdd( sprite_maxs, origin, sprite_maxs ); + + return R_CullModel( e, sprite_mins, sprite_maxs ); +} + +/* +================ +R_GlowSightDistance + +Set sprite brightness factor +================ +*/ +static float R_SpriteGlowBlend( vec3_t origin, int rendermode, int renderfx, float *pscale ) +{ + float dist, brightness; + vec3_t glowDist; + pmtrace_t *tr; + + VectorSubtract( origin, RI.vieworg, glowDist ); + dist = VectorLength( glowDist ); + + if( RP_NORMALPASS( )) + { + tr = gEngfuncs.EV_VisTraceLine( RI.vieworg, origin, r_traceglow->value ? PM_GLASS_IGNORE : (PM_GLASS_IGNORE|PM_STUDIO_IGNORE)); + + if(( 1.0f - tr->fraction ) * dist > 8.0f ) + return 0.0f; + } + + if( renderfx == kRenderFxNoDissipation ) + return 1.0f; + + brightness = GLARE_FALLOFF / ( dist * dist ); + brightness = bound( 0.05f, brightness, 1.0f ); + *pscale *= dist * ( 1.0f / 200.0f ); + + return brightness; +} + +/* +================ +R_SpriteOccluded + +Do occlusion test for glow-sprites +================ +*/ +qboolean R_SpriteOccluded( cl_entity_t *e, vec3_t origin, float *pscale ) +{ + if( e->curstate.rendermode == kRenderGlow ) + { + float blend; + vec3_t v; + + TriWorldToScreen( origin, v ); + + if( v[0] < RI.viewport[0] || v[0] > RI.viewport[0] + RI.viewport[2] ) + return true; // do scissor + if( v[1] < RI.viewport[1] || v[1] > RI.viewport[1] + RI.viewport[3] ) + return true; // do scissor + + blend = R_SpriteGlowBlend( origin, e->curstate.rendermode, e->curstate.renderfx, pscale ); + tr.blend *= blend; + + if( blend <= 0.01f ) + return true; // faded + } + else + { + if( R_CullSpriteModel( e, origin )) + return true; + } + + return false; +} + +/* +================= +R_DrawSpriteQuad +================= +*/ +static void R_DrawSpriteQuad( mspriteframe_t *frame, vec3_t org, vec3_t v_right, vec3_t v_up, float scale ) +{ + vec3_t point; + + r_stats.c_sprite_polys++; + + gu_vert_t* const out = ( gu_vert_t* )sceGuGetMemory( sizeof( gu_vert_t ) * 4 ); + VectorMA( org, frame->down * scale, v_up, point ); + VectorMA( point, frame->left * scale, v_right, point ); + out[0].uv[0] = 0.0f; + out[0].uv[1] = 1.0f; + VectorCopy( point, out[0].xyz ); + VectorMA( org, frame->up * scale, v_up, point ); + VectorMA( point, frame->left * scale, v_right, point ); + out[1].uv[0] = 0.0f; + out[1].uv[1] = 0.0f; + VectorCopy( point, out[1].xyz ); + VectorMA( org, frame->up * scale, v_up, point ); + VectorMA( point, frame->right * scale, v_right, point ); + out[2].uv[0] = 1.0f; + out[2].uv[1] = 0.0f; + VectorCopy( point, out[2].xyz ); + VectorMA( org, frame->down * scale, v_up, point ); + VectorMA( point, frame->right * scale, v_right, point ); + out[3].uv[0] = 1.0f; + out[3].uv[1] = 1.0f; + VectorCopy( point, out[3].xyz ); + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, out ); +} + +static qboolean R_SpriteHasLightmap( cl_entity_t *e, int texFormat ) +{ + if( !r_sprite_lighting->value ) + return false; + + if( texFormat != SPR_ALPHTEST ) + return false; + + if( e->curstate.effects & EF_FULLBRIGHT ) + return false; + + if( e->curstate.renderamt <= 127 ) + return false; + + switch( e->curstate.rendermode ) + { + case kRenderNormal: + case kRenderTransAlpha: + case kRenderTransTexture: + break; + default: + return false; + } + + return true; +} + +/* +================= +R_SpriteAllowLerping +================= +*/ +static qboolean R_SpriteAllowLerping( cl_entity_t *e, msprite_t *psprite ) +{ + if( !r_sprite_lerping->value ) + return false; + + if( psprite->numframes <= 1 ) + return false; + + if( psprite->texFormat != SPR_ADDITIVE ) + return false; + + if( e->curstate.rendermode == kRenderNormal || e->curstate.rendermode == kRenderTransAlpha ) + return false; + + return true; +} + +/* +================= +R_DrawSpriteModel +================= +*/ +void R_DrawSpriteModel( cl_entity_t *e ) +{ + mspriteframe_t *frame, *oldframe; + msprite_t *psprite; + model_t *model; + int i, type; + float angle, dot, sr, cr; + float lerp = 1.0f, ilerp, scale; + vec3_t v_forward, v_right, v_up; + vec3_t origin, color, color2 = { 0.0f }; + + if( RI.params & RP_ENVVIEW ) + return; + + model = e->model; + psprite = (msprite_t * )model->cache.data; + VectorCopy( e->origin, origin ); // set render origin + + // do movewith + if( e->curstate.aiment > 0 && e->curstate.movetype == MOVETYPE_FOLLOW ) + { + cl_entity_t *parent; + + parent = gEngfuncs.GetEntityByIndex( e->curstate.aiment ); + + if( parent && parent->model ) + { + if( parent->model->type == mod_studio && e->curstate.body > 0 ) + { + int num = bound( 1, e->curstate.body, MAXSTUDIOATTACHMENTS ); + VectorCopy( parent->attachment[num-1], origin ); + } + else VectorCopy( parent->origin, origin ); + } + } + + scale = e->curstate.scale; + if( !scale ) scale = 1.0f; + + if( R_SpriteOccluded( e, origin, &scale )) + return; // sprite culled + + r_stats.c_sprite_models_drawn++; + + if( e->curstate.rendermode == kRenderGlow || e->curstate.rendermode == kRenderTransAdd ) + R_AllowFog( false ); + + // select properly rendermode + switch( e->curstate.rendermode ) + { + case kRenderTransAlpha: + sceGuDepthMask( GU_TRUE ); + // fallthrough + case kRenderTransColor: + case kRenderTransTexture: + sceGuEnable( GU_BLEND ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + break; + case kRenderGlow: + sceGuDisable( GU_DEPTH_TEST ); + // fallthrough + case kRenderTransAdd: + sceGuEnable( GU_BLEND ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, GUBLEND1 ); + sceGuDepthMask( GU_TRUE ); + break; + case kRenderNormal: + default: + sceGuDisable( GU_BLEND ); + break; + } + + // all sprites can have color + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + sceGuEnable( GU_ALPHA_TEST ); + + // NOTE: never pass sprites with rendercolor '0 0 0' it's a stupid Valve Hammer Editor bug + if( e->curstate.rendercolor.r || e->curstate.rendercolor.g || e->curstate.rendercolor.b ) + { + color[0] = (float)e->curstate.rendercolor.r * ( 1.0f / 255.0f ); + color[1] = (float)e->curstate.rendercolor.g * ( 1.0f / 255.0f ); + color[2] = (float)e->curstate.rendercolor.b * ( 1.0f / 255.0f ); + } + else + { + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + } + + if( R_SpriteHasLightmap( e, psprite->texFormat )) + { + colorVec lightColor = R_LightPoint( origin ); + // FIXME: collect light from dlights? + color2[0] = (float)lightColor.r * ( 1.0f / 255.0f ); + color2[1] = (float)lightColor.g * ( 1.0f / 255.0f ); + color2[2] = (float)lightColor.b * ( 1.0f / 255.0f ); + + // NOTE: sprites with 'lightmap' looks ugly when alpha func is GL_GREATER 0.0 + sceGuAlphaFunc( GU_GREATER, 0x7f, 0xff ); + } + + if( R_SpriteAllowLerping( e, psprite )) + lerp = R_GetSpriteFrameInterpolant( e, &oldframe, &frame ); + else frame = oldframe = R_GetSpriteFrame( model, e->curstate.frame, e->angles[YAW] ); + + type = psprite->type; + + // automatically roll parallel sprites if requested + if( e->angles[ROLL] != 0.0f && type == SPR_FWD_PARALLEL ) + type = SPR_FWD_PARALLEL_ORIENTED; + + switch( type ) + { + case SPR_ORIENTED: + AngleVectors( e->angles, v_forward, v_right, v_up ); + VectorScale( v_forward, 0.01f, v_forward ); // to avoid z-fighting + VectorSubtract( origin, v_forward, origin ); + break; + case SPR_FACING_UPRIGHT: + VectorSet( v_right, origin[1] - RI.vieworg[1], -(origin[0] - RI.vieworg[0]), 0.0f ); + VectorSet( v_up, 0.0f, 0.0f, 1.0f ); + VectorNormalize( v_right ); + break; + case SPR_FWD_PARALLEL_UPRIGHT: + dot = RI.vforward[2]; + if(( dot > 0.999848f ) || ( dot < -0.999848f )) // cos(1 degree) = 0.999848 + return; // invisible + VectorSet( v_up, 0.0f, 0.0f, 1.0f ); + VectorSet( v_right, RI.vforward[1], -RI.vforward[0], 0.0f ); + VectorNormalize( v_right ); + break; + case SPR_FWD_PARALLEL_ORIENTED: + angle = e->angles[ROLL] * (M_PI2 / 360.0f); + SinCos( angle, &sr, &cr ); + for( i = 0; i < 3; i++ ) + { + v_right[i] = (RI.vright[i] * cr + RI.vup[i] * sr); + v_up[i] = RI.vright[i] * -sr + RI.vup[i] * cr; + } + break; + case SPR_FWD_PARALLEL: // normal sprite + default: + VectorCopy( RI.vright, v_right ); + VectorCopy( RI.vup, v_up ); + break; + } + + if( psprite->facecull == SPR_CULL_NONE ) + GL_Cull( GL_NONE ); + + if( oldframe == frame ) + { + // draw the single non-lerped frame + sceGuColor( GUCOLOR4F( color[0], color[1], color[2], tr.blend) ); + GL_Bind( XASH_TEXTURE0, frame->gl_texturenum ); + R_DrawSpriteQuad( frame, origin, v_right, v_up, scale ); + } + else + { + // draw two combined lerped frames + lerp = bound( 0.0f, lerp, 1.0f ); + ilerp = 1.0f - lerp; + + if( ilerp != 0.0f ) + { + sceGuColor( GUCOLOR4F( color[0], color[1], color[2], tr.blend * ilerp ) ); + GL_Bind( XASH_TEXTURE0, oldframe->gl_texturenum ); + R_DrawSpriteQuad( oldframe, origin, v_right, v_up, scale ); + } + + if( lerp != 0.0f ) + { + sceGuColor( GUCOLOR4F( color[0], color[1], color[2], tr.blend * ilerp ) ); + GL_Bind( XASH_TEXTURE0, frame->gl_texturenum ); + R_DrawSpriteQuad( frame, origin, v_right, v_up, scale ); + } + } + + // draw the sprite 'lightmap' :-) + if( R_SpriteHasLightmap( e, psprite->texFormat )) + { + if( !r_lightmap->value ) + sceGuEnable( GU_BLEND ); + else sceGuDisable( GU_BLEND ); + sceGuDepthFunc( GU_EQUAL ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuBlendFunc( GU_ADD, GU_FIX, GU_SRC_COLOR, GUBLEND0, 0 ); + sceGuTexFunc( GU_TFX_MODULATE , GU_TCC_RGBA ); + + sceGuColor( GUCOLOR4F( color2[0], color2[1], color2[2], tr.blend ) ); + GL_Bind( XASH_TEXTURE0, tr.whiteTexture ); + R_DrawSpriteQuad( frame, origin, v_right, v_up, scale ); + sceGuAlphaFunc( GU_GREATER, DEFAULT_ALPHATEST, 0xff ); + sceGuDepthFunc( GU_LEQUAL ); + } + + if( psprite->facecull == SPR_CULL_NONE ) + GL_Cull( GL_FRONT ); + + sceGuDisable( GU_ALPHA_TEST ); + sceGuDepthMask( GU_FALSE ); + + if( e->curstate.rendermode == kRenderGlow || e->curstate.rendermode == kRenderTransAdd ) + R_AllowFog( true ); + + if( e->curstate.rendermode != kRenderNormal ) + { + sceGuDisable( GU_BLEND ); + sceGuTexFunc( GU_TFX_REPLACE, GU_TCC_RGBA ); + sceGuEnable( GU_DEPTH_TEST ); + } +} diff --git a/ref_gu/gu_studio.c b/ref_gu/gu_studio.c new file mode 100644 index 00000000..9aba3679 --- /dev/null +++ b/ref_gu/gu_studio.c @@ -0,0 +1,4142 @@ +/* +gu_studio.c - studio model renderer +Copyright (C) 2010 Uncle Mike +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "xash3d_mathlib.h" +#include "const.h" +#include "r_studioint.h" +#include "triangleapi.h" +#include "studio.h" +#include "pm_local.h" +#include "cl_tent.h" +//#include "client.h" +#include "pmtrace.h" + +#define EVENT_CLIENT 5000 // less than this value it's a server-side studio events +#define MAX_LOCALLIGHTS 4 + +typedef struct +{ + char name[MAX_OSPATH]; + char modelname[MAX_OSPATH]; + model_t *model; +} player_model_t; + +cvar_t *r_glowshellfreq; + +cvar_t r_shadows = { "r_shadows", "0", 0 }; + +static vec3_t hullcolor[8] = +{ +{ 1.0f, 1.0f, 1.0f }, +{ 1.0f, 0.5f, 0.5f }, +{ 0.5f, 1.0f, 0.5f }, +{ 1.0f, 1.0f, 0.5f }, +{ 0.5f, 0.5f, 1.0f }, +{ 1.0f, 0.5f, 1.0f }, +{ 0.5f, 1.0f, 1.0f }, +{ 1.0f, 1.0f, 1.0f }, +}; + +typedef struct sortedmesh_s +{ + mstudiomesh_t *mesh; + int flags; // face flags +} sortedmesh_t; + +#if XASH_LOW_MEMORY + #undef MAXSTUDIOVERTS + #define MAXSTUDIOVERTS 1024 +#endif + +typedef struct +{ + double time; + double frametime; + int framecount; // studio framecount + qboolean interpolate; + int rendermode; + float blend; // blend value + + // bones + matrix3x4 rotationmatrix; + matrix3x4 bonestransform[MAXSTUDIOBONES]; + matrix3x4 lighttransform[MAXSTUDIOBONES]; + + // boneweighting stuff + matrix3x4 worldtransform[MAXSTUDIOBONES]; + + // cached bones + matrix3x4 cached_bonestransform[MAXSTUDIOBONES]; + matrix3x4 cached_lighttransform[MAXSTUDIOBONES]; + char cached_bonenames[MAXSTUDIOBONES][32]; + int cached_numbones; // number of bones in cache + + sortedmesh_t meshes[MAXSTUDIOMESHES]; // sorted meshes + vec3_t verts[MAXSTUDIOVERTS]; + vec3_t norms[MAXSTUDIOVERTS]; + + // lighting state + float ambientlight; + float shadelight; + vec3_t lightvec; // averaging light direction + vec3_t lightspot; // shadow spot + vec3_t lightcolor; // averaging lightcolor + vec3_t blightvec[MAXSTUDIOBONES]; // bone light vecs + vec3_t lightvalues[MAXSTUDIOVERTS]; // precomputed lightvalues per each shared vertex of submodel + + // chrome stuff + vec3_t chrome_origin; + vec2_t chrome[MAXSTUDIOVERTS]; // texture coords for surface normals + vec3_t chromeright[MAXSTUDIOBONES]; // chrome vector "right" in bone reference frames + vec3_t chromeup[MAXSTUDIOBONES]; // chrome vector "up" in bone reference frames + int chromeage[MAXSTUDIOBONES]; // last time chrome vectors were updated + + // glowshell stuff + int normaltable[MAXSTUDIOVERTS]; // glowshell uses this + + // elights cache + int numlocallights; + int lightage[MAXSTUDIOBONES]; + dlight_t *locallight[MAX_LOCALLIGHTS]; + color24 locallightcolor[MAX_LOCALLIGHTS]; + vec4_t lightpos[MAXSTUDIOVERTS][MAX_LOCALLIGHTS]; + vec3_t lightbonepos[MAXSTUDIOBONES][MAX_LOCALLIGHTS]; + float locallightR2[MAX_LOCALLIGHTS]; + + // playermodels + player_model_t player_models[MAX_CLIENTS]; + + // drawelements renderer + gu_vert_ftcv_t arrayverts[MAXSTUDIOVERTS]; + unsigned short arrayelems[MAXSTUDIOVERTS * 6]; + uint numverts; + uint numelems; +} studio_draw_state_t; + +// studio-related cvars +static cvar_t *r_studio_sort_textures; +static cvar_t *r_drawviewmodel; +cvar_t *cl_righthand = NULL; +static cvar_t *cl_himodels; +static cvar_t *r_studio_drawelements; + +static r_studio_interface_t *pStudioDraw; +static studio_draw_state_t g_studio; // global studio state + +// global variables +static qboolean m_fDoRemap; +mstudiomodel_t *m_pSubModel; +mstudiobodyparts_t *m_pBodyPart; +player_info_t *m_pPlayerInfo; +studiohdr_t *m_pStudioHeader; +float m_flGaitMovement; +int g_iBackFaceCull; +int g_nTopColor, g_nBottomColor; // remap colors +int g_nFaceFlags, g_nForceFaceFlags; + +/* +==================== +R_StudioInit + +==================== +*/ +void R_StudioInit( void ) +{ + cl_himodels = gEngfuncs.Cvar_Get( "cl_himodels", "0", FCVAR_ARCHIVE, "draw high-resolution player models in multiplayer" ); + r_studio_sort_textures = gEngfuncs.Cvar_Get( "r_studio_sort_textures", "0", FCVAR_ARCHIVE, "change draw order for additive meshes" ); + r_drawviewmodel = gEngfuncs.Cvar_Get( "r_drawviewmodel", "1", 0, "draw firstperson weapon model" ); + r_studio_drawelements = gEngfuncs.Cvar_Get( "r_studio_drawelements", "0", FCVAR_ARCHIVE, "use glDrawElements for studiomodels" ); + + Matrix3x4_LoadIdentity( g_studio.rotationmatrix ); + r_glowshellfreq = gEngfuncs.Cvar_Get( "r_glowshellfreq", "2.2", 0, "glowing shell frequency update" ); + + // g-cont. cvar disabled by Valve +// gEngfuncs.Cvar_RegisterVariable( &r_shadows ); + + g_studio.interpolate = true; + + g_studio.framecount = 0; + m_fDoRemap = false; +} + +/* +================ +R_StudioSetupTimings + +init current time for a given model +================ +*/ +_inline void R_StudioSetupTimings( void ) +{ + if( RI.drawWorld ) + { + // synchronize with server time + g_studio.time = gpGlobals->time; + g_studio.frametime = gpGlobals->time - gpGlobals->oldtime; + } + else + { + // menu stuff + g_studio.time = gpGlobals->realtime; + g_studio.frametime = gpGlobals->frametime; + } +} + +/* +================ +R_AllowFlipViewModel + +should a flip the viewmodel if cl_righthand is set to 1 +================ +*/ +_inline qboolean R_AllowFlipViewModel( cl_entity_t *e ) +{ + if( cl_righthand && cl_righthand->value > 0 ) + { + if( e == gEngfuncs.GetViewModel() ) + return true; + } + + return false; +} + +/* +================ +R_StudioComputeBBox + +Compute a full bounding box for current sequence +================ +*/ +_inline qboolean R_StudioComputeBBox( vec3_t bbox[8] ) +{ + vec3_t studio_mins, studio_maxs; + vec3_t mins, maxs, p1, p2; + cl_entity_t *e = RI.currententity; + mstudioseqdesc_t *pseqdesc; + int i; + + if( !m_pStudioHeader ) + return false; + + // check if we have valid mins\maxs + if( !VectorCompare( vec3_origin, RI.currentmodel->mins )) + { + // clipping bounding box + VectorCopy( RI.currentmodel->mins, mins ); + VectorCopy( RI.currentmodel->maxs, maxs ); + } + else + { + ClearBounds( mins, maxs ); + } + + // check sequence range + if( e->curstate.sequence < 0 || e->curstate.sequence >= m_pStudioHeader->numseq ) + e->curstate.sequence = 0; + + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + e->curstate.sequence; + + // add sequence box to the model box + AddPointToBounds( pseqdesc->bbmin, mins, maxs ); + AddPointToBounds( pseqdesc->bbmax, mins, maxs ); + ClearBounds( studio_mins, studio_maxs ); + + // compute a full bounding box + for( i = 0; i < 8; i++ ) + { + p1[0] = ( i & 1 ) ? mins[0] : maxs[0]; + p1[1] = ( i & 2 ) ? mins[1] : maxs[1]; + p1[2] = ( i & 4 ) ? mins[2] : maxs[2]; + + Matrix3x4_VectorTransform( g_studio.rotationmatrix, p1, p2 ); + AddPointToBounds( p2, studio_mins, studio_maxs ); + if( bbox ) VectorCopy( p2, bbox[i] ); + } + + if( !bbox && R_CullModel( e, studio_mins, studio_maxs )) + return false; // model culled + return true; // visible +} + +void R_StudioComputeSkinMatrix( mstudioboneweight_t *boneweights, matrix3x4 result ) +{ + vec4_t flWeight; + int i, numbones = 0; + float flTotal; + + for( i = 0; i < MAXSTUDIOBONEWEIGHTS; i++ ) + { + if( boneweights->bone[i] != -1 ) + numbones++; + } + + if( numbones == 4 ) + { + vec4_t *boneMat0 = (vec4_t *)g_studio.worldtransform[boneweights->bone[0]]; + vec4_t *boneMat1 = (vec4_t *)g_studio.worldtransform[boneweights->bone[1]]; + vec4_t *boneMat2 = (vec4_t *)g_studio.worldtransform[boneweights->bone[2]]; + vec4_t *boneMat3 = (vec4_t *)g_studio.worldtransform[boneweights->bone[3]]; + flWeight[0] = boneweights->weight[0] / 255.0f; + flWeight[1] = boneweights->weight[1] / 255.0f; + flWeight[2] = boneweights->weight[2] / 255.0f; + flWeight[3] = boneweights->weight[3] / 255.0f; + flTotal = flWeight[0] + flWeight[1] + flWeight[2] + flWeight[3]; + + if( flTotal < 1.0f ) flWeight[0] += 1.0f - flTotal; // compensate rounding error + + result[0][0] = boneMat0[0][0] * flWeight[0] + boneMat1[0][0] * flWeight[1] + boneMat2[0][0] * flWeight[2] + boneMat3[0][0] * flWeight[3]; + result[0][1] = boneMat0[0][1] * flWeight[0] + boneMat1[0][1] * flWeight[1] + boneMat2[0][1] * flWeight[2] + boneMat3[0][1] * flWeight[3]; + result[0][2] = boneMat0[0][2] * flWeight[0] + boneMat1[0][2] * flWeight[1] + boneMat2[0][2] * flWeight[2] + boneMat3[0][2] * flWeight[3]; + result[0][3] = boneMat0[0][3] * flWeight[0] + boneMat1[0][3] * flWeight[1] + boneMat2[0][3] * flWeight[2] + boneMat3[0][3] * flWeight[3]; + result[1][0] = boneMat0[1][0] * flWeight[0] + boneMat1[1][0] * flWeight[1] + boneMat2[1][0] * flWeight[2] + boneMat3[1][0] * flWeight[3]; + result[1][1] = boneMat0[1][1] * flWeight[0] + boneMat1[1][1] * flWeight[1] + boneMat2[1][1] * flWeight[2] + boneMat3[1][1] * flWeight[3]; + result[1][2] = boneMat0[1][2] * flWeight[0] + boneMat1[1][2] * flWeight[1] + boneMat2[1][2] * flWeight[2] + boneMat3[1][2] * flWeight[3]; + result[1][3] = boneMat0[1][3] * flWeight[0] + boneMat1[1][3] * flWeight[1] + boneMat2[1][3] * flWeight[2] + boneMat3[1][3] * flWeight[3]; + result[2][0] = boneMat0[2][0] * flWeight[0] + boneMat1[2][0] * flWeight[1] + boneMat2[2][0] * flWeight[2] + boneMat3[2][0] * flWeight[3]; + result[2][1] = boneMat0[2][1] * flWeight[0] + boneMat1[2][1] * flWeight[1] + boneMat2[2][1] * flWeight[2] + boneMat3[2][1] * flWeight[3]; + result[2][2] = boneMat0[2][2] * flWeight[0] + boneMat1[2][2] * flWeight[1] + boneMat2[2][2] * flWeight[2] + boneMat3[2][2] * flWeight[3]; + result[2][3] = boneMat0[2][3] * flWeight[0] + boneMat1[2][3] * flWeight[1] + boneMat2[2][3] * flWeight[2] + boneMat3[2][3] * flWeight[3]; + } + else if( numbones == 3 ) + { + vec4_t *boneMat0 = (vec4_t *)g_studio.worldtransform[boneweights->bone[0]]; + vec4_t *boneMat1 = (vec4_t *)g_studio.worldtransform[boneweights->bone[1]]; + vec4_t *boneMat2 = (vec4_t *)g_studio.worldtransform[boneweights->bone[2]]; + flWeight[0] = boneweights->weight[0] / 255.0f; + flWeight[1] = boneweights->weight[1] / 255.0f; + flWeight[2] = boneweights->weight[2] / 255.0f; + flTotal = flWeight[0] + flWeight[1] + flWeight[2]; + + if( flTotal < 1.0f ) flWeight[0] += 1.0f - flTotal; // compensate rounding error + + result[0][0] = boneMat0[0][0] * flWeight[0] + boneMat1[0][0] * flWeight[1] + boneMat2[0][0] * flWeight[2]; + result[0][1] = boneMat0[0][1] * flWeight[0] + boneMat1[0][1] * flWeight[1] + boneMat2[0][1] * flWeight[2]; + result[0][2] = boneMat0[0][2] * flWeight[0] + boneMat1[0][2] * flWeight[1] + boneMat2[0][2] * flWeight[2]; + result[0][3] = boneMat0[0][3] * flWeight[0] + boneMat1[0][3] * flWeight[1] + boneMat2[0][3] * flWeight[2]; + result[1][0] = boneMat0[1][0] * flWeight[0] + boneMat1[1][0] * flWeight[1] + boneMat2[1][0] * flWeight[2]; + result[1][1] = boneMat0[1][1] * flWeight[0] + boneMat1[1][1] * flWeight[1] + boneMat2[1][1] * flWeight[2]; + result[1][2] = boneMat0[1][2] * flWeight[0] + boneMat1[1][2] * flWeight[1] + boneMat2[1][2] * flWeight[2]; + result[1][3] = boneMat0[1][3] * flWeight[0] + boneMat1[1][3] * flWeight[1] + boneMat2[1][3] * flWeight[2]; + result[2][0] = boneMat0[2][0] * flWeight[0] + boneMat1[2][0] * flWeight[1] + boneMat2[2][0] * flWeight[2]; + result[2][1] = boneMat0[2][1] * flWeight[0] + boneMat1[2][1] * flWeight[1] + boneMat2[2][1] * flWeight[2]; + result[2][2] = boneMat0[2][2] * flWeight[0] + boneMat1[2][2] * flWeight[1] + boneMat2[2][2] * flWeight[2]; + result[2][3] = boneMat0[2][3] * flWeight[0] + boneMat1[2][3] * flWeight[1] + boneMat2[2][3] * flWeight[2]; + } + else if( numbones == 2 ) + { + vec4_t *boneMat0 = (vec4_t *)g_studio.worldtransform[boneweights->bone[0]]; + vec4_t *boneMat1 = (vec4_t *)g_studio.worldtransform[boneweights->bone[1]]; + flWeight[0] = boneweights->weight[0] / 255.0f; + flWeight[1] = boneweights->weight[1] / 255.0f; + flTotal = flWeight[0] + flWeight[1]; + + if( flTotal < 1.0f ) flWeight[0] += 1.0f - flTotal; // compensate rounding error + + result[0][0] = boneMat0[0][0] * flWeight[0] + boneMat1[0][0] * flWeight[1]; + result[0][1] = boneMat0[0][1] * flWeight[0] + boneMat1[0][1] * flWeight[1]; + result[0][2] = boneMat0[0][2] * flWeight[0] + boneMat1[0][2] * flWeight[1]; + result[0][3] = boneMat0[0][3] * flWeight[0] + boneMat1[0][3] * flWeight[1]; + result[1][0] = boneMat0[1][0] * flWeight[0] + boneMat1[1][0] * flWeight[1]; + result[1][1] = boneMat0[1][1] * flWeight[0] + boneMat1[1][1] * flWeight[1]; + result[1][2] = boneMat0[1][2] * flWeight[0] + boneMat1[1][2] * flWeight[1]; + result[1][3] = boneMat0[1][3] * flWeight[0] + boneMat1[1][3] * flWeight[1]; + result[2][0] = boneMat0[2][0] * flWeight[0] + boneMat1[2][0] * flWeight[1]; + result[2][1] = boneMat0[2][1] * flWeight[0] + boneMat1[2][1] * flWeight[1]; + result[2][2] = boneMat0[2][2] * flWeight[0] + boneMat1[2][2] * flWeight[1]; + result[2][3] = boneMat0[2][3] * flWeight[0] + boneMat1[2][3] * flWeight[1]; + } + else + { + Matrix3x4_Copy( result, g_studio.worldtransform[boneweights->bone[0]] ); + } +} + +/* +=============== +pfnGetCurrentEntity + +=============== +*/ +static cl_entity_t *pfnGetCurrentEntity( void ) +{ + return RI.currententity; +} + +/* +=============== +pfnPlayerInfo + +=============== +*/ +player_info_t *pfnPlayerInfo( int index ) +{ + if( !RI.drawWorld ) + index = -1; + + return gEngfuncs.pfnPlayerInfo( index ); +} + +/* +=============== +pfnMod_ForName + +=============== +*/ +static model_t *pfnMod_ForName( const char *model, int crash ) +{ + return gEngfuncs.Mod_ForName( model, crash, false ); +} + +/* +=============== +pfnGetPlayerState + +=============== +*/ +entity_state_t *R_StudioGetPlayerState( int index ) +{ + if( !RI.drawWorld ) + return &RI.currententity->curstate; + + return gEngfuncs.pfnGetPlayerState( index ); +} + +/* +=============== +pfnGetViewEntity + +=============== +*/ +static cl_entity_t *pfnGetViewEntity( void ) +{ + return gEngfuncs.GetViewModel(); +} + +/* +=============== +pfnGetEngineTimes + +=============== +*/ +static void pfnGetEngineTimes( int *framecount, double *current, double *old ) +{ + if( framecount ) *framecount = tr.realframecount; + if( current ) *current = gpGlobals->time; + if( old ) *old = gpGlobals->oldtime; +} + +/* +=============== +pfnGetViewInfo + +=============== +*/ +static void pfnGetViewInfo( float *origin, float *upv, float *rightv, float *forwardv ) +{ + if( origin ) VectorCopy( RI.vieworg, origin ); + if( forwardv ) VectorCopy( RI.vforward, forwardv ); + if( rightv ) VectorCopy( RI.vright, rightv ); + if( upv ) VectorCopy( RI.vup, upv ); +} + +/* +=============== +R_GetChromeSprite + +=============== +*/ +static model_t *R_GetChromeSprite( void ) +{ + return gEngfuncs.GetDefaultSprite( REF_CHROME_SPRITE ); +} + +/* +=============== +pfnGetModelCounters + +=============== +*/ +static void pfnGetModelCounters( int **s, int **a ) +{ + *s = &g_studio.framecount; + *a = &r_stats.c_studio_models_drawn; +} + +/* +=============== +pfnGetAliasScale + +=============== +*/ +static void pfnGetAliasScale( float *x, float *y ) +{ + if( x ) *x = 1.0f; + if( y ) *y = 1.0f; +} + +/* +=============== +pfnStudioGetBoneTransform + +=============== +*/ +static float ****pfnStudioGetBoneTransform( void ) +{ + return (float ****)g_studio.bonestransform; +} + +/* +=============== +pfnStudioGetLightTransform + +=============== +*/ +static float ****pfnStudioGetLightTransform( void ) +{ + return (float ****)g_studio.lighttransform; +} + +/* +=============== +pfnStudioGetAliasTransform + +=============== +*/ +static float ***pfnStudioGetAliasTransform( void ) +{ + return NULL; +} + +/* +=============== +pfnStudioGetRotationMatrix + +=============== +*/ +static float ***pfnStudioGetRotationMatrix( void ) +{ + return (float ***)g_studio.rotationmatrix; +} + +/* +==================== +StudioPlayerBlend + +==================== +*/ +_inline void R_StudioPlayerBlend( mstudioseqdesc_t *pseqdesc, int *pBlend, float *pPitch ) +{ + // calc up/down pointing + *pBlend = (*pPitch * 3.0f); + + if( *pBlend < pseqdesc->blendstart[0] ) + { + *pPitch -= pseqdesc->blendstart[0] / 3.0f; + *pBlend = 0; + } + else if( *pBlend > pseqdesc->blendend[0] ) + { + *pPitch -= pseqdesc->blendend[0] / 3.0f; + *pBlend = 255; + } + else + { + if( pseqdesc->blendend[0] - pseqdesc->blendstart[0] < 0.1f ) // catch qc error + *pBlend = 127; + else *pBlend = 255 * (*pBlend - pseqdesc->blendstart[0]) / (pseqdesc->blendend[0] - pseqdesc->blendstart[0]); + *pPitch = 0.0f; + } +} + +/* +==================== +R_StudioLerpMovement + +==================== +*/ +void R_StudioLerpMovement( cl_entity_t *e, double time, vec3_t origin, vec3_t angles ) +{ + float f = 1.0f; + + // don't do it if the goalstarttime hasn't updated in a while. + // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit + // was increased to 1.0 s., which is 2x the max lag we are accounting for. + if( g_studio.interpolate && ( time < e->curstate.animtime + 1.0f ) && ( e->curstate.animtime != e->latched.prevanimtime )) + f = ( time - e->curstate.animtime ) / ( e->curstate.animtime - e->latched.prevanimtime ); + + // Con_Printf( "%4.2f %.2f %.2f\n", f, e->curstate.animtime, g_studio.time ); + VectorLerp( e->latched.prevorigin, f, e->curstate.origin, origin ); + + if( !VectorCompareEpsilon( e->curstate.angles, e->latched.prevangles, ON_EPSILON )) + { + vec4_t q, q1, q2; + + AngleQuaternion( e->curstate.angles, q1, false ); + AngleQuaternion( e->latched.prevangles, q2, false ); + QuaternionSlerp( q2, q1, f, q ); + QuaternionAngle( q, angles ); + } + else VectorCopy( e->curstate.angles, angles ); +} + +/* +==================== +StudioSetUpTransform + +==================== +*/ +void R_StudioSetUpTransform( cl_entity_t *e ) +{ + vec3_t origin, angles; + + VectorCopy( e->origin, origin ); + VectorCopy( e->angles, angles ); + + // interpolate monsters position (moved into UpdateEntityFields by user request) + if( e->curstate.movetype == MOVETYPE_STEP && !FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_COMPUTE_STUDIO_LERP )) + { + R_StudioLerpMovement( e, g_studio.time, origin, angles ); + } + + if( !FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_COMPENSATE_QUAKE_BUG )) + angles[PITCH] = -angles[PITCH]; // stupid quake bug + + // don't rotate clients, only aim + if( e->player ) angles[PITCH] = 0.0f; + + Matrix3x4_CreateFromEntity( g_studio.rotationmatrix, angles, origin, 1.0f ); + + if( tr.fFlipViewModel ) + { + g_studio.rotationmatrix[0][1] = -g_studio.rotationmatrix[0][1]; + g_studio.rotationmatrix[1][1] = -g_studio.rotationmatrix[1][1]; + g_studio.rotationmatrix[2][1] = -g_studio.rotationmatrix[2][1]; + } +} + +/* +==================== +StudioEstimateFrame + +==================== +*/ +float R_StudioEstimateFrame( cl_entity_t *e, mstudioseqdesc_t *pseqdesc ) +{ + double dfdt, f; + + if( g_studio.interpolate ) + { + if( g_studio.time < e->curstate.animtime ) dfdt = 0.0; + else dfdt = (g_studio.time - e->curstate.animtime) * e->curstate.framerate * pseqdesc->fps; + } + else dfdt = 0; + + if( pseqdesc->numframes <= 1 ) f = 0.0; + else f = (e->curstate.frame * (pseqdesc->numframes - 1)) / 256.0f; + + f += dfdt; + + if( pseqdesc->flags & STUDIO_LOOPING ) + { + if( pseqdesc->numframes > 1 ) + f -= (int)(f / (pseqdesc->numframes - 1)) * (pseqdesc->numframes - 1); + if( f < 0 ) f += (pseqdesc->numframes - 1); + } + else + { + if( f >= pseqdesc->numframes - 1.001 ) + f = pseqdesc->numframes - 1.001; + if( f < 0.0 ) f = 0.0; + } + return f; +} + +/* +==================== +StudioEstimateInterpolant + +==================== +*/ +float R_StudioEstimateInterpolant( cl_entity_t *e ) +{ + float dadt = 1.0f; + + if( g_studio.interpolate && ( e->curstate.animtime >= e->latched.prevanimtime + 0.01f )) + { + dadt = ( g_studio.time - e->curstate.animtime ) / 0.1f; + if( dadt > 2.0f ) dadt = 2.0f; + } + + return dadt; +} + +/* +==================== +CL_GetSequenceDuration + +==================== +*/ +float CL_GetSequenceDuration( cl_entity_t *ent, int sequence ) +{ + studiohdr_t *pstudiohdr; + mstudioseqdesc_t *pseqdesc; + + if( ent->model != NULL && ent->model->type == mod_studio ) + { + pstudiohdr = (studiohdr_t *)gEngfuncs.Mod_Extradata( mod_studio, ent->model ); + + if( pstudiohdr ) + { + sequence = bound( 0, sequence, pstudiohdr->numseq - 1 ); + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + sequence; + + if( pseqdesc->numframes > 1 && pseqdesc->fps > 0 ) + return (float)pseqdesc->numframes / (float)pseqdesc->fps; + } + } + + return 0.1f; +} + + +/* +==================== +StudioFxTransform + +==================== +*/ +_inline void R_StudioFxTransform( cl_entity_t *ent, matrix3x4 transform ) +{ + switch( ent->curstate.renderfx ) + { + case kRenderFxDistort: + case kRenderFxHologram: + if( !gEngfuncs.COM_RandomLong( 0, 49 )) + { + int axis = gEngfuncs.COM_RandomLong( 0, 1 ); + + if( axis == 1 ) axis = 2; // choose between x & z + VectorScale( transform[axis], gEngfuncs.COM_RandomFloat( 1.0f, 1.484f ), transform[axis] ); + } + else if( !gEngfuncs.COM_RandomLong( 0, 49 )) + { + float offset; + int axis = gEngfuncs.COM_RandomLong( 0, 1 ); + + if( axis == 1 ) axis = 2; // choose between x & z + offset = gEngfuncs.COM_RandomFloat( -10.0f, 10.0f ); + transform[gEngfuncs.COM_RandomLong( 0, 2 )][3] += offset; + } + break; + case kRenderFxExplode: + { + float scale; + + scale = 1.0f + ( g_studio.time - ent->curstate.animtime ) * 10.0f; + if( scale > 2.0f ) scale = 2.0f; // don't blow up more than 200% + + transform[0][1] *= scale; + transform[1][1] *= scale; + transform[2][1] *= scale; + } + break; + } +} + +/* +==================== +StudioCalcBoneAdj + +==================== +*/ +_inline void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, const byte *pcontroller2, byte mouthopen ) +{ + mstudiobonecontroller_t *pbonecontroller; + float value = 0.0f; + int i, j; + + pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bonecontrollerindex); + + for( j = 0; j < m_pStudioHeader->numbonecontrollers; j++ ) + { + i = pbonecontroller[j].index; + + if( i == STUDIO_MOUTH ) + { + // mouth hardcoded at controller 4 + value = (float)mouthopen / 64.0f; + value = bound( 0.0f, value, 1.0f ); + value = (1.0f - value) * pbonecontroller[j].start + value * pbonecontroller[j].end; + } + else if( i < 4 ) + { + // check for 360% wrapping + if( FBitSet( pbonecontroller[j].type, STUDIO_RLOOP )) + { + if( abs( pcontroller1[i] - pcontroller2[i] ) > 128 ) + { + int a = (pcontroller1[i] + 128) % 256; + int b = (pcontroller2[i] + 128) % 256; + value = (( a * dadt ) + ( b * ( 1.0f - dadt )) - 128) * (360.0f / 256.0f) + pbonecontroller[j].start; + } + else + { + value = ((pcontroller1[i] * dadt + (pcontroller2[i]) * (1.0f - dadt))) * (360.0f / 256.0f) + pbonecontroller[j].start; + } + } + else + { + value = (pcontroller1[i] * dadt + pcontroller2[i] * (1.0f - dadt)) / 255.0f; + value = bound( 0.0f, value, 1.0f ); + value = (1.0f - value) * pbonecontroller[j].start + value * pbonecontroller[j].end; + } + } + + switch( pbonecontroller[j].type & STUDIO_TYPES ) + { + case STUDIO_XR: + case STUDIO_YR: + case STUDIO_ZR: + adj[j] = DEG2RAD( value ); + break; + case STUDIO_X: + case STUDIO_Y: + case STUDIO_Z: + adj[j] = value; + break; + } + } +} + +/* +==================== +StudioCalcRotations + +==================== +*/ +void R_StudioCalcRotations( cl_entity_t *e, float pos[][3], vec4_t *q, mstudioseqdesc_t *pseqdesc, mstudioanim_t *panim, float f ) +{ + int i, frame; + float adj[MAXSTUDIOCONTROLLERS]; + float s, dadt; + mstudiobone_t *pbone; + + // bah, fix this bug with changing sequences too fast + if( f > pseqdesc->numframes - 1 ) + { + f = 0.0f; + } + else if( f < -0.01f ) + { + // BUG ( somewhere else ) but this code should validate this data. + // This could cause a crash if the frame # is negative, so we'll go ahead + // and clamp it here + f = -0.01f; + } + + frame = (int)f; + + dadt = R_StudioEstimateInterpolant( e ); + s = (f - frame); + + // add in programtic controllers + pbone = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex); + + R_StudioCalcBoneAdj( dadt, adj, e->curstate.controller, e->latched.prevcontroller, e->mouth.mouthopen ); + + for( i = 0; i < m_pStudioHeader->numbones; i++, pbone++, panim++ ) + { + gEngfuncs.R_StudioCalcBoneQuaternion( frame, s, pbone, panim, adj, q[i] ); + gEngfuncs.R_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] ); + } + + if( pseqdesc->motiontype & STUDIO_X ) pos[pseqdesc->motionbone][0] = 0.0f; + if( pseqdesc->motiontype & STUDIO_Y ) pos[pseqdesc->motionbone][1] = 0.0f; + if( pseqdesc->motiontype & STUDIO_Z ) pos[pseqdesc->motionbone][2] = 0.0f; +} + +/* +==================== +StudioMergeBones + +==================== +*/ +_inline void R_StudioMergeBones( cl_entity_t *e, model_t *m_pSubModel ) +{ + int i, j; + mstudiobone_t *pbones; + mstudioseqdesc_t *pseqdesc; + mstudioanim_t *panim; + matrix3x4 bonematrix; + static vec4_t q[MAXSTUDIOBONES]; + static float pos[MAXSTUDIOBONES][3]; + float f; + + if( e->curstate.sequence >= m_pStudioHeader->numseq ) + e->curstate.sequence = 0; + + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + e->curstate.sequence; + + f = R_StudioEstimateFrame( e, pseqdesc ); + + panim = gEngfuncs.R_StudioGetAnim( m_pStudioHeader, m_pSubModel, pseqdesc ); + R_StudioCalcRotations( e, pos, q, pseqdesc, panim, f ); + pbones = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex); + + for( i = 0; i < m_pStudioHeader->numbones; i++ ) + { + for( j = 0; j < g_studio.cached_numbones; j++ ) + { + if( !Q_stricmp( pbones[i].name, g_studio.cached_bonenames[j] )) + { + Matrix3x4_Copy( g_studio.bonestransform[i], g_studio.cached_bonestransform[j] ); + Matrix3x4_Copy( g_studio.lighttransform[i], g_studio.cached_lighttransform[j] ); + break; + } + } + + if( j >= g_studio.cached_numbones ) + { + Matrix3x4_FromOriginQuat( bonematrix, q[i], pos[i] ); + if( pbones[i].parent == -1 ) + { + Matrix3x4_ConcatTransforms( g_studio.bonestransform[i], g_studio.rotationmatrix, bonematrix ); + Matrix3x4_Copy( g_studio.lighttransform[i], g_studio.bonestransform[i] ); + + // apply client-side effects to the transformation matrix + R_StudioFxTransform( e, g_studio.bonestransform[i] ); + } + else + { + Matrix3x4_ConcatTransforms( g_studio.bonestransform[i], g_studio.bonestransform[pbones[i].parent], bonematrix ); + Matrix3x4_ConcatTransforms( g_studio.lighttransform[i], g_studio.lighttransform[pbones[i].parent], bonematrix ); + } + } + } +} + +/* +==================== +StudioSetupBones + +==================== +*/ +_inline void R_StudioSetupBones( cl_entity_t *e ) +{ + float f; + mstudiobone_t *pbones; + mstudioseqdesc_t *pseqdesc; + mstudioanim_t *panim; + matrix3x4 bonematrix; + static vec3_t pos[MAXSTUDIOBONES]; + static vec4_t q[MAXSTUDIOBONES]; + static vec3_t pos2[MAXSTUDIOBONES]; + static vec4_t q2[MAXSTUDIOBONES]; + static vec3_t pos3[MAXSTUDIOBONES]; + static vec4_t q3[MAXSTUDIOBONES]; + static vec3_t pos4[MAXSTUDIOBONES]; + static vec4_t q4[MAXSTUDIOBONES]; + int i; + + if( e->curstate.sequence >= m_pStudioHeader->numseq ) + e->curstate.sequence = 0; + + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + e->curstate.sequence; + + f = R_StudioEstimateFrame( e, pseqdesc ); + + panim = gEngfuncs.R_StudioGetAnim( m_pStudioHeader, RI.currentmodel, pseqdesc ); + R_StudioCalcRotations( e, pos, q, pseqdesc, panim, f ); + + if( pseqdesc->numblends > 1 ) + { + float s; + float dadt; + + panim += m_pStudioHeader->numbones; + R_StudioCalcRotations( e, pos2, q2, pseqdesc, panim, f ); + + dadt = R_StudioEstimateInterpolant( e ); + s = (e->curstate.blending[0] * dadt + e->latched.prevblending[0] * (1.0f - dadt)) / 255.0f; + + gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q2, pos2, s ); + + if( pseqdesc->numblends == 4 ) + { + panim += m_pStudioHeader->numbones; + R_StudioCalcRotations( e, pos3, q3, pseqdesc, panim, f ); + + panim += m_pStudioHeader->numbones; + R_StudioCalcRotations( e, pos4, q4, pseqdesc, panim, f ); + + s = (e->curstate.blending[0] * dadt + e->latched.prevblending[0] * (1.0f - dadt)) / 255.0f; + gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); + + s = (e->curstate.blending[1] * dadt + e->latched.prevblending[1] * (1.0f - dadt)) / 255.0f; + gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q3, pos3, s ); + } + } + + if( g_studio.interpolate && e->latched.sequencetime && ( e->latched.sequencetime + 0.2f > g_studio.time ) && ( e->latched.prevsequence < m_pStudioHeader->numseq )) + { + // blend from last sequence + static vec3_t pos1b[MAXSTUDIOBONES]; + static vec4_t q1b[MAXSTUDIOBONES]; + float s; + + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + e->latched.prevsequence; + panim = gEngfuncs.R_StudioGetAnim( m_pStudioHeader, RI.currentmodel, pseqdesc ); + + // clip prevframe + R_StudioCalcRotations( e, pos1b, q1b, pseqdesc, panim, e->latched.prevframe ); + + if( pseqdesc->numblends > 1 ) + { + panim += m_pStudioHeader->numbones; + R_StudioCalcRotations( e, pos2, q2, pseqdesc, panim, e->latched.prevframe ); + + s = (e->latched.prevseqblending[0]) / 255.0f; + gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q2, pos2, s ); + + if( pseqdesc->numblends == 4 ) + { + panim += m_pStudioHeader->numbones; + R_StudioCalcRotations( e, pos3, q3, pseqdesc, panim, e->latched.prevframe ); + + panim += m_pStudioHeader->numbones; + R_StudioCalcRotations( e, pos4, q4, pseqdesc, panim, e->latched.prevframe ); + + s = (e->latched.prevseqblending[0]) / 255.0f; + gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); + + s = (e->latched.prevseqblending[1]) / 255.0f; + gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q3, pos3, s ); + } + } + + s = 1.0f - ( g_studio.time - e->latched.sequencetime ) / 0.2f; + gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q1b, pos1b, s ); + } + else + { + // store prevframe otherwise + e->latched.prevframe = f; + } + + pbones = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex); + + // calc gait animation + if( m_pPlayerInfo && m_pPlayerInfo->gaitsequence != 0 ) + { + qboolean copy_bones = true; + + if( m_pPlayerInfo->gaitsequence >= m_pStudioHeader->numseq ) + m_pPlayerInfo->gaitsequence = 0; + + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + m_pPlayerInfo->gaitsequence; + + panim = gEngfuncs.R_StudioGetAnim( m_pStudioHeader, RI.currentmodel, pseqdesc ); + R_StudioCalcRotations( e, pos2, q2, pseqdesc, panim, m_pPlayerInfo->gaitframe ); + + for( i = 0; i < m_pStudioHeader->numbones; i++ ) + { + if( !Q_strcmp( pbones[i].name, "Bip01 Spine" )) + copy_bones = false; + else if( !Q_strcmp( pbones[pbones[i].parent].name, "Bip01 Pelvis" )) + copy_bones = true; + + if( !copy_bones ) continue; + + VectorCopy( pos2[i], pos[i] ); + Vector4Copy( q2[i], q[i] ); + } + } + + for( i = 0; i < m_pStudioHeader->numbones; i++ ) + { + Matrix3x4_FromOriginQuat( bonematrix, q[i], pos[i] ); + + if( pbones[i].parent == -1 ) + { + Matrix3x4_ConcatTransforms( g_studio.bonestransform[i], g_studio.rotationmatrix, bonematrix ); + Matrix3x4_Copy( g_studio.lighttransform[i], g_studio.bonestransform[i] ); + + // apply client-side effects to the transformation matrix + R_StudioFxTransform( e, g_studio.bonestransform[i] ); + } + else + { + Matrix3x4_ConcatTransforms( g_studio.bonestransform[i], g_studio.bonestransform[pbones[i].parent], bonematrix ); + Matrix3x4_ConcatTransforms( g_studio.lighttransform[i], g_studio.lighttransform[pbones[i].parent], bonematrix ); + } + } +} + +/* +==================== +StudioSaveBones + +==================== +*/ +_inline void R_StudioSaveBones( void ) +{ + mstudiobone_t *pbones; + int i; + + pbones = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex); + g_studio.cached_numbones = m_pStudioHeader->numbones; + + for( i = 0; i < m_pStudioHeader->numbones; i++ ) + { + Matrix3x4_Copy( g_studio.cached_bonestransform[i], g_studio.bonestransform[i] ); + Matrix3x4_Copy( g_studio.cached_lighttransform[i], g_studio.lighttransform[i] ); + Q_strncpy( g_studio.cached_bonenames[i], pbones[i].name, 32 ); + } +} + +/* +==================== +StudioBuildNormalTable + +NOTE: m_pSubModel must be set +==================== +*/ +_inline void R_StudioBuildNormalTable( void ) +{ + cl_entity_t *e = RI.currententity; + mstudiomesh_t *pmesh; + int i, j; + + Assert( m_pSubModel != NULL ); + + // reset chrome cache + for( i = 0; i < m_pStudioHeader->numbones; i++ ) + g_studio.chromeage[i] = 0; + + for( i = 0; i < m_pSubModel->numverts; i++ ) + g_studio.normaltable[i] = -1; + + for( j = 0; j < m_pSubModel->nummesh; j++ ) + { + short *ptricmds; + + pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + j; + ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex); + + while(( i = *( ptricmds++ ))) + { + if( i < 0 ) i = -i; + + for( ; i > 0; i--, ptricmds += 4 ) + { + if( g_studio.normaltable[ptricmds[0]] < 0 ) + g_studio.normaltable[ptricmds[0]] = ptricmds[1]; + } + } + } + + g_studio.chrome_origin[0] = cos( r_glowshellfreq->value * g_studio.time ) * 4000.0f; + g_studio.chrome_origin[1] = sin( r_glowshellfreq->value * g_studio.time ) * 4000.0f; + g_studio.chrome_origin[2] = cos( r_glowshellfreq->value * g_studio.time * 0.33f ) * 4000.0f; + + if( e->curstate.rendercolor.r || e->curstate.rendercolor.g || e->curstate.rendercolor.b ) + TriColor4ub( e->curstate.rendercolor.r, e->curstate.rendercolor.g, e->curstate.rendercolor.b, 255 ); + else TriColor4ub( 255, 255, 255, 255 ); +} + +/* +==================== +StudioGenerateNormals + +NOTE: m_pSubModel must be set +g_studio.verts must be computed +==================== +*/ +_inline void R_StudioGenerateNormals( void ) +{ + int v0, v1, v2; + vec3_t e0, e1, norm; + mstudiomesh_t *pmesh; + int i, j; + + Assert( m_pSubModel != NULL ); + + for( i = 0; i < m_pSubModel->numverts; i++ ) + VectorClear( g_studio.norms[i] ); + + for( j = 0; j < m_pSubModel->nummesh; j++ ) + { + short *ptricmds; + + pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + j; + ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex); + + while(( i = *( ptricmds++ ))) + { + if( i < 0 ) + { + i = -i; + + if( i > 2 ) + { + v0 = ptricmds[0]; ptricmds += 4; + v1 = ptricmds[0]; ptricmds += 4; + + for( i -= 2; i > 0; i--, ptricmds += 4 ) + { + v2 = ptricmds[0]; + + VectorSubtract( g_studio.verts[v1], g_studio.verts[v0], e0 ); + VectorSubtract( g_studio.verts[v2], g_studio.verts[v0], e1 ); + CrossProduct( e1, e0, norm ); + + VectorAdd( g_studio.norms[v0], norm, g_studio.norms[v0] ); + VectorAdd( g_studio.norms[v1], norm, g_studio.norms[v1] ); + VectorAdd( g_studio.norms[v2], norm, g_studio.norms[v2] ); + + v1 = v2; + } + } + else + { + ptricmds += i; + } + } + else + { + if( i > 2 ) + { + qboolean odd = false; + + v0 = ptricmds[0]; ptricmds += 4; + v1 = ptricmds[0]; ptricmds += 4; + + for( i -= 2; i > 0; i--, ptricmds += 4 ) + { + v2 = ptricmds[0]; + + VectorSubtract( g_studio.verts[v1], g_studio.verts[v0], e0 ); + VectorSubtract( g_studio.verts[v2], g_studio.verts[v0], e1 ); + CrossProduct( e1, e0, norm ); + + VectorAdd( g_studio.norms[v0], norm, g_studio.norms[v0] ); + VectorAdd( g_studio.norms[v1], norm, g_studio.norms[v1] ); + VectorAdd( g_studio.norms[v2], norm, g_studio.norms[v2] ); + + if( odd ) v1 = v2; + else v0 = v2; + + odd = !odd; + } + } + else + { + ptricmds += i; + } + } + } + } + + for( i = 0; i < m_pSubModel->numverts; i++ ) + VectorNormalize( g_studio.norms[i] ); +} + +/* +==================== +StudioSetupChrome + +==================== +*/ +_inline void R_StudioSetupChrome( float *pchrome, int bone, vec3_t normal ) +{ + float n; + + if( g_studio.chromeage[bone] != g_studio.framecount ) + { + // calculate vectors from the viewer to the bone. This roughly adjusts for position + vec3_t chromeupvec; // g_studio.chrome t vector in world reference frame + vec3_t chromerightvec; // g_studio.chrome s vector in world reference frame + vec3_t tmp; // vector pointing at bone in world reference frame + + VectorNegate( g_studio.chrome_origin, tmp ); + tmp[0] += g_studio.bonestransform[bone][0][3]; + tmp[1] += g_studio.bonestransform[bone][1][3]; + tmp[2] += g_studio.bonestransform[bone][2][3]; + + VectorNormalize( tmp ); + CrossProduct( tmp, RI.vright, chromeupvec ); + VectorNormalize( chromeupvec ); + CrossProduct( tmp, chromeupvec, chromerightvec ); + VectorNormalize( chromerightvec ); + + Matrix3x4_VectorIRotate( g_studio.bonestransform[bone], chromeupvec, g_studio.chromeup[bone] ); + Matrix3x4_VectorIRotate( g_studio.bonestransform[bone], chromerightvec, g_studio.chromeright[bone] ); + + g_studio.chromeage[bone] = g_studio.framecount; + } + + // calc s coord + n = DotProduct( normal, g_studio.chromeright[bone] ); + pchrome[0] = (n + 1.0f) * 32.0f; + + // calc t coord + n = DotProduct( normal, g_studio.chromeup[bone] ); + pchrome[1] = (n + 1.0f) * 32.0f; +} + +/* +==================== +StudioCalcAttachments + +==================== +*/ +_inline void R_StudioCalcAttachments( void ) +{ + mstudioattachment_t *pAtt; + int i; + + // calculate attachment points + pAtt = (mstudioattachment_t *)((byte *)m_pStudioHeader + m_pStudioHeader->attachmentindex); + + for( i = 0; i < Q_min( MAXSTUDIOATTACHMENTS, m_pStudioHeader->numattachments ); i++ ) + { + Matrix3x4_VectorTransform( g_studio.lighttransform[pAtt[i].bone], pAtt[i].org, RI.currententity->attachment[i] ); + } +} + +/* +=============== +pfnStudioSetupModel + +=============== +*/ +static void R_StudioSetupModel( int bodypart, void **ppbodypart, void **ppsubmodel ) +{ + int index; + + if( bodypart > m_pStudioHeader->numbodyparts ) + bodypart = 0; + + m_pBodyPart = (mstudiobodyparts_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bodypartindex) + bodypart; + + index = RI.currententity->curstate.body / m_pBodyPart->base; + index = index % m_pBodyPart->nummodels; + + m_pSubModel = (mstudiomodel_t *)((byte *)m_pStudioHeader + m_pBodyPart->modelindex) + index; + + if( ppbodypart ) *ppbodypart = m_pBodyPart; + if( ppsubmodel ) *ppsubmodel = m_pSubModel; +} + +/* +=============== +R_StudioCheckBBox + +=============== +*/ +static int R_StudioCheckBBox( void ) +{ + if( !RI.currententity || !RI.currentmodel ) + return false; + + return R_StudioComputeBBox( NULL ); +} + +/* +=============== +R_StudioDynamicLight + +=============== +*/ +void R_StudioDynamicLight( cl_entity_t *ent, alight_t *plight ) +{ + movevars_t *mv = gEngfuncs.pfnGetMoveVars(); + vec3_t lightDir, vecSrc, vecEnd; + vec3_t origin, dist, finalLight; + float add, radius, total; + colorVec light; + uint lnum; + dlight_t *dl; + + if( !plight || !ent || !ent->model ) + return; + + if( !RI.drawWorld || r_fullbright->value || FBitSet( ent->curstate.effects, EF_FULLBRIGHT )) + { + plight->shadelight = 0; + plight->ambientlight = 192; + + VectorSet( plight->plightvec, 0.0f, 0.0f, -1.0f ); + VectorSet( plight->color, 1.0f, 1.0f, 1.0f ); + return; + } + + // determine plane to get lightvalues from: ceil or floor + if( FBitSet( ent->curstate.effects, EF_INVLIGHT )) + VectorSet( lightDir, 0.0f, 0.0f, 1.0f ); + else VectorSet( lightDir, 0.0f, 0.0f, -1.0f ); + + VectorCopy( ent->origin, origin ); + + VectorSet( vecSrc, origin[0], origin[1], origin[2] - lightDir[2] * 8.0f ); + light.r = light.g = light.b = light.a = 0; + + if(( mv->skycolor_r + mv->skycolor_g + mv->skycolor_b ) != 0 ) + { + msurface_t *psurf = NULL; + pmtrace_t trace; + + if( FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_WRITE_LARGE_COORD )) + { + vecEnd[0] = origin[0] - mv->skyvec_x * 65536.0f; + vecEnd[1] = origin[1] - mv->skyvec_y * 65536.0f; + vecEnd[2] = origin[2] - mv->skyvec_z * 65536.0f; + } + else + { + vecEnd[0] = origin[0] - mv->skyvec_x * 8192.0f; + vecEnd[1] = origin[1] - mv->skyvec_y * 8192.0f; + vecEnd[2] = origin[2] - mv->skyvec_z * 8192.0f; + } + + trace = gEngfuncs.CL_TraceLine( vecSrc, vecEnd, PM_WORLD_ONLY ); + if( trace.ent > 0 ) psurf = gEngfuncs.EV_TraceSurface( trace.ent, vecSrc, vecEnd ); + else psurf = gEngfuncs.EV_TraceSurface( 0, vecSrc, vecEnd ); + + if( FBitSet( ent->model->flags, STUDIO_FORCE_SKYLIGHT ) || ( psurf && FBitSet( psurf->flags, SURF_DRAWSKY ))) + { + VectorSet( lightDir, mv->skyvec_x, mv->skyvec_y, mv->skyvec_z ); + + light.r = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_r, 255 )); + light.g = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_g, 255 )); + light.b = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_b, 255 )); + } + } + + if(( light.r + light.g + light.b ) < 16 ) // TESTTEST + { + colorVec gcolor; + float grad[4]; + + VectorScale( lightDir, 2048.0f, vecEnd ); + VectorAdd( vecEnd, vecSrc, vecEnd ); + + light = R_LightVec( vecSrc, vecEnd, g_studio.lightspot, g_studio.lightvec ); + + if( VectorIsNull( g_studio.lightvec )) + { + vecSrc[0] -= 16.0f; + vecSrc[1] -= 16.0f; + vecEnd[0] -= 16.0f; + vecEnd[1] -= 16.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[0] += 32.0f; + vecEnd[0] += 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[1] += 32.0f; + vecEnd[1] += 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[0] -= 32.0f; + vecEnd[0] -= 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3]; + lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3]; + VectorNormalize( lightDir ); + } + else + { + VectorCopy( g_studio.lightvec, lightDir ); + } + } + + VectorSet( finalLight, light.r, light.g, light.b ); + ent->cvFloorColor = light; + + total = Q_max( Q_max( light.r, light.g ), light.b ); + if( total == 0.0f ) total = 1.0f; + + // scale lightdir by light intentsity + VectorScale( lightDir, total, lightDir ); + + for( lnum = 0; lnum < MAX_DLIGHTS; lnum++ ) + { + dl = gEngfuncs.GetDynamicLight( lnum ); + + if( dl->die < g_studio.time || !r_dynamic->value ) + continue; + + VectorSubtract( ent->origin, dl->origin, dist ); + + radius = VectorLength( dist ); + add = (dl->radius - radius); + + if( add > 0.0f ) + { + total += add; + + if( radius > 1.0f ) + VectorScale( dist, ( add / radius ), dist ); + else VectorScale( dist, add, dist ); + + VectorAdd( lightDir, dist, lightDir ); + + finalLight[0] += gEngfuncs.LightToTexGamma( dl->color.r ) * ( add / 256.0f ) * 2.0f; + finalLight[1] += gEngfuncs.LightToTexGamma( dl->color.g ) * ( add / 256.0f ) * 2.0f; + finalLight[2] += gEngfuncs.LightToTexGamma( dl->color.b ) * ( add / 256.0f ) * 2.0f; + } + } + + if( FBitSet( ent->model->flags, STUDIO_AMBIENT_LIGHT )) + add = 0.6f; + else add = 0.9f; + + VectorScale( lightDir, add, lightDir ); + + plight->shadelight = VectorLength( lightDir ); + plight->ambientlight = total - plight->shadelight; + + total = Q_max( Q_max( finalLight[0], finalLight[1] ), finalLight[2] ); + + if( total > 0.0f ) + { + plight->color[0] = finalLight[0] * ( 1.0f / total ); + plight->color[1] = finalLight[1] * ( 1.0f / total ); + plight->color[2] = finalLight[2] * ( 1.0f / total ); + } + else VectorSet( plight->color, 1.0f, 1.0f, 1.0f ); + + if( plight->ambientlight > 128 ) + plight->ambientlight = 128; + + if( plight->ambientlight + plight->shadelight > 255 ) + plight->shadelight = 255 - plight->ambientlight; + + VectorNormalize2( lightDir, plight->plightvec ); +} + +/* +=============== +pfnStudioEntityLight + +=============== +*/ +void R_StudioEntityLight( alight_t *lightinfo ) +{ + int lnum, i, j, k; + float minstrength, dist2, f, r2; + float lstrength[MAX_LOCALLIGHTS]; + cl_entity_t *ent = RI.currententity; + vec3_t mid, origin, pos; + dlight_t *el; + + g_studio.numlocallights = 0; + + if( !ent || !r_dynamic->value ) + return; + + for( i = 0; i < MAX_LOCALLIGHTS; i++ ) + lstrength[i] = 0; + + Matrix3x4_OriginFromMatrix( g_studio.rotationmatrix, origin ); + dist2 = 1000000.0f; + k = 0; + + for( lnum = 0; lnum < MAX_ELIGHTS; lnum++ ) + { + el = gEngfuncs.GetEntityLight( lnum ); + + if( el->die < g_studio.time || el->radius <= 0.0f ) + continue; + + if(( el->key & 0xFFF ) == ent->index ) + { + int att = (el->key >> 12) & 0xF; + + if( att ) VectorCopy( ent->attachment[att], el->origin ); + else VectorCopy( ent->origin, el->origin ); + } + + VectorCopy( el->origin, pos ); + VectorSubtract( origin, el->origin, mid ); + + f = DotProduct( mid, mid ); + r2 = el->radius * el->radius; + + if( f > r2 ) minstrength = r2 / f; + else minstrength = 1.0f; + + if( minstrength > 0.05f ) + { + if( g_studio.numlocallights >= MAX_LOCALLIGHTS ) + { + for( j = 0, k = -1; j < g_studio.numlocallights; j++ ) + { + if( lstrength[j] < dist2 && lstrength[j] < minstrength ) + { + dist2 = lstrength[j]; + k = j; + } + } + } + else k = g_studio.numlocallights; + + if( k != -1 ) + { + g_studio.locallightcolor[k].r = gEngfuncs.LightToTexGamma( el->color.r ); + g_studio.locallightcolor[k].g = gEngfuncs.LightToTexGamma( el->color.g ); + g_studio.locallightcolor[k].b = gEngfuncs.LightToTexGamma( el->color.b ); + g_studio.locallightR2[k] = r2; + g_studio.locallight[k] = el; + lstrength[k] = minstrength; + + if( k >= g_studio.numlocallights ) + g_studio.numlocallights = k + 1; + } + } + } +} + +/* +=============== +R_StudioSetupLighting + +=============== +*/ +void R_StudioSetupLighting( alight_t *plight ) +{ + float scale = 1.0f; + int i; + + if( !m_pStudioHeader || !plight ) + return; + + if( RI.currententity != NULL ) + scale = RI.currententity->curstate.scale; + + g_studio.ambientlight = plight->ambientlight; + g_studio.shadelight = plight->shadelight; + VectorCopy( plight->plightvec, g_studio.lightvec ); + + for( i = 0; i < m_pStudioHeader->numbones; i++ ) + { + Matrix3x4_VectorIRotate( g_studio.lighttransform[i], plight->plightvec, g_studio.blightvec[i] ); + if( scale > 1.0f ) VectorNormalize( g_studio.blightvec[i] ); // in case model may be scaled + } + + VectorCopy( plight->color, g_studio.lightcolor ); +} + +/* +=============== +R_StudioLighting + +=============== +*/ +_inline void R_StudioLighting( float *lv, int bone, int flags, vec3_t normal ) +{ + float illum; + + if( FBitSet( flags, STUDIO_NF_FULLBRIGHT )) + { + *lv = 1.0f; + return; + } + + illum = g_studio.ambientlight; + + if( FBitSet( flags, STUDIO_NF_FLATSHADE )) + { + illum += g_studio.shadelight * 0.8f; + } + else + { + float r, lightcos; + + if( bone != -1 ) lightcos = DotProduct( normal, g_studio.blightvec[bone] ); + else lightcos = DotProduct( normal, g_studio.lightvec ); // -1 colinear, 1 opposite + if( lightcos > 1.0f ) lightcos = 1.0f; + + illum += g_studio.shadelight; + + r = SHADE_LAMBERT; + + // do modified hemispherical lighting + if( r <= 1.0f ) + { + r += 1.0f; + lightcos = (( r - 1.0f ) - lightcos) / r; + if( lightcos > 0.0f ) + illum += g_studio.shadelight * lightcos; + } + else + { + lightcos = (lightcos + ( r - 1.0f )) / r; + if( lightcos > 0.0f ) + illum -= g_studio.shadelight * lightcos; + } + + illum = Q_max( illum, 0.0f ); + } + + illum = Q_min( illum, 255.0f ); + *lv = illum * ( 1.0f / 255.0f ); +} + +/* +==================== +R_LightLambert + +==================== +*/ +_inline void R_LightLambert( vec4_t light[MAX_LOCALLIGHTS], vec3_t normal, vec3_t color, byte *out ) +{ + vec3_t finalLight; + vec3_t localLight; + int i; + + VectorCopy( color, finalLight ); + + for( i = 0; i < g_studio.numlocallights; i++ ) + { + float r, r2; + + if( tr.fFlipViewModel ) + r = DotProduct( normal, light[i] ); + else r = -DotProduct( normal, light[i] ); + + if( r > 0.0f ) + { + if( light[i][3] == 0.0f ) + { + r2 = DotProduct( light[i], light[i] ); + + if( r2 > 0.0f ) + light[i][3] = g_studio.locallightR2[i] / ( r2 * sqrt( r2 )); + else light[i][3] = 0.0001f; + } + + localLight[0] = Q_min( g_studio.locallightcolor[i].r * r * light[i][3], 255.0f ); + localLight[1] = Q_min( g_studio.locallightcolor[i].g * r * light[i][3], 255.0f ); + localLight[2] = Q_min( g_studio.locallightcolor[i].b * r * light[i][3], 255.0f ); + VectorScale( localLight, ( 1.0f / 255.0f ), localLight ); + + finalLight[0] = Q_min( finalLight[0] + localLight[0], 1.0f ); + finalLight[1] = Q_min( finalLight[1] + localLight[1], 1.0f ); + finalLight[2] = Q_min( finalLight[2] + localLight[2], 1.0f ); + } + } + + out[0] = finalLight[0] * 255; + out[1] = finalLight[1] * 255; + out[2] = finalLight[2] * 255; +} + +#if 0 /* R_StudioSetColorArray used */ +static void R_StudioSetColorBegin(short *ptricmds, vec3_t *pstudionorms ) +{ + float *lv = (float *)g_studio.lightvalues[ptricmds[1]]; + rgba_t color; + + if( g_studio.numlocallights ) + { + color[3] = tr.blend * 255; + R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color ); + pglColor4ubv( color ); + } + else + { + if( RI.currententity->curstate.rendermode == kRenderTransColor ) + { + color[3] = tr.blend * 255; + VectorCopy( (byte*)&RI.currententity->curstate.rendercolor, color ); + pglColor4ubv( color ); + } + else pglColor4f( lv[0], lv[1], lv[2], tr.blend ); + } +} +#endif + +_inline void R_StudioSetColorArray(short *ptricmds, vec3_t *pstudionorms, byte *color ) +{ + float *lv = (float *)g_studio.lightvalues[ptricmds[1]]; + + color[3] = tr.blend * 255; + + if( g_studio.numlocallights ) + R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color ); + else + { + if( RI.currententity->curstate.rendermode == kRenderTransColor ) + VectorCopy( (byte*)&RI.currententity->curstate.rendercolor, color ); + else + { + color[0] = lv[0] * 255; + color[1] = lv[1] * 255; + color[2] = lv[2] * 255; + } + } +} + +/* +==================== +R_LightStrength + +==================== +*/ +_inline void R_LightStrength( int bone, vec3_t localpos, vec4_t light[MAX_LOCALLIGHTS] ) +{ + int i; + + if( g_studio.lightage[bone] != g_studio.framecount ) + { + for( i = 0; i < g_studio.numlocallights; i++ ) + { + dlight_t *el = g_studio.locallight[i]; + Matrix3x4_VectorITransform( g_studio.lighttransform[bone], el->origin, g_studio.lightbonepos[bone][i] ); + } + + g_studio.lightage[bone] = g_studio.framecount; + } + + for( i = 0; i < g_studio.numlocallights; i++ ) + { + VectorSubtract( localpos, g_studio.lightbonepos[bone][i], light[i] ); + light[i][3] = 0.0f; + } +} + +/* +=============== +R_StudioSetupSkin + +=============== +*/ +static void R_StudioSetupSkin( studiohdr_t *ptexturehdr, int index ) +{ + mstudiotexture_t *ptexture = NULL; + + if( FBitSet( g_nForceFaceFlags, STUDIO_NF_CHROME )) + return; + + if( ptexturehdr == NULL ) + return; + + // NOTE: user may ignore to call StudioRemapColors and remap_info will be unavailable + if( m_fDoRemap ) ptexture = gEngfuncs.CL_GetRemapInfoForEntity( RI.currententity )->ptexture; + if( !ptexture ) ptexture = (mstudiotexture_t *)((byte *)ptexturehdr + ptexturehdr->textureindex); // fallback + + if( r_lightmap->value && !r_fullbright->value ) + GL_Bind( XASH_TEXTURE0, tr.whiteTexture ); + else GL_Bind( XASH_TEXTURE0, ptexture[index].index ); +} + +/* +=============== +R_StudioGetTexture + +Doesn't changes studio global state at all +=============== +*/ +mstudiotexture_t *R_StudioGetTexture( cl_entity_t *e ) +{ + mstudiotexture_t *ptexture; + studiohdr_t *phdr, *thdr; + + if(( phdr = gEngfuncs.Mod_Extradata( mod_studio, e->model )) == NULL ) + return NULL; + + thdr = m_pStudioHeader; + if( !thdr ) return NULL; + + if( m_fDoRemap ) ptexture = gEngfuncs.CL_GetRemapInfoForEntity( e )->ptexture; + else ptexture = (mstudiotexture_t *)((byte *)thdr + thdr->textureindex); + + return ptexture; +} + +void R_StudioSetRenderamt( int iRenderamt ) +{ + if( !RI.currententity ) return; + + RI.currententity->curstate.renderamt = iRenderamt; + tr.blend = CL_FxBlend( RI.currententity ) / 255.0f; +} + +/* +=============== +R_StudioSetCullState + +sets true for enable backculling (for left-hand viewmodel) +=============== +*/ +void R_StudioSetCullState( int iCull ) +{ + g_iBackFaceCull = iCull; +} + +/* +=============== +R_StudioRenderShadow + +just a prefab for render shadow +=============== +*/ +void R_StudioRenderShadow( int iSprite, float *p1, float *p2, float *p3, float *p4 ) +{ + if( !p1 || !p2 || !p3 || !p4 ) + return; + + if( TriSpriteTexture( gEngfuncs.pfnGetModelByIndex( iSprite ), 0 )) + { + TriRenderMode( kRenderTransAlpha ); + TriColor4f( 0.0f, 0.0f, 0.0f, 1.0f ); +#if 1 + gu_vert_t* const out = ( gu_vert_t* )sceGuGetMemory( sizeof( gu_vert_t ) * 4 ); + out[0].uv[0] = 0.0f; + out[0].uv[1] = 0.0f; + VectorCopy( p1, out[0].xyz ); + out[1].uv[0] = 0.0f; + out[1].uv[1] = 0.1f; + VectorCopy( p2, out[1].xyz ); + out[2].uv[0] = 1.0f; + out[2].uv[1] = 1.0f; + VectorCopy( p3, out[2].xyz ); + out[3].uv[0] = 1.0f; + out[3].uv[1] = 0.0f; + VectorCopy( p4, out[3].xyz ); + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, out ); +#else + pglBegin( GL_QUADS ); + pglTexCoord2f( 0.0f, 0.0f ); + pglVertex3fv( p1 ); + pglTexCoord2f( 0.0f, 1.0f ); + pglVertex3fv( p2 ); + pglTexCoord2f( 1.0f, 1.0f ); + pglVertex3fv( p3 ); + pglTexCoord2f( 1.0f, 0.0f ); + pglVertex3fv( p4 ); + pglEnd(); +#endif + TriRenderMode( kRenderNormal ); + } +} + +/* +=============== +R_StudioMeshCompare + +Sorting opaque entities by model type +=============== +*/ +static int R_StudioMeshCompare( const void *a, const void *b ) +{ + if( FBitSet( ((const sortedmesh_t*)a)->flags, STUDIO_NF_ADDITIVE )) + return 1; + + if( FBitSet( ((const sortedmesh_t*)a)->flags, STUDIO_NF_MASKED )) + return -1; + + return 0; +} + +/* +=============== +R_StudioDrawNormalMesh + +generic path +=============== +*/ +_inline void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float s, float t ) +{ + float *lv; + int i, prim, index; + + while(( i = *( ptricmds++ ))) + { + if( i < 0 ) + { + prim = GU_TRIANGLE_FAN; + i = -i; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + gu_vert_ftcv_t* const out = ( gu_vert_ftcv_t* )sceGuGetMemory( sizeof( gu_vert_ftcv_t ) * i ); + + for( index = 0; index < i; index++, ptricmds += 4 ) + { + byte color[4]; + R_StudioSetColorArray( ptricmds, pstudionorms, color ); + out[index].u = ptricmds[2] * s; + out[index].v = ptricmds[3] * t; + out[index].c = GUCOLOR4UBV( color ); + out[index].x = g_studio.verts[ptricmds[0]][0]; + out[index].y = g_studio.verts[ptricmds[0]][1]; + out[index].z = g_studio.verts[ptricmds[0]][2]; + } + sceGuDrawArray( prim, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF, i, 0, out ); + } +} + +/* +=============== +R_StudioDrawFloatMesh + +generic path +=============== +*/ +_inline void R_StudioDrawFloatMesh( short *ptricmds, vec3_t *pstudionorms ) +{ + float *lv; + int i, prim, index; + + while(( i = *( ptricmds++ ))) + { + if( i < 0 ) + { + prim = GU_TRIANGLE_FAN; + i = -i; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + gu_vert_ftcv_t* const out = ( gu_vert_ftcv_t* )sceGuGetMemory( sizeof( gu_vert_ftcv_t ) * i ); + + for( index = 0; index < i; index++, ptricmds += 4 ) + { + byte color[4]; + R_StudioSetColorArray( ptricmds, pstudionorms, color ); + out[index].u = HalfToFloat( ptricmds[2] ); + out[index].u = HalfToFloat( ptricmds[3] ); + out[index].c = GUCOLOR4UBV( color ); + out[index].x = g_studio.verts[ptricmds[0]][0]; + out[index].y = g_studio.verts[ptricmds[0]][1]; + out[index].z = g_studio.verts[ptricmds[0]][2]; + } + sceGuDrawArray( prim, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF, i, 0, out ); + } +} + +/* +=============== +R_StudioDrawNormalMesh + +generic path +=============== +*/ +_inline void R_StudioDrawChromeMesh( short *ptricmds, vec3_t *pstudionorms, float s, float t, float scale ) +{ + float *lv, *av; + int i, idx, prim, index; + qboolean glowShell = (scale > 0.0f) ? true : false; + vec3_t vert; + byte color[4]; + + while(( i = *( ptricmds++ ))) + { + if( i < 0 ) + { + prim = GU_TRIANGLE_FAN; + i = -i; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + gu_vert_ftcv_t* const out = ( gu_vert_ftcv_t* )sceGuGetMemory( sizeof( gu_vert_ftcv_t ) * i ); + + for( index = 0; index < i; index++, ptricmds += 4 ) + { + if( glowShell ) + { + color24 *clr = &RI.currententity->curstate.rendercolor; + + idx = g_studio.normaltable[ptricmds[0]]; + av = g_studio.verts[ptricmds[0]]; + lv = g_studio.norms[ptricmds[0]]; + VectorMA( av, scale, lv, vert ); + out[index].u = g_studio.chrome[idx][0] * s; + out[index].v = g_studio.chrome[idx][1] * t; + out[index].c = GUCOLOR4UB( clr->r, clr->g, clr->b, 255 ); + out[index].x = vert[0]; + out[index].y = vert[1]; + out[index].z = vert[2]; + } + else + { + idx = ptricmds[1]; + //lv = (float *)g_studio.lightvalues[ptricmds[1]]; //unused + R_StudioSetColorArray( ptricmds, pstudionorms, color ); + out[index].u = g_studio.chrome[idx][0] * s; + out[index].v = g_studio.chrome[idx][1] * t; + out[index].c = GUCOLOR4UBV( color ); + out[index].x = g_studio.verts[ptricmds[0]][0]; + out[index].y = g_studio.verts[ptricmds[0]][1]; + out[index].z = g_studio.verts[ptricmds[0]][2]; + } + } + sceGuDrawArray( prim, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF, i, 0, out ); + } +} + + +_inline int R_StudioBuildIndices( qboolean tri_strip, int vertexState ) +{ + // build in indices + if( vertexState++ < 3 ) + { + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts; + } + else if( tri_strip ) + { + // flip triangles between clockwise and counter clockwise + if( vertexState & 1 ) + { + // draw triangle [n-2 n-1 n] + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 2; + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 1; + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts; + } + else + { + // draw triangle [n-1 n-2 n] + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 1; + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 2; + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts; + } + } + else + { + // draw triangle fan [0 n-1 n] + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - ( vertexState - 1 ); + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 1; + g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts; + } + + return vertexState; +} + +/* +=============== +R_StudioDrawNormalMesh + +generic path +=============== +*/ +_inline void R_StudioBuildArrayNormalMesh( short *ptricmds, vec3_t *pstudionorms, float s, float t ) +{ + float *lv; + int i; + float alpha = tr.blend; + + while(( i = *( ptricmds++ ))) + { + int vertexState = 0; + qboolean tri_strip = true; + + if( i < 0 ) + { + tri_strip = false; + i = -i; + } + + for( ; i > 0; i--, ptricmds += 4 ) + { +#if 1 + byte cl[4]; + lv = (float *)g_studio.lightvalues[ptricmds[1]]; /* unused */ + + vertexState = R_StudioBuildIndices( tri_strip, vertexState ); + + R_StudioSetColorArray( ptricmds, pstudionorms, cl ); + + g_studio.arrayverts[g_studio.numverts].u = ptricmds[2] * s; + g_studio.arrayverts[g_studio.numverts].v = ptricmds[3] * t; + g_studio.arrayverts[g_studio.numverts].c = GUCOLOR4UBV( cl ); + g_studio.arrayverts[g_studio.numverts].x = g_studio.verts[ptricmds[0]][0]; + g_studio.arrayverts[g_studio.numverts].y = g_studio.verts[ptricmds[0]][1]; + g_studio.arrayverts[g_studio.numverts].z = g_studio.verts[ptricmds[0]][2]; + g_studio.numverts++; +#else + GLubyte *cl; + + cl = g_studio.arraycolor[g_studio.numverts]; + lv = (float *)g_studio.lightvalues[ptricmds[1]]; + + vertexState = R_StudioBuildIndices( tri_strip, vertexState ); + + R_StudioSetColorArray( ptricmds, pstudionorms, cl ); + + g_studio.arraycoord[g_studio.numverts][0] = ptricmds[2] * s; + g_studio.arraycoord[g_studio.numverts][1] = ptricmds[3] * t; + + VectorCopy( g_studio.verts[ptricmds[0]], g_studio.arrayverts[g_studio.numverts] ); + g_studio.numverts++; +#endif + } + } +} + +/* +=============== +R_StudioDrawNormalMesh + +generic path +=============== +*/ +_inline void R_StudioBuildArrayFloatMesh( short *ptricmds, vec3_t *pstudionorms ) +{ + float *lv; + int i; + float alpha = tr.blend; + + while(( i = *( ptricmds++ ))) + { + int vertexState = 0; + qboolean tri_strip = true; + + if( i < 0 ) + { + tri_strip = false; + i = -i; + } + + for( ; i > 0; i--, ptricmds += 4 ) + { +#if 1 + byte cl[4]; + lv = (float *)g_studio.lightvalues[ptricmds[1]]; /* unused */ + + vertexState = R_StudioBuildIndices( tri_strip, vertexState ); + + R_StudioSetColorArray( ptricmds, pstudionorms, cl ); +#if 0 + __asm__ volatile ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S000, 4(%0)\n" // S000 = ptricmds[2], S001 = ptricmds[3] + "vh2f.s C000, S000\n" // S000 = ptricmds[2], S001 = ptricmds[3] + "sv.s S000, 0(%1)\n" // out[index].u = S000 = ptricmds[2] + "sv.s S001, 4(%1)\n" // out[index].v = S001 = ptricmds[3] + ".set pop\n" // restore assembler option + :: "r"( ptricmds ), "r"( &g_studio.arrayverts[g_studio.numverts] ) + ); +#else + g_studio.arrayverts[g_studio.numverts].u = HalfToFloat( ptricmds[2] ); + g_studio.arrayverts[g_studio.numverts].v = HalfToFloat( ptricmds[3] ); /* Error fixed */ +#endif + g_studio.arrayverts[g_studio.numverts].u = HalfToFloat( ptricmds[2] ); + g_studio.arrayverts[g_studio.numverts].v = HalfToFloat( ptricmds[3] ); /* Error fixed */ + g_studio.arrayverts[g_studio.numverts].c = GUCOLOR4UBV( cl ); + g_studio.arrayverts[g_studio.numverts].x = g_studio.verts[ptricmds[0]][0]; + g_studio.arrayverts[g_studio.numverts].y = g_studio.verts[ptricmds[0]][1]; + g_studio.arrayverts[g_studio.numverts].z = g_studio.verts[ptricmds[0]][2]; + g_studio.numverts++; +#else + GLubyte *cl; + cl = g_studio.arraycolor[g_studio.numverts]; + lv = (float *)g_studio.lightvalues[ptricmds[1]]; + + vertexState = R_StudioBuildIndices( tri_strip, vertexState ); + + R_StudioSetColorArray( ptricmds, pstudionorms, cl ); + + g_studio.arraycoord[g_studio.numverts][0] = HalfToFloat( ptricmds[2] ); + g_studio.arraycoord[g_studio.numverts][1] = HalfToFloat( ptricmds[2] ); + + VectorCopy( g_studio.verts[ptricmds[0]], g_studio.arrayverts[g_studio.numverts] ); + g_studio.numverts++; +#endif + } + } +} + +/* +=============== +R_StudioDrawNormalMesh + +generic path +=============== +*/ +_inline void R_StudioBuildArrayChromeMesh( short *ptricmds, vec3_t *pstudionorms, float s, float t, float scale ) +{ + float *lv, *av; + int i, idx; + qboolean glowShell = (scale > 0.0f) ? true : false; + vec3_t vert; + float alpha = tr.blend; + + while(( i = *( ptricmds++ ))) + { + int vertexState = 0; + qboolean tri_strip = true; + + if( i < 0 ) + { + tri_strip = false; + i = -i; + } + + for( ; i > 0; i--, ptricmds += 4 ) + { +#if 1 + byte cl[4]; + lv = (float *)g_studio.lightvalues[ptricmds[1]]; /* unused */ + + vertexState = R_StudioBuildIndices( tri_strip, vertexState ); + + if( glowShell ) + { + idx = g_studio.normaltable[ptricmds[0]]; + av = g_studio.verts[ptricmds[0]]; + lv = g_studio.norms[ptricmds[0]]; + + cl[0] = RI.currententity->curstate.rendercolor.r; + cl[1] = RI.currententity->curstate.rendercolor.g; + cl[2] = RI.currententity->curstate.rendercolor.b; + cl[3] = 255; + + VectorMA( av, scale, lv, vert ); + + g_studio.arrayverts[g_studio.numverts].x = vert[0]; + g_studio.arrayverts[g_studio.numverts].y = vert[1]; + g_studio.arrayverts[g_studio.numverts].z = vert[2]; + } + else + { + idx = ptricmds[1]; + R_StudioSetColorArray( ptricmds, pstudionorms, cl ); + + g_studio.arrayverts[g_studio.numverts].x = g_studio.verts[ptricmds[0]][0]; + g_studio.arrayverts[g_studio.numverts].y = g_studio.verts[ptricmds[0]][1]; + g_studio.arrayverts[g_studio.numverts].z = g_studio.verts[ptricmds[0]][2]; + } + + g_studio.arrayverts[g_studio.numverts].u = g_studio.chrome[idx][0] * s; + g_studio.arrayverts[g_studio.numverts].v = g_studio.chrome[idx][1] * t; + g_studio.arrayverts[g_studio.numverts].c = GUCOLOR4UBV( cl ); + g_studio.numverts++; +#else + GLubyte *cl; + cl = g_studio.arraycolor[g_studio.numverts]; + lv = (float *)g_studio.lightvalues[ptricmds[1]]; + + vertexState = R_StudioBuildIndices( tri_strip, vertexState ); + + if( glowShell ) + { + idx = g_studio.normaltable[ptricmds[0]]; + av = g_studio.verts[ptricmds[0]]; + lv = g_studio.norms[ptricmds[0]]; + + cl[0] = RI.currententity->curstate.rendercolor.r; + cl[1] = RI.currententity->curstate.rendercolor.g; + cl[2] = RI.currententity->curstate.rendercolor.b; + cl[3] = 255; + + VectorMA( av, scale, lv, vert ); + VectorCopy( vert, g_studio.arrayverts[g_studio.numverts] ); + } + else + { + idx = ptricmds[1]; + R_StudioSetColorArray( ptricmds, pstudionorms, cl ); + + VectorCopy( g_studio.verts[ptricmds[0]], g_studio.arrayverts[g_studio.numverts] ); + } + + g_studio.arraycoord[g_studio.numverts][0] = g_studio.chrome[idx][0] * s; + g_studio.arraycoord[g_studio.numverts][1] = g_studio.chrome[idx][1] * t; + + g_studio.numverts++; +#endif + } + } +} + +_inline void R_StudioDrawArrays( uint startverts, uint startelems ) +{ + sceKernelDcacheWritebackAll(); // writeback cache + sceGuDrawArray( GU_TRIANGLES, GU_TEXTURE_32BITF | GU_COLOR_8888 | + GU_VERTEX_32BITF | GU_INDEX_16BIT, g_studio.numelems - startelems, + &g_studio.arrayelems[startelems], g_studio.arrayverts ); +} + +/* +=============== +R_StudioDrawPoints + +=============== +*/ +static void R_StudioDrawPoints( void ) +{ + int i, j, k, m_skinnum; + float shellscale = 0.0f; + qboolean need_sort = false; + byte *pvertbone; + byte *pnormbone; + vec3_t *pstudioverts; + vec3_t *pstudionorms; + mstudiotexture_t *ptexture; + mstudiomesh_t *pmesh; + short *pskinref; + float lv_tmp; + + if( !m_pStudioHeader ) return; + + + g_studio.numverts = g_studio.numelems = 0; + + // safety bounding the skinnum + m_skinnum = bound( 0, RI.currententity->curstate.skin, ( m_pStudioHeader->numskinfamilies - 1 )); + ptexture = (mstudiotexture_t *)((byte *)m_pStudioHeader + m_pStudioHeader->textureindex); + pvertbone = ((byte *)m_pStudioHeader + m_pSubModel->vertinfoindex); + pnormbone = ((byte *)m_pStudioHeader + m_pSubModel->norminfoindex); + + pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex); + pstudioverts = (vec3_t *)((byte *)m_pStudioHeader + m_pSubModel->vertindex); + pstudionorms = (vec3_t *)((byte *)m_pStudioHeader + m_pSubModel->normindex); + + pskinref = (short *)((byte *)m_pStudioHeader + m_pStudioHeader->skinindex); + if( m_skinnum != 0 ) pskinref += (m_skinnum * m_pStudioHeader->numskinref); + + if( FBitSet( m_pStudioHeader->flags, STUDIO_HAS_BONEWEIGHTS ) && m_pSubModel->blendvertinfoindex != 0 && m_pSubModel->blendnorminfoindex != 0 ) + { + mstudioboneweight_t *pvertweight = (mstudioboneweight_t *)((byte *)m_pStudioHeader + m_pSubModel->blendvertinfoindex); + mstudioboneweight_t *pnormweight = (mstudioboneweight_t *)((byte *)m_pStudioHeader + m_pSubModel->blendnorminfoindex); + matrix3x4 skinMat __attribute__( (aligned( 16 ) ) ); + + for( i = 0; i < m_pSubModel->numverts; i++ ) + { + R_StudioComputeSkinMatrix( &pvertweight[i], skinMat ); + Matrix3x4_VectorTransform( skinMat, pstudioverts[i], g_studio.verts[i] ); + R_LightStrength( pvertbone[i], pstudioverts[i], g_studio.lightpos[i] ); + } + + for( i = 0; i < m_pSubModel->numnorms; i++ ) + { + R_StudioComputeSkinMatrix( &pnormweight[i], skinMat ); + Matrix3x4_VectorRotate( skinMat, pstudionorms[i], g_studio.norms[i] ); + } + } + else + { + for( i = 0; i < m_pSubModel->numverts; i++ ) + { + Matrix3x4_VectorTransform( g_studio.bonestransform[pvertbone[i]], pstudioverts[i], g_studio.verts[i] ); + R_LightStrength( pvertbone[i], pstudioverts[i], g_studio.lightpos[i] ); + } + } + + // generate shared normals for properly scaling glowing shell + if( RI.currententity->curstate.renderfx == kRenderFxGlowShell ) + { + float factor = (1.0f / 128.0f); + shellscale = Q_max( factor, RI.currententity->curstate.renderamt * factor ); + R_StudioBuildNormalTable(); + R_StudioGenerateNormals(); + } + + for( j = k = 0; j < m_pSubModel->nummesh; j++ ) + { + g_nFaceFlags = ptexture[pskinref[pmesh[j].skinref]].flags | g_nForceFaceFlags; + + // fill in sortedmesh info + g_studio.meshes[j].flags = g_nFaceFlags; + g_studio.meshes[j].mesh = &pmesh[j]; + + if( FBitSet( g_nFaceFlags, STUDIO_NF_MASKED|STUDIO_NF_ADDITIVE )) + need_sort = true; + + if( RI.currententity->curstate.rendermode == kRenderTransAdd ) + { + for( i = 0; i < pmesh[j].numnorms; i++, k++, pstudionorms++, pnormbone++ ) + { + if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME )) + R_StudioSetupChrome( g_studio.chrome[k], *pnormbone, (float *)pstudionorms ); + VectorSet( g_studio.lightvalues[k], tr.blend, tr.blend, tr.blend ); + } + } + else + { + for( i = 0; i < pmesh[j].numnorms; i++, k++, pstudionorms++, pnormbone++ ) + { + if( FBitSet( m_pStudioHeader->flags, STUDIO_HAS_BONEWEIGHTS )) + R_StudioLighting( &lv_tmp, -1, g_nFaceFlags, g_studio.norms[k] ); + else R_StudioLighting( &lv_tmp, *pnormbone, g_nFaceFlags, (float *)pstudionorms ); + + if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME )) + R_StudioSetupChrome( g_studio.chrome[k], *pnormbone, (float *)pstudionorms ); + VectorScale( g_studio.lightcolor, lv_tmp, g_studio.lightvalues[k] ); + } + } + } + + if( r_studio_sort_textures->value && need_sort ) + { + // resort opaque and translucent meshes draw order + qsort( g_studio.meshes, m_pSubModel->nummesh, sizeof( sortedmesh_t ), R_StudioMeshCompare ); + } + + // NOTE: rewind normals at start + pstudionorms = (vec3_t *)((byte *)m_pStudioHeader + m_pSubModel->normindex); + + for( j = 0; j < m_pSubModel->nummesh; j++ ) + { + float oldblend = tr.blend; + uint startArrayVerts = g_studio.numverts; + uint startArrayElems = g_studio.numelems; + short *ptricmds; + float s, t; + + pmesh = g_studio.meshes[j].mesh; + ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex); + + g_nFaceFlags = ptexture[pskinref[pmesh->skinref]].flags | g_nForceFaceFlags; + + s = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].width; + t = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].height; + + if( FBitSet( g_nFaceFlags, STUDIO_NF_MASKED )) + { + sceGuEnable( GU_ALPHA_TEST ); + sceGuAlphaFunc( GU_GREATER, 0x7f, 0xff ); + sceGuDepthMask( GU_FALSE ); + + if( R_ModelOpaque( RI.currententity->curstate.rendermode )) + tr.blend = 1.0f; + } + else if( FBitSet( g_nFaceFlags, STUDIO_NF_ADDITIVE )) + { + if( R_ModelOpaque( RI.currententity->curstate.rendermode )) + { + sceGuBlendFunc( GU_ADD, GU_FIX, GU_FIX, GUBLEND1, GUBLEND1 ); + sceGuDepthMask( GU_TRUE ); + sceGuEnable( GU_BLEND ); + + R_AllowFog( false ); + } + else sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, GUBLEND1 ); + } + + R_StudioSetupSkin( m_pStudioHeader, pskinref[pmesh->skinref] ); + + if( CVAR_TO_BOOL(r_studio_drawelements) ) + { + if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME )) + R_StudioBuildArrayChromeMesh( ptricmds, pstudionorms, s, t, shellscale ); + else if( FBitSet( g_nFaceFlags, STUDIO_NF_UV_COORDS )) + R_StudioBuildArrayFloatMesh( ptricmds, pstudionorms ); + else R_StudioBuildArrayNormalMesh( ptricmds, pstudionorms, s, t ); + + R_StudioDrawArrays( startArrayVerts, startArrayElems ); + } + else + { + if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME )) + R_StudioDrawChromeMesh( ptricmds, pstudionorms, s, t, shellscale ); + else if( FBitSet( g_nFaceFlags, STUDIO_NF_UV_COORDS )) + R_StudioDrawFloatMesh( ptricmds, pstudionorms ); + else R_StudioDrawNormalMesh( ptricmds, pstudionorms, s, t ); + } + + if( FBitSet( g_nFaceFlags, STUDIO_NF_MASKED )) + { + sceGuAlphaFunc( GU_GREATER, DEFAULT_ALPHATEST, 0xff ); + sceGuDisable( GU_ALPHA_TEST ); + } + else if( FBitSet( g_nFaceFlags, STUDIO_NF_ADDITIVE ) && R_ModelOpaque( RI.currententity->curstate.rendermode )) + { + sceGuDepthMask( GU_FALSE ); + sceGuDisable( GU_BLEND ); + + R_AllowFog( true ); + } + + r_stats.c_studio_polys += pmesh->numtris; + tr.blend = oldblend; + } +} + +/* +=============== +R_StudioDrawHulls + +=============== +*/ +static void R_StudioDrawHulls( void ) +{ +#if 0 + float alpha, lv; + int i, j; + + if( r_drawentities->value == 4 ) + alpha = 0.5f; + else alpha = 1.0f; + + GL_Bind( XASH_TEXTURE0, tr.whiteTexture ); + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + + for( i = 0; i < m_pStudioHeader->numhitboxes; i++ ) + { + mstudiobbox_t *pbbox = (mstudiobbox_t *)((byte *)m_pStudioHeader + m_pStudioHeader->hitboxindex); + vec3_t tmp, p[8]; + + for( j = 0; j < 8; j++ ) + { + tmp[0] = (j & 1) ? pbbox[i].bbmin[0] : pbbox[i].bbmax[0]; + tmp[1] = (j & 2) ? pbbox[i].bbmin[1] : pbbox[i].bbmax[1]; + tmp[2] = (j & 4) ? pbbox[i].bbmin[2] : pbbox[i].bbmax[2]; + + Matrix3x4_VectorTransform( g_studio.bonestransform[pbbox[i].bone], tmp, p[j] ); + } + + j = (pbbox[i].group % 8); + + TriBegin( TRI_QUADS ); + TriColor4f( hullcolor[j][0], hullcolor[j][1], hullcolor[j][2], alpha ); + + for( j = 0; j < 6; j++ ) + { + VectorClear( tmp ); + tmp[j % 3] = (j < 3) ? 1.0f : -1.0f; + R_StudioLighting( &lv, pbbox[i].bone, 0, tmp ); + + TriBrightness( lv ); + TriVertex3fv( p[boxpnt[j][0]] ); + TriVertex3fv( p[boxpnt[j][1]] ); + TriVertex3fv( p[boxpnt[j][2]] ); + TriVertex3fv( p[boxpnt[j][3]] ); + } + TriEnd(); + } +#endif +} + +/* +=============== +R_StudioDrawAbsBBox + +=============== +*/ +static void R_StudioDrawAbsBBox( void ) +{ +#if 0 + vec3_t p[8], tmp; + float lv; + int i; + + // looks ugly, skip + if( RI.currententity == gEngfuncs.GetViewModel() ) + return; + + if( !R_StudioComputeBBox( p )) + return; + + GL_Bind( XASH_TEXTURE0, tr.whiteTexture ); + TriColor4f( 0.5f, 0.5f, 1.0f, 0.5f ); + TriRenderMode( kRenderTransAdd ); + + TriBegin( TRI_QUADS ); + for( i = 0; i < 6; i++ ) + { + VectorClear( tmp ); + tmp[i % 3] = (i < 3) ? 1.0f : -1.0f; + R_StudioLighting( &lv, -1, 0, tmp ); + + TriBrightness( lv ); + TriVertex3fv( p[boxpnt[i][0]] ); + TriVertex3fv( p[boxpnt[i][1]] ); + TriVertex3fv( p[boxpnt[i][2]] ); + TriVertex3fv( p[boxpnt[i][3]] ); + } + TriEnd(); + TriRenderMode( kRenderNormal ); +#endif +} + +/* +=============== +R_StudioDrawBones + +=============== +*/ +static void R_StudioDrawBones( void ) +{ +#if 0 + mstudiobone_t *pbones = (mstudiobone_t *) ((byte *)m_pStudioHeader + m_pStudioHeader->boneindex); + vec3_t point; + int i; + + pglDisable( GL_TEXTURE_2D ); + + for( i = 0; i < m_pStudioHeader->numbones; i++ ) + { + if( pbones[i].parent >= 0 ) + { + pglPointSize( 3.0f ); + pglColor3f( 1, 0.7f, 0 ); + pglBegin( GL_LINES ); + + Matrix3x4_OriginFromMatrix( g_studio.bonestransform[pbones[i].parent], point ); + pglVertex3fv( point ); + Matrix3x4_OriginFromMatrix( g_studio.bonestransform[i], point ); + pglVertex3fv( point ); + + pglEnd(); + + pglColor3f( 0, 0, 0.8f ); + pglBegin( GL_POINTS ); + if( pbones[pbones[i].parent].parent != -1 ) + { + Matrix3x4_OriginFromMatrix( g_studio.bonestransform[pbones[i].parent], point ); + pglVertex3fv( point ); + } + Matrix3x4_OriginFromMatrix( g_studio.bonestransform[i], point ); + pglVertex3fv( point ); + pglEnd(); + } + else + { + // draw parent bone node + pglPointSize( 5.0f ); + pglColor3f( 0.8f, 0, 0 ); + pglBegin( GL_POINTS ); + Matrix3x4_OriginFromMatrix( g_studio.bonestransform[i], point ); + pglVertex3fv( point ); + pglEnd(); + } + } + + pglPointSize( 1.0f ); + pglEnable( GL_TEXTURE_2D ); +#endif +} + +static void R_StudioDrawAttachments( void ) +{ +#if 0 + int i; + + pglDisable( GL_TEXTURE_2D ); + pglDisable( GL_DEPTH_TEST ); + + for( i = 0; i < m_pStudioHeader->numattachments; i++ ) + { + mstudioattachment_t *pattachments; + vec3_t v[4]; + + pattachments = (mstudioattachment_t *)((byte *)m_pStudioHeader + m_pStudioHeader->attachmentindex); + Matrix3x4_VectorTransform( g_studio.bonestransform[pattachments[i].bone], pattachments[i].org, v[0] ); + Matrix3x4_VectorTransform( g_studio.bonestransform[pattachments[i].bone], pattachments[i].vectors[0], v[1] ); + Matrix3x4_VectorTransform( g_studio.bonestransform[pattachments[i].bone], pattachments[i].vectors[1], v[2] ); + Matrix3x4_VectorTransform( g_studio.bonestransform[pattachments[i].bone], pattachments[i].vectors[2], v[3] ); + + pglBegin( GL_LINES ); + pglColor3f( 1, 0, 0 ); + pglVertex3fv( v[0] ); + pglColor3f( 1, 1, 1 ); + pglVertex3fv (v[1] ); + pglColor3f( 1, 0, 0 ); + pglVertex3fv (v[0] ); + pglColor3f( 1, 1, 1 ); + pglVertex3fv (v[2] ); + pglColor3f( 1, 0, 0 ); + pglVertex3fv (v[0] ); + pglColor3f( 1, 1, 1 ); + pglVertex3fv( v[3] ); + pglEnd(); + + pglPointSize( 5.0f ); + pglColor3f( 0, 1, 0 ); + pglBegin( GL_POINTS ); + pglVertex3fv( v[0] ); + pglEnd(); + pglPointSize( 1.0f ); + } + + pglEnable( GL_TEXTURE_2D ); + pglEnable( GL_DEPTH_TEST ); +#endif +} + +/* +=============== +R_StudioSetRemapColors + +=============== +*/ +static void R_StudioSetRemapColors( int newTop, int newBottom ) +{ + gEngfuncs.CL_AllocRemapInfo( RI.currententity, newTop, newBottom ); + + if( gEngfuncs.CL_GetRemapInfoForEntity( RI.currententity )) + { + gEngfuncs.CL_UpdateRemapInfo( RI.currententity, newTop, newBottom ); + m_fDoRemap = true; + } +} + +void R_StudioResetPlayerModels( void ) +{ + memset( g_studio.player_models, 0, sizeof( g_studio.player_models )); +} + +/* +=============== +R_StudioSetupPlayerModel + +=============== +*/ +static model_t *R_StudioSetupPlayerModel( int index ) +{ + player_info_t *info = gEngfuncs.pfnPlayerInfo( index ); + player_model_t *state; + + state = &g_studio.player_models[index]; + + // g-cont: force for "dev-mode", non-local games and menu preview + if(( gpGlobals->developer || !ENGINE_GET_PARM( PARM_LOCAL_GAME ) || !RI.drawWorld ) && info->model[0] ) + { + if( Q_strcmp( state->name, info->model )) + { + Q_strncpy( state->name, info->model, sizeof( state->name )); + state->name[sizeof( state->name ) - 1] = 0; + + Q_snprintf( state->modelname, sizeof( state->modelname ), "models/player/%s/%s.mdl", info->model, info->model ); + + if( gEngfuncs.FS_FileExists( state->modelname, false )) + state->model = gEngfuncs.Mod_ForName( state->modelname, false, true ); + else state->model = NULL; + + if( !state->model ) + state->model = RI.currententity->model; + } + } + else + { + if( state->model != RI.currententity->model ) + state->model = RI.currententity->model; + state->name[0] = 0; + } + + return state->model; +} + +/* +================ +R_GetEntityRenderMode + +check for texture flags +================ +*/ +int R_GetEntityRenderMode( cl_entity_t *ent ) +{ + int i, opaque, trans; + mstudiotexture_t *ptexture; + cl_entity_t *oldent; + model_t *model; + studiohdr_t *phdr; + + oldent = RI.currententity; + RI.currententity = ent; + + if( ent->player ) // check it for real playermodel + model = R_StudioSetupPlayerModel( ent->curstate.number - 1 ); + else model = ent->model; + + RI.currententity = oldent; + + if(( phdr = gEngfuncs.Mod_Extradata( mod_studio, model )) == NULL ) + { + if( R_ModelOpaque( ent->curstate.rendermode )) + { + // forcing to choose right sorting type + if(( model && model->type == mod_brush ) && FBitSet( model->flags, MODEL_TRANSPARENT )) + return kRenderTransAlpha; + } + return ent->curstate.rendermode; + } + ptexture = (mstudiotexture_t *)((byte *)phdr + phdr->textureindex); + + for( opaque = trans = i = 0; i < phdr->numtextures; i++, ptexture++ ) + { + // ignore chrome & additive it's just a specular-like effect + if( FBitSet( ptexture->flags, STUDIO_NF_ADDITIVE ) && !FBitSet( ptexture->flags, STUDIO_NF_CHROME )) + trans++; + else opaque++; + } + + // if model is more additive than opaque + if( trans > opaque ) + return kRenderTransAdd; + return ent->curstate.rendermode; +} + +/* +=============== +R_StudioClientEvents + +=============== +*/ +static void R_StudioClientEvents( void ) +{ + mstudioseqdesc_t *pseqdesc; + mstudioevent_t *pevent; + cl_entity_t *e = RI.currententity; + int i, sequence; + float end, start; + + if( g_studio.frametime == 0.0 ) + return; // gamepaused + + // fill attachments with interpolated origin + if( m_pStudioHeader->numattachments <= 0 ) + { + Matrix3x4_OriginFromMatrix( g_studio.rotationmatrix, e->attachment[0] ); + Matrix3x4_OriginFromMatrix( g_studio.rotationmatrix, e->attachment[1] ); + Matrix3x4_OriginFromMatrix( g_studio.rotationmatrix, e->attachment[2] ); + Matrix3x4_OriginFromMatrix( g_studio.rotationmatrix, e->attachment[3] ); + } + + if( FBitSet( e->curstate.effects, EF_MUZZLEFLASH )) + { + dlight_t *el = gEngfuncs.CL_AllocElight( 0 ); + + ClearBits( e->curstate.effects, EF_MUZZLEFLASH ); + VectorCopy( e->attachment[0], el->origin ); + el->die = gpGlobals->time + 0.05f; + el->color.r = 255; + el->color.g = 192; + el->color.b = 64; + el->decay = 320; + el->radius = 24; + } + + sequence = bound( 0, e->curstate.sequence, m_pStudioHeader->numseq - 1 ); + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + sequence; + + // no events for this animation + if( pseqdesc->numevents == 0 ) + return; + + end = R_StudioEstimateFrame( e, pseqdesc ); + start = end - e->curstate.framerate * gpGlobals->frametime * pseqdesc->fps; + pevent = (mstudioevent_t *)((byte *)m_pStudioHeader + pseqdesc->eventindex); + + if( e->latched.sequencetime == e->curstate.animtime ) + { + if( !FBitSet( pseqdesc->flags, STUDIO_LOOPING )) + start = -0.01f; + } + + for( i = 0; i < pseqdesc->numevents; i++ ) + { + // ignore all non-client-side events + if( pevent[i].event < EVENT_CLIENT ) + continue; + + if( (float)pevent[i].frame > start && pevent[i].frame <= end ) + gEngfuncs.pfnStudioEvent( &pevent[i], e ); + } +} + +/* +=============== +R_StudioGetForceFaceFlags + +=============== +*/ +int R_StudioGetForceFaceFlags( void ) +{ + return g_nForceFaceFlags; +} + +/* +=============== +R_StudioSetForceFaceFlags + +=============== +*/ +void R_StudioSetForceFaceFlags( int flags ) +{ + g_nForceFaceFlags = flags; +} + +/* +=============== +pfnStudioSetHeader + +=============== +*/ +void R_StudioSetHeader( studiohdr_t *pheader ) +{ + m_pStudioHeader = pheader; + m_fDoRemap = false; +} + +/* +=============== +R_StudioSetRenderModel + +=============== +*/ +void R_StudioSetRenderModel( model_t *model ) +{ + RI.currentmodel = model; +} + +/* +=============== +R_StudioSetupRenderer + +=============== +*/ +static void R_StudioSetupRenderer( int rendermode ) +{ + studiohdr_t *phdr = m_pStudioHeader; + int i; + + if( rendermode > kRenderTransAdd ) rendermode = 0; + g_studio.rendermode = bound( 0, rendermode, kRenderTransAdd ); + + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + sceGuDisable( GU_ALPHA_TEST ); + sceGuShadeModel( GU_SMOOTH ); + + // a point to setup local to world transform for boneweighted models + if( phdr && FBitSet( phdr->flags, STUDIO_HAS_BONEINFO )) + { + // NOTE: extended boneinfo goes immediately after bones + mstudioboneinfo_t *boneinfo = (mstudioboneinfo_t *)((byte *)phdr + phdr->boneindex + phdr->numbones * sizeof( mstudiobone_t )); + + for( i = 0; i < phdr->numbones; i++ ) + Matrix3x4_ConcatTransforms( g_studio.worldtransform[i], g_studio.bonestransform[i], boneinfo[i].poseToBone ); + } +} + +/* +=============== +R_StudioRestoreRenderer + +=============== +*/ +static void R_StudioRestoreRenderer( void ) +{ + if( g_studio.rendermode != kRenderNormal ) + sceGuDisable( GU_BLEND ); + + sceGuTexFunc( GU_TFX_REPLACE, GU_TCC_RGBA ); + sceGuShadeModel( GU_FLAT ); + + m_fDoRemap = false; +} + +/* +=============== +R_StudioSetChromeOrigin + +=============== +*/ +void R_StudioSetChromeOrigin( void ) +{ + VectorCopy( RI.vieworg, g_studio.chrome_origin ); +} + +/* +=============== +pfnIsHardware + +Xash3D is always works in hardware mode +=============== +*/ +static int pfnIsHardware( void ) +{ + return 1; // 0 is Software, 1 is OpenGL, 2 is Direct3D +} + +/* +=============== +R_StudioDrawPointsShadow + +=============== +*/ +static void R_StudioDrawPointsShadow( void ) +{ + float *av, height; + float vec_x, vec_y; + mstudiomesh_t *pmesh; + vec3_t point; + int i, k; + + if( FBitSet( RI.currententity->curstate.effects, EF_NOSHADOW )) + return; + + if( glState.stencilEnabled ) + sceGuEnable( GU_STENCIL_TEST ); + + height = g_studio.lightspot[2] + 1.0f; + vec_x = -g_studio.lightvec[0] * 8.0f; + vec_y = -g_studio.lightvec[1] * 8.0f; + + for( k = 0; k < m_pSubModel->nummesh; k++ ) + { + short *ptricmds; + + pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + k; + ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex); + + r_stats.c_studio_polys += pmesh->numtris; + + while( ( i = *( ptricmds++ ) ) ) + { + int prim; + if( i < 0 ) + { + prim = GU_TRIANGLE_FAN; + i = -i; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + gu_vert_fv_t* const out = ( gu_vert_fv_t* )sceGuGetMemory( sizeof( gu_vert_fv_t ) * i ); + + for(int index = 0; index < i; index++, ptricmds += 4) + { + av = g_studio.verts[ptricmds[0]]; + point[0] = av[0] - (vec_x * ( av[2] - g_studio.lightspot[2] )); + point[1] = av[1] - (vec_y * ( av[2] - g_studio.lightspot[2] )); + point[2] = g_studio.lightspot[2] + 1.0f; + + out[index].x = point[0]; + out[index].y = point[1]; + out[index].z = point[2]; + } + sceGuDrawArray( prim, GU_VERTEX_32BITF, i, 0, out ); + } + } + if( glState.stencilEnabled ) + sceGuDisable( GU_STENCIL_TEST ); +} + +/* +=============== +GL_StudioSetRenderMode + +set rendermode for studiomodel +=============== +*/ +void GL_StudioSetRenderMode( int rendermode ) +{ + switch( rendermode ) + { + case kRenderNormal: + break; + case kRenderTransColor: + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuEnable( GU_BLEND ); + break; + case kRenderTransAdd: + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuColor( GUCOLOR4F( tr.blend, tr.blend, tr.blend, 1.0f ) ); + sceGuBlendFunc( GU_ADD, GU_FIX, GU_FIX, GUBLEND1, GUBLEND1 ); + sceGuDepthMask( GU_TRUE ); + sceGuEnable( GU_BLEND ); + break; + default: + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuColor( GUCOLOR4F( 1.0f, 1.0f, 1.0f, tr.blend ) ); + sceGuDepthMask( GU_FALSE ); + sceGuEnable( GU_BLEND ); + break; + } +} + +/* +=============== +GL_StudioDrawShadow + +g-cont: don't modify this code it's 100% matched with +original GoldSrc code and used in some mods to enable +studio shadows with some asm tricks +=============== +*/ +static void GL_StudioDrawShadow( void ) +{ + sceGuDepthMask( GU_FALSE ); + + if( r_shadows.value && g_studio.rendermode != kRenderTransAdd && !FBitSet( RI.currentmodel->flags, STUDIO_AMBIENT_LIGHT )) + { + float color = 1.0f - (tr.blend * 0.5f); + + sceGuDisable( GU_TEXTURE_2D ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuEnable( GU_BLEND ); + sceGuColor( GUCOLOR4F( 0.0f, 0.0f, 0.0f, 1.0f - color ) ); + + sceGuDepthFunc( GU_LESS ); + R_StudioDrawPointsShadow(); + sceGuDepthFunc( GU_LEQUAL ); + + sceGuEnable( GU_TEXTURE_2D ); + sceGuDisable( GU_BLEND ); + sceGuColor( GUCOLOR4F( 1.0f, 1.0f, 1.0f, 1.0f ) ); + sceGuShadeModel( GU_SMOOTH ); + } +} + +/* +==================== +StudioRenderFinal + +==================== +*/ +void R_StudioRenderFinal( void ) +{ + int i, rendermode; + + rendermode = R_StudioGetForceFaceFlags() ? kRenderTransAdd : RI.currententity->curstate.rendermode; + R_StudioSetupRenderer( rendermode ); + + if( r_drawentities->value == 2 ) + { + R_StudioDrawBones(); + } + else if( r_drawentities->value == 3 ) + { + R_StudioDrawHulls(); + } + else + { + for( i = 0; i < m_pStudioHeader->numbodyparts; i++ ) + { + R_StudioSetupModel( i, (void**)&m_pBodyPart, (void**)&m_pSubModel ); + + GL_StudioSetRenderMode( rendermode ); + R_StudioDrawPoints(); + GL_StudioDrawShadow(); + } + } + + if( r_drawentities->value == 4 ) + { + TriRenderMode( kRenderTransAdd ); + R_StudioDrawHulls( ); + TriRenderMode( kRenderNormal ); + } + + if( r_drawentities->value == 5 ) + { + R_StudioDrawAbsBBox( ); + } + + if( r_drawentities->value == 6 ) + { + R_StudioDrawAttachments(); + } + + if( r_drawentities->value == 7 ) + { + vec3_t origin; +#if 1 +#else + pglDisable( GL_TEXTURE_2D ); + pglDisable( GL_DEPTH_TEST ); + + Matrix3x4_OriginFromMatrix( g_studio.rotationmatrix, origin ); + + pglBegin( GL_LINES ); + pglColor3f( 1, 0.5, 0 ); + pglVertex3fv( origin ); + pglVertex3fv( g_studio.lightspot ); + pglEnd(); + + pglBegin( GL_LINES ); + pglColor3f( 0, 0.5, 1 ); + VectorMA( g_studio.lightspot, -64.0f, g_studio.lightvec, origin ); + pglVertex3fv( g_studio.lightspot ); + pglVertex3fv( origin ); + pglEnd(); + + pglPointSize( 5.0f ); + pglColor3f( 1, 0, 0 ); + pglBegin( GL_POINTS ); + pglVertex3fv( g_studio.lightspot ); + pglEnd(); + pglPointSize( 1.0f ); + + pglEnable( GL_DEPTH_TEST ); + pglEnable( GL_TEXTURE_2D ); +#endif + } + + R_StudioRestoreRenderer(); +} + +/* +==================== +StudioRenderModel + +==================== +*/ +void R_StudioRenderModel( void ) +{ + R_StudioSetChromeOrigin(); + R_StudioSetForceFaceFlags( 0 ); + + if( RI.currententity->curstate.renderfx == kRenderFxGlowShell ) + { + RI.currententity->curstate.renderfx = kRenderFxNone; + + R_StudioRenderFinal( ); + + R_StudioSetForceFaceFlags( STUDIO_NF_CHROME ); + TriSpriteTexture( R_GetChromeSprite(), 0 ); + RI.currententity->curstate.renderfx = kRenderFxGlowShell; + + R_StudioRenderFinal( ); + } + else + { + R_StudioRenderFinal( ); + } +} + +/* +==================== +StudioEstimateGait + +==================== +*/ +void R_StudioEstimateGait( entity_state_t *pplayer ) +{ + vec3_t est_velocity; + float dt; + + dt = bound( 0.0f, g_studio.frametime, 1.0f ); + + if( dt == 0.0f || m_pPlayerInfo->renderframe == tr.realframecount ) + { + m_flGaitMovement = 0; + return; + } + + VectorSubtract( RI.currententity->origin, m_pPlayerInfo->prevgaitorigin, est_velocity ); + VectorCopy( RI.currententity->origin, m_pPlayerInfo->prevgaitorigin ); + m_flGaitMovement = VectorLength( est_velocity ); + + if( dt <= 0.0f || m_flGaitMovement / dt < 5.0f ) + { + m_flGaitMovement = 0.0f; + est_velocity[0] = 0.0f; + est_velocity[1] = 0.0f; + } + + if( est_velocity[1] == 0.0f && est_velocity[0] == 0.0f ) + { + float flYawDiff = RI.currententity->angles[YAW] - m_pPlayerInfo->gaityaw; + + flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360; + if( flYawDiff > 180.0f ) flYawDiff -= 360.0f; + if( flYawDiff < -180.0f ) flYawDiff += 360.0f; + + if( dt < 0.25f ) + flYawDiff *= dt * 4.0f; + else flYawDiff *= dt; + + m_pPlayerInfo->gaityaw += flYawDiff; + m_pPlayerInfo->gaityaw = m_pPlayerInfo->gaityaw - (int)(m_pPlayerInfo->gaityaw / 360) * 360; + + m_flGaitMovement = 0.0f; + } + else + { + m_pPlayerInfo->gaityaw = ( atan2( est_velocity[1], est_velocity[0] ) * 180 / M_PI_F ); + if( m_pPlayerInfo->gaityaw > 180.0f ) m_pPlayerInfo->gaityaw = 180.0f; + if( m_pPlayerInfo->gaityaw < -180.0f ) m_pPlayerInfo->gaityaw = -180.0f; + } + +} + +/* +==================== +StudioProcessGait + +==================== +*/ +void R_StudioProcessGait( entity_state_t *pplayer ) +{ + mstudioseqdesc_t *pseqdesc; + int iBlend; + float dt, flYaw; // view direction relative to movement + + if( RI.currententity->curstate.sequence >= m_pStudioHeader->numseq ) + RI.currententity->curstate.sequence = 0; + + dt = bound( 0.0f, g_studio.frametime, 1.0f ); + + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + RI.currententity->curstate.sequence; + + R_StudioPlayerBlend( pseqdesc, &iBlend, &RI.currententity->angles[PITCH] ); + + RI.currententity->latched.prevangles[PITCH] = RI.currententity->angles[PITCH]; + RI.currententity->curstate.blending[0] = iBlend; + RI.currententity->latched.prevblending[0] = RI.currententity->curstate.blending[0]; + RI.currententity->latched.prevseqblending[0] = RI.currententity->curstate.blending[0]; + R_StudioEstimateGait( pplayer ); + + // calc side to side turning + flYaw = RI.currententity->angles[YAW] - m_pPlayerInfo->gaityaw; + flYaw = flYaw - (int)(flYaw / 360) * 360; + if( flYaw < -180.0f ) flYaw = flYaw + 360.0f; + if( flYaw > 180.0f ) flYaw = flYaw - 360.0f; + + if( flYaw > 120.0f ) + { + m_pPlayerInfo->gaityaw = m_pPlayerInfo->gaityaw - 180.0f; + m_flGaitMovement = -m_flGaitMovement; + flYaw = flYaw - 180.0f; + } + else if( flYaw < -120.0f ) + { + m_pPlayerInfo->gaityaw = m_pPlayerInfo->gaityaw + 180.0f; + m_flGaitMovement = -m_flGaitMovement; + flYaw = flYaw + 180.0f; + } + + // adjust torso + RI.currententity->curstate.controller[0] = ((flYaw / 4.0f) + 30.0f) / (60.0f / 255.0f); + RI.currententity->curstate.controller[1] = ((flYaw / 4.0f) + 30.0f) / (60.0f / 255.0f); + RI.currententity->curstate.controller[2] = ((flYaw / 4.0f) + 30.0f) / (60.0f / 255.0f); + RI.currententity->curstate.controller[3] = ((flYaw / 4.0f) + 30.0f) / (60.0f / 255.0f); + RI.currententity->latched.prevcontroller[0] = RI.currententity->curstate.controller[0]; + RI.currententity->latched.prevcontroller[1] = RI.currententity->curstate.controller[1]; + RI.currententity->latched.prevcontroller[2] = RI.currententity->curstate.controller[2]; + RI.currententity->latched.prevcontroller[3] = RI.currententity->curstate.controller[3]; + + RI.currententity->angles[YAW] = m_pPlayerInfo->gaityaw; + if( RI.currententity->angles[YAW] < -0 ) RI.currententity->angles[YAW] += 360.0f; + RI.currententity->latched.prevangles[YAW] = RI.currententity->angles[YAW]; + + if( pplayer->gaitsequence >= m_pStudioHeader->numseq ) + pplayer->gaitsequence = 0; + + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + pplayer->gaitsequence; + + // calc gait frame + if( pseqdesc->linearmovement[0] > 0 ) + m_pPlayerInfo->gaitframe += (m_flGaitMovement / pseqdesc->linearmovement[0]) * pseqdesc->numframes; + else m_pPlayerInfo->gaitframe += pseqdesc->fps * dt; + + // do modulo + m_pPlayerInfo->gaitframe = m_pPlayerInfo->gaitframe - (int)(m_pPlayerInfo->gaitframe / pseqdesc->numframes) * pseqdesc->numframes; + if( m_pPlayerInfo->gaitframe < 0 ) m_pPlayerInfo->gaitframe += pseqdesc->numframes; +} + +/* +=============== +R_StudioDrawPlayer + +=============== +*/ +static int R_StudioDrawPlayer( int flags, entity_state_t *pplayer ) +{ + int m_nPlayerIndex; + alight_t lighting; + vec3_t dir; + + m_nPlayerIndex = pplayer->number - 1; + + if( m_nPlayerIndex < 0 || m_nPlayerIndex >= ENGINE_GET_PARM( PARM_MAX_CLIENTS ) ) + return 0; + + RI.currentmodel = R_StudioSetupPlayerModel( m_nPlayerIndex ); + if( RI.currentmodel == NULL ) + return 0; + + R_StudioSetHeader((studiohdr_t *)gEngfuncs.Mod_Extradata( mod_studio, RI.currentmodel )); + + if( pplayer->gaitsequence ) + { + vec3_t orig_angles; + + m_pPlayerInfo = pfnPlayerInfo( m_nPlayerIndex ); + VectorCopy( RI.currententity->angles, orig_angles ); + + R_StudioProcessGait( pplayer ); + + m_pPlayerInfo->gaitsequence = pplayer->gaitsequence; + m_pPlayerInfo = NULL; + + R_StudioSetUpTransform( RI.currententity ); + VectorCopy( orig_angles, RI.currententity->angles ); + } + else + { + RI.currententity->curstate.controller[0] = 127; + RI.currententity->curstate.controller[1] = 127; + RI.currententity->curstate.controller[2] = 127; + RI.currententity->curstate.controller[3] = 127; + RI.currententity->latched.prevcontroller[0] = RI.currententity->curstate.controller[0]; + RI.currententity->latched.prevcontroller[1] = RI.currententity->curstate.controller[1]; + RI.currententity->latched.prevcontroller[2] = RI.currententity->curstate.controller[2]; + RI.currententity->latched.prevcontroller[3] = RI.currententity->curstate.controller[3]; + + m_pPlayerInfo = pfnPlayerInfo( m_nPlayerIndex ); + m_pPlayerInfo->gaitsequence = 0; + + R_StudioSetUpTransform( RI.currententity ); + } + + if( flags & STUDIO_RENDER ) + { + // see if the bounding box lets us trivially reject, also sets + if( !R_StudioCheckBBox( )) + return 0; + + r_stats.c_studio_models_drawn++; + g_studio.framecount++; // render data cache cookie + + if( m_pStudioHeader->numbodyparts == 0 ) + return 1; + } + + m_pPlayerInfo = pfnPlayerInfo( m_nPlayerIndex ); + R_StudioSetupBones( RI.currententity ); + R_StudioSaveBones( ); + + m_pPlayerInfo->renderframe = tr.realframecount; + m_pPlayerInfo = NULL; + + if( flags & STUDIO_EVENTS ) + { + R_StudioCalcAttachments( ); + R_StudioClientEvents( ); + + // copy attachments into global entity array + if( RI.currententity->index > 0 ) + { + cl_entity_t *ent = gEngfuncs.GetEntityByIndex( RI.currententity->index ); + memcpy( ent->attachment, RI.currententity->attachment, sizeof( vec3_t ) * 4 ); + } + } + + if( flags & STUDIO_RENDER ) + { + if( cl_himodels->value && RI.currentmodel != RI.currententity->model ) + { + // show highest resolution multiplayer model + RI.currententity->curstate.body = 255; + } + + if( !( !gpGlobals->developer && ENGINE_GET_PARM( PARM_MAX_CLIENTS ) == 1 ) && ( RI.currentmodel == RI.currententity->model )) + RI.currententity->curstate.body = 1; // force helmet + + lighting.plightvec = dir; + R_StudioDynamicLight( RI.currententity, &lighting ); + + R_StudioEntityLight( &lighting ); + + // model and frame independant + R_StudioSetupLighting( &lighting ); + + m_pPlayerInfo = pfnPlayerInfo( m_nPlayerIndex ); + + // get remap colors + g_nTopColor = m_pPlayerInfo->topcolor; + g_nBottomColor = m_pPlayerInfo->bottomcolor; + + if( g_nTopColor < 0 ) g_nTopColor = 0; + if( g_nTopColor > 360 ) g_nTopColor = 360; + if( g_nBottomColor < 0 ) g_nBottomColor = 0; + if( g_nBottomColor > 360 ) g_nBottomColor = 360; + + R_StudioSetRemapColors( g_nTopColor, g_nBottomColor ); + + R_StudioRenderModel( ); + m_pPlayerInfo = NULL; + + if( pplayer->weaponmodel ) + { + cl_entity_t saveent = *RI.currententity; + model_t *pweaponmodel = gEngfuncs.pfnGetModelByIndex( pplayer->weaponmodel ); + + m_pStudioHeader = (studiohdr_t *)gEngfuncs.Mod_Extradata( mod_studio, pweaponmodel ); + + R_StudioMergeBones( RI.currententity, pweaponmodel ); + R_StudioSetupLighting( &lighting ); + R_StudioRenderModel( ); + R_StudioCalcAttachments( ); + + *RI.currententity = saveent; + } + } + + return 1; +} + +/* +=============== +R_StudioDrawModel + +=============== +*/ +static int R_StudioDrawModel( int flags ) +{ + alight_t lighting; + vec3_t dir; + + if( RI.currententity->curstate.renderfx == kRenderFxDeadPlayer ) + { + qboolean save_interpolate; + entity_state_t deadplayer; + int result; + + if( RI.currententity->curstate.renderamt <= 0 || + RI.currententity->curstate.renderamt > ENGINE_GET_PARM( PARM_MAX_CLIENTS ) ) + return 0; + + // get copy of player + deadplayer = *R_StudioGetPlayerState( RI.currententity->curstate.renderamt - 1 ); + + // clear weapon, movement state + deadplayer.number = RI.currententity->curstate.renderamt; + deadplayer.weaponmodel = 0; + deadplayer.gaitsequence = 0; + + deadplayer.movetype = MOVETYPE_NONE; + VectorCopy( RI.currententity->curstate.angles, deadplayer.angles ); + VectorCopy( RI.currententity->curstate.origin, deadplayer.origin ); + + save_interpolate = g_studio.interpolate; + g_studio.interpolate = false; + result = R_StudioDrawPlayer( flags, &deadplayer ); // draw as though it were a player + g_studio.interpolate = save_interpolate; + + return result; + } + + R_StudioSetHeader((studiohdr_t *)gEngfuncs.Mod_Extradata( mod_studio, RI.currentmodel )); + + R_StudioSetUpTransform( RI.currententity ); + + if( flags & STUDIO_RENDER ) + { + // see if the bounding box lets us trivially reject, also sets + if( !R_StudioCheckBBox( )) + return 0; + + r_stats.c_studio_models_drawn++; + g_studio.framecount++; // render data cache cookie + + if( m_pStudioHeader->numbodyparts == 0 ) + return 1; + } + + if( RI.currententity->curstate.movetype == MOVETYPE_FOLLOW ) + R_StudioMergeBones( RI.currententity, RI.currentmodel ); + else R_StudioSetupBones( RI.currententity ); + + R_StudioSaveBones(); + + if( flags & STUDIO_EVENTS ) + { + R_StudioCalcAttachments( ); + R_StudioClientEvents( ); + + // copy attachments into global entity array + if( RI.currententity->index > 0 ) + { + cl_entity_t *ent = gEngfuncs.GetEntityByIndex( RI.currententity->index ); + memcpy( ent->attachment, RI.currententity->attachment, sizeof( vec3_t ) * 4 ); + } + } + + if( flags & STUDIO_RENDER ) + { + lighting.plightvec = dir; + R_StudioDynamicLight( RI.currententity, &lighting ); + + R_StudioEntityLight( &lighting ); + + // model and frame independant + R_StudioSetupLighting( &lighting ); + + // get remap colors + g_nTopColor = RI.currententity->curstate.colormap & 0xFF; + g_nBottomColor = (RI.currententity->curstate.colormap & 0xFF00) >> 8; + + R_StudioSetRemapColors( g_nTopColor, g_nBottomColor ); + + R_StudioRenderModel(); + } + + return 1; +} + +/* +================= +R_StudioDrawModelInternal +================= +*/ +void R_StudioDrawModelInternal( cl_entity_t *e, int flags ) +{ + if( !RI.drawWorld ) + { + if( e->player ) + R_StudioDrawPlayer( flags, &e->curstate ); + else R_StudioDrawModel( flags ); + } + else + { + // select the properly method + if( e->player ) + pStudioDraw->StudioDrawPlayer( flags, R_StudioGetPlayerState( e->index - 1 )); + else pStudioDraw->StudioDrawModel( flags ); + } +} + +/* +================= +R_DrawStudioModel +================= +*/ +void R_DrawStudioModel( cl_entity_t *e ) +{ + if( FBitSet( RI.params, RP_ENVVIEW )) + return; + + R_StudioSetupTimings(); + + if( e->player ) + { + R_StudioDrawModelInternal( e, STUDIO_RENDER|STUDIO_EVENTS ); + } + else + { + if( e->curstate.movetype == MOVETYPE_FOLLOW && e->curstate.aiment > 0 ) + { + cl_entity_t *parent = gEngfuncs.GetEntityByIndex( e->curstate.aiment ); + + if( parent && parent->model && parent->model->type == mod_studio ) + { + RI.currententity = parent; + R_StudioDrawModelInternal( RI.currententity, 0 ); + VectorCopy( parent->curstate.origin, e->curstate.origin ); + VectorCopy( parent->origin, e->origin ); + RI.currententity = e; + } + } + + R_StudioDrawModelInternal( e, STUDIO_RENDER|STUDIO_EVENTS ); + } +} + +/* +================= +R_RunViewmodelEvents +================= +*/ +void R_RunViewmodelEvents( void ) +{ + int i; + vec3_t simorg; + + if( r_drawviewmodel->value == 0 ) + return; + + if( ENGINE_GET_PARM( PARM_THIRDPERSON )) + return; + + // ignore in thirdperson, camera view or client is died + if( !RP_NORMALPASS() || ENGINE_GET_PARM( PARM_LOCAL_HEALTH ) <= 0 || !CL_IsViewEntityLocalPlayer()) + return; + + RI.currententity = gEngfuncs.GetViewModel(); + + if( !RI.currententity->model || RI.currententity->model->type != mod_studio ) + return; + + R_StudioSetupTimings(); + + gEngfuncs.GetPredictedOrigin( simorg ); + for( i = 0; i < 4; i++ ) + VectorCopy( simorg, RI.currententity->attachment[i] ); + RI.currentmodel = RI.currententity->model; + + R_StudioDrawModelInternal( RI.currententity, STUDIO_EVENTS ); +} + +/* +================= +R_GatherPlayerLight +================= +*/ +void R_GatherPlayerLight( void ) +{ + cl_entity_t *view = gEngfuncs.GetViewModel(); + colorVec c; + + tr.ignore_lightgamma = true; + c = R_LightPoint( view->origin ); + tr.ignore_lightgamma = false; + gEngfuncs.SetLocalLightLevel( ( c.r + c.g + c.b ) / 3 ); +} + +/* +================= +R_DrawViewModel +================= +*/ +void R_DrawViewModel( void ) +{ + qboolean m_flip; + + cl_entity_t *view = gEngfuncs.GetViewModel(); + + R_GatherPlayerLight(); + + if( r_drawviewmodel->value == 0 ) + return; + + if( ENGINE_GET_PARM( PARM_THIRDPERSON )) + return; + + // ignore in thirdperson, camera view or client is died + if( !RP_NORMALPASS() || ENGINE_GET_PARM( PARM_LOCAL_HEALTH ) <= 0 || !CL_IsViewEntityLocalPlayer()) + return; + + tr.blend = CL_FxBlend( view ) / 255.0f; + if( !R_ModelOpaque( view->curstate.rendermode ) && tr.blend <= 0.0f ) + return; // invisible ? + + RI.currententity = view; + + if( !RI.currententity->model ) + return; + + // adjust the depth range to prevent view model from poking into walls + sceGuDepthRange( ( int )gldepthmin, ( int )( gldepthmin + 0.3f * ( gldepthmax - gldepthmin ) ) ); + + RI.currentmodel = RI.currententity->model; + + m_flip = R_AllowFlipViewModel( RI.currententity ); + + // backface culling for left-handed weapons + if( m_flip || g_iBackFaceCull ) + { + tr.fFlipViewModel = true; + GL_Cull( GL_FRONT ); + } + + switch( RI.currententity->model->type ) + { + case mod_alias: + R_DrawAliasModel( RI.currententity ); + break; + case mod_studio: + R_StudioSetupTimings(); + R_StudioDrawModelInternal( RI.currententity, STUDIO_RENDER ); + break; + } + + // restore depth range + sceGuDepthRange( ( int )gldepthmin, ( int )gldepthmax ); + + // backface culling for left-handed weapons + if( m_flip || g_iBackFaceCull ) + { + tr.fFlipViewModel = false; + GL_Cull( GL_BACK ); + } +} + +/* +==================== +R_StudioLoadTexture + +load model texture with unique name +==================== +*/ +static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture_t *ptexture ) +{ + size_t size; + int flags = 0; + char texname[128], name[128], mdlname[128]; + texture_t *tx = NULL; + + if( ptexture->flags & STUDIO_NF_NORMALMAP ) + flags |= (TF_NORMALMAP); + + // store some textures for remapping + if( !Q_strnicmp( ptexture->name, "DM_Base", 7 ) || !Q_strnicmp( ptexture->name, "remap", 5 )) + { + int i, size; + char val[6]; + byte *pixels; + + i = mod->numtextures; + mod->textures = (texture_t **)Mem_Realloc( mod->mempool, mod->textures, ( i + 1 ) * sizeof( texture_t* )); + size = ptexture->width * ptexture->height + 768; + tx = Mem_Calloc( mod->mempool, sizeof( *tx ) + size ); + mod->textures[i] = tx; + + // store ranges into anim_min, anim_max etc + if( !Q_strnicmp( ptexture->name, "DM_Base", 7 )) + { + Q_strncpy( tx->name, "DM_Base", sizeof( tx->name )); + tx->anim_min = PLATE_HUE_START; // topcolor start + tx->anim_max = PLATE_HUE_END; // topcolor end + // bottomcolor start always equal is (topcolor end + 1) + tx->anim_total = SUIT_HUE_END;// bottomcolor end + } + else + { + Q_strncpy( tx->name, "DM_User", sizeof( tx->name )); // custom remapped + Q_strncpy( val, ptexture->name + 7, 4 ); + tx->anim_min = bound( 0, Q_atoi( val ), 255 ); // topcolor start + Q_strncpy( val, ptexture->name + 11, 4 ); + tx->anim_max = bound( 0, Q_atoi( val ), 255 ); // topcolor end + // bottomcolor start always equal is (topcolor end + 1) + Q_strncpy( val, ptexture->name + 15, 4 ); + tx->anim_total = bound( 0, Q_atoi( val ), 255 ); // bottomcolor end + } + + tx->width = ptexture->width; + tx->height = ptexture->height; + + // the pixels immediately follow the structures + pixels = (byte *)phdr + ptexture->index; + memcpy( tx+1, pixels, size ); + + ptexture->flags |= STUDIO_NF_COLORMAP; // yes, this is colormap image + flags |= TF_FORCE_COLOR; + + mod->numtextures++; // done + } + + Q_strncpy( mdlname, mod->name, sizeof( mdlname )); + COM_FileBase( ptexture->name, name ); + COM_StripExtension( mdlname ); + + if( FBitSet( ptexture->flags, STUDIO_NF_NOMIPS )) + SetBits( flags, TF_NOMIPMAP ); + + // NOTE: replace index with pointer to start of imagebuffer, ImageLib expected it + //ptexture->index = (int)((byte *)phdr) + ptexture->index; + gEngfuncs.Image_SetMDLPointer((byte *)phdr + ptexture->index); + size = sizeof( mstudiotexture_t ) + ptexture->width * ptexture->height + 768; + + if( FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_IMPROVED_LINETRACE ) && FBitSet( ptexture->flags, STUDIO_NF_MASKED )) + flags |= TF_KEEP_SOURCE; // Paranoia2 texture alpha-tracing + + // build the texname + Q_snprintf( texname, sizeof( texname ), "#%s/%s.mdl", mdlname, name ); + ptexture->index = GL_LoadTexture( texname, (byte *)ptexture, size, flags ); + + if( !ptexture->index ) + { + ptexture->index = tr.defaultTexture; + } + else if( tx ) + { + // duplicate texnum for easy acess + tx->gl_texturenum = ptexture->index; + } +} + +/* +================= +Mod_StudioLoadTextures +================= +*/ +void Mod_StudioLoadTextures( model_t *mod, void *data ) +{ + studiohdr_t *phdr = (studiohdr_t *)data; + mstudiotexture_t *ptexture; + int i; + + if( !phdr ) + return; + + ptexture = (mstudiotexture_t *)(((byte *)phdr) + phdr->textureindex); + if( phdr->textureindex > 0 && phdr->numtextures <= MAXSTUDIOSKINS ) + { + for( i = 0; i < phdr->numtextures; i++ ) + R_StudioLoadTexture( mod, phdr, &ptexture[i] ); + } +} + +/* +================= +Mod_StudioUnloadTextures +================= +*/ +void Mod_StudioUnloadTextures( void *data ) +{ + studiohdr_t *phdr = (studiohdr_t *)data; + mstudiotexture_t *ptexture; + int i; + + if( !phdr ) + return; + + ptexture = (mstudiotexture_t *)(((byte *)phdr) + phdr->textureindex); + + // release all textures + for( i = 0; i < phdr->numtextures; i++ ) + { + if( ptexture[i].index == tr.defaultTexture ) + continue; + GL_FreeTexture( ptexture[i].index ); + } +} + +static model_t *pfnModelHandle( int modelindex ) +{ + return gEngfuncs.pfnGetModelByIndex( modelindex ); +} + +static void *pfnMod_CacheCheck( struct cache_user_s *c ) +{ + return gEngfuncs.Mod_CacheCheck( c ); +} + +static void *pfnMod_StudioExtradata( model_t *mod ) +{ + return gEngfuncs.Mod_Extradata( mod_studio, mod ); +} + +static void pfnMod_LoadCacheFile( const char *path, struct cache_user_s *cu ) +{ + gEngfuncs.Mod_LoadCacheFile( path, cu ); +} + +static cvar_t *pfnGetCvarPointer( const char *name ) +{ + return (cvar_t*)gEngfuncs.pfnGetCvarPointer( name, 0 ); +} + +static void *pfnMod_Calloc( int number, size_t size ) +{ + return gEngfuncs.Mod_Calloc( number, size ); +} + +static engine_studio_api_t gStudioAPI = +{ + pfnMod_Calloc, + pfnMod_CacheCheck, + pfnMod_LoadCacheFile, + pfnMod_ForName, + pfnMod_StudioExtradata, + pfnModelHandle, + pfnGetCurrentEntity, + pfnPlayerInfo, + R_StudioGetPlayerState, + pfnGetViewEntity, + pfnGetEngineTimes, + pfnGetCvarPointer, + pfnGetViewInfo, + R_GetChromeSprite, + pfnGetModelCounters, + pfnGetAliasScale, + pfnStudioGetBoneTransform, + pfnStudioGetLightTransform, + pfnStudioGetAliasTransform, + pfnStudioGetRotationMatrix, + R_StudioSetupModel, + R_StudioCheckBBox, + R_StudioDynamicLight, + R_StudioEntityLight, + R_StudioSetupLighting, + R_StudioDrawPoints, + R_StudioDrawHulls, + R_StudioDrawAbsBBox, + R_StudioDrawBones, + (void*)R_StudioSetupSkin, + R_StudioSetRemapColors, + R_StudioSetupPlayerModel, + R_StudioClientEvents, + R_StudioGetForceFaceFlags, + R_StudioSetForceFaceFlags, + (void*)R_StudioSetHeader, + R_StudioSetRenderModel, + R_StudioSetupRenderer, + R_StudioRestoreRenderer, + R_StudioSetChromeOrigin, + pfnIsHardware, + GL_StudioDrawShadow, + GL_StudioSetRenderMode, + R_StudioSetRenderamt, + R_StudioSetCullState, + R_StudioRenderShadow, +}; + +static r_studio_interface_t gStudioDraw = +{ + STUDIO_INTERFACE_VERSION, + R_StudioDrawModel, + R_StudioDrawPlayer, +}; + +/* +=============== +CL_InitStudioAPI + +Initialize client studio +=============== +*/ +void CL_InitStudioAPI( void ) +{ + pStudioDraw = &gStudioDraw; + + // trying to grab them from client.dll + cl_righthand = gEngfuncs.pfnGetCvarPointer( "cl_righthand", 0 ); + + if( cl_righthand == NULL ) + cl_righthand = gEngfuncs.Cvar_Get( "cl_righthand", "0", FCVAR_ARCHIVE, "flip viewmodel (left to right)" ); + + // Xash will be used internal StudioModelRenderer + if( gEngfuncs.pfnGetStudioModelInterface( STUDIO_INTERFACE_VERSION, &pStudioDraw, &gStudioAPI )) + return; + + // NOTE: we always return true even if game interface was not correct + // because we need Draw our StudioModels + // just restore pointer to builtin function + pStudioDraw = &gStudioDraw; +} diff --git a/ref_gu/gu_triapi.c b/ref_gu/gu_triapi.c new file mode 100644 index 00000000..caff6115 --- /dev/null +++ b/ref_gu/gu_triapi.c @@ -0,0 +1,577 @@ +/* +gu_triapi.c - TriAPI draw methods +Copyright (C) 2011 Uncle Mike +Copyright (C) 2019 a1batross +Copyright (C) 2021 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" +#include "const.h" + +static struct +{ + int renderMode; // override kRenderMode from TriAPI + vec4_t triRGBA; +} ds; + +/* +=============================================================== + + TRIAPI IMPLEMENTATION + +=============================================================== +*/ +#if 1 +static struct +{ + void *start; + gu_vert_ftcv_t *list; + qboolean begin; + int count; + int prim; + unsigned int flags; +}tri_gucontext = +{ + .start = NULL, + .list = NULL, + .begin = false, + .count = 0, + .prim = 0, + .flags = 0 +}; +#endif + +/* +============= +TriRenderMode + +set rendermode +============= +*/ +void TriRenderMode( int mode ) +{ +#if 1 + ds.renderMode = mode; + switch( mode ) + { + case kRenderNormal: + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuDisable( GU_BLEND ); + sceGuDepthMask( GU_FALSE ); + break; + case kRenderTransAlpha: + sceGuEnable( GU_BLEND ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuDepthMask( GU_TRUE ); + break; + case kRenderTransColor: + case kRenderTransTexture: + sceGuEnable( GU_BLEND ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + break; + case kRenderGlow: + case kRenderTransAdd: + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, GUBLEND1 ); + sceGuEnable( GU_BLEND ); + sceGuDepthMask( GU_TRUE ); + break; + } +#else + ds.renderMode = mode; + switch( mode ) + { + case kRenderNormal: + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + pglDisable( GL_BLEND ); + pglDepthMask( GL_TRUE ); + break; + case kRenderTransAlpha: + pglEnable( GL_BLEND ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + pglDepthMask( GL_FALSE ); + break; + case kRenderTransColor: + case kRenderTransTexture: + pglEnable( GL_BLEND ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + break; + case kRenderGlow: + case kRenderTransAdd: + pglBlendFunc( GL_SRC_ALPHA, GL_ONE ); + pglEnable( GL_BLEND ); + pglDepthMask( GL_FALSE ); + break; + } +#endif +} + +/* +============= +TriBegin + +begin triangle sequence +============= +*/ +void TriBegin( int mode ) +{ +#if 1 + switch( mode ) + { + case TRI_POINTS: + tri_gucontext.prim = GU_POINTS; + break; + case TRI_TRIANGLES: + tri_gucontext.prim = GU_TRIANGLES; + break; + case TRI_TRIANGLE_FAN: + tri_gucontext.prim = GU_TRIANGLE_FAN; + break; + case TRI_QUADS: + tri_gucontext.prim = GU_TRIANGLE_FAN; + break; + case TRI_LINES: + tri_gucontext.prim = GU_LINES; + break; + case TRI_TRIANGLE_STRIP: + tri_gucontext.prim = GU_TRIANGLE_STRIP; + break; + case TRI_QUAD_STRIP: + tri_gucontext.prim = GU_TRIANGLE_STRIP; + break; + case TRI_POLYGON: + default: + tri_gucontext.prim = GU_TRIANGLE_FAN; + break; + } + tri_gucontext.start = extGuBeginPacket( NULL ); + tri_gucontext.list = ( gu_vert_ftcv_t* )tri_gucontext.start; + tri_gucontext.begin = true; + tri_gucontext.count = 0; + tri_gucontext.flags = 0; +#else + switch( mode ) + { + case TRI_POINTS: + mode = GL_POINTS; + break; + case TRI_TRIANGLES: + mode = GL_TRIANGLES; + break; + case TRI_TRIANGLE_FAN: + mode = GL_TRIANGLE_FAN; + break; + case TRI_QUADS: + mode = GL_QUADS; + break; + case TRI_LINES: + mode = GL_LINES; + break; + case TRI_TRIANGLE_STRIP: + mode = GL_TRIANGLE_STRIP; + break; + case TRI_QUAD_STRIP: + mode = GL_QUAD_STRIP; + break; + case TRI_POLYGON: + default: + mode = GL_POLYGON; + break; + } + + pglBegin( mode ); +#endif +} + +/* +============= +TriEnd + +draw triangle sequence +============= +*/ +void TriEnd( void ) +{ +#if 1 + unsigned int vert_size; + + if( !tri_gucontext.begin ) + return; + + extGuEndPacket( ( void* )tri_gucontext.list ); + sceGuDrawArray( tri_gucontext.prim, tri_gucontext.flags, tri_gucontext.count, 0, tri_gucontext.start ); + + tri_gucontext.begin = false; +#else + pglEnd( ); +#endif +} + +/* +============= +_TriColor4f + +============= +*/ +void _TriColor4f( float r, float g, float b, float a ) +{ +#if 1 + if( tri_gucontext.begin ) + { + tri_gucontext.flags |= GU_COLOR_8888; + tri_gucontext.list->c = GUCOLOR4F( r, g, b, a ); + } + else + { + sceGuColor( GUCOLOR4F( r, g, b, a ) ); + } +#else + pglColor4f( r, g, b, a ); +#endif +} + +/* +============= +_TriColor4ub + +============= +*/ +void _TriColor4ub( byte r, byte g, byte b, byte a ) +{ +#if 1 + if( tri_gucontext.begin ) + { + tri_gucontext.flags |= GU_COLOR_8888; + tri_gucontext.list->c = GUCOLOR4UB( r, g, b, a ); + } + else + { + sceGuColor( GUCOLOR4UB( r, g, b, a ) ); + } +#else + pglColor4ub( r, g, b, a ); +#endif +} + + +/* +============= +TriColor4ub + +============= +*/ +void TriColor4ub( byte r, byte g, byte b, byte a ) +{ + ds.triRGBA[0] = r * (1.0f / 255.0f); + ds.triRGBA[1] = g * (1.0f / 255.0f); + ds.triRGBA[2] = b * (1.0f / 255.0f); + ds.triRGBA[3] = a * (1.0f / 255.0f); + + _TriColor4f( ds.triRGBA[0], ds.triRGBA[1], ds.triRGBA[2], 1.0f ); +} + +/* +================= +TriColor4f +================= +*/ +void TriColor4f( float r, float g, float b, float a ) +{ + if( ds.renderMode == kRenderTransAlpha ) + TriColor4ub( r * 255.0f, g * 255.0f, b * 255.0f, a * 255.0f ); + else _TriColor4f( r * a, g * a, b * a, 1.0 ); + + ds.triRGBA[0] = r; + ds.triRGBA[1] = g; + ds.triRGBA[2] = b; + ds.triRGBA[3] = a; +} + +/* +============= +TriTexCoord2f + +============= +*/ +void TriTexCoord2f( float u, float v ) +{ +#if 1 + if( !tri_gucontext.begin ) + return; + + tri_gucontext.flags |= GU_TEXTURE_32BITF; + tri_gucontext.list->u = u; + tri_gucontext.list->v = v; +#else + pglTexCoord2f( u, v ); +#endif +} + +/* +============= +TriVertex3fv + +============= +*/ +void TriVertex3fv( const float *v ) +{ +#if 1 + if( !tri_gucontext.begin ) + return; + + if( !(tri_gucontext.flags & GU_TEXTURE_32BITF) ) + { + tri_gucontext.list->u = 0; + tri_gucontext.list->v = 0; + } + + if( !(tri_gucontext.flags & GU_COLOR_8888) ) + { + tri_gucontext.list->c = 0xffffffff; + } + + tri_gucontext.flags |= GU_VERTEX_32BITF; + tri_gucontext.list->x = v[0]; + tri_gucontext.list->y = v[1]; + tri_gucontext.list->z = v[2]; + tri_gucontext.list++; + + tri_gucontext.count++; +#else + pglVertex3fv( v ); +#endif +} + +/* +============= +TriVertex3f + +============= +*/ +void TriVertex3f( float x, float y, float z ) +{ +#if 1 + if( !tri_gucontext.begin ) + return; + + if( !(tri_gucontext.flags & GU_TEXTURE_32BITF) ) + { + tri_gucontext.list->u = 0; + tri_gucontext.list->v = 0; + } + + if( !(tri_gucontext.flags & GU_COLOR_8888) ) + { + tri_gucontext.list->c = 0xffffffff; + } + + tri_gucontext.flags |= GU_VERTEX_32BITF; + tri_gucontext.list->x = x; + tri_gucontext.list->y = y; + tri_gucontext.list->z = z; + tri_gucontext.list++; + + tri_gucontext.count++; +#else + pglVertex3f( x, y, z ); +#endif +} + +/* +============= +TriWorldToScreen + +convert world coordinates (x,y,z) into screen (x, y) +============= +*/ +int TriWorldToScreen( const float *world, float *screen ) +{ + int retval; + + retval = R_WorldToScreen( world, screen ); + + screen[0] = 0.5f * screen[0] * (float)RI.viewport[2]; + screen[1] = -0.5f * screen[1] * (float)RI.viewport[3]; + screen[0] += 0.5f * (float)RI.viewport[2]; + screen[1] += 0.5f * (float)RI.viewport[3]; + + return retval; +} + +/* +============= +TriSpriteTexture + +bind current texture +============= +*/ +int TriSpriteTexture( model_t *pSpriteModel, int frame ) +{ + int gl_texturenum; + + if(( gl_texturenum = R_GetSpriteTexture( pSpriteModel, frame )) == 0 ) + return 0; + + if( gl_texturenum <= 0 || gl_texturenum > MAX_TEXTURES ) + gl_texturenum = tr.defaultTexture; + + GL_Bind( XASH_TEXTURE0, gl_texturenum ); + + return 1; +} + +/* +============= +TriFog + +enables global fog on the level +============= +*/ +void TriFog( float flFogColor[3], float flStart, float flEnd, int bOn ) +{ + // overrided by internal fog + if( RI.fogEnabled ) return; + RI.fogCustom = bOn; + + // check for invalid parms + if( flEnd <= flStart ) + { + glState.isFogEnabled = RI.fogCustom = false; +#if 1 + sceGuDisable( GU_FOG ); +#else + pglDisable( GL_FOG ); +#endif + return; + } +#if 1 + if( RI.fogCustom ) + sceGuEnable( GU_FOG ); + else sceGuDisable( GU_FOG ); +#else + if( RI.fogCustom ) + pglEnable( GL_FOG ); + else pglDisable( GL_FOG ); +#endif + // copy fog params + RI.fogColor[0] = flFogColor[0] / 255.0f; + RI.fogColor[1] = flFogColor[1] / 255.0f; + RI.fogColor[2] = flFogColor[2] / 255.0f; + RI.fogStart = flStart; + RI.fogColor[3] = 1.0f; + RI.fogDensity = 0.0f; + RI.fogSkybox = true; + RI.fogEnd = flEnd; + +#if 1 + printf("TRI FOG: s %f e %f c %f %f %f %f \n", RI.fogStart, RI.fogEnd, + RI.fogColor[0], RI.fogColor[1], RI.fogColor[2], RI.fogColor[3] ); + + //glState.fogDensity = RI.fogDensity + glState.fogColor = GUCOLOR4F( RI.fogColor[0], RI.fogColor[1], RI.fogColor[2], glState.fogDensity ); + glState.fogStart = RI.fogStart; + glState.fogEnd = RI.fogEnd; + + sceGuFog( glState.fogStart, glState.fogEnd, glState.fogColor ); +#else + pglFogi( GL_FOG_MODE, GL_LINEAR ); + pglFogfv( GL_FOG_COLOR, RI.fogColor ); + pglFogf( GL_FOG_START, RI.fogStart ); + pglFogf( GL_FOG_END, RI.fogEnd ); + pglHint( GL_FOG_HINT, GL_NICEST ); +#endif +} + +/* +============= +TriGetMatrix + +very strange export +============= +*/ +void TriGetMatrix( const int pname, float *matrix ) +{ +#if 1 + printf("%s:%i:%s - Not implemented\n", __FILE__, __LINE__, __FUNCTION__ ); +#else + pglGetFloatv( pname, matrix ); +#endif +} + +/* +============= +TriForParams + +============= +*/ +void TriFogParams( float flDensity, int iFogSkybox ) +{ + RI.fogDensity = flDensity; + RI.fogSkybox = iFogSkybox; +} + +/* +============= +TriCullFace + +============= +*/ +void TriCullFace( TRICULLSTYLE mode ) +{ + int glMode; + + switch( mode ) + { + case TRI_FRONT: + glMode = GL_FRONT; + break; + default: + glMode = GL_NONE; + break; + } + + GL_Cull( mode ); +} + +/* +============= +TriBrightness +============= +*/ +void TriBrightness( float brightness ) +{ + float r, g, b; + + r = ds.triRGBA[0] * ds.triRGBA[3] * brightness; + g = ds.triRGBA[1] * ds.triRGBA[3] * brightness; + b = ds.triRGBA[2] * ds.triRGBA[3] * brightness; + + _TriColor4f( r, g, b, 1.0f ); +} + +unsigned int getTriBrightness( float brightness ) +{ + vec3_t color; + + if(brightness < 0.0f) return 0; + + color[0] = ds.triRGBA[0] * ds.triRGBA[3] * brightness; + color[1] = ds.triRGBA[1] * ds.triRGBA[3] * brightness; + color[2] = ds.triRGBA[2] * ds.triRGBA[3] * brightness; + + VectorNormalizeFast( color ); + + return GUCOLOR3FV( color ); +} + diff --git a/ref_gu/gu_vgui.c b/ref_gu/gu_vgui.c new file mode 100644 index 00000000..e9f7a354 --- /dev/null +++ b/ref_gu/gu_vgui.c @@ -0,0 +1,309 @@ +/* +gl_vgui.c - OpenGL vgui draw methods +Copyright (C) 2011 Uncle Mike +Copyright (C) 2019 a1batross +Copyright (C) 2020 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "gu_local.h" + +#define VGUI_MAX_TEXTURES ( MAX_TEXTURES / 2 ) // a half of total textures count +#if 1 +static short g_textures[VGUI_MAX_TEXTURES]; +#else +static int g_textures[VGUI_MAX_TEXTURES]; +#endif +static int g_textureId = 0; +static int g_iBoundTexture; + +/* +================ +VGUI_DrawInit + +Startup VGUI backend +================ +*/ +void GAME_EXPORT VGUI_DrawInit( void ) +{ + memset( g_textures, 0, sizeof( g_textures )); + g_textureId = g_iBoundTexture = 0; +} + +/* +================ +VGUI_DrawShutdown + +Release all textures +================ +*/ +void GAME_EXPORT VGUI_DrawShutdown( void ) +{ + int i; + + for( i = 1; i < g_textureId; i++ ) + { + GL_FreeTexture( g_textures[i] ); + } +} + +/* +================ +VGUI_GenerateTexture + +generate unique texture number +================ +*/ +int GAME_EXPORT VGUI_GenerateTexture( void ) +{ + if( ++g_textureId >= VGUI_MAX_TEXTURES ) + gEngfuncs.Host_Error( "VGUI_GenerateTexture: VGUI_MAX_TEXTURES limit exceeded\n" ); + return g_textureId; +} + +/* +================ +VGUI_UploadTexture + +Upload texture into video memory +================ +*/ +void GAME_EXPORT VGUI_UploadTexture( int id, const char *buffer, int width, int height ) +{ + rgbdata_t r_image; + char texName[32]; + + if( id <= 0 || id >= VGUI_MAX_TEXTURES ) + { + gEngfuncs.Con_DPrintf( S_ERROR "VGUI_UploadTexture: bad texture %i. Ignored\n", id ); + return; + } + + Q_snprintf( texName, sizeof( texName ), "*vgui%i", id ); + memset( &r_image, 0, sizeof( r_image )); + + r_image.width = width; + r_image.height = height; + r_image.type = PF_RGBA_32; + r_image.size = r_image.width * r_image.height * 4; + r_image.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA; + r_image.buffer = (byte *)buffer; + + g_textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE ); +} + +/* +================ +VGUI_CreateTexture + +Create empty rgba texture and upload them into video memory +================ +*/ +void GAME_EXPORT VGUI_CreateTexture( int id, int width, int height ) +{ + rgbdata_t r_image; + char texName[32]; + + if( id <= 0 || id >= VGUI_MAX_TEXTURES ) + { + gEngfuncs.Con_Reportf( S_ERROR "VGUI_CreateTexture: bad texture %i. Ignored\n", id ); + return; + } + + Q_snprintf( texName, sizeof( texName ), "*vgui%i", id ); + memset( &r_image, 0, sizeof( r_image )); + + r_image.width = width; + r_image.height = height; + r_image.type = PF_RGBA_32; + r_image.size = r_image.width * r_image.height * 4; + r_image.flags = IMAGE_HAS_ALPHA; + r_image.buffer = NULL; + + g_textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE|TF_NEAREST ); + g_iBoundTexture = id; +} + +void GAME_EXPORT VGUI_UploadTextureBlock( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight ) +{ + if( id <= 0 || id >= VGUI_MAX_TEXTURES || g_textures[id] == 0 || g_textures[id] == tr.whiteTexture ) + { + gEngfuncs.Con_Reportf( S_ERROR "VGUI_UploadTextureBlock: bad texture %i. Ignored\n", id ); + return; + } +#if 1 + +#else + pglTexSubImage2D( GL_TEXTURE_2D, 0, drawX, drawY, blockWidth, blockHeight, GL_RGBA, GL_UNSIGNED_BYTE, rgba ); +#endif + g_iBoundTexture = id; +} + +void GAME_EXPORT VGUI_SetupDrawingRect( int *pColor ) +{ +#if 1 + sceGuEnable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuColor( GUCOLOR4UB( pColor[0], pColor[1], pColor[2], 255 - pColor[3] ) ); +#else + pglEnable( GL_BLEND ); + pglDisable( GL_ALPHA_TEST ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + pglColor4ub( pColor[0], pColor[1], pColor[2], 255 - pColor[3] ); +#endif +} + +void GAME_EXPORT VGUI_SetupDrawingText( int *pColor ) +{ +#if 1 + sceGuEnable( GU_BLEND ); + sceGuEnable( GU_ALPHA_TEST ); + sceGuAlphaFunc( GU_GREATER, 0x00, 0xff ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuColor( GUCOLOR4UB( pColor[0], pColor[1], pColor[2], 255 - pColor[3] ) ); +#else + pglEnable( GL_BLEND ); + pglEnable( GL_ALPHA_TEST ); + pglAlphaFunc( GL_GREATER, 0.0f ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + pglColor4ub( pColor[0], pColor[1], pColor[2], 255 - pColor[3] ); +#endif +} + +void GAME_EXPORT VGUI_SetupDrawingImage( int *pColor ) +{ +#if 1 + sceGuEnable( GU_BLEND ); + sceGuEnable( GU_ALPHA_TEST ); + sceGuAlphaFunc( GU_GREATER, 0x00, 0xff ); + sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + sceGuTexFunc( GU_TFX_MODULATE, GU_TCC_RGBA ); + sceGuColor( GUCOLOR4UB( pColor[0], pColor[1], pColor[2], 255 - pColor[3] ) ); +#else + pglEnable( GL_BLEND ); + pglEnable( GL_ALPHA_TEST ); + pglAlphaFunc( GL_GREATER, 0.0f ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + pglColor4ub( pColor[0], pColor[1], pColor[2], 255 - pColor[3] ); +#endif +} + +void GAME_EXPORT VGUI_BindTexture( int id ) +{ + if( id > 0 && id < VGUI_MAX_TEXTURES && g_textures[id] ) + { + GL_Bind( XASH_TEXTURE0, g_textures[id] ); + g_iBoundTexture = id; + } + else + { + // NOTE: same as bogus index 2700 in GoldSrc + id = g_iBoundTexture = 1; + GL_Bind( XASH_TEXTURE0, g_textures[id] ); + } +} + +/* +================ +VGUI_GetTextureSizes + +returns wide and tall for currently binded texture +================ +*/ +void GAME_EXPORT VGUI_GetTextureSizes( int *width, int *height ) +{ + gl_texture_t *glt; + int texnum; + + if( g_iBoundTexture ) + texnum = g_textures[g_iBoundTexture]; + else texnum = tr.defaultTexture; + + glt = R_GetTexture( texnum ); + if( width ) *width = glt->srcWidth; + if( height ) *height = glt->srcHeight; +} + +/* +================ +VGUI_EnableTexture + +disable texturemode for fill rectangle +================ +*/ +void GAME_EXPORT VGUI_EnableTexture( qboolean enable ) +{ +#if 1 + if( enable ) sceGuEnable( GU_TEXTURE_2D ); + else sceGuDisable( GU_TEXTURE_2D ); +#else + if( enable ) pglEnable( GL_TEXTURE_2D ); + else pglDisable( GL_TEXTURE_2D ); +#endif +} + +/* +================ +VGUI_DrawQuad + +generic method to fill rectangle +================ +*/ +void GAME_EXPORT VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr ) +{ + int width, height; + float xscale, yscale; +#if 1 + gl_texture_t *glt; + float twidth, theight; +#endif + gEngfuncs.CL_GetScreenInfo( &width, &height ); + + xscale = gpGlobals->width / (float)width; + yscale = gpGlobals->height / (float)height; + + ASSERT( ul != NULL && lr != NULL ); +#if 1 + glt = R_GetTexture( g_iBoundTexture ? g_textures[g_iBoundTexture] : tr.defaultTexture ); + + gu_vert_htv_t* const out = ( gu_vert_htv_t* )sceGuGetMemory( sizeof( gu_vert_htv_t ) * 2 ); + out[0].u = ul->coord[0] * glt->width; + out[0].v = ul->coord[1] * glt->height; + out[0].x = ul->point[0] * xscale; + out[0].y = ul->point[1] * yscale; + out[0].z = 0; + out[1].u = lr->coord[0] * glt->width; + out[1].v = lr->coord[1] * glt->height; + out[1].x = lr->point[0] * xscale; + out[1].y = lr->point[1] * yscale; + out[1].z = 0; + sceGuDrawArray( GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, out ); +#else + pglBegin( GL_QUADS ); + pglTexCoord2f( ul->coord[0], ul->coord[1] ); + pglVertex2f( ul->point[0] * xscale, ul->point[1] * yscale ); + + pglTexCoord2f( lr->coord[0], ul->coord[1] ); + pglVertex2f( lr->point[0] * xscale, ul->point[1] * yscale ); + + pglTexCoord2f( lr->coord[0], lr->coord[1] ); + pglVertex2f( lr->point[0] * xscale, lr->point[1] * yscale ); + + pglTexCoord2f( ul->coord[0], lr->coord[1] ); + pglVertex2f( ul->point[0] * xscale, lr->point[1] * yscale ); + pglEnd(); +#endif +} diff --git a/ref_gu/gu_warp.c b/ref_gu/gu_warp.c new file mode 100644 index 00000000..ad60361e --- /dev/null +++ b/ref_gu/gu_warp.c @@ -0,0 +1,871 @@ +/* +gl_warp.c - sky and water polygons +Copyright (C) 2010 Uncle Mike +Copyright (C) 2020 Sergey Galushko + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + + +#include "gu_local.h" +#include "wadfile.h" +#define SKYCLOUDS_QUALITY 12 +#define MAX_CLIP_VERTS 128 // skybox clip vertices +#define TURBSCALE ( 256.0f / ( M_PI2 )) +const char* r_skyBoxSuffix[6] = { "rt", "bk", "lf", "ft", "up", "dn" }; +static const int r_skyTexOrder[6] = { 0, 2, 1, 3, 4, 5 }; + +static const vec3_t skyclip[6] = +{ +{ 1, 1, 0 }, +{ 1, -1, 0 }, +{ 0, -1, 1 }, +{ 0, 1, 1 }, +{ 1, 0, 1 }, +{ -1, 0, 1 } +}; + +// 1 = s, 2 = t, 3 = 2048 +static const int st_to_vec[6][3] = +{ +{ 3, -1, 2 }, +{ -3, 1, 2 }, +{ 1, 3, 2 }, +{ -1, -3, 2 }, +{ -2, -1, 3 }, // 0 degrees yaw, look straight up +{ 2, -1, -3 } // look straight down +}; + +// s = [0]/[2], t = [1]/[2] +static const int vec_to_st[6][3] = +{ +{ -2, 3, 1 }, +{ 2, 3, -1 }, +{ 1, 3, 2 }, +{ -1, 3, -2 }, +{ -2, -1, 3 }, +{ -2, 1, -3 } +}; + +// speed up sin calculations +float r_turbsin[] = +{ + #include "warpsin.h" +}; + +#define SKYBOX_MISSED 0 +#define SKYBOX_HLSTYLE 1 +#define SKYBOX_Q1STYLE 2 + +static int CheckSkybox( const char *name ) +{ + const char *skybox_ext[3] = { "dds", "tga", "bmp" }; + int i, j, num_checked_sides; + const char *sidename; + + // search for skybox images + for( i = 0; i < 3; i++ ) + { + num_checked_sides = 0; + for( j = 0; j < 6; j++ ) + { + // build side name + sidename = va( "%s%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); + if( gEngfuncs.FS_FileExists( sidename, false )) + num_checked_sides++; + + } + + if( num_checked_sides == 6 ) + return SKYBOX_HLSTYLE; // image exists + + for( j = 0; j < 6; j++ ) + { + // build side name + sidename = va( "%s_%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); + if( gEngfuncs.FS_FileExists( sidename, false )) + num_checked_sides++; + } + + if( num_checked_sides == 6 ) + return SKYBOX_Q1STYLE; // images exists + } + + return SKYBOX_MISSED; +} + +void DrawSkyPolygon( int nump, vec3_t vecs ) +{ + int i, j, axis; + float s, t, dv, *vp; + vec3_t v, av; + + // decide which face it maps to + VectorClear( v ); + + for( i = 0, vp = vecs; i < nump; i++, vp += 3 ) + VectorAdd( vp, v, v ); + + av[0] = fabs( v[0] ); + av[1] = fabs( v[1] ); + av[2] = fabs( v[2] ); + + if( av[0] > av[1] && av[0] > av[2] ) + axis = (v[0] < 0) ? 1 : 0; + else if( av[1] > av[2] && av[1] > av[0] ) + axis = (v[1] < 0) ? 3 : 2; + else axis = (v[2] < 0) ? 5 : 4; + + // project new texture coords + for( i = 0; i < nump; i++, vecs += 3 ) + { + j = vec_to_st[axis][2]; + dv = (j > 0) ? vecs[j-1] : -vecs[-j-1]; + + if( dv == 0.0f ) continue; + + j = vec_to_st[axis][0]; + s = (j < 0) ? -vecs[-j-1] / dv : vecs[j-1] / dv; + + j = vec_to_st[axis][1]; + t = (j < 0) ? -vecs[-j-1] / dv : vecs[j-1] / dv; + + if( s < RI.skyMins[0][axis] ) RI.skyMins[0][axis] = s; + if( t < RI.skyMins[1][axis] ) RI.skyMins[1][axis] = t; + if( s > RI.skyMaxs[0][axis] ) RI.skyMaxs[0][axis] = s; + if( t > RI.skyMaxs[1][axis] ) RI.skyMaxs[1][axis] = t; + } +} + +/* +============== +ClipSkyPolygon +============== +*/ +void ClipSkyPolygon( int nump, vec3_t vecs, int stage ) +{ + const float *norm; + float *v, d, e; + qboolean front, back; + float dists[MAX_CLIP_VERTS + 1]; + int sides[MAX_CLIP_VERTS + 1]; + vec3_t newv[2][MAX_CLIP_VERTS + 1]; + int newc[2]; + int i, j; + + if( nump > MAX_CLIP_VERTS ) + gEngfuncs.Host_Error( "ClipSkyPolygon: MAX_CLIP_VERTS\n" ); +loc1: + if( stage == 6 ) + { + // fully clipped, so draw it + DrawSkyPolygon( nump, vecs ); + return; + } + + front = back = false; + norm = skyclip[stage]; + for( i = 0, v = vecs; i < nump; i++, v += 3 ) + { + d = DotProduct( v, norm ); + if( d > ON_EPSILON ) + { + front = true; + sides[i] = SIDE_FRONT; + } + else if( d < -ON_EPSILON ) + { + back = true; + sides[i] = SIDE_BACK; + } + else + { + sides[i] = SIDE_ON; + } + dists[i] = d; + } + + if( !front || !back ) + { + // not clipped + stage++; + goto loc1; + } + + // clip it + sides[i] = sides[0]; + dists[i] = dists[0]; + VectorCopy( vecs, ( vecs + ( i * 3 ))); + newc[0] = newc[1] = 0; + + for( i = 0, v = vecs; i < nump; i++, v += 3 ) + { + switch( sides[i] ) + { + case SIDE_FRONT: + VectorCopy( v, newv[0][newc[0]] ); + newc[0]++; + break; + case SIDE_BACK: + VectorCopy( v, newv[1][newc[1]] ); + newc[1]++; + break; + case SIDE_ON: + VectorCopy( v, newv[0][newc[0]] ); + newc[0]++; + VectorCopy( v, newv[1][newc[1]] ); + newc[1]++; + break; + } + + if( sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) + continue; + + d = dists[i] / ( dists[i] - dists[i+1] ); + for( j = 0; j < 3; j++ ) + { + e = v[j] + d * ( v[j+3] - v[j] ); + newv[0][newc[0]][j] = e; + newv[1][newc[1]][j] = e; + } + newc[0]++; + newc[1]++; + } + + // continue + ClipSkyPolygon( newc[0], newv[0][0], stage + 1 ); + ClipSkyPolygon( newc[1], newv[1][0], stage + 1 ); +} +#if 1 +_inline void MakeSkyVec( float s, float t, int axis, gu_vert_t *out ) +{ + int j, k, farclip; + vec3_t v, b; + + farclip = RI.farClip; + + b[0] = s * (farclip >> 1); + b[1] = t * (farclip >> 1); + b[2] = (farclip >> 1); + + for( j = 0; j < 3; j++ ) + { + k = st_to_vec[axis][j]; + v[j] = (k < 0) ? -b[-k-1] : b[k-1]; + v[j] += RI.cullorigin[j]; + } + + // avoid bilerp seam + s = (s + 1.0f) * 0.5f; + t = (t + 1.0f) * 0.5f; + + if( s < 1.0f / 512.0f ) + s = 1.0f / 512.0f; + else if( s > 511.0f / 512.0f ) + s = 511.0f / 512.0f; + if( t < 1.0f / 512.0f ) + t = 1.0f / 512.0f; + else if( t > 511.0f / 512.0f ) + t = 511.0f / 512.0f; + + t = 1.0f - t; + + out->uv[0] = s; + out->uv[1] = t; + VectorCopy( v, out->xyz ); +} +#else +void MakeSkyVec( float s, float t, int axis ) +{ + int j, k, farclip; + vec3_t v, b; + + farclip = RI.farClip; + + b[0] = s * (farclip >> 1); + b[1] = t * (farclip >> 1); + b[2] = (farclip >> 1); + + for( j = 0; j < 3; j++ ) + { + k = st_to_vec[axis][j]; + v[j] = (k < 0) ? -b[-k-1] : b[k-1]; + v[j] += RI.cullorigin[j]; + } + + // avoid bilerp seam + s = (s + 1.0f) * 0.5f; + t = (t + 1.0f) * 0.5f; + + if( s < 1.0f / 512.0f ) + s = 1.0f / 512.0f; + else if( s > 511.0f / 512.0f ) + s = 511.0f / 512.0f; + if( t < 1.0f / 512.0f ) + t = 1.0f / 512.0f; + else if( t > 511.0f / 512.0f ) + t = 511.0f / 512.0f; + + t = 1.0f - t; + + pglTexCoord2f( s, t ); + pglVertex3fv( v ); +} +#endif +/* +============== +R_ClearSkyBox +============== +*/ +void R_ClearSkyBox( void ) +{ + int i; + + for( i = 0; i < 6; i++ ) + { + RI.skyMins[0][i] = RI.skyMins[1][i] = 9999999.0f; + RI.skyMaxs[0][i] = RI.skyMaxs[1][i] = -9999999.0f; + } +} + +/* +================= +R_AddSkyBoxSurface +================= +*/ +void R_AddSkyBoxSurface( msurface_t *fa ) +{ + vec3_t verts[MAX_CLIP_VERTS]; + glpoly_t *p; + float *v; + int i; + + if( ENGINE_GET_PARM( PARM_SKY_SPHERE ) && fa->polys && !tr.fCustomSkybox ) + { + glpoly_t *p = fa->polys; + + // draw the sky poly + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, p->numverts, 0, p->verts ); + } + + // calculate vertex values for sky box + for( p = fa->polys; p; p = p->next ) + { + for( i = 0; i < p->numverts; i++ ) + VectorSubtract( p->verts[i].xyz, RI.cullorigin, verts[i] ); + ClipSkyPolygon( p->numverts, verts[0], 0 ); + } +} + +/* +============== +R_UnloadSkybox + +Unload previous skybox +============== +*/ +void R_UnloadSkybox( void ) +{ + int i; + + // release old skybox + for( i = 0; i < 6; i++ ) + { + if( !tr.skyboxTextures[i] ) continue; + GL_FreeTexture( tr.skyboxTextures[i] ); + } +#if 0 + tr.skyboxbasenum = 5800; // set skybox base (to let some mods load hi-res skyboxes) +#endif + memset( tr.skyboxTextures, 0, sizeof( tr.skyboxTextures )); + tr.fCustomSkybox = false; +} + +/* +============== +R_DrawSkybox +============== +*/ +void R_DrawSkyBox( void ) +{ + int i; + RI.isSkyVisible = true; + + // don't fogging skybox (this fix old Half-Life bug) + if( !RI.fogSkybox ) R_AllowFog( false ); + +/* + if( RI.fogEnabled ) + pglFogf( GL_FOG_DENSITY, RI.fogDensity * 0.5f ); +*/ + sceGuDisable( GU_BLEND ); + sceGuDisable( GU_ALPHA_TEST ); + sceGuTexFunc( GU_TFX_REPLACE, GU_TCC_RGBA ); + + for( i = 0; i < 6; i++ ) + { + if( RI.skyMins[0][i] >= RI.skyMaxs[0][i] || RI.skyMins[1][i] >= RI.skyMaxs[1][i] ) + continue; + + if( tr.skyboxTextures[r_skyTexOrder[i]] ) + GL_Bind( XASH_TEXTURE0, tr.skyboxTextures[r_skyTexOrder[i]] ); + else GL_Bind( XASH_TEXTURE0, tr.grayTexture ); // stub + + //gu_vert_t uv[4] __attribute__( ( aligned( 16 ) ) ); // dcache writeback required! + gu_vert_t* const uv = ( gu_vert_t* )sceGuGetMemory( sizeof( gu_vert_t ) * 4 ); + MakeSkyVec( RI.skyMins[0][i], RI.skyMins[1][i], i, &uv[0] ); + MakeSkyVec( RI.skyMins[0][i], RI.skyMaxs[1][i], i, &uv[1] ); + MakeSkyVec( RI.skyMaxs[0][i], RI.skyMaxs[1][i], i, &uv[2] ); + MakeSkyVec( RI.skyMaxs[0][i], RI.skyMins[1][i], i, &uv[3] ); + + if ( GU_ClipIsRequired( uv, 4 ) ) + { + gu_vert_t* cv; + size_t cvc; + + GU_Clip( uv, 4, &cv, &cvc ); + if( cvc ) sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, cvc, 0, cv ); + } + else sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, uv ); + } + + if( !RI.fogSkybox ) + R_AllowFog( true ); +/* + if( RI.fogEnabled ) + pglFogf( GL_FOG_DENSITY, RI.fogDensity ); +*/ + R_LoadIdentity(); +} + +/* +=============== +R_SetupSky +=============== +*/ +void R_SetupSky( const char *skyboxname ) +{ + char loadname[MAX_STRING]; + char sidename[MAX_STRING]; + int i, result; + + if( !COM_CheckString( skyboxname )) + { + R_UnloadSkybox(); + return; // clear old skybox + } + + Q_snprintf( loadname, sizeof( loadname ), "gfx/env/%s", skyboxname ); + COM_StripExtension( loadname ); + + // kill the underline suffix to find them manually later + if( loadname[Q_strlen( loadname ) - 1] == '_' ) + loadname[Q_strlen( loadname ) - 1] = '\0'; + result = CheckSkybox( loadname ); + + // to prevent infinite recursion if default skybox was missed + if( result == SKYBOX_MISSED && Q_stricmp( loadname, DEFAULT_SKYBOX_PATH )) + { + gEngfuncs.Con_Reportf( S_WARN "missed or incomplete skybox '%s'\n", skyboxname ); + R_SetupSky( "desert" ); // force to default + return; + } + + // release old skybox + R_UnloadSkybox(); + gEngfuncs.Con_DPrintf( "SKY: " ); + + for( i = 0; i < 6; i++ ) + { + if( result == SKYBOX_HLSTYLE ) + Q_snprintf( sidename, sizeof( sidename ), "%s%s", loadname, r_skyBoxSuffix[i] ); + else Q_snprintf( sidename, sizeof( sidename ), "%s_%s", loadname, r_skyBoxSuffix[i] ); + + tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY ); + if( !tr.skyboxTextures[i] ) break; + gEngfuncs.Con_DPrintf( "%s%s%s", skyboxname, r_skyBoxSuffix[i], i != 5 ? ", " : ". " ); + } + + if( i == 6 ) + { + tr.fCustomSkybox = true; + gEngfuncs.Con_DPrintf( "done\n" ); + return; // loaded + } + + gEngfuncs.Con_DPrintf( "^2failed\n" ); + R_UnloadSkybox(); +} + +//============================================================================== +// +// RENDER CLOUDS +// +//============================================================================== +/* +============== +R_CloudVertex +============== +*/ +void R_CloudVertex( float s, float t, int axis, vec3_t v ) +{ + int j, k, farclip; + vec3_t b; + + farclip = RI.farClip; + + b[0] = s * (farclip >> 1); + b[1] = t * (farclip >> 1); + b[2] = (farclip >> 1); + + for( j = 0; j < 3; j++ ) + { + k = st_to_vec[axis][j]; + v[j] = (k < 0) ? -b[-k-1] : b[k-1]; + v[j] += RI.cullorigin[j]; + } +} + +/* +============= +R_CloudTexCoord +============= +*/ +#if 1 +_inline void R_CloudTexCoord( vec3_t v, float speed, float *s, float *t ) +#else +void R_CloudTexCoord( vec3_t v, float speed, float *s, float *t ) +#endif +{ + float length, speedscale; + vec3_t dir; + + speedscale = gpGlobals->time * speed; + speedscale -= (int)speedscale & ~127; + + VectorSubtract( v, RI.vieworg, dir ); + dir[2] *= 3.0f; // flatten the sphere + + length = VectorLength( dir ); + length = 6.0f * 63.0f / length; + + *s = ( speedscale + dir[0] * length ) * (1.0f / 128.0f); + *t = ( speedscale + dir[1] * length ) * (1.0f / 128.0f); +} + +/* +=============== +R_CloudDrawPoly +=============== +*/ +void R_CloudDrawPoly( glpoly_t *p ) +{ + float *v; + int i; + + GL_SetRenderMode( kRenderNormal ); + GL_Bind( XASH_TEXTURE0, tr.solidskyTexture ); + + gu_vert_t* out = ( gu_vert_t* )sceGuGetMemory( sizeof( gu_vert_t ) * 4 ); + for( i = 0; i < 4; i++ ) + { + R_CloudTexCoord( p->verts[i].xyz, 8.0f, &out[i].uv[0], &out[i].uv[1] ); + VectorCopy( p->verts[i].xyz, out[i].xyz ); + } + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, out ); + + GL_SetRenderMode( kRenderTransTexture ); + GL_Bind( XASH_TEXTURE0, tr.alphaskyTexture ); + + out = ( gu_vert_t* )sceGuGetMemory( sizeof( gu_vert_t ) * 4 ); + for( i = 0; i < 4; i++ ) + { + R_CloudTexCoord( p->verts[i].xyz, 16.0f, &out[i].uv[0], &out[i].uv[1] ); + VectorCopy( p->verts[i].xyz, out[i].xyz ); + } + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, out ); + + sceGuDisable( GU_BLEND ); +} + +/* +============== +R_CloudRenderSide +============== +*/ +void R_CloudRenderSide( int axis ) +{ + vec3_t verts[4]; + float di, qi, dj, qj; + vec3_t vup, vright; + vec3_t temp, temp2; + glpoly_t p[1]; + int i, j; + + R_CloudVertex( -1.0f, -1.0f, axis, verts[0] ); + R_CloudVertex( -1.0f, 1.0f, axis, verts[1] ); + R_CloudVertex( 1.0f, 1.0f, axis, verts[2] ); + R_CloudVertex( 1.0f, -1.0f, axis, verts[3] ); + + VectorSubtract( verts[2], verts[3], vup ); + VectorSubtract( verts[2], verts[1], vright ); + + p->numverts = 4; + di = SKYCLOUDS_QUALITY; + qi = 1.0f / di; + dj = (axis < 4) ? di * 2 : di; //subdivide vertically more than horizontally on skybox sides + qj = 1.0f / dj; + + for( i = 0; i < di; i++ ) + { + for( j = 0; j < dj; j++ ) + { + if( i * qi < RI.skyMins[0][axis] / 2 + 0.5f - qi + || i * qi > RI.skyMaxs[0][axis] / 2 + 0.5f + || j * qj < RI.skyMins[1][axis] / 2 + 0.5f - qj + || j * qj > RI.skyMaxs[1][axis] / 2 + 0.5f ) + continue; + + VectorScale( vright, qi * i, temp ); + VectorScale( vup, qj * j, temp2 ); + VectorAdd( temp, temp2, temp ); + VectorAdd( verts[0], temp, p->verts[0].xyz ); + + VectorScale( vup, qj, temp ); + VectorAdd( p->verts[0].xyz, temp, p->verts[1].xyz ); + + VectorScale( vright, qi, temp ); + VectorAdd( p->verts[1].xyz, temp, p->verts[2].xyz ); + + VectorAdd( p->verts[0].xyz, temp, p->verts[3].xyz ); + + R_CloudDrawPoly( p ); + } + } +} + +/* +============== +R_DrawClouds + +Quake-style clouds +============== +*/ +void R_DrawClouds( void ) +{ + int i; + + RI.isSkyVisible = true; + +/* + if( RI.fogEnabled ) + pglFogf( GL_FOG_DENSITY, RI.fogDensity * 0.25f ); +*/ + sceGuDepthFunc( GU_GEQUAL ); + sceGuDepthMask( GU_TRUE ); + + for( i = 0; i < 6; i++ ) + { + if( RI.skyMins[0][i] >= RI.skyMaxs[0][i] || RI.skyMins[1][i] >= RI.skyMaxs[1][i] ) + continue; + R_CloudRenderSide( i ); + } + + sceGuDepthFunc( GU_LEQUAL ); + sceGuDepthMask( GU_FALSE ); +/* + if( RI.fogEnabled ) + pglFogf( GL_FOG_DENSITY, RI.fogDensity ); +*/ +} + +/* +============= +R_InitSkyClouds + +A sky texture is 256*128, with the right side being a masked overlay +============== +*/ +void R_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette ) +{ + rgbdata_t r_temp, *r_sky; + uint *trans, *rgba; + uint transpix; + int r, g, b; + int i, j, p; + char texname[32]; + + if( !glw_state.initialized ) + return; + + Q_snprintf( texname, sizeof( texname ), "%s%s.mip", ( mt->offsets[0] > 0 ) ? "#" : "", tx->name ); + + if( mt->offsets[0] > 0 ) + { + int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); + + if( custom_palette ) size += sizeof( short ) + 768; + r_sky = gEngfuncs.FS_LoadImage( texname, (byte *)mt, size ); + } + else + { + // okay, loading it from wad + r_sky = gEngfuncs.FS_LoadImage( texname, NULL, 0 ); + } + + // make sure what sky image is valid + if( !r_sky || !r_sky->palette || r_sky->type != PF_INDEXED_32 || r_sky->height == 0 ) + { + gEngfuncs.Con_Reportf( S_ERROR "R_InitSky: unable to load sky texture %s\n", tx->name ); + if( r_sky ) gEngfuncs.FS_FreeImage( r_sky ); + return; + } + + // make an average value for the back to avoid + // a fringe on the top level + trans = Mem_Malloc( r_temppool, r_sky->height * r_sky->height * sizeof( *trans )); + r = g = b = 0; + + for( i = 0; i < r_sky->width >> 1; i++ ) + { + for( j = 0; j < r_sky->height; j++ ) + { + p = r_sky->buffer[i * r_sky->width + j + r_sky->height]; + rgba = (uint *)r_sky->palette + p; + trans[(i * r_sky->height) + j] = *rgba; + r += ((byte *)rgba)[0]; + g += ((byte *)rgba)[1]; + b += ((byte *)rgba)[2]; + } + } + + ((byte *)&transpix)[0] = r / ( r_sky->height * r_sky->height ); + ((byte *)&transpix)[1] = g / ( r_sky->height * r_sky->height ); + ((byte *)&transpix)[2] = b / ( r_sky->height * r_sky->height ); + ((byte *)&transpix)[3] = 0; + + // build a temporary image + r_temp = *r_sky; + r_temp.width = r_sky->width >> 1; + r_temp.height = r_sky->height; + r_temp.type = PF_RGBA_32; + r_temp.flags = IMAGE_HAS_COLOR; + r_temp.size = r_temp.width * r_temp.height * 4; + r_temp.buffer = (byte *)trans; + r_temp.palette = NULL; + + // load it in + tr.solidskyTexture = GL_LoadTextureInternal( REF_SOLIDSKY_TEXTURE, &r_temp, TF_NOMIPMAP ); + + for( i = 0; i < r_sky->width >> 1; i++ ) + { + for( j = 0; j < r_sky->height; j++ ) + { + p = r_sky->buffer[i * r_sky->width + j]; + + if( p == 0 ) + { + trans[(i * r_sky->height) + j] = transpix; + } + else + { + rgba = (uint *)r_sky->palette + p; + trans[(i * r_sky->height) + j] = *rgba; + } + } + } + + r_temp.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA; + + // load it in + tr.alphaskyTexture = GL_LoadTextureInternal( REF_ALPHASKY_TEXTURE, &r_temp, TF_NOMIPMAP ); + + // clean up + gEngfuncs.FS_FreeImage( r_sky ); + Mem_Free( trans ); +} + +/* +============= +EmitWaterPolys + +Does a water warp on the pre-fragmented glpoly_t chain +============= +*/ +void EmitWaterPolys( msurface_t *warp, qboolean reverse ) +{ + gu_vert_t *verts; + float *v, nv, waveHeight; + float s, t, os, ot; + glpoly_t *p; + int i; + + if( !warp->polys ) return; + + // set the current waveheight + if( warp->polys->verts[0].xyz[2] >= RI.vieworg[2] ) + waveHeight = -RI.currententity->curstate.scale; + else waveHeight = RI.currententity->curstate.scale; + + // reset fog color for nonlightmapped water + GL_ResetFogColor(); + + for( p = warp->polys; p; p = p->next ) + { + if( reverse ) + verts = &p->verts[p->numverts - 1]; + else verts = &p->verts[0]; + + gu_vert_t* const out = ( gu_vert_t* )sceGuGetMemory( sizeof( gu_vert_t ) * p->numverts ); + for( i = 0; i < p->numverts; i++ ) + { + if( waveHeight ) + { + nv = r_turbsin[( int )( gpGlobals->time * 160.0f + verts->xyz[1] + verts->xyz[0]) & 255] + 8.0f; + nv = (r_turbsin[( int )( verts->xyz[0] * 5.0f + gpGlobals->time * 171.0f - verts->xyz[1]) & 255] + 8.0f ) * 0.8f + nv; + nv = nv * waveHeight + verts->xyz[2]; + } + else nv = verts->xyz[2]; + + os = verts->uv[0]; + ot = verts->uv[1]; + + s = os + r_turbsin[(int)((ot * 0.125f + gpGlobals->time) * TURBSCALE) & 255]; + s *= ( 1.0f / SUBDIVIDE_SIZE ); + + t = ot + r_turbsin[(int)((os * 0.125f + gpGlobals->time) * TURBSCALE) & 255]; + t *= ( 1.0f / SUBDIVIDE_SIZE ); + + out[i].uv[0] = s; + out[i].uv[1] = t; + out[i].xyz[0] = verts->xyz[0]; + out[i].xyz[1] = verts->xyz[1]; + out[i].xyz[2] = nv; + + if( reverse ) + verts -= 1; + else verts += 1; + } + if ( GU_ClipIsRequired( out, p->numverts ) ) + { + // Clip the polygon. + gu_vert_t* cv; + size_t cvc; + + GU_Clip( out, p->numverts, &cv, &cvc ); + if( cvc ) + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, cvc, 0, cv ); + } + else + sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, p->numverts, 0, out ); + } + GL_SetupFogColorForSurfaces(); +} diff --git a/ref_gu/vram_psp.h b/ref_gu/vram_psp.h new file mode 100644 index 00000000..0b974f99 --- /dev/null +++ b/ref_gu/vram_psp.h @@ -0,0 +1,39 @@ +/* + * Helper for use with the PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed as 'free to use and modify as long as credited appropriately' + * + * vram.h - Standard C high performance VRAM allocation routines. + * + * Copyright (c) 2007 Alexander Berl 'Raphael' + * http://wordpress.fx-world.org + */ +#ifndef vram_h__ +#define vram_h__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void* vrelptr( void *ptr ); // make a pointer relative to memory base address (ATTENTION: A NULL rel ptr is not illegal/invalid!) +void* vabsptr( void *ptr ); // make a pointer absolute (default return type of valloc) + +void* valloc( size_t size ); +void vfree( void* ptr ); +size_t vmemavail(); +size_t vlargestblock(); + + +#ifdef _DEBUG +// Debug printf (to stdout) a trace of the current Memblocks +void __memwalk(); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // ifdef vram_h__ diff --git a/ref_gu/wscript b/ref_gu/wscript new file mode 100644 index 00000000..7c05884b --- /dev/null +++ b/ref_gu/wscript @@ -0,0 +1,58 @@ +#! /usr/bin/env python +# encoding: utf-8 +# mittorn, 2018 + +from waflib import Logs +import os + +top = '.' + +def options(opt): + grp = opt.add_option_group('ref_gu options') + + grp.add_option('--enable-static-gl', action='store_true', dest='GL_STATIC', default=False, + help = 'enable direct linking to opengl [default: %default]') + + grp.add_option('--disable-gl', action='store_false', dest='GL', default=True, + help = 'disable opengl [default: %default]') + + # stub + return + +def configure(conf): + # check for dedicated server build + if conf.options.DEDICATED: + return + + conf.define_cond('SUPPORT_BSP2_FORMAT', conf.options.SUPPORT_BSP2_FORMAT) + + conf.env.GL = conf.options.GL + conf.env.GL_STATIC = conf.options.GL_STATIC + + conf.define('REF_DLL', 1) + +def build(bld): + libs = [ 'public', 'M' ] + + source = bld.path.ant_glob(['*.c']) + + includes = ['.', + '../engine', + '../engine/common', + '../engine/server', + '../engine/client', + '../public', + '../common', + '../pm_shared' ] + + if bld.env.GL: + bld.shlib( + source = source, + target = 'ref_gu', + features = 'c', + includes = includes, + use = libs + (['GL'] if bld.env.GL_STATIC else []), + defines = ['XASH_GL_STATIC'] if bld.env.GL_STATIC else [], + install_path = bld.env.LIBDIR, + subsystem = bld.env.MSVC_SUBSYSTEM + ) diff --git a/ref_soft/exports.txt b/ref_soft/exports.txt new file mode 100644 index 00000000..b79f9600 --- /dev/null +++ b/ref_soft/exports.txt @@ -0,0 +1,2 @@ +GetRefAPI +GetRefHumanReadableName \ No newline at end of file diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 3f0be086..06a53c32 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -28,6 +28,8 @@ ANDROID_NDK_SYSROOT_FLAG_MAX = 19 # latest NDK that need --sysroot flag ANDROID_NDK_API_MIN = { 10: 3, 19: 16, 20: 16 } # minimal API level ndk revision supports ANDROID_64BIT_API_MIN = 21 # minimal API level that supports 64-bit targets +PSP_SDK_ENVVARS = ['PSPDEV', 'PSPSDK', 'PSPTOOLCHAIN'] + # This class does support ONLY r10e and r19c/r20 NDK class Android: ctx = None # waf context @@ -320,11 +322,80 @@ class Android: ldflags += ['-march=armv5te'] return ldflags + + +class PSP: + ctx = None # waf context + sdk_home = None + psptoolchain_path = None + pspsdk_path = None + binutils_path = None + module_type = None + fw_version = None + render_type = None + + def __init__(self, ctx, moduletype, fwversion, rendertype): + self.ctx = ctx + self.module_type = moduletype + self.fw_version = fwversion + self.render_type = rendertype + + for i in PSP_SDK_ENVVARS: + self.sdk_home = os.getenv(i) + if self.sdk_home != None: + break + else: + ctx.fatal('Set %s environment variable pointing to the root of PSP SDK!' % + ' or '.join(PSP_SDK_ENVVARS)) + + self.psptoolchain_path = os.path.join(self.sdk_home, 'psp') + self.pspsdk_path = os.path.join(self.psptoolchain_path, 'sdk') + self.binutils_path = os.path.join(self.sdk_home, 'bin') + + def cflags(self, cxx = False): + cflags = [] + cflags += ['-I%s' % (os.path.join(self.pspsdk_path, 'include'))] +# cflags += ['-I%s' % (os.path.join(self.psptoolchain_path, 'include'))] + cflags += ['-I.'] + cflags += ['-D_PSP_FW_VERSION=%s' % self.fw_version, '-G0'] + return cflags + # they go before object list + def linkflags(self): + linkflags = [] +# if self.module_type == 'prx': +# linkflags += ['-specs=%s' % os.path.join(self.pspsdk_path, 'lib/prxspecs')] +# linkflags += ['-Wl,-q,-T%s' % os.path.join(self.pspsdk_path, 'lib/linkfile.prx')] +# linkflags += ['-Wl,--hash-style=sysv', '-Wl,--no-undefined', '-no-canonical-prefixes'] + return linkflags + + def ldflags(self): + ldflags = [] + if self.module_type == 'prx': + ldflags += ['-specs=%s' % os.path.join(self.pspsdk_path, 'lib/prxspecs')] + ldflags += ['-Wl,-q,-T%s' % os.path.join(self.pspsdk_path, 'lib/linkfile.prx')] + ldflags += ['-L%s' % os.path.join(self.pspsdk_path, 'lib')] +# ldflags += ['-L%s' % os.path.join(self.psptoolchain_path, 'lib')] + ldflags += ['-L.'] +# ldflags += ['-lgcc', '-no-canonical-prefixes'] + return ldflags + + def stdlibs(self): + stdlibs = [] + stdlibs += ['-lpspdisplay', '-lpspgum_vfpu', '-lpspgu','-lpspge', '-lpspvfpu'] + stdlibs += ['-lpspaudiolib', '-lpspaudio'] + stdlibs += ['-lstdc++', '-lc', '-lm', '-lpspdebug', '-lpspctrl', '-lpspsdk', '-lpsprtc'] + stdlibs += ['-lpspnet', '-lpspnet_inet', '-lpspnet_apctl', '-lpspnet_resolver', '-lpsputility', '-lpspuser', '-lpspkernel', '-lpsppower'] + return stdlibs + def options(opt): android = opt.add_option_group('Android options') android.add_option('--android', action='store', dest='ANDROID_OPTS', default=None, help='enable building for android, format: --android=,,, example: --android=armeabi-v7a-hard,4.9,9') + psp = opt.add_option_group('Sony PSP options') + psp.add_option('--psp', action='store', dest='PSP_OPTS', default=None, + help='enable building for Sony PSP, format: --psp=,, example: --psp=prx,660,HW') + def configure(conf): if conf.options.ANDROID_OPTS: values = conf.options.ANDROID_OPTS.split(',') @@ -361,11 +432,98 @@ def configure(conf): # conf.env.ANDROID_OPTS = android conf.env.DEST_OS2 = 'android' - MACRO_TO_DESTOS = OrderedDict({ '__ANDROID__' : 'android' }) + if conf.options.PSP_OPTS: + values = conf.options.PSP_OPTS.split(',') + if len(values) != 3: + conf.fatal('Invalid --psp paramater value!') + + valid_module_type = ['elf', 'prx'] + valid_render_type = ['SW', 'HW', 'ALL'] + + if values[0] not in valid_module_type: + conf.fatal('Unknown module type: %s. Supported: %r' % (values[0], ', '.join(valid_module_type))) + if values[2] not in valid_render_type: + conf.fatal('Unknown render type: %s. Supported: %r' % (values[2], ', '.join(valid_render_type))) + + conf.psp = psp = PSP(conf, values[0], values[1], values[2]) + conf.environ['CC'] = os.path.join(psp.binutils_path, 'psp-gcc') + conf.environ['CXX'] = os.path.join(psp.binutils_path, 'psp-gcc') + conf.environ['AS'] = os.path.join(psp.binutils_path, 'psp-gcc') + conf.environ['STRIP'] = os.path.join(psp.binutils_path, 'psp-strip') + conf.environ['LD'] = os.path.join(psp.binutils_path, 'psp-gcc') + conf.environ['AR'] = os.path.join(psp.binutils_path, 'psp-ar') + conf.environ['RANLIB'] = os.path.join(psp.binutils_path, 'psp-ranlib') + conf.environ['OBJCOPY'] = os.path.join(psp.binutils_path, 'psp-objcopy') + +# conf.environ['PRXGEN'] = os.path.join(psp.binutils_path, 'prxgen') +# conf.environ['MKSFO'] = os.path.join(psp.binutils_path, 'mksfoex') +# conf.environ['PACK_PBP'] = os.path.join(psp.binutils_path, 'pack-pbp') +# conf.environ['FIXUP'] = os.path.join(psp.binutils_path, 'psp-fixup-imports') + + conf.env.PRXGEN = conf.find_program('psp-prxgen', path_list = psp.binutils_path) + conf.env.MKSFO = conf.find_program('mksfoex', path_list = psp.binutils_path) + conf.env.PACK_PBP = conf.find_program('pack-pbp', path_list = psp.binutils_path) + conf.env.FIXUP = conf.find_program('psp-fixup-imports', path_list = psp.binutils_path) + + conf.env.CFLAGS += psp.cflags() + conf.env.CXXFLAGS += psp.cflags() + conf.env.LINKFLAGS += psp.linkflags() + conf.env.LDFLAGS += psp.ldflags() + + conf.env.PREFIX = '/lib' + + conf.msg('Selected PSPSDK', '%s' % (psp.sdk_home)) + # no need to print C/C++ compiler, as it would be printed by compiler_c/cxx + conf.msg('... C/C++ flags', ' '.join(psp.cflags()).replace(psp.sdk_home, '$PSPSDK')) + conf.msg('... link flags', ' '.join(psp.linkflags()).replace(psp.sdk_home, '$PSPSDK')) + conf.msg('... ld flags', ' '.join(psp.ldflags()).replace(psp.sdk_home, '$PSPSDK')) + + if conf.options.PROFILING: + conf.env.LDFLAGS += ['-lpspprof'] + conf.env.CFLAGS += ['-pg'] + conf.env.LDFLAGS += psp.stdlibs() + + conf.env.DEST_OS2 = 'psp' + conf.env.PSP_RENDER_TYPE = psp.render_type + conf.env.PSP_PATTERN_program = '%s' + conf.env.PSP_PATTERN_stlib = 'lib%s.a' + conf.env.PSP_PATTERN_shlib = '%s.elf' + + if psp.module_type == 'prx': + conf.env.PSP_BUILD_PRX = True + + MACRO_TO_DESTOS = OrderedDict({ '__ANDROID__' : 'android', '__psp__' : 'psp'}) for k in c_config.MACRO_TO_DESTOS: MACRO_TO_DESTOS[k] = c_config.MACRO_TO_DESTOS[k] # ordering is important c_config.MACRO_TO_DESTOS = MACRO_TO_DESTOS +def build(bld): + if bld.env.DEST_OS == 'psp': + bld(rule='${FIXUP} -o ${TGT} ${SRC}', source='engine/xash', target='xash.elf') + if bld.env.PSP_BUILD_PRX: + bld(rule='${PRXGEN} ${SRC} ${TGT}', source='xash.elf', target='xash.prx') + bld(rule='${MKSFO} -d MEMSIZE=1 Xash3D ${TGT}', source='xash.prx', target='PARAM.SFO') + bld(rule='${PACK_PBP} ${TGT} ${SRC[1]} ${PSP_EBOOT_ICON} ${PSP_EBOOT_ICON1} ${PSP_EBOOT_UNKPNG} ${PSP_EBOOT_PIC1} ${PSP_EBOOT_SND0} ${SRC[0]} ${PSP_EBOOT_PSAR}', source='xash.prx PARAM.SFO', target='EBOOT.PBP') + else: + bld(rule='${STRIP} -o ${TGT} ${SRC}', source='xash.elf', target='xash_strip.elf') + bld(rule='${MKSFO} -d MEMSIZE=1 Xash3D ${TGT}', source='xash_strip.elf', target='PARAM.SFO') + bld(rule='${PACK_PBP} ${TGT} ${SRC[1]} ${PSP_EBOOT_ICON} ${PSP_EBOOT_ICON1} ${PSP_EBOOT_UNKPNG} ${PSP_EBOOT_PIC1} ${PSP_EBOOT_SND0} ${SRC[0]} ${PSP_EBOOT_PSAR}', source='xash_strip.elf PARAM.SFO', target='EBOOT.PBP') +# source_target = os.path.join('engine', bld.env.PSP_BUILD_TARGET) +# Logs.info(source_target) +# Logs.info(bld.env.PSP_BUILD_TARGET) +# bld(rule='${FIXUP} -o ${PSP_BUILD_TARGET}.elf ${SRC}', source=source_target, always=True) +# if bld.env.PSP_BUILD_PRX: +# bld(rule='${PRXGEN} ${PSP_BUILD_TARGET}.elf ${PSP_BUILD_TARGET}.prx', always=True) +# if bld.env.PSP_BUILD_EBOOT: +# bld(rule='${MKSFO} -d MEMSIZE=1 ${PSP_EBOOT_TITLE} ${PSP_EBOOT_SFO}', always=True) +# bld(rule='${PACK_PBP} ${PSP_EBOOT} ${PSP_EBOOT_SFO} ${PSP_EBOOT_ICON} ${PSP_EBOOT_ICON1} ${PSP_EBOOT_UNKPNG} ${PSP_EBOOT_PIC1} ${PSP_EBOOT_SND0} ${PSP_BUILD_TARGET}.prx ${PSP_EBOOT_PSAR}', always=True) +# else: +# if bld.env.PSP_BUILD_EBOOT: +# bld(rule='${STRIP} -o ${PSP_BUILD_TARGET}.elf ${PSP_BUILD_TARGET}_strip.elf', always=True) +# bld(rule='${MKSFO} -d MEMSIZE=1 ${PSP_EBOOT_TITLE} ${PSP_EBOOT_SFO}', always=True) +# bld(rule='${PACK_PBP} ${PSP_EBOOT} ${PSP_EBOOT_SFO} ${PSP_EBOOT_ICON} ${PSP_EBOOT_ICON1} ${PSP_EBOOT_UNKPNG} ${PSP_EBOOT_PIC1} ${PSP_EBOOT_SND0} ${PSP_BUILD_TARGET}_strip.elf ${PSP_EBOOT_PSAR}', always=True) +# bld(rule='rm -f ${PSP_BUILD_TARGET}_strip.elf', always=True) + def post_compiler_cxx_configure(conf): conf.msg('Target OS', conf.env.DEST_OS) conf.msg('Target CPU', conf.env.DEST_CPU) diff --git a/wscript b/wscript index 259f6baf..575edb56 100644 --- a/wscript +++ b/wscript @@ -49,11 +49,12 @@ SUBDIRS = [ Subproject('public', dedicated=False, mandatory = True), Subproject('game_launch', singlebin=True), Subproject('ref_gl',), + Subproject('ref_gu',), Subproject('ref_soft'), Subproject('mainui'), Subproject('vgui_support'), - Subproject('stub/server', dedicated=False), - Subproject('stub/client'), + Subproject('stubserver/server', dedicated=False), + Subproject('stubserver/client'), Subproject('dllemu'), Subproject('engine', dedicated=False), ] @@ -92,7 +93,10 @@ def options(opt): help = 'enable low memory mode (only for devices have <128 ram)') grp.add_option('--enable-magx', action = 'store_true', dest = 'MAGX', default = False, - help = 'enable targetting for MotoMAGX phones [default: %default]') + help = 'enable targeting for MotoMAGX phones [default: %default]') + + grp.add_option('--enable-profiling', action = 'store_true', dest = 'PROFILING', default = False, + help = 'enable building with GNU profiling tools') grp.add_option('--ignore-projects', action = 'store', dest = 'IGNORE_PROJECTS', default = None, help = 'disable selected projects from build [default: %default]') @@ -116,6 +120,7 @@ def configure(conf): enforce_pic = True # modern defaults valid_build_types = ['fastnative', 'fast', 'release', 'debug', 'nooptimize', 'sanitize', 'none'] conf.load('fwgslib reconfigure') + if conf.options.IGNORE_PROJECTS: conf.env.IGNORE_PROJECTS = conf.options.IGNORE_PROJECTS.split(',') @@ -139,7 +144,7 @@ def configure(conf): conf.env.MSVC_TARGETS = ['x86'] # explicitly request x86 target for MSVC if sys.platform == 'win32': conf.load('msvc msvc_pdb msdev msvs') - conf.load('xshlib subproject xcompile compiler_c compiler_cxx gitversion clang_compilation_database strip_on_install waf_unit_test') + conf.load('xshlib subproject xcompile gas compiler_c compiler_cxx gitversion clang_compilation_database strip_on_install waf_unit_test') try: conf.env.CC_VERSION[0] @@ -153,6 +158,58 @@ def configure(conf): conf.options.GLWES = True conf.options.GL = False + if conf.env.DEST_OS == 'psp': + conf.options.NO_VGUI= True # skip vgui + conf.options.SINGLE_BINARY = True + conf.options.NO_ASYNC_RESOLVE = True + conf.options.LOW_MEMORY = 2 + conf.options.GL = True + conf.options.GL_STATIC = True + conf.options.STATIC = True + enforce_pic = False + + # rewrite compile patterns + conf.env.cprogram_PATTERN = conf.env.PSP_PATTERN_program + conf.env.cxxprogram_PATTERN = conf.env.PSP_PATTERN_program + conf.env.cshlib_PATTERN = conf.env.PSP_PATTERN_shlib + conf.env.cxxshlib_PATTERN = conf.env.PSP_PATTERN_shlib + conf.env.cstlib_PATTERN = conf.env.PSP_PATTERN_stlib + conf.env.cxxstlib_PATTERN = conf.env.PSP_PATTERN_stlib + + # set EBOOT.PBP generation parameters + conf.env.PSP_EBOOT_TITLE = 'Xash3D' + conf.env.PSP_EBOOT_SFO = 'PARAM.SFO' + conf.env.PSP_EBOOT_ICON = 'NULL' + conf.env.PSP_EBOOT_ICON1 = 'NULL' + conf.env.PSP_EBOOT_UNKPNG = 'NULL' + conf.env.PSP_EBOOT_PIC1 = 'NULL' + conf.env.PSP_EBOOT_SND0 = 'NULL' + conf.env.PSP_EBOOT_PSAR = 'NULL' + conf.env.PSP_EBOOT = 'EBOOT.PBP' + + # set target parameters + conf.env.PSP_BUILD_TARGET = 'xash' + conf.env.PSP_BUILD_EBOOT = True + + if conf.options.SINGLE_BINARY: + conf.options.IGNORE_PROJECTS = 'ref_gl,stub/server,stub/client' + conf.options.STATIC_LINKING = 'menu' + if conf.env.PSP_RENDER_TYPE == 'SW': + conf.options.STATIC_LINKING += ',ref_soft' + else: + conf.options.IGNORE_PROJECTS += ',ref_soft' + + if conf.env.PSP_RENDER_TYPE == 'HW': + conf.options.STATIC_LINKING += ',ref_gu' + else: + conf.options.IGNORE_PROJECTS += ',ref_gu' + + if conf.env.PSP_RENDER_TYPE == 'ALL': + conf.options.STATIC_LINKING += 'ref_soft,ref_gu' + + conf.env.IGNORE_PROJECTS = conf.options.IGNORE_PROJECTS.split(',') + conf.load('xshlib') # for set linking + if conf.env.STATIC_LINKING: enforce_pic = False # PIC may break static linking @@ -244,7 +301,7 @@ def configure(conf): }, 'debug': { 'msvc': ['/O1'], - 'gcc': ['-Og'], + 'gcc': ['-Og', '-fno-omit-frame-pointer', '-funwind-tables'], 'owcc': ['-O0', '-fno-omit-frame-pointer', '-funwind-tables', '-fno-omit-leaf-frame-pointer'], 'default': ['-O1'] }, @@ -268,12 +325,12 @@ def configure(conf): '-Werror=vla', '-Werror=tautological-compare', '-Werror=duplicated-cond', - '-Werror=duplicated-branches', # BEWARE: buggy +# '-Werror=duplicated-branches', # BEWARE: buggy '-Werror=bool-compare', '-Werror=bool-operation', - '-Werror=cast-align', +# '-Werror=cast-align', '-Werror=cast-align=strict', # =strict is for GCC >=8 - '-Werror=packed', +# '-Werror=packed', '-Werror=packed-not-aligned', '-Wuninitialized', # older GCC versions have -Wmaybe-uninitialized enabled by this switch, which is not accurate # so just warn, not error @@ -288,10 +345,10 @@ def configure(conf): '-Werror=implicit-function-declaration', '-Werror=int-conversion', '-Werror=implicit-int', - '-Werror=strict-prototypes', +# '-Werror=strict-prototypes', '-Werror=old-style-declaration', '-Werror=old-style-definition', - '-Werror=declaration-after-statement', +# '-Werror=declaration-after-statement', '-Werror=enum-conversion', '-fnonconst-initializers' # owcc ] @@ -299,6 +356,13 @@ def configure(conf): linkflags = conf.get_flags_by_type(linker_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC, conf.env.CC_VERSION[0]) cflags = conf.get_flags_by_type(compiler_c_cxx_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC, conf.env.CC_VERSION[0]) + # Profiling +# if conf.options.PROFILING: +# prof_cflags = { +# 'gcc': ['-pg'] +# } +# cflags += conf.get_flags_by_compiler(prof_cflags, conf.env.COMPILER_CC) + # Here we don't differentiate C or C++ flags if conf.options.LTO: lto_cflags = { @@ -323,7 +387,7 @@ def configure(conf): } cflags += conf.get_flags_by_compiler(polly_cflags, conf.env.COMPILER_CC) - + # And here C++ flags starts to be treated separately cxxflags = list(cflags) if conf.env.COMPILER_CC != 'msvc': @@ -359,7 +423,8 @@ def configure(conf): else: # include portable stdint by Paul Hsich conf.define('STDINT_H', 'pstdint.h') - + + conf.env.PROFILING = conf.options.PROFILING conf.env.DEDICATED = conf.options.DEDICATED conf.env.SINGLE_BINARY = conf.options.SINGLE_BINARY or conf.env.DEDICATED if conf.env.DEST_OS == 'dos': @@ -407,6 +472,9 @@ def configure(conf): if conf.options.LOW_MEMORY: conf.define('XASH_LOW_MEMORY', conf.options.LOW_MEMORY) + if conf.options.PROFILING: + conf.define('XASH_PROFILING', 1) + for i in SUBDIRS: if not i.is_enabled(conf): continue @@ -420,3 +488,4 @@ def build(bld): continue bld.add_subproject(i.name) + bld.load('xcompile')