From bf333c6d106886441d35cc37dc8dffc9f3dfeeae Mon Sep 17 00:00:00 2001 From: VReaperV Date: Mon, 15 Jan 2024 17:44:53 +0300 Subject: [PATCH] Fix portal surface blending, entity selection and recursive chains with mirrors Fixes portal blending with portal surfaces, misc_portal_surface entity selection and GL_FrontFace for mirrors in chains of recursive portals/mirrors. Fixes #1011. Disables the 64qu distance limit for portal entities which wasn't working properly anyway. We only need to render one stage for portal surfaces in depth and stencil passes, so render the first one available in Tess_StageIteratorPortal(). Don't compute color and texMatrices since we don't use those at this stage. --- src/engine/renderer/tr_backend.cpp | 29 ++++++++++---- src/engine/renderer/tr_local.h | 2 + src/engine/renderer/tr_main.cpp | 49 ++++++++++++++---------- src/engine/renderer/tr_shade.cpp | 61 ++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 28 deletions(-) diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index ce00605dc5..05ca2a961d 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -386,7 +386,7 @@ void GL_PolygonOffset( float factor, float units ) void GL_Cull( cullType_t cullType ) { - if ( backEnd.viewParms.isMirror ) + if ( backEnd.viewParms.mirrorLevel % 2 == 1 ) { GL_FrontFace( GL_CW ); } @@ -5543,7 +5543,7 @@ const RenderCommand *PreparePortalCommand::ExecuteSelf( ) const GL_State( GLS_COLORMASK_BITS ); glState.glStateBitsMask = GLS_COLORMASK_BITS; - Tess_Begin( Tess_StageIteratorGeneric, nullptr, shader, + Tess_Begin( Tess_StageIteratorPortal, nullptr, shader, nullptr, false, false, -1, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); @@ -5556,10 +5556,10 @@ const RenderCommand *PreparePortalCommand::ExecuteSelf( ) const glStencilFunc( GL_EQUAL, backEnd.viewParms.portalLevel + 1, 0xff ); glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); - GL_State( GLS_DEPTHMASK_TRUE | GLS_DEPTHTEST_DISABLE | GLS_COLORMASK_BITS ); - glState.glStateBitsMask = GLS_DEPTHMASK_TRUE | GLS_DEPTHTEST_DISABLE | GLS_COLORMASK_BITS; + 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_StageIteratorGeneric, nullptr, shader, + Tess_Begin( Tess_StageIteratorPortal, nullptr, shader, nullptr, false, false, -1, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); @@ -5604,17 +5604,30 @@ const RenderCommand *FinalisePortalCommand::ExecuteSelf( ) const GL_LoadModelViewMatrix( backEnd.orientation.modelViewMatrix ); + GL_State( GLS_DEPTHMASK_TRUE | GLS_DEPTHFUNC_ALWAYS ); + glState.glStateBitsMask = GLS_DEPTHMASK_TRUE | GLS_DEPTHFUNC_ALWAYS; + glStencilFunc( GL_EQUAL, backEnd.viewParms.portalLevel + 1, 0xff ); + glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); + + Tess_Begin( Tess_StageIteratorGeneric, nullptr, shader, + nullptr, false, false, -1, -1 ); + rb_surfaceTable[Util::ordinal( *( surface->surface ) )]( surface->surface ); + Tess_End(); + glStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); - GL_State( GLS_DEPTHMASK_TRUE | GLS_DEPTHFUNC_ALWAYS | GLS_COLORMASK_BITS ); - glState.glStateBitsMask = GLS_DEPTHMASK_TRUE | GLS_DEPTHFUNC_ALWAYS | GLS_COLORMASK_BITS; + GL_State( GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS); + glState.glStateBitsMask = GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS; - Tess_Begin( Tess_StageIteratorGeneric, nullptr, shader, + Tess_Begin( Tess_StageIteratorPortal, nullptr, shader, nullptr, false, false, -1, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); + glStencilFunc( GL_EQUAL, backEnd.viewParms.portalLevel, 0xff ); + glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); + glState.glStateBitsMask = 0; if( backEnd.viewParms.portalLevel == 0 ) { diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index fdf87faf4e..97719b56bb 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1619,6 +1619,7 @@ enum class deluxeMode_t { NONE, GRID, MAP }; vec3_t pvsOrigin; // may be different than or.origin for portals int portalLevel; // number of portals this view is through + int mirrorLevel; bool isMirror; // the portal is a mirror, invert the face culling int frameSceneNum; // copied from tr.frameSceneNum @@ -3521,6 +3522,7 @@ inline bool checkGLErrors() void Tess_StageIteratorDebug(); void Tess_StageIteratorGeneric(); + void Tess_StageIteratorPortal(); void Tess_StageIteratorDepthFill(); void Tess_StageIteratorShadowFill(); void Tess_StageIteratorLighting(); diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 3cd22c9793..e3321bf44b 100644 --- a/src/engine/renderer/tr_main.cpp +++ b/src/engine/renderer/tr_main.cpp @@ -1301,28 +1301,39 @@ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surfac // locate the portal entity closest to this plane. // origin will be the origin of the portal, origin2 will be // the origin of the camera - for ( i = 0; i < tr.refdef.numEntities; i++ ) - { - e = &tr.refdef.entities[ i ]; - - if ( e->e.reType != refEntityType_t::RT_PORTALSURFACE ) - { + int numVertsOld = tess.numVertexes; + rb_surfaceTable[Util::ordinal( *( drawSurf->surface ) )]( drawSurf->surface ); + int numVerts = tess.numVertexes - numVertsOld; + vec3_t portalCenter{ 0.0, 0.0, 0.0 }; + for ( int vertIndex = 0; vertIndex < numVerts; vertIndex++ ) { + VectorAdd( portalCenter, tess.verts[vertIndex].xyz, portalCenter ); + } + VectorScale( portalCenter, 1.0 / numVerts, portalCenter ); + float minDistance = FLT_MAX; + trRefEntity_t* currentPortal = nullptr; + for ( i = 0; i < tr.refdef.numEntities; i++ ) { + e = &tr.refdef.entities[i]; + + if ( e->e.reType != refEntityType_t::RT_PORTALSURFACE ) { continue; } - d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; - - if ( d > 64 || d < -64 ) - { - continue; + float distance = Distance( e->e.origin, portalCenter ); + if ( distance < minDistance ) { + minDistance = distance; + currentPortal = e; } - - // get the pvsOrigin from the entity + } + if( currentPortal) { + // project the origin onto the surface plane to get + // an origin point we can rotate around + e = currentPortal; VectorCopy( e->e.oldorigin, pvsOrigin ); + d = DotProduct( e->e.origin, plane.normal ) - plane.dist; + VectorMA( e->e.origin, -d, surface->axis[ 0 ], surface->origin ); // if the entity is just a mirror, don't use as a camera point - if ( e->e.oldorigin[ 0 ] == e->e.origin[ 0 ] && e->e.oldorigin[ 1 ] == e->e.origin[ 1 ] && e->e.oldorigin[ 2 ] == e->e.origin[ 2 ] ) - { + if ( e->e.oldorigin[ 0 ] == e->e.origin[ 0 ] && e->e.oldorigin[ 1 ] == e->e.origin[ 1 ] && e->e.oldorigin[ 2 ] == e->e.origin[ 2 ] ) { VectorScale( plane.normal, plane.dist, surface->origin ); VectorCopy( surface->origin, camera->origin ); VectorSubtract( vec3_origin, surface->axis[ 0 ], camera->axis[ 0 ] ); @@ -1333,11 +1344,6 @@ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surfac return true; } - // project the origin onto the surface plane to get - // an origin point we can rotate around - d = DotProduct( e->e.origin, plane.normal ) - plane.dist; - VectorMA( e->e.origin, -d, surface->axis[ 0 ], surface->origin ); - // now get the camera origin and orientation VectorCopy( e->e.oldorigin, camera->origin ); AxisCopy( e->e.axis, camera->axis ); @@ -1865,6 +1871,9 @@ static bool R_MirrorViewBySurface(drawSurf_t *drawSurf) { return false; // bad portal, no portalentity } + if ( newParms.isMirror ) { + newParms.mirrorLevel++; + } // draw stencil mask R_AddPreparePortalCmd( drawSurf ); diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index c5d19ac8b5..a6db45aeae 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -2876,6 +2876,67 @@ void Tess_StageIteratorGeneric() } } + +void Tess_StageIteratorPortal() { + int stage; + + // log this call + if ( r_logFile->integer ) { + // don't just call LogComment, or we will get + // a call to va() every frame! + GLimp_LogComment( va + ( "--- Tess_StageIteratorPortal( %s, %i vertices, %i triangles ) ---\n", tess.surfaceShader->name, + tess.numVertexes, tess.numIndexes / 3 ) ); + } + + GL_CheckErrors(); + + if ( !glState.currentVBO || !glState.currentIBO || glState.currentVBO == tess.vbo || glState.currentIBO == tess.ibo ) { + Tess_UpdateVBOs(); + } + + // set face culling appropriately + if ( backEnd.currentEntity->e.renderfx & RF_SWAPCULL ) + GL_Cull( ReverseCull( tess.surfaceShader->cullType ) ); + else + GL_Cull( tess.surfaceShader->cullType ); + + // call shader function + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { + shaderStage_t* pStage = tess.surfaceStages[stage]; + + if ( !pStage ) { + break; + } + + if ( !RB_EvalExpression( &pStage->ifExp, 1.0 ) ) { + continue; + } + + switch ( pStage->type ) { + case stageType_t::ST_COLORMAP: + case stageType_t::ST_LIGHTMAP: + case stageType_t::ST_DIFFUSEMAP: + case stageType_t::ST_COLLAPSE_lighting_PHONG: + case stageType_t::ST_COLLAPSE_lighting_PBR: + case stageType_t::ST_COLLAPSE_reflection_CB: + case stageType_t::ST_REFLECTIONMAP: + case stageType_t::ST_REFRACTIONMAP: + case stageType_t::ST_DISPERSIONMAP: + case stageType_t::ST_SKYBOXMAP: + case stageType_t::ST_SCREENMAP: + case stageType_t::ST_PORTALMAP: + case stageType_t::ST_HEATHAZEMAP: + case stageType_t::ST_LIQUIDMAP: + Render_generic( pStage ); + return; + + default: + break; + } + } +} + void Tess_StageIteratorDepthFill() { int stage;