Bake models into the level mesh

This commit is contained in:
Magnus Norddahl 2025-06-11 03:38:40 +02:00
commit 3f1dcce970
5 changed files with 188 additions and 28 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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<VSMatrix>& bones) override { return -1; }
@ -150,6 +150,9 @@ public:
TArray<FFlatVertex> mVertices;
TArray<uint32_t> mIndexes;
VSMatrix objectToWorld = VSMatrix::identity();
VSMatrix normalToWorld = VSMatrix::identity();
private:
void Apply();

View file

@ -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>();
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>();
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());

View file

@ -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<HWWall>& list, LevelMeshDrawType drawType, unsigned int sectorIndex, const LightListAllocInfo& lightlist);
void CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, TArray<HWFlat>& list, LevelMeshDrawType drawType, bool translucent, unsigned int sectorIndex, const LightListAllocInfo& lightlist, int lightlistSection);