#include "world.h" World::World(){} World::~World(){} Array2d& World::GetChunks() { return m_chunks; } Chunk* World::ChunkAt(float x, float y, float z) const { int cx = (int)x / CHUNK_SIZE_X; int cz = (int)z / CHUNK_SIZE_Z; if (x < 0 || y < 0 || z < 0 || x >= WORLD_SIZE_X * CHUNK_SIZE_X || z >= CHUNK_SIZE_Z * WORLD_SIZE_Y || y > CHUNK_SIZE_Y) return 0; return m_chunks.Get(cx, cz); } Chunk* World::ChunkAt(const Vector3f& pos) const { return ChunkAt(pos.x, pos.y, pos.z); } BlockType World::BlockAt(float x, float y, float z, BlockType defaultBlockType) const { Chunk* c = ChunkAt(x, y, z); if (!c) return defaultBlockType; int bx = (int)x % CHUNK_SIZE_X; int by = (int)y % CHUNK_SIZE_Y; int bz = (int)z % CHUNK_SIZE_Z; return c->GetBlock(bx, by, bz); } BlockType World::BlockAt(const Vector3f& pos, BlockType defaultBlockType) const { return BlockAt(pos.x, pos.y, pos.z, defaultBlockType); } void World::TransposeWorld(Player& player) { int x = 0; int y = 0; if (player.GetPosition().x > (WORLD_SIZE_X * CHUNK_SIZE_X) * .66f) ++x; else if (player.GetPosition().x < (WORLD_SIZE_X * CHUNK_SIZE_X) * .33f) --x; if (player.GetPosition().z > (WORLD_SIZE_Y * CHUNK_SIZE_Z) * .66f) ++y; else if (player.GetPosition().z < (WORLD_SIZE_Y * CHUNK_SIZE_Z) * .33f) --y; if (!x && !y) return; if (x != 0) for (int ay = 0; ay < WORLD_SIZE_Y; ++ay) for (int ax = 0; ax < abs(x); ++ay) if (ChunkAt(x < 0 ? (WORLD_SIZE_X - 1 - ax) * WORLD_SIZE_X : ax, 1, ay * WORLD_SIZE_Y)) m_tbDeleted.push_back(std::move(m_chunks.Get(x < 0? WORLD_SIZE_X - 1 - ax: ax, ay))); if (y != 0) for (int ax = 0; ax < WORLD_SIZE_X; ++ax) for (int ay = 0; ay < abs(y); ++ay) if (ChunkAt(ax * WORLD_SIZE_X , 1, y < 0 ? (WORLD_SIZE_Y - 1 - ay) * WORLD_SIZE_Y : ay)) m_tbDeleted.push_back(std::move(m_chunks.Get(ax, y < 0 ? WORLD_SIZE_Y - 1 - ay: ay))); for (int ax = 0; ax < WORLD_SIZE_X; ++ax) for (int ay = 0; ay < WORLD_SIZE_Y; ++ay) if (ax + x < WORLD_SIZE_X && ax + x > 0 && ay + y < WORLD_SIZE_Y && ay + y > 0) m_chunks.Set(x > 0? ax: ax + x, y > 0? ay: ay + y, m_chunks.Get(x < 0 ? ax : ax + x, y < 0 ? ay : ay + y)); if (x != 0) for (int ay = 0; ay < WORLD_SIZE_Y; ++ay) for (int ax = 0; ax < abs(x); ++ay) m_chunks.Set(x > 0 ? WORLD_SIZE_X - 1 - ax : ax, ay, nullptr); if (y != 0) for (int ax = 0; ax < WORLD_SIZE_X; ++ax) for (int ay = 0; ay < abs(y); ++ay) m_chunks.Set(ax, y > 0 ? WORLD_SIZE_Y - 1 - ay : ay, nullptr); m_center[0] += x; m_center[1] += y; player.Transpose(x, y); } void World::CleanUpWorld(int& deleteframes, bool clear = false) { if (clear) m_tbDeleted.clear(); if (!m_tbDeleted.empty() && !deleteframes) { m_tbDeleted.pop_back(); deleteframes = FRAMES_DELETE_CHUNKS; } } Chunk* World::RetrieveChunk(int x, int y) { for (int index = 0; index < m_tbDeleted.size(); ++index) { int cx, cy; m_tbDeleted.at(index)->GetPosition(cx, cy); if (cx == x && cy == y) return std::move(m_tbDeleted.at(index)); } return nullptr; } void World::GetScope(int& x, int& y) { x = m_center[0]; y = m_center[1]; } void World::Update(int& rendercount, int& badhitcount, Player& player, Transformation& world, Shader& shader, TextureAtlas& atlas, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]) { atlas.Bind(); TransposeWorld(player); RenderWorld(rendercount, badhitcount, player, world, shader); UpdateWorld(player, perlin, blockinfo); shader.Disable(); } bool World::GenerateChunk(int chx, int chy, Perlin& perlin) { if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && chx >= 0 && chy >= 0) if (!ChunkAt(chx, 1, chy)) { for (int index = 0; index < m_tbDeleted.size(); ++index) { // Vérifie l'existence d'un chunk dans le buffer de suppression avec sa position. int x, y; m_tbDeleted.at(index)->GetPosition(x, y); if (chx / CHUNK_SIZE_X + m_center[0] == x && chy / CHUNK_SIZE_Z + m_center[1] == y) { GetChunks().Set(chx / CHUNK_SIZE_X, chy / CHUNK_SIZE_Z, std::move(m_tbDeleted.at(index))); return true; } } std::ostringstream pos; // Vérifie l'existence d'un fichier .chunk avec sa position. pos << CHUNK_PATH << chx / CHUNK_SIZE_X + m_center[0] << '_' << chy / CHUNK_SIZE_Z + m_center[1] << ".chunk"; std::ifstream input(pos.str().c_str(), std::fstream::binary); if (input.fail()) { GetChunks().Set(chx / CHUNK_SIZE_X, chy / CHUNK_SIZE_Z, new Chunk(chx / CHUNK_SIZE_X + m_center[0], chy / CHUNK_SIZE_Z + m_center[1])); Chunk* chunk = GetChunks().Get(chx / CHUNK_SIZE_X, chy / CHUNK_SIZE_Z); for (int x = 0; x < CHUNK_SIZE_X; ++x) for (int z = 0; z < CHUNK_SIZE_Z; ++z) { Vector3f noise; noise.x = x * (CHUNK_SIZE_X + 1) + (CHUNK_SIZE_X - 1) * (chx + m_center[0]); noise.y = 0; noise.z = z * (CHUNK_SIZE_Z + 1) + (CHUNK_SIZE_Z - 1) * (chy + m_center[1]); noise.Normalize(); float height = perlin.Get(noise.x, noise.z) * 3 - 32; for (int y = 0; y <= (int)height % CHUNK_SIZE_Y; ++y) chunk->SetBlock(x, y, z, BTYPE_METAL, this); } for (int x = 0; x < CHUNK_SIZE_X; ++x) for (int z = 0; z < CHUNK_SIZE_Z; ++z) { Vector3f noise; noise.x = x * (CHUNK_SIZE_X + 1) + (CHUNK_SIZE_X - 1) * (chx + m_center[0]); noise.y = 0; noise.z = z * (CHUNK_SIZE_Z + 1) + (CHUNK_SIZE_Z - 1) * (chy + m_center[1]); noise.Normalize(); float height = perlin.Get(noise.x, noise.z) + 16; for (int y = 0; y <= (int)height % CHUNK_SIZE_Y; ++y) { if (chunk->GetBlock(x, y, z) == BTYPE_AIR) chunk->SetBlock(x, y, z, BTYPE_GRASS, this); } } for (int x = 0; x < CHUNK_SIZE_X; ++x) for (int z = 0; z < CHUNK_SIZE_Z; ++z) { for (int y = 0; y <= 10; ++y) { if (chunk->GetBlock(x, y, z) == BTYPE_AIR) chunk->SetBlock(x, y, z, BTYPE_ICE, this); } } for (int x = 0; x < CHUNK_SIZE_X; ++x) for (int z = 0; z < CHUNK_SIZE_Z; ++z) { for (int y = 0; y < CHUNK_SIZE_Y; ++y) { Vector3f noise; noise.x = x * (CHUNK_SIZE_X + 1) + (CHUNK_SIZE_X - 1) * (chx + m_center[0]); noise.y = (x + z) * CHUNK_SIZE_Y + m_center[0]; noise.z = z * (CHUNK_SIZE_Z + 1) + (CHUNK_SIZE_Z - 1) * (chy + m_center[1]); noise.Normalize(); float height = perlin.Get(noise.x, noise.y, noise.z); if (chunk->GetBlock(x, y, z) != BTYPE_AIR && height > 18) chunk->SetBlock(x, y, z, BTYPE_DIRT, this); } } } else { input.seekg(0, std::ios_base::end); int size = input.tellg(); input.seekg(0, std::ios_base::beg); char* data = new char[size]; input.read(data, size); input.close(); GetChunks().Set(chx / CHUNK_SIZE_X, chy / CHUNK_SIZE_Z, new Chunk(chx / CHUNK_SIZE_X + m_center[0], chy / CHUNK_SIZE_Z + m_center[1], data)); delete[] data; } std::cout << "Chunk generated: " << chx / CHUNK_SIZE_X + m_center[0] << ", " << chy / CHUNK_SIZE_Z + m_center[1] << std::endl; return true; } return false; } void World::UpdateChunk(int& generates, int& updates, int chx, int chy, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]) { if (generates == 0 && GenerateChunk(chx, chy, perlin)) generates = FRAMES_RENDER_CHUNKS; if (updates == 0 && ChunkAt(chx, 1, chy) && ChunkAt(chx, 1, chy)->IsDirty()) { ChunkAt(chx, 1, chy)->Update(blockinfo, this); updates = FRAMES_UPDATE_CHUNKS; } } void World::ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block) { Vector3f currentPos = player.GetPosition(); Vector3f currentBlock = currentPos; Vector3f ray = player.GetDirection(); bool found = false; if (block) return; while ((currentPos - currentBlock).Length() <= MAX_SELECTION_DISTANCE && !found) { currentBlock += ray / 10.f; BlockType bt = BlockAt(currentBlock); if (bt != BTYPE_AIR) found = true; } if (found) if (blockType != BTYPE_AIR) { found = false; while ((currentPos - currentBlock).Length() >= 1.7f && !found) { currentBlock -= ray / 10.f; BlockType bt = BlockAt(currentBlock); if (bt == BTYPE_AIR) { // Vérification pour être sûr que le bloc à changer n'est pas dans le joueur. int Bx = (int)currentBlock.x; int By = (int)currentBlock.y; int Bz = (int)currentBlock.z; int Px = (int)currentPos.x; int PyA = (int)currentPos.y; int PyB = (int)(currentPos.y - .9f); int PyC = (int)(currentPos.y - 1.7f); int Pz = (int)currentPos.z; if (!(Bx == Px && (By == PyA || By == PyB || By == PyC) && Bz == Pz)) found = true; } } } if (found && (int)currentBlock.y < CHUNK_SIZE_Y) { int bx = (int)currentBlock.x % CHUNK_SIZE_X; int by = (int)currentBlock.y % CHUNK_SIZE_Y; int bz = (int)currentBlock.z % CHUNK_SIZE_Z; ChunkAt(currentBlock)->SetBlock(bx, by, bz, blockType, this); ChunkAt(currentBlock)->MakeModified(); block = true; } } void World::RenderWorld(int& rendercount, int& badhitcount, Player& player, Transformation& world, Shader& shader) { shader.Use(); rendercount = 0; badhitcount = 0; static std::vector renderManifest; Vector3f angle; Vector3f cursor; Vector3f direct = player.GetDirection(); Vector3f pos = player.GetPosition() - direct; direct.y = 0; direct.Normalize(); pos.y = 1; renderManifest.clear(); for (int dist = VIEW_DISTANCE; dist >= 0; dist -= CHUNK_SIZE_X) { // Configuration du radar. angle.x = direct.z + direct.x; angle.y = 0; angle.z = direct.z - direct.x; angle.Normalize(); float sinus = .01745240643; // sin(1 degré) float cosinus = .99984769515; // cos(1 degré) int echantillons = 90; for (int radar = 0; radar < echantillons; ++radar) { float x = angle.x; float z = angle.z; angle.x = x * cosinus - z * sinus; angle.z = z * cosinus + x * sinus; angle.Normalize(); cursor = pos - direct * CHUNK_SIZE_X * 2 + angle * dist; if (cursor.y >= 128.f || cursor.y >= 0.f) cursor.y = 1; bool valide = true; if (ChunkAt(cursor)) { int chx, chy; ChunkAt(cursor)->GetPosition(chx, chy); for (int index = 0; index < renderManifest.size(); ++index) if (renderManifest[index] == Vector3i(chx, 0, chy)) { valide = false; ++badhitcount; } if (valide) { world.ApplyTranslation((chx - m_center[0]) * CHUNK_SIZE_X, 0, (chy - m_center[1]) * CHUNK_SIZE_Z); world.Use(); float dist = (pos - cursor).Length(); float blend = ((float)VIEW_DISTANCE - dist * 2.f + 128.f) / (float)VIEW_DISTANCE; glBlendColor(0.f, 0.f, 0.f, blend); ChunkAt(cursor)->Render(); world.ApplyTranslation(-(chx - m_center[0]) * CHUNK_SIZE_X, 0, -(chy - m_center[1]) * CHUNK_SIZE_Z); renderManifest.push_back(Vector3i(chx, 0, chy)); ++rendercount; } } } } shader.Disable(); }; void World::UpdateWorld(Player& player, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]) { int cx = player.GetPosition().x; int cy = player.GetPosition().z; static int frameGenerate = 0; static int frameUpdate = 0; static int frameDelete = 0; int side = 0; if (frameGenerate > 0) --frameGenerate; if (frameUpdate > 0) --frameUpdate; if (frameDelete > 0) --frameDelete; if (!frameGenerate || !frameUpdate) while (side * CHUNK_SIZE_X <= VIEW_DISTANCE * 2) { int tx = -side, ty = -side; for (; tx <= side; ++tx) UpdateChunk(frameGenerate, frameUpdate, cx + tx * CHUNK_SIZE_X, cy + ty * CHUNK_SIZE_Z, perlin, blockinfo); for (; ty <= side; ++ty) UpdateChunk(frameGenerate, frameUpdate, cx + tx * CHUNK_SIZE_X, cy + ty * CHUNK_SIZE_Z, perlin, blockinfo); for (; tx >= -side; --tx) UpdateChunk(frameGenerate, frameUpdate, cx + tx * CHUNK_SIZE_X, cy + ty * CHUNK_SIZE_Z, perlin, blockinfo); for (; ty >= -side; --ty) UpdateChunk(frameGenerate, frameUpdate, cx + tx * CHUNK_SIZE_X, cy + ty * CHUNK_SIZE_Z, perlin, blockinfo); ++side; } CleanUpWorld(frameDelete); }