- changed shadowmap setup so that the AABB tree is owned and controlled by the map, not the renderer.

Needed to properly separate game logic from backend implementation, the shadowmap had both in the same object thanks to the old setup.
This commit is contained in:
Christoph Oelckers 2020-04-26 18:54:43 +02:00
parent 0a3e9a49f8
commit ba0b42465d
14 changed files with 356 additions and 315 deletions

View file

@ -945,6 +945,7 @@ set (PCH_SOURCES
rendering/gl/system/gl_framebuffer.cpp
rendering/hwrenderer/hw_entrypoint.cpp
rendering/hwrenderer/data/hw_vertexbuilder.cpp
rendering/hwrenderer/dynlights/doom_aabbtree.cpp
rendering/hwrenderer/dynlights/hw_aabbtree.cpp
rendering/hwrenderer/dynlights/hw_shadowmap.cpp
rendering/hwrenderer/models/hw_models.cpp

View file

@ -866,6 +866,27 @@ PPPresent::PPPresent()
/////////////////////////////////////////////////////////////////////////////
void PPShadowMap::Update(PPRenderState* renderstate)
{
ShadowMapUniforms uniforms;
uniforms.ShadowmapQuality = (float)gl_shadowmap_quality;
uniforms.NodesCount = screen->mShadowMap.NodesCount();
renderstate->PushGroup("shadowmap");
renderstate->Clear();
renderstate->Shader = &ShadowMap;
renderstate->Uniforms.Set(uniforms);
renderstate->Viewport = { 0, 0, gl_shadowmap_quality, 1024 };
renderstate->SetShadowMapBuffers(true);
renderstate->SetOutputShadowMap();
renderstate->SetNoBlend();
renderstate->Draw();
renderstate->PopGroup();
}
/////////////////////////////////////////////////////////////////////////////
CVAR(Bool, gl_custompost, true, 0)

View file

@ -1573,6 +1573,7 @@ FLevelLocals::FLevelLocals() : Behaviors(this), tagManager(this)
FLevelLocals::~FLevelLocals()
{
if (localEventManager) delete localEventManager;
if (aabbTree) delete aabbTree;
}
//==========================================================================

View file

@ -54,6 +54,7 @@
#include "r_data/r_sections.h"
#include "r_data/r_canvastexture.h"
#include "r_data/r_interpolate.h"
#include "hwrenderer/dynlights/doom_aabbtree.h"
//============================================================================
//
@ -456,6 +457,7 @@ public:
FSectionContainer sections;
FCanvasTextureInfo canvasTextureInfo;
EventManager *localEventManager = nullptr;
DoomLevelAABBTree* aabbTree = nullptr;
// [ZZ] Destructible geometry information
TMap<int, FHealthGroup> healthGroups;

View file

@ -3239,5 +3239,7 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position)
PO_Init(); // Initialize the polyobjs
if (!Level->IsReentering())
Level->FinalizePortals(); // finalize line portals after polyobjects have been initialized. This info is needed for properly flagging them.
Level->aabbTree = new DoomLevelAABBTree(Level);
}

View file

@ -363,6 +363,9 @@ void FLevelLocals::ClearLevelData()
if (automap) automap->Destroy();
Behaviors.UnloadModules();
localEventManager->Shutdown();
if (aabbTree) delete aabbTree;
aabbTree = nullptr;
}
//==========================================================================

View file

