#include #include "client/World.h" #include "client/rendering/Engine.h" #include "client/rendering/Mesh.h" #include "common/BlockStorage.h" #include "rendering/FileTexture.h" #include "utils/List.h" #include "utils/Logger.h" #include "utils/Random.h" static BlockStorage blocks{7, 7}; static List entities; static Mesh mesh; static FileTexture texture; static bool dirty = true; bool World::init() { mesh.init(); Error error = texture.load("resources/textures.png", 4); if(error.has()) { LOG_ERROR(error.message); } return error.has(); } void World::addEntity(Entity* e) { entities.add(e); } void World::removeEntity(Entity* e) { for(int i = 0; i < entities.getLength(); i++) { if(entities[i] == e) { entities.removeBySwap(i); return; } } } static List getBoxes(const Box& box) { int minX = floorf(box.getMin()[0]); int minY = floorf(box.getMin()[1]); int minZ = floorf(box.getMin()[2]); int maxX = floorf(box.getMax()[0]); int maxY = floorf(box.getMax()[1]); int maxZ = floorf(box.getMax()[2]); List boxes; for(int x = minX; x <= maxX; x++) { for(int y = minY; y <= maxY; y++) { for(int z = minZ; z <= maxZ; z++) { Block::addBoxes(blocks.get(x, y, z), boxes, Vector3(static_cast(x), static_cast(y), static_cast(z))); } } } return boxes; } static Vector3 limitMove(const Entity& e, Vector3 move) { Box box = e.getBox(); List boxes = getBoxes(box.expand(move)); if(boxes.getLength() == 0) { return move; } Vector3 realMove; constexpr float step = 0.05f; while(move[0] != 0.0f || move[1] != 0.0f || move[2] != 0.0f) { for(int i = 0; i < 3; i++) { Vector3 old = realMove; if(move[i] > step) { realMove[i] += step; move[i] -= step; } else if(move[i] < -step) { realMove[i] -= step; move[i] += step; } else if(move[i] != 0.0f) { realMove[i] += move[i]; move[i] = 0.0f; } Box moved = box.offset(realMove); for(const Box& box : boxes) { if(box.collidesWith(moved)) { move[i] = 0.0f; realMove = old; break; } } } } return realMove; } void World::tick() { for(Entity* e : entities) { e->tick(); if(!e->skip) { Vector3 move = limitMove(*e, e->getVelocity()); e->move(move); } } } static bool isAir(int x, int y, int z) { return blocks.get(x, y, z) == 0; } static void addCube(TypedBuffer& buffer, float x, float y, float z) { Vector3 v000(x, y, z); Vector3 v001(x, y, z + 1); Vector3 v010(x, y + 1, z); Vector3 v011(x, y + 1, z + 1); Vector3 v100(x + 1, y, z); Vector3 v101(x + 1, y, z + 1); Vector3 v110(x + 1, y + 1, z); Vector3 v111(x + 1, y + 1, z + 1); Vector2 t1(0.1875f, 0.0f); Vector2 t2(0.25f, 0.0f); Vector2 t3(0.25f, 0.0625f); Vector2 t4(0.1875f, 0.0625f); if(isAir(x, y - 1, z)) { Vector2 tb(0.125f, 0.0625f); buffer.add(Triangle(Vertex(v000, Vector2(0.125f, 0.0f)), Vertex(v100, t1), Vertex(v001, tb))); buffer.add( Triangle(Vertex(v100, t1), Vertex(v101, t4), Vertex(v001, tb))); } if(isAir(x, y + 1, z)) { Vector2 tt(0.3125f, 0.0f); buffer.add( Triangle(Vertex(v010, t2), Vertex(v011, t3), Vertex(v110, tt))); buffer.add(Triangle(Vertex(v110, tt), Vertex(v011, t3), Vertex(v111, Vector2(0.3125f, 0.0625f)))); } if(isAir(x - 1, y, z)) { buffer.add( Triangle(Vertex(v000, t4), Vertex(v001, t3), Vertex(v010, t1))); buffer.add( Triangle(Vertex(v001, t3), Vertex(v011, t2), Vertex(v010, t1))); } if(isAir(x + 1, y, z)) { buffer.add( Triangle(Vertex(v100, t3), Vertex(v110, t2), Vertex(v101, t4))); buffer.add( Triangle(Vertex(v101, t4), Vertex(v110, t2), Vertex(v111, t1))); } if(isAir(x, y, z + 1)) { buffer.add( Triangle(Vertex(v001, t4), Vertex(v101, t3), Vertex(v011, t1))); buffer.add( Triangle(Vertex(v111, t2), Vertex(v011, t1), Vertex(v101, t3))); } if(isAir(x, y, z - 1)) { buffer.add( Triangle(Vertex(v000, t3), Vertex(v010, t2), Vertex(v100, t4))); buffer.add( Triangle(Vertex(v110, t1), Vertex(v100, t4), Vertex(v010, t2))); } } static void rebuildRenderData() { TypedBuffer buffer(100); for(int x = 0; x < blocks.getSize(); x++) { for(int y = 0; y < blocks.getHeight(); y++) { for(int z = 0; z < blocks.getSize(); z++) { if(!isAir(x, y, z)) { addCube(buffer, x, y, z); } } } } mesh.build(buffer); LOG_DEBUG("world render update"); } void World::render() { if(dirty) { rebuildRenderData(); dirty = false; } texture.bindTo(0); for(int x = -1; x <= 1; x++) { for(int z = -1; z <= 1; z++) { Engine::matrix .translateTo(blocks.getSize() * x, 0.0f, blocks.getSize() * z) .update(); mesh.draw(); } } } int World::getHeight() { return blocks.getHeight(); } void World::setBlock(int x, int y, int z, Block::Id block) { dirty = true; blocks.set(x, y, z, block); }