]> git.localhorst.tv Git - blank.git/blob - src/world.cpp
4d14dfd89855932058e52e8a00c44d8b8f0d5d30
[blank.git] / src / world.cpp
1 #include "world.hpp"
2
3 #include <limits>
4 #include <glm/gtx/transform.hpp>
5
6
7 namespace blank {
8
9 World::World()
10 : blockType()
11 , blockShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }})
12 , stairShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f })
13 , slabShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }})
14 , blockNoise(0)
15 , colorNoise(1)
16 , player()
17 , player_chunk(0, 0, 0)
18 , loaded()
19 , to_generate()
20 , to_free() {
21         blockType.Add(BlockType{ true, { 1.0f, 1.0f, 1.0f }, &blockShape }); // white block
22         blockType.Add(BlockType{ true, { 1.0f, 1.0f, 1.0f }, &stairShape }); // white stair
23         blockType.Add(BlockType{ true, { 1.0f, 1.0f, 1.0f }, &slabShape }); // white slab
24         blockType.Add(BlockType{ true, { 1.0f, 0.0f, 0.0f }, &blockShape }); // red block
25         blockType.Add(BlockType{ true, { 1.0f, 0.0f, 0.0f }, &stairShape }); // red stair
26         blockType.Add(BlockType{ true, { 1.0f, 0.0f, 0.0f }, &slabShape }); // red slab
27         blockType.Add(BlockType{ true, { 0.0f, 1.0f, 0.0f }, &blockShape }); // green block
28         blockType.Add(BlockType{ true, { 0.0f, 1.0f, 0.0f }, &stairShape }); // green stair
29         blockType.Add(BlockType{ true, { 0.0f, 1.0f, 0.0f }, &slabShape }); // green slab
30         blockType.Add(BlockType{ true, { 0.0f, 0.0f, 1.0f }, &blockShape }); // blue block
31         blockType.Add(BlockType{ true, { 0.0f, 0.0f, 1.0f }, &stairShape }); // blue stair
32         blockType.Add(BlockType{ true, { 0.0f, 0.0f, 1.0f }, &slabShape }); // blue slab
33
34         player.Position({ 4.0f, 4.0f, 4.0f });
35 }
36
37
38 namespace {
39
40 bool ChunkLess(const Chunk &a, const Chunk &b) {
41         return
42                 a.Position().x * a.Position().x +
43                 a.Position().y * a.Position().y +
44                 a.Position().z * a.Position().z <
45                 b.Position().x * b.Position().x +
46                 b.Position().y * b.Position().y +
47                 b.Position().z * b.Position().z;
48 }
49
50 }
51
52 void World::Generate(const glm::tvec3<int> &from, const glm::tvec3<int> &to) {
53         for (int z = from.z; z < to.z; ++z) {
54                 for (int y = from.y; y < to.y; ++y) {
55                         for (int x = from.x; x < to.x; ++x) {
56                                 glm::vec3 pos{float(x), float(y), float(z)};
57                                 if (ChunkAvailable(pos)) {
58                                         continue;
59                                 } else if (x == 0 && y == 0 && z == 0) {
60                                         loaded.emplace_back();
61                                         loaded.back().Position(pos);
62                                         Generate(loaded.back());
63                                 } else {
64                                         to_generate.emplace_back();
65                                         to_generate.back().Position(pos);
66                                 }
67                         }
68                 }
69         }
70         to_generate.sort(ChunkLess);
71 }
72
73 void World::Generate(Chunk &chunk) {
74         chunk.Allocate();
75         glm::vec3 pos(chunk.Position());
76         if (pos.x == 0 && pos.y == 0 && pos.z == 0) {
77                 for (size_t i = 1; i < blockType.Size(); ++i) {
78                         chunk.BlockAt(i) = Block(blockType[i]);
79                         chunk.BlockAt(i + 257) = Block(blockType[i]);
80                         chunk.BlockAt(i + 514) = Block(blockType[i]);
81                 }
82         } else {
83                 for (int z = 0; z < Chunk::Depth(); ++z) {
84                         for (int y = 0; y < Chunk::Height(); ++y) {
85                                 for (int x = 0; x < Chunk::Width(); ++x) {
86                                         glm::vec3 block_pos{float(x), float(y), float(z)};
87                                         glm::vec3 gen_pos = (pos * glm::vec3(Chunk::Extent()) + block_pos) / 64.0f;
88                                         float val = blockNoise(gen_pos);
89                                         if (val > 0.8f) {
90                                                 int col_val = int((colorNoise(gen_pos) + 1.0f) * 2.0f) % 4;
91                                                 chunk.BlockAt(block_pos) = Block(blockType[col_val * 3 + 1]);
92                                         }
93                                 }
94                         }
95                 }
96         }
97         chunk.Invalidate();
98 }
99
100 bool World::Intersection(
101                 const Ray &ray,
102                 const glm::mat4 &M,
103                 Chunk **chunk,
104                 int *blkid,
105                 float *dist,
106                 glm::vec3 *normal) {
107         Chunk *closest_chunk = nullptr;
108         int closest_blkid = -1;
109         float closest_dist = std::numeric_limits<float>::infinity();
110         glm::vec3 closest_normal;
111
112         for (Chunk &cur_chunk : loaded) {
113                 int cur_blkid;
114                 float cur_dist;
115                 glm::vec3 cur_normal;
116                 if (cur_chunk.Intersection(ray, M * cur_chunk.Transform(player.ChunkCoords()), &cur_blkid, &cur_dist, &cur_normal)) {
117                         if (cur_dist < closest_dist) {
118                                 closest_chunk = &cur_chunk;
119                                 closest_blkid = cur_blkid;
120                                 closest_dist = cur_dist;
121                                 closest_normal = cur_normal;
122                         }
123                 }
124         }
125
126         if (chunk) {
127                 *chunk = closest_chunk;
128         }
129         if (blkid) {
130                 *blkid = closest_blkid;
131         }
132         if (dist) {
133                 *dist = closest_dist;
134         }
135         if (normal) {
136                 *normal = closest_normal;
137         }
138         return closest_chunk;
139 }
140
141
142 Chunk *World::ChunkLoaded(const glm::tvec3<int> &pos) {
143         for (Chunk &chunk : loaded) {
144                 if (glm::tvec3<int>(chunk.Position()) == pos) {
145                         return &chunk;
146                 }
147         }
148         return nullptr;
149 }
150
151 Chunk *World::ChunkQueued(const glm::tvec3<int> &pos) {
152         for (Chunk &chunk : to_generate) {
153                 if (glm::tvec3<int>(chunk.Position()) == pos) {
154                         return &chunk;
155                 }
156         }
157         return nullptr;
158 }
159
160 Chunk *World::ChunkAvailable(const glm::tvec3<int> &pos) {
161         Chunk *chunk = ChunkLoaded(pos);
162         if (chunk) return chunk;
163
164         return ChunkQueued(pos);
165 }
166
167 Chunk &World::Next(const Chunk &to, const glm::tvec3<int> &dir) {
168         const glm::tvec3<int> tgt_pos = to.Position() + dir;
169
170         Chunk *chunk = ChunkLoaded(tgt_pos);
171         if (chunk) {
172                 return *chunk;
173         }
174
175         chunk = ChunkQueued(tgt_pos);
176         if (chunk) {
177                 Generate(*chunk);
178                 return *chunk;
179         }
180
181         loaded.emplace_back();
182         loaded.back().Position(tgt_pos);
183         Generate(loaded.back());
184         return loaded.back();
185 }
186
187
188 void World::Update(int dt) {
189         player.Update(dt);
190
191         CheckChunkGeneration();
192 }
193
194 void World::CheckChunkGeneration() {
195         if (player.ChunkCoords() != player_chunk) {
196                 player_chunk = player.ChunkCoords();
197
198                 constexpr int max_dist = 8;
199                 // unload far away chunks
200                 for (auto iter(loaded.begin()), end(loaded.end()); iter != end;) {
201                         if (std::abs(player_chunk.x - iter->Position().x) > max_dist
202                                         || std::abs(player_chunk.y - iter->Position().y) > max_dist
203                                         || std::abs(player_chunk.z - iter->Position().z) > max_dist) {
204                                 auto saved = iter;
205                                 ++iter;
206                                 to_free.splice(to_free.end(), loaded, saved);
207                         } else {
208                                 ++iter;
209                         }
210                 }
211                 // abort far away queued chunks
212                 for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end;) {
213                         if (std::abs(player_chunk.x - iter->Position().x) > max_dist
214                                         || std::abs(player_chunk.y - iter->Position().y) > max_dist
215                                         || std::abs(player_chunk.z - iter->Position().z) > max_dist) {
216                                 iter = to_generate.erase(iter);
217                         } else {
218                                 ++iter;
219                         }
220                 }
221                 // add missing new chunks
222                 glm::tvec3<int> offset(max_dist, max_dist, max_dist);
223                 Generate(player_chunk - offset, player_chunk + offset);
224         }
225
226         if (!to_generate.empty()) {
227                 Generate(to_generate.front());
228                 loaded.splice(loaded.end(), to_generate, to_generate.begin());
229         }
230
231         if (!to_free.empty()) {
232                 to_free.pop_front();
233         }
234 }
235
236
237 void World::Render(DirectionalLighting &program) {
238         program.SetLightDirection({ -1.0f, -3.0f, -2.0f });
239         program.SetView(glm::inverse(player.Transform(player.ChunkCoords())));
240
241         for (Chunk &chunk : LoadedChunks()) {
242                 glm::mat4 m(chunk.Transform(player.ChunkCoords()));
243                 program.SetM(m);
244                 glm::mat4 mvp(program.GetVP() * m);
245                 if (!CullTest(Chunk::Bounds(), mvp)) {
246                         chunk.Draw();
247                 }
248         }
249 }
250
251 }