From 05763162825c843775314ac158e9039e7177f911 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 30 Oct 2023 15:19:58 +0300 Subject: [PATCH] Engine core and assets loading refactor --- src/AssetsLoader.cpp | 79 +++++++++++++++ src/AssetsLoader.h | 38 ++++++++ src/declarations.cpp | 67 ++----------- src/declarations.h | 4 +- src/hud_render.cpp | 42 ++++++++ src/voxel_engine.cpp | 225 ++++++++++++++++++++----------------------- src/world/Level.cpp | 15 +++ src/world/Level.h | 3 + src/world/World.cpp | 28 ++++++ src/world/World.h | 5 + 10 files changed, 325 insertions(+), 181 deletions(-) create mode 100644 src/AssetsLoader.cpp create mode 100644 src/AssetsLoader.h diff --git a/src/AssetsLoader.cpp b/src/AssetsLoader.cpp new file mode 100644 index 000000000..eaeba7b99 --- /dev/null +++ b/src/AssetsLoader.cpp @@ -0,0 +1,79 @@ +#include "AssetsLoader.h" +#include "Assets.h" + +#include + +AssetsLoader::AssetsLoader(Assets* assets) : assets(assets) { +} + +void AssetsLoader::addLoader(int tag, aloader_func func) { + loaders[tag] = func; +} + +void AssetsLoader::add(int tag, const std::string filename, const std::string alias) { + entries.push(aloader_entry{ tag, filename, alias }); +} + +bool AssetsLoader::hasNext() const { + return !entries.empty(); +} + +bool AssetsLoader::loadNext() { + const aloader_entry& entry = entries.front(); + std::cout << " loading " << entry.filename << " as " << entry.alias << std::endl; + std::cout.flush(); + auto found = loaders.find(entry.tag); + if (found == loaders.end()) { + std::cerr << "unknown asset tag " << entry.tag << std::endl; + return false; + } + aloader_func loader = found->second; + bool status = loader(assets, entry.filename, entry.alias); + entries.pop(); + return status; +} + +#include "graphics/Shader.h" +#include "graphics/Texture.h" +#include "graphics/Font.h" + +bool _load_shader(Assets* assets, const std::string& filename, const std::string& name) { + Shader* shader = load_shader(filename + ".glslv", filename + ".glslf"); + if (shader == nullptr) { + std::cerr << "failed to load shader '" << name << "'" << std::endl; + return false; + } + assets->store(shader, name); + return true; +} + +bool _load_texture(Assets* assets, const std::string& filename, const std::string& name) { + Texture* texture = load_texture(filename); + if (texture == nullptr) { + std::cerr << "failed to load texture '" << name << "'" << std::endl; + return false; + } + assets->store(texture, name); + return true; +} + +bool _load_font(Assets* assets, const std::string& filename, const std::string& name) { + std::vector pages; + for (size_t i = 0; i <= 4; i++) { + Texture* texture = load_texture(filename + "_" + std::to_string(i) + ".png"); + if (texture == nullptr) { + std::cerr << "failed to load bitmap font '" << name << "' (missing page " << std::to_string(i) << ")" << std::endl; + return false; + } + pages.push_back(texture); + } + Font* font = new Font(pages); + assets->store(font, name); + return true; +} + +void AssetsLoader::createDefaults(AssetsLoader& loader) { + loader.addLoader(ASSET_SHADER, _load_shader); + loader.addLoader(ASSET_TEXTURE, _load_texture); + loader.addLoader(ASSET_FONT, _load_font); +} diff --git a/src/AssetsLoader.h b/src/AssetsLoader.h new file mode 100644 index 000000000..ff4ff5ed5 --- /dev/null +++ b/src/AssetsLoader.h @@ -0,0 +1,38 @@ +#ifndef SRC_ASSETS_LOADER_H +#define SRC_ASSETS_LOADER_H + +#include +#include +#include +#include + +#define ASSET_TEXTURE 1 +#define ASSET_SHADER 2 +#define ASSET_FONT 3 + +class Assets; + +typedef std::function aloader_func; + +struct aloader_entry { + int tag; + const std::string filename; + const std::string alias; +}; + +class AssetsLoader { + Assets* assets; + std::map loaders; + std::queue entries; +public: + AssetsLoader(Assets* assets); + void addLoader(int tag, aloader_func func); + void add(int tag, const std::string filename, const std::string alias); + + bool hasNext() const; + bool loadNext(); + + static void createDefaults(AssetsLoader& loader); +}; + +#endif // SRC_ASSETS_LOADER_H \ No newline at end of file diff --git a/src/declarations.cpp b/src/declarations.cpp index bb85c0c1e..2f89985df 100644 --- a/src/declarations.cpp +++ b/src/declarations.cpp @@ -1,70 +1,21 @@ #include "declarations.h" -#include "Assets.h" -#include "graphics/Shader.h" -#include "graphics/Texture.h" -#include "graphics/Font.h" +#include "AssetsLoader.h" #include "window/Window.h" #include "voxels/Block.h" -// Shaders, textures -bool _load_shader(Assets* assets, std::string vertex_file, std::string fragment_file, std::string name){ - Shader* shader = load_shader(vertex_file, fragment_file); - if (shader == nullptr){ - std::cerr << "failed to load shader '" << name << "'" << std::endl; - return false; - } - assets->store(shader, name); - return true; -} -bool _load_texture(Assets* assets, std::string filename, std::string name){ - Texture* texture = load_texture(filename); - if (texture == nullptr){ - std::cerr << "failed to load texture '" << name << "'" << std::endl; - return false; - } - assets->store(texture, name); - return true; -} +void initialize_assets(AssetsLoader* loader) { + loader->add(ASSET_SHADER, "res/main", "main"); + loader->add(ASSET_SHADER, "res/crosshair", "crosshair"); + loader->add(ASSET_SHADER, "res/lines", "lines"); + loader->add(ASSET_SHADER, "res/ui", "ui"); -bool _load_font(Assets* assets, std::string filename, std::string name){ - std::vector pages; - for (size_t i = 0; i <= 4; i++){ - Texture* texture = load_texture(filename+"_"+std::to_string(i)+".png"); - if (texture == nullptr){ - std::cerr << "failed to load bitmap font '" << name << "' (missing page " << std::to_string(i) << ")" << std::endl; - return false; - } - pages.push_back(texture); - } - Font* font = new Font(pages); - assets->store(font, name); - return true; -} + loader->add(ASSET_TEXTURE, "res/block.png", "block"); + loader->add(ASSET_TEXTURE, "res/slot.png", "slot"); -int initialize_assets(Assets* assets) { -#define LOAD_SHADER(VERTEX, FRAGMENT, NAME) \ - if (!_load_shader(assets, VERTEX, FRAGMENT, NAME))\ - return 1; -#define LOAD_TEXTURE(FILENAME, NAME) \ - if (!_load_texture(assets, FILENAME, NAME))\ - return 1; -#define LOAD_FONT(FILENAME, NAME) \ - if (!_load_font(assets, FILENAME, NAME))\ - return 1; - - LOAD_SHADER("res/main.glslv", "res/main.glslf", "main"); - LOAD_SHADER("res/crosshair.glslv", "res/crosshair.glslf", "crosshair"); - LOAD_SHADER("res/lines.glslv", "res/lines.glslf", "lines"); - LOAD_SHADER("res/ui.glslv", "res/ui.glslf", "ui"); - - LOAD_TEXTURE("res/block.png", "block"); - LOAD_TEXTURE("res/slot.png", "slot"); - - LOAD_FONT("res/font", "normal"); - return 0; + loader->add(ASSET_FONT, "res/font", "normal"); } // All in-game definitions (blocks, items, etc..) diff --git a/src/declarations.h b/src/declarations.h index 13ff14be8..b2585cb39 100644 --- a/src/declarations.h +++ b/src/declarations.h @@ -21,9 +21,9 @@ #define BLOCK_METAL 15 #define BLOCK_RUST 16 -class Assets; +class AssetsLoader; -int initialize_assets(Assets* assets); +void initialize_assets(AssetsLoader* loader); void setup_definitions(); #endif // DECLARATIONS_H diff --git a/src/hud_render.cpp b/src/hud_render.cpp index 6d1e008b2..9e028b035 100644 --- a/src/hud_render.cpp +++ b/src/hud_render.cpp @@ -20,6 +20,19 @@ HudRenderer::HudRenderer() { + // float vertices[] = { + // // x y + // -0.01f,-0.01f, + // 0.01f, 0.01f, + + // -0.01f, 0.01f, + // 0.01f,-0.01f, + // }; + // int attrs[] = { + // 2, 0 //null terminator + // }; + // crosshair = new Mesh(vertices, 4, attrs); + batch = new Batch2D(1024); uicamera = new Camera(glm::vec3(), Window::height / 1.0f); uicamera->perspective = false; @@ -66,6 +79,7 @@ void HudRenderer::draw(Level* level, Assets* assets){ // Chosen block preview Texture* blocks = assets->getTexture("block"); + Texture* sprite = assets->getTexture("slot"); batch->texture(nullptr); batch->color = vec4(1.0f); @@ -76,6 +90,13 @@ void HudRenderer::draw(Level* level, Assets* assets){ batch->line(Window::width/2-5, Window::height/2-5, Window::width/2+5, Window::height/2+5, 0.9f, 0.9f, 0.9f, 1.0f); batch->line(Window::width/2+5, Window::height/2-5, Window::width/2-5, Window::height/2+5, 0.9f, 0.9f, 0.9f, 1.0f); } + + // batch->texture(sprite); + // batch->sprite(Window::width/2-32, uicamera->fov - 80, 64, 64, 16, 0, vec4(1.0f)); + // batch->rect(Window::width/2-128-4, Window::height-80-4, 256+8, 64+8, + // 0.85f, 0.85f, 0.85f, 0.95f, 0.95f, 0.95f, + // 0.55f, 0.55f, 0.55f, + // 0.45f, 0.45f, 0.45f, 0.7f, 0.7f, 0.7f, 2); batch->rect(Window::width/2-128-4, Window::height-80-4, 256+8, 64+8, 0.95f, 0.95f, 0.95f, 0.85f, 0.85f, 0.85f, 0.7f, 0.7f, 0.7f, @@ -146,6 +167,7 @@ void HudRenderer::draw(Level* level, Assets* assets){ for (uint i = 1; i < count; i++) { x = xs + step * ((i-1) % (inv_w / step)); y = ys + step * ((i-1) / (inv_w / step)); + // batch->rect(x-2, y-2, size+4, size+4); batch->rect(x-2, y-2, size+4, size+4, 0.45f, 0.45f, 0.45f, 0.55f, 0.55f, 0.55f, 0.7f, 0.7f, 0.7f, @@ -156,6 +178,13 @@ void HudRenderer::draw(Level* level, Assets* assets){ 0.65f, 0.65f, 0.65f, 0.65f, 0.65f, 0.65f, 2); } + // batch->color = vec4(0.5f, 0.5f, 0.5f, 1.0f); + // for (unsigned i = 1; i < count; i++) { + // x = xs + step * ((i-1) % (inv_w / step)); + // y = ys + step * ((i-1) / (inv_w / step)); + // batch->rect(x, y, size, size); + // } + //front batch->texture(blocks); for (uint i = 1; i < count; i++) { @@ -171,8 +200,10 @@ void HudRenderer::draw(Level* level, Assets* assets){ if (Events::jclicked(GLFW_MOUSE_BUTTON_LEFT)) { player->choosenBlock = i; } + // size = 50; } else { + // size = 48; tint = vec4(1.0f); } @@ -183,4 +214,15 @@ void HudRenderer::draw(Level* level, Assets* assets){ } } } + + // batch->render(); + + if (Events::_cursor_locked && !level->player->debug){ + // Shader* crosshairShader = assets->getShader("crosshair"); + // crosshairShader->use(); + // crosshairShader->uniform1f("u_ar", (float)Window::height / (float)Window::width); + // crosshairShader->uniform1f("u_scale", 1.0f / ((float)Window::height / 1000.0f)); + // glLineWidth(2.0f); + // crosshair->draw(GL_LINES); + } } diff --git a/src/voxel_engine.cpp b/src/voxel_engine.cpp index d5aa94fc3..635ebd224 100644 --- a/src/voxel_engine.cpp +++ b/src/voxel_engine.cpp @@ -10,6 +10,7 @@ #include #include +#include // GLM #include @@ -18,140 +19,124 @@ using namespace glm; -#include "graphics/Shader.h" -#include "graphics/Texture.h" -#include "graphics/Mesh.h" -#include "graphics/VoxelRenderer.h" -#include "graphics/LineBatch.h" -#include "graphics/Batch2D.h" -#include "graphics/Framebuffer.h" #include "window/Window.h" #include "window/Events.h" #include "window/Camera.h" -#include "loaders/png_loading.h" -#include "voxels/voxel.h" +#include "audio/Audio.h" #include "voxels/Chunk.h" #include "voxels/Chunks.h" -#include "voxels/Block.h" -#include "voxels/WorldGenerator.h" #include "voxels/ChunksController.h" -#include "files/files.h" -#include "files/WorldFiles.h" -#include "lighting/LightSolver.h" -#include "lighting/Lightmap.h" -#include "lighting/Lighting.h" -#include "physics/Hitbox.h" -#include "physics/PhysicsSolver.h" -#include "world/World.h" -#include "world/Level.h" - -#include "audio/Audio.h" -#include "audio/audioutil.h" -#include "Assets.h" +#include "voxels/ChunksLoader.h" #include "objects/Player.h" - +#include "world/Level.h" +#include "world/World.h" #include "declarations.h" +#include "Assets.h" +#include "AssetsLoader.h" #include "world_render.h" #include "hud_render.h" -#include "player_control.h" -int WIDTH = 1280; -int HEIGHT = 720; -// Save all world data to files -void write_world(World* world, Level* level){ - WorldFiles* wfile = world->wfile; - Chunks* chunks = level->chunks; +class initialize_error : public std::runtime_error { + initialize_error(const std::string& message) : std::runtime_error(message) {} +}; - for (unsigned int i = 0; i < chunks->volume; i++){ - Chunk* chunk = chunks->chunks[i]; - if (chunk == nullptr || !chunk->isUnsaved()) - continue; - wfile->put((const char*)chunk->voxels, chunk->x, chunk->z); - } +struct EngineSettings { + int displayWidth; + int displayHeight; + const char* title; +}; - wfile->write(); - world->wfile->writePlayer(level->player); -} - -void update_level(World* world, Level* level, float delta) { - level->playerController->update_controls(delta); - if (Events::_cursor_locked){ - level->playerController->update_interaction(); - } else - { - level->playerController->selectedBlockId = -1; - } - - vec3 position = level->player->hitbox->position; - level->chunks->setCenter(world->wfile, position.x, position.z); -} +class Engine { + Assets* assets; + Level* level; -Level* load_level(World* world, Player* player) { - Level* level = new Level(world, player, new Chunks(56, 56, 0, 0), new PhysicsSolver(vec3(0, -19.6f, 0))); - world->wfile->readPlayer(player); + uint64_t frame = 0; + float lastTime = 0.0f; + float delta = 0.0f; + bool occlusion = true; +public: + Engine(const EngineSettings& settings); + ~Engine(); - Camera* camera = player->camera; - camera->rotation = mat4(1.0f); - camera->rotate(player->camY, player->camX, 0); - return level; -} + void updateTimers(); + void updateHotkeys(); + void mainloop(); +}; -int initialize(Assets*& assets) { - Window::initialize(WIDTH, HEIGHT, "VoxelEngine-Cpp v12"); +Engine::Engine(const EngineSettings& settings) { + Window::initialize(settings.displayWidth, settings.displayHeight, settings.title); Events::initialize(); assets = new Assets(); std::cout << "-- loading assets" << std::endl; - int result = initialize_assets(assets); - if (result){ - delete assets; - Window::terminate(); - return result; + AssetsLoader loader(assets); + AssetsLoader::createDefaults(loader); + initialize_assets(&loader); + while (loader.hasNext()) { + if (!loader.loadNext()) { + delete assets; + Window::terminate(); + throw std::runtime_error("could not to initialize assets"); + } } - return 0; + std::cout << "-- loading world" << std::endl; + vec3 playerPosition = vec3(0, 64, 0); + Camera* camera = new Camera(playerPosition, radians(90.0f)); + World* world = new World("world-1", "world/", 42); + Player* player = new Player(playerPosition, 4.0f, camera); + level = world->loadLevel(player); + + std::cout << "-- initializing finished" << std::endl; + + Audio::initialize(); } -void mainloop(Level* level, Assets* assets) { +void Engine::updateTimers() { + frame++; + float currentTime = glfwGetTime(); + delta = currentTime - lastTime; + lastTime = currentTime; +} + +void Engine::updateHotkeys() { + if (Events::jpressed(GLFW_KEY_ESCAPE)) { + Window::setShouldClose(true); + } + if (Events::jpressed(GLFW_KEY_TAB) || Events::jpressed(GLFW_KEY_E)) { + Events::toggleCursor(); + } + if (Events::jpressed(GLFW_KEY_O)) { + occlusion = !occlusion; + } + if (Events::jpressed(GLFW_KEY_F3)) { + level->player->debug = !level->player->debug; + } + if (Events::jpressed(GLFW_KEY_F5)) { + for (unsigned i = 0; i < level->chunks->volume; i++) { + Chunk* chunk = level->chunks->chunks[i]; + if (chunk != nullptr && chunk->isReady()) { + chunk->setModified(true); + } + } + } +} + +void Engine::mainloop() { Camera* camera = level->player->camera; std::cout << "-- preparing systems" << std::endl; World* world = level->world; WorldRenderer worldRenderer(level, assets); HudRenderer hud; - long frame = 0; - float lastTime = glfwGetTime(); - float delta = 0.0f; - bool occlusion = true; + lastTime = glfwGetTime(); + Window::swapInterval(1); while (!Window::isShouldClose()){ - frame++; - float currentTime = glfwGetTime(); - delta = currentTime - lastTime; - lastTime = currentTime; - int fps = 1 / delta; - if (Events::jpressed(GLFW_KEY_ESCAPE)){ - Window::setShouldClose(true); - } - if (Events::jpressed(GLFW_KEY_TAB) || Events::jpressed(GLFW_KEY_E)){ - Events::toggleCursor(); - } - if (Events::jpressed(GLFW_KEY_O)){ - occlusion = !occlusion; - } - if (Events::jpressed(GLFW_KEY_F3)){ - level->player->debug = !level->player->debug; - } - if (Events::jpressed(GLFW_KEY_F5)){ - for (unsigned i = 0; i < level->chunks->volume; i++) { - Chunk* chunk = level->chunks->chunks[i]; - if (chunk != nullptr && chunk->isReady()){ - chunk->setModified(true); - } - } - } + updateTimers(); + updateHotkeys(); - update_level(world, level, delta); + level->update(delta, Events::_cursor_locked); int freeLoaders = level->chunksController->countFreeLoaders(); for (int i = 0; i < freeLoaders; i++) level->chunksController->_buildMeshes(); @@ -165,7 +150,7 @@ void mainloop(Level* level, Assets* assets) { worldRenderer.draw(camera, occlusion); hud.draw(level, assets); if (level->player->debug) { - hud.drawDebug(level, assets, fps, occlusion); + hud.drawDebug(level, assets, 1 / delta, occlusion); } Window::swapBuffers(); @@ -173,28 +158,12 @@ void mainloop(Level* level, Assets* assets) { } } -int main() { - setup_definitions(); - - Assets* assets; - int status = initialize(assets); - if (status) return status; - - std::cout << "-- loading world" << std::endl; - vec3 playerPosition = vec3(0,64,0); - Camera* camera = new Camera(playerPosition, radians(90.0f)); - World* world = new World("world-1", "world/", 42); - Player* player = new Player(playerPosition, 4.0f, camera); - Level* level = load_level(world, player); - - std::cout << "-- initializing finished" << std::endl; - - Audio::initialize(); - mainloop(level, assets); +Engine::~Engine() { Audio::finalize(); + World* world = level->world; std::cout << "-- saving world" << std::endl; - write_world(world, level); + world->write(level); delete level; delete world; @@ -203,5 +172,19 @@ int main() { delete assets; Events::finalize(); Window::terminate(); +} + +int main() { + setup_definitions(); + + try { + Engine engine(EngineSettings{ 1280, 720, "VoxelEngine-Cpp v13" }); + engine.mainloop(); + } + catch (const initialize_error& err) { + std::cerr << "could not to initialize engine" << std::endl; + std::cerr << err.what() << std::endl; + } + return 0; } diff --git a/src/world/Level.cpp b/src/world/Level.cpp index c86f95075..b591fc9dc 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -1,8 +1,10 @@ #include "Level.h" +#include "World.h" #include "../lighting/Lighting.h" #include "../voxels/Chunks.h" #include "../voxels/ChunksController.h" #include "../player_control.h" +#include "../physics/Hitbox.h" #include "../physics/PhysicsSolver.h" #include "../objects/Player.h" @@ -24,3 +26,16 @@ Level::~Level(){ delete chunksController; delete playerController; } + +void Level::update(float delta, bool interactions) { + playerController->update_controls(delta); + if (interactions) { + playerController->update_interaction(); + } + else + { + playerController->selectedBlockId = -1; + } + vec3 position = player->hitbox->position; + chunks->setCenter(world->wfile, position.x, position.z); +} diff --git a/src/world/Level.h b/src/world/Level.h index 47728e2a4..aea33c5a2 100644 --- a/src/world/Level.h +++ b/src/world/Level.h @@ -20,6 +20,9 @@ class Level { PlayerController* playerController; Level(World* world, Player* player, Chunks* chunks, PhysicsSolver* physics); ~Level(); + + void update(float delta, bool interactions); + }; #endif /* WORLD_LEVEL_H_ */ diff --git a/src/world/World.cpp b/src/world/World.cpp index 2e336dce1..04b32d334 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -3,6 +3,10 @@ #include "../files/WorldFiles.h" #include "../voxels/Chunk.h" #include "../voxels/Chunks.h" +#include "Level.h" +#include "../objects/Player.h" +#include "../physics/PhysicsSolver.h" +#include "../window/Camera.h" World::World(std::string name, std::string directory, int seed) : name(name), seed(seed) { wfile = new WorldFiles(directory, REGION_VOL * (CHUNK_VOL * 2 + 8)); @@ -11,3 +15,27 @@ World::World(std::string name, std::string directory, int seed) : name(name), se World::~World(){ delete wfile; } + +void World::write(Level* level) { + Chunks* chunks = level->chunks; + + for (unsigned int i = 0; i < chunks->volume; i++) { + Chunk* chunk = chunks->chunks[i]; + if (chunk == nullptr || !chunk->isUnsaved()) + continue; + wfile->put((const char*)chunk->voxels, chunk->x, chunk->z); + } + + wfile->write(); + wfile->writePlayer(level->player); +} + +Level* World::loadLevel(Player* player) { + Level* level = new Level(this, player, new Chunks(56, 56, 0, 0), new PhysicsSolver(vec3(0, -19.6f, 0))); + wfile->readPlayer(player); + + Camera* camera = player->camera; + camera->rotation = mat4(1.0f); + camera->rotate(player->camY, player->camX, 0); + return level; +} \ No newline at end of file diff --git a/src/world/World.h b/src/world/World.h index 7b51192b8..d67b27c6b 100644 --- a/src/world/World.h +++ b/src/world/World.h @@ -5,6 +5,8 @@ class WorldFiles; class Chunks; +class Level; +class Player; class World { public: @@ -14,6 +16,9 @@ class World { World(std::string name, std::string directory, int seed); ~World(); + + void write(Level* level); + Level* loadLevel(Player* player); }; #endif /* WORLD_WORLD_H_ */