mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-25 05:21:02 +00:00
- moved gl_spritelight out of gl/. This required a few more changes:
* split gl_shadowmap.cpp into a GL dependent and an API independent part. * gl_drawinfo must be kept around for the HUD sprite because it connects the renderer with the hardware indpendent part of the engine.
This commit is contained in:
parent
819ea8f937
commit
634b3cf413
12 changed files with 256 additions and 193 deletions
|
@ -825,7 +825,6 @@ set( FASTMATH_SOURCES
|
|||
gl/scene/gl_swscene.cpp
|
||||
gl/scene/gl_portal.cpp
|
||||
gl/scene/gl_walls_draw.cpp
|
||||
gl/scene/gl_spritelight.cpp
|
||||
gl_load/gl_load.c
|
||||
hwrenderer/dynlights/hw_dynlightdata.cpp
|
||||
hwrenderer/scene/hw_fakeflat.cpp
|
||||
|
@ -835,6 +834,7 @@ set( FASTMATH_SOURCES
|
|||
hwrenderer/scene/hw_renderhacks.cpp
|
||||
hwrenderer/scene/hw_sky.cpp
|
||||
hwrenderer/scene/hw_sprites.cpp
|
||||
hwrenderer/scene/hw_spritelight.cpp
|
||||
hwrenderer/scene/hw_walls.cpp
|
||||
hwrenderer/scene/hw_walls_vertex.cpp
|
||||
r_data/models/models.cpp
|
||||
|
@ -1059,6 +1059,7 @@ set (PCH_SOURCES
|
|||
gl/textures/gl_samplers.cpp
|
||||
hwrenderer/data/flatvertices.cpp
|
||||
hwrenderer/dynlights/hw_aabbtree.cpp
|
||||
hwrenderer/dynlights/hw_shadowmap.cpp
|
||||
hwrenderer/scene/hw_skydome.cpp
|
||||
hwrenderer/textures/hw_material.cpp
|
||||
hwrenderer/textures/hw_precache.cpp
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// 1D dynamic shadow maps
|
||||
// 1D dynamic shadow maps (OpenGL dependent part)
|
||||
// Copyright(C) 2017 Magnus Norddahl
|
||||
// All rights reserved.
|
||||
//
|
||||
|
@ -32,77 +32,6 @@
|
|||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "stats.h"
|
||||
|
||||
/*
|
||||
The 1D shadow maps are stored in a 1024x1024 texture as float depth values (R32F).
|
||||
|
||||
Each line in the texture is assigned to a single light. For example, to grab depth values for light 20
|
||||
the fragment shader (main.fp) needs to sample from row 20. That is, the V texture coordinate needs
|
||||
to be 20.5/1024.
|
||||
|
||||
The texel row for each light is split into four parts. One for each direction, like a cube texture,
|
||||
but then only in 2D where this reduces itself to a square. When main.fp samples from the shadow map
|
||||
it first decides in which direction the fragment is (relative to the light), like cubemap sampling does
|
||||
for 3D, but once again just for the 2D case.
|
||||
|
||||
Texels 0-255 is Y positive, 256-511 is X positive, 512-767 is Y negative and 768-1023 is X negative.
|
||||
|
||||
Generating the shadow map itself is done by FShadowMap::Update(). The shadow map texture's FBO is
|
||||
bound and then a screen quad is drawn to make a fragment shader cover all texels. For each fragment
|
||||
it shoots a ray and collects the distance to what it hit.
|
||||
|
||||
The shadowmap.fp shader knows which light and texel it is processing by mapping gl_FragCoord.y back
|
||||
to the light index, and it knows which direction to ray trace by looking at gl_FragCoord.x. For
|
||||
example, if gl_FragCoord.y is 20.5, then it knows its processing light 20, and if gl_FragCoord.x is
|
||||
127.5, then it knows we are shooting straight ahead for the Y positive direction.
|
||||
|
||||
Ray testing is done by uploading two GPU storage buffers - one holding AABB tree nodes, and one with
|
||||
the line segments at the leaf nodes of the tree. The fragment shader then performs a test same way
|
||||
as on the CPU, except everything uses indexes as pointers are not allowed in GLSL.
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
cycle_t UpdateCycles;
|
||||
int LightsProcessed;
|
||||
int LightsShadowmapped;
|
||||
}
|
||||
|
||||
ADD_STAT(shadowmap)
|
||||
{
|
||||
FString out;
|
||||
out.Format("upload=%04.2f ms lights=%d shadowmapped=%d", UpdateCycles.TimeMS(), LightsProcessed, LightsShadowmapped);
|
||||
return out;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_shadowmap_quality, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
case 128:
|
||||
case 256:
|
||||
case 512:
|
||||
case 1024:
|
||||
break;
|
||||
default:
|
||||
self = 128;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR (Bool, gl_light_shadowmap, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
// Unset any residual shadow map indices in the light actors.
|
||||
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
||||
while (auto light = it.Next())
|
||||
{
|
||||
light->mShadowmapIndex = 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FShadowMap::Update()
|
||||
{
|
||||
UpdateCycles.Reset();
|
||||
|
@ -145,53 +74,10 @@ void FShadowMap::Update()
|
|||
UpdateCycles.Unclock();
|
||||
}
|
||||
|
||||
bool FShadowMap::ShadowTest(ADynamicLight *light, const DVector3 &pos)
|
||||
{
|
||||
if (light->shadowmapped && light->radius > 0.0 && IsEnabled() && mAABBTree)
|
||||
return mAABBTree->RayTest(light->Pos(), pos) >= 1.0f;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FShadowMap::IsEnabled() const
|
||||
{
|
||||
return gl_light_shadowmap && !!(gl.flags & RFL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
void FShadowMap::UploadLights()
|
||||
{
|
||||
if (mLights.Size() != 1024 * 4) mLights.Resize(1024 * 4);
|
||||
int lightindex = 0;
|
||||
|
||||
// Todo: this should go through the blockmap in a spiral pattern around the player so that closer lights are preferred.
|
||||
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
||||
while (auto light = it.Next())
|
||||
{
|
||||
LightsProcessed++;
|
||||
if (light->shadowmapped && lightindex < 1024 * 4)
|
||||
{
|
||||
LightsShadowmapped++;
|
||||
|
||||
light->mShadowmapIndex = lightindex >> 2;
|
||||
|
||||
mLights[lightindex] = light->X();
|
||||
mLights[lightindex+1] = light->Y();
|
||||
mLights[lightindex+2] = light->Z();
|
||||
mLights[lightindex+3] = light->GetRadius();
|
||||
lightindex += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
light->mShadowmapIndex = 1024;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (; lightindex < 1024 * 4; lightindex++)
|
||||
{
|
||||
mLights[lightindex] = 0;
|
||||
}
|
||||
|
||||
CollectLights();
|
||||
|
||||
if (mLightList == 0)
|
||||
glGenBuffers(1, (GLuint*)&mLightList);
|
||||
|
||||
|
@ -204,28 +90,21 @@ void FShadowMap::UploadLights()
|
|||
|
||||
void FShadowMap::UploadAABBTree()
|
||||
{
|
||||
// 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))
|
||||
Clear();
|
||||
if (!ValidateAABBTree())
|
||||
{
|
||||
int oldBinding = 0;
|
||||
glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &oldBinding);
|
||||
|
||||
if (mAABBTree)
|
||||
return;
|
||||
glGenBuffers(1, (GLuint*)&mNodesBuffer);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mNodesBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(hwrenderer::AABBTreeNode) * mAABBTree->nodes.Size(), &mAABBTree->nodes[0], GL_STATIC_DRAW);
|
||||
|
||||
mAABBTree.reset(new hwrenderer::LevelAABBTree());
|
||||
glGenBuffers(1, (GLuint*)&mLinesBuffer);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mLinesBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(hwrenderer::AABBTreeLine) * mAABBTree->lines.Size(), &mAABBTree->lines[0], GL_STATIC_DRAW);
|
||||
|
||||
int oldBinding = 0;
|
||||
glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &oldBinding);
|
||||
|
||||
glGenBuffers(1, (GLuint*)&mNodesBuffer);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mNodesBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(hwrenderer::AABBTreeNode) * mAABBTree->nodes.Size(), &mAABBTree->nodes[0], GL_STATIC_DRAW);
|
||||
|
||||
glGenBuffers(1, (GLuint*)&mLinesBuffer);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mLinesBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(hwrenderer::AABBTreeLine) * mAABBTree->lines.Size(), &mAABBTree->lines[0], GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding);
|
||||
}
|
||||
}
|
||||
|
||||
void FShadowMap::Clear()
|
||||
|
@ -247,10 +126,4 @@ void FShadowMap::Clear()
|
|||
glDeleteBuffers(1, (GLuint*)&mLinesBuffer);
|
||||
mLinesBuffer = 0;
|
||||
}
|
||||
|
||||
mAABBTree.reset();
|
||||
|
||||
mLastLevel = level.info;
|
||||
mLastNumNodes = level.nodes.Size();
|
||||
mLastNumSegs = level.segs.Size();
|
||||
}
|
||||
|
|
|
@ -1,30 +1,18 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hwrenderer/dynlights/hw_aabbtree.h"
|
||||
#include "tarray.h"
|
||||
#include <memory>
|
||||
#include "hwrenderer/dynlights/hw_shadowmap.h"
|
||||
|
||||
class ADynamicLight;
|
||||
struct level_info_t;
|
||||
|
||||
class FShadowMap
|
||||
class FShadowMap : public IShadowMap
|
||||
{
|
||||
public:
|
||||
FShadowMap() { }
|
||||
~FShadowMap() { Clear(); }
|
||||
|
||||
// Release resources
|
||||
void Clear();
|
||||
void Clear() override;
|
||||
|
||||
// Update shadow map texture
|
||||
void Update();
|
||||
|
||||
// Test if a world position is in shadow relative to the specified light and returns false if it is
|
||||
bool ShadowTest(ADynamicLight *light, const DVector3 &pos);
|
||||
|
||||
// Returns true if gl_light_shadowmap is enabled and supported by the hardware
|
||||
bool IsEnabled() const;
|
||||
void Update() override;
|
||||
|
||||
private:
|
||||
// Upload the AABB-tree to the GPU
|
||||
|
@ -36,21 +24,7 @@ private:
|
|||
// OpenGL storage buffer with the list of lights in the shadow map texture
|
||||
int mLightList = 0;
|
||||
|
||||
// Working buffer for creating the list of lights. Stored here to avoid allocating memory each frame
|
||||
TArray<float> mLights;
|
||||
|
||||
// OpenGL storage buffers for the AABB tree
|
||||
int mNodesBuffer = 0;
|
||||
int mLinesBuffer = 0;
|
||||
|
||||
// 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;
|
||||
|
||||
FShadowMap(const FShadowMap &) = delete;
|
||||
FShadowMap &operator=(FShadowMap &) = delete;
|
||||
};
|
||||
|
|
|
@ -256,6 +256,7 @@ void GLSceneDrawer::CreateScene()
|
|||
gl_drawinfo->mAngles = GLRenderer->mAngles;
|
||||
gl_drawinfo->mViewVector = GLRenderer->mViewVector;
|
||||
gl_drawinfo->mViewActor = GLRenderer->mViewActor;
|
||||
gl_drawinfo->mShadowMap = &GLRenderer->mShadowMap;
|
||||
|
||||
RenderBSPNode (level.HeadNode());
|
||||
if (GLRenderer->mCurrentPortal != NULL) GLRenderer->mCurrentPortal->RenderAttached();
|
||||
|
@ -581,7 +582,6 @@ void GLSceneDrawer::DrawEndScene2D(sector_t * viewsector)
|
|||
|
||||
void GLSceneDrawer::ProcessScene(bool toscreen)
|
||||
{
|
||||
FDrawInfo::StartDrawInfo(this);
|
||||
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0;
|
||||
GLPortal::BeginScene();
|
||||
|
||||
|
@ -590,7 +590,6 @@ void GLSceneDrawer::ProcessScene(bool toscreen)
|
|||
CurrentMapSections.Zero();
|
||||
CurrentMapSections.Set(mapsection);
|
||||
DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN);
|
||||
FDrawInfo::EndDrawInfo();
|
||||
|
||||
}
|
||||
|
||||
|
@ -714,8 +713,11 @@ sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, f
|
|||
SetViewMatrix(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, false, false);
|
||||
gl_RenderState.ApplyMatrices();
|
||||
|
||||
FDrawInfo::StartDrawInfo(this);
|
||||
ProcessScene(toscreen);
|
||||
if (mainview && toscreen) EndDrawScene(lviewsector); // do not call this for camera textures.
|
||||
FDrawInfo::EndDrawInfo();
|
||||
|
||||
if (mainview && FGLRenderBuffers::IsEnabled())
|
||||
{
|
||||
GLRenderer->PostProcessScene(FixedColormap, [&]() { if (gl_bloom && FixedColormap == CM_DEFAULT) DrawEndScene2D(lviewsector); });
|
||||
|
|
|
@ -69,7 +69,7 @@ int gl_SetDynModelLight(AActor *self, int dynlightindex)
|
|||
if (gl.legacyMode)
|
||||
{
|
||||
float out[3];
|
||||
hw_GetDynSpriteLight(self, nullptr, out);
|
||||
gl_drawinfo->GetDynSpriteLight(self, nullptr, out);
|
||||
gl_RenderState.SetDynLight(out[0], out[1], out[2]);
|
||||
return -1;
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass)
|
|||
else
|
||||
{
|
||||
float out[3];
|
||||
hw_GetDynSpriteLight(gl_light_sprites ? sprite->actor : nullptr, gl_light_particles ? sprite->particle : nullptr, out);
|
||||
GetDynSpriteLight(gl_light_sprites ? sprite->actor : nullptr, gl_light_particles ? sprite->particle : nullptr, out);
|
||||
gl_RenderState.SetDynLight(out[0], out[1], out[2]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -491,7 +491,7 @@ void FDrawInfo::DrawDecal(GLDecal *gldecal)
|
|||
double x, y;
|
||||
float out[3];
|
||||
decal->GetXY(seg->sidedef, x, y);
|
||||
hw_GetDynSpriteLight(nullptr, x, y, gldecal->zcenter, wall->sub, out);
|
||||
GetDynSpriteLight(nullptr, x, y, gldecal->zcenter, wall->sub, out);
|
||||
gl_RenderState.SetDynLight(out[0], out[1], out[2]);
|
||||
}
|
||||
|
||||
|
|
|
@ -457,7 +457,7 @@ void GLSceneDrawer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep)
|
|||
else
|
||||
{
|
||||
float out[3];
|
||||
hw_GetDynSpriteLight(playermo, nullptr, out);
|
||||
gl_drawinfo->GetDynSpriteLight(playermo, nullptr, out);
|
||||
gl_RenderState.SetDynLight(out[0], out[1], out[2]);
|
||||
}
|
||||
}
|
||||
|
|
162
src/hwrenderer/dynlights/hw_shadowmap.cpp
Normal file
162
src/hwrenderer/dynlights/hw_shadowmap.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// 1D dynamic shadow maps (API independent part)
|
||||
// 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 "hwrenderer/dynlights/hw_shadowmap.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "stats.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
/*
|
||||
The 1D shadow maps are stored in a 1024x1024 texture as float depth values (R32F).
|
||||
|
||||
Each line in the texture is assigned to a single light. For example, to grab depth values for light 20
|
||||
the fragment shader (main.fp) needs to sample from row 20. That is, the V texture coordinate needs
|
||||
to be 20.5/1024.
|
||||
|
||||
The texel row for each light is split into four parts. One for each direction, like a cube texture,
|
||||
but then only in 2D where this reduces itself to a square. When main.fp samples from the shadow map
|
||||
it first decides in which direction the fragment is (relative to the light), like cubemap sampling does
|
||||
for 3D, but once again just for the 2D case.
|
||||
|
||||
Texels 0-255 is Y positive, 256-511 is X positive, 512-767 is Y negative and 768-1023 is X negative.
|
||||
|
||||
Generating the shadow map itself is done by FShadowMap::Update(). The shadow map texture's FBO is
|
||||
bound and then a screen quad is drawn to make a fragment shader cover all texels. For each fragment
|
||||
it shoots a ray and collects the distance to what it hit.
|
||||
|
||||
The shadowmap.fp shader knows which light and texel it is processing by mapping gl_FragCoord.y back
|
||||
to the light index, and it knows which direction to ray trace by looking at gl_FragCoord.x. For
|
||||
example, if gl_FragCoord.y is 20.5, then it knows its processing light 20, and if gl_FragCoord.x is
|
||||
127.5, then it knows we are shooting straight ahead for the Y positive direction.
|
||||
|
||||
Ray testing is done by uploading two GPU storage buffers - one holding AABB tree nodes, and one with
|
||||
the line segments at the leaf nodes of the tree. The fragment shader then performs a test same way
|
||||
as on the CPU, except everything uses indexes as pointers are not allowed in GLSL.
|
||||
*/
|
||||
|
||||
cycle_t IShadowMap::UpdateCycles;
|
||||
int IShadowMap::LightsProcessed;
|
||||
int IShadowMap::LightsShadowmapped;
|
||||
|
||||
ADD_STAT(shadowmap)
|
||||
{
|
||||
FString out;
|
||||
out.Format("upload=%04.2f ms lights=%d shadowmapped=%d", IShadowMap::UpdateCycles.TimeMS(), IShadowMap::LightsProcessed, IShadowMap::LightsShadowmapped);
|
||||
return out;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_shadowmap_quality, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
case 128:
|
||||
case 256:
|
||||
case 512:
|
||||
case 1024:
|
||||
break;
|
||||
default:
|
||||
self = 128;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR (Bool, gl_light_shadowmap, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
// Unset any residual shadow map indices in the light actors.
|
||||
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
||||
while (auto light = it.Next())
|
||||
{
|
||||
light->mShadowmapIndex = 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IShadowMap::ShadowTest(ADynamicLight *light, const DVector3 &pos)
|
||||
{
|
||||
if (light->shadowmapped && light->radius > 0.0 && IsEnabled() && mAABBTree)
|
||||
return mAABBTree->RayTest(light->Pos(), pos) >= 1.0f;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IShadowMap::IsEnabled() const
|
||||
{
|
||||
return gl_light_shadowmap && (screen->hwcaps & RFL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
void IShadowMap::CollectLights()
|
||||
{
|
||||
if (mLights.Size() != 1024 * 4) mLights.Resize(1024 * 4);
|
||||
int lightindex = 0;
|
||||
|
||||
// Todo: this should go through the blockmap in a spiral pattern around the player so that closer lights are preferred.
|
||||
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
||||
while (auto light = it.Next())
|
||||
{
|
||||
LightsProcessed++;
|
||||
if (light->shadowmapped && light->IsActive() && lightindex < 1024 * 4)
|
||||
{
|
||||
LightsShadowmapped++;
|
||||
|
||||
light->mShadowmapIndex = lightindex >> 2;
|
||||
|
||||
mLights[lightindex] = (float)light->X();
|
||||
mLights[lightindex+1] = (float)light->Y();
|
||||
mLights[lightindex+2] = (float)light->Z();
|
||||
mLights[lightindex+3] = light->GetRadius();
|
||||
lightindex += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
light->mShadowmapIndex = 1024;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (; lightindex < 1024 * 4; lightindex++)
|
||||
{
|
||||
mLights[lightindex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool IShadowMap::ValidateAABBTree()
|
||||
{
|
||||
// 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());
|
||||
return false;
|
||||
}
|
51
src/hwrenderer/dynlights/hw_shadowmap.h
Normal file
51
src/hwrenderer/dynlights/hw_shadowmap.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hw_aabbtree.h"
|
||||
#include "tarray.h"
|
||||
#include "stats.h"
|
||||
#include <memory>
|
||||
|
||||
class ADynamicLight;
|
||||
struct level_info_t;
|
||||
|
||||
class IShadowMap
|
||||
{
|
||||
public:
|
||||
IShadowMap() { }
|
||||
virtual ~IShadowMap() { }
|
||||
|
||||
// Release resources
|
||||
virtual void Clear() = 0;
|
||||
|
||||
// Update shadow map texture
|
||||
virtual void Update() = 0;
|
||||
|
||||
// Test if a world position is in shadow relative to the specified light and returns false if it is
|
||||
bool ShadowTest(ADynamicLight *light, const DVector3 &pos);
|
||||
|
||||
// Returns true if gl_light_shadowmap is enabled and supported by the hardware
|
||||
bool IsEnabled() const;
|
||||
|
||||
static cycle_t UpdateCycles;
|
||||
static int LightsProcessed;
|
||||
static int LightsShadowmapped;
|
||||
|
||||
protected:
|
||||
void CollectLights();
|
||||
bool ValidateAABBTree();
|
||||
|
||||
// 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;
|
||||
|
||||
IShadowMap(const IShadowMap &) = delete;
|
||||
IShadowMap &operator=(IShadowMap &) = delete;
|
||||
};
|
|
@ -10,6 +10,8 @@ class GLWall;
|
|||
class GLFlat;
|
||||
class GLSprite;
|
||||
struct GLDecal;
|
||||
class IShadowMap;
|
||||
struct particle_t;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -78,6 +80,7 @@ struct HWDrawInfo
|
|||
FRotator mAngles;
|
||||
FVector2 mViewVector;
|
||||
AActor *mViewActor;
|
||||
IShadowMap *mShadowMap;
|
||||
|
||||
TArray<MissingTextureInfo> MissingUpperTextures;
|
||||
TArray<MissingTextureInfo> MissingLowerTextures;
|
||||
|
@ -137,6 +140,10 @@ public:
|
|||
void AddOtherFloorPlane(int sector, gl_subsectorrendernode * node);
|
||||
void AddOtherCeilingPlane(int sector, gl_subsectorrendernode * node);
|
||||
|
||||
void GetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * subsec, float *out);
|
||||
void GetDynSpriteLight(AActor *thing, particle_t *particle, float *out);
|
||||
|
||||
|
||||
virtual void FloodUpperGap(seg_t * seg) = 0;
|
||||
virtual void FloodLowerGap(seg_t * seg) = 0;
|
||||
virtual void ProcessLowerMinisegs(TArray<seg_t *> &lowersegs) = 0;
|
||||
|
|
|
@ -432,8 +432,6 @@ inline float Dist2(float x1,float y1,float x2,float y2)
|
|||
}
|
||||
|
||||
bool hw_SetPlaneTextureRotation(const GLSectorPlane * secplane, FMaterial * gltexture, VSMatrix &mat);
|
||||
void hw_GetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t *subsec, float *out);
|
||||
void hw_GetDynSpriteLight(AActor *actor, particle_t *particle, float *out);
|
||||
void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata);
|
||||
|
||||
extern const float LARGE_VALUE;
|
||||
|
|
|
@ -33,13 +33,8 @@
|
|||
#include "g_levellocals.h"
|
||||
#include "actorinlines.h"
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
|
||||
#include "gl/renderer/gl_renderer.h"
|
||||
#include "gl/renderer/gl_lightdata.h"
|
||||
#include "gl/scene/gl_drawinfo.h"
|
||||
#include "gl/shaders/gl_shader.h"
|
||||
#include "gl/dynlights/gl_lightbuffer.h"
|
||||
|
||||
#include "hwrenderer/dynlights/hw_shadowmap.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
|
||||
template<class T>
|
||||
T smoothstep(const T edge0, const T edge1, const T x)
|
||||
|
@ -54,7 +49,7 @@ T smoothstep(const T edge0, const T edge1, const T x)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void hw_GetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * subsec, float *out)
|
||||
void HWDrawInfo::GetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * subsec, float *out)
|
||||
{
|
||||
ADynamicLight *light;
|
||||
float frac, lr, lg, lb;
|
||||
|
@ -109,7 +104,7 @@ void hw_GetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t *
|
|||
frac *= (float)smoothstep(light->SpotOuterAngle.Cos(), light->SpotInnerAngle.Cos(), cosDir);
|
||||
}
|
||||
|
||||
if (frac > 0 && GLRenderer->mShadowMap.ShadowTest(light, { x, y, z }))
|
||||
if (frac > 0 && (!light->shadowmapped || mShadowMap->ShadowTest(light, { x, y, z })))
|
||||
{
|
||||
lr = light->GetRed() / 255.0f;
|
||||
lg = light->GetGreen() / 255.0f;
|
||||
|
@ -133,15 +128,15 @@ void hw_GetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t *
|
|||
}
|
||||
}
|
||||
|
||||
void hw_GetDynSpriteLight(AActor *thing, particle_t *particle, float *out)
|
||||
void HWDrawInfo::GetDynSpriteLight(AActor *thing, particle_t *particle, float *out)
|
||||
{
|
||||
if (thing != NULL)
|
||||
{
|
||||
hw_GetDynSpriteLight(thing, thing->X(), thing->Y(), thing->Center(), thing->subsector, out);
|
||||
GetDynSpriteLight(thing, thing->X(), thing->Y(), thing->Center(), thing->subsector, out);
|
||||
}
|
||||
else if (particle != NULL)
|
||||
{
|
||||
hw_GetDynSpriteLight(NULL, particle->Pos.X, particle->Pos.Y, particle->Pos.Z, particle->subsector, out);
|
||||
GetDynSpriteLight(NULL, particle->Pos.X, particle->Pos.Y, particle->Pos.Z, particle->subsector, out);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue