Skip to content

Commit b555c7f

Browse files
committed
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.
1 parent e124fe0 commit b555c7f

File tree

4 files changed

+105
-113
lines changed

4 files changed

+105
-113
lines changed

src/engine/renderer/tr_bsp.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,13 +2929,17 @@ static void R_CreateWorldVBO()
29292929
numVerts = 0;
29302930
numTriangles = 0;
29312931
numSurfaces = 0;
2932+
int numPortals = 0;
29322933

29332934
for ( k = 0; k < s_worldData.numSurfaces; k++ )
29342935
{
29352936
surface = &s_worldData.surfaces[ k ];
29362937

29372938
if ( surface->shader->isPortal || surface->shader->autoSpriteMode != 0 )
29382939
{
2940+
if ( surface->shader->isPortal ) {
2941+
numPortals++;
2942+
}
29392943
continue;
29402944
}
29412945

@@ -3156,6 +3160,44 @@ static void R_CreateWorldVBO()
31563160
vboNumVerts += numSurfVerts;
31573161
}
31583162

3163+
s_worldData.numPortals = numPortals;
3164+
s_worldData.portals = ( AABB* ) ri.Hunk_Alloc( numPortals * sizeof( AABB ), ha_pref::h_low );
3165+
int portal = 0;
3166+
for ( i = 0; i < s_worldData.numSurfaces; i++ ) {
3167+
surface = &s_worldData.surfaces[i];
3168+
3169+
if ( surface->shader->isPortal ) {
3170+
surface->portalNum = portal;
3171+
AABB* aabb = &s_worldData.portals[portal];
3172+
switch ( *surface->data ) {
3173+
case surfaceType_t::SF_GRID:
3174+
{
3175+
srfGeneric_t* srf = ( srfGeneric_t* ) surface->data;
3176+
VectorCopy( srf->origin, aabb->origin );
3177+
VectorCopy( srf->bounds[0], aabb->mins );
3178+
VectorCopy( srf->bounds[1], aabb->maxs );
3179+
Log::Warn( "Grid portals aren't properly supported" );
3180+
break;
3181+
}
3182+
case surfaceType_t::SF_FACE:
3183+
case surfaceType_t::SF_TRIANGLES:
3184+
{
3185+
srfGeneric_t* srf = ( srfGeneric_t* ) surface->data;
3186+
VectorCopy( srf->origin, aabb->origin );
3187+
VectorCopy( srf->bounds[0], aabb->mins );
3188+
VectorCopy( srf->bounds[1], aabb->maxs );
3189+
break;
3190+
}
3191+
default:
3192+
Log::Warn( "Unsupported portal surface type" );
3193+
break;
3194+
}
3195+
portal++;
3196+
} else {
3197+
surface->portalNum = -1;
3198+
}
3199+
}
3200+
31593201
ASSERT_EQ( vboNumVerts, numVerts );
31603202
ASSERT_EQ( vboNumIndexes, numTriangles * 3 );
31613203

src/engine/renderer/tr_local.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1610,6 +1610,7 @@ enum class shaderProfilerRenderSubGroupsMode {
16101610
uint64_t sort;
16111611
bool bspSurface;
16121612
int fog;
1613+
int portalNum = -1;
16131614

16141615
uint materialsSSBOOffset[ MAX_SHADER_STAGES ];
16151616
bool initialized[ MAX_SHADER_STAGES ];
@@ -1871,6 +1872,7 @@ enum class shaderProfilerRenderSubGroupsMode {
18711872

18721873
int16_t lightmapNum; // -1 = no lightmap
18731874
int16_t fogIndex;
1875+
int portalNum;
18741876

18751877
surfaceType_t *data; // any of srf*_t
18761878
};
@@ -1919,6 +1921,12 @@ enum class shaderProfilerRenderSubGroupsMode {
19191921
byte unused;
19201922
};
19211923

1924+
struct AABB {
1925+
vec3_t origin;
1926+
vec3_t mins;
1927+
vec3_t maxs;
1928+
};
1929+
19221930
// ydnar: optimization
19231931
#define WORLD_MAX_SKY_NODES 32
19241932

@@ -1945,6 +1953,9 @@ enum class shaderProfilerRenderSubGroupsMode {
19451953
int numSkyNodes;
19461954
bspNode_t **skyNodes; // ydnar: don't walk the entire bsp when rendering sky
19471955

1956+
int numPortals;
1957+
AABB *portals;
1958+
19481959
VBO_t *vbo;
19491960
IBO_t *ibo;
19501961

@@ -3095,7 +3106,7 @@ inline bool checkGLErrors()
30953106

30963107
void R_AddPolygonSurfaces();
30973108

3098-
int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface = false );
3109+
int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface = false, int portalNum = -1 );
30993110

