From cb9416269ec8d36a627cc782539590423fc327fe Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sat, 29 Oct 2022 21:59:07 +0200 Subject: [PATCH 1/7] 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); From 0a9c2cf1314e6aa0bedbf32c96525dba5a2d008e Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sun, 30 Oct 2022 13:47:10 +0100 Subject: [PATCH 2/7] Implement sector groups --- src/level/doomdata.h | 18 +++++++++++ src/level/level.cpp | 35 +++++++++++++++++++++ src/level/level_light.cpp | 1 + src/lightmap/levelmesh.cpp | 63 +++++++++++++++++++++++--------------- src/lightmap/levelmesh.h | 3 +- 5 files changed, 95 insertions(+), 25 deletions(-) diff --git a/src/level/doomdata.h b/src/level/doomdata.h index 5534c7b..1b46399 100644 --- a/src/level/doomdata.h +++ b/src/level/doomdata.h @@ -70,6 +70,8 @@ struct IntSideDef inline int GetSampleDistanceBottom() const { return sampleDistanceBottom ? sampleDistanceBottom : sampleDistance; } TArray props; + + inline int GetSectorGroup() const; }; struct MapLineDef @@ -107,6 +109,8 @@ struct IntLineDef TArray ids; IntSector *frontsector = nullptr, *backsector = nullptr; + + inline int GetSectorGroup() const; }; struct MapSector @@ -158,6 +162,8 @@ struct IntSector TArray lines; TArray portals; + int group = 0; + // Utility functions inline const char* GetTextureName(int plane) const { return plane != PLANE_FLOOR ? data.ceilingpic : data.floorpic; } @@ -173,6 +179,16 @@ struct IntSector } }; +inline int IntLineDef::GetSectorGroup() const +{ + return frontsector ? frontsector->group : (backsector ? backsector->group : 0); +} + +inline int IntSideDef::GetSectorGroup() const +{ + return line ? line->GetSectorGroup() : 0; +} + struct MapSubsector { uint16_t numlines; @@ -329,7 +345,9 @@ struct ThingLight IntSector *sector; MapSubsectorEx *ssect; + // Portal related functionality vec3 relativePosition = vec3(0); + int sectorGroup = 0; // Portal aware position vec3 LightRelativeOrigin() const diff --git a/src/level/level.cpp b/src/level/level.cpp index 060cd76..20f94c9 100644 --- a/src/level/level.cpp +++ b/src/level/level.cpp @@ -691,6 +691,41 @@ void FLevel::PostLoadInitialization() } } } + + // Discover islands of sectors + { + int groupId = 0; + std::vector candidates; + + for (auto& sector : Sectors) + { + if (!sector.group) + { + sector.group = ++groupId; + candidates.push_back(§or); + + while (!candidates.empty()) + { + auto* sector = candidates[candidates.size() - 1]; + candidates.pop_back(); + + for (const auto& line : sector->lines) + { + if (line->frontsector && !line->frontsector->group) + { + line->frontsector->group = groupId; + candidates.push_back(line->frontsector); + } + if (line->backsector && !line->backsector->group) + { + line->backsector->group = groupId; + candidates.push_back(line->backsector); + } + } + } + } + } + } } void FProcessor::BuildNodes() diff --git a/src/level/level_light.cpp b/src/level/level_light.cpp index 6e16283..756a0f8 100644 --- a/src/level/level_light.cpp +++ b/src/level/level_light.cpp @@ -290,6 +290,7 @@ void FLevel::CreateLights() thingLight.sector = GetSectorFromSubSector(thingLight.ssect); thingLight.origin.x = x; thingLight.origin.y = y; + thingLight.sectorGroup = thingLight.sector->group; ThingLights.Push(thingLight); } diff --git a/src/lightmap/levelmesh.cpp b/src/lightmap/levelmesh.cpp index 12228b7..b855349 100644 --- a/src/lightmap/levelmesh.cpp +++ b/src/lightmap/levelmesh.cpp @@ -147,7 +147,6 @@ 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"); BuildLightLists(doomMap); /* std::map lightStats; @@ -166,7 +165,8 @@ int depth = 0; void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) { - if (depth > 4) + // Because of sectorGroups, this will unlikely get this deep anyway + if (depth > 32) { return; } @@ -180,35 +180,43 @@ void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) { Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get(); - if (surface->portalIndex >= 0) + auto surfaceSectorGroup = surface->type == ST_CEILING || surface->type == ST_FLOOR ? + doomMap.GetSectorFromSubSector(&doomMap.GLSubsectors[surface->typeIndex])->group : (doomMap.Sides[surface->typeIndex].GetSectorGroup()); + + // Reject any surface which isn't physically connected to the sector group in which the light resided + if (light->sectorGroup == surfaceSectorGroup) { - auto portal = portals[surface->portalIndex].get(); + if (surface->portalIndex >= 0) + { - if (touchedPortals.insert(*portal).second) - { - auto fakeLight = std::make_unique(*light); + auto portal = portals[surface->portalIndex].get(); - fakeLight->relativePosition = portal->TransformPosition(light->LightRelativeOrigin()); + if (touchedPortals.insert(*portal).second) + { + auto fakeLight = std::make_unique(*light); - PropagateLight(doomMap, fakeLight.get()); - portalsToErase.insert(*portal); - portalLights.push_back(std::move(fakeLight)); + fakeLight->relativePosition = portal->TransformPosition(light->LightRelativeOrigin()); + fakeLight->sectorGroup = portal->targetSectorGroup; + + 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) + // Add light to the list if it isn't already there + bool found = false; + for (ThingLight* light2 : surface->LightList) { - found = true; - break; + if (light2 == light) + { + found = true; + break; + } } + if (!found) + surface->LightList.push_back(light); } - if (!found) - surface->LightList.push_back(light); - } for (auto& portal : portalsToErase) @@ -220,10 +228,13 @@ void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) void LevelMesh::BuildLightLists(FLevel& doomMap) { - for (ThingLight& light : map->ThingLights) + for (unsigned i = 0; i < map->ThingLights.Size(); ++i) { - PropagateLight(doomMap, &light); + printf("Building light lists: %u / %u\r", i, map->ThingLights.Size()); + PropagateLight(doomMap, &map->ThingLights[i]); } + + printf("Building light lists: %u / %u\n", map->ThingLights.Size(), map->ThingLights.Size()); } // Determines a lightmap block in which to map to the lightmap texture. @@ -548,6 +559,8 @@ int LevelMesh::CreateLinePortal(FLevel& doomMap, const IntLineDef& srcLine, cons // printf("Portal translation: %.3f %.3f %.3f\n", translation.x, translation.y, translation.z); portal->transformation = mat4::translate(translation); + portal->sourceSectorGroup = srcLine.GetSectorGroup(); + portal->targetSectorGroup = dstLine.GetSectorGroup(); } portals.push_back(std::move(portal)); @@ -632,6 +645,8 @@ int LevelMesh::CreatePlanePortal(FLevel& doomMap, const IntLineDef& srcLine, con // printf("Portal translation: %.3f %.3f %.3f\n", translation.x, translation.y, translation.z); portal->transformation = mat4::translate(translation); + portal->sourceSectorGroup = srcLine.GetSectorGroup(); + portal->targetSectorGroup = dstLine.GetSectorGroup(); } portals.push_back(std::move(portal)); diff --git a/src/lightmap/levelmesh.h b/src/lightmap/levelmesh.h index 32e23ba..b6df607 100644 --- a/src/lightmap/levelmesh.h +++ b/src/lightmap/levelmesh.h @@ -63,6 +63,8 @@ enum SurfaceType struct Portal { mat4 transformation = mat4::identity(); + int sourceSectorGroup = 0; + int targetSectorGroup = 0; inline vec3 TransformPosition(const vec3& pos) const { @@ -99,7 +101,6 @@ 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; } }; From 364d71a24a8f18dbc1c53006f2644acbd5b4cffc Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sun, 30 Oct 2022 14:17:39 +0100 Subject: [PATCH 3/7] Group together equal portals --- src/lightmap/gpuraytracer.cpp | 11 ++++----- src/lightmap/levelmesh.cpp | 26 +++++++++++++++++---- src/lightmap/levelmesh.h | 44 ++++++++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/lightmap/gpuraytracer.cpp b/src/lightmap/gpuraytracer.cpp index 93bd2b2..35c6223 100644 --- a/src/lightmap/gpuraytracer.cpp +++ b/src/lightmap/gpuraytracer.cpp @@ -1016,14 +1016,11 @@ std::vector GPURaytracer::CreatePortalInfo() portals.push_back(noPortal); } - for (const auto& surface : mesh->surfaces) + for (const auto& portal : mesh->portals) { - if (surface->portalIndex >= 0) - { - PortalInfo info; - info.Transformation = mesh->portals[surface->portalIndex]->transformation; - portals.push_back(info); - } + PortalInfo info; + info.Transformation = portal->transformation; + portals.push_back(info); } return portals; diff --git a/src/lightmap/levelmesh.cpp b/src/lightmap/levelmesh.cpp index b855349..df96676 100644 --- a/src/lightmap/levelmesh.cpp +++ b/src/lightmap/levelmesh.cpp @@ -563,8 +563,17 @@ int LevelMesh::CreateLinePortal(FLevel& doomMap, const IntLineDef& srcLine, cons portal->targetSectorGroup = dstLine.GetSectorGroup(); } - portals.push_back(std::move(portal)); - return int(portals.size() - 1); + // Deduplicate portals + auto it = portalCache.find(*portal); + + if (it == portalCache.end()) + { + int id = int(portals.size()); + portalCache.emplace(*portal, id); + portals.push_back(std::move(portal)); + return id; + } + return it->second; } int LevelMesh::CheckAndMakePortal(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, int typeIndex, int plane) @@ -649,8 +658,17 @@ int LevelMesh::CreatePlanePortal(FLevel& doomMap, const IntLineDef& srcLine, con portal->targetSectorGroup = dstLine.GetSectorGroup(); } - portals.push_back(std::move(portal)); - return int(portals.size() - 1); + // Deduplicate portals + auto it = portalCache.find(*portal); + + if (it == portalCache.end()) + { + int id = int(portals.size()); + portalCache.emplace(*portal, id); + portals.push_back(std::move(portal)); + return id; + } + return it->second; } void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side) diff --git a/src/lightmap/levelmesh.h b/src/lightmap/levelmesh.h index b6df607..7db293e 100644 --- a/src/lightmap/levelmesh.h +++ b/src/lightmap/levelmesh.h @@ -31,6 +31,7 @@ #include #include #include +#include #include "framework/tarray.h" #include "framework/halffloat.h" @@ -92,16 +93,52 @@ struct Portal return (abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001); } + // Do the portals travel between the same group of sectors? + /* + inline bool IsTravelingBetweenSameSectorGroupsInEitherDirection(const Portal& portal) const + { + int thisGroupA = sourceSectorGroup < targetSectorGroup ? sourceSectorGroup : targetSectorGroup; + int thisGroupB = sourceSectorGroup > targetSectorGroup ? sourceSectorGroup : targetSectorGroup; + + int otherGroupA = portal.sourceSectorGroup < portal.targetSectorGroup ? portal.sourceSectorGroup : portal.targetSectorGroup; + int otherGroupB = portal.sourceSectorGroup > portal.targetSectorGroup ? portal.sourceSectorGroup : portal.targetSectorGroup; + + return thisGroupA == otherGroupA && thisGroupB == otherGroupB; + } + */ + + // do both portals travel from sector group A to sector group B? + inline bool IsTravelingBetweenSameSectorGroups(const Portal& portal) const + { + return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup; + } }; - - // for use with std::set to recursively go through portals struct RejectRecursivePortals { + inline bool IsEqual(const Portal& a, const Portal& b) const + { + return a.IsInversePortal(b) || a.IsEqualPortal(b); + } + bool operator()(const Portal& a, const Portal& b) const { - return !(a.IsInversePortal(b) || a.IsEqualPortal(b)) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; + return !IsEqual(a, b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; + } +}; + +// for use with std::map to reject portals which have equal transformation between equal sector groups +struct IdenticalPortalComparator +{ + inline bool IsEqual(const Portal& a, const Portal& b) const + { + return a.IsEqualPortal(b) && a.IsTravelingBetweenSameSectorGroups(b); + } + + bool operator()(const Portal& a, const Portal& b) const + { + return !IsEqual(a, b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; } }; @@ -189,6 +226,7 @@ public: private: std::vector> portalLights; // Portal generated lights + std::map portalCache; void CreateSubsectorSurfaces(FLevel &doomMap); void CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor); From 1a94878e4edce060a0629df0c40f64edf1f8baae Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sun, 30 Oct 2022 14:28:07 +0100 Subject: [PATCH 4/7] Move portal stuff from levelmesh.h to portal.h --- CMakeLists.txt | 1 + src/lightmap/levelmesh.h | 82 +--------------------------------------- src/lightmap/portal.h | 70 ++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 81 deletions(-) create mode 100644 src/lightmap/portal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f3a2da2..8c77cec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,6 +196,7 @@ set( SOURCES src/lightmap/glsl_vert.h src/lightmap/glsl_frag_resolve.h src/lightmap/renderdoc_app.h + src/lightmap/portal.h src/math/mat.cpp src/math/plane.cpp src/math/angle.cpp diff --git a/src/lightmap/levelmesh.h b/src/lightmap/levelmesh.h index 7db293e..4c5b590 100644 --- a/src/lightmap/levelmesh.h +++ b/src/lightmap/levelmesh.h @@ -38,6 +38,7 @@ #include "lightmaptexture.h" #include "math/mathlib.h" #include "collision.h" +#include "portal.h" #include "dp_rect_pack/dp_rect_pack.h" @@ -61,87 +62,6 @@ enum SurfaceType ST_FLOOR }; -struct Portal -{ - mat4 transformation = mat4::identity(); - int sourceSectorGroup = 0; - int targetSectorGroup = 0; - - 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); - } - - // Do the portals travel between the same group of sectors? - /* - inline bool IsTravelingBetweenSameSectorGroupsInEitherDirection(const Portal& portal) const - { - int thisGroupA = sourceSectorGroup < targetSectorGroup ? sourceSectorGroup : targetSectorGroup; - int thisGroupB = sourceSectorGroup > targetSectorGroup ? sourceSectorGroup : targetSectorGroup; - - int otherGroupA = portal.sourceSectorGroup < portal.targetSectorGroup ? portal.sourceSectorGroup : portal.targetSectorGroup; - int otherGroupB = portal.sourceSectorGroup > portal.targetSectorGroup ? portal.sourceSectorGroup : portal.targetSectorGroup; - - return thisGroupA == otherGroupA && thisGroupB == otherGroupB; - } - */ - - // do both portals travel from sector group A to sector group B? - inline bool IsTravelingBetweenSameSectorGroups(const Portal& portal) const - { - return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup; - } -}; - -// for use with std::set to recursively go through portals -struct RejectRecursivePortals -{ - inline bool IsEqual(const Portal& a, const Portal& b) const - { - return a.IsInversePortal(b) || a.IsEqualPortal(b); - } - - bool operator()(const Portal& a, const Portal& b) const - { - return !IsEqual(a, b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; - } -}; - -// for use with std::map to reject portals which have equal transformation between equal sector groups -struct IdenticalPortalComparator -{ - inline bool IsEqual(const Portal& a, const Portal& b) const - { - return a.IsEqualPortal(b) && a.IsTravelingBetweenSameSectorGroups(b); - } - - bool operator()(const Portal& a, const Portal& b) const - { - return !IsEqual(a, b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; - } -}; - struct Surface { // Surface geometry diff --git a/src/lightmap/portal.h b/src/lightmap/portal.h new file mode 100644 index 0000000..4f4fe2a --- /dev/null +++ b/src/lightmap/portal.h @@ -0,0 +1,70 @@ +#pragma once + +#include "math/mathlib.h" + +struct Portal +{ + mat4 transformation = mat4::identity(); + int sourceSectorGroup = 0; + int targetSectorGroup = 0; + + 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); + } + + // Do both portals travel from sector group A to sector group B? + inline bool IsTravelingBetweenSameSectorGroups(const Portal& portal) const + { + return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup; + } +}; + +// for use with std::set to recursively go through portals +struct RejectRecursivePortals +{ + inline bool IsEqual(const Portal& a, const Portal& b) const + { + return a.IsInversePortal(b) || a.IsEqualPortal(b); + } + + bool operator()(const Portal& a, const Portal& b) const + { + return !IsEqual(a, b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; + } +}; + +// for use with std::map to reject portals which have equal transformation between equal sector groups +struct IdenticalPortalComparator +{ + inline bool IsEqual(const Portal& a, const Portal& b) const + { + return a.IsEqualPortal(b) && a.IsTravelingBetweenSameSectorGroups(b); + } + + bool operator()(const Portal& a, const Portal& b) const + { + return !IsEqual(a, b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; + } +}; From 22f24e799319a63e9c37fb20cf9fd6ce630efd44 Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sun, 30 Oct 2022 15:02:19 +0100 Subject: [PATCH 5/7] Fix smoothing groups causing pixels to bleed over portals --- src/level/level.cpp | 26 +++++++---- src/lightmap/levelmesh.cpp | 89 +++++++++++++++++++++++--------------- src/lightmap/levelmesh.h | 13 +++++- 3 files changed, 82 insertions(+), 46 deletions(-) diff --git a/src/level/level.cpp b/src/level/level.cpp index 20f94c9..d3391a4 100644 --- a/src/level/level.cpp +++ b/src/level/level.cpp @@ -692,11 +692,16 @@ void FLevel::PostLoadInitialization() } } - // Discover islands of sectors + // Discover sector groups (graph islands) { int groupId = 0; std::vector candidates; + auto canPass = [&](IntLineDef* line) { + // Further conditions can be added to further split the map into groups + return line->special == 0 || (line->special != Line_SetPortal && line->special != Line_Horizon); + }; + for (auto& sector : Sectors) { if (!sector.group) @@ -711,15 +716,18 @@ void FLevel::PostLoadInitialization() for (const auto& line : sector->lines) { - if (line->frontsector && !line->frontsector->group) + if (canPass(line)) { - line->frontsector->group = groupId; - candidates.push_back(line->frontsector); - } - if (line->backsector && !line->backsector->group) - { - line->backsector->group = groupId; - candidates.push_back(line->backsector); + if (line->frontsector && !line->frontsector->group) + { + line->frontsector->group = groupId; + candidates.push_back(line->frontsector); + } + if (line->backsector && !line->backsector->group) + { + line->backsector->group = groupId; + candidates.push_back(line->backsector); + } } } } diff --git a/src/lightmap/levelmesh.cpp b/src/lightmap/levelmesh.cpp index df96676..462dfc6 100644 --- a/src/lightmap/levelmesh.cpp +++ b/src/lightmap/levelmesh.cpp @@ -60,6 +60,13 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize) printf("Surfaces total: %i\n\n", (int)surfaces.size()); + // Update sector group of the surfacesaa + for (auto& surface : surfaces) + { + surface->sectorGroup = surface->type == ST_CEILING || surface->type == ST_FLOOR ? + doomMap.GetSectorFromSubSector(&doomMap.GLSubsectors[surface->typeIndex])->group : (doomMap.Sides[surface->typeIndex].GetSectorGroup()); + } + printf("Building level mesh...\n\n"); for (size_t i = 0; i < surfaces.size(); i++) @@ -112,37 +119,7 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize) } printf("Finding smoothing groups...\n\n"); - - for (size_t i = 0; i < surfaces.size(); i++) - { - // Is this surface in the same plane as an existing smoothing group? - int smoothingGroupIndex = -1; - for (size_t j = 0; j < smoothingGroups.size(); j++) - { - float direction = std::abs(dot(smoothingGroups[j].Normal(), surfaces[i]->plane.Normal())); - if (direction >= 0.9999f && direction <= 1.001f) - { - float dist = std::abs(smoothingGroups[j].Distance(surfaces[i]->plane.Normal() * surfaces[i]->plane.d)); - if (dist <= 0.01f) - { - smoothingGroupIndex = (int)j; - break; - } - } - } - - // Surface is in a new plane. Create a smoothing group for it - if (smoothingGroupIndex == -1) - { - smoothingGroupIndex = smoothingGroups.size(); - smoothingGroups.push_back(surfaces[i]->plane); - } - - surfaces[i]->smoothingGroupIndex = smoothingGroupIndex; - } - - printf("Created %d smoothing groups for %d surfaces\n\n", (int)smoothingGroups.size(), (int)surfaces.size()); - + BuildSmoothingGroups(doomMap); printf("Building collision data...\n\n"); Collision = std::make_unique(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size()); @@ -158,6 +135,50 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize) */ } +void LevelMesh::BuildSmoothingGroups(FLevel& doomMap) +{ + for (size_t i = 0; i < surfaces.size(); i++) + { + // Is this surface in the same plane as an existing smoothing group? + int smoothingGroupIndex = -1; + + auto surface = surfaces[i].get(); + + for (size_t j = 0; j < smoothingGroups.size(); j++) + { + if (surface->sectorGroup == smoothingGroups[j].sectorGroup) + { + float direction = std::abs(dot(smoothingGroups[j].plane.Normal(), surface->plane.Normal())); + if (direction >= 0.9999f && direction <= 1.001f) + { + float dist = std::abs(smoothingGroups[j].plane.Distance(surface->plane.Normal() * surface->plane.d)); + if (dist <= 0.01f) + { + smoothingGroupIndex = (int)j; + break; + } + } + } + } + + // Surface is in a new plane. Create a smoothing group for it + if (smoothingGroupIndex == -1) + { + smoothingGroupIndex = smoothingGroups.size(); + + SmoothingGroup group; + group.plane = surface->plane; + group.sectorGroup = surface->sectorGroup; + smoothingGroups.push_back(group); + } + + surface->smoothingGroupIndex = smoothingGroupIndex; + } + + printf("Created %d smoothing groups for %d surfaces\n\n", (int)smoothingGroups.size(), (int)surfaces.size()); +} + + #include // hack std::set touchedPortals; @@ -179,12 +200,8 @@ void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere)) { Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get(); - - auto surfaceSectorGroup = surface->type == ST_CEILING || surface->type == ST_FLOOR ? - doomMap.GetSectorFromSubSector(&doomMap.GLSubsectors[surface->typeIndex])->group : (doomMap.Sides[surface->typeIndex].GetSectorGroup()); - // Reject any surface which isn't physically connected to the sector group in which the light resided - if (light->sectorGroup == surfaceSectorGroup) + if (light->sectorGroup == surface->sectorGroup) { if (surface->portalIndex >= 0) { diff --git a/src/lightmap/levelmesh.h b/src/lightmap/levelmesh.h index 4c5b590..8bc6fd6 100644 --- a/src/lightmap/levelmesh.h +++ b/src/lightmap/levelmesh.h @@ -83,6 +83,9 @@ struct Surface // Portal int portalDestinationIndex = -1; // line or sector index int portalIndex = -1; + + // Sector group + int sectorGroup = 0; // Touching light sources std::vector LightList; @@ -114,6 +117,12 @@ struct Surface int smoothingGroupIndex = -1; }; +struct SmoothingGroup +{ + Plane plane = Plane(0, 0, 1, 0); + int sectorGroup = 0; +}; + class LevelMesh { public: @@ -129,7 +138,7 @@ public: std::vector> textures; - std::vector smoothingGroups; + std::vector smoothingGroups; std::vector> portals; @@ -159,6 +168,8 @@ private: void BuildLightLists(FLevel &doomMap); void PropagateLight(FLevel& doomMap, ThingLight* thing); + void BuildSmoothingGroups(FLevel& doomMap); + void BlurSurfaces(); void FinishSurface(RectPacker& packer, Surface* surface); From ea5b1a3cac9a0757fe84476b9d803df33781fece Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sun, 30 Oct 2022 16:12:06 +0100 Subject: [PATCH 6/7] Refactor light propagation --- src/lightmap/levelmesh.cpp | 20 ++++++---------- src/lightmap/levelmesh.h | 8 ++++++- src/lightmap/portal.h | 47 ++++++++++++++++++++------------------ 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/lightmap/levelmesh.cpp b/src/lightmap/levelmesh.cpp index 462dfc6..762975a 100644 --- a/src/lightmap/levelmesh.cpp +++ b/src/lightmap/levelmesh.cpp @@ -178,16 +178,9 @@ void LevelMesh::BuildSmoothingGroups(FLevel& doomMap) printf("Created %d smoothing groups for %d surfaces\n\n", (int)smoothingGroups.size(), (int)surfaces.size()); } - -#include // hack -std::set touchedPortals; - -int depth = 0; - void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) { - // Because of sectorGroups, this will unlikely get this deep anyway - if (depth > 32) + if (lightPropagationRecursiveDepth > 32) { return; } @@ -195,17 +188,17 @@ void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) SphereShape sphere; sphere.center = light->LightRelativeOrigin(); sphere.radius = light->LightRadius(); - depth++; - std::set portalsToErase; + lightPropagationRecursiveDepth++; + std::set portalsToErase; for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere)) { Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get(); - // Reject any surface which isn't physically connected to the sector group in which the light resided + + // skip any surface which isn't physically connected to the sector group in which the light resides if (light->sectorGroup == surface->sectorGroup) { if (surface->portalIndex >= 0) { - auto portal = portals[surface->portalIndex].get(); if (touchedPortals.insert(*portal).second) @@ -240,7 +233,8 @@ void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) { touchedPortals.erase(portal); } - depth--; + + lightPropagationRecursiveDepth--; } void LevelMesh::BuildLightLists(FLevel& doomMap) diff --git a/src/lightmap/levelmesh.h b/src/lightmap/levelmesh.h index 8bc6fd6..9c3add9 100644 --- a/src/lightmap/levelmesh.h +++ b/src/lightmap/levelmesh.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "framework/tarray.h" #include "framework/halffloat.h" @@ -154,9 +155,14 @@ public: std::unique_ptr Collision; private: - std::vector> portalLights; // Portal generated lights + // Portal to portals[] index std::map portalCache; + // Portal lights + std::vector> portalLights; + std::set touchedPortals; + int lightPropagationRecursiveDepth = 0; + 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); diff --git a/src/lightmap/portal.h b/src/lightmap/portal.h index 4f4fe2a..1adcbe9 100644 --- a/src/lightmap/portal.h +++ b/src/lightmap/portal.h @@ -20,51 +20,54 @@ struct Portal return vec3(v.x, v.y, v.z); } - // Check if the portal is inverse - inline bool IsInversePortal(const Portal& portal) const + // Checks only transformation + inline bool IsInverseTransformationPortal(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 + // Checks only transformation + inline bool IsEqualTransformationPortal(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); } - // Do both portals travel from sector group A to sector group B? - inline bool IsTravelingBetweenSameSectorGroups(const Portal& portal) const + + // Checks transformation, source and destiantion sector groups + inline bool IsEqualPortal(const Portal& portal) const { - return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup; + return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup && IsEqualTransformationPortal(portal); + } + + // Checks transformation, source and destiantion sector groups + inline bool IsInversePortal(const Portal& portal) const + { + return sourceSectorGroup == portal.targetSectorGroup && targetSectorGroup == portal.sourceSectorGroup && IsInverseTransformationPortal(portal); + } + + inline void DumpInfo() + { + auto v = TransformPosition(vec3(0)); + printf("Portal offset: %.3f %.3f %.3f\n\tsource group:\t%d\n\ttarget group:\t%d", v.x, v.y, v.z, sourceSectorGroup, targetSectorGroup); } }; -// for use with std::set to recursively go through portals -struct RejectRecursivePortals +// for use with std::set to recursively go through portals and skip returning portals +struct RecursivePortalComparator { - inline bool IsEqual(const Portal& a, const Portal& b) const - { - return a.IsInversePortal(b) || a.IsEqualPortal(b); - } - bool operator()(const Portal& a, const Portal& b) const { - return !IsEqual(a, b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; + return !a.IsInversePortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; } }; -// for use with std::map to reject portals which have equal transformation between equal sector groups +// for use with std::map to reject portals which have the same effect for light rays struct IdenticalPortalComparator { - inline bool IsEqual(const Portal& a, const Portal& b) const - { - return a.IsEqualPortal(b) && a.IsTravelingBetweenSameSectorGroups(b); - } - bool operator()(const Portal& a, const Portal& b) const { - return !IsEqual(a, b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; + return !a.IsEqualPortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; } }; From 91c0871b0b1568ac2e7558b29c0bdf4cb9472449 Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sun, 30 Oct 2022 22:25:45 +0100 Subject: [PATCH 7/7] Fix edge case where relativePosition of light is valid at 0,0,0 --- CMakeLists.txt | 2 +- src/level/doomdata.h | 7 ++++--- src/lightmap/levelmesh.cpp | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c77cec..ae6a3b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include( CheckCXXCompilerFlag ) project( ZDRay ) -SET( CMAKE_CXX_STANDARD 14 ) +SET( CMAKE_CXX_STANDARD 17 ) IF( NOT CMAKE_BUILD_TYPE ) SET( CMAKE_BUILD_TYPE Debug CACHE STRING diff --git a/src/level/doomdata.h b/src/level/doomdata.h index 1b46399..a39518e 100644 --- a/src/level/doomdata.h +++ b/src/level/doomdata.h @@ -7,6 +7,7 @@ #include "math/mathlib.h" #include #include +#include #undef MIN #undef MAX #undef min @@ -346,15 +347,15 @@ struct ThingLight MapSubsectorEx *ssect; // Portal related functionality - vec3 relativePosition = vec3(0); + std::optional relativePosition; int sectorGroup = 0; // Portal aware position vec3 LightRelativeOrigin() const { - if (relativePosition != vec3(0)) + if (relativePosition) { - return relativePosition; + return *relativePosition; } return LightOrigin(); } diff --git a/src/lightmap/levelmesh.cpp b/src/lightmap/levelmesh.cpp index 762975a..7372f63 100644 --- a/src/lightmap/levelmesh.cpp +++ b/src/lightmap/levelmesh.cpp @@ -205,7 +205,7 @@ void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) { auto fakeLight = std::make_unique(*light); - fakeLight->relativePosition = portal->TransformPosition(light->LightRelativeOrigin()); + fakeLight->relativePosition.emplace(portal->TransformPosition(light->LightRelativeOrigin())); fakeLight->sectorGroup = portal->targetSectorGroup; PropagateLight(doomMap, fakeLight.get());