From 39481f0d076dd413b8f7a32258526fced9b9fb15 Mon Sep 17 00:00:00 2001 From: nashmuhandes Date: Fri, 24 Sep 2021 14:51:48 +0800 Subject: [PATCH] - Add support for loading lightmap data generated by ZDRay - Initialize lightmap texture in the hardware abstract parts of the engine --- src/common/rendering/v_video.h | 2 + src/doomdata.h | 1 + src/g_levellocals.h | 7 ++ src/gamedata/r_defs.h | 29 ++++++ src/maploader/maploader.cpp | 168 +++++++++++++++++++++++++++++++++ src/maploader/maploader.h | 4 + src/p_openmap.cpp | 9 ++ src/p_setup.h | 1 + 8 files changed, 221 insertions(+) diff --git a/src/common/rendering/v_video.h b/src/common/rendering/v_video.h index b78afd4b09..76bbd07a61 100644 --- a/src/common/rendering/v_video.h +++ b/src/common/rendering/v_video.h @@ -219,6 +219,8 @@ public: virtual int GetClientWidth() = 0; virtual int GetClientHeight() = 0; virtual void BlurScene(float amount) {} + + virtual void InitLightmap() {} // Interface to hardware rendering resources virtual IVertexBuffer *CreateVertexBuffer() { return nullptr; } diff --git a/src/doomdata.h b/src/doomdata.h index 00487ec2f6..48ce405499 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -62,6 +62,7 @@ enum ML_BEHAVIOR, // [RH] Hexen-style scripts. If present, THINGS // and LINEDEFS are also Hexen-style. ML_CONVERSATION, // Strife dialog (only for TEXTMAP format) + ML_LIGHTMAP, // ZDRay generated lightmap ML_MAX, // [RH] These are compressed (and extended) nodes. They combine the data from diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 887616e078..c23c59d63a 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -451,6 +451,13 @@ public: TArray sectorPortals; TArray linePortals; + // Lightmaps + TArray LMSurfaces; + TArray LMTexCoords; + int LMTextureCount = 0; + int LMTextureSize = 0; + TArray LMTextureData; + // Portal information. FDisplacementTable Displacements; FPortalBlockmap PortalBlockmap; diff --git a/src/gamedata/r_defs.h b/src/gamedata/r_defs.h index c2606635ad..8d2d7d910a 100644 --- a/src/gamedata/r_defs.h +++ b/src/gamedata/r_defs.h @@ -58,6 +58,7 @@ struct sector_t; class AActor; struct FSection; struct FLevelLocals; +struct LightmapSurface; const uint16_t NO_INDEX = 0xffffu; const uint32_t NO_SIDE = 0xffffffffu; @@ -707,6 +708,8 @@ struct sector_t int vbocount[2]; // Total count of vertices belonging to this sector's planes. This is used when a sector height changes and also contains all attached planes. int ibocount; // number of indices per plane (identical for all planes.) If this is -1 the index buffer is not in use. + bool HasLightmaps = false; // Sector has lightmaps, each subsector vertex needs its own unique lightmap UV data + // Below are all properties which are not used by the renderer. TObjPtr SoundTarget; @@ -1458,6 +1461,8 @@ struct line_t int healthgroup; // [ZZ] this is the "destructible object" id int linenum; + LightmapSurface *lightmap[4]; + DVector2 Delta() const { return delta; @@ -1615,6 +1620,8 @@ struct subsector_t int Index() const { return subsectornum; } // 2: has one-sided walls FPortalCoverage portalcoverage[2]; + + LightmapSurface *lightmap[2]; }; @@ -1659,6 +1666,28 @@ struct FMiniBSP TArray Verts; }; +// Lightmap data + +enum SurfaceType +{ + ST_NULL, + ST_MIDDLEWALL, + ST_UPPERWALL, + ST_LOWERWALL, + ST_CEILING, + ST_FLOOR +}; + +struct LightmapSurface +{ + SurfaceType Type; + subsector_t *Subsector; + line_t *Line; + sector_t *ControlSector; + uint32_t LightmapNum; + float *TexCoords; +}; + // // OTHER TYPES // diff --git a/src/maploader/maploader.cpp b/src/maploader/maploader.cpp index 7faaa01d1d..8dcccfe8c7 100644 --- a/src/maploader/maploader.cpp +++ b/src/maploader/maploader.cpp @@ -3206,6 +3206,12 @@ 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 (!ForceNodeBuild) + { + LoadLightmap(map); + } + for (int i = 0; i < MAXPLAYERS; ++i) { if (Level->PlayerInGame(i) && Level->Players[i]->mo != nullptr) @@ -3241,6 +3247,8 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position) Level->ClearDynamic3DFloorData(); // CreateVBO must be run on the plain 3D floor data. CreateVBO(screen->mVertexData, Level->sectors); + screen->InitLightmap(); + for (auto &sec : Level->sectors) { P_Recalculate3DFloors(&sec); @@ -3258,3 +3266,163 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position) Level->aabbTree = new DoomLevelAABBTree(Level); } +//========================================================================== +// +// +// +//========================================================================== + +void MapLoader::SetSubsectorLightmap(const LightmapSurface &surface) +{ + int index = surface.Type == ST_CEILING ? 1 : 0; + if (!surface.ControlSector) + { + surface.Subsector->lightmap[index][0] = surface; + } + else + { + const auto& ffloors = surface.Subsector->sector->e->XFloor.ffloors; + for (unsigned int i = 0; i < ffloors.Size(); i++) + { + if (ffloors[i]->model == surface.ControlSector) + { + surface.Subsector->lightmap[index][i + 1] = surface; + } + } + } +} + +void MapLoader::SetLineLightmap(const LightmapSurface &surface) +{ + int index = 0; + switch (surface.Type) + { + default: + case ST_MIDDLEWALL: index = 1; break; + case ST_UPPERWALL: index = 0; break; + case ST_LOWERWALL: index = 3; break; + } + + if (!surface.ControlSector) + { + surface.Line->lightmap[index][0] = surface; + } + else + { + const auto& ffloors = surface.Line->frontsector->e->XFloor.ffloors; + for (unsigned int i = 0; i < ffloors.Size(); i++) + { + if (ffloors[i]->model == surface.ControlSector) + { + surface.Line->lightmap[index][i + 1] = surface; + } + } + } +} + +void MapLoader::LoadLightmap(MapData *map) +{ + if (!map->Size(ML_LIGHTMAP)) + return; + + FileReader fr; + if (!fr.OpenDecompressor(map->Reader(ML_LIGHTMAP), -1, METHOD_ZLIB, false, [](const char* err) { I_Error("%s", err); })) + return; + + int version = fr.ReadInt32(); + if (version != 0) + return; + + uint16_t textureSize = fr.ReadUInt16(); + uint16_t numTextures = fr.ReadUInt16(); + uint32_t numSurfaces = fr.ReadUInt32(); + uint32_t numTexCoords = fr.ReadUInt32(); + uint32_t numTexBytes = numSurfaces * numTextures * textureSize * 2; + + if (numSurfaces == 0 || numTexCoords == 0 || numTexBytes == 0) + return; + + Level->LMTexCoords.Resize(numTexCoords * 2); + + // Allocate room for all surfaces + + unsigned int allSurfaces = 0; + + for (unsigned int i = 0; i < Level->lines.Size(); i++) + allSurfaces += 3 + Level->lines[i].frontsector->e->XFloor.ffloors.Size() * 3; + + 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, lines and their 3D floors + + unsigned int offset = 0; + for (unsigned int i = 0; i < Level->lines.Size(); i++) + { + auto& line = Level->lines[i]; + unsigned int count = 1 + line.frontsector->e->XFloor.ffloors.Size(); + line.lightmap[0] = &Level->LMSurfaces[offset]; + line.lightmap[1] = &Level->LMSurfaces[offset + count]; + line.lightmap[2] = line.lightmap[1]; + line.lightmap[3] = &Level->LMSurfaces[offset + count * 2]; + offset += count * 3; + } + 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; + } + + // Load the surfaces we have lightmap data for + + for (uint32_t i = 0; i < numSurfaces; i++) + { + LightmapSurface surface; + memset(&surface, 0, sizeof(LightmapSurface)); + + SurfaceType type = (SurfaceType)fr.ReadUInt32(); + uint32_t typeIndex = fr.ReadUInt32(); + uint32_t controlSector = fr.ReadUInt32(); + uint32_t lightmapNum = fr.ReadUInt32(); + uint32_t firstTexCoord = fr.ReadUInt32(); + + if (controlSector != 0xffffffff) + surface.ControlSector = &Level->sectors[controlSector]; + + surface.Type = type; + surface.LightmapNum = lightmapNum; + surface.TexCoords = &Level->LMTexCoords[firstTexCoord]; + + if (type == ST_CEILING || type == ST_FLOOR) + { + surface.Subsector = &Level->subsectors[typeIndex]; + surface.Subsector->firstline->sidedef->sector->HasLightmaps = true; + SetSubsectorLightmap(surface); + } + else if (type != ST_NULL) + { + surface.Line = &Level->lines[typeIndex]; + SetLineLightmap(surface); + } + } + + // Load texture coordinates + + fr.Read(&Level->LMTexCoords[0], numTexCoords * 2 * sizeof(float)); + + // Load lightmap textures + + Level->LMTextureCount = numTextures; + Level->LMTextureSize = textureSize; + Level->LMTextureData.Resize(numTexBytes); + uint8_t* data = &Level->LMTextureData[0]; + fr.Read(data, numTexBytes); + for (uint32_t i = 1; i < numTexBytes; i++) + data[i] += data[i - 1]; +} diff --git a/src/maploader/maploader.h b/src/maploader/maploader.h index 4330b9309d..37b0c4016a 100644 --- a/src/maploader/maploader.h +++ b/src/maploader/maploader.h @@ -304,6 +304,10 @@ public: void SetSlopes(); void CopySlopes(); + void SetSubsectorLightmap(const LightmapSurface &surface); + void SetLineLightmap(const LightmapSurface &surface); + void LoadLightmap(MapData *map); + void LoadLevel(MapData *map, const char *lumpname, int position); MapLoader(FLevelLocals *lev) diff --git a/src/p_openmap.cpp b/src/p_openmap.cpp index 98f03906a1..771ae1d877 100644 --- a/src/p_openmap.cpp +++ b/src/p_openmap.cpp @@ -81,6 +81,7 @@ static int GetMapIndex(const char *mapname, int lastindex, const char *lumpname, {"REJECT", false}, {"BLOCKMAP", false}, {"BEHAVIOR", false}, + {"LIGHTMAP", false }, //{"SCRIPTS", false}, }; @@ -235,6 +236,10 @@ MapData *P_OpenMapData(const char * mapname, bool justcheck) index = ML_BEHAVIOR; map->HasBehavior = true; } + else if (!stricmp(lumpname, "LIGHTMAP")) + { + index = ML_LIGHTMAP; + } else if (!stricmp(lumpname, "ENDMAP")) { break; @@ -312,6 +317,10 @@ MapData *P_OpenMapData(const char * mapname, bool justcheck) index = ML_BEHAVIOR; map->HasBehavior = true; } + else if (!strnicmp(lumpname, "LIGHTMAP", 8)) + { + index = ML_LIGHTMAP; + } else if (!strnicmp(lumpname, "ENDMAP",8)) { return map; diff --git a/src/p_setup.h b/src/p_setup.h index e6bef780d0..30248e90ab 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -141,6 +141,7 @@ MapData * P_OpenMapData(const char * mapname, bool justcheck); bool P_CheckMapData(const char * mapname); void P_SetupLevel (FLevelLocals *Level, int position, bool newGame); +void P_LoadLightmap(MapData *map); void P_FreeLevelData();