diff --git a/src.cmake b/src.cmake index 7430a577b9..6c43cc7363 100644 --- a/src.cmake +++ b/src.cmake @@ -108,7 +108,6 @@ set(GLSL_EMBED_LIST blur_fp.glsl cameraEffects_fp.glsl contrast_fp.glsl - fogGlobal_fp.glsl fxaa_fp.glsl fxaa3_11_fp.glsl motionblur_fp.glsl @@ -130,8 +129,8 @@ set(GLSL_EMBED_LIST vertexSkinning_vp.glsl # Regular shaders - fogQuake3_vp.glsl - fogQuake3_fp.glsl + fog_vp.glsl + fog_fp.glsl generic_vp.glsl generic_fp.glsl heatHaze_vp.glsl diff --git a/src/engine/renderer/GeometryOptimiser.cpp b/src/engine/renderer/GeometryOptimiser.cpp index d802a8c642..b151ddd988 100644 --- a/src/engine/renderer/GeometryOptimiser.cpp +++ b/src/engine/renderer/GeometryOptimiser.cpp @@ -54,12 +54,6 @@ static int LeafSurfaceCompare( const void* a, const void* b ) { return 1; } - if ( aa->fogIndex < bb->fogIndex ) { - return -1; - } else if ( aa->fogIndex > bb->fogIndex ) { - return 1; - } - // sort by leaf if ( aa->scratch2 < bb->scratch2 ) { return -1; @@ -168,7 +162,6 @@ void OptimiseMapGeometryCore( world_t* world, bspSurface_t** rendererSurfaces, i shader_t* shader1 = surf1->shader; - int fogIndex1 = surf1->fogIndex; int lightMapNum1 = surf1->lightmapNum; surf1->viewCount = surf1 - world->surfaces; surf1->scratch1 = j; @@ -187,9 +180,8 @@ void OptimiseMapGeometryCore( world_t* world, bspSurface_t** rendererSurfaces, i } shader_t* shader2 = surf2->shader; - int fogIndex2 = surf2->fogIndex; int lightMapNum2 = surf2->lightmapNum; - if ( shader1 != shader2 || fogIndex1 != fogIndex2 || lightMapNum1 != lightMapNum2 ) { + if ( shader1 != shader2 || lightMapNum1 != lightMapNum2 ) { continue; } @@ -315,7 +307,6 @@ void MergeLeafSurfacesCore( world_t* world, bspSurface_t** rendererSurfaces, int SphereFromBounds( vboSurf->bounds[0], vboSurf->bounds[1], vboSurf->origin, &vboSurf->radius ); mergedSurf->data = ( surfaceType_t* ) vboSurf; - mergedSurf->fogIndex = surf1->fogIndex; mergedSurf->shader = surf1->shader; mergedSurf->lightmapNum = surf1->lightmapNum; mergedSurf->viewCount = -1; @@ -557,7 +548,6 @@ std::vector OptimiseMapGeometryMaterial( world_t* world, bspSur srf.skyBrush = surface->skyBrush; srf.lightMapNum = surface->lightmapNum; - srf.fog = surface->fogIndex; srf.portalNum = surface->portalNum; srf.firstIndex = ( ( srfGeneric_t* ) surface->data )->firstIndex; @@ -568,7 +558,7 @@ std::vector OptimiseMapGeometryMaterial( world_t* world, bspSur VectorCopy( ( ( srfGeneric_t* ) surface->data )->origin, srf.origin ); srf.radius = ( ( srfGeneric_t* ) surface->data )->radius; - materialSystem.GenerateMaterial( &srf, world->globalFog ); + materialSystem.GenerateMaterial( &srf ); static const float MAX_NORMAL_SURFACE_DISTANCE = 65536.0f * sqrtf( 2.0f ); if ( VectorLength( ( ( srfGeneric_t* ) surface->data )->bounds[0] ) > MAX_NORMAL_SURFACE_DISTANCE diff --git a/src/engine/renderer/GeometryOptimiser.h b/src/engine/renderer/GeometryOptimiser.h index 3a24a972d9..cfce0db7e1 100644 --- a/src/engine/renderer/GeometryOptimiser.h +++ b/src/engine/renderer/GeometryOptimiser.h @@ -87,7 +87,6 @@ void MarkShaderBuildScreen( const shaderStage_t* pStage ); void MarkShaderBuildPortal( const shaderStage_t* pStage ); void MarkShaderBuildHeatHaze( const shaderStage_t* pStage ); void MarkShaderBuildLiquid( const shaderStage_t* pStage ); -void MarkShaderBuildFog( const shaderStage_t* pStage ); void MarkShaderBuildIQM( const IQModel_t* model ); void MarkShaderBuildMDV( const mdvModel_t* model ); diff --git a/src/engine/renderer/Material.cpp b/src/engine/renderer/Material.cpp index a091109746..0909b96ada 100644 --- a/src/engine/renderer/Material.cpp +++ b/src/engine/renderer/Material.cpp @@ -334,14 +334,6 @@ void UpdateSurfaceDataLiquid( uint32_t* materials, shaderStage_t* pStage, bool, gl_liquidShaderMaterial->WriteUniformsToBuffer( materials, GLShader::MATERIAL ); } -void UpdateSurfaceDataFog( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ) { - // shader_t* shader = pStage->shader; - - materials += pStage->bufferOffset; - - gl_fogQuake3ShaderMaterial->WriteUniformsToBuffer( materials, GLShader::MATERIAL ); -} - /* * Buffer layout: * // Static surfaces data: @@ -746,10 +738,10 @@ class ListMaterialsCmd : public Cmd::StaticCmd { shaderSortName.at( materialPack.fromSort ), shaderSortName.at( materialPack.toSort ) ); for ( const Material& material : materialPack.materials ) { Print( "id: %u, sync: %5s, stateBits: %10x, GLShader: %s, GLProgramID: %u," - " deform: %i, fog: %i, drawCmdCount: %u", + " deform: %i, drawCmdCount: %u", material.id, material.useSync, material.stateBits, material.shader->_name, material.program, - material.deformIndex, material.fog, material.drawCommandCount ); + material.deformIndex, material.drawCommandCount ); } } } @@ -762,7 +754,6 @@ static std::string GetStageInfo( const shaderStage_t* pStage, const uint32_t dyn { BindShaderGeneric3D, "genericMaterial " }, { BindShaderLightMapping, "lightMappingMaterial" }, { BindShaderHeatHaze, "heatHazeMaterial " }, - { BindShaderFog, "fogQuake3Material " }, { BindShaderLiquid, "liquidMaterial " }, { BindShaderScreen, "screenMaterial " }, { BindShaderSkybox, "skyboxMaterial " }, @@ -1090,42 +1081,6 @@ void BindShaderLiquid( Material* material ) { gl_liquidShaderMaterial->SetUniform_PortalMapBindless( GL_BindToTMU( 1, tr.portalRenderImage ) ); } -void BindShaderFog( Material* material ) { - // Bind shader program. - gl_fogQuake3ShaderMaterial->SetDeform( material->deformIndex ); - gl_fogQuake3ShaderMaterial->BindProgram(); - - // Set shader uniforms. - const fog_t* fog = tr.world->fogs + material->fog; - - // rotate the gradient vector for this orientation - float eyeT; - vec4_t fogDepthVector; - if ( fog->hasSurface ) { - VectorCopy( fog->surface, fogDepthVector ); - fogDepthVector[ 3 ] = -fog->surface[ 3 ]; - eyeT = DotProduct( backEnd.viewParms.orientation.origin, fogDepthVector ) + fogDepthVector[ 3 ]; - } else { - Vector4Set( fogDepthVector, 0, 0, 0, 1 ); - eyeT = 1; // non-surface fog always has eye inside - } - - // Note: things that seemingly should be per-shader or per-surface can be set as global uniforms - // since fognum is grouped with the GL state stuff, segregating each fognum in a separate draw call. - - gl_fogQuake3ShaderMaterial->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - gl_fogQuake3ShaderMaterial->SetUniform_FogDensity( fog->tcScale ); - gl_fogQuake3ShaderMaterial->SetUniform_FogDepthVector( fogDepthVector ); - gl_fogQuake3ShaderMaterial->SetUniform_FogEyeT( eyeT ); - - gl_fogQuake3ShaderMaterial->SetUniform_ColorGlobal_Uint( fog->color ); - - gl_fogQuake3ShaderMaterial->SetUniform_ModelMatrix( backEnd.orientation.transformMatrix ); - gl_fogQuake3ShaderMaterial->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[glState.stackIndex] ); - - gl_fogQuake3ShaderMaterial->SetUniform_Time( backEnd.refdef.floatTime - backEnd.currentEntity->e.shaderTime ); -} - void ProcessMaterialNONE( Material*, shaderStage_t*, MaterialSurface* ) { ASSERT_UNREACHABLE(); } @@ -1263,15 +1218,6 @@ void ProcessMaterialLiquid( Material* material, shaderStage_t* pStage, MaterialS material->program = gl_liquidShaderMaterial->GetProgram( materialSystem.buildOneShader ); } -void ProcessMaterialFog( Material* material, shaderStage_t* pStage, MaterialSurface* surface ) { - material->shader = gl_fogQuake3ShaderMaterial; - material->fog = surface->fog; - - gl_fogQuake3ShaderMaterial->SetDeform( pStage->deformIndex ); - - material->program = gl_fogQuake3ShaderMaterial->GetProgram( materialSystem.buildOneShader ); -} - void MaterialSystem::AddStage( MaterialSurface* surface, shaderStage_t* pStage, uint32_t stage, const bool mayUseVertexOverbright, const bool vertexLit, const bool fullbright ) { const int variant = ( mayUseVertexOverbright ? ShaderStageVariant::VERTEX_OVERBRIGHT : 0 ) @@ -1495,7 +1441,7 @@ void MaterialSystem::ProcessStage( MaterialSurface* surface, shaderStage_t* pSta /* This will only generate a material itself A material represents a distinct global OpenGL state (e. g. blend function, depth test, depth write etc.) Materials can have a dependency on other materials to make sure that consecutive stages are rendered in the proper order */ -void MaterialSystem::GenerateMaterial( MaterialSurface* surface, int globalFog ) { +void MaterialSystem::GenerateMaterial( MaterialSurface* surface ) { uint32_t stage = 0; uint32_t previousMaterialID = 0; @@ -1515,13 +1461,6 @@ void MaterialSystem::GenerateMaterial( MaterialSurface* surface, int globalFog ) surface->stages++; } - - if ( !surface->shader->noFog && surface->fog >= 1 && surface->fog != globalFog ) { - uint32_t unused; - ProcessStage( surface, surface->shader->fogShader->stages, surface->shader->fogShader, packIDs, stage, unused, true ); - - surface->stages++; - } } void MaterialSystem::SetWorldBounds( vec3_t bounds[2] ) { @@ -1917,7 +1856,6 @@ bool MaterialSystem::AddPortalSurface( uint32_t viewID, PortalSurface* portalSur { drawSurf.bspSurface = portalSurfaces[portalSurface->drawSurfID].bspSurface; drawSurf.entity = &tr.worldEntity; - drawSurf.fog = portalSurfaces[portalSurface->drawSurfID].fog; drawSurf.portalNum = portalSurfaces[portalSurface->drawSurfID].portalNum; drawSurf.shader = portalSurfaces[portalSurface->drawSurfID].shader; drawSurf.surface = portalSurfaces[portalSurface->drawSurfID].surface; @@ -1993,7 +1931,7 @@ void MaterialSystem::AddAutospriteSurfaces() { for ( const bspSurface_t* surface : autospriteSurfaces ) { R_AddDrawSurf( surface->data, surface->shader, - surface->lightmapNum, surface->fogIndex, true ); + surface->lightmapNum, true ); } } @@ -2045,7 +1983,7 @@ void MaterialSystem::RenderMaterials( const shaderSort_t fromSort, const shaderS } tr.drawingSky = true; - Tess_Begin( Tess_StageIteratorSky, skyShader, false, -1, 0, false ); + Tess_Begin( Tess_StageIteratorSky, skyShader, false, -1, false ); Tess_End(); } } @@ -2095,12 +2033,6 @@ void MaterialSystem::RenderMaterial( Material& material, const uint32_t viewID ) stateBits &= ~( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ); } - if( material.shaderBinder == BindShaderFog ) { - if ( r_noFog->integer || !r_wolfFog->integer || ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) { - return; - } - } - backEnd.currentEntity = &tr.worldEntity; GL_State( stateBits ); diff --git a/src/engine/renderer/Material.h b/src/engine/renderer/Material.h index 46ec632eb8..0147ac030b 100644 --- a/src/engine/renderer/Material.h +++ b/src/engine/renderer/Material.h @@ -85,7 +85,6 @@ struct MaterialSurface { bool skyBrush; int16_t lightMapNum; - int fog; int portalNum = -1; GLuint firstIndex; @@ -148,15 +147,13 @@ struct Material { bool usePolygonOffset = false; - int fog = 0; - uint32_t drawCommandCount = 0; bool texturesResident = false; std::vector textures; bool operator==( const Material& other ) { return program == other.program && stateBits == other.stateBits - && fog == other.fog && cullType == other.cullType && usePolygonOffset == other.usePolygonOffset; + && cullType == other.cullType && usePolygonOffset == other.usePolygonOffset; } void AddTexture( Texture* texture ) { @@ -381,7 +378,7 @@ class MaterialSystem { const bool mayUseVertexOverbright, const bool vertexLit, const bool fullbright ); void ProcessStage( MaterialSurface* surface, shaderStage_t* pStage, shader_t* shader, uint32_t* packIDs, uint32_t& stage, uint32_t& previousMaterialID, bool skipStageSync = false ); - void GenerateMaterial( MaterialSurface* surface, int globalFog ); + void GenerateMaterial( MaterialSurface* surface ); void GenerateWorldMaterialsBuffer(); void GenerateWorldCommandBuffer( std::vector& surfaces ); void GeneratePortalBoundingSpheres(); @@ -462,7 +459,6 @@ void UpdateSurfaceDataSkybox( uint32_t* materials, shaderStage_t* pStage, bool, void UpdateSurfaceDataScreen( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ); void UpdateSurfaceDataHeatHaze( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ); void UpdateSurfaceDataLiquid( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ); -void UpdateSurfaceDataFog( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ); void BindShaderNONE( Material* ); void BindShaderNOP( Material* ); @@ -473,7 +469,6 @@ void BindShaderSkybox( Material* material ); void BindShaderScreen( Material* material ); void BindShaderHeatHaze( Material* material ); void BindShaderLiquid( Material* material ); -void BindShaderFog( Material* material ); void ProcessMaterialNONE( Material*, shaderStage_t*, MaterialSurface* ); void ProcessMaterialNOP( Material*, shaderStage_t*, MaterialSurface* ); @@ -484,6 +479,5 @@ void ProcessMaterialSkybox( Material* material, shaderStage_t* pStage, MaterialS void ProcessMaterialScreen( Material* material, shaderStage_t* pStage, MaterialSurface* /* surface */ ); void ProcessMaterialHeatHaze( Material* material, shaderStage_t* pStage, MaterialSurface* surface ); void ProcessMaterialLiquid( Material* material, shaderStage_t* pStage, MaterialSurface* /* surface */ ); -void ProcessMaterialFog( Material* material, shaderStage_t* pStage, MaterialSurface* surface ); #endif // MATERIAL_H diff --git a/src/engine/renderer/VertexSpecification.h b/src/engine/renderer/VertexSpecification.h index 9c06d264a3..d3f774e846 100644 --- a/src/engine/renderer/VertexSpecification.h +++ b/src/engine/renderer/VertexSpecification.h @@ -41,6 +41,13 @@ enum // GPU vertex animations ATTR_INDEX_POSITION2, ATTR_INDEX_QTANGENT2, + + ATTR_INDEX_FOG_SURFACE, + + // This occupies 5 attribute slots + ATTR_INDEX_FOG_PLANES_0, + ATTR_INDEX_FOG_PLANES_LAST = ATTR_INDEX_FOG_PLANES_0 + 4, + ATTR_INDEX_MAX }; @@ -53,7 +60,9 @@ static const char* const attributeNames[] = "attr_Color", "attr_BoneFactors", "attr_Position2", - "attr_QTangent2" + "attr_QTangent2", + "attr_FogSurface", + "attr_FogPlanes", nullptr, nullptr, nullptr, nullptr, }; enum @@ -69,6 +78,9 @@ enum ATTR_POSITION2 = BIT( ATTR_INDEX_POSITION2 ), ATTR_QTANGENT2 = BIT( ATTR_INDEX_QTANGENT2 ), + ATTR_FOG_SURFACE = BIT( ATTR_INDEX_FOG_SURFACE ), + ATTR_FOG_PLANES = BIT( ATTR_INDEX_FOG_PLANES_0 ) * ( BIT( 5 ) - 1 ), + ATTR_INTERP_BITS = ATTR_POSITION2 | ATTR_QTANGENT2, }; diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 0526d2c5aa..1ab26b620f 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -55,7 +55,7 @@ GLShader_processSurfaces *gl_processSurfacesShader = nullptr; GLShader_blur *gl_blurShader = nullptr; GLShader_cameraEffects *gl_cameraEffectsShader = nullptr; GLShader_contrast *gl_contrastShader = nullptr; -GLShader_fogGlobal *gl_fogGlobalShader = nullptr; +GLShader_fog *gl_fogShader = nullptr; GLShader_fxaa *gl_fxaaShader = nullptr; GLShader_motionblur *gl_motionblurShader = nullptr; GLShader_ssao *gl_ssaoShader = nullptr; @@ -68,8 +68,6 @@ GLShader_generic *gl_genericShader = nullptr; GLShader_genericMaterial *gl_genericShaderMaterial = nullptr; GLShader_lightMapping *gl_lightMappingShader = nullptr; GLShader_lightMappingMaterial *gl_lightMappingShaderMaterial = nullptr; -GLShader_fogQuake3 *gl_fogQuake3Shader = nullptr; -GLShader_fogQuake3Material *gl_fogQuake3ShaderMaterial = nullptr; GLShader_heatHaze *gl_heatHazeShader = nullptr; GLShader_heatHazeMaterial *gl_heatHazeShaderMaterial = nullptr; GLShader_liquid *gl_liquidShader = nullptr; @@ -2007,7 +2005,10 @@ void GLShaderManager::BindAttribLocations( GLuint program ) const { for ( uint32_t i = 0; i < ATTR_INDEX_MAX; i++ ) { - glBindAttribLocation( program, i, attributeNames[ i ] ); + if ( attributeNames[ i ] != nullptr ) + { + glBindAttribLocation( program, i, attributeNames[ i ] ); + } } } @@ -2687,53 +2688,22 @@ GLShader_skyboxMaterial::GLShader_skyboxMaterial() : u_ModelViewProjectionMatrix( this ) {} -GLShader_fogQuake3::GLShader_fogQuake3() : - GLShader( "fogQuake3", ATTR_POSITION | ATTR_QTANGENT, - false, "fogQuake3", "fogQuake3" ), - u_ModelMatrix( this ), - u_ModelViewProjectionMatrix( this ), - u_ColorGlobal_Float( this ), - u_ColorGlobal_Uint( this ), - u_Bones( this ), - u_VertexInterpolation( this ), - u_ViewOrigin( this ), - u_FogDensity( this ), - u_FogDepthVector( this ), - u_FogEyeT( this ), - GLDeformStage( this ), - GLCompileMacro_USE_VERTEX_SKINNING( this ), - GLCompileMacro_USE_VERTEX_ANIMATION( this ) -{ -} - -GLShader_fogQuake3Material::GLShader_fogQuake3Material() : - GLShader( "fogQuake3Material", ATTR_POSITION | ATTR_QTANGENT, - true, "fogQuake3", "fogQuake3" ), - u_ModelMatrix( this ), - u_ModelViewProjectionMatrix( this ), - u_ColorGlobal_Uint( this ), - u_ViewOrigin( this ), - u_FogDensity( this ), - u_FogDepthVector( this ), - u_FogEyeT( this ), - GLDeformStage( this ) { -} - -GLShader_fogGlobal::GLShader_fogGlobal() : - GLShader( "fogGlobal", ATTR_POSITION, - false, "screenSpace", "fogGlobal" ), +GLShader_fog::GLShader_fog() : + GLShader( "fog", ATTR_POSITION | ATTR_FOG_SURFACE, + false, "fog", "fog" ), u_DepthMap( this ), + u_ModelViewProjectionMatrix( this ), u_UnprojectMatrix( this ), u_Color_Float( this ), u_Color_Uint( this ), u_ViewOrigin( this ), - u_FogDensity( this ) + u_FogGradient( this ), + GLCompileMacro_OUTSIDE_FOG( this ) { } -void GLShader_fogGlobal::SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) +void GLShader_fog::SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) { - glUniform1i( glGetUniformLocation( shaderProgram->id, "u_ColorMap" ), 0 ); glUniform1i( glGetUniformLocation( shaderProgram->id, "u_DepthMap" ), 1 ); } diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 8a62521d5f..cef9c7df2c 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -919,6 +919,7 @@ class GLCompileMacro // It also works regardless of RTTI is enabled or not. enum EGLCompileMacro : unsigned { + OUTSIDE_FOG, USE_BSP_SURFACE, USE_VERTEX_SKINNING, USE_VERTEX_ANIMATION, @@ -985,6 +986,40 @@ class GLCompileMacro virtual ~GLCompileMacro() = default; }; +class GLCompileMacro_OUTSIDE_FOG : + GLCompileMacro +{ +public: + GLCompileMacro_OUTSIDE_FOG( GLShader *shader ) : + GLCompileMacro( shader ) + { + } + + const char *GetName() const override + { + return "OUTSIDE_FOG"; + } + + EGLCompileMacro GetType() const override + { + return EGLCompileMacro::OUTSIDE_FOG; + } + + int GetShaderTypes() const override { + return ShaderType::VERTEX | ShaderType::FRAGMENT; + } + + void SetOutsideFog( bool enable ) + { + SetMacro( enable ); + } + + uint32_t GetRequiredVertexAttributes() const override + { + return ATTR_FOG_PLANES; + } +}; + class GLCompileMacro_USE_BSP_SURFACE : GLCompileMacro { @@ -1859,6 +1894,22 @@ class u_FogDensity : } }; +class u_FogGradient : + GLUniform2f +{ +public: + u_FogGradient( GLShader *shader ) : + GLUniform2f( shader, "u_FogGradient", PUSH ) + { + } + + void SetUniform_FogGradient( float density, float falloffExp ) + { + vec2_t value{ density, falloffExp }; + this->SetValue( value ); + } +}; + class u_FogColor : GLUniform3f { @@ -2645,36 +2696,6 @@ template void SetUniform_ColorModulateColorGen( } } -class u_FogDepthVector : - GLUniform4f -{ -public: - u_FogDepthVector( GLShader *shader ) : - GLUniform4f( shader, "u_FogDepthVector", PUSH ) - { - } - - void SetUniform_FogDepthVector( const vec4_t v ) - { - this->SetValue( v ); - } -}; - -class u_FogEyeT : - GLUniform1f -{ -public: - u_FogEyeT( GLShader *shader ) : - GLUniform1f( shader, "u_FogEyeT", PUSH ) - { - } - - void SetUniform_FogEyeT( float value ) - { - this->SetValue( value ); - } -}; - class u_DeformEnable : GLUniform1f { public: @@ -3157,51 +3178,19 @@ class GLShader_skyboxMaterial : GLShader_skyboxMaterial(); }; -class GLShader_fogQuake3 : - public GLShader, - public u_ModelMatrix, - public u_ModelViewProjectionMatrix, - public u_ColorGlobal_Float, - public u_ColorGlobal_Uint, - public u_Bones, - public u_VertexInterpolation, - public u_ViewOrigin, - public u_FogDensity, - public u_FogDepthVector, - public u_FogEyeT, - public GLDeformStage, - public GLCompileMacro_USE_VERTEX_SKINNING, - public GLCompileMacro_USE_VERTEX_ANIMATION -{ -public: - GLShader_fogQuake3(); -}; - -class GLShader_fogQuake3Material : - public GLShader, - public u_ModelMatrix, - public u_ModelViewProjectionMatrix, - public u_ColorGlobal_Uint, - public u_ViewOrigin, - public u_FogDensity, - public u_FogDepthVector, - public u_FogEyeT, - public GLDeformStage { - public: - GLShader_fogQuake3Material(); -}; - -class GLShader_fogGlobal : +class GLShader_fog : public GLShader, public u_DepthMap, + public u_ModelViewProjectionMatrix, public u_UnprojectMatrix, public u_Color_Float, public u_Color_Uint, public u_ViewOrigin, - public u_FogDensity + public u_FogGradient, + public GLCompileMacro_OUTSIDE_FOG { public: - GLShader_fogGlobal(); + GLShader_fog(); void SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) override; }; @@ -3539,7 +3528,7 @@ extern GLShader_processSurfaces *gl_processSurfacesShader; extern GLShader_blur *gl_blurShader; extern GLShader_cameraEffects *gl_cameraEffectsShader; extern GLShader_contrast *gl_contrastShader; -extern GLShader_fogGlobal *gl_fogGlobalShader; +extern GLShader_fog *gl_fogShader; extern GLShader_fxaa *gl_fxaaShader; extern GLShader_motionblur *gl_motionblurShader; extern GLShader_ssao *gl_ssaoShader; @@ -3552,8 +3541,6 @@ extern GLShader_generic *gl_genericShader; extern GLShader_genericMaterial *gl_genericShaderMaterial; extern GLShader_lightMapping *gl_lightMappingShader; extern GLShader_lightMappingMaterial *gl_lightMappingShaderMaterial; -extern GLShader_fogQuake3 *gl_fogQuake3Shader; -extern GLShader_fogQuake3Material *gl_fogQuake3ShaderMaterial; extern GLShader_heatHaze *gl_heatHazeShader; extern GLShader_heatHazeMaterial *gl_heatHazeShaderMaterial; extern GLShader_liquid *gl_liquidShader; diff --git a/src/engine/renderer/glsl_source/fogEquation_fp.glsl b/src/engine/renderer/glsl_source/fogEquation_fp.glsl index 48658cd6c0..2be37f45ad 100644 --- a/src/engine/renderer/glsl_source/fogEquation_fp.glsl +++ b/src/engine/renderer/glsl_source/fogEquation_fp.glsl @@ -32,11 +32,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================================== */ -float GetFogAlpha(float s, float t) +float FogGradientFunction(float k, float t) { - t = clamp(t, 0.0, 1.0); + return 1 - exp(-k * t); +} - float x = min(1, s * t); +float FogGradientAntiderivative(float k, float t) +{ + return t + exp(-k * t) / k; +} + +float GetFogGradientModifier(float k, float t0, float t1) +{ + t0 = max(0.0, t0); + t1 = max(0.0, t1); + + float deltaT = t1 - t0; + if (abs(deltaT) > 0.1) + { + return ( FogGradientAntiderivative(k, t1) - FogGradientAntiderivative(k, t0) ) / deltaT; + } + else + { + return FogGradientFunction(k, t0); + } +} + +float GetFogAlpha(float x) +{ + x = clamp(x, 0.0, 1.0); // sqrt(x) is bad near 0 because it increases too quickly resulting in sharp edges. // x ≤ 1/32: √32 * x diff --git a/src/engine/renderer/glsl_source/fogQuake3_fp.glsl b/src/engine/renderer/glsl_source/fogQuake3_fp.glsl deleted file mode 100644 index e76ea8c5a5..0000000000 --- a/src/engine/renderer/glsl_source/fogQuake3_fp.glsl +++ /dev/null @@ -1,55 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 Robert Beckebans - -This file is part of XreaL source code. - -XreaL source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -XreaL source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with XreaL source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/* fogQuake3_fp.glsl */ - -#insert fogEquation_fp - -#define DEPTHMAP_GLSL - -uniform float u_FogEyeT; - -IN(smooth) float var_FogPlaneDistance; -IN(smooth) vec3 var_ScaledViewerOffset; -IN(smooth) vec4 var_Color; - -DECLARE_OUTPUT(vec4) - -void main() -{ - #insert material_fp - - float s = length(var_ScaledViewerOffset); - float t = step( 0, var_FogPlaneDistance ); - - if ( u_FogEyeT < 0 ) // eye outside fog - { - // fraction of the viewer-to-vertex ray which is inside fog - t *= var_FogPlaneDistance / ( max( 0, var_FogPlaneDistance ) - u_FogEyeT ); - } - - vec4 color = vec4(1, 1, 1, GetFogAlpha(s, t)); - - color *= var_Color; - - outputColor = color; -} diff --git a/src/engine/renderer/glsl_source/fogQuake3_vp.glsl b/src/engine/renderer/glsl_source/fogQuake3_vp.glsl deleted file mode 100644 index 8c4549fedb..0000000000 --- a/src/engine/renderer/glsl_source/fogQuake3_vp.glsl +++ /dev/null @@ -1,85 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 Robert Beckebans - -This file is part of XreaL source code. - -XreaL source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -XreaL source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with XreaL source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/* fogQuake3_vp.glsl */ - -#insert common -#insert vertexSimple_vp -#insert vertexSkinning_vp -#insert vertexAnimation_vp - -uniform float u_Time; - -uniform colorPack u_ColorGlobal; - -uniform mat4 u_ModelMatrix; -uniform mat4 u_ModelViewProjectionMatrix; -uniform vec3 u_ViewOrigin; - -uniform float u_FogDensity; -uniform vec4 u_FogDepthVector; // fog plane - -// how far the vertex is under the fog plane -OUT(smooth) float var_FogPlaneDistance; - -OUT(smooth) vec3 var_ScaledViewerOffset; -OUT(smooth) vec4 var_Color; - -void DeformVertex( inout vec4 pos, - inout vec3 normal, - inout vec2 st, - inout vec4 color, - in float time ); - -void main() -{ - #insert material_vp - - vec4 position; - localBasis LB; - vec2 texCoord, lmCoord; - vec4 color; - - VertexFetch( position, LB, color, texCoord, lmCoord ); - - color = UnpackColor( u_ColorGlobal ); - - DeformVertex( position, - LB.normal, - texCoord, - color, - u_Time ); - - // transform vertex position into homogenous clip-space - gl_Position = u_ModelViewProjectionMatrix * position; - - // transform position into world space - position = u_ModelMatrix * position; - position.xyz /= position.w; - - var_ScaledViewerOffset = u_FogDensity * (position.xyz - u_ViewOrigin); - - // calculate the length in fog - var_FogPlaneDistance = dot(position.xyz, u_FogDepthVector.xyz) + u_FogDepthVector.w; - - var_Color = color; -} diff --git a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl b/src/engine/renderer/glsl_source/fog_fp.glsl similarity index 50% rename from src/engine/renderer/glsl_source/fogGlobal_fp.glsl rename to src/engine/renderer/glsl_source/fog_fp.glsl index 6097825900..b64e765860 100644 --- a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl +++ b/src/engine/renderer/glsl_source/fog_fp.glsl @@ -20,23 +20,49 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ -/* fogGlobal_fp.glsl */ +/* fog_fp.glsl */ #insert common #insert fogEquation_fp #define DEPTHMAP_GLSL +IN(smooth) vec3 var_Position; +IN(flat) vec4 var_FogSurface; + +#ifdef OUTSIDE_FOG +#define NUM_PLANES 5 +IN(flat) vec4 var_FogPlanes[NUM_PLANES]; +#endif + uniform sampler2D u_DepthMap; uniform colorPack u_Color; uniform vec3 u_ViewOrigin; -uniform float u_FogDensity; +uniform vec2 u_FogGradient; uniform mat4 u_UnprojectMatrix; DECLARE_OUTPUT(vec4) +#ifdef OUTSIDE_FOG +// Trace against the inner sides of the fog brush +float FogDistance(vec3 start, vec3 dir) +{ + vec4 start4 = vec4(-start, 1.0); + float minDist = 1.0e20; + for (int i = 0; i < NUM_PLANES; i++) + { + float dist = dot(start4, var_FogPlanes[i]) / dot(dir, var_FogPlanes[i].xyz) ; + if (dist >= 0.0) + { + minDist = min(minDist, dist); + } + } + return minDist < 1.0e20 ? minDist : 0.0; +} +#endif + void main() { #insert material_fp @@ -49,10 +75,41 @@ void main() vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0); P.xyz /= P.w; - // calculate the length in fog (t is always 1 if eye is in fog) - float s = distance(u_ViewOrigin, P.xyz) * u_FogDensity; - - vec4 color = vec4(1, 1, 1, GetFogAlpha(s, 1.0)); + #ifdef OUTSIDE_FOG + vec3 startPoint = var_Position; + vec3 viewDir = normalize(var_Position - u_ViewOrigin); + float fogBoundaryDist = FogDistance(var_Position, viewDir); + #else + vec3 startPoint = u_ViewOrigin; + float fogBoundaryDist = distance(u_ViewOrigin, var_Position); + #endif + + float depthDist = distance(startPoint, P.xyz); + + vec3 endPoint; + float distInFog; + if ( depthDist < fogBoundaryDist ) + { + endPoint = P.xyz; + distInFog = depthDist; + } + else + { + #ifdef OUTSIDE_FOG + endPoint = var_Position + fogBoundaryDist * viewDir; + #else + endPoint = var_Position; + #endif + + distInFog = fogBoundaryDist; + } + + float t0 = dot(var_FogSurface.xyz, startPoint) + var_FogSurface.w; + float t1 = dot(var_FogSurface.xyz, endPoint) + var_FogSurface.w; + + float s = distInFog * GetFogGradientModifier(u_FogGradient.y, t0, t1) * u_FogGradient.x; + + vec4 color = vec4(1, 1, 1, GetFogAlpha(s)); outputColor = UnpackColor( u_Color ) * color; } diff --git a/src/engine/renderer/glsl_source/fog_vp.glsl b/src/engine/renderer/glsl_source/fog_vp.glsl new file mode 100644 index 0000000000..c139b90eac --- /dev/null +++ b/src/engine/renderer/glsl_source/fog_vp.glsl @@ -0,0 +1,62 @@ +/* +=========================================================================== + +Daemon BSD Source Code +Copyright (c) 2026 Daemon Developers +All rights reserved. + +This file is part of the Daemon BSD Source Code (Daemon Source Code). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=========================================================================== +*/ + +uniform mat4 u_ModelViewProjectionMatrix; + +IN vec3 attr_Position; +IN vec4 attr_FogSurface; + +OUT(smooth) vec3 var_Position; +OUT(flat) vec4 var_FogSurface; + +#ifdef OUTSIDE_FOG +#define NUM_PLANES 5 +IN vec4 attr_FogPlanes[NUM_PLANES]; +OUT(flat) vec4 var_FogPlanes[NUM_PLANES]; +#endif + +void main() +{ + vec4 position = vec4(attr_Position, 1.0); + gl_Position = u_ModelViewProjectionMatrix * position; + var_Position = attr_Position; + var_FogSurface = attr_FogSurface; + + #ifdef OUTSIDE_FOG + for (int i = 0; i < NUM_PLANES; i++) + { + var_FogPlanes[i] = attr_FogPlanes[i]; + } + #endif +} diff --git a/src/engine/renderer/tr_animation.cpp b/src/engine/renderer/tr_animation.cpp index ea4e57160a..f0dd4670b5 100644 --- a/src/engine/renderer/tr_animation.cpp +++ b/src/engine/renderer/tr_animation.cpp @@ -661,7 +661,6 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) md5Model_t *model; md5Surface_t *surface; bool personalModel; - int fogNum; model = tr.currentModel->md5; @@ -676,9 +675,6 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) return; } - // see if we are in a fog volume - fogNum = R_FogWorldBox( ent->worldBounds ); - if ( !r_vboModels.Get() || !model->numVBOSurfaces || ( !glConfig.vboVertexSkinningAvailable && ent->e.skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) ) { @@ -732,7 +728,7 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( (surfaceType_t*) surface, shader, -1, fogNum ); + R_AddDrawSurf( (surfaceType_t*) surface, shader, -1 ); } } } @@ -781,7 +777,7 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( (surfaceType_t*) vboSurface, shader, -1, fogNum ); + R_AddDrawSurf( (surfaceType_t*) vboSurface, shader, -1 ); } } } diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 18795fec41..5e7319d852 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -993,7 +993,6 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, trRefEntity_t *entity, *oldEntity; shader_t *shader, *oldShader; int lightmapNum, oldLightmapNum; - int fogNum, oldFogNum; bool bspSurface; bool depthRange, oldDepthRange; int i; @@ -1006,7 +1005,6 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, oldEntity = nullptr; oldShader = nullptr; oldLightmapNum = -1; - oldFogNum = -1; oldDepthRange = false; depthRange = false; @@ -1025,7 +1023,6 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, entity = drawSurf->entity; shader = drawSurf->shader; lightmapNum = drawSurf->lightmapNum(); - fogNum = drawSurf->fog; bspSurface = drawSurf->bspSurface; if( entity == &tr.worldEntity ) { @@ -1039,7 +1036,7 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, continue; } - if ( entity == oldEntity && shader == oldShader && lightmapNum == oldLightmapNum && fogNum == oldFogNum ) + if ( entity == oldEntity && shader == oldShader && lightmapNum == oldLightmapNum ) { // fast path, same as previous sort rb_surfaceTable[Util::ordinal(*drawSurf->surface)](drawSurf->surface ); @@ -1049,18 +1046,17 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, // change the tess parameters if needed // an "entityMergable" shader is a shader that can have surfaces from separate // entities merged into a single batch, like smoke and blood puff sprites - if ( shader != oldShader || lightmapNum != oldLightmapNum || fogNum != oldFogNum || ( entity != oldEntity && !shader->entityMergable ) ) + if ( shader != oldShader || lightmapNum != oldLightmapNum || ( entity != oldEntity && !shader->entityMergable ) ) { if ( oldShader != nullptr ) { Tess_End(); } - Tess_Begin( Tess_StageIteratorColor, shader, false, lightmapNum, fogNum, bspSurface ); + Tess_Begin( Tess_StageIteratorColor, shader, false, lightmapNum, bspSurface ); oldShader = shader; oldLightmapNum = lightmapNum; - oldFogNum = fogNum; } // change the modelview matrix if needed @@ -1423,58 +1419,6 @@ void RB_RenderPostDepthLightTile() GL_CheckErrors(); } -void RB_RenderGlobalFog() -{ - if ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) - { - return; - } - - if ( r_noFog->integer ) - { - return; - } - - if ( !tr.world || tr.world->globalFog < 0 ) - { - return; - } - - GLIMP_LOGCOMMENT( "--- RB_RenderGlobalFog ---" ); - - RB_PrepareForSamplingDepthMap(); - - GL_Cull( cullType_t::CT_TWO_SIDED ); - - gl_fogGlobalShader->BindProgram(); - - // go back to the world modelview matrix - backEnd.orientation = backEnd.viewParms.world; - - { - fog_t* fog = &tr.world->fogs[ tr.world->globalFog ]; - - GLIMP_LOGCOMMENT( "--- RB_RenderGlobalFog( fogNum = %i ) ---", tr.world->globalFog ); - - GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - - gl_fogGlobalShader->SetUniform_FogDensity( fog->tcScale ); - gl_fogGlobalShader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - SetUniform_Color( gl_fogGlobalShader, fog->color ); - } - - gl_fogGlobalShader->SetUniform_UnprojectMatrix( backEnd.viewParms.unprojectionMatrix ); - - // bind u_DepthMap - gl_fogGlobalShader->SetUniform_DepthMapBindless( - GL_BindToTMU( 1, tr.depthSamplerImage ) - ); - - Tess_InstantScreenSpaceQuad(); - - GL_CheckErrors(); -} - void RB_RenderBloom() { if ( ( backEnd.refdef.rdflags & ( RDF_NOWORLDMODEL | RDF_NOBLOOM ) ) @@ -1827,7 +1771,7 @@ static void RB_RenderDebugUtils() GL_LoadModelViewMatrix( backEnd.orientation.modelViewMatrix ); gl_genericShader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); if ( r_showEntityBounds.Get() == 2) { @@ -1944,7 +1888,7 @@ static void RB_RenderDebugUtils() static vec3_t worldOrigins[ MAX_BONES ]; GL_State( GLS_POLYMODE_LINE | GLS_DEPTHTEST_DISABLE ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); for ( j = 0; j < skel->numBones; j++ ) { @@ -2048,7 +1992,7 @@ static void RB_RenderDebugUtils() } Tess_End(); - Tess_Begin( Tess_StageIteratorDebug, shader, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, shader, true, -1 ); gl_genericShader->SetUniform_ColorMapBindless( GL_BindToTMU( 0, shader->stages[ 0 ].bundle[ TB_COLORMAP ].image[ 0 ] ) ); @@ -2111,7 +2055,7 @@ static void RB_RenderDebugUtils() cubemapProbe_t* cubeProbe = &tr.cubeProbes[tr.cubeProbeGrid( x, y, z )]; - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); gl_reflectionShader->SetUniform_CameraPosition( position ); @@ -2127,7 +2071,7 @@ static void RB_RenderDebugUtils() } else { for ( const cubemapProbe_t& cubeProbe : tr.cubeProbes ) { - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); gl_reflectionShader->SetUniform_CameraPosition( cubeProbe.origin ); @@ -2181,7 +2125,7 @@ static void RB_RenderDebugUtils() vec3_t gridPoints[2]; R_GetNearestCubeMaps( backEnd.viewParms.orientation.origin, probes, trilerp, 2, gridPoints ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); vec3_t position; if ( r_showCubeProbes.Get() == Util::ordinal( showCubeProbesMode::GRID ) ) { @@ -2255,7 +2199,7 @@ static void RB_RenderDebugUtils() ); gl_genericShader->SetUniform_TextureMatrix( matrixIdentity ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); GL_CheckErrors(); for ( z = 0; z < tr.world->lightGridBounds[ 2 ]; z++ ) { @@ -2443,7 +2387,7 @@ static void RB_RenderDebugUtils() R_CalcFrustumNearCorners( splitFrustum, nearCorners ); R_CalcFrustumFarCorners( splitFrustum, farCorners ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); // draw outer surfaces for ( j = 0; j < 4; j++ ) @@ -2540,7 +2484,7 @@ static void RB_RenderDebugUtils() GL_PolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); } - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); Tess_AddCube( vec3_origin, node->mins, node->maxs, Color::White ); Tess_End(); @@ -2797,13 +2741,11 @@ static void RB_RenderView( bool depthPass ) } // Here so we don't do an extra MSAA resolve if both SSAO and global fog are present + // FIXME: global fog is done in a different place now, should this change? TransitionMSAAToMain( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); RB_RenderSSAO(); - // render global fog post process effect - RB_RenderGlobalFog(); - TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); // draw everything that is translucent @@ -3066,11 +3008,11 @@ const RenderCommand *StretchPicCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } if( !tess.indexes ) { - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( 4, 6 ); @@ -3152,7 +3094,7 @@ const RenderCommand *Poly2dCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( numverts, ( numverts - 2 ) * 3 ); @@ -3206,11 +3148,11 @@ const RenderCommand *Poly2dIndexedCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } if( !tess.verts ) { - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( numverts, numIndexes ); @@ -3302,11 +3244,11 @@ const RenderCommand *RotatedPicCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } if( !tess.indexes ) { - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( 4, 6 ); @@ -3391,11 +3333,11 @@ const RenderCommand *GradientPicCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } if( !tess.indexes ) { - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( 4, 6 ); @@ -3601,7 +3543,7 @@ const RenderCommand *PreparePortalCommand::ExecuteSelf( ) const GL_State( GLS_COLORMASK_BITS ); glState.glStateBitsMask = GLS_COLORMASK_BITS; - Tess_Begin( Tess_StageIteratorPortal, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorPortal, shader, false, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); @@ -3616,7 +3558,7 @@ const RenderCommand *PreparePortalCommand::ExecuteSelf( ) const GL_State( GLS_DEPTHMASK_TRUE | GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS); glState.glStateBitsMask = GLS_DEPTHMASK_TRUE | GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS; - Tess_Begin( Tess_StageIteratorPortal, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorPortal, shader, false, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); @@ -3667,7 +3609,7 @@ const RenderCommand *FinalisePortalCommand::ExecuteSelf( ) const glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); Tess_Begin( Tess_StageIteratorColor, shader, - false, surface->lightmapNum(), surface->fog, surface->bspSurface ); + false, surface->lightmapNum(), surface->bspSurface ); rb_surfaceTable[Util::ordinal( *( surface->surface ) )]( surface->surface ); Tess_End(); @@ -3676,7 +3618,7 @@ const RenderCommand *FinalisePortalCommand::ExecuteSelf( ) const GL_State( GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS); glState.glStateBitsMask = GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS; - Tess_Begin( Tess_StageIteratorPortal, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorPortal, shader, false, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index d03844d401..81cf6b4302 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -952,7 +952,6 @@ static void ParseTriangleSurface( dsurface_t* ds, drawVert_t* verts, bspSurface_ surf->lightmapNum /= 2; } - surf->fogIndex = LittleLong( ds->fogNum ) + 1; surf->shader = ShaderForShaderNum( ds->shaderNum ); if ( r_singleShader->integer && !surf->shader->isSky ) { @@ -1185,9 +1184,6 @@ static void ParseMesh( dsurface_t *ds, drawVert_t *verts, bspSurface_t *surf ) surf->lightmapNum /= 2; } - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - // get shader value surf->shader = ShaderForShaderNum( ds->shaderNum ); @@ -2720,7 +2716,6 @@ static void R_CreateWorldVBO() { srf.surface = surface->data; srf.bspSurface = true; srf.lightMapNum = surface->lightmapNum; - srf.fog = surface->fogIndex; srf.portalNum = surface->portalNum; srf.firstIndex = ( ( srfGeneric_t* ) surface->data )->firstIndex; @@ -3217,8 +3212,6 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) int count, brushesCount, sidesCount; int sideNum; int planeNum; - shader_t *shader; - float d; int firstSide = 0; Log::Debug("...loading fogs" ); @@ -3237,9 +3230,6 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) s_worldData.fogs = (fog_t*) ri.Hunk_Alloc( s_worldData.numFogs * sizeof( *out ), ha_pref::h_low ); out = s_worldData.fogs + 1; - // ydnar: reset global fog - s_worldData.globalFog = -1; - if ( !count ) { Log::Debug("no fog volumes loaded" ); @@ -3264,6 +3254,15 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) sidesCount = sidesLump->filelen / sizeof( *sides ); + struct FogVert + { + vec3_t position; + vec4_t surface; + vec4_t planes[ 5 ]; // faces other than the current triangle's + }; + std::vector fogVerts(24 * count); + std::vector fogIndexes(36 * count); + for ( i = 0; i < count; i++, fogs++ ) { out->originalBrushNumber = LittleLong( fogs->brushNum ); @@ -3316,32 +3315,9 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) out->bounds[ 1 ][ 2 ] = s_worldData.planes[ planeNum ].dist; } - // get information from the shader for fog parameters // it says RSF_3D but if there is no shader text found it should probably just error instead // of trying to create an implicit shader from an image... - shader = R_FindShader( fogs->shader, RSF_3D ); - - out->fogParms = shader->fogParms; - - out->color = Color::Adapt( shader->fogParms.color ); - - if ( tr.worldLinearizeTexture ) - { - out->color = out->color.ConvertFromSRGB(); - } - - out->color *= tr.identityLight; - - out->color.SetAlpha( 1 ); - - d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque; - out->tcScale = 1.0f / d; - - // ydnar: global fog sets clearcolor/zfar - if ( out->originalBrushNumber == -1 ) - { - s_worldData.globalFog = i + 1; - } + out->shader = R_FindShader( fogs->shader, RSF_3D ); // set the gradient vector sideNum = LittleLong( fogs->visibleSide ); @@ -3349,19 +3325,89 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) // ydnar: made this check a little more strenuous (was sideNum == -1) if ( sideNum < 0 || sideNum >= sidesCount ) { - out->hasSurface = false; + Vector4Set( out->surface, 0.0f, 0.0f, 0.0f, -1.0e10f ); } else { - out->hasSurface = true; planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); out->surface[ 3 ] = -s_worldData.planes[ planeNum ].dist; } + // add faces of fog brush with information about the other planes + // TODO: allow non-axis-aligned boxes. The GLSL can draw any brush with up to 6 faces. + constexpr int faces[ 6 ][ 4 ] + { + { 2, 0, 6, 4 }, + { 1, 3, 5, 7 }, + { 0, 1, 4, 5 }, + { 3, 2, 7, 6 }, + { 2, 3, 0, 1 }, + { 7, 6, 5, 4 } + }; + + for ( int face = 0; face < 6; face++ ) + { + for ( int v = 0; v < 4; v++ ) + { + FogVert &vert = fogVerts[ i * 24 + face * 4 + v ]; + int p = faces[ face ][ v ]; + vert.position[ 0 ] = out->bounds[ p & 1 ][ 0 ]; + vert.position[ 1 ] = out->bounds[ ( p >> 1 ) & 1 ][ 1 ]; + vert.position[ 2 ] = out->bounds[ p >> 2 ][ 2 ]; + + VectorCopy( out->surface, vert.surface ); + vert.surface[ 3 ] = -out->surface[ 3 ]; + + vec4_t *plane = vert.planes; + for ( int otherFace = 0; otherFace < 6; otherFace++ ) + { + if ( face != otherFace ) + { + planeNum = LittleLong( sides[ firstSide + otherFace ].planeNum ); + VectorCopy( s_worldData.planes[ planeNum ].normal, *plane ); + ( *plane )[ 3 ] = s_worldData.planes[ planeNum ].dist; + ++plane; + } + } + } + + fogIndexes[ 36 * i + 6 * face + 0 ] = i * 24 + face * 4 + 0; + fogIndexes[ 36 * i + 6 * face + 1 ] = i * 24 + face * 4 + 1; + fogIndexes[ 36 * i + 6 * face + 2 ] = i * 24 + face * 4 + 2; + fogIndexes[ 36 * i + 6 * face + 3 ] = i * 24 + face * 4 + 2; + fogIndexes[ 36 * i + 6 * face + 4 ] = i * 24 + face * 4 + 1; + fogIndexes[ 36 * i + 6 * face + 5 ] = i * 24 + face * 4 + 3; + } + + // add draw surf for fog brush faces + out->surf.firstIndex = 36 * i; + out->surf.numTriangles = 12; + out->surf.surfaceType = surfaceType_t::SF_TRIANGLES; + out++; } + vertexAttributeSpec_t attributes[] { + { ATTR_INDEX_POSITION, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].position, 3, sizeof( fogVerts[ 0 ] ), 0 }, + { ATTR_INDEX_FOG_SURFACE, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].surface, 4, sizeof( fogVerts[ 0 ] ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 0, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 0 ], 4, sizeof( FogVert ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 1, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 1 ], 4, sizeof( FogVert ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 2, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 2 ], 4, sizeof( FogVert ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 3, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 3 ], 4, sizeof( FogVert ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 4, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 4 ], 4, sizeof( FogVert ), 0 }, + }; + VBO_t *fogVBO = R_CreateStaticVBO( + "fogs VBO", std::begin( attributes ), std::end( attributes ), fogVerts.size() ); + IBO_t *fogIBO = R_CreateStaticIBO( "fogs IBO", &fogIndexes[ 0 ], fogIndexes.size() ); + SetupVAOBuffers( fogVBO, fogIBO, ATTR_POSITION | ATTR_FOG_SURFACE | ATTR_FOG_PLANES, &fogVBO->VAO ); + + for ( int j = 1; j < s_worldData.numFogs; j++ ) + { + s_worldData.fogs[ j ].surf.vbo = fogVBO; + s_worldData.fogs[ j ].surf.ibo = fogIBO; + } + Log::Debug("%i fog volumes loaded", s_worldData.numFogs ); } diff --git a/src/engine/renderer/tr_cmds.cpp b/src/engine/renderer/tr_cmds.cpp index 0dd9c83371..34845afa65 100644 --- a/src/engine/renderer/tr_cmds.cpp +++ b/src/engine/renderer/tr_cmds.cpp @@ -74,10 +74,6 @@ void R_PerformanceCounters() { Log::Notice("viewcluster: %i", tr.visClusters[ tr.visIndex ] ); } - else if ( r_speeds->integer == Util::ordinal(renderSpeeds_t::RSPEEDS_FOG )) - { - Log::Notice("fog srf:%i batches:%i", backEnd.pc.c_fogSurfaces, backEnd.pc.c_fogBatches ); - } else if ( r_speeds->integer == Util::ordinal(renderSpeeds_t::RSPEEDS_NEAR_FAR )) { Log::Notice("zNear: %.0f zFar: %.0f", tr.viewParms.zNear, tr.viewParms.zFar ); diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 6d52b59b4b..13b65030a5 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -222,7 +222,6 @@ Cvar::Cvar r_rendererAPI( "r_rendererAPI", "Renderer API: 0: OpenGL, 1: Vul cvar_t *r_showImages; - cvar_t *r_wolfFog; cvar_t *r_noFog; Cvar::Range> r_forceAmbient( "r_forceAmbient", "Minimal light amount in lightGrid; -1 to use map value", @@ -1157,7 +1156,6 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p r_heatHaze = Cvar_Get( "r_heatHaze", "1", CVAR_LATCH | CVAR_ARCHIVE ); r_noMarksOnTrisurfs = Cvar_Get( "r_noMarksOnTrisurfs", "1", CVAR_CHEAT ); - r_wolfFog = Cvar_Get( "r_wolfFog", "1", CVAR_CHEAT ); r_noFog = Cvar_Get( "r_noFog", "0", CVAR_CHEAT ); Cvar::Latch( r_forceAmbient ); @@ -1536,12 +1534,10 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p if ( r_lazyShaders.Get() == 1 ) { if ( tr.world->numFogs > 0 ) { - // Fog is applied dynamically based on the surface's BBox, so build all of the q3 fog shaders here - for ( uint32_t i = 0; i < 3; i++ ) { - gl_fogQuake3Shader->SetVertexSkinning( i & 1 ); - gl_fogQuake3Shader->SetVertexAnimation( i & 2 ); - gl_fogQuake3Shader->SetDeform( 0 ); - gl_fogQuake3Shader->MarkProgramForBuilding(); + for ( bool outer : { false, true } ) + { + gl_fogShader->SetOutsideFog( outer ); + gl_fogShader->MarkProgramForBuilding(); } } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 100f14228c..e736d33d1b 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -350,7 +350,6 @@ enum class ssaoMode { RSPEEDS_GENERAL = 1, RSPEEDS_CULLING, RSPEEDS_VIEWCLUSTER, - RSPEEDS_FOG, RSPEEDS_CHC, RSPEEDS_NEAR_FAR, }; @@ -939,7 +938,8 @@ enum ST_PORTALMAP, ST_HEATHAZEMAP, // heatHaze post process effect ST_LIQUIDMAP, - ST_FOGMAP, + ST_FOGMAP_INNER, // a fog seen from inside + ST_FOGMAP_OUTER, // a fog seen from outside ST_LIGHTMAP, ST_STYLELIGHTMAP, ST_STYLECOLORMAP, @@ -1109,13 +1109,6 @@ enum // reverse the cull operation # define ReverseCull(c) Util::enum_cast(2 - (c)) - enum class fogPass_t - { - FP_NONE, // surface is translucent and will just be adjusted properly - FP_EQUAL, // surface is opaque but possibly alpha tested - FP_LE // surface is translucent, but still needs a fog pass (fog surface) - }; - struct skyParms_t { float cloudHeight; @@ -1124,13 +1117,16 @@ enum struct fogParms_t { - vec3_t color; + Color::Color color; float depthForOpaque; + float falloffExp; }; struct shader_t { - char name[ MAX_QPATH ]; // game path, including extension + // max name length is MAX_QPATH - 1 but with room for automatically added suffixes + char name[ MAX_QPATH + 8 ]; + int registerFlags; // RSF_ int index; // this shader == tr.shaders[index] @@ -1150,7 +1146,8 @@ enum bool entityMergable; // merge across entites optimizable (smoke, blood) fogParms_t fogParms; - fogPass_t fogPass; // draw a blended pass, possibly with depth test equals + // FIXME: does this ever do anything useful? Support for opaque surfaces was + // removed and translucent stuff is mostly drawn on top of fog anyway. bool noFog; bool disableReliefMapping; // disable relief mapping for this material even if it's available @@ -1199,7 +1196,8 @@ enum } altShader[ MAX_ALTSHADERS ]; // state-based remapping; note that index 0 is unused struct shader_t *depthShader; - struct shader_t *fogShader; + struct shader_t *fogInnerShader; + struct shader_t *fogOuterShader; struct shader_t *next; }; @@ -1349,20 +1347,6 @@ enum //================================================================================= - struct fog_t - { - int originalBrushNumber; - vec3_t bounds[ 2 ]; - - Color::Color color; // in packed byte format - float tcScale; // texture coordinate vector scales - fogParms_t fogParms; - - // for clipping distance in fog when outside - bool hasSurface; - float surface[ 4 ]; - }; - struct viewParms_t { orientationr_t orientation; @@ -1488,7 +1472,6 @@ enum shader_t *shader; uint64_t sort; bool bspSurface; - int fog; int portalNum = -1; inline int index() const { @@ -1524,7 +1507,6 @@ enum surfaceType_t surfaceType; qhandle_t hShader; int16_t numVerts; - int16_t fogIndex; polyVert_t *verts; }; @@ -1633,6 +1615,19 @@ enum IBO_t *ibo; }; + struct fog_t + { + int originalBrushNumber; + vec3_t bounds[ 2 ]; + + shader_t *shader; // has the fog parms + + // for clipping distance in fog when outside + float surface[ 4 ]; + + srfGeneric_t surf; + }; + extern void ( *rb_surfaceTable[Util::ordinal(surfaceType_t::SF_NUM_SURFACE_TYPES)] )(void * ); void ValidateVertex( srfVert_t* vertex, int vertexID, shader_t* shader ); @@ -1651,7 +1646,6 @@ enum struct shader_t *shader; int16_t lightmapNum; // -1 = no lightmap - int16_t fogIndex; int portalNum; bool renderable = false; @@ -1751,8 +1745,6 @@ enum int numFogs; fog_t *fogs; - int globalFog; // Arnout: index of global fog - vec3_t lightGridOrigin; vec3_t lightGridSize; vec3_t lightGridInverseSize; @@ -2122,7 +2114,6 @@ enum int c_leafs; }; -#define FOG_TABLE_SIZE 256 #define FUNCTABLE_SIZE 1024 #define FUNCTABLE_SIZE2 10 #define FUNCTABLE_MASK ( FUNCTABLE_SIZE - 1 ) @@ -2184,9 +2175,6 @@ enum int c_vboVertexes; int c_vboIndexes; - int c_fogSurfaces; - int c_fogBatches; - int c_multiDrawElements; int c_multiDrawPrimitives; int c_multiVboIndexes; @@ -2504,8 +2492,6 @@ enum // internal shaders shader_t *defaultShader; - shader_t *fogEqualShader; - shader_t *fogLEShader; std::vector lightmaps; std::vector deluxemaps; @@ -2645,7 +2631,6 @@ enum extern cvar_t *r_lodBias; // push/pull LOD transitions extern cvar_t *r_lodScale; - extern cvar_t *r_wolfFog; extern cvar_t *r_noFog; extern Cvar::Range> r_forceAmbient; @@ -2833,18 +2818,16 @@ inline bool checkGLErrors() void R_AddPolygonSurfaces(); - void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface = false, int portalNum = -1 ); + void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, bool bspSurface = false, int portalNum = -1 ); void R_LocalNormalToWorld( const vec3_t local, vec3_t world ); void R_LocalPointToWorld( const vec3_t local, vec3_t world ); - cullResult_t R_CullBox( vec3_t worldBounds[ 2 ] ); + cullResult_t R_CullBox( const vec3_t worldBounds[ 2 ], int lastPlane = FRUSTUM_NEAR ); cullResult_t R_CullLocalBox( vec3_t bounds[ 2 ] ); cullResult_t R_CullLocalPointAndRadius( vec3_t origin, float radius ); cullResult_t R_CullPointAndRadius( vec3_t origin, float radius ); - int R_FogWorldBox( vec3_t bounds[ 2 ] ); - void R_SetupEntityWorldBounds( trRefEntity_t *ent ); void R_RotateEntityForViewParms( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *orien ); @@ -3136,7 +3119,6 @@ void GLimp_LogComment_( std::string comment ); // some drawing parameters from drawSurf_t int16_t lightmapNum; - int16_t fogNum; bool bspSurface; // Signals that ATTR_QTANGENT will not be needed, so functions that generate vertexes @@ -3194,7 +3176,6 @@ void GLimp_LogComment_( std::string comment ); shader_t *surfaceShader, bool skipTangents, int lightmapNum, - int fogNum, bool bspSurface = false ); // *INDENT-ON* @@ -3248,7 +3229,6 @@ void GLimp_LogComment_( std::string comment ); void ProcessShaderReflection( const shaderStage_t* pStage ); void ProcessShaderHeatHaze( const shaderStage_t* ); void ProcessShaderLiquid( const shaderStage_t* pStage ); - void ProcessShaderFog( const shaderStage_t* ); void Render_NONE( shaderStage_t *pStage ); void Render_NOP( shaderStage_t *pStage ); @@ -3261,7 +3241,7 @@ void GLimp_LogComment_( std::string comment ); void Render_portal( shaderStage_t *pStage ); void Render_heatHaze( shaderStage_t *pStage ); void Render_liquid( shaderStage_t *pStage ); - void Render_fog( shaderStage_t* pStage ); + void Render_fog( shaderStage_t *pStage ); /* ============================================================ @@ -3353,6 +3333,8 @@ void GLimp_LogComment_( std::string comment ); void RE_AddPolyToSceneET( qhandle_t hShader, int numVerts, const polyVert_t *verts ); void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ); + void R_AddFogBrushSurfaces(); + void RE_AddDynamicLightToSceneET( const vec3_t org, float radius, float intensity, float r, float g, float b, qhandle_t hShader, int flags ); void RE_AddDynamicLightToSceneQ3A( const vec3_t org, float intensity, float r, float g, float b ); diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 8937479b04..8afd91fc3f 100644 --- a/src/engine/renderer/tr_main.cpp +++ b/src/engine/renderer/tr_main.cpp @@ -341,7 +341,7 @@ R_CullBox Returns CULL_IN, CULL_CLIP, or CULL_OUT ================= */ -cullResult_t R_CullBox( vec3_t worldBounds[ 2 ] ) +cullResult_t R_CullBox( const vec3_t worldBounds[ 2 ], int lastPlane ) { bool anyClip; cplane_t *frust; @@ -355,7 +355,7 @@ cullResult_t R_CullBox( vec3_t worldBounds[ 2 ] ) // check against frustum planes anyClip = false; - for ( i = 0; i < FRUSTUM_PLANES; i++ ) + for ( i = 0; i <= lastPlane; i++ ) { frust = &tr.viewParms.frustum[ i ]; @@ -456,47 +456,6 @@ cullResult_t R_CullPointAndRadius( vec3_t pt, float radius ) return cullResult_t::CULL_IN; // completely inside frustum } -/* -================= -R_FogWorldBox -================= -*/ -int R_FogWorldBox( vec3_t bounds[ 2 ] ) -{ - int i, j; - fog_t *fog; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) - { - return 0; - } - - for ( i = 1; i < tr.world->numFogs; i++ ) - { - fog = &tr.world->fogs[ i ]; - - for ( j = 0; j < 3; j++ ) - { - if ( bounds[ 0 ][ j ] >= fog->bounds[ 1 ][ j ] ) - { - break; - } - - if ( bounds[ 1 ][ j ] <= fog->bounds[ 0 ][ j ] ) - { - break; - } - } - - if ( j == 3 ) - { - return i; - } - } - - return 0; -} - /* ================= R_LocalNormalToWorld @@ -878,7 +837,7 @@ static void R_SetupFrustum() MatrixAffineInverse(tr.viewParms.world.viewMatrix, invTransform); //transform planes back to world space for culling - for (int i = 0; i <= FRUSTUM_NEAR; i++) + for (int i = 0; i < FRUSTUM_PLANES; i++) { plane_t plane; VectorCopy(tr.viewParms.portalFrustum[i].normal, plane.normal); @@ -922,13 +881,18 @@ static void R_SetupFrustum() SetPlaneSignbits( &tr.viewParms.frustum[ i ] ); } - // Tr3B: set extra near plane which is required by the dynamic occlusion culling tr.viewParms.frustum[ FRUSTUM_NEAR ].type = PLANE_NON_AXIAL; VectorCopy( tr.viewParms.orientation.axis[ 0 ], tr.viewParms.frustum[ FRUSTUM_NEAR ].normal ); VectorMA( tr.viewParms.orientation.origin, r_znear->value, tr.viewParms.frustum[ FRUSTUM_NEAR ].normal, planeOrigin ); tr.viewParms.frustum[ FRUSTUM_NEAR ].dist = DotProduct( planeOrigin, tr.viewParms.frustum[ FRUSTUM_NEAR ].normal ); SetPlaneSignbits( &tr.viewParms.frustum[ FRUSTUM_NEAR ] ); + + tr.viewParms.frustum[ FRUSTUM_FAR ].type = PLANE_NON_AXIAL; + VectorCopy( tr.viewParms.orientation.axis[ 0 ], tr.viewParms.frustum[ FRUSTUM_FAR ].normal ); + tr.viewParms.frustum[ FRUSTUM_FAR ].dist = tr.viewParms.zFar + + DotProduct( tr.viewParms.orientation.origin, tr.viewParms.frustum[ FRUSTUM_FAR ].normal ); + SetPlaneSignbits( &tr.viewParms.frustum[ FRUSTUM_FAR ] ); } } @@ -1641,55 +1605,12 @@ bool R_MirrorViewBySurface(drawSurf_t *drawSurf) return true; } -/* -================= -R_SpriteFogNum - -See if a sprite is inside a fog volume -================= -*/ -int R_SpriteFogNum( trRefEntity_t *ent ) -{ - int i, j; - fog_t *fog; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) - { - return 0; - } - - for ( i = 1; i < tr.world->numFogs; i++ ) - { - fog = &tr.world->fogs[ i ]; - - for ( j = 0; j < 3; j++ ) - { - if ( ent->e.origin[ j ] - ent->e.radius >= fog->bounds[ 1 ][ j ] ) - { - break; - } - - if ( ent->e.origin[ j ] + ent->e.radius <= fog->bounds[ 0 ][ j ] ) - { - break; - } - } - - if ( j == 3 ) - { - return i; - } - } - - return 0; -} - /* ================= R_AddDrawSurf ================= */ -void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface, int portalNum ) +void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, bool bspSurface, int portalNum ) { // instead of checking for overflow, we just mask the index // so it wraps around @@ -1702,9 +1623,6 @@ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, i drawSurf->surface = surface; drawSurf->shader = shader; drawSurf->bspSurface = bspSurface; - /* Allow the renderer backend to merge main surfaces that have fog, ignoring the fogNum, - as it only matters for the emitted fog surfaces */ - drawSurf->fog = ( shader == tr.fogEqualShader || shader == tr.fogLEShader ) ? fogNum : 0; drawSurf->portalNum = portalNum; int entityNum; @@ -1728,15 +1646,7 @@ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, i tr.refdef.numDrawSurfs++; if ( shader->depthShader != nullptr ) { - R_AddDrawSurf( surface, shader->depthShader, 0, 0, bspSurface ); - } - - // don't draw the global fog twice on opaque surfaces - // allow global fog with the fogLE shader although drawing the global fog with the - // volumetric (fogQuake3) shader looks kind of buggy - if ( !shader->noFog && fogNum >= 1 && - ( fogNum != tr.world->globalFog || shader->fogPass == fogPass_t::FP_LE ) ) { - R_AddDrawSurf( surface, shader->fogShader, 0, fogNum, bspSurface ); + R_AddDrawSurf( surface, shader->depthShader, 0, bspSurface ); } } @@ -1882,7 +1792,7 @@ void R_AddEntitySurfaces() } shader = R_GetShaderByHandle( ent->e.customShader ); - R_AddDrawSurf( &entitySurface, shader, -1, R_SpriteFogNum( ent ) ); + R_AddDrawSurf( &entitySurface, shader, -1 ); break; case refEntityType_t::RT_MODEL: @@ -1893,7 +1803,7 @@ void R_AddEntitySurfaces() if ( !tr.currentModel ) { - R_AddDrawSurf( &entitySurface, tr.defaultShader, -1, 0 ); + R_AddDrawSurf( &entitySurface, tr.defaultShader, -1 ); } else { @@ -1927,7 +1837,7 @@ void R_AddEntitySurfaces() VectorClear( ent->worldBounds[ 0 ] ); VectorClear( ent->worldBounds[ 1 ] ); shader = R_GetShaderByHandle( ent->e.customShader ); - R_AddDrawSurf( &entitySurface, tr.defaultShader, -1, 0 ); + R_AddDrawSurf( &entitySurface, tr.defaultShader, -1 ); break; default: @@ -2085,6 +1995,8 @@ void R_RenderView( viewParms_t *parms ) // set camera frustum planes in world space again, but this time including the far plane tr.orientation = tr.viewParms.world; + R_AddFogBrushSurfaces(); + R_AddEntitySurfaces(); // Transform the blur vector in view space, FIXME for some we need reason invert its Z component diff --git a/src/engine/renderer/tr_mesh.cpp b/src/engine/renderer/tr_mesh.cpp index be367a213b..e4e5975f91 100644 --- a/src/engine/renderer/tr_mesh.cpp +++ b/src/engine/renderer/tr_mesh.cpp @@ -250,7 +250,6 @@ void R_AddMDVSurfaces( trRefEntity_t *ent ) mdvSurface_t *mdvSurface = nullptr; int lod; bool personalModel; - int fogNum; // don't add third_person objects if not in a portal personalModel = ( ent->e.renderfx & RF_THIRD_PERSON ) && @@ -294,9 +293,6 @@ void R_AddMDVSurfaces( trRefEntity_t *ent ) return; } - // see if we are in a fog volume - fogNum = R_FogWorldBox( ent->worldBounds ); - // draw all surfaces if ( r_vboModels.Get() && model->numVBOSurfaces ) { @@ -312,7 +308,7 @@ void R_AddMDVSurfaces( trRefEntity_t *ent ) // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( ( surfaceType_t * ) vboSurface, shader, -1, fogNum ); + R_AddDrawSurf( ( surfaceType_t * ) vboSurface, shader, -1 ); } } } @@ -328,7 +324,7 @@ void R_AddMDVSurfaces( trRefEntity_t *ent ) // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( ( surfaceType_t * ) mdvSurface, shader, -1, fogNum ); + R_AddDrawSurf( ( surfaceType_t * ) mdvSurface, shader, -1 ); } } } diff --git a/src/engine/renderer/tr_model_iqm.cpp b/src/engine/renderer/tr_model_iqm.cpp index 938b3d5089..eb7feb11b2 100644 --- a/src/engine/renderer/tr_model_iqm.cpp +++ b/src/engine/renderer/tr_model_iqm.cpp @@ -990,7 +990,6 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { srfIQModel_t *surface; int i, j; bool personalModel; - int fogNum; shader_t *shader; skin_t *skin; @@ -1008,9 +1007,6 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { return; } - // see if we are in a fog volume - fogNum = R_FogWorldBox( ent->worldBounds ); - for ( i = 0 ; i < IQModel->num_surfaces ; i++ ) { if(ent->e.customShader) shader = R_GetShaderByHandle( ent->e.customShader ); @@ -1048,7 +1044,7 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { // we will add shadows even if the main object isn't visible in the view if( !personalModel ) { - R_AddDrawSurf( ( surfaceType_t *)surface, shader, -1, fogNum ); + R_AddDrawSurf( ( surfaceType_t *)surface, shader, -1 ); } surface++; diff --git a/src/engine/renderer/tr_scene.cpp b/src/engine/renderer/tr_scene.cpp index e14632ef18..981f1872be 100644 --- a/src/engine/renderer/tr_scene.cpp +++ b/src/engine/renderer/tr_scene.cpp @@ -126,7 +126,7 @@ void R_AddPolygonSurfaces() for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys; i++, poly++ ) { sh = R_GetShaderByHandle( poly->hShader ); - R_AddDrawSurf( ( surfaceType_t * ) poly, sh, -1, poly->fogIndex ); + R_AddDrawSurf( ( surfaceType_t * ) poly, sh, -1 ); } } @@ -138,10 +138,7 @@ R_AddPolysToScene static void R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { srfPoly_t *poly; - int i, j; - int fogIndex; - fog_t *fog; - vec3_t bounds[ 2 ]; + int j; if ( !tr.registered ) { @@ -184,45 +181,6 @@ static void R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t // done. r_numPolys++; r_numPolyVerts += numVerts; - - // if no world is loaded - if ( tr.world == nullptr ) - { - fogIndex = 0; - } - // see if it is in a fog volume - else if ( tr.world->numFogs == 1 ) - { - fogIndex = 0; - } - else - { - // find which fog volume the poly is in - VectorCopy( poly->verts[ 0 ].xyz, bounds[ 0 ] ); - VectorCopy( poly->verts[ 0 ].xyz, bounds[ 1 ] ); - - for ( i = 1; i < poly->numVerts; i++ ) - { - AddPointToBounds( poly->verts[ i ].xyz, bounds[ 0 ], bounds[ 1 ] ); - } - - for ( fogIndex = 1; fogIndex < tr.world->numFogs; fogIndex++ ) - { - fog = &tr.world->fogs[ fogIndex ]; - - if ( BoundsIntersect( bounds[ 0 ], bounds[ 1 ], fog->bounds[ 0 ], fog->bounds[ 1 ] ) ) - { - break; - } - } - - if ( fogIndex == tr.world->numFogs ) - { - fogIndex = 0; - } - } - - poly->fogIndex = fogIndex; } } @@ -248,6 +206,67 @@ void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *vert //================================================================================= +// TODO don't recalc this every time, OR check if the near plane actually intersects the fog? +static float NearPlaneCornerDist() +{ + plane_t frustum[ 6 ]; + for ( int i = 0; i < 6; i++ ) + { + VectorCopy( tr.viewParms.frustum[ i ].normal, frustum[ i ].normal ); + frustum[ i ].dist = tr.viewParms.frustum[ i ].dist; + } + + vec3_t intersection; + PlanesGetIntersectionPoint( + frustum[ FRUSTUM_NEAR ], frustum[ FRUSTUM_LEFT ], frustum[ FRUSTUM_TOP ], intersection ); + + return Distance( tr.viewParms.orientation.origin, intersection ); +} + +// Returns true also if the view origin is slightly outside, but close enough that the view +// frustum's near plane might intersect. This causes some slight artifacts - the fog grows +// by ~5 qu when the view origin crosses the tolerance boundary. However it's not an +// issue on the surface plane, since the GLSL calcs chop off overhang there. Note that +// with original Q3 you are required to construct the fog so that it is impossible to pass +// through non-surface planes at all. +static bool R_InsideFog( int fognum, float tol ) +{ + // TODO: with portals this should use the point at the view frustum near plane instead + const vec3_t &origin = tr.viewParms.orientation.origin; + + const auto &bounds = tr.world->fogs[ fognum ].bounds; + + for ( int i = 0; i < 3; i++ ) + { + if ( origin[ i ] + tol < bounds[ 0 ][ i ] || origin[ i ] - tol > bounds[ 1 ][ i ] ) + { + return false; + } + } + + return true; +} + +void R_AddFogBrushSurfaces() +{ + float tol = NearPlaneCornerDist() + 0.1f; + + for ( int i = 1; i < tr.world->numFogs; i++ ) + { + const fog_t &fog = tr.world->fogs[ i ]; + + if ( R_CullBox( fog.bounds, FRUSTUM_FAR ) == cullResult_t::CULL_OUT ) + { + continue; + } + + shader_t *shader = R_InsideFog( i, tol ) + ? fog.shader->fogInnerShader + : fog.shader->fogOuterShader; + R_AddDrawSurf( ( surfaceType_t *)&fog.surf, shader, -1 ); + } +} + /* ===================== RE_AddRefEntityToScene diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 7d1a1d5c15..cff047758d 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -299,20 +299,7 @@ static void GLSL_InitGPUShadersOrError() } // Fog GLSL is always loaded and built because disabling fog is cheat. - { - // Q3A volumetric fog - gl_shaderManager.LoadShader( gl_fogQuake3Shader ); - - if ( glConfig.usingMaterialSystem ) - { - gl_shaderManager.LoadShader( gl_fogQuake3ShaderMaterial ); - } - - // global fog post process effect - gl_shaderManager.LoadShader( gl_fogGlobalShader ); - - gl_fogGlobalShader->MarkProgramForBuilding(); - } + gl_shaderManager.LoadShader( gl_fogShader ); if ( r_heatHaze->integer ) { @@ -491,9 +478,7 @@ void GLSL_ShutdownGPUShaders() gl_reflectionShaderMaterial = nullptr; gl_skyboxShader = nullptr; gl_skyboxShaderMaterial = nullptr; - gl_fogQuake3Shader = nullptr; - gl_fogQuake3ShaderMaterial = nullptr; - gl_fogGlobalShader = nullptr; + gl_fogShader = nullptr; gl_heatHazeShader = nullptr; gl_heatHazeShaderMaterial = nullptr; gl_screenShader = nullptr; @@ -717,7 +702,6 @@ void Tess_Begin( void ( *stageIteratorFunc )(), shader_t *surfaceShader, bool skipTangents, int lightmapNum, - int fogNum, bool bspSurface ) { if ( tess.numIndexes || tess.numVertexes || tess.multiDrawPrimitives ) @@ -736,7 +720,6 @@ void Tess_Begin( void ( *stageIteratorFunc )(), tess.skipTangents = skipTangents; tess.lightmapNum = lightmapNum; - tess.fogNum = fogNum; tess.bspSurface = bspSurface; // materials are optional (some debug drawing code doesn't use them) @@ -769,9 +752,9 @@ void Tess_Begin( void ( *stageIteratorFunc )(), } GLIMP_LOGCOMMENT( "--- Tess_Begin( surfaceShader = %s, " - "skipTangents = %i, lightmapNum = %i, fogNum = %i) ---", + "skipTangents = %i, lightmapNum = %i) ---", tess.surfaceShader ? tess.surfaceShader->name : "NULL", - tess.skipTangents, tess.lightmapNum, tess.fogNum ); + tess.skipTangents, tess.lightmapNum ); } void SetNormalScale( const shaderStage_t *pStage, vec3_t normalScale ) @@ -872,12 +855,6 @@ void ProcessShaderLiquid( const shaderStage_t* pStage ) { gl_liquidShader->SetGridLighting( lightMode == lightMode_t::GRID ); } -void ProcessShaderFog( const shaderStage_t* pStage ) { - gl_fogQuake3Shader->SetVertexSkinning( glConfig.vboVertexSkinningAvailable && tess.vboVertexSkinning ); - gl_fogQuake3Shader->SetVertexAnimation( tess.vboVertexAnimation ); - gl_fogQuake3Shader->SetDeform( pStage->deformIndex ); -} - void Render_NONE( shaderStage_t * ) { ASSERT_UNREACHABLE(); @@ -1613,64 +1590,63 @@ void Render_liquid( shaderStage_t *pStage ) GL_CheckErrors(); } -void Render_fog( shaderStage_t* pStage ) +void Render_fog( shaderStage_t *stage ) { - if ( r_noFog->integer || !r_wolfFog->integer || ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) + if ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) { return; } - const fog_t* fog = tr.world->fogs + tess.fogNum; - - GLIMP_LOGCOMMENT( "--- Render_fog( fogNum = %i, originalBrushNumber = %i ) ---", - tess.fogNum, fog->originalBrushNumber ); - - // rotate the gradient vector for this orientation - float eyeT; - vec4_t fogDepthVector; - if ( fog->hasSurface ) - { - VectorCopy( fog->surface, fogDepthVector ); - fogDepthVector[ 3 ] = -fog->surface[ 3 ]; - eyeT = DotProduct( backEnd.viewParms.orientation.origin, fogDepthVector ) + fogDepthVector[ 3 ]; - } - else + if ( r_noFog->integer ) { - Vector4Set( fogDepthVector, 0, 0, 0, 1 ); - eyeT = 1; // non-surface fog always has eye inside + return; } - GL_State( pStage->stateBits ); + GLIMP_LOGCOMMENT( "--- Render_fog ---" ); - ProcessShaderFog( pStage ); - gl_fogQuake3Shader->BindProgram(); + RB_PrepareForSamplingDepthMap(); - gl_fogQuake3Shader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - gl_fogQuake3Shader->SetUniform_FogDensity( fog->tcScale ); - gl_fogQuake3Shader->SetUniform_FogDepthVector( fogDepthVector ); - gl_fogQuake3Shader->SetUniform_FogEyeT( eyeT ); + GL_Cull( stage->shader->cullType ); - // u_Color - SetUniform_ColorGlobal( gl_fogQuake3Shader, fog->color ); + gl_fogShader->SetOutsideFog( stage->type == stageType_t::ST_FOGMAP_OUTER ); - gl_fogQuake3Shader->SetUniform_ModelMatrix( backEnd.orientation.transformMatrix ); - gl_fogQuake3Shader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); + gl_fogShader->BindProgram(); - // u_Bones - if ( glConfig.vboVertexSkinningAvailable && tess.vboVertexSkinning ) + GL_State( stage->stateBits ); + + gl_fogShader->SetUniform_FogGradient( + 1.0f / stage->shader->fogParms.depthForOpaque, stage->shader->fogParms.falloffExp ); + gl_fogShader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); + SetUniform_Color( gl_fogShader, stage->shader->fogParms.color ); + + switch ( stage->type ) { - gl_fogQuake3Shader->SetUniform_Bones( tess.numBones, tess.bones ); + case stageType_t::ST_FOGMAP_INNER: + { + // It's important to avoid far plane clipping + matrix_t projection, mvp; + MatrixPerspectiveProjectionFovXYInfiniteRH( projection, tr.refdef.fov_x, tr.refdef.fov_y, 1.0f ); + MatrixMultiply( projection, glState.modelViewMatrix[ glState.stackIndex ], mvp ); + gl_fogShader->SetUniform_ModelViewProjectionMatrix( mvp ); + break; } - - // u_VertexInterpolation - if ( tess.vboVertexAnimation ) + case stageType_t::ST_FOGMAP_OUTER: { - gl_fogQuake3Shader->SetUniform_VertexInterpolation( glState.vertexAttribsInterpolation ); + gl_fogShader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); + break; } + default: + ASSERT_UNREACHABLE(); + } + + gl_fogShader->SetUniform_UnprojectMatrix( backEnd.viewParms.unprojectionMatrix ); - gl_fogQuake3Shader->SetUniform_Time( backEnd.refdef.floatTime - backEnd.currentEntity->e.shaderTime ); + // bind u_DepthMap + gl_fogShader->SetUniform_DepthMapBindless( + GL_BindToTMU( 1, tr.depthSamplerImage ) + ); - gl_fogQuake3Shader->SetRequiredVertexPointers(); + gl_fogShader->SetRequiredVertexPointers(); Tess_DrawElements(); diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 7c622a26e4..5d54851263 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -4586,17 +4586,23 @@ static bool ParseShader( const char *_text ) // fogParms else if ( !Q_stricmp( token, "fogParms" ) ) { - /* - Log::Warn("fogParms keyword not supported in shader '%s'", shader.name); - SkipRestOfLine(text); + vec3_t fogColor; - */ - - if ( !ParseVector( text, 3, shader.fogParms.color ) ) + if ( !ParseVector( text, 3, fogColor ) ) { return false; } + shader.fogParms.color = Color::Adapt( fogColor ); + + if ( tr.worldLinearizeTexture ) + { + shader.fogParms.color = shader.fogParms.color.ConvertFromSRGB(); + } + + shader.fogParms.color *= tr.identityLight; + shader.fogParms.color.SetAlpha( 1 ); + token = COM_ParseExt2( text, false ); if ( !token[ 0 ] ) @@ -4605,7 +4611,7 @@ static bool ParseShader( const char *_text ) continue; } - shader.fogParms.depthForOpaque = atof( token ); + shader.fogParms.depthForOpaque = std::max( 1.0, atof( token ) ); shader.sort = Util::ordinal(shaderSort_t::SS_FOG); @@ -4613,6 +4619,39 @@ static bool ParseShader( const char *_text ) SkipRestOfLine( text ); continue; } + // fogGradient + // Default: fogGradient expFalloff 5 + // TODO: make the gradient plane configurable + else if ( !Q_stricmp( token, "fogGradient" ) ) + { + token = COM_ParseExt2( text, false ); + + if ( !Q_stricmp( token, "const" ) ) + { + shader.fogParms.falloffExp = 9999; + } + // fogGradient expFalloff + // scales density by 1 - exp(-k*t) where t is distance under fog plane + else if ( !Q_stricmp( token, "expFalloff" ) ) + { + token = COM_ParseExt2( text, false ); + float val = atof( token ); + + if ( val > 0.0f ) + { + shader.fogParms.falloffExp = M_LN2 / val; + } + else + { + Log::Warn( "bad expFalloff value in shader '%s'", shader.name ); + } + } + else + { + Log::Warn( "invalid fogGradient type '%s' in shader '%s'", token, shader.name ); + } + continue; + } // noFog else if ( !Q_stricmp( token, "noFog" ) ) { @@ -5611,11 +5650,11 @@ static void SetStagesRenderers() &UpdateSurfaceDataLiquid, &BindShaderLiquid, &ProcessMaterialLiquid, }; break; - case stageType_t::ST_FOGMAP: + case stageType_t::ST_FOGMAP_INNER: + case stageType_t::ST_FOGMAP_OUTER: stageRendererOptions = { - // All q3 fog shaders are built in RE_EndRegistration because they're assigned to surfaces dynamically &Render_fog, &MarkShaderBuildNOP, - &UpdateSurfaceDataFog, &BindShaderFog, &ProcessMaterialFog, + &UpdateSurfaceDataNOP, &BindShaderNOP, &ProcessMaterialNOP, }; break; default: @@ -5780,17 +5819,6 @@ static void GeneratePermanentShaderTable( const float *values, int numValues ) shaderTableHashTable[ hash ] = newTable; } -bool CheckShaderNameLength( const char* func_err, const char* name, const char* suffix ) -{ - if ( strlen( name ) + strlen( suffix ) >= MAX_QPATH ) - { - Log::Warn("%s Shader name %s%s length longer than MAX_QPATH %d", func_err, name, suffix, MAX_QPATH ); - return false; - } - - return true; -} - static void ValidateStage( shaderStage_t *pStage ) { struct stageCheck_t { @@ -5811,7 +5839,8 @@ static void ValidateStage( shaderStage_t *pStage ) { stageType_t::ST_SPECULARMAP, { true, true, false, "specularMap" } }, { stageType_t::ST_HEATHAZEMAP, { true, true, false, "heatHazeMap" } }, { stageType_t::ST_LIQUIDMAP, { true, true, false, "liquidMap" } }, - { stageType_t::ST_FOGMAP, { true, false, false, "fogMap" } }, + { stageType_t::ST_FOGMAP_INNER, { true, false, false, "fogMapInner" } }, + { stageType_t::ST_FOGMAP_OUTER, { true, false, false, "fogMapOuter" } }, // The lightmap is fetched at render time. { stageType_t::ST_LIGHTMAP, { true, false, false, "light map" } }, // The lightmap is fetched at render time. @@ -5925,7 +5954,7 @@ static float DetermineShaderSort() return Util::ordinal(shaderSort_t::SS_BLEND0); } - // determine sort order and fog color adjustment + // determine sort order if ( ( stages[ stage ].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) && ( stages[ 0 ].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) ) { @@ -5968,6 +5997,11 @@ static shader_t *FinishShader() shader.portalRange = r_portalDefaultRange.Get(); } + if ( shader.fogParms.falloffExp == 0.0f ) + { + shader.fogParms.falloffExp = M_LN2 / 5; + } + numStages = MAX_SHADER_STAGES; GroupActiveStages(); @@ -5993,14 +6027,6 @@ static shader_t *FinishShader() shader.sort = DetermineShaderSort(); - if ( shader.sort <= Util::ordinal( shaderSort_t::SS_OPAQUE ) ) { - shader.fogPass = fogPass_t::FP_EQUAL; - } else if ( shader.contentFlags & CONTENTS_FOG ) { - shader.fogPass = fogPass_t::FP_LE; - } - - shader.noFog = shader.noFog || shader.fogPass == fogPass_t::FP_NONE; - // look for multitexture potential CollapseStages(); @@ -6013,13 +6039,7 @@ static shader_t *FinishShader() // Copy the current global shader to a newly allocated shader. shader_t *ret = MakeShaderPermanent(); - if ( !shader.noFog ) { - if ( shader.fogPass == fogPass_t::FP_EQUAL ) { - ret->fogShader = tr.fogEqualShader; - } else { - ret->fogShader = tr.fogLEShader; - } - } + size_t nameLength = strlen( shader.name ); // generate depth-only shader if necessary if( r_depthShaders.Get() && @@ -6033,26 +6053,8 @@ static shader_t *FinishShader() stages[1].active = false; numStages = 1; shader.noFog = true; - shader.fogShader = nullptr; - - const char* depthShaderSuffix = "$depth"; - - if ( !CheckShaderNameLength( "FinishShader", shader.name, depthShaderSuffix ) ) - { - ret->depthShader = nullptr; - - if ( glConfig.usingMaterialSystem && !tr.worldLoaded ) { - uint8_t maxStages = ret->lastStage - ret->stages; - - // Add 1 for potential fog stages - maxStages = PAD( maxStages + 1, 4 ); // Aligned to 4 components - materialSystem.maxStages = std::max( maxStages, materialSystem.maxStages ); - } - - return ret; - } - strcat( shader.name, depthShaderSuffix ); + Q_strcat( shader.name, sizeof( shader.name ), "$depth" ); if( stages[0].stateBits & GLS_ATEST_BITS ) { // alpha test requires a custom depth shader @@ -6099,12 +6101,34 @@ static shader_t *FinishShader() ret->depthShader = nullptr; } + if ( ret->fogParms.depthForOpaque != 0 ) + { + shader.name[ nameLength ] = '\0'; + Q_strcat( shader.name, sizeof( shader.name ), "$fogIn" ); + stages[1].active = false; + numStages = 1; + ResetStruct( stages[ 0 ] ); + stages[ 0 ].active = true; + stages[ 0 ].type = stageType_t::ST_FOGMAP_INNER; + stages[ 0 ].stateBits = GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + shader.cullType = cullType_t::CT_FRONT_SIDED; + SetStagesRenderers(); + ret->fogInnerShader = MakeShaderPermanent(); + + shader.name[ nameLength ] = '\0'; + Q_strcat( shader.name, sizeof( shader.name ), "$fogOut" ); + stages[ 0 ].type = stageType_t::ST_FOGMAP_OUTER; + stages[ 0 ].stateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + shader.cullType = cullType_t::CT_BACK_SIDED; + SetStagesRenderers(); + ret->fogOuterShader = MakeShaderPermanent(); + } + if ( glConfig.usingMaterialSystem && !tr.worldLoaded ) { uint8_t maxStages = ret->lastStage - ret->stages; // Add 1 for potential depth stages - // Add 1 for potential fog stages - maxStages = PAD( maxStages + 2, 4 ); // Aligned to 4 components + maxStages = PAD( maxStages + 1, 4 ); // Aligned to 4 components materialSystem.maxStages = std::max( maxStages, materialSystem.maxStages ); } @@ -6241,6 +6265,7 @@ shader_t *R_FindShader( const char *name, int flags ) ClearGlobalShader(); Q_strncpyz( shader.name, strippedName, sizeof( shader.name ) ); + ASSERT_LT( strlen( shader.name ), MAX_QPATH ); shader.registerFlags = flags; for ( i = 0; i < MAX_SHADER_STAGES; i++ ) @@ -6431,8 +6456,9 @@ qhandle_t RE_RegisterShader( const char *name, int flags ) { shader_t *sh; - if ( !CheckShaderNameLength( "RE_RegisterShader", name, "" ) ) + if ( strlen( name ) >= MAX_QPATH ) { + Log::Warn( "RE_RegisterShader: name '%s' is too long", name ); return 0; } @@ -6536,7 +6562,6 @@ class ListShadersCmd : public Cmd::StaticCmd { stageType_t::ST_PORTALMAP, "PORTALMAP" }, { stageType_t::ST_HEATHAZEMAP, "HEATHAZEMAP" }, { stageType_t::ST_LIQUIDMAP, "LIQUIDMAP" }, - { stageType_t::ST_FOGMAP, "FOGMAP" }, { stageType_t::ST_LIGHTMAP, "LIGHTMAP" }, { stageType_t::ST_STYLELIGHTMAP, "STYLELIGHTMAP" }, { stageType_t::ST_STYLECOLORMAP, "STYLECOLORMAP" }, @@ -6958,34 +6983,11 @@ static void CreateInternalShaders() Q_strncpyz( shader.name, "", sizeof( shader.name ) ); shader.noFog = true; - shader.fogShader = nullptr; stages[ 0 ].type = stageType_t::ST_DIFFUSEMAP; stages[ 0 ].bundle[ 0 ].image[ 0 ] = tr.defaultImage; stages[ 0 ].active = true; stages[ 0 ].stateBits = GLS_DEFAULT; tr.defaultShader = FinishShader(); - - Q_strncpyz( shader.name, "", sizeof( shader.name ) ); - - shader.sort = Util::ordinal( shaderSort_t::SS_FOG ); - stages[0].type = stageType_t::ST_FOGMAP; - for ( int i = 0; i < 5; i++ ) { - stages[0].bundle[i].image[0] = nullptr; - } - stages[0].active = true; - stages[0].stateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL; - tr.fogEqualShader = FinishShader(); - - Q_strncpyz( shader.name, "", sizeof( shader.name ) ); - - shader.sort = Util::ordinal( shaderSort_t::SS_FOG ); - stages[0].type = stageType_t::ST_FOGMAP; - for ( int i = 0; i < 5; i++ ) { - stages[0].bundle[i].image[0] = nullptr; - } - stages[0].active = true; - stages[0].stateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - tr.fogLEShader = FinishShader(); } /* diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index d12279d07f..12981a768e 100644 --- a/src/engine/renderer/tr_surface.cpp +++ b/src/engine/renderer/tr_surface.cpp @@ -53,7 +53,7 @@ void Tess_EndBegin() { Tess_End(); Tess_Begin( tess.stageIteratorFunc, tess.surfaceShader, tess.skipTangents, - tess.lightmapNum, tess.fogNum, tess.bspSurface ); + tess.lightmapNum, tess.bspSurface ); } /* @@ -114,7 +114,7 @@ void Tess_CheckOverflow( int verts, int indexes ) } Tess_Begin( tess.stageIteratorFunc, tess.surfaceShader, tess.skipTangents, - tess.lightmapNum, tess.fogNum, tess.bspSurface ); + tess.lightmapNum, tess.bspSurface ); } /* @@ -534,14 +534,14 @@ void Tess_InstantScreenSpaceQuad() { if ( glConfig.gpuShader4Available ) { tr.skipVBO = true; - Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1 ); rb_surfaceTable[Util::ordinal( *( tr.genericTriangle->surface ) )]( tr.genericTriangle->surface ); Tess_DrawElements(); tr.skipVBO = false; } else { - Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1 ); rb_surfaceTable[Util::ordinal( *( tr.genericQuad->surface ) )]( tr.genericQuad->surface ); GL_VertexAttribsState( ATTR_POSITION ); Tess_DrawElements(); @@ -556,7 +556,7 @@ void Tess_InstantQuad( u_ModelViewProjectionMatrix &shader, const float x, const { GLIMP_LOGCOMMENT( "--- Tess_InstantQuad ---" ); - Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1 ); /* We don't use x, y, width, height directly to make it compatible with R_InitGenericVBOs() in tr_vbo.cpp. diff --git a/src/engine/renderer/tr_world.cpp b/src/engine/renderer/tr_world.cpp index a8bac11e42..b02cb35d49 100644 --- a/src/engine/renderer/tr_world.cpp +++ b/src/engine/renderer/tr_world.cpp @@ -130,7 +130,7 @@ static bool R_CullSurface( surfaceType_t *surface, shader_t *shader, int planeBi R_AddWorldSurface ====================== */ -static bool R_AddWorldSurface( bspSurface_t *surf, int fogIndex, int portalNum, int planeBits ) +static bool R_AddWorldSurface( bspSurface_t *surf, int portalNum, int planeBits ) { if ( surf->viewCount == tr.viewCountNoReset ) { @@ -145,7 +145,7 @@ static bool R_AddWorldSurface( bspSurface_t *surf, int fogIndex, int portalNum, return true; } - R_AddDrawSurf( surf->data, surf->shader, surf->lightmapNum, fogIndex, true, portalNum ); + R_AddDrawSurf( surf->data, surf->shader, surf->lightmapNum, true, portalNum ); return true; } @@ -168,7 +168,6 @@ void R_AddBSPModelSurfaces( trRefEntity_t *ent ) model_t *pModel; unsigned int i; vec3_t boundsCenter; - int fogNum; pModel = R_GetModelByHandle( ent->e.hModel ); bspModel = pModel->bsp; @@ -190,11 +189,9 @@ void R_AddBSPModelSurfaces( trRefEntity_t *ent ) return; } - fogNum = R_FogWorldBox( ent->worldBounds ); - for ( i = 0; i < bspModel->numSurfaces; i++ ) { - R_AddWorldSurface( bspModel->firstSurface + i, fogNum, -1, FRUSTUM_CLIPALL ); + R_AddWorldSurface( bspModel->firstSurface + i, -1, FRUSTUM_CLIPALL ); } } @@ -254,7 +251,7 @@ static void R_AddLeafSurfaces( bspNode_t *node, int planeBits ) { // the surface may have already been added if it // spans multiple leafs - R_AddWorldSurface( *view, ( *view )->fogIndex, ( *mark )->portalNum, planeBits); + R_AddWorldSurface( *view, ( *mark )->portalNum, planeBits); ( *mark )->viewCount = tr.viewCountNoReset;