Compare commits

...

58 Commits

Author SHA1 Message Date
Alibek Omarov
a131a59414 engine: server: don't limit difficulty level on engine side 2024-03-03 19:01:02 +03:00
Alibek Omarov
cd7727b7de ref: gl: allow shadows, use shadows from magic's engine fork 2024-03-03 19:01:02 +03:00
Alibek Omarov
462b64b8ce ref: gl: add water warp scroll effect for compatibility with Magic-Nipples's engine fork 2024-03-03 19:01:02 +03:00
Alibek Omarov
5525af3769 engine: server: add SOLID_GIB extension from TimeWarp that disables interactions for gibs except for the solid brushes 2024-03-03 19:01:02 +03:00
Alibek Omarov
e334699c0b ref: gl: exclude SURF_REFLECT faces from VBO 2024-03-03 19:01:02 +03:00
Alibek Omarov
f5832faaa0 ref: gl: implement mirrors 2024-03-03 19:01:02 +03:00
Alibek Omarov
547a41ef2d ref: gl: mirror entities culling 2024-03-03 19:01:02 +03:00
Alibek Omarov
ab60b1e981 engine: mod_bmodel: detect reflective textures and set appropriate flags on surfaces with that textures 2024-03-03 19:01:02 +03:00
Alibek Omarov
d3e698d042 common: add mirror definitions 2024-03-03 19:01:02 +03:00
Alibek Omarov
7584bbe47e engine: client: fix sound mute when losing focus 2024-03-03 19:00:50 +03:00
Andrey Akhmichin
505b6cc264 utils: mdldec: boneweights fix. 2024-03-01 20:26:18 +03:00
Alibek Omarov
24f23908be engine: platform: stub Sys_DebuggerPresent on unsupported platforms 2024-02-27 05:17:01 +03:00
Alibek Omarov
43bd73f37b engine: platform: that's why commit --amend and/or rebase --interactive your fixes in your patches before proposing them 2024-02-27 04:59:54 +03:00
mittorn
bb2753cbfc engine/system: add asm-based raise implementation for arm and aarch64, making Sys_DebugBreak more relevant, use tgkill instead of kill 2024-02-27 04:54:55 +03:00
mittorn
5568eccc8a ref/r_sprite: move dframetype to inner block 2024-02-27 04:17:21 +03:00
mittorn
c3b2cc4423 waifulib/compiler_optimizations: disable no-semantic-interposition for gcc4 in release builds too 2024-02-27 04:16:14 +03:00
mittorn
d84130219f crashhandler: set host.crashed before messagebox to allow platform code handle crashed state in different way 2024-02-27 04:14:37 +03:00
mittorn
d97d4d6c42 filesystem: add XASH_ANDROID_ASSETS macro for aassets switch 2024-02-27 04:04:54 +03:00
mittorn
15798aaf52 ref/r_sprite: move dframetype to inner block 2024-02-27 04:04:54 +03:00
mittorn
e53892e827 ref_gl: fix loop scope in ripple code 2024-02-27 04:04:54 +03:00
mittorn
cb12af69ed gl2shim: add SOFTFP_LINK support for gles3compat 2024-02-27 04:04:54 +03:00
mittorn
e82050a4bc gl2shim: fix gcc 4.9 build 2024-02-27 04:04:54 +03:00
mittorn
e4edd2da45 platform/posix: check for non-zero dladdr ret 2024-02-27 04:04:54 +03:00
mittorn
4f80922358 waf/compiler_optimizations: remove no-semantic-interposition for gcc4 2024-02-27 04:04:54 +03:00
mittorn
ae7ed03376 ref_soft: fix crash when y < -height 2024-02-27 04:04:54 +03:00
mittorn
c082e46662 imagelib: fix for loop scope 2024-02-27 04:04:54 +03:00
mittorn
b0bbcab6b4 ref_soft,ref_gl: fix unaligned read in sprite code 2024-02-27 04:04:54 +03:00
SNMetamorph
0fc83080ec engine: server: allowed bandwidth test on listen servers 2024-02-26 19:41:32 +03:00
SNMetamorph
73883b504f engine: client: improved bandwidth test logic 2024-02-26 19:41:32 +03:00
SNMetamorph
8efc9d74e8 engine: server: fixed test packet buffer was not being created 2024-02-26 19:41:32 +03:00
mittorn
373321ff00 ref/gl: do not set type on 1D textures on GLES (will fallback to 2D) 2024-02-26 16:08:37 +03:00
Alibek Omarov
a508467aac engine: client: silently ignore if server didn't sent local player info in delta at spawn
The bug happens somewhere in server, not sure where or how. Remove Host_Error so mods
can be played again.
2024-02-26 11:48:46 +03:00
Alibek Omarov
abbe993e80 3rdparty: gl4es: update 2024-02-26 11:20:04 +03:00
Alibek Omarov
c644e4d569 3rdparty: nanogl: update, fix link error with missing glTexEnvfv 2024-02-26 11:06:09 +03:00
Alibek Omarov
c200e008f1 wscript: disable gl-wes-v2 renderer from Android builds 2024-02-26 10:55:42 +03:00
Alibek Omarov
e95a2da6d0 engine: platform: sdl: slightly rework previous patch to not call SDL each frame and check for NULL pointers 2024-02-23 20:55:32 +03:00
Alibek Omarov
8afca1a79c engine: server: allow starting game from custom map 2024-02-23 20:54:32 +03:00
Alibek Omarov
827c9ca857 filesystem: restore original Xash behavior with unreferenced keys in gameinfo.txt/liblist.gam 2024-02-23 20:54:32 +03:00
Alibek Omarov
2416635ba3 common: validate extended surfaces size 2024-02-23 20:54:32 +03:00
Alibek Omarov
39eec89580 engine: increase elights limit up to 128 2024-02-23 20:54:32 +03:00
Sheridan Kane Rathbun
98d88266a6 engine: platform: sdl: fix incorrect mouse cursor positioning on high-dpi displays (#1623)
Signed-off-by: SheridanR <sheridan.rathbun@gmail.com>
2024-02-23 20:54:17 +03:00
mittorn
0f3359fc05 ref_gl: do not apply overbright color if lightscale correction used 2024-02-22 21:02:34 +03:00
mittorn
6c77608c34 ref_gl: implement color-modulate overbrigths with gl_vbo using texture GL_COMBINE 2024-02-22 21:02:34 +03:00
Alibek Omarov
abdff6eff6 mainui: update 2024-02-22 15:43:49 +03:00
Alibek Omarov
95a75465c9 common: remove FORCEINLINE and NOINLINE macros, we don't use them in engine 2024-02-22 15:43:37 +03:00
Alibek Omarov
119926805f mainui: update 2024-02-21 03:39:11 +03:00
Alibek Omarov
f749b5cb4b 3rdparty: nanogl: update 2024-02-21 02:57:18 +03:00
Alibek Omarov
c29ad6b598 scripts: waifulib: compiler_optimizations: add option to use profiling 2024-02-21 02:57:07 +03:00
Alibek Omarov
4c5dfb963e engine: zone: few more tunings for realloc 2024-02-21 00:59:02 +03:00
Alibek Omarov
e11f9e05d4 engine: server: add GetNativeObject to server's PhysicsAPI 2024-02-19 17:49:37 +03:00
Alibek Omarov
5c8b5b3511 Documentation: extensions: add doc about native-object 2024-02-19 17:49:37 +03:00
Alibek Omarov
2ea7162287 engine: gameui: add GetNativeObject to extended menu API 2024-02-19 17:49:37 +03:00
Alibek Omarov
e62ab51842 engine: platforms: android: platforms aren't expected to get NULL or zero sized native object names anymore 2024-02-19 17:49:37 +03:00
Bohdan Shulyar
f1bc9b87b1 platform: android: port to SDL 2024-02-19 17:47:40 +03:00
Alibek Omarov
a9bddaac64 engine: platform: win32: do not call filesystem functions if filesystem_stdio was failed to load 2024-02-19 06:27:24 +03:00
Alibek Omarov
6cef6f6a75 engine: zone: refactoring 2024-02-19 06:13:54 +03:00
Alibek Omarov
25ea6ed500 scripts: waifulib: don't use relative path while creating ZIP file (so cwd might not be equal to sources path) 2024-02-19 05:00:12 +03:00
Alibek Omarov
3d5173f257 engine: zone: implement Mem_Realloc through standard realloc 2024-02-19 04:22:16 +03:00
68 changed files with 1563 additions and 337 deletions

View File

@@ -24,14 +24,9 @@ jobs:
# - os: ubuntu-aarch64-20.04
# targetos: linux
# targetarch: aarch64
# - os: ubuntu-20.04
# targetos: android
# targetarch: 32
# - os: ubuntu-20.04
# targetos: android
# targetarch: 64
- os: ubuntu-20.04
targetos: android
targetarch: multiarch
# - os: ubuntu-20.04
# targetos: motomagx
# targetarch: armv6
@@ -51,7 +46,6 @@ jobs:
env:
SDL_VERSION: 2.28.5
GH_CPU_ARCH: ${{ matrix.targetarch }}
ANDROID_SDK_TOOLS_VER: 4333796
steps:
- name: Checkout
uses: actions/checkout@v3

5
.gitignore vendored
View File

@@ -54,6 +54,11 @@ CMakeFiles
Makefile
cmake_install.cmake
install_manifest.txt
CMakeLists.txt*
CMakeScripts
Testing
compile_commands.json
_deps
# makedepend
Makefile.dep
*.bak

View File

@@ -0,0 +1,25 @@
# GetNativeObject API
To be able to use platform-specific features or get optional engine interfaces, we've added a simple call to MobilityAPI on client DLL and PhysicsAPI for server DLL and extended MenuAPI for menu DLL.
It's defined like this:
```
void *pfnGetNativeObject( const char *name );
```
#### Cross-platform objects
Only these objects are guaranteed to be available on all targets.
| Object name | Interface |
|-------------|-----------|
| `VFileSystem009` | Provides C++ interface to filesystem, binary-compatible with Valve's VFileSystem009. |
| `XashFileSystemXXX` | Provides C interface to filesystem. This interface is unstable and not recommended for generic use, outside of engine internals. For more info about current version look into `filesystem.h`. |
#### Android-specific objects
| Object name | Interface |
|-------------|-----------|
| `JNIEnv` | Allows interfacing with Java Native Interface. |
| `ActivityClass` | Returns JNI object for engine Android activity class. |

View File

@@ -54,6 +54,7 @@ BRUSH MODELS
#define SURF_CONVEYOR BIT( 6 ) // scrolled texture (was SURF_DRAWBACKGROUND)
#define SURF_UNDERWATER BIT( 7 ) // caustics
#define SURF_TRANSPARENT BIT( 8 ) // it's a transparent texture (was SURF_DONTWARP)
#define SURF_REFLECT BIT( 31 ) // mirrors
// lightstyle management
#define LM_STYLES 4 // MAXLIGHTMAPS

View File

@@ -228,7 +228,9 @@ typedef struct mextrasurf_s
unsigned short numverts; // world->vertexes[]
int firstvertex; // fisrt look up in tr.tbn_vectors[], then acess to world->vertexes[]
intptr_t reserved[32]; // just for future expansions or mod-makers
struct mextrasurf_s *mirrorchain; // engine-side mirrors, may be ignored in mods with custom renderers
intptr_t reserved[31]; // just for future expansions or mod-makers
} mextrasurf_t;
struct msurface_s
@@ -534,5 +536,14 @@ typedef struct
#define MAX_EFRAGS 8192 // Arcane Dimensions required
#define MAX_REQUESTS 64
#if ! XASH_64BIT
STATIC_ASSERT( sizeof( mextrasurf_t ) == 324, "mextrasurf_t unexpected size" );
STATIC_ASSERT( sizeof( decal_t ) == 60, "decal_t unexpected size");
STATIC_ASSERT( sizeof( mfaceinfo_t ) == 176, "mfaceinfo_t unexpected size");
#else
STATIC_ASSERT( sizeof( mextrasurf_t) == 496, "mextrasurf_t unexpected size");
STATIC_ASSERT( sizeof( decal_t ) == 88, "decal_t unexpected size");
STATIC_ASSERT( sizeof( mfaceinfo_t ) == 304, "mfaceinfo_t unexpected size");
#endif
#endif//COM_MODEL_H

View File

@@ -92,6 +92,7 @@
#define SOLID_BSP 4 // bsp clip, touch on edge, block
#define SOLID_CUSTOM 5 // call external callbacks for tracing
#define SOLID_PORTAL 6 // borrowed from FTE
#define SOLID_GIB 7 // do not block, only collide with solid brushes
// edict->deadflag values
#define DEAD_NO 0 // alive
@@ -114,6 +115,8 @@
#define EF_LIGHT 64 // rocket flare glow sprite
#define EF_NODRAW 128 // don't draw entity
#define EF_NOREFLECT (1U<<24) // Entity won't reflecting in mirrors
#define EF_REFLECTONLY (1U<<25) // Entity will be drawing only in mirrors
#define EF_WATERSIDES (1U<<26) // Do not remove sides for func_water entity
#define EF_FULLBRIGHT (1U<<27) // Just get fullbright
#define EF_NOSHADOW (1U<<28) // ignore shadow for this entity

View File

@@ -164,6 +164,10 @@ Default build-depended cvar and constant values
#define XASH_INTERNAL_GAMELIBS
#endif // XASH_ANDROID || XASH_IOS || XASH_EMSCRIPTEN
#if XASH_ANDROID && XASH_SDL
#define XASH_ANDROID_ASSETS 1
#endif
// Defaults
#ifndef DEFAULT_TOUCH_ENABLE
#define DEFAULT_TOUCH_ENABLE "0"

View File

@@ -27,6 +27,7 @@ GNU General Public License for more details.
#define ENGINE_COMPUTE_STUDIO_LERP (1<<7) // enable MOVETYPE_STEP lerping back in engine
#define ENGINE_LINEAR_GAMMA_SPACE (1<<8) // disable influence of gamma/brightness cvars to textures/lightmaps, for mods with custom renderer
#define ENGINE_ALLOW_MIRRORS (1U<<30) // allow mirrors in engine renderer
#define ENGINE_STEP_POSHISTORY_LERP (1U<<31) // enable MOVETYPE_STEP interpolation based on position history. Incompatible with ENGINE_COMPUTE_STUDIO_LERP!
// adjust the mask when features will be added or removed
@@ -40,6 +41,7 @@ GNU General Public License for more details.
| ENGINE_IMPROVED_LINETRACE \
| ENGINE_COMPUTE_STUDIO_LERP \
| ENGINE_LINEAR_GAMMA_SPACE \
| ENGINE_ALLOW_MIRRORS \
| ENGINE_STEP_POSHISTORY_LERP )
#endif//FEATURES_H

View File

@@ -86,8 +86,6 @@ typedef uint64_t longtime_t;
#define NORETURN __attribute__((noreturn))
#define NONNULL __attribute__((nonnull))
#define ALLOC_CHECK(x) __attribute__((alloc_size(x)))
#define FORCEINLINE inline __attribute__((always_inline))
#define NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define EXPORT __declspec( dllexport )
#define GAME_EXPORT
@@ -95,8 +93,6 @@ typedef uint64_t longtime_t;
#define NORETURN
#define NONNULL
#define ALLOC_CHECK(x)
#define FORCEINLINE __forceinline
#define NOINLINE __declspec( noinline )
#else
#define EXPORT
#define GAME_EXPORT
@@ -104,8 +100,6 @@ typedef uint64_t longtime_t;
#define NORETURN
#define NONNULL
#define ALLOC_CHECK(x)
#define FORCEINLINE
#define NOINLINE
#endif
#if ( __GNUC__ >= 3 )

View File

