diff options
Diffstat (limited to 'src/World')
| -rw-r--r-- | src/World/Chunk.cpp | 5 | ||||
| -rw-r--r-- | src/World/Chunk.hpp | 1 | ||||
| -rw-r--r-- | src/World/ChunkIndex.hpp | 2 | ||||
| -rw-r--r-- | src/World/Position.hpp | 25 | ||||
| -rw-r--r-- | src/World/VoxelTraversal.hpp | 37 | ||||
| -rw-r--r-- | src/World/World.cpp | 11 | ||||
| -rw-r--r-- | src/World/World.hpp | 9 |
7 files changed, 78 insertions, 12 deletions
diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp index 6cb67bb..b4a9ece 100644 --- a/src/World/Chunk.cpp +++ b/src/World/Chunk.cpp @@ -50,4 +50,9 @@ AABB Chunk::block_bounds(Position::BlockLocal pos) { }; } +AABB Chunk::block_bounds(Position::BlockWorld pos) { + auto chunk_position = ChunkIndex::from_position(pos).world_position(); + return block_bounds(pos.to_local()).offset(Vec3(chunk_position)); +} + } diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp index 9a29284..bab20f2 100644 --- a/src/World/Chunk.hpp +++ b/src/World/Chunk.hpp @@ -68,6 +68,7 @@ public: static Bool is_valid_position(Position::BlockLocal pos); static AABB block_bounds(Position::BlockLocal pos); + static AABB block_bounds(Position::BlockWorld pos); private: static U64 pos(U32 x, U32 y, U32 z); diff --git a/src/World/ChunkIndex.hpp b/src/World/ChunkIndex.hpp index da23e3d..1e2b9ac 100644 --- a/src/World/ChunkIndex.hpp +++ b/src/World/ChunkIndex.hpp @@ -18,7 +18,7 @@ struct ChunkIndex { Position::BlockWorld local_to_world_position(Position::BlockLocal local) const { using namespace ChunkDimensions; - return {x * Width + local.x(), local.y(), y * Width + local.z()}; + return {TO(I64, x) * Width + local.x(), local.y(), TO(I64, y) * Width + local.z()}; } Position::BlockWorld world_position() const { diff --git a/src/World/Position.hpp b/src/World/Position.hpp index f2915aa..f9b8ad1 100644 --- a/src/World/Position.hpp +++ b/src/World/Position.hpp @@ -2,6 +2,7 @@ #include <array> #include <algorithm> +#include "../Common/Casts.hpp" #include "ChunkDimensions.hpp" #include "../Math/Common.hpp" #include "../Math/Vector.hpp" @@ -19,6 +20,18 @@ class BlockLocalOffset : public Vector<3, I16> { public: MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(BlockLocalOffset, I16) + // Create a block local offset from a unit normal, + // which is a vector with exactly one component being 1 or -1, + // with the rest being 0. + template<typename T> + static BlockLocalOffset from_unit_normal(Vector<3, T> normal) { + return { + TO(I16, normal.x()), + TO(I16, normal.y()), + TO(I16, normal.z()) + }; + } + Bool fits_within_chunk() const { using namespace MC::World::ChunkDimensions; return x() >= 0 && x() < Width && y() >= 0 && y() < Height && z() >= 0 && z() < Width; @@ -44,6 +57,18 @@ public: class BlockWorldOffset : public Vector<3, I64> { public: MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(BlockWorldOffset, I64) + + // Create a block world offset from a unit normal, + // which is a vector with exactly one component being 1 or -1, + // with the rest being 0. + template<typename T> + static BlockWorldOffset from_unit_normal(Vector<3, T> normal) { + return { + TO(I64, normal.x()), + TO(I64, normal.y()), + TO(I64, normal.z()) + }; + } }; // Position of a block within entire world. diff --git a/src/World/VoxelTraversal.hpp b/src/World/VoxelTraversal.hpp index 1638901..08a53eb 100644 --- a/src/World/VoxelTraversal.hpp +++ b/src/World/VoxelTraversal.hpp @@ -6,11 +6,22 @@ namespace MC::World::VoxelTraversal { +struct TraversalResult { + // Whether the ray hit a block. + Bool hit{}; + // The block that was hit. + Position::BlockWorld block; + // The vector from the ray origin to where the block that was hit. + Position::WorldOffset approach; + // The normal of the collision. + Position::BlockWorldOffset normal; +}; + // Amanatides, John, and Andrew Woo. 1987. // "A Fast Voxel Traversal Algorithm for Ray Tracing." // https://doi.org/10.2312/EGTP.19871000. template <typename P> -Position::BlockWorld traverse(Ray ray, Real max_distance, P&& predicate) { +TraversalResult traverse(Ray ray, P&& predicate, Real max_distance = 0) { // Find the voxel grid cell containing the origin of the ray. Position::BlockWorld block_pos = Position::World(ray.origin).round_to_block(); Position::BlockWorldOffset const step = { @@ -31,30 +42,48 @@ Position::BlockWorld traverse(Ray ray, Real max_distance, P&& predicate) { TO(Real, step.z()) / ray.direction.z() }; + // The original algorithm does not mention calculating the normal of the + // block that was hit. When we increment t_max to a collision on one of the axes + // of the voxel grid, the axis gives us the normal of the current block. + Position::BlockWorldOffset normal = {}; + + // Also not mentioned in the original algorithm, but we need to keep track of + // how far along we have traversed the voxel grid. + // This just means we add the direction of the ray at the axis we have progressed along. + Position::WorldOffset approach = {}; + while (!predicate(block_pos)) { - // TODO: Calculate distance exactly. - if (ray.origin.distance(Vec3(block_pos)) > max_distance) return {}; + if (max_distance > 0 && approach.magnitude() > max_distance) + return {}; if (t_max.x() < t_max.y()) { if (t_max.x() < t_max.z()) { block_pos.x() += step.x(); t_max.x() += t_delta.x(); + normal = {-step.x(), 0, 0}; + approach.x() += ray.direction.x(); } else { block_pos.z() += step.z(); t_max.z() += t_delta.z(); + normal = {0, 0, -step.z()}; + approach.z() += ray.direction.z(); } } else { if (t_max.y() < t_max.z()) { block_pos.y() += step.y(); t_max.y() += t_delta.y(); + normal = {0, -step.y(), 0}; + approach.y() += ray.direction.y(); } else { block_pos.z() += step.z(); t_max.z() += t_delta.z(); + normal = {0, 0, -step.z()}; + approach.z() += ray.direction.z(); } } } - return block_pos; + return {true, block_pos, approach, normal}; } } diff --git a/src/World/World.cpp b/src/World/World.cpp index cc5d91f..2b5848b 100644 --- a/src/World/World.cpp +++ b/src/World/World.cpp @@ -47,17 +47,20 @@ Chunk::BlockData World::block_at(Position::BlockWorld pos) { return {}; } -void World::break_block(Position::BlockWorld pos) { +void World::set_block(Position::BlockWorld pos, BlockType type) { auto& chunk_data = m_registry.find(pos); if (!chunk_data.chunk.has_value()) return; auto& block_data = chunk_data.chunk->at(pos.to_local()); - if (!block_data.type.is_solid()) return; - - block_data.type = {}; + block_data.type = type; chunk_data.damage(); } +void World::break_block(Position::BlockWorld pos) { + if (!block_at(pos).type.is_solid()) return; + set_block(pos, BlockType::Air); +} + std::vector<ChunkIndex> World::get_visible_chunk_indices(const Position::World position) const { ChunkIndex center = ChunkIndex::from_position(position.round_to_block()); diff --git a/src/World/World.hpp b/src/World/World.hpp index af49378..76005da 100644 --- a/src/World/World.hpp +++ b/src/World/World.hpp @@ -16,14 +16,17 @@ public: std::vector<ChunkRegistry::Data*> get_visible_chunks(Position::World position); Chunk::BlockData block_at(Position::BlockWorld pos); + void set_block(Position::BlockWorld pos, BlockType type); void break_block(Position::BlockWorld pos); + using TraversalResult = VoxelTraversal::TraversalResult; + template <typename F> - Position::BlockWorld traverse(Ray ray, Real max_distance, F&& predicate) { + TraversalResult traverse(Ray ray, F&& predicate, Real max_distance = 0) { return VoxelTraversal::traverse( ray, - max_distance, - [this, &predicate](Position::BlockWorld pos) { return predicate(block_at(pos)); } + [this, &predicate](Position::BlockWorld pos) { return predicate(block_at(pos)); }, + max_distance ); } |
