mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 23:02:08 +00:00
- 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:
parent
0a3e9a49f8
commit
ba0b42465d
14 changed files with 356 additions and 315 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1573,6 +1573,7 @@ FLevelLocals::FLevelLocals() : Behaviors(this), tagManager(this)
|
|||
FLevelLocals::~FLevelLocals()
|
||||
{
|
||||
if (localEventManager) delete localEventManager;
|
||||
if (aabbTree) delete aabbTree;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -363,6 +363,9 @@ void FLevelLocals::ClearLevelData()
|
|||
if (automap) automap->Destroy();
|
||||
Behaviors.UnloadModules();
|
||||
localEventManager->Shutdown();
|
||||
if (aabbTree) delete aabbTree;
|
||||
aabbTree = nullptr;
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
273
src/rendering/hwrenderer/dynlights/doom_aabbtree.cpp
Normal file
273
src/rendering/hwrenderer/dynlights/doom_aabbtree.cpp
Normal 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(¢roids[0], false))
|
||||
return;
|
||||
|
||||
int staticroot = nodes.Size() - 1;
|
||||
|
||||
dynamicStartNode = nodes.Size();
|
||||
dynamicStartLine = treelines.Size();
|
||||
|
||||
// Create the dynamic subtree
|
||||
if (GenerateTree(¢roids[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;
|
||||
}
|
||||
|
21
src/rendering/hwrenderer/dynlights/doom_aabbtree.h
Normal file
21
src/rendering/hwrenderer/dynlights/doom_aabbtree.h
Normal 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;
|
||||
};
|
||||
|
|
@ -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(¢roids[0], false))
|
||||
return;
|
||||
|
||||
int staticroot = nodes.Size() - 1;
|
||||
|
||||
dynamicStartNode = nodes.Size();
|
||||
dynamicStartLine = treelines.Size();
|
||||
|
||||
// Create the dynamic subtree
|
||||
if (GenerateTree(¢roids[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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
Loading…
Reference in a new issue