Experimenting...
This commit is contained in:
parent
a63c6e683e
commit
5a13579849
6 changed files with 346 additions and 6833 deletions
6732
Data/sandbox-log.txt
6732
Data/sandbox-log.txt
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = §or;
|
||||||
}
|
}
|
||||||
|
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 = §or;
|
l.portal = §or;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(§or);
|
||||||
|
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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue