summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/Entities/Player.cpp31
-rw-r--r--src/Entities/Player.hpp3
-rw-r--r--src/World/ChunkRegistry.hpp4
-rw-r--r--src/World/VoxelTraversal.hpp60
-rw-r--r--src/World/World.cpp11
-rw-r--r--src/World/World.hpp12
7 files changed, 115 insertions, 7 deletions
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<AABB> 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 <typename P>
+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<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 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<ChunkRegistry::Data*> get_visible_chunks(Position::World position);
 
     Chunk::BlockData block_at(Position::BlockWorld pos);
+    void break_block(Position::BlockWorld pos);
+
+    template <typename F>
+    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;