3 #include "WorldCollision.hpp"
4 #include "../app/Assets.hpp"
5 #include "../graphics/Format.hpp"
6 #include "../graphics/Viewport.hpp"
9 #include <glm/gtx/io.hpp>
10 #include <glm/gtx/transform.hpp>
15 World::World(const Assets &assets, const Config &config, const WorldSave &save)
17 , blockShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }})
18 , stairShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f })
19 , slabShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }})
21 , generate(config.gen)
22 , chunks(config.load, blockType, generate, save)
25 , light_direction(config.light_direction)
26 , fog_density(config.fog_density) {
27 BlockType::Faces block_fill = { true, true, true, true, true, true };
28 BlockType::Faces slab_fill = { false, true, false, false, false, false };
29 BlockType::Faces stair_fill = { false, true, false, false, false, true };
32 block_tex.Reserve(16, 16, 4, Format());
33 assets.LoadTexture("debug", block_tex, 0);
34 assets.LoadTexture("rock-1", block_tex, 1);
35 assets.LoadTexture("rock-2", block_tex, 2);
36 assets.LoadTexture("rock-3", block_tex, 3);
37 block_tex.FilterNearest();
40 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
42 type.label = "White Block";
43 type.block_light = true;
44 type.collision = true;
45 type.collide_block = true;
46 type.fill = block_fill;
50 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &slabShape);
52 type.label = "White Slab";
53 type.block_light = true;
54 type.collision = true;
55 type.collide_block = true;
56 type.fill = slab_fill;
60 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &stairShape);
62 type.label = "White Stair";
63 type.block_light = true;
64 type.collision = true;
65 type.collide_block = true;
66 type.fill = stair_fill;
71 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &blockShape);
73 type.label = "Red Block";
74 type.block_light = true;
75 type.collision = true;
76 type.collide_block = true;
77 type.fill = block_fill;
81 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &slabShape);
83 type.label = "Red Slab";
84 type.block_light = true;
85 type.collision = true;
86 type.collide_block = true;
87 type.fill = slab_fill;
91 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &stairShape);
93 type.label = "Red Stair";
94 type.block_light = true;
95 type.collision = true;
96 type.collide_block = true;
97 type.fill = stair_fill;
102 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &blockShape);
104 type.label = "Green Block";
105 type.block_light = true;
106 type.collision = true;
107 type.collide_block = true;
108 type.fill = block_fill;
112 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &slabShape);
114 type.label = "Green Slab";
115 type.block_light = true;
116 type.collision = true;
117 type.collide_block = true;
118 type.fill = slab_fill;
122 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &stairShape);
124 type.label = "Green Stair";
125 type.block_light = true;
126 type.collision = true;
127 type.collide_block = true;
128 type.fill = stair_fill;
133 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &blockShape);
135 type.label = "Blue Block";
136 type.block_light = true;
137 type.collision = true;
138 type.collide_block = true;
139 type.fill = block_fill;
143 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &slabShape);
145 type.label = "Blue Slab";
146 type.block_light = true;
147 type.collision = true;
148 type.collide_block = true;
149 type.fill = slab_fill;
153 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &stairShape);
155 type.label = "Blue Stair";
156 type.block_light = true;
157 type.collision = true;
158 type.collide_block = true;
159 type.fill = stair_fill;
163 { // glowing yellow block
164 BlockType type(true, { 1.0f, 1.0f, 0.0f }, &blockShape);
166 type.label = "Light";
167 type.luminosity = 15;
168 type.block_light = true;
169 type.collision = true;
170 type.collide_block = true;
171 type.fill = block_fill;
175 { // the mysterious debug cube
176 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
178 type.label = "Debug Cube";
180 type.block_light = true;
181 type.collision = true;
182 type.collide_block = true;
183 type.fill = block_fill;
189 generate.Solids({ 1, 4, 7, 10 });
191 player = &AddEntity();
192 player->Name("player");
193 player->Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
194 player->WorldCollidable(true);
195 player->Position(config.spawn);
197 chunks.QueueSurrounding(player->ChunkCoords());
208 std::vector<Candidate> candidates;
212 bool World::Intersection(
222 for (Chunk &cur_chunk : chunks.Loaded()) {
224 if (cur_chunk.Intersection(ray, M * cur_chunk.Transform(player->ChunkCoords()), cur_dist)) {
225 candidates.push_back({ &cur_chunk, cur_dist });
229 if (candidates.empty()) return false;
232 dist = std::numeric_limits<float>::infinity();
235 for (Candidate &cand : candidates) {
236 if (cand.dist > dist) continue;
239 glm::vec3 cur_normal;
240 if (cand.chunk->Intersection(ray, M * cand.chunk->Transform(player->ChunkCoords()), cur_blkid, cur_dist, cur_normal)) {
241 if (cur_dist < dist) {
253 bool World::Intersection(const Entity &e, std::vector<WorldCollision> &col) {
254 AABB box = e.Bounds();
255 glm::mat4 M = e.Transform(player->ChunkCoords());
257 // TODO: this only needs to check the chunks surrounding the entity's chunk position
258 // need find out if that is quicker than the rough chunk bounds test
259 for (Chunk &cur_chunk : chunks.Loaded()) {
260 if (cur_chunk.Intersection(box, M, cur_chunk.Transform(player->ChunkCoords()), col)) {
268 Chunk &World::PlayerChunk() {
269 return chunks.ForceLoad(player->ChunkCoords());
272 Chunk &World::Next(const Chunk &to, const glm::ivec3 &dir) {
273 const Chunk::Pos tgt_pos = to.Position() + dir;
274 return chunks.ForceLoad(tgt_pos);
280 std::vector<WorldCollision> col;
284 void World::Update(int dt) {
285 for (Entity &entity : entities) {
288 for (Entity &entity : entities) {
290 if (entity.WorldCollidable() && Intersection(entity, col)) {
291 // entity collides with the world
292 Resolve(entity, col);
295 for (auto iter = entities.begin(), end = entities.end(); iter != end;) {
296 if (iter->CanRemove()) {
297 iter = entities.erase(iter);
302 chunks.Rebase(player->ChunkCoords());
306 void World::Resolve(Entity &e, std::vector<WorldCollision> &col) {
307 // determine displacement for each cardinal axis and move entity accordingly
308 glm::vec3 min_disp(0.0f);
309 glm::vec3 max_disp(0.0f);
310 for (const WorldCollision &c : col) {
311 if (!c.Blocks()) continue;
312 glm::vec3 local_disp(c.normal * c.depth);
313 // swap if neccessary (normal may point away from the entity)
314 if (dot(c.normal, e.Position() - c.BlockCoords()) < 0) {
317 min_disp = min(min_disp, local_disp);
318 max_disp = max(max_disp, local_disp);
321 // if only one direction is set, use that as the final
322 // if both directions are set, use average
323 glm::vec3 final_disp(0.0f);
324 for (int axis = 0; axis < 3; ++axis) {
325 if (std::abs(min_disp[axis]) > std::numeric_limits<float>::epsilon()) {
326 if (std::abs(max_disp[axis]) > std::numeric_limits<float>::epsilon()) {
327 final_disp[axis] = (min_disp[axis] + max_disp[axis]) * 0.5f;
329 final_disp[axis] = min_disp[axis];
331 } else if (std::abs(max_disp[axis]) > std::numeric_limits<float>::epsilon()) {
332 final_disp[axis] = max_disp[axis];
339 void World::Render(Viewport &viewport) {
340 viewport.WorldPosition(player->Transform(player->ChunkCoords()));
342 BlockLighting &chunk_prog = viewport.ChunkProgram();
343 chunk_prog.SetTexture(block_tex);
344 chunk_prog.SetFogDensity(fog_density);
346 for (Chunk &chunk : chunks.Loaded()) {
347 glm::mat4 m(chunk.Transform(player->ChunkCoords()));
349 glm::mat4 mvp(chunk_prog.GetVP() * m);
350 if (!CullTest(Chunk::Bounds(), mvp)) {
355 DirectionalLighting &entity_prog = viewport.EntityProgram();
356 entity_prog.SetLightDirection(light_direction);
357 entity_prog.SetFogDensity(fog_density);
359 for (Entity &entity : entities) {
360 entity.Render(entity.ChunkTransform(player->ChunkCoords()), entity_prog);