From 955fbb45dedb570520fc45d2ce69f420bed2ad08 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Fri, 26 Jun 2015 12:39:31 +0200 Subject: [PATCH] entity/world collision response --- TODO | 14 ++++++-- running | 2 ++ src/app/FPSController.hpp | 3 ++ src/ui/Interface.hpp | 2 ++ src/ui/ui.cpp | 9 +++++ src/world/BlockType.hpp | 8 +++++ src/world/World.cpp | 64 +++++++++++++++++++++++++++++++++--- src/world/World.hpp | 2 +- src/world/WorldCollision.hpp | 9 +++-- src/world/block.cpp | 2 ++ src/world/chunk.cpp | 2 +- 11 files changed, 106 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index 7b481da..a0c8c1d 100644 --- a/TODO +++ b/TODO @@ -48,6 +48,9 @@ entity ai that as the light power for the directional lighting shader and use a direction that's fixed relative to the camera? + there's a bug where a chunk's model is not updated if its neighbor + changes border light levels + gravity maybe like light levels? should also store a direction with it in @@ -70,6 +73,10 @@ chunk traversal profiling indicates that this is not neccessary atm. maybe it will when there's some more action in the world + I got a segfault (sadly in release mode, so no sensible trace, but + it was in ChunkLoader::Update). I suspect it has something to do + with how chunks are relinked after a near death experience. + transparency (blocks and entities) transparent blocks because awesome @@ -81,9 +88,10 @@ world generator that is not boring entity/world collision - entities should be stopped from entering solid parts of the world - - also, current ray/obb intersection test sucks + first draft of entity/world collision is implemented + it jitters and has some surprising behaviour + finding a spawn point which doesn't put entities in solids is + now a little more crucial. press N if you're in trouble better noise diff --git a/running b/running index 7bc9a0e..6764224 100644 --- a/running +++ b/running @@ -61,3 +61,5 @@ front, and back) and E changes the turn (none, left, around, and right). Pressing B prints details about the block you're pointing at and P prints info about the active block. L spits out the player position and light level there. C dumps info about the chunk of the pointed at block. + +Press N to toggle player/world collision. diff --git a/src/app/FPSController.hpp b/src/app/FPSController.hpp index 01c7451..a7b11a4 100644 --- a/src/app/FPSController.hpp +++ b/src/app/FPSController.hpp @@ -18,6 +18,9 @@ class FPSController { public: explicit FPSController(Entity &) noexcept; + Entity &Controlled() noexcept { return entity; } + const Entity &Controlled() const noexcept { return entity; } + /// get position and face direction of controlled entity Ray Aim() const noexcept { return entity.Aim(entity.ChunkCoords()); } diff --git a/src/ui/Interface.hpp b/src/ui/Interface.hpp index 0c140fb..a08a0bf 100644 --- a/src/ui/Interface.hpp +++ b/src/ui/Interface.hpp @@ -44,6 +44,8 @@ public: void FaceBlock(); void TurnBlock(); + void ToggleCollision(); + void PickBlock(); void PlaceBlock(); void RemoveBlock() noexcept; diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index c71b7c1..ea9786b 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -128,6 +128,10 @@ void Interface::HandlePress(const SDL_KeyboardEvent &event) { TurnBlock(); break; + case SDLK_n: + ToggleCollision(); + break; + case SDLK_b: PrintBlockInfo(); break; @@ -178,6 +182,11 @@ void Interface::TurnBlock() { hud.Display(selection); } +void Interface::ToggleCollision() { + ctrl.Controlled().WorldCollidable(!ctrl.Controlled().WorldCollidable()); + std::cout << "collision " << (ctrl.Controlled().WorldCollidable() ? "on" : "off") << std::endl; +} + void Interface::PrintBlockInfo() { std::cout << std::endl; if (!aim_chunk) { diff --git a/src/world/BlockType.hpp b/src/world/BlockType.hpp index 344bec7..28f814b 100644 --- a/src/world/BlockType.hpp +++ b/src/world/BlockType.hpp @@ -22,11 +22,19 @@ struct BlockType { Block::Type id; + // light level that blocks of this type emit int luminosity; + // whether to draw bool visible; + // if true, stops light from propagating and fixes level to luminosity bool block_light; + // whether to check for collisions at all + bool collision; + // if the block should be impenetrable + bool collide_block; + struct Faces { bool face[Block::FACE_COUNT]; Faces &operator =(const Faces &other) noexcept { diff --git a/src/world/World.cpp b/src/world/World.cpp index cb8186d..d51520e 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -29,18 +30,24 @@ World::World(const Config &config) { // white block BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = block_fill; blockType.Add(type); } { // white slab BlockType type(true, { 1.0f, 1.0f, 1.0f }, &slabShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = slab_fill; blockType.Add(type); } { // white stair BlockType type(true, { 1.0f, 1.0f, 1.0f }, &stairShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = stair_fill; blockType.Add(type); } @@ -48,18 +55,24 @@ World::World(const Config &config) { // red block BlockType type(true, { 1.0f, 0.0f, 0.0f }, &blockShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = block_fill; blockType.Add(type); } { // red slab BlockType type(true, { 1.0f, 0.0f, 0.0f }, &slabShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = slab_fill; blockType.Add(type); } { // red stair BlockType type(true, { 1.0f, 0.0f, 0.0f }, &stairShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = stair_fill; blockType.Add(type); } @@ -67,18 +80,24 @@ World::World(const Config &config) { // green block BlockType type(true, { 0.0f, 1.0f, 0.0f }, &blockShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = block_fill; blockType.Add(type); } { // green slab BlockType type(true, { 0.0f, 1.0f, 0.0f }, &slabShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = slab_fill; blockType.Add(type); } { // green stair BlockType type(true, { 0.0f, 1.0f, 0.0f }, &stairShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = stair_fill; blockType.Add(type); } @@ -86,18 +105,24 @@ World::World(const Config &config) { // blue block BlockType type(true, { 0.0f, 0.0f, 1.0f }, &blockShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = block_fill; blockType.Add(type); } { // blue slab BlockType type(true, { 0.0f, 0.0f, 1.0f }, &slabShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = slab_fill; blockType.Add(type); } { // blue stair BlockType type(true, { 0.0f, 0.0f, 1.0f }, &stairShape); type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = stair_fill; blockType.Add(type); } @@ -106,6 +131,8 @@ World::World(const Config &config) BlockType type(true, { 1.0f, 1.0f, 0.0f }, &blockShape); type.luminosity = 15; type.block_light = true; + type.collision = true; + type.collide_block = true; type.fill = block_fill; blockType.Add(type); } @@ -179,14 +206,15 @@ bool World::Intersection( bool World::Intersection(const Entity &e, std::vector &col) { AABB box = e.Bounds(); glm::mat4 M = e.Transform(player->ChunkCoords()); + bool any = false; // TODO: this only needs to check the chunks surrounding the entity's chunk position // need find out if that is quicker than the rough chunk bounds test for (Chunk &cur_chunk : chunks.Loaded()) { if (cur_chunk.Intersection(box, M, cur_chunk.Transform(player->ChunkCoords()), col)) { - return true; + any = true; } } - return false; + return any; } @@ -221,8 +249,36 @@ void World::Update(int dt) { chunks.Update(dt); } -void World::Resolve(const Entity &e, std::vector &col) { - std::cout << e.Name() << " entity intersects world at " << col.size() << " blocks" << std::endl; +void World::Resolve(Entity &e, std::vector &col) { + // determine displacement for each cardinal axis and move entity accordingly + glm::vec3 min_disp(0.0f); + glm::vec3 max_disp(0.0f); + for (const WorldCollision &c : col) { + if (!c.Blocks()) continue; + glm::vec3 local_disp(c.normal * c.depth); + // swap if neccessary (normal may point away from the entity) + if (dot(c.normal, e.Position() - c.BlockCoords()) < 0) { + local_disp *= -1; + } + min_disp = min(min_disp, local_disp); + max_disp = max(max_disp, local_disp); + } + // for each axis + // if only one direction is set, use that as the final + // if both directions are set, use average + glm::vec3 final_disp(0.0f); + for (int axis = 0; axis < 3; ++axis) { + if (std::abs(min_disp[axis]) > std::numeric_limits::epsilon()) { + if (std::abs(max_disp[axis]) > std::numeric_limits::epsilon()) { + final_disp[axis] = (min_disp[axis] + max_disp[axis]) * 0.5f; + } else { + final_disp[axis] = min_disp[axis]; + } + } else if (std::abs(max_disp[axis]) > std::numeric_limits::epsilon()) { + final_disp[axis] = max_disp[axis]; + } + } + e.Move(final_disp); } diff --git a/src/world/World.hpp b/src/world/World.hpp index 183fa60..9ec4cb2 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -47,7 +47,7 @@ public: glm::vec3 &normal); bool Intersection(const Entity &e, std::vector &); - void Resolve(const Entity &e, std::vector &); + void Resolve(Entity &e, std::vector &); BlockTypeRegistry &BlockTypes() { return blockType; } diff --git a/src/world/WorldCollision.hpp b/src/world/WorldCollision.hpp index eb2e5e2..ebf01c1 100644 --- a/src/world/WorldCollision.hpp +++ b/src/world/WorldCollision.hpp @@ -1,13 +1,14 @@ #ifndef BLANK_WORLD_WORLDCOLLISION_HPP_ #define BLANK_WORLD_WORLDCOLLISION_HPP_ +#include "BlockType.hpp" +#include "Chunk.hpp" + #include namespace blank { -class Chunk; - struct WorldCollision { const Chunk *chunk; @@ -19,6 +20,10 @@ struct WorldCollision { WorldCollision(const Chunk *c, int b, float d, const glm::vec3 &n) : chunk(c), block(b), depth(d), normal(n) { } + bool Blocks() const noexcept { return chunk->Type(block).collide_block; } + + glm::vec3 BlockCoords() const noexcept { return Chunk::ToCoords(block); } + }; } diff --git a/src/world/block.cpp b/src/world/block.cpp index aabb2b1..78aa9d8 100644 --- a/src/world/block.cpp +++ b/src/world/block.cpp @@ -81,6 +81,8 @@ BlockType::BlockType(bool v, const glm::vec3 &col, const Shape *s) noexcept , luminosity(0) , visible(v) , block_light(false) +, collision(false) +, collide_block(false) , fill({ false, false, false, false, false, false }) { } diff --git a/src/world/chunk.cpp b/src/world/chunk.cpp index 2c5e7ec..a65f5c3 100644 --- a/src/world/chunk.cpp +++ b/src/world/chunk.cpp @@ -500,7 +500,7 @@ bool Chunk::Intersection( for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x, ++idx) { const BlockType &type = Type(idx); - if (!type.visible) { + if (!type.collision) { continue; } if (type.shape->Intersects(Mchunk * ToTransform(Pos(x, y, z), idx), box, Mbox, penetration, normal)) { -- 2.39.2