@@ -1220,6 +1220,7 @@ static ui_extendedfuncs_t gExtendedfuncs =
pfnParseFileSafe,
NET_AdrToString,
NET_CompareAdrSort,
Sys_GetNativeObject,
};
void UI_UnloadProgs( void )

View File

@@ -28,7 +28,6 @@ GNU General Public License for more details.
#define MAX_CMD_BUFFER 8000
#define CONNECTION_PROBLEM_TIME 15.0 // 15 seconds
#define CL_CONNECTION_RETRIES 10
#define CL_TEST_RETRIES_NORESPONCE 3
#define CL_TEST_RETRIES 5
CVAR_DEFINE_AUTO( mp_decals, "300", FCVAR_ARCHIVE, "decals limit in multiplayer" );
@@ -1072,6 +1071,22 @@ static void CL_SendConnectPacket( void )
cls.timestart = Sys_DoubleTime();
}
/*
=================
CL_GetTestFragmentSize
Returns bandwidth test fragment size
=================
*/
static int CL_GetTestFragmentSize( void )
{
const int fragmentSizes[CL_TEST_RETRIES] = { 64000, 32000, 10666, 5200, 1400 };
if( cls.connect_retry >= 0 && cls.connect_retry < CL_TEST_RETRIES )
return bound( FRAGMENT_MIN_SIZE, fragmentSizes[cls.connect_retry], FRAGMENT_MAX_SIZE );
else
return FRAGMENT_MIN_SIZE;
}
/*
=================
CL_CheckForResend
@@ -1083,8 +1098,9 @@ static void CL_CheckForResend( void )
{
netadr_t adr;
net_gai_state_t res;
float resendTime;
qboolean bandwidthTest;
if( cls.internetservers_wait )
CL_SendMasterServerScanRequest();
@@ -1110,7 +1126,10 @@ static void CL_CheckForResend( void )
else if( cl_resend.value > CL_MAX_RESEND_TIME )
Cvar_SetValue( "cl_resend", CL_MAX_RESEND_TIME );
if(( host.realtime - cls.connect_time ) < cl_resend.value )
bandwidthTest = !cls.legacymode && cl_test_bandwidth.value && cls.connect_retry <= CL_TEST_RETRIES;
resendTime = bandwidthTest ? 1.0f : cl_resend.value;
if(( host.realtime - cls.connect_time ) < resendTime )
return;
res = NET_StringToAdrNB( cls.servername, &adr );
@@ -1137,10 +1156,11 @@ static void CL_CheckForResend( void )
if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER );
if( cls.connect_retry == CL_TEST_RETRIES_NORESPONCE )
if( cls.connect_retry == CL_TEST_RETRIES )
{
// too many fails use default connection method
Con_Printf( "hi-speed connection is failed, use default method\n" );
Con_Printf( "Bandwidth test failed, fallback to default connecting method\n" );
Con_Printf( "Connecting to %s... (retry #%i)\n", cls.servername, cls.connect_retry + 1 );
Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" );
Cvar_SetValue( "cl_dlmax", FRAGMENT_MIN_SIZE );
cls.connect_time = host.realtime;
@@ -1148,16 +1168,15 @@ static void CL_CheckForResend( void )
return;
}
bandwidthTest = !cls.legacymode && cl_test_bandwidth.value;
cls.serveradr = adr;
cls.max_fragment_size = Q_min( FRAGMENT_MAX_SIZE, cls.max_fragment_size / (cls.connect_retry + 1));
cls.max_fragment_size = CL_GetTestFragmentSize();
cls.connect_time = host.realtime; // for retransmit requests
cls.connect_retry++;
if( bandwidthTest )
Con_Printf( "Connecting to %s... [retry #%i, max fragment size %i]\n", cls.servername, cls.connect_retry, cls.max_fragment_size );
Con_Printf( "Connecting to %s... (retry #%i, fragment size %i)\n", cls.servername, cls.connect_retry, cls.max_fragment_size );
else
Con_Printf( "Connecting to %s... [retry #%i]\n", cls.servername, cls.connect_retry );
Con_Printf( "Connecting to %s... (retry #%i)\n", cls.servername, cls.connect_retry );
if( bandwidthTest )
Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size );

View File

@@ -795,25 +795,14 @@ static void CL_SetupPMove( playermove_t *pmove, const local_state_t *from, const
pmove->player_index = ps->number - 1;
// a1ba: workaround bug on old protocol where the server refuse to send
// our local player in delta. cl.playernum, in theory, must be equal
// to our local player index anyway
// a1ba: workaround bug where the server refuse to send our local player in delta
// cl.playernum, in theory, must be equal to our local player index anyway
//
// this might not be a real solution, since everything else will be bogus
// but we need to properly run prediction and avoid potential memory
// corruption
// either debug this, or remove when old protocol will be dropped!!!
if( pmove->player_index < 0 )
{
if( cls.legacymode )
{
pmove->player_index = bound( 0, cl.playernum, cl.maxclients - 1 );
}
else
{
// if this happens, record a demo and send it to a1ba
Host_Error( "%s: ps->number == %d\n", __func__, ps->number );
}
}
pmove->player_index = bound( 0, cl.playernum, cl.maxclients - 1 );
pmove->multiplayer = (cl.maxclients > 1);
pmove->runfuncs = runfuncs;

View File

@@ -2100,9 +2100,13 @@ void Con_RunConsole( void )
// decide on the destination height of the console
if( host.allow_console && cls.key_dest == key_console )
{
#if XASH_MOBILE_PLATFORM
con.showlines = refState.height; // always full screen on mobile devices
#else
if( cls.state < ca_active || cl.first_frame )
con.showlines = refState.height; // full screen
else con.showlines = (refState.height >> 1); // half screen
#endif
}
else con.showlines = 0; // none visible

View File

@@ -2029,10 +2029,18 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx
{
touch.move_finger = touch.resize_finger = touch.look_finger = -1;
// Hack for keyboard, hope it help
// a1ba: this is absolutely horrible
if( cls.key_dest == key_console || cls.key_dest == key_message )
{
if ( type == event_down ) // don't pop it again on event_up
static float x1 = 0.0f;
x1 += dx;
if( type == event_up ) // don't show keyboard on every tap
{
Key_EnableTextInput( true, true );
x1 = 0.0f;
}
if( cls.key_dest == key_console )
{
static float y1 = 0;
@@ -2054,6 +2062,13 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx
// exit of console area
if( type == event_down && x < 0.1f && y > 0.9f )
Cbuf_AddText( "escape\n" );
// swipe from edge to exit console/chat
if(( x > 0.8f && x1 < -0.1f ) || ( x < 0.2f && x1 > 0.1f ))
{
Cbuf_AddText( "escape\n" );
x1 = 0.0f;
}
}
UI_MouseMove( TO_SCRN_X(x), TO_SCRN_Y(y) );
//MsgDev( D_NOTE, "touch %d %d\n", TO_SCRN_X(x), TO_SCRN_Y(y) );

View File

@@ -43,7 +43,7 @@ CVAR_DEFINE_AUTO( s_lerping, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "apply interpo
static CVAR_DEFINE( s_ambient_level, "ambient_level", "0.3", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "volume of environment noises (water and wind)" );
static CVAR_DEFINE( s_ambient_fade, "ambient_fade", "1000", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "rate of volume fading when client is moving" );
static CVAR_DEFINE_AUTO( s_combine_sounds, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "combine channels with same sounds" );
CVAR_DEFINE_AUTO( snd_mute_losefocus, "1", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "silence the audio when game window loses focus" );
static CVAR_DEFINE_AUTO( snd_mute_losefocus, "1", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "silence the audio when game window loses focus" );
CVAR_DEFINE_AUTO( s_test, "0", 0, "engine developer cvar for quick testing new features" );
CVAR_DEFINE_AUTO( s_samplecount, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "sample count (0 for default value)" );
CVAR_DEFINE_AUTO( s_warn_late_precache, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "warn about late precached sounds on client-side" );
@@ -64,6 +64,12 @@ float S_GetMasterVolume( void )
{
float scale = 1.0f;
if( host.status == HOST_NOFOCUS && snd_mute_losefocus.value != 0.0f )
{
// we return zero volume to keep sounds running
return 0.0f;
}
if( !s_listener.inmenu && soundfade.percent != 0 )
{
scale = bound( 0.0f, soundfade.percent / 100.0f, 1.0f );

View File

@@ -221,7 +221,6 @@ extern convar_t s_musicvolume;
extern convar_t s_lerping;
extern convar_t s_test; // cvar to testify new effects
extern convar_t s_samplecount;
extern convar_t snd_mute_losefocus;
extern convar_t s_warn_late_precache;
void S_InitScaletable( void );

View File

@@ -20,8 +20,6 @@ GNU General Public License for more details.
#include "vid_common.h"
#include "platform/platform.h"
static CVAR_DEFINE( window_width, "width", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen width" );
static CVAR_DEFINE( window_height, "height", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen height" );
static CVAR_DEFINE_AUTO( vid_mode, "0", FCVAR_RENDERINFO, "current video mode index (used only for storage)" );
static CVAR_DEFINE_AUTO( vid_rotate, "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen rotation (0-3)" );
static CVAR_DEFINE_AUTO( vid_scale, "1.0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "pixel scale" );
@@ -29,6 +27,8 @@ static CVAR_DEFINE_AUTO( vid_scale, "1.0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "p
CVAR_DEFINE_AUTO( vid_highdpi, "1", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable High-DPI mode" );
CVAR_DEFINE_AUTO( vid_maximized, "0", FCVAR_RENDERINFO, "window maximized state, read-only" );
CVAR_DEFINE( vid_fullscreen, "fullscreen", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "fullscreen state (0 windowed, 1 fullscreen, 2 borderless)" );
CVAR_DEFINE( window_width, "width", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen width" );
CVAR_DEFINE( window_height, "height", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen height" );
CVAR_DEFINE( window_xpos, "_window_xpos", "-1", FCVAR_RENDERINFO, "window position by horizontal" );
CVAR_DEFINE( window_ypos, "_window_ypos", "-1", FCVAR_RENDERINFO, "window position by vertical" );

View File

@@ -38,6 +38,8 @@ extern glwstate_t glw_state;
extern convar_t vid_fullscreen;
extern convar_t vid_maximized;
extern convar_t vid_highdpi;
extern convar_t window_width;
extern convar_t window_height;
extern convar_t window_xpos;
extern convar_t window_ypos;
extern convar_t gl_msaa_samples;

View File

@@ -490,13 +490,14 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context)
#ifdef XASH_SDL
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
#endif
host.crashed = true;
Platform_MessageBox( "Xash Error", message, false );
// log saved, now we can try to save configs and close log correctly, it may crash
if( host.type == HOST_NORMAL )
CL_Crashed();
host.status = HOST_CRASHED;
host.crashed = true;
Sys_Quit();
}

View File

@@ -70,6 +70,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
ktx2_index_t index;
size_t total_size = 0;
size_t max_offset = 0;
int mip, cursor;
const byte *const levels_begin = buffer + KTX2_LEVELS_OFFSET;
// Sets image.type and image.flags
@@ -125,7 +126,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
memcpy( &index, buffer + KTX2_IDENTIFIER_SIZE + sizeof( ktx2_header_t ), sizeof( index ));
for( int mip = 0; mip < header->levelCount; ++mip )
for( mip = 0; mip < header->levelCount; ++mip )
{
const uint32_t width = Q_max( 1, ( header->pixelWidth >> mip ));
const uint32_t height = Q_max( 1, ( header->pixelHeight >> mip ));
@@ -154,7 +155,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
image.rgba = Mem_Malloc( host.imagepool, image.size );
memcpy( image.rgba, buffer, image.size );
for( int mip = 0, cursor = 0; mip < header->levelCount; ++mip )
for( mip = 0, cursor = 0; mip < header->levelCount; ++mip )
{
ktx2_level_t level;
memcpy( &level, levels_begin + mip * sizeof( level ), sizeof( level ));

View File

@@ -2026,6 +2026,14 @@ static qboolean Mod_LooksLikeWaterTexture( const char *name )
return false;
}
static qboolean Mod_LooksLikeReflectiveTexture( const char *name )
{
if( !Q_strcmp( name, "reflect1" ) || !Q_strncmp( name, "!reflect", 8 ))
return true;
return false;
}
static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureIndex )
{
#if !XASH_DEDICATED
@@ -2480,6 +2488,13 @@ static void Mod_LoadSurfaces( model_t *mod, dbspmodel_t *bmod )
if( !Q_strncmp( tex->name, "{scroll", 7 ))
SetBits( out->flags, SURF_CONVEYOR|SURF_TRANSPARENT );
// some mods don't work with mirrors or make random surfaces reflective
if( FBitSet( host.features, ENGINE_ALLOW_MIRRORS ) && Mod_LooksLikeReflectiveTexture( tex->name ))
{
SetBits( world.flags, FWORLD_HAS_MIRRORS );
SetBits( out->flags, SURF_REFLECT );
}
if( tex->name[0] == '{' )
SetBits( out->flags, SURF_TRANSPARENT );
@@ -2697,6 +2712,9 @@ static void Mod_LoadLeafs( model_t *mod, dbspmodel_t *bmod )
{
// mark underwater surfaces
SetBits( out->firstmarksurface[j]->flags, SURF_UNDERWATER );
// underwater surfaces can't have reflection (performance)
ClearBits( out->firstmarksurface[j]->flags, SURF_REFLECT );
}
}
}

View File

@@ -127,7 +127,7 @@ GNU General Public License for more details.
#define MAX_CUSTOM (1<<MAX_CUSTOM_BITS)// 10 bits == 1024 generic file
#define MAX_USER_MESSAGES 197 // another 58 messages reserved for engine routines
#define MAX_DLIGHTS 32 // dynamic lights (rendered per one frame)
#define MAX_ELIGHTS 64 // entity only point lights
#define MAX_ELIGHTS 128 // a1ba: increased from 64 to 128, entity only point lights
#define MAX_LIGHTSTYLES 256 // a1ba: increased from 64 to 256, protocol limit
#define MAX_RENDER_DECALS 4096 // max rendering decals per a level

View File

@@ -19,6 +19,10 @@ GNU General Public License for more details.
#include <stdlib.h>
#include <errno.h>
#if _MSC_VER
#include <intrin.h>
#endif
#ifdef XASH_SDL
#include <SDL.h>
#endif
@@ -68,17 +72,15 @@ Sys_DebugBreak
*/
void Sys_DebugBreak( void )
{
#if XASH_LINUX || ( XASH_WIN32 && !XASH_64BIT )
#if _MSC_VER
if( Sys_DebuggerPresent() )
_asm { int 3 }
#elif XASH_X86
if( Sys_DebuggerPresent() )
asm volatile( "int $3;" );
#else
if( Sys_DebuggerPresent() )
raise( SIGINT );
#endif
if( Sys_DebuggerPresent( ))
__debugbreak();
#else // !_MSC_VER
if( Sys_DebuggerPresent( ))
{
INLINE_RAISE( SIGINT );
INLINE_NANOSLEEP1(); // sometimes signal comes with delay, let it interrupt nanosleep
}
#endif
}

View File