31003111
void R_LocalNormalToWorld( const vec3_t local, vec3_t world );
31013112
void R_LocalPointToWorld( const vec3_t local, vec3_t world );

src/engine/renderer/tr_main.cpp

Lines changed: 47 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,24 +1255,22 @@ entityNum is the entity that the portal surface is a part of, which may
12551255
be moving and rotating.
12561256
12571257
Returns true if it should be mirrored
1258-
1259-
PRECONDITION: tess.verts/indexes are populated with the surface data
12601258
=================
12611259
*/
12621260
static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surface, orientation_t *camera, vec3_t pvsOrigin,
12631261
bool *mirror, vec3_t *outOrigin, vec3_t *outAxis )
12641262
{
12651263
cplane_t originalPlane, plane;
12661264

1267-
ASSERT( tess.numVertexes && tess.numIndexes );
1268-
12691265
// create plane axis for the portal we are seeing
12701266
R_PlaneForSurface( drawSurf->surface, &originalPlane );
12711267

12721268
// rotate the plane if necessary
1269+
vec3_t portalCenter;
12731270
if ( drawSurf->entity != &tr.worldEntity )
12741271
{
12751272
tr.currentEntity = drawSurf->entity;
1273+
VectorCopy( drawSurf->entity->e.origin, portalCenter );
12761274

12771275
// get the orientation of the entity
12781276
R_RotateEntityForViewParms( tr.currentEntity, &tr.viewParms, &tr.orientation );
@@ -1284,6 +1282,7 @@ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surfac
12841282
}
12851283
else
12861284
{
1285+
VectorCopy( tr.world->portals[drawSurf->portalNum].origin, portalCenter );
12871286
plane = originalPlane;
12881287
}
12891288

@@ -1294,12 +1293,6 @@ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surfac
12941293
// locate the portal entity closest to this plane.
12951294
// origin will be the origin of the portal, origin2 will be
12961295
// the origin of the camera
1297-
vec3_t portalCenter{ 0.0, 0.0, 0.0 };
1298-
for ( uint32_t vertIndex = 0; vertIndex < tess.numVertexes; vertIndex++ ) {
1299-
VectorAdd( portalCenter, tess.verts[vertIndex].xyz, portalCenter );
1300-
}
1301-
VectorScale( portalCenter, 1.0 / tess.numVertexes, portalCenter );
1302-
13031296
trRefEntity_t* currentPortal = nullptr;
13041297
trRefEntity_t* e;
13051298
float minDistance = FLT_MAX;
@@ -1316,9 +1309,6 @@ static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surfac
13161309
currentPortal = e;
13171310
}
13181311
}
1319-
if( drawSurf->entity != &tr.worldEntity ) {
1320-
VectorAdd( portalCenter, drawSurf->entity->e.origin, portalCenter );
1321-
}
13221312

