From 5b374f01f6a9cb59d4c5751fc6c60843ed3d4ca9 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Tue, 19 Nov 2024 02:05:58 +0300 Subject: [PATCH] Fix portals with r_smp 1 Save portal AABBs when loading the map, then use them to cull portals instead of using the vertex buffer. --- src/engine/renderer/tr_bsp.cpp | 42 +++++++++ src/engine/renderer/tr_local.h | 13 ++- src/engine/renderer/tr_main.cpp | 155 ++++++++++--------------------- src/engine/renderer/tr_world.cpp | 8 +- 4 files changed, 105 insertions(+), 113 deletions(-) diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index a997ced291..e8a56f282f 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -2929,6 +2929,7 @@ static void R_CreateWorldVBO() numVerts = 0; numTriangles = 0; numSurfaces = 0; + int numPortals = 0; for ( k = 0; k < s_worldData.numSurfaces; k++ ) { @@ -2936,6 +2937,9 @@ static void R_CreateWorldVBO() if ( surface->shader->isPortal || surface->shader->autoSpriteMode != 0 ) { + if ( surface->shader->isPortal ) { + numPortals++; + } continue; } @@ -3156,6 +3160,44 @@ static void R_CreateWorldVBO() vboNumVerts += numSurfVerts; } + s_worldData.numPortals = numPortals; + s_worldData.portals = ( AABB* ) ri.Hunk_Alloc( numPortals * sizeof( AABB ), ha_pref::h_low ); + int portal = 0; + for ( i = 0; i < s_worldData.numSurfaces; i++ ) { + surface = &s_worldData.surfaces[i]; + + if ( surface->shader->isPortal ) { + surface->portalNum = portal; + AABB* aabb = &s_worldData.portals[portal]; + switch ( *surface->data ) { + case surfaceType_t::SF_GRID: + { + srfGeneric_t* srf = ( srfGeneric_t* ) surface->data; + VectorCopy( srf->origin, aabb->origin ); + VectorCopy( srf->bounds[0], aabb->mins ); + VectorCopy( srf->bounds[1], aabb->maxs ); + Log::Warn( "Grid portals aren't properly supported" ); + break; + } + case surfaceType_t::SF_FACE: + case surfaceType_t::SF_TRIANGLES: + { + srfGeneric_t* srf = ( srfGeneric_t* ) surface->data; + VectorCopy( srf->origin, aabb->origin ); + VectorCopy( srf->bounds[0], aabb->mins ); + VectorCopy( srf->bounds[1], aabb->maxs ); + break; + } + default: + Log::Warn( "Unsupported portal surface type" ); + break; + } + portal++; + } else { + surface->portalNum = -1; + } + } + ASSERT_EQ( vboNumVerts, numVerts ); ASSERT_EQ( vboNumIndexes, numTriangles * 3 ); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 9350907f6f..95665ffb55 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1610,6 +1610,7 @@ enum class shaderProfilerRenderSubGroupsMode { uint64_t sort; bool bspSurface; int fog; + int portalNum = -1; uint materialsSSBOOffset[ MAX_SHADER_STAGES ]; bool initialized[ MAX_SHADER_STAGES ]; @@ -1871,6 +1872,7 @@ enum class shaderProfilerRenderSubGroupsMode { int16_t lightmapNum; // -1 = no lightmap int16_t fogIndex; + int portalNum; surfaceType_t *data; // any of srf*_t }; @@ -1919,6 +1921,12 @@ enum class shaderProfilerRenderSubGroupsMode { byte unused; }; + struct AABB { + vec3_t origin; + vec3_t mins; + vec3_t maxs; + }; + // ydnar: optimization #define WORLD_MAX_SKY_NODES 32 @@ -1945,6 +1953,9 @@ enum class shaderProfilerRenderSubGroupsMode { int numSkyNodes; bspNode_t **skyNodes; // ydnar: don't walk the entire bsp when rendering sky + int numPortals; + AABB *portals; + VBO_t *vbo; IBO_t *ibo; @@ -3095,7 +3106,7 @@ inline bool checkGLErrors() void R_AddPolygonSurfaces(); - int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface = false ); + int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, 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 ); diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 7af43e1f51..a70bf36a9b 100644 --- a/src/engine/renderer/tr_main.cpp +++ b/src/engine/renderer/tr_main.cpp @@ -1255,8 +1255,6 @@ entityNum is the entity that the portal surface is a part of, which may be moving and rotating. Returns true if it should be mirrored - -PRECONDITION: tess.verts/indexes are populated with the surface data ================= */ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surface, orientation_t *camera, vec3_t pvsOrigin, @@ -1264,15 +1262,15 @@ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surfac { cplane_t originalPlane, plane; - ASSERT( tess.numVertexes && tess.numIndexes ); - // create plane axis for the portal we are seeing R_PlaneForSurface( drawSurf->surface, &originalPlane ); // rotate the plane if necessary + vec3_t portalCenter; if ( drawSurf->entity != &tr.worldEntity ) { tr.currentEntity = drawSurf->entity; + VectorCopy( drawSurf->entity->e.origin, portalCenter ); // get the orientation of the entity R_RotateEntityForViewParms( tr.currentEntity, &tr.viewParms, &tr.orientation ); @@ -1284,6 +1282,7 @@ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surfac } else { + VectorCopy( tr.world->portals[drawSurf->portalNum].origin, portalCenter ); plane = originalPlane; } @@ -1294,12 +1293,6 @@ 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 - vec3_t portalCenter{ 0.0, 0.0, 0.0 }; - for ( uint32_t vertIndex = 0; vertIndex < tess.numVertexes; vertIndex++ ) { - VectorAdd( portalCenter, tess.verts[vertIndex].xyz, portalCenter ); - } - VectorScale( portalCenter, 1.0 / tess.numVertexes, portalCenter ); - trRefEntity_t* currentPortal = nullptr; trRefEntity_t* e; float minDistance = FLT_MAX; @@ -1316,9 +1309,6 @@ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surfac currentPortal = e; } } - if( drawSurf->entity != &tr.worldEntity ) { - VectorAdd( portalCenter, drawSurf->entity->e.origin, portalCenter ); - } if( currentPortal ) { // project the origin onto the surface plane to get @@ -1523,8 +1513,6 @@ static bool IsMirror( const drawSurf_t *drawSurf ) ** 0 = on screen, in range ** 1 = on screen, out of range ** 2 = off screen -** -** Note: caller must clear tess data afterward */ int PortalOffScreenOrOutOfRange( const drawSurf_t *drawSurf, screenRect_t& surfRect ) { @@ -1538,64 +1526,58 @@ int PortalOffScreenOrOutOfRange( const drawSurf_t *drawSurf, screenRect_t& surfR tr.currentEntity = drawSurf->entity; // rotate if necessary - if ( tr.currentEntity != &tr.worldEntity ) - { - R_RotateEntityForViewParms( tr.currentEntity, &tr.viewParms, &tr.orientation ); - } - else - { + AABB aabb; + if ( tr.currentEntity != &tr.worldEntity ) { + VectorCopy( tr.currentEntity->e.origin, aabb.origin ); + VectorCopy( tr.currentEntity->localBounds[0], aabb.mins ); + VectorCopy( tr.currentEntity->localBounds[1], aabb.maxs ); + } else { tr.orientation = tr.viewParms.world; + VectorCopy( tr.world->portals[drawSurf->portalNum].origin, aabb.origin ); + VectorCopy( tr.world->portals[drawSurf->portalNum].mins, aabb.mins ); + VectorCopy( tr.world->portals[drawSurf->portalNum].maxs, aabb.maxs ); } - if ( glConfig.smpActive ) - { - // https://github.com/DaemonEngine/Daemon/issues/1216 - Log::Warn( "portals are not compatible with r_smp" ); - return 1; - } - - // Try to do tessellation CPU-side... won't work for static VBO surfaces - // (https://github.com/DaemonEngine/Daemon/issues/1199) - R_BindNullVBO(); - Tess_MapVBOs( /*forceCPU=*/ true ); - Tess_Begin( Tess_StageIteratorDummy, drawSurf->shader, nullptr, true, -1, 0 ); - rb_surfaceTable[Util::ordinal( *( drawSurf->surface ) )]( drawSurf->surface ); - - if ( tess.numVertexes <= 0 || tess.numIndexes <= 0 || glState.currentVBO != nullptr) - { - Log::Warn( "failed to generate portal vertices" ); - return 1; + if ( drawSurf->portalNum == -1 ) { + return 0; } screenRect_t newRect; - Vector4Set(newRect.coords, 999999, 999999, -999999, -999999); + Vector4Set( newRect.coords, 999999, 999999, -999999, -999999 ); uint32_t pointOr = 0; uint32_t pointAnd = ( uint32_t ) ~0; - for ( uint32_t i = 0; i < tess.numVertexes; i++ ) - { + + // TODO: Can we just drop the scissor test here? Then we could simply do R_CullBox() + vec3_t verts[8]; + VectorCopy( aabb.mins, verts[0] ); + VectorSet( verts[1], aabb.maxs[0], aabb.mins[1], aabb.mins[2] ); + VectorSet( verts[2], aabb.mins[0], aabb.maxs[1], aabb.mins[2] ); + VectorSet( verts[3], aabb.maxs[0], aabb.maxs[1], aabb.mins[2] ); + VectorCopy( aabb.maxs, verts[4] ); + VectorSet( verts[5], aabb.maxs[0], aabb.mins[1], aabb.maxs[2] ); + VectorSet( verts[6], aabb.mins[0], aabb.maxs[1], aabb.maxs[2] ); + VectorSet( verts[7], aabb.maxs[0], aabb.maxs[1], aabb.maxs[2] ); + + for ( uint32_t i = 0; i < 8; i++ ) { uint32_t pointFlags = 0; vec4_t normalized; vec4_t window; vec4_t clip, eye; - R_TransformModelToClip( tess.verts[ i ].xyz, tr.orientation.modelViewMatrix, tr.viewParms.projectionMatrix, eye, clip ); + R_TransformModelToClip( verts[i], tr.orientation.modelViewMatrix, tr.viewParms.projectionMatrix, eye, clip ); - R_TransformClipToWindow(clip, &tr.viewParms, normalized, window); + R_TransformClipToWindow( clip, &tr.viewParms, normalized, window ); - newRect.coords[0] = std::min(newRect.coords[0], (int)window[0]); - newRect.coords[1] = std::min(newRect.coords[1], (int)window[1]); - newRect.coords[2] = std::max(newRect.coords[2], (int)window[0]); - newRect.coords[3] = std::max(newRect.coords[3], (int)window[1]); + newRect.coords[0] = std::min( newRect.coords[0], ( int ) window[0] ); + newRect.coords[1] = std::min( newRect.coords[1], ( int ) window[1] ); + newRect.coords[2] = std::max( newRect.coords[2], ( int ) window[0] ); + newRect.coords[3] = std::max( newRect.coords[3], ( int ) window[1] ); - for ( int j = 0; j < 3; j++ ) - { - if ( clip[ j ] >= clip[ 3 ] ) - { + for ( int j = 0; j < 3; j++ ) { + if ( clip[j] >= clip[3] ) { pointFlags |= ( 1 << ( j * 2 ) ); - } - else if ( clip[ j ] <= -clip[ 3 ] ) - { + } else if ( clip[j] <= -clip[3] ) { pointFlags |= ( 1 << ( j * 2 + 1 ) ); } } @@ -1606,69 +1588,27 @@ int PortalOffScreenOrOutOfRange( const drawSurf_t *drawSurf, screenRect_t& surfR // if the surface intersects the near plane, then expand the scissor rect to cover the screen because of back projection // OPTIMIZE: can be avoided by clipping triangle edges with the near plane - if (pointOr & 0x20) - { + if ( pointOr & 0x20 ) { newRect = parentRect; } - surfRect.coords[0] = std::max(newRect.coords[0], surfRect.coords[0]); - surfRect.coords[1] = std::max(newRect.coords[1], surfRect.coords[1]); - surfRect.coords[2] = std::min(newRect.coords[2], surfRect.coords[2]); - surfRect.coords[3] = std::min(newRect.coords[3], surfRect.coords[3]); + surfRect.coords[0] = std::max( newRect.coords[0], surfRect.coords[0] ); + surfRect.coords[1] = std::max( newRect.coords[1], surfRect.coords[1] ); + surfRect.coords[2] = std::min( newRect.coords[2], surfRect.coords[2] ); + surfRect.coords[3] = std::min( newRect.coords[3], surfRect.coords[3] ); // trivially reject - if ( pointAnd ) - { - return 2; - } - - // determine if this surface is backfaced and also determine the distance - // to the nearest vertex so we can cull based on portal range. Culling - // based on vertex distance isn't 100% correct (we should be checking for - // range to the surface), but it's good enough for the types of portals - // we have in the game right now. - uint32_t numTriangles = tess.numIndexes / 3; - - float shortest = FLT_MAX; - for ( uint32_t i = 0; i < tess.numIndexes; i += 3 ) - { - vec3_t normal; - vec3_t qnormal; - - VectorSubtract( tess.verts[ tess.indexes[ i ] ].xyz, tr.viewParms.pvsOrigin, normal ); - if ( tr.currentEntity != &tr.worldEntity ) { - VectorAdd( normal, tr.currentEntity->e.origin, normal ); - } - - float len = VectorLengthSquared( normal ); - - if ( len < shortest ) - { - shortest = len; - } - - R_QtangentsToNormal( tess.verts[ tess.indexes[ i ] ].qtangents, qnormal ); - - if ( ( DotProduct( normal, qnormal ) ) >= 0 ) - { - numTriangles--; - } - } - - if ( !numTriangles ) - { + if ( pointAnd ) { return 2; } // mirrors can early out at this point, since we don't do a fade over distance // with them (although we could) - if ( IsMirror( drawSurf ) ) - { + if ( IsMirror( drawSurf ) ) { return 0; } - if ( shortest > ( tess.surfaceShader->portalRange * tess.surfaceShader->portalRange ) ) - { + if ( Distance( tr.viewParms.pvsOrigin, aabb.origin ) > ( drawSurf->shader->portalRange * drawSurf->shader->portalRange ) ) { return 1; } @@ -1803,7 +1743,6 @@ bool R_MirrorViewBySurface(drawSurf_t *drawSurf) DAEMON_FALLTHROUGH; case 2: - Tess_Clear(); return false; } @@ -1812,7 +1751,6 @@ bool R_MirrorViewBySurface(drawSurf_t *drawSurf) bool foundPortal = R_GetPortalOrientations( drawSurf, &surface, &camera, newParms.pvsOrigin, &newParms.isMirror, &newParms.orientation.origin, newParms.orientation.axis ); - Tess_Clear(); if ( !foundPortal ) { @@ -1912,7 +1850,7 @@ int R_SpriteFogNum( trRefEntity_t *ent ) R_AddDrawSurf ================= */ -int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface ) +int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface, int portalNum ) { // instead of checking for overflow, we just mask the index // so it wraps around @@ -1926,6 +1864,7 @@ int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, in drawSurf->shader = shader; drawSurf->bspSurface = bspSurface; drawSurf->fog = fogNum; + drawSurf->portalNum = portalNum; int entityNum; diff --git a/src/engine/renderer/tr_world.cpp b/src/engine/renderer/tr_world.cpp index d049b5ba4d..2f05042212 100644 --- a/src/engine/renderer/tr_world.cpp +++ b/src/engine/renderer/tr_world.cpp @@ -260,7 +260,7 @@ static void R_AddInteractionSurface( bspSurface_t *surf, trRefLight_t *light, in R_AddWorldSurface ====================== */ -static bool R_AddWorldSurface( bspSurface_t *surf, int fogIndex, int planeBits ) +static bool R_AddWorldSurface( bspSurface_t *surf, int fogIndex, int portalNum, int planeBits ) { if ( surf->viewCount == tr.viewCountNoReset ) { @@ -275,7 +275,7 @@ static bool R_AddWorldSurface( bspSurface_t *surf, int fogIndex, int planeBits ) return true; } - R_AddDrawSurf( surf->data, surf->shader, surf->lightmapNum, fogIndex, true ); + R_AddDrawSurf( surf->data, surf->shader, surf->lightmapNum, fogIndex, true, portalNum ); return true; } @@ -329,7 +329,7 @@ void R_AddBSPModelSurfaces( trRefEntity_t *ent ) for ( i = 0; i < bspModel->numSurfaces; i++ ) { - R_AddWorldSurface( bspModel->firstSurface + i, fogNum, FRUSTUM_CLIPALL ); + R_AddWorldSurface( bspModel->firstSurface + i, fogNum, -1, FRUSTUM_CLIPALL ); } } @@ -389,7 +389,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, planeBits ); + R_AddWorldSurface( *view, ( *view )->fogIndex, ( *mark )->portalNum, planeBits); ( *mark )->viewCount = tr.viewCountNoReset;