diff --git a/CMakeLists.txt b/CMakeLists.txt index 67eafa9..9e8fe47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,16 +17,15 @@ endif() project (EtrediaEmpires LANGUAGES CXX) -add_subdirectory(sdk/map) -add_subdirectory(sdk/assets) +add_subdirectory(sdk) add_subdirectory(p7lib) add_subdirectory(utils) -add_subdirectory(server/lib) +#add_subdirectory(server/lib) add_subdirectory(engine-api) -add_subdirectory(renderers/magnum-renderer) +# add_subdirectory(renderers/magnum-renderer) add_subdirectory(renderers/vulkan) add_subdirectory(engine) \ No newline at end of file diff --git a/sdk/assets/_material.schema.json b/assets-src/_material.schema.json similarity index 100% rename from sdk/assets/_material.schema.json rename to assets-src/_material.schema.json diff --git a/sdk/assets/_save.schema.json b/assets-src/_save.schema.json similarity index 100% rename from sdk/assets/_save.schema.json rename to assets-src/_save.schema.json diff --git a/sdk/map/maps/ET.map.bmp b/assets-src/map/ET.map.bmp similarity index 100% rename from sdk/map/maps/ET.map.bmp rename to assets-src/map/ET.map.bmp diff --git a/sdk/map/maps/regions.map.bmp b/assets-src/map/regions.map.bmp similarity index 100% rename from sdk/map/maps/regions.map.bmp rename to assets-src/map/regions.map.bmp diff --git a/sdk/map/maps/test.map.bmp b/assets-src/map/test.map.bmp similarity index 100% rename from sdk/map/maps/test.map.bmp rename to assets-src/map/test.map.bmp diff --git a/sdk/assets/materials/provinces.mat.json b/assets-src/materials/provinces.mat.json similarity index 100% rename from sdk/assets/materials/provinces.mat.json rename to assets-src/materials/provinces.mat.json diff --git a/assets-src/regions.json b/assets-src/regions.json new file mode 100644 index 0000000..8d4270e --- /dev/null +++ b/assets-src/regions.json @@ -0,0 +1,11 @@ +{ + "pepene": "#AA00FF", + "scarna": "#FF007F", + + "dineria": "#FFFF00", + "perdania": "#7FFF00", + "arstinia": "#FF0000", + "alende": "#0000FF", + "zerandia": "#FF5400", + "mgahvatia": "#5400FF" +} \ No newline at end of file diff --git a/sdk/assets/saves/empires.save.json b/assets-src/scenarios/empires.save.json similarity index 100% rename from sdk/assets/saves/empires.save.json rename to assets-src/scenarios/empires.save.json diff --git a/assets/map/ET.map b/assets/map/ET.map index 8f40f94..c537c7f 100644 Binary files a/assets/map/ET.map and b/assets/map/ET.map differ diff --git a/assets/map/regions.map b/assets/map/regions.map index db1df15..984a4c6 100644 Binary files a/assets/map/regions.map and b/assets/map/regions.map differ diff --git a/assets/map/test.map b/assets/map/test.map index cdec31f..ba999a4 100644 Binary files a/assets/map/test.map and b/assets/map/test.map differ diff --git a/assets/materials/provinces.mat b/assets/materials/provinces.mat index aefbe17..3561dc7 100644 Binary files a/assets/materials/provinces.mat and b/assets/materials/provinces.mat differ diff --git a/assets/materials/provinces.mat.bak b/assets/materials/provinces.mat.bak deleted file mode 100644 index ef58fa3..0000000 --- a/assets/materials/provinces.mat.bak +++ /dev/null @@ -1 +0,0 @@ -æÛPDÞÿÀ² \ No newline at end of file diff --git a/assets/scenarios/empires.save b/assets/scenarios/empires.save index 51b9795..0ee991c 100644 Binary files a/assets/scenarios/empires.save and b/assets/scenarios/empires.save differ diff --git a/engine-api/CMakeLists.txt b/engine-api/CMakeLists.txt index ad9bd7f..2f00c5e 100644 --- a/engine-api/CMakeLists.txt +++ b/engine-api/CMakeLists.txt @@ -1,7 +1,7 @@ -project(engine-api) - include(${CMAKE_SOURCE_DIR}/cmake/library.cmake) +set(LIB engine-api) + # Find dependencies find_package(glfw3 CONFIG REQUIRED) @@ -9,10 +9,9 @@ find_package(glm CONFIG REQUIRED) # Link library -add_library_target(${PROJECT_NAME}) +add_library_target(${LIB}) -target_link_libraries(${PROJECT_NAME} PRIVATE p7) -target_link_libraries(${PROJECT_NAME} PUBLIC server) -target_link_libraries(${PROJECT_NAME} PUBLIC utils) -target_link_libraries(${PROJECT_NAME} PRIVATE glfw) -target_link_libraries(${PROJECT_NAME} PRIVATE glm::glm) \ No newline at end of file +target_link_libraries(${LIB} PRIVATE p7) +target_link_libraries(${LIB} PUBLIC utils) +target_link_libraries(${LIB} PRIVATE glfw) +target_link_libraries(${LIB} PRIVATE glm::glm) \ No newline at end of file diff --git a/engine-api/modules/AssetData.cppm b/engine-api/modules/AssetData.cppm index 640d48d..6074d80 100644 --- a/engine-api/modules/AssetData.cppm +++ b/engine-api/modules/AssetData.cppm @@ -1,12 +1,12 @@ export module AssetData; -import GameData; -import views; +export import GameData; +export import views; import std; export namespace ee_assets { - struct AssetData {}; + struct AssetTag {}; - template concept Asset = std::is_base_of_v; + template concept Asset = requires { typename T::asset_tag; }; enum AssetType { BINARY, @@ -19,33 +19,33 @@ export namespace ee_assets { IMAGE }; - struct AssetBinary : public AssetData { + struct AssetBinary { + using asset_tag = AssetTag; + std::uint8_t *content = nullptr; std::size_t size = 0; - AssetBinary(std::uint8_t * & source, const std::size_t sourceSize); + AssetBinary(std::uint8_t * & source, std::size_t sourceSize); ~AssetBinary(); }; - struct AssetMapMesh : public AssetData { - ee_engine::MapMesh content; + struct AssetMapMesh { + using asset_tag = AssetTag; - std::uint8_t* Serialize(std::size_t& size); - AssetMapMesh() {}; - AssetMapMesh(std::uint8_t * & source, const std::size_t sourceSize); + ee_engine::MapMesh content; }; - struct AssetSave : public AssetData { - ee_data::GameState content; + struct AssetSave { + using asset_tag = AssetTag; - std::uint8_t* Serialize(std::size_t& size); - AssetSave() {}; - AssetSave(std::uint8_t * & source, const std::size_t sourceSize); + ee_data::GameState content; }; using AssetShader = AssetBinary; - struct AssetMaterial : public AssetData { + struct AssetMaterial { + using asset_tag = AssetTag; + std::uint32_t vertexShaderAssetID; std::uint32_t fragmentShaderAssetID; }; diff --git a/server/lib/modules/GameData.cppm b/engine-api/modules/GameData.cppm similarity index 100% rename from server/lib/modules/GameData.cppm rename to engine-api/modules/GameData.cppm diff --git a/engine-api/modules/Packets.cppm b/engine-api/modules/Packets.cppm new file mode 100644 index 0000000..edcd7da --- /dev/null +++ b/engine-api/modules/Packets.cppm @@ -0,0 +1,17 @@ +export module Packets; +import std; + +export namespace ee_data { + enum class Route { + LOAD_SAVE + }; + + struct Request { + Route route; + }; + + struct Response { + Route route; + std::span payload; + }; +} \ No newline at end of file diff --git a/engine-api/modules/Render.cppm b/engine-api/modules/Render.cppm index 329f72e..e7079cd 100644 --- a/engine-api/modules/Render.cppm +++ b/engine-api/modules/Render.cppm @@ -26,7 +26,7 @@ export namespace ee_engine { float rotation; }; - struct RendererCommand : public ee_utils::WorkerCommand { + struct RendererCommand : ee_utils::WorkerCommand { enum Type : std::uint8_t { NONE, diff --git a/engine-api/modules/views/MapView.cppm b/engine-api/modules/views/MapView.cppm index d41087c..62523e4 100644 --- a/engine-api/modules/views/MapView.cppm +++ b/engine-api/modules/views/MapView.cppm @@ -12,7 +12,7 @@ export namespace ee_engine { std::vector indices; }; - std::unordered_map> regions; + std::unordered_map> regions; }; struct ProvinceSSBO { diff --git a/engine-api/src/AssetData.cpp b/engine-api/src/AssetData.cpp index 862fc31..0e0bb35 100644 --- a/engine-api/src/AssetData.cpp +++ b/engine-api/src/AssetData.cpp @@ -10,8 +10,6 @@ YOU CAN PREVENT SOURCE BUFFER FROM CLOSING. IT CAN BE USED FOR STORING ASSET DATA WITHOUT COPY. IN THIS CASE, YOU MUST SET SOURCE TO NULLPTR AND FREE BUFFER YOURSELF. -TODO: make universal binary format, think about flatbuffers or msgpacks - */ ee_assets::AssetBinary::AssetBinary(std::uint8_t * & source, const std::size_t sourceSize) { @@ -23,129 +21,4 @@ ee_assets::AssetBinary::AssetBinary(std::uint8_t * & source, const std::size_t s ee_assets::AssetBinary::~AssetBinary() { if (content != nullptr) DeallocateArray(content); -} - - -ee_assets::AssetMapMesh::AssetMapMesh(std::uint8_t * & source, const std::size_t sourceSize) { - std::uint8_t *buffer = source; - std::uint8_t *pointer = source; - std::size_t leftoverSize = sourceSize; - source = nullptr; - - std::uint32_t regionsCount = 0; - ee_utils::readNext(®ionsCount, pointer, leftoverSize, sizeof(std::uint32_t)); - - for (std::uint32_t region = 0; region < regionsCount; region++) { - std::uint32_t id = 0; - glm::u8vec3 color{}; - if (!ee_utils::readNext(&color, pointer, leftoverSize, sizeof(glm::u8vec3))) - break; - id = static_cast(color.r) << 16 | - static_cast(color.g) << 8 | - static_cast(color.b); - - std::uint32_t provincesCount = 0; - ee_utils::readNext(&provincesCount, pointer, leftoverSize, sizeof(std::uint32_t)); - - for (std::uint32_t pi = 0; pi < provincesCount; pi++) { - ee_engine::MapMesh::Province prov; - - std::uint32_t verticesCount = 0; - if (!ee_utils::readNext(&verticesCount, pointer, leftoverSize, sizeof(std::uint32_t))) - break; - - prov.vertices.reserve(verticesCount); - for (std::uint32_t vi = 0; vi < verticesCount; vi++) { - glm::vec2 vertex; - if (!ee_utils::readNext(&vertex, pointer, leftoverSize, sizeof(glm::vec2))) - break; - - prov.vertices.push_back(vertex); - } - - std::uint32_t indicesCount = 0; - if (!ee_utils::readNext(&indicesCount, pointer, leftoverSize, sizeof(std::uint32_t))) - break; - - prov.indices.reserve(indicesCount); - for (std::uint32_t ii = 0; ii < indicesCount; ii++) { - std::uint16_t index = 0; - if (!ee_utils::readNext(&index, pointer, leftoverSize, sizeof(std::uint16_t))) - break; - - prov.indices.push_back(index); - } - - content.regions[id].push_back(prov); - } - } - - DeallocateArray(buffer); -} - - -ee_assets::AssetSave::AssetSave(std::uint8_t * & source, const std::size_t sourceSize) { - std::uint8_t *buffer = source; - std::uint8_t *pointer = source; - std::size_t leftoverSize = sourceSize; - source = nullptr; - - std::uint16_t subjectsCount = static_cast(content.subjects.size()); - ee_utils::readNext(&subjectsCount, pointer, leftoverSize, sizeof(std::uint16_t)); - - for (std::uint16_t i = 0; i < subjectsCount; i++) { - ee_data::Subject subject; - - ee_utils::readNext(&subject.tag, pointer, leftoverSize, sizeof(subject.tag)); - ee_utils::readNext(&subject.color, pointer, leftoverSize, sizeof(subject.color)); - - std::uint16_t regionsCount = 0; - ee_utils::readNext(®ionsCount, pointer, leftoverSize, sizeof(std::uint16_t)); - subject.regions.resize(regionsCount); - ee_utils::readNext( - subject.regions.data(), - pointer, - leftoverSize, - subject.regions.size() * sizeof(std::uint32_t) - ); - - content.subjects.push_back(subject); - } - - DeallocateArray(buffer); -} - - -// SERIALIZERS - -std::uint8_t* ee_assets::AssetSave::Serialize(std::size_t& size) { - size = sizeof(std::uint16_t); - for (ee_data::Subject& subject : content.subjects) { - size += sizeof(subject.tag); - size += sizeof(subject.color); - size += sizeof(std::uint16_t) + subject.regions.size() * sizeof(std::uint32_t); - } - - std::size_t leftover = size; - std::uint8_t *result = AllocateArray(size); - std::uint8_t *pointer = result; - - std::uint16_t subjectsCount = static_cast(content.subjects.size()); - ee_utils::writeNext(pointer, &subjectsCount, leftover, sizeof(std::uint16_t)); - - for (ee_data::Subject& subject : content.subjects) { - ee_utils::writeNext(pointer, &subject.tag, leftover, sizeof(subject.tag)); - ee_utils::writeNext(pointer, &subject.color, leftover, sizeof(subject.color)); - - std::uint16_t regionsCount = static_cast(subject.regions.size()); - ee_utils::writeNext(pointer, ®ionsCount, leftover, sizeof(std::uint16_t)); - ee_utils::writeNext( - pointer, - subject.regions.data(), - leftover, - subject.regions.size() * sizeof(std::uint32_t) - ); - } - - return result; -} +} \ No newline at end of file diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index ea341f1..a578e77 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -8,6 +8,7 @@ find_package(Boost REQUIRED COMPONENTS lockfree) find_package(xxHash CONFIG REQUIRED) find_package(glfw3 CONFIG REQUIRED) find_package(glm CONFIG REQUIRED) +find_package(reflectcpp CONFIG REQUIRED) # Link library @@ -15,7 +16,8 @@ add_executable_target(${EXE}) target_link_libraries(${EXE} PUBLIC utils) target_link_libraries(${EXE} PUBLIC engine-api) -target_link_libraries(${EXE} PUBLIC magnum-renderer) +# target_link_libraries(${EXE} PUBLIC server) +# target_link_libraries(${EXE} PUBLIC magnum-renderer) target_link_libraries(${EXE} PUBLIC vulkan-renderer) target_link_libraries(${EXE} PRIVATE Boost::lockfree) @@ -23,6 +25,7 @@ target_link_libraries(${EXE} PRIVATE xxHash::xxhash) target_link_libraries(${EXE} PRIVATE p7) target_link_libraries(${EXE} PRIVATE glfw) target_link_libraries(${EXE} PRIVATE glm::glm) +target_link_libraries(${EXE} PRIVATE reflectcpp::reflectcpp) # Compile options if (APP_VERSION) diff --git a/engine/modules/AssetManager.cppm b/engine/modules/AssetManager.cppm index 71ca215..4d548ef 100644 --- a/engine/modules/AssetManager.cppm +++ b/engine/modules/AssetManager.cppm @@ -2,6 +2,7 @@ module; #include "compile_config.hpp" #include +#include export module AssetManager; export import AssetData; @@ -29,7 +30,7 @@ export std::atomic isReady; AssetAddress *resourceAddress; - AssetInfo(void *resource) { + explicit AssetInfo(void *resource) { this->resourceAddress = Allocate(); this->resourceAddress->resource.store(resource); } @@ -122,7 +123,7 @@ export Request(command); while (command.out->load() == nullptr) {} - return *reinterpret_cast(command.out->load()); + return *static_cast(command.out->load()); } AssetHandle& Find(const std::uint32_t id) { @@ -133,8 +134,8 @@ export command.out = &result; Request(command); - while (command.out->load() == nullptr); - return *reinterpret_cast(command.out->load()); + while (command.out->load() == nullptr) {} + return *static_cast(command.out->load()); } // Returned resource reference should not be stored @@ -149,7 +150,7 @@ export return Get(Find(id)); } - void Redirect(const AssetHandle& handle, void *resource) { + void Redirect(const AssetHandle& handle, void *resource) const { const AssetInfo& asset = activeAssets[handle.slot]; if (asset.resourceAddress == nullptr) { LOG(WARNING, "Attempt to redirect empty handle"); @@ -176,11 +177,11 @@ export command.out = &result; Request(command); - while (command.out->load() == nullptr); - return *reinterpret_cast(command.out->load()); + while (command.out->load() == nullptr) {} + return *static_cast(command.out->load()); } - void Free(AssetHandle& handle) { + void Free(const AssetHandle& handle) { AssetInfo& asset = activeAssets[handle.slot]; Redirect(handle, nullptr); Deallocate(asset.resourceAddress); @@ -193,23 +194,22 @@ export void Update() override { // TODO: FinishLoadTasks() for (AssetLoadingEntry& entry : loadingEntries) { - bool isReady = entry.fileOwner->PollReady(*entry.file); - if (isReady) { + if (entry.fileOwner->PollReady(*entry.file)) { AssetInfo& asset = activeAssets[entry.handle.slot]; switch (asset.type) { case ee_assets::MAP_MESH: - FinishLoadConstruct(entry, mapMeshesStorage); + FinishLoadCapnprotoDeserialize(entry, mapMeshesStorage); break; case ee_assets::SAVE: - FinishLoadConstruct(entry, savesStorage); + FinishLoadCapnprotoDeserialize(entry, savesStorage); break; case ee_assets::SHADER: FinishLoadConstruct(entry, shadersStorage); break; case ee_assets::MATERIAL: - FinishLoadPlain(entry, materialsStorage); + FinishLoadCapnprotoDeserialize(entry, materialsStorage); break; case ee_assets::IMAGE: break; @@ -298,13 +298,13 @@ export assetPos = activeAssets.size() - 1; } handle->slot = assetPos; - auto assetPath = command.targetPath.generic_u8string(); - std::uint32_t assetId = assetsIdMapping[command.targetPath] = + const std::basic_string assetPath = command.targetPath.generic_u8string(); + const std::uint32_t assetId = assetsIdMapping[command.targetPath] = XXH32(assetPath.data(), assetPath.size(), ASSETS_HASH_SPACE); handle->id = assetId; assetsMapping[assetId] = handle; - AssetLoadingEntry loadingEntry{ + const AssetLoadingEntry loadingEntry{ .handle = *handle, .file = &file, .fileOwner = diskSystem @@ -324,7 +324,7 @@ export id = idMapping->second; } - std::unordered_map::iterator assetMapping = assetsMapping.find(id); + const std::unordered_map::iterator assetMapping = assetsMapping.find(id); if (assetMapping == assetsMapping.end()) return; @@ -335,7 +335,7 @@ export command.type = AssetStreamCommand::COMPLETED; } - void GetCmd(AssetStreamCommand& command) { + void GetCmd(AssetStreamCommand& command) const { if (activeAssets.size() <= command.target.slot) return; if (!activeAssets[command.target.slot].isReady) @@ -355,13 +355,36 @@ export } template - void FinishLoadPlain(AssetLoadingEntry& loaded, std::vector& storage) { - T *resource = Allocate(); - if (loaded.file->size < sizeof(T)) - LOG(CRITICAL, "File size is less than needed for asset of type: %d", typeid(T).name()); - std::memcpy(resource, loaded.file->data, sizeof(T)); - storage.push_back(resource); - Redirect(loaded.handle, storage.back()); + void FinishLoadCapnprotoDeserialize(AssetLoadingEntry& loaded, std::vector& storage) { + try { + if (rfl::Result result = rfl::capnproto::read( + reinterpret_cast(loaded.file->data), + loaded.file->size + )) { + storage.push_back(Allocate(std::move(*result))); + Redirect(loaded.handle, storage.back()); + } + else { +#if !defined(NDEBUG) + LOG( + ERROR, + "Failed to read asset: [%s]. [%hs]", + loaded.file->debugPath.c_str(), + result.error().what().c_str() + ); +#else + LOG(ERROR, "Failed to read asset: ID[%d]. [%hs]", loaded.file->id, result.error().what().c_str()); +#endif + } + } catch (std::exception e) { + storage.push_back(Allocate()); + Redirect(loaded.handle, storage.back()); +#if !defined(NDEBUG) + LOG(ERROR, "Failed to read asset: [%s]. [%hs]", loaded.file->debugPath.c_str(), e.what()); +#else + LOG(ERROR, "Failed to read asset: ID[%d]. [%hs]", loaded.file->id, e.what()); +#endif + } } template diff --git a/engine/modules/Client.cppm b/engine/modules/Client.cppm index 05c3a5c..91555b8 100644 --- a/engine/modules/Client.cppm +++ b/engine/modules/Client.cppm @@ -7,7 +7,7 @@ import std; export { - struct ClientCommand : public ee_utils::WorkerCommand { + struct ClientCommand : ee_utils::WorkerCommand { enum Type : std::uint8_t { NONE, @@ -21,14 +21,14 @@ export public: Client(); ~Client() override; - void Init(); + void Init() const; //void Send(); -> Request(SEND) - const IDataChannel* GetConnection(); + const IDataChannel* GetConnection() const; void ConnectServer(); - void LoadSave(const AssetHandle save); + void LoadSave(AssetHandle save); protected: void Update() override; @@ -40,6 +40,6 @@ export void SendCmd(ClientCommand& command); - void ViewModelSubject(ee_data::Subject subject); + static void ViewModelSubject(const ee_data::Subject &subject); }; } diff --git a/engine/modules/DataChannel.cppm b/engine/modules/DataChannel.cppm index 9a4c51d..bdd6615 100644 --- a/engine/modules/DataChannel.cppm +++ b/engine/modules/DataChannel.cppm @@ -1,17 +1,26 @@ module; export module DataChannel; +import std; export { class IDataChannel { public: - // virtual void Request() = 0; + virtual ~IDataChannel() = default; + + virtual void Request() = 0; // virtual void Listen() = 0; // virtual void Mute() = 0; }; - class NetworkChannel final : public IDataChannel {}; + class NetworkChannel final : public IDataChannel { + public: + void Request() override; + }; - class ThreadChannel final : public IDataChannel {}; + class ThreadChannel final : public IDataChannel { + public: + void Request() override; + }; } diff --git a/engine/modules/FileSystem.cppm b/engine/modules/FileSystem.cppm index ffc669e..c4a8457 100644 --- a/engine/modules/FileSystem.cppm +++ b/engine/modules/FileSystem.cppm @@ -30,16 +30,16 @@ export class IFileSystem { public: - inline static constexpr std::uint32_t FILE_OPEN_NONE = 0; - inline static constexpr std::uint32_t FILE_OPEN_READ = (1 << 0); - inline static constexpr std::uint32_t FILE_OPEN_WRITE = (1 << 1); - inline static constexpr std::uint32_t FILE_OPEN_MMAP = (1 << 2); - inline static constexpr std::uint32_t FILE_READ_SEQUENTIAL = 0; - inline static constexpr std::uint32_t FILE_READ_RANDOM_ACCESS = (1 << 0); + static constexpr std::uint32_t FILE_OPEN_NONE = 0; + static constexpr std::uint32_t FILE_OPEN_READ = (1 << 0); + static constexpr std::uint32_t FILE_OPEN_WRITE = (1 << 1); + static constexpr std::uint32_t FILE_OPEN_MMAP = (1 << 2); + static constexpr std::uint32_t FILE_READ_SEQUENTIAL = 0; + static constexpr std::uint32_t FILE_READ_RANDOM_ACCESS = (1 << 0); - virtual FileHandle& Open(const std::filesystem::path& path, const std::uint32_t flags) = 0; + virtual FileHandle& Open(const std::filesystem::path& path, std::uint32_t flags) = 0; - virtual void ReadAsync(FileHandle& file, const std::uint32_t bufferSize, const std::uint32_t flags) = 0; + virtual void ReadAsync(FileHandle& file, std::uint32_t bufferSize, std::uint32_t flags) = 0; virtual bool PollReady(FileHandle& file) = 0; @@ -55,14 +55,12 @@ export class NativeFileSystem final : public IFileSystem { public: - FileHandle& Open(const std::filesystem::path& path, const std::uint32_t flags) override; + FileHandle& Open(const std::filesystem::path& path, std::uint32_t flags) override; - void ReadAsync(FileHandle& file, const std::uint32_t bufferSize, std::uint32_t flags) override; + void ReadAsync(FileHandle& file, std::uint32_t bufferSize, std::uint32_t flags) override; bool PollReady(FileHandle& file) override; void Close(FileHandle& file) override; - - private: }; } diff --git a/engine/src/Client.cpp b/engine/src/Client.cpp index 2ab0a5b..91c6db7 100644 --- a/engine/src/Client.cpp +++ b/engine/src/Client.cpp @@ -7,7 +7,7 @@ Client::Client() : ThreadedWorker(L"Client", 0) { commandsMapping[ClientCommand::SEND] = std::bind_front(&Client::SendCmd, this); } -void Client::Init() { +void Client::Init() const { if (Engine::renderer == nullptr || !Engine::renderer->isReady()) { LOG(CRITICAL, "Renderer is not ready. Client cannot be created"); throw std::logic_error("Renderer is not ready"); @@ -19,7 +19,7 @@ void Client::Init() { ViewModelSubject(asset.content.subjects[1]); } -const IDataChannel* Client::GetConnection() { +const IDataChannel* Client::GetConnection() const { return networkConnection; } @@ -39,14 +39,15 @@ void Client::Update() {} void Client::Release() {} +// ReSharper disable once CppMemberFunctionMayBeStatic void Client::SendCmd(ClientCommand& command) { command.type = ClientCommand::COMPLETED; } -void Client::ViewModelSubject(ee_data::Subject subject) { +void Client::ViewModelSubject(const ee_data::Subject &subject) { for (const std::uint32_t region : subject.regions) { - ee_engine::ProvinceSSBO vm{ + const ee_engine::ProvinceSSBO vm { .regionId = region, .color = subject.color }; diff --git a/engine/src/DataChannel.cpp b/engine/src/DataChannel.cpp new file mode 100644 index 0000000..c39110a --- /dev/null +++ b/engine/src/DataChannel.cpp @@ -0,0 +1,7 @@ +module DataChannel; + +void NetworkChannel::Request() { + +} + +void ThreadChannel::Request() {} diff --git a/engine/src/main.cpp b/engine/src/main.cpp index 3e3c94a..1cafe65 100644 --- a/engine/src/main.cpp +++ b/engine/src/main.cpp @@ -31,7 +31,7 @@ int main(int argc, char **argv) { glfwMakeContextCurrent(window); Engine::window = window; - // Setup Asset Manager TODO: builder + // Setup Asset Manager Engine::assetManager = Allocate(); Engine::assetManager->Start(); Engine::assetManager->LoadDir(ee_assets::SHADER, "assets/shaders"); @@ -41,18 +41,17 @@ int main(int argc, char **argv) { Engine::assetManager->LoadAsync(ee_assets::MAP_MESH, "assets/map/regions.map"); Engine::assetManager->LoadAsync(ee_assets::SAVE, "assets/scenarios/empires.save"); - // Setup input manager TODO: builder + // Setup input manager Engine::input = Allocate(Actions::mappings); - // Setup renderer TODO: builder - //Engine::renderer = reinterpret_cast(Allocate(window)); + // Setup renderer Engine::renderer = reinterpret_cast(Allocate(window)); ee_assets::AssetMapMesh& regions = Engine::assetManager->FindOnce("assets/map/regions.map"); Engine::renderer->RegisterMapMesh(®ions.content); //TODO: ShaderRefs builder from set of AssetMaterial - ee_assets::AssetMaterial& provincesMaterial = + const ee_assets::AssetMaterial& provincesMaterial = Engine::assetManager->FindOnce("assets/materials/provinces.mat"); ee_assets::AssetShader& vertProv = Engine::assetManager->FindOnce( provincesMaterial.vertexShaderAssetID); @@ -74,7 +73,7 @@ int main(int argc, char **argv) { Engine::renderer->ResizeWindow(width, height); }); - // Setup Client TODO: builder + // Setup Client Engine::client = Allocate(); const AssetHandle save = Engine::assetManager->Find("assets/scenarios/empires.save"); Engine::client->LoadSave(save); diff --git a/renderers/magnum-renderer/src/MagnumRender.cpp b/renderers/magnum-renderer/src/MagnumRender.cpp index 2b57f55..c0b9b81 100644 --- a/renderers/magnum-renderer/src/MagnumRender.cpp +++ b/renderers/magnum-renderer/src/MagnumRender.cpp @@ -82,7 +82,7 @@ void ee_magnum::MagnumRenderer::LoadProvinces() { regionMesh.setPrimitive(GL::MeshPrimitive::Triangles) .setCount(static_cast(province.indices.size())) - .addVertexBuffer(GL::Buffer{provinceVertices}, 0, GL::Attribute<0, Magnum::Vector2>{}) + .addVertexBuffer(GL::Buffer{provinceVertices}, 0, GL::Attribute<0, Magnum::Vector3>{}) .setIndexBuffer(GL::Buffer{provinceIndices}, 0, GL::MeshIndexType::UnsignedShort); provinceMeshes.push_back(std::move(regionMesh)); diff --git a/sdk/CMakeLists.txt b/sdk/CMakeLists.txt new file mode 100644 index 0000000..46e6603 --- /dev/null +++ b/sdk/CMakeLists.txt @@ -0,0 +1,25 @@ +include(${CMAKE_SOURCE_DIR}/cmake/executable.cmake) + +set(EXE sdk) + +# Find dependencies + +find_package(cxxopts CONFIG REQUIRED) +find_package(reflectcpp CONFIG REQUIRED) +find_package(glm CONFIG REQUIRED) +find_package(nlohmann_json CONFIG REQUIRED) +find_package(xxHash CONFIG REQUIRED) + +# Link library + +add_executable_target(${EXE}) + +target_link_libraries(${EXE} PUBLIC utils) +target_link_libraries(${EXE} PUBLIC engine-api) + +target_link_libraries(${EXE} PUBLIC p7) +target_link_libraries(${EXE} PRIVATE cxxopts::cxxopts) +target_link_libraries(${EXE} PRIVATE reflectcpp::reflectcpp) +target_link_libraries(${EXE} PRIVATE glm::glm) +target_link_libraries(${EXE} PRIVATE nlohmann_json::nlohmann_json) +target_link_libraries(${EXE} PRIVATE xxHash::xxhash) \ No newline at end of file diff --git a/sdk/assets/CMakeLists.txt b/sdk/assets/CMakeLists.txt deleted file mode 100644 index aab223f..0000000 --- a/sdk/assets/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required (VERSION 3.8) - -if (POLICY CMP0141) - cmake_policy(SET CMP0141 NEW) - set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") -endif() - -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -project (compile_assets) - -find_package(nlohmann_json CONFIG REQUIRED) -find_package(xxHash CONFIG REQUIRED) - -add_executable(${PROJECT_NAME} compile_assets.cpp define/regions.def) -target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json) -target_link_libraries(${PROJECT_NAME} PRIVATE xxHash::xxhash) -target_link_libraries(${PROJECT_NAME} PUBLIC engine-api) -target_link_libraries(${PROJECT_NAME} PUBLIC server) - -target_compile_options(${PROJECT_NAME} PRIVATE /Zc:preprocessor) - -if (CMAKE_VERSION VERSION_GREATER 3.12) - set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) -endif() diff --git a/sdk/assets/compile_assets.cpp b/sdk/assets/compile_assets.cpp deleted file mode 100644 index c4f9566..0000000 --- a/sdk/assets/compile_assets.cpp +++ /dev/null @@ -1,187 +0,0 @@ -import AssetData; -import GameData; - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using json = nlohmann::json; - -#define bytes(value) reinterpret_cast(value) - -namespace ee_data -{ - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( - Subject, - tag, - color, - regions - ) -} - -namespace ee_data -{ - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( - GameState, - subjects - ) -} - -namespace ee_assets -{ - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( - AssetSave, - content - ) - - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( - AssetMaterial, - vertexShaderAssetID, - fragmentShaderAssetID - ) -} - -struct MappingEntry -{ - std::string_view name; - uint32_t value; -}; -static constexpr MappingEntry mappings[] = { - #define MAP(name, value) {#name, ((value & 0x0000FF) << 16) | ( value & 0x00FF00) | ((value & 0xFF0000) >> 16)}, - #include "define/regions.def" - #undef MAP -}; - -void CompileRecursive(json &obj, std::function &key, json &value)> callback) -{ - if (obj.is_array()) {for (auto &element : obj) CompileRecursive(element, callback); return;} - if (!obj.is_object()) {callback("", obj); return;} - - for (auto &[key, value] : obj.items()) - { - if (value.is_object()) - CompileRecursive(value, callback); - else if (value.is_array()) - for (auto &element : value) - CompileRecursive(element, callback); - else callback(key, value); - } -} - -int main(int argc, char **argv) -{ - try - { - std::filesystem::path in = argv[1]; - std::filesystem::path out = argv[2]; - char* assetTypeStr = argv[3]; - - std::ifstream fin(in); - std::ofstream fout(out); - ee_assets::AssetType assetType = static_cast(atoi(assetTypeStr)); - - if (!fin.is_open()) { - std::cout << "Error: Could not open input file: " << in << std::endl; - return 1; - } - if (!fout.is_open()) { - std::cout << "Error: Could not open output file: " << out << std::endl; - return 1; - } - - json data = json::parse(fin); - - // Compile hashes - CompileRecursive(data, [](const std::basic_string &key, json &value) { - if (!value.is_string()) return; - - if ( - !(key == "tag") && - !key.ends_with("AssetID") && - !key.ends_with("Type") - ) return; - - std::filesystem::path linkPath = value; - auto linkPath8 = linkPath.generic_u8string(); - uint32_t assetId = XXH32(linkPath8.data(), linkPath8.size(), 0); - - std::cout << "Replace string [" << value << "] with [" << assetId << "]" << std::endl; - value = assetId; - }); - - // Compile mappings - CompileRecursive(data, [](const std::basic_string &key, json &value) { - if (!value.is_string()) return; - const std::string& value_string = value.get_ref(); - - for (const MappingEntry &map : mappings) - { - if (value_string == map.name) - { - std::cout << "Replace string [" << value << "] with [" << map.value << "]" << std::endl; - value = map.value; - break; - } - } - }); - - // Compile colors - CompileRecursive(data, [](const std::basic_string &key, json &value) { - if (!value.is_string()) return; - - const char* color_str = value.get_ref().c_str() + 1; - uint8_t b = 0, g = 0, r = 0; - uint32_t hex = 0; - - std::from_chars(color_str, color_str + 2, r, 16); color_str += 2; - std::from_chars(color_str, color_str + 2, g, 16); color_str += 2; - std::from_chars(color_str, color_str + 2, b, 16); color_str += 2; - - hex = static_cast(r) << 16 | - static_cast(g) << 8 | - static_cast(b); - - std::cout << "Replace string [" << value << "] with [" << (int)r << "," << (int)g << "," << (int)b << "]" << std::endl; - value = hex; - }); - - switch (assetType) - { - case ee_assets::MATERIAL: - { - ee_assets::AssetMaterial material = data.get(); - fout.write(bytes(&material), sizeof(material)); - break; - } - - case ee_assets::SAVE: - { - ee_assets::AssetSave save = data.get(); - size_t size = 0; - const char* data = bytes(save.Serialize(size)); - fout.write(data, size); - break; - } - - default: - break; - } - - fin.close(); - fout.close(); - } - catch (const std::exception &e) - { - std::cerr << e.what() << std::endl; - return 1; - } - - - return 0; -} \ No newline at end of file diff --git a/sdk/assets/define/regions.def b/sdk/assets/define/regions.def deleted file mode 100644 index 5af5463..0000000 --- a/sdk/assets/define/regions.def +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef MAP -#define MAP(name, color) -#endif - -MAP(pepene, 0xAA00FF) -MAP(scarna, 0xFF007F) - -MAP(dineria, 0xFFFF00) -MAP(perdania, 0x7FFF00) -MAP(arstinia, 0xFF0000) -MAP(alende, 0x0000FF) -MAP(zerandia, 0xFF5400) -MAP(mgahvatia, 0x5400FF) - -#undef MAP \ No newline at end of file diff --git a/sdk/assets/run.cmd b/sdk/assets/run.cmd deleted file mode 100644 index cff6743..0000000 --- a/sdk/assets/run.cmd +++ /dev/null @@ -1,10 +0,0 @@ -@echo off - -set ASSETS=../../assets -set COMPILE=%CD%/../../out/build/DEBUG/sdk/assets/compile_assets.exe - -set MATERIAL=4 -set SAVE=2 - -for %%F in (materials/*) do %COMPILE% "materials/%%~nF.json" "%ASSETS%/materials/%%~nF" %MATERIAL% && echo "Compiled: %%~nF" -for %%F in (saves/*) do %COMPILE% "saves/%%~nF.json" "%ASSETS%/scenarios/%%~nF" %SAVE% && echo "Compiled: %%~nF" \ No newline at end of file diff --git a/sdk/assets/shaders/compile.cmd b/sdk/assets/shaders/compile.cmd deleted file mode 100644 index 70b978a..0000000 --- a/sdk/assets/shaders/compile.cmd +++ /dev/null @@ -1,4 +0,0 @@ -@echo off - -glslc provinces/provinces.frag -O -o ../../../assets/shaders/provinces.frag -glslc provinces/provinces.vert -O -o ../../../assets/shaders/provinces.vert \ No newline at end of file diff --git a/sdk/assets/shaders/helloworld/helloworld.frag b/sdk/assets/shaders/helloworld/helloworld.frag deleted file mode 100644 index 1730b4f..0000000 --- a/sdk/assets/shaders/helloworld/helloworld.frag +++ /dev/null @@ -1,10 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(location = 0) in vec3 fragColor; - -layout(location = 0) out vec4 outColor; - -void main() { - outColor = vec4(fragColor, 1.0); -} \ No newline at end of file diff --git a/sdk/assets/shaders/helloworld/helloworld.vert b/sdk/assets/shaders/helloworld/helloworld.vert deleted file mode 100644 index 6ea328c..0000000 --- a/sdk/assets/shaders/helloworld/helloworld.vert +++ /dev/null @@ -1,21 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(binding = 0) uniform Global { - mat4 view; - mat4 proj; -} global; - -layout(binding = 1) uniform Transform { - mat4 model; -} transform; - -layout(location = 0) in vec2 inPosition; -layout(location = 1) in vec3 inColor; - -layout(location = 0) out vec3 fragColor; - -void main() { - gl_Position = global.proj * global.view * transform.model * vec4(inPosition, 0.0, 1.0); - fragColor = inColor; -} \ No newline at end of file diff --git a/sdk/assets/shaders/provinces/provinces.frag b/sdk/assets/shaders/provinces/provinces.frag deleted file mode 100644 index 1730b4f..0000000 --- a/sdk/assets/shaders/provinces/provinces.frag +++ /dev/null @@ -1,10 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(location = 0) in vec3 fragColor; - -layout(location = 0) out vec4 outColor; - -void main() { - outColor = vec4(fragColor, 1.0); -} \ No newline at end of file diff --git a/sdk/assets/shaders/provinces/provinces.vert b/sdk/assets/shaders/provinces/provinces.vert deleted file mode 100644 index 4b5cd46..0000000 --- a/sdk/assets/shaders/provinces/provinces.vert +++ /dev/null @@ -1,35 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(location = 0) in vec2 position; - -layout(binding = 0) uniform Camera { - mat4 viewProjection; -} camera; - -layout(push_constant) uniform Model { - mat4 model; -} model; - -struct Province { - uint regionId; - uint color; -}; -layout(std430, binding = 1) readonly buffer Data { - Province provinces[]; -}; - -layout(location = 0) out vec3 fragColor; - -vec3 unpackColor(uint c) { - return vec3( - c & 0xFFu, - (c >> 8) & 0xFFu, - (c >> 16) & 0xFFu - ) / 255.0; -} - -void main() { - gl_Position = camera.viewProjection * vec4(position, 0.0, 1.0); - fragColor = unpackColor(provinces[gl_InstanceIndex].color); -} \ No newline at end of file diff --git a/sdk/map/CMakeLists.txt b/sdk/map/CMakeLists.txt deleted file mode 100644 index 255a992..0000000 --- a/sdk/map/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -cmake_minimum_required (VERSION 3.8) - -if (POLICY CMP0141) - cmake_policy(SET CMP0141 NEW) - set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") -endif() - -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -project (convert_map) - -# find_package(glm CONFIG REQUIRED) - -add_executable(${PROJECT_NAME} convert_map.cpp) - -# target_link_libraries(${PROJECT_NAME} PRIVATE glm::glm) -# target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/engine/include/) - -if (CMAKE_VERSION VERSION_GREATER 3.12) - set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) -endif() - -file(GLOB MAP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.bmp") - -set(COMPILED_DIR "${CMAKE_SOURCE_DIR}/assets/map") - -foreach(MAP ${MAP_FILES}) - get_filename_component(MAP_NAME ${MAP} NAME_WE) - set(OUT_FILE "${COMPILED_DIR}/${MAP_NAME}.map") - - add_custom_command( - OUTPUT ${OUT_FILE} - COMMAND $ ${MAP} ${OUT_FILE} - DEPENDS ${MAP} ${PROJECT_NAME} - COMMENT "Converting map ${MAP_NAME}" - VERBATIM - ) - - list(APPEND COMPILED_BINARIES ${OUT_FILE}) -endforeach() - -add_custom_target(compile_maps ALL - DEPENDS ${COMPILED_BINARIES} -) diff --git a/sdk/map/convert_map.cpp b/sdk/map/convert_map.cpp deleted file mode 100644 index 5e2c21a..0000000 --- a/sdk/map/convert_map.cpp +++ /dev/null @@ -1,499 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#define bytes(value) reinterpret_cast(value) - -#pragma pack(push, 1) -struct BMPFileHeader { - uint16_t bfType; - uint32_t bfSize; - uint16_t bfReserved1; - uint16_t bfReserved2; - uint32_t bfOffBits; -}; - -struct BMPInfoHeader { - uint32_t biSize; - int32_t biWidth; - int32_t biHeight; - uint16_t biPlanes; - uint16_t biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - int32_t biXPelsPerMeter; - int32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; -}; - -struct BGR { uint8_t b,g,r; }; -#pragma pack(pop) - -struct vertex { float x,y; }; - -struct BGRHash { - size_t operator()(const BGR& c) const noexcept { - return static_cast(c.r) << 16 | - static_cast(c.g) << 8 | - static_cast(c.b); - } -}; - -bool operator==(const BGR&a,const BGR&b){ - return a.r==b.r && a.g==b.g && a.b==b.b; -} - -BGR EMPTY_COLOR{255,255,255}; - -uint8_t* imageData; -uint8_t* tempImageData; -size_t width,height,rowSize; - -struct prov -{ - std::vector vertices; - std::vector indices; -}; - -std::unordered_map,BGRHash> pixelsPerProvincesPerState; - -inline BGR getPixelSafe(size_t r,size_t c){ - if(r>=height||c>=width) return EMPTY_COLOR; - return *reinterpret_cast( - imageData + r*rowSize + c*3 - ); -} - -inline BGR getPixelSafeIndex(size_t i){ - return getPixelSafe(i/width,i%width); -} - -/* ================= FLOOD FILL ================= */ - -void FloodFill(){ - size_t size=width*height; - for(size_t i=0;i(tempImageData + (i/width)*rowSize + (i%width)*3)==EMPTY_COLOR) - continue; - - BGR color=*reinterpret_cast(tempImageData + (i/width)*rowSize + (i%width)*3); - std::vector prov; - std::queue q; - q.push(i); - - while(!q.empty()){ - size_t n=q.front(); q.pop(); - BGR& px=*reinterpret_cast(tempImageData + (n/width)*rowSize + (n%width)*3); - if(px!=color) continue; - px=EMPTY_COLOR; - prov.push_back(n); - - if(n>=width) q.push(n-width); - if(n+width&prov,BGR id){ - for(size_t i:prov){ - size_t r=i/width,c=i%width; - if(getPixelSafe(r-1,c)!=id|| - getPixelSafe(r+1,c)!=id|| - getPixelSafe(r,c-1)!=id|| - getPixelSafe(r,c+1)!=id) - return i; - } - return std::numeric_limits::max(); -} - -void FindContour(){ - for(auto&[state,provs]:pixelsPerProvincesPerState){ - for(auto&prov:provs){ - if(prov.vertices.empty()) continue; - BGR id=getPixelSafeIndex(prov.vertices[0]); - size_t start=FindStart(prov.vertices,id); - if(start==std::numeric_limits::max()) continue; - - std::vector contour; - contour.push_back(start); - - size_t cur=start; - size_t prev = (start % width > 0) ? start - 1 : start + 1; - - - const size_t MAX=prov.vertices.size()*8; - for(size_t i=0;i keep_point(n, true); - std::stack> st; - - // в стек кладем первый и последний индекс - st.push({0, n - 1}); - - while(!st.empty()) - { - auto [startIndex, endIndex] = st.top(); - st.pop(); - - double dMax = 0.0; - size_t index = startIndex; - - for(size_t i = startIndex + 1; i < endIndex; ++i) - { - if(keep_point[i]) - { - double d = PerpendicularDistance( - prov.vertices[i], - prov.vertices[startIndex], - prov.vertices[endIndex], - width - ); - - if(d > dMax) - { - dMax = d; - index = i; - } - } - } - - if(dMax >= epsilon) - { - st.push({startIndex, index}); - st.push({index, endIndex}); - } - else - { - for(size_t j = startIndex + 1; j < endIndex; ++j) - keep_point[j] = false; - } - } - - // формируем итоговый список - std::vector result; - result.reserve(n); - - for(size_t i = 0; i < n; ++i) - { - if(keep_point[i]) - result.push_back(prov.vertices[i]); - } - - prov.vertices.swap(result); - } - } -} - -struct Pt { - int x, y; - size_t id; -}; - -inline Pt make_pt(size_t i, size_t width) { - return { int(i % width), int(i / width), i }; -} - -inline long long cross(const Pt& a, const Pt& b, const Pt& c) { - return 1LL * (b.x - a.x) * (c.y - a.y) - - 1LL * (b.y - a.y) * (c.x - a.x); -} - -long long signed_area2(const std::vector& p) { - long long a = 0; - for (size_t i = 0; i < p.size(); ++i) { - const auto& p0 = p[i]; - const auto& p1 = p[(i + 1) % p.size()]; - a += 1LL * p0.x * p1.y - 1LL * p1.x * p0.y; - } - return a; -} - -bool point_in_triangle(const Pt& p, - const Pt& a, - const Pt& b, - const Pt& c) -{ - long long c1 = cross(a, b, p); - long long c2 = cross(b, c, p); - long long c3 = cross(c, a, p); - return (c1 >= 0 && c2 >= 0 && c3 >= 0); -} - -std::vector triangulate_prov(const std::vector& prov, - size_t width) -{ - std::vector poly; - poly.reserve(prov.size()); - for (size_t i : prov) - poly.push_back(make_pt(i, width)); - - // CCW - if (signed_area2(poly) < 0) - std::reverse(poly.begin(), poly.end()); - - std::vector out; // a,b,c,a,b,c,... - - while (poly.size() > 3) { - bool ear_found = false; - - for (size_t i = 0; i < poly.size(); ++i) { - size_t i0 = (i + poly.size() - 1) % poly.size(); - size_t i1 = i; - size_t i2 = (i + 1) % poly.size(); - - const Pt& a = poly[i0]; - const Pt& b = poly[i1]; - const Pt& c = poly[i2]; - - if (cross(a, b, c) <= 0) - continue; // не выпуклая - - bool inside = false; - for (size_t j = 0; j < poly.size(); ++j) { - if (j == i0 || j == i1 || j == i2) continue; - if (point_in_triangle(poly[j], a, b, c)) { - inside = true; - break; - } - } - - if (!inside) { - out.push_back(a.id); - out.push_back(b.id); - out.push_back(c.id); - poly.erase(poly.begin() + i1); - ear_found = true; - break; - } - } - - if (!ear_found) { - // плохой контур (самопересечение / коллинеарность) - break; - } - } - - if (poly.size() == 3) { - out.push_back(poly[0].id); - out.push_back(poly[1].id); - out.push_back(poly[2].id); - } - - return out; -} - - -void Triangulate() -{ - for(auto& [state, provs] : pixelsPerProvincesPerState) - { - for(prov& prov : provs) - { - if (prov.vertices.size() < 3) - continue; - - prov.vertices = triangulate_prov(prov.vertices, width); - } - } -} - -void Optimize() -{ - for(auto& [state, provs] : pixelsPerProvincesPerState) - { - for(prov& prov : provs) - { - std::unordered_map vertexToIndex; - vertexToIndex.reserve(prov.vertices.size()); - - std::vector uniqueVertices; - uniqueVertices.reserve(prov.vertices.size()); - - prov.indices.clear(); - - for (size_t v : prov.vertices) - { - auto it = vertexToIndex.find(v); - if (it != vertexToIndex.end()) - { - // вершина уже есть - prov.indices.push_back(it->second); - } - else - { - // новая вершина - uint16_t newIndex = static_cast(uniqueVertices.size()); - uniqueVertices.push_back(v); - vertexToIndex[v] = newIndex; - prov.indices.push_back(newIndex); - } - } - - prov.vertices.swap(uniqueVertices); - } - } -} - -/* ================= OUTPUT ================= */ - -void GenVertices(std::ofstream&out){ - uint32_t statesCount = static_cast(pixelsPerProvincesPerState.size()); - out.write(bytes(&statesCount),4); - - for(auto&[color, provs]:pixelsPerProvincesPerState) - { - out.write(bytes(&color), sizeof(BGR)); - - uint32_t provsCount = static_cast(provs.size()); - out.write(bytes(&provsCount), sizeof(uint32_t)); - - for(prov& prov:provs) - { - //Skip invalid meshes - if (prov.indices.size() % 3 != 0) - { - prov.vertices.clear(); - prov.indices.clear(); - } - - uint32_t verticesCount = static_cast(prov.vertices.size()); - out.write(bytes(&verticesCount), sizeof(uint32_t)); - - for(size_t i: prov.vertices) - { - vertex v{ - static_cast(i%width), - static_cast(i/width) - }; - out.write(bytes(&v),sizeof(v)); - } - - uint32_t indicesCount = static_cast(prov.indices.size()); - out.write(bytes(&indicesCount), sizeof(uint32_t)); - - out.write(reinterpret_cast(prov.indices.data()), prov.indices.size() * sizeof(uint16_t)); - } - } -} - -/* ================= MAIN ================= */ - -int main(int argc,char**argv){ - std::ifstream fin(argv[1],std::ios::binary); - std::ofstream fout(argv[2],std::ios::binary); - - BMPFileHeader fh; - BMPInfoHeader ih; - fin.read((char*)&fh,sizeof(fh)); - fin.read((char*)&ih,sizeof(ih)); - - width=ih.biWidth; - height=ih.biHeight; - rowSize=(width*3+3)&~3; - - std::vector img(rowSize*height); - std::vector tmp(rowSize*height); - - fin.seekg(fh.bfOffBits); - fin.read((char*)img.data(),img.size()); - fin.seekg(fh.bfOffBits); - fin.read((char*)tmp.data(),tmp.size()); - - imageData=img.data(); - tempImageData=tmp.data(); - - FloodFill(); - FindContour(); - Aproximate(1); - Triangulate(); - Optimize(); - GenVertices(fout); -} diff --git a/sdk/map/run.cmd b/sdk/map/run.cmd deleted file mode 100644 index c33f181..0000000 --- a/sdk/map/run.cmd +++ /dev/null @@ -1,6 +0,0 @@ -@echo off - -set ASSETS=../../assets -set COMPILE=%CD%/../../out/build/DEBUG/sdk/map/convert_map.exe - -for %%F in (maps/*) do %COMPILE% "maps/%%~nF.bmp" "%ASSETS%/map/%%~nF" && echo "Compiled: %%~nF" diff --git a/sdk/modules/compile.json.cppm b/sdk/modules/compile.json.cppm new file mode 100644 index 0000000..10dc8de --- /dev/null +++ b/sdk/modules/compile.json.cppm @@ -0,0 +1,121 @@ +module; + +#include +#include + +export module compile.json; +import utils; +import io; +import std; + +using json = nlohmann::json; + +export namespace ee_sdk { + class JsonCompiler + { + public: + explicit JsonCompiler(const std::filesystem::path &mappingsConfig) { + for ( + const json mappingsJson = ReadJson(mappingsConfig); + auto &[key, value] : mappingsJson.items() + ) { + const char* color_str = value.get_ref().c_str() + 1; + std::uint8_t b = 0, g = 0, r = 0; + std::uint32_t hex = 0; + + std::from_chars(color_str, color_str + 2, r, 16); color_str += 2; + std::from_chars(color_str, color_str + 2, g, 16); color_str += 2; + std::from_chars(color_str, color_str + 2, b, 16); + + hex = colorToHex(r, g, b); + + mappings[key] = hex; + } + } + + void Compile(json &asset) const { + CompileRecursive(asset, CompileHashes); + CompileRecursive(asset, std::bind_front(&JsonCompiler::CompileMappings, this)); + CompileRecursive(asset, &CompileColors); + } + + private: + std::unordered_map mappings; + + static void CompileRecursive( + json &obj, + const std::function &key, json &value)> &callback + ) + { + if (obj.is_array()) {for (auto &element : obj) CompileRecursive(element, callback); return;} + if (!obj.is_object()) {callback("", obj); return;} + + for (auto &[key, value] : obj.items()) + { + if (value.is_object()) + CompileRecursive(value, callback); + else if (value.is_array()) + for (auto &element : value) + CompileRecursive(element, callback); + else callback(key, value); + } + } + + //TODO: extern config + static bool NeedsCompile(const std::basic_string &key) { + return + key == "tag" || + key.ends_with("AssetID") || + key.ends_with("Type"); + } + + static void CompileHashes(const std::basic_string &key, json &value) { + if (!value.is_string()) return; + if (!NeedsCompile(key)) return; + + const std::filesystem::path linkPath = value; + const auto linkPath8 = linkPath.generic_u8string(); + std::uint32_t assetId = XXH32(linkPath8.data(), linkPath8.size(), 0); + + LOG(INFO, "COMPILED HASH: Replace string [%s] with [%d] asset ID", linkPath.c_str(), assetId); + value = assetId; + } + + void CompileMappings(const std::basic_string &, json &value) const { + if (!value.is_string()) return; + const std::string& value_string = value.get_ref(); + + for (const auto &[name, toValue] : mappings) + { + if (value_string == name) + { + LOG(INFO, "COMPILED MAPPING: Replace string [%hs] with [0x%X] (%d) value", + value_string.c_str(), + toValue, + toValue + ); + value = toValue; + break; + } + } + } + + static void CompileColors(const std::basic_string &, json &value) { + if (!value.is_string()) return; + + const char *colorStr = value.get_ref().c_str() + 1; + const char *colorStrPointer = colorStr; + std::uint8_t b = 0, g = 0, r = 0; + std::uint32_t hex = 0; + + std::from_chars(colorStrPointer, colorStrPointer + 2, r, 16); colorStrPointer += 2; + std::from_chars(colorStrPointer, colorStrPointer + 2, g, 16); colorStrPointer += 2; + std::from_chars(colorStrPointer, colorStrPointer + 2, b, 16); colorStrPointer += 2; + + hex = colorToHex(r, g, b); + + LOG(INFO, "COMPILED COLOR: Replace string [%hs] with 0x[%X] (RGB %d, %d, %d) color", colorStr, hex, r, g, b); + value = hex; + } + }; +} \ No newline at end of file diff --git a/sdk/modules/compile.mapmesh.cppm b/sdk/modules/compile.mapmesh.cppm new file mode 100644 index 0000000..50655da --- /dev/null +++ b/sdk/modules/compile.mapmesh.cppm @@ -0,0 +1,499 @@ +module; + +#include + +export module compile.mapmesh; +import utils; +import io; +import AssetData; +import std; + +struct Pt; + +namespace ee_sdk { +#pragma pack(push, 1) + struct BMPFileHeader { + std::uint16_t bfType; + std::uint32_t bfSize; + std::uint16_t bfReserved1; + std::uint16_t bfReserved2; + std::uint32_t bfOffBits; + }; + + struct BMPInfoHeader { + std::uint32_t biSize; + std::int32_t biWidth; + std::int32_t biHeight; + std::uint16_t biPlanes; + std::uint16_t biBitCount; + std::uint32_t biCompression; + std::uint32_t biSizeImage; + std::int32_t biXPelsPerMeter; + std::int32_t biYPelsPerMeter; + std::uint32_t biClrUsed; + std::uint32_t biClrImportant; + }; +#pragma pack(pop) + + using BGR = glm::vec<3, std::uint8_t>; + struct BGRHash { + std::size_t operator()(const BGR& c) const noexcept {return colorToHex(c.r, c.g, c.b, true);} + }; + + struct prov + { + std::vector vertices; // pixels indices + std::vector indices; + }; + + using PixelMesh = std::unordered_map,BGRHash>; + + struct Image { + std::int32_t width; + std::int32_t height; + std::size_t rowSize; + std::vector image; + std::vector tempImage; + }; + + class Point { + public: + std::int32_t x, y; + std::size_t id; + + Point() = default; + Point (const std::size_t i, const std::size_t width) { + x = i % width; + y = i / width; + id = i; + } + + static std::int64_t Cross(const Point &a, const Point &b, const Point &c) { + return 1LL * (b.x - a.x) * (c.y - a.y) - 1LL * (b.y - a.y) * (c.x - a.x); + } + + static std::int64_t SignedArea2(const std::span &p) { + std::int64_t a = 0; + for (std::size_t i = 0; i < p.size(); i++) { + const Point &p0 = p[i]; + const Point& p1 = p[(i + 1) % p.size()]; + a += 1LL * p0.x * p1.y - 1LL * p1.x * p0.y; + } + return a; + } + + bool IsInTriangle(const Point &a, const Point &b, const Point &c) const { + const std::int64_t c1 = Cross(a, b, *this); + const std::int64_t c2 = Cross(b, c, *this); + const std::int64_t c3 = Cross(c, a, *this); + return (c1 >= 0 && c2 >= 0 && c3 >= 0) || (c1 <= 0 && c2 <= 0 && c3 <= 0); + } + }; +} + +export namespace ee_sdk { + class MapMeshCompiler { + public: + static ee_engine::MapMesh CompileBMP(std::span asset) { + + ee_engine::MapMesh mesh; + + // Read BMP file info from header + BMPFileHeader fileHeader; + BMPInfoHeader infoHeader; + std::memcpy(&fileHeader, asset.data(), sizeof(BMPFileHeader)); + std::memcpy(&infoHeader, asset.data() + sizeof(BMPFileHeader), sizeof(BMPInfoHeader)); + + Image imageInfo { + .width = infoHeader.biWidth, + .height = infoHeader.biHeight, + .rowSize = static_cast(infoHeader.biWidth)*3+3&~3 + }; + + // Copy file to buffers for algorithms + imageInfo.image.resize(imageInfo.rowSize * imageInfo.height); + imageInfo.tempImage.resize(imageInfo.rowSize * imageInfo.height); + + std::memcpy(imageInfo.image.data(), asset.data() + fileHeader.bfOffBits, imageInfo.image.size()); + std::memcpy(imageInfo.tempImage.data(), asset.data() + fileHeader.bfOffBits, imageInfo.tempImage.size()); + + PixelMesh pixelMesh; + + // Extract mesh from image + FloodFill(imageInfo, pixelMesh); + FindContour(imageInfo, pixelMesh); + Aproximate(imageInfo, pixelMesh, 1); + Triangulate(imageInfo, pixelMesh); + Optimize(pixelMesh); + + // Provide mesh in suitable format + for(auto&[color, provs]: pixelMesh) + { + std::vector outProvinces; + + for(auto&[vertices, indices]: provs) + { + ee_engine::MapMesh::Province outProvince; + + //Skip invalid meshes + if (indices.size() % 3 != 0) + { + vertices.clear(); + indices.clear(); + } + + for(std::size_t i: vertices) + { + glm::vec2 v{ + static_cast(i % imageInfo.width), + static_cast(i / imageInfo.width) + }; + outProvince.vertices.push_back(v); + } + outProvince.indices = indices; + + outProvinces.push_back(outProvince); + } + + mesh.regions[colorToHex(color.r, color.g, color.b, true)] = outProvinces; + } + + LOG(INFO, "COMPILED MAPMESH: generated [%d] regions", mesh.regions.size()); + + return mesh; + } + + private: + static constexpr BGR EMPTY_COLOR = {255, 255, 255}; + + static inline BGR getPixelSafe(const Image &image, const std::size_t r, const std::size_t c) { + if(r >= image.height || c >= image.width) return EMPTY_COLOR; + return *reinterpret_cast( + image.image.data() + r * image.rowSize + c * 3 + ); + } + + static inline BGR getPixelSafeIndex(const Image &image, const std::size_t i) { + return getPixelSafe(image, i / image.width, i % image.width); + } + + static void FloodFill(Image &image, PixelMesh &mesh) { + const std::size_t size = image.width * image.height; + for(std::size_t i = 0; i < size; i++) { + BGR color= *reinterpret_cast(image.tempImage.data() + (i/image.width)*image.rowSize + (i%image.width)*3); + + if(color == EMPTY_COLOR) + continue; + + std::vector prov; + std::queue q; + q.push(i); + + while (!q.empty()) { + std::size_t n = q.front(); q.pop(); + BGR& px = *reinterpret_cast(image.tempImage.data() + (n/image.width)*image.rowSize + (n%image.width)*3); + if(px != color) continue; + px = EMPTY_COLOR; + prov.push_back(n); + + if(n >= image.width) q.push(n - image.width); + if(n + image.width < size) q.push(n + image.width); + if(n % image.width) q.push(n - 1); + if(n % image.width + 1 < image.width) q.push(n + 1); + } + mesh[color].push_back({.vertices = prov}); + } + } + + static bool MooreStep( + const Image &image, + const std::size_t cur, + const std::size_t prev, + const BGR id, + std::size_t &next + ) { + struct offset{int8_t x,y;}; + + static const offset moore[8]={ + {1,0},{1,-1},{0,-1},{-1,-1}, + {-1,0},{-1,1},{0,1},{1,1} + }; + + const std::int32_t cr = cur / image.width, cc = cur % image.width; + const std::int32_t pr = prev / image.width, pc = prev % image.width; + + const std::int32_t dx = cc - pc, dy = cr - pr; + std::int32_t startDir = 0; + for(std::int32_t i = 0; i < 8; i++) { + if(moore[i].x == dx && moore[i].y == dy) + startDir=(i+6)&7; + } + + for(std::int32_t k=0; k<8; k++){ + const std::int32_t d = (startDir+k)&7; + const std::int32_t nr = cr + moore[d].y; + if( + const std::int32_t nc = cc + moore[d].x; + getPixelSafe(image, nr,nc) == id + ){ + next= static_cast(nr) * image.width + static_cast(nc); + return true; + } + } + return false; + } + + + static std::size_t FindStart(const Image &image, const std::vector&prov, const BGR &id) { + for(const std::size_t i : prov){ + const std::size_t r = i / image.width; + const std::size_t c = i % image.width; + + if(getPixelSafe(image, r - 1, c) != id|| + getPixelSafe(image, r + 1, c) != id|| + getPixelSafe(image, r, c- 1) != id|| + getPixelSafe(image, r, c+ 1) != id) + return i; + } + return std::numeric_limits::max(); + } + + static void FindContour(const Image &image, PixelMesh &mesh) { + for(auto &provs : mesh | std::views::values){ + for(auto&[vertices, indices] : provs){ + if(vertices.empty()) continue; + BGR id = getPixelSafeIndex(image, vertices[0]); + std::size_t start = FindStart(image, vertices,id); + if(start == std::numeric_limits::max()) continue; + + std::vector contour; + contour.push_back(start); + + std::size_t cur=start; + std::size_t prev = (start % image.width > 0) ? start - 1 : start + 1; + + + const std::size_t MAX = vertices.size()*8; + for(std::size_t i = 0 ;i < MAX; i++){ + std::size_t next; + if(!MooreStep(image, cur, prev, id, next)) break; + if(next == start) break; + contour.push_back(next); + prev = cur; + cur = next; + } + vertices.swap(contour); + } + } + } + + static double PerpendicularDistance( + const std::size_t p, + const std::size_t a, + const std::size_t b, + const std::size_t width + ) + { + const double px = p % width; + const double py = p / width; + + const double ax = a % width; + const double ay = a / width; + + const double bx = b % width; + const double by = b / width; + + const double dx = bx - ax; + const double dy = by - ay; + + if(dx == 0 && dy == 0) + return std::hypot(px - ax, py - ay); + + const double num = std::abs(dy * px - dx * py + bx * ay - by * ax); + const double den = std::sqrt(dx * dx + dy * dy); + + return num / den; + } + + // Douglas-Peucker iterative + static void Aproximate(const Image &image, PixelMesh &mesh, const std::size_t epsilon) + { + for(auto &provs : mesh | std::views::values) + { + for(auto&[vertices, indices] : provs) + { + if(vertices.size() < 3) + continue; + + const std::size_t n = vertices.size(); + + std::vector keep_point(n, true); + std::stack> st; + + // в стек кладем первый и последний индекс + st.push({0, n - 1}); + + while(!st.empty()) + { + auto [startIndex, endIndex] = st.top(); + st.pop(); + + double dMax = 0.0; + std::size_t index = startIndex; + + for(std::size_t i = startIndex + 1; i < endIndex; ++i) + { + if(keep_point[i]) + { + double d = PerpendicularDistance( + vertices[i], + vertices[startIndex], + vertices[endIndex], + image.width + ); + + if(d > dMax) + { + dMax = d; + index = i; + } + } + } + + if(dMax >= epsilon) + { + st.push({startIndex, index}); + st.push({index, endIndex}); + } + else + { + for(size_t j = startIndex + 1; j < endIndex; ++j) + keep_point[j] = false; + } + } + + // формируем итоговый список + std::vector result; + result.reserve(n); + + for(std::size_t i = 0; i < n; ++i) + { + if(keep_point[i]) + result.push_back(vertices[i]); + } + + vertices.swap(result); + } + } + } + + static std::vector TriangulateProv(const std::vector &prov, const std::size_t width) { + std::vector poly; + poly.reserve(prov.size()); + for (const std::size_t i : prov) + poly.push_back(Point(i, width)); + + // CCW + if (Point::SignedArea2(poly) < 0) + std::ranges::reverse(poly); + + std::vector out; // a,b,c,a,b,c,... + + while (poly.size() > 3) { + bool ear_found = false; + + for (std::size_t i = 0; i < poly.size(); ++i) { + const std::size_t i0 = (i + poly.size() - 1) % poly.size(); + const std::size_t i1 = i; + const std::size_t i2 = (i + 1) % poly.size(); + + const Point& a = poly[i0]; + const Point& b = poly[i1]; + const Point& c = poly[i2]; + + if (Point::Cross(a, b, c) <= 0) + continue; // не выпуклая + + bool inside = false; + for (std::size_t j = 0; j < poly.size(); ++j) { + if (j == i0 || j == i1 || j == i2) continue; + if (poly[j].IsInTriangle(a, b, c)) { + inside = true; + break; + } + } + + if (!inside) { + out.push_back(a.id); + out.push_back(b.id); + out.push_back(c.id); + poly.erase(poly.begin() + i1); + ear_found = true; + break; + } + } + + if (!ear_found) { + // плохой контур (самопересечение / коллинеарность) + break; + } + } + + if (poly.size() == 3) { + out.push_back(poly[0].id); + out.push_back(poly[1].id); + out.push_back(poly[2].id); + } + + return out; + } + + static void Triangulate(const Image &image, PixelMesh &pixelMesh) + { + for(auto &provs : pixelMesh | std::views::values) + { + for(auto&[vertices, indices] : provs) + { + if (vertices.size() < 3) + continue; + + vertices = TriangulateProv(vertices, image.width); + } + } + } + + static void Optimize(PixelMesh &pixelMesh) + { + for(auto &provs : pixelMesh | std::views::values) + { + for(auto&[vertices, indices] : provs) + { + std::unordered_map vertexToIndex; + vertexToIndex.reserve(vertices.size()); + + std::vector uniqueVertices; + uniqueVertices.reserve(vertices.size()); + + indices.clear(); + + for (std::size_t v : vertices) + { + if (auto it = vertexToIndex.find(v); it != vertexToIndex.end()) + { + indices.push_back(it->second); + } + else + { + std::uint16_t newIndex = static_cast(uniqueVertices.size()); + uniqueVertices.push_back(v); + vertexToIndex[v] = newIndex; + indices.push_back(newIndex); + } + } + + vertices.swap(uniqueVertices); + } + } + } + }; +} \ No newline at end of file diff --git a/sdk/modules/io.cppm b/sdk/modules/io.cppm new file mode 100644 index 0000000..473077e --- /dev/null +++ b/sdk/modules/io.cppm @@ -0,0 +1,113 @@ +module; + +#include +#include + +export module io; +import utils; +import AssetData; +import std; + +using json = nlohmann::json; + +export namespace ee_data +{ + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( + Subject, + tag, + color, + regions + ) +} + +export namespace ee_data +{ + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( + GameState, + subjects + ) +} + +export namespace ee_assets +{ + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( + AssetSave, + content + ) + + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( + AssetMaterial, + vertexShaderAssetID, + fragmentShaderAssetID + ) +} + +export namespace ee_sdk { + + std::optional ReadJson(const std::filesystem::path &assetPath) { + std::ifstream fin(assetPath); + if (!fin.is_open()) { + LOG(ERROR, "Could not open output file: [%s]", assetPath.c_str()); + return std::nullopt; + } + + json data = json::parse(fin); + + fin.close(); + return data; + } + + std::optional> ReadBinary(const std::filesystem::path &assetPath) { + std::ifstream fin(assetPath, std::ios::binary); + if (!fin.is_open()) { + LOG(ERROR, "Could not open output file: [%s]", assetPath.c_str()); + return std::nullopt; + } + + std::vector data; + + // Read size + fin.seekg(0, std::ios::end); + const std::streamsize size = fin.tellg(); + fin.seekg(0, std::ios::beg); + data.reserve(size); + + // Read content + fin.read(reinterpret_cast(data.data()), size); + fin.close(); + return data; + } + + template + void WriteAsset(const std::filesystem::path &assetPath, const TAsset &asset) { + std::ofstream fout(assetPath, std::ios::binary); + if (!fout.is_open()) { + LOG(ERROR, "Could not open input file: [%s]", assetPath.c_str()); + return; + } + + const std::vector data = rfl::capnproto::write(asset); + + fout.write(data.data(), data.size()); + fout.close(); + } + + constexpr std::uint32_t colorToHex( + const std::uint8_t r, + const std::uint8_t g, + const std::uint8_t b, + const bool isBGR = false + ) { + if (isBGR) { + return + static_cast(b) << 16 | + static_cast(g) << 8 | + static_cast(r); + } + + return + static_cast(r) << 16 | + static_cast(g) << 8 | + static_cast(b); + } +} diff --git a/sdk/src/main.cpp b/sdk/src/main.cpp new file mode 100644 index 0000000..ffd2f22 --- /dev/null +++ b/sdk/src/main.cpp @@ -0,0 +1,95 @@ +#include +#include +// IT'S NEEDED FOR MSVC COMPILER TO NOT CRASH WITH C1001 +#include + +import utils; +import AssetData; +import io; +import compile.json; +import compile.mapmesh; +import std; + +using json = nlohmann::json; +using path = std::filesystem::path; + +template +void CompileJson(const ee_sdk::JsonCompiler &compiler, const path &relative, const path &src, const path &out) { + LOG(INFO, "Start compiling %s...", relative.c_str()); + + std::filesystem::create_directories(out/relative); + for (const std::filesystem::directory_entry &asset : std::filesystem::directory_iterator(src/relative)) { + if (!asset.is_regular_file()) continue; + + std::optional data = ee_sdk::ReadJson(asset.path()); + if (!data.has_value()) continue; + + compiler.Compile(*data); + ee_sdk::WriteAsset(out/relative/asset.path().stem().filename(), data->get()); + } +} + +void CompileMaps(const ee_sdk::MapMeshCompiler &compiler, const path &relative, const path &src, const path &out) { + LOG(INFO, "Start compiling %s...", relative.c_str()); + + std::filesystem::create_directories(out/relative); + for (const std::filesystem::directory_entry &asset : std::filesystem::directory_iterator(src/relative)) { + if (!asset.is_regular_file()) continue; + + std::optional> data = ee_sdk::ReadBinary(asset.path()); + if (!data.has_value()) continue; + + ee_sdk::WriteAsset( + out/relative/asset.path().stem().filename(), + ee_assets::AssetMapMesh{ .content = compiler.CompileBMP(*data) } + ); + } +} + +int main(const int argc, char **argv) { + std::uint32_t status = 0; + + Logger::Init(L"/P7.Sink=Console"); + Logger::RegisterThread(L"Main"); + + try { + + cxxopts::Options options( + "Etredia Empires SDK", + "This modding kit prepares your content assets for integration into game." + ); + options.add_options() + ("src", "Path to your mod source assets", cxxopts::value()) + ("o,out", "Path to compiled assets", cxxopts::value()->default_value("out")) + ("h,help", "Print usage"); + + if ( + const cxxopts::ParseResult cmd = options.parse(argc, argv); + cmd.contains("help") + ) { + LOG(INFO, "\n%hs", options.help().c_str()); + } + else { + const path &src = cmd["src"].as(); + const path &out = cmd["out"].as(); + + const ee_sdk::JsonCompiler jsonCompiler(src/"regions.json"); + CompileJson(jsonCompiler, "materials", src, out); + CompileJson(jsonCompiler, "scenarios", src, out); + + constexpr ee_sdk::MapMeshCompiler mapMeshCompiler; + CompileMaps(mapMeshCompiler, "map", src, out); + } + + } + catch (const std::exception &e) + { + LOG(CRITICAL, "%hs", e.what()); + status = 1; + } + + Logger::UnregisterThread(); + Logger::Release(); + + return status; +} \ No newline at end of file diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index b7760a8..e60951f 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -5,10 +5,14 @@ set(LIB utils) # Find dependencies find_package(Boost REQUIRED COMPONENTS lockfree) +find_package(reflectcpp CONFIG REQUIRED) +find_package(glm CONFIG REQUIRED) # Link library add_library_target(${LIB}) target_link_libraries(${LIB} PUBLIC p7) -target_link_libraries(${LIB} PRIVATE Boost::lockfree) \ No newline at end of file +target_link_libraries(${LIB} PRIVATE Boost::lockfree) +target_link_libraries(${LIB} PRIVATE reflectcpp::reflectcpp) +target_link_libraries(${LIB} PRIVATE glm::glm) \ No newline at end of file diff --git a/utils/modules/logger.cppm b/utils/modules/logger.cppm index ed9e22f..a813824 100644 --- a/utils/modules/logger.cppm +++ b/utils/modules/logger.cppm @@ -83,6 +83,7 @@ namespace logger { return; } + P7_Set_Crash_Handler(); client = P7_Create_Client(options); P7_Set_Crash_Handler(); trace = P7_Create_Trace(client, L"TC 1"); @@ -159,10 +160,14 @@ namespace logger { nullptr, location.line(), location.file_name(), - location.function_name(), + "", //TODO: location.function_name(), format, std::forward(args)... ); + + if (level == ERROR || level == CRITICAL) { + Logger::client->Flush(); + } } }; @@ -202,10 +207,14 @@ namespace logger { nullptr, location.line(), location.file_name(), - location.function_name(), + "", //TODO: location.function_name(), TM(format), std::forward(args)... ); + + if (level == ERROR || level == CRITICAL) { + Logger::client->Flush(); + } } }; diff --git a/utils/modules/serialization.cppm b/utils/modules/serialization.cppm new file mode 100644 index 0000000..3110fd7 --- /dev/null +++ b/utils/modules/serialization.cppm @@ -0,0 +1,35 @@ +module; +#include +#include + +export module utils:serialization; + +namespace rfl { + template + struct GlmVecImpl { + float x; float y; + + static GlmVecImpl from_class(const glm::vec& vector) noexcept { + return {vector.x,vector.y}; + } + glm::vec to_class() const { return {x, y}; } + }; +} + +export namespace rfl { + template + struct parsing::Parser + : CustomParser> {}; + + template + struct parsing::Parser + : CustomParser> {}; + + template + struct parsing::Parser + : CustomParser> {}; +} + +export namespace ee_utils { + +} \ No newline at end of file diff --git a/utils/modules/utils.cppm b/utils/modules/utils.cppm index b635cd6..6cd07fe 100644 --- a/utils/modules/utils.cppm +++ b/utils/modules/utils.cppm @@ -4,5 +4,6 @@ export import :worker; export import :buffers; export import :lockfree; export import :logger; +export import :serialization; export namespace ee_utils {} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index bacfd41..11ede29 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,22 +1,33 @@ { - "dependencies": [ - "boost-lockfree", - "xxhash", - "nlohmann-json", - "entt", - "vulkan", - "glfw3", - "glm", - { - "name": "magnum", - "features": ["glfwapplication"], - "version>=": "0" - }, - { - "name": "corrade", - "version>=": "0" - }, - "rmlui", - "gtest" - ] + "dependencies": [ + "cxxopts", + "boost-lockfree", + "xxhash", + "nlohmann-json", + { + "name": "reflectcpp", + "features": [ + "capnproto" + ] + }, + "entt", + "vulkan", + "glfw3", + "glm", + { + "name": "magnum", + "features": [ + "glfwapplication" + ], + "version>=": "0" + }, + { + "name": "corrade", + "version>=": "0" + }, + "rmlui", + "gtest" + ], + "version": "0.0.1", + "name": "etredia-empires" }