From 250e37c742f3ad46f093f4534098cdf8f68a29a9 Mon Sep 17 00:00:00 2001 From: Mel Date: Fri, 2 Feb 2024 03:03:36 +0100 Subject: Breaking blocks --- CMakeLists.txt | 1 + src/Entities/Player.cpp | 31 +++++++++++++++++++---- src/Entities/Player.hpp | 3 ++- src/World/ChunkRegistry.hpp | 4 ++- src/World/VoxelTraversal.hpp | 60 ++++++++++++++++++++++++++++++++++++++++++++ src/World/World.cpp | 11 ++++++++ src/World/World.hpp | 12 +++++++++ 7 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 src/World/VoxelTraversal.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e98c87c..2c14505 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(meowcraft src/Common/Lambda.hpp src/Math/Functions.hpp src/Common/Casts.hpp + src/World/VoxelTraversal.hpp ) target_link_libraries(meowcraft glfw GLEW::GLEW) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 66f08c6..a8abb1d 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -23,6 +23,18 @@ void Player::update(const Time& time, GFX::Window& window, GFX::Camera& camera, rotate(rotational_input(window)); update_camera_position(camera); + + if (window.mouse(GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS)) { + auto look_transform = camera_transform(); + auto look_direction = -look_transform.forward(); // Why does this need to be inverted? + auto block = world.traverse( + Ray{look_transform.position(), look_direction}, + 4.0, + [](auto b) { return b.type.is_solid(); } + ); + + if (block != Position::BlockWorld{}) world.break_block(block); + } } void Player::move(Position::WorldOffset by) { @@ -203,12 +215,21 @@ std::vector Player::terrain_collision_domain( return colliding_blocks; } -void Player::update_camera_position(GFX::Camera& camera) { - auto camera_position = m_transform.position(); - camera_position.y() += 1.5; +Transform Player::camera_transform() const { + auto position = m_transform.position(); + position.y() += 1.5; + return { + position, + m_transform.rotation(), + m_transform.scale(), + }; +} + +void Player::update_camera_position(GFX::Camera& camera) const { + auto cam_transform = camera_transform(); - camera.set_position(camera_position); - camera.set_angles(m_transform.rotation()); + camera.set_position(cam_transform.position()); + camera.set_angles(cam_transform.rotation()); } Vec3 Player::directional_input(GFX::Window& window) { diff --git a/src/Entities/Player.hpp b/src/Entities/Player.hpp index 5d70559..b1ec8e7 100644 --- a/src/Entities/Player.hpp +++ b/src/Entities/Player.hpp @@ -40,7 +40,8 @@ private: Vec3 walking_velocity(GFX::Window& window, const Time& time, Vec3 input_direction); Vec3 flying_velocity(GFX::Window& window, const Time& time, Vec3 input_direction); - void update_camera_position(GFX::Camera& camera); + Transform camera_transform() const; + void update_camera_position(GFX::Camera& camera) const; static Vec3 directional_input(GFX::Window& window); static Rotation rotational_input(GFX::Window& window); diff --git a/src/World/ChunkRegistry.hpp b/src/World/ChunkRegistry.hpp index ea887e4..2b87a3a 100644 --- a/src/World/ChunkRegistry.hpp +++ b/src/World/ChunkRegistry.hpp @@ -34,7 +34,9 @@ public: } void damage() { - if (status == Status::Done) chunk.value().damage(); + // if (status == Status::Done) chunk.value().damage(); + // TODO: Properly damage the chunk, notifying the nearby chunks, too. + status = Status::NeedsReification; } }; diff --git a/src/World/VoxelTraversal.hpp b/src/World/VoxelTraversal.hpp new file mode 100644 index 0000000..1638901 --- /dev/null +++ b/src/World/VoxelTraversal.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "../Common/Casts.hpp" +#include "../Math/Ray.hpp" +#include "Position.hpp" + +namespace MC::World::VoxelTraversal { + +// Amanatides, John, and Andrew Woo. 1987. +// "A Fast Voxel Traversal Algorithm for Ray Tracing." +// https://doi.org/10.2312/EGTP.19871000. +template +Position::BlockWorld traverse(Ray ray, Real max_distance, P&& predicate) { + // 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 = { + Math::sign(ray.direction.x()), + Math::sign(ray.direction.y()), + Math::sign(ray.direction.z()), + }; + + Position::WorldOffset t_max = { + (TO(Real, block_pos.x()) + TO(Real, step.x() > 0) - ray.origin.x()) / ray.direction.x(), + (TO(Real, block_pos.y()) + TO(Real, step.y() > 0) - ray.origin.y()) / ray.direction.y(), + (TO(Real, block_pos.z()) + TO(Real, step.z() > 0) - ray.origin.z()) / ray.direction.z() + }; + + Position::WorldOffset const t_delta = { + TO(Real, step.x()) / ray.direction.x(), + TO(Real, step.y()) / ray.direction.y(), + TO(Real, step.z()) / ray.direction.z() + }; + + while (!predicate(block_pos)) { + // TODO: Calculate distance exactly. + if (ray.origin.distance(Vec3(block_pos)) > 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(); + } else { + block_pos.z() += step.z(); + t_max.z() += t_delta.z(); + } + } else { + if (t_max.y() < t_max.z()) { + block_pos.y() += step.y(); + t_max.y() += t_delta.y(); + } else { + block_pos.z() += step.z(); + t_max.z() += t_delta.z(); + } + } + } + + return block_pos; +} + +} diff --git a/src/World/World.cpp b/src/World/World.cpp index ce15ae8..cc5d91f 100644 --- a/src/World/World.cpp +++ b/src/World/World.cpp @@ -47,6 +47,17 @@ Chunk::BlockData World::block_at(Position::BlockWorld pos) { return {}; } +void World::break_block(Position::BlockWorld pos) { + 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 = {}; + chunk_data.damage(); +} + std::vector 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 3dbd720..af49378 100644 --- a/src/World/World.hpp +++ b/src/World/World.hpp @@ -4,8 +4,10 @@ #include "ChunkIndex.hpp" #include "ChunkRegistry.hpp" #include "../Compute/Queue.hpp" +#include "../Math/Ray.hpp" #include "Generation/ChunkMeshing.hpp" #include "Generation/Lighting.hpp" +#include "VoxelTraversal.hpp" namespace MC::World { @@ -14,6 +16,16 @@ public: std::vector get_visible_chunks(Position::World position); Chunk::BlockData block_at(Position::BlockWorld pos); + void break_block(Position::BlockWorld pos); + + template + Position::BlockWorld traverse(Ray ray, Real max_distance, F&& predicate) { + return VoxelTraversal::traverse( + ray, + max_distance, + [this, &predicate](Position::BlockWorld pos) { return predicate(block_at(pos)); } + ); + } Real get_average_chunk_time() const; -- cgit 1.4.1