New Dimension code which deletes mesh & block chunks far away [!BUG]

* Important note: There is a ram leak in this code somewhere, and probably other bugs, writing code at midnight is a bad idea.
master
aurailus 2019-04-23 00:07:17 -07:00
parent 46f455939c
commit fa82be9c59
16 changed files with 298 additions and 212 deletions

View File

@ -6,7 +6,7 @@
GameScene::GameScene(ClientState* state) : Scene(state),
defs("../res/tex/game"),
world(defs),
world(defs, &playerChunkPos),
server("127.0.0.1", 12345),
gameGui(state->renderer->getCamera()->getBufferDimensions()),
@ -31,6 +31,8 @@ GameScene::GameScene(ClientState* state) : Scene(state),
void GameScene::update() {
server.update(player);
playerChunkPos = TransPos::roundPos(*player.getPos() / glm::vec3(TransPos::CHUNK_SIZE));
auto window = state->renderer->getWindow();
player.update(window->input, state->deltaTime, window->getDeltaX(), window->getDeltaY());
@ -49,8 +51,8 @@ void GameScene::update() {
world.loadChunkPacket(p);
}
debugGui.update(player, world, defs, state->fps, (int)world.getMeshChunks()->size(), drawCalls, server.serverSideChunkGens, server.recvPackets);
world.update(TransPos::roundPos(*player.getPos() / glm::vec3(16.0f)));
debugGui.update(player, world, defs, state->fps, world.getMeshChunkCount(), drawCalls, server.serverSideChunkGens, server.recvPackets);
world.update();
if (window->input.isKeyPressed(GLFW_KEY_F1)) {
hudVisible = !hudVisible;
@ -73,7 +75,7 @@ void GameScene::draw() {
renderer.begin();
renderer.enableTexture(&defs.textures().getTexture());
drawCalls = world.render(renderer, TransPos::roundPos(*player.getPos() / glm::vec3(16.0f)));
drawCalls = world.render(renderer);
for (auto entity : entities) {
entity->draw(renderer);

View File

@ -29,6 +29,8 @@ public:
public:
GameDefs defs;
glm::vec3 playerChunkPos;
ServerConnection server;
Player player;
LocalWorld world;

View File

@ -8,7 +8,8 @@
#include "LocalWorld.h"
#include "../../../util/Vec.h"
LocalWorld::LocalWorld(GameDefs& defs) :
LocalWorld::LocalWorld(GameDefs& defs, glm::vec3* playerPos) :
dimension(playerPos),
meshGenStream(defs, dimension),
worldGenStream(55),
defs(defs) {}
@ -18,7 +19,7 @@ void LocalWorld::loadChunkPacket(Packet *p) {
}
void LocalWorld::commitChunk(glm::vec3 pos, std::shared_ptr<BlockChunk> c) {
dimension.addChunk(pos, std::move(c));
dimension.addBlockChunk(std::move(c));
attemptMeshChunk(pos);
}
@ -56,7 +57,7 @@ bool LocalWorld::getAdjacentExists(glm::vec3 pos, glm::vec3 otherPos) {
return false;
}
void LocalWorld::update(glm::vec3 playerChunkPos) {
void LocalWorld::update() {
//Create Finished Messages
auto finishedMeshes = meshGenStream.update();
@ -72,7 +73,8 @@ void LocalWorld::update(glm::vec3 playerChunkPos) {
meshChunks.erase(mesh.pos);
delete oldChunk;
}
meshChunks.insert(std::pair<glm::vec3, MeshChunk*>(mesh.pos, meshChunk));
// meshChunks.insert(std::pair<glm::vec3, MeshChunk*>(mesh.pos, meshChunk));
dimension.addMeshChunk(meshChunk);
lastMeshUpdates++;
}
@ -103,14 +105,7 @@ void LocalWorld::update(glm::vec3 playerChunkPos) {
lastGenUpdates++;
}
//Delete far-out regions
// int toDelete = 0;
// for (const auto &region : dimension.getRegions()) {
// glm::vec3 middlePos = region.second->getRawPos() + glm::vec3(TransPos::REGION_CHUNK_LENGTH / 2);
// float dist = glm::distance(middlePos, playerChunkPos);
// if (dist > 64) toDelete++;
// }
// std::cout << toDelete << ", " << dimension.getRegions().size() << std::endl;
dimension.update();
}
int LocalWorld::getBlock(glm::vec3 pos) {
@ -146,28 +141,10 @@ std::shared_ptr<BlockChunk> LocalWorld::getChunk(glm::vec3 chunkPos) {
return dimension.getChunk(chunkPos);
}
std::unordered_map<glm::vec3, MeshChunk*, VecUtils::compareFunc>* LocalWorld::getMeshChunks() {
return &meshChunks;
int LocalWorld::getMeshChunkCount() {
return dimension.getMeshChunkCount();
}
int LocalWorld::render(Renderer &renderer, glm::vec3 playerChunkPos) {
int count = 0;
for (auto &chunkPair : meshChunks) {
auto chunk = chunkPair.second;
auto diffVec = chunkPair.first - playerChunkPos;
float distance = max(abs(diffVec.x), max(abs(diffVec.y), abs(diffVec.z)));
if (distance < 8) {
FrustumAABB bbox(chunk->getPos(), glm::vec3(TransPos::CHUNK_SIZE));
if (renderer.getCamera()->inFrustum(bbox) != Frustum::OUTSIDE) {
chunk->draw(renderer);
count++;
}
}
}
return count;
}
int LocalWorld::render(Renderer &renderer) {
dimension.render(renderer);
}

View File

@ -26,9 +26,9 @@
class LocalWorld {
public:
explicit LocalWorld(GameDefs& defs);
explicit LocalWorld(GameDefs& defs, glm::vec3* playerPos);
void update(glm::vec3 playerChunkPos);
void update();
void commitChunk(glm::vec3 pos, std::shared_ptr<BlockChunk>);
void remeshChunk(glm::vec3 pos);
@ -38,8 +38,9 @@ public:
void attemptMeshChunk(glm::vec3 pos);
bool getAdjacentExists(glm::vec3 pos, glm::vec3 myPos);
std::unordered_map<glm::vec3, MeshChunk*, VecUtils::compareFunc>* getMeshChunks();
int render(Renderer& render, glm::vec3 playerChunkPos);
// std::unordered_map<glm::vec3, MeshChunk*, VecUtils::compareFunc>* getMeshChunks();
int render(Renderer& render);
int getMeshChunkCount();
std::shared_ptr<BlockChunk> getChunk(glm::vec3 chunkPos);
int getBlock(glm::vec3 pos);

View File

@ -59,8 +59,14 @@ std::vector<MeshGenStream::MeshDetails>* MeshGenStream::update() {
u.chunk = dimension.getChunk(pos);
u.adjacent = getAdjacentsCull(pos);
//Lock it in to allow the thread to edit it.
u.unlocked = false;
if (u.adjacent == nullptr) {
//Some of the adjacent chunks have been GC'd since the task was queued
u.chunk = nullptr;
}
else {
//Lock it in to allow the thread to edit it.
u.unlocked = false;
}
}
}
}
@ -117,6 +123,10 @@ std::vector<bool>* MeshGenStream::getAdjacentsCull(glm::vec3 pos) {
for (int i = 0; i < vectors.size(); i++) {
auto chunk = dimension.getChunk(pos + vectors[i]);
if (chunk == nullptr) {
return nullptr;
}
for (int j = 0; j < TransPos::CHUNK_SIZE; j++) {
for (int k = 0; k < TransPos::CHUNK_SIZE; k++) {

View File

@ -14,3 +14,7 @@ void MeshChunk::build(std::vector<float> *vertices, std::vector<unsigned int> *i
void MeshChunk::setPos(glm::vec3 pos) {
Entity::setPos(pos * glm::vec3(TransPos::CHUNK_SIZE));
}
glm::vec3 MeshChunk::getPos() {
return Entity::getPos() / glm::vec3(TransPos::CHUNK_SIZE);
}

View File

@ -16,11 +16,11 @@ public:
MeshChunk() = default;
void build(std::vector<float> *vertices, std::vector<unsigned int> *indices);
void setPos(glm::vec3 pos);
glm::vec3 getPos();
public:
using Entity::setMesh;
using Entity::draw;
using Entity::getPos;
using Entity::getModelMatrix;
};

View File

@ -14,8 +14,8 @@
class ServerPlayer {
public:
//This 16 shouldn't be converted to TransPos::CHUNK_SIZE, it's just so that my brain can gauge the distance easier.
const static int ACTIVE_RANGE_H = (16 * 16) / TransPos::CHUNK_SIZE;
const static int ACTIVE_RANGE_V = (8 * 16) / TransPos::CHUNK_SIZE;
const static int ACTIVE_RANGE_H = 16;
const static int ACTIVE_RANGE_V = 6;
//TODO: Refactor instances of UUID to username, or create seperate username flag
explicit ServerPlayer(ServerPeer* peer, std::string uuid);

View File

@ -104,7 +104,7 @@ void ServerWorld::update() {
generatedChunks = (int)finished.size();
for (const auto &chunk : finished) {
dimension.addChunk(chunk->pos, chunk);
dimension.addBlockChunk(chunk);
for (auto player : players) {
auto bounds = player->getBounds();

View File

@ -4,52 +4,91 @@
#include "Dimension.h"
void Dimension::addChunk(glm::vec3 pos, std::shared_ptr<BlockChunk> chunk) {
Dimension::Dimension(glm::vec3 *playerPos) {
this->playerPos = playerPos;
}
glm::vec3 regionPos = TransPos::Dimension::regionFromVec(pos);
glm::vec3 regionRawPos = TransPos::Dimension::regionRawFromRegionVec(pos);
void Dimension::addBlockChunk(std::shared_ptr<BlockChunk> chunk) {
if (blockChunks.count(chunk->pos)) {
auto oldChunk = blockChunks[chunk->pos];
chunk->meshChunk = oldChunk->meshChunk;
chunk->meshChunkIter = oldChunk->meshChunkIter;
blockChunks.erase(chunk->pos);
}
blockChunks.insert({chunk->pos, chunk});
}
if (!regions.count(regionPos)) regions.insert({regionPos, new Region(regionPos, regionRawPos)});
Region* region = regions[regionPos];
void Dimension::addMeshChunk(MeshChunk* meshChunk) {
auto blockChunk = blockChunks[meshChunk->getPos()];
glm::vec3 mapBlockPos = TransPos::Dimension::mapBlockOffsetFromRegion(pos);
unsigned int mapBlockInd = TransPos::mapBlockIndFromVec(mapBlockPos);
if (!blockChunk) {
blockChunk = std::make_shared<BlockChunk>();
addBlockChunk(blockChunk);
}
else if (blockChunk->meshChunk != nullptr) {
meshChunks.erase(blockChunk->meshChunkIter);
delete blockChunk->meshChunk;
}
//TODO: Don't use 0, 0, 0
if ((*region)[mapBlockInd] == nullptr) region->set(mapBlockInd, new MapBlock(mapBlockPos, {0, 0, 0}));
MapBlock* mapBlock = (*region)[mapBlockInd];
glm::vec3 chunkPos = TransPos::Dimension::chunkOffsetFromMapBlock(pos);
unsigned int chunkInd = TransPos::chunkIndFromVec(chunkPos);
mapBlock->set(chunkInd, std::move(chunk));
blockChunk->meshChunk = meshChunk;
meshChunks.push_back(meshChunk);
blockChunk->meshChunkIter = (std::list<MeshChunk*>::iterator) --meshChunks.end();
}
std::shared_ptr<BlockChunk> Dimension::getChunk(glm::vec3 pos) {
glm::vec3 regionPos = TransPos::Dimension::regionFromVec(pos);
if (!regions.count(regionPos)) return nullptr;
Region* region = regions[regionPos];
glm::vec3 mapBlockPos = TransPos::Dimension::mapBlockOffsetFromRegion(pos);
unsigned int mapBlockInd = TransPos::mapBlockIndFromVec(mapBlockPos);
if ((*region)[mapBlockInd] == nullptr) return nullptr;
MapBlock* mapBlock = (*region)[mapBlockInd];
glm::vec3 chunkPos = TransPos::Dimension::chunkOffsetFromMapBlock(pos);
unsigned int chunkInd = TransPos::chunkIndFromVec(chunkPos);
return (*mapBlock)[chunkInd];
}
Region* Dimension::getRegion(glm::vec3 pos) {
return regions[pos];
}
Dimension::region_map &Dimension::getRegions() {
return regions;
if (blockChunks.count(pos)) {
return blockChunks.at(pos);
}
return nullptr;
}
Dimension::~Dimension() {
for (const auto &region : regions) {
delete region.second;
for (auto chunk : blockChunks) {
delete chunk.second->meshChunk;
}
}
void Dimension::update() {
for (auto it = blockChunks.begin(); it != blockChunks.end();) {
auto chunk = it->second;
auto pos = it->first;
//TODO: Figure out why there are NULL CHUNKS in the map
if (chunk != nullptr) {
auto diffVec = pos - *playerPos;
float distance = max(abs(diffVec.x), max(abs(diffVec.y), abs(diffVec.z)));
if (distance > 16) {
if (chunk->meshChunk != nullptr) {
meshChunks.erase(chunk->meshChunkIter);
delete chunk->meshChunk;
}
it = blockChunks.erase(it);
} else {
it++;
}
}
else {
it = blockChunks.erase(it);
}
}
}
int Dimension::render(Renderer &renderer) {
int count = 0;
for (auto &chunk : meshChunks) {
FrustumAABB bbox(chunk->getPos() * glm::vec3(TransPos::CHUNK_SIZE), glm::vec3(TransPos::CHUNK_SIZE));
if (renderer.getCamera()->inFrustum(bbox) != Frustum::OUTSIDE) {
chunk->draw(renderer);
count++;
}
}
return count;
}
int Dimension::getMeshChunkCount() {
return (int)meshChunks.size();
}

View File

@ -10,22 +10,30 @@
#include <unordered_map>
#include "region/Region.h"
#include "../util/Vec.h"
#include "../game/scene/world/graph/MeshChunk.h"
class Dimension {
public:
typedef std::unordered_map<glm::vec3, Region*, VecUtils::compareFunc> region_map;
typedef std::unordered_map<glm::vec3, std::shared_ptr<BlockChunk>, VecUtils::compareFunc> chunk_map;
Dimension() = default;
explicit Dimension(glm::vec3* playerPos);
void addBlockChunk(std::shared_ptr<BlockChunk> chunk);
void addMeshChunk(MeshChunk* chunk);
void update();
int render(Renderer &renderer);
int getMeshChunkCount();
void addChunk(glm::vec3 pos, std::shared_ptr<BlockChunk> chunk);
std::shared_ptr<BlockChunk> getChunk(glm::vec3 pos);
Region* getRegion(glm::vec3 pos);
region_map& getRegions();
~Dimension();
private:
region_map regions;
glm::vec3* playerPos = nullptr;
std::list<MeshChunk*> meshChunks;
chunk_map blockChunks;
};

View File

@ -8,9 +8,11 @@
#include <vec3.hpp>
#include <vector>
#include <iostream>
#include <list>
#include "../../util/Vec.h"
#include "../../util/net/Packet.h"
#include "../../util/Vec.h"
#include "../../game/scene/world/graph/MeshChunk.h"
class BlockChunk {
public:
@ -38,6 +40,9 @@ public:
bool deserialize(std::string gzip);
static std::vector<int> deserializeToVec(std::string gzip);
MeshChunk* meshChunk = nullptr;
std::list<MeshChunk*>::iterator meshChunkIter;
private:
std::vector<int> blocks;
bool empty;

View File

@ -3,6 +3,6 @@ set(ZEUS_TEST_FILES
tests/NetHandler.cpp
tests/Serializer.cpp
tests/LibNoise.cpp
tests/Dimension.cpp)
tests/Dimension.cpp tests/List.cpp)
add_library (zeusTest ${ZEUS_TEST_FILES})

View File

@ -18,5 +18,6 @@ TEST_CASE("Sanity Check", "[core]") {
#include "tests/Packet.cpp"
#include "tests/LibNoise.cpp"
#include "tests/Dimension.cpp"
#include "tests/List.cpp"
#pragma clang diagnostic pop

View File

@ -9,123 +9,123 @@ TEST_CASE("Dimension", "[world]") {
Dimension d;
SECTION("Inputting") {
SECTION("[0, 0, 0]") {
auto c = new BlockChunk();
d.addChunk({0, 0, 0}, c);
auto o = (*(*d.getRegion({0, 0, 0}))[0])[0];
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[1, 2, 3]") {
auto c = new BlockChunk();
d.addChunk({1, 2, 3}, c);
auto o = (*(*d.getRegion({0, 0, 0}))[0])[TransPos::chunkIndFromVec({1, 2, 3})];
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[9, 2, 3]") {
auto c = new BlockChunk();
d.addChunk({9, 2, 3}, c);
auto o = (*(*d.getRegion({0, 0, 0}))[TransPos::mapBlockIndFromVec({1, 0, 0})])[TransPos::chunkIndFromVec({1, 2, 3})];
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[66, 2, 16]") {
auto c = new BlockChunk();
d.addChunk({66, 2, 16}, c);
auto o = (*(*d.getRegion({1, 0, 0}))[TransPos::mapBlockIndFromVec({0, 0, 2})])[TransPos::chunkIndFromVec({2, 2, 0})];
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[-1, -2, 3]") {
auto c = new BlockChunk();
d.addChunk({-1, -2, 3}, c);
auto o = (*(*d.getRegion({-1, -1, 0}))[TransPos::mapBlockIndFromVec({7, 7, 0})])[TransPos::chunkIndFromVec({7, 6, 3})];
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[-1, -2, -3]") {
auto c = new BlockChunk();
d.addChunk({-1, -2, -3}, c);
auto o = (*(*d.getRegion({-1, -1, -1}))[TransPos::mapBlockIndFromVec({7, 7, 7})])[TransPos::chunkIndFromVec({7, 6, 5})];
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[66, -2, 3]") {
auto c = new BlockChunk();
d.addChunk({66, -2, 3}, c);
auto o = (*(*d.getRegion({1, -1, 0}))[TransPos::mapBlockIndFromVec({0, 7, 0})])[TransPos::chunkIndFromVec({2, 6, 3})];
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
}
SECTION("Retrieving") {
SECTION("[0, 0, 0]") {
auto c = new BlockChunk();
d.addChunk({0, 0, 0}, c);
auto o = d.getChunk({0, 0, 0});
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[1, 2, 3]") {
auto c = new BlockChunk();
d.addChunk({1, 2, 3}, c);
auto o = d.getChunk({1, 2, 3});
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[-1, -2, 3]") {
auto c = new BlockChunk();
d.addChunk({-1, -2, 3}, c);
auto o = d.getChunk({-1, -2, 3});
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[-1, -2, -3]") {
auto c = new BlockChunk();
d.addChunk({-1, -2, -3}, c);
auto o = d.getChunk({-1, -2, -3});
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
SECTION("[1026, -2, 3]") {
auto c = new BlockChunk();
d.addChunk({1026, -2, 3}, c);
auto o = d.getChunk({1026, -2, 3});
REQUIRE(o != nullptr);
REQUIRE(c == o);
}
}
// SECTION("Inputting") {
// SECTION("[0, 0, 0]") {
// auto c = new BlockChunk();
// d.addChunk({0, 0, 0}, c);
//
// auto o = (*(*d.getRegion({0, 0, 0}))[0])[0];
//
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[1, 2, 3]") {
// auto c = new BlockChunk();
// d.addChunk({1, 2, 3}, c);
//
// auto o = (*(*d.getRegion({0, 0, 0}))[0])[TransPos::chunkIndFromVec({1, 2, 3})];
//
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[9, 2, 3]") {
// auto c = new BlockChunk();
// d.addChunk({9, 2, 3}, c);
//
// auto o = (*(*d.getRegion({0, 0, 0}))[TransPos::mapBlockIndFromVec({1, 0, 0})])[TransPos::chunkIndFromVec({1, 2, 3})];
//
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[66, 2, 16]") {
// auto c = new BlockChunk();
// d.addChunk({66, 2, 16}, c);
//
// auto o = (*(*d.getRegion({1, 0, 0}))[TransPos::mapBlockIndFromVec({0, 0, 2})])[TransPos::chunkIndFromVec({2, 2, 0})];
//
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[-1, -2, 3]") {
// auto c = new BlockChunk();
// d.addChunk({-1, -2, 3}, c);
//
// auto o = (*(*d.getRegion({-1, -1, 0}))[TransPos::mapBlockIndFromVec({7, 7, 0})])[TransPos::chunkIndFromVec({7, 6, 3})];
//
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
//
// SECTION("[-1, -2, -3]") {
// auto c = new BlockChunk();
// d.addChunk({-1, -2, -3}, c);
//
// auto o = (*(*d.getRegion({-1, -1, -1}))[TransPos::mapBlockIndFromVec({7, 7, 7})])[TransPos::chunkIndFromVec({7, 6, 5})];
//
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[66, -2, 3]") {
// auto c = new BlockChunk();
// d.addChunk({66, -2, 3}, c);
//
// auto o = (*(*d.getRegion({1, -1, 0}))[TransPos::mapBlockIndFromVec({0, 7, 0})])[TransPos::chunkIndFromVec({2, 6, 3})];
//
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
// }
//
// SECTION("Retrieving") {
// SECTION("[0, 0, 0]") {
// auto c = new BlockChunk();
// d.addChunk({0, 0, 0}, c);
//
// auto o = d.getChunk({0, 0, 0});
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[1, 2, 3]") {
// auto c = new BlockChunk();
// d.addChunk({1, 2, 3}, c);
//
// auto o = d.getChunk({1, 2, 3});
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[-1, -2, 3]") {
// auto c = new BlockChunk();
// d.addChunk({-1, -2, 3}, c);
//
// auto o = d.getChunk({-1, -2, 3});
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[-1, -2, -3]") {
// auto c = new BlockChunk();
// d.addChunk({-1, -2, -3}, c);
//
// auto o = d.getChunk({-1, -2, -3});
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
//
// SECTION("[1026, -2, 3]") {
// auto c = new BlockChunk();
// d.addChunk({1026, -2, 3}, c);
//
// auto o = d.getChunk({1026, -2, 3});
// REQUIRE(o != nullptr);
// REQUIRE(c == o);
// }
// }
}

37
test/tests/List.cpp Normal file
View File

@ -0,0 +1,37 @@
//
// Created by aurailus on 22/04/19.
//
#include <catch.hpp>
#include <list>
struct IntHolder {
int i;
};
TEST_CASE("List Iterators are static", "random") {
std::list<IntHolder> list;
list.push_back({5});
auto five = --list.end();
list.push_back({3});
auto three = --list.end();
list.push_back({11});
auto eleven = --list.end();
REQUIRE(five->i == 5);
REQUIRE(three->i == 3);
REQUIRE(eleven->i == 11);
list.erase(three);
REQUIRE(five->i == 5);
REQUIRE(eleven->i == 11);
list.erase(eleven);
REQUIRE(five->i == 5);
}