@@ -93,123 +93,241 @@ static mempool_t *Mem_FindPool( poolhandle_t poolptr )
}
#endif
static inline void Mem_PoolAdd( mempool_t *pool, size_t size )
{
pool->totalsize += size;
pool->realsize += sizeof( memheader_t ) + size + sizeof( byte );
}
static inline void Mem_PoolSubtract( mempool_t *pool, size_t size )
{
pool->totalsize -= size;
pool->realsize -= sizeof( memheader_t ) + size + sizeof( byte );
}
static inline void Mem_PoolLinkAlloc( mempool_t *pool, memheader_t *mem )
{
mem->next = pool->chain;
if( mem->next ) mem->next->prev = mem;
pool->chain = mem;
mem->prev = NULL;
mem->pool = pool;
}
static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem )
{
if( mem->next ) mem->next->prev = mem->prev;
if( mem->prev ) mem->prev->next = mem->next;
else pool->chain = mem->next;
mem->pool = NULL;
}
static inline void Mem_InitAlloc( memheader_t *mem, size_t size, const char *filename, int fileline )
{
mem->size = size;
mem->filename = filename;
mem->fileline = fileline;
mem->sentinel1 = MEMHEADER_SENTINEL1;
*((byte *)mem + sizeof( memheader_t ) + mem->size ) = MEMHEADER_SENTINEL2;
}
static const char *Mem_CheckFilename( const char *filename )
{
static const char *dummy = "<corrupted>\0";
if( !COM_CheckString( filename ))
return dummy;
if( memchr( filename, '\0', MAX_OSPATH ) != NULL )
return filename;
return dummy;
}
static qboolean Mem_CheckAllocHeader( const char *func, const memheader_t *mem, const char *filename, int fileline )
{
const char *memfilename;
if( mem->sentinel1 != MEMHEADER_SENTINEL1 )
{
memfilename = Mem_CheckFilename( mem->filename );
Sys_Error( "%s: trashed header sentinel 1 (alloc at %s:%i, check at %s:%i)\n", func, memfilename, mem->fileline, filename, fileline );
return false;
}
if( *((byte *)mem + sizeof( memheader_t ) + mem->size ) != MEMHEADER_SENTINEL2 )
{
memfilename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "%s: trashed header sentinel 2 (alloc at %s:%i, check at %s:%i)\n", func, memfilename, mem->fileline, filename, fileline );
return false;
}
return true;
}
static qboolean Mem_CheckPool( const char *func, const mempool_t *pool, const char *filename, int fileline )
{
if( pool->sentinel1 != MEMHEADER_SENTINEL1 )
{
Sys_Error( "%s: trashed pool sentinel 1 (allocpool at %s:%i, freepool at %s:%i)\n", func, pool->filename, pool->fileline, filename, fileline );
return false;
}
if( pool->sentinel2 != MEMHEADER_SENTINEL1 )
{
Sys_Error( "%s: trashed pool sentinel 2 (allocpool at %s:%i, freepool at %s:%i)\n", func, pool->filename, pool->fileline, filename, fileline );
return false;
}
return true;
}
void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
{
memheader_t *mem;
mempool_t *pool;
if( size <= 0 ) return NULL;
if( !poolptr ) Sys_Error( "Mem_Alloc: pool == NULL (alloc at %s:%i)\n", filename, fileline );
if( size <= 0 )
return NULL;
if( !poolptr )
{
Sys_Error( "%s: pool == NULL (alloc at %s:%i)\n", __func__, filename, fileline );
return NULL;
}
mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( byte ));
if( mem == NULL )
{
Sys_Error( "%s: out of memory (alloc size %s at %s:%i)\n", __func__, Q_memprint( size ), filename, fileline );
return NULL;
}
Mem_InitAlloc( mem, size, filename, fileline );
pool = Mem_FindPool( poolptr );
Mem_PoolAdd( pool, size );
Mem_PoolLinkAlloc( pool, mem );
pool->totalsize += size;
// big allocations are not clumped
pool->realsize += sizeof( memheader_t ) + size + sizeof( size_t );
mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( size_t ));
if( mem == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline );
mem->filename = filename;
mem->fileline = fileline;
mem->size = size;
mem->pool = pool;
mem->sentinel1 = MEMHEADER_SENTINEL1;
// we have to use only a single byte for this sentinel, because it may not be aligned
// and some platforms can't use unaligned accesses
*((byte *)mem + sizeof( memheader_t ) + mem->size ) = MEMHEADER_SENTINEL2;
// append to head of list
mem->next = pool->chain;
mem->prev = NULL;
pool->chain = mem;
if( mem->next ) mem->next->prev = mem;
if( clear )
memset((void *)((byte *)mem + sizeof( memheader_t )), 0, mem->size );
return (void *)((byte *)mem + sizeof( memheader_t ));
}
static const char *Mem_CheckFilename( const char *filename )
{
static const char *dummy = "<corrupted>\0";
const char *out = filename;
int i;
if( !COM_CheckString( out ))
return dummy;
for( i = 0; i < MAX_OSPATH; i++, out++ )
{
if( *out == '\0' )
return filename; // valid name
}
return dummy;
}
static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline )
{
mempool_t *pool;
if( mem->sentinel1 != MEMHEADER_SENTINEL1 )
{
mem->filename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "Mem_Free: trashed header sentinel 1 (alloc at %s:%i, free at %s:%i)\n", mem->filename, mem->fileline, filename, fileline );
}
if( *((byte *)mem + sizeof( memheader_t ) + mem->size ) != MEMHEADER_SENTINEL2 )
{
mem->filename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "Mem_Free: trashed header sentinel 2 (alloc at %s:%i, free at %s:%i)\n", mem->filename, mem->fileline, filename, fileline );
}
if( !Mem_CheckAllocHeader( __func__, mem, filename, fileline ))
return;
pool = mem->pool;
// unlink memheader from doubly linked list
if(( mem->prev ? mem->prev->next != mem : pool->chain != mem ) || ( mem->next && mem->next->prev != mem ))
Sys_Error( "Mem_Free: not allocated or double freed (free at %s:%i)\n", filename, fileline );
{
Sys_Error( "%s: not allocated or double freed (free at %s:%i)\n", __func__, filename, fileline );
return;
}
if( mem->prev ) mem->prev->next = mem->next;
else pool->chain = mem->next;
Mem_PoolSubtract( pool, mem->size );
Mem_PoolUnlinkAlloc( pool, mem );
if( mem->next )
mem->next->prev = mem->prev;
// memheader has been unlinked, do the actual free now
pool->totalsize -= mem->size;
pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( size_t );
Q_free( mem );
}
void _Mem_Free( void *data, const char *filename, int fileline )
{
if( data == NULL ) Sys_Error( "Mem_Free: data == NULL (called at %s:%i)\n", filename, fileline );
if( data == NULL )
{
Sys_Error( "Mem_Free: data == NULL (called at %s:%i)\n", filename, fileline );
return;
}
Mem_FreeBlock((memheader_t *)((byte *)data - sizeof( memheader_t )), filename, fileline );
}
void *_Mem_Realloc( poolhandle_t poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline )
void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clear, const char *filename, int fileline )
{
memheader_t *memhdr = NULL;
char *nb;
memheader_t *mem;
uintptr_t oldmem;
mempool_t *pool;
size_t oldsize;
if( size <= 0 ) return memptr; // no need to reallocate
if( size <= 0 )
return data; // no need to reallocate
if( memptr )
if( !poolptr )
{
memhdr = (memheader_t *)((byte *)memptr - sizeof( memheader_t ));
if( size == memhdr->size ) return memptr;
Sys_Error( "Mem_Realloc: pool == NULL (alloc at %s:%i)\n", filename, fileline );
return NULL;
}
nb = _Mem_Alloc( poolptr, size, clear, filename, fileline );
if( !data )
return _Mem_Alloc( poolptr, size, clear, filename, fileline );
if( memptr ) // first allocate?
mem = (memheader_t *)((byte *)data - sizeof( memheader_t ));
if( !Mem_CheckAllocHeader( "Mem_Realloc", mem, filename, fileline ))
return NULL;
oldsize = mem->size;
if( size == oldsize )
return data;
#if XASH_CUSTOM_SWAP
{
size_t newsize = memhdr->size < size ? memhdr->size : size; // upper data can be trucnated!
memcpy( nb, memptr, newsize );
_Mem_Free( memptr, filename, fileline ); // free unused old block
char *nb = _Mem_Alloc( poolptr, size, clear, filename, fileline );
size_t newsize = mem->size < size ? mem->size : size; // upper data can be trucnated!
memcpy( nb, data, newsize );
_Mem_Free( data, filename, fileline ); // free unused old block
return nb;
}
#else // XASH_CUSTOM_SWAP
pool = Mem_FindPool( poolptr );
oldmem = (uintptr_t)mem;
mem = realloc( mem, sizeof( memheader_t ) + size + sizeof( byte ));
if( mem == NULL )
{
Sys_Error( "Mem_Realloc: out of memory (alloc size %s at %s:%i)\n", Q_memprint( size ), filename, fileline );
return NULL;
}
return (void *)nb;
// Con_Printf( S_NOTE "%s: mem %s oldmem, size before %zu now %zu (alloc at %s:%i)\n",
// __func__, (uintptr_t)mem != oldmem ? "!=" : "==", oldsize, size, filename, fileline );
Mem_InitAlloc( mem, size, filename, fileline );
if( size > oldsize )
{
Mem_PoolAdd( pool, size - oldsize );
if( clear )
memset((byte *)mem + sizeof( memheader_t ) + oldsize, 0, size - oldsize );
}
else Mem_PoolSubtract( pool, oldsize - size );
// if allocation was migrated from one pool to another
// (this is possible with original Mem_Realloc func)
if( unlikely( mem->pool != pool ))
{
Mem_PoolUnlinkAlloc( mem->pool, mem );
Mem_PoolLinkAlloc( pool, mem );
}
else if( oldmem != (uintptr_t)mem ) // just relink pointers
{
if( mem->next ) mem->next->prev = mem;
if( mem->prev ) mem->prev->next = mem;
else pool->chain = mem;
}
return (void *)((byte *)mem + sizeof( memheader_t ));
#endif // XASH_CUSTOM_SWAP
}
poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline )
@@ -254,8 +372,7 @@ void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline )
// unlink pool from chain
for( chainaddress = &poolchain; *chainaddress && *chainaddress != pool; chainaddress = &((*chainaddress)->next));
if( *chainaddress != pool ) Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline );
if( pool->sentinel1 != MEMHEADER_SENTINEL1 ) Sys_Error( "Mem_FreePool: trashed pool sentinel 1 (allocpool at %s:%i, freepool at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
if( pool->sentinel2 != MEMHEADER_SENTINEL1 ) Sys_Error( "Mem_FreePool: trashed pool sentinel 2 (allocpool at %s:%i, freepool at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
Mem_CheckPool( "Mem_FreePool", pool, filename, fileline );
*chainaddress = pool->next;
// free memory owned by the pool
@@ -272,8 +389,7 @@ void _Mem_EmptyPool( poolhandle_t poolptr, const char *filename, int fileline )
mempool_t *pool = Mem_FindPool( poolptr );
if( !poolptr ) Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline );
if( pool->sentinel1 != MEMHEADER_SENTINEL1 ) Sys_Error( "Mem_EmptyPool: trashed pool sentinel 1 (allocpool at %s:%i, emptypool at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
if( pool->sentinel2 != MEMHEADER_SENTINEL1 ) Sys_Error( "Mem_EmptyPool: trashed pool sentinel 2 (allocpool at %s:%i, emptypool at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
Mem_CheckPool( "Mem_FreePool", pool, filename, fileline );
// free memory owned by the pool
while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline );
@@ -288,14 +404,19 @@ static qboolean Mem_CheckAlloc( mempool_t *pool, void *data )
// search only one pool
target = (memheader_t *)((byte *)data - sizeof( memheader_t ));
for( header = pool->chain; header; header = header->next )
if( header == target ) return true;
{
if( header == target )
return true;
}
}
else
{
// search all pools
for( pool = poolchain; pool; pool = pool->next )
{
if( Mem_CheckAlloc( pool, data ))
return true;
}
}
return false;
}
@@ -308,49 +429,24 @@ Check pointer for memory
qboolean Mem_IsAllocatedExt( poolhandle_t poolptr, void *data )
{
mempool_t *pool = NULL;
if( poolptr ) pool = Mem_FindPool( poolptr );
if( poolptr )
pool = Mem_FindPool( poolptr );
return Mem_CheckAlloc( pool, data );
}
static void Mem_CheckHeaderSentinels( void *data, const char *filename, int fileline )
{
memheader_t *mem;
if( data == NULL )
Sys_Error( "Mem_CheckSentinels: data == NULL (sentinel check at %s:%i)\n", filename, fileline );
mem = (memheader_t *)((byte *) data - sizeof(memheader_t));
if( mem->sentinel1 != MEMHEADER_SENTINEL1 )
{
mem->filename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "Mem_CheckSentinels: trashed header sentinel 1 (block allocated at %s:%i, sentinel check at %s:%i)\n", mem->filename, mem->fileline, filename, fileline );
}
if( *((byte *)mem + sizeof(memheader_t) + mem->size) != MEMHEADER_SENTINEL2 )
{
mem->filename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "Mem_CheckSentinels: trashed header sentinel 2 (block allocated at %s:%i, sentinel check at %s:%i)\n", mem->filename, mem->fileline, filename, fileline );
}
}
void _Mem_Check( const char *filename, int fileline )
{
memheader_t *mem;
mempool_t *pool;
for( pool = poolchain; pool; pool = pool->next )
{
if( pool->sentinel1 != MEMHEADER_SENTINEL1 )
Sys_Error( "Mem_CheckSentinelsGlobal: trashed pool sentinel 1 (allocpool at %s:%i, sentinel check at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
if( pool->sentinel2 != MEMHEADER_SENTINEL1 )
Sys_Error( "Mem_CheckSentinelsGlobal: trashed pool sentinel 2 (allocpool at %s:%i, sentinel check at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
}
Mem_CheckPool( "Mem_CheckSentinels", pool, filename, fileline );
for( pool = poolchain; pool; pool = pool->next )
for( mem = pool->chain; mem; mem = mem->next )
Mem_CheckHeaderSentinels((void *)((byte *) mem + sizeof(memheader_t)), filename, fileline );
Mem_CheckAllocHeader( "Mem_CheckSentinels", mem, filename, fileline );
}
void Mem_PrintStats( void )
@@ -377,28 +473,31 @@ void Mem_PrintList( size_t minallocationsize )
Mem_Check();
Con_Printf( "memory pool list:\n"" ^3size name\n");
Con_Printf( "memory pool list:\n" );
Con_Printf( "\t^3size\t\t\t\tname\n");
for( pool = poolchain; pool; pool = pool->next )
{
long changed_size = (long)pool->totalsize - (long)pool->lastchecksize;
// poolnames can contain color symbols, make sure what color is reset
if( changed_size != 0 )
if( pool->lastchecksize != 0 && changed_size != 0 )
{
char sign = (changed_size < 0) ? '-' : '+';
Con_Printf( "%10s (%10s actual) %s (^7%c%s change)\n", Q_memprint( pool->totalsize ), Q_memprint( pool->realsize ),
pool->name, sign, Q_memprint( abs( changed_size )));
Con_Printf( "%10s (%10s real)\t%s (^7%c%s change)\n", Q_memprint( pool->totalsize ), Q_memprint( pool->realsize ),
pool->name, sign, Q_memprint( abs( changed_size )));
}
else
{
Con_Printf( "%5s (%5s actual) %s\n", Q_memprint( pool->totalsize ), Q_memprint( pool->realsize ), pool->name );
Con_Printf( "%10s (%10s real)\t%s\n", Q_memprint( pool->totalsize ), Q_memprint( pool->realsize ), pool->name );
}
pool->lastchecksize = pool->totalsize;
for( mem = pool->chain; mem; mem = mem->next )
{
if( mem->size >= minallocationsize )
Con_Printf( "%10s allocated at %s:%i\n", Q_memprint( mem->size ), mem->filename, mem->fileline );
}
}
}

View File

@@ -215,6 +215,7 @@ typedef struct ui_extendedfuncs_s {
// network address funcs
const char *(*pfnAdrToString)( const struct netadr_s a );
int (*pfnCompareAdr)( const void *a, const void *b ); // netadr_t
void *(*pfnGetNativeObject)( const char *name );
} ui_extendedfuncs_t;
// deprecated export from old engine

View File

@@ -107,7 +107,10 @@ typedef struct server_physics_api_s
int (*pfnSaveFile)( const char *filename, const void *data, int len );
const byte *(*pfnLoadImagePixels)( const char *filename, int *width, int *height );
const char* (*pfnGetModelName)( int modelindex );
const char *(*pfnGetModelName)( int modelindex );
// FWGS extension
void *(*pfnGetNativeObject)( const char *object );
} server_physics_api_t;
// physic callbacks

