From d2fa8ca97d291508ce3812fb052a8255d3190d00 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Tue, 18 Aug 2015 13:23:47 +0200 Subject: [PATCH] use collision structures for ray tests --- src/ai/ai.cpp | 10 ++- src/ui/Interface.hpp | 10 ++- src/ui/ui.cpp | 111 +++++++++++++++------------------- src/world/BlockLookup.hpp | 8 ++- src/world/Chunk.hpp | 6 +- src/world/EntityCollision.hpp | 28 +++++++++ src/world/World.cpp | 47 ++++++-------- src/world/World.hpp | 17 +++--- src/world/WorldCollision.hpp | 19 +++++- src/world/chunk.cpp | 25 ++++---- 10 files changed, 149 insertions(+), 132 deletions(-) create mode 100644 src/world/EntityCollision.hpp diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index 6b380e4..5f5474b 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -5,6 +5,7 @@ #include "../model/geometry.hpp" #include "../world/Entity.hpp" #include "../world/World.hpp" +#include "../world/WorldCollision.hpp" #include @@ -34,12 +35,9 @@ void Chaser::Update(int dt) { bool line_of_sight = true; // FIXME: this only works if target is in the reference chunk (which is true for the player) Ray aim{Target().Position() - diff, norm_diff}; - Chunk *chunk; - int blkid; - float distance; - glm::vec3 normal; - if (world.Intersection(aim, glm::mat4(1.0f), chunk, blkid, distance, normal)) { - line_of_sight = distance > dist; + WorldCollision coll; + if (world.Intersection(aim, glm::mat4(1.0f), coll)) { + line_of_sight = coll.depth > dist; } if (!line_of_sight) { diff --git a/src/ui/Interface.hpp b/src/ui/Interface.hpp index 907f9c2..2538c46 100644 --- a/src/ui/Interface.hpp +++ b/src/ui/Interface.hpp @@ -10,6 +10,8 @@ #include "../model/geometry.hpp" #include "../model/OutlineModel.hpp" #include "../world/Block.hpp" +#include "../world/EntityCollision.hpp" +#include "../world/WorldCollision.hpp" #include #include @@ -18,8 +20,6 @@ namespace blank { -class Chunk; -class Entity; class Environment; class Viewport; class World; @@ -94,10 +94,8 @@ private: HUD hud; Ray aim; - Chunk *aim_chunk; - Entity *aim_entity; - int aim_block; - glm::vec3 aim_normal; + WorldCollision aim_world; + EntityCollision aim_entity; OutlineModel outline; glm::mat4 outline_transform; diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index c063099..1de6237 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -11,6 +11,7 @@ #include "../graphics/Viewport.hpp" #include "../io/TokenStreamReader.hpp" #include "../model/shapes.hpp" +#include "../world/BlockLookup.hpp" #include "../world/World.hpp" #include @@ -103,9 +104,8 @@ Interface::Interface( , ctrl(world.Player()) , hud(world.BlockTypes(), env.assets.small_ui_font) , aim{{ 0, 0, 0 }, { 0, 0, -1 }} -, aim_chunk(nullptr) -, aim_block(0) -, aim_normal() +, aim_world() +, aim_entity() , outline() , outline_transform(1.0f) , counter_text() @@ -276,7 +276,7 @@ void Interface::ToggleCollision() { void Interface::PrintBlockInfo() { std::cout << std::endl; - if (!aim_chunk) { + if (!aim_world) { PostMessage("not looking at any block"); Ray aim = ctrl.Aim(); std::stringstream s; @@ -285,53 +285,53 @@ void Interface::PrintBlockInfo() { return; } std::stringstream s; - s << "looking at block " << aim_block - << " " << Chunk::ToCoords(aim_block) - << " of chunk " << aim_chunk->Position() + s << "looking at block " << aim_world.block + << " " << aim_world.BlockCoords() + << " of chunk " << aim_world.GetChunk().Position() ; PostMessage(s.str()); - Print(aim_chunk->BlockAt(aim_block)); + Print(aim_world.GetBlock()); } void Interface::PrintChunkInfo() { std::cout << std::endl; - if (!aim_chunk) { + if (!aim_world) { PostMessage("not looking at any block"); return; } std::stringstream s; - s << "looking at chunk " << aim_chunk->Position(); + s << "looking at chunk " << aim_world.GetChunk().Position(); PostMessage(s.str()); PostMessage(" neighbors:"); - if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) { + if (aim_world.GetChunk().HasNeighbor(Block::FACE_LEFT)) { s.str(""); - s << " left " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position(); + s << " left " << aim_world.GetChunk().GetNeighbor(Block::FACE_LEFT).Position(); PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) { + if (aim_world.GetChunk().HasNeighbor(Block::FACE_RIGHT)) { s.str(""); - s << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position(); + s << " right " << aim_world.GetChunk().GetNeighbor(Block::FACE_RIGHT).Position(); PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_UP)) { + if (aim_world.GetChunk().HasNeighbor(Block::FACE_UP)) { s.str(""); - s << " up " << aim_chunk->GetNeighbor(Block::FACE_UP).Position(); + s << " up " << aim_world.GetChunk().GetNeighbor(Block::FACE_UP).Position(); PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) { + if (aim_world.GetChunk().HasNeighbor(Block::FACE_DOWN)) { s.str(""); - s << " down " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position(); + s << " down " << aim_world.GetChunk().GetNeighbor(Block::FACE_DOWN).Position(); PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) { + if (aim_world.GetChunk().HasNeighbor(Block::FACE_FRONT)) { s.str(""); - s << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position(); + s << " front " << aim_world.GetChunk().GetNeighbor(Block::FACE_FRONT).Position(); PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_BACK)) { + if (aim_world.GetChunk().HasNeighbor(Block::FACE_BACK)) { s.str(""); - s << " back " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position(); + s << " back " << aim_world.GetChunk().GetNeighbor(Block::FACE_BACK).Position(); PostMessage(s.str()); } std::cout << std::endl; @@ -411,12 +411,12 @@ void Interface::UpdateOrientation() { } void Interface::UpdateBlockInfo() { - if (aim_chunk) { - const Block &block = aim_chunk->BlockAt(aim_block); + if (aim_world) { + const Block &block = aim_world.GetBlock(); if (last_displayed != block) { std::stringstream s; s << "Block: " - << aim_chunk->Type(block).label + << aim_world.GetType().label << ", face: " << block.GetFace() << ", turn: " << block.GetTurn(); block_text.Set(env.assets.small_ui_font, s.str()); @@ -464,38 +464,37 @@ void Interface::HandleRelease(const SDL_MouseButtonEvent &event) { } void Interface::PickBlock() { - if (!aim_chunk) return; - selection = aim_chunk->BlockAt(aim_block); + if (!aim_world) return; + selection = aim_world.GetBlock(); hud.Display(selection); } void Interface::PlaceBlock() { - if (!aim_chunk) return; - Chunk *mod_chunk = aim_chunk; - glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal; - if (!Chunk::InBounds(next_pos)) { - mod_chunk = &world.Next(*aim_chunk, aim_normal); - next_pos -= aim_normal * glm::vec3(Chunk::Extent()); + if (!aim_world) return; + + glm::vec3 next_pos = aim_world.BlockCoords() + aim_world.normal; + BlockLookup next_block(&aim_world.GetChunk(), next_pos); + if (next_block) { } - mod_chunk->SetBlock(next_pos, selection); + next_block.SetBlock(selection); if (config.audio_disabled) return; const Entity &player = ctrl.Controlled(); env.audio.Play( place_sound, - mod_chunk->ToSceneCoords(player.ChunkCoords(), next_pos) + aim_world.GetChunk().ToSceneCoords(player.ChunkCoords(), next_pos) ); } void Interface::RemoveBlock() noexcept { - if (!aim_chunk) return; - aim_chunk->SetBlock(aim_block, remove); + if (!aim_world) return; + aim_world.SetBlock(remove); if (config.audio_disabled) return; const Entity &player = ctrl.Controlled(); env.audio.Play( remove_sound, - aim_chunk->ToSceneCoords(player.ChunkCoords(), Chunk::ToCoords(aim_block)) + aim_world.GetChunk().ToSceneCoords(player.ChunkCoords(), aim_world.BlockCoords()) ); } @@ -576,32 +575,22 @@ OutlineModel::Buffer outl_buf; } void Interface::CheckAim() { - float chunk_dist; - glm::vec3 chunk_normal; - if (world.Intersection(aim, glm::mat4(1.0f), aim_chunk, aim_block, chunk_dist, chunk_normal)) { - } else { - aim_chunk = nullptr; + if (!world.Intersection(aim, glm::mat4(1.0f), aim_world)) { + aim_world = WorldCollision(); } - float entity_dist; - glm::vec3 entity_normal; - if (!world.Intersection(aim, glm::mat4(1.0f), aim_entity, entity_dist, entity_normal)) { - aim_entity = nullptr; + if (!world.Intersection(aim, glm::mat4(1.0f), aim_entity)) { + aim_entity = EntityCollision(); } - if (aim_chunk && aim_entity) { + if (aim_world && aim_entity) { // got both, pick the closest one - if (chunk_dist < entity_dist) { - aim_normal = chunk_normal; + if (aim_world.depth < aim_entity.depth) { UpdateOutline(); - aim_entity = nullptr; + aim_entity = EntityCollision(); } else { - aim_normal = entity_normal; - aim_chunk = nullptr; + aim_world = WorldCollision(); } - } else if (aim_chunk) { - aim_normal = chunk_normal; + } else if (aim_world) { UpdateOutline(); - } else if (aim_entity) { - aim_normal = entity_normal; } if (debug) { UpdateBlockInfo(); @@ -610,10 +599,10 @@ void Interface::CheckAim() { void Interface::UpdateOutline() { outl_buf.Clear(); - aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outl_buf); + aim_world.GetType().FillOutlineModel(outl_buf); outline.Update(outl_buf); - outline_transform = aim_chunk->Transform(world.Player().ChunkCoords()); - outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block); + outline_transform = aim_world.GetChunk().Transform(world.Player().ChunkCoords()); + outline_transform *= aim_world.BlockTransform(); outline_transform *= glm::scale(glm::vec3(1.005f)); } @@ -621,7 +610,7 @@ void Interface::UpdateOutline() { void Interface::Render(Viewport &viewport) noexcept { if (config.visual_disabled) return; - if (aim_chunk) { + if (aim_world) { PlainColor &outline_prog = viewport.WorldOutlineProgram(); outline_prog.SetM(outline_transform); outline.Draw(); diff --git a/src/world/BlockLookup.hpp b/src/world/BlockLookup.hpp index 4c8b12b..122b3c7 100644 --- a/src/world/BlockLookup.hpp +++ b/src/world/BlockLookup.hpp @@ -10,13 +10,13 @@ namespace blank { class BlockLookup { public: - // resolve chunk/position from oob coordinates + /// resolve chunk/position from oob coordinates BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept; - // resolve chunk/position from ib coordinates and direction + /// resolve chunk/position from ib coordinates and direction BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face dir) noexcept; - // check if lookup was successful + /// check if lookup was successful operator bool() const { return chunk; } // only valid if lookup was successful @@ -26,6 +26,8 @@ public: const BlockType &GetType() const noexcept { return GetChunk().Type(GetBlock()); } int GetLight() const noexcept { return GetChunk().GetLight(GetBlockPos()); } + void SetBlock(const Block &b) noexcept { GetChunk().SetBlock(GetBlockPos(), b); } + // traverse in given direction BlockLookup Next(Block::Face f) const { return BlockLookup(chunk, pos, f); } diff --git a/src/world/Chunk.hpp b/src/world/Chunk.hpp index e9a075b..61ec83b 100644 --- a/src/world/Chunk.hpp +++ b/src/world/Chunk.hpp @@ -142,15 +142,13 @@ public: bool Intersection( const Ray &, const glm::mat4 &M, - int &blkid, - float &dist, - glm::vec3 &normal) const noexcept; + WorldCollision &) noexcept; bool Intersection( const AABB &box, const glm::mat4 &Mbox, const glm::mat4 &Mchunk, - std::vector &) const noexcept; + std::vector &) noexcept; void Position(const Pos &pos) noexcept { position = pos; } const Pos &Position() const noexcept { return position; } diff --git a/src/world/EntityCollision.hpp b/src/world/EntityCollision.hpp new file mode 100644 index 0000000..be1a82d --- /dev/null +++ b/src/world/EntityCollision.hpp @@ -0,0 +1,28 @@ +#ifndef BLANK_WORLD_ENTITYCOLLISION_HPP_ +#define BLANK_WORLD_ENTITYCOLLISION_HPP_ + + +namespace blank { + +class Entity; + +struct EntityCollision { + + Entity *entity; + + float depth; + glm::vec3 normal; + + EntityCollision() + : entity(nullptr), depth(0.0f), normal(0.0f) { } + EntityCollision(Entity *e, float d, const glm::vec3 &n) + : entity(e), depth(d), normal(n) { } + + /// check if an actual collision + operator bool() const noexcept { return entity; } + +}; + +} + +#endif diff --git a/src/world/World.cpp b/src/world/World.cpp index 2c0d164..997a5f0 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -1,5 +1,6 @@ #include "World.hpp" +#include "EntityCollision.hpp" #include "WorldCollision.hpp" #include "../app/Assets.hpp" #include "../app/TextureIndex.hpp" @@ -57,10 +58,7 @@ std::vector candidates; bool World::Intersection( const Ray &ray, const glm::mat4 &M, - Chunk *&chunk, - int &blkid, - float &dist, - glm::vec3 &normal + WorldCollision &coll ) { candidates.clear(); @@ -73,37 +71,30 @@ bool World::Intersection( if (candidates.empty()) return false; - chunk = nullptr; - dist = std::numeric_limits::infinity(); - blkid = -1; + coll.chunk = nullptr; + coll.block = -1; + coll.depth = std::numeric_limits::infinity(); for (Candidate &cand : candidates) { - if (cand.dist > dist) continue; - int cur_blkid; - float cur_dist; - glm::vec3 cur_normal; - if (cand.chunk->Intersection(ray, M * cand.chunk->Transform(player->ChunkCoords()), cur_blkid, cur_dist, cur_normal)) { - if (cur_dist < dist) { - chunk = cand.chunk; - blkid = cur_blkid; - dist = cur_dist; - normal = cur_normal; + if (cand.dist > coll.depth) continue; + WorldCollision cur_coll; + if (cand.chunk->Intersection(ray, M * cand.chunk->Transform(player->ChunkCoords()), cur_coll)) { + if (cur_coll.depth < coll.depth) { + coll = cur_coll; } } } - return chunk; + return coll.chunk; } bool World::Intersection( const Ray &ray, const glm::mat4 &M, - Entity *&entity, - float &dist, - glm::vec3 &normal + EntityCollision &coll ) { - entity = nullptr; - dist = std::numeric_limits::infinity(); + coll.entity = nullptr; + coll.depth = std::numeric_limits::infinity(); for (Entity &cur_entity : entities) { // TODO: better check for skipping self (because the check might not be for the player) if (&cur_entity == player) { @@ -113,15 +104,15 @@ bool World::Intersection( glm::vec3 cur_normal; if (blank::Intersection(ray, cur_entity.Bounds(), M * cur_entity.Transform(player->ChunkCoords()), &cur_dist, &cur_normal)) { // TODO: fine grained check goes here? maybe? - if (cur_dist < dist) { - entity = &cur_entity; - dist = cur_dist; - normal = cur_normal; + if (cur_dist < coll.depth) { + coll.entity = &cur_entity; + coll.depth = cur_dist; + coll.normal = cur_normal; } } } - return entity; + return coll.entity; } bool World::Intersection(const Entity &e, std::vector &col) { diff --git a/src/world/World.hpp b/src/world/World.hpp index 6d27d12..a138513 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -15,6 +15,7 @@ namespace blank { class Assets; +class EntityCollision; class Viewport; class WorldCollision; @@ -38,23 +39,21 @@ public: World(const Assets &, const Config &, const WorldSave &); - // check if this ray hits a block + /// check if this ray hits a block + /// depth in the collision is the distance between the ray's + /// origin and the intersection point bool Intersection( const Ray &, const glm::mat4 &M, - Chunk *&chunk, - int &blkid, - float &dist, - glm::vec3 &normal); + WorldCollision &); - // check if this ray hits an entity + /// check if this ray hits an entity bool Intersection( const Ray &, const glm::mat4 &M, - Entity *&entity, - float &dist, - glm::vec3 &normal); + EntityCollision &); + /// check if given entity intersects with the world bool Intersection(const Entity &e, std::vector &); void Resolve(Entity &e, std::vector &); diff --git a/src/world/WorldCollision.hpp b/src/world/WorldCollision.hpp index ebf01c1..3c81595 100644 --- a/src/world/WorldCollision.hpp +++ b/src/world/WorldCollision.hpp @@ -11,18 +11,33 @@ namespace blank { struct WorldCollision { - const Chunk *chunk; + Chunk *chunk; int block; float depth; glm::vec3 normal; - WorldCollision(const Chunk *c, int b, float d, const glm::vec3 &n) + WorldCollision() + : chunk(nullptr), block(-1), depth(0.0f), normal(0.0f) { } + WorldCollision(Chunk *c, int b, float d, const glm::vec3 &n) : chunk(c), block(b), depth(d), normal(n) { } + /// check if an actual collision + operator bool() const noexcept { return chunk; } + + // following only valid if test true + Chunk &GetChunk() noexcept { return *chunk; } + const Chunk &GetChunk() const noexcept { return *chunk; } + const Block &GetBlock() const noexcept { return GetChunk().BlockAt(block); } + const BlockType &GetType() const noexcept { return GetChunk().Type(GetBlock()); } + + void SetBlock(const Block &b) noexcept { GetChunk().SetBlock(block, b); } + bool Blocks() const noexcept { return chunk->Type(block).collide_block; } + glm::vec3 BlockPos() const noexcept { return Chunk::ToPos(block); } glm::vec3 BlockCoords() const noexcept { return Chunk::ToCoords(block); } + glm::mat4 BlockTransform() const noexcept { return GetChunk().ToTransform(BlockPos(), block); } }; diff --git a/src/world/chunk.cpp b/src/world/chunk.cpp index a6decf7..83c94e3 100644 --- a/src/world/chunk.cpp +++ b/src/world/chunk.cpp @@ -440,13 +440,12 @@ void Chunk::Draw() noexcept { bool Chunk::Intersection( const Ray &ray, const glm::mat4 &M, - int &blkid, - float &dist, - glm::vec3 &normal -) const noexcept { + WorldCollision &coll +) noexcept { int idx = 0; - blkid = -1; - dist = std::numeric_limits::infinity(); + coll.chunk = this; + coll.block = -1; + coll.depth = std::numeric_limits::infinity(); for (int z = 0; z < depth; ++z) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x, ++idx) { @@ -457,20 +456,20 @@ bool Chunk::Intersection( float cur_dist; glm::vec3 cur_norm; if (type.shape->Intersects(ray, M * ToTransform(Pos(x, y, z), idx), cur_dist, cur_norm)) { - if (cur_dist < dist) { - blkid = idx; - dist = cur_dist; - normal = cur_norm; + if (cur_dist < coll.depth) { + coll.block = idx; + coll.depth = cur_dist; + coll.normal = cur_norm; } } } } } - if (blkid < 0) { + if (coll.block < 0) { return false; } else { - normal = glm::vec3(BlockAt(blkid).Transform() * glm::vec4(normal, 0.0f)); + coll.normal = glm::vec3(BlockAt(coll.block).Transform() * glm::vec4(coll.normal, 0.0f)); return true; } } @@ -480,7 +479,7 @@ bool Chunk::Intersection( const glm::mat4 &Mbox, const glm::mat4 &Mchunk, std::vector &col -) const noexcept { +) noexcept { bool any = false; float penetration; glm::vec3 normal; -- 2.39.2