@ -0,0 +1,273 @@
//
//---------------------------------------------------------------------------
// AABB-tree used for ray testing
// Copyright(C) 2017 Magnus Norddahl
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
#include "doom_aabbtree.h"
#include "g_levellocals.h"
using namespace hwrenderer;
DoomLevelAABBTree::DoomLevelAABBTree(FLevelLocals *lev)
{
Level = lev;
// Calculate the center of all lines
TArray<FVector2> centroids;
for (unsigned int i = 0; i < Level->lines.Size(); i++)
{
FVector2 v1 = { (float)Level->lines[i].v1->fX(), (float)Level->lines[i].v1->fY() };
FVector2 v2 = { (float)Level->lines[i].v2->fX(), (float)Level->lines[i].v2->fY() };
centroids.Push((v1 + v2) * 0.5f);
}
// Create the static subtree
if (!GenerateTree(&centroids[0], false))
return;
int staticroot = nodes.Size() - 1;
dynamicStartNode = nodes.Size();
dynamicStartLine = treelines.Size();
// Create the dynamic subtree
if (GenerateTree(&centroids[0], true))
{
int dynamicroot = nodes.Size() - 1;
// Create a shared root node
FVector2 aabb_min, aabb_max;
const auto &left = nodes[staticroot];
const auto &right = nodes[dynamicroot];
aabb_min.X = MIN(left.aabb_left, right.aabb_left);
aabb_min.Y = MIN(left.aabb_top, right.aabb_top);
aabb_max.X = MAX(left.aabb_right, right.aabb_right);
aabb_max.Y = MAX(left.aabb_bottom, right.aabb_bottom);
nodes.Push({ aabb_min, aabb_max, staticroot, dynamicroot });
}
// Add the lines referenced by the leaf nodes
treelines.Resize(mapLines.Size());
for (unsigned int i = 0; i < mapLines.Size(); i++)
{
const auto &line = Level->lines[mapLines[i]];
auto &treeline = treelines[i];
treeline.x = (float)line.v1->fX();
treeline.y = (float)line.v1->fY();
treeline.dx = (float)line.v2->fX() - treeline.x;
treeline.dy = (float)line.v2->fY() - treeline.y;
}
}
bool DoomLevelAABBTree::GenerateTree(const FVector2 *centroids, bool dynamicsubtree)
{
// Create a list of level lines we want to add:
TArray<int> line_elements;
auto &maplines = Level->lines;
for (unsigned int i = 0; i < maplines.Size(); i++)
{
if (!maplines[i].backsector)
{
bool isPolyLine = maplines[i].sidedef[0] && (maplines[i].sidedef[0]->Flags & WALLF_POLYOBJ);
if (isPolyLine && dynamicsubtree)
{
line_elements.Push(mapLines.Size());
mapLines.Push(i);
}
else if (!isPolyLine && !dynamicsubtree)
{
line_elements.Push(mapLines.Size());
mapLines.Push(i);
}
}
}
if (line_elements.Size() == 0)
return false;
// GenerateTreeNode needs a buffer where it can store line indices temporarily when sorting lines into the left and right child AABB buckets
TArray<int> work_buffer;
work_buffer.Resize(line_elements.Size() * 2);
// Generate the AABB tree
GenerateTreeNode(&line_elements[0], (int)line_elements.Size(), centroids, &work_buffer[0]);
return true;
}
bool DoomLevelAABBTree::Update()
{
bool modified = false;
for (unsigned int i = dynamicStartLine; i < mapLines.Size(); i++)
{
const auto &line = Level->lines[mapLines[i]];
AABBTreeLine treeline;
treeline.x = (float)line.v1->fX();
treeline.y = (float)line.v1->fY();
treeline.dx = (float)line.v2->fX() - treeline.x;
treeline.dy = (float)line.v2->fY() - treeline.y;
if (memcmp(&treelines[i], &treeline, sizeof(AABBTreeLine)))
{
TArray<int> path = FindNodePath(i, nodes.Size() - 1);
if (path.Size())
{
float x1 = (float)line.v1->fX();
float y1 = (float)line.v1->fY();
float x2 = (float)line.v2->fX();
float y2 = (float)line.v2->fY();
int nodeIndex = path[0];
nodes[nodeIndex].aabb_left = MIN(x1, x2);
nodes[nodeIndex].aabb_right = MAX(x1, x2);
nodes[nodeIndex].aabb_top = MIN(y1, y2);
nodes[nodeIndex].aabb_bottom = MAX(y1, y2);
for (unsigned int j = 1; j < path.Size(); j++)
{
auto &cur = nodes[path[j]];
const auto &left = nodes[cur.left_node];
const auto &right = nodes[cur.right_node];
cur.aabb_left = MIN(left.aabb_left, right.aabb_left);
cur.aabb_top = MIN(left.aabb_top, right.aabb_top);
cur.aabb_right = MAX(left.aabb_right, right.aabb_right);
cur.aabb_bottom = MAX(left.aabb_bottom, right.aabb_bottom);
}
treelines[i] = treeline;
modified = true;
}
}
}
return modified;
}
int DoomLevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer)
{
if (num_lines == 0)
return -1;
// Find bounding box and median of the lines
FVector2 median = FVector2(0.0f, 0.0f);
FVector2 aabb_min, aabb_max;
auto &maplines = Level->lines;
aabb_min.X = (float)maplines[mapLines[lines[0]]].v1->fX();
aabb_min.Y = (float)maplines[mapLines[lines[0]]].v1->fY();
aabb_max = aabb_min;
for (int i = 0; i < num_lines; i++)
{
float x1 = (float)maplines[mapLines[lines[i]]].v1->fX();
float y1 = (float)maplines[mapLines[lines[i]]].v1->fY();
float x2 = (float)maplines[mapLines[lines[i]]].v2->fX();
float y2 = (float)maplines[mapLines[lines[i]]].v2->fY();
aabb_min.X = MIN(aabb_min.X, x1);
aabb_min.X = MIN(aabb_min.X, x2);
aabb_min.Y = MIN(aabb_min.Y, y1);
aabb_min.Y = MIN(aabb_min.Y, y2);
aabb_max.X = MAX(aabb_max.X, x1);
aabb_max.X = MAX(aabb_max.X, x2);
aabb_max.Y = MAX(aabb_max.Y, y1);
aabb_max.Y = MAX(aabb_max.Y, y2);
median += centroids[mapLines[lines[i]]];
}
median /= (float)num_lines;
if (num_lines == 1) // Leaf node
{
nodes.Push(AABBTreeNode(aabb_min, aabb_max, lines[0]));
return (int)nodes.Size() - 1;
}
// Find the longest axis
float axis_lengths[2] =
{
aabb_max.X - aabb_min.X,
aabb_max.Y - aabb_min.Y
};
int axis_order[2] = { 0, 1 };
FVector2 axis_plane[2] = { FVector2(1.0f, 0.0f), FVector2(0.0f, 1.0f) };
std::sort(axis_order, axis_order + 2, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; });
// Try sort at longest axis, then if that fails then the other one.
// We place the sorted lines into work_buffer and then move the result back to the lines list when done.
int left_count, right_count;
for (int attempt = 0; attempt < 2; attempt++)
{
// Find the sort plane for axis
FVector2 axis = axis_plane[axis_order[attempt]];
FVector3 plane(axis, -(median | axis));
// Sort lines into two based ib whether the line center is on the front or back side of a plane
left_count = 0;
right_count = 0;
for (int i = 0; i < num_lines; i++)
{
int line_index = lines[i];
float side = FVector3(centroids[mapLines[lines[i]]], 1.0f) | plane;
if (side >= 0.0f)
{
work_buffer[left_count] = line_index;
left_count++;
}
else
{
work_buffer[num_lines + right_count] = line_index;
right_count++;
}
}
if (left_count != 0 && right_count != 0)
break;
}
// Check if something went wrong when sorting and do a random sort instead
if (left_count == 0 || right_count == 0)
{
left_count = num_lines / 2;
right_count = num_lines - left_count;
}
else
{
// Move result back into lines list:
for (int i = 0; i < left_count; i++)
lines[i] = work_buffer[i];
for (int i = 0; i < right_count; i++)
lines[i + left_count] = work_buffer[num_lines + i];
}
// Create child nodes:
int left_index = -1;
int right_index = -1;
if (left_count > 0)
left_index = GenerateTreeNode(lines, left_count, centroids, work_buffer);
if (right_count > 0)
right_index = GenerateTreeNode(lines + left_count, right_count, centroids, work_buffer);
// Store resulting node and return its index
nodes.Push(AABBTreeNode(aabb_min, aabb_max, left_index, right_index));
return (int)nodes.Size() - 1;
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "hw_aabbtree.h"
// Axis aligned bounding box tree used for ray testing treelines.
class DoomLevelAABBTree : public hwrenderer::LevelAABBTree
{
public:
// Constructs a tree for the current level
DoomLevelAABBTree(FLevelLocals *lev);
bool Update() override;
private:
bool GenerateTree(const FVector2 *centroids, bool dynamicsubtree);
// Generate a tree node and its children recursively
int GenerateTreeNode(int *treelines, int num_lines, const FVector2 *centroids, int *work_buffer);
TArray<int> mapLines;
FLevelLocals *Level;
};