View File

@@ -61,23 +61,16 @@ Android_GetNativeObject
void *Android_GetNativeObject( const char *name )
{
static const char *availObjects[] = { "JNIEnv", "ActivityClass", NULL };
void *object = NULL;
if( !name )
if( !strcasecmp( name, "JNIEnv" ) )
{
object = (void *)availObjects;
}
else if( !strcasecmp( name, "JNIEnv" ) )
{
object = (void *)jni.env;
return (void *)jni.env;
}
else if( !strcasecmp( name, "ActivityClass" ) )
{
object = (void *)jni.actcls;
return (void *)jni.actcls;
}
return object;
return NULL;
}
/*

View File

@@ -52,7 +52,7 @@ static pid_t gettid( void )
static void *g_hsystemd;
static int (*g_pfn_sd_notify)( int unset_environment, const char *state );
qboolean Sys_DebuggerPresent( void )
qboolean Platform_DebuggerPresent( void )
{
char buf[4096];
ssize_t num_read;

View File

@@ -34,8 +34,8 @@ double Platform_DoubleTime( void );
void Platform_Sleep( int msec );
void Platform_ShellExecute( const char *path, const char *parms );
void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow );
qboolean Sys_DebuggerPresent( void ); // optional, see Sys_DebugBreak
void Platform_SetStatus( const char *status );
qboolean Platform_DebuggerPresent( void );
// legacy iOS port functions
#if TARGET_OS_IOS
@@ -64,6 +64,8 @@ const char *Android_LoadID( void );
void Android_SaveID( const char *id );
void Android_Init( void );
void *Android_GetNativeObject( const char *name );
int Android_GetKeyboardHeight( void );
void Android_Shutdown( void );
#endif
#if XASH_WIN32
@@ -140,6 +142,15 @@ static inline void Platform_Shutdown( void )
#endif
}
static inline qboolean Sys_DebuggerPresent( void )
{
#if XASH_LINUX || XASH_WIN32
return Platform_DebuggerPresent();
#else
return false;
#endif
}
/*
==============================================================================
@@ -256,4 +267,89 @@ void VoiceCapture_Shutdown( void );
qboolean VoiceCapture_Activate( qboolean activate );
qboolean VoiceCapture_Lock( qboolean lock );
// this allows to make break in current line, without entering libc code
// libc built with -fomit-frame-pointer may just eat stack frame (hello, glibc), making entering libc even more useless
// calling syscalls directly allows to make break like if it was asm("int $3") on x86
#if XASH_LINUX && XASH_X86
#define INLINE_RAISE(x) asm volatile( "int $3;" );
#define INLINE_NANOSLEEP1() // nothing!
#elif XASH_LINUX && XASH_ARM && !XASH_64BIT
#define INLINE_RAISE(x) do \
{ \
int raise_pid = getpid(); \
int raise_tid = gettid(); \
int raise_sig = (x); \
__asm__ volatile ( \
"mov r7,#268\n\t" \
"mov r0,%0\n\t" \
"mov r1,%1\n\t" \
"mov r2,%2\n\t" \
"svc 0\n\t" \
: \
: "r"(raise_pid), "r"(raise_tid), "r"(raise_sig) \
: "r0", "r1", "r2", "r7", "memory" \
); \
} while( 0 )
#define INLINE_NANOSLEEP1() do \
{ \
struct timespec ns_t1 = {1, 0}; \
struct timespec ns_t2 = {0, 0}; \
__asm__ volatile ( \
"mov r7,#162\n\t" \
"mov r0,%0\n\t" \
"mov r1,%1\n\t" \
"svc 0\n\t" \
: \
: "r"(&ns_t1), "r"(&ns_t2) \
: "r0", "r1", "r7", "memory" \
); \
} while( 0 )
#elif XASH_LINUX && XASH_ARM && XASH_64BIT
#define INLINE_RAISE(x) do \
{ \
int raise_pid = getpid(); \
int raise_tid = gettid(); \
int raise_sig = (x); \
__asm__ volatile ( \
"mov x8,#131\n\t" \
"mov x0,%0\n\t" \
"mov x1,%1\n\t" \
"mov x2,%2\n\t" \
"svc 0\n\t" \
: \
: "r"(raise_pid), "r"(raise_tid), "r"(raise_sig) \
: "x0", "x1", "x2", "x8", "memory", "cc" \
); \
} while( 0 )
#define INLINE_NANOSLEEP1() do \
{ \
struct timespec ns_t1 = {1, 0}; \
struct timespec ns_t2 = {0, 0}; \
__asm__ volatile ( \
"mov x8,#101\n\t" \
"mov x0,%0\n\t" \
"mov x1,%1\n\t" \
"svc 0\n\t" \
: \
: "r"(&ns_t1), "r"(&ns_t2) \
: "x0", "x1", "x8", "memory", "cc" \
); \
} while( 0 )
#elif XASH_LINUX
#ifdef __NR_tgkill
#define INLINE_RAISE(x) syscall( __NR_tgkill, getpid(), gettid(), x )
#else // __NR_tgkill
#define INLINE_RAISE(x) raise(x)
#endif // __NR_tgkill
#define INLINE_NANOSLEEP1() do \
{ \
struct timespec ns_t1 = {1, 0}; \
struct timespec ns_t2 = {0, 0}; \
nanosleep( &ns_t1, &ns_t2 ); \
} while( 0 )
#else // generic
#define INLINE_RAISE(x) raise(x)
#define INLINE_NANOSLEEP1() sleep(1)
#endif // generic
#endif // PLATFORM_H

View File

@@ -212,8 +212,8 @@ 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 )
int ret = dladdr( (void*)function, &info );
if( ret && info.dli_sname )
return COM_GetPlatformNeutralName( info.dli_sname );
}
#ifdef XASH_ALLOW_SAVERESTORE_OFFSETS

View File

@@ -374,14 +374,8 @@ static void SDLash_ActiveEvent( int gain )
{
host.status = HOST_FRAME;
if( cls.key_dest == key_game )
{
IN_ActivateMouse( );
}
if( dma.initialized && snd_mute_losefocus.value )
{
SNDDMA_Activate( true );
}
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME;
if( vid_fullscreen.value == WINDOW_MODE_FULLSCREEN )
VID_SetMode();
@@ -397,14 +391,8 @@ static void SDLash_ActiveEvent( int gain )
#endif
host.status = HOST_NOFOCUS;
if( cls.key_dest == key_game )
{
IN_DeactivateMouse();
}
if( dma.initialized && snd_mute_losefocus.value )
{
SNDDMA_Activate( false );
}
host.force_draw_version_time = host.realtime + 2.0;
VID_RestoreScreenResolution();
}
@@ -654,7 +642,9 @@ static void SDLash_EventFilter( SDL_Event *event )
SDLash_ActiveEvent( false );
break;
case SDL_WINDOWEVENT_RESIZED:
#if !XASH_MOBILE_PLATFORM
if( vid_fullscreen.value == WINDOW_MODE_WINDOWED )
#endif
{
SDL_Window *wnd = SDL_GetWindowFromID( event->window.windowID );
VID_SaveWindowSize( event->window.data1, event->window.data2,

View File

@@ -44,6 +44,18 @@ Platform_GetMousePos
void GAME_EXPORT Platform_GetMousePos( int *x, int *y )
{
SDL_GetMouseState( x, y );
if( x && window_width.value && window_width.value != refState.width )
{
float factor = refState.width / window_width.value;
*x = *x * factor;
}
if( y && window_height.value && window_height.value != refState.height )
{
float factor = refState.height / window_height.value;
*y = *y * factor;
}
}
/*

View File

@@ -576,10 +576,8 @@ static qboolean VID_SetScreenResolution( int width, int height, window_mode_t wi
SDL_DisplayMode got;
Uint32 wndFlags = 0;
#if !XASH_APPLE
if( vid_highdpi.value )
SetBits( wndFlags, SDL_WINDOW_ALLOW_HIGHDPI );
#endif
SDL_SetWindowBordered( host.hWnd, SDL_FALSE );
@@ -744,7 +742,6 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
if( !glw_state.software )
SetBits( wndFlags, SDL_WINDOW_OPENGL );
#if !XASH_MOBILE_PLATFORM
if( window_mode == WINDOW_MODE_WINDOWED )
{
SDL_Rect r;
@@ -787,10 +784,6 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
SetBits( wndFlags, SDL_WINDOW_BORDERLESS );
xpos = ypos = 0;
}
#else
SetBits( wndFlags, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_INPUT_GRABBED );
xpos = ypos = SDL_WINDOWPOS_UNDEFINED;
#endif
if( !VID_CreateWindowWithSafeGL( wndname, xpos, ypos, width, height, wndFlags ))
return false;
@@ -799,14 +792,12 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
if( FBitSet( SDL_GetWindowFlags( host.hWnd ), SDL_WINDOW_MAXIMIZED|SDL_WINDOW_FULLSCREEN_DESKTOP ) != 0 )
SDL_GetWindowSize( host.hWnd, &width, &height );
#if !XASH_MOBILE_PLATFORM
if( window_mode != WINDOW_MODE_WINDOWED )
{
if( !VID_SetScreenResolution( width, height, window_mode ))
return false;
}
else VID_RestoreScreenResolution();
#endif
VID_SetWindowIcon( host.hWnd );
SDL_ShowWindow( host.hWnd );
@@ -1175,6 +1166,14 @@ qboolean VID_SetMode( void )
#endif // SDL_VERSION_ATLEAST( 2, 0, 0 )
}
#if XASH_MOBILE_PLATFORM
if( Q_strcmp( vid_fullscreen.string, DEFAULT_FULLSCREEN ))
{
Cvar_DirectSet( &vid_fullscreen, DEFAULT_FULLSCREEN );
Con_Reportf( S_ERROR "VID_SetMode: windowed unavailable on this platform\n" );
}
#endif
if( !FBitSet( vid_fullscreen.flags, FCVAR_CHANGED ))
Cvar_DirectSet( &vid_fullscreen, DEFAULT_FULLSCREEN );
else

View File

@@ -356,13 +356,13 @@ static void ListMissingModules( dll_user_t *hInst )
byte *data;
char buf[MAX_VA_STRING];
if ( !hInst ) return;
data = FS_LoadFile( hInst->dllName, NULL, false );
if ( !data ) return;
if( !hInst || !g_fsapi.LoadFile ) return;
data = g_fsapi.LoadFile( hInst->dllName, NULL, false );
if( !data ) return;
importDesc = GetImportDescriptor( hInst->dllName, data, &peHeader );
if ( !importDesc )
if( !importDesc )
{
Mem_Free( data );
return;

View File

@@ -41,7 +41,7 @@ void Platform_Sleep( int msec )
}
#endif // XASH_TIMER == TIMER_WIN32
qboolean Sys_DebuggerPresent( void )
qboolean Platform_DebuggerPresent( void )
{
return IsDebuggerPresent();
}

View File

@@ -77,6 +77,7 @@ GNU General Public License for more details.
#define FWORLD_CUSTOM_SKYBOX BIT( 1 )
#define FWORLD_WATERALPHA BIT( 2 )
#define FWORLD_HAS_DELUXEMAP BIT( 3 )
#define FWORLD_HAS_MIRRORS BIT( 4 )
// special rendermode for screenfade modulate
// (probably will be expanded at some point)

View File

@@ -368,13 +368,12 @@ SV_NewGame_f
*/
static void SV_NewGame_f( void )
{
if( Cmd_Argc() != 1 )
{
if( Cmd_Argc() == 1 )
COM_NewGame( GI->startmap );
else if( Cmd_Argc() == 2 )
COM_NewGame( Cmd_Argv( 1 ));
else
Con_Printf( S_USAGE "newgame\n" );
return;
}
COM_NewGame( GI->startmap );
}
/*

View File

@@ -922,11 +922,11 @@ static void SV_GenerateTestPacket( void )
// testpacket already generated once, exit
// testpacket and lookup table takes ~300k of memory
// disable for low memory mode
if( svs.testpacket_buf || XASH_LOW_MEMORY >= 0 )
if( svs.testpacket_buf || XASH_LOW_MEMORY > 0 )
return;
// don't need in singleplayer with full client
if( svs.maxclients <= 1 && !Host_IsDedicated( ))
if( svs.maxclients <= 1 )
return;
file = FS_Open( "gfx.wad", "rb", false );
@@ -1045,7 +1045,9 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba
// make cvars consistant
if( coop.value ) Cvar_SetValue( "deathmatch", 0 );
current_skill = Q_rint( skill.value );
#if 0 // a1ba: don't limit the skill, it's limited in game rules anyway
current_skill = bound( 0, current_skill, 3 );
#endif
Cvar_SetValue( "skill", (float)current_skill );
// enforce hpk_maxsize

View File

@@ -802,7 +802,7 @@ static trace_t SV_PushEntity( edict_t *ent, const vec3_t lpush, const vec3_t apu
if( ent->v.movetype == MOVETYPE_FLYMISSILE )
type = MOVE_MISSILE;
else if( ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT )
else if( ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT || ent->v.solid == SOLID_GIB )
type = MOVE_NOMONSTERS; // only clip against bmodels
else type = MOVE_NORMAL;
@@ -878,7 +878,7 @@ static qboolean SV_CanBlock( edict_t *ent )
if( ent->v.mins[0] == ent->v.maxs[0] )
return false;
if( ent->v.solid == SOLID_NOT || ent->v.solid == SOLID_TRIGGER )
if( ent->v.solid == SOLID_NOT || ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_GIB )
{
// clear bounds for deadbody
ent->v.mins[0] = ent->v.mins[1] = 0;
@@ -2122,6 +2122,7 @@ static server_physics_api_t gPhysicsAPI =
COM_SaveFile,
pfnLoadImagePixels,
pfnGetModelName,
Sys_GetNativeObject
};
/*

View File

@@ -1069,7 +1069,7 @@ static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip )
return true;
}
if( touch == clip->passedict || touch->v.solid == SOLID_NOT )
if( touch == clip->passedict || touch->v.solid == SOLID_NOT || touch->v.solid == SOLID_GIB )
return true;
if( touch->v.solid == SOLID_TRIGGER )

View File

@@ -237,7 +237,8 @@ def build(bld):
app_name = 'xash3d-fwgs'
)
else:
if bld.env.DISABLE_LAUNCHER:
# always build as shared library on Android
if bld.env.DISABLE_LAUNCHER and bld.env.DEST_OS != "android":
install_path = bld.env.BINDIR
program = 'cxxprogram' if is_cxx_link else 'cprogram'
if bld.env.STATIC:

View File

@@ -15,7 +15,7 @@ GNU General Public License for more details.
#include "port.h"
#if XASH_ANDROID
#if XASH_ANDROID_ASSETS
#include <sys/types.h>
#include <sys/stat.h>
@@ -328,4 +328,4 @@ void FS_InitAndroid( void )
jni.getAssets = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getAssets", "(Z)Landroid/content/res/AssetManager;" );
}
#endif // XASH_ANDROID
#endif // XASH_ANDROID_ASSETS

View File

@@ -72,7 +72,7 @@ const fs_archive_t g_archives[] =
static const fs_archive_t g_directory_archive =
{ NULL, SEARCHPATH_PLAIN, FS_AddDir_Fullpath, false };
#ifdef XASH_ANDROID
#if XASH_ANDROID_ASSETS
static const fs_archive_t g_android_archive =
{ NULL, SEARCHPATH_ANDROID_ASSETS, FS_AddAndroidAssets_Fullpath, false };
#endif
@@ -406,7 +406,7 @@ void FS_AddGameDirectory( const char *dir, uint flags )
stringlistfreecontents( &list );
#ifdef XASH_ANDROID
#if XASH_ANDROID_ASSETS
FS_AddArchive_Fullpath( &g_android_archive, dir, flags );
#endif
@@ -632,30 +632,23 @@ static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir )
memset( GameInfo, 0, sizeof( *GameInfo ));
// filesystem info
Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
Q_strncpy( GameInfo->basedir, "valve", sizeof( GameInfo->basedir ));
GameInfo->falldir[0] = 0;
Q_strncpy( GameInfo->startmap, "c0a0", sizeof( GameInfo->startmap ));
Q_strncpy( GameInfo->trainmap, "t0a0", sizeof( GameInfo->trainmap ));
Q_strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
GameInfo->version = 1.0f;
// .dll pathes
Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
Q_strncpy( GameInfo->basedir, fs_basedir, sizeof( GameInfo->basedir ));
Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
Q_strncpy( GameInfo->startmap, "newmap", sizeof( GameInfo->startmap ));
Q_strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
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 ));
// .ico path
Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath ));
Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
GameInfo->max_edicts = DEFAULT_MAX_EDICTS; // default value if not specified
GameInfo->max_tents = 500;
GameInfo->max_beams = 128;
GameInfo->max_particles = 4096;
GameInfo->version = 1.0f;
}
static void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qboolean isGameInfo )
@@ -1468,7 +1461,7 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
FS_InitMemory();
#ifdef XASH_ANDROID
#if XASH_ANDROID_ASSETS
FS_InitAndroid();
#endif

View File

@@ -383,7 +383,9 @@ static gl2wrap_prog_t *GL2_GetProg( const GLuint flags )
{
if( gl2wrap_config.vao_mandatory || gl2wrap_config.incremental )
{
for( int j = 0; j < gl2wrap_config.cycle_buffers; j++ )
int j;
for( j = 0; j < gl2wrap_config.cycle_buffers; j++ )
{
pglBindVertexArray( prog->vao_begin[j] );
pglEnableVertexAttribArrayARB( prog->attridx[i] );
@@ -472,12 +474,14 @@ static void GL2_InitTriQuads( void )
static void GL2_InitIncrementalBuffer( int i, GLuint size )
{
int j;
gl2wrap.attrbufobj[i] = Mem_Calloc( r_temppool, gl2wrap_config.cycle_buffers * sizeof( GLuint ));
if( gl2wrap_config.buf_storage )
gl2wrap.mappings[i] = Mem_Calloc( r_temppool, gl2wrap_config.cycle_buffers * sizeof( void * ));
pglGenBuffersARB( gl2wrap_config.cycle_buffers, gl2wrap.attrbufobj[i] );
for( int j = 0; j < gl2wrap_config.cycle_buffers; j++ )
for( j = 0; j < gl2wrap_config.cycle_buffers; j++ )
{
rpglBindBufferARB( GL_ARRAY_BUFFER_ARB, gl2wrap.attrbufobj[i][j] );
if( gl2wrap_config.buf_storage )
@@ -628,7 +632,9 @@ int GL2_ShimInit( void )
pglGenBuffersARB( gl2wrap_config.cycle_buffers, gl2wrap.attrbufobj[i] );
if( gl2wrap_config.supports_mapbuffer )
{
for( int j = 0; j < gl2wrap_config.cycle_buffers; j++ )
int j;
for( j = 0; j < gl2wrap_config.cycle_buffers; j++ )
{
rpglBindBufferARB( GL_ARRAY_BUFFER_ARB, gl2wrap.attrbufobj[i][j] );
pglBufferDataARB( GL_ARRAY_BUFFER_ARB, MAX_BEGINEND_VERTS, NULL, GL_STREAM_DRAW_ARB );
@@ -1550,6 +1556,7 @@ static void GL2_SetupArrays( GLuint start, GLuint end )
{
gl2wrap_prog_t *prog;
unsigned int flags = gl2wrap_arrays.flags;
int i;
if( !flags )
return; // Legacy pointers not used
@@ -1573,7 +1580,7 @@ static void GL2_SetupArrays( GLuint start, GLuint end )
pglBindVertexArray( gl2wrap_arrays.vao_dynamic );
}
for( int i = 0; i < GL2_ATTR_MAX; i++ )
for( i = 0; i < GL2_ATTR_MAX; i++ )
{
if( prog->attridx[i] < 0 )
continue;

View File

@@ -111,6 +111,9 @@ void GL_BackendEndFrame( void )
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;
case 6:
Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i mirrors\n", r_stats.c_mirror_passes );
break;
}
memset( &r_stats, 0, sizeof( r_stats ));

View File

@@ -119,6 +119,10 @@ qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly )
vec3_t mins, maxs;
int i;
// support for custom mirror management
if( RI.currentbeam && R_CullEntityInMirror( RI.currentbeam ))
return true;
for( i = 0; i < 3; i++ )
{
if( start[i] < end[i] )

View File

@@ -35,6 +35,31 @@ qboolean R_CullBox( const vec3_t mins, const vec3_t maxs )
return GL_FrustumCullBox( &RI.frustum, mins, maxs, 0 );
}
/*
=================
R_CullEntityInMirror
decide whether entity should be reflected in mirror
=================
*/
qboolean R_CullEntityInMirror( cl_entity_t *e )
{
if( FBitSet( RI.params, RP_MIRRORVIEW ))
{
// cull this entity out of mirrors but not normal views
if( FBitSet( e->curstate.effects, EF_NOREFLECT ))
return true;
}
else
{
// cull this entity out of normal views but not mirrors
if( FBitSet( e->curstate.effects, EF_REFLECTONLY ))
return true;
}
return false;
}
/*
=============
R_CullModel
@@ -53,6 +78,9 @@ int R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax )
return 1;
}
if( R_CullEntityInMirror( e ))
return 1;
if( R_CullBox( absmin, absmax ))
return 1;
@@ -98,7 +126,7 @@ int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags )
}
else dist = PlaneDiff( tr.modelorg, surf->plane );
if( glState.faceCull == GL_FRONT )
if( glState.faceCull == GL_FRONT || FBitSet( RI.params, RP_MIRRORVIEW ))
{
if( FBitSet( surf->flags, SURF_PLANEBACK ))
{

View File

@@ -28,6 +28,10 @@ GNU General Public License for more details.
#define XASH_GL_STATIC
#define REF_GL_KEEP_MANGLED_FUNCTIONS
#elif defined XASH_GLES3COMPAT
#ifdef SOFTFP_LINK
#undef APIENTRY
#define APIENTRY __attribute__((pcs("aapcs")))
#endif
#define XASH_GLES
#endif

View File

@@ -631,9 +631,12 @@ static void GL_SetTextureTarget( gl_texture_t *tex, rgbdata_t *pic )
pic->numMips = Q_max( 1, pic->numMips );
// trying to determine texture type
#ifndef XASH_GLES
if( pic->width > 1 && pic->height <= 1 )
tex->target = GL_TEXTURE_1D;
else if( FBitSet( pic->flags, IMAGE_CUBEMAP ))
else
#endif // just skip first condition
if( FBitSet( pic->flags, IMAGE_CUBEMAP ))
tex->target = GL_TEXTURE_CUBE_MAP_ARB;
else if( FBitSet( pic->flags, IMAGE_MULTILAYER ) && pic->depth >= 1 )
tex->target = GL_TEXTURE_2D_ARRAY_EXT;

View File

@@ -74,6 +74,8 @@ extern poolhandle_t r_temppool;
#define SUBDIVIDE_SIZE 64
#define MAX_DECAL_SURFS 4096
#define MAX_DRAW_STACK 2 // normal view and menu view
#define MAX_MIRRORS 32 // per frame
#define MAX_MIRROR_ENTITIES MAX_VISIBLE_PACKET
#define SHADEDOT_QUANT 16 // precalculated dot products for quantized angles
#define SHADE_LAMBERT 1.4953241
@@ -84,8 +86,9 @@ extern poolhandle_t r_temppool;
#define RP_ENVVIEW BIT( 0 ) // used for cubemapshot
#define RP_OLDVIEWLEAF BIT( 1 )
#define RP_CLIPPLANE BIT( 2 )
#define RP_MIRRORVIEW BIT( 3 ) // lock pvs at vieworg
#define RP_NONVIEWERREF (RP_ENVVIEW)
#define RP_NONVIEWERREF (RP_ENVVIEW|RP_MIRRORVIEW)
#define R_ModelOpaque( rm ) ( rm == kRenderNormal )
#define R_StaticEntity( ent ) ( VectorIsNull( ent->origin ) && VectorIsNull( ent->angles ))
#define RP_LOCALCLIENT( e ) ((e) != NULL && (e)->index == ( gp_cl->playernum + 1 ) && e->player )
@@ -136,6 +139,13 @@ typedef struct gltexture_s
struct gltexture_s *nextHash;
} gl_texture_t;
// mirror entity
typedef struct
{
cl_entity_t *ent;
mextrasurf_t *chain;
} gl_entity_t;
typedef struct
{
int params; // rendering parameters
@@ -200,9 +210,11 @@ 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];
gl_entity_t mirror_entities[MAX_MIRROR_ENTITIES]; // an entities that has mirror
uint num_solid_entities;
uint num_trans_entities;
uint num_beam_entities;
uint num_mirror_entities;
} draw_list_t;
typedef struct
@@ -218,6 +230,8 @@ typedef struct
int dlightTexture; // custom dlight texture
int skyboxTextures[SKYBOX_MAX_SIDES]; // skybox sides
int cinTexture; // cinematic texture
int mirrorTextures[MAX_MIRRORS];
int num_mirrors_used;
int skytexturenum; // this not a gl_texturenum!
int skyboxbasenum; // start with 5800
@@ -280,6 +294,8 @@ typedef struct
uint c_client_ents; // entities that moved to client
double t_world_node;
double t_world_draw;
uint c_mirror_passes;
} ref_speeds_t;
extern ref_speeds_t r_stats;
@@ -324,6 +340,7 @@ qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly );
//
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_CullEntityInMirror( cl_entity_t *e );
int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags );
//
@@ -496,7 +513,7 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse );
void R_InitRipples( void );
void R_ResetRipples( void );
void R_AnimateRipples( void );
void R_UploadRipples( texture_t *image );
void R_UploadRipples( texture_t *image, qboolean is_mirror );
//
// gl_vgui.c
@@ -515,7 +532,13 @@ 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"
//
// gl_mirror.c
//
void R_BeginDrawMirror( msurface_t *fa );
void R_EndDrawMirror( void );
void R_DrawMirrors( void );
void R_FindMirrors( void );
//
// renderer exports
@@ -541,7 +564,6 @@ void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, c
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 );
@@ -767,6 +789,7 @@ extern convar_t gl_test; // cvar to testify new effects
extern convar_t gl_msaa;
extern convar_t gl_stencilbits;
extern convar_t gl_overbright;
extern convar_t gl_allow_mirrors;
extern convar_t r_lighting_extended;
extern convar_t r_lighting_ambient;
@@ -780,11 +803,15 @@ extern convar_t r_traceglow;
extern convar_t r_vbo;
extern convar_t r_vbo_dlightmode;
extern convar_t r_vbo_detail;
extern convar_t r_vbo_overbrightmode;
extern convar_t r_studio_sort_textures;
extern convar_t r_studio_drawelements;
extern convar_t r_ripple;
extern convar_t r_ripple_updatetime;
extern convar_t r_ripple_spawntime;
extern convar_t r_water_warp;
extern convar_t r_water_warp_x;
extern convar_t r_water_warp_y;
//
// engine shared convars

615
ref/gl/gl_mirror.c Normal file
View File

@@ -0,0 +1,615 @@
/*
gl_mirror.c - draw reflected surfaces
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 "gl_local.h"
#include "mod_local.h"
#include "xash3d_mathlib.h"
static void GL_LoadTexMatrix( const matrix4x4 m )
{
pglMatrixMode( GL_TEXTURE );
GL_LoadMatrix( m );
glState.texIdentityMatrix[glState.activeTMU] = false;
}
static void Matrix4x4_CreateScale( matrix4x4 out, float x )
{
out[0][0] = x;
out[0][1] = out[0][2] = out[0][3] = 0.0f;
out[1][0] = out[1][2] = out[1][3] = 0.0f;
out[1][1] = x;
out[2][0] = out[2][1] = out[2][3] = 0.0f;
out[2][2] = x;
out[3][0] = out[3][1] = out[3][2] = 0.0f;
out[3][3] = 1.0f;
}
static void Matrix4x4_ConcatScale( matrix4x4 out, float x )
{
matrix4x4 base, temp;
Matrix4x4_Copy( base, out );
Matrix4x4_CreateScale( temp, x );
Matrix4x4_Concat( out, base, temp );
}
/*
================
R_BeginDrawMirror
Setup texture matrix for mirror texture
================
*/
void R_BeginDrawMirror( msurface_t *fa )
{
matrix4x4 m1, m2, matrix;
GLfloat genVector[4][4];
int i;
Matrix4x4_Copy( matrix, fa->info->mirrormatrix );
Matrix4x4_LoadIdentity( m1 );
Matrix4x4_ConcatScale( m1, 0.5f );
Matrix4x4_Concat( m2, m1, matrix );
Matrix4x4_LoadIdentity( m1 );
Matrix4x4_ConcatTranslate( m1, 0.5f, 0.5f, 0.5f );
Matrix4x4_Concat( matrix, m1, m2 );
for( i = 0; i < 4; i++ )
{
genVector[0][i] = i == 0 ? 1 : 0;
genVector[1][i] = i == 1 ? 1 : 0;
genVector[2][i] = i == 2 ? 1 : 0;
genVector[3][i] = i == 3 ? 1 : 0;
}
GL_TexGen( GL_S, GL_OBJECT_LINEAR );
GL_TexGen( GL_T, GL_OBJECT_LINEAR );
GL_TexGen( GL_R, GL_OBJECT_LINEAR );
GL_TexGen( GL_Q, GL_OBJECT_LINEAR );
pglTexGenfv( GL_S, GL_OBJECT_PLANE, genVector[0] );
pglTexGenfv( GL_T, GL_OBJECT_PLANE, genVector[1] );
pglTexGenfv( GL_R, GL_OBJECT_PLANE, genVector[2] );
pglTexGenfv( GL_Q, GL_OBJECT_PLANE, genVector[3] );
GL_LoadTexMatrix( matrix );
}
/*
================
R_EndDrawMirror
Restore identity texmatrix
================
*/
void R_EndDrawMirror( void )
{
GL_CleanUpTextureUnits( 0 );
pglMatrixMode( GL_MODELVIEW );
}
/*
=============================================================
MIRROR RENDERING
=============================================================
*/
/*
================
R_PlaneForMirror
Get transformed mirrorplane and entity matrix
================
*/
static void R_PlaneForMirror( msurface_t *surf, mplane_t *out, matrix4x4 m )
{
cl_entity_t *ent;
ent = RI.currententity;
// setup mirror plane
*out = *surf->plane;
if( surf->flags & SURF_PLANEBACK )
{
VectorNegate( out->normal, out->normal );
out->dist = -out->dist;
}
if( !VectorIsNull( ent->origin ) || !VectorIsNull( ent->angles ))
{
mplane_t tmp;
if( !VectorIsNull( ent->angles )) Matrix4x4_CreateFromEntity( m, ent->angles, ent->origin, 1.0f );
else Matrix4x4_CreateFromEntity( m, vec3_origin, ent->origin, 1.0f );
tmp = *out;
// transform mirror plane by entity matrix
Matrix4x4_TransformPositivePlane( m, tmp.normal, tmp.dist, out->normal, &out->dist );
}
else Matrix4x4_LoadIdentity( m );
}
/*
================
R_AllocateMirrorTexture
Allocate the screen texture and make copy
================
*/
static int R_AllocateMirrorTexture( void )
{
rgbdata_t r_screen;
int i, texture;
char txName[16];
i = tr.num_mirrors_used;
if( i >= MAX_MIRRORS )
{
gEngfuncs.Con_Printf( S_ERROR "%s: mirror textures limit exceeded!\n", __func__ );
return 0; // disable
}
texture = tr.mirrorTextures[i];
tr.num_mirrors_used++;
if( !texture )
{
// not initialized ?
memset( &r_screen, 0, sizeof( r_screen ));
Q_snprintf( txName, sizeof( txName ), "*screen%i", i );
r_screen.width = RI.viewport[2];
r_screen.height = RI.viewport[3];
r_screen.type = PF_RGBA_32;
r_screen.size = r_screen.width * r_screen.height * 4;
r_screen.flags = IMAGE_HAS_COLOR;
r_screen.buffer = NULL; // create empty texture for now
tr.mirrorTextures[i] = GL_LoadTextureInternal( txName, &r_screen, TF_IMAGE );
texture = tr.mirrorTextures[i];
}
GL_Bind( XASH_TEXTURE0, texture );
pglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3], 0 );
return texture;
}
/*
================
R_DrawMirrors
Draw all viewpasess from mirror position
Mirror textures will be drawn in normal pass
================
*/
void R_DrawMirrors( void )
{
ref_instance_t oldRI;
mplane_t plane;
msurface_t *surf, *surf2;
int i, oldframecount;
mextrasurf_t *es, *tmp, *mirrorchain;
vec3_t forward, right, up;
vec3_t origin, angles;
matrix4x4 mirrormatrix;
cl_entity_t *e;
model_t *m;
float d;
if( !tr.draw_list->num_mirror_entities ) return; // mo mirrors for this frame
oldRI = RI; // make refinst backup
oldframecount = tr.framecount;
for( i = 0; i < tr.draw_list->num_mirror_entities; i++ )
{
mirrorchain = tr.draw_list->mirror_entities[i].chain;
for( es = mirrorchain; es != NULL; es = es->mirrorchain )
{
RI.currententity = e = tr.draw_list->mirror_entities[i].ent;
RI.currentmodel = m = RI.currententity->model;
surf = es->surf;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currentmodel != NULL );
// NOTE: copy mirrortexture and mirrormatrix from another surfaces
// from this entity\world that has same planes and reduce number of viewpasses
// make sure what we have one pass at least
if( es != mirrorchain )
{
for( tmp = mirrorchain; tmp != es; tmp = tmp->mirrorchain )
{
surf2 = tmp->surf;
if( !tmp->mirrortexturenum )
continue; // not filled?
if( surf->plane->dist != surf2->plane->dist )
continue;
if( !VectorCompare( surf->plane->normal, surf2->plane->normal ))
continue;
// found surface with same plane!
break;
}
if( tmp != es && tmp && tmp->mirrortexturenum )
{
// just copy reflection texture from surface with same plane
Matrix4x4_Copy( es->mirrormatrix, tmp->mirrormatrix );
es->mirrortexturenum = tmp->mirrortexturenum;
continue; // pass skiped
}
}
R_PlaneForMirror( surf, &plane, mirrormatrix );
d = -2.0f * ( DotProduct( RI.vieworg, plane.normal ) - plane.dist );
VectorMA( RI.vieworg, d, plane.normal, origin );
d = -2.0f * DotProduct( RI.vforward, plane.normal );
VectorMA( RI.vforward, d, plane.normal, forward );
VectorNormalize( forward );
d = -2.0f * DotProduct( RI.vright, plane.normal );
VectorMA( RI.vright, d, plane.normal, right );
VectorNormalize( right );
d = -2.0f * DotProduct( RI.vup, plane.normal );
VectorMA( RI.vup, d, plane.normal, up );
VectorNormalize( up );
VectorsAngles( forward, right, up, angles );
angles[ROLL] = -angles[ROLL];
RI.params = RP_MIRRORVIEW|RP_CLIPPLANE|RP_OLDVIEWLEAF;
RI.clipPlane = plane;
GL_FrustumSetPlane( &RI.frustum, FRUSTUM_NEAR, plane.normal, plane.dist );
RI.viewangles[0] = anglemod( angles[0] );
RI.viewangles[1] = anglemod( angles[1] );
RI.viewangles[2] = anglemod( angles[2] );
VectorCopy( origin, RI.vieworg );
VectorCopy( origin, RI.cullorigin );
// put pvsorigin before the mirror plane to avoid get full visibility on world mirrors
if( RI.currententity == tr.entities )
{
VectorMA( es->origin, 1.0f, plane.normal, origin );
}
else
{
Matrix4x4_VectorTransform( mirrormatrix, es->origin, origin );
VectorMA( origin, 1.0f, plane.normal, origin );
}
VectorCopy( origin, RI.pvsorigin );
if( GL_Support( GL_ARB_TEXTURE_NPOT_EXT ))
{
// allow screen size
RI.viewport[2] = bound( 96, RI.viewport[2], 1024 );
RI.viewport[3] = bound( 72, RI.viewport[3], 768 );
}
else
{
RI.viewport[2] = NearestPOW( RI.viewport[2], true );
RI.viewport[3] = NearestPOW( RI.viewport[3], true );
RI.viewport[2] = bound( 128, RI.viewport[2], 1024 );
RI.viewport[3] = bound( 64, RI.viewport[3], 512 );
}
R_RenderScene();
r_stats.c_mirror_passes++;
es->mirrortexturenum = R_AllocateMirrorTexture();
// create personal projection matrix for mirror
if( VectorIsNull( e->origin ) && VectorIsNull( e->angles ))
{
Matrix4x4_Copy( es->mirrormatrix, RI.worldviewProjectionMatrix );
}
else
{
Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, mirrormatrix );
Matrix4x4_Concat( es->mirrormatrix, RI.projectionMatrix, RI.modelviewMatrix );
}
RI = oldRI; // restore ref instance
}
// clear chain for this entity
for( es = mirrorchain; es != NULL; )
{
tmp = es->mirrorchain;
es->mirrorchain = NULL;
es = tmp;
}
tr.draw_list->mirror_entities[i].chain = NULL; // done
tr.draw_list->mirror_entities[i].ent = NULL;
}
RI.viewleaf = NULL; // force markleafs next frame
tr.draw_list->num_mirror_entities = 0;
tr.num_mirrors_used = 0;
}
/*
================
R_RecursiveMirrorNode
================
*/
static void R_RecursiveMirrorNode( mnode_t *node, uint clipflags )
{
int i, clipped;
msurface_t *surf, **mark;
mleaf_t *pleaf;
int c, side;
float dot;
if( node->contents == CONTENTS_SOLID )
return; // hit a solid leaf
if( node->visframe != tr.visframecount )
return;
if( clipflags )
{
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 );
}
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) ? 0 : 1;
// recurse down the children, front side first
R_RecursiveMirrorNode( node->children[side], clipflags );
// draw stuff
for( c = node->numsurfaces, surf = WORLDMODEL->surfaces + node->firstsurface; c; c--, surf++ )
{
if( !FBitSet( surf->flags, SURF_REFLECT ))
continue;
if( R_CullSurface( surf, &RI.frustum, clipflags ))
continue;
surf->info->mirrorchain = tr.draw_list->mirror_entities[0].chain;
tr.draw_list->mirror_entities[0].chain = surf->info;
}
// recurse down the back side
R_RecursiveMirrorNode( node->children[!side], clipflags );
}
/*
=================
R_FindBmodelMirrors
Check all bmodel surfaces and make personal mirror chain
=================
*/
static void R_FindBmodelMirrors( cl_entity_t *e, qboolean static_entity )
{
vec3_t mins, maxs;
msurface_t *psurf;
model_t *clmodel;
qboolean rotated;
gl_frustum_t *frustum = NULL;
int i;
draw_list_t *const draw_list = tr.draw_list;
if( draw_list->num_mirror_entities >= MAX_MIRROR_ENTITIES )
return;
clmodel = e->model;
// don't draw any water reflections if we underwater
if( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 && FBitSet( clmodel->flags, MODEL_LIQUID ))
return;
if( static_entity )
{
Matrix4x4_LoadIdentity( RI.objectMatrix );
if( R_CullBox( clmodel->mins, clmodel->maxs ))
return;
VectorCopy( RI.cullorigin, tr.modelorg );
frustum = &RI.frustum;
}
else
{
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;
if( !VectorIsNull( e->origin ) || !VectorIsNull( e->angles ))
{
if( rotated ) Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, 1.0f );
else Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, 1.0f );
}
else Matrix4x4_LoadIdentity( RI.objectMatrix );
e->visframe = tr.framecount; // visible
if( rotated ) Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg );
else VectorSubtract( RI.cullorigin, e->origin, tr.modelorg );
}
psurf = &clmodel->surfaces[clmodel->firstmodelsurface];
for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ )
{
if( !FBitSet( psurf->flags, SURF_REFLECT ))
continue;
if( R_CullSurface( psurf, frustum, 0 ))
continue;
psurf->info->mirrorchain = draw_list->mirror_entities[draw_list->num_mirror_entities].chain;
draw_list->mirror_entities[draw_list->num_mirror_entities].chain = psurf->info;
}
// store new mirror entity
if( !static_entity && draw_list->mirror_entities[draw_list->num_mirror_entities].chain != NULL )
{
draw_list->mirror_entities[draw_list->num_mirror_entities].ent = RI.currententity;
draw_list->num_mirror_entities++;
}
}
/*
=================
R_CheckEntitiesOnList
Check all bmodels for mirror surfaces
=================
*/
static void R_CheckEntitiesOnList( void )
{
int i;
// world has mirror surfaces
if( tr.draw_list->mirror_entities[0].chain != NULL )
{
tr.draw_list->mirror_entities[0].ent = tr.entities;
tr.draw_list->num_mirror_entities++;
}
// check solid entities
for( i = 0; i < tr.draw_list->num_solid_entities; i++ )
{
RI.currententity = tr.draw_list->solid_entities[i];
RI.currentmodel = RI.currententity->model;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currententity->model != NULL );
switch( RI.currentmodel->type )
{
case mod_brush:
R_FindBmodelMirrors( RI.currententity, false );
break;
}
}
// check translucent entities
for( i = 0; i < tr.draw_list->num_trans_entities; i++ )
{
RI.currententity = tr.draw_list->trans_entities[i];
RI.currentmodel = RI.currententity->model;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currententity->model != NULL );
switch( RI.currentmodel->type )
{
case mod_brush:
R_FindBmodelMirrors( RI.currententity, false );
break;
}
}
}
/*
================
R_FindMirrors
Build mirror chains for this frame
================
*/
void R_FindMirrors( void )
{
if( !FBitSet( tr.world->flags, FWORLD_HAS_MIRRORS ) || RI.drawOrtho || !RI.drawWorld || RI.onlyClientDraw || !WORLDMODEL )
return;
// NOTE: we already has initial params at this point like vieworg, viewangles
// all other will be sets into R_SetupFrustum
R_FindViewLeaf ();
// player is outside world. Don't update mirrors for speedup reasons
if(( RI.viewleaf - WORLDMODEL->leafs - 1 ) == -1 )
return;
R_SetupFrustum ();
R_MarkLeaves ();
VectorCopy( RI.cullorigin, tr.modelorg );
RI.currententity = tr.entities;
RI.currentmodel = RI.currententity->model;
R_RecursiveMirrorNode( WORLDMODEL->nodes, RI.frustum.clipFlags );
R_CheckEntitiesOnList();
}

