From 005bae73488049d07db9a4d1acb23ada5dde69f6 Mon Sep 17 00:00:00 2001 From: RaveYard <29225776+MrRaveYard@users.noreply.github.com> Date: Thu, 31 Aug 2023 10:07:19 +0200 Subject: [PATCH] WIP BuildSurfaceParams from ZDRay --- src/maploader/maploader.cpp | 138 +++++++++++++++++++- src/maploader/maploader.h | 4 + src/rendering/hwrenderer/doom_levelmesh.cpp | 112 ++++++++++++++++ src/rendering/hwrenderer/doom_levelmesh.h | 89 +++++++++++++ 4 files changed, 339 insertions(+), 4 deletions(-) diff --git a/src/maploader/maploader.cpp b/src/maploader/maploader.cpp index f07e2be442..7cc432bdc3 100644 --- a/src/maploader/maploader.cpp +++ b/src/maploader/maploader.cpp @@ -3205,10 +3205,16 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position) SpawnThings(position); // Load and link lightmaps - must be done after P_Spawn3DFloors (and SpawnThings? Potentially for baking static model actors?) +#if 0 if (!ForceNodeBuild) { LoadLightmap(map); } +#endif + Level->levelMesh = new DoomLevelMesh(*Level); + InitLightmap(map); + + screen->InitLightmap(Level->LMTextureSize, Level->LMTextureCount, Level->LMTextureData); for (int i = 0; i < MAXPLAYERS; ++i) { @@ -3246,8 +3252,6 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position) CreateVBO(*screen->RenderState(0), Level->sectors); meshcache.Clear(); - screen->InitLightmap(Level->LMTextureSize, Level->LMTextureCount, Level->LMTextureData); - for (auto &sec : Level->sectors) { P_Recalculate3DFloors(&sec); @@ -3263,7 +3267,6 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position) Level->FinalizePortals(); // finalize line portals after polyobjects have been initialized. This info is needed for properly flagging them. Level->aabbTree = new DoomLevelAABBTree(Level); - Level->levelMesh = new DoomLevelMesh(*Level); } //========================================================================== @@ -3324,15 +3327,141 @@ void MapLoader::SetSideLightmap(const LightmapSurface &surface) } } -void MapLoader::LoadLightmap(MapData *map) +#include "halffloat.h" + +void MapLoader::InitLightmap(MapData* map) { // We have to reset everything as FLevelLocals is recycled between maps Level->LMTexCoords.Reset(); Level->LMSurfaces.Reset(); Level->LMTextureData.Reset(); + + // Debug placeholder stuff + { + Level->LMTextureCount = 1; + Level->LMTextureSize = 1024; + + for (int i = 0; i < 1024; ++i) // avoid crashing lol + { + Level->LMTexCoords.Push(0); + Level->LMTexCoords.Push(0); + + Level->LMTexCoords.Push(1); + Level->LMTexCoords.Push(0); + + Level->LMTexCoords.Push(1); + Level->LMTexCoords.Push(1); + + Level->LMTexCoords.Push(0); + Level->LMTexCoords.Push(1); + } + + auto constructDebugTexture = [&](TArray& buffer, int width, int height) { + uint16_t* ptr = buffer.Data(); + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + *(ptr++) = floatToHalf(float(x) / width); + *(ptr++) = floatToHalf(float(y) / height); + *(ptr++) = floatToHalf((x + y) % 2 == 0 ? 1.0f : 0.0f); + } + } + }; + + int size = Level->LMTextureSize; + int layers = 1; + + Level->LMTextureData.Resize(size * size * 3 * layers); + constructDebugTexture(Level->LMTextureData, size, size); + } + + for (auto& surface : Level->levelMesh->Surfaces) + { + Level->levelMesh->BuildSurfaceParams(Level->LMTextureSize, Level->LMTextureSize, surface); + } + + Level->LMTexCoords = Level->levelMesh->LightmapUvs; + + + // Allocate room for all surfaces + + unsigned int allSurfaces = 0; + + for (unsigned int i = 0; i < Level->sides.Size(); i++) + allSurfaces += 4 + Level->sides[i].sector->e->XFloor.ffloors.Size(); + + for (unsigned int i = 0; i < Level->subsectors.Size(); i++) + allSurfaces += 2 + Level->subsectors[i].sector->e->XFloor.ffloors.Size() * 2; + + Level->LMSurfaces.Resize(allSurfaces); + memset(&Level->LMSurfaces[0], 0, sizeof(LightmapSurface) * allSurfaces); + + // Link the surfaces to sectors, sides and their 3D floors + + unsigned int offset = 0; + for (unsigned int i = 0; i < Level->sides.Size(); i++) + { + auto& side = Level->sides[i]; + side.lightmap = &Level->LMSurfaces[offset]; + offset += 4 + side.sector->e->XFloor.ffloors.Size(); + } + for (unsigned int i = 0; i < Level->subsectors.Size(); i++) + { + auto& subsector = Level->subsectors[i]; + unsigned int count = 1 + subsector.sector->e->XFloor.ffloors.Size(); + subsector.lightmap[0] = &Level->LMSurfaces[offset]; + subsector.lightmap[1] = &Level->LMSurfaces[offset + count]; + offset += count * 2; + } + + // Copy and build properties + size_t index = 0; + for (auto& surface : Level->levelMesh->Surfaces) + { + LightmapSurface l; + //LightmapSurface& l = Level->LMSurfaces[index++]; + memset(&l, 0, sizeof(LightmapSurface)); + + l.ControlSector = surface.controlSector; + l.Type = surface.type; + l.LightmapNum = 0; + + //l.TexCoords = &Level->LMTexCoords[0]; + //l.TexCoords = &Level->levelMesh->LightmapUvs[surface.startUvIndex]; + l.TexCoords = &Level->LMTexCoords[surface.startUvIndex]; + + if (surface.type == ST_FLOOR || surface.type == ST_CEILING) + { + l.Subsector = &Level->subsectors[surface.typeIndex]; + l.Subsector->firstline->sidedef->sector->HasLightmaps = true; + SetSubsectorLightmap(l); + } + else + { + l.Side = &Level->sides[surface.typeIndex]; + SetSideLightmap(l); + } + + //Level->LMSurfaces.Push(l); + + } + + Printf("Generated custom lightmap data"); +} + +#if 0 +void MapLoader::LoadLightmap(MapData *map) +{ + Level->LMTexCoords.Reset(); + Level->LMSurfaces.Reset(); + Level->LMTextureData.Reset(); Level->LMTextureCount = 0; Level->LMTextureSize = 0; + // We have to reset everything as FLevelLocals is recycled between maps + //if (!Args->CheckParm("-enablelightmaps")) // return; // this feature is still too early WIP to allow general access @@ -3459,3 +3588,4 @@ void MapLoader::LoadLightmap(MapData *map) data[i] += data[i - 1]; #endif } +#endif \ No newline at end of file diff --git a/src/maploader/maploader.h b/src/maploader/maploader.h index 6ea2e6121f..58e4644f1c 100644 --- a/src/maploader/maploader.h +++ b/src/maploader/maploader.h @@ -306,7 +306,11 @@ public: void SetSubsectorLightmap(const LightmapSurface &surface); void SetSideLightmap(const LightmapSurface &surface); + +#if 0 void LoadLightmap(MapData *map); +#endif + void InitLightmap(MapData* map); void LoadLevel(MapData *map, const char *lumpname, int position); diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index 77e22471ca..695ae34507 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -463,3 +463,115 @@ void DoomLevelMesh::DumpMesh(const FString& filename) const fclose(f); } + +void DoomLevelMesh::BuildSurfaceParams(int lightMapTextureWidth, int lightMapTextureHeight, Surface& surface) +{ + secplane_t* plane; + BBox bounds; + FVector3 roundedSize; + int i; + PlaneAxis axis; + FVector3 tOrigin; + int width; + int height; + float d; + + plane = &surface.plane; + bounds = GetBoundsFromSurface(surface); + //surface->bounds = bounds; + + if (surface.sampleDimension <= 0) + { + surface.sampleDimension = 1; // TODO change? + } + + surface.sampleDimension = 1; + //surface->sampleDimension = Math::RoundPowerOfTwo(surface->sampleDimension); + + + // round off dimensions + for (i = 0; i < 3; i++) + { + bounds.min[i] = surface.sampleDimension * (floor(bounds.min[i] / surface.sampleDimension) - 1); + bounds.max[i] = surface.sampleDimension * (ceil(bounds.max[i] / surface.sampleDimension) + 1); + + roundedSize[i] = (bounds.max[i] - bounds.min[i]) / surface.sampleDimension; + } + + FVector3 tCoords[2] = { FVector3(0.0f, 0.0f, 0.0f), FVector3(0.0f, 0.0f, 0.0f) }; + + axis = BestAxis(*plane); + + switch (axis) + { + case AXIS_YZ: + width = (int)roundedSize.Y; + height = (int)roundedSize.Z; + tCoords[0].Y = 1.0f / surface.sampleDimension; + tCoords[1].Z = 1.0f / surface.sampleDimension; + break; + + case AXIS_XZ: + width = (int)roundedSize.X; + height = (int)roundedSize.Z; + tCoords[0].X = 1.0f / surface.sampleDimension; + tCoords[1].Z = 1.0f / surface.sampleDimension; + break; + + case AXIS_XY: + width = (int)roundedSize.X; + height = (int)roundedSize.Y; + tCoords[0].X = 1.0f / surface.sampleDimension; + tCoords[1].Y = 1.0f / surface.sampleDimension; + break; + } + + // clamp width + if (width > lightMapTextureWidth - 2) + { + tCoords[0] *= ((float)(lightMapTextureWidth - 2) / (float)width); + width = (lightMapTextureWidth - 2); + } + + // clamp height + if (height > lightMapTextureHeight - 2) + { + tCoords[1] *= ((float)(lightMapTextureHeight - 2) / (float)height); + height = (lightMapTextureHeight - 2); + } + + surface.translateWorldToLocal = bounds.min; + surface.projLocalToU = tCoords[0]; + surface.projLocalToV = tCoords[1]; + + surface.startUvIndex = AllocUvs(surface.numVerts); + auto uv = surface.startUvIndex; + for (i = 0; i < surface.numVerts; i++) + { + FVector3 tDelta = MeshVertices[surface.startVertIndex + i] - surface.translateWorldToLocal; + + LightmapUvs[uv++] = (tDelta | surface.projLocalToU); + LightmapUvs[uv++] = (tDelta | surface.projLocalToV); + } + + + tOrigin = bounds.min; + + // project tOrigin and tCoords so they lie on the plane + d = float(((bounds.min | FVector3(plane->Normal())) - plane->D) / plane->Normal()[axis]); //d = (plane->PointToDist(bounds.min)) / plane->Normal()[axis]; + tOrigin[axis] -= d; + + for (i = 0; i < 2; i++) + { + tCoords[i].MakeUnit(); + d = (tCoords[i] | FVector3(plane->Normal())) / plane->Normal()[axis]; //d = dot(tCoords[i], plane->Normal()) / plane->Normal()[axis]; + tCoords[i][axis] -= d; + } + + surface.texWidth = width; + surface.texHeight = height; + //surface->texPixels.resize(width * height); + surface.worldOrigin = tOrigin; + surface.worldStepX = tCoords[0] * (float)surface.sampleDimension; + surface.worldStepY = tCoords[1] * (float)surface.sampleDimension; +} \ No newline at end of file diff --git a/src/rendering/hwrenderer/doom_levelmesh.h b/src/rendering/hwrenderer/doom_levelmesh.h index 2d2aee75cf..5c301dcf02 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.h +++ b/src/rendering/hwrenderer/doom_levelmesh.h @@ -5,6 +5,7 @@ #include "tarray.h" #include "vectors.h" #include "r_defs.h" +#include "bounds.h" struct FLevelLocals; @@ -17,6 +18,28 @@ struct Surface secplane_t plane; sector_t *controlSector; bool bSky; + + // + // Required for internal lightmapper: + + int sampleDimension = 0; + + // Lightmap world coordinates for the texture + FVector3 worldOrigin = { 0.f, 0.f, 0.f }; + FVector3 worldStepX = { 0.f, 0.f, 0.f }; + FVector3 worldStepY = { 0.f, 0.f, 0.f }; + + // Calculate world coordinates to UV coordinates + FVector3 translateWorldToLocal = { 0.f, 0.f, 0.f }; + FVector3 projLocalToU = { 0.f, 0.f, 0.f }; + FVector3 projLocalToV = { 0.f, 0.f, 0.f }; + + // Output lightmap for the surface + int texWidth = 0; + int texHeight = 0; + + // UV coordinates for the vertices + int startUvIndex = -666; }; class DoomLevelMesh : public hwrenderer::LevelMesh @@ -37,6 +60,7 @@ public: } TArray Surfaces; + TArray LightmapUvs; void DumpMesh(const FString& filename) const; @@ -66,4 +90,69 @@ private: static FVector4 ToFVector4(const DVector4& v) { return FVector4((float)v.X, (float)v.Y, (float)v.Z, (float)v.W); } static bool IsDegenerate(const FVector3 &v0, const FVector3 &v1, const FVector3 &v2); + + // WIP internal lightmapper + + BBox GetBoundsFromSurface(const Surface& surface) const + { + constexpr float M_INFINITY = 1e30; // TODO cleanup + + FVector3 low(M_INFINITY, M_INFINITY, M_INFINITY); + FVector3 hi(-M_INFINITY, -M_INFINITY, -M_INFINITY); + + for (int i = int(surface.startVertIndex); i < int(surface.startVertIndex) + surface.numVerts; i++) + { + for (int j = 0; j < 3; j++) + { + if (MeshVertices[i][j] < low[j]) + { + low[j] = MeshVertices[i][j]; + } + if (MeshVertices[i][j] > hi[j]) + { + hi[j] = MeshVertices[i][j]; + } + } + } + + BBox bounds; + bounds.Clear(); + bounds.min = low; + bounds.max = hi; + return bounds; + } + + enum PlaneAxis + { + AXIS_YZ = 0, + AXIS_XZ, + AXIS_XY + }; + + inline static PlaneAxis BestAxis(const secplane_t& p) + { + float na = fabs(float(p.Normal().X)); + float nb = fabs(float(p.Normal().Y)); + float nc = fabs(float(p.Normal().Z)); + + // figure out what axis the plane lies on + if (na >= nb && na >= nc) + { + return AXIS_YZ; + } + else if (nb >= na && nb >= nc) + { + return AXIS_XZ; + } + + return AXIS_XY; + } + + int AllocUvs(int amount) + { + return LightmapUvs.Reserve(amount * 2); + } + + public: + void BuildSurfaceParams(int lightMapTextureWidth, int lightMapTextureHeight, Surface& surface); };