From ba0b42465d2357188cd739c3b3ec3c8aecf749f7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 26 Apr 2020 18:54:43 +0200 Subject: [PATCH] - 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. --- src/CMakeLists.txt | 1 + .../postprocessing/hw_postprocess.cpp | 21 ++ src/g_level.cpp | 1 + src/g_levellocals.h | 2 + src/maploader/maploader.cpp | 2 + src/p_setup.cpp | 3 + .../hwrenderer/dynlights/doom_aabbtree.cpp | 273 ++++++++++++++++++ .../hwrenderer/dynlights/doom_aabbtree.h | 21 ++ .../hwrenderer/dynlights/hw_aabbtree.cpp | 243 ---------------- .../hwrenderer/dynlights/hw_aabbtree.h | 36 +-- .../hwrenderer/dynlights/hw_shadowmap.cpp | 44 +-- .../hwrenderer/dynlights/hw_shadowmap.h | 17 +- src/rendering/hwrenderer/hw_entrypoint.cpp | 3 + src/rendering/v_video.h | 4 + 14 files changed, 356 insertions(+), 315 deletions(-) create mode 100644 src/rendering/hwrenderer/dynlights/doom_aabbtree.cpp create mode 100644 src/rendering/hwrenderer/dynlights/doom_aabbtree.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f6e1d2a8b..899e45fd68 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp b/src/common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp index 519e03b11e..a206a4c166 100644 --- a/src/common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp +++ b/src/common/rendering/hwrenderer/postprocessing/hw_postprocess.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) diff --git a/src/g_level.cpp b/src/g_level.cpp index ddb8ebbcd4..57d0ac6c67 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1573,6 +1573,7 @@ FLevelLocals::FLevelLocals() : Behaviors(this), tagManager(this) FLevelLocals::~FLevelLocals() { if (localEventManager) delete localEventManager; + if (aabbTree) delete aabbTree; } //========================================================================== diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 863133705d..576cdd6060 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -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 healthGroups; diff --git a/src/maploader/maploader.cpp b/src/maploader/maploader.cpp index 050292e5d1..bf560e1ac9 100644 --- a/src/maploader/maploader.cpp +++ b/src/maploader/maploader.cpp @@ -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); } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index c7ab8dfd4e..d65d08ec1a 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -363,6 +363,9 @@ void FLevelLocals::ClearLevelData() if (automap) automap->Destroy(); Behaviors.UnloadModules(); localEventManager->Shutdown(); + if (aabbTree) delete aabbTree; + aabbTree = nullptr; + } //========================================================================== diff --git a/src/rendering/hwrenderer/dynlights/doom_aabbtree.cpp b/src/rendering/hwrenderer/dynlights/doom_aabbtree.cpp new file mode 100644 index 0000000000..21256271df --- /dev/null +++ b/src/rendering/hwrenderer/dynlights/doom_aabbtree.cpp @@ -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 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 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 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 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; +} + diff --git a/src/rendering/hwrenderer/dynlights/doom_aabbtree.h b/src/rendering/hwrenderer/dynlights/doom_aabbtree.h new file mode 100644 index 0000000000..f41612b445 --- /dev/null +++ b/src/rendering/hwrenderer/dynlights/doom_aabbtree.h @@ -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 mapLines; + FLevelLocals *Level; +}; + diff --git a/src/rendering/hwrenderer/dynlights/hw_aabbtree.cpp b/src/rendering/hwrenderer/dynlights/hw_aabbtree.cpp index a0c3657404..61e6da9477 100644 --- a/src/rendering/hwrenderer/dynlights/hw_aabbtree.cpp +++ b/src/rendering/hwrenderer/dynlights/hw_aabbtree.cpp @@ -27,140 +27,6 @@ namespace hwrenderer { -LevelAABBTree::LevelAABBTree(FLevelLocals *lev) -{ - Level = lev; - // Calculate the center of all lines - TArray 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 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 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 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 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; -} - } diff --git a/src/rendering/hwrenderer/dynlights/hw_aabbtree.h b/src/rendering/hwrenderer/dynlights/hw_aabbtree.h index f435d68cef..19f3e75311 100644 --- a/src/rendering/hwrenderer/dynlights/hw_aabbtree.h +++ b/src/rendering/hwrenderer/dynlights/hw_aabbtree.h @@ -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 nodes; + // Line segments for the leaf nodes in the tree. + TArray 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 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 FindNodePath(unsigned int line, unsigned int node); - - // Nodes in the AABB tree. Last node is the root node. - TArray nodes; - - // Line segments for the leaf nodes in the tree. - TArray treelines; - - int dynamicStartNode = 0; - int dynamicStartLine = 0; - - TArray mapLines; - FLevelLocals *Level; }; } // namespace diff --git a/src/rendering/hwrenderer/dynlights/hw_shadowmap.cpp b/src/rendering/hwrenderer/dynlights/hw_shadowmap.cpp index 5f8cf06768..5510de719d 100644 --- a/src/rendering/hwrenderer/dynlights/hw_shadowmap.cpp +++ b/src/rendering/hwrenderer/dynlights/hw_shadowmap.cpp @@ -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(); -} - diff --git a/src/rendering/hwrenderer/dynlights/hw_shadowmap.h b/src/rendering/hwrenderer/dynlights/hw_shadowmap.h index a3b837e1c1..08c44d1cf6 100644 --- a/src/rendering/hwrenderer/dynlights/hw_shadowmap.h +++ b/src/rendering/hwrenderer/dynlights/hw_shadowmap.h @@ -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 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 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; diff --git a/src/rendering/hwrenderer/hw_entrypoint.cpp b/src/rendering/hwrenderer/hw_entrypoint.cpp index adaccd971d..70b1d5e05e 100644 --- a/src/rendering/hwrenderer/hw_entrypoint.cpp +++ b/src/rendering/hwrenderer/hw_entrypoint.cpp @@ -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. diff --git a/src/rendering/v_video.h b/src/rendering/v_video.h index e790d72271..ead9eb5a2c 100644 --- a/src/rendering/v_video.h +++ b/src/rendering/v_video.h @@ -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; }