#include "Fixtures.h" #include "SceneColor.h" #include "zeroerr/assert.h" #include "zeroerr/unittest.h" #include "procgen/heightmap/TerrainSampler.h" #include #include #include #include #include #include #include #include #include "RenderImageAudit.h" #include "graphics/AmbientOcclusion.h" #include "graphics/AntiAliasing.h" #include "graphics/Canvas.h" #include "graphics/ClipSpace.h" #include "graphics/DrawItem2D.h" #include "graphics/Font.h" #include "graphics/GBuffer.h" #include "graphics/GlobalIllumination.h" #include "graphics/Graphics.h" #include "graphics/Grass.h" #include "graphics/Light.h" #include "graphics/Material.h" #include "graphics/Mesh.h" #include "graphics/Outline.h" #include "graphics/Quad.h" #include "graphics/RenderControl.h" #include "graphics/RenderSystem.h" #include "graphics/RenderSystem3D.h" #include "graphics/ScreenSpaceReflection.h" #include "graphics/Shader.h" #include "graphics/Texture.h" #include "graphics/Volumetric.h" #include "graphics/Water.h" #include "graphics/Waterfall.h" #include "image/ImageData.h" #include "voxel/Chunk.h" #include "voxel/CubeTypeRegistry.h" #include "voxel/FaceDir.h" #include "voxel/Voxel.h" #include "voxel/VoxelPack.h" #include "voxel/VoxelWorld.h" #include "window/Window.h" #include #include #include #include #include // Color lives in eve::graphics (see graphics/Canvas.h); keep the unqualified form. using eve::graphics::Color; using namespace eve::graphics; using namespace eve::voxel; using eve::image::ImageData; static float luma(const Color &c) { return (c.r + c.g + c.b) / 3.f; } /** How many samples on a coarse grid differ from the 3D clear color. */ static int countNonBgSamples(Graphics *gfx, const Color &bg, float eps = 0.08f) { const int w = gfx->getWidth(); const int h = gfx->getHeight(); const float bl = luma(bg); int n = 0; for (int y = h / 10; y < h * 9 / 10; y += std::max(1, h / 12)) { for (int x = w / 10; x < w * 9 / 10; x += std::max(1, w / 12)) { if (std::fabs(luma(gfx->getPixel(x, y)) - bl) > eps) ++n; } } return n; } static Texture *makeSolid(Graphics *gfx, uint8_t r, uint8_t g, uint8_t b) { uint8_t px[4] = {r, g, b, 255}; return gfx->newTexture(1, 1, px); } /** Deterministic high-contrast palette for atlas tile `id` (0..127). */ static void atlasTileRgb(int id, uint8_t &r, uint8_t &g, uint8_t &b) { static const uint8_t kPal[][3] = { {220, 40, 40}, {40, 220, 40}, {40, 40, 220}, {220, 220, 40}, {220, 40, 220}, {40, 220, 220}, {240, 240, 240}, {220, 120, 40}, {120, 40, 220}, {40, 160, 120}, {200, 80, 120}, {80, 200, 80}, {80, 80, 200}, {200, 160, 40}, {160, 40, 80}, {40, 200, 200}, }; const int n = int(sizeof(kPal) / sizeof(kPal[0])); const int i = ((id % n) + n) % n; r = kPal[i][0]; g = kPal[i][1]; b = kPal[i][2]; } /** NxM tile atlas (1px per tile). `tilesPerRow` must match draw call. */ static Texture *makeTileAtlas(Graphics *gfx, int tilesPerRow, int rows) { const int w = std::max(1, tilesPerRow); const int h = std::max(1, rows); std::vector px(size_t(w * h * 4)); for (int ty = 0; ty < h; ++ty) { for (int tx = 0; tx < w; ++tx) { const int id = tx + ty * w; uint8_t r, g, b; atlasTileRgb(id, r, g, b); const size_t o = size_t((ty * w + tx) * 4); px[o + 0] = r; px[o + 1] = g; px[o + 2] = b; px[o + 3] = 255; } } return gfx->newTexture(w, h, px.data()); } static void fillCube(VoxelWorld *world, int x0, int y0, int z0, int s, uint8_t tex) { for (int z = 0; z < s; ++z) for (int y = 0; y < s; ++y) for (int x = 0; x < s; ++x) world->setVoxel(x0 + x, y0 + y, z0 + z, tex); } static void tinyHud(Graphics *gfx) { (void)gfx; // Keep 2D present path consistent with other 3D tests. auto *hud = Renderable2D::create(); hud->transform()->x = 0; hud->transform()->y = 0; hud->sprite()->width = 2; hud->sprite()->height = 2; hud->sprite()->r = 0.f; hud->sprite()->g = 0.f; hud->sprite()->b = 0.f; hud->sprite()->a = 0.f; hud->sprite()->visible = true; } static void hideLeftover3D() { if (ecs::current()->getManager() != nullptr) { auto view = ecs::View(); for (auto it = view.begin(); it != view.end(); ++it) { auto [mr] = *it; mr->visible = false; } } if (ecs::current()->getManager() != nullptr) { auto camView = ecs::View(); for (auto it = camView.begin(); it != camView.end(); ++it) { auto [data] = *it; data->active = false; } } } static void renderVoxelFrame(Graphics *gfx, VoxelWorld *world, Texture *atlas, int tilesPerRow, const glm::vec3 &eye, const glm::vec3 &target, float viewRange, bool faceCull) { const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::mat4 view = glm::lookAtRH(eye, target, glm::vec3(0.f, 1.f, 0.f)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 500.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, viewRange, faceCull); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (!gfx->had3DThisFrame()) return; gfx->setMesh3DViewProj(vp); gfx->setMesh3DCameraPos(eye); world->drawVisible(gfx, atlas, tilesPerRow); RenderSystem::render(*gfx); // closes / presents via 2D path } static Color renderVoxelAoSample(Graphics *gfx, Texture *atlas, uint32_t ao) { const uint32_t packed = PackedRect::pack(0, 0, 0, 4, 4, 1).bits; const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.f, 8.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 0.f, 2.f), glm::vec3(0.f, 0.f, -1.f)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(proj * view); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posY", atlas, 1, &ao); } RenderSystem::render(*gfx); return gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); } /** @brief Vertex AO must shade identically on Vulkan and WebGPU voxel pipelines. */ TEST_CASE("voxel.render.vertexAoDarkensPixels") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 300, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeSolid(gfx, 220, 220, 220); const Color bright = renderVoxelAoSample(gfx, atlas, 0xFFu); const Color dark = renderVoxelAoSample(gfx, atlas, 0x00u); REQUIRE(luma(bright) > 0.25f); // AO multiplies linear radiance; ACES and sRGB compress the displayed ratio. REQUIRE(luma(testSceneLinearColor(dark)) < luma(testSceneLinearColor(bright)) * 0.6f); } // NOTE: Graphics is a process-wide singleton — reuse one window for these cases. TEST_CASE("voxel.render.smokeDrawVisibleProducesPixels") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); // Small solid block near origin so a +Z camera sees it. for (int z = 0; z < 4; ++z) for (int y = 0; y < 4; ++y) for (int x = 0; x < 4; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); CHECK(world->getOrCreateChunk(0, 0, 0)->totalRectCount() > 0); Texture *atlas = makeSolid(gfx, 230, 60, 50); REQUIRE(atlas != nullptr); const glm::vec3 eye(2.f, 2.f, 12.f); const glm::vec3 target(2.f, 2.f, 2.f); for (int i = 0; i < 3; ++i) renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); // Background is dark slate; a lit red face should raise luma / red channel. CHECK(mid.r > 0.15f); CHECK(luma(mid) > 0.08f); CHECK(luma(mid) < 0.98f); } TEST_CASE("voxel.render.emptyInstancesNoCrash") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 160, 120); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::mat4 view = glm::lookAtRH(glm::vec3(0, 0, 5), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.1f, 0.1f, 0.12f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); // count == 0 must be a no-op. gfx->drawVoxelFaceInstances(nullptr, 0, 0.f, 0.f, 0.f, "posZ", nullptr, 1); uint32_t dummy = PackedRect::pack(0, 0, 0, 1, 1, 0).bits; // Also tolerate a single instance draw. gfx->drawVoxelFaceInstances(&dummy, 1, 0.f, 0.f, 0.f, "posZ", nullptr, 1); } RenderSystem::render(*gfx); Color c = gfx->getPixel(1, 1); CHECK(c.a >= 0.f); } TEST_CASE("voxel.render.invalidFaceDirThrows") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 160, 120); const glm::mat4 vp(1.f); gfx->begin3DFrame(); if (!gfx->had3DThisFrame()) { RenderSystem::render(*gfx); return; } gfx->setMesh3DViewProj(vp); uint32_t dummy = PackedRect::pack(0, 0, 0, 1, 1, 0).bits; bool threw = false; try { gfx->drawVoxelFaceInstances(&dummy, 1, 0.f, 0.f, 0.f, "sideways", nullptr, 1); } catch (...) { threw = true; } CHECK(threw); // Recover present path. RenderSystem::render(*gfx); } TEST_CASE("voxel.render.atlasTexIndexTintVisible") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); // Texture id 1 (air is 0); solid red 1×1 atlas. world->setVoxel(0, 0, 0, 1); world->remeshDirty(); Texture *red = makeSolid(gfx, 240, 40, 40); const glm::vec3 eye(0.5f, 0.5f, 6.f); const glm::vec3 target(0.5f, 0.5f, 0.5f); for (int i = 0; i < 3; ++i) renderVoxelFrame(gfx, world.get(), red, 1, eye, target, 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.r > mid.g); CHECK(mid.r > mid.b); CHECK(mid.r > 0.12f); } TEST_CASE("voxel.render.faceCullChangesCoverage") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 8; ++z) for (int y = 0; y < 8; ++y) for (int x = 0; x < 8; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 200, 200, 210); // Camera on +X; with face cull, back faces dropped — still should see the +X wall. const glm::vec3 eye(20.f, 4.f, 4.f); const glm::vec3 target(4.f, 4.f, 4.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); CHECK(world->getVisibleBatchCount() >= 1); bool sawPosX = false, sawNegX = false; for (int i = 0; i < world->getVisibleBatchCount(); ++i) { if (world->getVisibleBatch(i).dir == FaceDir::PosX) sawPosX = true; if (world->getVisibleBatch(i).dir == FaceDir::NegX) sawNegX = true; } CHECK(sawPosX); CHECK(!sawNegX); Color withCull = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, false); CHECK(world->getVisibleRectCount() >= 6); Color noCull = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); // Both frames should show geometry (not pure background). CHECK(luma(withCull) > 0.06f); CHECK(luma(noCull) > 0.06f); } TEST_CASE("voxel.render.outOfRangeDrawsNothingBright") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); world->getOrCreateChunk(0, 0, 0)->fill(1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 255, 255, 255); const glm::vec3 eye(16.f, 16.f, 80.f); const glm::vec3 target(16.f, 16.f, 16.f); // Tiny range → selectVisible yields no batches. renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 1.f, true); CHECK_EQ(world->getVisibleBatchCount(), 0); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); // Should be near the dark clear color. CHECK(luma(mid) < 0.2f); } TEST_CASE("voxel.render.multiBatchSameFrame") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); // Two separated pillars → multiple face batches. for (int y = 0; y < 6; ++y) { world->setVoxel(2, y, 2, 1); world->setVoxel(10, y, 10, 1); } world->remeshDirty(); Texture *atlas = makeSolid(gfx, 180, 170, 90); const glm::vec3 eye(6.f, 8.f, 22.f); const glm::vec3 target(6.f, 3.f, 6.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); CHECK(world->getVisibleBatchCount() >= 2); CHECK(world->getVisibleRectCount() >= 2); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.drawVisibleMatchesManualBatches") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 3; ++z) for (int y = 0; y < 3; ++y) for (int x = 0; x < 3; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 90, 140, 220); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(1.5f, 1.5f, 10.f); const glm::vec3 target(1.5f, 1.5f, 1.5f); const glm::mat4 view = glm::lookAtRH(eye, target, glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 200.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 200.f, true); const int batches = world->getVisibleBatchCount(); REQUIRE(batches > 0); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->setMesh3DCameraPos(eye); for (int i = 0; i < batches; ++i) { const DrawBatch &b = world->getVisibleBatch(i); gfx->drawVoxelFaceInstances(b.packed, b.count, b.chunk->originX(), b.chunk->originY(), b.chunk->originZ(), faceDirName(b.dir), atlas, 1); } } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.06f); } TEST_CASE("voxel.render.requiresBegin3DFrame") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 160, 120); uint32_t dummy = PackedRect::pack(0, 0, 0, 1, 1, 1).bits; bool threw = false; try { gfx->drawVoxelFaceInstances(&dummy, 1, 0.f, 0.f, 0.f, "posZ", nullptr, 1); } catch (...) { threw = true; } CHECK(threw); } TEST_CASE("voxel.render.faceDirAliasesDraw") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(0.5f, 0.5f, 6.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; // One +Z face covering the voxel front. uint32_t packed = PackedRect::pack(0, 0, 0, 1, 1, 1).bits; Texture *atlas = makeSolid(gfx, 40, 180, 220); const char *aliases[] = {"posZ", "+z"}; for (const char *name : aliases) { gfx->setBackgroundColor(Color(0.04f, 0.05f, 0.06f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, name, atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } } TEST_CASE("voxel.render.nullAtlasUsesWhite") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 2; ++z) for (int y = 0; y < 2; ++y) for (int x = 0; x < 2; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); const glm::vec3 eye(1.f, 1.f, 8.f); const glm::vec3 target(1.f, 1.f, 1.f); renderVoxelFrame(gfx, world.get(), nullptr, 1, eye, target, 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.largeInstanceCount") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // Many non-merged top faces: sparse pillars. std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 16; ++z) for (int x = 0; x < 16; ++x) if (((x * 3 + z * 5) % 7) == 0) world->setVoxel(x, 0, z, 1); world->remeshDirty(); CHECK(world->getOrCreateChunk(0, 0, 0)->totalRectCount() > 20); Texture *atlas = makeSolid(gfx, 200, 180, 60); const glm::vec3 eye(8.f, 10.f, 28.f); const glm::vec3 target(8.f, 0.f, 8.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); CHECK(world->getVisibleRectCount() > 10); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.farChunkOrigin") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); // Chunk (2,0,0) origin at x=64. for (int z = 0; z < 4; ++z) for (int y = 0; y < 4; ++y) for (int x = 0; x < 4; ++x) world->setVoxel(64 + x, y, z, 1); world->remeshDirty(); CHECK(world->hasChunk(2, 0, 0)); Texture *atlas = makeSolid(gfx, 220, 80, 200); const glm::vec3 eye(66.f, 2.f, 16.f); const glm::vec3 target(66.f, 2.f, 2.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); CHECK(world->getVisibleChunkCount() >= 1); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.depthNearOccludesFar") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // Near red wall at z=2, far green wall at z=0 — camera looks -Z from z=12. std::unique_ptr world(new VoxelWorld()); for (int y = 0; y < 4; ++y) for (int x = 0; x < 4; ++x) { world->setVoxel(x, y, 0, 2); // far (will use green atlas tile... single solid tex) world->setVoxel(x, y, 2, 1); // near } world->remeshDirty(); // Use bright red atlas — both walls same color; verify something draws, then // compare against far-only scene luma stability (near wall should dominate). Texture *red = makeSolid(gfx, 240, 30, 30); const glm::vec3 eye(2.f, 2.f, 12.f); const glm::vec3 target(2.f, 2.f, 2.f); renderVoxelFrame(gfx, world.get(), red, 1, eye, target, 200.f, true); Color withNear = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(withNear.r > 0.12f); // Remove near wall voxels. for (int y = 0; y < 4; ++y) for (int x = 0; x < 4; ++x) world->setVoxel(x, y, 2, 0); world->remeshDirty(); renderVoxelFrame(gfx, world.get(), red, 1, eye, target, 200.f, true); Color farOnly = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); // Far wall still visible (same color); just ensure render path still works. CHECK(farOnly.r > 0.08f); CHECK(luma(withNear) > 0.05f); } TEST_CASE("voxel.render.multiFrameSlotReuse") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); world->setVoxel(0, 0, 0, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 160, 160, 200); const glm::vec3 eye(0.5f, 0.5f, 5.f); const glm::vec3 target(0.5f, 0.5f, 0.5f); for (int frame = 0; frame < 6; ++frame) { renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } } TEST_CASE("voxel.render.allSixFacesFromOrbit") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 256, 192); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 6; ++z) for (int y = 0; y < 6; ++y) for (int x = 0; x < 6; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 210, 200, 190); struct Cam { glm::vec3 eye; glm::vec3 target; FaceDir expect; }; const Cam cams[] = { {{20.f, 3.f, 3.f}, {3.f, 3.f, 3.f}, FaceDir::PosX}, {{-8.f, 3.f, 3.f}, {3.f, 3.f, 3.f}, FaceDir::NegX}, {{3.f, 20.f, 3.f}, {3.f, 3.f, 3.f}, FaceDir::PosY}, {{3.f, -8.f, 3.f}, {3.f, 3.f, 3.f}, FaceDir::NegY}, {{3.f, 3.f, 20.f}, {3.f, 3.f, 3.f}, FaceDir::PosZ}, {{3.f, 3.f, -8.f}, {3.f, 3.f, 3.f}, FaceDir::NegZ}, }; for (const auto &c : cams) { // For top/bottom views, renderVoxelFrame uses +Y up which is fine for ±X/±Z; // for ±Y eye, lookAtRH with up=(0,1,0) degenerates — use custom path. const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); glm::vec3 up(0.f, 1.f, 0.f); if (std::fabs(c.eye.y - c.target.y) > 10.f) up = glm::vec3(0.f, 0.f, 1.f); const glm::mat4 view = glm::lookAtRH(c.eye, c.target, up); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 200.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], c.eye.x, c.eye.y, c.eye.z, 200.f, true); bool saw = false; for (int i = 0; i < world->getVisibleBatchCount(); ++i) if (world->getVisibleBatch(i).dir == c.expect) saw = true; CHECK(saw); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->setMesh3DCameraPos(c.eye); world->drawVisible(gfx, atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } } TEST_CASE("voxel.render.tilesPerRowAtlasSample") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // 2×2 atlas: index 1 (col1,row0) = pure green. uint8_t px[16] = { 200, 40, 40, 255, // 0 red 40, 200, 40, 255, // 1 green 40, 40, 200, 255, // 2 blue 200, 200, 40, 255, // 3 yellow }; Texture *atlas = gfx->newTexture(2, 2, px); std::unique_ptr world(new VoxelWorld()); // tex id 1 → green tile for (int z = 0; z < 3; ++z) for (int y = 0; y < 3; ++y) for (int x = 0; x < 3; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); const glm::vec3 eye(1.5f, 1.5f, 10.f); const glm::vec3 target(1.5f, 1.5f, 1.5f); renderVoxelFrame(gfx, world.get(), atlas, 2, eye, target, 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.g > mid.r); CHECK(mid.g > mid.b); CHECK(mid.g > 0.1f); } TEST_CASE("voxel.render.editRemeshChangesPixels") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); Texture *atlas = makeSolid(gfx, 230, 230, 240); const glm::vec3 eye(4.f, 4.f, 18.f); const glm::vec3 target(4.f, 4.f, 4.f); // Empty world → dark world->remeshDirty(); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); CHECK_EQ(world->getVisibleRectCount(), 0); Color empty = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(empty) < 0.2f); for (int z = 0; z < 8; ++z) for (int y = 0; y < 8; ++y) for (int x = 0; x < 8; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); Color filled = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(filled) > luma(empty) + 0.03f); } TEST_CASE("voxel.render.emptyWorldDrawVisible") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 200, 150); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); Texture *atlas = makeSolid(gfx, 255, 255, 255); const glm::vec3 eye(0.f, 0.f, 5.f); const glm::vec3 target(0.f, 0.f, 0.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 100.f, true); CHECK_EQ(world->getVisibleBatchCount(), 0); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) < 0.25f); } TEST_CASE("voxel.render.negFaceDirAliases") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); // Camera on -Z looking toward origin → see NegZ? Actually looking +Z direction from -Z. // Eye at z=-4 looking at z=0.5 → see -Z face of voxel at origin (plane z=0). const glm::vec3 eye(0.5f, 0.5f, -4.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; uint32_t packed = PackedRect::pack(0, 0, 0, 1, 1, 1).bits; Texture *atlas = makeSolid(gfx, 180, 90, 40); for (const char *name : {"negZ", "-z"}) { gfx->setBackgroundColor(Color(0.04f, 0.05f, 0.06f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, name, atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } } TEST_CASE("voxel.render.manyChunksBatched") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int cx = 0; cx < 3; ++cx) for (int cz = 0; cz < 3; ++cz) { for (int y = 0; y < 2; ++y) for (int x = 0; x < 2; ++x) for (int z = 0; z < 2; ++z) world->setVoxel(cx * 32 + x + 8, y, cz * 32 + z + 8, 1); } world->remeshDirty(); CHECK_EQ(world->getChunkCount(), 9); Texture *atlas = makeSolid(gfx, 120, 160, 200); const glm::vec3 eye(48.f, 20.f, 120.f); const glm::vec3 target(48.f, 1.f, 48.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 300.f, true); CHECK(world->getVisibleChunkCount() >= 3); CHECK(world->getVisibleBatchCount() >= 3); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.singleFaceManualDraw") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 256, 192); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // Large +Z quad covering much of the view. uint32_t packed = PackedRect::pack(0, 0, 0, 8, 8, 1).bits; Texture *atlas = makeSolid(gfx, 50, 200, 80); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(4.f, 4.f, 20.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(4.f, 4.f, 1.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(45.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.05f, 0.07f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posZ", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.g > mid.r); CHECK(mid.g > 0.08f); // Corners of screen may still be background depending on FOV; center must be lit. Color corner = gfx->getPixel(2, 2); CHECK(luma(mid) > luma(corner)); } TEST_CASE("voxel.render.tilesPerRowBlueTile") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint8_t px[16] = { 200, 40, 40, 255, 40, 200, 40, 255, 40, 40, 220, 255, 200, 200, 40, 255, }; Texture *atlas = gfx->newTexture(2, 2, px); std::unique_ptr world(new VoxelWorld()); // tex id 2 → blue (row1,col0) for (int z = 0; z < 4; ++z) for (int y = 0; y < 4; ++y) for (int x = 0; x < 4; ++x) world->setVoxel(x, y, z, 2); world->remeshDirty(); const glm::vec3 eye(2.f, 2.f, 12.f); renderVoxelFrame(gfx, world.get(), atlas, 2, eye, glm::vec3(2.f, 2.f, 2.f), 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.b > mid.r); CHECK(mid.b > mid.g); CHECK(mid.b > 0.1f); } TEST_CASE("voxel.render.instanceCountGrowth") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeSolid(gfx, 200, 200, 210); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(8.f, 8.f, 40.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(8.f, 0.f, 8.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 200.f); const glm::mat4 vp = proj * view; // Grow instance buffer across draws in one frame: 1, then many. std::vector many; many.reserve(64); for (int z = 0; z < 8; ++z) for (int x = 0; x < 8; ++x) many.push_back(PackedRect::pack(x, 0, z, 1, 1, 1).bits); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); uint32_t one = many[0]; gfx->drawVoxelFaceInstances(&one, 1, 0.f, 0.f, 0.f, "posY", atlas, 1); gfx->drawVoxelFaceInstances(many.data(), int(many.size()), 0.f, 0.f, 0.f, "posY", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.posXWallFromSide") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 8; ++z) for (int y = 0; y < 8; ++y) world->setVoxel(0, y, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 230, 120, 50); const glm::vec3 eye(12.f, 4.f, 4.f); const glm::vec3 target(0.f, 4.f, 4.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 100.f, true); bool sawPosX = false; for (int i = 0; i < world->getVisibleBatchCount(); ++i) if (world->getVisibleBatch(i).dir == FaceDir::PosX) sawPosX = true; CHECK(sawPosX); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.r > 0.1f); } TEST_CASE("voxel.render.clearColorWhenCulled") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); world->getOrCreateChunk(0, 0, 0)->fill(1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 255, 255, 255); // Camera behind looking away from chunk. const glm::vec3 eye(16.f, 16.f, 16.f); const glm::vec3 target(16.f, 16.f, 100.f); // look +Z, chunk is around same place but... // Better: put eye far and look opposite. renderVoxelFrame(gfx, world.get(), atlas, 1, glm::vec3(-40.f, 16.f, 16.f), glm::vec3(-80.f, 16.f, 16.f), 200.f, true); // Chunk at origin not in front of camera looking toward -X further. CHECK_EQ(world->getVisibleChunkCount(), 0); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) < 0.22f); } TEST_CASE("voxel.render.posYFromAbove") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 6; ++z) for (int x = 0; x < 6; ++x) world->setVoxel(x, 0, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 200, 180, 60); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(3.f, 18.f, 3.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(3.f, 0.f, 3.f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 100.f, true); bool sawPosY = false; for (int i = 0; i < world->getVisibleBatchCount(); ++i) if (world->getVisibleBatch(i).dir == FaceDir::PosY) sawPosY = true; CHECK(sawPosY); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); world->drawVisible(gfx, atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.twoOriginsSameFrame") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t a = PackedRect::pack(0, 0, 0, 2, 2, 1).bits; uint32_t b = PackedRect::pack(0, 0, 0, 2, 2, 1).bits; Texture *atlas = makeSolid(gfx, 90, 200, 120); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(4.f, 4.f, 20.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(4.f, 1.f, 4.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&a, 1, 0.f, 0.f, 0.f, "posY", atlas, 1); gfx->drawVoxelFaceInstances(&b, 1, 6.f, 0.f, 6.f, "posY", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.faceCullRectCountAboutHalf") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); std::unique_ptr world(new VoxelWorld()); world->getOrCreateChunk(0, 0, 0)->fill(1); world->remeshDirty(); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(40.f, 16.f, 16.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(16.f, 16.f, 16.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 200.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 200.f, false); CHECK_EQ(world->getVisibleRectCount(), 6); world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 200.f, true); CHECK(world->getVisibleRectCount() >= 1); CHECK(world->getVisibleRectCount() <= 5); CHECK(world->getVisibleRectCount() < 6); } TEST_CASE("voxel.render.removeVoxelsDarkens") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 6; ++z) for (int y = 0; y < 6; ++y) for (int x = 0; x < 6; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 240, 240, 250); const glm::vec3 eye(3.f, 3.f, 16.f); const glm::vec3 target(3.f, 3.f, 3.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 100.f, true); Color filled = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(filled) > 0.08f); world->clear(); world->remeshDirty(); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 100.f, true); Color empty = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(empty) < luma(filled)); CHECK(luma(empty) < 0.22f); } TEST_CASE("voxel.render.posXAliasPlusX") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 0, 4, 4, 1).bits; Texture *atlas = makeSolid(gfx, 220, 80, 40); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(10.f, 2.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(1.f, 2.f, 2.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.05f, 0.07f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "+x", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.r > 0.08f); } TEST_CASE("voxel.render.stressHundredsOfInstances") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::vector many; many.reserve(256); for (int z = 0; z < 16; ++z) for (int x = 0; x < 16; ++x) many.push_back(PackedRect::pack(x, 0, z, 1, 1, 1).bits); CHECK_EQ(int(many.size()), 256); Texture *atlas = makeSolid(gfx, 180, 190, 200); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(8.f, 12.f, 30.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(8.f, 0.f, 8.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 200.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(many.data(), int(many.size()), 0.f, 0.f, 0.f, "posY", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.alternatingAtlasFrames") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 4; ++z) for (int y = 0; y < 4; ++y) for (int x = 0; x < 4; ++x) world->setVoxel(x, y, z, 1); world->remeshDirty(); Texture *red = makeSolid(gfx, 230, 40, 40); Texture *blue = makeSolid(gfx, 40, 40, 230); const glm::vec3 eye(2.f, 2.f, 12.f); const glm::vec3 target(2.f, 2.f, 2.f); renderVoxelFrame(gfx, world.get(), red, 1, eye, target, 100.f, true); Color cRed = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(cRed.r > cRed.b); renderVoxelFrame(gfx, world.get(), blue, 1, eye, target, 100.f, true); Color cBlue = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(cBlue.b > cBlue.r); } TEST_CASE("voxel.render.chunkSeamDoubleFaceStillDraws") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); // Voxels on both sides of x=32 seam. for (int y = 0; y < 4; ++y) for (int z = 0; z < 4; ++z) { world->setVoxel(31, y, z, 1); world->setVoxel(32, y, z, 1); } world->remeshDirty(); Texture *atlas = makeSolid(gfx, 200, 160, 100); const glm::vec3 eye(48.f, 2.f, 2.f); const glm::vec3 target(32.f, 2.f, 2.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); CHECK(world->getVisibleChunkCount() >= 1); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.negYFromBelow") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 6; ++z) for (int x = 0; x < 6; ++x) world->setVoxel(x, 4, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 180, 120, 60); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(3.f, -10.f, 3.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(3.f, 4.f, 3.f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 100.f, true); bool sawNegY = false; for (int i = 0; i < world->getVisibleBatchCount(); ++i) if (world->getVisibleBatch(i).dir == FaceDir::NegY) sawNegY = true; CHECK(sawNegY); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); world->drawVisible(gfx, atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.posZWallFromFront") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 0, 6, 6, 1).bits; Texture *atlas = makeSolid(gfx, 80, 200, 220); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(3.f, 3.f, 14.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(3.f, 3.f, 1.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.05f, 0.07f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posZ", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.tilesPerRowZeroClamped") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 0, 4, 4, 1).bits; Texture *atlas = makeSolid(gfx, 210, 90, 50); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.f, 8.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 0.f, 2.f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); // tilesPerRow <= 0 should clamp to 1 and still sample the solid atlas. gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posY", atlas, 0); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.zeroCountAfterLargeDraw") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::vector many; for (int i = 0; i < 64; ++i) many.push_back(PackedRect::pack(i % 8, 0, i / 8, 1, 1, 1).bits); Texture *atlas = makeSolid(gfx, 200, 200, 200); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(4.f, 10.f, 4.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(4.f, 0.f, 4.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(many.data(), int(many.size()), 0.f, 0.f, 0.f, "posY", atlas, 1); gfx->drawVoxelFaceInstances(many.data(), 0, 0.f, 0.f, 0.f, "posY", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.manualAllSixDirsOneFrame") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 0, 2, 2, 1).bits; Texture *atlas = makeSolid(gfx, 220, 220, 40); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); // Orbit-ish view so several faces of a unit cube-ish placement are on screen. const glm::vec3 eye(8.f, 8.f, 8.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(1.f, 1.f, 1.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(55.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; const char *dirs[6] = {"posX", "negX", "posY", "negY", "posZ", "negZ"}; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); for (const char *d : dirs) gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, d, atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.03f); } TEST_CASE("voxel.render.rangeCullDarkensDistant") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 4; ++z) for (int y = 0; y < 4; ++y) for (int x = 0; x < 4; ++x) world->setVoxel(x + 64, y, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 240, 240, 250); const glm::vec3 eye(66.f, 2.f, 20.f); const glm::vec3 target(66.f, 2.f, 2.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 200.f, true); Color nearEnough = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(nearEnough) > 0.06f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 5.f, true); Color culled = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(culled) < luma(nearEnough)); CHECK(luma(culled) < 0.22f); } TEST_CASE("voxel.render.negZAliasMinusZ") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 5, 4, 4, 1).bits; Texture *atlas = makeSolid(gfx, 40, 180, 90); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.f, 2.f, -6.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 2.f, 5.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.05f, 0.07f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "-z", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.fullChunkSixFacesOrbitPixel") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); world->getOrCreateChunk(0, 0, 0)->fill(1); world->remeshDirty(); CHECK_EQ(world->getChunk(0, 0, 0)->totalRectCount(), 6); Texture *atlas = makeSolid(gfx, 160, 170, 180); const glm::vec3 eye(48.f, 40.f, 48.f); const glm::vec3 target(16.f, 16.f, 16.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 300.f, true); CHECK(world->getVisibleBatchCount() >= 2); CHECK(world->getVisibleBatchCount() <= 3); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.atlas4x4_tex1_green") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); std::unique_ptr world(new VoxelWorld()); // Voxel storage 0 = air; solid tex starts at 1 → atlas tile 1 (green). fillCube(world.get(), 0, 0, 0, 4, 1); world->remeshDirty(); const glm::vec3 eye(2.f, 2.f, 14.f); renderVoxelFrame(gfx, world.get(), atlas, 4, eye, glm::vec3(2.f, 2.f, 2.f), 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.g > mid.r); CHECK(mid.g > mid.b); CHECK(mid.g > 0.08f); } TEST_CASE("voxel.render.atlas4x4_eachPaletteChannel") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 280, 200); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); const glm::vec3 eye(2.f, 2.f, 14.f); const glm::vec3 target(2.f, 2.f, 2.f); // tex 1 green, 2 blue, 3 yellow, 4 magenta, 5 cyan struct Expect { uint8_t tex; char channel; // 'r','g','b' dominant or 'y' r&g, 'm' r&b, 'c' g&b }; const Expect cases[] = {{1, 'g'}, {2, 'b'}, {3, 'y'}, {4, 'm'}, {5, 'c'}}; for (const Expect &e : cases) { std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 4, e.tex); world->remeshDirty(); renderVoxelFrame(gfx, world.get(), atlas, 4, eye, target, 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); if (e.channel == 'g') { CHECK(mid.g > mid.r); CHECK(mid.g > mid.b); } else if (e.channel == 'b') { CHECK(mid.b > mid.r); CHECK(mid.b > mid.g); } else if (e.channel == 'y') { CHECK(mid.r > mid.b); CHECK(mid.g > mid.b); } else if (e.channel == 'm') { CHECK(mid.r > mid.g); CHECK(mid.b > mid.g); } else if (e.channel == 'c') { CHECK(mid.g > mid.r); CHECK(mid.b > mid.r); } } } TEST_CASE("voxel.render.atlas8x8_highTexIndex") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // 8×8 = 64 tiles; tex 15 uses palette slot 15 → cyan-ish (40,200,200) Texture *atlas = makeTileAtlas(gfx, 8, 8); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 5, 15); world->remeshDirty(); CHECK_EQ(world->getChunk(0, 0, 0)->faceRects(FaceDir::PosZ)[0].tex(), 15); const glm::vec3 eye(2.5f, 2.5f, 16.f); renderVoxelFrame(gfx, world.get(), atlas, 8, eye, glm::vec3(2.5f, 2.5f, 2.5f), 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.g > mid.r); CHECK(mid.b > mid.r); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.atlas16x8_tex63") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // tilesPerRow=16, 8 rows → indices 0..127 Texture *atlas = makeTileAtlas(gfx, 16, 8); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 4, 63); world->remeshDirty(); REQUIRE(world->getChunk(0, 0, 0)->faceRectCount(FaceDir::PosY) >= 1); CHECK_EQ(world->getChunk(0, 0, 0)->faceRects(FaceDir::PosY)[0].tex(), 63); const glm::vec3 eye(2.f, 10.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 2.f, 2.f), glm::vec3(0, 0, -1)); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 100.f, true); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); world->drawVisible(gfx, atlas, 16); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.swapTexIdChangesAtlasColor") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 4, 1); // green world->remeshDirty(); const glm::vec3 eye(2.f, 2.f, 14.f); const glm::vec3 target(2.f, 2.f, 2.f); renderVoxelFrame(gfx, world.get(), atlas, 4, eye, target, 100.f, true); Color green = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(green.g > green.r); for (int z = 0; z < 4; ++z) for (int y = 0; y < 4; ++y) for (int x = 0; x < 4; ++x) world->setVoxel(x, y, z, 2); // blue world->remeshDirty(); renderVoxelFrame(gfx, world.get(), atlas, 4, eye, target, 100.f, true); Color blue = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(blue.b > blue.g); CHECK(blue.b > green.b); } TEST_CASE("voxel.render.multiTexStripesDoNotMerge") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); std::unique_ptr world(new VoxelWorld()); // Horizontal stripes of alternating tex 1 and 2 on a slab. for (int z = 0; z < 8; ++z) for (int x = 0; x < 8; ++x) world->setVoxel(x, 0, z, (z % 2) ? 1 : 2); world->remeshDirty(); Chunk *c = world->getChunk(0, 0, 0); REQUIRE(c != nullptr); CHECK(c->faceRectCount(FaceDir::PosY) >= 4); const glm::vec3 eye(4.f, 12.f, 4.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(4.f, 0.f, 4.f), glm::vec3(0, 0, -1)); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 100.f, true); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); world->drawVisible(gfx, atlas, 4); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.manualDrawDifferentTexIndices") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); // Two quads side by side: tex1 green @ x=0, tex2 blue @ origin shifted. uint32_t green = PackedRect::pack(0, 0, 0, 3, 3, 1).bits; uint32_t blue = PackedRect::pack(0, 0, 0, 3, 3, 2).bits; const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(1.5f, 8.f, 1.5f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(1.5f, 0.f, 1.5f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&green, 1, 0.f, 0.f, 0.f, "posY", atlas, 4); } RenderSystem::render(*gfx); Color cGreen = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(cGreen.g > cGreen.b); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&blue, 1, 0.f, 0.f, 0.f, "posY", atlas, 4); } RenderSystem::render(*gfx); Color cBlue = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(cBlue.b > cBlue.g); } TEST_CASE("voxel.render.maxTex127_on16WideAtlas") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 16, 8); uint32_t packed = PackedRect::pack(0, 0, 0, 4, 4, 127).bits; const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.f, 10.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 0.f, 2.f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posY", atlas, 16); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.fourTexBlocksSameFrame") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 360, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 3, 1); fillCube(world.get(), 5, 0, 0, 3, 2); fillCube(world.get(), 0, 0, 5, 3, 3); fillCube(world.get(), 5, 0, 5, 3, 4); world->remeshDirty(); // Four different top-face textures → at least 4 PosY rects. CHECK(world->getChunk(0, 0, 0)->faceRectCount(FaceDir::PosY) >= 4); const glm::vec3 eye(4.f, 16.f, 4.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(4.f, 0.f, 4.f), glm::vec3(0, 0, -1)); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(55.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 100.f, true); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); world->drawVisible(gfx, atlas, 4); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.03f); } TEST_CASE("voxel.render.packedTex0_samplesFirstAtlasTile") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // Packed tex 0 is a valid atlas index even though voxel air is also 0. Texture *atlas = makeTileAtlas(gfx, 4, 4); uint32_t packed = PackedRect::pack(0, 0, 0, 4, 4, 0).bits; // tile 0 = red const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.f, 10.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 0.f, 2.f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posY", atlas, 4); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.r > mid.g); CHECK(mid.r > mid.b); CHECK(mid.r > 0.08f); } TEST_CASE("voxel.render.atlasYellowThenMagentaSwap") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 300, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 5, 3); // yellow world->remeshDirty(); const glm::vec3 eye(2.5f, 2.5f, 16.f); const glm::vec3 target(2.5f, 2.5f, 2.5f); renderVoxelFrame(gfx, world.get(), atlas, 4, eye, target, 100.f, true); Color yellow = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(yellow.r > yellow.b); CHECK(yellow.g > yellow.b); for (int z = 0; z < 5; ++z) for (int y = 0; y < 5; ++y) for (int x = 0; x < 5; ++x) world->setVoxel(x, y, z, 4); // magenta world->remeshDirty(); renderVoxelFrame(gfx, world.get(), atlas, 4, eye, target, 100.f, true); Color magenta = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(magenta.r > magenta.g); CHECK(magenta.b > magenta.g); } TEST_CASE("voxel.render.plusY_alias") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 0, 4, 4, 1).bits; Texture *atlas = makeSolid(gfx, 50, 210, 90); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.f, 12.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 0.f, 2.f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "+y", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.g > 0.08f); } TEST_CASE("voxel.render.plusZ_alias") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 0, 4, 4, 1).bits; Texture *atlas = makeSolid(gfx, 40, 100, 230); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.f, 2.f, 12.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 2.f, 1.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.05f, 0.07f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "+z", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.b > 0.08f); } TEST_CASE("voxel.render.wide32x1_rect") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 200); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 0, 32, 1, 1).bits; Texture *atlas = makeSolid(gfx, 230, 180, 40); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(16.f, 10.f, 16.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(16.f, 0.f, 0.5f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posY", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.tall1x32_side") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 280, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 0, 0, 1, 32, 1).bits; // ±X: w along Z, h along Y Texture *atlas = makeSolid(gfx, 200, 60, 200); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(20.f, 16.f, 0.5f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(1.f, 16.f, 0.5f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.05f, 0.07f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posX", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.negXWallFromLeft") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 300, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 6; ++z) for (int y = 0; y < 6; ++y) world->setVoxel(4, y, z, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 230, 100, 50); const glm::vec3 eye(-6.f, 3.f, 3.f); const glm::vec3 target(4.f, 3.f, 3.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 100.f, true); bool sawNegX = false; for (int i = 0; i < world->getVisibleBatchCount(); ++i) if (world->getVisibleBatch(i).dir == FaceDir::NegX) sawNegX = true; CHECK(sawNegX); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.frustumCulledChunkDark") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 280, 200); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); // Chunk far to the side — outside typical forward frustum when looking +Z. fillCube(world.get(), 200, 0, 200, 4, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 255, 255, 255); const glm::vec3 eye(0.f, 2.f, 0.f); const glm::vec3 target(0.f, 2.f, 10.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 500.f, true); CHECK_EQ(world->getVisibleChunkCount(), 0); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) < 0.22f); } TEST_CASE("voxel.render.carveHoleDarkensCenter") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 8, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 230, 230, 240); const glm::vec3 eye(4.f, 4.f, 20.f); const glm::vec3 target(4.f, 4.f, 4.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 100.f, true); Color solid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(solid) > 0.08f); // Carve a tunnel through the center along Z so the mid pixel looks through. for (int z = 0; z < 8; ++z) for (int y = 3; y <= 4; ++y) for (int x = 3; x <= 4; ++x) world->setVoxel(x, y, z, 0); world->remeshDirty(); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, target, 100.f, true); Color carved = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(carved) < luma(solid)); } TEST_CASE("voxel.render.twoChunksDifferentAtlasTiles") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 4, 1); // green fillCube(world.get(), 32, 0, 0, 4, 2); // blue, next chunk world->remeshDirty(); CHECK_EQ(world->getChunkCount(), 2); // Look at green chunk. renderVoxelFrame(gfx, world.get(), atlas, 4, glm::vec3(2.f, 2.f, 14.f), glm::vec3(2.f, 2.f, 2.f), 100.f, true); Color green = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(green.g > green.b); // Look at blue chunk. renderVoxelFrame(gfx, world.get(), atlas, 4, glm::vec3(34.f, 2.f, 14.f), glm::vec3(34.f, 2.f, 2.f), 100.f, true); Color blue = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(blue.b > blue.g); } TEST_CASE("voxel.render.atlas32wide_tex31") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 300, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 32, 2); // 64 tiles uint32_t packed = PackedRect::pack(0, 0, 0, 3, 3, 31).bits; const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(1.5f, 8.f, 1.5f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(1.5f, 0.f, 1.5f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posY", atlas, 32); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.minusX_alias") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(5, 0, 0, 4, 4, 1).bits; Texture *atlas = makeSolid(gfx, 220, 70, 40); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(-4.f, 2.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(5.f, 2.f, 2.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.05f, 0.07f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "-x", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.r > 0.08f); } TEST_CASE("voxel.render.nearPlaneOccludesFarDifferentColor") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *red = makeSolid(gfx, 240, 30, 30); Texture *green = makeSolid(gfx, 30, 240, 30); uint32_t nearR = PackedRect::pack(0, 0, 0, 6, 6, 1).bits; uint32_t farG = PackedRect::pack(0, 0, 0, 6, 6, 1).bits; const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(3.f, 3.f, 20.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(3.f, 3.f, 0.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); // Far green at z≈1, near red at z≈8 — both posZ planes. gfx->drawVoxelFaceInstances(&farG, 1, 0.f, 0.f, 0.f, "posZ", green, 1); gfx->drawVoxelFaceInstances(&nearR, 1, 0.f, 0.f, 8.f, "posZ", red, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.r > mid.g); CHECK(mid.r > 0.08f); } TEST_CASE("voxel.render.minusY_alias") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 240, 180); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); uint32_t packed = PackedRect::pack(0, 4, 0, 4, 4, 1).bits; Texture *atlas = makeSolid(gfx, 180, 90, 40); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.f, -6.f, 2.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.f, 4.f, 2.f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "-y", atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.nullPackedPointerNoCrash") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 200, 150); tinyHud(gfx); Texture *atlas = makeSolid(gfx, 255, 255, 255); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::mat4 vp = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f) * glm::lookAtRH(glm::vec3(0, 0, 5), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(nullptr, 10, 0.f, 0.f, 0.f, "posY", atlas, 1); gfx->drawVoxelFaceInstances(nullptr, 0, 0.f, 0.f, 0.f, "posY", atlas, 1); } RenderSystem::render(*gfx); } TEST_CASE("voxel.render.drawVisibleNullAtlasWhite") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 280, 200); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 4, 1); world->remeshDirty(); const glm::vec3 eye(2.f, 2.f, 12.f); renderVoxelFrame(gfx, world.get(), nullptr, 1, eye, glm::vec3(2.f, 2.f, 2.f), 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.multiFrameAtlasSwapStable") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 300, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlasA = makeTileAtlas(gfx, 4, 4); Texture *atlasB = makeSolid(gfx, 40, 40, 230); std::unique_ptr world(new VoxelWorld()); fillCube(world.get(), 0, 0, 0, 4, 1); world->remeshDirty(); const glm::vec3 eye(2.f, 2.f, 14.f); const glm::vec3 target(2.f, 2.f, 2.f); for (int i = 0; i < 4; ++i) { renderVoxelFrame(gfx, world.get(), (i % 2) ? atlasB : atlasA, (i % 2) ? 1 : 4, eye, target, 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } } TEST_CASE("voxel.render.sparsePillarsMultiTex") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *atlas = makeTileAtlas(gfx, 4, 4); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 12; z += 3) for (int x = 0; x < 12; x += 3) for (int y = 0; y < 3; ++y) world->setVoxel(x, y, z, uint8_t(1 + ((x + z) % 4))); world->remeshDirty(); CHECK(world->getChunk(0, 0, 0)->totalRectCount() > 10); const glm::vec3 eye(6.f, 8.f, 22.f); renderVoxelFrame(gfx, world.get(), atlas, 4, eye, glm::vec3(6.f, 1.f, 6.f), 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.03f); } TEST_CASE("voxel.render.fullLayerTopFromAbove") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); for (int z = 0; z < 32; ++z) for (int x = 0; x < 32; ++x) world->setVoxel(x, 0, z, 1); world->remeshDirty(); CHECK_EQ(world->getChunk(0, 0, 0)->faceRectCount(FaceDir::PosY), 1); Texture *atlas = makeSolid(gfx, 200, 160, 80); const glm::vec3 eye(16.f, 40.f, 16.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(16.f, 0.f, 16.f), glm::vec3(0, 0, -1)); const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 200.f); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, 200.f, true); gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); world->drawVisible(gfx, atlas, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); } TEST_CASE("voxel.render.twoFacesStackedDepth") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 300, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); Texture *nearTex = makeSolid(gfx, 40, 200, 60); Texture *farTex = makeSolid(gfx, 200, 40, 40); uint32_t a = PackedRect::pack(0, 0, 0, 5, 5, 1).bits; uint32_t b = PackedRect::pack(0, 0, 0, 5, 5, 1).bits; const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(2.5f, 2.5f, 18.f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(2.5f, 2.5f, 0.f), glm::vec3(0, 1, 0)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&b, 1, 0.f, 0.f, 0.f, "posZ", farTex, 1); gfx->drawVoxelFaceInstances(&a, 1, 0.f, 0.f, 6.f, "posZ", nearTex, 1); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(mid.g > mid.r); } TEST_CASE("voxel.render.chunkOriginNegativeDraw") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 300, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); // Local (0,0,0) in chunk (-1,0,0) → world x in [-32,-1] world->setVoxel(-16, 0, 0, 1); world->setVoxel(-15, 0, 0, 1); world->setVoxel(-16, 1, 0, 1); world->setVoxel(-15, 1, 0, 1); world->remeshDirty(); Texture *atlas = makeSolid(gfx, 220, 180, 60); const glm::vec3 eye(-15.5f, 1.f, 12.f); renderVoxelFrame(gfx, world.get(), atlas, 1, eye, glm::vec3(-15.5f, 1.f, 0.f), 100.f, true); CHECK(world->getVisibleChunkCount() >= 1); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); } TEST_CASE("voxel.render.tilesPerRowMismatchStillDraws") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 280, 200); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // 4×4 atlas but claim tilesPerRow=8 — UVs still sample something opaque. Texture *atlas = makeTileAtlas(gfx, 4, 4); uint32_t packed = PackedRect::pack(0, 0, 0, 3, 3, 1).bits; const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::vec3 eye(1.5f, 8.f, 1.5f); const glm::mat4 view = glm::lookAtRH(eye, glm::vec3(1.5f, 0.f, 1.5f), glm::vec3(0, 0, -1)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(50.f), aspect, 0.1f, 100.f); const glm::mat4 vp = proj * view; gfx->setBackgroundColor(Color(0.05f, 0.06f, 0.08f, 1.f)); gfx->begin3DFrame(); if (gfx->had3DThisFrame()) { gfx->setMesh3DViewProj(vp); gfx->drawVoxelFaceInstances(&packed, 1, 0.f, 0.f, 0.f, "posY", atlas, 8); } RenderSystem::render(*gfx); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.03f); } TEST_CASE("voxel.render.remeshIncreasesThenDecreasesRects") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 300, 220); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); world->setVoxel(2, 2, 2, 1); world->remeshDirty(); const int one = world->getChunk(0, 0, 0)->totalRectCount(); CHECK_EQ(one, 6); fillCube(world.get(), 0, 0, 0, 5, 1); world->remeshDirty(); const int many = world->getChunk(0, 0, 0)->totalRectCount(); CHECK(many < one * 5); // greedy merges CHECK(many >= 6); Texture *atlas = makeSolid(gfx, 200, 200, 210); renderVoxelFrame(gfx, world.get(), atlas, 1, glm::vec3(2.5f, 2.5f, 14.f), glm::vec3(2.5f, 2.5f, 2.5f), 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.05f); world->clear(); world->setVoxel(2, 2, 2, 1); world->remeshDirty(); CHECK_EQ(world->getChunk(0, 0, 0)->totalRectCount(), 6); } // --------------------------------------------------------------------------- // Large visual scenes (PNG under test/out/voxel_scenes/) // --------------------------------------------------------------------------- static uint32_t voxelHash(uint32_t x) { x ^= x >> 16; x *= 0x7feb352du; x ^= x >> 15; x *= 0x846ca68bu; x ^= x >> 16; return x; } static float voxelH21(int x, int z) { return float(voxelHash(uint32_t(x) * 0x9E3779B9u ^ uint32_t(z) * 0x85EBCA6Bu) & 0xFFFFu) / 65535.f; } static float voxelValueNoise(float x, float z) { const int x0 = int(std::floor(x)); const int z0 = int(std::floor(z)); const float tx = x - float(x0); const float tz = z - float(z0); const float sx = tx * tx * (3.f - 2.f * tx); const float sz = tz * tz * (3.f - 2.f * tz); const float a = voxelH21(x0, z0); const float b = voxelH21(x0 + 1, z0); const float c = voxelH21(x0, z0 + 1); const float d = voxelH21(x0 + 1, z0 + 1); return (a * (1.f - sx) + b * sx) * (1.f - sz) + (c * (1.f - sx) + d * sx) * sz; } static float voxelFbm(float x, float z) { float sum = 0.f, amp = 1.f, freq = 1.f, norm = 0.f; for (int i = 0; i < 4; ++i) { sum += amp * voxelValueNoise(x * freq, z * freq); norm += amp; amp *= 0.5f; freq *= 2.03f; } return sum / std::max(norm, 1e-4f); } static Texture *makeEarthAtlas(Graphics *gfx) { static const uint8_t pal[16][3] = { {20, 22, 28}, {86, 158, 62}, {145, 96, 55}, {128, 128, 132}, {210, 186, 120}, {48, 96, 168}, {118, 78, 42}, {46, 118, 48}, {236, 240, 245}, {96, 96, 100}, {168, 52, 42}, {210, 196, 168}, {72, 48, 32}, {148, 72, 56}, {212, 168, 64}, {64, 68, 76}, }; std::vector px(16u * 4u); for (int i = 0; i < 16; ++i) { px[size_t(i) * 4u + 0] = pal[i][0]; px[size_t(i) * 4u + 1] = pal[i][1]; px[size_t(i) * 4u + 2] = pal[i][2]; px[size_t(i) * 4u + 3] = 255; } return gfx->newTexture(4, 4, px.data()); } static void fillBox(VoxelWorld *w, int x0, int y0, int z0, int x1, int y1, int z1, uint8_t tex) { for (int z = z0; z < z1; ++z) for (int y = y0; y < y1; ++y) for (int x = x0; x < x1; ++x) w->setVoxel(x, y, z, tex); } static void fillHollowBox(VoxelWorld *w, int x0, int y0, int z0, int x1, int y1, int z1, uint8_t tex) { fillBox(w, x0, y0, z0, x1, y1, z1, tex); if (x1 - x0 > 2 && y1 - y0 > 1 && z1 - z0 > 2) fillBox(w, x0 + 1, y0, z0 + 1, x1 - 1, y1, z1 - 1, 0); } static void placeTree(VoxelWorld *w, int x, int groundY, int z) { const int h = 3 + int(voxelH21(x, z) * 3.f); for (int y = 1; y <= h; ++y) w->setVoxel(x, groundY + y, z, 6); const int cy = groundY + h; for (int dz = -2; dz <= 2; ++dz) { for (int dx = -2; dx <= 2; ++dx) { for (int dy = 0; dy <= 2; ++dy) { if (dx * dx + dy * dy + dz * dz > 6) continue; w->setVoxel(x + dx, cy + dy, z + dz, 7); } } } } static void placeHouse(VoxelWorld *w, int x, int z, int wdt, int dpt, int hgt, uint8_t wall, uint8_t roof) { fillHollowBox(w, x, 1, z, x + wdt, 1 + hgt, z + dpt, wall); fillBox(w, x, 1 + hgt, z, x + wdt, 1 + hgt + 1, z + dpt, roof); const int ridgeZ = z + dpt / 2; for (int i = 0; i < wdt; ++i) w->setVoxel(x + i, 2 + hgt, ridgeZ, roof); } static std::string voxelSceneOutDir() { return std::string(EVENGINE_TEST_BINARY_DIR) + "/out/voxel_scenes"; } static void renderVoxelScene(Graphics *gfx, VoxelWorld *world, Texture *atlas, const glm::vec3 &eye, const glm::vec3 &target, float farZ, float viewRange) { const float aspect = float(std::max(1, gfx->getPixelWidth())) / float(std::max(1, gfx->getPixelHeight())); const glm::mat4 view = glm::lookAtRH(eye, target, glm::vec3(0.f, 1.f, 0.f)); const glm::mat4 proj = perspectiveVulkanRH_ZO(glm::radians(48.f), aspect, 0.2f, farZ); const glm::mat4 vp = proj * view; world->selectVisible(&vp[0][0], eye.x, eye.y, eye.z, viewRange, true); const Color sky(0.42f, 0.58f, 0.78f, 1.f); gfx->setBackgroundColor(sky); gfx->clear(sky, std::nullopt, std::nullopt); gfx->begin3DFrame(); if (!gfx->had3DThisFrame()) return; gfx->setMesh3DViewProj(vp); gfx->setMesh3DCameraPos(eye); world->drawVisible(gfx, atlas, 4); RenderSystem::render(*gfx); } static void saveVoxelScenePng(Graphics *gfx, const std::string &name) { std::unique_ptr img(gfx->newImageData()); REQUIRE(img.get() != nullptr); const std::string dir = voxelSceneOutDir(); std::error_code ec; std::filesystem::create_directories(dir, ec); const std::string path = dir + "/" + name + ".png"; REQUIRE(saveImagePng(*img, path)); std::printf("voxel scene: %s\n", path.c_str()); } TEST_CASE("voxel.render.sceneHillsIsland") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 960, 540); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); constexpr int kN = 160; constexpr int kSea = 6; for (int z = 0; z < kN; ++z) { for (int x = 0; x < kN; ++x) { const float n = voxelFbm(float(x) * 0.035f, float(z) * 0.035f); const float ridge = std::fabs(voxelFbm(float(x) * 0.018f + 40.f, float(z) * 0.018f) * 2.f - 1.f); const float h01 = n * 0.62f + (1.f - ridge) * 0.38f; const float cx = (float(x) - 80.f) / 88.f; const float cz = (float(z) - 80.f) / 88.f; float island = std::clamp(1.12f - std::sqrt(cx * cx + cz * cz), 0.f, 1.f); island *= island; int h = int(2.f + h01 * 20.f * island); if (h < 1) h = 1; if (h > 24) h = 24; for (int y = 0; y <= h; ++y) { uint8_t tex = 3; if (y == h) { if (h <= kSea + 1) tex = 4; else if (h >= 18) tex = 8; else tex = 1; } else if (y >= h - 2) { tex = 2; } world->setVoxel(x, y, z, tex); } if (h < kSea) { for (int y = h + 1; y <= kSea; ++y) world->setVoxel(x, y, z, 5); } } } for (int z = 8; z < kN - 8; z += 11) { for (int x = 8; x < kN - 8; x += 13) { if (voxelH21(x * 3, z * 5) < 0.78f) continue; uint8_t top = 0; int gy = 24; while (gy > 0 && (top = world->getVoxel(x, gy, z)) == 0) --gy; if (top != 1) continue; placeTree(world.get(), x, gy, z); } } const int remeshed = world->remeshDirty(); CHECK(remeshed >= 20); CHECK(world->getChunkCount() >= 20); Texture *atlas = makeEarthAtlas(gfx); REQUIRE(atlas != nullptr); const glm::vec3 eye(78.f, 52.f, 168.f); const glm::vec3 target(80.f, 8.f, 78.f); for (int i = 0; i < 2; ++i) renderVoxelScene(gfx, world.get(), atlas, eye, target, 420.f, 360.f); CHECK(world->getVisibleRectCount() > 80); const Color sky(0.42f, 0.58f, 0.78f, 1.f); CHECK(countNonBgSamples(gfx, sky) >= 20); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.06f); saveVoxelScenePng(gfx, "hills_island"); } TEST_CASE("voxel.render.sceneWalledTown") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 960, 540); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); std::unique_ptr world(new VoxelWorld()); constexpr int kN = 96; fillBox(world.get(), 0, 0, 0, kN, 1, kN, 2); fillBox(world.get(), 2, 1, 2, kN - 2, 2, kN - 2, 9); const int wallH = 8; for (int i = 4; i < kN - 4; ++i) { for (int y = 1; y <= wallH; ++y) { world->setVoxel(i, y, 4, 3); world->setVoxel(i, y, kN - 5, 3); world->setVoxel(4, y, i, 3); world->setVoxel(kN - 5, y, i, 3); } if ((i % 2) == 0) { world->setVoxel(i, wallH + 1, 4, 3); world->setVoxel(i, wallH + 1, kN - 5, 3); world->setVoxel(4, wallH + 1, i, 3); world->setVoxel(kN - 5, wallH + 1, i, 3); } } // Gate on +Z wall fillBox(world.get(), 44, 1, 4, 52, wallH + 2, 6, 0); fillBox(world.get(), 43, 1, 3, 44, wallH + 2, 7, 12); fillBox(world.get(), 52, 1, 3, 53, wallH + 2, 7, 12); auto tower = [&](int x, int z) { fillHollowBox(world.get(), x, 1, z, x + 7, 14, z + 7, 3); fillBox(world.get(), x, 14, z, x + 7, 15, z + 7, 10); fillBox(world.get(), x + 1, 15, z + 1, x + 6, 16, z + 6, 10); fillBox(world.get(), x + 2, 16, z + 2, x + 5, 17, z + 5, 10); world->setVoxel(x + 3, 17, z + 3, 14); }; tower(2, 2); tower(kN - 9, 2); tower(2, kN - 9); tower(kN - 9, kN - 9); fillHollowBox(world.get(), 36, 1, 36, 60, 16, 60, 13); fillBox(world.get(), 36, 16, 36, 60, 17, 60, 10); fillBox(world.get(), 38, 17, 38, 58, 18, 58, 10); fillBox(world.get(), 46, 18, 46, 50, 22, 50, 12); world->setVoxel(48, 22, 48, 14); fillBox(world.get(), 46, 1, 36, 50, 8, 37, 0); for (int row = 0; row < 3; ++row) { for (int col = 0; col < 4; ++col) { const int x = 10 + col * 8; const int z = 12 + row * 8; if (x + 6 >= 34) continue; placeHouse(world.get(), x, z, 6, 6, 4 + ((col + row) % 3), 11, 10); } } for (int row = 0; row < 3; ++row) { for (int col = 0; col < 3; ++col) { placeHouse(world.get(), 64 + col * 8, 12 + row * 9, 7, 7, 3 + (row % 2), 11, 10); } } fillBox(world.get(), 12, 1, 70, 30, 2, 88, 4); for (int i = 0; i < 5; ++i) placeTree(world.get(), 16 + i * 3, 1, 74 + (i % 2) * 4); const int remeshed = world->remeshDirty(); CHECK(remeshed >= 4); CHECK(world->getChunkCount() >= 4); Texture *atlas = makeEarthAtlas(gfx); REQUIRE(atlas != nullptr); const glm::vec3 eye(118.f, 68.f, 118.f); const glm::vec3 target(48.f, 6.f, 48.f); for (int i = 0; i < 2; ++i) renderVoxelScene(gfx, world.get(), atlas, eye, target, 320.f, 280.f); CHECK(world->getVisibleRectCount() > 40); const Color sky(0.42f, 0.58f, 0.78f, 1.f); CHECK(countNonBgSamples(gfx, sky) >= 20); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.06f); saveVoxelScenePng(gfx, "walled_town"); } TEST_CASE("voxel.render.registryMultiFaceTex") { hideLeftover3D(); eve::window::Window *win = nullptr; Graphics *gfx = nullptr; openGfxWindow(win, gfx, 320, 240); tinyHud(gfx); gfx->setScreenReadbackEnabled(true); // grass:顶面绿色 tile(1),其余面红色 tile(0);方向性炉子:前脸蓝色 tile(2) CubeTypeRegistry reg; CubeType grass; grass.name = "grass"; for (int i = 0; i < 6; ++i) grass.faceTex[i] = 0; grass.faceTex[2] = 1; // 顶 = green reg.add(grass); CubeType furnace; furnace.name = "furnace"; for (int i = 0; i < 6; ++i) furnace.faceTex[i] = 0; furnace.faceTex[0] = 2; // +X = blue furnace.directional = true; reg.add(furnace); VoxelWorld world(reg); for (int z = 0; z < 3; ++z) for (int y = 0; y < 3; ++y) for (int x = 0; x < 3; ++x) world.setVoxelByName(x, y, z, "grass"); world.remeshDirty(); Chunk *c = world.getChunk(0, 0, 0); REQUIRE(c != nullptr); CHECK_EQ(c->faceRects(FaceDir::PosY)[0].tex(), 1); // 顶面解析为 green CHECK_EQ(c->faceRects(FaceDir::PosX)[0].tex(), 0); // 侧面 red // 4×4 图集:tile0=red, tile1=green, tile2=blue Texture *atlas = makeTileAtlas(gfx, 4, 4); const glm::vec3 eye(1.5f, 1.5f, 10.f); const glm::vec3 target(1.5f, 1.5f, 1.5f); renderVoxelFrame(gfx, &world, atlas, 4, eye, target, 100.f, true); Color mid = gfx->getPixel(gfx->getWidth() / 2, gfx->getHeight() / 2); CHECK(luma(mid) > 0.04f); }