Implement basic linedef sunlight portal support

This commit is contained in:
RaveYard 2022-10-25 17:42:20 +02:00
parent 1427ca5f35
commit 38916e7f75
7 changed files with 254 additions and 42 deletions

View file

@ -445,6 +445,7 @@ struct FLevel
FloatVertex GetSegVertex(int index);
int FindFirstSectorFromTag(int tag);
unsigned FindFirstLineId(int lineId);
inline IntSector* PointInSector(const dvec2& pos) { return GetSectorFromSubSector(PointInSubSector(int(pos.x), int(pos.y))); }
private:

View file

@ -551,6 +551,19 @@ int FLevel::FindFirstSectorFromTag(int tag)
return -1;
}
unsigned FLevel::FindFirstLineId(int lineId)
{
for (unsigned i = 0; i < Lines.Size(); ++i)
{
for (const auto& e : Lines[i].ids)
{
if (e == lineId)
return i;
}
}
return -1;
}
void FProcessor::GetPolySpots ()
{
if (Extended && CheckPolyobjs)

View file

@ -39,7 +39,13 @@ struct SurfaceInfo
vec3 Normal;
float Sky;
float SamplingDistance;
float Padding1, Padding2, Padding3;
uint PortalIndex;
float Padding1, Padding2;
};
struct PortalInfo
{
mat4 Transformation;
};
struct LightInfo
@ -59,6 +65,7 @@ struct LightInfo
layout(set = 0, binding = 1) buffer SurfaceIndexBuffer { uint surfaceIndices[]; };
layout(set = 0, binding = 2) buffer SurfaceBuffer { SurfaceInfo surfaces[]; };
layout(set = 0, binding = 3) buffer LightBuffer { LightInfo lights[]; };
layout(set = 0, binding = 4) buffer PortalBuffer { PortalInfo portals[]; };
layout(push_constant) uniform PushConstants
{
@ -209,38 +216,7 @@ float RadicalInverse_VdC(uint bits)
#if defined(USE_RAYQUERY)
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax);
while(rayQueryProceedEXT(rayQuery)) { }
return rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT;
}
int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax)
{
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax);
while(rayQueryProceedEXT(rayQuery))
{
if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCommittedIntersectionTriangleEXT)
{
rayQueryConfirmIntersectionEXT(rayQuery);
}
}
if (rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT)
{
return rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true);
}
else
{
return -1;
}
}
int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t)
int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float t)
{
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax);
@ -264,6 +240,16 @@ int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out fl
}
}
/*
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax);
while(rayQueryProceedEXT(rayQuery)) { }
return rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT;
}
*/
#else
struct RayBBox
@ -376,6 +362,7 @@ bool is_leaf(int node_index)
return nodes[node_index].element_index != -1;
}
/*
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
if (tmax <= 0.0f)
@ -410,6 +397,7 @@ bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
} while (stackIndex > 0);
return false;
}
*/
struct TraceHit
{
@ -457,13 +445,7 @@ TraceHit find_first_hit(RayBBox ray)
return hit;
}
int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax)
{
float t;
return TraceFirstHitTriangleT(origin, tmin, dir, tmax, t);
}
int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float hitFraction)
int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float tparam)
{
// Perform segmented tracing to keep the ray AABB box smaller
vec3 ray_start = origin;
@ -480,14 +462,54 @@ int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out fl
TraceHit hit = find_first_hit(ray);
if (hit.fraction < 1.0)
{
hit.fraction = segstart * (1.0 - hit.fraction) + segend * hit.fraction;
tparam = hit.fraction = segstart * (1.0 - hit.fraction) + segend * hit.fraction;
return hit.triangle;
}
}
return -1;
}
#endif
int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t)
{
int primitiveID;
while(true)
{
primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t);
if(primitiveID < 0)
{
break;
}
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
if(surface.PortalIndex == 0)
{
break;
}
// Portal was hit: Apply transformation onto the ray
mat4 transformationMatrix = portals[surface.PortalIndex].Transformation;
origin = (transformationMatrix * vec4(origin + dir * t, 1.0)).xyz;
dir = (transformationMatrix * vec4(dir, 0.0)).xyz;
tmax -= t;
}
return primitiveID;
}
int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax)
{
float t;
return TraceFirstHitTriangleT(origin, tmin, dir, tmax, t);
}
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
return TraceFirstHitTriangle(origin, tmin, dir, tmax) >= 0;
}
)glsl";

View file

