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