summary refs log tree commit diff
path: root/src/Entities/Player.cpp
blob: fb882224253ff77a3b2162dd98f6792bce63e7ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#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 = (10.0f + boost) * time.delta();
    auto rotation_speed = 0.1f;

    auto direction = m_transform.right() * x + Vec3(0, y, 0) + m_transform.forward() * z;
    auto destination = m_transform.position() + direction * move_speed;

    auto collision_domain = terrain_collision_domain(m_transform.position(), destination, world);
    for (auto collision : collision_domain) {
        destination = collision_reposition(m_transform.position(), destination, collision);
    }

    move_to(destination);
    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());
}

std::vector<AABB> Player::terrain_collision_domain(
    Position::World from, Position::World to,
    World::World& world
) {
    auto domain_box = bounding_box_for_position(from).unite(bounding_box_for_position(to));
    std::vector<AABB> colliding_blocks;

    // TODO: Unbind from chunks and loop through all the
    // blocks individually inside domain.
    // This will be more efficient and actually accurate,
    // since right now if you're fast enough you can clip
    // through whole chunks.
    auto add_colliding = [&](Position::World chunk_pos, Position::BlockLocal pos, World::Chunk::BlockData& b) {
        if (b.type == World::BlockType::Air) return;
        auto block_bounds = World::Chunk::block_bounds(pos).offset(chunk_pos);
        if (domain_box.collides(block_bounds)) colliding_blocks.push_back(block_bounds);
    };

    auto chunk_from = world.chunks().find(to);
    if (chunk_from.chunk.has_value()) {
        auto position = chunk_from.chunk.value().position();
        chunk_from.chunk->for_each([&](auto p, auto b) { add_colliding(position, p, b); });
    }

    auto chunk_to = world.chunks().find(to);
    if (chunk_to.chunk.has_value()) {
        auto position = chunk_to.chunk.value().position();
        chunk_to.chunk->for_each([&](auto p, auto b) { add_colliding(position, p, b); });
    }

    return colliding_blocks;
}

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);
}

Position::World Player::position_for_bounding_box(AABB box) {
    auto center = box.center();
    return {center.x(), box.min.y(), center.z()};
}

Position::World Player::collision_reposition(Position::World from, Position::World to, AABB colliding) {
    if (from == to) return from;
    auto resulting_bounding_box = bounding_box_for_position(from).collision_response(to - from, colliding);
    return position_for_bounding_box(resulting_bounding_box); // Ugly, we convert to AABB and back.
}

}