]> git.localhorst.tv Git - blank.git/blob - src/world/render.cpp
first draft for client/server architecture
[blank.git] / src / world / render.cpp
1 #include "ChunkRenderer.hpp"
2
3 #include "World.hpp"
4 #include "../app/Assets.hpp"
5 #include "../graphics/BlockLighting.hpp"
6 #include "../graphics/Viewport.hpp"
7
8
9 namespace blank {
10
11 ChunkRenderer::ChunkRenderer(World &world, int rd)
12 : world(world)
13 , block_tex()
14 , render_dist(rd)
15 , side_length(2 * rd + 1)
16 , total_length(side_length * side_length * side_length)
17 , total_indexed(0)
18 , stride(1, side_length, side_length * side_length)
19 , models(total_length)
20 , chunks(total_length)
21 , base(0, 0, 0)
22 , fog_density(0.0f) {
23
24 }
25
26
27 void ChunkRenderer::LoadTextures(const AssetLoader &loader, const TextureIndex &tex_index) {
28         block_tex.Bind();
29         loader.LoadTextures(tex_index, block_tex);
30         block_tex.FilterNearest();
31 }
32
33
34 bool ChunkRenderer::InRange(const Chunk::Pos &pos) const noexcept {
35         return manhattan_radius(pos - base) <= render_dist;
36 }
37
38 int ChunkRenderer::IndexOf(const Chunk::Pos &pos) const noexcept {
39         Chunk::Pos mod_pos(
40                 GetCol(pos.x),
41                 GetCol(pos.y),
42                 GetCol(pos.z)
43         );
44         return mod_pos.x * stride.x
45                 +  mod_pos.y * stride.y
46                 +  mod_pos.z * stride.z;
47 }
48
49
50 void ChunkRenderer::Rebase(const Chunk::Pos &new_base) {
51         if (new_base == base) return;
52
53         Chunk::Pos diff(new_base - base);
54
55         if (manhattan_radius(diff) > render_dist) {
56                 // that's more than half, so probably not worth shifting
57                 base = new_base;
58                 Rescan();
59                 return;
60         }
61
62         while (diff.x > 0) {
63                 Shift(Block::FACE_RIGHT);
64                 --diff.x;
65         }
66         while (diff.x < 0) {
67                 Shift(Block::FACE_LEFT);
68                 ++diff.x;
69         }
70         while (diff.y > 0) {
71                 Shift(Block::FACE_UP);
72                 --diff.y;
73         }
74         while (diff.y < 0) {
75                 Shift(Block::FACE_DOWN);
76                 ++diff.y;
77         }
78         while (diff.z > 0) {
79                 Shift(Block::FACE_FRONT);
80                 --diff.z;
81         }
82         while (diff.z < 0) {
83                 Shift(Block::FACE_BACK);
84                 ++diff.z;
85         }
86 }
87
88 int ChunkRenderer::GetCol(int c) const noexcept {
89         c %= side_length;
90         if (c < 0) c += side_length;
91         return c;
92 }
93
94 void ChunkRenderer::Shift(Block::Face f) {
95         int a_axis = Block::Axis(f);
96         int b_axis = (a_axis + 1) % 3;
97         int c_axis = (a_axis + 2) % 3;
98         int dir = Block::Direction(f);
99         base[a_axis] += dir;
100         int a = GetCol(base[a_axis] + (render_dist * dir));
101         int a_stride = a * stride[a_axis];
102         for (int b = 0; b < side_length; ++b) {
103                 int b_stride = b * stride[b_axis];
104                 for (int c = 0; c < side_length; ++c) {
105                         int bc_stride = b_stride + c * stride[c_axis];
106                         int index = a_stride + bc_stride;
107                         if (chunks[index]) {
108                                 chunks[index] = nullptr;
109                                 --total_indexed;
110                         }
111                         int neighbor = ((a - dir + side_length) % side_length) * stride[a_axis] + bc_stride;
112                         if (chunks[neighbor] && chunks[neighbor]->HasNeighbor(f)) {
113                                 chunks[index] = &chunks[neighbor]->GetNeighbor(f);
114                                 chunks[index]->InvalidateModel();
115                                 ++total_indexed;
116                         }
117                 }
118         }
119 }
120
121
122 void ChunkRenderer::Rescan() {
123         chunks.assign(total_length, nullptr);
124         total_indexed = 0;
125         Scan();
126 }
127
128 void ChunkRenderer::Scan() {
129         for (Chunk &chunk : world.Loader().Loaded()) {
130                 if (!InRange(chunk.Position())) continue;
131                 int index = IndexOf(chunk.Position());
132                 if (!chunks[index]) {
133                         chunks[index] = &chunk;
134                         chunk.InvalidateModel();
135                         ++total_indexed;
136                 }
137         }
138 }
139
140 void ChunkRenderer::Update(int dt) {
141         if (MissingChunks()) {
142                 Scan();
143         }
144
145         // maximum of 1000 per second too high?
146         for (int i = 0, updates = 0; i < total_length && updates < dt; ++i) {
147                 if (chunks[i] && chunks[i]->ShouldUpdateModel()) {
148                         chunks[i]->Update(models[i]);
149                         ++updates;
150                 }
151         }
152 }
153
154
155 void ChunkRenderer::Render(Viewport &viewport) {
156         BlockLighting &chunk_prog = viewport.ChunkProgram();
157         chunk_prog.SetTexture(block_tex);
158         chunk_prog.SetFogDensity(fog_density);
159
160         for (int i = 0; i < total_length; ++i) {
161                 if (!chunks[i]) continue;
162                 glm::mat4 m(chunks[i]->Transform(base));
163                 glm::mat4 mvp(chunk_prog.GetVP() * m);
164                 if (!CullTest(Chunk::Bounds(), mvp)) {
165                         if (chunks[i]->ShouldUpdateModel()) {
166                                 chunks[i]->Update(models[i]);
167                         }
168                         chunk_prog.SetM(m);
169                         models[i].Draw();
170                 }
171         }
172 }
173
174 }