Skip to content

Commit

Permalink
Add meshgen tests
Browse files Browse the repository at this point in the history
  • Loading branch information
numberZero authored and rubenwardy committed Feb 22, 2024
1 parent 753f03f commit 0d30a30
Show file tree
Hide file tree
Showing 2 changed files with 269 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/unittest/CMakeLists.txt
Expand Up @@ -44,6 +44,7 @@ set (UNITTEST_SRCS
set (UNITTEST_CLIENT_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/mesh_compare.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_clientactiveobjectmgr.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_content_mapblock.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_eventmanager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_gameui.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_mesh_compare.cpp
Expand Down
268 changes: 268 additions & 0 deletions src/unittest/test_content_mapblock.cpp
@@ -0,0 +1,268 @@
/*
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 <algorithm>
#include <numeric>

#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<IWritableItemDefManager *>(m_itemdef);
}

NodeDefManager *node_mgr() noexcept {
return const_cast<NodeDefManager *>(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<u8 *>(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}));
}

}

0 comments on commit 0d30a30

Please sign in to comment.