@ -489,6 +489,7 @@ void GPURaytracer::FinishCommands()
void GPURaytracer::CreateVertexAndIndexBuffers()
{
std::vector<SurfaceInfo> surfaces = CreateSurfaceInfo();
std::vector<PortalInfo> portals = CreatePortalInfo();
std::vector<CollisionNode> nodes = CreateCollisionNodes();
// std430 alignment rules forces us to convert the vec3 to a vec4
@ -536,6 +537,12 @@ void GPURaytracer::CreateVertexAndIndexBuffers()
.DebugName("surfaceBuffer")
.Create(device.get());
portalBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(portals.size() * sizeof(PortalInfo))
.DebugName("portalBuffer")
.Create(device.get());
nodesBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(sizeof(CollisionNodeBufferHeader) + nodes.size() * sizeof(CollisionNode))
@ -547,6 +554,7 @@ void GPURaytracer::CreateVertexAndIndexBuffers()
.AddBuffer(indexBuffer.get(), mesh->MeshElements.Data(), (size_t)mesh->MeshElements.Size() * sizeof(uint32_t))
.AddBuffer(surfaceIndexBuffer.get(), mesh->MeshSurfaces.Data(), (size_t)mesh->MeshSurfaces.Size() * sizeof(uint32_t))
.AddBuffer(surfaceBuffer.get(), surfaces.data(), surfaces.size() * sizeof(SurfaceInfo))
.AddBuffer(portalBuffer.get(), portals.data(), portals.size() * sizeof(PortalInfo))
.AddBuffer(nodesBuffer.get(), &nodesHeader, sizeof(CollisionNodeBufferHeader), nodes.data(), nodes.size() * sizeof(CollisionNode))
.Execute(device.get(), cmdbuffer.get());
@ -728,6 +736,7 @@ void GPURaytracer::CreateRaytracePipeline()
.AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
.DebugName("raytrace.descriptorSetLayout0")
.Create(device.get());
@ -838,6 +847,7 @@ void GPURaytracer::CreateRaytracePipeline()
.AddBuffer(raytrace.descriptorSet0.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceIndexBuffer.get())
.AddBuffer(raytrace.descriptorSet0.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceBuffer.get())
.AddBuffer(raytrace.descriptorSet0.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, sceneLightBuffer.get())
.AddBuffer(raytrace.descriptorSet0.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, portalBuffer.get())
.Execute(device.get());
}
@ -977,6 +987,16 @@ std::vector<SurfaceInfo> GPURaytracer::CreateSurfaceInfo()
info.Sky = surface->bSky ? 1.0f : 0.0f;
info.Normal = surface->plane.Normal();
info.SamplingDistance = float(surface->sampleDimension);
if (surface->portalIndex >= 0)
{
info.PortalIndex = surface->portalIndex + 1; // Index 0 is already occupied. See GPURaytracer::CreatePortalInfo()
}
else
{
info.PortalIndex = 0;
}
surfaces.push_back(info);
}
if (surfaces.empty()) // vulkan doesn't support zero byte buffers
@ -984,6 +1004,28 @@ std::vector<SurfaceInfo> GPURaytracer::CreateSurfaceInfo()
return surfaces;
}
std::vector<PortalInfo> GPURaytracer::CreatePortalInfo()
{
std::vector<PortalInfo> portals;
{
PortalInfo noPortal;
noPortal.Transformation = mat4::identity(); // index 0 will always contain identity matrix (for convenience)
portals.push_back(noPortal);
}
for (const auto& surface : mesh->surfaces)
{
if (surface->portalDestinationIndex >= 0)
{
PortalInfo info;
info.Transformation = mesh->portals[surface->portalIndex]->transformation;
portals.push_back(info);
}
}
return portals;
}
std::vector<CollisionNode> GPURaytracer::CreateCollisionNodes()
{
std::vector<CollisionNode> nodes;

View file

@ -33,9 +33,19 @@ struct SurfaceInfo
vec3 Normal;
float Sky;
float SamplingDistance;
float Padding1, Padding2, Padding3;
uint32_t PortalIndex;
float Padding1, Padding2;
};
static_assert(sizeof(SurfaceInfo) == sizeof(float) * 8);
struct PortalInfo
{
mat4 Transformation;
};
static_assert(sizeof(PortalInfo) == sizeof(float) * 16);
struct LightInfo
{
vec3 Origin;
@ -128,6 +138,7 @@ private:
void PrintVulkanInfo();
std::vector<SurfaceInfo> CreateSurfaceInfo();
std::vector<PortalInfo> CreatePortalInfo();
std::vector<CollisionNode> CreateCollisionNodes();
static vec2 ToUV(const vec3& vert, const Surface* targetSurface);
@ -158,6 +169,7 @@ private:
std::unique_ptr<VulkanBuffer> transferBuffer;
std::unique_ptr<VulkanBuffer> surfaceIndexBuffer;
std::unique_ptr<VulkanBuffer> surfaceBuffer;
std::unique_ptr<VulkanBuffer> portalBuffer;
std::unique_ptr<VulkanBuffer> nodesBuffer;
std::unique_ptr<VulkanBuffer> blScratchBuffer;

View file

@ -480,6 +480,117 @@ void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side)
vec2 dx(v2.x - v1.x, v2.y - v1.y);
float distance = length(dx);
// line portal
if (side->line->special == Line_SetPortal && side->line->frontsector == front)
{
const unsigned destLineIndex = doomMap.FindFirstLineId(side->line->args[0]);
if (destLineIndex < doomMap.Lines.Size())
{
float texWidth = 128.0f;
float texHeight = 128.0f;
auto surf = std::make_unique<Surface>();
surf->material = side->midtexture;
surf->verts.resize(4);
surf->bSky = false;
surf->verts[0].x = surf->verts[2].x = v1.x;
surf->verts[0].y = surf->verts[2].y = v1.y;
surf->verts[1].x = surf->verts[3].x = v2.x;
surf->verts[1].y = surf->verts[3].y = v2.y;
surf->verts[0].z = v1Bottom;
surf->verts[1].z = v2Bottom;
surf->verts[2].z = v1Top;
surf->verts[3].z = v2Top;
surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]);
surf->plane.SetDistance(surf->verts[0]);
surf->type = ST_MIDDLESIDE;
surf->typeIndex = typeIndex;
surf->controlSector = nullptr;
surf->sampleDimension = (surf->sampleDimension = side->GetSampleDistanceMiddle()) ? surf->sampleDimension : defaultSamples;
float texZ = surf->verts[0].z;
surf->texUV.resize(4);
surf->texUV[0].x = 0.0f;
surf->texUV[1].x = distance / texWidth;
surf->texUV[2].x = 0.0f;
surf->texUV[3].x = distance / texWidth;
surf->texUV[0].y = (surf->verts[0].z - texZ) / texHeight;
surf->texUV[1].y = (surf->verts[1].z - texZ) / texHeight;
surf->texUV[2].y = (surf->verts[2].z - texZ) / texHeight;
surf->texUV[3].y = (surf->verts[3].z - texZ) / texHeight;
surf->portalDestinationIndex = destLineIndex;
// Portal calculation
{
auto portal = std::make_unique<Portal>();
// Calculate portal transformation
{
auto& destLine = doomMap.Lines[destLineIndex];
FloatVertex destV1 = doomMap.GetSegVertex(destLine.v1);
FloatVertex destV2 = doomMap.GetSegVertex(destLine.v2);
int alignment = side->line->args[3];
double dstAZ = 0;
double dstBZ = 0;
double srcAZ = 0;
double srcBZ = 0;
const auto* dstFront = doomMap.GetFrontSector(&doomMap.Sides[destLine.sidenum[0]]);
if (alignment == 1) // floor
{
srcAZ = front->floorplane.zAt(v1.x, v1.y);
srcBZ = front->floorplane.zAt(v2.x, v2.y);
dstAZ = dstFront->floorplane.zAt(destV1.x, destV1.y);
dstBZ = dstFront->floorplane.zAt(destV2.x, destV2.y);
}
else if (alignment == 2) // ceiling
{
srcAZ = front->ceilingplane.zAt(v1.x, v1.y);
srcBZ = front->ceilingplane.zAt(v2.x, v2.y);
dstAZ = dstFront->ceilingplane.zAt(destV1.x, destV1.y);
dstBZ = dstFront->ceilingplane.zAt(destV2.x, destV2.y);
}
const vec3 vecSrcA = vec3(vec2(v1.x, v1.y), srcAZ);
const vec3 vecSrcB = vec3(vec2(v2.x, v2.y), srcAZ);
const vec3 vecDstA = vec3(vec2(destV1.x, destV1.y), dstAZ);
const vec3 vecDstB = vec3(vec2(destV2.x, destV2.y), dstBZ);
// Translation
vec3 originSrc = (vecSrcB + vecSrcA) * 0.5f;
vec3 originDst = (vecDstB + vecDstA) * 0.5f;
vec3 translation = originDst - originSrc;
// Rotation
// TODO :(
// printf("Portal translation: %.3f %.3f %.3f\n", translation.x, translation.y, translation.z);
portal->transformation = mat4::translate(translation);
}
surf->portalIndex = portals.size();
portals.push_back(std::move(portal));
}
surfaces.push_back(std::move(surf));
return;
}
else
{
// Warn about broken portal?
}
}
// line_horizont consumes everything
if (side->line->special == Line_Horizon && front != back)
{

View file

@ -59,6 +59,11 @@ enum SurfaceType
ST_FLOOR
};
struct Portal
{
mat4 transformation = mat4::identity();
};
struct Surface
{
// Surface geometry
@ -77,6 +82,10 @@ struct Surface
int sampleDimension = 0;
bool bSky = false;
// Portal
int portalDestinationIndex = -1; // line or sector index
int portalIndex = -1;
// Touching light sources
std::vector<ThingLight*> LightList;
@ -124,6 +133,8 @@ public:
std::vector<Plane> smoothingGroups;
std::vector<std::unique_ptr<Portal>> portals;
int defaultSamples = 16;
int textureWidth = 128;
int textureHeight = 128;