Some initial decal rendering

This commit is contained in:
Magnus Norddahl 2024-09-28 22:17:17 +02:00
commit 1e23dcee7c
12 changed files with 249 additions and 68 deletions

View file

@ -3,20 +3,21 @@
struct NullLevelMeshUpdater : UpdateLevelMesh
{
virtual void FloorHeightChanged(struct sector_t* sector) {};
virtual void CeilingHeightChanged(struct sector_t* sector) {};
virtual void MidTex3DHeightChanged(struct sector_t* sector) {};
void FloorHeightChanged(sector_t* sector) override {}
void CeilingHeightChanged(sector_t* sector) override {}
void MidTex3DHeightChanged(sector_t* sector) override {}
virtual void FloorTextureChanged(struct sector_t* sector) {};
virtual void CeilingTextureChanged(struct sector_t* sector) {};
void FloorTextureChanged(sector_t* sector) override {}
void CeilingTextureChanged(sector_t* sector) override {}
virtual void SectorChangedOther(struct sector_t* sector) {};
void SectorChangedOther(sector_t* sector) override {}
virtual void SideTextureChanged(struct side_t* side, int section) {};
void SideTextureChanged(side_t* side, int section) override {}
void SideDecalsChanged(side_t* side) override {}
virtual void SectorLightChanged(struct sector_t* sector) {};
virtual void SectorLightThinkerCreated(struct sector_t* sector, class DLighting* lightthinker) {};
virtual void SectorLightThinkerDestroyed(struct sector_t* sector, class DLighting* lightthinker) {};
void SectorLightChanged(sector_t* sector) override {}
void SectorLightThinkerCreated(sector_t* sector, DLighting* lightthinker) override {}
void SectorLightThinkerDestroyed(sector_t* sector, DLighting* lightthinker) override {}
};
static NullLevelMeshUpdater nullUpdater;

View file

@ -2,22 +2,27 @@
// level mesh helper
// this is a minimal include to keep the other source files a little bit leaner
struct sector_t;
struct side_t;
class DLighting;
struct UpdateLevelMesh
{
virtual void FloorHeightChanged(struct sector_t *sector) = 0;
virtual void CeilingHeightChanged(struct sector_t *sector) = 0;
virtual void MidTex3DHeightChanged(struct sector_t *sector) = 0;
virtual void FloorHeightChanged(sector_t *sector) = 0;
virtual void CeilingHeightChanged(sector_t *sector) = 0;
virtual void MidTex3DHeightChanged(sector_t *sector) = 0;
virtual void FloorTextureChanged(struct sector_t *sector) = 0;
virtual void CeilingTextureChanged(struct sector_t *sector) = 0;
virtual void FloorTextureChanged(sector_t *sector) = 0;
virtual void CeilingTextureChanged(sector_t *sector) = 0;
virtual void SectorChangedOther(struct sector_t *sector) = 0;
virtual void SectorChangedOther(sector_t *sector) = 0;
virtual void SideTextureChanged(struct side_t *side, int section) = 0;
virtual void SideTextureChanged(side_t *side, int section) = 0;
virtual void SideDecalsChanged(side_t* side) = 0;
virtual void SectorLightChanged(struct sector_t* sector) = 0;
virtual void SectorLightThinkerCreated(struct sector_t* sector, class DLighting* lightthinker) = 0;
virtual void SectorLightThinkerDestroyed(struct sector_t* sector, class DLighting* lightthinker) = 0;
virtual void SectorLightChanged(sector_t* sector) = 0;
virtual void SectorLightThinkerCreated(sector_t* sector, DLighting* lightthinker) = 0;
virtual void SectorLightThinkerDestroyed(sector_t* sector, DLighting* lightthinker) = 0;
};
extern UpdateLevelMesh* LevelMeshUpdater;

View file

