Implement line portals and improve sky handling

This commit is contained in:
Magnus Norddahl 2023-12-14 18:06:53 +01:00
commit 4ca2ffd58e
10 changed files with 121 additions and 56 deletions

View file

@ -31,6 +31,7 @@ enum ERenderEffect
EFF_SPHEREMAP,
EFF_BURN,
EFF_STENCIL,
EFF_PORTAL,
MAX_EFFECTS
};
@ -689,8 +690,8 @@ public:
}
// Draw level mesh
virtual void DrawLevelMeshDepthPass() { }
virtual void DrawLevelMeshOpaquePass() { }
virtual void DrawLevelMeshSurfaces(bool noFragmentShader) { }
virtual void DrawLevelMeshPortals(bool noFragmentShader) { }
virtual int GetNextQueryIndex() { return 0; }
virtual void BeginQuery() { }
virtual void EndQuery() { }

View file

@ -71,6 +71,7 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key)
{ "spheremap", "shaders/scene/frag_main.glsl", "shaders/scene/material_default.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SPHEREMAP\n#define NO_ALPHATEST\n" },
{ "burn", "shaders/scene/frag_burn.glsl", nullptr, nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "stencil", "shaders/scene/frag_stencil.glsl", nullptr, nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "portal", "shaders/scene/frag_portal.glsl", nullptr, nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
};
const auto& desc = effectshaders[key.SpecialEffect];

View file

@ -851,31 +851,31 @@ void VkRenderState::ApplyLevelMesh()
mCommandBuffer->bindIndexBuffer(fb->GetLevelMesh()->GetIndexBuffer()->buffer, 0, VK_INDEX_TYPE_UINT32);
}
void VkRenderState::DrawLevelMeshDepthPass()
void VkRenderState::DrawLevelMeshSurfaces(bool noFragmentShader)
{
ApplyLevelMesh();
auto submesh = fb->GetLevelMesh()->GetMesh()->StaticMesh.get();
for (LevelSubmeshDrawRange& range : submesh->DrawList)
auto mesh = fb->GetLevelMesh()->GetMesh();
for (LevelSubmesh* submesh : { mesh->StaticMesh.get(), mesh->DynamicMesh.get() })
{
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, true);
for (LevelSubmeshDrawRange& range : submesh->DrawList)
{
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, noFragmentShader);
}
}
/*
for (LevelSubmeshDrawRange& range : submesh->PortalList)
{
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, true);
}
*/
}
void VkRenderState::DrawLevelMeshOpaquePass()
void VkRenderState::DrawLevelMeshPortals(bool noFragmentShader)
{
ApplyLevelMesh();
auto submesh = fb->GetLevelMesh()->GetMesh()->StaticMesh.get();
for (LevelSubmeshDrawRange& range : submesh->DrawList)
auto mesh = fb->GetLevelMesh()->GetMesh();
for (LevelSubmesh* submesh : { mesh->StaticMesh.get(), mesh->DynamicMesh.get() })
{
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, false);
for (LevelSubmeshDrawRange& range : submesh->PortalList)
{
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, noFragmentShader);
}
}
}
@ -925,6 +925,8 @@ void VkRenderState::DrawLevelMeshRange(VulkanCommandBuffer* cmdbuffer, VkPipelin
pipelineKey.StencilPassOp = mStencilOp;
pipelineKey.ColorMask = mColorMask;
pipelineKey.CullMode = mCullMode;
if (!mTextureEnabled)
pipelineKey.ShaderKey.EffectState = SHADER_NoTexture;
mPipelineKey = pipelineKey;
PushConstants pushConstants = {};

View file

@ -57,8 +57,8 @@ public:
void ResetVertices() override;
// Draw level mesh
void DrawLevelMeshDepthPass() override;
void DrawLevelMeshOpaquePass() override;
void DrawLevelMeshSurfaces(bool noFragmentShader) override;
void DrawLevelMeshPortals(bool noFragmentShader) override;
int GetNextQueryIndex() override;
void BeginQuery() override;
void EndQuery() override;

View file

@ -3693,7 +3693,7 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position)
// set up world state
SpawnSpecials();
InitLevelMesh(map);
//InitLevelMesh(map);
// disable reflective planes on sloped sectors.
for (auto &sec : Level->sectors)
@ -3728,7 +3728,7 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position)
if (!Level->IsReentering())
Level->FinalizePortals(); // finalize line portals after polyobjects have been initialized. This info is needed for properly flagging them.
Level->levelMesh->CreatePortals(*Level); // [RaveYard]: needs portal data, but at the same time intializing the level mesh here breaks floor/ceiling planes!
InitLevelMesh(map);
Level->aabbTree = new DoomLevelAABBTree(Level);
}