13231313
if( currentPortal ) {
13241314
// project the origin onto the surface plane to get
@@ -1523,8 +1513,6 @@ static bool IsMirror( const drawSurf_t *drawSurf )
15231513
** 0 = on screen, in range
15241514
** 1 = on screen, out of range
15251515
** 2 = off screen
1526-
**
1527-
** Note: caller must clear tess data afterward
15281516
*/
15291517
int PortalOffScreenOrOutOfRange( const drawSurf_t *drawSurf, screenRect_t& surfRect )
15301518
{
@@ -1538,64 +1526,58 @@ int PortalOffScreenOrOutOfRange( const drawSurf_t *drawSurf, screenRect_t& surfR
15381526
tr.currentEntity = drawSurf->entity;
15391527

15401528
// rotate if necessary
1541-
if ( tr.currentEntity != &tr.worldEntity )
1542-
{
1543-
R_RotateEntityForViewParms( tr.currentEntity, &tr.viewParms, &tr.orientation );
1544-
}
1545-
else
1546-
{
1529+
AABB aabb;
1530+
if ( tr.currentEntity != &tr.worldEntity ) {
1531+
VectorCopy( tr.currentEntity->e.origin, aabb.origin );
1532+
VectorCopy( tr.currentEntity->localBounds[0], aabb.mins );
1533+
VectorCopy( tr.currentEntity->localBounds[1], aabb.maxs );
1534+
} else {
15471535
tr.orientation = tr.viewParms.world;
1536+
VectorCopy( tr.world->portals[drawSurf->portalNum].origin, aabb.origin );
1537+
VectorCopy( tr.world->portals[drawSurf->portalNum].mins, aabb.mins );
1538+
VectorCopy( tr.world->portals[drawSurf->portalNum].maxs, aabb.maxs );
15481539
}
15491540

1550-
if ( glConfig.smpActive )
1551-
{
1552-
// https://github.com/DaemonEngine/Daemon/issues/1216
1553-
Log::Warn( "portals are not compatible with r_smp" );
1554-
return 1;
1555-
}
1556-
1557-
// Try to do tessellation CPU-side... won't work for static VBO surfaces
1558-
// (https://github.com/DaemonEngine/Daemon/issues/1199)
1559-
R_BindNullVBO();
1560-
Tess_MapVBOs( /*forceCPU=*/ true );
1561-
Tess_Begin( Tess_StageIteratorDummy, drawSurf->shader, nullptr, true, -1, 0 );
1562-
rb_surfaceTable[Util::ordinal( *( drawSurf->surface ) )]( drawSurf->surface );
1563-
1564-
if ( tess.numVertexes <= 0 || tess.numIndexes <= 0 || glState.currentVBO != nullptr)
1565-
{
1566-
Log::Warn( "failed to generate portal vertices" );
1567-
return 1;
1541+
if ( drawSurf->portalNum == -1 ) {
1542+
return 0;
15681543
}
15691544

15701545
screenRect_t newRect;
1571-
Vector4Set(newRect.coords, 999999, 999999, -999999, -999999);
1546+
Vector4Set( newRect.coords, 999999, 999999, -999999, -999999 );
15721547

15731548
uint32_t pointOr = 0;
15741549
uint32_t pointAnd = ( uint32_t ) ~0;
1575-
for ( uint32_t i = 0; i < tess.numVertexes; i++ )
1576-
{
1550+
1551+
// TODO: Can we just drop the scissor test here? Then we could simply do R_CullBox()
1552+
vec3_t verts[8];
1553+
VectorCopy( aabb.mins, verts[0] );
1554+
VectorSet( verts[1], aabb.maxs[0], aabb.mins[1], aabb.mins[2] );
1555+
VectorSet( verts[2], aabb.mins[0], aabb.maxs[1], aabb.mins[2] );
1556+
VectorSet( verts[3], aabb.maxs[0], aabb.maxs[1], aabb.mins[2] );
1557+
VectorCopy( aabb.maxs, verts[4] );
1558+
VectorSet( verts[5], aabb.maxs[0], aabb.mins[1], aabb.maxs[2] );
1559+
VectorSet( verts[6], aabb.mins[0], aabb.maxs[1], aabb.maxs[2] );
1560+
VectorSet( verts[7], aabb.maxs[0], aabb.maxs[1], aabb.maxs[2] );
1561+
1562+
for ( uint32_t i = 0; i < 8; i++ ) {
15771563
uint32_t pointFlags = 0;
15781564
vec4_t normalized;
15791565
vec4_t window;
15801566

15811567
vec4_t clip, eye;
1582-
R_TransformModelToClip( tess.verts[ i ].xyz, tr.orientation.modelViewMatrix, tr.viewParms.projectionMatrix, eye, clip );
1568+
R_TransformModelToClip( verts[i], tr.orientation.modelViewMatrix, tr.viewParms.projectionMatrix, eye, clip );
15831569

1584-
R_TransformClipToWindow(clip, &tr.viewParms, normalized, window);
1570+
R_TransformClipToWindow( clip, &tr.viewParms, normalized, window );
15851571

1586-
newRect.coords[0] = std::min(newRect.coords[0], (int)window[0]);
1587-
newRect.coords[1] = std::min(newRect.coords[1], (int)window[1]);
1588-
newRect.coords[2] = std::max(newRect.coords[2], (int)window[0]);
1589-
newRect.coords[3] = std::max(newRect.coords[3], (int)window[1]);
1572+
newRect.coords[0] = std::min( newRect.coords[0], ( int ) window[0] );
1573+
newRect.coords[1] = std::min( newRect.coords[1], ( int ) window[1] );
1574+
newRect.coords[2] = std::max( newRect.coords[2], ( int ) window[0] );
1575+
newRect.coords[3] = std::max( newRect.coords[3], ( int ) window[1] );
15901576

1591-
for ( int j = 0; j < 3; j++ )
1592-
{
1593-
if ( clip[ j ] >= clip[ 3 ] )
1594-
{
1577+
for ( int j = 0; j < 3; j++ ) {
1578+
if ( clip[j] >= clip[3] ) {
15951579
pointFlags |= ( 1 << ( j * 2 ) );
1596-
}
1597-
else if ( clip[ j ] <= -clip[ 3 ] )
1598-
{
1580+
} else if ( clip[j] <= -clip[3] ) {
15991581
pointFlags |= ( 1 << ( j * 2 + 1 ) );
16001582
}
16011583
}
@@ -1606,69 +1588,27 @@ int PortalOffScreenOrOutOfRange( const drawSurf_t *drawSurf, screenRect_t& surfR
16061588

16071589
// if the surface intersects the near plane, then expand the scissor rect to cover the screen because of back projection
16081590
// OPTIMIZE: can be avoided by clipping triangle edges with the near plane
1609-
if (pointOr & 0x20)
1610-
{
1591+
if ( pointOr & 0x20 ) {
16111592
newRect = parentRect;
16121593
}
16131594

1614-
surfRect.coords[0] = std::max(newRect.coords[0], surfRect.coords[0]);
1615-
surfRect.coords[1] = std::max(newRect.coords[1], surfRect.coords[1]);
1616-
surfRect.coords[2] = std::min(newRect.coords[2], surfRect.coords[2]);
1617-
surfRect.coords[3] = std::min(newRect.coords[3], surfRect.coords[3]);
1595+
surfRect.coords[0] = std::max( newRect.coords[0], surfRect.coords[0] );
1596+
surfRect.coords[1] = std::max( newRect.coords[1], surfRect.coords[1] );
1597+
surfRect.coords[2] = std::min( newRect.coords[2], surfRect.coords[2] );
1598+
surfRect.coords[3] = std::min( newRect.coords[3], surfRect.coords[3] );
16181599

16191600
// trivially reject
1620-
if ( pointAnd )
1621-
{
1622-
return 2;
1623-
}
1624-
1625-
// determine if this surface is backfaced and also determine the distance
1626-
// to the nearest vertex so we can cull based on portal range. Culling
1627-
// based on vertex distance isn't 100% correct (we should be checking for
1628-
// range to the surface), but it's good enough for the types of portals
1629-
// we have in the game right now.
1630-
uint32_t numTriangles = tess.numIndexes / 3;
1631-
1632-
float shortest = FLT_MAX;
1633-
for ( uint32_t i = 0; i < tess.numIndexes; i += 3 )
1634-
{
1635-
vec3_t normal;
1636-
vec3_t qnormal;
1637-
1638-
VectorSubtract( tess.verts[ tess.indexes[ i ] ].xyz, tr.viewParms.pvsOrigin, normal );
1639-
if ( tr.currentEntity != &tr.worldEntity ) {
1640-
VectorAdd( normal, tr.currentEntity->e.origin, normal );
1641-
}
1642-
1643-
float len = VectorLengthSquared( normal );
1644-
1645-
if ( len < shortest )
1646-
{
1647-
shortest = len;
1648-
}
1649-
1650-
R_QtangentsToNormal( tess.verts[ tess.indexes[ i ] ].qtangents, qnormal );
1651-
1652-
if ( ( DotProduct( normal, qnormal ) ) >= 0 )
1653-
{
1654-
numTriangles--;
1655-
}
1656-
}
1657-
1658-
if ( !numTriangles )
1659-
{
1601+
if ( pointAnd ) {
16601602
return 2;
16611603
}
16621604

16631605
// mirrors can early out at this point, since we don't do a fade over distance
16641606
// with them (although we could)
1665-
if ( IsMirror( drawSurf ) )
1666-
{
1607+
if ( IsMirror( drawSurf ) ) {
16671608
return 0;
16681609
}
16691610

1670-
if ( shortest > ( tess.surfaceShader->portalRange * tess.surfaceShader->portalRange ) )
1671-
{
1611+
if ( Distance( tr.viewParms.pvsOrigin, aabb.origin ) > ( drawSurf->shader->portalRange * drawSurf->shader->portalRange ) ) {
16721612
return 1;
16731613
}
16741614

@@ -1803,7 +1743,6 @@ bool R_MirrorViewBySurface(drawSurf_t *drawSurf)
18031743
DAEMON_FALLTHROUGH;
18041744

18051745
case 2:
1806-
Tess_Clear();
18071746
return false;
18081747
}
18091748

@@ -1812,7 +1751,6 @@ bool R_MirrorViewBySurface(drawSurf_t *drawSurf)
18121751
bool foundPortal = R_GetPortalOrientations(
18131752
drawSurf, &surface, &camera, newParms.pvsOrigin, &newParms.isMirror,
18141753
&newParms.orientation.origin, newParms.orientation.axis );
1815-
Tess_Clear();
18161754

18171755
if ( !foundPortal )
18181756
{
@@ -1912,7 +1850,7 @@ int R_SpriteFogNum( trRefEntity_t *ent )
19121850
R_AddDrawSurf
19131851
=================
19141852
*/
1915-
int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface )
1853+
int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface, int portalNum )
19161854
{
19171855
// instead of checking for overflow, we just mask the index
19181856
// so it wraps around
@@ -1926,6 +1864,7 @@ int R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, in
19261864
drawSurf->shader = shader;
19271865
drawSurf->bspSurface = bspSurface;
19281866
drawSurf->fog = fogNum;
1867+
drawSurf->portalNum = portalNum;
19291868

19301869
int entityNum;
19311870

0 commit comments

Comments
 (0)