mirror of
https://github.com/ZDoom/ZDRay.git
synced 2025-01-24 16:51:08 +00:00
Merge pull request #51 from MrRaveYard/pr_portal_pointlights
Full Portal Light Support
This commit is contained in:
commit
001a936994
10 changed files with 393 additions and 81 deletions
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "math/mathlib.h"
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
#undef MIN
|
||||
#undef MAX
|
||||
#undef min
|
||||
|
@ -70,6 +71,8 @@ struct IntSideDef
|
|||
inline int GetSampleDistanceBottom() const { return sampleDistanceBottom ? sampleDistanceBottom : sampleDistance; }
|
||||
|
||||
TArray<UDMFKey> props;
|
||||
|
||||
inline int GetSectorGroup() const;
|
||||
};
|
||||
|
||||
struct MapLineDef
|
||||
|
@ -107,6 +110,8 @@ struct IntLineDef
|
|||
TArray<int> ids;
|
||||
|
||||
IntSector *frontsector = nullptr, *backsector = nullptr;
|
||||
|
||||
inline int GetSectorGroup() const;
|
||||
};
|
||||
|
||||
struct MapSector
|
||||
|
@ -158,6 +163,8 @@ struct IntSector
|
|||
TArray<IntLineDef*> lines;
|
||||
TArray<IntLineDef*> portals;
|
||||
|
||||
int group = 0;
|
||||
|
||||
// Utility functions
|
||||
inline const char* GetTextureName(int plane) const { return plane != PLANE_FLOOR ? data.ceilingpic : data.floorpic; }
|
||||
|
||||
|
@ -173,6 +180,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,6 +346,21 @@ struct ThingLight
|
|||
IntSector *sector;
|
||||
MapSubsectorEx *ssect;
|
||||
|
||||
// Portal related functionality
|
||||
std::optional<vec3> relativePosition;
|
||||
int sectorGroup = 0;
|
||||
|
||||
// Portal aware position
|
||||
vec3 LightRelativeOrigin() const
|
||||
{
|
||||
if (relativePosition)
|
||||
{
|
||||
return *relativePosition;
|
||||
}
|
||||
return LightOrigin();
|
||||
}
|
||||
|
||||
// Absolute X, Y, Z position of the light
|
||||
vec3 LightOrigin() const
|
||||
{
|
||||
float originZ;
|
||||
|
|
|
@ -691,6 +691,49 @@ void FLevel::PostLoadInitialization()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Discover sector groups (graph islands)
|
||||
{
|
||||
int groupId = 0;
|
||||
std::vector<IntSector*> 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)
|
||||
{
|
||||
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 (canPass(line))
|
||||
{
|
||||
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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
@ -1015,15 +1016,12 @@ std::vector<PortalInfo> GPURaytracer::CreatePortalInfo()
|
|||
portals.push_back(noPortal);
|
||||
}
|
||||
|
||||
for (const auto& surface : mesh->surfaces)
|
||||
{
|
||||
if (surface->portalIndex >= 0)
|
||||
for (const auto& portal : mesh->portals)
|
||||
{
|
||||
PortalInfo info;
|
||||
info.Transformation = mesh->portals[surface->portalIndex]->transformation;
|
||||
info.Transformation = portal->transformation;
|
||||
portals.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
return portals;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,66 +119,12 @@ 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<TriangleMeshShape>(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<int, int> lightStats;
|
||||
for (auto& surface : surfaces)
|
||||
|
@ -182,6 +135,119 @@ 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());
|
||||
}
|
||||
|
||||
void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light)
|
||||
{
|
||||
if (lightPropagationRecursiveDepth > 32)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SphereShape sphere;
|
||||
sphere.center = light->LightRelativeOrigin();
|
||||
sphere.radius = light->LightRadius();
|
||||
lightPropagationRecursiveDepth++;
|
||||
std::set<Portal, RecursivePortalComparator> portalsToErase;
|
||||
for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere))
|
||||
{
|
||||
Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get();
|
||||
|
||||
// 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)
|
||||
{
|
||||
auto fakeLight = std::make_unique<ThingLight>(*light);
|
||||
|
||||
fakeLight->relativePosition.emplace(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)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
surface->LightList.push_back(light);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& portal : portalsToErase)
|
||||
{
|
||||
touchedPortals.erase(portal);
|
||||
}
|
||||
|
||||
lightPropagationRecursiveDepth--;
|
||||
}
|
||||
|
||||
void LevelMesh::BuildLightLists(FLevel& doomMap)
|
||||
{
|
||||
for (unsigned i = 0; i < map->ThingLights.Size(); ++i)
|
||||
{
|
||||
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.
|
||||
// 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)
|
||||
|
@ -504,10 +570,21 @@ 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();
|
||||
}
|
||||
|
||||
// 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 int(portals.size() - 1);
|
||||
return id;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int LevelMesh::CheckAndMakePortal(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, int typeIndex, int plane)
|
||||
|
@ -588,10 +665,21 @@ 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();
|
||||
}
|
||||
|
||||
// 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 int(portals.size() - 1);
|
||||
return id;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side)
|
||||
|
|
|
@ -31,12 +31,15 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "framework/tarray.h"
|
||||
#include "framework/halffloat.h"
|
||||
#include "lightmaptexture.h"
|
||||
#include "math/mathlib.h"
|
||||
#include "collision.h"
|
||||
#include "portal.h"
|
||||
|
||||
#include "dp_rect_pack/dp_rect_pack.h"
|
||||
|
||||
|
@ -60,11 +63,6 @@ enum SurfaceType
|
|||
ST_FLOOR
|
||||
};
|
||||
|
||||
struct Portal
|
||||
{
|
||||
mat4 transformation = mat4::identity();
|
||||
};
|
||||
|
||||
struct Surface
|
||||
{
|
||||
// Surface geometry
|
||||
|
@ -87,6 +85,9 @@ struct Surface
|
|||
int portalDestinationIndex = -1; // line or sector index
|
||||
int portalIndex = -1;
|
||||
|
||||
// Sector group
|
||||
int sectorGroup = 0;
|
||||
|
||||
// Touching light sources
|
||||
std::vector<ThingLight*> LightList;
|
||||
|
||||
|
@ -117,6 +118,12 @@ struct Surface
|
|||
int smoothingGroupIndex = -1;
|
||||
};
|
||||
|
||||
struct SmoothingGroup
|
||||
{
|
||||
Plane plane = Plane(0, 0, 1, 0);
|
||||
int sectorGroup = 0;
|
||||
};
|
||||
|
||||
class LevelMesh
|
||||
{
|
||||
public:
|
||||
|
@ -132,7 +139,7 @@ public:
|
|||
|
||||
std::vector<std::unique_ptr<LightmapTexture>> textures;
|
||||
|
||||
std::vector<Plane> smoothingGroups;
|
||||
std::vector<SmoothingGroup> smoothingGroups;
|
||||
|
||||
std::vector<std::unique_ptr<Portal>> portals;
|
||||
|
||||
|
@ -148,6 +155,14 @@ public:
|
|||
std::unique_ptr<TriangleMeshShape> Collision;
|
||||
|
||||
private:
|
||||
// Portal to portals[] index
|
||||
std::map<Portal, int, IdenticalPortalComparator> portalCache;
|
||||
|
||||
// Portal lights
|
||||
std::vector<std::unique_ptr<ThingLight>> portalLights;
|
||||
std::set<Portal, RecursivePortalComparator> 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);
|
||||
|
@ -155,6 +170,12 @@ private:
|
|||
|
||||
void BuildSurfaceParams(Surface* surface);
|
||||
BBox GetBoundsFromSurface(const Surface* surface);
|
||||
|
||||
void BuildLightLists(FLevel &doomMap);
|
||||
void PropagateLight(FLevel& doomMap, ThingLight* thing);
|
||||
|
||||
void BuildSmoothingGroups(FLevel& doomMap);
|
||||
|
||||
void BlurSurfaces();
|
||||
void FinishSurface(RectPacker& packer, Surface* surface);
|
||||
|
||||
|
|
73
src/lightmap/portal.h
Normal file
73
src/lightmap/portal.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
// Checks transformation, source and destiantion sector groups
|
||||
inline bool IsEqualPortal(const Portal& portal) const
|
||||
{
|
||||
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 and skip returning portals
|
||||
struct RecursivePortalComparator
|
||||
{
|
||||
bool operator()(const Portal& a, const Portal& b) const
|
||||
{
|
||||
return !a.IsInversePortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
// for use with std::map to reject portals which have the same effect for light rays
|
||||
struct IdenticalPortalComparator
|
||||
{
|
||||
bool operator()(const Portal& a, const Portal& b) const
|
||||
{
|
||||
return !a.IsEqualPortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0;
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue