diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..170a09c --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/clang++", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "clang-x64" + } + ], + "version": 4 +} diff --git a/TSE_Base/include/uuid.h b/TSE_Base/include/uuid.h new file mode 100644 index 0000000..1e331db --- /dev/null +++ b/TSE_Base/include/uuid.h @@ -0,0 +1,967 @@ +#ifndef STDUUID_H +#define STDUUID_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus + +# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +# define LIBUUID_CPP20_OR_GREATER +# endif + +#endif + + +#ifdef LIBUUID_CPP20_OR_GREATER +#include +#else +#include +#endif + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#ifdef UUID_TIME_GENERATOR +#include +#pragma comment(lib, "IPHLPAPI.lib") +#endif + +#elif defined(__linux__) || defined(__unix__) + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#elif defined(__APPLE__) + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#endif + +namespace uuids +{ +#ifdef __cpp_lib_span + template + using span = std::span; +#else + template + using span = gsl::span; +#endif + + namespace detail + { + template + [[nodiscard]] constexpr inline unsigned char hex2char(TChar const ch) noexcept + { + if (ch >= static_cast('0') && ch <= static_cast('9')) + return static_cast(ch - static_cast('0')); + if (ch >= static_cast('a') && ch <= static_cast('f')) + return static_cast(10 + ch - static_cast('a')); + if (ch >= static_cast('A') && ch <= static_cast('F')) + return static_cast(10 + ch - static_cast('A')); + return 0; + } + + template + [[nodiscard]] constexpr inline bool is_hex(TChar const ch) noexcept + { + return + (ch >= static_cast('0') && ch <= static_cast('9')) || + (ch >= static_cast('a') && ch <= static_cast('f')) || + (ch >= static_cast('A') && ch <= static_cast('F')); + } + + template + [[nodiscard]] constexpr std::basic_string_view to_string_view(TChar const * str) noexcept + { + if (str) return str; + return {}; + } + + template + [[nodiscard]] + constexpr std::basic_string_view< + typename StringType::value_type, + typename StringType::traits_type> + to_string_view(StringType const & str) noexcept + { + return str; + } + + class sha1 + { + public: + using digest32_t = uint32_t[5]; + using digest8_t = uint8_t[20]; + + static constexpr unsigned int block_bytes = 64; + + [[nodiscard]] inline static uint32_t left_rotate(uint32_t value, size_t const count) noexcept + { + return (value << count) ^ (value >> (32 - count)); + } + + sha1() { reset(); } + + void reset() noexcept + { + m_digest[0] = 0x67452301; + m_digest[1] = 0xEFCDAB89; + m_digest[2] = 0x98BADCFE; + m_digest[3] = 0x10325476; + m_digest[4] = 0xC3D2E1F0; + m_blockByteIndex = 0; + m_byteCount = 0; + } + + void process_byte(uint8_t octet) + { + this->m_block[this->m_blockByteIndex++] = octet; + ++this->m_byteCount; + if (m_blockByteIndex == block_bytes) + { + this->m_blockByteIndex = 0; + process_block(); + } + } + + void process_block(void const * const start, void const * const end) + { + const uint8_t* begin = static_cast(start); + const uint8_t* finish = static_cast(end); + while (begin != finish) + { + process_byte(*begin); + begin++; + } + } + + void process_bytes(void const * const data, size_t const len) + { + const uint8_t* block = static_cast(data); + process_block(block, block + len); + } + + uint32_t const * get_digest(digest32_t digest) + { + size_t const bitCount = this->m_byteCount * 8; + process_byte(0x80); + if (this->m_blockByteIndex > 56) { + while (m_blockByteIndex != 0) { + process_byte(0); + } + while (m_blockByteIndex < 56) { + process_byte(0); + } + } + else { + while (m_blockByteIndex < 56) { + process_byte(0); + } + } + process_byte(0); + process_byte(0); + process_byte(0); + process_byte(0); + process_byte(static_cast((bitCount >> 24) & 0xFF)); + process_byte(static_cast((bitCount >> 16) & 0xFF)); + process_byte(static_cast((bitCount >> 8) & 0xFF)); + process_byte(static_cast((bitCount) & 0xFF)); + + memcpy(digest, m_digest, 5 * sizeof(uint32_t)); + return digest; + } + + uint8_t const * get_digest_bytes(digest8_t digest) + { + digest32_t d32; + get_digest(d32); + size_t di = 0; + digest[di++] = static_cast(d32[0] >> 24); + digest[di++] = static_cast(d32[0] >> 16); + digest[di++] = static_cast(d32[0] >> 8); + digest[di++] = static_cast(d32[0] >> 0); + + digest[di++] = static_cast(d32[1] >> 24); + digest[di++] = static_cast(d32[1] >> 16); + digest[di++] = static_cast(d32[1] >> 8); + digest[di++] = static_cast(d32[1] >> 0); + + digest[di++] = static_cast(d32[2] >> 24); + digest[di++] = static_cast(d32[2] >> 16); + digest[di++] = static_cast(d32[2] >> 8); + digest[di++] = static_cast(d32[2] >> 0); + + digest[di++] = static_cast(d32[3] >> 24); + digest[di++] = static_cast(d32[3] >> 16); + digest[di++] = static_cast(d32[3] >> 8); + digest[di++] = static_cast(d32[3] >> 0); + + digest[di++] = static_cast(d32[4] >> 24); + digest[di++] = static_cast(d32[4] >> 16); + digest[di++] = static_cast(d32[4] >> 8); + digest[di++] = static_cast(d32[4] >> 0); + + return digest; + } + + private: + void process_block() + { + uint32_t w[80]; + for (size_t i = 0; i < 16; i++) { + w[i] = static_cast(m_block[i * 4 + 0] << 24); + w[i] |= static_cast(m_block[i * 4 + 1] << 16); + w[i] |= static_cast(m_block[i * 4 + 2] << 8); + w[i] |= static_cast(m_block[i * 4 + 3]); + } + for (size_t i = 16; i < 80; i++) { + w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); + } + + uint32_t a = m_digest[0]; + uint32_t b = m_digest[1]; + uint32_t c = m_digest[2]; + uint32_t d = m_digest[3]; + uint32_t e = m_digest[4]; + + for (std::size_t i = 0; i < 80; ++i) + { + uint32_t f = 0; + uint32_t k = 0; + + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5A827999; + } + else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } + else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } + else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = left_rotate(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = left_rotate(b, 30); + b = a; + a = temp; + } + + m_digest[0] += a; + m_digest[1] += b; + m_digest[2] += c; + m_digest[3] += d; + m_digest[4] += e; + } + + private: + digest32_t m_digest; + uint8_t m_block[64]; + size_t m_blockByteIndex; + size_t m_byteCount; + }; + + template + inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; + + template <> + inline constexpr wchar_t empty_guid[37] = L"00000000-0000-0000-0000-000000000000"; + + template + inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; + + template <> + inline constexpr wchar_t guid_encoder[17] = L"0123456789abcdef"; + } + + // -------------------------------------------------------------------------------------------------------------------------- + // UUID format https://tools.ietf.org/html/rfc4122 + // -------------------------------------------------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------------------------------------------------- + // Field NDR Data Type Octet # Note + // -------------------------------------------------------------------------------------------------------------------------- + // time_low unsigned long 0 - 3 The low field of the timestamp. + // time_mid unsigned short 4 - 5 The middle field of the timestamp. + // time_hi_and_version unsigned short 6 - 7 The high field of the timestamp multiplexed with the version number. + // clock_seq_hi_and_reserved unsigned small 8 The high field of the clock sequence multiplexed with the variant. + // clock_seq_low unsigned small 9 The low field of the clock sequence. + // node character 10 - 15 The spatially unique node identifier. + // -------------------------------------------------------------------------------------------------------------------------- + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time_low | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time_mid | time_hi_and_version | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |clk_seq_hi_res | clk_seq_low | node (0-1) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | node (2-5) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // -------------------------------------------------------------------------------------------------------------------------- + // enumerations + // -------------------------------------------------------------------------------------------------------------------------- + + // indicated by a bit pattern in octet 8, marked with N in xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx + enum class uuid_variant + { + // NCS backward compatibility (with the obsolete Apollo Network Computing System 1.5 UUID format) + // N bit pattern: 0xxx + // > the first 6 octets of the UUID are a 48-bit timestamp (the number of 4 microsecond units of time since 1 Jan 1980 UTC); + // > the next 2 octets are reserved; + // > the next octet is the "address family"; + // > the final 7 octets are a 56-bit host ID in the form specified by the address family + ncs, + + // RFC 4122/DCE 1.1 + // N bit pattern: 10xx + // > big-endian byte order + rfc, + + // Microsoft Corporation backward compatibility + // N bit pattern: 110x + // > little endian byte order + // > formely used in the Component Object Model (COM) library + microsoft, + + // reserved for possible future definition + // N bit pattern: 111x + reserved + }; + + // indicated by a bit pattern in octet 6, marked with M in xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx + enum class uuid_version + { + none = 0, // only possible for nil or invalid uuids + time_based = 1, // The time-based version specified in RFC 4122 + dce_security = 2, // DCE Security version, with embedded POSIX UIDs. + name_based_md5 = 3, // The name-based version specified in RFS 4122 with MD5 hashing + random_number_based = 4, // The randomly or pseudo-randomly generated version specified in RFS 4122 + name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing + }; + + // Forward declare uuid & to_string so that we can declare to_string as a friend later. + class uuid; + template , + class Allocator = std::allocator> + std::basic_string to_string(uuid const &id); + + // -------------------------------------------------------------------------------------------------------------------------- + // uuid class + // -------------------------------------------------------------------------------------------------------------------------- + class uuid + { + public: + using value_type = uint8_t; + + constexpr uuid() noexcept = default; + + uuid(value_type(&arr)[16]) noexcept + { + std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); + } + + constexpr uuid(std::array const & arr) noexcept : data{arr} {} + + explicit uuid(span bytes) + { + std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); + } + + template + explicit uuid(ForwardIterator first, ForwardIterator last) + { + if (std::distance(first, last) == 16) + std::copy(first, last, std::begin(data)); + } + + [[nodiscard]] constexpr uuid_variant variant() const noexcept + { + if ((data[8] & 0x80) == 0x00) + return uuid_variant::ncs; + else if ((data[8] & 0xC0) == 0x80) + return uuid_variant::rfc; + else if ((data[8] & 0xE0) == 0xC0) + return uuid_variant::microsoft; + else + return uuid_variant::reserved; + } + + [[nodiscard]] constexpr uuid_version version() const noexcept + { + if ((data[6] & 0xF0) == 0x10) + return uuid_version::time_based; + else if ((data[6] & 0xF0) == 0x20) + return uuid_version::dce_security; + else if ((data[6] & 0xF0) == 0x30) + return uuid_version::name_based_md5; + else if ((data[6] & 0xF0) == 0x40) + return uuid_version::random_number_based; + else if ((data[6] & 0xF0) == 0x50) + return uuid_version::name_based_sha1; + else + return uuid_version::none; + } + + [[nodiscard]] constexpr bool is_nil() const noexcept + { + for (size_t i = 0; i < data.size(); ++i) if (data[i] != 0) return false; + return true; + } + + void swap(uuid & other) noexcept + { + data.swap(other.data); + } + + [[nodiscard]] inline span as_bytes() const + { + return span(reinterpret_cast(data.data()), 16); + } + + template + [[nodiscard]] constexpr static bool is_valid_uuid(StringType const & in_str) noexcept + { + auto str = detail::to_string_view(in_str); + bool firstDigit = true; + size_t hasBraces = 0; + size_t index = 0; + + if (str.empty()) + return false; + + if (str.front() == '{') + hasBraces = 1; + if (hasBraces && str.back() != '}') + return false; + + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) + { + if (str[i] == '-') continue; + + if (index >= 16 || !detail::is_hex(str[i])) + { + return false; + } + + if (firstDigit) + { + firstDigit = false; + } + else + { + index++; + firstDigit = true; + } + } + + if (index < 16) + { + return false; + } + + return true; + } + + template + [[nodiscard]] constexpr static std::optional from_string(StringType const & in_str) noexcept + { + auto str = detail::to_string_view(in_str); + bool firstDigit = true; + size_t hasBraces = 0; + size_t index = 0; + + std::array data{ { 0 } }; + + if (str.empty()) return {}; + + if (str.front() == '{') + hasBraces = 1; + if (hasBraces && str.back() != '}') + return {}; + + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) + { + if (str[i] == '-') continue; + + if (index >= 16 || !detail::is_hex(str[i])) + { + return {}; + } + + if (firstDigit) + { + data[index] = static_cast(detail::hex2char(str[i]) << 4); + firstDigit = false; + } + else + { + data[index] = static_cast(data[index] | detail::hex2char(str[i])); + index++; + firstDigit = true; + } + } + + if (index < 16) + { + return {}; + } + + return uuid{ data }; + } + + private: + std::array data{ { 0 } }; + + friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; + friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; + + template + friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); + + template + friend std::basic_string to_string(uuid const& id); + + friend std::hash; + }; + + // -------------------------------------------------------------------------------------------------------------------------- + // operators and non-member functions + // -------------------------------------------------------------------------------------------------------------------------- + + [[nodiscard]] inline bool operator== (uuid const& lhs, uuid const& rhs) noexcept + { + return lhs.data == rhs.data; + } + + [[nodiscard]] inline bool operator!= (uuid const& lhs, uuid const& rhs) noexcept + { + return !(lhs == rhs); + } + + [[nodiscard]] inline bool operator< (uuid const& lhs, uuid const& rhs) noexcept + { + return lhs.data < rhs.data; + } + + template + [[nodiscard]] inline std::basic_string to_string(uuid const & id) + { + std::basic_string uustr{detail::empty_guid}; + + for (size_t i = 0, index = 0; i < 36; ++i) + { + if (i == 8 || i == 13 || i == 18 || i == 23) + { + continue; + } + uustr[i] = detail::guid_encoder[id.data[index] >> 4 & 0x0f]; + uustr[++i] = detail::guid_encoder[id.data[index] & 0x0f]; + index++; + } + + return uustr; + } + + template + std::basic_ostream& operator<<(std::basic_ostream& s, uuid const& id) + { + s << to_string(id); + return s; + } + + inline void swap(uuids::uuid & lhs, uuids::uuid & rhs) noexcept + { + lhs.swap(rhs); + } + + // -------------------------------------------------------------------------------------------------------------------------- + // namespace IDs that could be used for generating name-based uuids + // -------------------------------------------------------------------------------------------------------------------------- + + // Name string is a fully-qualified domain name + static uuid uuid_namespace_dns{ {0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; + + // Name string is a URL + static uuid uuid_namespace_url{ {0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; + + // Name string is an ISO OID (See https://oidref.com/, https://en.wikipedia.org/wiki/Object_identifier) + static uuid uuid_namespace_oid{ {0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; + + // Name string is an X.500 DN, in DER or a text output format (See https://en.wikipedia.org/wiki/X.500, https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One) + static uuid uuid_namespace_x500{ {0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; + + // -------------------------------------------------------------------------------------------------------------------------- + // uuid generators + // -------------------------------------------------------------------------------------------------------------------------- + +#ifdef UUID_SYSTEM_GENERATOR + class uuid_system_generator + { + public: + using result_type = uuid; + + uuid operator()() + { +#ifdef _WIN32 + + GUID newId; + HRESULT hr = ::CoCreateGuid(&newId); + + if (FAILED(hr)) + { + throw std::system_error(hr, std::system_category(), "CoCreateGuid failed"); + } + + std::array bytes = + { { + static_cast((newId.Data1 >> 24) & 0xFF), + static_cast((newId.Data1 >> 16) & 0xFF), + static_cast((newId.Data1 >> 8) & 0xFF), + static_cast((newId.Data1) & 0xFF), + + (unsigned char)((newId.Data2 >> 8) & 0xFF), + (unsigned char)((newId.Data2) & 0xFF), + + (unsigned char)((newId.Data3 >> 8) & 0xFF), + (unsigned char)((newId.Data3) & 0xFF), + + newId.Data4[0], + newId.Data4[1], + newId.Data4[2], + newId.Data4[3], + newId.Data4[4], + newId.Data4[5], + newId.Data4[6], + newId.Data4[7] + } }; + + return uuid{ std::begin(bytes), std::end(bytes) }; + +#elif defined(__linux__) || defined(__unix__) + + uuid_t id; + uuid_generate(id); + + std::array bytes = + { { + id[0], + id[1], + id[2], + id[3], + id[4], + id[5], + id[6], + id[7], + id[8], + id[9], + id[10], + id[11], + id[12], + id[13], + id[14], + id[15] + } }; + + return uuid{ std::begin(bytes), std::end(bytes) }; + +#elif defined(__APPLE__) + auto newId = CFUUIDCreate(NULL); + auto bytes = CFUUIDGetUUIDBytes(newId); + CFRelease(newId); + + std::array arrbytes = + { { + bytes.byte0, + bytes.byte1, + bytes.byte2, + bytes.byte3, + bytes.byte4, + bytes.byte5, + bytes.byte6, + bytes.byte7, + bytes.byte8, + bytes.byte9, + bytes.byte10, + bytes.byte11, + bytes.byte12, + bytes.byte13, + bytes.byte14, + bytes.byte15 + } }; + return uuid{ std::begin(arrbytes), std::end(arrbytes) }; +#else + return uuid{}; +#endif + } + }; +#endif + + template + class basic_uuid_random_generator + { + public: + using engine_type = UniformRandomNumberGenerator; + + explicit basic_uuid_random_generator(engine_type& gen) : + generator(&gen, [](auto) {}) {} + explicit basic_uuid_random_generator(engine_type* gen) : + generator(gen, [](auto) {}) {} + + [[nodiscard]] uuid operator()() + { + alignas(uint32_t) uint8_t bytes[16]; + for (int i = 0; i < 16; i += 4) + *reinterpret_cast(bytes + i) = distribution(*generator); + + // variant must be 10xxxxxx + bytes[8] &= 0xBF; + bytes[8] |= 0x80; + + // version must be 0100xxxx + bytes[6] &= 0x4F; + bytes[6] |= 0x40; + + return uuid{std::begin(bytes), std::end(bytes)}; + } + + private: + std::uniform_int_distribution distribution; + std::shared_ptr generator; + }; + + using uuid_random_generator = basic_uuid_random_generator; + + class uuid_name_generator + { + public: + explicit uuid_name_generator(uuid const& namespace_uuid) noexcept + : nsuuid(namespace_uuid) + {} + + template + [[nodiscard]] uuid operator()(StringType const & name) + { + reset(); + process_characters(detail::to_string_view(name)); + return make_uuid(); + } + + private: + void reset() + { + hasher.reset(); + std::byte bytes[16]; + auto nsbytes = nsuuid.as_bytes(); + std::copy(std::cbegin(nsbytes), std::cend(nsbytes), bytes); + hasher.process_bytes(bytes, 16); + } + + template + void process_characters(std::basic_string_view const str) + { + for (uint32_t c : str) + { + hasher.process_byte(static_cast(c & 0xFF)); + if constexpr (!std::is_same_v) + { + hasher.process_byte(static_cast((c >> 8) & 0xFF)); + hasher.process_byte(static_cast((c >> 16) & 0xFF)); + hasher.process_byte(static_cast((c >> 24) & 0xFF)); + } + } + } + + [[nodiscard]] uuid make_uuid() + { + detail::sha1::digest8_t digest; + hasher.get_digest_bytes(digest); + + // variant must be 0b10xxxxxx + digest[8] &= 0xBF; + digest[8] |= 0x80; + + // version must be 0b0101xxxx + digest[6] &= 0x5F; + digest[6] |= 0x50; + + return uuid{ digest, digest + 16 }; + } + + private: + uuid nsuuid; + detail::sha1 hasher; + }; + +#ifdef UUID_TIME_GENERATOR + // !!! DO NOT USE THIS IN PRODUCTION + // this implementation is unreliable for good uuids + class uuid_time_generator + { + using mac_address = std::array; + + std::optional device_address; + + [[nodiscard]] bool get_mac_address() + { + if (device_address.has_value()) + { + return true; + } + +#ifdef _WIN32 + DWORD len = 0; + auto ret = GetAdaptersInfo(nullptr, &len); + if (ret != ERROR_BUFFER_OVERFLOW) return false; + std::vector buf(len); + auto pips = reinterpret_cast(&buf.front()); + ret = GetAdaptersInfo(pips, &len); + if (ret != ERROR_SUCCESS) return false; + mac_address addr; + std::copy(pips->Address, pips->Address + 6, std::begin(addr)); + device_address = addr; +#endif + + return device_address.has_value(); + } + + [[nodiscard]] long long get_time_intervals() + { + auto start = std::chrono::system_clock::from_time_t(time_t(-12219292800)); + auto diff = std::chrono::system_clock::now() - start; + auto ns = std::chrono::duration_cast(diff).count(); + return ns / 100; + } + + [[nodiscard]] static unsigned short get_clock_sequence() + { + static std::mt19937 clock_gen(std::random_device{}()); + static std::uniform_int_distribution clock_dis; + static std::atomic_ushort clock_sequence = clock_dis(clock_gen); + return clock_sequence++; + } + + public: + [[nodiscard]] uuid operator()() + { + if (get_mac_address()) + { + std::array data; + + auto tm = get_time_intervals(); + + auto clock_seq = get_clock_sequence(); + + auto ptm = reinterpret_cast(&tm); + + memcpy(&data[0], ptm + 4, 4); + memcpy(&data[4], ptm + 2, 2); + memcpy(&data[6], ptm, 2); + + memcpy(&data[8], &clock_seq, 2); + + // variant must be 0b10xxxxxx + data[8] &= 0xBF; + data[8] |= 0x80; + + // version must be 0b0001xxxx + data[6] &= 0x1F; + data[6] |= 0x10; + + memcpy(&data[10], &device_address.value()[0], 6); + + return uuids::uuid{std::cbegin(data), std::cend(data)}; + } + + return {}; + } + }; +#endif +} + +namespace std +{ + template <> + struct hash + { + using argument_type = uuids::uuid; + using result_type = std::size_t; + + [[nodiscard]] result_type operator()(argument_type const &uuid) const + { +#ifdef UUID_HASH_STRING_BASED + std::hash hasher; + return static_cast(hasher(uuids::to_string(uuid))); +#else + uint64_t l = + static_cast(uuid.data[0]) << 56 | + static_cast(uuid.data[1]) << 48 | + static_cast(uuid.data[2]) << 40 | + static_cast(uuid.data[3]) << 32 | + static_cast(uuid.data[4]) << 24 | + static_cast(uuid.data[5]) << 16 | + static_cast(uuid.data[6]) << 8 | + static_cast(uuid.data[7]); + uint64_t h = + static_cast(uuid.data[8]) << 56 | + static_cast(uuid.data[9]) << 48 | + static_cast(uuid.data[10]) << 40 | + static_cast(uuid.data[11]) << 32 | + static_cast(uuid.data[12]) << 24 | + static_cast(uuid.data[13]) << 16 | + static_cast(uuid.data[14]) << 8 | + static_cast(uuid.data[15]); + + if constexpr (sizeof(result_type) > 4) + { + return result_type(l ^ h); + } + else + { + uint64_t hash64 = l ^ h; + return result_type(uint32_t(hash64 >> 32) ^ uint32_t(hash64)); + } +#endif + } + }; +} + +#endif /* STDUUID_H */ diff --git a/TSE_Base/src/IdGenerator.cpp b/TSE_Base/src/IdGenerator.cpp new file mode 100644 index 0000000..a730631 --- /dev/null +++ b/TSE_Base/src/IdGenerator.cpp @@ -0,0 +1,9 @@ +#include "IdGenerator.hpp" + +std::mt19937 rnd = std::mt19937(); +auto gen = uuids::uuid_random_generator(rnd); + +uuids::uuid TSE::GenerateRandomUUID() +{ + return gen(); +} \ No newline at end of file diff --git a/TSE_Base/src/IdGenerator.hpp b/TSE_Base/src/IdGenerator.hpp new file mode 100644 index 0000000..4e3ce8a --- /dev/null +++ b/TSE_Base/src/IdGenerator.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "uuid.h" + +namespace TSE +{ + uuids::uuid GenerateRandomUUID(); +} // namespace TSE diff --git a/TSE_Core/src/BehaviourScripts/MeshContainer.cpp b/TSE_Core/src/BehaviourScripts/MeshContainer.cpp new file mode 100644 index 0000000..67e1b30 --- /dev/null +++ b/TSE_Core/src/BehaviourScripts/MeshContainer.cpp @@ -0,0 +1,16 @@ +#include "MeshContainer.hpp" + +TSE::MeshContainer::MeshContainer() = default; + +TSE::MeshContainer::MeshContainer(Mesh *m) +: mesh(m) {} + +void TSE::MeshContainer::SetMesh(Mesh *m) +{ + mesh = m; +} + +TSE::Mesh *TSE::MeshContainer::GetMesh() const +{ + return mesh; +} diff --git a/TSE_Core/src/BehaviourScripts/MeshContainer.hpp b/TSE_Core/src/BehaviourScripts/MeshContainer.hpp new file mode 100644 index 0000000..f611781 --- /dev/null +++ b/TSE_Core/src/BehaviourScripts/MeshContainer.hpp @@ -0,0 +1,28 @@ +#pragma once + +#define MESH_CONTAINER typeid(TSE::MeshContainer).name() + +#include "Types.hpp" +#include "Mesh.hpp" +#include "elements/BehaviourScript.hpp" + +namespace TSE +{ + class MeshContainer : public BehaviourScript + { + private: + Mesh* mesh = nullptr; + + public: + MeshContainer(); + explicit MeshContainer(Mesh* mesh); + + void SetMesh(Mesh* mesh); + Mesh* GetMesh() const; + + inline const char* GetName() override + { + return "Mesh Container"; + }; + }; +} // namespace TSE diff --git a/TSE_Core/src/BehaviourScripts/RectBase.cpp b/TSE_Core/src/BehaviourScripts/RectBase.cpp new file mode 100644 index 0000000..8d76f21 --- /dev/null +++ b/TSE_Core/src/BehaviourScripts/RectBase.cpp @@ -0,0 +1,92 @@ +#include "RectBase.hpp" + +#include "elements/Transformable.hpp" + +TSE::RectBase::RectBase() +{ + meshCach = Mesh::GetQuadMesh(); +} + +TSE::Mesh *TSE::RectBase::GetMesh() +{ + return &meshCach; +} + +void TSE::RectBase::SetCustomUVs(const std::vector &uvs) +{ + meshCach.uvs = uvs; +} + +void TSE::RectBase::OnParentChanged(RectBase *parent) +{ + if(anchors != Rect(0.5f,0.5f, 0.5f, 0.5f)) + RecalculateMesh(parent); +} + +bool IsTransformableRect(TSE::Transformable* trans) +{ + if(trans == nullptr || !trans->HasBehaviourScript(RECT_BASE)) return false; + return true; +} + +void TSE::RectBase::RecalculateMesh(RectBase *parent) +{ + // Determin parent size, or fallback to 1x1 + Vector2 psize (1,1); + if(parent != nullptr) + psize = parent->tmpSize; + Vector2 phsize = psize * 0.5f; + + + Vector2 pposoffset (0,0); + if(parent != nullptr) + { + pposoffset = ((parent->pivit * 2) - Vector2::one) * phsize; + } + + Vector2 mappedAnchor = (Vector2::Midpoint(anchors.p1,anchors.p2) * 2 - Vector2::one) * phsize; + pposoffset = pposoffset + mappedAnchor; + + Vector2 strechscaler = anchors.p2 - anchors.p1; + + tmpSize = psize * strechscaler + size; + + Vector2 hs = tmpSize * 0.5f; + + Vector2 mappedPivit = pivit * 2 - Vector2::one; + Vector2 pivitOffset = hs * mappedPivit; + + meshCach.vertecies.clear(); + meshCach.vertecies.push_back(Vector2(-hs.x, -hs.y) + pivitOffset); + meshCach.vertecies.push_back(Vector2( hs.x, -hs.y) + pivitOffset); + meshCach.vertecies.push_back(Vector2( hs.x, hs.y) + pivitOffset); + meshCach.vertecies.push_back(Vector2(-hs.x, hs.y) + pivitOffset); + + + baseObject->position = pposoffset; + + lastSize = size; + lastAnchors = anchors; + lastPivit = pivit; + + // Notify children of change + auto children = baseObject->GetChildren(); + for(auto child : children) + { + if(IsTransformableRect(child)) + { + ((RectBase*)child->GetBehaviourScript(RECT_BASE))->OnParentChanged(this); + } + } +} + +void TSE::RectBase::OnUpdate() +{ + if(lastSize != size || lastAnchors != anchors || pivit != lastPivit){ + //local change + RectBase* parent = nullptr; + if(baseObject->GetParent() != nullptr) + parent = (RectBase*)baseObject->GetParent()->GetBehaviourScript(RECT_BASE); + RecalculateMesh(parent); + } +} diff --git a/TSE_Core/src/BehaviourScripts/RectBase.hpp b/TSE_Core/src/BehaviourScripts/RectBase.hpp new file mode 100644 index 0000000..4c6decd --- /dev/null +++ b/TSE_Core/src/BehaviourScripts/RectBase.hpp @@ -0,0 +1,42 @@ +#pragma once + +#define RECT_BASE typeid(TSE::RectBase).name() + +#include "elements/BehaviourScript.hpp" +#include "Rect.hpp" +#include "Vector2.hpp" +#include "Mesh.hpp" + +namespace TSE +{ + class RectBase : public BehaviourScript + { + private: + Rect lastAnchors = Rect(0.5f, 0.5f, 0.5f, 0.5f); + Vector2 lastSize = Vector2(0,0); + Vector2 lastPivit = Vector2(0.5f,0.5f); + Mesh meshCach; + + Vector2 tmpSize; + + public: + Rect anchors = Rect(0.5f,0.5f, 0.5f, 0.5f); //streching in parent + Vector2 size = Vector2(1,1); //size + Vector2 pivit = Vector2(0.5f,0.5f); //positioning in parent + + RectBase(); + Mesh* GetMesh(); + void SetCustomUVs(const std::vector& uvs); + + private: + void OnParentChanged(RectBase* parent); + void RecalculateMesh(RectBase *parent); + + public: + void OnUpdate() override; + inline const char* GetName() override + { + return "Rect Base"; + } + }; +} // namespace TSE diff --git a/TSE_Core/src/BehaviourScripts/Renderable.cpp b/TSE_Core/src/BehaviourScripts/Renderable.cpp new file mode 100644 index 0000000..eb68237 --- /dev/null +++ b/TSE_Core/src/BehaviourScripts/Renderable.cpp @@ -0,0 +1,42 @@ +#include "Renderable.hpp" + +#include +#include "MeshContainer.hpp" +#include "elements/Transformable.hpp" + +namespace TSE +{ + Renderable::Renderable() = default; + + Renderable::Renderable(Material* mat) + { + SetMaterial(mat); + } + + void Renderable::SetMaterial(Material* mat) { + material = mat; + } + + Material* Renderable::GetMaterial() { + return material; + } + + Mesh* Renderable::GetMeshContainer() const { + if(baseObject->HasBehaviourScript(MESH_CONTAINER)) + return dynamic_cast(baseObject->GetBehaviourScript(MESH_CONTAINER))->GetMesh(); + // if(baseObject->HasBehaviourScript(RECT_BASE)) + // return dynamic_cast(baseObject->GetBehaviourScript(RECT_BASE))->GetMesh(); + return nullptr; + } + + const Vector3* Renderable::GetVertices() const { + auto* container = GetMeshContainer(); + return container ? container->vertecies.data() : nullptr; + } + + const Vector2* Renderable::GetUVs() const { + auto* container = GetMeshContainer(); + return container ? container->uvs.data() : nullptr; + } +} // namespace TSE + diff --git a/TSE_Core/src/BehaviourScripts/Renderable.hpp b/TSE_Core/src/BehaviourScripts/Renderable.hpp new file mode 100644 index 0000000..f9f6395 --- /dev/null +++ b/TSE_Core/src/BehaviourScripts/Renderable.hpp @@ -0,0 +1,35 @@ +#pragma once + +#define RENDERABLE typeid(TSE::Renderable).name() + +#include "elements/Material.hpp" +#include "interfaces/IRenderable.hpp" +#include "elements/BehaviourScript.hpp" +#include "Mesh.hpp" + +namespace TSE +{ + class Renderable : public BehaviourScript, public IRenderable + { + private: + Material* material = nullptr; + + public: + Renderable(); + explicit Renderable(Material* material); + + const Vector3* GetVertices() const override; + const Vector2* GetUVs() const override; + const std::vector GetIndices() const override; + size_t GetVertexCount() const override; + inline const char* GetName() override + { + return "Renderable"; + } + void SetMaterial(Material* material); + Material* GetMaterial(); + + private: + Mesh* GetMeshContainer() const; + }; +} // namespace TSE diff --git a/TSE_Core/src/elements/BehaviourScript.cpp b/TSE_Core/src/elements/BehaviourScript.cpp new file mode 100644 index 0000000..8ec7526 --- /dev/null +++ b/TSE_Core/src/elements/BehaviourScript.cpp @@ -0,0 +1,23 @@ +#include "BehaviourScript.hpp" + +namespace TSE +{ + BehaviourScript::BehaviourScript() + : baseObject(nullptr), enabled(true) {} + + void BehaviourScript::SetEnabled(bool value) { + enabled = value; + } + + bool BehaviourScript::IsEnabled() const { + return enabled; + } + + void BehaviourScript::SetBaseObject(Transformable* obj) { + baseObject = obj; + } + + Transformable* BehaviourScript::GetTransform() const { + return baseObject; + } +} // namespace TSE diff --git a/TSE_Core/src/elements/BehaviourScript.hpp b/TSE_Core/src/elements/BehaviourScript.hpp new file mode 100644 index 0000000..1fdf94f --- /dev/null +++ b/TSE_Core/src/elements/BehaviourScript.hpp @@ -0,0 +1,29 @@ +#pragma once + +namespace TSE +{ + class Transformable; + + class BehaviourScript + { + public: + BehaviourScript(); + virtual ~BehaviourScript() = default; + + inline virtual void OnUpdate() { }; + inline virtual void OnEnable() { }; + inline virtual void OnDisable() { }; + inline virtual void Start() { }; + virtual const char* GetName() = 0; + inline virtual void CustomDraw(const bool& debug) { }; + void SetEnabled(bool v); + bool IsEnabled() const; + + void SetBaseObject(Transformable* obj); + Transformable* GetTransform() const; + + Transformable* baseObject; + protected: + bool enabled = true; + }; +} // namespace TSE diff --git a/TSE_Core/src/elements/Layer.cpp b/TSE_Core/src/elements/Layer.cpp new file mode 100644 index 0000000..1a42474 --- /dev/null +++ b/TSE_Core/src/elements/Layer.cpp @@ -0,0 +1,96 @@ +#include "Layer.hpp" +#include "BehaviourScripts/Renderable.hpp" +#include "elements/BehaviourScript.hpp" + +void HandleObject(TSE::Transformable* trans, TSE::TransformationStack& stack, TSE::IRenderer& rnd) +{ + using namespace TSE; + if(!trans->IsEnabled()) return; + //OPTIMIZE this if statement + if(trans->HasBehaviourScript(RENDERABLE) && trans->GetBehaviourScript(RENDERABLE)->IsEnabled() && ((Renderable*)trans->GetBehaviourScript(RENDERABLE))->GetMaterial() != nullptr && ((Renderable*)trans->GetBehaviourScript(RENDERABLE))->GetMaterial()->GetShader() != nullptr) + { + rnd.Submit(*trans, ((Renderable*)trans->GetBehaviourScript(RENDERABLE))->GetMaterial()->GetShader(), stack); + } + if(trans->GetChildren().size() > 0) + { + stack.Push(trans->GetLocalMatrix()); + for(auto child : trans->GetChildren()) + { + HandleObject(child, stack, rnd); + } + stack.Pop(); + } +} + +TSE::Layer::Layer(string &n) +{ + name = n; +} + +void TSE::Layer::Render(IRenderer &rnd) const +{ + TransformationStack stack; + for(auto obj : objectsToRender) + { + HandleObject(obj, stack, rnd); + } +} + +void TSE::Layer::AddTransformable(Transformable *t) +{ + objectsToRender.push_back(t); +} + +bool TSE::Layer::HasTransformable(const Transformable *t) const +{ + for(auto trans2 : objectsToRender) + { + if(t->id == trans2->id) + { + return true; + } + } + return false; +} + +void TSE::Layer::RemoveTransformable(const Transformable *t) +{ + RemoveTransformable(t->id); +} + +void TSE::Layer::RemoveTransformable(const uuids::uuid id) +{ + auto it = objectsToRender.begin(); + for(auto trans : objectsToRender) + { + if(trans->id == id) + { + objectsToRender.erase(it); + break; + } + it++; + } +} + +TSE::string TSE::Layer::GetName() const +{ + return name; +} + +void TSE::Layer::SetName(const string &n) +{ + name = n; +} + +std::vector &TSE::Layer::GetAllObjects() +{ + return objectsToRender; +} + +void TSE::Layer::Update() +{ + for(auto trans : objectsToRender) + { + trans->Update(); + } +} diff --git a/TSE_Core/src/elements/Layer.hpp b/TSE_Core/src/elements/Layer.hpp new file mode 100644 index 0000000..20d7c0e --- /dev/null +++ b/TSE_Core/src/elements/Layer.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "Transformable.hpp" +#include "Types.hpp" +#include "interfaces/IRenderer.hpp" +#include + +namespace TSE +{ + class Layer + { + private: + string name; + std::vector objectsToRender; + + public: + Layer(string& name); + + void Render(IRenderer& rnd) const; + void AddTransformable(Transformable* t); + bool HasTransformable(const Transformable* t) const; + + void RemoveTransformable(const Transformable* t); + void RemoveTransformable(const uuids::uuid id); + + string GetName() const; + void SetName(const string& name); + std::vector& GetAllObjects(); + void Update(); + }; +} // namespace TSE diff --git a/TSE_Core/src/elements/Material.cpp b/TSE_Core/src/elements/Material.cpp new file mode 100644 index 0000000..32d02a1 --- /dev/null +++ b/TSE_Core/src/elements/Material.cpp @@ -0,0 +1,81 @@ +#include "Material.hpp" + +#include +#include "IdGenerator.hpp" +#include "interfaces/ITexture.hpp" +#include "Types.hpp" +#include "Vector2.hpp" +#include "Vector3.hpp" +#include "Vector4.hpp" +#include "Color.hpp" +#include "Matrix4x4.hpp" + +namespace TSE +{ + Material::Material() + : name(""), shader(nullptr) + { + id = GenerateRandomUUID(); + } + Material::Material(const string& name, IShader* shader, uuids::uuid id) + : name(name), shader(shader), id(id) {} + + Material::Material(const string &name, IShader *shader) + : name(name), shader(shader) + { + id = GenerateRandomUUID(); + } + bool Material::HasValue(const string &key) const + { + return values.find(key) != values.end(); + } + int Material::GetValueCount() const + { + return values.size(); + } + std::tuple &Material::GetValueAt(int j) + { + auto it = values.begin(); + for (int i = 0; i < j; i++) + { + it++; + } + return it->second; + } + string &Material::GetName() + { + return name; + } + IShader *Material::GetShader() const + { + return shader; + } + void Material::SetShader(IShader *shader) + { + this->shader = shader; + } + uuids::uuid Material::GetID() const + { + return id; + } + + template void Material::SetValue(const string&, const int&); + template void Material::SetValue(const string&, const float&); + template void Material::SetValue(const string&, const Vector2&); + template void Material::SetValue(const string&, const Vector3&); + template void Material::SetValue(const string&, const Vector4&); + template void Material::SetValue(const string&, const Matrix4x4&); + template void Material::SetValue(const string&, const Color&); + template void Material::SetValue(const string&, const string&); + template void Material::SetValue(const string&, const ITexture*); + + template int Material::GetValue(const string&) const; + template float Material::GetValue(const string&) const; + template Vector2 Material::GetValue(const string&) const; + template Vector3 Material::GetValue(const string&) const; + template Vector4 Material::GetValue(const string&) const; + template Matrix4x4 Material::GetValue(const string&) const; + template Color Material::GetValue(const string&) const; + template string Material::GetValue(const string&) const; + template ITexture* Material::GetValue(const string&) const; +} // namespace TSE diff --git a/TSE_Core/src/elements/Material.hpp b/TSE_Core/src/elements/Material.hpp new file mode 100644 index 0000000..1fa65cd --- /dev/null +++ b/TSE_Core/src/elements/Material.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "uuid.h" +#include "interfaces/IShader.hpp" +#include +#include +#include +#include + +namespace TSE +{ + struct Material + { + private: + string name; + uuids::uuid id; + IShader* shader; + + std::unordered_map> values; + + public: + Material(); + Material(const string& name, IShader* shader); + Material(const string& name, IShader* shader, uuids::uuid id); + + template + void SetValue(const string& key, const T& value); + + template + T GetValue(const string& key) const; + + template + void SetValue(const string& key, const T* value); + + bool HasValue(const string& key) const; + int GetValueCount() const; + + std::tuple& GetValueAt(int i); + string& GetName(); + IShader* GetShader() const; + void SetShader(IShader* shader); + uuids::uuid GetID() const; + }; + + template + void Material::SetValue(const string& key, const T& value) { + values[key] = std::make_tuple(value, typeid(T).name(), key); + } + + template + T Material::GetValue(const string& key) const { + auto it = values.find(key); + if (it == values.end()) { + throw std::runtime_error("Material::GetValue - key '" + key + "' not found"); + } + auto [a,b,c] = it->second; + return std::any_cast(a); + } + + template + void Material::SetValue(const string& key, const T* value) { + values[key] = std::make_tuple(value, typeid(T).name(), key); + } + +} // namespace TSE diff --git a/TSE_Core/src/elements/Transformable.cpp b/TSE_Core/src/elements/Transformable.cpp new file mode 100644 index 0000000..c37916d --- /dev/null +++ b/TSE_Core/src/elements/Transformable.cpp @@ -0,0 +1,469 @@ +#include "Transformable.hpp" + +#include "IdGenerator.hpp" +#include "MathF.hpp" +#include "BehaviourScript.hpp" +#include "Layer.hpp" +#include "Debug.hpp" + +namespace TSE +{ + Transformable::Transformable() + : position(0, 0, 0), scale(1, 1, 1), rotation(), name("") + { + id = GenerateRandomUUID(); + objectEntries[id] = this; + } + + Transformable::Transformable(uuids::uuid id) + : id(id), position(0, 0, 0), scale(1, 1, 1), rotation(), name("") + { + objectEntries[id] = this; + } + + Transformable::Transformable(const string &name) + : position(0, 0, 0), scale(1, 1, 1), rotation(), name(name) + { + id = GenerateRandomUUID(); + objectEntries[id] = this; + } + + Transformable::Transformable(const string &name, uuids::uuid id) + : id(id), position(0, 0, 0), scale(1, 1, 1), rotation(), name(name) + { + objectEntries[id] = this; + } + + Vector3 Transformable::forward() const + { + return Vector3::Normalize(GetGlobalMatrix() * Vector3::forward - GetGlobalMatrix() * Vector3::zero); + } + + Vector3 Transformable::right() const + { + return Vector3::Normalize(GetGlobalMatrix() * Vector3::right - GetGlobalMatrix() * Vector3::zero); + } + + Vector3 Transformable::up() const + { + return Vector3::Normalize(GetGlobalMatrix() * Vector3::up - GetGlobalMatrix() * Vector3::zero); + } + + Vector3 Transformable::GetPosition() const + { + return position; + } + + void Transformable::SetPosition(const Vector3 &pos) + { + position = pos; + } + + Quaternion Transformable::GetRotation() const + { + return rotation; + } + + void Transformable::SetRotation(const Quaternion &rot) + { + rotation = rot; + } + + Vector3 Transformable::GetScale() const + { + return scale; + } + + void Transformable::SetScale(const Vector3 &s) + { + scale = s; + } + + string Transformable::GetName() const + { + return name; + } + + uuids::uuid Transformable::GetId() const + { + return id; + } + + Vector3 Transformable::GetEuler() + { + return rotation.ToEulerAngles(); + } + + void Transformable::SetEuler(const Vector3 &euler) + { + rotation = Quaternion::FromEulerAngles(euler); + } + + int Transformable::GetComponentCount() + { + return components.size(); + } + + Matrix4x4 Transformable::GetLocalMatrix() const + { + return Matrix4x4::ToTranslationMatrix(position) * Matrix4x4::ToRotationMatrix(rotation) * Matrix4x4::ToScaleMatrix(scale);; + } + + Matrix4x4 Transformable::GetGlobalMatrix() const + { + if (parent != nullptr) + return parent->GetGlobalMatrix() * GetLocalMatrix(); + return GetLocalMatrix(); + } + + Vector3 Transformable::GetGlobalPosition() const + { + return LocalToGlobalPosition(Vector3::zero); + } + + Vector3 Transformable::GlobalToLocalPosition(const Vector3& global) const + { + Matrix4x4 globalMatrix = GetGlobalMatrix(); + if(globalMatrix.IsAffine()) + { + globalMatrix.InvertAffine(); + } + else + { + globalMatrix.Invert(); + } + return globalMatrix * global; + } + + Vector3 Transformable::LocalToGlobalPosition(const Vector3& local) const + { + return GetGlobalMatrix() * local; + } + + void Transformable::LookAt_2D(const Vector3& globalTarget) + { + Vector3 to = globalTarget - GetGlobalPosition(); + Vector3 toXY = {to.x, to.y, 0.0f}; + if(Vector3::Dot(toXY, toXY) < TSE_EPSILON) return; + toXY.Normalize(); + + Vector3 fwdWorld = rotation.Rotate(Vector3::up); + Vector3 fwdXY = Vector3(fwdWorld.x, fwdWorld.y, 0); + fwdXY.Normalize(); + + float cosA = std::clamp(Vector3::Dot(fwdXY, toXY), -1.0f, 1.0f); + float sinA = fwdXY.x * toXY.y - fwdXY.y * toXY.x; + float angle = atan2(sinA, cosA); + + Quaternion twist = Quaternion::FromAngleAxis(angle, Vector3::forward); + rotation = twist * rotation; + } + + void Transformable::SetParent(Transformable *p) + { + Transformable* t(this); + if (parent != nullptr && parent != p) { + + auto it = parent->children.begin(); + for (int i = 0; i < parent->children.size(); i++) + { + if(**it == *t) + { + parent->children.erase(it); + break; + } + it++; + } + } + if (p != nullptr && p != parent) { + p->children.push_back(t); + } + parent = p; + } + + Transformable *Transformable::GetParent() const + { + return parent; + } + + const std::vector &Transformable::GetChildren() const + { + return children; + } + + bool Transformable::IsMyChild(Transformable *other) + { + for (auto child : children) + { + if(child->id == other->id) + { + return true; + } + else if(child->IsMyChild(other)) + { + return true; + } + } + return false; + } + + void Transformable::MoveUp(Layer *l) + { + if(l != nullptr) + { + auto it = l->GetAllObjects().begin(); + for(int i = 0; i < l->GetAllObjects().size(); i++) + { + if((*it)->id == id) + { + if(i == 0) return; + std::swap(l->GetAllObjects()[i - 1], l->GetAllObjects()[i]); + } + it++; + } + return; + } + auto it = parent->children.begin(); + for(int i = 0; i < parent->children.size(); i++) + { + if((*it)->id == id) + { + if(i == 0) return; + std::swap(parent->children[i - 1], parent->children[i]); + } + it++; + } + } + + void Transformable::MoveDown(Layer *l) + { + if(l != nullptr) + { + auto it = l->GetAllObjects().begin(); + for(int i = 0; i < l->GetAllObjects().size(); i++) + { + if((*it)->id == id) + { + if(i == l->GetAllObjects().size() - 1) return; + std::swap(l->GetAllObjects()[i + 1], l->GetAllObjects()[i]); + } + it++; + } + return; + } + auto it = parent->children.begin(); + for(int i = 0; i < parent->children.size(); i++) + { + if((*it)->id == id) + { + if(i == parent->children.size() - 1) return; + std::swap(parent->children[i + 1], parent->children[i]); + } + it++; + } + } + + bool Transformable::IsEnabled() const + { + return _enabled; + } + + void Transformable::SetEnabled(bool v) + { + if (_enabled != v) { + _enabled = v; + if (_enabled) OnEnable(); + else OnDisable(); + } + } + + BehaviourScript *Transformable::AddBehaviourScript(BehaviourScript *script) + { + if (!script) return nullptr; + const std::string key = typeid(*script).name(); + components.emplace(key, script); + script->SetBaseObject(this); + script->Start(); + return script; + } + + BehaviourScript *Transformable::GetBehaviourScript(const string &name) const + { + auto it = components.find(name); + return (it != components.end()) ? it->second : nullptr; + } + + BehaviourScript *Transformable::GetBehaviourScriptAt(const int i) const + { + auto it = components.begin(); + for (int j = 0; j < i; j++) + { + it++; + + } + + return (*it).second; + } + + std::vector Transformable::GetAllBehaviourScripts(const string &name) const + { + std::vector res; + + auto it = components.begin(); + while(it != components.end()) + { + if((*it).first == name) + { + res.push_back((*it).second); + } + } + + return res; + } + + void Transformable::RemoveBehaviourScript(BehaviourScript *script) + { + for (auto it = components.begin(); it != components.end(); ++it) { + if (it->second == script) { + components.erase(it); + break; + } + } + } + + bool Transformable::HasBehaviourScript(const string &name) const + { + auto it = components.find(name); + return it != components.end(); + } + + void Transformable::Update() + { + for (auto& [name, script] : components) { + if (script && script->IsEnabled()) script->OnUpdate(); + } + for (auto child : children) + { + child->Update(); + } + } + + void Transformable::Delete(Transformable *t) + { + objectEntries.erase(t->id); + delete t; + } + + void Transformable::Delete(const uuids::uuid id) + { + if(!Exists(id)) return; + Transformable* t = objectEntries.at(id); + objectEntries.erase(id); + delete t; + } + + void Transformable::HardDelete(Transformable *t, bool onlyThis) + { + //deleting children + if(!onlyThis) + for(auto child : t->children) + { + HardDelete(child); + } + + //deleting atteched scripts + for (auto& [_, script] : t->components) + delete script; + + //deleting self + Delete(t); + } + + void Transformable::HardDelete(const uuids::uuid id, bool onlyThis) + { + if(!Exists(id)) return; + Transformable* t = objectEntries.at(id); + //deleting children + if(!onlyThis) + for(auto child : t->children) + { + HardDelete(child); + } + + //deleting atteched scripts + for (auto& [_, script] : t->components) + delete script; + + //deleting self + Delete(t); + } + + void Transformable::DeleteAll() + { + auto it = objectEntries.begin(); + for(int i = 0; i < objectEntries.size(); i++) + { + delete it->second; + it++; + } + objectEntries.clear(); + } + + bool Transformable::Exists(const uuids::uuid id) + { + auto it = objectEntries.find(id); + return it != objectEntries.end(); + } + + bool Transformable::operator==(const Transformable &other) const + { + return other.id == id; + } + + bool Transformable::operator!=(const Transformable &other) const + { + return other.id != id; + } + + void Transformable::OnEnable() + { + for (auto& [_, script] : components) + if (script && script->IsEnabled()) script->OnEnable(); + } + + void Transformable::OnDisable() + { + for (auto& [_, script] : components) + if (script && script->IsEnabled()) script->OnDisable(); + } + + int Transformable::GetTansformableCount() + { + return objectEntries.size(); + } + + Transformable *Transformable::Find(string name) + { + for(auto obj : objectEntries) + { + if(obj.second->name == name) + { + return obj.second; + } + } + return nullptr; + } + + Transformable *Transformable::Find(uuids::uuid id) + { + for(auto obj : objectEntries) + { + if(obj.second->id == id) + { + return obj.second; + } + } + return nullptr; + } + +} // namespace TSE diff --git a/TSE_Core/src/elements/Transformable.hpp b/TSE_Core/src/elements/Transformable.hpp new file mode 100644 index 0000000..02a6375 --- /dev/null +++ b/TSE_Core/src/elements/Transformable.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include "Vector3.hpp" +#include "Quaternion.hpp" +#include "Matrix4x4.hpp" +#include "uuid.h" +#include "Types.hpp" +#include +#include +#include + +namespace TSE +{ + class BehaviourScript; + class Layer; + + class Transformable + { + public: + Vector3 position; + Vector3 scale; + Quaternion rotation; + string name; + bool _enabled = true; + uuids::uuid id; + + private: + std::unordered_multimap components; + std::vector children; + Transformable* parent = nullptr; + + static std::unordered_map objectEntries; + + public: + Transformable(); + Transformable(uuids::uuid id); + Transformable(const string& name); + Transformable(const string& name, uuids::uuid id); + ~Transformable() = default; + + Vector3 forward() const; + Vector3 right() const; + Vector3 up() const; + + Vector3 GetPosition() const; + void SetPosition(const Vector3& pos); + Quaternion GetRotation() const; + void SetRotation(const Quaternion& rot); + Vector3 GetScale() const; + void SetScale(const Vector3& scale); + string GetName() const; + uuids::uuid GetId() const; + Vector3 GetEuler(); + void SetEuler(const Vector3& euler); + int GetComponentCount(); + Matrix4x4 GetLocalMatrix() const; + Matrix4x4 GetGlobalMatrix() const; + Vector3 GetGlobalPosition() const; + + Vector3 GlobalToLocalPosition(const Vector3& global) const; + Vector3 LocalToGlobalPosition(const Vector3& local) const; + + void LookAt_2D(const Vector3& globalTarget); + + void SetParent(Transformable* parent); + Transformable* GetParent() const; + const std::vector& GetChildren() const; + bool IsMyChild(Transformable* other); + + void MoveUp(Layer* l = nullptr); + void MoveDown(Layer* l = nullptr); + + bool IsEnabled() const; + void SetEnabled(bool v); + + BehaviourScript* AddBehaviourScript(BehaviourScript* script); + BehaviourScript* GetBehaviourScript(const string& name) const; + BehaviourScript* GetBehaviourScriptAt(const int i) const; + std::vector GetAllBehaviourScripts(const string& name) const; + void RemoveBehaviourScript(BehaviourScript* script); + bool HasBehaviourScript(const string& name) const; + + void Update(); + + static void Delete(Transformable* t); + static void Delete(const uuids::uuid id); + static void HardDelete(Transformable* t, bool onlyThis = false); + static void HardDelete(const uuids::uuid id, bool onlyThis = false); + static void DeleteAll(); + + static bool Exists(const uuids::uuid id); + + bool operator==(const Transformable& other) const; + bool operator!=(const Transformable& other) const; + + private: + void OnEnable(); + void OnDisable(); + + public: + + static int GetTansformableCount(); + static Transformable* Find(string name); + static Transformable* Find(uuids::uuid id); + }; +} \ No newline at end of file diff --git a/TSE_Core/src/interfaces/IRenderable.hpp b/TSE_Core/src/interfaces/IRenderable.hpp new file mode 100644 index 0000000..e30edee --- /dev/null +++ b/TSE_Core/src/interfaces/IRenderable.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "Vector2.hpp" +#include "Vector3.hpp" +#include + +namespace TSE +{ + class IRenderable + { + public: + virtual const Vector3* GetVertices() const = 0; + virtual const Vector2* GetUVs() const = 0; + virtual const std::vector GetIndices() const = 0; + virtual size_t GetVertexCount() const = 0; + virtual ~IRenderable() = default; + }; +} // namespace TSE diff --git a/TSE_Core/src/interfaces/IRenderer.cpp b/TSE_Core/src/interfaces/IRenderer.cpp new file mode 100644 index 0000000..3b24fa7 --- /dev/null +++ b/TSE_Core/src/interfaces/IRenderer.cpp @@ -0,0 +1,3 @@ +#include "IRenderer.hpp" + +std::vector TSE::IRenderer::camerasToRenderWith = std::vector(); \ No newline at end of file diff --git a/TSE_Core/src/interfaces/IRenderer.hpp b/TSE_Core/src/interfaces/IRenderer.hpp new file mode 100644 index 0000000..29e4f63 --- /dev/null +++ b/TSE_Core/src/interfaces/IRenderer.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "IShader.hpp" +#include "TransformationStack.hpp" +#include "elements/Transformable.hpp" + +namespace TSE +{ + class Camera; + + class IRenderer + { + public: + + static std::vector camerasToRenderWith; + + virtual void End() = 0; + virtual void Flush() = 0; + virtual void Begin() = 0; + + virtual void Submit(const Transformable& trans, TransformationStack& stack) = 0; + virtual void Submit(const Transformable& trans, IShader* shader, TransformationStack& stack) = 0; + + virtual ~IRenderer() = default; + }; +} // namespace TSE diff --git a/TSE_Core/src/interfaces/IShader.hpp b/TSE_Core/src/interfaces/IShader.hpp new file mode 100644 index 0000000..9019c17 --- /dev/null +++ b/TSE_Core/src/interfaces/IShader.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Types.hpp" +#include "Matrix4x4.hpp" +#include "Vector2.hpp" +#include "Vector3.hpp" +#include "Vector4.hpp" + +namespace TSE +{ + class IShader + { + public: + + virtual void Bind() const = 0; + virtual void Unbind() const = 0; + + virtual void SetUniform(const char* name, int value) = 0; + virtual void SetUniform(const char* name, const int* value, int count) = 0; + virtual void SetUniform(const char* name, const Matrix4x4* value) = 0; + virtual void SetUniform(const char* name, float value) = 0; + virtual void SetUniform(const char* name, const float* value, int count) = 0; + virtual void SetUniform(const char* name, const Vector2* value) = 0; + virtual void SetUniform(const char* name, const Vector3* value) = 0; + virtual void SetUniform(const char* name, const Vector4* value) = 0; + + virtual ~IShader() = default; + }; +} // namespace TSE diff --git a/TSE_Core/src/interfaces/ITexture.hpp b/TSE_Core/src/interfaces/ITexture.hpp new file mode 100644 index 0000000..2794bba --- /dev/null +++ b/TSE_Core/src/interfaces/ITexture.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "Vector2.hpp" + +namespace TSE +{ + class ITexture + { + public: + virtual ~ITexture() = default; + virtual Vector2 size() const = 0; + virtual float width() const = 0; + virtual float height() const = 0; + virtual uint GetTextureId() const = 0; + }; +} // namespace TSE