diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f6e1d2a8..899e45fd6 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 519e03b11..a206a4c16 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 ddb8ebbcd..57d0ac6c6 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 863133705..576cdd606 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 050292e5d..bf560e1ac 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 c7ab8dfd4..d65d08ec1 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 000000000..21256271d --- /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 000000000..f41612b44 --- /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 a0c365740..61e6da947 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 f435d68ce..19f3e7531 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 5f8cf0676..5510de719 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 a3b837e1c..08c44d1cf 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 adaccd971..70b1d5e05 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 e790d7227..ead9eb5a2 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; }