Fully Meshed Entire Chunk in 3d

Finally have fully meshed chunk. it takes 20 seconds to fully run on one
chunk.
Its my beauty and i love it.

so far the axis iterators i feel are showing their annoyance. we can
possibly refactor things to use ints on the planular and cast between
that and chunk vs using the iterators any further.

Got the math all figured out and now it works fully without any crashing
or assert failures, everything asserts properly :O
Very exciting time for this, got a demo setup with a camera angle and
everything.

so we still have work to do with performance and scaling/offsetting the output so
we can actually fit it on the screen.
This commit is contained in:
Rebekah 2024-02-23 07:21:40 -05:00
parent d9a491f56a
commit eb6694ba5f
Signed by: oneechanhax
GPG Key ID: 183EB7902964DAE5

View File

@ -496,8 +496,17 @@ public:
ENUM_BLOCK_TYPE::E_BLOCK_AIR,
ENUM_BLOCK_TYPE::E_BLOCK_STONE,
};
static std::size_t MakeIndexFromPlanularPosition(PlaneVecType in_pos) {
return cChunkDef::MakeIndex(in_pos.x, 0, in_pos.y);
private:
static std::size_t MakeIndexFromPlanularPosition(int x, int y) {
assert(cChunkDef::IsValidWidth(x) && cChunkDef::IsValidHeight(Vector3i(0, y, 0)));
return static_cast<size_t>(x + (y * cChunkDef::Width)) /*+ (y * Width * Width))*/; // assert fails us on the other
}
public:
static std::size_t MakeIndexFromPlanularPosition(PlaneVecType plane_pos) {
return MakeIndexFromPlanularPosition(plane_pos.x, plane_pos.y); // assert fails us on the other
// return cChunkDef::MakeIndex(in_pos.x, 0, in_pos.y);
}
BlockType GetVoxelType(std::size_t v) const {
return raw_chunk_data[v];
@ -652,31 +661,31 @@ public:
case CardinalDirections::kDown:
return ChunkVecType(plane_coords.x, (cChunkDef::Height - 1) - depth, (cChunkDef::Width - 1) - plane_coords.y);
case CardinalDirections::kNorth:
return ChunkVecType((cChunkDef::Height - 1) - plane_coords.x, (cChunkDef::Height - 1) - plane_coords.y, (cChunkDef::Width - 1) - depth);
return ChunkVecType((cChunkDef::Width - 1) - plane_coords.x, (cChunkDef::Height - 1) - plane_coords.y, (cChunkDef::Width - 1) - depth);
case CardinalDirections::kSouth:
return ChunkVecType(plane_coords.x, (cChunkDef::Height - 1) - plane_coords.y, depth);
case CardinalDirections::kWest:
return ChunkVecType((cChunkDef::Width - 1) - depth, (cChunkDef::Height - 1) - plane_coords.y, plane_coords.x);
case CardinalDirections::kEast:
return ChunkVecType(depth, (cChunkDef::Height - 1) - plane_coords.y, cChunkDef::Height - plane_coords.x - 1);
return ChunkVecType(depth, (cChunkDef::Height - 1) - plane_coords.y, (cChunkDef::Width - 1) - plane_coords.x);
default:
throw std::logic_error("Trying to access a cardinal Direction that does not work!");
};
}
static constexpr PlaneVecType ProjectChunkVecToPlanular(ChunkVecType block_coords, const CardinalDirections facing_chunk_from_dir, std::size_t depth) {
static constexpr PlaneVecType ProjectChunkVecToPlanular(ChunkVecType block_coords, const CardinalDirections facing_chunk_from_dir) {
switch (facing_chunk_from_dir) { // essentially we offset by a "seed block" we set depending on the direction.
case CardinalDirections::kUp:
return PlaneVecType(block_coords.x, block_coords.z);
case CardinalDirections::kDown:
return PlaneVecType(block_coords.x, (cChunkDef::Width - 1) - block_coords.z);
case CardinalDirections::kNorth:
return PlaneVecType((cChunkDef::Height - 1) - block_coords.x, (cChunkDef::Width - 1) - depth);
return PlaneVecType((cChunkDef::Width - 1) - block_coords.x, (cChunkDef::Height - 1) - block_coords.y);
case CardinalDirections::kSouth:
return PlaneVecType(block_coords.x, depth);
return PlaneVecType(block_coords.x, (cChunkDef::Height - 1) - block_coords.y);
case CardinalDirections::kWest:
return PlaneVecType((cChunkDef::Width - 1) - depth, block_coords.z);
return PlaneVecType(block_coords.z, (cChunkDef::Height - 1) - block_coords.y);
case CardinalDirections::kEast:
return PlaneVecType(depth, (cChunkDef::Height - 1) - block_coords.x);
return PlaneVecType((cChunkDef::Width - 1) - block_coords.z, (cChunkDef::Height - 1) - block_coords.y);
default:
throw std::logic_error("Trying to access a cardinal Direction that does not work!");
};
@ -694,7 +703,7 @@ public:
kIntrinsicGreedy, // Using bitwise operations :O
kGlobalLattice // literally Impossible to recreate, meshing everything INCLUDING air, and rendering it straight up in shader
};
static constexpr float box_size = 25.0f; // TODO find out where its located!!! if we can find it in server, we can apply this then try to do some simple transformations.
static constexpr float box_size = 5.0f; // TODO find out where its located!!! if we can find it in server, we can apply this then try to do some simple transformations.
// glez::vertex not verbose enough for us sadly...
struct ChunkMeshedQuad {
geo::Box<geo::Vec2> quad;
@ -711,11 +720,17 @@ public:
glez::color::green, // sapling
glez::color::black // bedrock
};
static constexpr bool gfx_mesher_debuglog = true;
static constexpr bool gfx_mesher_debuglog = false;
static constexpr bool gfx_mesher_debuglog_spam = gfx_mesher_debuglog && false;
std::vector<ChunkMeshedQuad> MeshSinglePlaneAtDepth(MesherComplexity cmplxity, CardinalDirections facing_direction_from = CardinalDirections::kUp, int depth = 0) const { // Only good for the TOP/BOTTOM face of the bottom row. Doesnt check if air is in front of the face.
bool is_chunk_only_air = this->IsEmpty();
if (is_chunk_only_air)
return {}; // Your fault you didnt check it first...
{
const bool is_chunk_only_air = this->IsEmpty();
if (is_chunk_only_air)
return {}; // Your fault you didnt check it first...
if (gfx_mesher_debuglog)
std::cout << "Mesher is starting!: " << std::endl;
}
const auto blockface_plane_geo_axis_info = AxisIterator::CreateCardinalDirectionalMasksForFacingFromDirection(facing_direction_from);
const auto south_dir_mask = blockface_plane_geo_axis_info.first;
@ -723,27 +738,27 @@ public:
const auto plane_facing_us_size = AxisIterator::GetPlaneSliceFacingFromDirectionSize(facing_direction_from);
const auto IsPlaneEmpty = [&]() -> bool {
for (std::size_t south = 0; south < plane_facing_us_size.first; south++) {
for (std::size_t east = 0; east < plane_facing_us_size.second; east++) {
const PlaneVecType planular_block_coords = { south, east };
std::cout << "Greedy found Seed block at planular coords: {" << (int)planular_block_coords.x << ", " << (int)planular_block_coords.y << "}" << std::endl;
const auto block_coords = ProjectPlanularToChunkVec(planular_block_coords, facing_direction_from, depth);
std::cout << " Seed Block Real Coords: \"" << (int)this->GetVoxelType(cChunkDef::MakeIndex(block_coords)) << "\": {" << (int)block_coords.x << ", " << (int)block_coords.y << ", " << (int)block_coords.z << "}" << std::endl;
if (this->GetVoxelType(cChunkDef::MakeIndex(block_coords)) != ENUM_BLOCK_TYPE::E_BLOCK_AIR)
return false;
}
{
const auto IsPlaneEmpty = [&]() -> bool {
for (std::size_t south = 0; south < plane_facing_us_size.first; south++) {
for (std::size_t east = 0; east < plane_facing_us_size.second; east++) {
const PlaneVecType planular_block_coords = { south, east };
const auto block_coords = ProjectPlanularToChunkVec(planular_block_coords, facing_direction_from, depth);
if (this->GetVoxelType(cChunkDef::MakeIndex(block_coords)) != ENUM_BLOCK_TYPE::E_BLOCK_AIR)
return false;
}
};
return true;
};
return true;
};
if (IsPlaneEmpty())
return {};
if (IsPlaneEmpty())
return {};
}
std::vector<ChunkMeshedQuad>
finished_quads;
if (gfx_mesher_debuglog)
std::cout << "Start Meshing: ";
const auto start_time = std::chrono::high_resolution_clock::now();
std::vector<ChunkMeshedQuad> finished_quads;
switch (cmplxity) {
case MesherComplexity::kNieve: // I dont think this is possible in 2d for the time being...
case MesherComplexity::kPlain: {
@ -785,7 +800,9 @@ public:
std::vector<bool> chunk_finished_checksum(AxisIterator::GetPlaneSliceFacingFromDirectionBlockCount(facing_direction_from), false); // i believe its faster(vs an array) when checking the entire thing for boolean
const auto IsPlanularBlockMarkedOff = [&](std::size_t blockidx) -> bool { return chunk_finished_checksum[blockidx]; };
const auto IsChunkBlockMarkedOff = [&](ChunkVecType block_coords) -> bool {
const auto planular_coords = ProjectChunkVecToPlanular(block_coords, facing_direction_from, depth);
const auto planular_coords = ProjectChunkVecToPlanular(block_coords, facing_direction_from);
if (gfx_mesher_debuglog_spam)
std::cout << " checksum test projecting to planular coords from the above block: {" << (int)planular_coords.x << ", " << (int)planular_coords.y << "}" << std::endl;
return IsPlanularBlockMarkedOff(MakeIndexFromPlanularPosition(planular_coords));
};
using UnmeshedOpenSpacePosType = std::optional<std::pair<std::size_t, PlaneVecType>>; // WITHIN PLANULAR SPACE WARNING!!!
@ -800,6 +817,7 @@ public:
}
return UnmeshedOpenSpacePosType();
};
const auto IsMeshIncomplete = [&]() -> bool {
for (const bool& i : chunk_finished_checksum)
if (!i)
@ -814,8 +832,8 @@ public:
const auto seed_planular_blockpos = unmeshed_face.value();
const auto seed_planular_blockidx = seed_planular_blockpos.first;
const auto seed_planular_block_coords = seed_planular_blockpos.second;
// if (gfx_mesher_debuglog) // TOO MUCH LOGS! enable it if ur debugging rly hard.
// std::cout << "Greedy found Seed block at planular coords: {" << (int)seed_planular_block_coords.x << ", " << (int)seed_planular_block_coords.y << "}" << std::endl;
if (gfx_mesher_debuglog_spam)
std::cout << "Greedy testing Seed block at planular coords: {" << (int)seed_planular_block_coords.x << ", " << (int)seed_planular_block_coords.y << "}" << std::endl;
const auto seed_block_coords = ProjectPlanularToChunkVec(seed_planular_block_coords, facing_direction_from, depth);
const auto seed_blockidx = cChunkDef::MakeIndex(seed_block_coords);
@ -831,16 +849,24 @@ public:
PlaneVecType block_quad_size = { 1, 1 }; // in coords
// Need a better "search line down" func... instead of one block we need to check multiple and return how many are the same.
const auto SearchLineSouthForSameTypes = [&](ChunkVecType src_line, std::uint8_t max_size = cChunkDef::Width) -> std::uint8_t {
auto SearchLineSouthForSameTypes = [&](ChunkVecType src_line, std::optional<std::uint8_t> wanted_max_size = std::optional<std::uint8_t>()) -> std::uint8_t {
auto max_size = plane_facing_us_size.second;
if (wanted_max_size)
max_size = *wanted_max_size;
std::size_t alike_counter = 0;
auto south_axis_crawl = AxisIterator(src_line, south_dir_mask);
assert(south_axis_crawl.GetNumAxisDirections() == 1);
for (; south_axis_crawl != AxisIterator::end(); south_axis_crawl++) {
if (gfx_mesher_debuglog_spam)
std::cout << " Southern Plane sameface block searcher looking at real coords: {" << (int)(*south_axis_crawl).x << ", " << (int)(*south_axis_crawl).y << ", " << (int)(*south_axis_crawl).z << "}" << std::endl;
const std::size_t south_blockidx_to_check = cChunkDef::MakeIndex(*south_axis_crawl);
const auto south_block_type = this->GetVoxelType(south_blockidx_to_check);
if (south_block_type != seed_block_type)
break;
if (IsChunkBlockMarkedOff(*south_axis_crawl))
break;
alike_counter++;
@ -858,7 +884,7 @@ public:
std::cout << " Found blocks going south: " << (int)block_quad_size.y << std::endl;
const auto SearchLineEastForSameTypes = [&]() -> std::uint8_t {
if (gfx_mesher_debuglog)
if (gfx_mesher_debuglog_spam)
std::cout << " Searching More East!!" << std::endl;
auto east_axis_crawl = AxisIterator(seed_block_coords, east_dir_mask);
@ -867,6 +893,10 @@ public:
east_axis_crawl++; // starting one ahead
std::size_t alike_counter = 1;
for (; east_axis_crawl != AxisIterator::end(); east_axis_crawl++) {
if (gfx_mesher_debuglog_spam)
std::cout << " Eastern Plane sameface block searcher looking at real coords: \""
<< "\": {" << (int)(*east_axis_crawl).x << ", " << (int)(*east_axis_crawl).y << ", " << (int)(*east_axis_crawl).z << "}" << std::endl;
const std::size_t east_blockidx_to_check = cChunkDef::MakeIndex(*east_axis_crawl);
const auto east_block_type = this->GetVoxelType(east_blockidx_to_check);
if (east_block_type != seed_block_type)
@ -933,15 +963,30 @@ public:
return finished_quads;
}
MeshedChunk MeshEntireChunk(MesherComplexity cmplxity) const {
const auto start_time = std::chrono::high_resolution_clock::now();
std::size_t quad_count = 0;
MeshedChunk ret;
for (int facing_from_direction = 0; facing_from_direction < 6; facing_from_direction++) {
const auto max_depth = AxisIterator::GetChunkDepthFacingDirection(CardinalDirections(facing_from_direction));
ret[facing_from_direction] = std::vector<std::vector<ChunkMeshedQuad>>(max_depth);
for (std::size_t depth = 0; depth < max_depth; depth++) {
ret[facing_from_direction][depth] = MeshSinglePlaneAtDepth(cmplxity, CardinalDirections(facing_from_direction), depth);
auto new_mesh = this->MeshSinglePlaneAtDepth(cmplxity, CardinalDirections(facing_from_direction), depth);
if (!new_mesh.empty()) {
quad_count += new_mesh.size();
ret[facing_from_direction][depth] = std::move(new_mesh);
}
}
}
const auto end_time = std::chrono::high_resolution_clock::now(); // Calculate duration and convert to milliseconds
const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
std::cout << "Chunk Fully Meshed: QuadCount: " << (int)quad_count << ", Within: " << duration << "ms!" << std::endl;
return ret;
}
bool meshes_are_dirty = false;
std::optional<MeshedChunk> cached_meshes;
// static bool IsPosValidWithinPlane(ChunkVecType block_coords) {
static bool IsPosValidWithinChunk(ChunkVecType block_coords) {
if (!cChunkDef::IsValidWidth(block_coords.x))
@ -960,7 +1005,10 @@ class WindowSettings : public CBaseWindow {
public:
CCheckbox* activate;
CCheckbox* wireframe;
CDropdown* dropdown;
CDropdown* mesh_complexity;
CDropdown* camera_angle;
CSlider* depth;
WindowSettings(IWidget* parent)
: CBaseWindow(parent, "qubelmesh_test_settings") {
this->always_visible = false;
@ -973,24 +1021,41 @@ public:
this->Add<CTextLabel>("activate_label", "Activate Viewing:");
activate = this->Add<CCheckbox>("activate", false);
this->Add<CTextLabel>("spread_label", "VoxelSpread:");
this->Add<CSlider>("spread")->SetStep(0.1f);
// this->Add<CTextLabel>("spread_label", "VoxelSpread:");
// this->Add<CSlider>("spread")->SetStep(0.1f);
this->Add<CTextLabel>("wireframe_label", "Wireframe Mesh:");
wireframe = this->Add<CCheckbox>("wireframe", true);
this->Add<CTextLabel>("wanted_mesher_label", "Meshing Algorithm:");
dropdown = this->Add<CDropdown>("wanted_mesher");
dropdown->AddValue("NoFaceOcclusion, Plain");
dropdown->AddValue("Nieve");
dropdown->AddValue("Greedy"); // Just expand same type faces
dropdown->AddValue("Intrinsic Greedy"); // Using bitwise operations :O
dropdown->AddValue("Global Lattice"); // literally Impossible to recreate, meshing everything INCLUDING air, and rendering it straight up in shader
dropdown->SetSize(150, 16);
dropdown->SetValue(2);
mesh_complexity = this->Add<CDropdown>("wanted_mesher");
mesh_complexity->AddValue("NoFaceOcclusion, Plain");
mesh_complexity->AddValue("Nieve");
mesh_complexity->AddValue("Greedy"); // Just expand same type faces
mesh_complexity->AddValue("Intrinsic Greedy"); // Using bitwise operations :O
mesh_complexity->AddValue("Global Lattice"); // literally Impossible to recreate, meshing everything INCLUDING air, and rendering it straight up in shader
mesh_complexity->SetSize(150, 16);
mesh_complexity->SetValue(2);
this->Add<CTextLabel>("binary_meshing_label", "Binary Meshing(ignore types):");
this->Add<CCheckbox>("binary_meshing", false);
this->Add<CTextLabel>("wanted_mesher_angle_label", "Camera Angle:");
camera_angle = this->Add<CDropdown>("wanted_mesher_angle");
camera_angle->AddValue("North");
camera_angle->AddValue("South");
camera_angle->AddValue("East");
camera_angle->AddValue("West");
camera_angle->AddValue("Up");
camera_angle->AddValue("Down");
camera_angle->SetSize(150, 16);
camera_angle->SetValue(int(CardinalDirections::kUp));
this->Add<CTextLabel>("spread_label", "Depth:");
depth = this->Add<CSlider>("depth");
depth->SetStep(1.0f);
depth->Setup(0, 2);
depth->SetValue(0);
// this->Add<CTextLabel>("binary_meshing_label", "Binary Meshing(ignore types):");
// this->Add<CCheckbox>("binary_meshing", false);
}
};
} // namespace test
@ -1125,11 +1190,11 @@ namespace plane {
class ChunkRenderer : public CBaseWidget {
public:
static constexpr std::pair<int, int> min_size = { 856, 480 };
const ClientChunk* world_slice;
const test::WindowSettings* settings;
const CardinalDirections camera_angle;
ClientChunk* const world_slice;
const test::WindowSettings* const settings;
CardinalDirections camera_angle;
std::size_t depth;
std::vector<ClientChunk::ChunkMeshedQuad> current_render_quads;
ChunkRenderer(IWidget* parent, test::WindowSettings* settings, ClientChunk* world_slice, CardinalDirections facing_the_chunk_from)
: CBaseWidget("qubelmesh_test_renderer_sceneoutput", parent)
, world_slice(world_slice)
@ -1137,39 +1202,69 @@ public:
, camera_angle(facing_the_chunk_from) {
assert(settings && world_slice);
assert(facing_the_chunk_from < 6);
settings->dropdown->SetCallback([&](CDropdown*, int value) { this->MarkChunkDirty(); });
this->SetSize(ChunkRenderer::min_size.first, ChunkRenderer::min_size.second);
settings->mesh_complexity->SetCallback([&](CDropdown*, int value) { this->MarkChunkDirty(); });
settings->camera_angle->SetCallback([this](CDropdown*, int value) {
this->camera_angle = CardinalDirections(value);
auto cur_depth = this->settings->depth->Value();
const auto new_max_depth = ClientChunk::AxisIterator::GetChunkDepthFacingDirection(CardinalDirections(value));
if (cur_depth < 0)
cur_depth++;
if (cur_depth > new_max_depth)
cur_depth -= (cur_depth - new_max_depth);
this->settings->depth->SetValue(cur_depth);
this->settings->depth->Setup(0.0f, static_cast<float>(new_max_depth) - 1);
});
settings->camera_angle->SetValue(CardinalDirections::kUp);
settings->depth->SetCallback([&](CSlider*, float old_value, float new_value) {
assert(new_value >= 0.0f);
this->depth = (int)new_value;
});
// this->SetSize(ChunkRenderer::min_size.first, ChunkRenderer::min_size.second);
this->SetSize(ChunkRenderer::min_size.first, cChunkDef::Height * ClientChunk::box_size);
}
void MarkChunkDirty() {
this->current_render_quads.clear();
void MarkChunkDirty() { this->world_slice->meshes_are_dirty = true; }
const auto& GetCurrentQuads() const {
assert(world_slice->cached_meshes);
const ClientChunk::MeshedChunk& meshes = *world_slice->cached_meshes;
return meshes[this->camera_angle][this->depth];
}
bool IsChunkReadyToDraw() {
if (!world_slice->cached_meshes)
return false;
return true;
}
virtual void Draw(ICanvas* the_drawing_machine) override {
this->CBaseWidget::Draw(the_drawing_machine);
if (!this->IsChunkReadyToDraw())
return;
const auto ConvertGLMVecToSTDPairVec = [](const geo::Vec2& vec_in) -> std::pair<int, int> {
return std::pair<int, int>(vec_in.x, vec_in.y);
};
const auto ConvertGLMQuadToSTDPairQuad = [&](const geo::Box<geo::Vec2>& box_in) -> Canvas::TranslationMatrix {
return Canvas::TranslationMatrix(ConvertGLMVecToSTDPairVec(box_in.origin), ConvertGLMVecToSTDPairVec(box_in.GetSize()));
};
for (const ClientChunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads)
for (const ClientChunk::ChunkMeshedQuad& kinda_a_vert : this->GetCurrentQuads())
the_drawing_machine->Rect(ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad), kinda_a_vert.clr);
if (this->settings->wireframe->Value()) {
for (const ClientChunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads) {
for (const ClientChunk::ChunkMeshedQuad& kinda_a_vert : this->GetCurrentQuads()) {
the_drawing_machine->Rect({ ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad) }, glez::color::black, Canvas::RectType::Outline);
the_drawing_machine->Line({ ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad) }, glez::color::black);
}
}
}
virtual void Update() override {
this->CBaseWidget::Update();
if (current_render_quads.empty()) {
if (this->world_slice->meshes_are_dirty || !this->world_slice->cached_meshes) {
if (world_slice->IsEmpty())
return;
current_render_quads = world_slice->MeshSinglePlaneAtDepth(ClientChunk::MesherComplexity(settings->dropdown->Value()), this->camera_angle, 255);
world_slice->cached_meshes = world_slice->MeshEntireChunk(ClientChunk::MesherComplexity(settings->mesh_complexity->Value()));
if (ClientChunk::gfx_mesher_debuglog) {
std::pair<int, int> found_size;
for (const ClientChunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads) {
for (const ClientChunk::ChunkMeshedQuad& kinda_a_vert : this->GetCurrentQuads()) {
const auto max_coord = kinda_a_vert.quad.GetMax();
if (max_coord.x > found_size.first)
found_size.first = max_coord.x;
@ -1335,8 +1430,6 @@ int start_nyqubel_client() {
std::cout << "User Requested Client quit." << std::endl;
});
// Need a 856/480 size window.
hydride_show();
while (1) {
input::RefreshInput();