diff --git a/src/constants.hpp b/src/constants.hpp index bf3d51e5..56ff3160 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -19,6 +19,7 @@ inline const std::string ENGINE_VERSION_STRING = "0.22"; inline constexpr blockid_t BLOCK_AIR = 0; inline constexpr itemid_t ITEM_EMPTY = 0; +inline constexpr entityid_t ENTITY_NONE = 0; inline constexpr int CHUNK_W = 16; inline constexpr int CHUNK_H = 256; diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index e6c44efb..d472c3e5 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -12,6 +12,7 @@ #include "../logic/scripting/scripting.hpp" #include "../objects/Player.hpp" #include "../objects/Entities.hpp" +#include "../objects/EntityDef.hpp" #include "../physics/Hitbox.hpp" #include "../util/stringutil.hpp" #include "../voxels/Block.hpp" @@ -60,24 +61,24 @@ std::shared_ptr create_debug_panel( fpsMin = fps; fpsMax = fps; }); - panel->add(create_label([](){ return L"fps: "+fpsString;})); + panel->add(create_label([]() { return L"fps: "+fpsString;})); - panel->add(create_label([](){ + panel->add(create_label([]() { return L"meshes: " + std::to_wstring(Mesh::meshesCount); })); - panel->add(create_label([](){ + panel->add(create_label([]() { int drawCalls = Mesh::drawCalls; Mesh::drawCalls = 0; return L"draw-calls: " + std::to_wstring(drawCalls); })); - panel->add(create_label([](){ + panel->add(create_label([]() { return L"speakers: " + std::to_wstring(audio::count_speakers())+ L" streams: " + std::to_wstring(audio::count_streams()); })); - panel->add(create_label([](){ + panel->add(create_label([]() { return L"lua-stack: " + std::to_wstring(scripting::get_values_on_stack()); })); - panel->add(create_label([=](){ + panel->add(create_label([=]() { auto& settings = engine->getSettings(); bool culling = settings.graphics.frustumCulling.get(); return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off"); @@ -90,7 +91,7 @@ std::shared_ptr create_debug_panel( return L"entities: "+std::to_wstring(level->entities->size())+L" next: "+ std::to_wstring(level->entities->peekNextID()); })); - panel->add(create_label([=](){ + panel->add(create_label([=]() { const auto& vox = player->selection.vox; std::wstringstream stream; stream << "r:" << vox.state.rotation << " s:" @@ -103,6 +104,17 @@ std::shared_ptr create_debug_panel( L" "+stream.str(); } })); + panel->add(create_label([=]() { + const auto& selection = player->selection; + if (selection.entity == ENTITY_NONE) { + return std::wstring {L"entity: -"}; + } else if (auto entity = level->entities->get(selection.entity)) { + return L"entity: "+util::str2wstr_utf8(entity->getDef().name)+ + L" uid: "+std::to_wstring(entity->getUID()); + } else { + return std::wstring {L"entity: error (invalid UID)"}; + } + })); panel->add(create_label([=](){ auto* indices = level->content->getIndices(); if (auto def = indices->blocks.get(player->selection.vox.id)) { diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 4279eb37..5c4bf69c 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -264,8 +264,7 @@ void PlayerController::postUpdate(float delta, bool input, bool pause) { if (input) { updateInteraction(); } else { - player->selection.vox.id = BLOCK_VOID; - player->selection.vox.state.rotation = 0; + player->selection = {}; } } @@ -365,13 +364,25 @@ voxel* PlayerController::updateSelection(float maxDistance) { maxDistance, end, norm, iend ); - if (vox == nullptr) { + if (vox) { + maxDistance = glm::distance(camera->position, end); + } + selection.entity = ENTITY_NONE; + selection.actualPosition = iend; + if (auto result = level->entities->rayCast( + camera->position, camera->front, maxDistance, player->getEntity())) { + selection.entity = result->entity; + selection.hitPosition = camera->position + camera->front * result->distance; + selection.position = selection.hitPosition; + selection.actualPosition = selection.position; + selection.normal = result->normal; + } + if (vox == nullptr || selection.entity) { selection.vox = {BLOCK_VOID, {}}; return nullptr; } blockstate selectedState = vox->state; selection.vox = *vox; - selection.actualPosition = iend; if (selectedState.segment) { selection.position = chunks->seekOrigin( iend, indices->blocks.get(selection.vox.id), selectedState diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 3077a3d5..255cde09 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -4,6 +4,7 @@ #include "../data/dynamic_util.hpp" #include "../assets/Assets.hpp" #include "../world/Level.hpp" +#include "../maths/rays.hpp" #include "../content/Content.hpp" #include "../physics/Hitbox.hpp" #include "../physics/PhysicsSolver.hpp" @@ -185,6 +186,37 @@ void Entities::loadEntity(const dynamic::Map_sptr& map, Entity entity) { } } +std::optional Entities::rayCast( + glm::vec3 start, glm::vec3 dir, float maxDistance, entityid_t ignore +) { + Ray ray(start, dir); + auto view = registry.view(); + + entityid_t foundUID = 0; + glm::ivec3 foundNormal; + + for (auto [entity, eid, transform, body] : view.each()) { + if (eid.uid == ignore) { + continue; + } + auto& hitbox = body.hitbox; + glm::ivec3 normal; + double distance; + if (ray.intersectAABB( + glm::vec3(), hitbox.getAABB(), maxDistance, normal, distance) > RayRelation::None) { + + foundUID = eid.uid; + foundNormal = normal; + maxDistance = static_cast(distance); + } + } + if (foundUID) { + return Entities::RaycastResult {foundUID, foundNormal, maxDistance}; + } else { + return std::nullopt; + } +} + void Entities::loadEntities(dynamic::Map_sptr root) { auto list = root->list("data"); for (size_t i = 0; i < list->size(); i++) { diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp index 731866e0..2e203f34 100644 --- a/src/objects/Entities.hpp +++ b/src/objects/Entities.hpp @@ -160,6 +160,12 @@ class Entities { void preparePhysics(); public: + struct RaycastResult { + entityid_t entity; + glm::ivec3 normal; + float distance; + }; + Entities(Level* level); void clean(); @@ -184,6 +190,16 @@ public: return std::nullopt; } + /// @brief Entities raycast. No blocks check included, use combined with + /// Chunks.rayCast + /// @param start Ray start + /// @param dir Ray direction normalized vector + /// @param maxDistance Max ray length + /// @param ignore Ignored entity ID + /// @return An optional structure containing entity, normal and distance + std::optional rayCast( + glm::vec3 start, glm::vec3 dir, float maxDistance, entityid_t ignore=-1); + void loadEntities(dynamic::Map_sptr map); void loadEntity(const dynamic::Map_sptr& map); void loadEntity(const dynamic::Map_sptr& map, Entity entity); diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index ac01964e..0e2a6c7e 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -60,6 +60,7 @@ Hitbox* Player::getHitbox() { return nullptr; } +#include "EntityDef.hpp" void Player::updateInput(PlayerInput& input, float delta) { auto hitbox = getHitbox(); if (hitbox == nullptr) { diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp index b2112092..dd80236d 100644 --- a/src/objects/Player.hpp +++ b/src/objects/Player.hpp @@ -33,12 +33,13 @@ struct PlayerInput { bool flight : 1; }; -struct BlockSelection { - voxel vox {0, {}}; +struct CursorSelection { + voxel vox {BLOCK_VOID, {}}; glm::ivec3 position {}; glm::ivec3 actualPosition {}; glm::ivec3 normal {}; glm::vec3 hitPosition; + entityid_t entity = ENTITY_NONE; }; class Player : public Object, public Serializable { @@ -56,7 +57,7 @@ public: std::shared_ptr currentCamera; bool debug = false; glm::vec3 cam {}; - BlockSelection selection {}; + CursorSelection selection {}; Player(Level* level, glm::vec3 position, float speed, std::shared_ptr inv, entityid_t eid); diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index ca9abd13..ffc8c0fe 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -461,7 +461,8 @@ voxel* Chunks::rayCast( box.b += offset; scalar_t boxDistance; glm::ivec3 boxNorm; - if (ray.intersectAABB(iend, box, maxDist, boxNorm, boxDistance) > RayRelation::None && boxDistance < distance) { + if (ray.intersectAABB(iend, box, maxDist, boxNorm, boxDistance) > RayRelation::None && + boxDistance < distance) { hit = true; distance = boxDistance; norm = boxNorm;