diff --git a/Data/sandbox-log.txt b/Data/sandbox-log.txt index 56551db..edc8eab 100644 --- a/Data/sandbox-log.txt +++ b/Data/sandbox-log.txt @@ -1,32 +1,27 @@ -[06:45:36 PM] Info: Starting... +[07:19:31 PM] Info: Starting... KP3D version 2 =============================== Copyright (C) kpworld.xyz 2018-2024 Contact me! @kp_cftsz -[06:45:36 PM] Info: Initializing SDL -[06:45:36 PM] Info: Initializing OpenGL -[06:45:36 PM] Info: OpenGL version: 4.6.0 NVIDIA 536.23 -[06:45:36 PM] Info: Initializing GLEW -[06:45:36 PM] Info: Initializing SDL_mixer -[06:45:36 PM] Info: Reticulating splines... -[06:45:36 PM] Info: Ready! -[06:45:36 PM] Info: Finalize mesh with 45 batches -[06:45:40 PM] Info: Finalize mesh with 50 batches -[06:45:43 PM] Info: Finalize mesh with 52 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:43 PM] Info: Finalize mesh with 56 batches -[06:45:47 PM] Error: Sector 9 discarded; a sector may not overlap another sector unless all of its walls fit inside -[06:45:47 PM] Error: Sector 8 discarded; a sector may not overlap another sector unless all of its walls fit inside -[06:45:47 PM] Info: Finalize mesh with 50 batches +[07:19:31 PM] Info: Initializing SDL +[07:19:31 PM] Info: Initializing OpenGL +[07:19:31 PM] Info: OpenGL version: 4.6.0 NVIDIA 536.23 +[07:19:31 PM] Info: Initializing GLEW +[07:19:31 PM] Info: Initializing SDL_mixer +[07:19:31 PM] Info: Reticulating splines... +[07:19:31 PM] Info: Ready! +[07:19:31 PM] Info: Finalized mesh with 45 batches +[07:19:37 PM] Info: Finalized mesh with 50 batches +[07:19:39 PM] Error: Sector 5 discarded; a sector may not overlap another sector unless all of its walls fit inside +[07:19:39 PM] Error: Sector 1 discarded; a sector may not overlap another sector unless all of its walls fit inside +[07:19:39 PM] Info: Finalized mesh with 25 batches +[07:19:39 PM] Info: Finalized mesh with 25 batches +[07:19:39 PM] Info: Finalized mesh with 25 batches +[07:19:39 PM] Info: Finalized mesh with 25 batches +[07:19:39 PM] Info: Finalized mesh with 25 batches +[07:19:39 PM] Info: Finalized mesh with 25 batches +[07:19:39 PM] Info: Finalized mesh with 25 batches +[07:19:39 PM] Info: Finalized mesh with 25 batches +[07:19:39 PM] Info: Finalized mesh with 25 batches diff --git a/KP3Dii/src/KP3D_Map.cpp b/KP3Dii/src/KP3D_Map.cpp index 7868d87..c43292d 100644 --- a/KP3Dii/src/KP3D_Map.cpp +++ b/KP3Dii/src/KP3D_Map.cpp @@ -23,8 +23,6 @@ struct SectorContainmentStatus bool SectorContains(const kp3d::Sector& outer, const kp3d::Sector& inner, SectorContainmentStatus* output = nullptr) { -#if 0 - using namespace Clipper2Lib; using namespace kp3d; bool completely_inside = false; @@ -32,55 +30,19 @@ bool SectorContains(const kp3d::Sector& outer, const kp3d::Sector& inner, Sector int count = 0; int partial_count = 0; - 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}); - - for (PointD& p: inner_path) - { - PointInPolygonResult status = Clipper2Lib::PointInPolygon(p, outer_path); - if (status == PointInPolygonResult::IsInside) - count++; - if (status == PointInPolygonResult::IsOn) - partial_count++; - } - if ((count + partial_count) == inner_path.size()) - completely_inside = true; - if (partial_count > 0 && count > 0) - partially_inside = true; -#else - bool completely_inside = false; - bool partially_inside = false; - int count = 0; - int partial_count = 0; - - using namespace kp3d; - for (const Wall& l : inner.walls) { if (PointInPolygon(outer.walls, l.start)) - { count++; - } for (const Wall& l2 : outer.walls) - { if (PointInLine(l.start, l2.start, l2.end)) - { partial_count++; - } - } } - if ((count) == inner.walls.size()) + if (count == inner.walls.size()) completely_inside = true; - if (partial_count > 0)// && count > 0) + if (partial_count > 0) partially_inside = true; -#endif - if (output) { output->count = count; @@ -90,7 +52,7 @@ bool SectorContains(const kp3d::Sector& outer, const kp3d::Sector& inner, Sector // KP3D_LOG_INFO("SectorContains: outer: {}, inner: {} - count: {}, partial count: {} / complete: {}, partial: {}", outer.id, inner.id, count, partial_count, completely_inside, partially_inside); - return completely_inside;// || (count > 0 && partial_count < 4); // stupid + return completely_inside; } // Callback used by the renderer and editor @@ -102,11 +64,6 @@ kp3d::uint ShouldHighlight(const kp3d::RenderBatch3D& batch) if (&batch != kp3d::editor_hovered_batch) return 0xFFFFFFFF; kp3d::BatchSectorInfo info = std::any_cast(batch.userdata); - // if (info.wall) - // return 0xFF5555FF; - // else if (info.flat) - // return 0xFFFF55FF; - // else if (info.sector) return 0x5555FFFF; } @@ -170,12 +127,9 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert) l.textures[TEX_FRONT] = nullptr; l.flags = Wall::FLAG_OPENING | Wall::FLAG_SUBSECTOR_OPENING; l.portal = §or; - // flat.steiner_points.push_back({ l.start.x, flat.base_height, l.start.y }); - //s->floor.steiner_points.push_back({ l.start.x, s->floor.base_height, l.start.y }); - //s->ceiling.steiner_points.push_back({ l.start.x, s->ceiling.base_height, l.start.y }); } - //if (l.flags & Wall::FLAG_SUBSECTOR_OPENING) - // steiner_points.push_back({l.start.x, l.start.y}); + // if (l.flags & Wall::FLAG_SUBSECTOR_OPENING) + // steiner_points.push_back({l.start.x, l.start.y}); } ss.push_back({l.start.x, l.start.y}); } @@ -280,18 +234,11 @@ void Map::BuildQuad(Sector& sector, Wall& wall, Flat& flat_topr, Flat& flat_bott Vec2 mpos = {Distance({v.position.x, v.position.z}, {pos_a.x, pos_a.z}), v.position.y}; points.push_back(mpos); } - if (!points.empty()) - { - //if (!FloatCmp(points.back().x, Distance({ pos_a.x, pos_a.z }, { pos_b.x, pos_b.z }), E) && !FloatCmp(points.back().y, flat_bottom.base_height, E)) - // points.push_back({ Distance({ pos_a.x, pos_a.z }, {pos_b.x, pos_b.z}), flat_bottom.base_height }); - } - else + if (points.empty()) { points.push_back({0.0f, flat_bottom.base_height}); points.push_back({Distance({pos_a.x, pos_a.z}, {pos_b.x, pos_b.z}), flat_bottom.base_height}); } - //if (points.empty()) - // points.push_back({ 0.0f, flat_bottom.base_height }); std::vector top_points; for (const Vertex3D& v: flat_top.triangulated_data) { @@ -300,18 +247,11 @@ void Map::BuildQuad(Sector& sector, Wall& wall, Flat& flat_topr, Flat& flat_bott Vec2 mpos = {Distance({v.position.x, v.position.z}, {pos_a.x, pos_a.z}), v.position.y}; top_points.push_back(mpos); } - if (!top_points.empty()) - { - //if (!FloatCmp(top_points.back().x, 0.0f, E) && !FloatCmp(top_points.back().y, flat_top.base_height, E)) - // top_points.push_back({ 0.0f, flat_top.base_height }); - } - else + if (top_points.empty()) { top_points.push_back({ Distance({pos_a.x, pos_a.z}, {pos_b.x, pos_b.z}), flat_top.base_height }); top_points.push_back({ 0.0f, flat_top.base_height }); } - // if (top_points.empty()) - // top_points.push_back({Distance({0.0f, 0.0f}, {pos_b.x - pos_a.x, pos_b.z - pos_a.x}), flat_top.base_height}); std::sort(points.begin(), points.end(), [&](Vec2 a, Vec2 b) { return a.x < b.x; }); 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()); @@ -362,6 +302,9 @@ void Map::BuildQuad(Sector& sector, Wall& wall, Flat& flat_topr, Flat& flat_bott } } +/* + * Deals with building walls for sectors as well as upper/lower walls for joined sectors, subsectors, and joined subsectors + */ void Map::BuildWall(Sector& sector, Wall& wall) { Vec3 a = {wall.start.x, 0.0f, wall.start.y}; @@ -397,53 +340,12 @@ void Map::BuildWall(Sector& sector, Wall& wall) } } -void Map::SanitizeSectors() -{ - // Before we can build the map, we need to do some safety checks and fix up the level geometry since we could easily encounter - // unexpected input, weird edge cases, etc. - // For instance, what do we do when two sectors overlap? - // - If the overlapping sector is completely contained by the sector it's overlapping, keep it as-is so we can make it a child - // - If the overlapping sector is *partially* overlapping, nuke the sector (if you need this functionality, prefer connected - // subsectors instead) - std::vector to_remove; - for (const auto& sp2: sectors) - { - for (const auto& sp : sectors) - { - Sector& s = *sp; - if (sp->id == sp2->id) - continue; - - Sector& s2 = *sp2; - if (s2.inverted || s.inverted) - continue; - - SectorContainmentStatus status; - SectorContains(s, s2, &status); - - if (status.partial_count == s2.walls.size()) - { - KP3D_LOG_ERROR("Sector {} discarded; a sector may not overlap another sector unless all of its walls fit inside", s2.id); - to_remove.push_back(sp2.get()); - } - } - } - - to_remove.erase(std::unique(to_remove.begin(), to_remove.end()), to_remove.end()); - - for (const auto& sp: to_remove) - sectors.erase(std::remove_if(sectors.begin(), sectors.end(), [&](const std::shared_ptr& sec) { return sec.get() == sp; }), sectors.end()); -} - void Map::JoinSectors(Sector& sector) { // "Connect" sectors by splitting up their walls where other sectors intersect - //for (int i = 0; i < sector.walls.size(); i++) for (int i = 0; i < sector.walls.size(); i++) { Wall& ld = sector.walls[i]; - //if (ld.flags & Wall::FLAG_TOUCHED) - // continue; for (const auto& sp: sectors) { Sector& s = *sp; @@ -459,17 +361,15 @@ void Map::JoinSectors(Sector& sector) float yt = s.ceiling.base_height; bool not_child = s.parent_id != sector.id; - float e = 1.0f / 128.0f; + const float E = 1.0f / 128.0f; for (Wall& l: s.walls) { - //if (l.flags & Wall::FLAG_TOUCHED) - // continue; bool good = false; - bool start_cmp = PosCmp({l.start.x, 0.0f, l.start.y}, {pos_a.x, 0.0f, pos_a.z}, e) || - PosCmp({l.start.x, 0.0f, l.start.y}, {pos_b.x, 0.0f, pos_b.z}, e); - bool end_cmp = PosCmp({l.end.x, 0.0f, l.end.y}, {pos_b.x, 0.0f, pos_b.z}, e) || - PosCmp({l.end.x, 0.0f, l.end.y}, {pos_a.x, 0.0f, pos_a.z}, e); + bool start_cmp = PosCmp({l.start.x, 0.0f, l.start.y}, {pos_a.x, 0.0f, pos_a.z}, E) || + PosCmp({l.start.x, 0.0f, l.start.y}, {pos_b.x, 0.0f, pos_b.z}, E); + bool end_cmp = PosCmp({l.end.x, 0.0f, l.end.y}, {pos_b.x, 0.0f, pos_b.z}, E) || + PosCmp({l.end.x, 0.0f, l.end.y}, {pos_a.x, 0.0f, pos_a.z}, E); bool same_points = start_cmp && end_cmp; bool start_on_seg = PointInLine(l.start, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z}); bool end_on_seg = PointInLine(l.end, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z}); @@ -511,6 +411,9 @@ void Map::JoinSectors(Sector& sector) switch (type) { + // e.g. + // +----|-------------|----+ + // +-------------+ case OVERLAP_NO_TOUCH: { XYf old_end = ld.end; @@ -523,7 +426,6 @@ void Map::JoinSectors(Sector& sector) w1.end.x = l.start.x; w1.end.y = l.start.y; w1.textures[TEX_FRONT] = nullptr; - // w1.flags = Wall::FLAG_OPENING; w1.portal = &s; w1.flags = Wall::FLAG_TOUCHED; InsertLine(sector.walls, i + 1, w1); @@ -536,6 +438,9 @@ void Map::JoinSectors(Sector& sector) InsertLine(sector.walls, i + 2, w2); } break; + // e.g. + // +-------------|---------+ + // +-------------+ case OVERLAP_LEFT_TOUCH: { Wall w1 = ld; @@ -552,6 +457,9 @@ void Map::JoinSectors(Sector& sector) InsertLine(sector.walls, i + 1, w1); } break; + // e.g. + // +---------|-------------+ + // +-------------+ case OVERLAP_RIGHT_TOUCH: { Wall w1 = ld; @@ -569,6 +477,9 @@ void Map::JoinSectors(Sector& sector) InsertLine(sector.walls, i + 1, w1); } break; + // e.g. + // +-----------------------+ + // +-----------------------+ case OVERLAP_BOTH_TOUCH: { if (!sector.inverted) // not sure if we want to discount inverted sectors for just this case or all of @@ -595,59 +506,31 @@ void Map::JoinSectors(Sector& sector) } } -bool Map::WallOverlaps(Sector& sector, Wall& ld) +void Map::SanitizeSectors() { - bool d = false; - for (const auto& sp: sectors) + // Before we can build the map, we need to do some safety checks and fix up the level geometry since we could easily encounter + // unexpected input, weird edge cases, etc. + std::vector to_remove; + for (const auto& sp2 : sectors) { - Sector& s = *sp; - if (s.id == sector.id) - continue; - - Vec3 pos_a = { ld.start.x, sector.floor.base_height, ld.start.y }; - Vec3 pos_b = { ld.end.x, sector.ceiling.base_height, ld.end.y }; - - float yb = s.floor.base_height; - float yt = s.ceiling.base_height; - bool not_child = s.parent_id != sector.id; - - float e = 1.0f / 128.0f; - - for (Wall& l: s.walls) + for (const auto& sp : sectors) { - //if (l.flags & Wall::FLAG_TOUCHED) - // continue; - bool good = false; - bool start_cmp = PosCmp({ l.start.x, 0.0f, l.start.y }, { pos_a.x, 0.0f, pos_a.z }, e) || - PosCmp({ l.start.x, 0.0f, l.start.y }, { pos_b.x, 0.0f, pos_b.z }, e); - bool end_cmp = PosCmp({ l.end.x, 0.0f, l.end.y }, { pos_b.x, 0.0f, pos_b.z }, e) || - PosCmp({ l.end.x, 0.0f, l.end.y }, { pos_a.x, 0.0f, pos_a.z }, e); - bool same_points = start_cmp && end_cmp; - bool start_on_seg = PointInLine(l.start, { pos_a.x, pos_a.z }, { pos_b.x, pos_b.z }); - bool end_on_seg = PointInLine(l.end, { pos_a.x, pos_a.z }, { pos_b.x, pos_b.z }); - bool on_segment = start_on_seg && end_on_seg; - - bool right_join = start_on_seg && end_cmp; - bool left_join = end_on_seg && start_cmp; - - if (on_segment && not_child) { - - if (same_points) - return true; - //if (left_join) - // return true; - //else if (right_join) - // return true;//d = true; - - //KP3D_LOG_INFO("Wall is overlapping!"); + if (sp->id == sp2->id || sp2->inverted || sp->inverted) + continue; + SectorContainmentStatus status; + SectorContains(*sp, *sp2, &status); + if (status.partial_count == sp2->walls.size()) + { + KP3D_LOG_ERROR("Sector {} discarded; a sector may not overlap another sector unless all of its walls fit inside", sp2->id); + to_remove.push_back(sp2.get()); } - - //if (on_segment && not_child) - // good = true; } } - return d; + to_remove.erase(std::unique(to_remove.begin(), to_remove.end()), to_remove.end()); + + for (const auto& sp : to_remove) + sectors.erase(std::remove_if(sectors.begin(), sectors.end(), [&](const std::shared_ptr& sec) { return sec.get() == sp; }), sectors.end()); } void Map::Init() @@ -710,13 +593,6 @@ void Map::Rebuild(NormalGenType gen_normals) { m_mesh.Reset(); - //sectors.clear(); - //for (Sector& s : original_sectors) - //{ - // s.original = &s; - // sectors.push_back(s); - //} - // Correct sectors with counter-clockwise linedef order using the shoelace formula, // also while we're at it grab the sector areas (will be useful later) and reset old data. int i = 0; @@ -738,13 +614,7 @@ void Map::Rebuild(NormalGenType gen_normals) 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; l.portal = nullptr; area += l.start.x * l.end.y; @@ -760,7 +630,6 @@ void Map::Rebuild(NormalGenType gen_normals) s.area = fabsf(area); } - // Now perform the process of "parenting" sectors; this essentially takes in our flat list of sectors and creates a hierarchy. std::sort(sectors.begin(), sectors.end(), [](const auto& a, const auto& b) { return a->area < b->area; }); SanitizeSectors(); @@ -782,6 +651,7 @@ void Map::Rebuild(NormalGenType gen_normals) } } } + SanitizeSectors(); #if 0 // Debug: Print out the sector hierachy @@ -881,11 +751,12 @@ void Map::Rebuild(NormalGenType gen_normals) (au.sector != bu.sector || au.flat != bu.flat || au.wall != bu.wall); } ); - KP3D_LOG_INFO("Finalize mesh with {} batches", m_mesh.GetBatchesRef().size()); + KP3D_LOG_INFO("Finalized mesh with {} batches", m_mesh.GetBatchesRef().size()); } void Map::Update() { + // ... } void Map::Render() @@ -894,7 +765,7 @@ void Map::Render() RenderSkybox(); // Map - glDisable(GL_CULL_FACE); + glDisable(GL_CULL_FACE); // <- we shouldn't count on this kp3d::Renderer3D::PushShader(kp3d::Renderer3D::GetMapShader()); kp3d::Renderer3D::DrawMesh(m_mesh, m_transform, true, ShouldHighlight); kp3d::Renderer3D::PopShader(); @@ -974,7 +845,6 @@ void Map::BuildSkybox(const std::string& texture_path, float scale) void Map::RenderSkybox() { - return; glDisable(GL_DEPTH_TEST); Renderer3D::PushShader(Renderer3D::GetDefaultShader()); m_skybox_data.transform.translation = Renderer3D::GetPrimaryCamera()->position; diff --git a/KP3Dii/src/KP3D_Map.h b/KP3Dii/src/KP3D_Map.h index cd7cf9a..4aae9c6 100644 --- a/KP3Dii/src/KP3D_Map.h +++ b/KP3Dii/src/KP3D_Map.h @@ -132,9 +132,8 @@ public: void BuildFlat(Sector& sector, Flat& flat, bool invert); void BuildQuad(Sector& sector, Wall& wall, Flat& flat_top, Flat& flat_bottom, const Texture* texture, Vec3 pos_a, Vec3 pos_b, bool flip, bool flip_u, bool flip_v, XYf uv_offset); void BuildWall(Sector& sector, Wall& wall); - void SanitizeSectors(); void JoinSectors(Sector& sector); - bool WallOverlaps(Sector& sector, Wall& wall); + void SanitizeSectors(); void Init();