View file

@ -17,12 +17,12 @@ public:
void DumpMesh(const FString& objFilename, const FString& mtlFilename) const;
void BuildSectorGroups(const FLevelLocals& doomMap);
void CreatePortals(FLevelLocals& doomMap);
TArray<int> sectorGroup; // index is sector, value is sectorGroup
TArray<int> sectorPortals[2]; // index is sector+plane, value is index into the portal list
TArray<int> linePortals; // index is linedef, value is index into the portal list
private:
void CreatePortals(FLevelLocals& doomMap);
FLightNode* GetSurfaceLightNode(const DoomLevelMeshSurface* doomsurf);
};

View file

@ -19,7 +19,6 @@
VSMatrix GetPlaneTextureRotationMatrix(FGameTexture* gltexture, const sector_t* sector, int plane);
void GetTexCoordInfo(FGameTexture* tex, FTexCoordInfo* tci, side_t* side, int texpos);
EXTERN_CVAR(Bool, gl_texture)
EXTERN_CVAR(Float, lm_scale);
DoomLevelSubmesh::DoomLevelSubmesh(DoomLevelMesh* mesh, FLevelLocals& doomMap, bool staticMesh) : LevelMesh(mesh), StaticMesh(staticMesh)
@ -61,7 +60,7 @@ void DoomLevelSubmesh::Update(FLevelLocals& doomMap, int lightmapStartIndex)
void DoomLevelSubmesh::Reset()
{
Surfaces.Clear();
WallPortals.Clear();
Portals.Clear();
Mesh.Vertices.Clear();
Mesh.Indexes.Clear();
Mesh.SurfaceIndexes.Clear();
@ -113,22 +112,24 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap)
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
state.EnableTexture(gl_texture);
state.EnableTexture(true);
state.EnableBrightmap(true);
state.AlphaFunc(Alpha_GEqual, 0.f);
CreateWallSurface(side, disp, state, result.list);
for (HWWall& portal : result.portals)
{
Portals.Push(portal);
}
CreateWallSurface(side, disp, state, result.portals, true);
// final pass: translucent stuff
state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold);
state.SetRenderStyle(STYLE_Translucent);
CreateWallSurface(side, disp, state, result.translucent);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.SetRenderStyle(STYLE_Normal);
for (const HWWall& portal : result.portals)
{
WallPortals.Push(portal);
}
}
// Create surfaces for all flats
@ -149,7 +150,7 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap)
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
state.EnableTexture(gl_texture);
state.EnableTexture(true);
state.EnableBrightmap(true);
CreateFlatSurface(disp, state, result.list);
@ -168,20 +169,39 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap)
}
}
void DoomLevelSubmesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, TArray<HWWall>& list)
void DoomLevelSubmesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, TArray<HWWall>& list, bool isSky)
{
for (HWWall& wallpart : list)
{
if (wallpart.texture && wallpart.texture->isMasked())
if (isSky)
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
state.SetEffect(EFF_PORTAL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Normal);
wallpart.MakeVertices(state, false);
wallpart.RenderWall(state, HWWall::RWF_BLANK);
wallpart.vertcount = 0;
wallpart.LevelMeshInfo.Type = ST_UPPERSIDE;
wallpart.LevelMeshInfo.ControlSector = nullptr;
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
if (wallpart.texture && wallpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
wallpart.DrawWall(&disp, state, false);
wallpart.DrawWall(&disp, state, false);
}
int pipelineID = 0;
int startVertIndex = Mesh.Vertices.Size();
@ -230,7 +250,8 @@ void DoomLevelSubmesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, M
surf.Plane = ToPlane(Mesh.Vertices[startVertIndex + 3].fPos(), Mesh.Vertices[startVertIndex + 2].fPos(), Mesh.Vertices[startVertIndex + 1].fPos(), Mesh.Vertices[startVertIndex].fPos());
surf.Texture = wallpart.texture;
surf.PipelineID = pipelineID;
surf.PortalIndex = (surf.Type == ST_MIDDLESIDE) ? LevelMesh->linePortals[side->linedef->Index()] : 0;
surf.PortalIndex = isSky ? LevelMesh->linePortals[side->linedef->Index()] : 0;
surf.IsSky = isSky;
Surfaces.Push(surf);
}
}
@ -239,16 +260,30 @@ void DoomLevelSubmesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& st
{
for (HWFlat& flatpart : list)
{
if (flatpart.texture && flatpart.texture->isMasked())
if (isSky)
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
state.SetEffect(EFF_PORTAL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Normal);
flatpart.DrawSubsectors(&disp, state);
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
if (flatpart.texture && flatpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
flatpart.DrawFlat(&disp, state, false);
flatpart.DrawFlat(&disp, state, false);
}
int pipelineID = 0;
int uniformsIndex = 0;
@ -290,6 +325,8 @@ void DoomLevelSubmesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& st
if (surf.ControlSector)
surf.Plane = -surf.Plane;
float skyZ = flatpart.ceiling ? 32768.0f : -32768.0f;
for (subsector_t* sub : flatpart.section->subsectors)
{
int startVertIndex = Mesh.Vertices.Size();
@ -301,7 +338,7 @@ void DoomLevelSubmesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& st
FFlatVertex ffv;
ffv.x = (float)vt->fX();
ffv.y = (float)vt->fY();
ffv.z = (float)plane.ZatPoint(vt);
ffv.z = isSky ? skyZ : (float)plane.ZatPoint(vt);
ffv.u = (float)vt->fX() / 64.f;
ffv.v = -(float)vt->fY() / 64.f;
ffv.lu = 0.0f;

View file

@ -49,7 +49,7 @@ public:
TArray<std::unique_ptr<DoomLevelMeshSurface*[]>> PolyLMSurfaces;
TArray<HWWall> WallPortals;
TArray<HWWall> Portals;
private:
void Reset();
@ -64,7 +64,7 @@ private:
void CreateIndexes();
void CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, TArray<HWWall>& list);
void CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, TArray<HWWall>& list, bool isSky = false);
void CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, TArray<HWFlat>& list, bool isSky = false);
static FVector4 ToPlane(const FVector3& pt1, const FVector3& pt2, const FVector3& pt3)

