From 95bdb1c8bb25afb37ba81a4dd0532ecea721eed8f0be023caf8198c3dd54f622 Mon Sep 17 00:00:00 2001 From: Mexpert_RPO Date: Wed, 6 May 2026 19:45:14 +0200 Subject: [PATCH] Fist assetsystem implementations for textures and materials --- TSE_Core/include/picosha2.h | 388 ++++++++++++++++++ TSE_Core/src/elements/AssetLibrary.cpp | 127 ++++++ TSE_Core/src/elements/AssetLibrary.hpp | 47 +++ TSE_Core/src/elements/Material.cpp | 256 +++++++++++- TSE_Core/src/elements/Material.hpp | 12 +- TSE_Core/src/elements/ShaderRegistry.hpp | 4 +- TSE_Core/src/elements/Texture.cpp | 134 ++++++ TSE_Core/src/elements/Texture.hpp | 28 +- TSE_Core/src/interfaces/IAsset.hpp | 44 ++ TSE_Core/src/interfaces/IShader.hpp | 5 + TSE_Core/src/utils/JsonExports.cpp | 51 +-- TSE_Editor/src/UI/ElementDrawer.cpp | 4 +- .../shader/basicOrderedSpriteSetShader.cpp | 1 + .../src/shader/basicParticleShader.cpp | 1 + TSE_OpenGlImpl/src/shader/basicShader.cpp | 1 + .../src/shader/basicTextureShader.cpp | 1 + .../src/shader/basicTileMapShader.cpp | 1 + .../src/shader/defaultShaderHandler.cpp | 12 +- TSE_OpenGlImpl/src/shader/ditheringShader.cpp | 1 + 19 files changed, 1069 insertions(+), 49 deletions(-) create mode 100644 TSE_Core/include/picosha2.h create mode 100644 TSE_Core/src/elements/AssetLibrary.cpp create mode 100644 TSE_Core/src/elements/AssetLibrary.hpp create mode 100644 TSE_Core/src/interfaces/IAsset.hpp diff --git a/TSE_Core/include/picosha2.h b/TSE_Core/include/picosha2.h new file mode 100644 index 0000000..7119bdf --- /dev/null +++ b/TSE_Core/include/picosha2.h @@ -0,0 +1,388 @@ +/* +The MIT License (MIT) + +Copyright (C) 2017 okdshin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#ifndef PICOSHA2_H +#define PICOSHA2_H +// picosha2:20140213 + +#ifndef PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR +#define PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR \ + 1048576 //=1024*1024: default is 1MB memory +#endif + +#include +#include +#include +#include +#include +#include +namespace picosha2 { +typedef unsigned long word_t; +typedef unsigned char byte_t; + +static const size_t k_digest_size = 32; + +namespace detail { +inline byte_t mask_8bit(byte_t x) { return x & 0xff; } + +inline word_t mask_32bit(word_t x) { return x & 0xffffffff; } + +const word_t add_constant[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +const word_t initial_message_digest[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, + 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19}; + +inline word_t ch(word_t x, word_t y, word_t z) { return (x & y) ^ ((~x) & z); } + +inline word_t maj(word_t x, word_t y, word_t z) { + return (x & y) ^ (x & z) ^ (y & z); +} + +inline word_t rotr(word_t x, std::size_t n) { + assert(n < 32); + return mask_32bit((x >> n) | (x << (32 - n))); +} + +inline word_t bsig0(word_t x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); } + +inline word_t bsig1(word_t x) { return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); } + +inline word_t shr(word_t x, std::size_t n) { + assert(n < 32); + return x >> n; +} + +inline word_t ssig0(word_t x) { return rotr(x, 7) ^ rotr(x, 18) ^ shr(x, 3); } + +inline word_t ssig1(word_t x) { return rotr(x, 17) ^ rotr(x, 19) ^ shr(x, 10); } + +template +void hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 last) { + assert(first + 64 == last); + static_cast(last); // for avoiding unused-variable warning + word_t w[64]; + std::fill(w, w + 64, word_t(0)); + for (std::size_t i = 0; i < 16; ++i) { + w[i] = (static_cast(mask_8bit(*(first + i * 4))) << 24) | + (static_cast(mask_8bit(*(first + i * 4 + 1))) << 16) | + (static_cast(mask_8bit(*(first + i * 4 + 2))) << 8) | + (static_cast(mask_8bit(*(first + i * 4 + 3)))); + } + for (std::size_t i = 16; i < 64; ++i) { + w[i] = mask_32bit(ssig1(w[i - 2]) + w[i - 7] + ssig0(w[i - 15]) + + w[i - 16]); + } + + word_t a = *message_digest; + word_t b = *(message_digest + 1); + word_t c = *(message_digest + 2); + word_t d = *(message_digest + 3); + word_t e = *(message_digest + 4); + word_t f = *(message_digest + 5); + word_t g = *(message_digest + 6); + word_t h = *(message_digest + 7); + + for (std::size_t i = 0; i < 64; ++i) { + word_t temp1 = h + bsig1(e) + ch(e, f, g) + add_constant[i] + w[i]; + word_t temp2 = bsig0(a) + maj(a, b, c); + h = g; + g = f; + f = e; + e = mask_32bit(d + temp1); + d = c; + c = b; + b = a; + a = mask_32bit(temp1 + temp2); + } + *message_digest += a; + *(message_digest + 1) += b; + *(message_digest + 2) += c; + *(message_digest + 3) += d; + *(message_digest + 4) += e; + *(message_digest + 5) += f; + *(message_digest + 6) += g; + *(message_digest + 7) += h; + for (std::size_t i = 0; i < 8; ++i) { + *(message_digest + i) = mask_32bit(*(message_digest + i)); + } +} + +} // namespace detail + +template +void output_hex(InIter first, InIter last, std::ostream& os) { + os.setf(std::ios::hex, std::ios::basefield); + while (first != last) { + os.width(2); + os.fill('0'); + os << static_cast(*first); + ++first; + } + os.setf(std::ios::dec, std::ios::basefield); +} + +template +void bytes_to_hex_string(InIter first, InIter last, std::string& hex_str) { + std::ostringstream oss; + output_hex(first, last, oss); + hex_str.assign(oss.str()); +} + +template +void bytes_to_hex_string(const InContainer& bytes, std::string& hex_str) { + bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str); +} + +template +std::string bytes_to_hex_string(InIter first, InIter last) { + std::string hex_str; + bytes_to_hex_string(first, last, hex_str); + return hex_str; +} + +template +std::string bytes_to_hex_string(const InContainer& bytes) { + std::string hex_str; + bytes_to_hex_string(bytes, hex_str); + return hex_str; +} + +class hash256_one_by_one { + public: + hash256_one_by_one() { init(); } + + void init() { + buffer_.clear(); + std::fill(data_length_digits_, data_length_digits_ + 4, word_t(0)); + std::copy(detail::initial_message_digest, + detail::initial_message_digest + 8, h_); + } + + template + void process(RaIter first, RaIter last) { + add_to_data_length(static_cast(std::distance(first, last))); + std::copy(first, last, std::back_inserter(buffer_)); + std::size_t i = 0; + for (; i + 64 <= buffer_.size(); i += 64) { + detail::hash256_block(h_, buffer_.begin() + i, + buffer_.begin() + i + 64); + } + buffer_.erase(buffer_.begin(), buffer_.begin() + i); + } + + void finish() { + byte_t temp[64]; + std::fill(temp, temp + 64, byte_t(0)); + std::size_t remains = buffer_.size(); + std::copy(buffer_.begin(), buffer_.end(), temp); + assert(remains < 64); + + // This branch is not executed actually (`remains` is always lower than 64), + // but needed to avoid g++ false-positive warning. + // See https://github.com/okdshin/PicoSHA2/issues/25 + // vvvvvvvvvvvvvvvv + if(remains >= 64) { + remains = 63; + } + // ^^^^^^^^^^^^^^^^ + + temp[remains] = 0x80; + + if (remains > 55) { + std::fill(temp + remains + 1, temp + 64, byte_t(0)); + detail::hash256_block(h_, temp, temp + 64); + std::fill(temp, temp + 64 - 4, byte_t(0)); + } else { + std::fill(temp + remains + 1, temp + 64 - 4, byte_t(0)); + } + + write_data_bit_length(&(temp[56])); + detail::hash256_block(h_, temp, temp + 64); + } + + template + void get_hash_bytes(OutIter first, OutIter last) const { + for (const word_t* iter = h_; iter != h_ + 8; ++iter) { + for (std::size_t i = 0; i < 4 && first != last; ++i) { + *(first++) = detail::mask_8bit( + static_cast((*iter >> (24 - 8 * i)))); + } + } + } + + private: + void add_to_data_length(word_t n) { + word_t carry = 0; + data_length_digits_[0] += n; + for (std::size_t i = 0; i < 4; ++i) { + data_length_digits_[i] += carry; + if (data_length_digits_[i] >= 65536u) { + carry = data_length_digits_[i] >> 16; + data_length_digits_[i] &= 65535u; + } else { + break; + } + } + } + void write_data_bit_length(byte_t* begin) { + word_t data_bit_length_digits[4]; + std::copy(data_length_digits_, data_length_digits_ + 4, + data_bit_length_digits); + + // convert byte length to bit length (multiply 8 or shift 3 times left) + word_t carry = 0; + for (std::size_t i = 0; i < 4; ++i) { + word_t before_val = data_bit_length_digits[i]; + data_bit_length_digits[i] <<= 3; + data_bit_length_digits[i] |= carry; + data_bit_length_digits[i] &= 65535u; + carry = (before_val >> (16 - 3)) & 65535u; + } + + // write data_bit_length + for (int i = 3; i >= 0; --i) { + (*begin++) = static_cast(data_bit_length_digits[i] >> 8); + (*begin++) = static_cast(data_bit_length_digits[i]); + } + } + std::vector buffer_; + word_t data_length_digits_[4]; // as 64bit integer (16bit x 4 integer) + word_t h_[8]; +}; + +inline void get_hash_hex_string(const hash256_one_by_one& hasher, + std::string& hex_str) { + byte_t hash[k_digest_size]; + hasher.get_hash_bytes(hash, hash + k_digest_size); + return bytes_to_hex_string(hash, hash + k_digest_size, hex_str); +} + +inline std::string get_hash_hex_string(const hash256_one_by_one& hasher) { + std::string hex_str; + get_hash_hex_string(hasher, hex_str); + return hex_str; +} + +namespace impl { +template +void hash256_impl(RaIter first, RaIter last, OutIter first2, OutIter last2, int, + std::random_access_iterator_tag) { + hash256_one_by_one hasher; + // hasher.init(); + hasher.process(first, last); + hasher.finish(); + hasher.get_hash_bytes(first2, last2); +} + +template +void hash256_impl(InputIter first, InputIter last, OutIter first2, + OutIter last2, int buffer_size, std::input_iterator_tag) { + std::vector buffer(buffer_size); + hash256_one_by_one hasher; + // hasher.init(); + while (first != last) { + int size = buffer_size; + for (int i = 0; i != buffer_size; ++i, ++first) { + if (first == last) { + size = i; + break; + } + buffer[i] = *first; + } + hasher.process(buffer.begin(), buffer.begin() + size); + } + hasher.finish(); + hasher.get_hash_bytes(first2, last2); +} +} + +template +void hash256(InIter first, InIter last, OutIter first2, OutIter last2, + int buffer_size = PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR) { + picosha2::impl::hash256_impl( + first, last, first2, last2, buffer_size, + typename std::iterator_traits::iterator_category()); +} + +template +void hash256(InIter first, InIter last, OutContainer& dst) { + hash256(first, last, dst.begin(), dst.end()); +} + +template +void hash256(const InContainer& src, OutIter first, OutIter last) { + hash256(src.begin(), src.end(), first, last); +} + +template +void hash256(const InContainer& src, OutContainer& dst) { + hash256(src.begin(), src.end(), dst.begin(), dst.end()); +} + +template +void hash256_hex_string(InIter first, InIter last, std::string& hex_str) { + byte_t hashed[k_digest_size]; + hash256(first, last, hashed, hashed + k_digest_size); + std::ostringstream oss; + output_hex(hashed, hashed + k_digest_size, oss); + hex_str.assign(oss.str()); +} + +template +std::string hash256_hex_string(InIter first, InIter last) { + std::string hex_str; + hash256_hex_string(first, last, hex_str); + return hex_str; +} + +inline void hash256_hex_string(const std::string& src, std::string& hex_str) { + hash256_hex_string(src.begin(), src.end(), hex_str); +} + +template +void hash256_hex_string(const InContainer& src, std::string& hex_str) { + hash256_hex_string(src.begin(), src.end(), hex_str); +} + +template +std::string hash256_hex_string(const InContainer& src) { + return hash256_hex_string(src.begin(), src.end()); +} +templatevoid hash256(std::ifstream& f, OutIter first, OutIter last){ + hash256(std::istreambuf_iterator(f), std::istreambuf_iterator(), first,last); + +} +}// namespace picosha2 +#endif // PICOSHA2_H diff --git a/TSE_Core/src/elements/AssetLibrary.cpp b/TSE_Core/src/elements/AssetLibrary.cpp new file mode 100644 index 0000000..fbd9f51 --- /dev/null +++ b/TSE_Core/src/elements/AssetLibrary.cpp @@ -0,0 +1,127 @@ +#include "AssetLibrary.hpp" +#include "Material.hpp" +#include "Texture.hpp" +#include +#include +#include +#include "Debug.hpp" + +std::unordered_map> TSE::AssetLibrary::assets; +std::unordered_map TSE::AssetLibrary::alreadyLoadedAssets; +TSE::string TSE::AssetLibrary::currentAssetPath; + +bool TSE::AssetLibrary::HasValue(const uuids::uuid &key) +{ + return assets.find(key) != assets.end(); +} + +int TSE::AssetLibrary::GetValueCount() +{ + return assets.size(); +} + +std::tuple &TSE::AssetLibrary::GetValueAt(int j) +{ + auto it = assets.begin(); + for (int i = 0; i < j; i++) + { + it++; + } + return it->second; +} + +void TSE::AssetLibrary::SaveAllAssets() +{ + int count = GetValueCount(); + for(int i = 0; i < count; i++) + { + std::tuple tupel = GetValueAt(i); + auto[ptr, type, name] = tupel; + if (type == typeid(Material*).name()) + { + Material* value = GetValue(name); + if(!value->IsVirtualAsset) + value->SaveAsset(); + } + if (type == typeid(Texture*).name()) + { + Texture* value = GetValue(name); + if(!value->IsVirtualAsset) + value->SaveAsset(); + } + } +} + +std::string ToLower(std::string str) +{ + std::transform(str.begin(), str.end(), str.begin(), + [](unsigned char c) + { + return static_cast(std::tolower(c)); + }); + + return str; +} + +void TSE::AssetLibrary::LoadAllAssets(string &path) +{ + namespace fs = std::filesystem; + + currentAssetPath = string(path); + if (!fs::exists(currentAssetPath) || !fs::is_directory(currentAssetPath)) + { + TSE_ERROR("Invalid Asset Path: " + currentAssetPath); + return; + } + for (const auto& entry : fs::recursive_directory_iterator(currentAssetPath)) + { + if (!entry.is_regular_file()) + continue; + + string ext = ToLower(entry.path().extension().string()); + string path = entry.path().string(); + + if(alreadyLoadedAssets.find(path) != alreadyLoadedAssets.end()) continue; + + if (ext == ".material") + { + Material* mat = new Material(); + mat->LoadAsset(path); + SetValue(mat->id, mat); + alreadyLoadedAssets[path] = mat->id; + } + else if (ext == ".png" || ext == ".jpeg" || ext == ".jpg") + { + Texture* tex = new Texture(0,0); + tex->LoadAsset(path); + SetValue(tex->id, tex); + alreadyLoadedAssets[path] = tex->id; + } + } + +} + +void TSE::AssetLibrary::RescanAssets() +{ + int count = GetValueCount(); + for(int i = 0; i < count; i++) + { + std::tuple tupel = GetValueAt(i); + auto[ptr, type, name] = tupel; + if (type == typeid(Material*).name()) + { + Material* value = GetValue(name); + if(!value->IsVirtualAsset) + value->Rescan(); + } + if (type == typeid(Texture*).name()) + { + Texture* value = GetValue(name); + if(!value->IsVirtualAsset) + value->Rescan(); + } + } +} + +template void TSE::AssetLibrary::SetValue(const uuids::uuid&, const TSE::Material*); +template void TSE::AssetLibrary::SetValue(const uuids::uuid&, const TSE::Texture*); diff --git a/TSE_Core/src/elements/AssetLibrary.hpp b/TSE_Core/src/elements/AssetLibrary.hpp new file mode 100644 index 0000000..4e37f0f --- /dev/null +++ b/TSE_Core/src/elements/AssetLibrary.hpp @@ -0,0 +1,47 @@ +#pragma once +#include "interfaces/IAsset.hpp" +#include +#include +#include "uuid.h" + +namespace TSE +{ + class AssetLibrary + { + private: + static std::unordered_map> assets; + static std::unordered_map alreadyLoadedAssets; + + static string currentAssetPath; + + public: + template + static T GetValue(const uuids::uuid& key); + + template + static void SetValue(const uuids::uuid& key, const T* value); + + static bool HasValue(const uuids::uuid& key); + static int GetValueCount(); + static std::tuple& GetValueAt(int i); + + static void SaveAllAssets(); + static void LoadAllAssets(string& path); + static void RescanAssets(); + }; + + template + T AssetLibrary::GetValue(const uuids::uuid& key) { + auto it = assets.find(key); + if (it == assets.end()) { + throw std::runtime_error("AssetLibrary::GetValue - key '" + uuids::to_string(key) + "' not found"); + } + auto [a,b,c] = it->second; + return std::any_cast(a); + } + + template + void AssetLibrary::SetValue(const uuids::uuid& key, const T* value) { + assets[key] = std::make_tuple(value, typeid(T).name(), key); + } +} \ No newline at end of file diff --git a/TSE_Core/src/elements/Material.cpp b/TSE_Core/src/elements/Material.cpp index 5f3d86d..6cd6bfe 100644 --- a/TSE_Core/src/elements/Material.cpp +++ b/TSE_Core/src/elements/Material.cpp @@ -9,6 +9,10 @@ #include "Vector4.hpp" #include "Color.hpp" #include "Matrix4x4.hpp" +#include "PathHelper.hpp" +#include "utils/JsonExports.hpp" +#include "Debug.hpp" +#include "ShaderRegistry.hpp" namespace TSE { @@ -18,7 +22,9 @@ namespace TSE id = GenerateRandomUUID(); } Material::Material(const string& name, IShader* shader, uuids::uuid id) - : name(name), shader(shader), id(id) {} + : name(name), shader(shader){ + this->id = id; + } Material::Material(const string &name, IShader *shader) : name(name), shader(shader) @@ -59,6 +65,254 @@ namespace TSE return id; } + bool Material::LoadAsset(string &path) + { + rawPath = string(path); + string metaPath = PathToMetaPath(rawPath); + using json = nlohmann::ordered_json; + + std::ifstream rawStream; + OpenFileReading(rawStream, rawPath); + json material = json::parse(rawStream); + if(material["type"] != "material") + { + rawStream.close(); + TSE_ERROR("Asset Loading Error (Material): not a material file = " + ((string)material["type"])); + return false; + } + if(material["version"] == 1) + { + name = material["name"]; + shader = ShaderRegistry::GetShader(material["shader"]); + if (material.contains("properties")) + for (auto& [name, prop] : material["properties"].items()) + { + std::string type = prop["type"]; + auto& value = prop["value"]; + + if (type == "Int") + { + int v = value.get(); + SetValue(name, v); + } + else if (type == "Float") + { + float v = value.get(); + SetValue(name, v); + } + else if (type == "Vector2") + { + Vector2 v = ImportVector2(value); + SetValue(name, v); + } + else if (type == "Vector3") + { + Vector3 v = ImportVector3(value); + SetValue(name, v); + } + else if (type == "Vector4") + { + Vector4 v = ImportVector4(value); + SetValue(name, v); + } + else if (type == "Color") + { + Color v = ImportColor(value); + SetValue(name, v); + } + else if (type == "String") + { + string v = value.get(); + SetValue(name, v); + } + else if (type == "Uint") + { + uint v = value.get(); + SetValue(name, v); + } + } + rawStream.close(); + + Hash(); + + rawStream.close(); + if(FileExists(metaPath)) + { + std::ifstream metaStream; + OpenFileReading(metaStream, metaPath); + json meta = json::parse(metaStream); + if(meta["type"] != "metadata") + { + metaStream.close(); + TSE_WARNING("Rebuilding corrupted meta file (Material): " + metaPath); + SaveMeta(); + return true; + } + if(meta["version"] == 1) + { + if(meta["assetType"] == "material") + { + id = uuids::uuid::from_string((string)meta["id"]).value_or(id); + + //dependencies; + + metaStream.close(); + SaveMeta(); + return true; + } + else + { + metaStream.close(); + TSE_WARNING("Meta file incompatible with asset type. Please delete for auto regeneration or fix maualy (Material): " + metaPath); + return true; + } + } + else + { + metaStream.close(); + TSE_WARNING("Meta file incompatible. Please delete for auto regeneration or fix maualy (Material): " + metaPath); + return true; + } + } + else + { + SaveMeta(); + return true; + } + } + else + { + rawStream.close(); + TSE_ERROR("Asset Loading Error (Material): incompatible asset version = " + std::to_string((int)material["version"])); + return false; + } + + } + + void Material::SaveAsset() + { + using json = nlohmann::ordered_json; + + json material = GenerateMaterialJson(); + std::ofstream rawStream; + CreateFileWriting(rawStream, rawPath); + rawStream << material.dump(4) << std::endl; + rawStream.close(); + + Hash(); + + SaveMeta(); + } + + nlohmann::ordered_json Material::GenerateMaterialJson() + { + using json = nlohmann::ordered_json; + + json material; + material["type"] = "material"; + material["version"] = 1; + material["name"] = name; + material["shader"] = shader->GetName(); + + int count = GetValueCount(); + for(int i = 0; i < count; i++) + { + std::tuple tupel = GetValueAt(i); + auto[ptr, type, name] = tupel; + if (type == typeid(int).name()) + { + int value = GetValue(name); + material["properties"][name]["type"] = "Int"; + material["properties"][name]["value"] = value; + } + else if (type == typeid(float).name()) + { + float value = GetValue(name); + material["properties"][name]["type"] = "Float"; + material["properties"][name]["value"] = value; + } + else if (type == typeid(Vector2).name()) + { + Vector2 value = GetValue(name); + material["properties"][name]["type"] = "Vector2"; + material["properties"][name]["value"] = ExportVector2(value); + } + else if (type == typeid(Vector3).name()) + { + Vector3 value = GetValue(name); + material["properties"][name]["type"] = "Vector3"; + material["properties"][name]["value"] = ExportVector3(value); + } + else if (type == typeid(Vector4).name()) + { + Vector4 value = GetValue(name); + material["properties"][name]["type"] = "Vector4"; + material["properties"][name]["value"] = ExportVector4(value); + } + else if (type == typeid(Matrix4x4).name()) + { + Matrix4x4 value = GetValue(name); + //TODO: need to implement; + } + else if (type == typeid(Color).name()) + { + Color value = GetValue(name); + material["properties"][name]["type"] = "Color"; + material["properties"][name]["value"] = ExportColor(value); + } + else if (type == typeid(std::string).name()) + { + std::string value = GetValue(name); + material["properties"][name]["type"] = "String"; + material["properties"][name]["value"] = value; + } + else if (type == typeid(Texture*).name()) + { + Texture* value = GetValue(name); + //todo: implement + } + if (type == typeid(uint).name()) + { + int value = GetValue(name); + material["properties"][name]["type"] = "Uint"; + material["properties"][name]["value"] = value; + } + } + return material; + } + + void Material::Hash() + { + using json = nlohmann::ordered_json; + + json material = GenerateMaterialJson(); + hash = sha256(material.dump(-1)); + } + + void Material::SaveMeta() + { + string metaPath = PathToMetaPath(rawPath); + using json = nlohmann::ordered_json; + + json meta; + meta["type"] = "metadata"; + meta["assetType"] = "material"; + meta["version"] = 1; + meta["source"] = rawPath; //todo: clamp to assets root + meta["id"] = uuids::to_string(id); + meta["hash"] = hash; + for (int i = 0; i < dependecies.size(); i++) + { + meta["dependencies"][i]["id"] = uuids::to_string(dependecies[i]->id); + meta["dependencies"][i]["path"] = dependecies[i]->rawPath; //todo: clamp to assets root + } + + std::ofstream metaStream; + CreateFileWriting(metaStream, metaPath); + metaStream << meta.dump(4) << std::endl; + metaStream.close(); + } + template void Material::SetValue(const string&, const int&); template void Material::SetValue(const string&, const uint&); template void Material::SetValue(const string&, const float&); diff --git a/TSE_Core/src/elements/Material.hpp b/TSE_Core/src/elements/Material.hpp index 1fa65cd..5d7644a 100644 --- a/TSE_Core/src/elements/Material.hpp +++ b/TSE_Core/src/elements/Material.hpp @@ -6,14 +6,15 @@ #include #include #include +#include "interfaces/IAsset.hpp" +#include "json.hpp" namespace TSE { - struct Material + struct Material : public IAsset { private: string name; - uuids::uuid id; IShader* shader; std::unordered_map> values; @@ -40,6 +41,13 @@ namespace TSE IShader* GetShader() const; void SetShader(IShader* shader); uuids::uuid GetID() const; + + bool LoadAsset(string& path) override; + void SaveAsset() override; + nlohmann::ordered_json GenerateMaterialJson(); + void Hash() override; + protected: + void SaveMeta() override; }; template diff --git a/TSE_Core/src/elements/ShaderRegistry.hpp b/TSE_Core/src/elements/ShaderRegistry.hpp index 01a3595..43f33d7 100644 --- a/TSE_Core/src/elements/ShaderRegistry.hpp +++ b/TSE_Core/src/elements/ShaderRegistry.hpp @@ -12,9 +12,9 @@ namespace TSE inline static std::unordered_map registeredShaders = {}; public: - inline static void SetShader(const string& name, IShader* shader) + inline static void SetShader(IShader* shader) { - registeredShaders[name] = shader; + registeredShaders[shader->GetName()] = shader; }; inline static IShader* GetShader(const string& name) { diff --git a/TSE_Core/src/elements/Texture.cpp b/TSE_Core/src/elements/Texture.cpp index c2351cc..1824ea3 100644 --- a/TSE_Core/src/elements/Texture.cpp +++ b/TSE_Core/src/elements/Texture.cpp @@ -3,9 +3,14 @@ #include "Color.hpp" #include "ErrorTextureData.hpp" #include +#include "json.hpp" +#include "PathHelper.hpp" +#include "utils/JsonExports.hpp" +#include "IdGenerator.hpp" TSE::Texture::Texture(const string &path) { + id = GenerateRandomUUID(); FREE_IMAGE_FORMAT fif = FREE_IMAGE_FORMAT::FIF_UNKNOWN; bmp = nullptr; imagePtr = nullptr; @@ -69,6 +74,7 @@ TSE::Texture::Texture(const string &path) TSE::Texture::Texture(const int &width, const int &height, int bpp) { + id = GenerateRandomUUID(); switch (bpp) { case 32: @@ -88,6 +94,7 @@ TSE::Texture::Texture(const int &width, const int &height, int bpp) TSE::Texture::Texture(const Vector2 &size, int bpp) { + id = GenerateRandomUUID(); switch (bpp) { case 32: @@ -120,6 +127,11 @@ TSE::Texture::~Texture() } } +TSE::string TSE::Texture::name() const +{ + return settings.name; +} + TSE::uint TSE::Texture::bpp() const { return Bpp; @@ -461,3 +473,125 @@ void TSE::Texture::PlatformDestroy() { helper->PlatromDestroy(this); } + +bool TSE::Texture::LoadAsset(string &path) +{ + *this = Texture(path); + rawPath = string(path); + UpdateImportSettings(rawPath, 0, {1,1}); + string metaPath = PathToMetaPath(rawPath); + using json = nlohmann::ordered_json; + + Hash(); + + if(FileExists(metaPath)) + { + std::ifstream metaStream; + OpenFileReading(metaStream, metaPath); + json meta = json::parse(metaStream); + if(meta["type"] != "metadata") + { + metaStream.close(); + TSE_WARNING("Rebuilding corrupted meta file (Texture): " + metaPath); + SaveMeta(); + return true; + } + if(meta["version"] == 1) + { + if(meta["assetType"] == "texture") + { + id = uuids::uuid::from_string((string)meta["id"]).value_or(id); + + //dependencies; + + settings.importMode = meta["importSettings"]["mode"]; + settings.importCount = ImportVector2(meta["importSettings"]["count"]); + settings.bpp = meta["importSettings"]["bpp"]; + settings.chanels = meta["importSettings"]["chanels"]; + settings.name = meta["importSettings"]["name"]; + + Bpp = settings.bpp; + chanels = settings.chanels; + + //create sub assets eg. sprites; + + metaStream.close(); + SaveMeta(); + return true; + } + else + { + metaStream.close(); + TSE_WARNING("Meta file incompatible with asset type. Please delete for auto regeneration or fix maualy (Texture): " + metaPath); + return true; + } + } + else + { + metaStream.close(); + TSE_WARNING("Meta file incompatible. Please delete for auto regeneration or fix maualy (Texture): " + metaPath); + return true; + } + } + else + { + SaveMeta(); + return true; + } +} + +void TSE::Texture::SaveAsset() +{ + Hash(); + SaveMeta(); +} + +void TSE::Texture::Hash() +{ + std::ifstream ifs(rawPath, std::ios::binary); + std::vector s(picosha2::k_digest_size); + picosha2::hash256(ifs, s.begin(), s.end()); + hash = picosha2::hash256_hex_string(s); + ifs.close(); +} + +void TSE::Texture::UpdateImportSettings(string name, byte mode, Vector2 count) +{ + settings.importMode = mode; + settings.importCount = count; + settings.name = name; + settings.bpp = Bpp; + settings.chanels = chanels; +} + +void TSE::Texture::SaveMeta() +{ + string metaPath = PathToMetaPath(rawPath); + using json = nlohmann::ordered_json; + + json meta; + meta["type"] = "metadata"; + meta["assetType"] = "texture"; + meta["version"] = 1; + meta["source"] = rawPath; //todo: clamp to assets root + meta["id"] = uuids::to_string(id); + meta["hash"] = hash; + + meta["importSettings"]["mode"] = settings.importMode; + meta["importSettings"]["count"] = ExportVector2(settings.importCount); + meta["importSettings"]["bpp"] = settings.bpp; + meta["importSettings"]["chanels"] = settings.chanels; + meta["importSettings"]["name"] = settings.name; + + for (int i = 0; i < dependecies.size(); i++) + { + meta["dependencies"][i]["id"] = uuids::to_string(dependecies[i]->id); + meta["dependencies"][i]["path"] = dependecies[i]->rawPath; //todo: clamp to assets root + } + + + std::ofstream metaStream; + CreateFileWriting(metaStream, metaPath); + metaStream << meta.dump(4) << std::endl; + metaStream.close(); +} diff --git a/TSE_Core/src/elements/Texture.hpp b/TSE_Core/src/elements/Texture.hpp index 38c18f6..1368906 100644 --- a/TSE_Core/src/elements/Texture.hpp +++ b/TSE_Core/src/elements/Texture.hpp @@ -5,15 +5,31 @@ #include "Types.hpp" #include "Vector2.hpp" #include "interfaces/ITextureHelper.hpp" +#include "interfaces/IAsset.hpp" #define FREEIMAGE_LIB #include "FI/FreeImage.h" namespace TSE { - class Texture : public ITexture + struct TextureImportSettings + { + //0 = rawTexture; + //1 = sprite; + //2 = spriteset; ==> need to set import count to sprite counts + //3 = ninetile; + byte importMode = 0; + Vector2 importCount = {1,1}; + //bpp and chanels are set from import, but can be changed for custum effects, or memory layouts + uint bpp; + byte chanels; + string name = "Unnamed"; + }; + + class Texture : public ITexture, public IAsset { protected: + TextureImportSettings settings; uint TextureID = 0; Vector2 Size; uint Bpp; @@ -22,13 +38,13 @@ namespace TSE byte* imagePtr = nullptr; public: - string name = "Unnamed"; Texture(const string& path); Texture(const int& width, const int& height, int bpp = 32); Texture(const Vector2& size, int bpp = 32); ~Texture(); + string name() const; uint bpp() const; Vector2 size() const override; float Width() const override; @@ -59,5 +75,13 @@ namespace TSE void Apply(); void regist(); void PlatformDestroy(); + + bool LoadAsset(string& path) override; + void SaveAsset() override; + void Hash() override; + void UpdateImportSettings(string name, byte mode, Vector2 count); + + protected: + void SaveMeta() override; }; } // namespace TSE diff --git a/TSE_Core/src/interfaces/IAsset.hpp b/TSE_Core/src/interfaces/IAsset.hpp new file mode 100644 index 0000000..96e06dc --- /dev/null +++ b/TSE_Core/src/interfaces/IAsset.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "Types.hpp" +#include +#include "picosha2.h" +#include "uuid.h" + +namespace TSE +{ + class IAsset + { + public: + uuids::uuid id; + string rawPath; + string hash; + std::vector dependecies; + bool IsVirtualAsset = false; + virtual ~IAsset() = default; + + virtual bool LoadAsset(string& path) = 0; + virtual void SaveAsset() = 0; + virtual void Hash() = 0; + inline void Rescan() + { + string oldHash = string(hash); + Hash(); + if(oldHash != hash) + { + LoadAsset(rawPath); + } + }; + + protected: + virtual void SaveMeta() = 0; + inline string PathToMetaPath(string path) const + { + return path + ".meta"; + }; + + inline std::string sha256(const std::string& str) { + return picosha2::hash256_hex_string(str); + }; + }; +} \ No newline at end of file diff --git a/TSE_Core/src/interfaces/IShader.hpp b/TSE_Core/src/interfaces/IShader.hpp index 9019c17..917c1e3 100644 --- a/TSE_Core/src/interfaces/IShader.hpp +++ b/TSE_Core/src/interfaces/IShader.hpp @@ -10,6 +10,9 @@ namespace TSE { class IShader { + protected: + string name = "UnnamedShader"; + public: virtual void Bind() const = 0; @@ -24,6 +27,8 @@ namespace TSE virtual void SetUniform(const char* name, const Vector3* value) = 0; virtual void SetUniform(const char* name, const Vector4* value) = 0; + inline string GetName() {return name;}; + virtual ~IShader() = default; }; } // namespace TSE diff --git a/TSE_Core/src/utils/JsonExports.cpp b/TSE_Core/src/utils/JsonExports.cpp index 4499d1f..bd0272a 100644 --- a/TSE_Core/src/utils/JsonExports.cpp +++ b/TSE_Core/src/utils/JsonExports.cpp @@ -3,77 +3,60 @@ nlohmann::json TSE::ExportColor(Color &color) { using json = nlohmann::json; - json exp; - exp["r"] = color.r; - exp["g"] = color.g; - exp["b"] = color.b; - exp["a"] = color.a; - return exp; + return json::array({color.r, color.g, color.b, color.a}); } nlohmann::json TSE::ExportVector2(Vector2 &vec2) { using json = nlohmann::json; - json exp; - exp["x"] = vec2.x; - exp["y"] = vec2.y; - return exp; + return json::array({vec2.x, vec2.y}); } nlohmann::json TSE::ExportVector3(Vector3 &vec3) { using json = nlohmann::json; - json exp; - exp["x"] = vec3.x; - exp["y"] = vec3.y; - exp["z"] = vec3.z; - return exp; + return json::array({vec3.x, vec3.y, vec3.z}); } nlohmann::json TSE::ExportVector4(Vector4 &vec4) { using json = nlohmann::json; - json exp; - exp["x"] = vec4.x; - exp["y"] = vec4.y; - exp["z"] = vec4.z; - exp["w"] = vec4.w; - return exp; + return json::array({vec4.x, vec4.y, vec4.z, vec4.w}); } TSE::Color TSE::ImportColor(nlohmann::json json) { Color imp; - imp.r = json["r"]; - imp.g = json["g"]; - imp.b = json["b"]; - imp.a = json["a"]; + imp.r = json[0]; + imp.g = json[1]; + imp.b = json[2]; + imp.a = json[3]; return imp; } TSE::Vector2 TSE::ImportVector2(nlohmann::json json) { Vector2 imp; - imp.x = json["x"]; - imp.y = json["y"]; + imp.x = json[0]; + imp.y = json[1]; return imp; } TSE::Vector3 TSE::ImportVector3(nlohmann::json json) { Vector3 imp; - imp.x = json["x"]; - imp.y = json["y"]; - imp.z = json["z"]; + imp.x = json[0]; + imp.y = json[1]; + imp.z = json[2]; return imp; } TSE::Vector4 TSE::ImportVector4(nlohmann::json json) { Vector4 imp; - imp.x = json["x"]; - imp.y = json["y"]; - imp.z = json["z"]; - imp.w = json["w"]; + imp.x = json[0]; + imp.y = json[1]; + imp.z = json[2]; + imp.w = json[3]; return imp; } diff --git a/TSE_Editor/src/UI/ElementDrawer.cpp b/TSE_Editor/src/UI/ElementDrawer.cpp index 5f52e8c..9b52da3 100644 --- a/TSE_Editor/src/UI/ElementDrawer.cpp +++ b/TSE_Editor/src/UI/ElementDrawer.cpp @@ -1183,7 +1183,7 @@ namespace TSE::EDITOR ImGui::Image(element->GetTextureId(), texSize, {0,1}, {1,0}); ImGui::SameLine(); ImGui::SetCursorPos(ImVec2(field_size.y + 1,(60-label_size.y) / 2)); - ImGui::TextUnformatted(element->name.c_str()); + ImGui::TextUnformatted(element->name().c_str()); ImGui::EndChild(); @@ -1198,7 +1198,7 @@ namespace TSE::EDITOR } void ElementDrawer::DrawTextureNormal(Texture *element, const bool &debug, const std::string& label) { - ImGui::TextUnformatted(element->name.c_str()); + ImGui::TextUnformatted(element->name().c_str()); if(debug) { ImGui::Separator(); diff --git a/TSE_OpenGlImpl/src/shader/basicOrderedSpriteSetShader.cpp b/TSE_OpenGlImpl/src/shader/basicOrderedSpriteSetShader.cpp index cd68a6f..19fc738 100644 --- a/TSE_OpenGlImpl/src/shader/basicOrderedSpriteSetShader.cpp +++ b/TSE_OpenGlImpl/src/shader/basicOrderedSpriteSetShader.cpp @@ -46,6 +46,7 @@ void TSE::OpenGL::BasicOrderedSpriteSetShader::Init(float width, float height) TSE::OpenGL::BasicOrderedSpriteSetShader::BasicOrderedSpriteSetShader(std::vector> &&parts) : Shader(parts) { + name = "Basic Ordered Sprite Set Shader"; PackageSize = SHADER_PACKAGE_SIZE; } diff --git a/TSE_OpenGlImpl/src/shader/basicParticleShader.cpp b/TSE_OpenGlImpl/src/shader/basicParticleShader.cpp index 7905fa5..dd67db7 100644 --- a/TSE_OpenGlImpl/src/shader/basicParticleShader.cpp +++ b/TSE_OpenGlImpl/src/shader/basicParticleShader.cpp @@ -36,6 +36,7 @@ void TSE::OpenGL::BasicParticleShader::Init(float width, float height) TSE::OpenGL::BasicParticleShader::BasicParticleShader(std::vector> &&parts) : Shader(parts) { + name = "Basic Unlit Particle Shader"; PackageSize = SHADER_PACKAGE_SIZE; } diff --git a/TSE_OpenGlImpl/src/shader/basicShader.cpp b/TSE_OpenGlImpl/src/shader/basicShader.cpp index b88667b..57a3f7c 100644 --- a/TSE_OpenGlImpl/src/shader/basicShader.cpp +++ b/TSE_OpenGlImpl/src/shader/basicShader.cpp @@ -35,6 +35,7 @@ void TSE::OpenGL::BasicShader::Init(float width, float height) TSE::OpenGL::BasicShader::BasicShader(std::vector> &&parts) : Shader(parts) { + name = "Basic Unlit Shader"; PackageSize = SHADER_PACKAGE_SIZE; } diff --git a/TSE_OpenGlImpl/src/shader/basicTextureShader.cpp b/TSE_OpenGlImpl/src/shader/basicTextureShader.cpp index e5d5674..2bcd66f 100644 --- a/TSE_OpenGlImpl/src/shader/basicTextureShader.cpp +++ b/TSE_OpenGlImpl/src/shader/basicTextureShader.cpp @@ -43,6 +43,7 @@ void TSE::OpenGL::BasicTextureShader::Init(float width, float height) TSE::OpenGL::BasicTextureShader::BasicTextureShader(std::vector> &&parts) : Shader(parts) { + name = "Basic Unlit Texture Shader"; PackageSize = SHADER_PACKAGE_SIZE; } diff --git a/TSE_OpenGlImpl/src/shader/basicTileMapShader.cpp b/TSE_OpenGlImpl/src/shader/basicTileMapShader.cpp index 057c1be..2deae9e 100644 --- a/TSE_OpenGlImpl/src/shader/basicTileMapShader.cpp +++ b/TSE_OpenGlImpl/src/shader/basicTileMapShader.cpp @@ -39,6 +39,7 @@ void TSE::OpenGL::BasicTileMapShader::Init(float width, float height) TSE::OpenGL::BasicTileMapShader::BasicTileMapShader(std::vector> &&parts) : Shader(parts) { + name = "Basic Unlit TileMap Shader"; PackageSize = SHADER_PACKAGE_SIZE; } diff --git a/TSE_OpenGlImpl/src/shader/defaultShaderHandler.cpp b/TSE_OpenGlImpl/src/shader/defaultShaderHandler.cpp index 93969e3..07fc603 100644 --- a/TSE_OpenGlImpl/src/shader/defaultShaderHandler.cpp +++ b/TSE_OpenGlImpl/src/shader/defaultShaderHandler.cpp @@ -15,12 +15,12 @@ void TSE::OpenGL::LoadBasicShaders(float width, float height) BasicParticleShader::Init(width, height); BasicTileMapShader::Init(width, height); BasicOrderedSpriteSetShader::Init(width, height); - ShaderRegistry::SetShader("Basic Unlit Shader", BasicShader::Instance()); - ShaderRegistry::SetShader("Basic Unlit Texture Shader", BasicTextureShader::Instance()); - ShaderRegistry::SetShader("Basic Unlit Dithering Shader", DitheringShader::Instance()); - ShaderRegistry::SetShader("Basic Unlit Particle Shader", BasicParticleShader::Instance()); - ShaderRegistry::SetShader("Basic Unlit TileMap Shader", BasicTileMapShader::Instance()); - ShaderRegistry::SetShader("Basic Ordered Sprite Set Shader", BasicOrderedSpriteSetShader::Instance()); + ShaderRegistry::SetShader(BasicShader::Instance()); + ShaderRegistry::SetShader(BasicTextureShader::Instance()); + ShaderRegistry::SetShader(DitheringShader::Instance()); + ShaderRegistry::SetShader(BasicParticleShader::Instance()); + ShaderRegistry::SetShader(BasicTileMapShader::Instance()); + ShaderRegistry::SetShader(BasicOrderedSpriteSetShader::Instance()); } void TSE::OpenGL::UnLoadBasicShaders() diff --git a/TSE_OpenGlImpl/src/shader/ditheringShader.cpp b/TSE_OpenGlImpl/src/shader/ditheringShader.cpp index d228368..5855369 100644 --- a/TSE_OpenGlImpl/src/shader/ditheringShader.cpp +++ b/TSE_OpenGlImpl/src/shader/ditheringShader.cpp @@ -34,6 +34,7 @@ void TSE::OpenGL::DitheringShader::Init(float width, float height) TSE::OpenGL::DitheringShader::DitheringShader(std::vector> &&parts) : Shader(parts) { + name = "Basic Unlit Dithering Shader"; PackageSize = SHADER_PACKAGE_SIZE; }