diff --git a/.gitmodules b/.gitmodules index fb40d0ce..f943cf3f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "lib/freetype"] path = lib/freetype url = https://gitlab.freedesktop.org/freetype/freetype.git +[submodule "lib/libarchive"] + path = lib/libarchive + url = https://github.com/libarchive/libarchive diff --git a/CMakeLists.txt b/CMakeLists.txt index 5849193d..d72d5a0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,8 @@ set(DAWN_CACHE_TARGET "dawn-target") # Set Common Build Variables set(DAWN_ROOT_DIR "${CMAKE_SOURCE_DIR}") set(DAWN_BUILD_DIR "${CMAKE_BINARY_DIR}") -set(DAWN_TOOLS_DIR "${DAWN_ROOT_DIR}/tools") set(DAWN_SOURCES_DIR "${DAWN_ROOT_DIR}/src") +set(DAWN_TOOLS_DIR "${DAWN_SOURCES_DIR}/dawntools") set(DAWN_ASSETS_SOURCE_DIR "${DAWN_ROOT_DIR}/assets") set(DAWN_ASSETS_BUILD_DIR "${DAWN_BUILD_DIR}/assets") set(DAWN_GENERATED_DIR "${DAWN_BUILD_DIR}/generated") diff --git a/assets/games/liminal/prefabs/ButtonPrefab.xml b/assets/games/liminal/prefabs/ButtonPrefab.xml index b87ad686..371a276a 100644 --- a/assets/games/liminal/prefabs/ButtonPrefab.xml +++ b/assets/games/liminal/prefabs/ButtonPrefab.xml @@ -8,11 +8,11 @@ --> - - - + + + - + @@ -20,7 +20,7 @@ Hello Button. @@ -109,10 +110,16 @@ useEvent([&]{ hoverDeocration->color = COLOR_WHITE; + backgroundLeft->color = backgroundMiddle->color = backgroundRight->color = COLOR_BLUE; + wingsLeft->texture = &wingsOpen->texture; + wingsRight->texture = &wingsOpen->texture; }, menuItem->eventHoveredOn); useEvent([&]{ hoverDeocration->color = COLOR_TRANSPARENT; + backgroundLeft->color = backgroundMiddle->color = backgroundRight->color = COLOR_RED; + wingsLeft->texture = &wingsDown->texture; + wingsRight->texture = &wingsDown->texture; }, menuItem->eventHoveredOff); \ No newline at end of file diff --git a/assets/games/liminal/prefabs/VNTextbox.xml b/assets/games/liminal/prefabs/VNTextbox.xml index d8a0ec8f..9410df5c 100644 --- a/assets/games/liminal/prefabs/VNTextbox.xml +++ b/assets/games/liminal/prefabs/VNTextbox.xml @@ -28,10 +28,10 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 4c1f567d..e805855a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -25,6 +25,9 @@ target_include_directories(stb INTERFACE stb) # FreeType add_subdirectory(freetype) +# LibArchive +add_subdirectory(libarchive) + # OpenAL if(DAWN_TARGET_OPENAL) set(LIBTYPE "STATIC") diff --git a/lib/libarchive b/lib/libarchive new file mode 160000 index 00000000..2ba3d927 --- /dev/null +++ b/lib/libarchive @@ -0,0 +1 @@ +Subproject commit 2ba3d92706384be14cd376734f3f7ebe5648591e diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b1328f4c..4e1b9a3a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -71,4 +71,7 @@ if(DEFINED DAWN_TARGET_NAME) else() add_dependencies(${DAWN_TARGET_NAME} ${DAWN_TARGET_DEPENDENCIES_LAST}) endif() + + # Compress the game assets. + add_dependencies(${DAWN_TARGET_NAME} dawnassets) endif() \ No newline at end of file diff --git a/src/dawn/CMakeLists.txt b/src/dawn/CMakeLists.txt index 5f90aa66..1be141bc 100644 --- a/src/dawn/CMakeLists.txt +++ b/src/dawn/CMakeLists.txt @@ -9,6 +9,7 @@ target_link_libraries(${DAWN_TARGET_NAME} glm stb freetype + archive_static ) # Includes diff --git a/src/dawn/asset/AssetLoader.cpp b/src/dawn/asset/AssetLoader.cpp index 2772b13b..4e90945e 100644 --- a/src/dawn/asset/AssetLoader.cpp +++ b/src/dawn/asset/AssetLoader.cpp @@ -4,91 +4,169 @@ // https://opensource.org/licenses/MIT #include "AssetLoader.hpp" +#include "util/mathutils.hpp" using namespace Dawn; +ssize_t assetLoaderArchiveRead( + struct archive *archive, + void *d, + const void **buffer +) { + assertNotNull(archive, "assetArchiveRead: Archive is NULL!"); + assertNotNull(d, "assetArchiveRead: Data is NULL!"); + assertNotNull(buffer, "assetArchiveRead: Buffer is NULL!"); + AssetLoader *loader = (AssetLoader*)d; + + *buffer = loader->buffer; + size_t read = fread( + loader->buffer, 1, ASSET_LOADER_BUFFER_SIZE, loader->assetArchiveFile + ); + if(ferror(loader->assetArchiveFile)) return ARCHIVE_FATAL; + return read; +} + +int64_t assetLoaderArchiveSeek( + struct archive *archive, + void *d, + int64_t offset, + int32_t whence +) { + assertNotNull(archive, "assetArchiveSeek: Archive is NULL!"); + assertNotNull(d, "assetArchiveSeek: Data is NULL!"); + assertTrue(offset > 0, "assetArchiveSeek: Offset must be greater than 0!"); + AssetLoader *loader = (AssetLoader*)d; + int32_t ret = fseek(loader->assetArchiveFile, offset, whence); + assertTrue(ret == 0, "assetArchiveSeek: Failed to seek!"); + return ftell(loader->assetArchiveFile); +} + +int32_t assetLoaderArchiveOpen(struct archive *a, void *d) { + assertNotNull(a, "assetArchiveOpen: Archive is NULL!"); + assertNotNull(d, "assetArchiveOpen: Data is NULL!"); + AssetLoader *loader = (AssetLoader*)d; + + int32_t ret = fseek(loader->assetArchiveFile, 0, SEEK_SET); + assertTrue(ret == 0, "assetArchiveOpen: Failed to seek to start of file!"); + return ARCHIVE_OK; +} + +int32_t assetLoaderArchiveClose(struct archive *a, void *d) { + assertNotNull(a, "assetArchiveClose: Archive is NULL!"); + assertNotNull(d, "assetArchiveClose: Data is NULL!"); + return assetLoaderArchiveOpen(a, d); +} + AssetLoader::AssetLoader(std::string fileName) { assertTrue(fileName.size() > 0, "AssetLoader::AssetLoader: fileName must be greater than 0"); - this->fileName = fileName; - this->handle = nullptr; } void AssetLoader::open() { - assertNull(this->handle, "AssetLoader::open: File is already open"); - std::string pathFull = DAWN_ASSET_BUILD_PREFIX + this->fileName; - this->handle = fopen(pathFull.c_str(), "rb"); - assertNotNull(this->handle, "AssetLoader::open: Failed to open file " + pathFull); + assertNull(this->assetArchiveFile, "AssetLoader::open: File is already open"); + assertNull(this->assetArchive, "AssetLoader::open: Archive is already open"); + assertNull(this->assetArchiveEntry, "AssetLoader::open: Entry is already open"); + + this->assetArchiveFile = fopen(DAWN_ASSET_LOCATION, "rb"); + assertNotNull(this->assetArchiveFile, "AssetLoader::open: Failed to open file " + std::string(DAWN_ASSET_LOCATION)); + + // Open archive reader + assetArchive = archive_read_new(); + assertNotNull(assetArchive, "AssetLoader::open: Failed to create archive reader"); + + // Set up the reader + archive_read_support_format_tar(assetArchive); + + // Open reader + archive_read_set_open_callback(assetArchive, &assetLoaderArchiveOpen); + archive_read_set_read_callback(assetArchive, &assetLoaderArchiveRead); + archive_read_set_seek_callback(assetArchive, &assetLoaderArchiveSeek); + archive_read_set_close_callback(assetArchive, &assetLoaderArchiveClose); + archive_read_set_callback_data(assetArchive, this); + + int32_t ret = archive_read_open1(assetArchive); + assertTrue(ret == ARCHIVE_OK, "AssetLoader::open: Failed to open archive!"); + position = 0; + + // Iterate over each file to find the one for this asset loader. + while(archive_read_next_header(assetArchive, &assetArchiveEntry) == ARCHIVE_OK) { + const char_t *headerFile = (char_t*)archive_entry_pathname(assetArchiveEntry); + if(std::string(headerFile) == this->fileName) return; + int32_t ret = archive_read_data_skip(assetArchive); + assertTrue(ret == ARCHIVE_OK, "AssetLoader::open: Failed to skip data!"); + } + + assertUnreachable("AssetLoader::open: Failed to find file!"); } int32_t AssetLoader::close() { - assertNotNull(this->handle, "AssetLoader::close: File is not open"); - int32_t ret = fclose(this->handle); - this->handle = nullptr; - return ret; + assertNotNull(this->assetArchiveFile, "AssetLoader::close: File is NULL"); + assertNotNull(this->assetArchive, "AssetLoader::close: Archive is NULL!"); + assertNotNull(this->assetArchiveEntry, "AssetLoader::close: Entry is NULL!"); + + // Close the archive + int32_t ret = archive_read_free(this->assetArchive); + assertTrue(ret == ARCHIVE_OK, "AssetLoader::close: Failed to close archive!"); + + this->assetArchive = nullptr; + this->assetArchiveEntry = nullptr; + + // Close the file + int32_t res = fclose(this->assetArchiveFile); + this->assetArchiveFile = nullptr; + return res; } size_t AssetLoader::read(uint8_t *buffer, size_t size) { - assertNotNull(buffer, "AssetLoader::read: buffer must not be null"); - assertTrue(size > 0, "AssetLoader::read: size must be greater than 0"); - assertNotNull(this->handle, "AssetLoader::read: File is not open"); - return fread(buffer, 1, size, this->handle); + assertNotNull(buffer, "assetArchiveRead: Buffer is NULL!"); + assertTrue(size > 0, "assetArchiveRead: Size must be greater than 0!"); + assertNotNull(this->assetArchive, "assetRead: Archive is NULL!"); + assertNotNull(this->assetArchiveEntry, "assetRead: Entry is NULL!"); + + ssize_t read = archive_read_data(this->assetArchive, buffer, size); + this->position += read; + + if(read == ARCHIVE_FATAL) { + assertUnreachable(archive_error_string(this->assetArchive)); + } + + assertTrue(read != ARCHIVE_RETRY, "assetRead: Failed to read data (RETRY)!"); + assertTrue(read != ARCHIVE_WARN, "assetRead: Failed to read data (WARN)!"); + + return read; } -int32_t AssetLoader::end() { - assertNotNull(this->handle, "AssetLoader::end: File is not open"); - return fseek(this->handle, 0, SEEK_END); +size_t AssetLoader::getSize() { + assertTrue(this->assetArchiveEntry != nullptr, "AssetLoader::getSize: Entry is NULL!"); + assertTrue(archive_entry_size_is_set(assetArchiveEntry), "assetGetSize: Entry size is not set!"); + return archive_entry_size(assetArchiveEntry); } size_t AssetLoader::skip(size_t n) { - assertTrue(n > 0, "AssetLoader::skip: n must be greater than 0"); - assertNotNull(this->handle, "AssetLoader::skip: File is not open"); - return fseek(this->handle, n, SEEK_CUR); + assertTrue(n >= 0, "AssetLoader::skip: Byte count must be greater than 0."); + + uint8_t dumpBuffer[ASSET_LOADER_BUFFER_SIZE]; + size_t skipped = 0; + size_t n2, n3; + while(n != 0) { + n2 = mathMin(n, ASSET_LOADER_BUFFER_SIZE); + n3 = this->read(dumpBuffer, n2); + assertTrue(n3 == n2, "AssetLoader::skip: Failed to skip bytes!"); + n -= n3; + } + + return skipped; } -int32_t AssetLoader::rewind() { - assertNotNull(this->handle, "AssetLoader::rewind: File is not open"); - return fseek(this->handle, 0, SEEK_SET); +void AssetLoader::rewind() { + // TODO: See if I can optimize this + this->close(); + this->open(); } size_t AssetLoader::getPosition() { - assertNotNull(this->handle, "AssetLoader::getPosition: File is not open"); - return ftell(this->handle); -} - -size_t AssetLoader::loadRaw(uint8_t **buffer) { - size_t length, read; - - assertNotNull(buffer, "AssetLoader::loadRaw: buffer must not be null"); - - // Open a buffer. - this->open(); - - // Read the count of bytes in the file - this->end(); - length = this->getPosition(); - - // Are we only reading the size? - if(buffer == nullptr) { - this->close(); - return length; - } - - // Reset to start - this->rewind(); - - // Read the string then close the file handle. - *buffer = static_cast(memoryAllocate(sizeof(uint8_t) * length)); - read = this->read(*buffer, length); - this->close(); - - // Did we read successfully? - if(read < length) { - throw "Failed to read all bytes of " + this->fileName; - } - - // Read successfully, return the read bytes. - return read; + assertNotNull(this->assetArchiveFile, "AssetLoader::getPosition: File is not open!"); + return this->position; } size_t AssetLoader::setPosition(size_t position) { @@ -98,8 +176,5 @@ size_t AssetLoader::setPosition(size_t position) { } AssetLoader::~AssetLoader() { - if(this->handle != nullptr) { - this->close(); - this->handle = nullptr; - } + if(this->assetArchiveFile != nullptr) this->close(); } \ No newline at end of file diff --git a/src/dawn/asset/AssetLoader.hpp b/src/dawn/asset/AssetLoader.hpp index c3b5d4b8..6cb820f5 100644 --- a/src/dawn/asset/AssetLoader.hpp +++ b/src/dawn/asset/AssetLoader.hpp @@ -4,17 +4,81 @@ // https://opensource.org/licenses/MIT #pragma once +extern "C" { + #include + #include +} #include "dawnlibs.hpp" #include "assert/assert.hpp" #include "util/memory.hpp" +#define ASSET_LOADER_BUFFER_SIZE 32768 + +#if !defined(DAWN_ASSET_LOCATION) + #error Asset Archive Location has not been defined. +#endif + +/** + * Method invoked by the libarchive internals to read bytes from the archive + * file pointer. + * + * @param archive Archive requesting the read. + * @param data Data pointer passed to the archive. + * @param buffer Pointer to where the buffer pointer should be stored. + * @return Count of bytes read. + */ +ssize_t assetLoaderArchiveRead( + struct archive *archive, + void *data, + const void **buffer +); + +/** + * Method invoked by the libarchive internals to seek the archive file pointer. + * + * @param archive Archive requesting the seek. + * @param data Data pointer passed to the archive. + * @param offset Offset to seek to. + * @param whence Whence to seek from. + * @return The new offset. + */ +int64_t assetLoaderArchiveSeek( + struct archive *archive, + void *data, + int64_t offset, + int32_t whence +); + +/** + * Method invoked by the libarchive internals to open the archive file pointer. + * + * @param archive Archive requesting the open. + * @param data Data pointer passed to the archive. + * @return 0 if success, otherwise for failure. + */ +int32_t assetLoaderArchiveOpen(struct archive *a, void *data); + +/** + * Method invoked by the libarchive internals to close the archive file pointer. + * + * @param archive Archive requesting the close. + * @param data Data pointer passed to the archive. + * @return 0 if success, otherwise for failure. + */ +int32_t assetLoaderArchiveClose(struct archive *a, void *data); + namespace Dawn { class AssetLoader { private: std::string fileName; - FILE *handle; + struct archive *assetArchive = nullptr; + struct archive_entry *assetArchiveEntry = nullptr; + size_t position; public: + uint8_t buffer[ASSET_LOADER_BUFFER_SIZE]; + FILE *assetArchiveFile = nullptr; + /** * Create a new asset loader. Asset Loaders can be used to load data from * a file in a myriad of ways. @@ -45,46 +109,39 @@ namespace Dawn { size_t read(uint8_t *buffer, size_t size); /** - * Skip to the end of the buffer, useful to find the length of the buffer. - * @return 0 if successful, otherwise false. + * Get the size of the asset. + * @return The size of the asset in bytes. */ - int32_t end(); + size_t getSize(); /** - * Method to skip n bytes in the buffer - * @param n Count of bytes to skip. - * @return 0 if successful, otherwise unsuccessful. + * Skips the read head forward to a given position. + * + * @param n Count of bytes to progress the read head by. + * @return Count of bytes progressed. */ size_t skip(size_t n); /** - * Rewinds to the start of the asset buffer. - * @return 0 if successful, otherwise unsuccessful. - */ - int32_t rewind(); - + * Rewind the read head to the beginning of the file. + */ + void rewind(); + /** - * Retreive the current byte position within the asset that the head is - * at. - * @return Position (in bytes) that the current seek is at. + * Sets the absolute position of the read head within the buffer of the + * file. + * + * @param absolutePosition Absolute position to set the read head to. + */ + size_t setPosition(size_t absolutePosition); + + /** + * Returns the current position of the read head. + * + * @return The current read head position. */ size_t getPosition(); - /** - * Sets the position of the file read head. - * - * @param position Position to set to. - * @return How many bytes were skipped / rewound. - */ - size_t setPosition(size_t position); - - /** - * Loads the entire file into a raw buffer. - * @param buffer Pointer to where a pointer to the buffer will be stored. - * @return Size of the buffer that was read (in bytes). - */ - size_t loadRaw(uint8_t **buffer); - /** * Run a callback for each byte within the asset. The callback will * receive each byte individually. diff --git a/src/dawn/asset/AssetManager.cpp b/src/dawn/asset/AssetManager.cpp index a2288948..8fc7f750 100644 --- a/src/dawn/asset/AssetManager.cpp +++ b/src/dawn/asset/AssetManager.cpp @@ -5,10 +5,6 @@ #include "AssetManager.hpp" -#if !defined(DAWN_ASSET_BUILD_PREFIX) - #error Asset Prefix has not been defined. -#endif - using namespace Dawn; void AssetManager::init() { diff --git a/src/dawn/asset/assets/LanguageAsset.cpp b/src/dawn/asset/assets/LanguageAsset.cpp index e277fb51..3c4ee765 100644 --- a/src/dawn/asset/assets/LanguageAsset.cpp +++ b/src/dawn/asset/assets/LanguageAsset.cpp @@ -19,22 +19,20 @@ void LanguageAsset::updateSync() { } void LanguageAsset::updateAsync() { - assertTrue(this->state == 0x00, "LanguageAsset::updateAsync: State must be 0x00"); + assertTrue( + this->state == LANGUAGE_ASSET_LOAD_STATE_NOT_LOADING, + "LanguageAsset::updateAsync: State must be NOT_LOADING" + ); // Open Asset - this->state = 0x01; + this->state = LANGUAGE_ASSET_LOAD_STATE_OPENING; this->loader.open(); // Get Length - this->state = 0x02; - this->loader.end(); - this->state = 0x03; - size_t len = this->loader.getPosition(); - this->state = 0x04; - this->loader.rewind(); + size_t len = this->loader.getSize(); // Create buffer - this->state = 0x05; + this->state = LANGUAGE_ASSET_LOAD_STATE_CREATING_BUFFER; size_t position = 0; // Loop over CSV @@ -55,23 +53,22 @@ void LanguageAsset::updateAsync() { // Prepare for next string. position = position + (size_t)(valueEnd - buffer + 1); - this->loader.rewind(); - this->loader.skip(position); + this->loader.setPosition(position); // Store strings. std::string key((char *)keyStart); this->values[key] = value; } - this->state = 0x06; - this->loader.rewind(); - - this->state = 0x07; + this->state = LANGUAGE_ASSET_LOAD_STATE_READY_TO_READ; this->loaded = true; } std::string LanguageAsset::getValue(std::string key) { - assertTrue(this->state == 0x07, "LanguageAsset::getValue: State must be 0x07"); + assertTrue( + this->state == LANGUAGE_ASSET_LOAD_STATE_READY_TO_READ, + "LanguageAsset::getValue: State must be LANGUAGE_ASSET_LOAD_STATE_READY_TO_READ" + ); assertMapHasKey(this->values, key, "LanguageAsset::getValue: Key does not exist"); assertTrue(this->values[key].begin >= 0 && this->values[key].length > 0, "LanguageAsset::getValue: Value is invalid"); diff --git a/src/dawn/asset/assets/LanguageAsset.hpp b/src/dawn/asset/assets/LanguageAsset.hpp index 7fda9a3a..72aaaef5 100644 --- a/src/dawn/asset/assets/LanguageAsset.hpp +++ b/src/dawn/asset/assets/LanguageAsset.hpp @@ -13,10 +13,17 @@ namespace Dawn { size_t length; }; + enum LangageAssetLoadState { + LANGUAGE_ASSET_LOAD_STATE_NOT_LOADING, + LANGUAGE_ASSET_LOAD_STATE_OPENING, + LANGUAGE_ASSET_LOAD_STATE_CREATING_BUFFER, + LANGUAGE_ASSET_LOAD_STATE_READY_TO_READ + }; + class LanguageAsset : public Asset { protected: AssetLoader loader; - uint8_t state = 0x00; + enum LangageAssetLoadState state = LANGUAGE_ASSET_LOAD_STATE_NOT_LOADING; std::map values; uint8_t buffer[1024]; diff --git a/src/dawn/asset/assets/TextureAsset.cpp b/src/dawn/asset/assets/TextureAsset.cpp index 79180e7f..69c17a40 100644 --- a/src/dawn/asset/assets/TextureAsset.cpp +++ b/src/dawn/asset/assets/TextureAsset.cpp @@ -37,7 +37,11 @@ void TextureAsset::updateSync() { void TextureAsset::updateAsync() { if(this->state != 0x00) return; this->state = 0x01; - this->loader.loadRaw(&this->buffer); + std::cout << "Update texture tool" << std::endl; + this->loader.open(); + this->buffer = (uint8_t*)memoryAllocate(this->loader.getSize()); + this->loader.read(this->buffer, this->loader.getSize()); + this->loader.close(); this->state = 0x02; // Parse header data. diff --git a/src/dawn/asset/assets/TilesetAsset.cpp b/src/dawn/asset/assets/TilesetAsset.cpp index ad96c160..3c475f81 100644 --- a/src/dawn/asset/assets/TilesetAsset.cpp +++ b/src/dawn/asset/assets/TilesetAsset.cpp @@ -19,11 +19,13 @@ void TilesetAsset::updateSync() { } void TilesetAsset::updateAsync() { - uint8_t *buffer; assertTrue(this->state == 0x00, "TilesetAsset::updateAsync: Initial state should be 0x00"); this->state = 0x01; - this->loader.loadRaw(&buffer); + this->loader.open(); + uint8_t *buffer = (uint8_t*)memoryAllocate(this->loader.getSize()); + this->loader.read(buffer, this->loader.getSize()); + this->loader.close(); this->state = 0x02; char *strCurrent = (char *)buffer; diff --git a/src/dawn/asset/assets/TrueTypeAsset.cpp b/src/dawn/asset/assets/TrueTypeAsset.cpp index 32194c09..65261230 100644 --- a/src/dawn/asset/assets/TrueTypeAsset.cpp +++ b/src/dawn/asset/assets/TrueTypeAsset.cpp @@ -103,7 +103,6 @@ void TrueTypeAsset::updateAsync() { struct TrueTypeAssetStyle style; // Buffer - this->loader.rewind(); this->loader.setPosition(styleListBegin); read = this->loader.read(buffer, 32); assertTrue(read == 32, "TrueTypeAsset::updateAsync: Could not read variant"); @@ -116,7 +115,6 @@ void TrueTypeAsset::updateAsync() { styleListBegin += i + 1; // Buffer - this->loader.rewind(); this->loader.setPosition(styleListBegin); read = this->loader.read(buffer, 32); assertTrue(read == 32, "TrueTypeAsset::updateAsync: Could not read variant style"); diff --git a/src/dawn/display/RenderPipeline.cpp b/src/dawn/display/RenderPipeline.cpp index 8e156588..7f33a1c2 100644 --- a/src/dawn/display/RenderPipeline.cpp +++ b/src/dawn/display/RenderPipeline.cpp @@ -143,12 +143,21 @@ void RenderPipeline::renderSceneCamera(Scene *scene, Camera *camera) { } #endif + // Inject index into each item + itPassItem = shaderPassItems.begin(); + while(itPassItem != shaderPassItems.end()) { + itPassItem->index = itPassItem; + ++itPassItem; + } + // Now we've queued everything, let's sort the rendering queue by the priority std::sort( shaderPassItems.begin(), shaderPassItems.end(), [](struct ShaderPassItem &a, struct ShaderPassItem &b) { if(a.priority == b.priority) { + // Compare indexes if w is same. + if(a.w == b.w) return a.index < b.index; return a.w < b.w; } return a.priority < b.priority; diff --git a/src/dawn/display/shader/ShaderPass.hpp b/src/dawn/display/shader/ShaderPass.hpp index 4943ffc1..6ff85a12 100644 --- a/src/dawn/display/shader/ShaderPass.hpp +++ b/src/dawn/display/shader/ShaderPass.hpp @@ -8,9 +8,12 @@ #include "display/mesh/Mesh.hpp" namespace Dawn { + struct ShaderPassItem; + struct ShaderPassItem { Shader *shader = nullptr; int32_t priority = 0; + std::vector::iterator index; Mesh *mesh; int32_t start = 0; diff --git a/src/dawn/scene/components/ui/UIComponent.cpp b/src/dawn/scene/components/ui/UIComponent.cpp index 5c0e74be..b22411a8 100644 --- a/src/dawn/scene/components/ui/UIComponent.cpp +++ b/src/dawn/scene/components/ui/UIComponent.cpp @@ -71,17 +71,6 @@ void UIComponent::updateAlignment() { glm::vec2(align[0], align[2]) ); } else { - UIComponent::calculateDimensions( - this->alignY, - this->alignUnitTop, - this->alignUnitBottom, - &translate.y, - &this->height, - parentInnerHeight, - this->getContentHeight(), - this->width, - glm::vec2(align[1], align[3]) - ); UIComponent::calculateDimensions( this->alignX, this->alignUnitLeft, @@ -93,6 +82,17 @@ void UIComponent::updateAlignment() { this->height, glm::vec2(align[0], align[2]) ); + UIComponent::calculateDimensions( + this->alignY, + this->alignUnitTop, + this->alignUnitBottom, + &translate.y, + &this->height, + parentInnerHeight, + this->getContentHeight(), + this->width, + glm::vec2(align[1], align[3]) + ); } @@ -104,6 +104,38 @@ void UIComponent::updateAlignment() { this->eventAlignmentUpdated.invoke(); } +float_t UIComponent::getWidthFromAlignment() { + switch(this->alignX) { + case UI_COMPONENT_ALIGN_STRETCH: + case UI_COMPONENT_ALIGN_START: + case UI_COMPONENT_ALIGN_MIDDLE: + return alignment._realValue[2]; + + case UI_COMPONENT_ALIGN_END: + return alignment._realValue[0]; + + default: + assertUnreachable("UIComponent::getWidthFromAlignment: Unknown alignment"); + return -1; + } +} + +float_t UIComponent::getHeightFromAlignment() { + switch(this->alignY) { + case UI_COMPONENT_ALIGN_STRETCH: + case UI_COMPONENT_ALIGN_START: + case UI_COMPONENT_ALIGN_MIDDLE: + return alignment._realValue[3]; + + case UI_COMPONENT_ALIGN_END: + return alignment._realValue[1]; + + default: + assertUnreachable("UIComponent::getWidthFromAlignment: Unknown alignment"); + return -1; + } +} + float_t UIComponent::calculateAlignmentValue( float_t alignmentValue, float_t parentSize, diff --git a/src/dawn/scene/components/ui/UIComponent.hpp b/src/dawn/scene/components/ui/UIComponent.hpp index e43fe6aa..37c87c6f 100644 --- a/src/dawn/scene/components/ui/UIComponent.hpp +++ b/src/dawn/scene/components/ui/UIComponent.hpp @@ -44,6 +44,24 @@ namespace Dawn { * Internal method to update the alignment of this item. */ virtual void updateAlignment(); + + /** + * Helper function used for UI components that intend to use whatever the + * dimensions that are set within the alignment parameter are for their + * width. + * + * @return The width as defined in the alignment. + */ + float_t getWidthFromAlignment(); + + /** + * Helper function used for UI components that intend to use whatever the + * dimensions that are set within the alignment parameter are for their + * height. + * + * @return The height as defined in the alignment. + */ + float_t getHeightFromAlignment(); public: StateProperty alignmentNeedsUpdating; diff --git a/src/dawn/scene/components/ui/UIEmpty.cpp b/src/dawn/scene/components/ui/UIEmpty.cpp index 4342c4d7..875e7a67 100644 --- a/src/dawn/scene/components/ui/UIEmpty.cpp +++ b/src/dawn/scene/components/ui/UIEmpty.cpp @@ -10,10 +10,10 @@ using namespace Dawn; UIEmpty::UIEmpty(SceneItem *item) : UIComponent(item) { } float_t UIEmpty::getContentWidth() { - return this->getWidth(); + return this->getWidthFromAlignment(); } float_t UIEmpty::getContentHeight() { - return this->getHeight(); + return this->getHeightFromAlignment(); } float_t UIEmpty::getChildOffsetX() { return 0.0f; } float_t UIEmpty::getChildOffsetY() { return 0.0f; } \ No newline at end of file diff --git a/src/dawn/scene/components/ui/text/UILabel.cpp b/src/dawn/scene/components/ui/text/UILabel.cpp index 6c211bcc..6f9107ab 100644 --- a/src/dawn/scene/components/ui/text/UILabel.cpp +++ b/src/dawn/scene/components/ui/text/UILabel.cpp @@ -10,7 +10,8 @@ using namespace Dawn; UILabel::UILabel(SceneItem *item) : UIComponentRenderable(item), - lineHeight(1.0f) + lineHeight(1.0f), + textAlign(UI_LABEL_TEXT_ALIGN_LEFT) { } diff --git a/src/dawn/scene/components/ui/text/UILabel.hpp b/src/dawn/scene/components/ui/text/UILabel.hpp index 1db2a1dc..0e881fcc 100644 --- a/src/dawn/scene/components/ui/text/UILabel.hpp +++ b/src/dawn/scene/components/ui/text/UILabel.hpp @@ -43,6 +43,13 @@ namespace Dawn { UI_LABEL_VERTICAL_ALIGN_BOTTOM }; + enum UILabelTextAlign { + UI_LABEL_TEXT_ALIGN_LEFT, + UI_LABEL_TEXT_ALIGN_CENTER, + UI_LABEL_TEXT_ALIGN_RIGHT + // TODO: Add justify + }; + class UILabel : public UIComponentRenderable { private: Mesh mesh; @@ -68,6 +75,9 @@ namespace Dawn { // @optional StateProperty lineHeight; + // @optional + StateProperty textAlign; + UILabel(SceneItem *item); void onStart() override; diff --git a/src/dawnliminal/CMakeLists.txt b/src/dawnliminal/CMakeLists.txt index 704d7567..ef104a51 100644 --- a/src/dawnliminal/CMakeLists.txt +++ b/src/dawnliminal/CMakeLists.txt @@ -17,5 +17,4 @@ add_subdirectory(game) add_subdirectory(save) # Assets -set(LIMINAL_ASSETS_DIR ) include("${DAWN_ASSETS_SOURCE_DIR}/games/liminal/CMakeLists.txt") \ No newline at end of file diff --git a/src/dawnliminal/game/LiminalGame.cpp b/src/dawnliminal/game/LiminalGame.cpp index 01b01942..eaaa4856 100644 --- a/src/dawnliminal/game/LiminalGame.cpp +++ b/src/dawnliminal/game/LiminalGame.cpp @@ -5,11 +5,13 @@ #include "game/DawnGame.hpp" #include "vnscenes/SceneInitial.hpp" +#include "scenes/SceneMainMenu.hpp" #include "scenes/HelloWorldScene.hpp" using namespace Dawn; Scene * Dawn::dawnGameGetInitialScene(DawnGame *game) { - return new SceneInitial(game); + // return new SceneInitial(game); // return new HelloWorldScene(game); + return new SceneMainMenu(game); } \ No newline at end of file diff --git a/src/dawnlinux64/CMakeLists.txt b/src/dawnlinux64/CMakeLists.txt index 2ae0f125..3f339a6a 100644 --- a/src/dawnlinux64/CMakeLists.txt +++ b/src/dawnlinux64/CMakeLists.txt @@ -18,7 +18,7 @@ target_include_directories(${DAWN_TARGET_NAME} # Platform variables target_compile_definitions(${DAWN_TARGET_NAME} PUBLIC - DAWN_ASSET_BUILD_PREFIX="../../assets/" + DAWN_ASSET_LOCATION="../../assets.tar" ) # Subdirs diff --git a/src/dawnshared/display/Color.cpp b/src/dawnshared/display/Color.cpp index 64304b0b..36b58d5f 100644 --- a/src/dawnshared/display/Color.cpp +++ b/src/dawnshared/display/Color.cpp @@ -38,6 +38,24 @@ struct Color Color::fromString(std::string str) { } // Hex code? + if(lower[0] == '#') { + // Remove the hash + lower = lower.substr(1); + + // Convert to RGB + if(lower.length() == 3) { + // Convert to 6 digit hex + lower = lower[0] + lower[0] + lower[1] + lower[1] + lower[2] + lower[2]; + } + + // Convert to RGB + return { + (float_t)std::stoi(lower.substr(0, 2), nullptr, 16) / 255.0f, + (float_t)std::stoi(lower.substr(2, 2), nullptr, 16) / 255.0f, + (float_t)std::stoi(lower.substr(4, 2), nullptr, 16) / 255.0f, + 1.0f + }; + } // Split by comma auto splitByComma = stringSplit(str, ","); diff --git a/src/dawnshared/util/parser/TypeParsers.hpp b/src/dawnshared/util/parser/TypeParsers.hpp index c518ef55..122675dc 100644 --- a/src/dawnshared/util/parser/TypeParsers.hpp +++ b/src/dawnshared/util/parser/TypeParsers.hpp @@ -139,6 +139,7 @@ namespace Dawn { if(v.find("middle") != std::string::npos) return "UI_COMPONENT_ALIGN_MIDDLE"; if(v.find("end") != std::string::npos) return "UI_COMPONENT_ALIGN_END"; *error = "Invalid UIComponentAlign value: " + v; + return ""; } static inline std::string uiComponentAlignUnitParser(std::string v, std::string *error) { @@ -147,6 +148,16 @@ namespace Dawn { if(v.find("percent") != std::string::npos) return "UI_COMPONENT_ALIGN_UNIT_PERCENT"; if(v.find("ratio") != std::string::npos) return "UI_COMPONENT_ALIGN_UNIT_RATIO"; *error = "Invalid UIComponentAlignUnit value: " + v; + return ""; + } + + static inline std::string uiLabelTextAlignParser(std::string v, std::string *error) { + v = stringToLowercase(v); + if(v.find("left") != std::string::npos) return "UI_LABEL_TEXT_ALIGN_LEFT"; + if(v.find("center") != std::string::npos) return "UI_LABEL_TEXT_ALIGN_CENTER"; + if(v.find("right") != std::string::npos) return "UI_LABEL_TEXT_ALIGN_RIGHT"; + *error = "Invalid UILabelTextAlign value: " + v; + return ""; } static inline std::function parserFromTypeName(std::string type) { @@ -176,6 +187,8 @@ namespace Dawn { parser = uiComponentAlignParser; } else if(type.ends_with("UIComponentAlignUnit")) { parser = uiComponentAlignUnitParser; + } else if(type.ends_with("UILabelTextAlign")) { + parser = uiLabelTextAlignParser; } else { parser = rawParser; } diff --git a/src/dawntools/CMakeLists.txt b/src/dawntools/CMakeLists.txt index 106d19e3..7c5af684 100644 --- a/src/dawntools/CMakeLists.txt +++ b/src/dawntools/CMakeLists.txt @@ -22,6 +22,7 @@ include(util/parser/CMakeLists.txt) include(util/generator/CMakeLists.txt) # Tools +add_subdirectory(assetstool) add_subdirectory(prefabtool) add_subdirectory(scenetool) add_subdirectory(texturetool) diff --git a/src/dawntools/assetstool/CMakeLists.txt b/src/dawntools/assetstool/CMakeLists.txt new file mode 100644 index 00000000..2cd1a33f --- /dev/null +++ b/src/dawntools/assetstool/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2023 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +add_custom_target(dawnassets + COMMAND ${DAWN_TOOLS_DIR}/assetstool/assetstool.py + --input=${DAWN_ASSETS_BUILD_DIR} + --output=${DAWN_BUILD_DIR}/assets.tar + COMMENT "Bundling assets..." + USES_TERMINAL + DEPENDS ${DAWN_ASSETS} +) \ No newline at end of file diff --git a/src/dawntools/assetstool/assetstool.py b/src/dawntools/assetstool/assetstool.py new file mode 100755 index 00000000..9db833e2 --- /dev/null +++ b/src/dawntools/assetstool/assetstool.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# Copyright (c) 2023 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +import os +import tarfile +import argparse + +# Args +parser = argparse.ArgumentParser(description='Bundles all assets into the internal archive format.') +parser.add_argument('-i', '--input'); +parser.add_argument('-o', '--output'); +args = parser.parse_args() + +# Ensure the directory for the output path exists +if not os.path.exists(os.path.dirname(args.output)): + os.makedirs(os.path.dirname(args.output)) + +# Create a ZIP archive and add the specified directory +# archive = tarfile.open(args.output, 'w:bz2') # BZ2 Compression + +# Does the archive already exist? +filesInArchive = [] + +if os.path.exists(args.output): + # Yes, open it + archive = tarfile.open(args.output, 'r:') + + # Get all the files in the archive + for member in archive.getmembers(): + filesInArchive.append(member.name) + + archive.close() + + # Open archive for appending. + archive = tarfile.open(args.output, 'a:') +else: + # No, create it + archive = tarfile.open(args.output, 'w:') + +# Add all files in the input directory +for foldername, subfolders, filenames in os.walk(args.input): + for filename in filenames: + + # Is the file already in the archive? + absolute_path = os.path.join(foldername, filename) + relative_path = os.path.relpath(absolute_path, args.input) + + if relative_path in filesInArchive: + # Yes, skip it + continue + + # No, add it + print(f"Archiving asset {filename}...") + archive.add(absolute_path, arcname=relative_path) + +# Close the archive +archive.close() \ No newline at end of file diff --git a/src/dawntools/texturetool/CMakeLists.txt b/src/dawntools/texturetool/CMakeLists.txt index 8dc5874b..530a1924 100644 --- a/src/dawntools/texturetool/CMakeLists.txt +++ b/src/dawntools/texturetool/CMakeLists.txt @@ -87,5 +87,5 @@ function(tool_texture target) COMMENT "Generating texture ${target} from ${FILE}" DEPENDS ${DEPS} ) - add_dependencies(${DAWN_TARGET_NAME} ${target}) + add_dependencies(dawnassets ${target}) endfunction() \ No newline at end of file diff --git a/src/dawntools/truetypetool/CMakeLists.txt b/src/dawntools/truetypetool/CMakeLists.txt index 4a3bda50..e5ddc74c 100644 --- a/src/dawntools/truetypetool/CMakeLists.txt +++ b/src/dawntools/truetypetool/CMakeLists.txt @@ -63,5 +63,5 @@ function(tool_truetype target) COMMENT "Generating truetype" DEPENDS ${DEPS} ) - add_dependencies(${DAWN_TARGET_NAME} ${target}) + add_dependencies(dawnassets ${target}) endfunction() \ No newline at end of file diff --git a/src/dawntools/util/generator/SceneItemGenerator.cpp b/src/dawntools/util/generator/SceneItemGenerator.cpp index 3f7154f6..c857bc7a 100644 --- a/src/dawntools/util/generator/SceneItemGenerator.cpp +++ b/src/dawntools/util/generator/SceneItemGenerator.cpp @@ -139,6 +139,14 @@ void SceneItemGenerator::generate( line(initBody, name + "->uiItem->alignment = " + item->alignment + ";", ""); } + if(item->alignX.size() > 0) { + line(initBody, name + "->uiItem->alignX = " + item->alignX + ";", ""); + } + + if(item->alignY.size() > 0) { + line(initBody, name + "->uiItem->alignY = " + item->alignY + ";", ""); + } + if(item->menuX.size() > 0) { line(initBody, name + "->menuItem->menuX = " + item->menuX + ";", ""); } diff --git a/src/dawntools/util/parser/SceneItemParser.cpp b/src/dawntools/util/parser/SceneItemParser.cpp index b995d20d..0dd77cf0 100644 --- a/src/dawntools/util/parser/SceneItemParser.cpp +++ b/src/dawntools/util/parser/SceneItemParser.cpp @@ -19,6 +19,8 @@ std::map SceneItemParser::getOptionalAttributes() { { "scale", "" }, { "prefab", "" }, { "alignment", "" }, + { "alignX", "" }, + { "aignY", "" }, { "menuX", "" }, { "menuY", "" } }; @@ -42,6 +44,16 @@ int32_t SceneItemParser::onParse( if(error->size() > 0) return 1; } + if(values["alignX"].size() > 0) { + out->alignX = uiComponentAlignParser(values["alignX"], error); + if(error->size() > 0) return 1; + } + + if(values["alignY"].size() > 0) { + out->alignY = uiComponentAlignParser(values["alignY"], error); + if(error->size() > 0) return 1; + } + if(values["menuX"].size() > 0) { out->menuX = intParser(values["menuX"], error); if(error->size() > 0) return 1; diff --git a/src/dawntools/util/parser/SceneItemParser.hpp b/src/dawntools/util/parser/SceneItemParser.hpp index c6da961f..534796a0 100644 --- a/src/dawntools/util/parser/SceneItemParser.hpp +++ b/src/dawntools/util/parser/SceneItemParser.hpp @@ -25,6 +25,8 @@ namespace Dawn { std::string ref; std::string position; std::string alignment; + std::string alignX; + std::string alignY; std::string lookAtPosition; std::string lookAtTarget; std::string scale;