Use clipper to find visible walls for hw_levelmesh

This commit is contained in:
Magnus Norddahl 2025-05-26 02:03:47 +02:00
commit addddcd8ed
5 changed files with 105 additions and 167 deletions

View file

@ -416,7 +416,6 @@ void DoomLevelMesh::BeginFrame(FLevelLocals& doomMap)
}
FlatUpdateList.Clear();
UpdateWallPortals();
UploadDynLights(doomMap);
Collision->Update();
@ -469,16 +468,9 @@ void DoomLevelMesh::UploadDynLights(FLevelLocals& doomMap)
UploadRanges.DynLight.Add(0, sizeof(int) * 4 + totalsize * sizeof(FDynLightInfo));
}
void DoomLevelMesh::UpdateWallPortals()
TArray<HWWall>& DoomLevelMesh::GetSidePortals(int sideIndex)
{
WallPortals.Clear();
for (int sideIndex : SidePortals)
{
for (HWWall& wall : Sides[sideIndex].WallPortals)
{
WallPortals.Push(&wall);
}
}
return Sides[sideIndex].WallPortals;
}
void DoomLevelMesh::ProcessDecals(HWDrawInfo* di, FRenderState& state)

View file

@ -109,10 +109,10 @@ public:
void DrawSector(FRenderState& renderstate, int sectorIndex, LevelMeshDrawType drawType, bool noFragmentShader);
void DrawSide(FRenderState& renderstate, int sideIndex, LevelMeshDrawType drawType, bool noFragmentShader);
TArray<HWWall>& GetSidePortals(int sideIndex);
TArray<int> SideDecals;
TArray<int> SidePortals;
TArray<HWWall*> WallPortals;
TArray<int> sectorGroup; // index is sector, value is sectorGroup
TArray<int> sectorPortals[2]; // index is sector+plane, value is index into the portal list
@ -179,8 +179,6 @@ private:
void FreeSide(FLevelLocals& doomMap, unsigned int sideIndex);
void FreeFlat(FLevelLocals& doomMap, unsigned int sectorIndex);
void UpdateWallPortals();
void SetSubsectorLightmap(int surfaceIndex);
void SetSideLightmap(int surfaceIndex);

View file

