procedural chunk loading
This commit is contained in:
parent
965ff9cc09
commit
9b60b862e5
12 changed files with 89 additions and 60 deletions
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue