]> git.localhorst.tv Git - blank.git/blob - src/world/World.cpp
load block types from data file
[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 <limits>
9 #include <glm/gtx/io.hpp>
10 #include <glm/gtx/transform.hpp>
11
12
13 namespace blank {
14
15 World::World(const Assets &assets, const Config &config, const WorldSave &save)
16 : block_type()
17 , block_tex()
18 , generate(config.gen)
19 , chunks(config.load, block_type, generate, save)
20 , player()
21 , entities()
22 , light_direction(config.light_direction)
23 , fog_density(config.fog_density) {
24         block_tex.Bind();
25         block_tex.Reserve(16, 16, 4, Format());
26         assets.LoadTexture("debug", block_tex, 0);
27         assets.LoadTexture("rock-1", block_tex, 1);
28         assets.LoadTexture("rock-2", block_tex, 2);
29         assets.LoadTexture("rock-3", block_tex, 3);
30         block_tex.FilterNearest();
31
32         assets.LoadBlockTypes("default", block_type);
33
34         generate.Space(0);
35         generate.Light(13);
36         generate.Solids({ 1, 4, 7, 10 });
37
38         player = &AddEntity();
39         player->Name("player");
40         player->Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
41         player->WorldCollidable(true);
42         player->Position(config.spawn);
43
44         chunks.QueueSurrounding(player->ChunkCoords());
45 }
46
47
48 namespace {
49
50 struct Candidate {
51         Chunk *chunk;
52         float dist;
53 };
54
55 std::vector<Candidate> candidates;
56
57 }
58
59 bool World::Intersection(
60         const Ray &ray,
61         const glm::mat4 &M,
62         Chunk *&chunk,
63         int &blkid,
64         float &dist,
65         glm::vec3 &normal
66 ) {
67         candidates.clear();
68
69         for (Chunk &cur_chunk : chunks.Loaded()) {
70                 float cur_dist;
71                 if (cur_chunk.Intersection(ray, M * cur_chunk.Transform(player->ChunkCoords()), cur_dist)) {
72                         candidates.push_back({ &cur_chunk, cur_dist });
73                 }
74         }
75
76         if (candidates.empty()) return false;
77
78         chunk = nullptr;
79         dist = std::numeric_limits<float>::infinity();
80         blkid = -1;
81
82         for (Candidate &cand : candidates) {
83                 if (cand.dist > dist) continue;
84                 int cur_blkid;
85                 float cur_dist;
86                 glm::vec3 cur_normal;
87                 if (cand.chunk->Intersection(ray, M * cand.chunk->Transform(player->ChunkCoords()), cur_blkid, cur_dist, cur_normal)) {
88                         if (cur_dist < dist) {
89                                 chunk = cand.chunk;
90                                 blkid = cur_blkid;
91                                 dist = cur_dist;
92                                 normal = cur_normal;
93                         }
94                 }
95         }
96
97         return chunk;
98 }
99
100 bool World::Intersection(const Entity &e, std::vector<WorldCollision> &col) {
101         AABB box = e.Bounds();
102         glm::mat4 M = e.Transform(player->ChunkCoords());
103         bool any = false;
104         // TODO: this only needs to check the chunks surrounding the entity's chunk position
105         //       need find out if that is quicker than the rough chunk bounds test
106         for (Chunk &cur_chunk : chunks.Loaded()) {
107                 if (cur_chunk.Intersection(box, M, cur_chunk.Transform(player->ChunkCoords()), col)) {
108                         any = true;
109                 }
110         }
111         return any;
112 }
113
114
115 Chunk &World::PlayerChunk() {
116         return chunks.ForceLoad(player->ChunkCoords());
117 }
118
119 Chunk &World::Next(const Chunk &to, const glm::ivec3 &dir) {
120         const Chunk::Pos tgt_pos = to.Position() + dir;
121         return chunks.ForceLoad(tgt_pos);
122 }
123
124
125 namespace {
126
127 std::vector<WorldCollision> col;
128
129 }
130
131 void World::Update(int dt) {
132         for (Entity &entity : entities) {
133                 entity.Update(dt);
134         }
135         for (Entity &entity : entities) {
136                 col.clear();
137                 if (entity.WorldCollidable() && Intersection(entity, col)) {
138                         // entity collides with the world
139                         Resolve(entity, col);
140                 }
141         }
142         for (auto iter = entities.begin(), end = entities.end(); iter != end;) {
143                 if (iter->CanRemove()) {
144                         iter = entities.erase(iter);
145                 } else {
146                         ++iter;
147                 }
148         }
149         chunks.Rebase(player->ChunkCoords());
150         chunks.Update(dt);
151 }
152
153 void World::Resolve(Entity &e, std::vector<WorldCollision> &col) {
154         // determine displacement for each cardinal axis and move entity accordingly
155         glm::vec3 min_disp(0.0f);
156         glm::vec3 max_disp(0.0f);
157         for (const WorldCollision &c : col) {
158                 if (!c.Blocks()) continue;
159                 glm::vec3 local_disp(c.normal * c.depth);
160                 // swap if neccessary (normal may point away from the entity)
161                 if (dot(c.normal, e.Position() - c.BlockCoords()) < 0) {
162                         local_disp *= -1;
163                 }
164                 min_disp = min(min_disp, local_disp);
165                 max_disp = max(max_disp, local_disp);
166         }
167         // for each axis
168         // if only one direction is set, use that as the final
169         // if both directions are set, use average
170         glm::vec3 final_disp(0.0f);
171         for (int axis = 0; axis < 3; ++axis) {
172                 if (std::abs(min_disp[axis]) > std::numeric_limits<float>::epsilon()) {
173                         if (std::abs(max_disp[axis]) > std::numeric_limits<float>::epsilon()) {
174                                 final_disp[axis] = (min_disp[axis] + max_disp[axis]) * 0.5f;
175                         } else {
176                                 final_disp[axis] = min_disp[axis];
177                         }
178                 } else if (std::abs(max_disp[axis]) > std::numeric_limits<float>::epsilon()) {
179                         final_disp[axis] = max_disp[axis];
180                 }
181         }
182         e.Move(final_disp);
183 }
184
185
186 void World::Render(Viewport &viewport) {
187         viewport.WorldPosition(player->Transform(player->ChunkCoords()));
188
189         BlockLighting &chunk_prog = viewport.ChunkProgram();
190         chunk_prog.SetTexture(block_tex);
191         chunk_prog.SetFogDensity(fog_density);
192
193         for (Chunk &chunk : chunks.Loaded()) {
194                 glm::mat4 m(chunk.Transform(player->ChunkCoords()));
195                 chunk_prog.SetM(m);
196                 glm::mat4 mvp(chunk_prog.GetVP() * m);
197                 if (!CullTest(Chunk::Bounds(), mvp)) {
198                         chunk.Draw();
199                 }
200         }
201
202         DirectionalLighting &entity_prog = viewport.EntityProgram();
203         entity_prog.SetLightDirection(light_direction);
204         entity_prog.SetFogDensity(fog_density);
205
206         for (Entity &entity : entities) {
207                 entity.Render(entity.ChunkTransform(player->ChunkCoords()), entity_prog);
208         }
209 }
210
211 }