@ -56,6 +56,7 @@ CVAR(Bool, gl_multithread, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
EXTERN_CVAR(Float, r_actorspriteshadowdist)
EXTERN_CVAR(Bool, r_radarclipper)
EXTERN_CVAR(Bool, r_dithertransparency)
EXTERN_CVAR(Bool, gl_levelmesh)
thread_local bool isWorkerThread;
ctpl::thread_pool renderPool(1);
@ -395,6 +396,14 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip, FRenderState& state)
{
jobQueue.AddJob(RenderJob::WallJob, seg->Subsector, seg);
}
else if (uselevelmesh)
{
if (seg->sidedef)
{
SeenSides.Add(seg->sidedef->Index());
rendered_lines++;
}
}
else
{
HWWall wall;
@ -887,6 +896,10 @@ void HWDrawInfo::DoSubsector(subsector_t * sub, FRenderState& state)
{
jobQueue.AddJob(RenderJob::FlatJob, sub);
}
else if (uselevelmesh)
{
SeenSectors.Add(sub->sector->Index());
}
else
{
HWFlat flat;
@ -917,6 +930,10 @@ void HWDrawInfo::DoSubsector(subsector_t * sub, FRenderState& state)
{
jobQueue.AddJob(RenderJob::PortalJob, sub, (seg_t *)portal);
}
else if (uselevelmesh)
{
// To do: what to do here?
}
else
{
AddSubsectorToPortal(portal, sub);
@ -930,6 +947,10 @@ void HWDrawInfo::DoSubsector(subsector_t * sub, FRenderState& state)
{
jobQueue.AddJob(RenderJob::PortalJob, sub, (seg_t *)portal);
}
else if (uselevelmesh)
{
// To do: what to do here?
}
else
{
AddSubsectorToPortal(portal, sub);
@ -1030,6 +1051,13 @@ void HWDrawInfo::RenderBSP(void *node, bool drawpsprites, FRenderState& state)
validcount++; // used for processing sidedefs only once by the renderer.
multithread = gl_multithread;
uselevelmesh = gl_levelmesh && !outer;
if (uselevelmesh)
multithread = false;
SeenSectors.Clear();
SeenSides.Clear();
if (multithread)
{
jobQueue.ReleaseAll();

View file

@ -464,168 +464,23 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
// clip the scene and fill the drawlists
if (gl_levelmesh && !outer)
RenderBSP(Level->HeadNode(), drawpsprites, state);
if (uselevelmesh)
{
// Give the DrawInfo the viewpoint in fixed point because that's what the nodes are.
viewx = FLOAT2FIXED(Viewpoint.Pos.X);
viewy = FLOAT2FIXED(Viewpoint.Pos.Y);
level.levelMesh->CurFrameStats.Portals++;
validcount++; // used for processing sidedefs only once by the renderer.
auto& portals = level.levelMesh->WallPortals;
// draw level into depth buffer
state.SetColorMask(false);
state.SetCulling(Cull_CW);
int numSectors = level.sectors.Size();
int numSides = level.sides.Size();
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Opaque, true);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Opaque, true);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Masked, true);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Masked, true);
if (gl_portals)
for (int sideIndex : SeenSides.Get())
{
state.SetDepthBias(1, 128);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Portal, true);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Portal, true);
state.SetDepthBias(0, 0);
}
// use occlusion queries on all portals in level to decide which are visible
int queryStart = state.GetNextQueryIndex();
state.SetDepthMask(false);
state.EnableTexture(false);
state.SetEffect(EFF_PORTAL);
if (!gl_portals)
state.SetColorMask(true); // For debugging where the query is
for (HWWall* wall : portals)
{
state.BeginQuery();
// sector portals not handled yet by PutWallPortal
if (wall->portaltype != PORTALTYPE_SECTORSTACK)
for (HWWall& portal : level.levelMesh->GetSidePortals(sideIndex))
{
wall->MakeVertices(state, false);
wall->RenderWall(state, HWWall::RWF_BLANK);
wall->vertcount = 0;
}
state.EndQuery();
}
state.SetEffect(EFF_NONE);
state.EnableTexture(gl_texture);
state.SetColorMask(true);
state.SetDepthMask(true);
int queryEnd = state.GetNextQueryIndex();
state.DispatchLightTiles(VPUniforms.mViewMatrix, VPUniforms.mProjectionMatrix.get()[5]);
// draw opaque level so the GPU has something to do while we examine the query results
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Opaque, false);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Opaque, false);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Masked, false);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Masked, false);
if (!gl_portals)
{
state.SetDepthBias(1, 128);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Portal, false);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Portal, false);
state.SetDepthBias(0, 0);
}
state.SetCulling(Cull_None);
if (queryStart != queryEnd)
{
// retrieve the query results and use them to fill the portal manager with portals
state.GetQueryResults(queryStart, queryEnd - queryStart, QueryResultsBuffer);
for (unsigned int i = 0, count = QueryResultsBuffer.Size(); i < count; i++)
{
bool portalVisible = QueryResultsBuffer[i];
if (portalVisible)
// sector portals not handled yet by PutWallPortal
if (portal.portaltype != PORTALTYPE_SECTORSTACK)
{
PutWallPortal(*portals[i], state);
PutWallPortal(portal, state);
}
}
}
// Draw Decals
{
level.levelMesh->ProcessDecals(this, state);
state.SetRenderStyle(STYLE_Translucent);
state.SetDepthFunc(DF_LEqual);
DrawDecals(state, Decals[0]);
DrawDecals(state, Decals[1]); // Mirror decals - when should they be drawn?
Decals[0].Clear();
Decals[1].Clear();
}
// Draw sprites
auto it = level.GetThinkerIterator<AActor>();
AActor* thing;
while ((thing = it.Next()) != nullptr)
{
HWSprite sprite;
if (R_ShouldDrawSpriteShadow(thing))
{
double dist = (thing->Pos() - vp.Pos).LengthSquared();
double check = r_actorspriteshadowdist;
if (dist <= check * check)
{
sprite.Process(this, state, thing, thing->Sector, in_area, false, true);
}
}
sprite.Process(this, state, thing, thing->Sector, in_area, false);
}
// Draw particles
for (uint16_t i = level.ActiveParticles; i != NO_PARTICLE; i = level.Particles[i].tnext)
{
if (Level->Particles[i].subsector)
{
HWSprite sprite;
sprite.ProcessParticle(this, state, &Level->Particles[i], Level->Particles[i].subsector->sector, nullptr);
}
}
// Process all the sprites on the current portal's back side which touch the portal.
if (mCurrentPortal != nullptr) mCurrentPortal->RenderAttached(this, state);
if (drawpsprites)
PreparePlayerSprites(Viewpoint.sector, in_area, state);
}
else
{
if (gl_levelmesh && level.levelMesh)
level.levelMesh->CurFrameStats.Portals++;
RenderBSP(Level->HeadNode(), drawpsprites, state);
}
// And now the crappy hacks that have to be done to avoid rendering anomalies.
@ -847,12 +702,22 @@ void HWDrawInfo::RenderScene(FRenderState &state)
state.EnableTexture(gl_texture);
state.EnableBrightmap(true);
// To do: replace this is classic light lists
DrawSeenSides(state, LevelMeshDrawType::Opaque, true);
DrawSeenFlats(state, LevelMeshDrawType::Opaque, true);
state.DispatchLightTiles(VPUniforms.mViewMatrix, VPUniforms.mProjectionMatrix.get()[5]);
DrawSeenSides(state, LevelMeshDrawType::Opaque, false);
drawlists[GLDL_PLAINWALLS].DrawWalls(this, state, false);
DrawSeenFlats(state, LevelMeshDrawType::Opaque, false);
drawlists[GLDL_PLAINFLATS].DrawFlats(this, state, false);
// Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
DrawSeenSides(state, LevelMeshDrawType::Masked, false);
drawlists[GLDL_MASKEDWALLS].DrawWalls(this, state, false);
DrawSeenFlats(state, LevelMeshDrawType::Masked, false);
drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false);
// Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use.
@ -876,6 +741,18 @@ void HWDrawInfo::RenderScene(FRenderState &state)
RenderAll.Unclock();
}
void HWDrawInfo::DrawSeenSides(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader)
{
for (int sideIndex : SeenSides.Get())
level.levelMesh->DrawSide(state, sideIndex, drawType, noFragmentShader);
}
void HWDrawInfo::DrawSeenFlats(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader)
{
for (int sectorIndex : SeenSectors.Get())
level.levelMesh->DrawSector(state, sectorIndex, drawType, noFragmentShader);
}
//-----------------------------------------------------------------------------
//
// RenderTranslucent
@ -894,6 +771,10 @@ void HWDrawInfo::RenderTranslucent(FRenderState &state)
drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, state, true);
state.SetDepthMask(false);
// To do: this needs to be sorted
DrawSeenSides(state, LevelMeshDrawType::Translucent, false);
DrawSeenFlats(state, LevelMeshDrawType::Translucent, false);
drawlists[GLDL_TRANSLUCENT].DrawSorted(this, state);
state.EnableBrightmap(false);

View file

@ -191,6 +191,42 @@ struct HWDrawInfo
area_t in_area;
fixed_t viewx, viewy; // since the nodes are still fixed point, keeping the view position also fixed point for node traversal is faster.
bool multithread;
bool uselevelmesh;
struct VisList
{
const TArray<int>& Get() const { return List; }
void Clear()
{
for (int index : List)
AddedToList[index] = false;
List.Clear();
}
void Add(int index)
{
if (index >= (int)AddedToList.size())
{
int lastSize = AddedToList.size();
AddedToList.resize(index + 1); // Beware! TArray does not clear on resize!
for (int i = lastSize; i < index + 1; i++)
AddedToList[i] = false;
}
if (!AddedToList[index])
{
AddedToList[index] = true;
List.Push(index);
}
}
private:
TArray<int> List;
TArray<bool> AddedToList;
};
VisList SeenSectors, SeenSides;
TArray<bool> QueryResultsBuffer;
@ -392,6 +428,9 @@ private:
void AddSpecialPortalLines(subsector_t* sub, sector_t* sector, linebase_t* line, FRenderState& state);
void UpdateLightmaps();
void DrawSeenSides(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader);
void DrawSeenFlats(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader);
};
void CleanSWDrawer();