View file

@ -27,140 +27,6 @@
namespace hwrenderer
{
LevelAABBTree::LevelAABBTree(FLevelLocals *lev)
{
Level = lev;
// Calculate the center of all lines
TArray<FVector2> centroids;
for (unsigned int i = 0; i < Level->lines.Size(); i++)
{
FVector2 v1 = { (float)Level->lines[i].v1->fX(), (float)Level->lines[i].v1->fY() };
FVector2 v2 = { (float)Level->lines[i].v2->fX(), (float)Level->lines[i].v2->fY() };
centroids.Push((v1 + v2) * 0.5f);
}
// Create the static subtree
if (!GenerateTree(&centroids[0], false))
return;
int staticroot = nodes.Size() - 1;
dynamicStartNode = nodes.Size();
dynamicStartLine = treelines.Size();
// Create the dynamic subtree
if (GenerateTree(&centroids[0], true))
{
int dynamicroot = nodes.Size() - 1;
// Create a shared root node
FVector2 aabb_min, aabb_max;
const auto &left = nodes[staticroot];
const auto &right = nodes[dynamicroot];
aabb_min.X = MIN(left.aabb_left, right.aabb_left);
aabb_min.Y = MIN(left.aabb_top, right.aabb_top);
aabb_max.X = MAX(left.aabb_right, right.aabb_right);
aabb_max.Y = MAX(left.aabb_bottom, right.aabb_bottom);
nodes.Push({ aabb_min, aabb_max, staticroot, dynamicroot });
}
// Add the lines referenced by the leaf nodes
treelines.Resize(mapLines.Size());
for (unsigned int i = 0; i < mapLines.Size(); i++)
{
const auto &line = Level->lines[mapLines[i]];
auto &treeline = treelines[i];
treeline.x = (float)line.v1->fX();
treeline.y = (float)line.v1->fY();
treeline.dx = (float)line.v2->fX() - treeline.x;
treeline.dy = (float)line.v2->fY() - treeline.y;
}
}
bool LevelAABBTree::GenerateTree(const FVector2 *centroids, bool dynamicsubtree)
{
// Create a list of level lines we want to add:
TArray<int> line_elements;
auto &maplines = Level->lines;
for (unsigned int i = 0; i < maplines.Size(); i++)
{
if (!maplines[i].backsector)
{
bool isPolyLine = maplines[i].sidedef[0] && (maplines[i].sidedef[0]->Flags & WALLF_POLYOBJ);
if (isPolyLine && dynamicsubtree)
{
line_elements.Push(mapLines.Size());
mapLines.Push(i);
}
else if (!isPolyLine && !dynamicsubtree)
{
line_elements.Push(mapLines.Size());
mapLines.Push(i);
}
}
}
if (line_elements.Size() == 0)
return false;
// GenerateTreeNode needs a buffer where it can store line indices temporarily when sorting lines into the left and right child AABB buckets
TArray<int> work_buffer;
work_buffer.Resize(line_elements.Size() * 2);
// Generate the AABB tree
GenerateTreeNode(&line_elements[0], (int)line_elements.Size(), centroids, &work_buffer[0]);
return true;
}
bool LevelAABBTree::Update()
{
bool modified = false;
for (unsigned int i = dynamicStartLine; i < mapLines.Size(); i++)
{
const auto &line = Level->lines[mapLines[i]];
AABBTreeLine treeline;
treeline.x = (float)line.v1->fX();
treeline.y = (float)line.v1->fY();
treeline.dx = (float)line.v2->fX() - treeline.x;
treeline.dy = (float)line.v2->fY() - treeline.y;
if (memcmp(&treelines[i], &treeline, sizeof(AABBTreeLine)))
{
TArray<int> path = FindNodePath(i, nodes.Size() - 1);
if (path.Size())
{
float x1 = (float)line.v1->fX();
float y1 = (float)line.v1->fY();
float x2 = (float)line.v2->fX();
float y2 = (float)line.v2->fY();
int nodeIndex = path[0];
nodes[nodeIndex].aabb_left = MIN(x1, x2);
nodes[nodeIndex].aabb_right = MAX(x1, x2);
nodes[nodeIndex].aabb_top = MIN(y1, y2);
nodes[nodeIndex].aabb_bottom = MAX(y1, y2);
for (unsigned int j = 1; j < path.Size(); j++)
{
auto &cur = nodes[path[j]];
const auto &left = nodes[cur.left_node];
const auto &right = nodes[cur.right_node];
cur.aabb_left = MIN(left.aabb_left, right.aabb_left);
cur.aabb_top = MIN(left.aabb_top, right.aabb_top);
cur.aabb_right = MAX(left.aabb_right, right.aabb_right);
cur.aabb_bottom = MAX(left.aabb_bottom, right.aabb_bottom);
}
treelines[i] = treeline;
modified = true;
}
}
}
return modified;
}
TArray<int> LevelAABBTree::FindNodePath(unsigned int line, unsigned int node)
{
const AABBTreeNode &n = nodes[node];
@ -297,114 +163,5 @@ double LevelAABBTree::IntersectRayLine(const DVector2 &ray_start, const DVector2
return 1.0;
}
int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer)
{
if (num_lines == 0)
return -1;
// Find bounding box and median of the lines
FVector2 median = FVector2(0.0f, 0.0f);
FVector2 aabb_min, aabb_max;
auto &maplines = Level->lines;
aabb_min.X = (float)maplines[mapLines[lines[0]]].v1->fX();
aabb_min.Y = (float)maplines[mapLines[lines[0]]].v1->fY();
aabb_max = aabb_min;
for (int i = 0; i < num_lines; i++)
{
float x1 = (float)maplines[mapLines[lines[i]]].v1->fX();
float y1 = (float)maplines[mapLines[lines[i]]].v1->fY();
float x2 = (float)maplines[mapLines[lines[i]]].v2->fX();
float y2 = (float)maplines[mapLines[lines[i]]].v2->fY();
aabb_min.X = MIN(aabb_min.X, x1);
aabb_min.X = MIN(aabb_min.X, x2);
aabb_min.Y = MIN(aabb_min.Y, y1);
aabb_min.Y = MIN(aabb_min.Y, y2);
aabb_max.X = MAX(aabb_max.X, x1);
aabb_max.X = MAX(aabb_max.X, x2);
aabb_max.Y = MAX(aabb_max.Y, y1);
aabb_max.Y = MAX(aabb_max.Y, y2);
median += centroids[mapLines[lines[i]]];
}
median /= (float)num_lines;
if (num_lines == 1) // Leaf node
{
nodes.Push(AABBTreeNode(aabb_min, aabb_max, lines[0]));
return (int)nodes.Size() - 1;
}
// Find the longest axis
float axis_lengths[2] =
{
aabb_max.X - aabb_min.X,
aabb_max.Y - aabb_min.Y
};
int axis_order[2] = { 0, 1 };
FVector2 axis_plane[2] = { FVector2(1.0f, 0.0f), FVector2(0.0f, 1.0f) };
std::sort(axis_order, axis_order + 2, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; });
// Try sort at longest axis, then if that fails then the other one.
// We place the sorted lines into work_buffer and then move the result back to the lines list when done.
int left_count, right_count;
for (int attempt = 0; attempt < 2; attempt++)
{
// Find the sort plane for axis
FVector2 axis = axis_plane[axis_order[attempt]];
FVector3 plane(axis, -(median | axis));
// Sort lines into two based ib whether the line center is on the front or back side of a plane
left_count = 0;
right_count = 0;
for (int i = 0; i < num_lines; i++)
{
int line_index = lines[i];
float side = FVector3(centroids[mapLines[lines[i]]], 1.0f) | plane;
if (side >= 0.0f)
{
work_buffer[left_count] = line_index;
left_count++;
}
else
{
work_buffer[num_lines + right_count] = line_index;
right_count++;
}
}
if (left_count != 0 && right_count != 0)
break;
}
// Check if something went wrong when sorting and do a random sort instead
if (left_count == 0 || right_count == 0)
{
left_count = num_lines / 2;
right_count = num_lines - left_count;
}
else
{
// Move result back into lines list:
for (int i = 0; i < left_count; i++)
lines[i] = work_buffer[i];
for (int i = 0; i < right_count; i++)
lines[i + left_count] = work_buffer[num_lines + i];
}
// Create child nodes:
int left_index = -1;
int right_index = -1;
if (left_count > 0)
left_index = GenerateTreeNode(lines, left_count, centroids, work_buffer);
if (right_count > 0)
right_index = GenerateTreeNode(lines + left_count, right_count, centroids, work_buffer);
// Store resulting node and return its index
nodes.Push(AABBTreeNode(aabb_min, aabb_max, left_index, right_index));
return (int)nodes.Size() - 1;
}
}

