From 0957581264b7c3d8b5566252801a9292b9380df6 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 19 Oct 2024 19:31:53 +0300 Subject: [PATCH 1/6] Use mat4_t for GL matrices. --- src/refresh/gl.h | 20 ++++++++++---------- src/refresh/main.c | 6 +++--- src/refresh/mesh.c | 4 ++-- src/refresh/shader.c | 4 ++-- src/refresh/state.c | 6 +++--- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/refresh/gl.h b/src/refresh/gl.h index c911eb2ac..9e1420acc 100644 --- a/src/refresh/gl.h +++ b/src/refresh/gl.h @@ -108,7 +108,7 @@ typedef struct { typedef struct { refdef_t fd; vec3_t viewaxis[3]; - GLfloat viewmatrix[16]; + mat4_t viewmatrix; unsigned visframe; unsigned drawframe; unsigned dlightframe; @@ -122,8 +122,8 @@ typedef struct { bool entrotated; float entscale; vec3_t entaxis[3]; - GLfloat entmatrix[16]; - GLfloat skymatrix[2][16]; + mat4_t entmatrix; + mat4_t skymatrix[2]; lightpoint_t lightpoint; int num_beams; int num_flares; @@ -601,9 +601,9 @@ typedef struct { } glMeshBlock_t; typedef struct { - GLfloat mvp[16]; + mat4_t m_vp; union { - GLfloat msky[2][16]; + mat4_t m_sky[2]; glMeshBlock_t mesh; }; GLfloat time; @@ -612,9 +612,9 @@ typedef struct { GLfloat intensity; GLfloat intensity2; GLfloat pad_4; - GLfloat w_amp[2]; - GLfloat w_phase[2]; - GLfloat scroll[2]; + vec2_t w_amp; + vec2_t w_phase; + vec2_t scroll; } glUniformBlock_t; typedef struct { @@ -627,8 +627,8 @@ typedef struct { GLuint currentbuffer[GLB_COUNT]; glVertexArray_t currentva; const GLfloat *currentmatrix; - GLfloat view_matrix[16]; - GLfloat proj_matrix[16]; + mat4_t view_matrix; + mat4_t proj_matrix; glUniformBlock_t u_block; bool u_block_dirty; } glState_t; diff --git a/src/refresh/main.c b/src/refresh/main.c index 9f5318c5b..95d4f71a2 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -313,12 +313,12 @@ void GL_RotationMatrix(GLfloat *matrix) void GL_RotateForEntity(bool skies) { - GLfloat matrix[16]; + mat4_t matrix; GL_RotationMatrix(matrix); if (skies) { - GL_MultMatrix(gls.u_block.msky[0], glr.skymatrix[0], matrix); - GL_MultMatrix(gls.u_block.msky[1], glr.skymatrix[1], matrix); + GL_MultMatrix(gls.u_block.m_sky[0], glr.skymatrix[0], matrix); + GL_MultMatrix(gls.u_block.m_sky[1], glr.skymatrix[1], matrix); } GL_MultMatrix(glr.entmatrix, glr.viewmatrix, matrix); GL_ForceMatrix(glr.entmatrix); diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index ee60422fc..dd48628d3 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -43,7 +43,7 @@ static bool dotshading; static float celscale; static drawshadow_t drawshadow; -static GLfloat shadowmatrix[16]; +static mat4_t shadowmatrix; #if USE_MD5 static md5_joint_t temp_skeleton[MD5_MAX_JOINTS]; @@ -524,7 +524,7 @@ static void proj_matrix(GLfloat *matrix, const cplane_t *plane, const vec3_t dir static void setup_shadow(void) { - GLfloat matrix[16], tmp[16]; + mat4_t matrix, tmp; vec3_t dir; if (!drawshadow) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index 302bbbac2..a48afb7e8 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -654,7 +654,7 @@ static void shader_load_matrix(GLenum mode, const GLfloat *matrix) Q_assert(!"bad mode"); } - GL_MultMatrix(gls.u_block.mvp, gls.proj_matrix, gls.view_matrix); + GL_MultMatrix(gls.u_block.m_vp, gls.proj_matrix, gls.view_matrix); gls.u_block_dirty = true; } @@ -687,7 +687,7 @@ static void shader_setup_3d(void) R_RotateForSky(); - memcpy(gls.u_block.msky, glr.skymatrix, sizeof(glr.skymatrix)); + memcpy(gls.u_block.m_sky, glr.skymatrix, sizeof(glr.skymatrix)); } static void shader_disable_state(void) diff --git a/src/refresh/state.c b/src/refresh/state.c index f9f802756..309e9dfa6 100644 --- a/src/refresh/state.c +++ b/src/refresh/state.c @@ -22,7 +22,7 @@ glState_t gls; const glbackend_t *gl_backend; -static const GLfloat identity[16] = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 }; +static const mat4_t identity = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 }; // for uploading void GL_ForceTexture(glTmu_t tmu, GLuint texnum) @@ -172,7 +172,7 @@ void GL_ScrollPos(vec2_t scroll, glStateBits_t bits) void GL_Ortho(GLfloat xmin, GLfloat xmax, GLfloat ymin, GLfloat ymax, GLfloat znear, GLfloat zfar) { GLfloat width, height, depth; - GLfloat matrix[16]; + mat4_t matrix; width = xmax - xmin; height = ymax - ymin; @@ -226,7 +226,7 @@ void GL_Frustum(GLfloat fov_x, GLfloat fov_y, GLfloat reflect_x) { GLfloat xmin, xmax, ymin, ymax, zfar, znear; GLfloat width, height, depth; - GLfloat matrix[16]; + mat4_t matrix; znear = gl_znear->value; From fbf0cd92429ad70177960d51a68745e288624613 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 19 Oct 2024 19:41:09 +0300 Subject: [PATCH 2/6] Add re-release fog effect support. Keep fog parameters in player_state_t for proper delta compression. Height fog seems broken but that's how it is in re-release. --- doc/client.asciidoc | 6 ++ inc/common/msg.h | 14 ++- inc/common/protocol.h | 38 ++++++-- inc/refresh/refresh.h | 2 + inc/shared/game.h | 2 +- inc/shared/shared.h | 23 ++++- src/client/client.h | 3 +- src/client/demo.c | 4 +- src/client/entities.c | 21 +++++ src/client/gtv.c | 10 ++- src/client/parse.c | 17 +++- src/common/msg.c | 197 +++++++++++++++++++++++++++++++++++++---- src/refresh/gl.h | 21 ++++- src/refresh/main.c | 27 ++++-- src/refresh/mesh.c | 2 +- src/refresh/shader.c | 93 ++++++++++++++++++- src/refresh/sky.c | 2 +- src/refresh/state.c | 4 +- src/refresh/tess.c | 9 +- src/server/game.c | 4 +- src/server/main.c | 3 + src/server/mvd.c | 2 +- src/server/mvd/game.c | 5 +- src/server/mvd/parse.c | 8 ++ 24 files changed, 454 insertions(+), 63 deletions(-) diff --git a/doc/client.asciidoc b/doc/client.asciidoc index 2e3dcf766..ed028c26d 100644 --- a/doc/client.asciidoc +++ b/doc/client.asciidoc @@ -687,6 +687,12 @@ gl_waterwarp:: Enable screen warping effect when underwater. Only effective when using GLSL backend. Default value is 0 (disabled). +gl_fog:: + Enable re-release fog effect. Default value is 2. + - 0 — disable fog + - 1 — enable global fog + - 2 — enable global fog and height fog + gl_flarespeed:: Specifies flare fading effect speed. Default value is 8. Set this to 0 to disable fading. diff --git a/inc/common/msg.h b/inc/common/msg.h index 4fb524a58..a83bd9438 100644 --- a/inc/common/msg.h +++ b/inc/common/msg.h @@ -48,6 +48,14 @@ typedef struct { uint8_t loop_attenuation; } entity_packed_t; +typedef struct { + uint8_t color[3][3]; + uint32_t density; + uint16_t height_density; + uint16_t height_falloff; + int32_t height_dist[2]; +} player_packed_fog_t; + typedef struct { pmove_state_new_t pmove; int16_t viewangles[3]; @@ -59,6 +67,7 @@ typedef struct { uint8_t gunframe; uint8_t blend[4]; uint8_t damage_blend[4]; + player_packed_fog_t fog; uint8_t fov; uint8_t rdflags; int16_t stats[MAX_STATS_NEW]; @@ -73,8 +82,9 @@ typedef enum { MSG_PS_IGNORE_PREDICTION = BIT(5), // mutually exclusive with IGNORE_VIEWANGLES MSG_PS_EXTENSIONS = BIT(6), // enable protocol extensions MSG_PS_EXTENSIONS_2 = BIT(7), // enable more protocol extensions - MSG_PS_FORCE = BIT(8), // send even if unchanged (MVD stream only) - MSG_PS_REMOVE = BIT(9), // player is removed (MVD stream only) + MSG_PS_MOREBITS = BIT(8), // read more playerstate bits + MSG_PS_FORCE = BIT(9), // send even if unchanged (MVD stream only) + MSG_PS_REMOVE = BIT(10), // player is removed (MVD stream only) } msgPsFlags_t; typedef enum { diff --git a/inc/common/protocol.h b/inc/common/protocol.h index 54b9e1e16..015d82444 100644 --- a/inc/common/protocol.h +++ b/inc/common/protocol.h @@ -29,8 +29,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PROTOCOL_VERSION_R1Q2 35 #define PROTOCOL_VERSION_Q2PRO 36 #define PROTOCOL_VERSION_MVD 37 // not used for UDP connections -#define PROTOCOL_VERSION_EXTENDED_OLD 3434 -#define PROTOCOL_VERSION_EXTENDED 3435 + +#define PROTOCOL_VERSION_EXTENDED_MINIMUM 3434 // r2894 +#define PROTOCOL_VERSION_EXTENDED_LIMITS_2 3435 // r3300 +#define PROTOCOL_VERSION_EXTENDED_PLAYERFOG 3436 // r3579 +#define PROTOCOL_VERSION_EXTENDED_CURRENT 3436 // r3579 #define PROTOCOL_VERSION_R1Q2_MINIMUM 1903 // b6377 #define PROTOCOL_VERSION_R1Q2_UCMD 1904 // b7387 @@ -48,13 +51,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PROTOCOL_VERSION_Q2PRO_CINEMATICS 1023 // r2263 #define PROTOCOL_VERSION_Q2PRO_EXTENDED_LIMITS 1024 // r2894 #define PROTOCOL_VERSION_Q2PRO_EXTENDED_LIMITS_2 1025 // r3300 -#define PROTOCOL_VERSION_Q2PRO_CURRENT 1025 // r3300 +#define PROTOCOL_VERSION_Q2PRO_PLAYERFOG 1026 // r3579 +#define PROTOCOL_VERSION_Q2PRO_CURRENT 1026 // r3579 #define PROTOCOL_VERSION_MVD_MINIMUM 2009 // r168 #define PROTOCOL_VERSION_MVD_DEFAULT 2010 // r177 #define PROTOCOL_VERSION_MVD_EXTENDED_LIMITS 2011 // r2894 #define PROTOCOL_VERSION_MVD_EXTENDED_LIMITS_2 2012 // r3300 -#define PROTOCOL_VERSION_MVD_CURRENT 2012 // r3300 +#define PROTOCOL_VERSION_MVD_PLAYERFOG 2013 // r3579 +#define PROTOCOL_VERSION_MVD_CURRENT 2013 // r3579 #define R1Q2_SUPPORTED(x) \ ((x) >= PROTOCOL_VERSION_R1Q2_MINIMUM && \ @@ -68,6 +73,10 @@ with this program; if not, write to the Free Software Foundation, Inc., ((x) >= PROTOCOL_VERSION_MVD_MINIMUM && \ (x) <= PROTOCOL_VERSION_MVD_CURRENT) +#define EXTENDED_SUPPORTED(x) \ + ((x) >= PROTOCOL_VERSION_EXTENDED_MINIMUM && \ + (x) <= PROTOCOL_VERSION_EXTENDED_CURRENT) + #define VALIDATE_CLIENTNUM(csr, x) \ ((x) >= -1 && (x) < (csr)->max_edicts - 1) @@ -213,6 +222,17 @@ typedef enum { //============================================== +typedef enum { + FOG_BIT_COLOR = BIT(0), + FOG_BIT_DENSITY = BIT(1), + FOG_BIT_HEIGHT_DENSITY = BIT(2), + FOG_BIT_HEIGHT_FALLOFF = BIT(3), + FOG_BIT_HEIGHT_START_COLOR = BIT(4), + FOG_BIT_HEIGHT_END_COLOR = BIT(5), + FOG_BIT_HEIGHT_START_DIST = BIT(6), + FOG_BIT_HEIGHT_END_DIST = BIT(7), +} fog_bits_t; + // player_state_t communication #define PS_M_TYPE BIT(0) @@ -231,7 +251,9 @@ typedef enum { #define PS_WEAPONINDEX BIT(12) #define PS_WEAPONFRAME BIT(13) #define PS_RDFLAGS BIT(14) -#define PS_RESERVED BIT(15) +#define PS_MOREBITS BIT(15) // read one additional byte + +#define PS_FOG BIT(16) // R1Q2 protocol specific extra flags #define EPS_GUNOFFSET BIT(0) @@ -264,7 +286,11 @@ typedef enum { #define PPS_GUNANGLES BIT(12) #define PPS_RDFLAGS BIT(13) #define PPS_STATS BIT(14) -#define PPS_REMOVE BIT(15) +#define PPS_MOREBITS BIT(15) // read one additional byte + // same as PPS_REMOVE for old demos!!! + +#define PPS_REMOVE BIT(16) +#define PPS_FOG BIT(17) // this is just a small hack to store inuse flag // in a field left otherwise unused by MVD code diff --git a/inc/refresh/refresh.h b/inc/refresh/refresh.h index 9302b4495..87980fae5 100644 --- a/inc/refresh/refresh.h +++ b/inc/refresh/refresh.h @@ -91,6 +91,8 @@ typedef struct { vec3_t viewangles; vec4_t screen_blend; // rgba 0-1 full screen blend vec4_t damage_blend; // rgba 0-1 damage blend + player_fog_t fog; + player_heightfog_t heightfog; float time; // time is uesed to auto animate int rdflags; // RDF_UNDERWATER, etc bool extended; diff --git a/inc/shared/game.h b/inc/shared/game.h index bc44762dc..b8390e0fb 100644 --- a/inc/shared/game.h +++ b/inc/shared/game.h @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // #define GAME_API_VERSION_OLD 3 // game uses gclient_old_t -#define GAME_API_VERSION_NEW 3300 // game uses gclient_new_t +#define GAME_API_VERSION_NEW 3301 // game uses gclient_new_t #if USE_NEW_GAME_API #define GAME_API_VERSION GAME_API_VERSION_NEW diff --git a/inc/shared/shared.h b/inc/shared/shared.h index 233384809..676cb40e5 100644 --- a/inc/shared/shared.h +++ b/inc/shared/shared.h @@ -1537,6 +1537,21 @@ typedef struct { } player_state_old_t; #if USE_NEW_GAME_API +typedef struct { + vec3_t color; + float density; + float sky_factor; +} player_fog_t; + +typedef struct { + struct { + vec3_t color; + float dist; + } start, end; + float density; + float falloff; +} player_heightfog_t; + typedef struct { pmove_state_new_t pmove; // for prediction @@ -1551,15 +1566,21 @@ typedef struct { vec3_t gunoffset; int gunindex; int gunframe; + int reserved_1; + int reserved_2; vec4_t blend; // rgba full screen effect vec4_t damage_blend; + player_fog_t fog; + player_heightfog_t heightfog; + float fov; // horizontal field of view int rdflags; // refdef flags - int reserved[4]; + int reserved_3; + int reserved_4; int16_t stats[MAX_STATS_NEW]; // fast status bar updates } player_state_new_t; diff --git a/src/client/client.h b/src/client/client.h index 6292f73a0..dcb85d518 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -495,6 +495,7 @@ typedef struct { player_packed_t ps; entity_packed_t entities[MAX_EDICTS]; msgEsFlags_t esFlags; // for writing + msgPsFlags_t psFlags; sizebuf_t message; } gtv; @@ -693,7 +694,7 @@ void CL_SendCmd(void); (MSG_ES_LONGSOLID | MSG_ES_UMASK | MSG_ES_BEAMORIGIN | MSG_ES_SHORTANGLES | MSG_ES_EXTENSIONS) #define CL_ES_EXTENDED_MASK_2 (CL_ES_EXTENDED_MASK | MSG_ES_EXTENSIONS_2) -#define CL_PS_EXTENDED_MASK_2 (MSG_PS_EXTENSIONS | MSG_PS_EXTENSIONS_2) +#define CL_PS_EXTENDED_MASK_2 (MSG_PS_EXTENSIONS | MSG_PS_EXTENSIONS_2 | MSG_PS_MOREBITS) typedef struct { int type; diff --git a/src/client/demo.c b/src/client/demo.c index ac6ea9ca6..317b07334 100644 --- a/src/client/demo.c +++ b/src/client/demo.c @@ -420,7 +420,7 @@ static void CL_Record_f(void) // send the serverdata MSG_WriteByte(svc_serverdata); if (cl.csr.extended) - MSG_WriteLong(PROTOCOL_VERSION_EXTENDED); + MSG_WriteLong(PROTOCOL_VERSION_EXTENDED_CURRENT); else MSG_WriteLong(min(cls.serverProtocol, PROTOCOL_VERSION_DEFAULT)); MSG_WriteLong(cl.servercount); @@ -1185,7 +1185,7 @@ bool CL_GetDemoInfo(const char *path, demoInfo_t *info) goto fail; } c = MSG_ReadLong(); - if (c == PROTOCOL_VERSION_EXTENDED || c == PROTOCOL_VERSION_EXTENDED_OLD) { + if (EXTENDED_SUPPORTED(c)) { csr = &cs_remap_new; } else if (c < PROTOCOL_VERSION_OLD || c > PROTOCOL_VERSION_DEFAULT) { goto fail; diff --git a/src/client/entities.c b/src/client/entities.c index acfe17da5..e399bf346 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -1315,6 +1315,14 @@ static inline float lerp_client_fov(float ofov, float nfov, float lerp) return ofov + lerp * (nfov - ofov); } +static inline void lerp_values(const void *from, const void *to, float lerp, void *out, int count) +{ + float backlerp = 1.0f - lerp; + + for (int i = 0; i < count; i++) + ((float *)out)[i] = ((const float *)from)[i] * backlerp + ((const float *)to)[i] * lerp; +} + /* =============== CL_CalcViewValues @@ -1389,6 +1397,19 @@ void CL_CalcViewValues(void) Vector4Copy(ps->blend, cl.refdef.screen_blend); Vector4Copy(ps->damage_blend, cl.refdef.damage_blend); + // interpolate fog + if (cl.psFlags & MSG_PS_MOREBITS) { + lerp_values(&ops->fog, &ps->fog, lerp, + &cl.refdef.fog, sizeof(cl.refdef.fog) / sizeof(float)); + // no lerping if moved too far + if (fabsf(ps->heightfog.start.dist - ops->heightfog.start.dist) > 512 || + fabsf(ps->heightfog.end .dist - ops->heightfog.end .dist) > 512) + cl.refdef.heightfog = ps->heightfog; + else + lerp_values(&ops->heightfog, &ps->heightfog, lerp, + &cl.refdef.heightfog, sizeof(cl.refdef.heightfog) / sizeof(float)); + } + #if USE_FPS ps = &cl.keyframe.ps; ops = &cl.oldkeyframe.ps; diff --git a/src/client/gtv.c b/src/client/gtv.c index a281f2319..877d6fdee 100644 --- a/src/client/gtv.c +++ b/src/client/gtv.c @@ -54,6 +54,10 @@ static void build_gamestate(void) // set protocol flags cls.gtv.esFlags = MSG_ES_UMASK | MSG_ES_BEAMORIGIN | (cl.esFlags & CL_ES_EXTENDED_MASK_2); + cls.gtv.psFlags = MSG_PS_FORCE | (cl.psFlags & CL_PS_EXTENDED_MASK_2); + + if (cls.gtv.psFlags & MSG_PS_EXTENSIONS_2) + cls.gtv.psFlags |= MSG_PS_MOREBITS; } static void emit_gamestate(void) @@ -99,8 +103,7 @@ static void emit_gamestate(void) MSG_WriteByte(0); // send player state - MSG_WriteDeltaPlayerstate_Packet(NULL, &cls.gtv.ps, - cl.clientNum, cl.psFlags | MSG_PS_FORCE); + MSG_WriteDeltaPlayerstate_Packet(NULL, &cls.gtv.ps, cl.clientNum, cls.gtv.psFlags); MSG_WriteByte(CLIENTNUM_NONE); // send entity states @@ -138,8 +141,7 @@ void CL_GTV_EmitFrame(void) // send player state MSG_PackPlayerNew(&newps, &cl.frame.ps); - MSG_WriteDeltaPlayerstate_Packet(&cls.gtv.ps, &newps, - cl.clientNum, cl.psFlags | MSG_PS_FORCE); + MSG_WriteDeltaPlayerstate_Packet(&cls.gtv.ps, &newps, cl.clientNum, cls.gtv.psFlags); // shuffle current state to previous cls.gtv.ps = newps; diff --git a/src/client/parse.c b/src/client/parse.c index 62f420a53..216c86d9e 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -320,6 +320,9 @@ static void CL_ParseFrame(int extrabits) // parse playerstate bits = MSG_ReadWord(); + if (cl.psFlags & MSG_PS_MOREBITS && bits & PS_MOREBITS) + bits |= (uint32_t)MSG_ReadByte() << 16; + if (cls.serverProtocol > PROTOCOL_VERSION_DEFAULT) { MSG_ParseDeltaPlayerstate_Enhanced(from, &frame.ps, bits, extraflags, cl.psFlags); #if USE_DEBUG @@ -532,7 +535,7 @@ static void CL_ParseServerData(void) cls.serverProtocol, protocol); } // BIG HACK to let demos from release work with the 3.0x patch!!! - if (protocol == PROTOCOL_VERSION_EXTENDED || protocol == PROTOCOL_VERSION_EXTENDED_OLD) { + if (EXTENDED_SUPPORTED(protocol)) { cl.csr = cs_remap_new; cls.serverProtocol = PROTOCOL_VERSION_DEFAULT; } else if (protocol < PROTOCOL_VERSION_OLD || protocol > PROTOCOL_VERSION_DEFAULT) { @@ -649,6 +652,8 @@ static void CL_ParseServerData(void) Com_DPrintf("Q2PRO protocol extensions v2 enabled\n"); cl.esFlags |= MSG_ES_EXTENSIONS_2; cl.psFlags |= MSG_PS_EXTENSIONS_2; + if (cls.protocolVersion >= PROTOCOL_VERSION_Q2PRO_PLAYERFOG) + cl.psFlags |= MSG_PS_MOREBITS; PmoveEnableExt(&cl.pmp); } } else { @@ -684,9 +689,13 @@ static void CL_ParseServerData(void) cl.psFlags |= MSG_PS_EXTENSIONS; // hack for demo playback - if (protocol == PROTOCOL_VERSION_EXTENDED) { - cl.esFlags |= MSG_ES_EXTENSIONS_2; - cl.psFlags |= MSG_PS_EXTENSIONS_2; + if (EXTENDED_SUPPORTED(protocol)) { + if (protocol >= PROTOCOL_VERSION_EXTENDED_LIMITS_2) { + cl.esFlags |= MSG_ES_EXTENSIONS_2; + cl.psFlags |= MSG_PS_EXTENSIONS_2; + } + if (protocol >= PROTOCOL_VERSION_EXTENDED_PLAYERFOG) + cl.psFlags |= MSG_PS_MOREBITS; } } diff --git a/src/common/msg.c b/src/common/msg.c index 6efdb7b49..862873efd 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -819,12 +819,18 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, #define OFFSET2CHAR(x) Q_clip_int8((x) * 4) #define BLEND2BYTE(x) Q_clip_uint8((x) * 255) +#define FRAC2SHORT(x) Q_clip_uint16((x) * 65535) #define PACK_OFFSET(out, in) \ (out[0] = OFFSET2CHAR(in[0]), \ out[1] = OFFSET2CHAR(in[1]), \ out[2] = OFFSET2CHAR(in[2])) +#define PACK_COLOR(out, in) \ + (out[0] = BLEND2BYTE(in[0]), \ + out[1] = BLEND2BYTE(in[1]), \ + out[2] = BLEND2BYTE(in[2])) + #define PACK_BLEND(out, in) \ (out[0] = BLEND2BYTE(in[0]), \ out[1] = BLEND2BYTE(in[1]), \ @@ -871,6 +877,20 @@ void MSG_PackPlayerNew(player_packed_t *out, const player_state_new_t *in) out->gunframe = in->gunframe; PACK_BLEND(out->blend, in->blend); PACK_BLEND(out->damage_blend, in->damage_blend); + + PACK_COLOR(out->fog.color[0], in->fog.color); + PACK_COLOR(out->fog.color[1], in->heightfog.start.color); + PACK_COLOR(out->fog.color[2], in->heightfog.end.color); + + uint32_t lo = FRAC2SHORT(in->fog.density); + uint32_t hi = FRAC2SHORT(in->fog.sky_factor); + out->fog.density = lo | hi << 16; + + out->fog.height_density = FRAC2SHORT(in->heightfog.density); + out->fog.height_falloff = FRAC2SHORT(in->heightfog.falloff); + out->fog.height_dist[0] = COORD2SHORT(in->heightfog.start.dist); + out->fog.height_dist[1] = COORD2SHORT(in->heightfog.end.dist); + out->fov = Q_clip_uint8(in->fov); out->rdflags = in->rdflags; @@ -940,9 +960,64 @@ static void MSG_WriteDeltaBlend(const player_packed_t *from, const player_packed MSG_WriteByte(to->damage_blend[i]); } +static fog_bits_t MSG_CalcFogBits(const player_packed_fog_t *from, const player_packed_fog_t *to) +{ + fog_bits_t bits = 0; + + if (!memcmp(to, from, sizeof(*to))) + return 0; + + if (!VectorCompare(to->color[0], from->color[0])) + bits |= FOG_BIT_COLOR; + if (to->density != from->density) + bits |= FOG_BIT_DENSITY; + + if (to->height_density != from->height_density) + bits |= FOG_BIT_HEIGHT_DENSITY; + if (to->height_falloff != from->height_falloff) + bits |= FOG_BIT_HEIGHT_FALLOFF; + + if (!VectorCompare(to->color[1], from->color[1])) + bits |= FOG_BIT_HEIGHT_START_COLOR; + if (!VectorCompare(to->color[2], from->color[2])) + bits |= FOG_BIT_HEIGHT_END_COLOR; + + if (to->height_dist[0] != from->height_dist[0]) + bits |= FOG_BIT_HEIGHT_START_DIST; + if (to->height_dist[1] != from->height_dist[1]) + bits |= FOG_BIT_HEIGHT_END_DIST; + + return bits; +} + +static void MSG_WriteFog(const player_packed_fog_t *to, fog_bits_t bits) +{ + MSG_WriteByte(bits); + + if (bits & FOG_BIT_COLOR) + MSG_WriteData(to->color[0], sizeof(to->color[0])); + if (bits & FOG_BIT_DENSITY) + MSG_WriteLong(to->density); + if (bits & FOG_BIT_HEIGHT_DENSITY) + MSG_WriteShort(to->height_density); + if (bits & FOG_BIT_HEIGHT_FALLOFF) + MSG_WriteShort(to->height_falloff); + + if (bits & FOG_BIT_HEIGHT_START_COLOR) + MSG_WriteData(to->color[1], sizeof(to->color[1])); + if (bits & FOG_BIT_HEIGHT_END_COLOR) + MSG_WriteData(to->color[2], sizeof(to->color[2])); + + if (bits & FOG_BIT_HEIGHT_START_DIST) + MSG_WriteDeltaInt23(0, to->height_dist[0]); + if (bits & FOG_BIT_HEIGHT_END_DIST) + MSG_WriteDeltaInt23(0, to->height_dist[1]); +} + void MSG_WriteDeltaPlayerstate_Default(const player_packed_t *from, const player_packed_t *to, msgPsFlags_t flags) { - int pflags; + int pflags = 0; + fog_bits_t fogbits = 0; uint64_t statbits; Q_assert(to); @@ -953,8 +1028,6 @@ void MSG_WriteDeltaPlayerstate_Default(const player_packed_t *from, const player // // determine what needs to be sent // - pflags = 0; - if (to->pmove.pm_type != from->pmove.pm_type) pflags |= PS_M_TYPE; @@ -991,6 +1064,9 @@ void MSG_WriteDeltaPlayerstate_Default(const player_packed_t *from, const player !Vector4Compare(to->damage_blend, from->damage_blend)) pflags |= PS_BLEND; + if (flags & MSG_PS_MOREBITS && (fogbits = MSG_CalcFogBits(&from->fog, &to->fog))) + pflags |= PS_FOG; + if (to->fov != from->fov) pflags |= PS_FOV; @@ -1005,10 +1081,15 @@ void MSG_WriteDeltaPlayerstate_Default(const player_packed_t *from, const player if (to->gunindex != from->gunindex) pflags |= PS_WEAPONINDEX; + if (pflags & 0xff0000) + pflags |= PS_MOREBITS; + // // write it // - MSG_WriteShort(pflags); + MSG_WriteShort(pflags & 0xffff); + if (pflags & PS_MOREBITS) + MSG_WriteByte(pflags >> 16); // // write the pmove_state_t @@ -1098,6 +1179,9 @@ void MSG_WriteDeltaPlayerstate_Default(const player_packed_t *from, const player MSG_WriteData(to->blend, sizeof(to->blend)); } + if (pflags & PS_FOG) + MSG_WriteFog(&to->fog, fogbits); + if (pflags & PS_FOV) MSG_WriteByte(to->fov); @@ -1113,7 +1197,8 @@ int MSG_WriteDeltaPlayerstate_Enhanced(const player_packed_t *from, player_packed_t *to, msgPsFlags_t flags) { - int pflags, eflags; + int pflags = 0, eflags = 0; + fog_bits_t fogbits = 0; uint64_t statbits; Q_assert(to); @@ -1124,9 +1209,6 @@ int MSG_WriteDeltaPlayerstate_Enhanced(const player_packed_t *from, // // determine what needs to be sent // - pflags = 0; - eflags = 0; - if (to->pmove.pm_type != from->pmove.pm_type) pflags |= PS_M_TYPE; @@ -1199,6 +1281,9 @@ int MSG_WriteDeltaPlayerstate_Enhanced(const player_packed_t *from, Vector4Copy(from->damage_blend, to->damage_blend); } + if (flags & MSG_PS_MOREBITS && (fogbits = MSG_CalcFogBits(&from->fog, &to->fog))) + pflags |= PS_FOG; + if (from->fov != to->fov) pflags |= PS_FOV; @@ -1233,10 +1318,15 @@ int MSG_WriteDeltaPlayerstate_Enhanced(const player_packed_t *from, if (statbits) eflags |= EPS_STATS; + if (pflags & 0xff0000) + pflags |= PS_MOREBITS; + // // write it // - MSG_WriteShort(pflags); + MSG_WriteShort(pflags & 0xffff); + if (pflags & PS_MOREBITS) + MSG_WriteByte(pflags >> 16); // // write the pmove_state_t @@ -1339,6 +1429,9 @@ int MSG_WriteDeltaPlayerstate_Enhanced(const player_packed_t *from, MSG_WriteData(to->blend, sizeof(to->blend)); } + if (pflags & PS_FOG) + MSG_WriteFog(&to->fog, fogbits); + if (pflags & PS_FOV) MSG_WriteByte(to->fov); @@ -1367,7 +1460,8 @@ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, int number, msgPsFlags_t flags) { - int pflags; + int pflags = 0; + fog_bits_t fogbits = 0; uint64_t statbits; // this can happen with client GTV @@ -1376,7 +1470,9 @@ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, if (!to) { MSG_WriteByte(number); - MSG_WriteShort(PPS_REMOVE); + MSG_WriteShort(PPS_MOREBITS); // MOREBITS == REMOVE for old demos + if (flags & MSG_PS_MOREBITS) + MSG_WriteByte(PPS_REMOVE >> 16); return; } @@ -1386,8 +1482,6 @@ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, // // determine what needs to be sent // - pflags = 0; - if (to->pmove.pm_type != from->pmove.pm_type) pflags |= PPS_M_TYPE; @@ -1419,6 +1513,9 @@ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, pflags |= PPS_BLEND; } + if (flags & MSG_PS_MOREBITS && (fogbits = MSG_CalcFogBits(&from->fog, &to->fog))) + pflags |= PPS_FOG; + if (from->fov != to->fov) pflags |= PPS_FOV; @@ -1449,11 +1546,16 @@ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, if (flags & MSG_PS_REMOVE) pflags |= PPS_REMOVE; // used for MVD stream only + if (pflags & 0xff0000) + pflags |= PPS_MOREBITS; + // // write it // MSG_WriteByte(number); - MSG_WriteShort(pflags); + MSG_WriteShort(pflags & 0xffff); + if (flags & MSG_PS_MOREBITS && pflags & PPS_MOREBITS) + MSG_WriteByte(pflags >> 16); // // write some part of the pmove_state_t @@ -1519,6 +1621,9 @@ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, MSG_WriteData(to->blend, sizeof(to->blend)); } + if (pflags & PPS_FOG) + MSG_WriteFog(&to->fog, fogbits); + if (pflags & PPS_FOV) MSG_WriteByte(to->fov); @@ -1721,6 +1826,17 @@ static void MSG_ReadDeltaCoord(float *to) } } +static float MSG_ReadExtCoord(void) +{ + uint32_t v = MSG_ReadWord(); + if (v & 1) { + v |= (uint32_t)MSG_ReadByte() << 16; + return SHORT2COORD(SignExtend(v >> 1, 23)); + } else { + return SHORT2COORD(SignExtend(v >> 1, 15)); + } +} + #endif #if USE_SERVER @@ -1729,10 +1845,9 @@ static inline void MSG_ReadPos(vec3_t pos, bool extended) { if (extended) { - VectorClear(pos); - MSG_ReadDeltaCoord(&pos[0]); - MSG_ReadDeltaCoord(&pos[1]); - MSG_ReadDeltaCoord(&pos[2]); + pos[0] = MSG_ReadExtCoord(); + pos[1] = MSG_ReadExtCoord(); + pos[2] = MSG_ReadExtCoord(); } else { pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); @@ -2187,6 +2302,40 @@ static void MSG_ReadBlend(player_state_t *to, msgPsFlags_t psflags) } } +static void MSG_ReadColor(vec3_t color) +{ + color[0] = MSG_ReadByte() / 255.0f; + color[1] = MSG_ReadByte() / 255.0f; + color[2] = MSG_ReadByte() / 255.0f; +} + +static void MSG_ReadFog(player_state_t *to) +{ + fog_bits_t bits = MSG_ReadByte(); + + if (bits & FOG_BIT_COLOR) + MSG_ReadColor(to->fog.color); + if (bits & FOG_BIT_DENSITY) { + to->fog.density = MSG_ReadWord() / 65535.0f; + to->fog.sky_factor = MSG_ReadWord() / 65535.0f; + } + + if (bits & FOG_BIT_HEIGHT_DENSITY) + to->heightfog.density = MSG_ReadWord() / 65535.0f; + if (bits & FOG_BIT_HEIGHT_FALLOFF) + to->heightfog.falloff = MSG_ReadWord() / 65535.0f; + + if (bits & FOG_BIT_HEIGHT_START_COLOR) + MSG_ReadColor(to->heightfog.start.color); + if (bits & FOG_BIT_HEIGHT_END_COLOR) + MSG_ReadColor(to->heightfog.end.color); + + if (bits & FOG_BIT_HEIGHT_START_DIST) + to->heightfog.start.dist = MSG_ReadExtCoord(); + if (bits & FOG_BIT_HEIGHT_END_DIST) + to->heightfog.end.dist = MSG_ReadExtCoord(); +} + #if USE_CLIENT /* @@ -2302,6 +2451,9 @@ void MSG_ParseDeltaPlayerstate_Default(const player_state_t *from, if (flags & PS_BLEND) MSG_ReadBlend(to, psflags); + if (flags & PS_FOG) + MSG_ReadFog(to); + if (flags & PS_FOV) to->fov = MSG_ReadByte(); @@ -2441,6 +2593,9 @@ void MSG_ParseDeltaPlayerstate_Enhanced(const player_state_t *from, if (flags & PS_BLEND) MSG_ReadBlend(to, psflags); + if (flags & PS_FOG) + MSG_ReadFog(to); + if (flags & PS_FOV) to->fov = MSG_ReadByte(); @@ -2539,6 +2694,9 @@ void MSG_ParseDeltaPlayerstate_Packet(player_state_t *to, if (flags & PPS_BLEND) MSG_ReadBlend(to, psflags); + if (flags & PPS_FOG) + MSG_ReadFog(to); + if (flags & PPS_FOV) to->fov = MSG_ReadByte(); @@ -2583,6 +2741,7 @@ void MSG_ShowDeltaPlayerstateBits_Default(int flags) S(WEAPONINDEX, "gunindex"); S(WEAPONFRAME, "gunframe"); S(BLEND, "blend"); + S(FOG, "fog"); S(FOV, "fov"); S(RDFLAGS, "rdflags"); #undef S @@ -2610,6 +2769,7 @@ void MSG_ShowDeltaPlayerstateBits_Enhanced(int flags, int extraflags) SE(GUNOFFSET, "gunoffset"); SE(GUNANGLES, "gunangles"); SP(BLEND, "blend"); + SP(FOG, "fog"); SP(FOV, "fov"); SP(RDFLAGS, "rdflags"); SE(STATS, "stats"); @@ -2712,6 +2872,7 @@ void MSG_ShowDeltaPlayerstateBits_Packet(int flags) S(GUNOFFSET, "gunoffset"); S(GUNANGLES, "gunangles"); S(BLEND, "blend"); + S(FOG, "fog"); S(FOV, "fov"); S(RDFLAGS, "rdflags"); S(STATS, "stats"); diff --git a/src/refresh/gl.h b/src/refresh/gl.h index 9e1420acc..6753bde65 100644 --- a/src/refresh/gl.h +++ b/src/refresh/gl.h @@ -127,6 +127,7 @@ typedef struct { lightpoint_t lightpoint; int num_beams; int num_flares; + int fog_bits, fog_bits_sky; int framebuffer_width; int framebuffer_height; bool framebuffer_ok; @@ -507,14 +508,20 @@ typedef enum { GLS_SCROLL_FLIP = BIT(24), GLS_SCROLL_SLOW = BIT(25), + GLS_FOG_GLOBAL = BIT(26), + GLS_FOG_HEIGHT = BIT(27), + GLS_FOG_SKY = BIT(28), + GLS_BLEND_MASK = GLS_BLEND_BLEND | GLS_BLEND_ADD | GLS_BLEND_MODULATE, GLS_COMMON_MASK = GLS_DEPTHMASK_FALSE | GLS_DEPTHTEST_DISABLE | GLS_CULL_DISABLE | GLS_BLEND_MASK, GLS_SKY_MASK = GLS_CLASSIC_SKY | GLS_DEFAULT_SKY, + GLS_FOG_MASK = GLS_FOG_GLOBAL | GLS_FOG_HEIGHT | GLS_FOG_SKY, GLS_MESH_ANY = GLS_MESH_MD2 | GLS_MESH_MD5, GLS_MESH_MASK = GLS_MESH_ANY | GLS_MESH_LERP | GLS_MESH_SHELL | GLS_MESH_SHADE, GLS_SHADER_MASK = GLS_ALPHATEST_ENABLE | GLS_TEXTURE_REPLACE | GLS_SCROLL_ENABLE | GLS_LIGHTMAP_ENABLE | GLS_WARP_ENABLE | GLS_INTENSITY_ENABLE | GLS_GLOWMAP_ENABLE | - GLS_SKY_MASK | GLS_DEFAULT_FLARE | GLS_MESH_MASK, + GLS_SKY_MASK | GLS_DEFAULT_FLARE | GLS_MESH_MASK | GLS_FOG_MASK, + GLS_UNIFORM_MASK = GLS_WARP_ENABLE | GLS_LIGHTMAP_ENABLE | GLS_INTENSITY_ENABLE | GLS_SKY_MASK | GLS_FOG_MASK, GLS_SCROLL_MASK = GLS_SCROLL_ENABLE | GLS_SCROLL_X | GLS_SCROLL_Y | GLS_SCROLL_FLIP | GLS_SCROLL_SLOW, } glStateBits_t; @@ -602,6 +609,7 @@ typedef struct { typedef struct { mat4_t m_vp; + mat4_t m_model; union { mat4_t m_sky[2]; glMeshBlock_t mesh; @@ -611,10 +619,17 @@ typedef struct { GLfloat add; GLfloat intensity; GLfloat intensity2; - GLfloat pad_4; + GLfloat fog_sky_factor; vec2_t w_amp; vec2_t w_phase; vec2_t scroll; + vec4_t fog_color; + vec4_t heightfog_start; + vec4_t heightfog_end; + GLfloat heightfog_density; + GLfloat heightfog_falloff; + vec2_t pad_4; + vec4_t vieworg; } glUniformBlock_t; typedef struct { @@ -667,6 +682,8 @@ typedef struct { extern const glbackend_t *gl_backend; +extern const mat4_t gl_identity; + static inline void GL_ActiveTexture(glTmu_t tmu) { if (gls.server_tmu != tmu) { diff --git a/src/refresh/main.c b/src/refresh/main.c index 95d4f71a2..6e56855af 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -61,6 +61,7 @@ cvar_t *gl_md5_distance; #endif cvar_t *gl_damageblend_frac; cvar_t *gl_waterwarp; +cvar_t *gl_fog; cvar_t *gl_swapinterval; // development variables @@ -313,14 +314,12 @@ void GL_RotationMatrix(GLfloat *matrix) void GL_RotateForEntity(bool skies) { - mat4_t matrix; - - GL_RotationMatrix(matrix); + GL_RotationMatrix(gls.u_block.m_model); if (skies) { - GL_MultMatrix(gls.u_block.m_sky[0], glr.skymatrix[0], matrix); - GL_MultMatrix(gls.u_block.m_sky[1], glr.skymatrix[1], matrix); + GL_MultMatrix(gls.u_block.m_sky[0], glr.skymatrix[0], gls.u_block.m_model); + GL_MultMatrix(gls.u_block.m_sky[1], glr.skymatrix[1], gls.u_block.m_model); } - GL_MultMatrix(glr.entmatrix, glr.viewmatrix, matrix); + GL_MultMatrix(glr.entmatrix, glr.viewmatrix, gls.u_block.m_model); GL_ForceMatrix(glr.entmatrix); } @@ -330,7 +329,7 @@ static void GL_DrawSpriteModel(const model_t *model) const mspriteframe_t *frame = &model->spriteframes[e->frame % model->numframes]; const image_t *image = frame->image; const float alpha = (e->flags & RF_TRANSLUCENT) ? e->alpha : 1.0f; - glStateBits_t bits = GLS_DEPTHMASK_FALSE; + glStateBits_t bits = GLS_DEPTHMASK_FALSE | glr.fog_bits; vec3_t up, down, left, right; if (alpha == 1.0f) { @@ -663,12 +662,21 @@ void R_RenderFrame(const refdef_t *fd) glr.drawframe++; glr.fd = *fd; - glr.num_beams = 0; - glr.num_flares = 0; + glr.num_beams = glr.num_flares = 0; + glr.fog_bits = glr.fog_bits_sky = 0; if (gl_dynamic->integer != 1 || gl_vertexlight->integer) glr.fd.num_dlights = 0; + if (gl_fog->integer > 0) { + if (glr.fd.fog.density > 0) + glr.fog_bits |= GLS_FOG_GLOBAL; + if (glr.fd.heightfog.density > 0 && glr.fd.heightfog.falloff > 0 && gl_fog->integer > 1) + glr.fog_bits |= GLS_FOG_HEIGHT; + if (glr.fd.fog.sky_factor > 0) + glr.fog_bits_sky |= GLS_FOG_SKY; + } + if (lm.dirty) { GL_RebuildLighting(); lm.dirty = false; @@ -905,6 +913,7 @@ static void GL_Register(void) #endif gl_damageblend_frac = Cvar_Get("gl_damageblend_frac", "0.2", 0); gl_waterwarp = Cvar_Get("gl_waterwarp", "0", 0); + gl_fog = Cvar_Get("gl_fog", "2", 0); gl_swapinterval = Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_swapinterval->changed = gl_swapinterval_changed; diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index dd48628d3..8e901519c 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -663,7 +663,7 @@ static void draw_alias_mesh(const uint16_t *indices, int num_indices, qglColorMask(1, 1, 1, 1); } - state = GLS_INTENSITY_ENABLE; + state = GLS_INTENSITY_ENABLE | glr.fog_bits; if (!gls.currentva) state |= meshbits; else if (dotshading) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index a48afb7e8..9ca0e9ef7 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -48,6 +48,7 @@ static void write_block(sizebuf_t *buf, glStateBits_t bits) { GLSF("layout(std140) uniform u_block {\n"); GLSL(mat4 m_vp;); + GLSL(mat4 m_model;); if (bits & GLS_MESH_ANY) { GLSL( @@ -76,10 +77,17 @@ static void write_block(sizebuf_t *buf, glStateBits_t bits) float u_add; float u_intensity; float u_intensity2; - float pad_4; + float u_fog_sky_factor; vec2 w_amp; vec2 w_phase; vec2 u_scroll; + vec4 u_fog_color; + vec4 u_heightfog_start; + vec4 u_heightfog_end; + float u_heightfog_density; + float u_heightfog_falloff; + vec2 pad_4; + vec3 u_vieworg; ) GLSF("};\n"); } @@ -135,6 +143,9 @@ static void write_skel_shader(sizebuf_t *buf, glStateBits_t bits) out vec4 v_color; ) + if (bits & GLS_FOG_HEIGHT) + GLSL(out vec3 v_world_pos;) + if (bits & GLS_MESH_SHADE) write_shadedot(buf); @@ -182,6 +193,9 @@ static void write_skel_shader(sizebuf_t *buf, glStateBits_t bits) if (bits & GLS_MESH_SHELL) GLSL(out_pos += out_norm * u_shellscale;) + if (bits & GLS_FOG_HEIGHT) + GLSL(v_world_pos = (m_model * vec4(out_pos, 1.0)).xyz;) + GLSL(gl_Position = m_vp * vec4(out_pos, 1.0);) GLSF("}\n"); } @@ -219,6 +233,9 @@ static void write_mesh_shader(sizebuf_t *buf, glStateBits_t bits) out vec4 v_color; ) + if (bits & GLS_FOG_HEIGHT) + GLSL(out vec3 v_world_pos;) + if (bits & (GLS_MESH_SHELL | GLS_MESH_SHADE)) write_getnormal(buf); @@ -259,6 +276,9 @@ static void write_mesh_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(v_color = u_color;) } + if (bits & GLS_FOG_HEIGHT) + GLSL(v_world_pos = (m_model * vec4(pos, 1.0)).xyz;) + GLSL(gl_Position = m_vp * vec4(pos, 1.0);) GLSF("}\n"); } @@ -298,6 +318,9 @@ static void write_vertex_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(out vec4 v_color;) } + if (bits & GLS_FOG_HEIGHT) + GLSL(out vec3 v_world_pos;) + GLSF("void main() {\n"); if (bits & GLS_CLASSIC_SKY) { GLSL(v_dir = (m_sky[1] * a_pos).xyz;) @@ -315,10 +338,30 @@ static void write_vertex_shader(sizebuf_t *buf, glStateBits_t bits) if (!(bits & GLS_TEXTURE_REPLACE)) GLSL(v_color = a_color;) + if (bits & GLS_FOG_HEIGHT) + GLSL(v_world_pos = (m_model * a_pos).xyz;) + GLSL(gl_Position = m_vp * a_pos;) GLSF("}\n"); } +// XXX: this is very broken. but that's how it is in re-release. +static void write_height_fog(sizebuf_t *buf) +{ + GLSL({ + float dir_z = normalize(v_world_pos - u_vieworg).z; + float eye = u_vieworg.z - u_heightfog_start.w; + float pos = v_world_pos.z - u_heightfog_start.w; + float density = (exp(-u_heightfog_falloff * eye) - + exp(-u_heightfog_falloff * pos)) / (u_heightfog_falloff * dir_z); + float extinction = 1.0 - clamp(exp(-density), 0.0, 1.0); + float fraction = clamp((pos - u_heightfog_start.w) / (u_heightfog_end.w - u_heightfog_start.w), 0.0, 1.0); + vec3 fog_color = mix(u_heightfog_start.rgb, u_heightfog_end.rgb, fraction) * extinction; + float fog = (1.0 - exp(-(u_heightfog_density * frag_depth))) * extinction; + diffuse.rgb = mix(diffuse.rgb, fog_color.rgb, fog); + }) +} + static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) { write_header(buf, bits); @@ -326,7 +369,7 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) if (gl_config.ver_es) GLSL(precision mediump float;) - if (bits & (GLS_WARP_ENABLE | GLS_LIGHTMAP_ENABLE | GLS_INTENSITY_ENABLE | GLS_SKY_MASK)) + if (bits & GLS_UNIFORM_MASK) write_block(buf, bits); if (bits & GLS_CLASSIC_SKY) { @@ -358,6 +401,9 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(out vec4 o_color;) + if (bits & GLS_FOG_HEIGHT) + GLSL(in vec3 v_world_pos;) + GLSF("void main() {\n"); if (bits & GLS_CLASSIC_SKY) { GLSL( @@ -414,6 +460,22 @@ static void write_fragment_shader(sizebuf_t *buf, glStateBits_t bits) GLSL(diffuse.rgb += glowmap.rgb;) } + if (bits & (GLS_FOG_GLOBAL | GLS_FOG_HEIGHT)) + GLSL(float frag_depth = gl_FragCoord.z / gl_FragCoord.w;) + + if (bits & GLS_FOG_GLOBAL) + GLSL({ + float d = u_fog_color.a * frag_depth; + float fog = 1.0f - exp(-(d * d)); + diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, fog); + }) + + if (bits & GLS_FOG_HEIGHT) + write_height_fog(buf); + + if (bits & GLS_FOG_SKY) + GLSL(diffuse.rgb = mix(diffuse.rgb, u_fog_color.rgb, u_fog_sky_factor);) + GLSL(o_color = diffuse;) GLSF("}\n"); } @@ -672,6 +734,25 @@ static void shader_setup_2d(void) gls.u_block.w_phase[1] = M_PIf * 10; } +static void shader_setup_fog(void) +{ + if (!glr.fog_bits) + return; + + VectorCopy(glr.fd.fog.color, gls.u_block.fog_color); + gls.u_block.fog_color[3] = glr.fd.fog.density / 64; + gls.u_block.fog_sky_factor = glr.fd.fog.sky_factor; + + VectorCopy(glr.fd.heightfog.start.color, gls.u_block.heightfog_start); + gls.u_block.heightfog_start[3] = glr.fd.heightfog.start.dist; + + VectorCopy(glr.fd.heightfog.end.color, gls.u_block.heightfog_end); + gls.u_block.heightfog_end[3] = glr.fd.heightfog.end.dist; + + gls.u_block.heightfog_density = glr.fd.heightfog.density; + gls.u_block.heightfog_falloff = glr.fd.heightfog.falloff; +} + static void shader_setup_3d(void) { gls.u_block.time = glr.fd.time; @@ -685,9 +766,15 @@ static void shader_setup_3d(void) gls.u_block.w_phase[0] = 4; gls.u_block.w_phase[1] = 4; + shader_setup_fog(); + R_RotateForSky(); - memcpy(gls.u_block.m_sky, glr.skymatrix, sizeof(glr.skymatrix)); + // setup default matrices for world + memcpy(gls.u_block.m_sky, glr.skymatrix, sizeof(gls.u_block.m_sky)); + memcpy(gls.u_block.m_model, gl_identity, sizeof(gls.u_block.m_model)); + + VectorCopy(glr.fd.vieworg, gls.u_block.vieworg); } static void shader_disable_state(void) diff --git a/src/refresh/sky.c b/src/refresh/sky.c index ef54248ee..b90236b9d 100644 --- a/src/refresh/sky.c +++ b/src/refresh/sky.c @@ -334,7 +334,7 @@ void R_DrawSkyBox(void) return; // nothing visible GL_BindArrays(VA_SPRITE); - GL_StateBits(GLS_TEXTURE_REPLACE); + GL_StateBits(GLS_TEXTURE_REPLACE | glr.fog_bits_sky); GL_ArrayBits(GLA_VERTEX | GLA_TC); for (i = 0; i < 6; i++) { diff --git a/src/refresh/state.c b/src/refresh/state.c index 309e9dfa6..ab66c5203 100644 --- a/src/refresh/state.c +++ b/src/refresh/state.c @@ -22,7 +22,7 @@ glState_t gls; const glbackend_t *gl_backend; -static const mat4_t identity = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 }; +const mat4_t gl_identity = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 }; // for uploading void GL_ForceTexture(glTmu_t tmu, GLuint texnum) @@ -219,7 +219,7 @@ void GL_Setup2D(void) if (gl_backend->setup_2d) gl_backend->setup_2d(); - gl_backend->load_matrix(GL_MODELVIEW, identity); + gl_backend->load_matrix(GL_MODELVIEW, gl_identity); } void GL_Frustum(GLfloat fov_x, GLfloat fov_y, GLfloat reflect_x) diff --git a/src/refresh/tess.c b/src/refresh/tess.c index 1a8eee154..8fe3f08d2 100644 --- a/src/refresh/tess.c +++ b/src/refresh/tess.c @@ -76,7 +76,7 @@ void GL_DrawParticles(void) GL_LoadUniforms(); GL_BindArrays(VA_EFFECT); - bits = (gl_partstyle->integer ? GLS_BLEND_ADD : GLS_BLEND_BLEND) | GLS_DEPTHMASK_FALSE; + bits = (gl_partstyle->integer ? GLS_BLEND_ADD : GLS_BLEND_BLEND) | GLS_DEPTHMASK_FALSE | glr.fog_bits; p = glr.fd.particles; total = glr.fd.num_particles; @@ -147,7 +147,7 @@ static void GL_FlushBeamSegments(void) array |= GLA_TC; GL_BindTexture(TMU_TEXTURE, texnum); - GL_StateBits(GLS_BLEND_BLEND | GLS_DEPTHMASK_FALSE); + GL_StateBits(GLS_BLEND_BLEND | GLS_DEPTHMASK_FALSE | glr.fog_bits); GL_ArrayBits(array); GL_DrawIndexed(SHOWTRIS_FX); @@ -667,6 +667,11 @@ void GL_Flush3D(void) if (!(state & GLS_TEXTURE_REPLACE)) array |= GLA_COLOR; + if (state & GLS_SKY_MASK) + state |= glr.fog_bits_sky; + else + state |= glr.fog_bits; + GL_StateBits(state); GL_ArrayBits(array); diff --git a/src/server/game.c b/src/server/game.c index 2f0647837..98bf90cee 100644 --- a/src/server/game.c +++ b/src/server/game.c @@ -1015,8 +1015,8 @@ void SV_InitGameProgs(void) Com_DPrintf("Game API version: %d\n", ge->apiversion); if (ge->apiversion != GAME_API_VERSION_OLD && ge->apiversion != GAME_API_VERSION_NEW) { - Com_Error(ERR_DROP, "Game library is version %d, expected %d", - ge->apiversion, GAME_API_VERSION_OLD); + Com_Error(ERR_DROP, "Game library is version %d, expected %d or %d", + ge->apiversion, GAME_API_VERSION_OLD, GAME_API_VERSION_NEW); } // get extended api if present diff --git a/src/server/main.c b/src/server/main.c index e3711c745..07bcd225b 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -1022,6 +1022,9 @@ static void init_pmove_and_es_flags(client_t *newcl) if (IS_NEW_GAME_API) { newcl->esFlags |= MSG_ES_EXTENSIONS_2; newcl->psFlags |= MSG_PS_EXTENSIONS_2; + if (newcl->version >= PROTOCOL_VERSION_Q2PRO_PLAYERFOG) { + newcl->psFlags |= MSG_PS_MOREBITS; + } } } force = 1; diff --git a/src/server/mvd.c b/src/server/mvd.c index 9cff1fae7..6886bbb03 100644 --- a/src/server/mvd.c +++ b/src/server/mvd.c @@ -2147,7 +2147,7 @@ void SV_MvdPostInit(void) if (IS_NEW_GAME_API) { mvd.esFlags |= MSG_ES_EXTENSIONS_2; - mvd.psFlags |= MSG_PS_EXTENSIONS_2; + mvd.psFlags |= MSG_PS_EXTENSIONS_2 | MSG_PS_MOREBITS; } } } diff --git a/src/server/mvd/game.c b/src/server/mvd/game.c index 1fdb46aba..509c4a6ab 100644 --- a/src/server/mvd/game.c +++ b/src/server/mvd/game.c @@ -775,7 +775,7 @@ void MVD_BroadcastPrintf(mvd_t *mvd, int level, int mask, const char *fmt, ...) } #define ES_MASK (MSG_ES_SHORTANGLES | MSG_ES_EXTENSIONS | MSG_ES_EXTENSIONS_2) -#define PS_MASK (MSG_PS_EXTENSIONS | MSG_PS_EXTENSIONS_2) +#define PS_MASK (MSG_PS_EXTENSIONS | MSG_PS_EXTENSIONS_2 | MSG_PS_MOREBITS) static void MVD_SetServerState(client_t *cl, mvd_t *mvd) { @@ -798,6 +798,9 @@ static void MVD_SetServerState(client_t *cl, mvd_t *mvd) cl->psFlags &= ~PS_MASK; cl->esFlags |= mvd->esFlags & ES_MASK; cl->psFlags |= mvd->psFlags & PS_MASK; + + if (cl->protocol != PROTOCOL_VERSION_Q2PRO || cl->version < PROTOCOL_VERSION_Q2PRO_PLAYERFOG) + cl->psFlags &= ~MSG_PS_MOREBITS; } void MVD_SwitchChannel(mvd_client_t *client, mvd_t *mvd) diff --git a/src/server/mvd/parse.c b/src/server/mvd/parse.c index cc5e4395e..576bf811b 100644 --- a/src/server/mvd/parse.c +++ b/src/server/mvd/parse.c @@ -737,6 +737,12 @@ static void MVD_ParsePacketPlayers(mvd_t *mvd) player = &mvd->players[number]; bits = MSG_ReadWord(); + if (bits & PPS_MOREBITS) { + if (mvd->psFlags & MSG_PS_MOREBITS) + bits |= (uint32_t)MSG_ReadByte() << 16; + else + bits |= PPS_REMOVE; // MOREBITS == REMOVE for old demos + } #if USE_DEBUG if (mvd_shownet->integer > 2) { @@ -929,6 +935,8 @@ static void MVD_ParseServerData(mvd_t *mvd, int extrabits) if (!(mvd->flags & MVF_EXTLIMITS)) { MVD_Destroyf(mvd, "MVF_EXTLIMITS_2 without MVF_EXTLIMITS"); } + if (mvd->version >= PROTOCOL_VERSION_MVD_PLAYERFOG) + mvd->psFlags |= MSG_PS_MOREBITS; } #if 0 From 1d54743bba2fee4084a1da6f7df6bbb01b8cff30 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 20 Oct 2024 22:37:18 +0300 Subject: [PATCH 3/6] Set fog params too if only for sky factor. --- src/refresh/shader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/refresh/shader.c b/src/refresh/shader.c index 9ca0e9ef7..1e0ae1b26 100644 --- a/src/refresh/shader.c +++ b/src/refresh/shader.c @@ -736,7 +736,7 @@ static void shader_setup_2d(void) static void shader_setup_fog(void) { - if (!glr.fog_bits) + if (!(glr.fog_bits | glr.fog_bits_sky)) return; VectorCopy(glr.fd.fog.color, gls.u_block.fog_color); From 887c468110c6d6bb9837b8c8e9192ece493f01fa Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 20 Oct 2024 22:40:31 +0300 Subject: [PATCH 4/6] Explicitly use ubuntu-24.04 for mingw CI. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d77e21be1..9eec5fed9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ env: jobs: mingw: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 strategy: matrix: arch: ["i686", "x86_64"] From 218da0a1b7b79f8d63790a226aa74bc7a4d3ec44 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 21 Oct 2024 01:56:16 +0300 Subject: [PATCH 5/6] Enable fog for shadows/celshading. --- src/refresh/mesh.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/refresh/mesh.c b/src/refresh/mesh.c index 8e901519c..29fb16bab 100644 --- a/src/refresh/mesh.c +++ b/src/refresh/mesh.c @@ -43,7 +43,8 @@ static bool dotshading; static float celscale; static drawshadow_t drawshadow; -static mat4_t shadowmatrix; +static mat4_t m_shadow_view; +static mat4_t m_shadow_model; // fog hack #if USE_MD5 static md5_joint_t temp_skeleton[MD5_MAX_JOINTS]; @@ -439,7 +440,7 @@ static void draw_celshading(const uint16_t *indices, int num_indices) return; GL_BindTexture(TMU_TEXTURE, TEXNUM_BLACK); - GL_StateBits(GLS_BLEND_BLEND | (meshbits & ~GLS_MESH_SHADE)); + GL_StateBits(GLS_BLEND_BLEND | (meshbits & ~GLS_MESH_SHADE) | glr.fog_bits); if (gls.currentva) GL_ArrayBits(GLA_VERTEX); @@ -499,7 +500,7 @@ static drawshadow_t cull_shadow(const model_t *model) return SHADOW_YES; } -static void proj_matrix(GLfloat *matrix, const cplane_t *plane, const vec3_t dir) +static void proj_matrix(mat4_t matrix, const cplane_t *plane, const vec3_t dir) { matrix[ 0] = plane->normal[1] * dir[1] + plane->normal[2] * dir[2]; matrix[ 4] = -plane->normal[1] * dir[0]; @@ -524,7 +525,7 @@ static void proj_matrix(GLfloat *matrix, const cplane_t *plane, const vec3_t dir static void setup_shadow(void) { - mat4_t matrix, tmp; + mat4_t m_proj, m_rot; vec3_t dir; if (!drawshadow) @@ -537,12 +538,13 @@ static void setup_shadow(void) VectorSet(dir, 0, 0, 1); // project shadow on ground plane - proj_matrix(matrix, &glr.lightpoint.plane, dir); - GL_MultMatrix(tmp, glr.viewmatrix, matrix); + proj_matrix(m_proj, &glr.lightpoint.plane, dir); // rotate for entity - GL_RotationMatrix(matrix); - GL_MultMatrix(shadowmatrix, tmp, matrix); + GL_RotationMatrix(m_rot); + + GL_MultMatrix(m_shadow_model, m_proj, m_rot); + GL_MultMatrix(m_shadow_view, glr.viewmatrix, m_shadow_model); } static void draw_shadow(const uint16_t *indices, int num_indices) @@ -550,8 +552,12 @@ static void draw_shadow(const uint16_t *indices, int num_indices) if (!drawshadow) return; + // fog hack + if (glr.fog_bits) + memcpy(gls.u_block.m_model, m_shadow_model, sizeof(gls.u_block.m_model)); + // load shadow projection matrix - GL_LoadMatrix(shadowmatrix); + GL_LoadMatrix(m_shadow_view); // eliminate z-fighting by utilizing stencil buffer, if available if (gl_config.stencilbits) { @@ -561,7 +567,7 @@ static void draw_shadow(const uint16_t *indices, int num_indices) } GL_BindTexture(TMU_TEXTURE, TEXNUM_WHITE); - GL_StateBits(GLS_BLEND_BLEND | (meshbits & ~GLS_MESH_SHADE)); + GL_StateBits(GLS_BLEND_BLEND | (meshbits & ~GLS_MESH_SHADE) | glr.fog_bits); if (gls.currentva) GL_ArrayBits(GLA_VERTEX); @@ -580,6 +586,10 @@ static void draw_shadow(const uint16_t *indices, int num_indices) qglDisable(GL_STENCIL_TEST); gl_static.stencil_buffer_bit |= GL_STENCIL_BUFFER_BIT; } + + // fog hack + if (glr.fog_bits) + GL_RotationMatrix(gls.u_block.m_model); } static const image_t *skin_for_mesh(image_t **skins, int num_skins) From 3e90b4da13359d22c4cf684a87f81c7d7effa82a Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 21 Oct 2024 02:00:08 +0300 Subject: [PATCH 6/6] =?UTF-8?q?Make=20=E2=80=98gl=5Ffog=E2=80=99=20a=20sim?= =?UTF-8?q?ple=20off/on=20toggle.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/client.asciidoc | 6 ++---- src/refresh/main.c | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/doc/client.asciidoc b/doc/client.asciidoc index ed028c26d..94e37f7c8 100644 --- a/doc/client.asciidoc +++ b/doc/client.asciidoc @@ -688,10 +688,8 @@ gl_waterwarp:: GLSL backend. Default value is 0 (disabled). gl_fog:: - Enable re-release fog effect. Default value is 2. - - 0 — disable fog - - 1 — enable global fog - - 2 — enable global fog and height fog + Enable re-release fog effect. Only effective when using GLSL backend. + Default value is 1 (enabled). gl_flarespeed:: Specifies flare fading effect speed. Default value is 8. Set this to 0 diff --git a/src/refresh/main.c b/src/refresh/main.c index 6e56855af..84a3ace5d 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -668,10 +668,10 @@ void R_RenderFrame(const refdef_t *fd) if (gl_dynamic->integer != 1 || gl_vertexlight->integer) glr.fd.num_dlights = 0; - if (gl_fog->integer > 0) { + if (gl_static.use_shaders && gl_fog->integer > 0) { if (glr.fd.fog.density > 0) glr.fog_bits |= GLS_FOG_GLOBAL; - if (glr.fd.heightfog.density > 0 && glr.fd.heightfog.falloff > 0 && gl_fog->integer > 1) + if (glr.fd.heightfog.density > 0 && glr.fd.heightfog.falloff > 0) glr.fog_bits |= GLS_FOG_HEIGHT; if (glr.fd.fog.sky_factor > 0) glr.fog_bits_sky |= GLS_FOG_SKY; @@ -913,7 +913,7 @@ static void GL_Register(void) #endif gl_damageblend_frac = Cvar_Get("gl_damageblend_frac", "0.2", 0); gl_waterwarp = Cvar_Get("gl_waterwarp", "0", 0); - gl_fog = Cvar_Get("gl_fog", "2", 0); + gl_fog = Cvar_Get("gl_fog", "1", 0); gl_swapinterval = Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_swapinterval->changed = gl_swapinterval_changed;