@ -156,7 +156,8 @@ void DBaseDecal::Remove ()
{
if (WallPrev == nullptr)
{
if (Side != nullptr) Side->AttachedDecals = WallNext;
if (Side != nullptr)
Side->AttachedDecals = WallNext;
}
else WallPrev->WallNext = WallNext;
@ -164,6 +165,9 @@ void DBaseDecal::Remove ()
WallPrev = nullptr;
WallNext = nullptr;
if (Side)
LevelMeshUpdater->SideDecalsChanged(Side);
}
//----------------------------------------------------------------------------
@ -262,6 +266,8 @@ FTextureID DBaseDecal::StickToWall (side_t *wall, double x, double y, F3DFloor *
else wall->AttachedDecals = this;
WallNext = nullptr;
LevelMeshUpdater->SideDecalsChanged(wall);
sector_t *front, *back;
line_t *line;

View file

@ -318,6 +318,44 @@ void DoomLevelMesh::UpdateWallPortals()
}
}
void DoomLevelMesh::ProcessDecals(HWDrawInfo* di, FRenderState& state)
{
for (int sideIndex : level.levelMesh->SideDecals)
{
const auto& side = Sides[sideIndex];
Plane p;
if (side.Decals[0].Size() != 0 || side.Decals[1].Size() != 0)
{
if (side.FirstSurface == -1)
continue;
p.Set(Mesh.Surfaces[side.FirstSurface].Plane.XYZ(), Mesh.Surfaces[side.FirstSurface].Plane.W);
}
for (int onmirror = 0; onmirror < 2; onmirror++)
{
for (const HWDecal& decal : side.Decals[onmirror])
{
HWDecal* gldecal = di->AddDecal(onmirror == 1);
*gldecal = decal;
if (di->Level->HasDynamicLights && !di->isFullbrightScene() && decal.texture != nullptr)
{
gldecal->SetupLights(di, state, lightdata, p, level.sides[sideIndex].lighthead);
}
auto verts = state.AllocVertices(4);
for (int i = 0; i < 4; i++)
{
verts.first[i] = side.DecalVertices[decal.vertindex + i];
}
gldecal->vertindex = verts.second;
}
}
}
}
void DoomLevelMesh::CreateLights(FLevelLocals& doomMap)
{
if (LightsCreated)
@ -525,6 +563,9 @@ void DoomLevelMesh::FreeSide(FLevelLocals& doomMap, unsigned int sideIndex)
Sides[sideIndex].Uniforms.Clear();
Sides[sideIndex].WallPortals.Clear();
Sides[sideIndex].Decals[0].Clear();
Sides[sideIndex].Decals[1].Clear();
Sides[sideIndex].DecalVertices.Clear();
}
void DoomLevelMesh::FreeFlat(FLevelLocals& doomMap, unsigned int sectorIndex)
@ -553,7 +594,7 @@ void DoomLevelMesh::FreeFlat(FLevelLocals& doomMap, unsigned int sectorIndex)
Flats[sectorIndex].Uniforms.Clear();
}
void DoomLevelMesh::FloorHeightChanged(struct sector_t* sector)
void DoomLevelMesh::FloorHeightChanged(sector_t* sector)
{
UpdateFlat(sector->Index(), SurfaceUpdateType::Full);
for (line_t* line : sector->Lines)
@ -565,7 +606,7 @@ void DoomLevelMesh::FloorHeightChanged(struct sector_t* sector)
}
}
void DoomLevelMesh::CeilingHeightChanged(struct sector_t* sector)
void DoomLevelMesh::CeilingHeightChanged(sector_t* sector)
{
UpdateFlat(sector->Index(), SurfaceUpdateType::Full);
for (line_t* line : sector->Lines)
@ -577,32 +618,37 @@ void DoomLevelMesh::CeilingHeightChanged(struct sector_t* sector)
}
}
void DoomLevelMesh::MidTex3DHeightChanged(struct sector_t* sector)
void DoomLevelMesh::MidTex3DHeightChanged(sector_t* sector)
{
// UpdateFlat(sector->Index(), SurfaceUpdateType::Full);
}
void DoomLevelMesh::FloorTextureChanged(struct sector_t* sector)
void DoomLevelMesh::FloorTextureChanged(sector_t* sector)
{
UpdateFlat(sector->Index(), SurfaceUpdateType::Full);
}
void DoomLevelMesh::CeilingTextureChanged(struct sector_t* sector)
void DoomLevelMesh::CeilingTextureChanged(sector_t* sector)
{
UpdateFlat(sector->Index(), SurfaceUpdateType::Full);
}
void DoomLevelMesh::SectorChangedOther(struct sector_t* sector)
void DoomLevelMesh::SectorChangedOther(sector_t* sector)
{
UpdateFlat(sector->Index(), SurfaceUpdateType::Full);
}
void DoomLevelMesh::SideTextureChanged(struct side_t* side, int section)
void DoomLevelMesh::SideTextureChanged(side_t* side, int section)
{
UpdateSide(side->Index(), SurfaceUpdateType::Full);
}
void DoomLevelMesh::SectorLightChanged(struct sector_t* sector)
void DoomLevelMesh::SideDecalsChanged(side_t* side)
{
UpdateSide(side->Index(), SurfaceUpdateType::Full);
}
void DoomLevelMesh::SectorLightChanged(sector_t* sector)
{
UpdateFlat(sector->Index(), SurfaceUpdateType::LightsOnly);
for (line_t* line : sector->Lines)
@ -614,11 +660,11 @@ void DoomLevelMesh::SectorLightChanged(struct sector_t* sector)
}
}
void DoomLevelMesh::SectorLightThinkerCreated(struct sector_t* sector, class DLighting* lightthinker)
void DoomLevelMesh::SectorLightThinkerCreated(sector_t* sector, DLighting* lightthinker)
{
}
void DoomLevelMesh::SectorLightThinkerDestroyed(struct sector_t* sector, class DLighting* lightthinker)
void DoomLevelMesh::SectorLightThinkerDestroyed(sector_t* sector, DLighting* lightthinker)
{
}
@ -676,6 +722,30 @@ void DoomLevelMesh::CreateSide(FLevelLocals& doomMap, unsigned int sideIndex)
wall.sub = sub;
wall.Process(&disp, state, seg, front, back);
auto& sideBlock = Sides[sideIndex];
// Grab the decals generated
for (int onmirror = 0; onmirror < 2; onmirror++)
{
if (result.decals[onmirror].Size() != 0 && !sideBlock.InSideDecalsList)
{
SideDecals.Push(sideIndex);
sideBlock.InSideDecalsList = true;
}
for (const HWDecal& decal : result.decals[onmirror])
{
int oldvertindex = decal.vertindex;
int newvertindex = sideBlock.DecalVertices.Size();
for (int i = 0; i < 4; i++)
{
sideBlock.DecalVertices.Push(state.mVertices[oldvertindex + i]);
}
sideBlock.Decals[onmirror].Push(decal);
sideBlock.Decals[onmirror].Last().vertindex = newvertindex;
}
}
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
@ -684,16 +754,16 @@ void DoomLevelMesh::CreateSide(FLevelLocals& doomMap, unsigned int sideIndex)
state.AlphaFunc(Alpha_GEqual, 0.f);
CreateWallSurface(side, disp, state, result.list, back ? LevelMeshDrawType::Masked : LevelMeshDrawType::Opaque, true, sideIndex);
if (result.portals.Size() != 0 && !Sides[sideIndex].InSidePortalsList)
if (result.portals.Size() != 0 && !sideBlock.InSidePortalsList)
{
// Register side having portals
SidePortals.Push(sideIndex);
Sides[sideIndex].InSidePortalsList = true;
sideBlock.InSidePortalsList = true;
}
for (HWWall& portal : result.portals)
{
Sides[sideIndex].WallPortals.Push(portal);
sideBlock.WallPortals.Push(portal);
}
CreateWallSurface(side, disp, state, result.portals, LevelMeshDrawType::Portal, false, sideIndex);

