Experimenting...

This commit is contained in:
KP 2024-07-24 00:08:44 -05:00
parent a63c6e683e
commit 5a13579849
6 changed files with 346 additions and 6833 deletions

File diff suppressed because it is too large Load diff

View file

@ -251,7 +251,7 @@ bool clip2tri::triangulateComplex(vector<Point> &outputTriangles, const Path &ou
F64(S64(steiner_points->at(j).x * CLIPPER_SCALE_FACT)), F64(S64(steiner_points->at(j).x * CLIPPER_SCALE_FACT)),
F64(S64(steiner_points->at(j).y * CLIPPER_SCALE_FACT)), F64(S64(steiner_points->at(j).y * CLIPPER_SCALE_FACT)),
}); });
cdt->AddPoint(steiners[j]); //cdt->AddPoint(steiners[j]);
} }
} }
// end: steiner points // end: steiner points

View file

@ -5,7 +5,7 @@
#include "KP3D_Log.h" #include "KP3D_Log.h"
namespace { namespace {
const float EPSILON = 0.0001f; const float EPSILON = 1.0f / 128.0f;
} }
namespace kp3d { namespace kp3d {
@ -50,14 +50,102 @@ std::vector<std::vector<Vec2>> SplitComplexPolygon(std::vector<Vec2> polygon)
output.push_back(polygon_excerpt); output.push_back(polygon_excerpt);
} }
if (output.size() > 1)
{
KP3D_LOG_INFO("Split: ");
for (int i = 0; i < output.size(); i++)
KP3D_LOG_INFO("{} size: {}", i, output[i].size());
}
return output; return output;
} }
std::vector<std::vector<Vec2>> ClipPolygonHoles(std::vector<Vec2> polygon, std::vector<std::vector<Vec2>> holes)
{
if (holes.empty())
{
return { polygon };
}
for (std::vector<Vec2>& hole : holes)
{
polygon.push_back(polygon[0]);
for (Vec2 point: hole)
polygon.push_back(point);
polygon.push_back(hole[0]);
}
//polygon.push_back(polygon[0]);
//polygon.erase(std::unique(polygon.begin(), polygon.end()), polygon.end());
return SplitComplexPolygon(polygon);
#if 0
using namespace Clipper2Lib;
if (holes.empty())
{
return {polygon};
}
if (holes.size() > 1)
holes.pop_back();
//ClipperD c;
PathsD solution;
// PolyPathD tree;
PathD complex_polygon;
for (const Vec2& v: polygon)
{
complex_polygon.push_back({v.x, v.y});
}
// PolyTreeD cp(&tree, complex_polygon);
PathsD subjects;
subjects.push_back(complex_polygon);
PathsD clips;
KP3D_LOG_INFO("# holes: {}", holes.size());
for (const auto& vl: holes)
{
PathD clip;
for (const Vec2& v : vl) {
clip.push_back({ v.x, v.y });
//complex_polygon.push_back({v.x, v.y});
}
clips.push_back(clip);
// cp.AddChild(clip);
//tree.AddChild(clip);
}
// tree.AddChild(complex_polygon); ?
//c.AddSubject({complex_polygon});
//c.AddSubject(clips);
//c.AddClip(clips);
//c.AddClip(clips);
// PolyTreeD clipper_output;
//PathsD clipper_output;
//c.Execute(ClipType::Difference, FillRule::NonZero, clipper_output);
PathsD clipper_output = Difference(subjects, clips, FillRule::EvenOdd);
std::vector<std::vector<Vec2>> output;
for (auto& o: clipper_output)
{
std::vector<Vec2> polygon_excerpt;
for (const auto& point: o)
polygon_excerpt.push_back({(float) point.x, (float) point.y});
output.push_back(polygon_excerpt);
}
if (output.size() > 0)
{
KP3D_LOG_INFO("Split ({}):", output.size());
for (int i = 0; i < output.size(); i++)
KP3D_LOG_INFO("{} size: {}", i, output[i].size());
}
if (output.empty())
{
return { polygon };
}
return output;
#endif
}
} // namespace kp3d } // namespace kp3d

View file

