Merge pull request #50 from MrRaveYard/pr_portals

Initial portal support for sunlight
This commit is contained in:
Magnus Norddahl 2022-10-29 19:00:28 +02:00 committed by GitHub
commit 7503219d3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 366 additions and 44 deletions

View file

@ -153,11 +153,23 @@ struct IntSector
struct { bool skyFloor, skyCeiling; };
};
inline const char* GetTextureName(int plane) const { return plane != PLANE_FLOOR ? data.ceilingpic : data.floorpic; }
TArray<UDMFKey> props;
TArray<IntLineDef*> lines;
// Utility functions
inline const char* GetTextureName(int plane) const { return plane != PLANE_FLOOR ? data.ceilingpic : data.floorpic; }
inline bool HasTag(int sectorTag) const
{
if (tags.Size() <= 0 && sectorTag == 0)
return true;
for (auto tag : tags)
{
if (tag == sectorTag) return true;
}
return false;
}
};
struct MapSubsector
@ -445,6 +457,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->portalIndex >= 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

@ -451,6 +451,151 @@ void LevelMesh::FinishSurface(RectPacker& packer, Surface* surface)
}
}
int LevelMesh::CreateLinePortal(FLevel& doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine)
{
auto portal = std::make_unique<Portal>();
// Calculate portal transformation
{
FloatVertex srcV1 = doomMap.GetSegVertex(srcLine.v1);
FloatVertex srcV2 = doomMap.GetSegVertex(srcLine.v2);
FloatVertex dstV1 = doomMap.GetSegVertex(dstLine.v1);
FloatVertex dstV2 = doomMap.GetSegVertex(dstLine.v2);
int alignment = srcLine.args[3];
double srcAZ = 0;
double srcBZ = 0;
double dstAZ = 0;
double dstBZ = 0;
const auto* srcFront = srcLine.frontsector;
const auto* dstFront = dstLine.frontsector;
if (alignment == 1) // floor
{
srcAZ = srcFront->floorplane.zAt(srcV1.x, srcV1.y);
srcBZ = srcFront->floorplane.zAt(srcV2.x, srcV2.y);
dstAZ = dstFront->floorplane.zAt(dstV1.x, dstV1.y);
dstBZ = dstFront->floorplane.zAt(dstV2.x, dstV2.y);
}
else if (alignment == 2) // ceiling
{
srcAZ = srcFront->ceilingplane.zAt(srcV1.x, srcV1.y);
srcBZ = srcFront->ceilingplane.zAt(srcV2.x, srcV2.y);
dstAZ = dstFront->ceilingplane.zAt(dstV1.x, dstV1.y);
dstBZ = dstFront->ceilingplane.zAt(dstV2.x, dstV2.y);
}
const vec3 vecSrcA = vec3(vec2(srcV1.x, srcV1.y), srcAZ);
const vec3 vecSrcB = vec3(vec2(srcV2.x, srcV2.y), srcAZ);
const vec3 vecDstA = vec3(vec2(dstV1.x, dstV1.y), dstAZ);
const vec3 vecDstB = vec3(vec2(dstV2.x, dstV2.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);
}
portals.push_back(std::move(portal));
return int(portals.size() - 1);
}
int LevelMesh::CheckAndMakePortal(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, int typeIndex, int plane)
{
const auto& lines = doomMap.GetSectorFromSubSector(sub)->lines;
for (const auto& line : lines)
{
if (line->special == Sector_SetPortal && line->args[0] && line->args[2] == plane && !line->args[3] && sector->HasTag(line->args[0]))
{
const IntLineDef* dstLine = nullptr;
// Find the other portal line
for (const auto &targetLine : doomMap.Lines)
{
if (targetLine.special == Sector_SetPortal && targetLine.args[2] == plane && targetLine.args[3] && line->args[0] == targetLine.args[0])
{
dstLine = &targetLine;
break;
}
}
if (dstLine)
{
return CreatePlanePortal(doomMap, *line, *dstLine);
}
}
}
return -1;
}
int LevelMesh::CreatePlanePortal(FLevel& doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine)
{
auto portal = std::make_unique<Portal>();
// Calculate portal transformation
{
FloatVertex srcV1 = doomMap.GetSegVertex(srcLine.v1);
FloatVertex srcV2 = doomMap.GetSegVertex(srcLine.v2);
FloatVertex dstV1 = doomMap.GetSegVertex(dstLine.v1);
FloatVertex dstV2 = doomMap.GetSegVertex(dstLine.v2);
int alignment = srcLine.args[2];
double srcAZ = 0;
double srcBZ = 0;
double dstAZ = 0;
double dstBZ = 0;
const auto* srcFront = srcLine.frontsector;
const auto* dstFront = dstLine.frontsector;
if (alignment == 0) // floor
{
srcAZ = srcFront->floorplane.zAt(srcV1.x, srcV1.y);
srcBZ = srcFront->floorplane.zAt(srcV2.x, srcV2.y);
dstAZ = dstFront->ceilingplane.zAt(dstV1.x, dstV1.y);
dstBZ = dstFront->ceilingplane.zAt(dstV2.x, dstV2.y);
}
else // ceiling
{
srcAZ = srcFront->ceilingplane.zAt(srcV1.x, srcV1.y);
srcBZ = srcFront->ceilingplane.zAt(srcV2.x, srcV2.y);
dstAZ = dstFront->floorplane.zAt(dstV1.x, dstV1.y);
dstBZ = dstFront->floorplane.zAt(dstV2.x, dstV2.y);
}
const vec3 vecSrcA = vec3(vec2(srcV1.x, srcV1.y), srcAZ);
const vec3 vecSrcB = vec3(vec2(srcV2.x, srcV2.y), srcAZ);
const vec3 vecDstA = vec3(vec2(dstV1.x, dstV1.y), dstAZ);
const vec3 vecDstB = vec3(vec2(dstV2.x, dstV2.y), dstBZ);
// Translation
vec3 originSrc = (vecSrcB + vecSrcA) * 0.5f;
vec3 originDst = (vecDstB + vecDstA) * 0.5f;
vec3 translation = originDst - originSrc;
// printf("Portal translation: %.3f %.3f %.3f\n", translation.x, translation.y, translation.z);
portal->transformation = mat4::translate(translation);
}
portals.push_back(std::move(portal));
return int(portals.size() - 1);
}
void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side)
{
IntSector *front;
@ -480,6 +625,62 @@ 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;
surf->portalIndex = CreateLinePortal(doomMap, *side->line, doomMap.Lines[destLineIndex]);
surfaces.push_back(std::move(surf));
return;
}
else
{
// Warn about broken portal?
}
}
// line_horizont consumes everything
if (side->line->special == Line_Horizon && front != back)
{
@ -761,6 +962,7 @@ void LevelMesh::CreateFloorSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSect
if (!is3DFloor)
{
surf->plane = sector->floorplane;
surf->portalIndex = CheckAndMakePortal(doomMap, sub, sector, typeIndex, PLANE_FLOOR);
}
else
{
@ -799,6 +1001,7 @@ void LevelMesh::CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSe
if (!is3DFloor)
{
surf->plane = sector->ceilingplane;
surf->portalIndex = CheckAndMakePortal(doomMap, sub, sector, typeIndex, PLANE_CEILING);
}
else
{

View file

@ -45,6 +45,7 @@ typedef dp::rect_pack::RectPacker<int> RectPacker;
struct MapSubsectorEx;
struct IntSector;
struct IntSideDef;
struct IntLineDef;
struct FLevel;
struct ThingLight;
class FWadWriter;
@ -59,6 +60,11 @@ enum SurfaceType
ST_FLOOR
};
struct Portal
{
mat4 transformation = mat4::identity();
};
struct Surface
{
// Surface geometry
@ -77,6 +83,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 +134,8 @@ public:
std::vector<Plane> smoothingGroups;
std::vector<std::unique_ptr<Portal>> portals;
int defaultSamples = 16;
int textureWidth = 128;
int textureHeight = 128;
@ -147,4 +159,9 @@ private:
void FinishSurface(RectPacker& packer, Surface* surface);
static bool IsDegenerate(const vec3 &v0, const vec3 &v1, const vec3 &v2);
int CheckAndMakePortal(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, int typeIndex, int plane);
int CreateLinePortal(FLevel &doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine);
int CreatePlanePortal(FLevel &doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine);
};