From 687c5c0257225bbe8f1a6b2fff36ea34e147ac9f Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sat, 25 Nov 2023 19:31:16 -0600 Subject: [PATCH] Texture loading done. --- src/dawn/CMakeLists.txt | 1 + src/dawn/asset/AssetDataLoader.cpp | 2 +- src/dawn/asset/AssetDataLoader.hpp | 2 +- src/dawn/asset/loaders/TextureLoader.cpp | 106 ++++++++++++++++++- src/dawn/asset/loaders/TextureLoader.hpp | 29 ++--- src/dawn/dawnlibs.hpp | 1 + src/dawn/display/ITexture.hpp | 4 +- src/dawn/environment/CMakeLists.txt | 10 ++ src/dawn/environment/Environment.cpp | 31 ++++++ src/dawn/environment/Environment.hpp | 30 +++++- src/dawnhelloworld/scene/HelloWorldScene.cpp | 2 +- src/dawnlinux/main.cpp | 47 +++++++- 12 files changed, 233 insertions(+), 32 deletions(-) create mode 100644 src/dawn/environment/CMakeLists.txt create mode 100644 src/dawn/environment/Environment.cpp diff --git a/src/dawn/CMakeLists.txt b/src/dawn/CMakeLists.txt index ef24eb1d..071da5d2 100644 --- a/src/dawn/CMakeLists.txt +++ b/src/dawn/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(asset) # add_subdirectory(audio) add_subdirectory(component) add_subdirectory(display) +add_subdirectory(environment) add_subdirectory(game) # add_subdirectory(games) # add_subdirectory(input) diff --git a/src/dawn/asset/AssetDataLoader.cpp b/src/dawn/asset/AssetDataLoader.cpp index ba959cd6..32609382 100644 --- a/src/dawn/asset/AssetDataLoader.cpp +++ b/src/dawn/asset/AssetDataLoader.cpp @@ -152,7 +152,7 @@ size_t AssetDataLoader::readUntil( if(buffer[i] == delimiter) break; i++; } - buffer[i] = '\0'; + buffer[i++] = '\0'; return i; } diff --git a/src/dawn/asset/AssetDataLoader.hpp b/src/dawn/asset/AssetDataLoader.hpp index 61b7941d..33394d2d 100644 --- a/src/dawn/asset/AssetDataLoader.hpp +++ b/src/dawn/asset/AssetDataLoader.hpp @@ -116,7 +116,7 @@ namespace Dawn { * @param buffer Buffer to read into. * @param maxSize Maximum size of the buffer. * @param delimiter Delimiter to read until. - * @return The count of bytes read. + * @return The count of bytes read (including null terminator) */ size_t readUntil( uint8_t *buffer, diff --git a/src/dawn/asset/loaders/TextureLoader.cpp b/src/dawn/asset/loaders/TextureLoader.cpp index c48dc3c0..14f2cbc8 100644 --- a/src/dawn/asset/loaders/TextureLoader.cpp +++ b/src/dawn/asset/loaders/TextureLoader.cpp @@ -14,6 +14,7 @@ TextureLoader::TextureLoader(const std::string name) : state(TextureLoaderLoadState::INITIAL) { sharedTexture = std::make_shared(); + weakTexture = sharedTexture; } void TextureLoader::updateAsync() { @@ -23,23 +24,124 @@ void TextureLoader::updateAsync() { // Read in the header. uint8_t buffer[TEXTURE_LOADER_HEADER_SIZE]; - this->loader.read(buffer, TEXTURE_LOADER_HEADER_SIZE); size_t pos = 0; // Read Version pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|'); - std::string version = std::string((char*)buffer, pos); + std::string version = std::string((char*)buffer); assertTrue(version == "DT_2.00", "Invalid Texture Version!"); // Read Texture Width this->loader.setPosition(pos); pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|'); + width = std::stoi(std::string((char*)buffer)); + assertTrue(width > 0, "Invalid Texture Width!"); + + // Read Texture Height + this->loader.setPosition(pos); + pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|'); + height = std::stoi(std::string((char*)buffer)); + assertTrue(height > 0, "Invalid Texture Height!"); + + // Texture Format (RGBA, RGB, etc) + this->loader.setPosition(pos); + pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|'); + int32_t iFormat = std::stoi(std::string((char*)buffer)); + switch(iFormat) { + case 1: format = TextureFormat::R; break; + case 2: format = TextureFormat::RG; break; + case 3: format = TextureFormat::RGB; break; + case 4: format = TextureFormat::RGBA; break; + default: assertUnreachable("Invalid Texture Format %i!", iFormat); + } + + // Wrap X + this->loader.setPosition(pos); + pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|'); + int32_t iWrapX = std::stoi(std::string((char*)buffer)); + switch(iWrapX) { + case 0: wrapX = TextureWrapMode::REPEAT; break; + case 1: wrapX = TextureWrapMode::MIRRORED_REPEAT; break; + case 2: wrapX = TextureWrapMode::CLAMP_TO_EDGE; break; + case 3: wrapX = TextureWrapMode::CLAMP_TO_BORDER; break; + default: assertUnreachable("Invalid Texture Wrap X %i!", iWrapX); + } + + // Wrap Y + this->loader.setPosition(pos); + pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|'); + int32_t iWrapY = std::stoi(std::string((char*)buffer)); + switch(iWrapY) { + case 0: wrapY = TextureWrapMode::REPEAT; break; + case 1: wrapY = TextureWrapMode::MIRRORED_REPEAT; break; + case 2: wrapY = TextureWrapMode::CLAMP_TO_EDGE; break; + case 3: wrapY = TextureWrapMode::CLAMP_TO_BORDER; break; + default: assertUnreachable("Invalid Texture Wrap Y %i!", iWrapY); + } + + // Filter Min + this->loader.setPosition(pos); + pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|'); + int32_t iFilterMin = std::stoi(std::string((char*)buffer)); + switch(iFilterMin) { + case 0: filterMin = TextureFilterMode::NEAREST; break; + case 1: filterMin = TextureFilterMode::LINEAR; break; + default: assertUnreachable("Invalid Texture Filter Min %i!", iFilterMin); + } + + // Filter Mag + this->loader.setPosition(pos); + pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|'); + int32_t iFilterMag = std::stoi(std::string((char*)buffer)); + switch(iFilterMag) { + case 0: filterMag = TextureFilterMode::NEAREST; break; + case 1: filterMag = TextureFilterMode::LINEAR; break; + default: assertUnreachable("Invalid Texture Filter Mag %i!", iFilterMag); + } + + // Data begins here. This part is done synchronously directly to the GPU. + this->loader.setPosition(pos); + size_t bufferSize = width * height * iFormat; + data = new uint8_t[bufferSize]; + assertNotNull(data, "Failed to allocate texture data!"); + this->loader.read(data, bufferSize); + + // Handoff to sync to buffer to GPU. + this->state = TextureLoaderLoadState::ASYNC_DONE; } void TextureLoader::updateSync() { + if(this->state != TextureLoaderLoadState::ASYNC_DONE) return; + this->state = TextureLoaderLoadState::SYNC_LOADING; + assertNotNull(this->sharedTexture, "Texture is null!"); + assertNotNull(this->data, "Texture data is null!"); + + // Setup Texture + this->sharedTexture->setSize( + this->width, + this->height, + this->format, + TextureDataFormat::UNSIGNED_BYTE + ); + this->sharedTexture->buffer(this->data); + + // Free data buffer + delete[] this->data; + this->data = nullptr; + + // Leat go of the held pointer + this->sharedTexture = nullptr; + + // Hand off and call done + this->state = TextureLoaderLoadState::SYNC_DONE; + this->loaded = true; } TextureLoader::~TextureLoader() { + if(this->data != nullptr) { + delete[] this->data; + this->data = nullptr; + } this->sharedTexture = nullptr; } \ No newline at end of file diff --git a/src/dawn/asset/loaders/TextureLoader.hpp b/src/dawn/asset/loaders/TextureLoader.hpp index 83e78bf4..d5b19ddf 100644 --- a/src/dawn/asset/loaders/TextureLoader.hpp +++ b/src/dawn/asset/loaders/TextureLoader.hpp @@ -11,34 +11,25 @@ #define TEXTURE_LOADER_HEADER_SIZE 256 namespace Dawn { - enum class TextureLoaderHeaderParseState { - VERSION, - WIDTH, - HEIGHT, - FORMAT, - WRAP_MODE_X, - WRAP_MODE_Y, - FILTER_MODE_MIN, - FILTER_MODE_MAG, - DONE - }; - enum class TextureLoaderLoadState { INITIAL, ASYNC_LOADING, + ASYNC_DONE, + SYNC_LOADING, + SYNC_DONE }; class TextureLoader : public AssetLoader { protected: AssetDataLoader loader; - // int32_t width = -1, height = -1; - // uint8_t *colors; enum TextureLoaderLoadState state; - // enum TextureFormat format; - // enum TextureWrapMode wrapModeX; - // enum TextureWrapMode wrapModeY; - // enum TextureFilterMode filterModeMin; - // enum TextureFilterMode filterModeMag; + uint8_t *data = nullptr; + int32_t width = -1, height = -1; + enum TextureFormat format; + enum TextureWrapMode wrapX; + enum TextureWrapMode wrapY; + enum TextureFilterMode filterMin; + enum TextureFilterMode filterMag; public: std::shared_ptr sharedTexture; diff --git a/src/dawn/dawnlibs.hpp b/src/dawn/dawnlibs.hpp index 05ed6c48..3a4c2081 100644 --- a/src/dawn/dawnlibs.hpp +++ b/src/dawn/dawnlibs.hpp @@ -17,6 +17,7 @@ extern "C" { #include #include #include + #include typedef bool bool_t; typedef char char_t; diff --git a/src/dawn/display/ITexture.hpp b/src/dawn/display/ITexture.hpp index 506aed57..dc0952e8 100644 --- a/src/dawn/display/ITexture.hpp +++ b/src/dawn/display/ITexture.hpp @@ -27,8 +27,8 @@ namespace Dawn { }; enum class TextureDataFormat { - UNSIGNED_BYTE = 0, - FLOAT = 1 + UNSIGNED_BYTE = sizeof(uint8_t), + FLOAT = sizeof(float_t) }; class ITexture { diff --git a/src/dawn/environment/CMakeLists.txt b/src/dawn/environment/CMakeLists.txt new file mode 100644 index 00000000..5893dcaa --- /dev/null +++ b/src/dawn/environment/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2023 Dominic Msters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DAWN_TARGET_NAME} + PRIVATE + Environment.cpp +) \ No newline at end of file diff --git a/src/dawn/environment/Environment.cpp b/src/dawn/environment/Environment.cpp new file mode 100644 index 00000000..023d4431 --- /dev/null +++ b/src/dawn/environment/Environment.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "Environment.hpp" +#include "assert/assert.hpp" + +using namespace Dawn; + +Environment environment; + +bool_t Environment::hasVariable(const std::string &key) { + assertTrue(key.length() > 0, "Key must be at least 1 character long."); + return this->variables.find(key) != this->variables.end(); +} + +void Environment::setVariable( + const std::string &key, + const std::string &value +) { + assertTrue(key.length() > 0, "Key must be at least 1 character long."); + + this->variables[key] = value; +} + +std::string Environment::getVariable(const std::string &key) { + assertTrue(key.length() > 0, "Key must be at least 1 character long."); + assertTrue(this->hasVariable(key), "Variable does not exist."); + return this->variables[key]; +} diff --git a/src/dawn/environment/Environment.hpp b/src/dawn/environment/Environment.hpp index 39c62e61..e4fd3c25 100644 --- a/src/dawn/environment/Environment.hpp +++ b/src/dawn/environment/Environment.hpp @@ -8,8 +8,34 @@ namespace Dawn { class Environment { - public: + private: + std::map variables; + public: + /** + * Checks if the environment has a variable. + * + * @param key Variable key to check. + * @return True if the variable exists, false otherwise. + */ + bool_t hasVariable(const std::string &key); + + /** + * Sets a variable in the environment. + * + * @param key Variable key to set. + * @param value Variable value to set. + */ + void setVariable(const std::string &key, const std::string &value); + + /** + * Gets a variable from the environment. + * + * @param key Variable key to get. + * @return Variable value, or empty string if not found. + */ + std::string getVariable(const std::string &key); }; - extern Dawn::Environment environment; } + +extern Dawn::Environment environment; \ No newline at end of file diff --git a/src/dawnhelloworld/scene/HelloWorldScene.cpp b/src/dawnhelloworld/scene/HelloWorldScene.cpp index 0acadcc4..65c87596 100644 --- a/src/dawnhelloworld/scene/HelloWorldScene.cpp +++ b/src/dawnhelloworld/scene/HelloWorldScene.cpp @@ -23,7 +23,7 @@ void Dawn::helloWorldScene(Scene &s) { cubeMesh->createBuffers(CUBE_VERTICE_COUNT, CUBE_INDICE_COUNT); CubeMesh::buffer(cubeMesh, glm::vec3(-1, -1, -1), glm::vec3(1, 1, 1), 0, 0); - auto texture = s.getGame()->assetManager.get("test_texture"); + auto texture = s.getGame()->assetManager.get("texture_border"); auto cubeItem = s.createSceneItem(); auto cubeMeshRenderer = cubeItem->addComponent(); diff --git a/src/dawnlinux/main.cpp b/src/dawnlinux/main.cpp index 0a95ccbc..47e26ed8 100644 --- a/src/dawnlinux/main.cpp +++ b/src/dawnlinux/main.cpp @@ -4,21 +4,60 @@ // https://opensource.org/licenses/MIT #include "main.hpp" +#include "assert/assert.hpp" #include "display/RenderHost.hpp" #include "game/Game.hpp" #include "asset/AssetDataLoader.hpp" +#include "environment/Environment.hpp" +#include using namespace Dawn; -std::string executablePath; +namespace fs = std::filesystem; FILE * AssetDataLoader::openAssetArchiveFile() { - std::string path = - return fopen((path + "../../assets.tar").c_str(), "rb"); + auto path = environment.getVariable("assetsPath"); + auto file = fopen(path.c_str(), "rb"); + if(!file) { + assertUnreachable("Failed to open assets file.\n%s!", strerror(errno)); + return nullptr; + } + return file; } int32_t main(int32_t argc, const char **argv) { //Set the path - path = argv[0]; + assertTrue(argc > 0, "No executable path provided."); + environment.setVariable("executablePath", argv[0]); + + auto execPath = fs::path(environment.getVariable("executablePath")); + auto execDir = execPath; + while(fs::is_directory(execDir)) execDir = execDir.parent_path(); + environment.setVariable("executableDir", execDir.string()); + + // Find the assets + std::vector pathsToTryAndFindAssets = { + execDir / "assets.tar", + execDir / "assets.zip", + execDir / "data" / "assets.tar", + execDir / "data" / "assets.zip", + execDir.parent_path() / "assets.tar", + execDir.parent_path() / "assets.zip", + execDir.parent_path() / "data" / "assets.tar", + execDir.parent_path() / "data" / "assets.zip", + execDir.parent_path().parent_path() / "assets.tar", + execDir.parent_path().parent_path() / "assets.zip", + execDir.parent_path().parent_path() / "data" / "assets.tar", + execDir.parent_path().parent_path() / "data" / "assets.zip" + }; + auto matchingPathIfAny = std::find_if(pathsToTryAndFindAssets.begin(), pathsToTryAndFindAssets.end(), [](const fs::path &p) { + return fs::exists(p); + }); + + if(matchingPathIfAny == pathsToTryAndFindAssets.end()) { + std::cout << "Could not find game assets" << std::endl; + return 1; + } + environment.setVariable("assetsPath", matchingPathIfAny->string()); //Create the game auto game = std::make_shared();