From 5769be8ec86b3cfe0bef4bd3f713cf070879f14d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 30 Jun 2024 16:25:08 +0300 Subject: [PATCH] add triggers --- res/content/base/scripts/components/drop.lua | 28 ++++++++ res/content/base/scripts/drop.lua | 23 ------- res/content/base/scripts/hud.lua | 8 +-- src/content/ContentLoader.cpp | 4 +- src/graphics/render/WorldRenderer.cpp | 2 +- src/logic/LevelController.cpp | 18 ++--- src/logic/scripting/scripting.cpp | 29 +++++++- src/logic/scripting/scripting.hpp | 4 +- src/maths/aabb.hpp | 43 +++++++++++- src/objects/Entities.cpp | 71 +++++++++++++++++--- src/objects/Entities.hpp | 11 ++- src/objects/Player.cpp | 3 +- src/physics/Hitbox.hpp | 16 +++++ src/physics/PhysicsSolver.cpp | 22 +++++- src/physics/PhysicsSolver.hpp | 21 ++++-- 15 files changed, 239 insertions(+), 64 deletions(-) create mode 100644 res/content/base/scripts/components/drop.lua delete mode 100644 res/content/base/scripts/drop.lua diff --git a/res/content/base/scripts/components/drop.lua b/res/content/base/scripts/components/drop.lua new file mode 100644 index 00000000..a1ab5aa2 --- /dev/null +++ b/res/content/base/scripts/components/drop.lua @@ -0,0 +1,28 @@ +local tsf = entity.transform +local body = entity.rigidbody + +inair = true +ready = false + +function on_grounded(force) + tsf:set_rot(mat4.rotate({0, 1, 0}, math.random()*360)) + inair = false + ready = true +end + +function on_fall() + inair = true +end + +function on_trigger_enter(index, oid) + if ready then + entity:despawn() + end +end + +function on_update() + if inair then + tsf:set_rot(mat4.rotate(tsf:get_rot(), {0, 1, 0}, math.random()*12)) + tsf:set_rot(mat4.rotate(tsf:get_rot(), {0, 0, 1}, math.random()*12)) + end +end diff --git a/res/content/base/scripts/drop.lua b/res/content/base/scripts/drop.lua deleted file mode 100644 index b228ec59..00000000 --- a/res/content/base/scripts/drop.lua +++ /dev/null @@ -1,23 +0,0 @@ -inair = true - -function on_grounded(force) - entity.transform:set_rot(mat4.rotate({0, 1, 0}, math.random()*360)) - inair = false -end - -function on_fall() - inair = true -end - -function on_update() - local tsf = entity.transform - local body = entity.rigidbody - if inair then - tsf:set_rot(mat4.rotate(tsf:get_rot(), {0, 1, 0}, math.random()*12)) - tsf:set_rot(mat4.rotate(tsf:get_rot(), {0, 0, 1}, math.random()*12)) - end - local dir = vec3.sub({player.get_pos(hud.get_player())}, tsf:get_pos()) - vec3.normalize(dir, dir) - vec3.mul(dir, time.delta()*50.0, dir) - --body:set_vel(vec3.add(rigidbody:get_vel(), dir)) -end diff --git a/res/content/base/scripts/hud.lua b/res/content/base/scripts/hud.lua index 155a0a00..78027e7f 100644 --- a/res/content/base/scripts/hud.lua +++ b/res/content/base/scripts/hud.lua @@ -3,15 +3,15 @@ local DROP_INIT_VEL = {0, 3, 0} function on_hud_open() input.add_callback("player.drop", function () - for i=1,5 do + for i=1,80 do local pid = hud.get_player() local pvel = {player.get_vel(pid)} local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0}) local throw_force = vec3.mul(vec3.add(player.get_dir(pid), { - math.random() - 0.5, - math.random() - 0.5, - math.random() - 0.5 + (math.random() - 0.5) * 5, + (math.random() - 0.5) * 5, + (math.random() - 0.5) * 5 }), DROP_FORCE) local drop = entity.spawn("base:drop", ppos) diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 530d218a..23d02953 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -322,9 +322,9 @@ void ContentLoader::loadEntity(EntityDef& def, const std::string& full, const st auto configFile = folder/fs::path("entities/"+name+".json"); if (fs::exists(configFile)) loadEntity(def, full, configFile); - auto scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua"); + auto scriptfile = folder/fs::path("scripts/components/"+def.scriptName+".lua"); if (fs::is_regular_file(scriptfile)) { - scripting::load_entity_script(env, def, scriptfile); + scripting::load_entity_component(env, def, scriptfile); } } diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 5d1c639b..1e6dd39a 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -233,7 +233,7 @@ void WorldRenderer::renderLines(Camera* camera, Shader* linesShader) { renderBlockSelection(); } if (player->debug) { - level->entities->renderDebug(*lineBatch); + level->entities->renderDebug(*lineBatch, *frustumCulling); } lineBatch->render(); } diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 11c7533d..20daeb00 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -30,15 +30,6 @@ void LevelController::update(float delta, bool input, bool pause) { settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2); chunks->update(settings.chunks.loadSpeed.get()); - player->update(delta, input, pause); - - // erease null pointers - level->objects.erase( - std::remove_if( - level->objects.begin(), level->objects.end(), - [](auto obj) { return obj == nullptr; }), - level->objects.end() - ); level->entities->clean(); if (!pause) { @@ -52,6 +43,15 @@ void LevelController::update(float delta, bool input, bool pause) { level->entities->updatePhysics(delta); level->entities->update(); } + player->update(delta, input, pause); + + // erease null pointers + level->objects.erase( + std::remove_if( + level->objects.begin(), level->objects.end(), + [](auto obj) { return obj == nullptr; }), + level->objects.end() + ); } void LevelController::saveWorld() { diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 720e2083..8d9e6a4c 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -273,6 +273,8 @@ scriptenv scripting::on_entity_spawn(const EntityDef& def, entityid_t eid, entit funcsset.on_grounded = lua::hasfield(L, "on_grounded"); funcsset.on_fall = lua::hasfield(L, "on_fall"); funcsset.on_despawn = lua::hasfield(L, "on_despawn"); + funcsset.on_trigger_enter = lua::hasfield(L, "on_trigger_enter"); + funcsset.on_trigger_exit = lua::hasfield(L, "on_trigger_exit"); lua::pop(L, 2); return entityenv; } @@ -325,6 +327,29 @@ bool scripting::on_entity_fall(const Entity& entity) { return true; } +void scripting::on_trigger_enter(const Entity& entity, size_t index, entityid_t oid) { + const auto& script = entity.getScripting(); + if (script.funcsset.on_trigger_enter) { + process_entity_callback(script.env, "on_trigger_enter", [index, oid](auto L) { + lua::pushinteger(L, index); + lua::pushinteger(L, oid); + return 2; + }); + } +} + + +void scripting::on_trigger_exit(const Entity& entity, size_t index, entityid_t oid) { + const auto& script = entity.getScripting(); + if (script.funcsset.on_trigger_exit) { + process_entity_callback(script.env, "on_trigger_exit", [index, oid](auto L) { + lua::pushinteger(L, index); + lua::pushinteger(L, oid); + return 2; + }); + } +} + void scripting::on_entities_update() { auto L = lua::get_main_thread(); lua::get_from(L, STDCOMP, "update", true); @@ -415,10 +440,10 @@ void scripting::load_item_script(const scriptenv& senv, const std::string& prefi funcsset.on_block_break_by = register_event(env, "on_block_break_by", prefix+".blockbreakby"); } -void scripting::load_entity_script(const scriptenv& penv, const EntityDef& def, const fs::path& file) { +void scripting::load_entity_component(const scriptenv& penv, const EntityDef& def, const fs::path& file) { auto L = lua::get_main_thread(); std::string src = files::read_string(file); - logger.info() << "script (entity) " << file.u8string(); + logger.info() << "script (component) " << file.u8string(); lua::loadbuffer(L, 0, src, file.u8string()); lua::store_in(L, lua::CHUNKS_TABLE, def.scriptName); } diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index a46d7500..f6d1ccdb 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -81,6 +81,8 @@ namespace scripting { bool on_entity_grounded(const Entity& entity, float force); bool on_entity_fall(const Entity& entity); void on_entities_update(); + void on_trigger_enter(const Entity& entity, size_t index, entityid_t oid); + void on_trigger_exit(const Entity& entity, size_t index, entityid_t oid); /// @brief Called on UI view show void on_ui_open( @@ -115,7 +117,7 @@ namespace scripting { const fs::path& file, item_funcs_set& funcsset); - void load_entity_script( + void load_entity_component( const scriptenv& env, const EntityDef& def, const fs::path& file); diff --git a/src/maths/aabb.hpp b/src/maths/aabb.hpp index 40f57ccc..7859ef65 100644 --- a/src/maths/aabb.hpp +++ b/src/maths/aabb.hpp @@ -6,13 +6,16 @@ /// @brief Axis Aligned Bounding Box struct AABB { glm::vec3 a {0.0f}; - glm::vec3 b {1.0f}; + glm::vec3 b {1.0f, 1.0f, 1.0f}; AABB() {} AABB(glm::vec3 size) : a(0.0f), b(size) { } + AABB(glm::vec3 pos, glm::vec3 size) : a(pos), b(size) { + } + /// @brief Get AABB point with minimal x,y,z inline glm::vec3 min() const { return glm::min(a, b); @@ -59,6 +62,44 @@ struct AABB { return !(pos.x < p.x || pos.y < p.y || pos.z < p.z || pos.x >= p.x+s.x || pos.y >= p.y+s.y || pos.z >= p.z+s.z); } + + void fix() { + auto beg = min(); + auto end = max(); + a = beg; + b = end; + } + + inline void addPoint(glm::vec3 p) { + a = glm::min(a, p); + b = glm::max(b, p); + } + + /// TODO: optimize + void transform(const glm::mat4& matrix) { + auto pa = a; + auto pb = b; + a = matrix * glm::vec4(a, 1.0f); + b = matrix * glm::vec4(b, 1.0f); + fix(); + addPoint(matrix * glm::vec4(pb.x, pa.y, pa.z, 1.0f)); + addPoint(matrix * glm::vec4(pb.x, pb.y, pa.z, 1.0f)); + addPoint(matrix * glm::vec4(pb.x, pb.y, pb.z, 1.0f)); + addPoint(matrix * glm::vec4(pa.x, pb.y, pa.z, 1.0f)); + addPoint(matrix * glm::vec4(pa.x, pa.y, pb.z, 1.0f)); + addPoint(matrix * glm::vec4(pb.x, pa.y, pb.z, 1.0f)); + } + + inline bool intersect(const AABB& aabb) { + return ( + a.x <= aabb.b.x && + b.x >= aabb.a.x && + a.y <= aabb.b.y && + b.y >= aabb.a.y && + a.z <= aabb.b.z && + b.z >= aabb.a.z + ); + } }; #endif // MATHS_AABB_HPP_ diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 52f914a6..95ace4a6 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -35,7 +35,22 @@ entityid_t Entities::spawn(EntityDef& def, glm::vec3 pos) { auto id = nextID++; registry.emplace(entity, static_cast(id), def); registry.emplace(entity, pos, size/4.0f, glm::mat3(1.0f)); - registry.emplace(entity, true, Hitbox {pos, def.hitbox}); + registry.emplace(entity, true, Hitbox {pos, def.hitbox}, std::vector{ + {true, id, AABB {glm::vec3{-1.0f, -1.0f, -1.0f}, glm::vec3{1.0f, 1.0f, 1.0f}}, {}, {}, {}, + [=](auto entityid, auto index, auto otherid) { + if (auto entity = get(entityid)) { + if (entity->isValid()) { + scripting::on_trigger_enter(*entity, index, otherid); + } + } + }, [=](auto entityid, auto index, auto otherid) { + if (auto entity = get(entityid)) { + if (entity->isValid()) { + scripting::on_trigger_exit(*entity, index, otherid); + } + } + }} + }); auto& scripting = registry.emplace(entity, entity_funcs_set {}, nullptr); entities[id] = entity; scripting.env = scripting::on_entity_spawn(def, id, scripting.funcsset); @@ -44,24 +59,50 @@ entityid_t Entities::spawn(EntityDef& def, glm::vec3 pos) { void Entities::despawn(entityid_t id) { if (auto entity = get(id)) { - scripting::on_entity_despawn(entity->getDef(), *entity); - registry.destroy(get(id)->getHandler()); + auto& eid = entity->getID(); + if (!eid.destroyFlag) { + eid.destroyFlag = true; + scripting::on_entity_despawn(entity->getDef(), *entity); + } } } void Entities::clean() { for (auto it = entities.begin(); it != entities.end();) { - if (registry.valid(it->second)) { + if (!registry.get(it->second).destroyFlag) { ++it; } else { + registry.destroy(it->second); it = entities.erase(it); } } } - +#include "../util/timeutil.hpp" void Entities::updatePhysics(float delta){ auto view = registry.view(); auto physics = level->physics.get(); + { + std::vector triggers; + for (auto [entity, eid, transform, rigidbody] : view.each()) { + if (!rigidbody.enabled) { + continue; + } + for (size_t i = 0; i < rigidbody.triggers.size(); i++) { + auto& trigger = rigidbody.triggers[i]; + for (auto oid : trigger.prevEntered) { + if (trigger.nextEntered.find(oid) == trigger.nextEntered.end()) { + trigger.exitCallback(trigger.entity, i, oid); + } + } + trigger.prevEntered = trigger.nextEntered; + trigger.nextEntered.clear(); + trigger.calculated = trigger.aabb; + trigger.calculated.transform(transform.combined); + triggers.push_back(&trigger); + } + } + physics->setTriggers(std::move(triggers)); + } for (auto [entity, eid, transform, rigidbody] : view.each()) { if (!rigidbody.enabled) { continue; @@ -76,11 +117,11 @@ void Entities::updatePhysics(float delta){ 10, false, 1.0f, - true + true, + eid.uid ); hitbox.linearDamping = hitbox.grounded * 24; transform.pos = hitbox.position; - //transform.rot = glm::rotate(glm::mat4(transform.rot), delta, glm::vec3(0, 1, 0)); if (hitbox.grounded && !grounded) { scripting::on_entity_grounded(*get(eid.uid), glm::length(prevVel-hitbox.velocity)); } @@ -92,14 +133,27 @@ void Entities::updatePhysics(float delta){ void Entities::update() { scripting::on_entities_update(); + auto view = registry.view(); + for (auto [entity, transform] : view.each()) { + transform.refresh(); + } } -void Entities::renderDebug(LineBatch& batch) { +void Entities::renderDebug(LineBatch& batch, Frustum& frustum) { batch.lineWidth(1.0f); auto view = registry.view(); for (auto [entity, transform, rigidbody] : view.each()) { const auto& hitbox = rigidbody.hitbox; + const auto& pos = transform.pos; + const auto& size = transform.size; + if (!frustum.isBoxVisible(pos-size, pos+size)) { + continue; + } batch.box(hitbox.position, hitbox.halfsize * 2.0f, glm::vec4(1.0f)); + + for (auto& trigger : rigidbody.triggers) { + batch.box(trigger.calculated.center(), trigger.calculated.size(), glm::vec4(1.0f, 1.0f, 0.0f, 1.0f)); + } } } @@ -110,7 +164,6 @@ void Entities::render(Assets* assets, ModelBatch& batch, Frustum& frustum) { const auto& pos = transform.pos; const auto& size = transform.size; if (frustum.isBoxVisible(pos-size, pos+size)) { - transform.refresh(); batch.pushMatrix(transform.combined); batch.draw(model); batch.popMatrix(); diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp index 80bf7e27..7403c645 100644 --- a/src/objects/Entities.hpp +++ b/src/objects/Entities.hpp @@ -4,6 +4,7 @@ #include "../typedefs.hpp" #include "../physics/Hitbox.hpp" +#include #include #include #include @@ -14,6 +15,8 @@ struct entity_funcs_set { bool on_despawn : 1; bool on_grounded : 1; bool on_fall : 1; + bool on_trigger_enter : 1; + bool on_trigger_exit : 1; }; struct EntityDef; @@ -21,6 +24,7 @@ struct EntityDef; struct EntityId { entityid_t uid; const EntityDef& def; + bool destroyFlag = false; }; struct Transform { @@ -35,6 +39,7 @@ struct Transform { struct Rigidbody { bool enabled = true; Hitbox hitbox; + std::vector triggers; }; struct Scripting { @@ -59,8 +64,8 @@ public: Entity(Entities& entities, entityid_t id, entt::registry& registry, const entt::entity entity) : entities(entities), id(id), registry(registry), entity(entity) {} - entityid_t getID() const { - return id; + EntityId& getID() const { + return registry.get(entity); } bool isValid() const { @@ -106,7 +111,7 @@ public: void updatePhysics(float delta); void update(); - void renderDebug(LineBatch& batch); + void renderDebug(LineBatch& batch, Frustum& frustum); void render(Assets* assets, ModelBatch& batch, Frustum& frustum); entityid_t spawn(EntityDef& def, glm::vec3 pos); diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 7d295819..91301c52 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -88,7 +88,8 @@ void Player::updateInput( substeps, crouch, flight ? 0.0f : 1.0f, - !noclip + !noclip, + 0 ); if (flight && hitbox->grounded) { diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 718a8a0d..5090d7dc 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -1,8 +1,24 @@ #ifndef PHYSICS_HITBOX_HPP_ #define PHYSICS_HITBOX_HPP_ +#include "../maths/aabb.hpp" +#include "../typedefs.hpp" + +#include +#include #include +struct Trigger { + bool enabled = true; + entityid_t entity; + AABB aabb; + AABB calculated; + std::set prevEntered; + std::set nextEntered; + std::function enterCallback; + std::function exitCallback; +}; + struct Hitbox { glm::vec3 position; glm::vec3 halfsize; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 5e03a04f..96f66ba2 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -6,12 +6,14 @@ #include "../voxels/Chunks.hpp" #include "../voxels/voxel.hpp" +#include + const float E = 0.03f; const float MAX_FIX = 0.1f; PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) { } - +#include "../util/timeutil.hpp" void PhysicsSolver::step( Chunks* chunks, Hitbox* hitbox, @@ -19,7 +21,8 @@ void PhysicsSolver::step( uint substeps, bool shifting, float gravityScale, - bool collisions + bool collisions, + entityid_t entity ) { float dt = delta / static_cast(substeps); float linearDamping = hitbox->linearDamping; @@ -78,6 +81,21 @@ void PhysicsSolver::step( hitbox->grounded = true; } } + AABB aabb; + aabb.a = hitbox->position - hitbox->halfsize; + aabb.b = hitbox->position + hitbox->halfsize; + for (size_t i = 0; i < triggers.size(); i++) { + auto& trigger = triggers[i]; + if (trigger->entity == entity) { + continue; + } + if (aabb.intersect(trigger->calculated)) { + if (trigger->prevEntered.find(entity) == trigger->prevEntered.end()) { + trigger->enterCallback(trigger->entity, i, entity); + } + trigger->nextEntered.insert(entity); + } + } } void PhysicsSolver::colisionCalc( diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index a9807af4..8de77ac8 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -1,17 +1,21 @@ #ifndef PHYSICS_PHYSICSSOLVER_HPP_ #define PHYSICS_PHYSICSSOLVER_HPP_ +#include "Hitbox.hpp" + #include "../typedefs.hpp" #include "../voxels/voxel.hpp" +#include #include class Block; class Chunks; -struct Hitbox; +struct Trigger; class PhysicsSolver { glm::vec3 gravity; + std::vector triggers; public: PhysicsSolver(glm::vec3 gravity); void step( @@ -21,18 +25,23 @@ public: uint substeps, bool shifting, float gravityScale, - bool collisions + bool collisions, + entityid_t entity ); void colisionCalc( - Chunks* chunks, - Hitbox* hitbox, - glm::vec3& vel, - glm::vec3& pos, + Chunks* chunks, + Hitbox* hitbox, + glm::vec3& vel, + glm::vec3& pos, const glm::vec3 half, float stepHeight ); bool isBlockInside(int x, int y, int z, Hitbox* hitbox); bool isBlockInside(int x, int y, int z, Block* def, blockstate state, Hitbox* hitbox); + + void setTriggers(std::vector triggers) { + this->triggers = std::move(triggers); + } }; #endif // PHYSICS_PHYSICSSOLVER_HPP_