View file

@ -15,6 +15,7 @@
struct FLevelLocals;
struct FPolyObj;
struct HWWallDispatcher;
struct HWDrawInfo;
class DoomLevelMesh;
class MeshBuilder;
@ -67,7 +68,10 @@ struct SideSurfaceBlock
TArray<GeometryFreeInfo> Geometries;
TArray<UniformsAllocInfo> Uniforms;
TArray<HWWall> WallPortals;
TArray<HWDecal> Decals[2];
TArray<FFlatVertex> DecalVertices;
bool InSidePortalsList = false;
bool InSideDecalsList = false;
TArray<DrawRangeInfo> DrawRanges;
SurfaceUpdateType UpdateType = SurfaceUpdateType::None;
};
@ -94,6 +98,9 @@ public:
void BuildSectorGroups(const FLevelLocals& doomMap);
void ProcessDecals(HWDrawInfo* drawinfo, FRenderState& state);
TArray<int> SideDecals;
TArray<int> SidePortals;
TArray<HWWall*> WallPortals;
@ -103,16 +110,17 @@ public:
void CreateLights(FLevelLocals& doomMap);
void FloorHeightChanged(struct sector_t* sector) override;
void CeilingHeightChanged(struct sector_t* sector) override;
void MidTex3DHeightChanged(struct sector_t* sector) override;
void FloorTextureChanged(struct sector_t* sector) override;
void CeilingTextureChanged(struct sector_t* sector) override;
void SectorChangedOther(struct sector_t* sector) override;
void SideTextureChanged(struct side_t* side, int section) override;
void SectorLightChanged(struct sector_t* sector) override;
void SectorLightThinkerCreated(struct sector_t* sector, class DLighting* lightthinker) override;
void SectorLightThinkerDestroyed(struct sector_t* sector, class DLighting* lightthinker) override;
void FloorHeightChanged(sector_t* sector) override;
void CeilingHeightChanged(sector_t* sector) override;
void MidTex3DHeightChanged(sector_t* sector) override;
void FloorTextureChanged(sector_t* sector) override;
void CeilingTextureChanged(sector_t* sector) override;
void SectorChangedOther(sector_t* sector) override;
void SideTextureChanged(side_t* side, int section) override;
void SideDecalsChanged(side_t* side) override;
void SectorLightChanged(sector_t* sector) override;
void SectorLightThinkerCreated(sector_t* sector, DLighting* lightthinker) override;
void SectorLightThinkerDestroyed(sector_t* sector, DLighting* lightthinker) override;
void Reset(const LevelMeshLimits& limits) override
{

View file

@ -39,6 +39,9 @@
#include "flatvertices.h"
#include "hw_renderstate.h"
#include "texturemanager.h"
#include "hw_walldispatcher.h"
#include "hw_dynlightdata.h"
#include "a_dynlight.h"
//==========================================================================
//
@ -189,7 +192,7 @@ static float mix(float a, float b, float t)
return a * (1.0f - t) + b * t;
}
void HWWall::ProcessDecal(HWDrawInfo *di, FRenderState& state, DBaseDecal *decal, const FVector3 &normal)
void HWWall::ProcessDecal(HWWallDispatcher* di, FRenderState& state, DBaseDecal *decal, const FVector3 &normal)
{
line_t * line = seg->linedef;
side_t * side = seg->sidedef;
@ -448,7 +451,7 @@ void HWWall::ProcessDecal(HWDrawInfo *di, FRenderState& state, DBaseDecal *decal
//
//==========================================================================
void HWWall::ProcessDecals(HWDrawInfo *di, FRenderState& state)
void HWWall::ProcessDecals(HWWallDispatcher* di, FRenderState& state)
{
if (seg->sidedef != nullptr)
{
@ -465,3 +468,73 @@ void HWWall::ProcessDecals(HWDrawInfo *di, FRenderState& state)
}
}
//==========================================================================
void HWDecal::SetupLights(HWDrawInfo* di, FRenderState& state, FDynLightData& lightdata, Plane p, FLightNode* node)
{
lightdata.Clear();
// if (RenderStyle == STYLE_Add && !di->Level->lightadditivesurfaces) return; // no lights on additively blended surfaces.
//float vtx[] = { glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2 };
//Plane p;
// auto normal = glseg.Normal();
// p.Set(normal, -normal.X * glseg.x1 - normal.Z * glseg.y1);
// Iterate through all dynamic lights which touch this wall and render them
while (node)
{
if (node->lightsource->IsActive() && !node->lightsource->DontLightMap())
{
iter_dlight++;
DVector3 posrel = node->lightsource->PosRelative(frontsector->PortalGroup);
float x = posrel.X;
float y = posrel.Y;
float z = posrel.Z;
float dist = fabsf(p.DistToPoint(x, z, y));
float radius = node->lightsource->GetRadius();
//float scale = 1.0f / ((2.f * radius) - dist);
//FVector3 fn, pos;
if (radius > 0.f && dist < radius)
{
/*
FVector3 nearPt, up, right;
pos = { x, z, y };
fn = p.Normal();
fn.GetRightUp(right, up);
FVector3 tmpVec = fn * dist;
nearPt = pos + tmpVec;
FVector3 t1;
int outcnt[4] = { 0,0,0,0 };
texcoord tcs[4];
// do a quick check whether the light touches this polygon
for (int i = 0; i < 4; i++)
{
t1 = FVector3(&vtx[i * 3]);
FVector3 nearToVert = t1 - nearPt;
tcs[i].u = ((nearToVert | right) * scale) + 0.5f;
tcs[i].v = ((nearToVert | up) * scale) + 0.5f;
if (tcs[i].u < 0) outcnt[0]++;
if (tcs[i].u > 1) outcnt[1]++;
if (tcs[i].v < 0) outcnt[2]++;
if (tcs[i].v > 1) outcnt[3]++;
}
if (outcnt[0] != 4 && outcnt[1] != 4 && outcnt[2] != 4 && outcnt[3] != 4)*/
{
draw_dlight += GetLight(lightdata, frontsector->PortalGroup, p, node->lightsource, true);
}
}
}
node = node->nextLight;
}
dynlightindex = state.UploadLights(lightdata);
}

View file

@ -507,6 +507,17 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& 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;

View file

@ -327,7 +327,7 @@ public:
void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub);
void AddWall(HWWall *w);
void AddMirrorSurface(HWWall *w, FRenderState& state);
void AddMirrorSurface(HWWallDispatcher* di, HWWall *w, FRenderState& state);
void AddFlat(HWFlat *flat, bool fog);
void AddSprite(HWSprite *sprite, bool translucent);

View file

@ -67,7 +67,7 @@ void HWDrawInfo::AddWall(HWWall *wall)
//
//==========================================================================
void HWDrawInfo::AddMirrorSurface(HWWall *w, FRenderState& state)
void HWDrawInfo::AddMirrorSurface(HWWallDispatcher* di, HWWall *w, FRenderState& state)
{
w->type = RENDERWALL_MIRRORSURFACE;
auto newwall = drawlists[GLDL_TRANSLUCENTBORDER].NewWall();
@ -87,7 +87,7 @@ void HWDrawInfo::AddMirrorSurface(HWWall *w, FRenderState& state)
{
newwall->SetupLights(this, state, lightdata);
}
newwall->ProcessDecals(this, state);
newwall->ProcessDecals(di, state);
newwall->dynlightindex = -1; // the environment map should not be affected by lights - only the decals.
}

View file

@ -281,8 +281,8 @@ public:
float fch1, float fch2, float ffh1, float ffh2,
float bch1, float bch2, float bfh1, float bfh2);
void ProcessDecal(HWDrawInfo* di, FRenderState& state, DBaseDecal* decal, const FVector3& normal);
void ProcessDecals(HWDrawInfo* di, FRenderState& state);
void ProcessDecal(HWWallDispatcher* di, FRenderState& state, DBaseDecal* decal, const FVector3& normal);
void ProcessDecals(HWWallDispatcher* di, FRenderState& state);
int CreateVertices(FFlatVertex*& ptr, bool nosplit);
void SplitLeftEdge(FFlatVertex*& ptr);
@ -427,8 +427,9 @@ public:
void DrawSprite(HWDrawInfo *di, FRenderState &state, bool translucent);
};
struct FDynLightData;
struct FLightNode;
class FRenderState;
struct DecalVertex
{
@ -455,7 +456,7 @@ struct HWDecal
FVector3 Normal;
void DrawDecal(HWDrawInfo *di, FRenderState &state);
void SetupLights(HWDrawInfo* di, FRenderState& state, FDynLightData& lightdata, Plane p, FLightNode* node);
};

