From 104177db8b51eaa81208bd2b01f79b61d39cead0 Mon Sep 17 00:00:00 2001 From: havoc Date: Tue, 26 May 2020 08:34:12 +0000 Subject: [PATCH] Added lightgrid texture based lighting in q3bsp maps by uploading it as a 3D texture, to make more accurate and continuous model lighting throughout a scene. Can be disabled via the mod_q3bsp_lightgrid_texture cvar. The lightgrid texture based lighting can also be enabled on world surfaces if one desires (mod_q3bsp_lightgrid_world_surfaces cvar) which gives levels a dream-like quality that is globally consistent, can also be enabled for bsp models that are not the world (mod_q3bsp_lightgrid_bsp_surfaces cvar) which may be more useful for ammo pickup models that are bsp or similar. This frobs the shader glsl crc so expect to update any modified combined*.glsl files as per usual with updates to the embedded shader code. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12584 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_main.c | 7 +++ client.h | 4 +- gl_rmain.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++-- model_brush.c | 84 ++++++++++++++++++++++++++++++++++ model_brush.h | 2 + model_shared.h | 9 +++- render.h | 1 + shader_glsl.h | 50 +++++++++++++++++++-- 8 files changed, 266 insertions(+), 10 deletions(-) diff --git a/cl_main.c b/cl_main.c index fc03dc69..5d22ba7b 100644 --- a/cl_main.c +++ b/cl_main.c @@ -2489,6 +2489,7 @@ static void CL_UpdateEntityShading_Entity(entity_render_t *ent) for (q = 0; q < 3; q++) a[q] = c[q] = dir[q] = 0; + ent->render_lightgrid = false; ent->render_modellight_forced = false; ent->render_rtlight_disabled = false; @@ -2569,6 +2570,12 @@ static void CL_UpdateEntityShading_Entity(entity_render_t *ent) ent->render_modellight_forced = true; ent->render_rtlight_disabled = true; } + else if (((ent->model && !ent->model->lit) || (ent->model == r_refdef.scene.worldmodel ? mod_q3bsp_lightgrid_world_surfaces.integer : mod_q3bsp_lightgrid_bsp_surfaces.integer)) + && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->lit && r_refdef.scene.worldmodel->brushq3.lightgridtexture && mod_q3bsp_lightgrid_texture.integer) + { + ent->render_lightgrid = true; + // no need to call R_CompleteLightPoint as we base it on render_lightmap_* + } else if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->lit && r_refdef.scene.worldmodel->brush.LightPoint) R_CompleteLightPoint(a, c, dir, shadingorigin, LP_LIGHTMAP, r_refdef.scene.lightmapintensity, r_refdef.scene.ambientintensity); else if (r_fullbright_directed.integer) diff --git a/client.h b/client.h index 45312d96..b1dbac0e 100644 --- a/client.h +++ b/client.h @@ -585,10 +585,12 @@ typedef struct entity_render_s // rtlights use these colors for the materials on this entity float render_rtlight_diffuse[3]; float render_rtlight_specular[3]; - // ignore lightmap and use lightgrid on this entity (e.g. FULLBRIGHT) + // ignore lightmap and use fixed lighting settings on this entity (e.g. FULLBRIGHT) qboolean render_modellight_forced; // do not process per pixel lights on this entity at all (like MATERIALFLAG_NORTLIGHT) qboolean render_rtlight_disabled; + // use the 3D lightmap from q3bsp on this entity + qboolean render_lightgrid; // storage of decals on this entity // (note: if allowdecals is set, be sure to call R_DecalSystem_Reset on removal!) diff --git a/gl_rmain.c b/gl_rmain.c index 053a91c4..7929d2f7 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -693,6 +693,7 @@ shadermodeinfo_t shadermodeinfo[SHADERLANGUAGE_COUNT][SHADERMODE_COUNT] = {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"}, {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP\n", " lightdirectionmap_forced_lightmap"}, {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR\n", " lightdirectionmap_forced_vertexcolor"}, + {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTGRID\n", " lightgrid"}, {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTDIRECTION\n", " lightdirection"}, {"combined", "glsl", builtinshaderstrings, "#define MODE_LIGHTSOURCE\n", " lightsource"}, {"combined", "glsl", builtinshaderstrings, "#define MODE_REFRACTION\n", " refraction"}, @@ -730,6 +731,7 @@ typedef struct r_glsl_permutation_s int tex_Texture_Shirt; int tex_Texture_FogHeightTexture; int tex_Texture_FogMask; + int tex_Texture_LightGrid; int tex_Texture_Lightmap; int tex_Texture_Deluxemap; int tex_Texture_Attenuation; @@ -760,6 +762,7 @@ typedef struct r_glsl_permutation_s int loc_Texture_Shirt; int loc_Texture_FogHeightTexture; int loc_Texture_FogMask; + int loc_Texture_LightGrid; int loc_Texture_Lightmap; int loc_Texture_Deluxemap; int loc_Texture_Attenuation; @@ -797,6 +800,8 @@ typedef struct r_glsl_permutation_s int loc_FogRangeRecip; int loc_LightColor; int loc_LightDir; + int loc_LightGridMatrix; + int loc_LightGridNormalMatrix; int loc_LightPosition; int loc_OffsetMapping_ScaleSteps; int loc_OffsetMapping_LodDistance; @@ -1192,6 +1197,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->loc_Texture_Shirt = qglGetUniformLocation(p->program, "Texture_Shirt"); p->loc_Texture_FogHeightTexture = qglGetUniformLocation(p->program, "Texture_FogHeightTexture"); p->loc_Texture_FogMask = qglGetUniformLocation(p->program, "Texture_FogMask"); + p->loc_Texture_LightGrid = qglGetUniformLocation(p->program, "Texture_LightGrid"); p->loc_Texture_Lightmap = qglGetUniformLocation(p->program, "Texture_Lightmap"); p->loc_Texture_Deluxemap = qglGetUniformLocation(p->program, "Texture_Deluxemap"); p->loc_Texture_Attenuation = qglGetUniformLocation(p->program, "Texture_Attenuation"); @@ -1228,6 +1234,8 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->loc_FogPlaneViewDist = qglGetUniformLocation(p->program, "FogPlaneViewDist"); p->loc_FogRangeRecip = qglGetUniformLocation(p->program, "FogRangeRecip"); p->loc_LightColor = qglGetUniformLocation(p->program, "LightColor"); + p->loc_LightGridMatrix = qglGetUniformLocation(p->program, "LightGridMatrix"); + p->loc_LightGridNormalMatrix = qglGetUniformLocation(p->program, "LightGridNormalMatrix"); p->loc_LightDir = qglGetUniformLocation(p->program, "LightDir"); p->loc_LightPosition = qglGetUniformLocation(p->program, "LightPosition"); p->loc_OffsetMapping_ScaleSteps = qglGetUniformLocation(p->program, "OffsetMapping_ScaleSteps"); @@ -1280,6 +1288,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->tex_Texture_Shirt = -1; p->tex_Texture_FogHeightTexture = -1; p->tex_Texture_FogMask = -1; + p->tex_Texture_LightGrid = -1; p->tex_Texture_Lightmap = -1; p->tex_Texture_Deluxemap = -1; p->tex_Texture_Attenuation = -1; @@ -1311,6 +1320,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode if (p->loc_Texture_Shirt >= 0) {p->tex_Texture_Shirt = sampler;qglUniform1i(p->loc_Texture_Shirt , sampler);sampler++;} if (p->loc_Texture_FogHeightTexture>= 0) {p->tex_Texture_FogHeightTexture = sampler;qglUniform1i(p->loc_Texture_FogHeightTexture, sampler);sampler++;} if (p->loc_Texture_FogMask >= 0) {p->tex_Texture_FogMask = sampler;qglUniform1i(p->loc_Texture_FogMask , sampler);sampler++;} + if (p->loc_Texture_LightGrid >= 0) {p->tex_Texture_LightGrid = sampler;qglUniform1i(p->loc_Texture_LightGrid , sampler);sampler++;} if (p->loc_Texture_Lightmap >= 0) {p->tex_Texture_Lightmap = sampler;qglUniform1i(p->loc_Texture_Lightmap , sampler);sampler++;} if (p->loc_Texture_Deluxemap >= 0) {p->tex_Texture_Deluxemap = sampler;qglUniform1i(p->loc_Texture_Deluxemap , sampler);sampler++;} if (p->loc_Texture_Attenuation >= 0) {p->tex_Texture_Attenuation = sampler;qglUniform1i(p->loc_Texture_Attenuation , sampler);sampler++;} @@ -1690,6 +1700,67 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif if (vid.allowalphatocoverage) GL_AlphaToCoverage(false); } + else if (t->currentmaterialflags & MATERIALFLAG_LIGHTGRID) + { + if (r_glsl_offsetmapping.integer && ((R_TextureFlags(t->nmaptexture) & TEXF_ALPHA) || t->offsetbias != 0.0f)) + { + switch(t->offsetmapping) + { + case OFFSETMAPPING_LINEAR: permutation |= SHADERPERMUTATION_OFFSETMAPPING;break; + case OFFSETMAPPING_RELIEF: permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;break; + case OFFSETMAPPING_DEFAULT: permutation |= SHADERPERMUTATION_OFFSETMAPPING;if (r_glsl_offsetmapping_reliefmapping.integer) permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;break; + case OFFSETMAPPING_OFF: break; + } + } + if (t->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND) + permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND; + if (t->currentmaterialflags & MATERIALFLAG_ALPHAGEN_VERTEX) + permutation |= SHADERPERMUTATION_ALPHAGEN_VERTEX; + // directional model lighting + mode = SHADERMODE_LIGHTGRID; + if ((t->glowtexture || t->backgroundglowtexture) && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) + permutation |= SHADERPERMUTATION_GLOW; + permutation |= SHADERPERMUTATION_DIFFUSE; + if (t->glosstexture || t->backgroundglosstexture) + permutation |= SHADERPERMUTATION_SPECULAR; + if (r_refdef.fogenabled) + permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE); + if (t->colormapping) + permutation |= SHADERPERMUTATION_COLORMAPPING; + if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW)) + { + permutation |= SHADERPERMUTATION_SHADOWMAPORTHO; + permutation |= SHADERPERMUTATION_SHADOWMAP2D; + + if (r_shadow_shadowmap2ddepthbuffer) + permutation |= SHADERPERMUTATION_DEPTHRGB; + } + if (t->currentmaterialflags & MATERIALFLAG_REFLECTION) + permutation |= SHADERPERMUTATION_REFLECTION; + if (r_shadow_usingdeferredprepass && !(t->currentmaterialflags & MATERIALFLAG_BLENDED)) + permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP; + if (t->reflectmasktexture) + permutation |= SHADERPERMUTATION_REFLECTCUBE; + if (r_shadow_bouncegrid_state.texture && cl.csqc_vidvars.drawworld && !notrippy) + { + permutation |= SHADERPERMUTATION_BOUNCEGRID; + if (r_shadow_bouncegrid_state.directional) + permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL; + } + GL_BlendFunc(t->currentblendfunc[0], t->currentblendfunc[1]); + blendfuncflags = R_BlendFuncFlags(t->currentblendfunc[0], t->currentblendfunc[1]); + // when using alphatocoverage, we don't need alphakill + if (vid.allowalphatocoverage) + { + if (r_transparent_alphatocoverage.integer) + { + GL_AlphaToCoverage((t->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0); + permutation &= ~SHADERPERMUTATION_ALPHAKILL; + } + else + GL_AlphaToCoverage(false); + } + } else if (t->currentmaterialflags & MATERIALFLAG_MODELLIGHT) { if (r_glsl_offsetmapping.integer && ((R_TextureFlags(t->nmaptexture) & TEXF_ALPHA) || t->offsetbias != 0.0f)) @@ -1879,6 +1950,13 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif { if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, t->render_modellight_ambient[0], t->render_modellight_ambient[1], t->render_modellight_ambient[2]); } + else if (mode == SHADERMODE_LIGHTGRID) + { + if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, t->render_lightmap_ambient[0], t->render_lightmap_ambient[1], t->render_lightmap_ambient[2]); + if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Diffuse, t->render_lightmap_diffuse[0], t->render_lightmap_diffuse[1], t->render_lightmap_diffuse[2]); + if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Specular, t->render_lightmap_specular[0], t->render_lightmap_specular[1], t->render_lightmap_specular[2]); + // other LightGrid uniforms handled below + } else if (mode == SHADERMODE_LIGHTDIRECTION) { if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, t->render_modellight_ambient[0], t->render_modellight_ambient[1], t->render_modellight_ambient[2]); @@ -1962,6 +2040,19 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2f(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height); if (r_glsl_permutation->loc_BounceGridMatrix >= 0) {Matrix4x4_Concat(&tempmatrix, &r_shadow_bouncegrid_state.matrix, &rsurface.matrix);Matrix4x4_ToArrayFloatGL(&tempmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_BounceGridMatrix, 1, false, m16f);} if (r_glsl_permutation->loc_BounceGridIntensity >= 0) qglUniform1f(r_glsl_permutation->loc_BounceGridIntensity, r_shadow_bouncegrid_state.intensity*r_refdef.view.colorscale); + if (r_glsl_permutation->loc_LightGridMatrix >= 0 && r_refdef.scene.worldmodel) + { + float m9f[9]; + Matrix4x4_Concat(&tempmatrix, &r_refdef.scene.worldmodel->brushq3.lightgridworldtotexturematrix, &rsurface.matrix); + Matrix4x4_ToArrayFloatGL(&tempmatrix, m16f); + qglUniformMatrix4fv(r_glsl_permutation->loc_LightGridMatrix, 1, false, m16f); + Matrix4x4_Normalize3(&tempmatrix, &rsurface.matrix); + Matrix4x4_ToArrayFloatGL(&tempmatrix, m16f); + m9f[0] = m16f[0];m9f[1] = m16f[1];m9f[2] = m16f[2]; + m9f[3] = m16f[4];m9f[4] = m16f[5];m9f[5] = m16f[6]; + m9f[6] = m16f[8];m9f[7] = m16f[9];m9f[8] = m16f[10]; + qglUniformMatrix3fv(r_glsl_permutation->loc_LightGridNormalMatrix, 1, false, m9f); + } if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , r_texture_white ); if (r_glsl_permutation->tex_Texture_Second >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second , r_texture_white ); @@ -2006,6 +2097,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif } } if (r_glsl_permutation->tex_Texture_BounceGrid >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_BounceGrid, r_shadow_bouncegrid_state.texture); + if (r_glsl_permutation->tex_Texture_LightGrid >= 0 && r_refdef.scene.worldmodel) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_LightGrid, r_refdef.scene.worldmodel->brushq3.lightgridtexture); CHECKGLERROR break; } @@ -6618,10 +6710,12 @@ texture_t *R_GetCurrentTexture(texture_t *t) t->currentmaterialflags |= MATERIALFLAG_MODELLIGHT; if (rsurface.entity->render_rtlight_disabled) t->currentmaterialflags |= MATERIALFLAG_NORTLIGHT; + if (rsurface.entity->render_lightgrid) + t->currentmaterialflags |= MATERIALFLAG_LIGHTGRID; if (t->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND && !(R_BlendFuncFlags(t->customblendfunc[0], t->customblendfunc[1]) & BLENDFUNC_ALLOWS_COLORMOD)) { // some CUSTOMBLEND blendfuncs are too weird, we have to ignore colormod and view colorscale - t->currentmaterialflags = t->currentmaterialflags | MATERIALFLAG_MODELLIGHT | MATERIALFLAG_NORTLIGHT; + t->currentmaterialflags = (t->currentmaterialflags | MATERIALFLAG_MODELLIGHT | MATERIALFLAG_NORTLIGHT) & ~MATERIALFLAG_LIGHTGRID; for (q = 0; q < 3; q++) { t->render_glowmod[q] = rsurface.entity->glowmod[q]; @@ -6639,7 +6733,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) else if ((t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT) || !(rsurface.ent_flags & RENDER_LIGHT)) { // fullbright is basically MATERIALFLAG_MODELLIGHT but with ambient locked to 1,1,1 and no shading - t->currentmaterialflags = t->currentmaterialflags | MATERIALFLAG_NORTLIGHT | MATERIALFLAG_MODELLIGHT; + t->currentmaterialflags = (t->currentmaterialflags | MATERIALFLAG_NORTLIGHT | MATERIALFLAG_MODELLIGHT) & ~MATERIALFLAG_LIGHTGRID; for (q = 0; q < 3; q++) { t->render_glowmod[q] = rsurface.entity->render_glowmod[q] * r_refdef.view.colorscale; @@ -6654,10 +6748,27 @@ texture_t *R_GetCurrentTexture(texture_t *t) t->render_rtlight_specular[q] = 0; } } + else if (t->currentmaterialflags & MATERIALFLAG_LIGHTGRID) + { + t->currentmaterialflags &= ~MATERIALFLAG_MODELLIGHT; + for (q = 0; q < 3; q++) + { + t->render_glowmod[q] = rsurface.entity->render_glowmod[q] * r_refdef.view.colorscale; + t->render_modellight_lightdir[q] = q == 2; + t->render_modellight_ambient[q] = 0; + t->render_modellight_diffuse[q] = 0; + t->render_modellight_specular[q] = 0; + t->render_lightmap_ambient[q] = rsurface.entity->render_lightmap_ambient[q] * r_refdef.view.colorscale; + t->render_lightmap_diffuse[q] = rsurface.entity->render_lightmap_diffuse[q] * 2 * r_refdef.view.colorscale; + t->render_lightmap_specular[q] = rsurface.entity->render_lightmap_specular[q] * 2 * r_refdef.view.colorscale; + t->render_rtlight_diffuse[q] = rsurface.entity->render_rtlight_diffuse[q] * r_refdef.view.colorscale; + t->render_rtlight_specular[q] = rsurface.entity->render_rtlight_specular[q] * r_refdef.view.colorscale; + } + } else if ((rsurface.ent_flags & (RENDER_DYNAMICMODELLIGHT | RENDER_CUSTOMIZEDMODELLIGHT)) || rsurface.modeltexcoordlightmap2f == NULL) { // ambient + single direction light (modellight) - t->currentmaterialflags |= MATERIALFLAG_MODELLIGHT; + t->currentmaterialflags = (t->currentmaterialflags | MATERIALFLAG_MODELLIGHT) & ~MATERIALFLAG_LIGHTGRID; for (q = 0; q < 3; q++) { t->render_glowmod[q] = rsurface.entity->render_glowmod[q] * r_refdef.view.colorscale; @@ -6698,7 +6809,7 @@ texture_t *R_GetCurrentTexture(texture_t *t) // // FIXME: this is fine for effects but CSQC polygons should be subject // to lighting. - t->currentmaterialflags &= ~MATERIALFLAG_MODELLIGHT; + t->currentmaterialflags &= ~(MATERIALFLAG_MODELLIGHT | MATERIALFLAG_LIGHTGRID); for (q = 0; q < 3; q++) { t->render_glowmod[q] = rsurface.entity->render_glowmod[q] * r_refdef.view.colorscale; diff --git a/model_brush.c b/model_brush.c index f7957977..9e92e530 100644 --- a/model_brush.c +++ b/model_brush.c @@ -52,6 +52,9 @@ cvar_t mod_q3bsp_lightmapmergepower = {CVAR_CLIENT | CVAR_SAVE, "mod_q3bsp_light cvar_t mod_q3bsp_nolightmaps = {CVAR_CLIENT | CVAR_SAVE, "mod_q3bsp_nolightmaps", "0", "do not load lightmaps in Q3BSP maps (to save video RAM, but be warned: it looks ugly)"}; cvar_t mod_q3bsp_tracelineofsight_brushes = {CVAR_CLIENT | CVAR_SERVER, "mod_q3bsp_tracelineofsight_brushes", "0", "enables culling of entities behind detail brushes, curves, etc"}; cvar_t mod_q3bsp_sRGBlightmaps = {CVAR_CLIENT, "mod_q3bsp_sRGBlightmaps", "0", "treat lightmaps from Q3 maps as sRGB when vid_sRGB is active"}; +cvar_t mod_q3bsp_lightgrid_texture = {CVAR_CLIENT, "mod_q3bsp_lightgrid_texture", "1", "directly apply the lightgrid as a global texture rather than only reading it at the entity origin"}; +cvar_t mod_q3bsp_lightgrid_world_surfaces = {CVAR_CLIENT, "mod_q3bsp_lightgrid_world_surfaces", "0", "apply lightgrid lighting to the world bsp geometry rather than using lightmaps (experimental/debug tool)"}; +cvar_t mod_q3bsp_lightgrid_bsp_surfaces = {CVAR_CLIENT, "mod_q3bsp_lightgrid_bsp_surfaces", "0", "apply lightgrid lighting to bsp models other than the world rather than using their lightmaps (experimental/debug tool)"}; cvar_t mod_q3shader_default_offsetmapping = {CVAR_CLIENT | CVAR_SAVE, "mod_q3shader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are using q3 shader files"}; cvar_t mod_q3shader_default_offsetmapping_scale = {CVAR_CLIENT | CVAR_SAVE, "mod_q3shader_default_offsetmapping_scale", "1", "default scale used for offsetmapping"}; cvar_t mod_q3shader_default_offsetmapping_bias = {CVAR_CLIENT | CVAR_SAVE, "mod_q3shader_default_offsetmapping_bias", "0", "default bias used for offsetmapping"}; @@ -100,6 +103,9 @@ void Mod_BrushInit(void) Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower); Cvar_RegisterVariable(&mod_q3bsp_nolightmaps); Cvar_RegisterVariable(&mod_q3bsp_sRGBlightmaps); + Cvar_RegisterVariable(&mod_q3bsp_lightgrid_texture); + Cvar_RegisterVariable(&mod_q3bsp_lightgrid_world_surfaces); + Cvar_RegisterVariable(&mod_q3bsp_lightgrid_bsp_surfaces); Cvar_RegisterVariable(&mod_q3bsp_tracelineofsight_brushes); Cvar_RegisterVariable(&mod_q3shader_default_offsetmapping); Cvar_RegisterVariable(&mod_q3shader_default_offsetmapping_scale); @@ -6568,6 +6574,9 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l) q3dlightgrid_t *out; int count; int i; + int texturesize[3]; + unsigned char *texturergba, *texturelayer[3], *texturepadding[2]; + double lightgridmatrix[4][4]; in = (q3dlightgrid_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) @@ -6642,6 +6651,81 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l) // all is good } } + + if (mod_q3bsp_lightgrid_texture.integer) + { + // build a texture to hold the data for per-pixel sampling + // this has 3 different kinds of data stacked in it: + // ambient color + // bent-normal light color + // bent-normal light dir + texturesize[0] = loadmodel->brushq3.num_lightgrid_isize[0]; + texturesize[1] = loadmodel->brushq3.num_lightgrid_isize[1]; + texturesize[2] = (loadmodel->brushq3.num_lightgrid_isize[2] + 2) * 3; + texturergba = (unsigned char*)Mem_Alloc(loadmodel->mempool, texturesize[0] * texturesize[1] * texturesize[2] * sizeof(char[4])); + texturelayer[0] = texturergba + loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * 4; + texturelayer[1] = texturelayer[0] + (loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * (loadmodel->brushq3.num_lightgrid_isize[2] + 2)) * 4; + texturelayer[2] = texturelayer[1] + (loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * (loadmodel->brushq3.num_lightgrid_isize[2] + 2)) * 4; + // the light dir layer needs padding above/below it that is a neutral unsigned normal (127,127,127,255) + texturepadding[0] = texturelayer[2] - loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * 4; + texturepadding[1] = texturelayer[2] + loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2] * 4; + for (i = 0; i < texturesize[0] * texturesize[1]; i++) + { + texturepadding[0][i * 4] = texturepadding[1][i * 4] = 127; + texturepadding[0][i * 4 + 1] = texturepadding[1][i * 4 + 1] = 127; + texturepadding[0][i * 4 + 2] = texturepadding[1][i * 4 + 2] = 127; + texturepadding[0][i * 4 + 3] = texturepadding[1][i * 4 + 3] = 255; + } + for (i = 0; i < count; i++) + { + texturelayer[0][i * 4 + 0] = out[i].ambientrgb[0]; + texturelayer[0][i * 4 + 1] = out[i].ambientrgb[1]; + texturelayer[0][i * 4 + 2] = out[i].ambientrgb[2]; + texturelayer[0][i * 4 + 3] = 255; + texturelayer[1][i * 4 + 0] = out[i].diffusergb[0]; + texturelayer[1][i * 4 + 1] = out[i].diffusergb[1]; + texturelayer[1][i * 4 + 2] = out[i].diffusergb[2]; + texturelayer[1][i * 4 + 3] = 255; + // this uses the mod_md3_sin table because the values are + // already in the 0-255 range, the 64+ bias fetches a cosine + // instead of a sine value + texturelayer[2][i * 4 + 0] = (char)((mod_md3_sin[64 + out[i].diffuseyaw] * mod_md3_sin[out[i].diffusepitch]) * 127 + 127); + texturelayer[2][i * 4 + 1] = (char)((mod_md3_sin[out[i].diffuseyaw] * mod_md3_sin[out[i].diffusepitch]) * 127 + 127); + texturelayer[2][i * 4 + 2] = (char)((mod_md3_sin[64 + out[i].diffusepitch]) * 127 + 127); + texturelayer[2][i * 4 + 3] = 255; + } +#if 0 + // debugging hack + int x, y, z; + for (z = 0; z < loadmodel->brushq3.num_lightgrid_isize[2]; z++) + { + for (y = 0; y < loadmodel->brushq3.num_lightgrid_isize[1]; y++) + { + for (x = 0; x < loadmodel->brushq3.num_lightgrid_isize[0]; x++) + { + i = (z * texturesize[1] + y) * texturesize[0] + x; + texturelayer[0][i * 4 + 0] = x * 256 / loadmodel->brushq3.num_lightgrid_isize[0]; + texturelayer[0][i * 4 + 1] = y * 256 / loadmodel->brushq3.num_lightgrid_isize[1]; + texturelayer[0][i * 4 + 2] = z * 256 / loadmodel->brushq3.num_lightgrid_isize[2]; + } + } + } +#endif + loadmodel->brushq3.lightgridtexturesize[0] = texturesize[0]; + loadmodel->brushq3.lightgridtexturesize[1] = texturesize[1]; + loadmodel->brushq3.lightgridtexturesize[2] = texturesize[2]; + memset(lightgridmatrix[0], 0, sizeof(lightgridmatrix)); + lightgridmatrix[0][0] = loadmodel->brushq3.num_lightgrid_scale[0] / texturesize[0]; + lightgridmatrix[1][1] = loadmodel->brushq3.num_lightgrid_scale[1] / texturesize[1]; + lightgridmatrix[2][2] = loadmodel->brushq3.num_lightgrid_scale[2] / texturesize[2]; + lightgridmatrix[0][3] = -(loadmodel->brushq3.num_lightgrid_imins[0] - 0.5f) / texturesize[0]; + lightgridmatrix[1][3] = -(loadmodel->brushq3.num_lightgrid_imins[1] - 0.5f) / texturesize[1]; + lightgridmatrix[2][3] = -(loadmodel->brushq3.num_lightgrid_imins[2] - 1.5f) / texturesize[2]; + lightgridmatrix[3][3] = 1; + Matrix4x4_FromArrayDoubleD3D(&loadmodel->brushq3.lightgridworldtotexturematrix, lightgridmatrix[0]); + loadmodel->brushq3.lightgridtexture = R_LoadTexture3D(loadmodel->texturepool, "lightgrid", texturesize[0], texturesize[1], texturesize[2], texturergba, TEXTYPE_RGBA, TEXF_CLAMP, 0, NULL); + Mem_Free(texturergba); + } } } diff --git a/model_brush.h b/model_brush.h index 5611e343..33e033c1 100644 --- a/model_brush.h +++ b/model_brush.h @@ -129,6 +129,8 @@ mplane_t; #define MATERIALFLAG_OCCLUDE 0x10000000 // use vertex color instead of lighting (e.g. particles and other glowy stuff), use with MATERIALFLAG_FULLBRIGHT #define MATERIALFLAG_VERTEXCOLOR 0x20000000 +// sample the q3bsp lightgrid in the shader rather than relying on MATERIALFLAG_MODELLIGHT +#define MATERIALFLAG_LIGHTGRID 0x40000000 // combined mask of all attributes that require depth sorted rendering #define MATERIALFLAGMASK_DEPTHSORTED (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST) // combined mask of all attributes that cause some sort of transparency diff --git a/model_shared.h b/model_shared.h index 62bbd7dc..2e0052ee 100644 --- a/model_shared.h +++ b/model_shared.h @@ -919,7 +919,7 @@ typedef struct model_brushq3_s rtexture_t **data_lightmaps; rtexture_t **data_deluxemaps; - // voxel light data with directional shading + // voxel light data with directional shading - data for cpu sampling of it... int num_lightgrid; q3dlightgrid_t *data_lightgrid; // size of each cell (may vary by map, typically 64 64 128) @@ -932,6 +932,10 @@ typedef struct model_brushq3_s int num_lightgrid_isize[3]; // transform modelspace coordinates to lightgrid index matrix4x4_t num_lightgrid_indexfromworld; + // parameters for fragment shader to sample the texture version of it: + int lightgridtexturesize[3]; // 3 layers tall (ambient, lightcolor, lightdir) + matrix4x4_t lightgridworldtotexturematrix; + rtexture_t *lightgridtexture; // true if this q3bsp file has been detected as using deluxemapping // (lightmap texture pairs, every odd one is never directly refernced, @@ -1093,6 +1097,9 @@ extern cvar_t mod_q3shader_default_polygonfactor; extern cvar_t mod_q3shader_default_refractive_index; extern cvar_t mod_q3shader_force_addalpha; extern cvar_t mod_q3shader_force_terrain_alphaflag; +extern cvar_t mod_q3bsp_lightgrid_texture; +extern cvar_t mod_q3bsp_lightgrid_world_surfaces; +extern cvar_t mod_q3bsp_lightgrid_bsp_surfaces; void Mod_Init (void); void Mod_Reload (void); diff --git a/render.h b/render.h index ef20c14a..8aac2031 100644 --- a/render.h +++ b/render.h @@ -58,6 +58,7 @@ typedef enum shadermode_e SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE, ///< (lightmap) use directional pixel shading from texture containing tangentspace light directions (q1bsp deluxemap) SHADERMODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP, // forced deluxemapping for lightmapped surfaces SHADERMODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR, // forced deluxemapping for vertexlit surfaces + SHADERMODE_LIGHTGRID, ///< (lightmap) use directional pixel shading from lightgrid data (q3bsp) SHADERMODE_LIGHTDIRECTION, ///< (lightmap) use directional pixel shading from fixed light direction (q3bsp) SHADERMODE_LIGHTSOURCE, ///< (lightsource) use directional pixel shading from light source (rtlight) SHADERMODE_REFRACTION, ///< refract background (the material is rendered normally after this pass) diff --git a/shader_glsl.h b/shader_glsl.h index 608e42d7..11ec83dc 100644 --- a/shader_glsl.h +++ b/shader_glsl.h @@ -37,7 +37,8 @@ "# endif\n", "#endif\n", "\n", -"#if (defined(GLSL120) || defined(GLSL130) || defined(GLSL140) || defined(GLES)) && defined(VERTEX_SHADER)\n" +"#if (defined(GLSL120) || defined(GLSL130) || defined(GLSL140) || defined(GLES)) && defined(VERTEX_SHADER)\n", +"\n", "invariant gl_Position; // fix for lighting polygons not matching base surface\n", "# endif\n", "#if defined(GLSL130) || defined(GLSL140)\n", @@ -721,7 +722,7 @@ "dp_varying highp vec4 EyeVectorFogDepth;\n", "#endif\n", "\n", -"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_DEFERREDGEOMETRY) || defined(USEREFLECTCUBE) || defined(USEBOUNCEGRIDDIRECTIONAL)\n", +"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_DEFERREDGEOMETRY) || defined(USEREFLECTCUBE) || defined(USEBOUNCEGRIDDIRECTIONAL) || defined(MODE_LIGHTGRID)\n", "dp_varying highp vec4 VectorS; // direction of S texcoord (sometimes crudely called tangent)\n", "dp_varying highp vec4 VectorT; // direction of T texcoord (sometimes crudely called binormal)\n", "dp_varying highp vec4 VectorR; // direction of R texcoord (surface normal)\n", @@ -752,6 +753,9 @@ "dp_varying highp vec3 ShadowMapTC;\n", "#endif\n", "\n", +"#ifdef MODE_LIGHTGRID\n", +"dp_varying highp vec3 LightGridTC;\n", +"#endif\n", "#ifdef USEBOUNCEGRID\n", "dp_varying highp vec3 BounceGridTexCoord;\n", "#endif\n", @@ -825,7 +829,7 @@ "uniform highp float FogHeightFade;\n", "vec3 FogVertex(vec4 surfacecolor)\n", "{\n", -"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_DEFERREDGEOMETRY) || defined(USEREFLECTCUBE) || defined(USEBOUNCEGRIDDIRECTIONAL)\n", +"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_DEFERREDGEOMETRY) || defined(USEREFLECTCUBE) || defined(USEBOUNCEGRIDDIRECTIONAL) || defined(MODE_LIGHTGRID)\n", " vec3 EyeVectorModelSpace = vec3(VectorS.w, VectorT.w, VectorR.w);\n", "#endif\n", " float FogPlaneVertexDist = EyeVectorFogDepth.w;\n", @@ -1282,6 +1286,9 @@ "#ifdef USESHADOWMAPORTHO\n", "uniform highp mat4 ShadowMapMatrix;\n", "#endif\n", +"#ifdef MODE_LIGHTGRID\n", +"uniform highp mat4 LightGridMatrix;\n", +"#endif\n", "#ifdef USEBOUNCEGRID\n", "uniform highp mat4 BounceGridMatrix;\n", "#endif\n", @@ -1323,6 +1330,9 @@ " TexCoord2 = vec2(BackgroundTexMatrix * Attrib_TexCoord0);\n", "#endif\n", "\n", +"#ifdef MODE_LIGHTGRID\n", +" LightGridTC = vec3(LightGridMatrix * Attrib_Position);\n", +"#endif\n", "#ifdef USEBOUNCEGRID\n", " BounceGridTexCoord = vec3(BounceGridMatrix * Attrib_Position);\n", "#ifdef USEBOUNCEGRIDDIRECTIONAL\n", @@ -1366,7 +1376,7 @@ "#endif\n", "\n", "\n", -"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(USEREFLECTCUBE) || defined(USEBOUNCEGRIDDIRECTIONAL)\n", +"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(USEREFLECTCUBE) || defined(USEBOUNCEGRIDDIRECTIONAL) || defined(MODE_LIGHTGRID)\n", "# ifdef USEFOG\n", " VectorS = vec4(Attrib_TexCoord1.xyz, EyePosition.x - Attrib_Position.x);\n", " VectorT = vec4(Attrib_TexCoord2.xyz, EyePosition.y - Attrib_Position.y);\n", @@ -1426,6 +1436,10 @@ "uniform sampler2D Texture_ReflectMask;\n", "uniform samplerCube Texture_ReflectCube;\n", "#endif\n", +"#ifdef MODE_LIGHTGRID\n", +"uniform sampler3D Texture_LightGrid;\n", +"uniform mat3 LightGridNormalMatrix;\n", +"#endif\n", "#ifdef USEBOUNCEGRID\n", "uniform sampler3D Texture_BounceGrid;\n", "uniform float BounceGridIntensity;\n", @@ -1539,6 +1553,34 @@ "\n", "\n", "\n", +"#ifdef MODE_LIGHTGRID\n", +" // clamp the LightGrid TC Z coordinate to the first of the 3 layers, to\n", +" // prevent repeat-artifacts for lightgrids smaller than the visible scene\n", +" // (which is often the case - the lightgrid bounds is defined by the level\n", +" // designer and usually matches the playable area, not the scenery around\n", +" // it), we can rely on GL_CLAMP_TO_EDGE for this in all other directions.\n", +" vec3 LGTC = vec3(LightGridTC.xy, min(LightGridTC.z, 0.333333));\n", +" myhalf3 ambientcolor = cast_myhalf3(dp_texture2D(Texture_LightGrid, LGTC));\n", +" myhalf3 lightcolor = cast_myhalf3(dp_texture2D(Texture_LightGrid, LGTC + vec3(0, 0, 0.333333)));\n", +" myhalf3 lightnormal_worldspace = cast_myhalf3(dp_texture2D(Texture_LightGrid, LGTC + vec3(0, 0, 0.6666667))) * 2.0 + cast_myhalf3(-1.0, -1.0, -1.0);\n", +" myhalf3 lightnormal_modelspace = cast_myhalf3(lightnormal_worldspace * LightGridNormalMatrix);\n", +" // convert modelspace light vector to tangentspace\n", +" myhalf3 lightnormal;\n", +" lightnormal.x = dot(lightnormal_modelspace, cast_myhalf3(VectorS));\n", +" lightnormal.y = dot(lightnormal_modelspace, cast_myhalf3(VectorT));\n", +" lightnormal.z = dot(lightnormal_modelspace, cast_myhalf3(VectorR));\n", +" lightnormal = normalize(lightnormal); // VectorS/T/R are not always perfectly normalized, and EXACTSPECULARMATH is very picky about this\n", +" // now we have the light parameters, so do the shading...\n", +"SHADEDIFFUSE\n", +" color.rgb = diffusetex * (Color_Ambient + Color_Diffuse * (ambientcolor + diffuse * lightcolor));\n", +"#ifdef USESPECULAR\n", +"SHADESPECULAR(SpecularPower * glosstex.a)\n", +" color.rgb += glosstex.rgb * (specular * Color_Specular * lightcolor);\n", +"#endif\n", +"#endif\n", +"\n", +"\n", +"\n", "#ifdef MODE_LIGHTDIRECTION\n", " #define SHADING\n", " #ifdef USEDIFFUSE\n", -- 2.39.2