From 850e61d1c9c7fda81da7a299cbbe087e42b8fb22 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 8 Mar 2017 00:34:08 +0100 Subject: [PATCH] Add shadow test to sprites --- src/gl/dynlights/gl_lightbsp.cpp | 143 +++++++++++++++++++++++++----- src/gl/dynlights/gl_lightbsp.h | 11 ++- src/gl/dynlights/gl_shadowmap.cpp | 7 +- src/gl/dynlights/gl_shadowmap.h | 2 + src/gl/scene/gl_spritelight.cpp | 2 +- 5 files changed, 139 insertions(+), 26 deletions(-) diff --git a/src/gl/dynlights/gl_lightbsp.cpp b/src/gl/dynlights/gl_lightbsp.cpp index c7ae65fa1..5df10f183 100644 --- a/src/gl/dynlights/gl_lightbsp.cpp +++ b/src/gl/dynlights/gl_lightbsp.cpp @@ -80,24 +80,11 @@ void FLightBSP::UploadNodes() void FLightBSP::UploadSegs() { - TArray gpulines; - gpulines.Resize(level.lines.Size()); - for (unsigned int i = 0; i < level.lines.Size(); i++) - { - const auto &line = level.lines[i]; - auto &gpuseg = gpulines[i]; - - gpuseg.x = (float)line.v1->fX(); - gpuseg.y = (float)line.v1->fY(); - gpuseg.dx = (float)line.v2->fX() - gpuseg.x; - gpuseg.dy = (float)line.v2->fY() - gpuseg.y; - } - #if 0 - if (gpulines.Size() > 0) + if (Shape->lines.Size() > 0) { FILE *file = fopen("lines.txt", "wb"); - fwrite(&gpulines[0], sizeof(GPULine) * gpulines.Size(), 1, file); + fwrite(&Shape->lines[0], sizeof(GPULine) * Shape->lines.Size(), 1, file); fclose(file); } #endif @@ -107,7 +94,7 @@ void FLightBSP::UploadSegs() glGenBuffers(1, (GLuint*)&LinesBuffer); glBindBuffer(GL_SHADER_STORAGE_BUFFER, LinesBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GPULine) * gpulines.Size(), &gpulines[0], GL_STATIC_DRAW); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GPULine) * Shape->lines.Size(), &Shape->lines[0], GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding); NumSegs = numsegs; @@ -128,11 +115,16 @@ void FLightBSP::Clear() Shape.reset(); } +bool FLightBSP::ShadowTest(const DVector3 &light, const DVector3 &pos) +{ + return Shape->RayTest(light, pos) >= 1.0f; +} + ///////////////////////////////////////////////////////////////////////////// Level2DShape::Level2DShape() { - TArray lines; + TArray line_elements; TArray centroids; for (unsigned int i = 0; i < level.lines.Size(); i++) { @@ -142,7 +134,7 @@ Level2DShape::Level2DShape() continue; } - lines.Push(i); + line_elements.Push(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() }; @@ -150,12 +142,117 @@ Level2DShape::Level2DShape() } TArray work_buffer; - work_buffer.Resize(lines.Size() * 2); + work_buffer.Resize(line_elements.Size() * 2); + root = Subdivide(&line_elements[0], (int)line_elements.Size(), ¢roids[0], &work_buffer[0]); - root = subdivide(&lines[0], (int)lines.Size(), ¢roids[0], &work_buffer[0]); + lines.Resize(level.lines.Size()); + for (unsigned int i = 0; i < level.lines.Size(); i++) + { + const auto &line = level.lines[i]; + auto &gpuseg = lines[i]; + + gpuseg.x = (float)line.v1->fX(); + gpuseg.y = (float)line.v1->fY(); + gpuseg.dx = (float)line.v2->fX() - gpuseg.x; + gpuseg.dy = (float)line.v2->fY() - gpuseg.y; + } } -int Level2DShape::subdivide(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer) +double Level2DShape::RayTest(const DVector3 &ray_start, const DVector3 &ray_end) +{ + DVector2 raydelta = ray_end - ray_start; + double raydist2 = raydelta | raydelta; + DVector2 raynormal = DVector2(raydelta.Y, -raydelta.X); + double rayd = raynormal | ray_start; + if (raydist2 < 1.0) + return 1.0f; + + double t = 1.0; + + int stack[16]; + int stack_pos = 1; + stack[0] = nodes.Size() - 1; + while (stack_pos > 0) + { + int node_index = stack[stack_pos - 1]; + + if (!OverlapRayAABB(ray_start, ray_end, nodes[node_index])) + { + stack_pos--; + } + else if (nodes[node_index].line_index != -1) // isLeaf(node_index) + { + t = MIN(IntersectRayLine(ray_start, ray_end, nodes[node_index].line_index, raydelta, rayd, raydist2), t); + stack_pos--; + } + else if (stack_pos == 16) + { + stack_pos--; // stack overflow + } + else + { + stack[stack_pos - 1] = nodes[node_index].left; + stack[stack_pos] = nodes[node_index].right; + stack_pos++; + } + } + + return t; +} + +bool Level2DShape::OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const GPUNode &node) +{ + // To do: simplify test to use a 2D test + DVector3 ray_start = DVector3(ray_start2d, 0.0); + DVector3 ray_end = DVector3(ray_end2d, 0.0); + DVector3 aabb_min = DVector3(node.aabb_left, node.aabb_top, -1.0); + DVector3 aabb_max = DVector3(node.aabb_right, node.aabb_bottom, 1.0); + + DVector3 c = (ray_start + ray_end) * 0.5f; + DVector3 w = ray_end - c; + DVector3 h = (aabb_max - aabb_min) * 0.5f; // aabb.extents(); + + c -= (aabb_max + aabb_min) * 0.5f; // aabb.center(); + + DVector3 v = DVector3(abs(w.X), abs(w.Y), abs(w.Z)); + + if (abs(c.X) > v.X + h.X || abs(c.Y) > v.Y + h.Y || abs(c.Z) > v.Z + h.Z) + return false; // disjoint; + + if (abs(c.Y * w.Z - c.Z * w.Y) > h.Y * v.Z + h.Z * v.Y || + abs(c.X * w.Z - c.Z * w.X) > h.X * v.Z + h.Z * v.X || + abs(c.X * w.Y - c.Y * w.X) > h.X * v.Y + h.Y * v.X) + return false; // disjoint; + + return true; // overlap; +} + +double Level2DShape::IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2) +{ + const double epsilon = 0.0000001; + const GPULine &line = lines[line_index]; + + DVector2 raynormal = DVector2(raydelta.Y, -raydelta.X); + + DVector2 line_pos(line.x, line.y); + DVector2 line_delta(line.dx, line.dy); + + double den = raynormal | line_delta; + if (abs(den) > epsilon) + { + double t_line = (rayd - (raynormal | line_pos)) / den; + if (t_line >= 0.0 && t_line <= 1.0) + { + DVector2 linehitdelta = line_pos + line_delta * t_line - ray_start; + double t = (raydelta | linehitdelta) / raydist2; + return t > 0.0 ? t : 1.0; + } + } + + return 1.0; +} + +int Level2DShape::Subdivide(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer) { if (num_lines == 0) return -1; @@ -255,9 +352,9 @@ int Level2DShape::subdivide(int *lines, int num_lines, const FVector2 *centroids int left_index = -1; int right_index = -1; if (left_count > 0) - left_index = subdivide(lines, left_count, centroids, work_buffer); + left_index = Subdivide(lines, left_count, centroids, work_buffer); if (right_count > 0) - right_index = subdivide(lines + left_count, right_count, centroids, work_buffer); + right_index = Subdivide(lines + left_count, right_count, centroids, work_buffer); nodes.Push(GPUNode(aabb_min, aabb_max, left_index, right_index)); return (int)nodes.Size() - 1; diff --git a/src/gl/dynlights/gl_lightbsp.h b/src/gl/dynlights/gl_lightbsp.h index c6f2b826d..137813992 100644 --- a/src/gl/dynlights/gl_lightbsp.h +++ b/src/gl/dynlights/gl_lightbsp.h @@ -1,6 +1,7 @@ #pragma once +#include "vectors.h" #include struct GPUNode @@ -28,10 +29,16 @@ public: Level2DShape(); TArray nodes; + TArray lines; int root; + double RayTest(const DVector3 &ray_start, const DVector3 &ray_end); + private: - int subdivide(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer); + bool OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const GPUNode &node); + double IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2); + + int Subdivide(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer); }; class FLightBSP @@ -44,6 +51,8 @@ public: int GetLinesBuffer(); void Clear(); + bool ShadowTest(const DVector3 &light, const DVector3 &pos); + private: void UpdateBuffers(); void GenerateBuffers(); diff --git a/src/gl/dynlights/gl_shadowmap.cpp b/src/gl/dynlights/gl_shadowmap.cpp index 384b616b2..d7215e9b4 100644 --- a/src/gl/dynlights/gl_shadowmap.cpp +++ b/src/gl/dynlights/gl_shadowmap.cpp @@ -105,4 +105,9 @@ void FShadowMap::UploadLights() glBindBuffer(GL_SHADER_STORAGE_BUFFER, mLightList); glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float) * mLights.Size(), &mLights[0], GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding); -} \ No newline at end of file +} + +bool FShadowMap::ShadowTest(ADynamicLight *light, const DVector3 &pos) +{ + return mLightBSP.ShadowTest(light->Pos(), pos); +} diff --git a/src/gl/dynlights/gl_shadowmap.h b/src/gl/dynlights/gl_shadowmap.h index cc32ec777..a8cc70129 100644 --- a/src/gl/dynlights/gl_shadowmap.h +++ b/src/gl/dynlights/gl_shadowmap.h @@ -17,6 +17,8 @@ public: int ShadowMapIndex(ADynamicLight *light) { return mLightToShadowmap[light]; } + bool ShadowTest(ADynamicLight *light, const DVector3 &pos); + private: void UploadLights(); diff --git a/src/gl/scene/gl_spritelight.cpp b/src/gl/scene/gl_spritelight.cpp index 6efa078ca..3283f0086 100644 --- a/src/gl/scene/gl_spritelight.cpp +++ b/src/gl/scene/gl_spritelight.cpp @@ -91,7 +91,7 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * frac = 1.0f - (dist / radius); - if (frac > 0) + if (frac > 0 && GLRenderer->mShadowMap.ShadowTest(light, { x, y, z })) { lr = light->GetRed() / 255.0f; lg = light->GetGreen() / 255.0f;