View file

@ -14,6 +14,7 @@ struct HWMeshHelper
TArray<HWWall> portals;
TArray<HWMissing> lower;
TArray<HWMissing> upper;
TArray<HWDecal> decals[2];
};
@ -77,4 +78,10 @@ struct HWWallDispatcher
mh->portals.Push(*wal);
}
HWDecal* AddDecal(bool onmirror)
{
if (di) return di->AddDecal(onmirror);
mh->decals[onmirror].Reserve(1);
return &mh->decals[onmirror].Last();
}
};

View file

@ -524,24 +524,23 @@ void HWWall::PutWall(HWWallDispatcher *di, FRenderState& state, bool translucent
{
flags &= ~HWF_GLOW;
}
bool solid;
if (passflag[type] == 1) solid = true;
else if (type == RENDERWALL_FFBLOCK) solid = texture && !texture->isMasked();
else solid = false;
bool hasDecals = solid && seg->sidedef && seg->sidedef->AttachedDecals;
if (hasDecals)
{
// If we want to use the light infos for the decal we cannot delay the creation until the render pass.
if (ddi->Level->HasDynamicLights && !ddi->isFullbrightScene() && texture != nullptr)
{
SetupLights(ddi, state, lightdata);
}
ProcessDecals(ddi, state);
}
}
bool solid;
if (passflag[type] == 1) solid = true;
else if (type == RENDERWALL_FFBLOCK) solid = texture && !texture->isMasked();
else solid = false;
bool hasDecals = solid && seg->sidedef && seg->sidedef->AttachedDecals;
if (hasDecals)
{
// If we want to use the light infos for the decal we cannot delay the creation until the render pass.
if (ddi && ddi->Level->HasDynamicLights && !ddi->isFullbrightScene() && texture != nullptr)
{
SetupLights(ddi, state, lightdata);
}
ProcessDecals(di, state);
}
di->AddWall(this);
@ -631,7 +630,7 @@ void HWWall::PutPortal(HWWallDispatcher *di, FRenderState& state, int ptype, int
if (gl_mirror_envmap)
{
// draw a reflective layer over the mirror
ddi->AddMirrorSurface(this, state);
ddi->AddMirrorSurface(di, this, state);
}
break;