diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index fba83c64..d7290998 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -27,6 +27,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" +#include "voxels/Pathfinding.hpp" #include "window/Window.hpp" #include "world/Level.hpp" #include "world/LevelEvents.hpp" @@ -386,6 +387,36 @@ void WorldRenderer::renderFrame( skybox->draw(ctx, camera, assets, worldInfo.daytime, clouds); // In-world lines + for (const auto& [_, agent] : level.pathfinding->getAgents()) { + const auto& route = agent.route; + if (!route.found) + continue; + for (int i = 1; i < route.nodes.size(); i++) { + const auto& a = route.nodes.at(i - 1); + const auto& b = route.nodes.at(i); + + if (i == 1) { + lines->pushLine( + glm::vec3(a.pos) + glm::vec3(0.5f), + glm::vec3(a.pos) + glm::vec3(0.5f, 1.0f, 0.5f), + glm::vec4(1, 1, 1, 1) + ); + } + + lines->pushLine( + glm::vec3(a.pos) + glm::vec3(0.5f), + glm::vec3(b.pos) + glm::vec3(0.5f), + glm::vec4(1, 0, 1, 1) + ); + + lines->pushLine( + glm::vec3(b.pos) + glm::vec3(0.5f), + glm::vec3(b.pos) + glm::vec3(0.5f, 1.0f, 0.5f), + glm::vec4(1, 1, 1, 1) + ); + } + } + linesShader.use(); linesShader.uniformMatrix("u_projview", camera.getProjView()); lines->draw(*lineBatch); diff --git a/src/logic/scripting/lua/libs/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp index 6e65b790..c1d9f933 100644 --- a/src/logic/scripting/lua/libs/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -38,6 +38,7 @@ extern const luaL_Reg mat4lib[]; extern const luaL_Reg networklib[]; extern const luaL_Reg packlib[]; extern const luaL_Reg particleslib[]; // gfx.particles +extern const luaL_Reg pathfindinglib[]; extern const luaL_Reg playerlib[]; extern const luaL_Reg posteffectslib[]; // gfx.posteffects extern const luaL_Reg quatlib[]; diff --git a/src/logic/scripting/lua/libs/libpathfinding.cpp b/src/logic/scripting/lua/libs/libpathfinding.cpp new file mode 100644 index 00000000..96ac0d13 --- /dev/null +++ b/src/logic/scripting/lua/libs/libpathfinding.cpp @@ -0,0 +1,55 @@ +#include "api_lua.hpp" + +#include "voxels/Pathfinding.hpp" +#include "world/Level.hpp" + +using namespace scripting; + +static voxels::Agent* get_agent(lua::State* L) { + return level->pathfinding->getAgent(lua::tointeger(L, 1)); +} + +static int l_create_agent(lua::State* L) { + return lua::pushinteger(L, level->pathfinding->createAgent()); +} + +static int l_set_enabled(lua::State* L) { + if (auto agent = get_agent(L)) { + agent->enabled = lua::toboolean(L, 2); + } + return 0; +} + +static int l_is_enabled(lua::State* L) { + if (auto agent = get_agent(L)) { + return lua::pushboolean(L, agent->enabled); + } + return lua::pushboolean(L, false); +} + +static int l_make_route(lua::State* L) { + if (auto agent = get_agent(L)) { + auto start = lua::tovec3(L, 2); + auto target = lua::tovec3(L, 3); + auto route = level->pathfinding->perform(*agent, start, target); + agent->route = route; + if (!route.found) { + return 0; + } + lua::createtable(L, route.nodes.size(), 0); + for (int i = 0; i < route.nodes.size(); i++) { + lua::pushvec3(L, route.nodes[i].pos); + lua::rawseti(L, i + 1); + } + return 1; + } + return 0; +} + +const luaL_Reg pathfindinglib[] = { + {"create_agent", lua::wrap}, + {"set_enabled", lua::wrap}, + {"is_enabled", lua::wrap}, + {"make_route", lua::wrap}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 891b89c7..f6ce4fe3 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -72,6 +72,7 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "input", inputlib); openlib(L, "inventory", inventorylib); openlib(L, "network", networklib); + openlib(L, "pathfinding", pathfindinglib); openlib(L, "player", playerlib); openlib(L, "time", timelib); openlib(L, "world", worldlib); diff --git a/src/voxels/Pathfinding.cpp b/src/voxels/Pathfinding.cpp index 4282d5bb..54020cdd 100644 --- a/src/voxels/Pathfinding.cpp +++ b/src/voxels/Pathfinding.cpp @@ -75,6 +75,12 @@ static void restore_route( } } +int Pathfinding::createAgent() { + int id = nextAgent++; + agents[id] = Agent(); + return id; +} + Route Pathfinding::perform( const Agent& agent, const glm::ivec3& start, const glm::ivec3& end ) { @@ -89,12 +95,16 @@ Route Pathfinding::perform( std::unordered_map parents; const auto& chunks = *level.chunks; + int height = std::max(agent.height, 1); while (!queue.empty()) { + if (blocked.size() == agent.maxVisitedBlocks) { + break; + } auto node = queue.top(); queue.pop(); - if (node.pos.x == end.x && glm::abs((node.pos.y - end.y) / agent.height) == 0 && node.pos.z == end.z) { + if (node.pos.x == end.x && glm::abs((node.pos.y - end.y) / height) == 0 && node.pos.z == end.z) { restore_route(route, node, parents); route.nodes.push_back({start}); route.found = true; @@ -144,6 +154,18 @@ Route Pathfinding::perform( return route; } +Agent* Pathfinding::getAgent(int id) { + const auto& found = agents.find(id); + if (found != agents.end()) { + return &found->second; + } + return nullptr; +} + +const std::unordered_map& Pathfinding::getAgents() const { + return agents; +} + static int check_point( const ContentUnitIndices& defs, const GlobalChunks& chunks, diff --git a/src/voxels/Pathfinding.hpp b/src/voxels/Pathfinding.hpp index f743fd1c..04e039cb 100644 --- a/src/voxels/Pathfinding.hpp +++ b/src/voxels/Pathfinding.hpp @@ -4,6 +4,7 @@ #include #include #include +#include class Level; class GlobalChunks; @@ -19,7 +20,12 @@ namespace voxels { }; struct Agent { - int height; + bool enabled = false; + int height = 1; + int maxVisitedBlocks = 1e5; + glm::ivec3 start; + glm::ivec3 target; + Route route; }; struct Map { @@ -46,12 +52,20 @@ namespace voxels { public: Pathfinding(const Level& level); + int createAgent(); + Route perform( const Agent& agent, const glm::ivec3& start, const glm::ivec3& end ); + + Agent* getAgent(int id); + + const std::unordered_map& getAgents() const; private: const Level& level; const GlobalChunks& chunks; + std::unordered_map agents; + int nextAgent = 1; int getSurfaceAt(const glm::ivec3& pos, int maxDelta); };