]> git.localhorst.tv Git - blank.git/blob - src/world/World.cpp
73a870dc28b7e1d9002d7efce6058a2f3894b71f
[blank.git] / src / world / World.cpp
1 #include "World.hpp"
2
3 #include "WorldCollision.hpp"
4 #include "../app/Assets.hpp"
5 #include "../graphics/Format.hpp"
6 #include "../graphics/Viewport.hpp"
7
8 #include <iostream>
9 #include <limits>
10 #include <glm/gtx/io.hpp>
11 #include <glm/gtx/transform.hpp>
12
13
14 namespace blank {
15
16 World::World(const Assets &assets, const Config &config)
17 : blockType()
18 , blockShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }})
19 , stairShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f })
20 , slabShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }})
21 , block_tex()
22 , generate(config.gen)
23 , chunks(config.load, blockType, generate)
24 , player()
25 , entities()
26 , light_direction(config.light_direction)
27 , fog_density(config.fog_density) {
28         BlockType::Faces block_fill = {  true,  true,  true,  true,  true,  true };
29         BlockType::Faces slab_fill  = { false,  true, false, false, false, false };
30         BlockType::Faces stair_fill = { false,  true, false, false, false,  true };
31
32         block_tex.Bind();
33         block_tex.Reserve(16, 16, 4, Format());
34         assets.LoadTexture("debug", block_tex, 0);
35         assets.LoadTexture("rock-1", block_tex, 1);
36         assets.LoadTexture("rock-2", block_tex, 2);
37         assets.LoadTexture("rock-3", block_tex, 3);
38         block_tex.FilterNearest();
39
40         { // white block
41                 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
42                 type.texture = 1;
43                 type.label = "White Block";
44                 type.block_light = true;
45                 type.collision = true;
46                 type.collide_block = true;
47                 type.fill = block_fill;
48                 blockType.Add(type);
49         }
50         { // white slab
51                 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &slabShape);
52                 type.texture = 1;
53                 type.label = "White Slab";
54                 type.block_light = true;
55                 type.collision = true;
56                 type.collide_block = true;
57                 type.fill = slab_fill;
58                 blockType.Add(type);
59         }
60         { // white stair
61                 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &stairShape);
62                 type.texture = 1;
63                 type.label = "White Stair";
64                 type.block_light = true;
65                 type.collision = true;
66                 type.collide_block = true;
67                 type.fill = stair_fill;
68                 blockType.Add(type);
69         }
70
71         { // red block
72                 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &blockShape);
73                 type.texture = 3;
74                 type.label = "Red Block";
75                 type.block_light = true;
76                 type.collision = true;
77                 type.collide_block = true;
78                 type.fill = block_fill;
79                 blockType.Add(type);
80         }
81         { // red slab
82                 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &slabShape);
83                 type.texture = 3;
84                 type.label = "Red Slab";
85                 type.block_light = true;
86                 type.collision = true;
87                 type.collide_block = true;
88                 type.fill = slab_fill;
89                 blockType.Add(type);
90         }
91         { // red stair
92                 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &stairShape);
93                 type.texture = 3;
94                 type.label = "Red Stair";
95                 type.block_light = true;
96                 type.collision = true;
97                 type.collide_block = true;
98                 type.fill = stair_fill;
99                 blockType.Add(type);
100         }
101
102         { // green block
103                 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &blockShape);
104                 type.texture = 1;
105                 type.label = "Green Block";
106                 type.block_light = true;
107                 type.collision = true;
108                 type.collide_block = true;
109                 type.fill = block_fill;
110                 blockType.Add(type);
111         }
112         { // green slab
113                 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &slabShape);
114                 type.texture = 1;
115                 type.label = "Green Slab";
116                 type.block_light = true;
117                 type.collision = true;
118                 type.collide_block = true;
119                 type.fill = slab_fill;
120                 blockType.Add(type);
121         }
122         { // green stair
123                 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &stairShape);
124                 type.texture = 1;
125                 type.label = "Green Stair";
126                 type.block_light = true;
127                 type.collision = true;
128                 type.collide_block = true;
129                 type.fill = stair_fill;
130                 blockType.Add(type);
131         }
132
133         { // blue block
134                 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &blockShape);
135                 type.texture = 3;
136                 type.label = "Blue Block";
137                 type.block_light = true;
138                 type.collision = true;
139                 type.collide_block = true;
140                 type.fill = block_fill;
141                 blockType.Add(type);
142         }
143         { // blue slab
144                 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &slabShape);
145                 type.texture = 3;
146                 type.label = "Blue Slab";
147                 type.block_light = true;
148                 type.collision = true;
149                 type.collide_block = true;
150                 type.fill = slab_fill;
151                 blockType.Add(type);
152         }
153         { // blue stair
154                 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &stairShape);
155                 type.texture = 3;
156                 type.label = "Blue Stair";
157                 type.block_light = true;
158                 type.collision = true;
159                 type.collide_block = true;
160                 type.fill = stair_fill;
161                 blockType.Add(type);
162         }
163
164         { // glowing yellow block
165                 BlockType type(true, { 1.0f, 1.0f, 0.0f }, &blockShape);
166                 type.texture = 2;
167                 type.label = "Light";
168                 type.luminosity = 15;
169                 type.block_light = true;
170                 type.collision = true;
171                 type.collide_block = true;
172                 type.fill = block_fill;
173                 blockType.Add(type);
174         }
175
176         { // the mysterious debug cube
177                 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
178                 type.texture = 0;
179                 type.label = "Debug Cube";
180                 type.luminosity = 0;
181                 type.block_light = true;
182                 type.collision = true;
183                 type.collide_block = true;
184                 type.fill = block_fill;
185                 blockType.Add(type);
186         }
187
188         generate.Space(0);
189         generate.Light(13);
190         generate.Solids({ 1, 4, 7, 10 });
191
192         player = &AddEntity();
193         player->Name("player");
194         player->Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
195         player->WorldCollidable(true);
196         player->Position(config.spawn);
197
198         chunks.GenerateSurrounding(player->ChunkCoords());
199 }
200
201
202 namespace {
203
204 struct Candidate {
205         Chunk *chunk;
206         float dist;
207 };
208
209 std::vector<Candidate> candidates;
210
211 }
212
213 bool World::Intersection(
214         const Ray &ray,
215         const glm::mat4 &M,
216         Chunk *&chunk,
217         int &blkid,
218         float &dist,
219         glm::vec3 &normal
220 ) {
221         candidates.clear();
222
223         for (Chunk &cur_chunk : chunks.Loaded()) {
224                 float cur_dist;
225                 if (cur_chunk.Intersection(ray, M * cur_chunk.Transform(player->ChunkCoords()), cur_dist)) {
226                         candidates.push_back({ &cur_chunk, cur_dist });
227                 }
228         }
229
230         if (candidates.empty()) return false;
231
232         chunk = nullptr;
233         dist = std::numeric_limits<float>::infinity();
234         blkid = -1;
235
236         for (Candidate &cand : candidates) {
237                 if (cand.dist > dist) continue;
238                 int cur_blkid;
239                 float cur_dist;
240                 glm::vec3 cur_normal;
241                 if (cand.chunk->Intersection(ray, M * cand.chunk->Transform(player->ChunkCoords()), cur_blkid, cur_dist, cur_normal)) {
242                         if (cur_dist < dist) {
243                                 chunk = cand.chunk;
244                                 blkid = cur_blkid;
245                                 dist = cur_dist;
246                                 normal = cur_normal;
247                         }
248                 }
249         }
250
251         return chunk;
252 }
253
254 bool World::Intersection(const Entity &e, std::vector<WorldCollision> &col) {
255         AABB box = e.Bounds();
256         glm::mat4 M = e.Transform(player->ChunkCoords());
257         bool any = false;
258         // TODO: this only needs to check the chunks surrounding the entity's chunk position
259         //       need find out if that is quicker than the rough chunk bounds test
260         for (Chunk &cur_chunk : chunks.Loaded()) {
261                 if (cur_chunk.Intersection(box, M, cur_chunk.Transform(player->ChunkCoords()), col)) {
262                         any = true;
263                 }
264         }
265         return any;
266 }
267
268
269 Chunk &World::PlayerChunk() {
270         return chunks.ForceLoad(player->ChunkCoords());
271 }
272
273 Chunk &World::Next(const Chunk &to, const glm::ivec3 &dir) {
274         const Chunk::Pos tgt_pos = to.Position() + dir;
275         return chunks.ForceLoad(tgt_pos);
276 }
277
278
279 namespace {
280
281 std::vector<WorldCollision> col;
282
283 }
284
285 void World::Update(int dt) {
286         for (Entity &entity : entities) {
287                 entity.Update(dt);
288         }
289         for (Entity &entity : entities) {
290                 col.clear();
291                 if (entity.WorldCollidable() && Intersection(entity, col)) {
292                         // entity collides with the world
293                         Resolve(entity, col);
294                 }
295         }
296         for (auto iter = entities.begin(), end = entities.end(); iter != end;) {
297                 if (iter->CanRemove()) {
298                         iter = entities.erase(iter);
299                 } else {
300                         ++iter;
301                 }
302         }
303         chunks.Rebase(player->ChunkCoords());
304         chunks.Update(dt);
305 }
306
307 void World::Resolve(Entity &e, std::vector<WorldCollision> &col) {
308         // determine displacement for each cardinal axis and move entity accordingly
309         glm::vec3 min_disp(0.0f);
310         glm::vec3 max_disp(0.0f);
311         for (const WorldCollision &c : col) {
312                 if (!c.Blocks()) continue;
313                 glm::vec3 local_disp(c.normal * c.depth);
314                 // swap if neccessary (normal may point away from the entity)
315                 if (dot(c.normal, e.Position() - c.BlockCoords()) < 0) {
316                         local_disp *= -1;
317                 }
318                 min_disp = min(min_disp, local_disp);
319                 max_disp = max(max_disp, local_disp);
320         }
321         // for each axis
322         // if only one direction is set, use that as the final
323         // if both directions are set, use average
324         glm::vec3 final_disp(0.0f);
325         for (int axis = 0; axis < 3; ++axis) {
326                 if (std::abs(min_disp[axis]) > std::numeric_limits<float>::epsilon()) {
327                         if (std::abs(max_disp[axis]) > std::numeric_limits<float>::epsilon()) {
328                                 final_disp[axis] = (min_disp[axis] + max_disp[axis]) * 0.5f;
329                         } else {
330                                 final_disp[axis] = min_disp[axis];
331                         }
332                 } else if (std::abs(max_disp[axis]) > std::numeric_limits<float>::epsilon()) {
333                         final_disp[axis] = max_disp[axis];
334                 }
335         }
336         e.Move(final_disp);
337 }
338
339
340 void World::Render(Viewport &viewport) {
341         viewport.WorldPosition(player->Transform(player->ChunkCoords()));
342
343         BlockLighting &chunk_prog = viewport.ChunkProgram();
344         chunk_prog.SetTexture(block_tex);
345         chunk_prog.SetFogDensity(fog_density);
346
347         for (Chunk &chunk : chunks.Loaded()) {
348                 glm::mat4 m(chunk.Transform(player->ChunkCoords()));
349                 chunk_prog.SetM(m);
350                 glm::mat4 mvp(chunk_prog.GetVP() * m);
351                 if (!CullTest(Chunk::Bounds(), mvp)) {
352                         chunk.Draw();
353                 }
354         }
355
356         DirectionalLighting &entity_prog = viewport.EntityProgram();
357         entity_prog.SetLightDirection(light_direction);
358         entity_prog.SetFogDensity(fog_density);
359
360         for (Entity &entity : entities) {
361                 if (entity.HasShape()) {
362                         entity_prog.SetM(entity.Transform(player->ChunkCoords()));
363                         entity.Draw();
364                 }
365         }
366 }
367
368 }