3 #include "WorldCollision.hpp"
4 #include "../app/Assets.hpp"
5 #include "../graphics/Format.hpp"
6 #include "../graphics/Viewport.hpp"
10 #include <glm/gtx/io.hpp>
11 #include <glm/gtx/transform.hpp>
16 World::World(const Assets &assets, const Config &config)
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 }})
22 , generate(config.gen)
23 , chunks(config.load, blockType, generate)
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 };
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();
41 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
43 type.label = "White Block";
44 type.block_light = true;
45 type.collision = true;
46 type.collide_block = true;
47 type.fill = block_fill;
51 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &slabShape);
53 type.label = "White Slab";
54 type.block_light = true;
55 type.collision = true;
56 type.collide_block = true;
57 type.fill = slab_fill;
61 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &stairShape);
63 type.label = "White Stair";
64 type.block_light = true;
65 type.collision = true;
66 type.collide_block = true;
67 type.fill = stair_fill;
72 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &blockShape);
74 type.label = "Red Block";
75 type.block_light = true;
76 type.collision = true;
77 type.collide_block = true;
78 type.fill = block_fill;
82 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &slabShape);
84 type.label = "Red Slab";
85 type.block_light = true;
86 type.collision = true;
87 type.collide_block = true;
88 type.fill = slab_fill;
92 BlockType type(true, { 1.0f, 0.0f, 0.0f }, &stairShape);
94 type.label = "Red Stair";
95 type.block_light = true;
96 type.collision = true;
97 type.collide_block = true;
98 type.fill = stair_fill;
103 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &blockShape);
105 type.label = "Green Block";
106 type.block_light = true;
107 type.collision = true;
108 type.collide_block = true;
109 type.fill = block_fill;
113 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &slabShape);
115 type.label = "Green Slab";
116 type.block_light = true;
117 type.collision = true;
118 type.collide_block = true;
119 type.fill = slab_fill;
123 BlockType type(true, { 0.0f, 1.0f, 0.0f }, &stairShape);
125 type.label = "Green Stair";
126 type.block_light = true;
127 type.collision = true;
128 type.collide_block = true;
129 type.fill = stair_fill;
134 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &blockShape);
136 type.label = "Blue Block";
137 type.block_light = true;
138 type.collision = true;
139 type.collide_block = true;
140 type.fill = block_fill;
144 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &slabShape);
146 type.label = "Blue Slab";
147 type.block_light = true;
148 type.collision = true;
149 type.collide_block = true;
150 type.fill = slab_fill;
154 BlockType type(true, { 0.0f, 0.0f, 1.0f }, &stairShape);
156 type.label = "Blue Stair";
157 type.block_light = true;
158 type.collision = true;
159 type.collide_block = true;
160 type.fill = stair_fill;
164 { // glowing yellow block
165 BlockType type(true, { 1.0f, 1.0f, 0.0f }, &blockShape);
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;
176 { // the mysterious debug cube
177 BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
179 type.label = "Debug Cube";
181 type.block_light = true;
182 type.collision = true;
183 type.collide_block = true;
184 type.fill = block_fill;
190 generate.Solids({ 1, 4, 7, 10 });
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);
198 chunks.GenerateSurrounding(player->ChunkCoords());
209 std::vector<Candidate> candidates;
213 bool World::Intersection(
223 for (Chunk &cur_chunk : chunks.Loaded()) {
225 if (cur_chunk.Intersection(ray, M * cur_chunk.Transform(player->ChunkCoords()), cur_dist)) {
226 candidates.push_back({ &cur_chunk, cur_dist });
230 if (candidates.empty()) return false;
233 dist = std::numeric_limits<float>::infinity();
236 for (Candidate &cand : candidates) {
237 if (cand.dist > dist) continue;
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) {
254 bool World::Intersection(const Entity &e, std::vector<WorldCollision> &col) {
255 AABB box = e.Bounds();
256 glm::mat4 M = e.Transform(player->ChunkCoords());
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)) {
269 Chunk &World::PlayerChunk() {
270 return chunks.ForceLoad(player->ChunkCoords());
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);
281 std::vector<WorldCollision> col;
285 void World::Update(int dt) {
286 for (Entity &entity : entities) {
289 for (Entity &entity : entities) {
291 if (entity.WorldCollidable() && Intersection(entity, col)) {
292 // entity collides with the world
293 Resolve(entity, col);
296 for (auto iter = entities.begin(), end = entities.end(); iter != end;) {
297 if (iter->CanRemove()) {
298 iter = entities.erase(iter);
303 chunks.Rebase(player->ChunkCoords());
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) {
318 min_disp = min(min_disp, local_disp);
319 max_disp = max(max_disp, local_disp);
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;
330 final_disp[axis] = min_disp[axis];
332 } else if (std::abs(max_disp[axis]) > std::numeric_limits<float>::epsilon()) {
333 final_disp[axis] = max_disp[axis];
340 void World::Render(Viewport &viewport) {
341 viewport.WorldPosition(player->Transform(player->ChunkCoords()));
343 BlockLighting &chunk_prog = viewport.ChunkProgram();
344 chunk_prog.SetTexture(block_tex);
345 chunk_prog.SetFogDensity(fog_density);
347 for (Chunk &chunk : chunks.Loaded()) {
348 glm::mat4 m(chunk.Transform(player->ChunkCoords()));
350 glm::mat4 mvp(chunk_prog.GetVP() * m);
351 if (!CullTest(Chunk::Bounds(), mvp)) {
356 DirectionalLighting &entity_prog = viewport.EntityProgram();
357 entity_prog.SetLightDirection(light_direction);
358 entity_prog.SetFogDensity(fog_density);
360 for (Entity &entity : entities) {
361 if (entity.HasShape()) {
362 entity_prog.SetM(entity.Transform(player->ChunkCoords()));