View file

@ -37,18 +37,22 @@ struct AABBTreeLine
float dx, dy;
};
// Axis aligned bounding box tree used for ray testing treelines.
class LevelAABBTree
{
public:
// Constructs a tree for the current level
LevelAABBTree(FLevelLocals *lev);
protected:
// Nodes in the AABB tree. Last node is the root node.
TArray<AABBTreeNode> nodes;
// Line segments for the leaf nodes in the tree.
TArray<AABBTreeLine> treelines;
int dynamicStartNode = 0;
int dynamicStartLine = 0;
public:
// Shoot a ray from ray_start to ray_end and return the closest hit as a fractional value between 0 and 1. Returns 1 if no line was hit.
double RayTest(const DVector3 &ray_start, const DVector3 &ray_end);
bool Update();
const void *Nodes() const { return nodes.Data(); }
const void *Lines() const { return treelines.Data(); }
size_t NodesSize() const { return nodes.Size() * sizeof(AABBTreeNode); }
@ -62,31 +66,17 @@ public:
size_t DynamicNodesOffset() const { return dynamicStartNode * sizeof(AABBTreeNode); }
size_t DynamicLinesOffset() const { return dynamicStartLine * sizeof(AABBTreeLine); }
private:
bool GenerateTree(const FVector2 *centroids, bool dynamicsubtree);
virtual bool Update() = 0;
protected:
TArray<int> FindNodePath(unsigned int line, unsigned int node);
// Test if a ray overlaps an AABB node or not
bool OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const AABBTreeNode &node);
// Intersection test between a ray and a line segment
double IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2);
// Generate a tree node and its children recursively
int GenerateTreeNode(int *treelines, int num_lines, const FVector2 *centroids, int *work_buffer);
TArray<int> FindNodePath(unsigned int line, unsigned int node);
// Nodes in the AABB tree. Last node is the root node.
TArray<AABBTreeNode> nodes;
// Line segments for the leaf nodes in the tree.
TArray<AABBTreeLine> treelines;
int dynamicStartNode = 0;
int dynamicStartLine = 0;
TArray<int> mapLines;
FLevelLocals *Level;
};
} // namespace

