Xash3D FWGS engine.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

714 lines
17 KiB

#include "common.h"
#include "client.h"
#include "library.h"
#include "cl_tent.h"
#include "platform/platform.h"
#include "vid_common.h"
struct ref_state_s ref;
ref_globals_t refState;
CVAR_DEFINE_AUTO( gl_vsync, "0", FCVAR_ARCHIVE, "enable vertical syncronization" );
CVAR_DEFINE_AUTO( r_showtextures, "0", FCVAR_CHEAT, "show all uploaded textures" );
CVAR_DEFINE_AUTO( r_adjust_fov, "1", FCVAR_ARCHIVE, "making FOV adjustment for wide-screens" );
CVAR_DEFINE_AUTO( r_decals, "4096", FCVAR_ARCHIVE, "sets the maximum number of decals" );
CVAR_DEFINE_AUTO( gl_msaa_samples, "0", FCVAR_GLCONFIG, "samples number for multisample anti-aliasing" );
CVAR_DEFINE_AUTO( gl_clear, "0", FCVAR_ARCHIVE, "clearing screen after each frame" );
CVAR_DEFINE_AUTO( r_showtree, "0", FCVAR_ARCHIVE, "build the graph of visible BSP tree" );
static CVAR_DEFINE_AUTO( r_refdll, "", FCVAR_RENDERINFO, "choose renderer implementation, if supported" );
static CVAR_DEFINE_AUTO( r_refdll_loaded, "", FCVAR_READ_ONLY, "currently loaded renderer" );
// there is no need to expose whole host and cl structs into the renderer
// but we still need to update timings accurately as possible
// this looks horrible but the only other option would be passing four
// time pointers and then it's looks even worse with dereferences everywhere
#define STATIC_OFFSET_CHECK( s1, s2, field, base, msg ) \
STATIC_ASSERT( offsetof( s1, field ) == offsetof( s2, field ) - offsetof( s2, base ), msg )
#define REF_CLIENT_CHECK( field ) \
STATIC_OFFSET_CHECK( ref_client_t, client_t, field, time, "broken ref_client_t offset" ); \
STATIC_ASSERT_( szchk_##__LINE__, sizeof(((ref_client_t *)0)->field ) == sizeof( cl.field ), "broken ref_client_t size" )
#define REF_HOST_CHECK( field ) \
STATIC_OFFSET_CHECK( ref_host_t, host_parm_t, field, realtime, "broken ref_client_t offset" ); \
STATIC_ASSERT_( szchk_##__LINE__, sizeof(((ref_host_t *)0)->field ) == sizeof( host.field ), "broken ref_client_t size" )
REF_CLIENT_CHECK( time );
REF_CLIENT_CHECK( oldtime );
REF_CLIENT_CHECK( viewentity );
REF_CLIENT_CHECK( playernum );
REF_CLIENT_CHECK( maxclients );
REF_CLIENT_CHECK( models );
REF_CLIENT_CHECK( paused );
REF_CLIENT_CHECK( simorg );
REF_HOST_CHECK( realtime );
REF_HOST_CHECK( frametime );
REF_HOST_CHECK( features );
void R_GetTextureParms( int *w, int *h, int texnum )
{
if( w ) *w = REF_GET_PARM( PARM_TEX_WIDTH, texnum );
if( h ) *h = REF_GET_PARM( PARM_TEX_HEIGHT, texnum );
}
/*
================
GL_FreeImage
Frees image by name
================
*/
void GAME_EXPORT GL_FreeImage( const char *name )
{
int texnum;
if( !ref.initialized )
return;
if(( texnum = ref.dllFuncs.GL_FindTexture( name )) != 0 )
ref.dllFuncs.GL_FreeTexture( texnum );
}
void R_UpdateRefState( void )
{
refState.time = cl.time;
refState.oldtime = cl.oldtime;
refState.realtime = host.realtime;
refState.frametime = host.frametime;
}
void GL_RenderFrame( const ref_viewpass_t *rvp )
{
R_UpdateRefState();
VectorCopy( rvp->vieworigin, refState.vieworg );
VectorCopy( rvp->viewangles, refState.viewangles );
ref.dllFuncs.GL_RenderFrame( rvp );
}
static intptr_t pfnEngineGetParm( int parm, int arg )
{
return CL_RenderGetParm( parm, arg, false ); // prevent recursion
}
static world_static_t *pfnGetWorld( void )
{
return &world;
}
static void pfnStudioEvent( const mstudioevent_t *event, const cl_entity_t *e )
{
clgame.dllFuncs.pfnStudioEvent( event, e );
}
static model_t *pfnGetDefaultSprite( enum ref_defaultsprite_e spr )
{
switch( spr )
{
case REF_DOT_SPRITE: return cl_sprite_dot;
case REF_CHROME_SPRITE: return cl_sprite_shell;
default: Host_Error( "GetDefaultSprite: unknown sprite %d\n", spr );
}
return NULL;
}
static void *pfnMod_Extradata( int type, model_t *m )
{
switch( type )
{
case mod_alias: return Mod_AliasExtradata( m );
case mod_studio: return Mod_StudioExtradata( m );
case mod_sprite: // fallthrough
case mod_brush: return NULL;
default: Host_Error( "Mod_Extradata: unknown type %d\n", type );
}
return NULL;
}
static void pfnGetPredictedOrigin( vec3_t v )
{
VectorCopy( cl.simorg, v );
}
static color24 *pfnCL_GetPaletteColor( void ) // clgame.palette[color]
{
return clgame.palette;
}
static void pfnCL_GetScreenInfo( int *width, int *height ) // clgame.scrInfo, ptrs may be NULL
{
if( width ) *width = clgame.scrInfo.iWidth;
if( height ) *height = clgame.scrInfo.iHeight;
}
static void pfnSetLocalLightLevel( int level )
{
cl.local.light_level = level;
}
/*
===============
pfnPlayerInfo
===============
*/
static player_info_t *pfnPlayerInfo( int index )
{
if( index == -1 ) // special index for menu
return &gameui.playerinfo;
if( index < 0 || index >= cl.maxclients )
return NULL;
return &cl.players[index];
}
/*
===============
pfnGetPlayerState
===============
*/
static entity_state_t *R_StudioGetPlayerState( int index )
{
if( index < 0 || index >= cl.maxclients )
return NULL;
return &cl.frames[cl.parsecountmod].playerstate[index];
}
static int pfnGetStudioModelInterface( int version, struct r_studio_interface_s **ppinterface, struct engine_studio_api_s *pstudio )
{
return clgame.dllFuncs.pfnGetStudioModelInterface ?
clgame.dllFuncs.pfnGetStudioModelInterface( version, ppinterface, pstudio ) :
0;
}
static const bpc_desc_t *pfnImage_GetPFDesc( int idx )
{
return &PFDesc[idx];
}
static void pfnDrawNormalTriangles( void )
{
clgame.dllFuncs.pfnDrawNormalTriangles();
}
static void pfnDrawTransparentTriangles( void )
{
clgame.dllFuncs.pfnDrawTransparentTriangles();
}
static screenfade_t *pfnRefGetScreenFade( void )
{
return &clgame.fade;
}
/*
===============
R_DoResetGamma
gamma will be reset for
some type of screenshots
===============
*/
static qboolean R_DoResetGamma( void )
{
switch( cls.scrshot_action )
{
case scrshot_envshot:
case scrshot_skyshot:
return true;
default:
return false;
}
}
static qboolean R_Init_Video_( const int type )
{
host.apply_opengl_config = true;
Cbuf_AddTextf( "exec %s.cfg", ref.dllFuncs.R_GetConfigName());
Cbuf_Execute();
host.apply_opengl_config = false;
return R_Init_Video( type );
}
static model_t **pfnGetModels( void )
{
return cl.models;
}
static ref_api_t gEngfuncs =
{
pfnEngineGetParm,
(void*)Cvar_Get,
(void*)Cvar_FindVarExt,
Cvar_VariableValue,
Cvar_VariableString,
Cvar_SetValue,
Cvar_Set,
Cvar_RegisterVariable,
Cvar_FullSet,
Cmd_AddRefCommand,
Cmd_RemoveCommand,
Cmd_Argc,
Cmd_Argv,
Cmd_Args,
Cbuf_AddText,
Cbuf_InsertText,
Cbuf_Execute,
Con_Printf,
Con_DPrintf,
Con_Reportf,
Con_NPrintf,
Con_NXPrintf,
CL_CenterPrint,
Con_DrawStringLen,
Con_DrawString,
CL_DrawCenterPrint,
CL_GetViewModel,
R_BeamGetEntity,
CL_GetWaterEntity,
CL_AddVisibleEntity,
Mod_SampleSizeForFace,
Mod_BoxVisible,
pfnGetWorld,
Mod_PointInLeaf,
Mod_CreatePolygonsForHull,
R_StudioGetAnim,
pfnStudioEvent,
CL_DrawEFX,
CL_ThinkParticle,
R_FreeDeadParticles,
CL_AllocParticleFast,
CL_AllocElight,
pfnGetDefaultSprite,
R_StoreEfrags,
Mod_ForName,
pfnMod_Extradata,
pfnGetModels,
CL_EntitySetRemapColors,
CL_GetRemapInfoForEntity,
CL_ExtraUpdate,
Host_Error,
COM_SetRandomSeed,
COM_RandomFloat,
COM_RandomLong,
pfnRefGetScreenFade,
pfnGetPredictedOrigin,
pfnCL_GetPaletteColor,
pfnCL_GetScreenInfo,
pfnSetLocalLightLevel,
Sys_CheckParm,
pfnPlayerInfo,
R_StudioGetPlayerState,
Mod_CacheCheck,
Mod_LoadCacheFile,
Mod_Calloc,
pfnGetStudioModelInterface,
_Mem_AllocPool,
_Mem_FreePool,
_Mem_Alloc,
_Mem_Realloc,
_Mem_Free,
COM_LoadLibrary,
COM_FreeLibrary,
COM_GetProcAddress,
R_Init_Video_,
R_Free_Video,
GL_SetAttribute,
GL_GetAttribute,
GL_GetProcAddress,
GL_SwapBuffers,
SW_CreateBuffer,
SW_LockBuffer,
SW_UnlockBuffer,
BuildGammaTable,
LightToTexGamma,
R_DoResetGamma,
CL_GetLightStyle,
CL_GetDynamicLight,
CL_GetEntityLight,
R_FatPVS,
GL_GetOverviewParms,
Sys_DoubleTime,
pfnGetPhysent,
pfnTraceSurface,
PM_CL_TraceLine,
CL_VisTraceLine,
CL_TraceLine,
pfnGetMoveVars,
Image_AddCmdFlags,
Image_SetForceFlags,
Image_ClearForceFlags,
Image_CustomPalette,
Image_Process,
FS_LoadImage,
FS_SaveImage,
FS_CopyImage,
FS_FreeImage,
Image_SetMDLPointer,
pfnImage_GetPFDesc,
pfnDrawNormalTriangles,
pfnDrawTransparentTriangles,
&clgame.drawFuncs,
&g_fsapi,
};
static void R_UnloadProgs( void )
{
if( !ref.hInstance ) return;
// deinitialize renderer
ref.dllFuncs.R_Shutdown();
Cvar_FullSet( "host_refloaded", "0", FCVAR_READ_ONLY );
Cvar_Unlink( FCVAR_RENDERINFO | FCVAR_GLCONFIG );
Cmd_Unlink( CMD_REFDLL );
COM_FreeLibrary( ref.hInstance );
ref.hInstance = NULL;
memset( &refState, 0, sizeof( refState ));
memset( &ref.dllFuncs, 0, sizeof( ref.dllFuncs ));
}
static void CL_FillTriAPIFromRef( triangleapi_t *dst, const ref_interface_t *src )
{
dst->version = TRI_API_VERSION;
dst->Begin = src->Begin;
dst->RenderMode = TriRenderMode;
dst->End = src->End;
dst->Color4f = TriColor4f;
dst->Color4ub = TriColor4ub;
dst->TexCoord2f = src->TexCoord2f;
dst->Vertex3f = src->Vertex3f;
dst->Vertex3fv = src->Vertex3fv;
dst->Brightness = TriBrightness;
dst->CullFace = TriCullFace;
dst->SpriteTexture = TriSpriteTexture;
dst->WorldToScreen = TriWorldToScreen;
dst->Fog = src->Fog;
dst->ScreenToWorld = src->ScreenToWorld;
dst->GetMatrix = src->GetMatrix;
dst->BoxInPVS = TriBoxInPVS;
dst->LightAtPoint = TriLightAtPoint;
dst->Color4fRendermode = TriColor4fRendermode;
dst->FogParams = src->FogParams;
}
static qboolean R_LoadProgs( const char *name )
{
extern triangleapi_t gTriApi;
static ref_api_t gpEngfuncs;
REFAPI GetRefAPI; // single export
if( ref.hInstance ) R_UnloadProgs();
FS_AllowDirectPaths( true );
if( !(ref.hInstance = COM_LoadLibrary( name, false, true ) ))
{
FS_AllowDirectPaths( false );
Con_Reportf( "R_LoadProgs: can't load renderer library %s: %s\n", name, COM_GetLibraryError() );
return false;
}
FS_AllowDirectPaths( false );
if( !( GetRefAPI = (REFAPI)COM_GetProcAddress( ref.hInstance, GET_REF_API )) )
{
COM_FreeLibrary( ref.hInstance );
Con_Reportf( "R_LoadProgs: can't find GetRefAPI entry point in %s\n", name );
ref.hInstance = NULL;
return false;
}
// make local copy of engfuncs to prevent overwrite it with user dll
memcpy( &gpEngfuncs, &gEngfuncs, sizeof( gpEngfuncs ));
if( GetRefAPI( REF_API_VERSION, &ref.dllFuncs, &gpEngfuncs, &refState ) != REF_API_VERSION )
{
COM_FreeLibrary( ref.hInstance );
Con_Reportf( "R_LoadProgs: can't init renderer API: wrong version\n" );
ref.hInstance = NULL;
return false;
}
refState.developer = host_developer.value;
if( !ref.dllFuncs.R_Init( ) )
{
COM_FreeLibrary( ref.hInstance );
Con_Reportf( "R_LoadProgs: can't init renderer!\n" ); //, ref.dllFuncs.R_GetInitError() );
ref.hInstance = NULL;
return false;
}
Cvar_FullSet( "host_refloaded", "1", FCVAR_READ_ONLY );
ref.initialized = true;
// initialize TriAPI callbacks
CL_FillTriAPIFromRef( &gTriApi, &ref.dllFuncs );
return true;
}
void R_Shutdown( void )
{
int i;
model_t *mod;
// release SpriteTextures
for( i = 1, mod = clgame.sprites; i < MAX_CLIENT_SPRITES; i++, mod++ )
{
if( !mod->name[0] ) continue;
Mod_FreeModel( mod );
}
memset( clgame.sprites, 0, sizeof( clgame.sprites ));
// correctly free all models before render unload
// change this if need add online render changing
Mod_FreeAll();
R_UnloadProgs();
ref.initialized = false;
}
static void R_GetRendererName( char *dest, size_t size, const char *opt )
{
if( !Q_strstr( opt, "." OS_LIB_EXT ))
{
const char *format;
#ifdef XASH_INTERNAL_GAMELIBS
if( !Q_strcmp( opt, "ref_" ))
format = "%s";
else
format = "ref_%s";
#else
if( !Q_strcmp( opt, "ref_" ))
format = OS_LIB_PREFIX "%s." OS_LIB_EXT;
else
format = OS_LIB_PREFIX "ref_%s." OS_LIB_EXT;
#endif
Q_snprintf( dest, size, format, opt );
}
else
{
// full path
Q_strncpy( dest, opt, size );
}
}
static qboolean R_LoadRenderer( const char *refopt )
{
string refdll;
R_GetRendererName( refdll, sizeof( refdll ), refopt );
Con_Printf( "Loading renderer: %s -> %s\n", refopt, refdll );
if( !R_LoadProgs( refdll ))
{
R_Shutdown();
Sys_Warn( S_ERROR "Can't initialize %s renderer!\n", refdll );
return false;
}
Cvar_FullSet( "r_refdll_loaded", refopt, FCVAR_READ_ONLY );
Con_Reportf( "Renderer %s initialized\n", refdll );
return true;
}
static void SetWidthAndHeightFromCommandLine( void )
{
int width, height;
Sys_GetIntFromCmdLine( "-width", &width );
Sys_GetIntFromCmdLine( "-height", &height );
if( width < 1 || height < 1 )
{
// Not specified or invalid, so don't bother.
return;
}
R_SaveVideoMode( width, height, width, height, false );
}
static void SetFullscreenModeFromCommandLine( void )
{
if( Sys_CheckParm( "-borderless" ))
Cvar_DirectSet( &vid_fullscreen, "2" );
else if( Sys_CheckParm( "-fullscreen" ))
Cvar_DirectSet( &vid_fullscreen, "1" );
else if( Sys_CheckParm( "-windowed" ))
Cvar_DirectSet( &vid_fullscreen, "0" );
}
static void R_CollectRendererNames( void )
{
// ordering is important!
static const char *shortNames[] =
{
#if XASH_REF_GL_ENABLED
"gl",
#endif
#if XASH_REF_NANOGL_ENABLED
"gles1",
#endif
#if XASH_REF_GLWES_ENABLED
"gles2",
#endif
#if XASH_REF_GL4ES_ENABLED
"gl4es",
#endif
#if XASH_REF_GLES3COMPAT_ENABLED
"gles3compat",
#endif
#if XASH_REF_SOFT_ENABLED
"soft",
#endif
};
// ordering is important here too!
static const char *readableNames[ARRAYSIZE( shortNames )] =
{
#if XASH_REF_GL_ENABLED
"OpenGL",
#endif
#if XASH_REF_NANOGL_ENABLED
"GLES1 (NanoGL)",
#endif
#if XASH_REF_GLWES_ENABLED
"GLES2 (gl-wes-v2)",
#endif
#if XASH_REF_GL4ES_ENABLED
"GL4ES",
#endif
#if XASH_REF_GLES3COMPAT_ENABLED
"GLES3 (gl2_shim)"
#endif
#if XASH_REF_SOFT_ENABLED
"Software",
#endif
};
ref.numRenderers = ARRAYSIZE( shortNames );
ref.shortNames = shortNames;
ref.readableNames = readableNames;
}
qboolean R_Init( void )
{
qboolean success = false;
string requested;
Cvar_RegisterVariable( &gl_vsync );
Cvar_RegisterVariable( &r_showtextures );
Cvar_RegisterVariable( &r_adjust_fov );
Cvar_RegisterVariable( &r_decals );
Cvar_RegisterVariable( &gl_msaa_samples );
Cvar_RegisterVariable( &gl_clear );
Cvar_RegisterVariable( &r_showtree );
Cvar_RegisterVariable( &r_refdll );
Cvar_RegisterVariable( &r_refdll_loaded );
// cvars that are expected to exist
Cvar_Get( "r_speeds", "0", FCVAR_ARCHIVE, "shows renderer speeds" );
Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" );
Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" );
Cvar_Get( "r_dynamic", "1", FCVAR_ARCHIVE, "allow dynamic lighting (dlights, lightstyles)" );
Cvar_Get( "r_lightmap", "0", FCVAR_CHEAT, "lightmap debugging tool" );
Cvar_Get( "tracerred", "0.8", 0, "tracer red component weight ( 0 - 1.0 )" );
Cvar_Get( "tracergreen", "0.8", 0, "tracer green component weight ( 0 - 1.0 )" );
Cvar_Get( "tracerblue", "0.4", 0, "tracer blue component weight ( 0 - 1.0 )" );
Cvar_Get( "traceralpha", "0.5", 0, "tracer alpha amount ( 0 - 1.0 )" );
Cvar_Get( "r_sprite_lerping", "1", FCVAR_ARCHIVE, "enables sprite animation lerping" );
Cvar_Get( "r_sprite_lighting", "1", FCVAR_ARCHIVE, "enables sprite lighting (blood etc)" );
Cvar_Get( "r_drawviewmodel", "1", 0, "draw firstperson weapon model" );
Cvar_Get( "r_glowshellfreq", "2.2", 0, "glowing shell frequency update" );
// cvars that are expected to exist by client.dll
// refdll should just get pointer to them
Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT, "render entities" );
Cvar_Get( "cl_himodels", "1", FCVAR_ARCHIVE, "draw high-resolution player models in multiplayer" );
// cvars are created, execute video config
Cbuf_AddText( "exec video.cfg" );
Cbuf_Execute();
// Set screen resolution and fullscreen mode if passed in on command line.
// this is done after executing video.cfg, as the command line values should take priority.
SetWidthAndHeightFromCommandLine();
SetFullscreenModeFromCommandLine();
R_CollectRendererNames();
// Priority:
// 1. Command line `-ref` argument.
// 2. `ref_dll` cvar.
// 3. Detected renderers in `DEFAULT_RENDERERS` order.
requested[0] = 0;
if( !success && Sys_GetParmFromCmdLine( "-ref", requested ))
success = R_LoadRenderer( requested );
if( !success && COM_CheckString( r_refdll.string ))
{
Q_strncpy( requested, r_refdll.string, sizeof( requested ));
success = R_LoadRenderer( requested );
}
if( !success )
{
int i;
for( i = 0; i < ref.numRenderers && !success; i++ )
{
// skip renderer that was requested but failed to load
if( !Q_strcmp( requested, ref.shortNames[i] ))
continue;
success = R_LoadRenderer( ref.shortNames[i] );
}
}
if( !success )
{
Host_Error( "Can't initialize any renderer. Check your video drivers!\n" );
return false;
}
SCR_Init();
return true;
}