summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-08-06 04:27:07 +0200
committerMel <einebeere@gmail.com>2023-08-06 04:27:07 +0200
commit5a1b126f1f6d55226c2b5068d0c17c428fd29ba8 (patch)
tree3acc0240cd8dedd0764eeae6df04e134b04584c8 /src
parente6f5f9e03f673db796f1babb308609ca2576db2f (diff)
downloadmeowcraft-5a1b126f1f6d55226c2b5068d0c17c428fd29ba8.tar.zst
meowcraft-5a1b126f1f6d55226c2b5068d0c17c428fd29ba8.zip
Create separate Player entity and add bad collision system
Diffstat (limited to 'src')
-rw-r--r--src/Entities/Player.cpp80
-rw-r--r--src/Entities/Player.hpp40
-rw-r--r--src/GFX/Util/Primitives.cpp6
-rw-r--r--src/GFX/Util/Primitives.hpp4
-rw-r--r--src/Math/AABB.hpp25
-rw-r--r--src/Math/Matrix.hpp3
-rw-r--r--src/Math/Rotation.hpp25
-rw-r--r--src/Math/Vector.hpp4
-rw-r--r--src/Transform.cpp24
-rw-r--r--src/Transform.hpp40
-rw-r--r--src/World/ChunkIndex.hpp4
-rw-r--r--src/World/Clouds.cpp2
-rw-r--r--src/World/Position.hpp4
-rw-r--r--src/World/World.cpp6
-rw-r--r--src/World/World.hpp2
-rw-r--r--src/main.cpp33
16 files changed, 252 insertions, 50 deletions
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
new file mode 100644
index 0000000..175b59e
--- /dev/null
+++ b/src/Entities/Player.cpp
@@ -0,0 +1,80 @@
+#include "Player.hpp"
+
+namespace MC::Entities {
+
+void Player::update(const Time& time, GFX::Window& window, GFX::Camera& camera, World::World& world) {
+    auto r = window.mouse_delta();
+
+    auto key = [&](Int k) -> Real { return window.key(k, GLFW_PRESS); };
+
+    Real x = key(GLFW_KEY_D) - key(GLFW_KEY_A);
+    Real y = key(GLFW_KEY_SPACE) - key(GLFW_KEY_LEFT_SHIFT);
+    Real z = key(GLFW_KEY_S) - key(GLFW_KEY_W);
+    Real boost = key(GLFW_KEY_LEFT_CONTROL) * 75.0f;
+
+    auto move_speed = (20.0f + boost) * time.delta();
+    auto rotation_speed = 0.1f;
+
+    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);
+    rotate({r.y() * rotation_speed, r.x() * rotation_speed, 0.0f});
+
+    update_camera_position(camera);
+}
+
+void Player::move(Position::WorldOffset by) {
+    m_transform.position() += by;
+}
+
+void Player::rotate(Rotation by) {
+    m_transform.rotation() += by;
+    m_transform.rotation().pitch() = std::clamp(m_transform.rotation().pitch(), -89.0, 89.0);
+}
+
+void Player::move_to(Position::World to) {
+    m_transform.position() = to;
+}
+
+void Player::rotate_to(Rotation to) {
+    m_transform.rotation() = to;
+}
+
+AABB Player::bounds() const {
+    return bounding_box_for_position(m_transform.position());
+}
+
+void Player::update_camera_position(GFX::Camera& camera) {
+    auto camera_position = m_transform.position();
+    camera_position.y() += 1.5;
+
+    camera.set_position(camera_position);
+    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();
+
+    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;
+    }
+
+    return false;
+}
+
+AABB Player::bounding_box_for_position(Position::World position) {
+    Vec3 box_start = {
+        position.x() - s_bounds.max.x() / 2,
+        position.y(),
+        position.z() - s_bounds.max.z() / 2,
+    };
+
+    return s_bounds.offset(box_start);
+}
+
+}
diff --git a/src/Entities/Player.hpp b/src/Entities/Player.hpp
new file mode 100644
index 0000000..86d8c90
--- /dev/null
+++ b/src/Entities/Player.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "../Time.hpp"
+#include "../Transform.hpp"
+#include "../GFX/Camera.hpp"
+#include "../World/World.hpp"
+#include "../GFX/Window.hpp"
+#include "../Math/AABB.hpp"
+#include "../Math/Rotation.hpp"
+#include "../World/Position.hpp"
+
+namespace MC::Entities {
+
+class Player {
+public:
+    explicit Player(Position::World position) : m_transform(position) {}
+
+    void update(const Time& time, GFX::Window& window, GFX::Camera& camera, World::World& world);
+
+    void move(Position::WorldOffset by);
+    void move_to(Position::World to);
+
+    void rotate(Rotation by);
+    void rotate_to(Rotation to);
+
+    AABB bounds() const;
+private:
+    void update_camera_position(GFX::Camera& camera);
+
+    static Bool collides_with_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);
+
+    Transform m_transform;
+
+    static inline AABB s_bounds{{0.35, 1.8, 0.35}};
+};
+
+}
diff --git a/src/GFX/Util/Primitives.cpp b/src/GFX/Util/Primitives.cpp
index d2c3690..4b44864 100644
--- a/src/GFX/Util/Primitives.cpp
+++ b/src/GFX/Util/Primitives.cpp
@@ -2,7 +2,7 @@
 
 namespace MC::GFX::Util::Primitives {
 
-PlanePrimitive plane(Math::AABB aabb, FaceSet face) {
+PlanePrimitive plane(AABB aabb, FaceSet face) {
     decltype(PlanePrimitive::positions) positions;
     auto [min, max] = aabb;
 
@@ -55,7 +55,7 @@ PlanePrimitive plane(Math::AABB aabb, FaceSet face) {
     return {positions, {normal, normal, normal, normal}, {0, 1, 2, 2, 3, 0}};
 }
 
-BoxPrimitive box(Math::AABB aabb, FaceSet faces) {
+BoxPrimitive box(AABB aabb, FaceSet faces) {
     BoxPrimitive box{};
     auto [min, max] = aabb;
 
@@ -65,7 +65,7 @@ BoxPrimitive box(Math::AABB aabb, FaceSet faces) {
         FaceSet face = (FaceSet::Value)face_value;
         if ((faces & face) == 0) continue;
 
-        Math::AABB face_aabb;
+        AABB face_aabb;
         switch (face) {
         case FaceSet::Front:
             face_aabb = {min, {max.x(), max.y(), min.z()}};
diff --git a/src/GFX/Util/Primitives.hpp b/src/GFX/Util/Primitives.hpp
index b96bc00..a267130 100644
--- a/src/GFX/Util/Primitives.hpp
+++ b/src/GFX/Util/Primitives.hpp
@@ -39,9 +39,9 @@ struct Primitive {
 };
 
 using PlanePrimitive = Primitive<4>;
-PlanePrimitive plane(Math::AABB aabb, FaceSet face);
+PlanePrimitive plane(AABB aabb, FaceSet face);
 
 using BoxPrimitive = Primitive<4 * 6>;
-BoxPrimitive box(Math::AABB aabb, FaceSet faces = FaceSet::all());
+BoxPrimitive box(AABB aabb, FaceSet faces = FaceSet::all());
 
 }
diff --git a/src/Math/AABB.hpp b/src/Math/AABB.hpp
index 2c02abf..11d26a8 100644
--- a/src/Math/AABB.hpp
+++ b/src/Math/AABB.hpp
@@ -1,10 +1,27 @@
 #pragma once
-#include "Vector.hpp"
 
-namespace Math {
+#include <array>
+#include "Vector.hpp"
 
 struct AABB {
+    AABB() = default;
+    AABB(Vector<3> min, Vector<3> max) : min(min), max(max) {}
+    explicit AABB(Vector<3> max) : max(max) {}
+
+    std::array<Vector<3>, 8> corners() const {
+        return {{
+            {min.x(), min.y(), min.z()},
+            {min.x(), min.y(), max.z()},
+            {min.x(), max.y(), min.z()},
+            {min.x(), max.y(), max.z()},
+            {max.x(), min.y(), min.z()},
+            {max.x(), min.y(), max.z()},
+            {max.x(), max.y(), min.z()},
+            {max.x(), max.y(), max.z()},
+        }};
+    }
+
+    AABB offset(Vector<3> by) const { return {min + by, max + by}; }
+
     Vector<3> min, max;
 };
-
-}
diff --git a/src/Math/Matrix.hpp b/src/Math/Matrix.hpp
index 41d3661..c4532c3 100644
--- a/src/Math/Matrix.hpp
+++ b/src/Math/Matrix.hpp
@@ -3,7 +3,6 @@
 #include <sstream>
 #include "../Common/Sizes.hpp"
 #include "Rotation.hpp"
-#include "Trig.hpp"
 
 template <uint R, uint C, typename T = Real>
 struct Matrix {
@@ -47,7 +46,7 @@ struct Matrix {
     }
 
     static Matrix<4, 4, T> rotation(Rotation angles) {
-        auto radians = angles.vector.map([](auto a) { return Math::radians(a); });
+        auto radians = angles.radians();
 
         auto c = radians.map([](auto a) { return cos(a); });
         auto s = radians.map([](auto a) { return sin(a); });
diff --git a/src/Math/Rotation.hpp b/src/Math/Rotation.hpp
index 83c1109..d5bc023 100644
--- a/src/Math/Rotation.hpp
+++ b/src/Math/Rotation.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <cmath>
+#include "Trig.hpp"
 #include "Vector.hpp"
 
 struct Rotation {
@@ -14,10 +15,19 @@ struct Rotation {
         vector = wrap({pitch, yaw, roll });
     }
 
+    Vector<3> radians() const {
+        return vector.map([](auto a) { return Math::radians(a); });
+    }
+
     Rotation operator+(Rotation other) const {
         return wrap(vector + other.vector);
     }
 
+    Rotation& operator+=(const Rotation& other) {
+        *this = *this + other;
+        return *this;
+    }
+
     std::string string() const {
         return vector.string();
     }
@@ -26,17 +36,14 @@ struct Rotation {
         return v.map([](auto a) { return fmod(a, 360.0f); });
     }
 
-    Real& pitch() {
-        return vector[0];
-    }
+    Real& pitch() { return vector.x(); }
+    const Real& pitch() const { return vector.x(); }
 
-    Real& yaw() {
-        return vector[1];
-    }
+    Real& yaw() { return vector.y(); }
+    const Real& yaw() const { return vector.y(); }
 
-    Real& roll() {
-        return vector[2];
-    }
+    Real& roll() { return vector.z(); }
+    const Real& roll() const { return vector.z(); }
 
     Vector<3> vector;
 };
\ No newline at end of file
diff --git a/src/Math/Vector.hpp b/src/Math/Vector.hpp
index 3d9cb0e..a7c3782 100644
--- a/src/Math/Vector.hpp
+++ b/src/Math/Vector.hpp
@@ -190,3 +190,7 @@ struct Vector {
 
     T elements[S];
 };
+
+using Vec2 = Vector<2, Real>;
+using Vec3 = Vector<3, Real>;
+using Vec4 = Vector<4, Real>;
\ No newline at end of file
diff --git a/src/Transform.cpp b/src/Transform.cpp
new file mode 100644
index 0000000..72be8ea
--- /dev/null
+++ b/src/Transform.cpp
@@ -0,0 +1,24 @@
+#include "Transform.hpp"
+#include "Math/Common.hpp"
+
+namespace MC {
+
+Vector<3> Transform::forward() const {
+    return unit_vector({0, 0, 1});
+}
+
+Vector<3> Transform::right() const {
+    return unit_vector({1, 0, 0});
+}
+
+Vector<3> Transform::up() const {
+    return unit_vector({0, 1, 0});
+}
+
+Vector<3> Transform::unit_vector(Vector<3> axis) const {
+    auto rotation = Matrix<4, 4>::rotation(m_rotation);
+    auto result = rotation.transpose() * Vector<4>{axis.x(), axis.y(), axis.z(), 1.0f};
+    return {result.x(), result.y(), result.z()};
+}
+
+}
diff --git a/src/Transform.hpp b/src/Transform.hpp
new file mode 100644
index 0000000..ea7f9b3
--- /dev/null
+++ b/src/Transform.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "Math/Rotation.hpp"
+#include "World/Position.hpp"
+
+namespace MC {
+
+class Transform {
+public:
+    Transform() : m_scale(1) {}
+    explicit Transform(Position::World position) : m_position(position), m_scale(1) {}
+    Transform(Position::World position, Rotation rotation, Vector<3> scale)
+        : m_position(position),
+          m_rotation(rotation),
+          m_scale(scale) {}
+
+    Vector<3> forward() const;
+    Vector<3> right() const;
+    Vector<3> up() const;
+
+    Position::World& position() { return m_position; }
+    const Position::World& position() const { return m_position; }
+
+    Rotation& rotation() { return m_rotation; }
+    const Rotation& rotation() const { return m_rotation; }
+
+    Vector<3>& scale() { return m_scale; }
+    const Vector<3>& scale() const { return m_scale; }
+private:
+    Vector<3> unit_vector(Vector<3> axis) const;
+
+    // TODO: Use a non-MC::World position class.
+    // I don't want coupling between the game implementation
+    // and what is essentially engine code.
+    Position::World m_position;
+    Rotation m_rotation;
+    Vector<3> m_scale;
+};
+
+}
diff --git a/src/World/ChunkIndex.hpp b/src/World/ChunkIndex.hpp
index 70de99f..2e4fa67 100644
--- a/src/World/ChunkIndex.hpp
+++ b/src/World/ChunkIndex.hpp
@@ -22,8 +22,8 @@ struct ChunkIndex {
     }
 
     static ChunkIndex from_position(Position::BlockWorld pos) {
-        I32 chunk_x = std::round(pos.x() / ChunkDimensions::Width);
-        I32 chunk_y = std::round(pos.z() / ChunkDimensions::Width);
+        I32 chunk_x = std::floor((Real)pos.x() / ChunkDimensions::Width);
+        I32 chunk_y = std::floor((Real)pos.z() / ChunkDimensions::Width);
         return {chunk_x, chunk_y};
     }
 
diff --git a/src/World/Clouds.cpp b/src/World/Clouds.cpp
index 2567985..b30c0ce 100644
--- a/src/World/Clouds.cpp
+++ b/src/World/Clouds.cpp
@@ -103,7 +103,7 @@ GFX::Mesh Clouds::create_mesh(const CloudMatrix& cloud_matrix) {
             if (!neighbors[2]) faces |= FaceSet::Front;
             if (!neighbors[3]) faces |= FaceSet::Back;
 
-            auto aabb = Math::AABB{{x, 0, y}, {x + 1, 1, y + 1}};
+            auto aabb = AABB{{x, 0, y}, {x + 1, 1, y + 1}};
             auto box = GFX::Util::Primitives::box(aabb, (FaceSet::Value)faces);
             builder.primitive(box);
         }
diff --git a/src/World/Position.hpp b/src/World/Position.hpp
index bc874c0..91ec306 100644
--- a/src/World/Position.hpp
+++ b/src/World/Position.hpp
@@ -53,7 +53,7 @@ public:
 
     BlockLocal to_local() const {
         using namespace MC::World::ChunkDimensions;
-        return {Math::mod(x(), Width), y(), Math::mod(z(), Width)};
+        return {Math::mod(x(), Width), std::clamp<I64>(y(), 0, Height), Math::mod(z(), Width)};
     }
 };
 
@@ -73,7 +73,7 @@ public:
     MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(World, Real)
 
     BlockWorld round_to_block() const {
-        auto rounded = map([](auto x) { return std::round(x); });
+        auto rounded = map([](auto x) { return std::floor(x); });
         return {rounded.x(), rounded.y(), rounded.z()};
     }
 };
diff --git a/src/World/World.cpp b/src/World/World.cpp
index 089c498..ce15ae8 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -41,6 +41,12 @@ std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Position::World posi
     return chunks;
 }
 
+Chunk::BlockData World::block_at(Position::BlockWorld pos) {
+    auto& data = m_registry.find(pos);
+    if (data.chunk.has_value()) return data.chunk->at(pos.to_local());
+    return {};
+}
+
 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 e3b08f5..3bf05f8 100644
--- a/src/World/World.hpp
+++ b/src/World/World.hpp
@@ -13,6 +13,8 @@ class World {
 public:
     std::vector<ChunkRegistry::Data*> get_visible_chunks(Position::World position);
 
+    Chunk::BlockData block_at(Position::BlockWorld pos);
+
     Real get_average_chunk_time() const;
 private:
     std::vector<ChunkIndex> get_visible_chunk_indices(Position::World position) const;
diff --git a/src/main.cpp b/src/main.cpp
index 815de28..9dd42c9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -12,6 +12,7 @@
 #include "GFX/Image/PPMParser.hpp"
 #include "World/Clouds.hpp"
 #include "World/World.hpp"
+#include "Entities/Player.hpp"
 
 #define APP_NAME "Meowcraft"
 
@@ -23,7 +24,6 @@
 
 void run();
 void render(MC::GFX::Mesh&, MC::GFX::Texture&);
-void process_input(MC::GFX::Window&, MC::GFX::Camera&, MC::Time&);
 void setup_gl();
 void fix_macos_render(const MC::GFX::Window&);
 
@@ -57,7 +57,6 @@ void run() {
     MC::World::World world;
 
     MC::GFX::Camera camera{};
-    camera.set_position({0, MC::World::Chunk::Height / 2.0, 0});
 
     MC::GFX::Shading::Program program(
         MC::GFX::Shading::Shader::create_vertex(),
@@ -83,6 +82,8 @@ void run() {
 
     MC::World::Clouds clouds{ASPECT, FOV, 0.1f, 1000.0f, sky_color, sun_direction};
 
+    MC::Entities::Player player{{0, MC::World::Chunk::Height / 2.0, 0}};
+
     glEnable(GL_DEPTH_TEST);
     glDepthFunc(GL_LEQUAL);
 
@@ -103,7 +104,11 @@ void run() {
         fix_macos_render(window);
 #endif
 
-        process_input(window, camera, time);
+        if (window.key(GLFW_KEY_ESCAPE, GLFW_PRESS)) {
+            window.close();
+        }
+
+        player.update(time, window, camera, world);
         clouds.update(time);
 
         glClearColor(sky_color.x(), sky_color.y(), sky_color.z(), 1.0f); // #DBDBDB
@@ -142,28 +147,6 @@ void render(MC::GFX::Mesh& mesh, MC::GFX::Texture& texture) {
     texture.unbind();
 }
 
-void process_input(MC::GFX::Window& window, MC::GFX::Camera& camera, MC::Time& time) {
-    if (window.key(GLFW_KEY_ESCAPE, GLFW_PRESS)) {
-        window.close();
-    }
-
-    auto r = window.mouse_delta();
-
-    auto key = [&](Int k) -> Real { return window.key(k, GLFW_PRESS); };
-
-    Real x = key(GLFW_KEY_D) - key(GLFW_KEY_A);
-    Real y = key(GLFW_KEY_SPACE) - key(GLFW_KEY_LEFT_SHIFT);
-    Real z = key(GLFW_KEY_S) - key(GLFW_KEY_W);
-    Real boost = key(GLFW_KEY_LEFT_CONTROL) * 75.0f;
-
-    auto move_speed = (20.0f + boost) * time.delta();
-    auto rotation_speed = 5.0f * time.delta();
-
-    camera.move_relative({x * move_speed, 0.0f, z * move_speed});
-    camera.move({0.0f, y * move_speed, 0.0f});
-    camera.rotate({r.y() * rotation_speed, r.x() * rotation_speed, 0.0f});
-}
-
 void setup_gl() {
     GLenum error;
     if ((error = glewInit()) != GLEW_OK) {