From 3217fad2d6ac515f8f467e57e8e59b0fb14bd2a4 Mon Sep 17 00:00:00 2001 From: Andrei Drexler Date: Wed, 5 Jan 2022 16:39:37 +0200 Subject: [PATCH] Lit water support (issue #10) --- Quake/gl_model.c | 9 ++++++++- Quake/gl_model.h | 1 + Quake/gl_rmain.c | 1 + Quake/gl_rmisc.c | 1 + Quake/gl_shaders.c | 10 +++++----- Quake/gl_shaders.h | 22 +++++++++++++++------- Quake/glquake.h | 6 +++++- Quake/r_brush.c | 31 +++++++++++++++++++++++++++---- Quake/r_world.c | 13 +++++++++---- 9 files changed, 72 insertions(+), 22 deletions(-) diff --git a/Quake/gl_model.c b/Quake/gl_model.c index 66f8ec9b6..778c3c733 100644 --- a/Quake/gl_model.c +++ b/Quake/gl_model.c @@ -1225,7 +1225,14 @@ void Mod_LoadFaces (lump_t *l, qboolean bsp2) } else if (TEXTYPE_ISLIQUID (texture->type)) { - out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + out->flags |= SURF_DRAWTURB; + if (out->texinfo->flags & TEX_SPECIAL) + out->flags |= SURF_DRAWTILED; + else if (out->samples && !loadmodel->haslitwater) + { + Con_DPrintf ("Map has lit water\n"); + loadmodel->haslitwater = true; + } if (texture->type == TEXTYPE_LAVA) out->flags |= SURF_DRAWLAVA; diff --git a/Quake/gl_model.h b/Quake/gl_model.h index 66c3be55b..d1875a55c 100644 --- a/Quake/gl_model.h +++ b/Quake/gl_model.h @@ -508,6 +508,7 @@ typedef struct qmodel_s int bspversion; int contentstransparent; //spike -- added this so we can disable glitchy wateralpha where its not supported. + qboolean haslitwater; // // alias model diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index dc9d599a1..a7aa9c2a1 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -70,6 +70,7 @@ cvar_t r_fullbright = {"r_fullbright","0",CVAR_NONE}; cvar_t r_lightmap = {"r_lightmap","0",CVAR_NONE}; cvar_t r_shadows = {"r_shadows","0",CVAR_ARCHIVE}; cvar_t r_wateralpha = {"r_wateralpha","1",CVAR_ARCHIVE}; +cvar_t r_litwater = {"r_litwater","1",CVAR_NONE}; cvar_t r_dynamic = {"r_dynamic","1",CVAR_ARCHIVE}; cvar_t r_novis = {"r_novis","0",CVAR_ARCHIVE}; #if defined(USE_SIMD) diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index daefd5ee7..0835b5e49 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -201,6 +201,7 @@ void R_Init (void) Cvar_RegisterVariable (&r_shadows); Cvar_RegisterVariable (&r_wateralpha); Cvar_SetCallback (&r_wateralpha, R_SetWateralpha_f); + Cvar_RegisterVariable (&r_litwater); Cvar_RegisterVariable (&r_dynamic); Cvar_RegisterVariable (&r_novis); #if defined(USE_SIMD) diff --git a/Quake/gl_shaders.c b/Quake/gl_shaders.c index 1f4a6bc72..dc2b3338f 100644 --- a/Quake/gl_shaders.c +++ b/Quake/gl_shaders.c @@ -252,7 +252,7 @@ GL_CreateShaders */ void GL_CreateShaders (void) { - int palettize, dither, alphatest, warp; + int palettize, dither, mode, warp; glprogs.gui = GL_CreateProgram (gui_vertex_shader, gui_fragment_shader, "gui"); glprogs.viewblend = GL_CreateProgram (viewblend_vertex_shader, viewblend_fragment_shader, "viewblend"); @@ -262,13 +262,13 @@ void GL_CreateShaders (void) glprogs.postprocess[palettize] = GL_CreateProgram (postprocess_vertex_shader, postprocess_fragment_shader, "postprocess|PALETTIZE %d", palettize); for (dither = 0; dither < 3; dither++) - for (alphatest = 0; alphatest < 2; alphatest++) - glprogs.world[dither][alphatest] = GL_CreateProgram (world_vertex_shader, world_fragment_shader, "world|DITHER %d; ALPHATEST %d", dither, alphatest); + for (mode = 0; mode < 3; mode++) + glprogs.world[dither][mode] = GL_CreateProgram (world_vertex_shader, world_fragment_shader, "world|DITHER %d; MODE %d", dither, mode); for (dither = 0; dither < 2; dither++) { - for (alphatest = 0; alphatest < 2; alphatest++) - glprogs.alias[dither][alphatest] = GL_CreateProgram (alias_vertex_shader, alias_fragment_shader, "alias|DITHER %d; ALPHATEST %d", dither, alphatest); + for (mode = 0; mode < 2; mode++) + glprogs.alias[dither][mode] = GL_CreateProgram (alias_vertex_shader, alias_fragment_shader, "alias|DITHER %d; ALPHATEST %d", dither, mode); glprogs.water[dither] = GL_CreateProgram (water_vertex_shader, water_fragment_shader, "water|DITHER %d", dither); glprogs.skylayers[dither] = GL_CreateProgram (sky_layers_vertex_shader, sky_layers_fragment_shader, "sky layers|DITHER %d", dither); glprogs.skycubemap[dither] = GL_CreateProgram (sky_cubemap_vertex_shader, sky_cubemap_fragment_shader, "sky cubemap|DITHER %d", dither); diff --git a/Quake/gl_shaders.h b/Quake/gl_shaders.h index e73c92476..9bf47dad3 100644 --- a/Quake/gl_shaders.h +++ b/Quake/gl_shaders.h @@ -468,7 +468,11 @@ BINDLESS_VERTEX_HEADER " out_depth = gl_Position.w;\n" " out_coord = (gl_Position.xy / gl_Position.w * 0.5 + 0.5) * vec2(LIGHT_TILES_X, LIGHT_TILES_Y);\n" " out_flags = call.flags;\n" +"#if MODE == " QS_STRINGIFY (WORLDSHADER_WATER) "\n" +" out_alpha = instance.alpha < 0.0 ? call.wateralpha : instance.alpha;\n" +"#else\n" " out_alpha = instance.alpha < 0.0 ? 1.0 : instance.alpha;\n" +"#endif\n" " vec4 styles = textureLod(LightmapStyles, in_uv.zw, 0.);\n" " out_styles.x = GetLightStyle(int(styles.x * 255. + .5));\n" " if (styles.y > 0.5)\n" @@ -510,7 +514,7 @@ NOISE_FUNCTIONS "layout(location=0) flat in uint in_flags;\n" "layout(location=1) flat in float in_alpha;\n" "layout(location=2) in vec3 in_pos;\n" -"#if ALPHATEST\n" +"#if MODE == " QS_STRINGIFY (WORLDSHADER_ALPHATEST) "\n" " layout(location=3) centroid in vec2 in_uv;\n" "#else\n" " layout(location=3) in vec2 in_uv;\n" @@ -528,26 +532,30 @@ NOISE_FUNCTIONS "void main()\n" "{\n" " vec3 fullbright = vec3(0.);\n" +" vec2 uv = in_uv;\n" +"#if MODE == " QS_STRINGIFY (WORLDSHADER_WATER) "\n" +" uv = uv * 2.0 + 0.125 * sin(in_uv.yx * (3.14159265 * 2.0) + Time);\n" +"#endif\n" "#if BINDLESS\n" " sampler2D Tex = sampler2D(in_samplers.xy);\n" " sampler2D FullbrightTex;\n" " if ((in_flags & CF_USE_FULLBRIGHT) != 0u)\n" " {\n" " FullbrightTex = sampler2D(in_samplers.zw);\n" -" fullbright = texture(FullbrightTex, in_uv).rgb;\n" +" fullbright = texture(FullbrightTex, uv).rgb;\n" " }\n" "#else\n" " if ((in_flags & CF_USE_FULLBRIGHT) != 0u)\n" -" fullbright = texture(FullbrightTex, in_uv).rgb;\n" +" fullbright = texture(FullbrightTex, uv).rgb;\n" "#endif\n" "#if DITHER >= 2\n" -" vec4 result = texture(Tex, in_uv, -1.0);\n" +" vec4 result = texture(Tex, uv, -1.0);\n" "#elif DITHER\n" -" vec4 result = texture(Tex, in_uv, -0.5);\n" +" vec4 result = texture(Tex, uv, -0.5);\n" "#else\n" -" vec4 result = texture(Tex, in_uv);\n" +" vec4 result = texture(Tex, uv);\n" "#endif\n" -"#if ALPHATEST\n" +"#if MODE == " QS_STRINGIFY (WORLDSHADER_ALPHATEST) "\n" " if (result.a < 0.666)\n" " discard;\n" "#endif\n" diff --git a/Quake/glquake.h b/Quake/glquake.h index 616c0e80d..60d807450 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -102,6 +102,7 @@ extern cvar_t r_wateralpha; extern cvar_t r_lavaalpha; extern cvar_t r_telealpha; extern cvar_t r_slimealpha; +extern cvar_t r_litwater; extern cvar_t r_dynamic; extern cvar_t r_novis; extern cvar_t r_scale; @@ -459,6 +460,9 @@ void GLMesh_DeleteVertexBuffers (void); int R_LightPoint (vec3_t p, lightcache_t *cache); +#define WORLDSHADER_SOLID 0 +#define WORLDSHADER_ALPHATEST 1 +#define WORLDSHADER_WATER 2 typedef struct glprogs_s { /* 2d */ @@ -468,7 +472,7 @@ typedef struct glprogs_s { GLuint postprocess[3]; // [palettize:off/dithered/direct] /* 3d */ - GLuint world[3][2]; // [dither][alpha test] + GLuint world[3][3]; // [dither][mode:solid/alpha test/water] GLuint water[2]; // [dither] GLuint skystencil; GLuint skylayers[2]; // [dither] diff --git a/Quake/r_brush.c b/Quake/r_brush.c index 12d99c97d..7cdb425a3 100644 --- a/Quake/r_brush.c +++ b/Quake/r_brush.c @@ -188,8 +188,14 @@ static int AllocBlock (int w, int h, int *x, int *y) lightmap_count++; lightmaps = (lightmap_t *) realloc(lightmaps, sizeof(*lightmaps)*lightmap_count); memset(&lightmaps[texnum], 0, sizeof(lightmaps[texnum])); - //as we're only tracking one texture, we don't need multiple copies any more. + // as we're only tracking one texture, we don't need multiple copies any more. Chart_Init (&lightmap_chart, LMBLOCK_WIDTH, LMBLOCK_HEIGHT); + // reserve 1 texel for unlit water surfaces in maps with lit water + if (lightmap_count == 1) + { + lightmap_chart.x = 1; + lightmap_chart.allocated[0] = 1; + } } if (!Chart_Add (&lightmap_chart, w, h, x, y)) @@ -463,6 +469,9 @@ void GL_BuildLightmaps (void) lm->yofs = (i / xblocks) * LMBLOCK_HEIGHT; } + // fill reserved texel + lightmap_layers[0][0] = 0xff808080u; + // fill lightmap samples for (i = 0, j = VEC_SIZE (lit_surfs); i < j; i++) GL_FillSurfaceLightmap (lit_surfs[i]); @@ -599,9 +608,17 @@ void GL_BuildBModelVertexBuffer (void) } else { - texscalex = 1.f / texture->width; - texscaley = 1.f / texture->height; - useofs = 1.f; + if (fa->flags & SURF_DRAWTURB) + { + texscaley = texscalex = 1.f / 128.f; //warp animation repeats every 128 + useofs = 0.f; + } + else + { + texscalex = 1.f / texture->width; + texscaley = 1.f / texture->height; + useofs = 1.f; + } lm = &lightmaps[fa->lightmaptexturenum]; } @@ -665,6 +682,12 @@ void GL_BuildBModelVertexBuffer (void) verts[5] = s; verts[6] = t; } + else + { + // first lightmap texel is fullbright + verts[5] = 0.5f / lightmap_width; + verts[6] = 0.5f / lightmap_height; + } } } } diff --git a/Quake/r_world.c b/Quake/r_world.c index afe9fa72f..f30ab99ba 100644 --- a/Quake/r_world.c +++ b/Quake/r_world.c @@ -539,12 +539,12 @@ static void R_DrawBrushModels_Real (entity_t **ents, int count, brushpass_t pass case BP_SOLID: texbegin = 0; texend = TEXTYPE_CUTOUT; - program = glprogs.world[q_max(0, (int)softemu - 1)][0]; + program = glprogs.world[q_max(0, (int)softemu - 1)][WORLDSHADER_SOLID]; break; case BP_ALPHATEST: texbegin = TEXTYPE_CUTOUT; texend = TEXTYPE_CUTOUT + 1; - program = glprogs.world[q_max(0, (int)softemu - 1)][1]; + program = glprogs.world[q_max(0, (int)softemu - 1)][WORLDSHADER_ALPHATEST]; break; case BP_SKYLAYERS: texbegin = TEXTYPE_SKY; @@ -653,7 +653,7 @@ void R_DrawBrushModels_Water (entity_t **ents, int count, qboolean translucent) int i, j; int totalinst, baseinst; unsigned state; - GLuint buf; + GLuint buf, program; GLbyte *ofs; if (count > countof(bmodel_instances)) @@ -679,7 +679,12 @@ void R_DrawBrushModels_Water (entity_t **ents, int count, qboolean translucent) else state |= GLS_BLEND_OPAQUE; - R_ResetBModelCalls (glprogs.water[softemu == SOFTEMU_COARSE]); + if (cl.worldmodel->haslitwater && r_litwater.value) + program = glprogs.world[q_max(0, (int)softemu - 1)][WORLDSHADER_WATER]; + else + program = glprogs.water[softemu == SOFTEMU_COARSE]; + + R_ResetBModelCalls (program); GL_SetState (state); GL_Bind (GL_TEXTURE2, r_fullbright_cheatsafe ? greytexture : lightmap_texture);