summaryrefslogtreecommitdiffstats
path: root/game/shared/world/ray_dda.cc
blob: 9f85e6b297c7b31ed1d5866593f5bf9e49163a49 (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
#include "shared/pch.hh"

#include "shared/world/ray_dda.hh"

#include "shared/world/dimension.hh"

#include "shared/coord.hh"

world::RayDDA::RayDDA(
    const world::Dimension* dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction)
{
    reset(dimension, start_chunk, start_fpos, direction);
}

world::RayDDA::RayDDA(
    const world::Dimension& dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction)
{
    reset(dimension, start_chunk, start_fpos, direction);
}

void world::RayDDA::reset(
    const world::Dimension* dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction)
{
    this->dimension = dimension;
    this->start_chunk = start_chunk;
    this->start_fpos = start_fpos;
    this->direction = direction;

    this->delta_dist.x = direction.x ? math::abs(1.0f / direction.x) : std::numeric_limits<float>::max();
    this->delta_dist.y = direction.y ? math::abs(1.0f / direction.y) : std::numeric_limits<float>::max();
    this->delta_dist.z = direction.z ? math::abs(1.0f / direction.z) : std::numeric_limits<float>::max();

    this->distance = 0.0f;
    this->vpos = coord::to_voxel(start_chunk, start_fpos);
    this->vnormal = voxel_pos(0, 0, 0);

    // Need this for initial direction calculations
    auto lpos = coord::to_local(start_fpos);

    if(direction.x < 0.0f) {
        this->side_dist.x = this->delta_dist.x * (start_fpos.x - lpos.x);
        this->vstep.x = voxel_pos::value_type(-1);
    }
    else {
        this->side_dist.x = this->delta_dist.x * (lpos.x + 1.0f - start_fpos.x);
        this->vstep.x = voxel_pos::value_type(+1);
    }

    if(direction.y < 0.0f) {
        this->side_dist.y = this->delta_dist.y * (start_fpos.y - lpos.y);
        this->vstep.y = voxel_pos::value_type(-1);
    }
    else {
        this->side_dist.y = this->delta_dist.y * (lpos.y + 1.0f - start_fpos.y);
        this->vstep.y = voxel_pos::value_type(+1);
    }

    if(direction.z < 0.0f) {
        this->side_dist.z = this->delta_dist.z * (start_fpos.z - lpos.z);
        this->vstep.z = voxel_pos::value_type(-1);
    }
    else {
        this->side_dist.z = this->delta_dist.z * (lpos.z + 1.0f - start_fpos.z);
        this->vstep.z = voxel_pos::value_type(+1);
    }
}

void world::RayDDA::reset(
    const world::Dimension& dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction)
{
    reset(&dimension, start_chunk, start_fpos, direction);
}

voxel_id world::RayDDA::step(void)
{
    if(side_dist.x < side_dist.z) {
        if(side_dist.x < side_dist.y) {
            vnormal = voxel_pos(-vstep.x, 0, 0);
            distance = side_dist.x;
            side_dist.x += delta_dist.x;
            vpos.x += vstep.x;
        }
        else {
            vnormal = voxel_pos(0, -vstep.y, 0);
            distance = side_dist.y;
            side_dist.y += delta_dist.y;
            vpos.y += vstep.y;
        }
    }
    else {
        if(side_dist.z < side_dist.y) {
            vnormal = voxel_pos(0, 0, -vstep.z);
            distance = side_dist.z;
            side_dist.z += delta_dist.z;
            vpos.z += vstep.z;
        }
        else {
            vnormal = voxel_pos(0, -vstep.y, 0);
            distance = side_dist.y;
            side_dist.y += delta_dist.y;
            vpos.y += vstep.y;
        }
    }

    // This is slower than I want it to be
    return dimension->get_voxel(vpos);
}