From 3f1dcce9705957e484bebbbd935850b63f378ef2 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 11 Jun 2025 03:38:40 +0200 Subject: [PATCH] Bake models into the level mesh --- .../rendering/hwrenderer/data/hw_levelmesh.h | 16 +- .../hwrenderer/data/hw_meshbuilder.cpp | 4 +- .../hwrenderer/data/hw_meshbuilder.h | 5 +- src/rendering/hwrenderer/doom_levelmesh.cpp | 189 ++++++++++++++++-- src/rendering/hwrenderer/doom_levelmesh.h | 2 + 5 files changed, 188 insertions(+), 28 deletions(-) diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.h b/src/common/rendering/hwrenderer/data/hw_levelmesh.h index 645594d5a..1702b5a9c 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.h +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.h @@ -42,6 +42,7 @@ struct SurfaceAllocInfo { LevelMeshSurface* Surface = nullptr; int Index = 0; + int Count = 0; }; struct LightAllocInfo @@ -124,14 +125,14 @@ public: GeometryAllocInfo AllocGeometry(int vertexCount, int indexCount); UniformsAllocInfo AllocUniforms(int count); - SurfaceAllocInfo AllocSurface(); + SurfaceAllocInfo AllocSurface(int count = 1); LightAllocInfo AllocLight(); LightListAllocInfo AllocLightList(int count); int AllocTile(const LightmapTile& tile); void FreeGeometry(int vertexStart, int vertexCount, int indexStart, int indexCount); void FreeUniforms(int start, int count); - void FreeSurface(unsigned int surfaceIndex); + void FreeSurface(unsigned int surfaceIndex, int count = 1); void FreeLightList(int start, int count); void FreeTile(int index); @@ -288,12 +289,13 @@ inline LightAllocInfo LevelMesh::AllocLight() return info; } -inline SurfaceAllocInfo LevelMesh::AllocSurface() +inline SurfaceAllocInfo LevelMesh::AllocSurface(int count) { SurfaceAllocInfo info; - info.Index = FreeLists.Surface.Alloc(1); + info.Index = FreeLists.Surface.Alloc(count); info.Surface = &Mesh.Surfaces[info.Index]; - UploadRanges.Surface.Add(info.Index, 1); + info.Count = count; + UploadRanges.Surface.Add(info.Index, info.Count); return info; } @@ -333,9 +335,9 @@ inline void LevelMesh::FreeLightList(int start, int count) FreeLists.LightIndex.Free(start, count); } -inline void LevelMesh::FreeSurface(unsigned int surfaceIndex) +inline void LevelMesh::FreeSurface(unsigned int surfaceIndex, int count) { - FreeLists.Surface.Free(surfaceIndex, 1); + FreeLists.Surface.Free(surfaceIndex, count); } inline void LevelMesh::FreeTile(int index) diff --git a/src/common/rendering/hwrenderer/data/hw_meshbuilder.cpp b/src/common/rendering/hwrenderer/data/hw_meshbuilder.cpp index 8d9bd15de..a6912261c 100644 --- a/src/common/rendering/hwrenderer/data/hw_meshbuilder.cpp +++ b/src/common/rendering/hwrenderer/data/hw_meshbuilder.cpp @@ -102,7 +102,9 @@ void MeshBuilderModelRender::BeginDrawModel(FRenderStyle style, int smf_flags, c void MeshBuilderModelRender::EndDrawModel(FRenderStyle style, int smf_flags) { renderstate.SetBoneIndexBase(-1); - renderstate.SetModelMatrix(VSMatrix::identity(), VSMatrix::identity()); + + // Don't do this so we don't have to track matrix changes in MeshBuilder (only models uses matrices like this) + //renderstate.SetModelMatrix(VSMatrix::identity(), VSMatrix::identity()); } IModelVertexBuffer* MeshBuilderModelRender::CreateVertexBuffer(bool needindex, bool singleframe) diff --git a/src/common/rendering/hwrenderer/data/hw_meshbuilder.h b/src/common/rendering/hwrenderer/data/hw_meshbuilder.h index ebc9ce252..bdb7ceecc 100644 --- a/src/common/rendering/hwrenderer/data/hw_meshbuilder.h +++ b/src/common/rendering/hwrenderer/data/hw_meshbuilder.h @@ -111,7 +111,7 @@ public: // Buffers int SetViewpoint(const HWViewpointUniforms& vp) override { return 0; } void SetViewpoint(int index) override { } - void SetModelMatrix(const VSMatrix& matrix, const VSMatrix& normalMatrix) override { } + void SetModelMatrix(const VSMatrix& matrix, const VSMatrix& normalMatrix) override { objectToWorld = matrix; normalToWorld = normalMatrix; } void SetTextureMatrix(const VSMatrix& matrix) override { mTextureMatrix = matrix; } int UploadLights(const FDynLightData& lightdata) override { return -1; } int UploadBones(const TArray& bones) override { return -1; } @@ -150,6 +150,9 @@ public: TArray mVertices; TArray mIndexes; + VSMatrix objectToWorld = VSMatrix::identity(); + VSMatrix normalToWorld = VSMatrix::identity(); + private: void Apply(); diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index 4f0876f1a..0b276c89b 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -7,6 +7,7 @@ #include "c_dispatch.h" #include "models.h" #include "a_dynlight.h" +#include "r_sky.h" #include "hw_renderstate.h" #include "hw_vertexbuilder.h" #include "hw_dynlightdata.h" @@ -236,6 +237,8 @@ CCMD(surfaceinfo) EXTERN_CVAR(Float, lm_scale); +CVAR(Bool, lm_models, false, 0); + ///////////////////////////////////////////////////////////////////////////// DoomLevelMesh::DoomLevelMesh(FLevelLocals& doomMap) @@ -277,30 +280,22 @@ DoomLevelMesh::DoomLevelMesh(FLevelLocals& doomMap) tile.NeedsInitialBake = true; } -#if 0 // Collect all the models we want to bake into the level mesh - auto it = doomMap.GetThinkerIterator(); - AActor* thing; - while ((thing = it.Next()) != nullptr) + if (lm_models) { - bool isPicnumOverride = thing->picnum.isValid(); - int spritenum = thing->sprite; - FSpriteModelFrame* modelframe = isPicnumOverride ? nullptr : FindModelFrame(thing, spritenum, thing->frame, !!(thing->flags & MF_DROPPED)); - if (modelframe && modelframe->modelIDs.size() != 0) + auto it = doomMap.GetThinkerIterator(); + AActor* thing; + while ((thing = it.Next()) != nullptr) { - DVector3 thingpos = thing->Pos(); - double x = thingpos.X + thing->WorldOffset.X; - double z = thingpos.Z + thing->WorldOffset.Z; - double y = thingpos.Y + thing->WorldOffset.Y; - - MeshBuilder builder; - MeshBuilderModelRender renderer(builder); - RenderModel(&renderer, x, y, z, modelframe, thing, 0.0); - - // To do: add the MeshBuilder output as surfaces + bool isPicnumOverride = thing->picnum.isValid(); + int spritenum = thing->sprite; + FSpriteModelFrame* modelframe = isPicnumOverride ? nullptr : FindModelFrame(thing, spritenum, thing->frame, !!(thing->flags & MF_DROPPED)); + if (modelframe && modelframe->modelIDs.size() != 0) + { + CreateModelSurfaces(thing, modelframe); + } } } -#endif CreateCollision(); UploadPortals(); @@ -313,6 +308,162 @@ DoomLevelMesh::~DoomLevelMesh() { } +void DoomLevelMesh::CreateModelSurfaces(AActor* thing, FSpriteModelFrame* modelframe) +{ + DVector3 thingpos = thing->Pos(); + double x = thingpos.X + thing->WorldOffset.X; + double z = thingpos.Z + thing->WorldOffset.Z; + double y = thingpos.Y + thing->WorldOffset.Y; + + state.mSortedLists.clear(); + state.mVertices.Clear(); + state.mIndexes.Clear(); + + state.SetDepthMask(true); + state.EnableFog(true); + state.SetRenderStyle(STYLE_Source); + + state.SetDepthFunc(DF_LEqual); + state.ClearDepthBias(); + state.EnableTexture(true); + state.EnableBrightmap(true); + state.AlphaFunc(Alpha_GEqual, 0.f); + + MeshBuilderModelRender renderer(state); + RenderModel(&renderer, x, y, z, modelframe, thing, 0.0); + + // Flatten the model as we need lightmap UV coordinates uniquely for every vertex for each surface. + int numUniforms = 0; + int numSurfaces = 0; + for (auto& it : state.mSortedLists) + { + numUniforms++; + for (MeshDrawCommand& command : it.second.mDraws) + { + if (command.DrawType == DT_Triangles) + { + numSurfaces += command.Count / 3; + } + } + for (MeshDrawCommand& command : it.second.mIndexedDraws) + { + if (command.DrawType == DT_Triangles) + { + numSurfaces += command.Count / 3; + } + } + } + + VSMatrix objectToWorld = state.objectToWorld; + //VSMatrix normalToWorld = state.normalToWorld; + + GeometryAllocInfo ginfo = AllocGeometry(numSurfaces * 3, numSurfaces * 3); + UniformsAllocInfo uinfo = AllocUniforms(numUniforms); + SurfaceAllocInfo sinfo = AllocSurface(numUniforms); // Note: this is not a typo. We currently only create a SurfaceInfo for each apply state. + + SurfaceUniforms* curUniforms = uinfo.Uniforms; + SurfaceLightUniforms* curLightUniforms = uinfo.LightUniforms; + FMaterialState* curMaterial = uinfo.Materials; + + int pipelineID = 0; + int uniformsIndex = uinfo.Start; + int vertIndex = ginfo.VertexStart; + for (auto& it : state.mSortedLists) + { + const MeshApplyState& applyState = it.first; + + pipelineID = screen->GetLevelMeshPipelineID(applyState.applyData, applyState.surfaceUniforms, applyState.material); + + auto indexBuffer = applyState.indexBuffer->Data.data(); + auto vertexBuffer = applyState.vertexBuffer->Data.data(); + + for (MeshDrawCommand& command : it.second.mDraws) + { + if (command.DrawType == DT_Triangles) + { + int numVertices = command.Count / 3 * 3; + for (int i = 0; i < numVertices; i++) + { + *(ginfo.Indexes++) = vertIndex + i; + } + for (int i = command.Start, end = command.Start + numVertices; i < end; i++) + { + const FModelVertex& vertIn = vertexBuffer[i]; + FVector4 pos = objectToWorld * FVector4(vertIn.x, vertIn.y, vertIn.z, 1.0f); + FFlatVertex vertOut; + vertOut.x = pos.X; + vertOut.y = pos.Z; + vertOut.z = pos.Y; + vertOut.u = vertIn.u; + vertOut.v = vertIn.v; + vertOut.lindex = -1.0f; + *(ginfo.Vertices++) = vertOut; + *(ginfo.UniformIndexes++) = uniformsIndex; + } + vertIndex += numVertices; + } + } + + for (MeshDrawCommand& command : it.second.mIndexedDraws) + { + if (command.DrawType == DT_Triangles) + { + int numVertices = command.Count / 3 * 3; + for (int i = 0; i < numVertices; i++) + { + *(ginfo.Indexes++) = vertIndex + i; + } + for (int i = command.Start, end = command.Start + numVertices; i < end; i++) + { + const FModelVertex& vertIn = vertexBuffer[indexBuffer[i]]; + FVector4 pos = objectToWorld * FVector4(vertIn.x, vertIn.y, vertIn.z, 1.0f); + FFlatVertex vertOut; + vertOut.x = pos.X; + vertOut.y = pos.Z; + vertOut.z = pos.Y; + vertOut.u = vertIn.u; + vertOut.v = vertIn.v; + vertOut.lindex = -1.0f; + *(ginfo.Vertices++) = vertOut; + *(ginfo.UniformIndexes++) = uniformsIndex; + } + vertIndex += numVertices; + } + } + + *(curUniforms++) = applyState.surfaceUniforms; + *(curMaterial++) = applyState.material; + + curLightUniforms->uVertexColor = applyState.surfaceUniforms.uVertexColor; + curLightUniforms->uDesaturationFactor = applyState.surfaceUniforms.uDesaturationFactor; + curLightUniforms->uLightLevel = applyState.surfaceUniforms.uLightLevel; + curLightUniforms++; + + sinfo.Surface->PipelineID = pipelineID; + sinfo.Surface->SectorGroup = thing->Sector ? sectorGroup[thing->Sector->Index()] : 0; + sinfo.Surface->Alpha = float(thing->Alpha); + sinfo.Surface->MeshLocation.StartVertIndex = ginfo.VertexStart; + sinfo.Surface->MeshLocation.StartElementIndex = ginfo.IndexStart; + sinfo.Surface->MeshLocation.NumVerts = ginfo.VertexCount; + sinfo.Surface->MeshLocation.NumElements = ginfo.IndexCount; + sinfo.Surface->Plane = FVector4(0.0f, 0.0f, 0.0f, 0.0f); + sinfo.Surface->Texture = TexMan.GetGameTexture(skyflatnum); // To do: how to get a FGameTexture from a material? + sinfo.Surface->PortalIndex = 0; + sinfo.Surface->IsSky = false; + sinfo.Surface->Bounds = GetBoundsFromSurface(*sinfo.Surface); + sinfo.Surface->LightList.Pos = 0; // To do: how to manage the light list for baked models? + sinfo.Surface->LightList.Count = 0; + sinfo.Surface->LightmapTileIndex = -1; // To do: create tiles for the model surfaces? Current SurfaceInfo is too big for this + + for (int i = ginfo.IndexStart / 3, end = (ginfo.IndexStart + ginfo.IndexCount) / 3; i < end; i++) + Mesh.SurfaceIndexes[i] = sinfo.Index; + + uniformsIndex++; + } + + // To do: save ginfo, uinfo and sinfo in a Models list like we are doing for sides and flats? We need that if we are to ever update them +} + void DoomLevelMesh::BuildSideVisibilityLists(FLevelLocals& doomMap) { VisibleSides.resize(doomMap.sides.size()); diff --git a/src/rendering/hwrenderer/doom_levelmesh.h b/src/rendering/hwrenderer/doom_levelmesh.h index 54fe41f3a..8c834f820 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.h +++ b/src/rendering/hwrenderer/doom_levelmesh.h @@ -231,6 +231,8 @@ private: void SetSubsectorLightmap(int surfaceIndex); void SetSideLightmap(int surfaceIndex); + void CreateModelSurfaces(AActor* thing, FSpriteModelFrame* modelframe); + void CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, TArray& list, LevelMeshDrawType drawType, unsigned int sectorIndex, const LightListAllocInfo& lightlist); void CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, TArray& list, LevelMeshDrawType drawType, bool translucent, unsigned int sectorIndex, const LightListAllocInfo& lightlist, int lightlistSection);