View file

@ -146,25 +146,6 @@ void IShadowMap::CollectLights()
}
}
bool IShadowMap::ValidateAABBTree(FLevelLocals *Level)
{
// Just comparing the level info is not enough. If two MAPINFO-less levels get played after each other,
// they can both refer to the same default level info.
if (Level->info != mLastLevel && (Level->nodes.Size() != mLastNumNodes || Level->segs.Size() != mLastNumSegs))
{
mAABBTree.reset();
mLastLevel = Level->info;
mLastNumNodes = Level->nodes.Size();
mLastNumSegs = Level->segs.Size();
}
if (mAABBTree)
return true;
mAABBTree.reset(new hwrenderer::LevelAABBTree(Level));
return false;
}
bool IShadowMap::PerformUpdate()
{
@ -196,8 +177,10 @@ void IShadowMap::UploadLights()
void IShadowMap::UploadAABBTree()
{
if (!ValidateAABBTree(&level))
if (mNewTree)
{
mNewTree = false;
if (!mNodesBuffer)
mNodesBuffer = screen->CreateDataBuffer(LIGHTNODES_BINDINGPOINT, true, false);
mNodesBuffer->SetData(mAABBTree->NodesSize(), mAABBTree->Nodes());
@ -224,24 +207,3 @@ IShadowMap::~IShadowMap()
{
Reset();
}
void PPShadowMap::Update(PPRenderState* renderstate)
{
ShadowMapUniforms uniforms;
uniforms.ShadowmapQuality = (float)gl_shadowmap_quality;
uniforms.NodesCount = screen->mShadowMap.NodesCount();
renderstate->PushGroup("shadowmap");
renderstate->Clear();
renderstate->Shader = &ShadowMap;
renderstate->Uniforms.Set(uniforms);
renderstate->Viewport = { 0, 0, gl_shadowmap_quality, 1024 };
renderstate->SetShadowMapBuffers(true);
renderstate->SetOutputShadowMap();
renderstate->SetNoBlend();
renderstate->Draw();
renderstate->PopGroup();
}

View file

@ -40,9 +40,14 @@ public:
return mAABBTree->NodesCount();
}
void SetAABBTree(hwrenderer::LevelAABBTree* tree)
{
mAABBTree = tree;
mNewTree = true;
}
protected:
void CollectLights();
bool ValidateAABBTree(FLevelLocals *lev);
// Upload the AABB-tree to the GPU
void UploadAABBTree();
@ -53,13 +58,9 @@ protected:
// Working buffer for creating the list of lights. Stored here to avoid allocating memory each frame
TArray<float> mLights;
// Used to detect when a level change requires the AABB tree to be regenerated
level_info_t *mLastLevel = nullptr;
unsigned mLastNumNodes = 0;
unsigned mLastNumSegs = 0;
// AABB-tree of the level, used for ray tests
std::unique_ptr<hwrenderer::LevelAABBTree> mAABBTree;
// AABB-tree of the level, used for ray tests, owned by the playsim, not the renderer.
hwrenderer::LevelAABBTree* mAABBTree = nullptr;
bool mNewTree = false;
IShadowMap(const IShadowMap &) = delete;
IShadowMap &operator=(IShadowMap &) = delete;

View file

@ -72,7 +72,10 @@ sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bou
R_SetupFrame(mainvp, r_viewwindow, camera);
if (mainview && toscreen)
{
screen->SetAABBTree(camera->Level->aabbTree);
screen->UpdateShadowMap();
}
// Update the attenuation flag of all light defaults for each viewpoint.
// This function will only do something if the setting differs.

View file

@ -186,6 +186,10 @@ public:
virtual void InitializeState() = 0; // For stuff that needs 'screen' set.
virtual bool IsVulkan() { return false; }
virtual bool IsPoly() { return false; }
void SetAABBTree(hwrenderer::LevelAABBTree * tree)
{
mShadowMap.SetAABBTree(tree);
}
virtual DCanvas* GetCanvas() { return nullptr; }