/* Minetest Copyright (C) 2023 Vitaliy Lobachevskiy This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "test.h" #include #include #include "gamedef.h" #include "dummygamedef.h" #include "client/content_mapblock.h" #include "client/mapblock_mesh.h" #include "client/meshgen/collector.h" #include "mesh_compare.h" #include "util/directiontables.h" namespace { class MockGameDef : public DummyGameDef { public: IWritableItemDefManager *item_mgr() noexcept { return static_cast(m_itemdef); } NodeDefManager *node_mgr() noexcept { return const_cast(m_nodedef); } content_t registerNode(ItemDefinition itemdef, ContentFeatures nodedef) { item_mgr()->registerItem(itemdef); return node_mgr()->set(nodedef.name, nodedef); } void finalize() { node_mgr()->resolveCrossrefs(); } MeshMakeData makeSingleNodeMMD(bool smooth_lighting = true, bool for_shaders = true) { MeshMakeData data{ndef(), 1, for_shaders}; data.setSmoothLighting(smooth_lighting); data.m_blockpos = {0, 0, 0}; for (s16 x = -1; x <= 1; x++) for (s16 y = -1; y <= 1; y++) for (s16 z = -1; z <= 1; z++) data.m_vmanip.setNode({x, y, z}, {CONTENT_AIR, 0, 0}); return data; } content_t addSimpleNode(std::string name, u32 texture) { ItemDefinition itemdef; itemdef.type = ITEM_NODE; itemdef.name = "test:" + name; itemdef.description = name; ContentFeatures f; f.name = itemdef.name; f.drawtype = NDT_NORMAL; f.solidness = 2; f.alpha = ALPHAMODE_OPAQUE; for (TileDef &tiledef : f.tiledef) tiledef.name = name + ".png"; for (TileSpec &tile : f.tiles) tile.layers[0].texture_id = texture; return registerNode(itemdef, f); } content_t addLiquidSource(std::string name, u32 texture) { ItemDefinition itemdef; itemdef.type = ITEM_NODE; itemdef.name = "test:" + name + "_source"; itemdef.description = name; ContentFeatures f; f.name = itemdef.name; f.drawtype = NDT_LIQUID; f.solidness = 1; f.alpha = ALPHAMODE_BLEND; f.light_propagates = true; f.param_type = CPT_LIGHT; f.liquid_type = LIQUID_SOURCE; f.liquid_viscosity = 4; f.groups["liquids"] = 3; f.liquid_alternative_source = "test:" + name + "_source"; f.liquid_alternative_flowing = "test:" + name + "_flowing"; for (TileDef &tiledef : f.tiledef) tiledef.name = name + ".png"; for (TileSpec &tile : f.tiles) tile.layers[0].texture_id = texture; return registerNode(itemdef, f); } content_t addLiquidFlowing(std::string name, u32 texture_top, u32 texture_side) { ItemDefinition itemdef; itemdef.type = ITEM_NODE; itemdef.name = "test:" + name + "_flowing"; itemdef.description = name; ContentFeatures f; f.name = itemdef.name; f.drawtype = NDT_FLOWINGLIQUID; f.solidness = 0; f.alpha = ALPHAMODE_BLEND; f.light_propagates = true; f.param_type = CPT_LIGHT; f.liquid_type = LIQUID_FLOWING; f.liquid_viscosity = 4; f.groups["liquids"] = 3; f.liquid_alternative_source = "test:" + name + "_source"; f.liquid_alternative_flowing = "test:" + name + "_flowing"; f.tiledef_special[0].name = name + "_top.png"; f.tiledef_special[1].name = name + "_side.png"; f.special_tiles[0].layers[0].texture_id = texture_top; f.special_tiles[1].layers[0].texture_id = texture_side; return registerNode(itemdef, f); } }; void set_light_decode_table() { u8 table[LIGHT_SUN + 1] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, }; memcpy(const_cast(light_decode_table), table, sizeof(table)); } class TestMapblockMeshGenerator : public TestBase { public: TestMapblockMeshGenerator() { TestManager::registerTestModule(this); } const char *getName() override { return "TestMapblockMeshGenerator"; } void runTests(IGameDef *gamedef) override; void testSimpleNode(); void testSurroundedNode(); void testInterliquidSame(); void testInterliquidDifferent(); }; static TestMapblockMeshGenerator g_test_instance; void TestMapblockMeshGenerator::runTests(IGameDef *gamedef) { set_light_decode_table(); TEST(testSimpleNode); TEST(testSurroundedNode); TEST(testInterliquidSame); TEST(testInterliquidDifferent); } namespace quad { constexpr float h = BS / 2.0f; const Quad zp{{{{-h, -h, h}, {0, 0, 1}, 0, {1, 1}}, {{h, -h, h}, {0, 0, 1}, 0, {0, 1}}, {{h, h, h}, {0, 0, 1}, 0, {0, 0}}, {{-h, h, h}, {0, 0, 1}, 0, {1, 0}}}}; const Quad yp{{{{-h, h, -h}, {0, 1, 0}, 0, {0, 1}}, {{-h, h, h}, {0, 1, 0}, 0, {0, 0}}, {{h, h, h}, {0, 1, 0}, 0, {1, 0}}, {{h, h, -h}, {0, 1, 0}, 0, {1, 1}}}}; const Quad xp{{{{h, -h, -h}, {1, 0, 0}, 0, {0, 1}}, {{h, h, -h}, {1, 0, 0}, 0, {0, 0}}, {{h, h, h}, {1, 0, 0}, 0, {1, 0}}, {{h, -h, h}, {1, 0, 0}, 0, {1, 1}}}}; const Quad zn{{{{-h, -h, -h}, {0, 0, -1}, 0, {0, 1}}, {{-h, h, -h}, {0, 0, -1}, 0, {0, 0}}, {{h, h, -h}, {0, 0, -1}, 0, {1, 0}}, {{h, -h, -h}, {0, 0, -1}, 0, {1, 1}}}}; const Quad yn{{{{-h, -h, -h}, {0, -1, 0}, 0, {0, 0}}, {{h, -h, -h}, {0, -1, 0}, 0, {1, 0}}, {{h, -h, h}, {0, -1, 0}, 0, {1, 1}}, {{-h, -h, h}, {0, -1, 0}, 0, {0, 1}}}}; const Quad xn{{{{-h, -h, -h}, {-1, 0, 0}, 0, {1, 1}}, {{-h, -h, h}, {-1, 0, 0}, 0, {0, 1}}, {{-h, h, h}, {-1, 0, 0}, 0, {0, 0}}, {{-h, h, -h}, {-1, 0, 0}, 0, {1, 0}}}}; } void TestMapblockMeshGenerator::testSimpleNode() { MockGameDef gamedef; content_t stone = gamedef.addSimpleNode("stone", 42); gamedef.finalize(); MeshMakeData data = gamedef.makeSingleNodeMMD(); data.m_vmanip.setNode({0, 0, 0}, {stone, 0, 0}); MeshCollector col{{}}; MapblockMeshGenerator mg{&data, &col, nullptr}; mg.generate(); UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1); UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0); auto &&buf = col.prebuffers[0][0]; UASSERTEQ(u32, buf.layer.texture_id, 42); UASSERT(checkMeshEqual(buf.vertices, buf.indices, {quad::xn, quad::xp, quad::yn, quad::yp, quad::zn, quad::zp})); } void TestMapblockMeshGenerator::testSurroundedNode() { MockGameDef gamedef; content_t stone = gamedef.addSimpleNode("stone", 42); content_t wood = gamedef.addSimpleNode("wood", 13); gamedef.finalize(); MeshMakeData data = gamedef.makeSingleNodeMMD(); data.m_vmanip.setNode({0, 0, 0}, {stone, 0, 0}); data.m_vmanip.setNode({1, 0, 0}, {wood, 0, 0}); MeshCollector col{{}}; MapblockMeshGenerator mg{&data, &col, nullptr}; mg.generate(); UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1); UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0); auto &&buf = col.prebuffers[0][0]; UASSERTEQ(u32, buf.layer.texture_id, 42); UASSERT(checkMeshEqual(buf.vertices, buf.indices, {quad::xn, quad::yn, quad::yp, quad::zn, quad::zp})); } void TestMapblockMeshGenerator::testInterliquidSame() { MockGameDef gamedef; auto water = gamedef.addLiquidSource("water", 42); gamedef.finalize(); MeshMakeData data = gamedef.makeSingleNodeMMD(); data.m_vmanip.setNode({0, 0, 0}, {water, 0, 0}); data.m_vmanip.setNode({1, 0, 0}, {water, 0, 0}); MeshCollector col{{}}; MapblockMeshGenerator mg{&data, &col, nullptr}; mg.generate(); UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1); UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0); auto &&buf = col.prebuffers[0][0]; UASSERTEQ(u32, buf.layer.texture_id, 42); UASSERT(checkMeshEqual(buf.vertices, buf.indices, {quad::xn, quad::yn, quad::yp, quad::zn, quad::zp})); } void TestMapblockMeshGenerator::testInterliquidDifferent() { MockGameDef gamedef; auto water = gamedef.addLiquidSource("water", 42); auto lava = gamedef.addLiquidSource("lava", 13); gamedef.finalize(); MeshMakeData data = gamedef.makeSingleNodeMMD(); data.m_vmanip.setNode({0, 0, 0}, {water, 0, 0}); data.m_vmanip.setNode({0, 0, 1}, {lava, 0, 0}); MeshCollector col{{}}; MapblockMeshGenerator mg{&data, &col, nullptr}; mg.generate(); UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1); UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0); auto &&buf = col.prebuffers[0][0]; UASSERTEQ(u32, buf.layer.texture_id, 42); UASSERT(checkMeshEqual(buf.vertices, buf.indices, {quad::xn, quad::xp, quad::yn, quad::yp, quad::zn, quad::zp})); } }