Remove the submesh class as that caused more problems than it solved

This commit is contained in:
Magnus Norddahl 2024-01-29 21:56:42 +01:00
commit 7bc2ed5436
20 changed files with 836 additions and 994 deletions

View file

@ -1,7 +1,6 @@
#include "templates.h"
#include "doom_levelmesh.h"
#include "doom_levelsubmesh.h"
#include "g_levellocals.h"
#include "texturemanager.h"
#include "playsim/p_lnspec.h"
@ -9,6 +8,13 @@
#include "g_levellocals.h"
#include "a_dynlight.h"
#include "hw_renderstate.h"
#include "hw_vertexbuilder.h"
#include "hwrenderer/scene/hw_drawstructs.h"
#include "hwrenderer/scene/hw_drawinfo.h"
#include "hwrenderer/scene/hw_walldispatcher.h"
#include "hwrenderer/scene/hw_flatdispatcher.h"
#include "common/rendering/hwrenderer/data/hw_meshbuilder.h"
#include <unordered_map>
static bool RequireLevelMesh()
{
@ -42,7 +48,7 @@ ADD_STAT(lightmap)
return out;
}
uint32_t atlasPixelCount = levelMesh->StaticMesh->AtlasPixelCount();
uint32_t atlasPixelCount = levelMesh->AtlasPixelCount();
auto stats = levelMesh->GatherTilePixelStats();
out.Format("Surfaces: %u (awaiting updates: %u)\nSurface pixel area to update: %u\nSurface pixel area: %u\nAtlas pixel area: %u\nAtlas efficiency: %.4f%%",
@ -67,13 +73,13 @@ CCMD(invalidatelightmap)
if (!RequireLightmap()) return;
int count = 0;
for (auto& tile : level.levelMesh->StaticMesh->LightmapTiles)
for (auto& tile : level.levelMesh->LightmapTiles)
{
if (!tile.NeedsUpdate)
++count;
tile.NeedsUpdate = true;
}
Printf("Marked %d out of %d tiles for update.\n", count, level.levelMesh->StaticMesh->LightmapTiles.Size());
Printf("Marked %d out of %d tiles for update.\n", count, level.levelMesh->LightmapTiles.Size());
}
void PrintSurfaceInfo(const DoomLevelMeshSurface* surface)
@ -82,10 +88,10 @@ void PrintSurfaceInfo(const DoomLevelMeshSurface* surface)
auto gameTexture = surface->Texture;
Printf("Surface %d (%p)\n Type: %d, TypeIndex: %d, ControlSector: %d\n", surface->Submesh->GetSurfaceIndex(surface), surface, surface->Type, surface->TypeIndex, surface->ControlSector ? surface->ControlSector->Index() : -1);
Printf("Surface %d (%p)\n Type: %d, TypeIndex: %d, ControlSector: %d\n", level.levelMesh->GetSurfaceIndex(surface), surface, surface->Type, surface->TypeIndex, surface->ControlSector ? surface->ControlSector->Index() : -1);
if (surface->LightmapTileIndex >= 0)
{
LightmapTile* tile = &surface->Submesh->LightmapTiles[surface->LightmapTileIndex];
LightmapTile* tile = &level.levelMesh->LightmapTiles[surface->LightmapTileIndex];
Printf(" Atlas page: %d, x:%d, y:%d\n", tile->AtlasLocation.ArrayIndex, tile->AtlasLocation.X, tile->AtlasLocation.Y);
Printf(" Pixels: %dx%d (area: %d)\n", tile->AtlasLocation.Width, tile->AtlasLocation.Height, tile->AtlasLocation.Area());
Printf(" Sample dimension: %d\n", tile->SampleDimension);
@ -142,24 +148,46 @@ EXTERN_CVAR(Float, lm_scale);
DoomLevelMesh::DoomLevelMesh(FLevelLocals& doomMap)
{
// Remove the empty mesh added in the LevelMesh constructor
Mesh.Vertices.clear();
Mesh.Indexes.clear();
SunColor = doomMap.SunColor; // TODO keep only one copy?
SunDirection = doomMap.SunDirection;
BuildSectorGroups(doomMap);
CreatePortals(doomMap);
StaticMesh = std::make_unique<DoomLevelSubmesh>(this, doomMap, true);
DynamicMesh = std::make_unique<DoomLevelSubmesh>(this, doomMap, false);
LightmapSampleDistance = doomMap.LightmapSampleDistance;
CreateSurfaces(doomMap);
LinkSurfaces(doomMap);
SortIndexes();
BuildTileSurfaceLists();
Mesh.DynamicIndexStart = Mesh.Indexes.size();
UpdateCollision();
// Assume double the size of the static mesh will be enough for anything dynamic.
Mesh.MaxVertices = std::max(Mesh.Vertices.size() * 2, (size_t)10000);
Mesh.MaxIndexes = std::max(Mesh.Indexes.size() * 2, (size_t)10000);
Mesh.MaxSurfaces = std::max(Mesh.SurfaceIndexes.size() * 2, (size_t)10000);
Mesh.MaxUniforms = std::max(Mesh.Uniforms.size() * 2, (size_t)10000);
Mesh.MaxSurfaceIndexes = std::max(Mesh.SurfaceIndexes.size() * 2, (size_t)10000);
Mesh.MaxNodes = std::max(Collision->get_nodes().size() * 2, (size_t)10000);
}
void DoomLevelMesh::BeginFrame(FLevelLocals& doomMap)
{
#if 0
static_cast<DoomLevelSubmesh*>(DynamicMesh.get())->Update(doomMap);
if (doomMap.lightmaps)
{
DynamicMesh->SetupTileTransforms();
DynamicMesh->PackLightmapAtlas(StaticMesh->LMTextureCount);
}
#endif
}
bool DoomLevelMesh::TraceSky(const FVector3& start, FVector3 direction, float dist)
@ -252,24 +280,585 @@ FLightNode* DoomLevelMesh::GetSurfaceLightNode(const DoomLevelMeshSurface* dooms
return node;
}
void DoomLevelMesh::CreateSurfaces(FLevelLocals& doomMap)
{
// We can't use side->segs since it is null.
TArray<std::pair<subsector_t*, seg_t*>> sideSegs(doomMap.sides.Size(), true);
for (unsigned int i = 0; i < doomMap.subsectors.Size(); i++)
{
subsector_t* sub = &doomMap.subsectors[i];
sector_t* sector = sub->sector;
for (int i = 0, count = sub->numlines; i < count; i++)
{
seg_t* seg = sub->firstline + i;
if (seg->sidedef)
sideSegs[seg->sidedef->Index()] = { sub, seg };
}
}
MeshBuilder state;
std::map<LightmapTileBinding, int> bindings;
// Create surface objects for all sides
for (unsigned int i = 0; i < doomMap.sides.Size(); i++)
{
side_t* side = &doomMap.sides[i];
bool isPolyLine = !!(side->Flags & WALLF_POLYOBJ);
if (isPolyLine)
continue;
subsector_t* sub = sideSegs[i].first;
seg_t* seg = sideSegs[i].second;
if (!seg)
continue;
sector_t* front = side->sector;
sector_t* back = (side->linedef->frontsector == front) ? side->linedef->backsector : side->linedef->frontsector;
HWMeshHelper result;
HWWallDispatcher disp(&doomMap, &result, getRealLightmode(&doomMap, true));
HWWall wall;
wall.sub = sub;
wall.Process(&disp, state, seg, front, back);
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
state.EnableTexture(true);
state.EnableBrightmap(true);
state.AlphaFunc(Alpha_GEqual, 0.f);
CreateWallSurface(side, disp, state, bindings, result.list, false, true);
for (HWWall& portal : result.portals)
{
WallPortals.Push(portal);
}
CreateWallSurface(side, disp, state, bindings, result.portals, true, false);
/*
// final pass: translucent stuff
state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold);
state.SetRenderStyle(STYLE_Translucent);
CreateWallSurface(side, disp, state, bindings, result.translucent, false, true);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.SetRenderStyle(STYLE_Normal);
*/
}
// Create surfaces for all flats
for (unsigned int i = 0; i < doomMap.sectors.Size(); i++)
{
sector_t* sector = &doomMap.sectors[i];
if (sector->subsectors[0]->flags & SSECF_POLYORG)
continue;
for (FSection& section : doomMap.sections.SectionsForSector(i))
{
int sectionIndex = doomMap.sections.SectionIndex(&section);
HWFlatMeshHelper result;
HWFlatDispatcher disp(&doomMap, &result, getRealLightmode(&doomMap, true));
HWFlat flat;
flat.section = &section;
flat.ProcessSector(&disp, state, sector);
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
state.EnableTexture(true);
state.EnableBrightmap(true);
CreateFlatSurface(disp, state, bindings, result.list);
CreateFlatSurface(disp, state, bindings, result.portals, true);
// final pass: translucent stuff
state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold);
state.SetRenderStyle(STYLE_Translucent);
CreateFlatSurface(disp, state, bindings, result.translucentborder);
state.SetDepthMask(false);
CreateFlatSurface(disp, state, bindings, result.translucent);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.SetDepthMask(true);
state.SetRenderStyle(STYLE_Normal);
}
}
}
void DoomLevelMesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, std::map<LightmapTileBinding, int>& bindings, TArray<HWWall>& list, bool isSky, bool translucent)
{
for (HWWall& wallpart : list)
{
if (isSky)
{
state.SetEffect(EFF_PORTAL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Normal);
wallpart.MakeVertices(state, false);
wallpart.RenderWall(state, HWWall::RWF_BLANK);
wallpart.vertcount = 0;
wallpart.LevelMeshInfo.Type = ST_NONE;
wallpart.LevelMeshInfo.ControlSector = nullptr;
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
else
{
if (wallpart.texture && wallpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
wallpart.DrawWall(&disp, state, translucent);
}
int pipelineID = 0;
int startVertIndex = Mesh.Vertices.Size();
int startElementIndex = Mesh.Indexes.Size();
for (auto& it : state.mSortedLists)
{
const MeshApplyState& applyState = it.first;
pipelineID = screen->GetLevelMeshPipelineID(applyState.applyData, applyState.surfaceUniforms, applyState.material);
int uniformsIndex = Mesh.Uniforms.Size();
Mesh.Uniforms.Push(applyState.surfaceUniforms);
Mesh.Materials.Push(applyState.material);
for (MeshDrawCommand& command : it.second.mDraws)
{
for (int i = command.Start, end = command.Start + command.Count; i < end; i++)
{
Mesh.Vertices.Push(state.mVertices[i]);
Mesh.UniformIndexes.Push(uniformsIndex);
}
if (command.DrawType == DT_TriangleFan)
{
for (int i = 2, count = command.Count; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex + i);
}
}
}
}
state.mSortedLists.clear();
state.mVertices.Clear();
state.mIndexes.Clear();
FVector2 v1 = FVector2(side->V1()->fPos());
FVector2 v2 = FVector2(side->V2()->fPos());
FVector2 N = FVector2(v2.Y - v1.Y, v1.X - v2.X).Unit();
DoomLevelMeshSurface surf;
surf.Type = wallpart.LevelMeshInfo.Type;
surf.ControlSector = wallpart.LevelMeshInfo.ControlSector;
surf.TypeIndex = side->Index();
surf.Side = side;
surf.AlwaysUpdate = !!(side->sector->Flags & SECF_LM_DYNAMIC);
surf.SectorGroup = sectorGroup[side->sector->Index()];
surf.Alpha = float(side->linedef->alpha);
surf.MeshLocation.StartVertIndex = startVertIndex;
surf.MeshLocation.StartElementIndex = startElementIndex;
surf.MeshLocation.NumVerts = Mesh.Vertices.Size() - startVertIndex;
surf.MeshLocation.NumElements = Mesh.Indexes.Size() - startElementIndex;
surf.Plane = FVector4(N.X, N.Y, 0.0f, v1 | N);
surf.Texture = wallpart.texture;
surf.PipelineID = pipelineID;
surf.PortalIndex = isSky ? linePortals[side->linedef->Index()] : 0;
surf.IsSky = isSky;
surf.Bounds = GetBoundsFromSurface(surf);
surf.LightmapTileIndex = disp.Level->lightmaps ? AddSurfaceToTile(surf, bindings) : -1;
Surfaces.Push(surf);
}
}
int DoomLevelMesh::AddSurfaceToTile(const DoomLevelMeshSurface& surf, std::map<LightmapTileBinding, int>& bindings)
{
if (surf.IsSky)
return -1;
LightmapTileBinding binding;
binding.Type = surf.Type;
binding.TypeIndex = surf.TypeIndex;
binding.ControlSector = surf.ControlSector ? surf.ControlSector->Index() : (int)0xffffffffUL;
auto it = bindings.find(binding);
if (it != bindings.end())
{
int index = it->second;
LightmapTile& tile = LightmapTiles[index];
tile.Bounds.min.X = std::min(tile.Bounds.min.X, surf.Bounds.min.X);
tile.Bounds.min.Y = std::min(tile.Bounds.min.Y, surf.Bounds.min.Y);
tile.Bounds.min.Z = std::min(tile.Bounds.min.Z, surf.Bounds.min.Z);
tile.Bounds.max.X = std::max(tile.Bounds.max.X, surf.Bounds.max.X);
tile.Bounds.max.Y = std::max(tile.Bounds.max.Y, surf.Bounds.max.Y);
tile.Bounds.max.Z = std::max(tile.Bounds.max.Z, surf.Bounds.max.Z);
return index;
}
else
{
int index = LightmapTiles.Size();
LightmapTile tile;
tile.Binding = binding;
tile.Bounds = surf.Bounds;
tile.Plane = surf.Plane;
tile.SampleDimension = GetSampleDimension(surf);
LightmapTiles.Push(tile);
bindings[binding] = index;
return index;
}
}
int DoomLevelMesh::GetSampleDimension(const DoomLevelMeshSurface& surf)
{
uint16_t sampleDimension = 0; // To do: something seems to have gone missing with the sample dimension!
if (sampleDimension <= 0)
{
sampleDimension = LightmapSampleDistance;
}
sampleDimension = uint16_t(max(int(roundf(float(sampleDimension) / max(1.0f / 4, float(lm_scale)))), 1));
// Round to nearest power of two
uint32_t n = uint16_t(sampleDimension);
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n = (n + 1) >> 1;
sampleDimension = uint16_t(n) ? uint16_t(n) : uint16_t(0xFFFF);
return sampleDimension;
}
void DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, std::map<LightmapTileBinding, int>& bindings, TArray<HWFlat>& list, bool isSky)
{
for (HWFlat& flatpart : list)
{
if (isSky)
{
state.SetEffect(EFF_PORTAL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Normal);
flatpart.DrawSubsectors(&disp, state);
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
else
{
if (flatpart.texture && flatpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
flatpart.DrawFlat(&disp, state, false);
}
VSMatrix textureMatrix;
textureMatrix.loadIdentity();
int pipelineID = 0;
int uniformsIndex = 0;
bool foundDraw = false;
for (auto& it : state.mSortedLists)
{
const MeshApplyState& applyState = it.first;
pipelineID = screen->GetLevelMeshPipelineID(applyState.applyData, applyState.surfaceUniforms, applyState.material);
uniformsIndex = Mesh.Uniforms.Size();
textureMatrix = applyState.textureMatrix;
Mesh.Uniforms.Push(applyState.surfaceUniforms);
Mesh.Materials.Push(applyState.material);
foundDraw = true;
break;
}
state.mSortedLists.clear();
state.mVertices.Clear();
state.mIndexes.Clear();
if (!foundDraw)
continue;
DoomLevelMeshSurface surf;
surf.Type = flatpart.ceiling ? ST_CEILING : ST_FLOOR;
surf.ControlSector = flatpart.controlsector ? flatpart.controlsector->model : nullptr;
surf.AlwaysUpdate = !!(flatpart.sector->Flags & SECF_LM_DYNAMIC);
surf.SectorGroup = sectorGroup[flatpart.sector->Index()];
surf.Alpha = flatpart.alpha;
surf.Texture = flatpart.texture;
surf.PipelineID = pipelineID;
surf.PortalIndex = sectorPortals[flatpart.ceiling][flatpart.sector->Index()];
surf.IsSky = isSky;
auto plane = surf.ControlSector ? surf.ControlSector->GetSecPlane(!flatpart.ceiling) : flatpart.sector->GetSecPlane(flatpart.ceiling);
surf.Plane = FVector4((float)plane.Normal().X, (float)plane.Normal().Y, (float)plane.Normal().Z, -(float)plane.D);
if (surf.ControlSector)
surf.Plane = -surf.Plane;
float skyZ = flatpart.ceiling ? 32768.0f : -32768.0f;
for (subsector_t* sub : flatpart.section->subsectors)
{
if (sub->numlines < 3)
continue;
int startVertIndex = Mesh.Vertices.Size();
int startElementIndex = Mesh.Indexes.Size();
for (int i = 0, end = sub->numlines; i < end; i++)
{
auto& vt = sub->firstline[end - 1 - i].v1;
FVector3 pt((float)vt->fX(), (float)vt->fY(), isSky ? skyZ : (float)plane.ZatPoint(vt));
FVector4 uv = textureMatrix * FVector4(pt.X * (1.0f / 64.0f), pt.Y * (-1.0f / 64.0f), 0.0f, 1.0f);
FFlatVertex ffv;
ffv.x = pt.X;
ffv.y = pt.Y;
ffv.z = pt.Z;
ffv.u = uv.X;
ffv.v = uv.Y;
ffv.lu = 0.0f;
ffv.lv = 0.0f;
ffv.lindex = -1.0f;
Mesh.Vertices.Push(ffv);
Mesh.UniformIndexes.Push(uniformsIndex);
}
if (flatpart.ceiling)
{
for (int i = 2, count = sub->numlines; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex + i);
}
}
else
{
for (int i = 2, count = sub->numlines; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex + i);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex);
}
}
surf.TypeIndex = sub->Index();
surf.Subsector = sub;
surf.MeshLocation.StartVertIndex = startVertIndex;
surf.MeshLocation.StartElementIndex = startElementIndex;
surf.MeshLocation.NumVerts = sub->numlines;
surf.MeshLocation.NumElements = (sub->numlines - 2) * 3;
surf.Bounds = GetBoundsFromSurface(surf);
surf.LightmapTileIndex = disp.Level->lightmaps ? AddSurfaceToTile(surf, bindings) : -1;
Surfaces.Push(surf);
}
}
}
#if 0
void DoomLevelMesh::CreateDynamicSurfaces(FLevelLocals& doomMap)
{
// Look for polyobjects
for (unsigned int i = 0; i < doomMap.lines.Size(); i++)
{
side_t* side = doomMap.lines[i].sidedef[0];
bool isPolyLine = !!(side->Flags & WALLF_POLYOBJ);
if (!isPolyLine)
continue;
// Make sure we have a surface array on the polyobj sidedef
if (!side->surface)
{
auto array = std::make_unique<DoomLevelMeshSurface * []>(4);
memset(array.get(), 0, sizeof(DoomLevelMeshSurface*));
side->surface = array.get();
PolyLMSurfaces.Push(std::move(array));
}
CreateSideSurfaces(doomMap, side);
}
}
#endif
void DoomLevelMesh::SortIndexes()
{
// Order surfaces by pipeline
std::unordered_map<int64_t, TArray<int>> pipelineSurfaces;
for (size_t i = 0; i < Surfaces.Size(); i++)
{
DoomLevelMeshSurface* s = &Surfaces[i];
pipelineSurfaces[(int64_t(s->PipelineID) << 32) | int64_t(s->IsSky)].Push(i);
}
// Create reorder surface indexes by pipeline and create a draw range for each
TArray<uint32_t> sortedIndexes;
for (const auto& it : pipelineSurfaces)
{
LevelSubmeshDrawRange range;
range.PipelineID = it.first >> 32;
range.Start = sortedIndexes.Size();
// Move indexes to new array
for (unsigned int i : it.second)
{
DoomLevelMeshSurface& s = Surfaces[i];
unsigned int start = s.MeshLocation.StartElementIndex;
unsigned int count = s.MeshLocation.NumElements;
s.MeshLocation.StartElementIndex = sortedIndexes.Size();
for (unsigned int j = 0; j < count; j++)
{
sortedIndexes.Push(Mesh.Indexes[start + j]);
}
for (unsigned int j = 0; j < count; j += 3)
{
Mesh.SurfaceIndexes.Push((int)i);
}
}
range.Count = sortedIndexes.Size() - range.Start;
if ((it.first & 1) == 0)
DrawList.Push(range);
else
PortalList.Push(range);
}
Mesh.Indexes.Swap(sortedIndexes);
}
void DoomLevelMesh::LinkSurfaces(FLevelLocals& doomMap)
{
for (auto& surface : Surfaces)
{
if (surface.Type == ST_FLOOR || surface.Type == ST_CEILING)
{
SetSubsectorLightmap(&surface);
}
else
{
SetSideLightmap(&surface);
}
}
}
void DoomLevelMesh::SetSubsectorLightmap(DoomLevelMeshSurface* surface)
{
if (surface->Subsector->firstline && surface->Subsector->firstline->sidedef)
surface->Subsector->firstline->sidedef->sector->HasLightmaps = true;
if (!surface->ControlSector)
{
int index = surface->Type == ST_CEILING ? 1 : 0;
surface->Subsector->surface[index][0] = surface;
}
else
{
int index = surface->Type == ST_CEILING ? 0 : 1;
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->surface[index][i + 1] = surface;
}
}
}
}
void DoomLevelMesh::SetSideLightmap(DoomLevelMeshSurface* surface)
{
if (!surface->ControlSector)
{
if (surface->Type == ST_UPPERSIDE)
{
surface->Side->surface[0] = surface;
}
else if (surface->Type == ST_MIDDLESIDE)
{
surface->Side->surface[1] = surface;
surface->Side->surface[2] = surface;
}
else if (surface->Type == ST_LOWERSIDE)
{
surface->Side->surface[3] = surface;
}
}
else
{
const auto& ffloors = surface->Side->sector->e->XFloor.ffloors;
for (unsigned int i = 0; i < ffloors.Size(); i++)
{
if (ffloors[i]->model == surface->ControlSector)
{
surface->Side->surface[4 + i] = surface;
}
}
}
}
BBox DoomLevelMesh::GetBoundsFromSurface(const LevelMeshSurface& surface) const
{
BBox bounds;
bounds.Clear();
for (int i = int(surface.MeshLocation.StartVertIndex); i < int(surface.MeshLocation.StartVertIndex) + surface.MeshLocation.NumVerts; i++)
{
FVector3 v = Mesh.Vertices[(int)i].fPos();
bounds.min.X = std::min(bounds.min.X, v.X);
bounds.min.Y = std::min(bounds.min.Y, v.Y);
bounds.min.Z = std::min(bounds.min.Z, v.Z);
bounds.max.X = std::max(bounds.max.X, v.X);
bounds.max.Y = std::max(bounds.max.Y, v.Y);
bounds.max.Z = std::max(bounds.max.Z, v.Z);
}
return bounds;
}
void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilename) const
{
DoomLevelSubmesh* submesh = static_cast<DoomLevelSubmesh*>(StaticMesh.get());
auto f = fopen(objFilename.GetChars(), "w");
fprintf(f, "# DoomLevelMesh debug export\n");
fprintf(f, "# Vertices: %u, Indexes: %u, Surfaces: %u\n", submesh->Mesh.Vertices.Size(), submesh->Mesh.Indexes.Size(), submesh->Surfaces.Size());
fprintf(f, "# Vertices: %u, Indexes: %u, Surfaces: %u\n", Mesh.Vertices.Size(), Mesh.Indexes.Size(), Surfaces.Size());
fprintf(f, "mtllib %s\n", mtlFilename.GetChars());
double scale = 1 / 10.0;
for (const auto& v : submesh->Mesh.Vertices)
for (const auto& v : Mesh.Vertices)
{
fprintf(f, "v %f %f %f\n", v.x * scale, v.y * scale, v.z * scale);
}
for (const auto& v : submesh->Mesh.Vertices)
for (const auto& v : Mesh.Vertices)
{
fprintf(f, "vt %f %f\n", v.lu, v.lv);
}
@ -302,15 +891,15 @@ void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilen
bool useErrorMaterial = false;
int highestUsedAtlasPage = -1;
for (unsigned i = 0, count = submesh->Mesh.Indexes.Size(); i + 2 < count; i += 3)
for (unsigned i = 0, count = Mesh.Indexes.Size(); i + 2 < count; i += 3)
{
auto index = submesh->Mesh.SurfaceIndexes[i / 3];
auto index = Mesh.SurfaceIndexes[i / 3];
if (index != lastSurfaceIndex)
{
lastSurfaceIndex = index;
if (unsigned(index) >= submesh->Surfaces.Size())
if (unsigned(index) >= Surfaces.Size())
{
fprintf(f, "o Surface[%d] (bad index)\n", index);
fprintf(f, "usemtl error\n");
@ -319,12 +908,12 @@ void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilen
}
else
{
const auto& surface = submesh->Surfaces[index];
const auto& surface = Surfaces[index];
fprintf(f, "o Surface[%d] %s %d%s\n", index, name(surface.Type), surface.TypeIndex, surface.IsSky ? " sky" : "");
if (surface.LightmapTileIndex >= 0)
{
auto& tile = submesh->LightmapTiles[surface.LightmapTileIndex];
auto& tile = LightmapTiles[surface.LightmapTileIndex];
fprintf(f, "usemtl lightmap%d\n", tile.AtlasLocation.ArrayIndex);
if (tile.AtlasLocation.ArrayIndex > highestUsedAtlasPage)
@ -337,9 +926,9 @@ void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilen
// fprintf(f, "f %d %d %d\n", MeshElements[i] + 1, MeshElements[i + 1] + 1, MeshElements[i + 2] + 1);
fprintf(f, "f %d/%d %d/%d %d/%d\n",
submesh->Mesh.Indexes[i + 0] + 1, submesh->Mesh.Indexes[i + 0] + 1,
submesh->Mesh.Indexes[i + 1] + 1, submesh->Mesh.Indexes[i + 1] + 1,
submesh->Mesh.Indexes[i + 2] + 1, submesh->Mesh.Indexes[i + 2] + 1);
Mesh.Indexes[i + 0] + 1, Mesh.Indexes[i + 0] + 1,
Mesh.Indexes[i + 1] + 1, Mesh.Indexes[i + 1] + 1,
Mesh.Indexes[i + 2] + 1, Mesh.Indexes[i + 2] + 1);
}