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 )
|
project( ZDRay )
|
||||||
|
|
||||||
SET( CMAKE_CXX_STANDARD 14 )
|
SET( CMAKE_CXX_STANDARD 17 )
|
||||||
|
|
||||||
IF( NOT CMAKE_BUILD_TYPE )
|
IF( NOT CMAKE_BUILD_TYPE )
|
||||||
SET( CMAKE_BUILD_TYPE Debug CACHE STRING
|
SET( CMAKE_BUILD_TYPE Debug CACHE STRING
|
||||||
|
@ -196,6 +196,7 @@ set( SOURCES
|
||||||
src/lightmap/glsl_vert.h
|
src/lightmap/glsl_vert.h
|
||||||
src/lightmap/glsl_frag_resolve.h
|
src/lightmap/glsl_frag_resolve.h
|
||||||
src/lightmap/renderdoc_app.h
|
src/lightmap/renderdoc_app.h
|
||||||
|
src/lightmap/portal.h
|
||||||
src/math/mat.cpp
|
src/math/mat.cpp
|
||||||
src/math/plane.cpp
|
src/math/plane.cpp
|
||||||
src/math/angle.cpp
|
src/math/angle.cpp
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "math/mathlib.h"
|
#include "math/mathlib.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <optional>
|
||||||
#undef MIN
|
#undef MIN
|
||||||
#undef MAX
|
#undef MAX
|
||||||
#undef min
|
#undef min
|
||||||
|
@ -70,6 +71,8 @@ struct IntSideDef
|
||||||
inline int GetSampleDistanceBottom() const { return sampleDistanceBottom ? sampleDistanceBottom : sampleDistance; }
|
inline int GetSampleDistanceBottom() const { return sampleDistanceBottom ? sampleDistanceBottom : sampleDistance; }
|
||||||
|
|
||||||
TArray<UDMFKey> props;
|
TArray<UDMFKey> props;
|
||||||
|
|
||||||
|
inline int GetSectorGroup() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MapLineDef
|
struct MapLineDef
|
||||||
|
@ -107,6 +110,8 @@ struct IntLineDef
|
||||||
TArray<int> ids;
|
TArray<int> ids;
|
||||||
|
|
||||||
IntSector *frontsector = nullptr, *backsector = nullptr;
|
IntSector *frontsector = nullptr, *backsector = nullptr;
|
||||||
|
|
||||||
|
inline int GetSectorGroup() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MapSector
|
struct MapSector
|
||||||
|
@ -158,6 +163,8 @@ struct IntSector
|
||||||
TArray<IntLineDef*> lines;
|
TArray<IntLineDef*> lines;
|
||||||
TArray<IntLineDef*> portals;
|
TArray<IntLineDef*> portals;
|
||||||
|
|
||||||
|
int group = 0;
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
inline const char* GetTextureName(int plane) const { return plane != PLANE_FLOOR ? data.ceilingpic : data.floorpic; }
|
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
|
struct MapSubsector
|
||||||
{
|
{
|
||||||
uint16_t numlines;
|
uint16_t numlines;
|
||||||
|
@ -329,6 +346,21 @@ struct ThingLight
|
||||||
IntSector *sector;
|
IntSector *sector;
|
||||||
MapSubsectorEx *ssect;
|
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
|
vec3 LightOrigin() const
|
||||||
{
|
{
|
||||||
float originZ;
|
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()
|
void FProcessor::BuildNodes()
|
||||||
|
|
|
@ -290,6 +290,7 @@ void FLevel::CreateLights()
|
||||||
thingLight.sector = GetSectorFromSubSector(thingLight.ssect);
|
thingLight.sector = GetSectorFromSubSector(thingLight.ssect);
|
||||||
thingLight.origin.x = x;
|
thingLight.origin.x = x;
|
||||||
thingLight.origin.y = y;
|
thingLight.origin.y = y;
|
||||||
|
thingLight.sectorGroup = thingLight.sector->group;
|
||||||
|
|
||||||
ThingLights.Push(thingLight);
|
ThingLights.Push(thingLight);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,14 +52,16 @@ struct LightInfo
|
||||||
{
|
{
|
||||||
vec3 Origin;
|
vec3 Origin;
|
||||||
float Padding0;
|
float Padding0;
|
||||||
|
vec3 RelativeOrigin;
|
||||||
|
float Padding1;
|
||||||
float Radius;
|
float Radius;
|
||||||
float Intensity;
|
float Intensity;
|
||||||
float InnerAngleCos;
|
float InnerAngleCos;
|
||||||
float OuterAngleCos;
|
float OuterAngleCos;
|
||||||
vec3 SpotDir;
|
vec3 SpotDir;
|
||||||
float Padding1;
|
|
||||||
vec3 Color;
|
|
||||||
float Padding2;
|
float Padding2;
|
||||||
|
vec3 Color;
|
||||||
|
float Padding3;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(set = 0, binding = 1) buffer SurfaceIndexBuffer { uint surfaceIndices[]; };
|
layout(set = 0, binding = 1) buffer SurfaceIndexBuffer { uint surfaceIndices[]; };
|
||||||
|
@ -91,6 +93,7 @@ vec2 Hammersley(uint i, uint N);
|
||||||
float RadicalInverse_VdC(uint bits);
|
float RadicalInverse_VdC(uint bits);
|
||||||
|
|
||||||
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax);
|
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 TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax);
|
||||||
int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t);
|
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;
|
const float minDistance = 0.01;
|
||||||
vec3 incoming = vec3(0.0);
|
vec3 incoming = vec3(0.0);
|
||||||
float dist = distance(light.Origin, origin);
|
float dist = distance(light.RelativeOrigin, origin);
|
||||||
if (dist > minDistance && dist < light.Radius)
|
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 distAttenuation = max(1.0 - (dist / light.Radius), 0.0);
|
||||||
float angleAttenuation = 1.0f;
|
float angleAttenuation = 1.0f;
|
||||||
|
@ -139,12 +142,13 @@ vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light)
|
||||||
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
|
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
|
||||||
if (attenuation > 0.0)
|
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);
|
incoming.rgb += light.Color * (attenuation * light.Intensity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return incoming;
|
return incoming;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +240,7 @@ int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
t = tmax;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -466,6 +471,8 @@ int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax,
|
||||||
return hit.triangle;
|
return hit.triangle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tparam = tracedist;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,4 +519,48 @@ bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
|
||||||
return TraceFirstHitTriangle(origin, tmin, dir, tmax) >= 0;
|
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";
|
)glsl";
|
||||||
|
|
|
@ -216,6 +216,7 @@ void GPURaytracer::RenderAtlasImage(size_t pageIndex)
|
||||||
for (ThingLight* light : surface->LightList)
|
for (ThingLight* light : surface->LightList)
|
||||||
{
|
{
|
||||||
lightinfo->Origin = light->LightOrigin();
|
lightinfo->Origin = light->LightOrigin();
|
||||||
|
lightinfo->RelativeOrigin = light->LightRelativeOrigin();
|
||||||
lightinfo->Radius = light->LightRadius();
|
lightinfo->Radius = light->LightRadius();
|
||||||
lightinfo->Intensity = light->intensity;
|
lightinfo->Intensity = light->intensity;
|
||||||
lightinfo->InnerAngleCos = light->innerAngleCos;
|
lightinfo->InnerAngleCos = light->innerAngleCos;
|
||||||
|
@ -1015,14 +1016,11 @@ std::vector<PortalInfo> GPURaytracer::CreatePortalInfo()
|
||||||
portals.push_back(noPortal);
|
portals.push_back(noPortal);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& surface : mesh->surfaces)
|
for (const auto& portal : mesh->portals)
|
||||||
{
|
{
|
||||||
if (surface->portalIndex >= 0)
|
PortalInfo info;
|
||||||
{
|
info.Transformation = portal->transformation;
|
||||||
PortalInfo info;
|
portals.push_back(info);
|
||||||
info.Transformation = mesh->portals[surface->portalIndex]->transformation;
|
|
||||||
portals.push_back(info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return portals;
|
return portals;
|
||||||
|
|
|
@ -50,16 +50,20 @@ struct LightInfo
|
||||||
{
|
{
|
||||||
vec3 Origin;
|
vec3 Origin;
|
||||||
float Padding0;
|
float Padding0;
|
||||||
|
vec3 RelativeOrigin;
|
||||||
|
float Padding1;
|
||||||
float Radius;
|
float Radius;
|
||||||
float Intensity;
|
float Intensity;
|
||||||
float InnerAngleCos;
|
float InnerAngleCos;
|
||||||
float OuterAngleCos;
|
float OuterAngleCos;
|
||||||
vec3 SpotDir;
|
vec3 SpotDir;
|
||||||
float Padding1;
|
|
||||||
vec3 Color;
|
|
||||||
float Padding2;
|
float Padding2;
|
||||||
|
vec3 Color;
|
||||||
|
float Padding3;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(LightInfo) == sizeof(float) * 20);
|
||||||
|
|
||||||
struct CollisionNodeBufferHeader
|
struct CollisionNodeBufferHeader
|
||||||
{
|
{
|
||||||
int root;
|
int root;
|
||||||
|
|
|
@ -60,6 +60,13 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize)
|
||||||
|
|
||||||
printf("Surfaces total: %i\n\n", (int)surfaces.size());
|
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");
|
printf("Building level mesh...\n\n");
|
||||||
|
|
||||||
for (size_t i = 0; i < surfaces.size(); i++)
|
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");
|
printf("Finding smoothing groups...\n\n");
|
||||||
|
BuildSmoothingGroups(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;
|
|
||||||
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());
|
|
||||||
|
|
||||||
printf("Building collision data...\n\n");
|
printf("Building collision data...\n\n");
|
||||||
|
|
||||||
Collision = std::make_unique<TriangleMeshShape>(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size());
|
Collision = std::make_unique<TriangleMeshShape>(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size());
|
||||||
|
|
||||||
printf("Building light list...\n\n");
|
BuildLightLists(doomMap);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
std::map<int, int> lightStats;
|
std::map<int, int> lightStats;
|
||||||
for (auto& surface : surfaces)
|
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.
|
// 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
|
// 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)
|
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);
|
// printf("Portal translation: %.3f %.3f %.3f\n", translation.x, translation.y, translation.z);
|
||||||
|
|
||||||
portal->transformation = mat4::translate(translation);
|
portal->transformation = mat4::translate(translation);
|
||||||
|
portal->sourceSectorGroup = srcLine.GetSectorGroup();
|
||||||
|
portal->targetSectorGroup = dstLine.GetSectorGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
portals.push_back(std::move(portal));
|
// Deduplicate portals
|
||||||
return int(portals.size() - 1);
|
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)
|
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);
|
// printf("Portal translation: %.3f %.3f %.3f\n", translation.x, translation.y, translation.z);
|
||||||
|
|
||||||
portal->transformation = mat4::translate(translation);
|
portal->transformation = mat4::translate(translation);
|
||||||
|
portal->sourceSectorGroup = srcLine.GetSectorGroup();
|
||||||
|
portal->targetSectorGroup = dstLine.GetSectorGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
portals.push_back(std::move(portal));
|
// Deduplicate portals
|
||||||
return int(portals.size() - 1);
|
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)
|
void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side)
|
||||||
|
|
|
@ -31,12 +31,15 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "framework/tarray.h"
|
#include "framework/tarray.h"
|
||||||
#include "framework/halffloat.h"
|
#include "framework/halffloat.h"
|
||||||
#include "lightmaptexture.h"
|
#include "lightmaptexture.h"
|
||||||
#include "math/mathlib.h"
|
#include "math/mathlib.h"
|
||||||
#include "collision.h"
|
#include "collision.h"
|
||||||
|
#include "portal.h"
|
||||||
|
|
||||||
#include "dp_rect_pack/dp_rect_pack.h"
|
#include "dp_rect_pack/dp_rect_pack.h"
|
||||||
|
|
||||||
|
@ -60,11 +63,6 @@ enum SurfaceType
|
||||||
ST_FLOOR
|
ST_FLOOR
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Portal
|
|
||||||
{
|
|
||||||
mat4 transformation = mat4::identity();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Surface
|
struct Surface
|
||||||
{
|
{
|
||||||
// Surface geometry
|
// Surface geometry
|
||||||
|
@ -86,6 +84,9 @@ struct Surface
|
||||||
// Portal
|
// Portal
|
||||||
int portalDestinationIndex = -1; // line or sector index
|
int portalDestinationIndex = -1; // line or sector index
|
||||||
int portalIndex = -1;
|
int portalIndex = -1;
|
||||||
|
|
||||||
|
// Sector group
|
||||||
|
int sectorGroup = 0;
|
||||||
|
|
||||||
// Touching light sources
|
// Touching light sources
|
||||||
std::vector<ThingLight*> LightList;
|
std::vector<ThingLight*> LightList;
|
||||||
|
@ -117,6 +118,12 @@ struct Surface
|
||||||
int smoothingGroupIndex = -1;
|
int smoothingGroupIndex = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SmoothingGroup
|
||||||
|
{
|
||||||
|
Plane plane = Plane(0, 0, 1, 0);
|
||||||
|
int sectorGroup = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class LevelMesh
|
class LevelMesh
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -132,7 +139,7 @@ public:
|
||||||
|
|
||||||
std::vector<std::unique_ptr<LightmapTexture>> textures;
|
std::vector<std::unique_ptr<LightmapTexture>> textures;
|
||||||
|
|
||||||
std::vector<Plane> smoothingGroups;
|
std::vector<SmoothingGroup> smoothingGroups;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Portal>> portals;
|
std::vector<std::unique_ptr<Portal>> portals;
|
||||||
|
|
||||||
|
@ -148,6 +155,14 @@ public:
|
||||||
std::unique_ptr<TriangleMeshShape> Collision;
|
std::unique_ptr<TriangleMeshShape> Collision;
|
||||||
|
|
||||||
private:
|
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 CreateSubsectorSurfaces(FLevel &doomMap);
|
||||||
void CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor);
|
void CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor);
|
||||||
void CreateFloorSurface(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);
|
void BuildSurfaceParams(Surface* surface);
|
||||||
BBox GetBoundsFromSurface(const Surface* surface);
|
BBox GetBoundsFromSurface(const Surface* surface);
|
||||||
|
|
||||||
|
void BuildLightLists(FLevel &doomMap);
|
||||||
|
void PropagateLight(FLevel& doomMap, ThingLight* thing);
|
||||||
|
|
||||||
|
void BuildSmoothingGroups(FLevel& doomMap);
|
||||||
|
|
||||||
void BlurSurfaces();
|
void BlurSurfaces();
|
||||||
void FinishSurface(RectPacker& packer, Surface* surface);
|
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