From 9b60b862e58905780bf47f69a34522fde046ebea Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Tue, 27 Sep 2022 17:03:43 -0400 Subject: [PATCH] procedural chunk loading --- engine/xe_engine.cpp | 18 ------------ engine/xe_engine.hpp | 10 ++----- engine/xe_image.cpp | 3 +- engine/xe_image.hpp | 2 +- engine/xe_model.cpp | 7 +++-- engine/xe_model.hpp | 4 +-- engine/xe_renderer.hpp | 1 - src/chunk.cpp | 24 +++++++++++----- src/chunk.hpp | 1 + src/first_app.cpp | 64 ++++++++++++++++++++++++++++++++--------- src/first_app.hpp | 11 +++++-- src/simple_renderer.cpp | 4 --- 12 files changed, 89 insertions(+), 60 deletions(-) diff --git a/engine/xe_engine.cpp b/engine/xe_engine.cpp index 6392d78..201ea81 100644 --- a/engine/xe_engine.cpp +++ b/engine/xe_engine.cpp @@ -32,24 +32,6 @@ void Engine::loadDescriptorPool() { .build(); } -Model* Engine::loadModelFromFile(const std::string &filename) { - return Model::createModelFromFile(xeDevice, filename); -} - -Model* Engine::loadModelFromData(std::vector vertexData, uint32_t vertexSize, std::vector indices) { - Model::Builder builder{}; - builder.vertexData.data = vertexData; - builder.vertexSize = vertexSize; - if(indices.size() > 0) { - builder.indices = indices; - } - return new Model(xeDevice, builder); -} - -Image* Engine::loadImageFromFile(const std::string &filename, bool anisotropic) { - return new Image(xeDevice, filename, anisotropic); -} - bool Engine::poll() { glfwPollEvents(); auto newTime = std::chrono::high_resolution_clock::now(); diff --git a/engine/xe_engine.hpp b/engine/xe_engine.hpp index eff2d13..ae75afa 100644 --- a/engine/xe_engine.hpp +++ b/engine/xe_engine.hpp @@ -1,13 +1,12 @@ #pragma once +#include "xe_buffer.hpp" #include "xe_device.hpp" #include "xe_renderer.hpp" #include "xe_camera.hpp" #include "xe_descriptors.hpp" -#include "xe_image.hpp" #include "xe_input.hpp" #include "xe_sound.hpp" -#include "xe_model.hpp" #include #include @@ -30,11 +29,6 @@ class Engine { Input& getInput() {return xeInput;} Camera& getCamera() {return xeCamera;} - Device& getDevice() {return xeDevice;} - - Model* loadModelFromFile(const std::string &filename); - Model* loadModelFromData(std::vector vertexData, uint32_t vertexSize, std::vector indices); - Image* loadImageFromFile(const std::string &filename, bool anisotropic = true); bool beginFrame() { return xeRenderer.beginFrame(); } void endFrame() { xeRenderer.endFrame(); } @@ -63,6 +57,8 @@ class Engine { std::unique_ptr xeDescriptorPool; friend class RenderSystem; + friend class Image; + friend class Model; }; } \ No newline at end of file diff --git a/engine/xe_image.cpp b/engine/xe_image.cpp index cabcea3..1ac82e2 100644 --- a/engine/xe_image.cpp +++ b/engine/xe_image.cpp @@ -1,4 +1,5 @@ #include "xe_image.hpp" +#include "xe_engine.hpp" #include #include @@ -10,7 +11,7 @@ namespace xe { -Image::Image(Device &xeDevice, const std::string &filename, bool anisotropic) : xeDevice{xeDevice} { +Image::Image(const std::string &filename, bool anisotropic) : xeDevice{Engine::getInstance()->xeDevice} { createTextureImage(filename); createTextureImageView(); createTextureSampler(anisotropic); diff --git a/engine/xe_image.hpp b/engine/xe_image.hpp index 6520a95..bc1dad2 100644 --- a/engine/xe_image.hpp +++ b/engine/xe_image.hpp @@ -10,7 +10,7 @@ class Image { public: - Image(Device &xeDevice, const std::string &filename, bool anisotropic); + Image(const std::string &filename, bool anisotropic); ~Image(); Image(const Image&) = delete; diff --git a/engine/xe_model.cpp b/engine/xe_model.cpp index ff0d630..dc39584 100644 --- a/engine/xe_model.cpp +++ b/engine/xe_model.cpp @@ -1,4 +1,5 @@ #include "xe_model.hpp" +#include "xe_engine.hpp" #define TINYOBJLOADER_IMPLEMENTATION #include "xe_obj_loader.hpp" @@ -13,17 +14,17 @@ namespace xe { -Model::Model(Device &device, const Model::Builder &builder) : xeDevice{device} { +Model::Model(const Model::Builder &builder) : xeDevice{Engine::getInstance()->xeDevice} { createVertexBuffers(builder.vertexData.data, builder.vertexSize); createIndexBuffers(builder.indices); } Model::~Model() {} -Model* Model::createModelFromFile(Device &device, const std::string &filepath) { +Model* Model::createModelFromFile(const std::string &filepath) { Builder builder{}; builder.loadModel(filepath); - return new Model(device, builder); + return new Model(builder); } void Model::createVertexBuffers(const std::vector &vertexData, uint32_t vertexSize) { diff --git a/engine/xe_model.hpp b/engine/xe_model.hpp index 1886657..b75576e 100644 --- a/engine/xe_model.hpp +++ b/engine/xe_model.hpp @@ -34,13 +34,13 @@ class Model { void loadModel(const std::string &filepath); }; - Model(Device &device, const Model::Builder &builder); + Model(const Model::Builder &builder); ~Model(); Model(const Model &) = delete; Model operator=(const Model &) = delete; - static Model* createModelFromFile(Device &device, const std::string &filepath); + static Model* createModelFromFile(const std::string &filepath); void bind(VkCommandBuffer commandBuffer); void draw(VkCommandBuffer commandBuffer); diff --git a/engine/xe_renderer.hpp b/engine/xe_renderer.hpp index 547de1e..8742f75 100644 --- a/engine/xe_renderer.hpp +++ b/engine/xe_renderer.hpp @@ -1,7 +1,6 @@ #pragma once #include "xe_device.hpp" -#include "xe_game_object.hpp" #include "xe_swap_chain.hpp" #include "xe_descriptors.hpp" #include "xe_window.hpp" diff --git a/src/chunk.cpp b/src/chunk.cpp index f9ddb54..e830c57 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -16,14 +16,14 @@ Chunk::Chunk(int32_t gridX, int32_t gridZ, uint32_t world_seed) } Chunk::~Chunk() { - if(chunkMesh != nullptr) - delete chunkMesh; if(worker.joinable()) worker.join(); + if(chunkMesh != nullptr) + delete chunkMesh; } // -// CHUNK CREATION AND DELETION +// CHUNK CREATION, DELETION, AND RETREVAL // static std::map, Chunk*> chunks{}; @@ -38,10 +38,17 @@ Chunk* Chunk::getChunk(int32_t gridX, int32_t gridZ) { if(chunks.count({gridX, gridZ})) { return chunks[{gridX, gridZ}]; } else { - return NULL; + return nullptr; } } +void Chunk::deleteChunk(int32_t gridX, int32_t gridZ) { + Chunk* chunk = getChunk(gridX, gridZ); + if(chunk == nullptr) return; // Chunk does not exist or is already deleted + delete chunk; + chunks.erase({gridX, gridZ}); +} + // // CHUNK TEXTURE AND BLOCK LOADING // @@ -51,7 +58,7 @@ static std::map texturesIds{}; static std::vector textures{}; void loadTexture(const std::string& filePath) { - xe::Image* image = xe::Engine::getInstance()->loadImageFromFile(filePath, false); + xe::Image* image = new xe::Image(filePath, false); texturesIds[filePath] = static_cast(textures.size()); textures.push_back(image); } @@ -88,12 +95,15 @@ void Chunk::unload() { // void Chunk::createMeshAsync(Chunk* c) { + if(c == nullptr) return; if(c->working) return; + if(c->worker.joinable()) + c->worker.join(); c->worker = std::thread(createMesh, c); } void Chunk::createMesh(Chunk* c) { - c->working = true; + if(c == nullptr) return; c->vertexData.data.clear(); for(int32_t x=0;x<16;x++) { for(int32_t y=0; y<256; y++) { @@ -152,7 +162,7 @@ xe::Model* Chunk::getMesh() { xe::Model::Builder builder{}; builder.vertexData = vertexData; builder.vertexSize = 36; - chunkMesh = new xe::Model(xe::Engine::getInstance()->getDevice(), builder); + chunkMesh = new xe::Model(builder); reloadRequired = false; } return chunkMesh; diff --git a/src/chunk.hpp b/src/chunk.hpp index c506ca0..c53a0d3 100644 --- a/src/chunk.hpp +++ b/src/chunk.hpp @@ -39,6 +39,7 @@ class Chunk { static Chunk* newChunk(int32_t gridX, int32_t gridZ, uint32_t world_seed); static Chunk* getChunk(int32_t gridX, int32_t gridZ); + static void deleteChunk(int32_t gridX, int32_t gridZ); static void createMesh(Chunk* c); static void createMeshAsync(Chunk* c); diff --git a/src/first_app.cpp b/src/first_app.cpp index 08f6f0c..43047f7 100755 --- a/src/first_app.cpp +++ b/src/first_app.cpp @@ -1,5 +1,4 @@ #include "first_app.hpp" -#include "chunk.hpp" namespace app { @@ -11,15 +10,11 @@ void FirstApp::run() { Chunk::load(); - for(int32_t x = 0; x < 10; x++) { - for(int32_t z = 0; z < 10; z++) { - Chunk* chunk = Chunk::newChunk(x, z, 53463); - Chunk::createMeshAsync(chunk); - auto chunkObject = xe::GameObject::createGameObject(); - chunkObject.transform.translation = {16.f*x, 0.f, 16.f*z}; - gameObjects.push_back(std::move(chunkObject)); - } - } + auto viewerObject = xe::GameObject::createGameObject(); + viewerObject.transform.translation = {0.f, 10.f, 0.f}; + viewerObject.transform.rotation.y = glm::radians(45.f); + + createGameObjects(viewerObject); SimpleRenderer renderer{xeEngine, Chunk::getTextures()}; @@ -27,9 +22,6 @@ void FirstApp::run() { sound.setLooping(true); sound.play(); - auto viewerObject = xe::GameObject::createGameObject(); - viewerObject.transform.translation = {0.f, 10.f, 0.f}; - viewerObject.transform.rotation.y = glm::radians(45.f); KeyboardMovementController cameraController{xeEngine.getInput(), viewerObject}; while (xeEngine.poll()) { @@ -40,10 +32,12 @@ void FirstApp::run() { xeEngine.getCamera().setViewYXZ(viewerObject.transform.translation, viewerObject.transform.rotation); if(xeEngine.beginFrame()) { - renderer.render(gameObjects, xeEngine.getCamera()); + renderer.render(loadedChunks, xeEngine.getCamera()); xeEngine.endFrame(); } + reloadLoadedChunks(viewerObject); + } xeEngine.close(); @@ -52,4 +46,46 @@ void FirstApp::run() { } +void FirstApp::createGameObjects(xe::GameObject& viewer) { + int width = 2*RENDER_DISTANCE+1; + loadedChunks.clear(); + for(int32_t x = 0; x < width; x++) { + for(int32_t z = 0; z < width; z++) { + auto gameObject = xe::GameObject::createGameObject(); + gameObject.transform.translation = glm::vec3(0.f); + loadedChunks.push_back(std::move(gameObject)); + } + } +} + +void FirstApp::reloadLoadedChunks(xe::GameObject& viewer) { + viewX = static_cast(floor(viewer.transform.translation.x / 16.f)); + viewZ = static_cast(floor(viewer.transform.translation.z / 16.f)); + int width = 2*RENDER_DISTANCE+1; + int minX = viewX - RENDER_DISTANCE; + int minZ = viewZ - RENDER_DISTANCE; + int maxX = viewX + RENDER_DISTANCE; + int maxZ = viewZ + RENDER_DISTANCE; + for(int32_t x = 0; x < width; x++) { + for(int32_t z = 0; z < width; z++) { + auto& gameObject = loadedChunks[x + z * width]; + int gridX = static_cast(floor(gameObject.transform.translation.x / 16.f)); + int gridZ = static_cast(floor(gameObject.transform.translation.z / 16.f)); + int newGridX = minX + x; + int newGridZ = minZ + z; + if(gridX < minX || gridZ < minZ || gridX > maxX || gridZ > maxZ) + Chunk::deleteChunk(gridX, gridZ); + Chunk* chunk = Chunk::getChunk(newGridX, newGridZ); + if(chunk == nullptr) { + chunk = Chunk::newChunk(newGridX, newGridZ, 12345); + Chunk::createMeshAsync(chunk); + Chunk::createMeshAsync(Chunk::getChunk(newGridX-1, newGridZ)); + Chunk::createMeshAsync(Chunk::getChunk(newGridX, newGridZ+1)); + } + gameObject.model = chunk->getMesh(); + gameObject.transform.translation = glm::vec3(newGridX * 16.f, 0, newGridZ * 16.f); + } + } +} + } \ No newline at end of file diff --git a/src/first_app.hpp b/src/first_app.hpp index 3339b04..d361296 100755 --- a/src/first_app.hpp +++ b/src/first_app.hpp @@ -4,6 +4,7 @@ #include "keyboard_movement_controller.hpp" #include "simple_renderer.hpp" +#include "chunk.hpp" #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE @@ -31,9 +32,15 @@ class FirstApp { static constexpr int WIDTH = 800; static constexpr int HEIGHT = 600; + static constexpr int RENDER_DISTANCE = 10; + + void createGameObjects(xe::GameObject& viewer); + void reloadLoadedChunks(xe::GameObject& viewer); + + + int viewX, viewZ; xe::Engine xeEngine; - - std::vector gameObjects; + std::vector loadedChunks; }; } \ No newline at end of file diff --git a/src/simple_renderer.cpp b/src/simple_renderer.cpp index 22046be..d13e7ac 100644 --- a/src/simple_renderer.cpp +++ b/src/simple_renderer.cpp @@ -29,10 +29,6 @@ void SimpleRenderer::render(std::vector &gameObjects, xe::Camera PushConstant pc{}; pc.modelMatrix = obj.transform.mat4(); pc.normalMatrix = obj.transform.normalMatrix(); - - Chunk* chunk = Chunk::getChunk(obj.transform.translation.x/16.f, obj.transform.translation.z/16.f); - obj.model = chunk->getMesh(); - xeRenderSystem->loadPushConstant(&pc); xeRenderSystem->render(obj); }