From 7562a79fa214245544cb379423314c3a0766a7f2 Mon Sep 17 00:00:00 2001 From: Mel Date: Mon, 14 Aug 2023 03:40:10 +0200 Subject: Generate AABBs for terrain that collides with Player --- src/Common/FlexArray.hpp | 85 +++++++++++++++++++++++++++++++++++++ src/Entities/Player.cpp | 17 +++++--- src/Entities/Player.hpp | 4 +- src/World/Chunk.cpp | 15 ++++--- src/World/Chunk.hpp | 6 +-- src/World/Generation/Decoration.cpp | 6 +-- src/World/Generation/Decoration.hpp | 2 +- 7 files changed, 116 insertions(+), 19 deletions(-) create mode 100644 src/Common/FlexArray.hpp (limited to 'src') diff --git a/src/Common/FlexArray.hpp b/src/Common/FlexArray.hpp new file mode 100644 index 0000000..4a648b6 --- /dev/null +++ b/src/Common/FlexArray.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include "Sizes.hpp" + +template +class FlexArray { +public: + FlexArray() = default; + + FlexArray& operator=(const FlexArray& other) { + m_count = other.m_count; + m_array = other.m_array; + return *this; + } + + T& at(USize index) { + assure_in_bounds(index); + return m_array.at(index); + } + const T& at(USize index) const { + assure_in_bounds(index); + return m_array.at(index); + } + + T& operator[](USize index) { + assure_in_bounds(index); // STL doesn't do bounds-checks here, and it led to many headaches :( + return m_array[index]; + } + const T& operator[](USize index) const { + assure_in_bounds(index); + return m_array[index]; + } + + T& front() { return at(0); } + const T& front() const { return at(0); } + + T& back() { return at(m_count - 1); } + const T& back() const { return at(m_count - 1); } + + const T* data() const { return m_array.data(); } + T* data() { return m_array.data(); } + + std::array& array() { return m_array; } + const std::array& array() const { return m_array; } + + T* begin() { return m_array.begin(); } + T* end() { return m_array.begin() + m_count; } + + Bool empty() const { return m_count == 0; } + Bool full() const { return m_count == S; } + + USize size() const { return m_count; } + USize max_size() const { return S; } + + void clear() { m_count = 0; m_array = {}; } + + void push_back(const T& value) { + if (m_count >= S) throw std::out_of_range("flex array is full"); + m_array[m_count] = value; + m_count++; + } + + template + void emplace_back(Args&&... args) { + if (full()) throw std::out_of_range("flex array is full"); + m_array[m_count] = T{std::forward(args)...}; + m_count++; + } + + void pop_back() { + if (empty()) throw std::out_of_range("flex array is empty"); + m_array[m_count - 1] = {}; + m_count--; + } + +private: + void assure_in_bounds(USize index) const { + if (index >= m_count) throw std::out_of_range("flex array index out of range"); + } + + USize m_count = 0; + std::array m_array{}; +}; + diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 175b59e..6c94490 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -18,7 +18,8 @@ void Player::update(const Time& time, GFX::Window& window, GFX::Camera& camera, auto direction = m_transform.right() * x + Vec3(0, y, 0) + m_transform.forward() * z; auto position = m_transform.position() + direction * move_speed; - if (!collides_with_terrain(position, world)) move_to(position); + auto collisions = colliding_terrain(position, world); + if (collisions.empty()) move_to(position); rotate({r.y() * rotation_speed, r.x() * rotation_speed, 0.0f}); update_camera_position(camera); @@ -53,18 +54,24 @@ void Player::update_camera_position(GFX::Camera& camera) { camera.set_angles(m_transform.rotation()); } -Bool Player::collides_with_terrain(Position::World new_position, World::World& world) { - auto corners = bounding_box_for_position(new_position).corners(); +FlexArray Player::colliding_terrain( + Position::World new_position, + World::World& world +) { + FlexArray colliding_blocks; + auto corners = bounding_box_for_position(new_position).corners(); for (auto corner : corners) { auto block_position = Position::World(corner).round_to_block(); if (block_position.y() < 0 || block_position.y() >= World::Chunk::Height) continue; auto block = world.block_at(block_position); - if (!block.empty()) return true; + if (!block.empty()) { + colliding_blocks.push_back(World::Chunk::block_bounds(block_position.to_local())); + } } - return false; + return colliding_blocks; } AABB Player::bounding_box_for_position(Position::World position) { diff --git a/src/Entities/Player.hpp b/src/Entities/Player.hpp index 86d8c90..0422509 100644 --- a/src/Entities/Player.hpp +++ b/src/Entities/Player.hpp @@ -1,5 +1,6 @@ #pragma once +#include "../Common/FlexArray.hpp" #include "../Time.hpp" #include "../Transform.hpp" #include "../GFX/Camera.hpp" @@ -27,7 +28,8 @@ public: private: void update_camera_position(GFX::Camera& camera); - static Bool collides_with_terrain(Position::World new_position, World::World& world); + static constexpr UInt MaxCollidingTerrain = 34; + static FlexArray colliding_terrain(Position::World new_position, World::World& world); // Creates a bounding box where `position` is at the center of the bottom face. static AABB bounding_box_for_position(Position::World position); diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp index 8b204c0..6cb67bb 100644 --- a/src/World/Chunk.cpp +++ b/src/World/Chunk.cpp @@ -19,10 +19,6 @@ Chunk::BlockData& Chunk::at(Position::BlockLocal pos) { return at(pos.x(), pos.y(), pos.z()); } -Bool Chunk::is_empty(U32 x, U32 y, U32 z) const { - return at(x, y, z).empty(); -} - ChunkIndex Chunk::index() const { return m_index; } @@ -39,12 +35,19 @@ void Chunk::damage() { m_damaged = true; } -Bool Chunk::is_valid_position(U32 x, U32 y, U32 z) { - return x < Width && y < Height && z < Width; +Bool Chunk::is_valid_position(Position::BlockLocal pos) { + return pos.x() < Width && pos.y() < Height && pos.z() < Width; } U64 Chunk::pos(U32 x, U32 y, U32 z) { return x + Width * y + Width * Height * z; } +AABB Chunk::block_bounds(Position::BlockLocal pos) { + return { + {pos.x(), pos.y(), pos.z()}, + {pos.x() + 1, pos.y() + 1, pos.z() + 1}, + }; +} + } diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp index 0b00c97..ed3220d 100644 --- a/src/World/Chunk.hpp +++ b/src/World/Chunk.hpp @@ -1,6 +1,7 @@ #pragma once #include "../Common/Sizes.hpp" +#include "../Math/AABB.hpp" #include "ChunkDimensions.hpp" #include "BiomeType.hpp" #include "BlockType.hpp" @@ -35,8 +36,6 @@ public: const BlockData& at(Position::BlockLocal pos) const; BlockData& at(Position::BlockLocal pos); - Bool is_empty(U32 x, U32 y, U32 z) const; - struct Details { Matrix landmass_values{}; Matrix hill_values{}; @@ -55,7 +54,8 @@ public: Bool is_damaged() const; void damage(); - static Bool is_valid_position(U32 x, U32 y, U32 z); + static Bool is_valid_position(Position::BlockLocal pos); + static AABB block_bounds(Position::BlockLocal pos); private: static U64 pos(U32 x, U32 y, U32 z); diff --git a/src/World/Generation/Decoration.cpp b/src/World/Generation/Decoration.cpp index 556b5e7..0623c05 100644 --- a/src/World/Generation/Decoration.cpp +++ b/src/World/Generation/Decoration.cpp @@ -4,11 +4,11 @@ namespace MC::World::Generation { void Decorator::put_block(Chunk& chunk, Pos pos, BlockType block) { - if (!Chunk::is_valid_position(pos.x(), pos.y(), pos.z())) { + if (!Chunk::is_valid_position(pos)) { return; } - auto& place = chunk.at(pos.x(), pos.y(), pos.z()); + auto& place = chunk.at(pos); if (place.empty()) { place = {block}; } @@ -88,7 +88,7 @@ void TreeDecorator::draw_tree(Chunk& chunk, Pos pos) const { } } -Bool TreeDecorator::is_valid_position(Vector<3, UInt> pos) { +Bool TreeDecorator::is_valid_position(Pos pos) { Int tree_radius = s_tree_radius; return (Int)pos.x() - tree_radius >= 0 && pos.x() + tree_radius <= Chunk::Width diff --git a/src/World/Generation/Decoration.hpp b/src/World/Generation/Decoration.hpp index 2f119ff..c157688 100644 --- a/src/World/Generation/Decoration.hpp +++ b/src/World/Generation/Decoration.hpp @@ -7,7 +7,7 @@ namespace MC::World::Generation { class Decorator { public: - using Pos = Vector<3, UInt>; + using Pos = Position::BlockLocal; virtual ~Decorator() = default; -- cgit 1.4.1