@ -54,5 +54,7 @@ bool PointInLine(XYf point, XYf line_start, XYf line_end);
float Distance(XYf a, XYf b); float Distance(XYf a, XYf b);
std::vector<std::vector<Vec2>> SplitComplexPolygon(std::vector<Vec2> polygon); std::vector<std::vector<Vec2>> SplitComplexPolygon(std::vector<Vec2> polygon);
std::vector<std::vector<Vec2>> ClipPolygonHoles(std::vector<Vec2> polygon, std::vector<std::vector<Vec2>> holes);
} // namespace kp3d } // namespace kp3d

View file

@ -4,6 +4,7 @@
#include <clip2tri/clip2tri.h> #include <clip2tri/clip2tri.h>
#include <clipper/clipper.hpp> #include <clipper/clipper.hpp>
#include <clipper2/clipper.h>
#include <poly2tri/poly2tri.h> #include <poly2tri/poly2tri.h>
#include "KP3D_Renderer3D.h" #include "KP3D_Renderer3D.h"
@ -13,9 +14,49 @@
namespace { namespace {
using namespace kp3d; bool SectorContains(const kp3d::Sector& outer, const kp3d::Sector& inner) {
} // namespace using namespace Clipper2Lib;
using namespace kp3d;
PathD outer_path;
for (const Wall& l: outer.walls)
outer_path.push_back({l.start.x, l.start.y});
PathD inner_path;
for (const Wall& l: inner.walls)
inner_path.push_back({l.start.x, l.start.y});
bool completely_inside = false;
bool partially_inside = false;
int count = 0;
int partial_count = 0;
for (auto& p : inner_path)
{
PointInPolygonResult status = Clipper2Lib::PointInPolygon(p, outer_path);
if (status == PointInPolygonResult::IsInside)
count++;
if (status == PointInPolygonResult::IsOn)
partial_count++;
}
if (count == inner_path.size())
completely_inside = true;
if (partial_count > 0 && count > 0)
partially_inside = true;
// KP3D_LOG_INFO("COMPLETE: {}, PARTIAL: {} / {}", count, partial_count, child_path.size());
if (completely_inside)
{
//s.children.push_back(&s2);
//s2.parents.push_back(s.id);
}
return completely_inside || (count > 0 && partial_count < 4);
}
}
namespace kp3d { namespace kp3d {
@ -29,13 +70,11 @@ Map::~Map()
ErrCode Map::LoadFromFile(const std::string& path) ErrCode Map::LoadFromFile(const std::string& path)
{ {
// TODO: ...
return FAILURE; return FAILURE;
} }
ErrCode Map::SaveToFile(const std::string& path) ErrCode Map::SaveToFile(const std::string& path)
{ {
// TODO: ...
return FAILURE; return FAILURE;
} }
@ -44,7 +83,7 @@ ErrCode Map::SaveToFile(const std::string& path)
*/ */
void Map::BuildFlat(Sector& sector, Flat& flat, bool invert) void Map::BuildFlat(Sector& sector, Flat& flat, bool invert)
{ {
const float e = 0.001f; const float e = 1.0f / 128.0f;
if (sector.inverted) if (sector.inverted)
invert ^= 1; invert ^= 1;
@ -53,75 +92,78 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert)
for (const auto& st: flat.steiner_points) for (const auto& st: flat.steiner_points)
steiner_points.emplace_back(st.x, st.z); steiner_points.emplace_back(st.x, st.z);
std::vector<std::vector<c2t::Point>> subsector_polygons; // unused for now std::vector<std::vector<c2t::Point>> subsector_polygons;
for (Sector* s: sector.children)
std::vector<Sector*> secs_inside;
for (Sector* sp : sector.children)
{ {
std::vector<c2t::Point> ss; std::vector<c2t::Point> ss;
Sector& s = *sp; float area = 0.0f;
if (!s.children.empty()) for (Wall& l : s->walls)
continue;
for (Wall& l : s.walls)
{ {
if (PointInPolygon(sector.walls, l.start)) if (s->inverted)
{ {
ss.push_back({ l.start.x, l.start.y }); if (FloatCmp(s->floor.base_height, sector.floor.base_height, e))
} s->floor.texture = nullptr;
} if (FloatCmp(s->ceiling.base_height, sector.ceiling.base_height, e))
for (Wall& l : s.walls) s->ceiling.texture = nullptr;
{ //continue;
if (s.inverted)
{
if (!FloatCmp(s.floor.base_height, sector.floor.base_height))
s.floor.texture = nullptr;
if (!FloatCmp(s.ceiling.base_height, sector.ceiling.base_height))
s.ceiling.texture = nullptr;
//if (s.ceiling.base_height <= sector.ceiling.base_height + 0.000001f)
// s.ceiling.texture = nullptr;
continue;
} }
else else
{ {
l.textures[TEX_FRONT] = nullptr; l.textures[TEX_FRONT] = nullptr;
l.flags = Wall::FLAG_OPENING;
l.portal = &sector;
} }
ss.push_back({l.start.x, l.start.y});
area += l.start.x * l.end.y;
area -= l.end.x * l.start.y;
} }
//if (area > 0)//<= 0.0f)
// std::reverse(ss.begin(), ss.end());
subsector_polygons.push_back(ss); subsector_polygons.push_back(ss);
} }
std::vector<c2t::Point> sector_polygons; std::vector<c2t::Point> flat_polygons;
for (Wall& l: sector.walls) for (Wall& l: sector.walls)
sector_polygons.emplace_back(l.start.x, l.start.y); flat_polygons.emplace_back(l.start.x, l.start.y);
KP3D_LOG_INFO("TRIANGULATING SECTOR: {}", sector.id);
// Triangulate the sector floors and ceilings and send the result to the mesh. To do this we're using version of the Clipper //std::vector<std::vector<Vec2>> polygons = ClipPolygonHoles(flat_polygons, subsector_polygons);
// library merged with poly2tri, called clip2tri, which I've since modified to support Steiner points. //for (const std::vector<Vec2>& polygon: polygons)
c2t::clip2tri clipper; //const auto& polygon = polygons[1 % polygons.size()];
std::vector<c2t::Point> clipper_out;
clipper.triangulate(subsector_polygons, clipper_out, sector_polygons, &steiner_points);
const float MAX = 1000000.0f;
float min_height = MAX;
float max_height = -MAX;
for (int i = 0; i < clipper_out.size(); i += 3)
{ {
c2t::Point a = clipper_out.at(i + 0);
c2t::Point b = clipper_out.at(i + 1);
c2t::Point c = clipper_out.at(i + 2);
float au = a.x * 0.5f, av = a.y * 0.5f; //std::vector<c2t::Point> sector_polygons;
float bu = b.x * 0.5f, bv = b.y * 0.5f; //for (const Vec2& l: sector.walls)
float cu = c.x * 0.5f, cv = c.y * 0.5f; // sector_polygons.push_back({l.x, l.y});
// Build mesh data for floor // Triangulate the sector floors and ceilings and send the result to the mesh. To do this we're using version of the Clipper
// library merged with poly2tri, called clip2tri, which I've since modified to support Steiner points.
c2t::clip2tri clipper;
std::vector<c2t::Point> clipper_out;
clipper.triangulate(subsector_polygons, clipper_out, flat_polygons, &steiner_points);
const float MAX = 1000000.0f;
float min_height = MAX;
float max_height = -MAX;
for (int i = 0; i < clipper_out.size(); i += 3)
{ {
c2t::Point a = clipper_out.at(i + 0);
c2t::Point b = clipper_out.at(i + 1);
c2t::Point c = clipper_out.at(i + 2);
float au = a.x * 0.5f, av = a.y * 0.5f;
float bu = b.x * 0.5f, bv = b.y * 0.5f;
float cu = c.x * 0.5f, cv = c.y * 0.5f;
// Build mesh data for floor
Vec3 fva = Vec3(a.x, flat.base_height, a.y); Vec3 fva = Vec3(a.x, flat.base_height, a.y);
Vec3 fvb = Vec3(b.x, flat.base_height, b.y); Vec3 fvb = Vec3(b.x, flat.base_height, b.y);
Vec3 fvc = Vec3(c.x, flat.base_height, c.y); Vec3 fvc = Vec3(c.x, flat.base_height, c.y);
for (const Vec3& steiner: flat.steiner_points) for (const Vec3& steiner : flat.steiner_points)
{ {
if (FloatCmp(fva.x, steiner.x, e) && FloatCmp(fva.z, steiner.z, e)) fva.y += steiner.y; if (FloatCmp(fva.x, steiner.x, e) && FloatCmp(fva.z, steiner.z, e)) fva.y += steiner.y;
if (FloatCmp(fvb.x, steiner.x, e) && FloatCmp(fvb.z, steiner.z, e)) fvb.y += steiner.y; if (FloatCmp(fvb.x, steiner.x, e) && FloatCmp(fvb.z, steiner.z, e)) fvb.y += steiner.y;
@ -147,7 +189,7 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert)
flat.triangulated_data.push_back(vtxb); flat.triangulated_data.push_back(vtxb);
flat.triangulated_data.push_back(vtxc); flat.triangulated_data.push_back(vtxc);
if (flat.texture) if (flat.texture)
m_mesh.AddBatch(flat.texture, {vtxa, vtxb, vtxc}, !invert); m_mesh.AddBatch(flat.texture, { vtxa, vtxb, vtxc }, !invert);
} }
} }
} }
@ -166,47 +208,27 @@ void Map::BuildQuad(Sector& sector, Flat& flat_top, Flat& flat_bottom, const Tex
if (sector.inverted) if (sector.inverted)
flip ^= 1; flip ^= 1;
Vec3 p1 = Vec3(pos_a.x, pos_a.y, pos_a.z);
Vec3 p2 = Vec3(pos_a.x, pos_b.y, pos_a.z);
Vec3 p3 = Vec3(pos_b.x, pos_a.y, pos_b.z);
Vec3 p4 = Vec3(pos_b.x, pos_b.y, pos_b.z);
std::vector<Vec2> points; std::vector<Vec2> points;
points.emplace_back(0.0f);
for (const Vertex3D& v: flat_bottom.triangulated_data) for (const Vertex3D& v: flat_bottom.triangulated_data)
{ {
if (!PointInLine({v.position.x, v.position.z}, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z})) if (!PointInLine({v.position.x, v.position.z}, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z}))
continue; continue;
Vec2 mpos = {Distance({v.position.x, v.position.z}, {pos_a.x, pos_a.z}), v.position.y};
// Project 3D point into 2D
Vec2 mpos = {Distance({v.position.x, v.position.z}, {p1.x, p1.z}), v.position.y};
// Overwrite the start point if we've landed on it
if (FloatCmp(v.position.x, p1.x, 0.0001f) && FloatCmp(v.position.z, p1.z, 0.0001f))
points[0] = mpos;
points.push_back(mpos); points.push_back(mpos);
} }
std::vector<Vec2> top_points; std::vector<Vec2> top_points;
top_points.emplace_back(Distance({ p4.x, p4.z }, { p1.x, p1.z }), p4.y); // This may be re-enabled, BUT
for (const Vertex3D& v: flat_top.triangulated_data) for (const Vertex3D& v: flat_top.triangulated_data)
{ {
if (!PointInLine({v.position.x, v.position.z}, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z})) if (!PointInLine({v.position.x, v.position.z}, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z}))
continue; continue;
Vec2 mpos = {Distance({v.position.x, v.position.z}, {pos_a.x, pos_a.z}), v.position.y};
// Project 3D point into 2D
Vec2 mpos = {Distance({v.position.x, v.position.z}, {p1.x, p1.z}), v.position.y};
if (FloatCmp(v.position.x, p4.x, 0.0001f) && FloatCmp(v.position.z, p4.z, 0.0001f))
top_points[0] = mpos;
top_points.push_back(mpos); top_points.push_back(mpos);
} }
std::sort(points.begin() + 1, points.end(), [&](Vec2 a, Vec2 b) { return a.x < b.x; }); std::sort(points.begin(), points.end(), [&](Vec2 a, Vec2 b) { return a.x < b.x; });
std::sort(top_points.begin() + 1 , top_points.end(), [&](Vec2 a, Vec2 b) { return a.x > b.x; }); // ^^^^ If you do, add +1 to begin() std::sort(top_points.begin(), top_points.end(), [&](Vec2 a, Vec2 b) { return a.x > b.x; });
points.insert(points.end(), top_points.begin(), top_points.end()); points.insert(points.end(), top_points.begin(), top_points.end());
float angle = atan2({p4.z - p1.z}, {p4.x - p1.x}); float angle = atan2({pos_b.z - pos_a.z}, {pos_b.x - pos_a.x});
points.erase(unique(points.begin(), points.end()), points.end()); points.erase(unique(points.begin(), points.end()), points.end());
@ -215,53 +237,29 @@ void Map::BuildQuad(Sector& sector, Flat& flat_top, Flat& flat_bottom, const Tex
// There's a chance we may have ended up with a complex polygon, which poly2tri isn't able to handle. So our solution is to // There's a chance we may have ended up with a complex polygon, which poly2tri isn't able to handle. So our solution is to
// break them up. // break them up.
std::vector<std::vector<Vec2>> polygons = SplitComplexPolygon(points); std::vector<std::vector<Vec2>> polygons = SplitComplexPolygon(points);
//auto& polygon = polygons.front();
for (const std::vector<Vec2>& polygon: polygons) for (const std::vector<Vec2>& polygon: polygons)
{ {
std::vector<std::vector<c2t::Point>> holes; // unused for now
std::vector<c2t::Point> wall_polygon; std::vector<c2t::Point> wall_polygon;
for (Vec2 p : polygon) for (Vec2 p: polygon)
wall_polygon.emplace_back(p.x, p.y); wall_polygon.emplace_back(p.x, p.y);
c2t::clip2tri clipper; c2t::clip2tri clipper;
std::vector<c2t::Point> clipper_out; std::vector<c2t::Point> clipper_out;
clipper.triangulate(holes, clipper_out, wall_polygon); clipper.triangulate({}, clipper_out, wall_polygon);
p2t::CDT cdt();
for (int i = 0; i < clipper_out.size(); i += 3) for (int i = 0; i < clipper_out.size(); i += 3)
{ {
c2t::Point a = clipper_out.at(i + 0); c2t::Point a = clipper_out.at(i + 0);
c2t::Point b = clipper_out.at(i + 1); c2t::Point b = clipper_out.at(i + 1);
c2t::Point c = clipper_out.at(i + 2); c2t::Point c = clipper_out.at(i + 2);
// Safety clamp
if (a.x < 0) a.x = 0;
if (b.x < 0) b.x = 0;
if (c.x < 0) c.x = 0;
if (a.x > top_points[0].x)
a.x = top_points[0].x;
if (b.x > top_points[0].x)
b.x = top_points[0].x;
if (c.x > top_points[0].x)
c.x = top_points[0].x;
//if (flip)
// std::swap(b, c);
float au = a.x * 0.5f, av = a.y * 0.5f; float au = a.x * 0.5f, av = a.y * 0.5f;
float bu = b.x * 0.5f, bv = b.y * 0.5f; float bu = b.x * 0.5f, bv = b.y * 0.5f;
float cu = c.x * 0.5f, cv = c.y * 0.5f; float cu = c.x * 0.5f, cv = c.y * 0.5f;
// Build mesh data for wall // Build mesh data for wall
Vec3 fva = Vec3(a.x, a.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fva.x += p1.x; fva.z += p1.z; Vec3 fva = Vec3(a.x, a.y, 0.0f).Rotated({0, 1, 0}, -ToDegrees(angle)); fva.x += pos_a.x; fva.z += pos_a.z;
Vec3 fvb = Vec3(b.x, b.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fvb.x += p1.x; fvb.z += p1.z; Vec3 fvb = Vec3(b.x, b.y, 0.0f).Rotated({0, 1, 0}, -ToDegrees(angle)); fvb.x += pos_a.x; fvb.z += pos_a.z;
Vec3 fvc = Vec3(c.x, c.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fvc.x += p1.x; fvc.z += p1.z; Vec3 fvc = Vec3(c.x, c.y, 0.0f).Rotated({0, 1, 0}, -ToDegrees(angle)); fvc.x += pos_a.x; fvc.z += pos_a.z;
bool iba = PointInLine({ fva.x, fva.z }, { pos_a.x, pos_a.z }, { pos_b.x, pos_b.z });
bool ibb = PointInLine({ fvb.x, fvb.z }, { pos_a.x, pos_a.z }, { pos_b.x, pos_b.z });
bool ibc = PointInLine({ fvc.x, fvc.z }, { pos_a.x, pos_a.z }, { pos_b.x, pos_b.z });
// Fix up the UVs so they keep the right scale // Fix up the UVs so they keep the right scale
float tw = texture ? texture_scale / texture->GetWidth() : 0.0f; float tw = texture ? texture_scale / texture->GetWidth() : 0.0f;
@ -281,20 +279,11 @@ void Map::BuildWall(Sector& sector, Wall& wall)
Vec3 b = {wall.end.x, 0.0f, wall.end.y}; Vec3 b = {wall.end.x, 0.0f, wall.end.y};
BuildQuad(sector, sector.floor, sector.ceiling, wall.textures[TEX_FRONT], a, b, false, false, false, wall.uv_offset[TEX_FRONT]); BuildQuad(sector, sector.floor, sector.ceiling, wall.textures[TEX_FRONT], a, b, false, false, false, wall.uv_offset[TEX_FRONT]);
if (wall.flags & Wall::OPENING) if (wall.flags & Wall::FLAG_OPENING)
{ {
Flat& ft = sector.floor; // Build upper and lower walls for sectors connected to other sectors
Flat& fb = wall.portal->floor; BuildQuad(sector, sector.floor, wall.portal->floor, wall.textures[TEX_LOWER], a, b, false, false, false, wall.uv_offset[TEX_LOWER]);
a.y = ft.base_height; BuildQuad(sector, sector.ceiling, wall.portal->ceiling, wall.textures[TEX_UPPER], a, b, true, false, false, wall.uv_offset[TEX_UPPER]);
b.y = fb.base_height;
BuildQuad(sector, ft, fb, wall.textures[TEX_LOWER], a, b, false, false, false, wall.uv_offset[TEX_LOWER]);
Flat& ct = sector.ceiling;
Flat& cb = wall.portal->ceiling;
a.y = ct.base_height;
b.y = cb.base_height;
BuildQuad(sector, ct, cb, wall.textures[TEX_UPPER], a, b, true, false, false, wall.uv_offset[TEX_UPPER]);
} }
} }
@ -334,8 +323,8 @@ void Map::JoinSectors(Sector& sector)
w1.end.x = l.start.x; w1.end.x = l.start.x;
w1.end.y = l.start.y; w1.end.y = l.start.y;
w1.textures[TEX_FRONT] = nullptr; w1.textures[TEX_FRONT] = nullptr;
w1.flags = Wall::FLAG_OPENING;
w1.portal = &s; w1.portal = &s;
w1.flags = Wall::OPENING;
Wall w2 = ld; Wall w2 = ld;
w2.start.x = w1.end.x; w2.start.x = w1.end.x;
w2.start.y = w1.end.y; w2.start.y = w1.end.y;
@ -345,10 +334,10 @@ void Map::JoinSectors(Sector& sector)
InsertLine(sector.walls, i + 1, w1); InsertLine(sector.walls, i + 1, w1);
InsertLine(sector.walls, i + 2, w2); InsertLine(sector.walls, i + 2, w2);
l.textures[TEX_FRONT] = nullptr;
// Mark the other one as an opening // Mark the other one as an opening
// This really shouldn't be necessary I think, but whatever // This really shouldn't be necessary I think, but whatever
l.flags = Wall::OPENING; l.textures[TEX_FRONT] = nullptr;
l.flags = Wall::FLAG_OPENING;
l.portal = &sector; l.portal = &sector;
} }
} }
@ -427,36 +416,90 @@ void Map::Init()
//build_sector(&tex3, &tex, &tex2, -1.2f, 3.0f, 5, points5, std::size(points5)); //build_sector(&tex3, &tex, &tex2, -1.2f, 3.0f, 5, points5, std::size(points5));
build_sector(&tex3, &tex, &tex2, test_u, test_l, 5, points5, std::size(points5), false); build_sector(&tex3, &tex, &tex2, test_u, test_l, 5, points5, std::size(points5), false);
// Build tree // Correct sectors with counter-clockwise linedef order using the shoelace formula,
for (Sector& sp: sectors) // also while we're at it grab the sector areas (will be useful later) and reset old data.
std::unordered_map<int, float> sector_areas;
int i = 0;
for (Sector& s: sectors)
{ {
for (Sector& s: sectors) // Since we're regenerating the map, we don't want to re-use this data, it may be old.
{ s.parents.clear();
if (s.id == sp.id) s.flags = Sector::NO_FLAGS;
continue; s.parent_id = 0;
s.id = ++i;
int cnt = 0; // Now perform the swap + save the area
float area = 0.0f;
for (Wall& l: s.walls)
{
bool touched = l.flags & Wall::FLAG_TOUCHED;
bool no_collisions = l.flags & Wall::FLAG_NO_COLLISION;
l.flags = Wall::NO_FLAGS;
if (touched)
l.flags |= Wall::FLAG_TOUCHED;
if (no_collisions)
l.flags |= Wall::FLAG_NO_COLLISION;
area += l.start.x * l.end.y;
area -= l.end.x * l.start.y;
}
area *= 0.5f;
sector_areas.emplace(s.id, fabsf(area));
if (area <= 0.0f)
{
std::reverse(s.walls.begin(), s.walls.end());
for (Wall& l: s.walls) for (Wall& l: s.walls)
{ std::swap(l.start, l.end);
if (PointInPolygon(sp.walls, l.start)) }
{
cnt++; s.area = fabsf(area);
} }
std::sort(sectors.begin(), sectors.end(), [](const Sector& a, const Sector& b) {
return a.area < b.area;
});
for (auto& sector: sectors) {
for (auto& potentialParent: sectors) {
if (sector.id == potentialParent.id)
continue;
if (SectorContains(potentialParent, sector)) {
potentialParent.children.push_back(&sector);
sector.parent_id = potentialParent.id;
break;
} }
if (cnt == s.walls.size())
sp.children.push_back(&s);
} }
} }
KP3D_LOG_INFO("SECTOR HIERARCHY");
for (Sector& s: sectors)
{
KP3D_LOG_INFO("Sector {} (area: {})", s.id, s.area);
KP3D_LOG_INFO(" Parent: ");
KP3D_LOG_INFO(" - {}", s.parent_id ? std::to_string(s.parent_id) : "[NO PARENT]");
KP3D_LOG_INFO(" Children: ");
for (Sector* c: s.children)
{
KP3D_LOG_INFO(" - {}", c->id);
}
if (s.children.empty())
KP3D_LOG_INFO(" - [NO CHILDREN]");
KP3D_LOG_INFO("-----");
}
// Preproc // Preproc
for (Sector& s : sectors) for (Sector& s: sectors)
{ {
// KP3D_LOG_INFO("BUILDING SECTOR: {}", s.id); // KP3D_LOG_INFO("BUILDING SECTOR: {}", s.id);
// Build up "steiner points" for terrain only // Build up "steiner points" for terrain only
// We'll do this along a grid for now; it should be noted that this will be expected to be part of the sector data later // We'll do this along a grid for now; it should be noted that this will be expected to be part of the sector data later
int num_steiners_along_xy = 50; int num_steiners_along_xy = 50;
float steiner_size = 1.0f; float steiner_size = 1.1f;
for (int i = 0; i < num_steiners_along_xy; i++) for (int i = 0; i < num_steiners_along_xy; i++)
{ {
for (int j = 0; j < num_steiners_along_xy; j++) for (int j = 0; j < num_steiners_along_xy; j++)
@ -489,12 +532,14 @@ void Map::Init()
} }
JoinSectors(s); JoinSectors(s);
}
for (Sector& s : sectors)
{
// Build level geometry // Build level geometry
BuildFlat(s, s.floor, false); BuildFlat(s, s.floor, false);
BuildFlat(s, s.ceiling, true); BuildFlat(s, s.ceiling, true);
} }
for (Sector& s : sectors) for (Sector& s : sectors)
for (Wall& ld : s.walls) for (Wall& ld : s.walls)
BuildWall(s, ld); BuildWall(s, ld);

View file

@ -23,8 +23,10 @@ struct Wall
enum Flags: byte enum Flags: byte
{ {
NO_FLAGS, NO_FLAGS,
OPENING FLAG_OPENING = 1 << 0,
// ... FLAG_DELETE = 1 << 1,
FLAG_NO_COLLISION = 1 << 2,
FLAG_TOUCHED = 1 << 3
}; };
const Texture* textures[3]; const Texture* textures[3];
@ -54,8 +56,13 @@ struct Sector
{ {
enum Flags: byte enum Flags: byte
{ {
NO_FLAGS NO_FLAGS,
// ... FLAG_SUBSECTOR = 1 << 0,
FLAG_HAS_SUBSECTOR = 1 << 1,
FLAG_FLIP_SSECTOR_UL_WALLS = 1 << 2,
FLAG_SUBSECTOR_CONNECTED_TO_SOMETHING = 1 << 3,
FLAG_SUBSECTOR_EXEMPT = 1 << 4,
FLAG_DELETE = 1 << 5
}; };
uint id; uint id;
@ -74,6 +81,9 @@ struct Sector
bool inverted = false; bool inverted = false;
std::vector<Sector*> children; std::vector<Sector*> children;
std::vector<int> parents;
float area = 0.0f;
}; };