784 lines
20 KiB
C++
784 lines
20 KiB
C++
|
|
#include "templates.h"
|
|
#include "doom_levelmesh.h"
|
|
#include "g_levellocals.h"
|
|
#include "texturemanager.h"
|
|
#include "playsim/p_lnspec.h"
|
|
|
|
|
|
#include "c_dispatch.h"
|
|
#include "g_levellocals.h"
|
|
|
|
#include "common/rendering/vulkan/accelstructs/vk_lightmap.h"
|
|
|
|
CCMD(dumplevelmesh)
|
|
{
|
|
if (level.levelMesh)
|
|
{
|
|
level.levelMesh->DumpMesh(FString("levelmesh.obj"));
|
|
Printf("Level mesh exported.");
|
|
}
|
|
else
|
|
{
|
|
Printf("No level mesh. Perhaps your level has no lightmap loaded?");
|
|
}
|
|
}
|
|
|
|
DoomLevelMesh::DoomLevelMesh(FLevelLocals &doomMap)
|
|
{
|
|
for (unsigned int i = 0; i < doomMap.sides.Size(); i++)
|
|
{
|
|
CreateSideSurfaces(doomMap, &doomMap.sides[i]);
|
|
}
|
|
|
|
CreateSubsectorSurfaces(doomMap);
|
|
|
|
for (size_t i = 0; i < Surfaces.Size(); i++)
|
|
{
|
|
const Surface &s = Surfaces[i];
|
|
int numVerts = s.numVerts;
|
|
unsigned int pos = s.startVertIndex;
|
|
FVector3* verts = &MeshVertices[pos];
|
|
|
|
for (int j = 0; j < numVerts; j++)
|
|
{
|
|
MeshUVIndex.Push(j);
|
|
}
|
|
|
|
if (s.type == ST_FLOOR || s.type == ST_CEILING)
|
|
{
|
|
for (int j = 2; j < numVerts; j++)
|
|
{
|
|
if (!IsDegenerate(verts[0], verts[j - 1], verts[j]))
|
|
{
|
|
MeshElements.Push(pos);
|
|
MeshElements.Push(pos + j - 1);
|
|
MeshElements.Push(pos + j);
|
|
MeshSurfaces.Push((int)i);
|
|
}
|
|
}
|
|
}
|
|
else if (s.type == ST_MIDDLEWALL || s.type == ST_UPPERWALL || s.type == ST_LOWERWALL)
|
|
{
|
|
if (!IsDegenerate(verts[0], verts[1], verts[2]))
|
|
{
|
|
MeshElements.Push(pos + 0);
|
|
MeshElements.Push(pos + 1);
|
|
MeshElements.Push(pos + 2);
|
|
MeshSurfaces.Push((int)i);
|
|
}
|
|
if (!IsDegenerate(verts[1], verts[2], verts[3]))
|
|
{
|
|
MeshElements.Push(pos + 3);
|
|
MeshElements.Push(pos + 2);
|
|
MeshElements.Push(pos + 1);
|
|
MeshSurfaces.Push((int)i);
|
|
}
|
|
}
|
|
}
|
|
|
|
Collision = std::make_unique<TriangleMeshShape>(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size());
|
|
}
|
|
|
|
void DoomLevelMesh::CreateSideSurfaces(FLevelLocals &doomMap, side_t *side)
|
|
{
|
|
sector_t *front;
|
|
sector_t *back;
|
|
|
|
front = side->sector;
|
|
back = (side->linedef->frontsector == front) ? side->linedef->backsector : side->linedef->frontsector;
|
|
|
|
if (IsControlSector(front))
|
|
return;
|
|
|
|
FVector2 v1 = ToFVector2(side->V1()->fPos());
|
|
FVector2 v2 = ToFVector2(side->V2()->fPos());
|
|
|
|
float v1Top = (float)front->ceilingplane.ZatPoint(v1);
|
|
float v1Bottom = (float)front->floorplane.ZatPoint(v1);
|
|
float v2Top = (float)front->ceilingplane.ZatPoint(v2);
|
|
float v2Bottom = (float)front->floorplane.ZatPoint(v2);
|
|
|
|
int typeIndex = side->Index();
|
|
|
|
FVector2 dx(v2.X - v1.X, v2.Y - v1.Y);
|
|
float distance = dx.Length();
|
|
|
|
// line_horizont consumes everything
|
|
if (side->linedef->special == Line_Horizon && front != back)
|
|
{
|
|
Surface surf;
|
|
surf.type = ST_MIDDLEWALL;
|
|
surf.typeIndex = typeIndex;
|
|
surf.bSky = front->GetTexture(sector_t::floor) == skyflatnum || front->GetTexture(sector_t::ceiling) == skyflatnum;
|
|
|
|
FVector3 verts[4];
|
|
verts[0].X = verts[2].X = v1.X;
|
|
verts[0].Y = verts[2].Y = v1.Y;
|
|
verts[1].X = verts[3].X = v2.X;
|
|
verts[1].Y = verts[3].Y = v2.Y;
|
|
verts[0].Z = v1Bottom;
|
|
verts[1].Z = v2Bottom;
|
|
verts[2].Z = v1Top;
|
|
verts[3].Z = v2Top;
|
|
|
|
surf.startVertIndex = MeshVertices.Size();
|
|
surf.numVerts = 4;
|
|
MeshVertices.Push(verts[0]);
|
|
MeshVertices.Push(verts[1]);
|
|
MeshVertices.Push(verts[2]);
|
|
MeshVertices.Push(verts[3]);
|
|
|
|
surf.plane = ToPlane(verts[0], verts[1], verts[2]);
|
|
Surfaces.Push(surf);
|
|
return;
|
|
}
|
|
|
|
if (back)
|
|
{
|
|
for (unsigned int j = 0; j < front->e->XFloor.ffloors.Size(); j++)
|
|
{
|
|
F3DFloor *xfloor = front->e->XFloor.ffloors[j];
|
|
|
|
// Don't create a line when both sectors have the same 3d floor
|
|
bool bothSides = false;
|
|
for (unsigned int k = 0; k < back->e->XFloor.ffloors.Size(); k++)
|
|
{
|
|
if (back->e->XFloor.ffloors[k] == xfloor)
|
|
{
|
|
bothSides = true;
|
|
break;
|
|
}
|
|
}
|
|
if (bothSides)
|
|
continue;
|
|
|
|
Surface surf;
|
|
surf.type = ST_MIDDLEWALL;
|
|
surf.typeIndex = typeIndex;
|
|
surf.controlSector = xfloor->model;
|
|
surf.bSky = false;
|
|
|
|
FVector3 verts[4];
|
|
verts[0].X = verts[2].X = v2.X;
|
|
verts[0].Y = verts[2].Y = v2.Y;
|
|
verts[1].X = verts[3].X = v1.X;
|
|
verts[1].Y = verts[3].Y = v1.Y;
|
|
verts[0].Z = (float)xfloor->model->floorplane.ZatPoint(v2);
|
|
verts[1].Z = (float)xfloor->model->floorplane.ZatPoint(v1);
|
|
verts[2].Z = (float)xfloor->model->ceilingplane.ZatPoint(v2);
|
|
verts[3].Z = (float)xfloor->model->ceilingplane.ZatPoint(v1);
|
|
|
|
surf.startVertIndex = MeshVertices.Size();
|
|
surf.numVerts = 4;
|
|
MeshVertices.Push(verts[0]);
|
|
MeshVertices.Push(verts[1]);
|
|
MeshVertices.Push(verts[2]);
|
|
MeshVertices.Push(verts[3]);
|
|
|
|
surf.plane = ToPlane(verts[0], verts[1], verts[2]);
|
|
Surfaces.Push(surf);
|
|
}
|
|
|
|
float v1TopBack = (float)back->ceilingplane.ZatPoint(v1);
|
|
float v1BottomBack = (float)back->floorplane.ZatPoint(v1);
|
|
float v2TopBack = (float)back->ceilingplane.ZatPoint(v2);
|
|
float v2BottomBack = (float)back->floorplane.ZatPoint(v2);
|
|
|
|
if (v1Top == v1TopBack && v1Bottom == v1BottomBack && v2Top == v2TopBack && v2Bottom == v2BottomBack)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// bottom seg
|
|
if (v1Bottom < v1BottomBack || v2Bottom < v2BottomBack)
|
|
{
|
|
if (IsBottomSideVisible(side))
|
|
{
|
|
Surface surf;
|
|
|
|
FVector3 verts[4];
|
|
verts[0].X = verts[2].X = v1.X;
|
|
verts[0].Y = verts[2].Y = v1.Y;
|
|
verts[1].X = verts[3].X = v2.X;
|
|
verts[1].Y = verts[3].Y = v2.Y;
|
|
verts[0].Z = v1Bottom;
|
|
verts[1].Z = v2Bottom;
|
|
verts[2].Z = v1BottomBack;
|
|
verts[3].Z = v2BottomBack;
|
|
|
|
surf.startVertIndex = MeshVertices.Size();
|
|
surf.numVerts = 4;
|
|
MeshVertices.Push(verts[0]);
|
|
MeshVertices.Push(verts[1]);
|
|
MeshVertices.Push(verts[2]);
|
|
MeshVertices.Push(verts[3]);
|
|
|
|
surf.plane = ToPlane(verts[0], verts[1], verts[2]);
|
|
surf.type = ST_LOWERWALL;
|
|
surf.typeIndex = typeIndex;
|
|
surf.bSky = false;
|
|
surf.controlSector = nullptr;
|
|
|
|
Surfaces.Push(surf);
|
|
}
|
|
|
|
v1Bottom = v1BottomBack;
|
|
v2Bottom = v2BottomBack;
|
|
}
|
|
|
|
// top seg
|
|
if (v1Top > v1TopBack || v2Top > v2TopBack)
|
|
{
|
|
bool bSky = IsTopSideSky(front, back, side);
|
|
if (bSky || IsTopSideVisible(side))
|
|
{
|
|
Surface surf;
|
|
|
|
FVector3 verts[4];
|
|
verts[0].X = verts[2].X = v1.X;
|
|
verts[0].Y = verts[2].Y = v1.Y;
|
|
verts[1].X = verts[3].X = v2.X;
|
|
verts[1].Y = verts[3].Y = v2.Y;
|
|
verts[0].Z = v1TopBack;
|
|
verts[1].Z = v2TopBack;
|
|
verts[2].Z = v1Top;
|
|
verts[3].Z = v2Top;
|
|
|
|
surf.startVertIndex = MeshVertices.Size();
|
|
surf.numVerts = 4;
|
|
MeshVertices.Push(verts[0]);
|
|
MeshVertices.Push(verts[1]);
|
|
MeshVertices.Push(verts[2]);
|
|
MeshVertices.Push(verts[3]);
|
|
|
|
surf.plane = ToPlane(verts[0], verts[1], verts[2]);
|
|
surf.type = ST_UPPERWALL;
|
|
surf.typeIndex = typeIndex;
|
|
surf.bSky = bSky;
|
|
surf.controlSector = nullptr;
|
|
|
|
Surfaces.Push(surf);
|
|
}
|
|
|
|
v1Top = v1TopBack;
|
|
v2Top = v2TopBack;
|
|
}
|
|
}
|
|
|
|
// middle seg
|
|
if (back == nullptr)
|
|
{
|
|
Surface surf;
|
|
surf.bSky = false;
|
|
|
|
FVector3 verts[4];
|
|
verts[0].X = verts[2].X = v1.X;
|
|
verts[0].Y = verts[2].Y = v1.Y;
|
|
verts[1].X = verts[3].X = v2.X;
|
|
verts[1].Y = verts[3].Y = v2.Y;
|
|
verts[0].Z = v1Bottom;
|
|
verts[1].Z = v2Bottom;
|
|
verts[2].Z = v1Top;
|
|
verts[3].Z = v2Top;
|
|
|
|
surf.startVertIndex = MeshVertices.Size();
|
|
surf.numVerts = 4;
|
|
surf.bSky = false;
|
|
MeshVertices.Push(verts[0]);
|
|
MeshVertices.Push(verts[1]);
|
|
MeshVertices.Push(verts[2]);
|
|
MeshVertices.Push(verts[3]);
|
|
|
|
surf.plane = ToPlane(verts[0], verts[1], verts[2]);
|
|
surf.type = ST_MIDDLEWALL;
|
|
surf.typeIndex = typeIndex;
|
|
surf.controlSector = nullptr;
|
|
|
|
Surfaces.Push(surf);
|
|
}
|
|
}
|
|
|
|
void DoomLevelMesh::CreateFloorSurface(FLevelLocals &doomMap, subsector_t *sub, sector_t *sector, int typeIndex, bool is3DFloor)
|
|
{
|
|
Surface surf;
|
|
surf.bSky = IsSkySector(sector, sector_t::floor);
|
|
|
|
if (!is3DFloor)
|
|
{
|
|
surf.plane = sector->floorplane;
|
|
}
|
|
else
|
|
{
|
|
surf.plane = sector->ceilingplane;
|
|
surf.plane.FlipVert();
|
|
}
|
|
|
|
surf.numVerts = sub->numlines;
|
|
surf.startVertIndex = MeshVertices.Size();
|
|
MeshVertices.Resize(surf.startVertIndex + surf.numVerts);
|
|
FVector3* verts = &MeshVertices[surf.startVertIndex];
|
|
|
|
for (int j = 0; j < surf.numVerts; j++)
|
|
{
|
|
seg_t *seg = &sub->firstline[(surf.numVerts - 1) - j];
|
|
FVector2 v1 = ToFVector2(seg->v1->fPos());
|
|
|
|
verts[j].X = v1.X;
|
|
verts[j].Y = v1.Y;
|
|
verts[j].Z = (float)surf.plane.ZatPoint(verts[j]);
|
|
}
|
|
|
|
surf.type = ST_FLOOR;
|
|
surf.typeIndex = typeIndex;
|
|
surf.controlSector = is3DFloor ? sector : nullptr;
|
|
|
|
Surfaces.Push(surf);
|
|
}
|
|
|
|
void DoomLevelMesh::CreateCeilingSurface(FLevelLocals &doomMap, subsector_t *sub, sector_t *sector, int typeIndex, bool is3DFloor)
|
|
{
|
|
Surface surf;
|
|
surf.bSky = IsSkySector(sector, sector_t::ceiling);
|
|
|
|
if (!is3DFloor)
|
|
{
|
|
surf.plane = sector->ceilingplane;
|
|
}
|
|
else
|
|
{
|
|
surf.plane = sector->floorplane;
|
|
surf.plane.FlipVert();
|
|
}
|
|
|
|
surf.numVerts = sub->numlines;
|
|
surf.startVertIndex = MeshVertices.Size();
|
|
MeshVertices.Resize(surf.startVertIndex + surf.numVerts);
|
|
FVector3* verts = &MeshVertices[surf.startVertIndex];
|
|
|
|
for (int j = 0; j < surf.numVerts; j++)
|
|
{
|
|
seg_t *seg = &sub->firstline[j];
|
|
FVector2 v1 = ToFVector2(seg->v1->fPos());
|
|
|
|
verts[j].X = v1.X;
|
|
verts[j].Y = v1.Y;
|
|
verts[j].Z = (float)surf.plane.ZatPoint(verts[j]);
|
|
}
|
|
|
|
surf.type = ST_CEILING;
|
|
surf.typeIndex = typeIndex;
|
|
surf.controlSector = is3DFloor ? sector : nullptr;
|
|
|
|
Surfaces.Push(surf);
|
|
}
|
|
|
|
void DoomLevelMesh::CreateSubsectorSurfaces(FLevelLocals &doomMap)
|
|
{
|
|
for (unsigned int i = 0; i < doomMap.subsectors.Size(); i++)
|
|
{
|
|
subsector_t *sub = &doomMap.subsectors[i];
|
|
|
|
if (sub->numlines < 3)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sector_t *sector = sub->sector;
|
|
if (!sector || IsControlSector(sector))
|
|
continue;
|
|
|
|
CreateFloorSurface(doomMap, sub, sector, i, false);
|
|
CreateCeilingSurface(doomMap, sub, sector, i, false);
|
|
|
|
for (unsigned int j = 0; j < sector->e->XFloor.ffloors.Size(); j++)
|
|
{
|
|
CreateFloorSurface(doomMap, sub, sector->e->XFloor.ffloors[j]->model, i, true);
|
|
CreateCeilingSurface(doomMap, sub, sector->e->XFloor.ffloors[j]->model, i, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DoomLevelMesh::IsTopSideSky(sector_t* frontsector, sector_t* backsector, side_t* side)
|
|
{
|
|
return IsSkySector(frontsector, sector_t::ceiling) && IsSkySector(backsector, sector_t::ceiling);
|
|
}
|
|
|
|
bool DoomLevelMesh::IsTopSideVisible(side_t* side)
|
|
{
|
|
auto tex = TexMan.GetGameTexture(side->GetTexture(side_t::top), true);
|
|
return tex && tex->isValid();
|
|
}
|
|
|
|
bool DoomLevelMesh::IsBottomSideVisible(side_t* side)
|
|
{
|
|
auto tex = TexMan.GetGameTexture(side->GetTexture(side_t::bottom), true);
|
|
return tex && tex->isValid();
|
|
}
|
|
|
|
bool DoomLevelMesh::IsSkySector(sector_t* sector, int plane)
|
|
{
|
|
// plane is either sector_t::ceiling or sector_t::floor
|
|
return sector->GetTexture(plane) == skyflatnum;
|
|
}
|
|
|
|
bool DoomLevelMesh::IsControlSector(sector_t* sector)
|
|
{
|
|
//return sector->controlsector;
|
|
return false;
|
|
}
|
|
|
|
bool DoomLevelMesh::IsDegenerate(const FVector3 &v0, const FVector3 &v1, const FVector3 &v2)
|
|
{
|
|
// A degenerate triangle has a zero cross product for two of its sides.
|
|
float ax = v1.X - v0.X;
|
|
float ay = v1.Y - v0.Y;
|
|
float az = v1.Z - v0.Z;
|
|
float bx = v2.X - v0.X;
|
|
float by = v2.Y - v0.Y;
|
|
float bz = v2.Z - v0.Z;
|
|
float crossx = ay * bz - az * by;
|
|
float crossy = az * bx - ax * bz;
|
|
float crossz = ax * by - ay * bx;
|
|
float crosslengthsqr = crossx * crossx + crossy * crossy + crossz * crossz;
|
|
return crosslengthsqr <= 1.e-6f;
|
|
}
|
|
|
|
void DoomLevelMesh::DumpMesh(const FString& filename) const
|
|
{
|
|
auto f = fopen(filename.GetChars(), "w");
|
|
|
|
fprintf(f, "# DoomLevelMesh debug export\n");
|
|
fprintf(f, "# MeshVertices: %d, MeshElements: %d\n", MeshVertices.Size(), MeshElements.Size());
|
|
|
|
double scale = 1 / 10.0;
|
|
|
|
for (const auto& v : MeshVertices)
|
|
{
|
|
fprintf(f, "v %f %f %f\n", v.X * scale, v.Y * scale, v.Z * scale);
|
|
}
|
|
|
|
{
|
|
const auto s = LightmapUvs.Size();
|
|
for (unsigned i = 0; i + 1 < s; i += 2)
|
|
{
|
|
fprintf(f, "vt %f %f\n", LightmapUvs[i], LightmapUvs[i + 1]);
|
|
}
|
|
}
|
|
|
|
const auto s = MeshElements.Size();
|
|
for (unsigned i = 0; i + 2 < s; i += 3)
|
|
{
|
|
// 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",
|
|
MeshElements[i + 0] + 1, MeshElements[i + 0] + 1,
|
|
MeshElements[i + 1] + 1, MeshElements[i + 1] + 1,
|
|
MeshElements[i + 2] + 1, MeshElements[i + 2] + 1);
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
}
|
|
|
|
int DoomLevelMesh::SetupLightmapUvs(int lightmapSize)
|
|
{
|
|
std::vector<Surface*> sortedSurfaces;
|
|
sortedSurfaces.reserve(Surfaces.Size());
|
|
|
|
for (auto& surface : Surfaces)
|
|
{
|
|
BuildSurfaceParams(lightmapSize, lightmapSize, surface);
|
|
sortedSurfaces.push_back(&surface);
|
|
}
|
|
|
|
{
|
|
this->portalInfo.Clear(); // TODO portals
|
|
|
|
PortalInfo portalInfo;
|
|
hwrenderer::Portal portal;
|
|
|
|
for (int i = 0; i < 16; ++i)
|
|
{
|
|
portalInfo.transformation[i] = (&portal.transformation[0][0])[i];
|
|
}
|
|
|
|
this->portalInfo.Push(portalInfo);
|
|
}
|
|
|
|
for (const auto& surface : Surfaces)
|
|
{
|
|
auto hwSurface = std::make_unique<hwrenderer::Surface>();
|
|
|
|
hwSurface->boundsMax = surface.bounds.max;
|
|
hwSurface->boundsMin = surface.bounds.min;
|
|
|
|
// hwSurface->LightList = // TODO
|
|
hwSurface->projLocalToU = surface.projLocalToU;
|
|
hwSurface->projLocalToV = surface.projLocalToV;
|
|
hwSurface->smoothingGroupIndex = -1;
|
|
hwSurface->texHeight = surface.texHeight;
|
|
hwSurface->texWidth = surface.texWidth;
|
|
|
|
hwSurface->translateWorldToLocal = surface.translateWorldToLocal;
|
|
hwSurface->type = hwrenderer::SurfaceType(surface.type);
|
|
|
|
hwSurface->texPixels.resize(surface.texWidth * surface.texHeight);
|
|
|
|
for (int i = 0; i < surface.numVerts; ++i)
|
|
{
|
|
hwSurface->verts.Push(MeshVertices[surface.startVertIndex + i]);
|
|
}
|
|
|
|
// TODO push
|
|
surfaces.push_back(std::move(hwSurface));
|
|
|
|
SurfaceInfo info;
|
|
info.Normal = FVector3(surface.plane.Normal());
|
|
info.PortalIndex = 0;
|
|
info.SamplingDistance = surface.sampleDimension;
|
|
info.Sky = surface.bSky;
|
|
|
|
surfaceInfo.Push(info);
|
|
}
|
|
|
|
std::sort(sortedSurfaces.begin(), sortedSurfaces.end(), [](Surface* a, Surface* b) { return a->texHeight != b->texHeight ? a->texHeight > b->texHeight : a->texWidth > b->texWidth; });
|
|
|
|
RectPacker packer(lightmapSize, lightmapSize, RectPacker::Spacing(0));
|
|
|
|
for (Surface* surf : sortedSurfaces)
|
|
{
|
|
FinishSurface(lightmapSize, lightmapSize, packer, *surf);
|
|
}
|
|
|
|
// You have no idea how long this took me to figure out...
|
|
|
|
// Reorder vertices into renderer format
|
|
for (Surface& surface : Surfaces)
|
|
{
|
|
if (surface.type == ST_FLOOR)
|
|
{
|
|
// reverse vertices on floor
|
|
for (int j = surface.startUvIndex + surface.numVerts * 2 - 2, k = surface.startUvIndex; j > k; j-=2, k+=2)
|
|
{
|
|
std::swap(LightmapUvs[k], LightmapUvs[j]);
|
|
std::swap(LightmapUvs[k + 1], LightmapUvs[j + 1]);
|
|
}
|
|
}
|
|
else if (surface.type != ST_CEILING) // walls
|
|
{
|
|
// from 0 1 2 3
|
|
// to 0 2 1 3
|
|
std::swap(LightmapUvs[surface.startUvIndex + 2 * 1], LightmapUvs[surface.startUvIndex + 2 * 2]);
|
|
std::swap(LightmapUvs[surface.startUvIndex + 2 * 2], LightmapUvs[surface.startUvIndex + 2 * 3]);
|
|
|
|
std::swap(LightmapUvs[surface.startUvIndex + 2 * 1 + 1], LightmapUvs[surface.startUvIndex + 2 * 2 + 1]);
|
|
std::swap(LightmapUvs[surface.startUvIndex + 2 * 2 + 1], LightmapUvs[surface.startUvIndex + 2 * 3 + 1]);
|
|
}
|
|
}
|
|
|
|
|
|
return packer.getNumPages();
|
|
}
|
|
|
|
void DoomLevelMesh::FinishSurface(int lightmapTextureWidth, int lightmapTextureHeight, RectPacker& packer, Surface& surface)
|
|
{
|
|
int sampleWidth = surface.texWidth;
|
|
int sampleHeight = surface.texHeight;
|
|
|
|
auto result = packer.insert(sampleWidth, sampleHeight);
|
|
int x = result.pos.x, y = result.pos.y;
|
|
surface.atlasPageIndex = (int)result.pageIndex;
|
|
|
|
// calculate final texture coordinates
|
|
auto uvIndex = surface.startUvIndex;
|
|
for (int i = 0; i < (int)surface.numVerts; i++)
|
|
{
|
|
auto& u = LightmapUvs[uvIndex++];
|
|
auto& v = LightmapUvs[uvIndex++];
|
|
u = (u + x) / (float)lightmapTextureWidth;
|
|
v = (v + y) / (float)lightmapTextureHeight;
|
|
}
|
|
|
|
surface.atlasX = x;
|
|
surface.atlasY = y;
|
|
|
|
#if 0
|
|
while (result.pageIndex >= textures.size())
|
|
{
|
|
textures.push_back(std::make_unique<LightmapTexture>(textureWidth, textureHeight));
|
|
}
|
|
|
|
uint16_t* currentTexture = textures[surface->atlasPageIndex]->Pixels();
|
|
|
|
FVector3* colorSamples = surface->texPixels.data();
|
|
// store results to lightmap texture
|
|
for (int i = 0; i < sampleHeight; i++)
|
|
{
|
|
for (int j = 0; j < sampleWidth; j++)
|
|
{
|
|
// get texture offset
|
|
int offs = ((textureWidth * (i + surface->atlasY)) + surface->atlasX) * 3;
|
|
|
|
// convert RGB to bytes
|
|
currentTexture[offs + j * 3 + 0] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].x, 0.0f, 65000.0f));
|
|
currentTexture[offs + j * 3 + 1] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].y, 0.0f, 65000.0f));
|
|
currentTexture[offs + j * 3 + 2] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].z, 0.0f, 65000.0f));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
BBox DoomLevelMesh::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;
|
|
}
|
|
|
|
DoomLevelMesh::PlaneAxis DoomLevelMesh::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;
|
|
}
|
|
|
|
void DoomLevelMesh::BuildSurfaceParams(int lightMapTextureWidth, int lightMapTextureHeight, Surface& surface)
|
|
{
|
|
secplane_t* plane;
|
|
BBox bounds;
|
|
FVector3 roundedSize;
|
|
FVector3 tOrigin;
|
|
int width;
|
|
int height;
|
|
float d;
|
|
|
|
plane = &surface.plane;
|
|
bounds = GetBoundsFromSurface(surface);
|
|
surface.bounds = bounds;
|
|
|
|
if (surface.sampleDimension <= 0)
|
|
{
|
|
surface.sampleDimension = 16;
|
|
}
|
|
//surface->sampleDimension = Math::RoundPowerOfTwo(surface->sampleDimension);
|
|
|
|
// round off dimensions
|
|
for (int 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) };
|
|
|
|
PlaneAxis 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 (int 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 (int 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;
|
|
}
|