View File

@@ -20,6 +20,7 @@ CVAR_DEFINE_AUTO( gl_test, "0", 0, "engine developer cvar for quick testing new
CVAR_DEFINE_AUTO( gl_msaa, "1", FCVAR_GLCONFIG, "enable or disable multisample anti-aliasing" );
CVAR_DEFINE_AUTO( gl_stencilbits, "8", FCVAR_GLCONFIG|FCVAR_READ_ONLY, "pixelformat stencil bits (0 - auto)" );
CVAR_DEFINE_AUTO( gl_overbright, "1", FCVAR_GLCONFIG, "overbrights" );
CVAR_DEFINE_AUTO( gl_allow_mirrors, "1", FCVAR_GLCONFIG, "allow to draw mirror surfaces" );
CVAR_DEFINE_AUTO( r_lighting_extended, "1", FCVAR_GLCONFIG, "allow to get lighting from world and bmodels" );
CVAR_DEFINE_AUTO( r_lighting_ambient, "0.3", FCVAR_GLCONFIG, "map ambient lighting scale" );
CVAR_DEFINE_AUTO( r_detailtextures, "1", FCVAR_ARCHIVE, "enable detail textures support" );
@@ -32,9 +33,13 @@ CVAR_DEFINE_AUTO( gl_round_down, "2", FCVAR_GLCONFIG|FCVAR_READ_ONLY, "round tex
CVAR_DEFINE( r_vbo, "gl_vbo", "0", FCVAR_ARCHIVE, "draw world using VBO (known to be glitchy)" );
CVAR_DEFINE( r_vbo_detail, "gl_vbo_detail", "0", FCVAR_ARCHIVE, "detail vbo mode (0: disable, 1: multipass, 2: singlepass, broken decal dlights)" );
CVAR_DEFINE( r_vbo_dlightmode, "gl_vbo_dlightmode", "1", FCVAR_ARCHIVE, "vbo dlight rendering mode (0-1)" );
CVAR_DEFINE( r_vbo_overbrightmode, "gl_vbo_overbrightmode", "0", FCVAR_ARCHIVE, "vbo overbright rendering mode (0-1)" );
CVAR_DEFINE_AUTO( r_ripple, "0", FCVAR_GLCONFIG, "enable software-like water texture ripple simulation" );
CVAR_DEFINE_AUTO( r_ripple_updatetime, "0.05", FCVAR_GLCONFIG, "how fast ripple simulation is" );
CVAR_DEFINE_AUTO( r_ripple_spawntime, "0.1", FCVAR_GLCONFIG, "how fast new ripples spawn" );
CVAR_DEFINE_AUTO( r_water_warp, "1", FCVAR_GLCONFIG, "set water warp style (0: disable, 1: sine waves, 2: constant scroll)" );
CVAR_DEFINE_AUTO( r_water_warp_x, "6", FCVAR_GLCONFIG, "water warp scroll x axis speed" );
CVAR_DEFINE_AUTO( r_water_warp_y, "6", FCVAR_GLCONFIG, "water warp scroll y axis speed" );
DEFINE_ENGINE_SHARED_CVAR_LIST()
@@ -1190,6 +1195,9 @@ static void GL_InitCommands( void )
gEngfuncs.Cvar_RegisterVariable( &r_ripple );
gEngfuncs.Cvar_RegisterVariable( &r_ripple_updatetime );
gEngfuncs.Cvar_RegisterVariable( &r_ripple_spawntime );
gEngfuncs.Cvar_RegisterVariable( &r_water_warp );
gEngfuncs.Cvar_RegisterVariable( &r_water_warp_x );
gEngfuncs.Cvar_RegisterVariable( &r_water_warp_y );
gEngfuncs.Cvar_RegisterVariable( &gl_extensions );
gEngfuncs.Cvar_RegisterVariable( &gl_texture_nearest );
@@ -1206,6 +1214,7 @@ static void GL_InitCommands( void )
gEngfuncs.Cvar_RegisterVariable( &gl_stencilbits );
gEngfuncs.Cvar_RegisterVariable( &gl_round_down );
gEngfuncs.Cvar_RegisterVariable( &gl_overbright );
gEngfuncs.Cvar_RegisterVariable( &gl_allow_mirrors );
// these cvar not used by engine but some mods requires this
gEngfuncs.Cvar_RegisterVariable( &gl_polyoffset );
@@ -1248,6 +1257,7 @@ static void R_CheckVBO( void )
gEngfuncs.Cvar_RegisterVariable( &r_vbo );
gEngfuncs.Cvar_RegisterVariable( &r_vbo_dlightmode );
gEngfuncs.Cvar_RegisterVariable( &r_vbo_overbrightmode );
gEngfuncs.Cvar_RegisterVariable( &r_vbo_detail );
}

View File

@@ -218,6 +218,7 @@ 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;
tr.draw_list->num_mirror_entities = 0;
// clear the scene befor start new frame
if( gEngfuncs.drawFuncs->R_ClearScene != NULL )
@@ -1011,10 +1012,11 @@ static void R_CheckGamma( void )
ClearBits( gl_overbright.flags, FCVAR_CHANGED );
}
if( gl_overbright.value && FBitSet( r_vbo.flags, FCVAR_CHANGED ))
if( gl_overbright.value && ( FBitSet( r_vbo.flags, FCVAR_CHANGED ) || FBitSet( r_vbo_overbrightmode.flags, FCVAR_CHANGED ) ) )
{
rebuild = true;
ClearBits( r_vbo.flags, FCVAR_CHANGED );
ClearBits( r_vbo_overbrightmode.flags, FCVAR_CHANGED );
}
if( rebuild )
@@ -1126,6 +1128,14 @@ void R_RenderFrame( const ref_viewpass_t *rvp )
R_RunViewmodelEvents();
tr.realframecount++; // right called after viewmodel events
if( gl_allow_mirrors.value )
{
// render mirrors
R_FindMirrors();
R_DrawMirrors();
}
R_RenderScene();
return;

