mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-01-18 23:21:41 +00:00
Changed the light collision structure uploaded to the GPU to be a binary tree using AABBs for the nodes instead of a BSP plane
This commit is contained in:
parent
8515f9720a
commit
6df3b3fbca
6 changed files with 294 additions and 157 deletions
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Doom BSP tree on the GPU
|
// 2D collision tree for 1D shadowmap lights
|
||||||
// Copyright(C) 2017 Magnus Norddahl
|
// Copyright(C) 2017 Magnus Norddahl
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
#include "gl/dynlights/gl_lightbsp.h"
|
#include "gl/dynlights/gl_lightbsp.h"
|
||||||
#include "gl/system/gl_interface.h"
|
#include "gl/system/gl_interface.h"
|
||||||
#include "r_state.h"
|
#include "r_state.h"
|
||||||
|
#include "g_levellocals.h"
|
||||||
|
|
||||||
int FLightBSP::GetNodesBuffer()
|
int FLightBSP::GetNodesBuffer()
|
||||||
{
|
{
|
||||||
|
@ -32,10 +33,10 @@ int FLightBSP::GetNodesBuffer()
|
||||||
return NodesBuffer;
|
return NodesBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FLightBSP::GetSegsBuffer()
|
int FLightBSP::GetLinesBuffer()
|
||||||
{
|
{
|
||||||
UpdateBuffers();
|
UpdateBuffers();
|
||||||
return SegsBuffer;
|
return LinesBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FLightBSP::UpdateBuffers()
|
void FLightBSP::UpdateBuffers()
|
||||||
|
@ -49,55 +50,19 @@ void FLightBSP::UpdateBuffers()
|
||||||
|
|
||||||
void FLightBSP::GenerateBuffers()
|
void FLightBSP::GenerateBuffers()
|
||||||
{
|
{
|
||||||
|
if (!Shape)
|
||||||
|
Shape.reset(new Level2DShape());
|
||||||
UploadNodes();
|
UploadNodes();
|
||||||
UploadSegs();
|
UploadSegs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FLightBSP::UploadNodes()
|
void FLightBSP::UploadNodes()
|
||||||
{
|
{
|
||||||
TArray<GPUNode> gpunodes;
|
|
||||||
gpunodes.Resize(numnodes);
|
|
||||||
for (int i = 0; i < numnodes; i++)
|
|
||||||
{
|
|
||||||
const auto &node = nodes[i];
|
|
||||||
auto &gpunode = gpunodes[i];
|
|
||||||
|
|
||||||
float a = -FIXED2FLOAT(node.dy);
|
|
||||||
float b = FIXED2FLOAT(node.dx);
|
|
||||||
float c = 0.0f;
|
|
||||||
float d = -(a * FIXED2FLOAT(node.x) + b * FIXED2FLOAT(node.y));
|
|
||||||
|
|
||||||
gpunode.plane[0] = a;
|
|
||||||
gpunode.plane[1] = b;
|
|
||||||
gpunode.plane[2] = c;
|
|
||||||
gpunode.plane[3] = d;
|
|
||||||
|
|
||||||
for (int j = 0; j < 2; j++)
|
|
||||||
{
|
|
||||||
bool isNode = (!((size_t)node.children[j] & 1));
|
|
||||||
if (isNode)
|
|
||||||
{
|
|
||||||
node_t *bsp = (node_t *)node.children[j];
|
|
||||||
gpunode.children[j] = (int)(ptrdiff_t)(bsp - nodes);
|
|
||||||
gpunode.linecount[j] = -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
subsector_t *sub = (subsector_t *)((BYTE *)node.children[j] - 1);
|
|
||||||
if (sub->numlines > 0)
|
|
||||||
gpunode.children[j] = (int)(ptrdiff_t)(sub->firstline - segs);
|
|
||||||
else
|
|
||||||
gpunode.children[j] = 0;
|
|
||||||
gpunode.linecount[j] = sub->numlines;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (gpunodes.Size() > 0)
|
if (Shape->nodes.Size() > 0)
|
||||||
{
|
{
|
||||||
FILE *file = fopen("nodes.txt", "wb");
|
FILE *file = fopen("nodes.txt", "wb");
|
||||||
fwrite(&gpunodes[0], sizeof(GPUNode) * gpunodes.Size(), 1, file);
|
fwrite(&Shape->nodes[0], sizeof(GPUNode) * Shape->nodes.Size(), 1, file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -107,7 +72,7 @@ void FLightBSP::UploadNodes()
|
||||||
|
|
||||||
glGenBuffers(1, (GLuint*)&NodesBuffer);
|
glGenBuffers(1, (GLuint*)&NodesBuffer);
|
||||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, NodesBuffer);
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, NodesBuffer);
|
||||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GPUNode) * gpunodes.Size(), &gpunodes[0], GL_STATIC_DRAW);
|
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GPUNode) * Shape->nodes.Size(), &Shape->nodes[0], GL_STATIC_DRAW);
|
||||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding);
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding);
|
||||||
|
|
||||||
NumNodes = numnodes;
|
NumNodes = numnodes;
|
||||||
|
@ -115,28 +80,24 @@ void FLightBSP::UploadNodes()
|
||||||
|
|
||||||
void FLightBSP::UploadSegs()
|
void FLightBSP::UploadSegs()
|
||||||
{
|
{
|
||||||
TArray<GPUSeg> gpusegs;
|
TArray<GPULine> gpulines;
|
||||||
gpusegs.Resize(numsegs);
|
gpulines.Resize(level.lines.Size());
|
||||||
for (int i = 0; i < numsegs; i++)
|
for (unsigned int i = 0; i < level.lines.Size(); i++)
|
||||||
{
|
{
|
||||||
const auto &seg = segs[i];
|
const auto &line = level.lines[i];
|
||||||
auto &gpuseg = gpusegs[i];
|
auto &gpuseg = gpulines[i];
|
||||||
|
|
||||||
gpuseg.x = (float)seg.v1->fX();
|
gpuseg.x = (float)line.v1->fX();
|
||||||
gpuseg.y = (float)seg.v1->fY();
|
gpuseg.y = (float)line.v1->fY();
|
||||||
gpuseg.dx = (float)seg.v2->fX() - gpuseg.x;
|
gpuseg.dx = (float)line.v2->fX() - gpuseg.x;
|
||||||
gpuseg.dy = (float)seg.v2->fY() - gpuseg.y;
|
gpuseg.dy = (float)line.v2->fY() - gpuseg.y;
|
||||||
gpuseg.bSolid = (seg.backsector == nullptr) ? 1.0f : 0.0f;
|
|
||||||
gpuseg.padding1 = 0.0f;
|
|
||||||
gpuseg.padding2 = 0.0f;
|
|
||||||
gpuseg.padding3 = 0.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (gpusegs.Size() > 0)
|
if (gpulines.Size() > 0)
|
||||||
{
|
{
|
||||||
FILE *file = fopen("segs.txt", "wb");
|
FILE *file = fopen("lines.txt", "wb");
|
||||||
fwrite(&gpusegs[0], sizeof(GPUSeg) * gpusegs.Size(), 1, file);
|
fwrite(&gpulines[0], sizeof(GPULine) * gpulines.Size(), 1, file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -144,9 +105,9 @@ void FLightBSP::UploadSegs()
|
||||||
int oldBinding = 0;
|
int oldBinding = 0;
|
||||||
glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &oldBinding);
|
glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &oldBinding);
|
||||||
|
|
||||||
glGenBuffers(1, (GLuint*)&SegsBuffer);
|
glGenBuffers(1, (GLuint*)&LinesBuffer);
|
||||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SegsBuffer);
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, LinesBuffer);
|
||||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GPUSeg) * gpusegs.Size(), &gpusegs[0], GL_STATIC_DRAW);
|
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GPULine) * gpulines.Size(), &gpulines[0], GL_STATIC_DRAW);
|
||||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding);
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding);
|
||||||
|
|
||||||
NumSegs = numsegs;
|
NumSegs = numsegs;
|
||||||
|
@ -159,9 +120,145 @@ void FLightBSP::Clear()
|
||||||
glDeleteBuffers(1, (GLuint*)&NodesBuffer);
|
glDeleteBuffers(1, (GLuint*)&NodesBuffer);
|
||||||
NodesBuffer = 0;
|
NodesBuffer = 0;
|
||||||
}
|
}
|
||||||
if (SegsBuffer != 0)
|
if (LinesBuffer != 0)
|
||||||
{
|
{
|
||||||
glDeleteBuffers(1, (GLuint*)&SegsBuffer);
|
glDeleteBuffers(1, (GLuint*)&LinesBuffer);
|
||||||
SegsBuffer = 0;
|
LinesBuffer = 0;
|
||||||
}
|
}
|
||||||
|
Shape.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Level2DShape::Level2DShape()
|
||||||
|
{
|
||||||
|
TArray<int> lines;
|
||||||
|
TArray<FVector2> centroids;
|
||||||
|
for (unsigned int i = 0; i < level.lines.Size(); i++)
|
||||||
|
{
|
||||||
|
if (level.lines[i].backsector)
|
||||||
|
{
|
||||||
|
centroids.Push(FVector2(0.0f, 0.0f));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.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() };
|
||||||
|
centroids.Push((v1 + v2) * 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<int> work_buffer;
|
||||||
|
work_buffer.Resize(lines.Size() * 2);
|
||||||
|
|
||||||
|
root = subdivide(&lines[0], (int)lines.Size(), ¢roids[0], &work_buffer[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Level2DShape::subdivide(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;
|
||||||
|
aabb_min.X = (float)level.lines[lines[0]].v1->fX();
|
||||||
|
aabb_min.Y = (float)level.lines[lines[0]].v1->fY();
|
||||||
|
aabb_max = aabb_min;
|
||||||
|
for (int i = 0; i < num_lines; i++)
|
||||||
|
{
|
||||||
|
float x1 = (float)level.lines[lines[i]].v1->fX();
|
||||||
|
float y1 = (float)level.lines[lines[i]].v1->fY();
|
||||||
|
float x2 = (float)level.lines[lines[i]].v2->fX();
|
||||||
|
float y2 = (float)level.lines[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[lines[i]];
|
||||||
|
}
|
||||||
|
median /= (float)num_lines;
|
||||||
|
|
||||||
|
if (num_lines == 1) // Leaf node
|
||||||
|
{
|
||||||
|
nodes.Push(GPUNode(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 split at longest axis, then if that fails the next longest, and then the remaining one
|
||||||
|
int left_count, right_count;
|
||||||
|
FVector2 axis;
|
||||||
|
for (int attempt = 0; attempt < 2; attempt++)
|
||||||
|
{
|
||||||
|
// Find the split plane for axis
|
||||||
|
FVector2 axis = axis_plane[axis_order[attempt]];
|
||||||
|
FVector3 plane(axis, -(median | axis));
|
||||||
|
|
||||||
|
// Split lines into two
|
||||||
|
left_count = 0;
|
||||||
|
right_count = 0;
|
||||||
|
for (int i = 0; i < num_lines; i++)
|
||||||
|
{
|
||||||
|
int line_index = lines[i];
|
||||||
|
|
||||||
|
float side = FVector3(centroids[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 splitting and do a random split 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 = subdivide(lines, left_count, centroids, work_buffer);
|
||||||
|
if (right_count > 0)
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,37 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
struct GPUNode
|
struct GPUNode
|
||||||
{
|
{
|
||||||
float plane[4];
|
GPUNode(const FVector2 &aabb_min, const FVector2 &aabb_max, int line_index) : aabb_left(aabb_min.X), aabb_top(aabb_min.Y), aabb_right(aabb_max.X), aabb_bottom(aabb_max.Y), left(-1), right(-1), line_index(line_index) { }
|
||||||
int children[2];
|
GPUNode(const FVector2 &aabb_min, const FVector2 &aabb_max, int left, int right) : aabb_left(aabb_min.X), aabb_top(aabb_min.Y), aabb_right(aabb_max.X), aabb_bottom(aabb_max.Y), left(left), right(right), line_index(-1) { }
|
||||||
int linecount[2];
|
|
||||||
|
float aabb_left, aabb_top;
|
||||||
|
float aabb_right, aabb_bottom;
|
||||||
|
int left;
|
||||||
|
int right;
|
||||||
|
int line_index;
|
||||||
|
int padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GPUSeg
|
struct GPULine
|
||||||
{
|
{
|
||||||
float x, y;
|
float x, y;
|
||||||
float dx, dy;
|
float dx, dy;
|
||||||
float bSolid;
|
};
|
||||||
float padding1, padding2, padding3;
|
|
||||||
|
class Level2DShape
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Level2DShape();
|
||||||
|
|
||||||
|
TArray<GPUNode> nodes;
|
||||||
|
int root;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int subdivide(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FLightBSP
|
class FLightBSP
|
||||||
|
@ -23,7 +41,7 @@ public:
|
||||||
~FLightBSP() { Clear(); }
|
~FLightBSP() { Clear(); }
|
||||||
|
|
||||||
int GetNodesBuffer();
|
int GetNodesBuffer();
|
||||||
int GetSegsBuffer();
|
int GetLinesBuffer();
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -36,7 +54,9 @@ private:
|
||||||
FLightBSP &operator=(FLightBSP &) = delete;
|
FLightBSP &operator=(FLightBSP &) = delete;
|
||||||
|
|
||||||
int NodesBuffer = 0;
|
int NodesBuffer = 0;
|
||||||
int SegsBuffer = 0;
|
int LinesBuffer = 0;
|
||||||
int NumNodes = 0;
|
int NumNodes = 0;
|
||||||
int NumSegs = 0;
|
int NumSegs = 0;
|
||||||
|
|
||||||
|
std::unique_ptr<Level2DShape> Shape;
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,7 +55,7 @@ void FShadowMap::Update()
|
||||||
GLRenderer->mShadowMapShader->Bind();
|
GLRenderer->mShadowMapShader->Bind();
|
||||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, mLightList);
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, mLightList);
|
||||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, mLightBSP.GetNodesBuffer());
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, mLightBSP.GetNodesBuffer());
|
||||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, mLightBSP.GetSegsBuffer());
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, mLightBSP.GetLinesBuffer());
|
||||||
|
|
||||||
glViewport(0, 0, 1024, 1024);
|
glViewport(0, 0, 1024, 1024);
|
||||||
GLRenderer->RenderScreenQuad();
|
GLRenderer->RenderScreenQuad();
|
||||||
|
|
|
@ -772,6 +772,11 @@ void FGLRenderBuffers::CreateShadowMap()
|
||||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding);
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding);
|
||||||
|
|
||||||
mShadowMapTexture = Create2DTexture("ShadowMap", GL_R32F, 1024, 1024);
|
mShadowMapTexture = Create2DTexture("ShadowMap", GL_R32F, 1024, 1024);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
mShadowMapFB = CreateFrameBuffer("ShadowMapFB", mShadowMapTexture);
|
mShadowMapFB = CreateFrameBuffer("ShadowMapFB", mShadowMapTexture);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureBinding);
|
glBindTexture(GL_TEXTURE_2D, textureBinding);
|
||||||
|
|
|
@ -158,7 +158,7 @@ float shadowmapAttenuation(vec4 lightpos, float shadowIndex)
|
||||||
else
|
else
|
||||||
u = dir.x / dir.y * 0.125 + (0.50 + 0.125);
|
u = dir.x / dir.y * 0.125 + (0.50 + 0.125);
|
||||||
}
|
}
|
||||||
dir -= sign(dir); // margin, to remove wall acne
|
dir -= sign(dir) * 5.0; // margin, to remove wall acne
|
||||||
float dist2 = dot(dir, dir);
|
float dist2 = dot(dir, dir);
|
||||||
return texture(ShadowMap, vec2(u, v)).x > dist2 ? 1.0 : 0.0;
|
return texture(ShadowMap, vec2(u, v)).x > dist2 ? 1.0 : 0.0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,26 +4,28 @@ out vec4 FragColor;
|
||||||
|
|
||||||
struct GPUNode
|
struct GPUNode
|
||||||
{
|
{
|
||||||
vec4 plane;
|
vec2 aabb_min;
|
||||||
int children[2];
|
vec2 aabb_max;
|
||||||
int linecount[2];
|
int left;
|
||||||
|
int right;
|
||||||
|
int line_index;
|
||||||
|
int padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GPUSeg
|
struct GPULine
|
||||||
{
|
{
|
||||||
vec2 pos;
|
vec2 pos;
|
||||||
vec2 delta;
|
vec2 delta;
|
||||||
vec4 bSolid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(std430, binding = 2) buffer LightNodes
|
layout(std430, binding = 2) buffer LightNodes
|
||||||
{
|
{
|
||||||
GPUNode bspNodes[];
|
GPUNode nodes[];
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(std430, binding = 3) buffer LightSegs
|
layout(std430, binding = 3) buffer LightLines
|
||||||
{
|
{
|
||||||
GPUSeg bspSegs[];
|
GPULine lines[];
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(std430, binding = 4) buffer LightList
|
layout(std430, binding = 4) buffer LightList
|
||||||
|
@ -31,87 +33,100 @@ layout(std430, binding = 4) buffer LightList
|
||||||
vec4 lights[];
|
vec4 lights[];
|
||||||
};
|
};
|
||||||
|
|
||||||
//===========================================================================
|
bool overlapRayAABB(vec2 ray_start2d, vec2 ray_end2d, vec2 aabb_min2d, vec2 aabb_max2d)
|
||||||
//
|
{
|
||||||
// Ray/BSP collision test. Returns where the ray hit something.
|
// To do: simplify test to use a 2D test
|
||||||
//
|
vec3 ray_start = vec3(ray_start2d, 0.0);
|
||||||
//===========================================================================
|
vec3 ray_end = vec3(ray_end2d, 0.0);
|
||||||
|
vec3 aabb_min = vec3(aabb_min2d, -1.0);
|
||||||
vec2 rayTest(vec2 from, vec2 to)
|
vec3 aabb_max = vec3(aabb_max2d, 1.0);
|
||||||
|
|
||||||
|
vec3 c = (ray_start + ray_end) * 0.5f;
|
||||||
|
vec3 w = ray_end - c;
|
||||||
|
vec3 h = (aabb_max - aabb_min) * 0.5f; // aabb.extents();
|
||||||
|
|
||||||
|
c -= (aabb_max + aabb_min) * 0.5f; // aabb.center();
|
||||||
|
|
||||||
|
vec3 v = abs(w);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
float intersectRayLine(vec2 ray_start, vec2 ray_end, int line_index, vec2 raydelta, float rayd, float raydist2)
|
||||||
{
|
{
|
||||||
const int max_iterations = 50;
|
|
||||||
const float epsilon = 0.0000001;
|
const float epsilon = 0.0000001;
|
||||||
|
GPULine line = lines[line_index];
|
||||||
|
|
||||||
// Avoid wall acne by adding some margin
|
|
||||||
vec2 margin = normalize(to - from);
|
|
||||||
|
|
||||||
vec2 raydelta = to - from;
|
|
||||||
float raydist2 = dot(raydelta, raydelta);
|
|
||||||
vec2 raynormal = vec2(raydelta.y, -raydelta.x);
|
vec2 raynormal = vec2(raydelta.y, -raydelta.x);
|
||||||
float rayd = dot(raynormal, from);
|
|
||||||
|
|
||||||
if (raydist2 < 1.0 || bspNodes.length() == 0)
|
float den = dot(raynormal, line.delta);
|
||||||
return to;
|
if (abs(den) > epsilon)
|
||||||
|
|
||||||
int nodeIndex = bspNodes.length() - 1;
|
|
||||||
|
|
||||||
for (int iteration = 0; iteration < max_iterations; iteration++)
|
|
||||||
{
|
{
|
||||||
GPUNode node = bspNodes[nodeIndex];
|
float t_line = (rayd - dot(raynormal, line.pos)) / den;
|
||||||
int side = (dot(node.plane, vec4(from, 0.0, 1.0)) > 0.0) ? 1 : 0;
|
if (t_line >= 0.0 && t_line <= 1.0)
|
||||||
int linecount = node.linecount[side];
|
|
||||||
if (linecount < 0)
|
|
||||||
{
|
{
|
||||||
nodeIndex = node.children[side];
|
vec2 linehitdelta = line.pos + line.delta * t_line - ray_start;
|
||||||
}
|
float t = dot(raydelta, linehitdelta) / raydist2;
|
||||||
else
|
return t > 0.0 ? t : 1.0;
|
||||||
{
|
|
||||||
int startLineIndex = node.children[side];
|
|
||||||
|
|
||||||
// Ray/line test each line segment.
|
|
||||||
bool hit_line = false;
|
|
||||||
for (int i = 0; i < linecount; i++)
|
|
||||||
{
|
|
||||||
GPUSeg seg = bspSegs[startLineIndex + i];
|
|
||||||
|
|
||||||
float den = dot(raynormal, seg.delta);
|
|
||||||
if (abs(den) > epsilon)
|
|
||||||
{
|
|
||||||
float t_seg = (rayd - dot(raynormal, seg.pos)) / den;
|
|
||||||
if (t_seg >= 0.0 && t_seg <= 1.0)
|
|
||||||
{
|
|
||||||
vec2 seghitdelta = seg.pos + seg.delta * t_seg - from;
|
|
||||||
if (dot(raydelta, seghitdelta) > 0.0 && dot(seghitdelta, seghitdelta) < raydist2) // We hit a line segment.
|
|
||||||
{
|
|
||||||
if (seg.bSolid.x > 0.0) // segment line is one-sided
|
|
||||||
return from + seghitdelta;
|
|
||||||
|
|
||||||
// We hit a two-sided segment line. Move to the other side and continue ray tracing.
|
|
||||||
from = from + seghitdelta + margin;
|
|
||||||
|
|
||||||
raydelta = to - from;
|
|
||||||
raydist2 = dot(raydelta, raydelta);
|
|
||||||
raynormal = vec2(raydelta.y, -raydelta.x);
|
|
||||||
rayd = dot(raynormal, from);
|
|
||||||
|
|
||||||
if (raydist2 < 1.0 || bspNodes.length() == 0)
|
|
||||||
return to;
|
|
||||||
|
|
||||||
nodeIndex = bspNodes.length() - 1;
|
|
||||||
|
|
||||||
hit_line = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hit_line)
|
|
||||||
return to;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return to;
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLeaf(int node_index)
|
||||||
|
{
|
||||||
|
return nodes[node_index].line_index != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float rayTest(vec2 ray_start, vec2 ray_end)
|
||||||
|
{
|
||||||
|
vec2 raydelta = ray_end - ray_start;
|
||||||
|
float raydist2 = dot(raydelta, raydelta);
|
||||||
|
vec2 raynormal = vec2(raydelta.y, -raydelta.x);
|
||||||
|
float rayd = dot(raynormal, ray_start);
|
||||||
|
if (raydist2 < 1.0)
|
||||||
|
return 1.0;
|
||||||
|
|
||||||
|
float t = 1.0;
|
||||||
|
|
||||||
|
int stack[16];
|
||||||
|
int stack_pos = 1;
|
||||||
|
stack[0] = nodes.length() - 1;
|
||||||
|
while (stack_pos > 0)
|
||||||
|
{
|
||||||
|
int node_index = stack[stack_pos - 1];
|
||||||
|
|
||||||
|
if (!overlapRayAABB(ray_start, ray_end, nodes[node_index].aabb_min, nodes[node_index].aabb_max))
|
||||||
|
{
|
||||||
|
stack_pos--;
|
||||||
|
}
|
||||||
|
else if (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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
|
@ -134,8 +149,8 @@ void main()
|
||||||
}
|
}
|
||||||
pixelpos = lightpos + pixelpos * radius;
|
pixelpos = lightpos + pixelpos * radius;
|
||||||
|
|
||||||
vec2 hitpos = rayTest(lightpos, pixelpos);
|
float t = rayTest(lightpos, pixelpos);
|
||||||
vec2 delta = hitpos - lightpos;
|
vec2 delta = (pixelpos - lightpos) * t;
|
||||||
float dist2 = dot(delta, delta);
|
float dist2 = dot(delta, delta);
|
||||||
|
|
||||||
FragColor = vec4(dist2, 0.0, 0.0, 1.0);
|
FragColor = vec4(dist2, 0.0, 0.0, 1.0);
|
||||||
|
|
Loading…
Reference in a new issue