procedural chunk loading

This commit is contained in:
Tyler Murphy 2022-09-27 17:03:43 -04:00
parent 965ff9cc09
commit 9b60b862e5
12 changed files with 89 additions and 60 deletions

View file

@ -32,24 +32,6 @@ void Engine::loadDescriptorPool() {
.build(); .build();
} }
Model* Engine::loadModelFromFile(const std::string &filename) {
return Model::createModelFromFile(xeDevice, filename);
}
Model* Engine::loadModelFromData(std::vector<unsigned char> vertexData, uint32_t vertexSize, std::vector<uint32_t> 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() { bool Engine::poll() {
glfwPollEvents(); glfwPollEvents();
auto newTime = std::chrono::high_resolution_clock::now(); auto newTime = std::chrono::high_resolution_clock::now();

View file

@ -1,13 +1,12 @@
#pragma once #pragma once
#include "xe_buffer.hpp"
#include "xe_device.hpp" #include "xe_device.hpp"
#include "xe_renderer.hpp" #include "xe_renderer.hpp"
#include "xe_camera.hpp" #include "xe_camera.hpp"
#include "xe_descriptors.hpp" #include "xe_descriptors.hpp"
#include "xe_image.hpp"
#include "xe_input.hpp" #include "xe_input.hpp"
#include "xe_sound.hpp" #include "xe_sound.hpp"
#include "xe_model.hpp"
#include <chrono> #include <chrono>
#include <string> #include <string>
@ -30,11 +29,6 @@ class Engine {
Input& getInput() {return xeInput;} Input& getInput() {return xeInput;}
Camera& getCamera() {return xeCamera;} Camera& getCamera() {return xeCamera;}
Device& getDevice() {return xeDevice;}
Model* loadModelFromFile(const std::string &filename);
Model* loadModelFromData(std::vector<unsigned char> vertexData, uint32_t vertexSize, std::vector<uint32_t> indices);
Image* loadImageFromFile(const std::string &filename, bool anisotropic = true);
bool beginFrame() { return xeRenderer.beginFrame(); } bool beginFrame() { return xeRenderer.beginFrame(); }
void endFrame() { xeRenderer.endFrame(); } void endFrame() { xeRenderer.endFrame(); }
@ -63,6 +57,8 @@ class Engine {
std::unique_ptr<DescriptorPool> xeDescriptorPool; std::unique_ptr<DescriptorPool> xeDescriptorPool;
friend class RenderSystem; friend class RenderSystem;
friend class Image;
friend class Model;
}; };
} }

View file

@ -1,4 +1,5 @@
#include "xe_image.hpp" #include "xe_image.hpp"
#include "xe_engine.hpp"
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <stdexcept> #include <stdexcept>
@ -10,7 +11,7 @@
namespace xe { 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); createTextureImage(filename);
createTextureImageView(); createTextureImageView();
createTextureSampler(anisotropic); createTextureSampler(anisotropic);

View file

@ -10,7 +10,7 @@ class Image {
public: public:
Image(Device &xeDevice, const std::string &filename, bool anisotropic); Image(const std::string &filename, bool anisotropic);
~Image(); ~Image();
Image(const Image&) = delete; Image(const Image&) = delete;

View file

@ -1,4 +1,5 @@
#include "xe_model.hpp" #include "xe_model.hpp"
#include "xe_engine.hpp"
#define TINYOBJLOADER_IMPLEMENTATION #define TINYOBJLOADER_IMPLEMENTATION
#include "xe_obj_loader.hpp" #include "xe_obj_loader.hpp"
@ -13,17 +14,17 @@
namespace xe { 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); createVertexBuffers(builder.vertexData.data, builder.vertexSize);
createIndexBuffers(builder.indices); createIndexBuffers(builder.indices);
} }
Model::~Model() {} Model::~Model() {}
Model* Model::createModelFromFile(Device &device, const std::string &filepath) { Model* Model::createModelFromFile(const std::string &filepath) {
Builder builder{}; Builder builder{};
builder.loadModel(filepath); builder.loadModel(filepath);
return new Model(device, builder); return new Model(builder);
} }
void Model::createVertexBuffers(const std::vector<unsigned char> &vertexData, uint32_t vertexSize) { void Model::createVertexBuffers(const std::vector<unsigned char> &vertexData, uint32_t vertexSize) {

View file

@ -34,13 +34,13 @@ class Model {
void loadModel(const std::string &filepath); void loadModel(const std::string &filepath);
}; };
Model(Device &device, const Model::Builder &builder); Model(const Model::Builder &builder);
~Model(); ~Model();
Model(const Model &) = delete; Model(const Model &) = delete;
Model operator=(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 bind(VkCommandBuffer commandBuffer);
void draw(VkCommandBuffer commandBuffer); void draw(VkCommandBuffer commandBuffer);

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "xe_device.hpp" #include "xe_device.hpp"
#include "xe_game_object.hpp"
#include "xe_swap_chain.hpp" #include "xe_swap_chain.hpp"
#include "xe_descriptors.hpp" #include "xe_descriptors.hpp"
#include "xe_window.hpp" #include "xe_window.hpp"

View file

@ -16,14 +16,14 @@ Chunk::Chunk(int32_t gridX, int32_t gridZ, uint32_t world_seed)
} }
Chunk::~Chunk() { Chunk::~Chunk() {
if(chunkMesh != nullptr)
delete chunkMesh;
if(worker.joinable()) if(worker.joinable())
worker.join(); worker.join();
if(chunkMesh != nullptr)
delete chunkMesh;
} }
// //
// CHUNK CREATION AND DELETION // CHUNK CREATION, DELETION, AND RETREVAL
// //
static std::map<std::pair<int32_t, int32_t>, Chunk*> chunks{}; static std::map<std::pair<int32_t, int32_t>, Chunk*> chunks{};
@ -38,10 +38,17 @@ Chunk* Chunk::getChunk(int32_t gridX, int32_t gridZ) {
if(chunks.count({gridX, gridZ})) { if(chunks.count({gridX, gridZ})) {
return chunks[{gridX, gridZ}]; return chunks[{gridX, gridZ}];
} else { } 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 // CHUNK TEXTURE AND BLOCK LOADING
// //
@ -51,7 +58,7 @@ static std::map<std::string, uint32_t> texturesIds{};
static std::vector<xe::Image*> textures{}; static std::vector<xe::Image*> textures{};
void loadTexture(const std::string& filePath) { 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<uint32_t>(textures.size()); texturesIds[filePath] = static_cast<uint32_t>(textures.size());
textures.push_back(image); textures.push_back(image);
} }
@ -88,12 +95,15 @@ void Chunk::unload() {
// //
void Chunk::createMeshAsync(Chunk* c) { void Chunk::createMeshAsync(Chunk* c) {
if(c == nullptr) return;
if(c->working) return; if(c->working) return;
if(c->worker.joinable())
c->worker.join();
c->worker = std::thread(createMesh, c); c->worker = std::thread(createMesh, c);
} }
void Chunk::createMesh(Chunk* c) { void Chunk::createMesh(Chunk* c) {
c->working = true; if(c == nullptr) return;
c->vertexData.data.clear(); c->vertexData.data.clear();
for(int32_t x=0;x<16;x++) { for(int32_t x=0;x<16;x++) {
for(int32_t y=0; y<256; y++) { for(int32_t y=0; y<256; y++) {
@ -152,7 +162,7 @@ xe::Model* Chunk::getMesh() {
xe::Model::Builder builder{}; xe::Model::Builder builder{};
builder.vertexData = vertexData; builder.vertexData = vertexData;
builder.vertexSize = 36; builder.vertexSize = 36;
chunkMesh = new xe::Model(xe::Engine::getInstance()->getDevice(), builder); chunkMesh = new xe::Model(builder);
reloadRequired = false; reloadRequired = false;
} }
return chunkMesh; return chunkMesh;

View file

@ -39,6 +39,7 @@ class Chunk {
static Chunk* newChunk(int32_t gridX, int32_t gridZ, uint32_t world_seed); static Chunk* newChunk(int32_t gridX, int32_t gridZ, uint32_t world_seed);
static Chunk* getChunk(int32_t gridX, int32_t gridZ); 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 createMesh(Chunk* c);
static void createMeshAsync(Chunk* c); static void createMeshAsync(Chunk* c);

View file

@ -1,5 +1,4 @@
#include "first_app.hpp" #include "first_app.hpp"
#include "chunk.hpp"
namespace app { namespace app {
@ -11,15 +10,11 @@ void FirstApp::run() {
Chunk::load(); Chunk::load();
for(int32_t x = 0; x < 10; x++) { auto viewerObject = xe::GameObject::createGameObject();
for(int32_t z = 0; z < 10; z++) { viewerObject.transform.translation = {0.f, 10.f, 0.f};
Chunk* chunk = Chunk::newChunk(x, z, 53463); viewerObject.transform.rotation.y = glm::radians(45.f);
Chunk::createMeshAsync(chunk);
auto chunkObject = xe::GameObject::createGameObject(); createGameObjects(viewerObject);
chunkObject.transform.translation = {16.f*x, 0.f, 16.f*z};
gameObjects.push_back(std::move(chunkObject));
}
}
SimpleRenderer renderer{xeEngine, Chunk::getTextures()}; SimpleRenderer renderer{xeEngine, Chunk::getTextures()};
@ -27,9 +22,6 @@ void FirstApp::run() {
sound.setLooping(true); sound.setLooping(true);
sound.play(); 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}; KeyboardMovementController cameraController{xeEngine.getInput(), viewerObject};
while (xeEngine.poll()) { while (xeEngine.poll()) {
@ -40,10 +32,12 @@ void FirstApp::run() {
xeEngine.getCamera().setViewYXZ(viewerObject.transform.translation, viewerObject.transform.rotation); xeEngine.getCamera().setViewYXZ(viewerObject.transform.translation, viewerObject.transform.rotation);
if(xeEngine.beginFrame()) { if(xeEngine.beginFrame()) {
renderer.render(gameObjects, xeEngine.getCamera()); renderer.render(loadedChunks, xeEngine.getCamera());
xeEngine.endFrame(); xeEngine.endFrame();
} }
reloadLoadedChunks(viewerObject);
} }
xeEngine.close(); 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<int>(floor(viewer.transform.translation.x / 16.f));
viewZ = static_cast<int>(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<int>(floor(gameObject.transform.translation.x / 16.f));
int gridZ = static_cast<int>(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);
}
}
}
} }

View file

@ -4,6 +4,7 @@
#include "keyboard_movement_controller.hpp" #include "keyboard_movement_controller.hpp"
#include "simple_renderer.hpp" #include "simple_renderer.hpp"
#include "chunk.hpp"
#define GLM_FORCE_RADIANS #define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE #define GLM_FORCE_DEPTH_ZERO_TO_ONE
@ -31,9 +32,15 @@ class FirstApp {
static constexpr int WIDTH = 800; static constexpr int WIDTH = 800;
static constexpr int HEIGHT = 600; 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; xe::Engine xeEngine;
std::vector<xe::GameObject> loadedChunks;
std::vector<xe::GameObject> gameObjects;
}; };
} }

View file

@ -29,10 +29,6 @@ void SimpleRenderer::render(std::vector<xe::GameObject> &gameObjects, xe::Camera
PushConstant pc{}; PushConstant pc{};
pc.modelMatrix = obj.transform.mat4(); pc.modelMatrix = obj.transform.mat4();
pc.normalMatrix = obj.transform.normalMatrix(); 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->loadPushConstant(&pc);
xeRenderSystem->render(obj); xeRenderSystem->render(obj);
} }