From cb9416269ec8d36a627cc782539590423fc327fe Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sat, 29 Oct 2022 21:59:07 +0200 Subject: [PATCH] Initial implementation of recursive portal pointlight propagation --- src/level/doomdata.h | 13 +++++ src/lightmap/glsl_frag.h | 61 +++++++++++++++++++++-- src/lightmap/gpuraytracer.cpp | 1 + src/lightmap/gpuraytracer.h | 8 ++- src/lightmap/levelmesh.cpp | 92 ++++++++++++++++++++++++++--------- src/lightmap/levelmesh.h | 45 +++++++++++++++++ 6 files changed, 189 insertions(+), 31 deletions(-) diff --git a/src/level/doomdata.h b/src/level/doomdata.h index 3406f58..5534c7b 100644 --- a/src/level/doomdata.h +++ b/src/level/doomdata.h @@ -329,6 +329,19 @@ struct ThingLight IntSector *sector; MapSubsectorEx *ssect; + vec3 relativePosition = vec3(0); + + // Portal aware position + vec3 LightRelativeOrigin() const + { + if (relativePosition != vec3(0)) + { + return relativePosition; + } + return LightOrigin(); + } + + // Absolute X, Y, Z position of the light vec3 LightOrigin() const { float originZ; diff --git a/src/lightmap/glsl_frag.h b/src/lightmap/glsl_frag.h index d163785..0edd66b 100644 --- a/src/lightmap/glsl_frag.h +++ b/src/lightmap/glsl_frag.h @@ -52,14 +52,16 @@ struct LightInfo { vec3 Origin; float Padding0; + vec3 RelativeOrigin; + float Padding1; float Radius; float Intensity; float InnerAngleCos; float OuterAngleCos; vec3 SpotDir; - float Padding1; - vec3 Color; float Padding2; + vec3 Color; + float Padding3; }; layout(set = 0, binding = 1) buffer SurfaceIndexBuffer { uint surfaceIndices[]; }; @@ -91,6 +93,7 @@ vec2 Hammersley(uint i, uint N); float RadicalInverse_VdC(uint bits); bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax); +bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax); int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax); int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t); @@ -117,10 +120,10 @@ vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light) { const float minDistance = 0.01; vec3 incoming = vec3(0.0); - float dist = distance(light.Origin, origin); + float dist = distance(light.RelativeOrigin, origin); if (dist > minDistance && dist < light.Radius) { - vec3 dir = normalize(light.Origin - origin); + vec3 dir = normalize(light.RelativeOrigin - origin); float distAttenuation = max(1.0 - (dist / light.Radius), 0.0); float angleAttenuation = 1.0f; @@ -139,12 +142,13 @@ vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light) float attenuation = distAttenuation * angleAttenuation * spotAttenuation; if (attenuation > 0.0) { - if (!TraceAnyHit(origin, minDistance, dir, dist)) + if(TracePoint(origin, light.Origin, minDistance, dir, dist)) { incoming.rgb += light.Color * (attenuation * light.Intensity); } } } + return incoming; } @@ -236,6 +240,7 @@ int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, } else { + t = tmax; return -1; } } @@ -466,6 +471,8 @@ int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, return hit.triangle; } } + + tparam = tracedist; return -1; } @@ -512,4 +519,48 @@ bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax) return TraceFirstHitTriangle(origin, tmin, dir, tmax) >= 0; } +bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax) +{ + int primitiveID; + float t; + while(true) + { + t = tmax; + primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t); + + origin += dir * t; + tmax -= t; + + if(primitiveID < 0) + { + // We didn't hit anything + break; + } + + SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; + + if(surface.PortalIndex == 0) + { + break; + } + + if(dot(surface.Normal, dir) >= 0.0) + { + continue; + } + + mat4 transformationMatrix = portals[surface.PortalIndex].Transformation; + origin = (transformationMatrix * vec4(origin, 1.0)).xyz; + dir = (transformationMatrix * vec4(dir, 0.0)).xyz; + +#if defined(USE_RAYQUERY) +#else + origin += dir * tmin; + tmax -= tmin; +#endif + } + + return distance(origin, target) <= 1.0; +} + )glsl"; diff --git a/src/lightmap/gpuraytracer.cpp b/src/lightmap/gpuraytracer.cpp index 11f1777..93bd2b2 100644 --- a/src/lightmap/gpuraytracer.cpp +++ b/src/lightmap/gpuraytracer.cpp @@ -216,6 +216,7 @@ void GPURaytracer::RenderAtlasImage(size_t pageIndex) for (ThingLight* light : surface->LightList) { lightinfo->Origin = light->LightOrigin(); + lightinfo->RelativeOrigin = light->LightRelativeOrigin(); lightinfo->Radius = light->LightRadius(); lightinfo->Intensity = light->intensity; lightinfo->InnerAngleCos = light->innerAngleCos; diff --git a/src/lightmap/gpuraytracer.h b/src/lightmap/gpuraytracer.h index 3fd480e..25b03f6 100644 --- a/src/lightmap/gpuraytracer.h +++ b/src/lightmap/gpuraytracer.h @@ -50,16 +50,20 @@ struct LightInfo { vec3 Origin; float Padding0; + vec3 RelativeOrigin; + float Padding1; float Radius; float Intensity; float InnerAngleCos; float OuterAngleCos; vec3 SpotDir; - float Padding1; - vec3 Color; float Padding2; + vec3 Color; + float Padding3; }; +static_assert(sizeof(LightInfo) == sizeof(float) * 20); + struct CollisionNodeBufferHeader { int root; diff --git a/src/lightmap/levelmesh.cpp b/src/lightmap/levelmesh.cpp index d4b1f4c..12228b7 100644 --- a/src/lightmap/levelmesh.cpp +++ b/src/lightmap/levelmesh.cpp @@ -148,30 +148,7 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize) Collision = std::make_unique(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size()); printf("Building light list...\n\n"); - - for (ThingLight& light : map->ThingLights) - { - SphereShape sphere; - sphere.center = light.LightOrigin(); - sphere.radius = light.LightRadius(); - - for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere)) - { - Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get(); - bool found = false; - for (ThingLight* light2 : surface->LightList) - { - if (light2 == &light) - { - found = true; - break; - } - } - if (!found) - surface->LightList.push_back(&light); - } - } - + BuildLightLists(doomMap); /* std::map lightStats; for (auto& surface : surfaces) @@ -182,6 +159,73 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize) */ } +#include // hack +std::set touchedPortals; + +int depth = 0; + +void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) +{ + if (depth > 4) + { + return; + } + + SphereShape sphere; + sphere.center = light->LightRelativeOrigin(); + sphere.radius = light->LightRadius(); + depth++; + std::set portalsToErase; + for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere)) + { + Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get(); + + if (surface->portalIndex >= 0) + { + auto portal = portals[surface->portalIndex].get(); + + if (touchedPortals.insert(*portal).second) + { + auto fakeLight = std::make_unique(*light); + + fakeLight->relativePosition = portal->TransformPosition(light->LightRelativeOrigin()); + + PropagateLight(doomMap, fakeLight.get()); + portalsToErase.insert(*portal); + portalLights.push_back(std::move(fakeLight)); + } + } + + // Add light to the list if it isn't already there + bool found = false; + for (ThingLight* light2 : surface->LightList) + { + if (light2 == light) + { + found = true; + break; + } + } + if (!found) + surface->LightList.push_back(light); + + } + + for (auto& portal : portalsToErase) + { + touchedPortals.erase(portal); + } + depth--; +} + +void LevelMesh::BuildLightLists(FLevel& doomMap) +{ + for (ThingLight& light : map->ThingLights) + { + PropagateLight(doomMap, &light); + } +} + // Determines a lightmap block in which to map to the lightmap texture. // Width and height of the block is calcuated and steps are computed to determine where each texel will be positioned on the surface void LevelMesh::BuildSurfaceParams(Surface* surface) diff --git a/src/lightmap/levelmesh.h b/src/lightmap/levelmesh.h index 63fb0d2..32e23ba 100644 --- a/src/lightmap/levelmesh.h +++ b/src/lightmap/levelmesh.h @@ -63,6 +63,45 @@ enum SurfaceType struct Portal { mat4 transformation = mat4::identity(); + + inline vec3 TransformPosition(const vec3& pos) const + { + auto v = transformation * vec4(pos, 1.0); + return vec3(v.x, v.y, v.z); + } + + inline vec3 TransformRotation(const vec3& dir) const + { + auto v = transformation * vec4(dir, 0.0); + return vec3(v.x, v.y, v.z); + } + + // Check if the portal is inverse + inline bool IsInversePortal(const Portal& portal) const + { + auto diff = portal.TransformPosition(TransformPosition(vec3(0))); + return abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001; + } + + // Check if the portal transformation is equal + inline bool IsEqualPortal(const Portal& portal) const + { + auto diff = portal.TransformPosition(vec3(0)) - TransformPosition(vec3(0)); + return (abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001); + } + +}; + + + +// for use with std::set to recursively go through portals +struct RejectRecursivePortals +{ + bool operator()(const Portal& a, const Portal& b) const + { + static_assert(sizeof(Portal) == sizeof(float) * 4 * 4); + return !(a.IsInversePortal(b) || a.IsEqualPortal(b)) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; + } }; struct Surface @@ -148,6 +187,8 @@ public: std::unique_ptr Collision; private: + std::vector> portalLights; // Portal generated lights + void CreateSubsectorSurfaces(FLevel &doomMap); void CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor); void CreateFloorSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor); @@ -155,6 +196,10 @@ private: void BuildSurfaceParams(Surface* surface); BBox GetBoundsFromSurface(const Surface* surface); + + void BuildLightLists(FLevel &doomMap); + void PropagateLight(FLevel& doomMap, ThingLight* thing); + void BlurSurfaces(); void FinishSurface(RectPacker& packer, Surface* surface);