View file

@ -52,6 +52,7 @@
EXTERN_CVAR(Float, r_visibility)
EXTERN_CVAR(Int, lm_background_updates);
EXTERN_CVAR(Float, r_actorspriteshadowdist)
EXTERN_CVAR(Bool, gl_portals)
CVAR(Bool, lm_always_update, false, 0)
@ -410,20 +411,25 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
validcount++; // used for processing sidedefs only once by the renderer.
auto& wallPortals = static_cast<DoomLevelSubmesh*>(level.levelMesh->StaticMesh.get())->WallPortals;
auto& portals = static_cast<DoomLevelSubmesh*>(level.levelMesh->StaticMesh.get())->Portals;
// draw level into depth buffer
state.SetColorMask(false);
state.SetCulling(Cull_CW);
state.DrawLevelMeshDepthPass();
state.DrawLevelMeshSurfaces(true);
if (gl_portals)
{
state.SetDepthBias(1, 128);
state.DrawLevelMeshPortals(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_FOGBOUNDARY);
state.AlphaFunc(Alpha_GEqual, 0.f);
for (HWWall& wall : wallPortals)
state.SetEffect(EFF_PORTAL);
for (HWWall& wall : portals)
{
state.BeginQuery();
@ -434,13 +440,19 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
state.EndQuery();
}
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
state.EnableTexture(gl_texture);
state.SetColorMask(true);
state.SetDepthMask(true);
int queryEnd = state.GetNextQueryIndex();
// draw opaque level so the GPU has something to do while we examine the query results
state.DrawLevelMeshOpaquePass();
state.DrawLevelMeshSurfaces(false);
if (!gl_portals)
{
state.SetDepthBias(1, 128);
state.DrawLevelMeshPortals(false);
state.SetDepthBias(0, 0);
}
state.SetCulling(Cull_None);
// retrieve the query results and use them to fill the portal manager with portals
@ -450,7 +462,7 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
bool portalVisible = QueryResultsBuffer[i];
if (portalVisible)
{
PutWallPortal(wallPortals[i], state);
PutWallPortal(portals[i], state);
}
}
@ -524,9 +536,11 @@ void HWDrawInfo::PutWallPortal(HWWall wall, FRenderState& state)
}
else if (portaltype == PORTALTYPE_SECTORSTACK)
{
if (screen->instack[1 - portalplane])
return;
wall.PutPortal(&ddi, state, portaltype, portalplane);
// To do: this seems to need AddSubsectorToPortal?
//if (screen->instack[1 - portalplane])
// return;
//wall.PutPortal(&ddi, state, portaltype, portalplane);
}
else if (portaltype == PORTALTYPE_PLANEMIRROR)
{

View file

@ -0,0 +1,10 @@
void main()
{
FragColor = vec4(0.0, 1.0, 0.0, 1.0);
#ifdef GBUFFER_PASS
FragFog = vec4(0.0, 0.0, 0.0, 1.0);
FragNormal = vec4(0.5, 0.5, 0.5, 1.0);
#endif
}