View File

@@ -718,7 +718,7 @@ static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride, qboolean
tmax = ( info->lightextents[1] / sample_size ) + 1;
size = smax * tmax;
if( gl_overbright.value )
lightscale = r_vbo.value ? 171 : 256;
lightscale = (r_vbo.value && !r_vbo_overbrightmode.value) ? 171 : 256;
else lightscale = ( pow( 2.0f, 1.0f / v_lightgamma->value ) * 256 ) + 0.5;
lm = surf->samples;
@@ -927,7 +927,8 @@ static void R_BlendLightmaps( void )
if( gl_overbright.value )
{
pglBlendFunc( GL_DST_COLOR, GL_SRC_COLOR );
pglColor4f( 128.0f / 192.0f, 128.0f / 192.0f, 128.0f / 192.0f, 1.0f );
if(!( r_vbo.value && !r_vbo_overbrightmode.value ))
pglColor4f( 128.0f / 192.0f, 128.0f / 192.0f, 128.0f / 192.0f, 1.0f );
}
else
{
@@ -1141,7 +1142,7 @@ R_RenderBrushPoly
*/
static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
{
qboolean is_dynamic = false;
qboolean is_dynamic = false, is_mirror = false;
int maps;
texture_t *t;
@@ -1152,15 +1153,46 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
t = R_TextureAnimation( fa );
// set mirror texture
if( RP_NORMALPASS() && FBitSet( fa->flags, SURF_REFLECT ))
{
if( fa->info->mirrortexturenum )
{
GL_Bind( XASH_TEXTURE0, fa->info->mirrortexturenum );
is_mirror = true;
// reset just in case if mirror failed to render
fa->info->mirrortexturenum = 0;
// blend-in water
if( FBitSet( fa->flags, SURF_DRAWTURB ))
{
R_BeginDrawMirror( fa );
R_UploadRipples( t, true );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
// warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
R_EndDrawMirror();
return;
}
}
}
if( FBitSet( fa->flags, SURF_DRAWTURB ))
{
R_UploadRipples( t );
R_UploadRipples( t, false );
// warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
return;
}
else GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
else if( !is_mirror ) // already set up mirror texture
{
GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
}
if( t->fb_texturenum )
{
@@ -1198,7 +1230,9 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
}
}
if( is_mirror ) R_BeginDrawMirror( fa );
DrawGLPoly( fa->polys, 0.0f, 0.0f );
if( is_mirror ) R_EndDrawMirror();
if( RI.currententity->curstate.rendermode == kRenderNormal )
{
@@ -1212,6 +1246,10 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
DrawSurfaceDecals( fa, true, (cull_type == CULL_BACKSIDE));
}
// NOTE: draw mirror through in mirror show dummy lightmapped texture
if( RP_NORMALPASS() && FBitSet( fa->flags, SURF_REFLECT ))
return; // no lightmaps for mirror
if( FBitSet( fa->flags, SURF_DRAWTILED ))
return; // no lightmaps anyway
@@ -1431,7 +1469,7 @@ void R_DrawWaterSurfaces( void )
continue;
// set modulate mode explicitly
R_UploadRipples( t );
R_UploadRipples( t, false );
for( ; s; s = s->texturechain )
EmitWaterPolys( s, false );
@@ -1550,6 +1588,9 @@ void R_DrawBrushModel( cl_entity_t *e )
if( clmodel->surfaces != WORLDMODEL->surfaces )
allow_vbo = false;
if( R_CullEntityInMirror( e ))
return;
if( !VectorIsNull( e->angles ))
{
for( i = 0; i < 3; i++ )
@@ -1902,7 +1943,7 @@ void R_GenerateVBO( void )
if( surf->lightmaptexturenum != k )
continue;
if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS ) )
if( FBitSet( surf->flags, SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS | SURF_REFLECT ))
continue;
if( R_TextureAnimation( surf ) != world->textures[j] )
@@ -1971,7 +2012,7 @@ void R_GenerateVBO( void )
if( surf->lightmaptexturenum != k )
continue;
if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS ) )
if( FBitSet( surf->flags, SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS | SURF_REFLECT ))
continue;
if( R_TextureAnimation( surf ) != world->textures[j] )
@@ -2189,15 +2230,30 @@ static void R_SetLightmap( void )
if( gl_overbright.value )
{
// GLfloat color[4] = { 128.0f / 192.0f, 128.0f / 192.0f, 128.0f / 192.0f, 1.0f };
if( r_vbo_overbrightmode.value == 1 )
{
GLfloat color[4] = { 128.0f / 192.0f, 128.0f / 192.0f, 128.0f / 192.0f, 1.0f };
int tmu = glState.activeTMU;
GL_SelectTexture( tmu - 1 );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB );
pglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE );
pglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_CONSTANT_ARB );
pglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
pglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR );
pglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR );
pglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE );
// doesn't work here for some reason
pglTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
GL_SelectTexture( tmu );
}
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB );
pglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE );
pglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB );
pglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE );
pglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE );
pglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB );
pglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2 );
// doesn't work here for some reason
// pglTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
}
else
{
@@ -3805,7 +3861,14 @@ void GL_BuildLightmaps( void )
GL_FreeTexture( tr.lightmapTextures[i] );
}
for( i = 0; i < MAX_MIRRORS; i++ )
{
if( !tr.mirrorTextures[i] ) break;
GL_FreeTexture( tr.mirrorTextures[i] );
}
memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures ));
memset( tr.mirrorTextures, 0, sizeof( tr.mirrorTextures ));
memset( &RI, 0, sizeof( RI ));
// update the lightmap blocksize
@@ -3821,6 +3884,8 @@ void GL_BuildLightmaps( void )
tr.realframecount = 1;
nColinElim = 0;
tr.num_mirrors_used = 0;
// setup the texture for dlights
R_InitDlightTexture();

View File

@@ -46,7 +46,7 @@ R_SpriteLoadFrame
upload a single frame
====================
*/
static const dframetype_t *R_SpriteLoadFrame( model_t *mod, const void *pin, mspriteframe_t **ppframe, int num )
static const byte *R_SpriteLoadFrame( model_t *mod, const void *pin, mspriteframe_t **ppframe, int num )
{
dspriteframe_t pinframe;
mspriteframe_t *pspriteframe;
@@ -82,7 +82,7 @@ static const dframetype_t *R_SpriteLoadFrame( model_t *mod, const void *pin, msp
pspriteframe->gl_texturenum = gl_texturenum;
*ppframe = pspriteframe;
return ( const dframetype_t* )(( const byte* )pin + sizeof( dspriteframe_t ) + pinframe.width * pinframe.height * bytes );
return (( const byte* )pin + sizeof( dspriteframe_t ) + pinframe.width * pinframe.height * bytes );
}
/*
@@ -92,7 +92,7 @@ R_SpriteLoadGroup
upload a group frames
====================
*/
static const dframetype_t *R_SpriteLoadGroup( model_t *mod, const void *pin, mspriteframe_t **ppframe, int framenum )
static const byte *R_SpriteLoadGroup( model_t *mod, const void *pin, mspriteframe_t **ppframe, int framenum )
{
const dspritegroup_t *pingroup;
mspritegroup_t *pspritegroup;
@@ -128,9 +128,10 @@ static const dframetype_t *R_SpriteLoadGroup( model_t *mod, const void *pin, msp
ptemp = R_SpriteLoadFrame( mod, ptemp, &pspritegroup->frames[i], framenum * 10 + i );
}
return (const dframetype_t *)ptemp;
return ptemp;
}
/*
====================
Mod_LoadSpriteModel
@@ -142,7 +143,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
{
const dsprite_t *pin;
const short *numi = NULL;
const dframetype_t *pframetype;
const byte *pframetype;
msprite_t *psprite;
int i;
@@ -152,7 +153,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
if( pin->version == SPRITE_VERSION_Q1 || pin->version == SPRITE_VERSION_32 )
numi = NULL;
else if( pin->version == SPRITE_VERSION_HL )
numi = (const short *)(void *)((const byte*)buffer + sizeof( dsprite_hl_t ));
numi = (const short *)((const byte*)buffer + sizeof( dsprite_hl_t ));
r_texFlags = texFlags;
sprite_version = pin->version;
@@ -164,7 +165,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
rgbdata_t *pal;
pal = gEngfuncs.FS_LoadImage( "#id.pal", (byte *)&i, 768 );
pframetype = (const dframetype_t *)(void *)((const byte*)buffer + sizeof( dsprite_q1_t )); // pinq1 + 1
pframetype = ((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 )
@@ -186,7 +187,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
break;
}
pframetype = (const dframetype_t *)(void *)(src + 768);
pframetype = (const byte *)(src + 768);
gEngfuncs.FS_FreeImage( pal ); // palette installed, no reason to keep this data
}
else
@@ -200,22 +201,26 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
for( i = 0; i < mod->numframes; i++ )
{
frametype_t frametype = pframetype->type;
frametype_t frametype;
dframetype_t dframetype;
memcpy( &dframetype, pframetype, sizeof( dframetype ));
frametype = dframetype.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 );
pframetype = R_SpriteLoadFrame( mod, pframetype + sizeof( dframetype_t ), &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 );
pframetype = R_SpriteLoadGroup( mod, pframetype + sizeof( dframetype_t ), &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 );
pframetype = R_SpriteLoadGroup( mod, pframetype + sizeof( dframetype_t ), &psprite->frames[i].frameptr, i );
break;
}
if( pframetype == NULL ) break; // technically an error
@@ -696,6 +701,9 @@ static qboolean R_SpriteOccluded( cl_entity_t *e, vec3_t origin, float *pscale )
float blend;
vec3_t v;
if( R_CullEntityInMirror( e ))
return true;
TriWorldToScreen( origin, v );
if( v[0] < RI.viewport[0] || v[0] > RI.viewport[0] + RI.viewport[2] )

View File

@@ -32,8 +32,11 @@ typedef struct
model_t *model;
} player_model_t;
// never gonna change, just shut up const warning
cvar_t r_shadows = { (char *)"r_shadows", (char *)"0", 0 };
// never gonna change you up, never gonna override you down
// TODO: actually let client.dll override it
CVAR_DEFINE_AUTO( r_shadows, "0", FCVAR_ARCHIVE, "drop ugly shadow" );
CVAR_DEFINE_AUTO( r_shadow_alpha, "0.5", FCVAR_ARCHIVE, "ugly shadow opacity" );
CVAR_DEFINE_AUTO( r_shadow_height, "0", FCVAR_ARCHIVE, "ugly shadow height" );
static vec3_t hullcolor[8] =
{
@@ -154,8 +157,12 @@ void R_StudioInit( void )
Matrix3x4_LoadIdentity( g_studio.rotationmatrix );
// g-cont. cvar disabled by Valve
// gEngfuncs.Cvar_RegisterVariable( &r_shadows );
// a1ba: enabled since HL25
gEngfuncs.Cvar_RegisterVariable( &r_shadows );
// a1ba: TimeWarp cvars (TODO: move to client.dll)
gEngfuncs.Cvar_RegisterVariable( &r_shadow_alpha );
gEngfuncs.Cvar_RegisterVariable( &r_shadow_height );
g_studio.interpolate = true;
g_studio.framecount = 0;
@@ -2930,25 +2937,35 @@ R_StudioDrawPointsShadow
static void R_StudioDrawPointsShadow( void )
{
float *av, height;
float vec_x, vec_y;
mstudiomesh_t *pmesh;
vec3_t point;
int i, k;
pmtrace_t tr;
vec3_t trEnd;
if( FBitSet( RI.currententity->curstate.effects, EF_NOSHADOW ))
return;
VectorCopy( RI.currententity->origin, trEnd );
trEnd[2] -= 4096;
tr = gEngfuncs.CL_TraceLine( RI.currententity->origin, trEnd, PM_STUDIO_IGNORE | PM_GLASS_IGNORE );
// didn't hit floor
if( tr.fraction >= 1.0f )
return;
if( glState.stencilEnabled )
pglEnable( GL_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;
height = r_shadow_height.value;
for( k = 0; k < m_pSubModel->nummesh; k++ )
{
short *ptricmds;
if( FBitSet( g_studio.meshes[k].flags, STUDIO_NF_MASKED | STUDIO_NF_ADDITIVE | STUDIO_NF_FULLBRIGHT ))
continue;
pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + k;
ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex);
@@ -2970,11 +2987,7 @@ static void R_StudioDrawPointsShadow( void )
for( ; i > 0; i--, 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;
pglVertex3fv( point );
pglVertex3f( av[0], av[1], tr.endpos[2] + height + 0.16f );
}
pglEnd();
@@ -3035,12 +3048,10 @@ static void GL_StudioDrawShadow( void )
if( r_shadows.value && g_studio.rendermode != kRenderTransAdd && !FBitSet( RI.currentmodel->flags, STUDIO_AMBIENT_LIGHT ))
{
float color = 1.0f - (tr.blend * 0.5f);
pglDisable( GL_TEXTURE_2D );
pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
pglEnable( GL_BLEND );
pglColor4f( 0.0f, 0.0f, 0.0f, 1.0f - color );
pglColor4f( 0.0f, 0.0f, 0.0f, ( tr.blend - 1 ) + r_shadow_alpha.value );
pglDepthFunc( GL_LESS );
R_StudioDrawPointsShadow();

View File

@@ -781,7 +781,7 @@ Does a water warp on the pre-fragmented glpoly_t chain
void EmitWaterPolys( msurface_t *warp, qboolean reverse )
{
float *v, nv, waveHeight;
float s, t, os, ot;
float s, t, os, ot, warp_s, warp_t;
glpoly_t *p;
int i;
@@ -822,15 +822,29 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )
os = v[3];
ot = v[4];
if( !r_ripple.value )
switch((int)r_water_warp.value )
{
s = os + r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255];
t = ot + r_turbsin[(int)((os * 0.125f + gp_cl->time) * TURBSCALE) & 255];
case 0:
warp_s = warp_t = 0;
break;
case 1:
warp_s = r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255];
warp_t = r_turbsin[(int)((os * 0.125f + gp_cl->time) * TURBSCALE) & 255];
break;
case 2:
default:
warp_s = gp_cl->time * r_water_warp_x.value;
warp_t = gp_cl->time * r_water_warp_y.value;
break;
}
else
s = os + warp_s;
t = ot + warp_t;
if( r_ripple.value )
{
s = os / g_ripple.texturescale;
t = ot / g_ripple.texturescale;
s /= g_ripple.texturescale;
t /= g_ripple.texturescale;
}
s *= ( 1.0f / SUBDIVIDE_SIZE );
@@ -963,27 +977,29 @@ void R_AnimateRipples( void )
R_RunRipplesAnimation( g_ripple.oldbuf, g_ripple.curbuf );
}
void R_UploadRipples( texture_t *image )
void R_UploadRipples( texture_t *image, qboolean is_mirror )
{
gl_texture_t *glt;
uint32_t *pixels;
int wbits, wmask, wshft;
int y;
int tmu = is_mirror ? XASH_TEXTURE1 : XASH_TEXTURE0;
// discard unuseful textures
if( !r_ripple.value || image->width > RIPPLES_CACHEWIDTH || image->width != image->height )
{
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
GL_Bind( tmu, image->gl_texturenum );
return;
}
glt = R_GetTexture( image->gl_texturenum );
if( !glt || !glt->original || !glt->original->buffer || !FBitSet( glt->flags, TF_EXPAND_SOURCE ))
{
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
GL_Bind( tmu, image->gl_texturenum );
return;
}
GL_Bind( XASH_TEXTURE0, g_ripple.rippletexturenum );
GL_Bind( tmu, g_ripple.rippletexturenum );
// no updates this frame
if( !g_ripple.update && image->gl_texturenum == g_ripple.gl_texturenum )
@@ -1005,11 +1021,12 @@ void R_UploadRipples( texture_t *image )
wshft = 7 - wbits;
wmask = image->width - 1;
for( int y = 0; y < image->height; y++ )
for( y = 0; y < image->height; y++ )
{
int ry = y << ( 7 + wshft );
int x;
for( int x = 0; x < image->width; x++ )
for( x = 0; x < image->width; x++ )
{
int rx = x << wshft;
int val = g_ripple.curbuf[ry + rx] >> 4;

View File

@@ -95,7 +95,11 @@ static void R_DrawStretchPicImplementation( int x, int y, int w, int h, int s1,
//gEngfuncs.Con_Printf ("pixels is %p\n", pic->pixels[0] );
height = h;
if (y < 0)
if( y < -h ) // out of display, out of bounds
return;
if( y < 0 )
{
skip = -y;
height += y;

View File

@@ -46,7 +46,7 @@ R_SpriteLoadFrame
upload a single frame
====================
*/
static const dframetype_t *R_SpriteLoadFrame( model_t *mod, const void *pin, mspriteframe_t **ppframe, int num )
static const byte *R_SpriteLoadFrame( model_t *mod, const void *pin, mspriteframe_t **ppframe, int num )
{
dspriteframe_t pinframe;
mspriteframe_t *pspriteframe;
@@ -82,7 +82,7 @@ static const dframetype_t *R_SpriteLoadFrame( model_t *mod, const void *pin, msp
pspriteframe->gl_texturenum = gl_texturenum;
*ppframe = pspriteframe;
return ( const dframetype_t* )(( const byte* )pin + sizeof( dspriteframe_t ) + pinframe.width * pinframe.height * bytes );
return ( const byte* )(( const byte* )pin + sizeof( dspriteframe_t ) + pinframe.width * pinframe.height * bytes );
}
/*
@@ -92,7 +92,7 @@ R_SpriteLoadGroup
upload a group frames
====================
*/
static const dframetype_t *R_SpriteLoadGroup( model_t *mod, const void *pin, mspriteframe_t **ppframe, int framenum )
static const void *R_SpriteLoadGroup( model_t *mod, const void *pin, mspriteframe_t **ppframe, int framenum )
{
const dspritegroup_t *pingroup;
mspritegroup_t *pspritegroup;
@@ -128,7 +128,7 @@ static const dframetype_t *R_SpriteLoadGroup( model_t *mod, const void *pin, msp
ptemp = R_SpriteLoadFrame( mod, ptemp, &pspritegroup->frames[i], framenum * 10 + i );
}
return (const dframetype_t *)ptemp;
return ptemp;
}
/*
@@ -142,7 +142,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
{
const dsprite_t *pin;
const short *numi = NULL;
const dframetype_t *pframetype;
const byte *pframetype;
msprite_t *psprite;
int i;
@@ -164,7 +164,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
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
pframetype = ((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 )
@@ -186,7 +186,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
break;
}
pframetype = (const dframetype_t *)(src + 768);
pframetype = (const byte *)(src + 768);
gEngfuncs.FS_FreeImage( pal ); // palette installed, no reason to keep this data
}
else
@@ -200,22 +200,26 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
for( i = 0; i < mod->numframes; i++ )
{
frametype_t frametype = pframetype->type;
frametype_t frametype;
dframetype_t dframetype;
memcpy( &dframetype, pframetype, sizeof( dframetype ));
frametype = dframetype.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 );
pframetype = R_SpriteLoadFrame( mod, pframetype + sizeof( dframetype_t ), &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 );
pframetype = R_SpriteLoadGroup( mod, pframetype + sizeof( dframetype_t ), &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 );
pframetype = R_SpriteLoadGroup( mod, pframetype + sizeof( dframetype_t ), &psprite->frames[i].frameptr, i );
break;
}
if( pframetype == NULL ) break; // technically an error

View File

@@ -7,16 +7,16 @@ export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$AN
pushd android
./gradlew assembleContinuous
./gradlew assembleDebug
pushd app/build/outputs/apk/continuous
pushd app/build/outputs/apk/debug
$ANDROID_HOME/build-tools/34.0.0/apksigner sign --ks $GITHUB_WORKSPACE/android/debug.keystore --ks-key-alias androiddebugkey \
--ks-pass pass:android --key-pass pass:android --out app-continuous-signed.apk app-continuous-unsigned.apk
--ks-pass pass:android --key-pass pass:android --out app-debug-signed.apk app-debug.apk
popd
popd
mkdir -p artifacts/
mv android/app/build/outputs/apk/continuous/app-continuous-signed.apk artifacts/xash3d-fwgs-android.apk
mv android/app/build/outputs/apk/debug/app-debug-signed.apk artifacts/xash3d-fwgs-android.apk

View File

@@ -64,6 +64,7 @@ CFLAGS = {
'msvc': ['/O2', '/Oy', '/Zi'],
'gcc': {
'3': ['-O3', '-fomit-frame-pointer'],
'4': ['-Ofast', '-funsafe-math-optimizations', '-funsafe-loop-optimizations', '-fomit-frame-pointer'],
'default': ['-Ofast', '-funsafe-math-optimizations', '-funsafe-loop-optimizations', '-fomit-frame-pointer', '-fno-semantic-interposition']
},
'clang': ['-Ofast'],
@@ -78,7 +79,11 @@ CFLAGS = {
'humanrights': {
'msvc': ['/O2', '/Zi'],
'owcc': ['-O3', '-foptimize-sibling-calls', '-fomit-leaf-frame-pointer', '-fomit-frame-pointer', '-fschedule-insns', '-funsafe-math-optimizations', '-funroll-loops', '-frerun-optimizer', '-finline-functions', '-finline-limit=512', '-fguess-branch-probability', '-fno-strict-aliasing', '-floop-optimize'],
'gcc': ['-O3', '-fno-semantic-interposition'],
'gcc': {
'4': ['-O3'],
'3': ['-O3'],
'default': ['-O3','-fno-semantic-interposition'],
},
'default': ['-O3']
},
'debug': {
@@ -116,6 +121,22 @@ POLLY_CFLAGS = {
# msvc sosat :(
}
PROFILE_GENERATE_CFLAGS = {
'gcc': ['-fprofile-generate=xash3d-prof'],
}
PROFILE_GENERATE_LINKFLAGS = {
'gcc': ['-fprofile-generate=xash3d-prof'],
}
PROFILE_USE_CFLAGS = {
'gcc': ['-fprofile-use=%s'],
}
PROFILE_USE_LINKFLAGS = {
'gcc': ['-fprofile-use=%s'],
}
def options(opt):
grp = opt.add_option_group('Compiler optimization options')
@@ -128,6 +149,12 @@ def options(opt):
grp.add_option('--enable-poly-opt', action = 'store_true', dest = 'POLLY', default = False,
help = 'enable polyhedral optimization if possible [default: %default]')
grp.add_option('--enable-profile', action = 'store_true', dest = 'PROFILE_GENERATE', default = False,
help = 'enable profile generating build (stored in xash3d-prof directory) [default: %default]')
grp.add_option('--use-profile', action = 'store', dest = 'PROFILE_USE', default = None,
help = 'use profile during build [default: %default]')
def configure(conf):
conf.start_msg('Build type')
@@ -144,6 +171,8 @@ def configure(conf):
conf.msg('LTO build', 'yes' if conf.options.LTO else 'no')
conf.msg('PolyOpt build', 'yes' if conf.options.POLLY else 'no')
conf.msg('Generate profile', 'yes' if conf.options.PROFILE_GENERATE else 'no')
conf.msg('Use profile', conf.options.PROFILE_USE if not conf.options.PROFILE_GENERATE else 'no')
# -march=native should not be used
if conf.options.BUILD_TYPE.startswith('fast'):
@@ -174,6 +203,13 @@ def get_optimization_flags(conf):
if conf.options.POLLY:
cflags += conf.get_flags_by_compiler(POLLY_CFLAGS, conf.env.COMPILER_CC)
if conf.options.PROFILE_GENERATE:
linkflags+= conf.get_flags_by_compiler(PROFILE_GENERATE_LINKFLAGS, conf.env.COMPILER_CC)
cflags += conf.get_flags_by_compiler(PROFILE_GENERATE_CFLAGS, conf.env.COMPILER_CC)
elif conf.options.PROFILE_USE:
linkflags+= [conf.get_flags_by_compiler(PROFILE_USE_LINKFLAGS, conf.env.COMPILER_CC)[0] % conf.options.PROFILE_USE]
cflags += [conf.get_flags_by_compiler(PROFILE_USE_CFLAGS, conf.env.COMPILER_CC)[0] % conf.options.PROFILE_USE]
if conf.env.DEST_OS == 'nswitch' and conf.options.BUILD_TYPE == 'debug':
# enable remote debugger
cflags.append('-DNSWITCH_DEBUG')

82
scripts/waifulib/psp.py Normal file
View File

@@ -0,0 +1,82 @@
# encoding: utf-8
# psp.py -- PSP EBOOT task
# Copyright (C) 2023 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.
##################################
# PSP Tools #
##################################
class psp_fixup(Task.Task):
run_str = '${FIXUP} -o ${TGT} ${SRC}'
color = 'BLUE'
class psp_prxgen(Task.Task):
run_str = '${PRXGEN} ${SRC} ${TGT}'
color = 'BLUE'
class psp_strip(Task.Task):
run_str = '${STRIP} -o ${TGT} ${SRC}'
color = 'BLUE'
class psp_mksfo(Task.Task):
run_str = '${MKSFO} -d MEMSIZE=1 ${PSP_EBOOT_TITLE} ${TGT}'
color = 'YELLOW'
class psp_packpbp(Task.Task):
run_str = '${PACK_PBP} ${TGT} ${SRC[1].abspath()} ${PSP_EBOOT_ICON} ${PSP_EBOOT_ICON1} ${PSP_EBOOT_UNKPNG} ${PSP_EBOOT_PIC1} ${PSP_EBOOT_SND0} ${SRC[0].abspath()} ${PSP_EBOOT_PSAR}'
color = 'GREEN'
@TaskGen.feature('cshlib', 'cxxshlib')
@TaskGen.after_method('apply_link')
def build_module(self):
link_output = self.link_task.outputs[0]
for d in self.env.STATIC_LINKING:
if link_output.name.startswith(d):
return
fixup_output = self.path.find_or_declare(link_output.name + '_fixup')
prxgen_output = self.path.find_or_declare(link_output.change_ext('.prx').name)
task = self.create_task('psp_fixup', src=link_output, tgt=fixup_output)
task = self.create_task('psp_prxgen', src=fixup_output, tgt=prxgen_output)
if getattr(self, 'install_path', None):
if self.bld.is_install:
for k in self.install_task.inputs:
if k == self.path.find_or_declare(link_output.name):
self.install_task.inputs.remove(k)
self.add_install_files(install_to=self.install_path, install_from=prxgen_output)
@TaskGen.feature('cprogram', 'cxxprogram', 'cprogram_static', 'cxxprogram_static')
@TaskGen.after_method('apply_link')
def build_eboot(self):
finalobj_ext = '.elf'
finalobj_tool = 'psp_strip'
if self.env.PSP_BUILD_PRX:
finalobj_ext = '.prx'
finalobj_tool = 'psp_prxgen'
link_output = self.link_task.outputs[0]
fixup_output = self.path.find_or_declare(link_output.name + '_fixup')
finalobj_output = self.path.find_or_declare(link_output.change_ext(finalobj_ext).name)
mksfo_output = self.path.find_or_declare('PARAM.SFO')
packpbp_output = self.path.find_or_declare('EBOOT.PBP')
task = self.create_task('psp_fixup', src=link_output, tgt=fixup_output)
task = self.create_task(finalobj_tool, src=fixup_output, tgt=finalobj_output)
task = self.create_task('psp_mksfo', tgt=mksfo_output)
task = self.create_task('psp_packpbp', src=[finalobj_output, mksfo_output], tgt=packpbp_output)
if getattr(self, 'install_path', None):
if getattr(self, 'install_task', None):
self.install_task.inputs = self.install_task.outputs = []
self.add_install_files(install_to=self.install_path, install_from=[packpbp_output, finalobj_output])

View File

@@ -16,7 +16,7 @@ class ziparchive(Task.Task):
return 'Creating'
def run(self):
outfile = self.outputs[0].path_from(self.outputs[0].ctx.launch_node())
outfile = self.outputs[0].abspath()
comp = zipfile.ZIP_STORED if self.compresslevel == 0 else zipfile.ZIP_DEFLATED
with zipfile.ZipFile(outfile, mode='w', compression=comp) as zf:

View File

@@ -601,6 +601,12 @@ void WriteQCScript( void )
fputs( "$scale 1.0\n", fp );
fputs( "\n", fp );
if( model_hdr->flags & STUDIO_HAS_BONEINFO )
{
if( model_hdr->flags & STUDIO_HAS_BONEWEIGHTS )
fputs( "$boneweights\n\n", fp );
}
WriteBodyGroupInfo( fp );
fprintf( fp, "$flags %u\n\n", model_hdr->flags &~( STUDIO_HAS_BONEINFO | STUDIO_HAS_BONEWEIGHTS ) );
@@ -611,10 +617,6 @@ void WriteQCScript( void )
WriteSkinFamilyInfo( fp );
WriteTextureRenderMode( fp );
if( model_hdr->flags & ( STUDIO_HAS_BONEINFO | STUDIO_HAS_BONEWEIGHTS ) )
fputs( "$boneweights\n\n", fp );
WriteAttachmentInfo( fp );
fprintf( fp, "$bbox %f %f %f", model_hdr->min[0], model_hdr->min[1], model_hdr->min[2] );

View File

@@ -197,7 +197,7 @@ def configure(conf):
if conf.env.DEST_OS == 'android':
conf.options.NO_VGUI = True # skip vgui
conf.options.NANOGL = True
conf.options.GLWES = True
conf.options.GLWES = False # deprecated
conf.options.GL4ES = True
conf.options.GLES3COMPAT = True
conf.options.GL = False