From 941c9f4b976fa9aa82f49cf8349b6f70fcdbd8ad Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 14:57:21 +0300 Subject: [PATCH 01/16] add demo model --- res/content/base/models/demo.vec3 | Bin 0 -> 2116 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 res/content/base/models/demo.vec3 diff --git a/res/content/base/models/demo.vec3 b/res/content/base/models/demo.vec3 new file mode 100644 index 0000000000000000000000000000000000000000..167be1e08bfda450c789a929f92693f848077f82 GIT binary patch literal 2116 zcmb_cOOI4V6h8Ip_q(Sb^Xyi9;A=)eK@qz~Mif*W(HUKlILO2hM=>%RT#$BwF>YA6 z(qN*j_zNb|_eK{cXb7(S1IG9Vkc~#qS9Qz2GXdhln^ev@Uw!A)`A&5g(d&nn_7Dle zlBC&=Kgn(>2^HHndDZu+=;ZMY3(LzUKH}gz{Kom~R}xN?_%0`f+cX~P2>OaoAsSl0 zT#&^71xMbUcz*Zx^+WERt)#=Dkn1!A!zg&(ZdVa@!P~+WJbg(@39W@OLS&@YGLw}u zr!sk^3Os(TirN^REg4fbx?;Fiwo07HS(zFVIV)R5B4>#@5;;pYkjPooMB+TB;kI#~ zLw3ISd@{OxAwGbKz>hz>HbQSH-{A=dxJ_~FEpYGyZg-u#7|s9i{fMpefBpFF2pCr4 zr#ZlFier=9G!A~i?ZxFAqxh$fUQcDsK4Y`DMV!(7AnfBC34uI2ABj>0{Ld-HLB(Z}HXyYq8^ z4=rJiL06b(9EEv=`wa8+Iw`3Ki`ute;oTeZiQ7M5$lU$~ES(cDJYWC5d%_5l!K0^h zGMiVjpfW|JOInt-sAyF+*_ttRlWS1ENree2wnV8d@=a0h$Vyk1CS`R>6{c0Ct4cGv zFst)(y4W+-zA1N1?Jla{O^qGYyhlvjE9&=&*8Q@*Q%*b}I}fVHL#q9-ns`Jv=5>8R zHy<^fU8c3$bobEYV>I|FHh~-rKP{eX_4Jcwcl_OBZ za&j*~5zA@52t_QX^%4}ZoX$}wVmY;!p@`+Qm!XK|)LwximNR_}idfFlY5l#;Q=6yO&n&#Zv37R-1EMmW*f{;xJ7*S7tbMq)vH1_1 C^4{bC literal 0 HcmV?d00001 From edc9d9546d02005c9c9603bfd517c3313ec6d6a9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 15:03:51 +0300 Subject: [PATCH 02/16] add VEC3 format specification --- doc/specs/vec3_model_spec.md | 109 +++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 doc/specs/vec3_model_spec.md diff --git a/doc/specs/vec3_model_spec.md b/doc/specs/vec3_model_spec.md new file mode 100644 index 00000000..8890cf21 --- /dev/null +++ b/doc/specs/vec3_model_spec.md @@ -0,0 +1,109 @@ +# VEC3 format specification + +3D models binary format. + +Byteorder: little-endian + +## Syntax + +```cpp +enum AttributeType:uint8 { + POSITION = 0, + UV, + NORMAL, + COLOR, +}; +sizeof(AttributeType) == 1; + +struct VertexAttribute { + AttributeType type; // data type is infered from attribute type + uint8 flags; + uint32 size; + float data[]; // if compressed, first 4 bytes of compressed data is decompressed size +}; +sizeof(VertexAttribute) == 6; // + dynamic data array + +struct Mesh { + uint32 triangles_count; // number of mesh triangles + uint16 material_id; + uint16 flags; + uint16 attribute_count; + VertexAttributes attributes[]; + uint8 indices[]; // if compressed, first 4 bytes of compressed data is compressed buffer size +}; +sizeof(Mesh) == 10; // + dynamic attributes array + dynamic indices array + +struct Model { + uint16 name_len; + vec3 origin; + uint32 mesh_count; + Mesh meshes[]; + char name[]; +}; +sizeof(Model) == 18; // + dynamic Mesh array + name length + +struct Material { + uint16 flags; + uint16 name_len; + char name[]; +}; +sizeof(Material) == 4; // + dynamic sized string + +struct Header { + char[8] ident; // "\0\0VEC3\0\0" + uint16 version; // current is 1 + uint16 reserved; // 0x0000 +}; +sizeof(Header) == 12; + +struct Body { + uint16 model_count + uint16 material_count + Model models[]; + Material materials[]; +}; +sizeof(Body) == 4; // + dynamic models array + dynamic materials array + +``` + +\* vertex data: positions are global. Model origins used to make it local. + +vertex - is a set of vertex data section entries indices divided by stride, starting from 0 (section first entry). + +Example: in file having sections (coordinates, texture_coordinates, normal) vertex is a set of 3 indices ordered the +same way as sections stored in the file. + +## Vertex Data section tags + +All sections are optional. + +| Value | Name | Stride (bytes) | Description | +| ----- | ------------------- | -------------- | --------------------------- | +| %x01 | Coordinates | 12 | vertex position | +| %x02 | Texture coordinates | 8 | vertex texture coordinates | +| %x03 | Normals | 12 | vertex normal vector | +| %x04 | Color | 16 | vertex RGBA color (0.0-1.0) | + +VertexAttribute flags: + +| Value | Name | +| ----- | ---------------- | +| %x01 | ZLib compression | + +## Mesh + +Mesh flags: + +| Value | Name | +| ----- | ----------------------------------- | +| %x01 | Indices ZLib compression | +| %x02 | Use 16 bit indices instead of 8 bit | + +## Material + +Material flags: + +| Bit offset | Description | +|------------|-------------| +| 0 | Shadeless | +| 1-7 | Reserved | From f89f8250819f6c37abdf90039e022373bce05dea Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 15:48:46 +0300 Subject: [PATCH 03/16] update files::read_bytes --- src/files/files.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/files/files.cpp b/src/files/files.cpp index 8be834b5..985cd2ba 100644 --- a/src/files/files.cpp +++ b/src/files/files.cpp @@ -75,7 +75,11 @@ std::unique_ptr files::read_bytes( const fs::path& filename, size_t& length ) { std::ifstream input(filename, std::ios::binary); - if (!input.is_open()) return nullptr; + if (!input.is_open()) { + throw std::runtime_error( + "could not to load file '" + filename.string() + "'" + ); + } input.seekg(0, std::ios_base::end); length = input.tellg(); input.seekg(0, std::ios_base::beg); @@ -102,12 +106,7 @@ std::vector files::read_bytes(const fs::path& filename) { std::string files::read_string(const fs::path& filename) { size_t size; - std::unique_ptr bytes(read_bytes(filename, size)); - if (bytes == nullptr) { - throw std::runtime_error( - "could not to load file '" + filename.string() + "'" - ); - } + auto bytes = read_bytes(filename, size); return std::string((const char*)bytes.get(), size); } From f8844552997fdf3a893d758c52c62a8bc3d204eb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 16:38:28 +0300 Subject: [PATCH 04/16] fix typos --- doc/specs/vec3_model_spec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/specs/vec3_model_spec.md b/doc/specs/vec3_model_spec.md index 8890cf21..681fead6 100644 --- a/doc/specs/vec3_model_spec.md +++ b/doc/specs/vec3_model_spec.md @@ -24,11 +24,11 @@ struct VertexAttribute { sizeof(VertexAttribute) == 6; // + dynamic data array struct Mesh { - uint32 triangles_count; // number of mesh triangles + uint32 triangle_count; // number of mesh triangles uint16 material_id; uint16 flags; uint16 attribute_count; - VertexAttributes attributes[]; + VertexAttribute attributes[]; uint8 indices[]; // if compressed, first 4 bytes of compressed data is compressed buffer size }; sizeof(Mesh) == 10; // + dynamic attributes array + dynamic indices array From 229456ced559a79c14d4e7fd80e98393551624dc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 17:39:23 +0300 Subject: [PATCH 05/16] add ByteReader.get(dst, size) method --- src/coders/byte_utils.cpp | 8 ++++++++ src/coders/byte_utils.hpp | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/coders/byte_utils.cpp b/src/coders/byte_utils.cpp index 380c932c..e30c14f2 100644 --- a/src/coders/byte_utils.cpp +++ b/src/coders/byte_utils.cpp @@ -107,6 +107,14 @@ void ByteReader::checkMagic(const char* data, size_t size) { pos += size; } +void ByteReader::get(char* dst, size_t size) { + if (pos + size > this->size) { + throw std::runtime_error("buffer underflow"); + } + std::memcpy(dst, data+pos, size); + pos += size; +} + ubyte ByteReader::get() { if (pos == size) { throw std::runtime_error("buffer underflow"); diff --git a/src/coders/byte_utils.hpp b/src/coders/byte_utils.hpp index 103327c5..0532fe43 100644 --- a/src/coders/byte_utils.hpp +++ b/src/coders/byte_utils.hpp @@ -52,6 +52,8 @@ public: ByteReader(const ubyte* data); void checkMagic(const char* data, size_t size); + /// @brief Get N bytes + void get(char* dst, size_t size); /// @brief Read one byte (unsigned 8 bit integer) ubyte get(); /// @brief Read one byte (unsigned 8 bit integer) without pointer move From b3d942b76cf7e21d0224603cd8614f63f7e04180 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 17:40:04 +0300 Subject: [PATCH 06/16] add Buffer move assignment operator --- src/util/Buffer.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/Buffer.hpp b/src/util/Buffer.hpp index f7bf3ff6..403fa4d8 100644 --- a/src/util/Buffer.hpp +++ b/src/util/Buffer.hpp @@ -37,6 +37,8 @@ namespace util { Buffer(std::nullptr_t) noexcept : ptr(nullptr), length(0) {} + Buffer& operator=(Buffer&&) = default; + inline bool operator==(std::nullptr_t) const noexcept { return ptr == nullptr; } From 91f45ad4d79b28cc6d1374082a6840e10091bd25 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 18:34:40 +0300 Subject: [PATCH 07/16] add VEC3 model loader --- src/coders/vec3.cpp | 215 +++++++++++++++++++++++++++++++++++++++++++ src/coders/vec3.hpp | 36 ++++++++ test/coders/vec3.cpp | 12 +++ 3 files changed, 263 insertions(+) create mode 100644 src/coders/vec3.cpp create mode 100644 src/coders/vec3.hpp create mode 100644 test/coders/vec3.cpp diff --git a/src/coders/vec3.cpp b/src/coders/vec3.cpp new file mode 100644 index 00000000..286261d6 --- /dev/null +++ b/src/coders/vec3.cpp @@ -0,0 +1,215 @@ +#include "vec3.hpp" + +#include + +#include "byte_utils.hpp" +#include "util/data_io.hpp" +#include "util/stringutil.hpp" +#include "graphics/core/Model.hpp" + +inline constexpr int VERSION = 1; + +inline constexpr int FLAG_ZLIB = 0x1; +inline constexpr int FLAG_16BIT_INDICES = 0x2; + +using namespace vec3; + +vec3::Model::~Model() = default; + +enum AttributeType { + POSITION = 0, + UV, + NORMAL, + COLOR +}; + +struct VertexAttribute { + AttributeType type; + int flags; + util::Buffer data; + + VertexAttribute() = default; + + VertexAttribute(VertexAttribute&&) = default; + + VertexAttribute& operator=(VertexAttribute&& o) { + type = o.type; + flags = o.flags; + data = std::move(o.data); + return *this; + } +}; + +static VertexAttribute load_attribute(ByteReader& reader) { + auto type = static_cast(reader.get()); + int flags = reader.get(); + assert(type >= POSITION && flags <= COLOR); + if (flags != 0) { + throw std::runtime_error("attribute compression is not supported yet"); + } + int size = reader.getInt32(); + + util::Buffer data(size / sizeof(float)); + reader.get(reinterpret_cast(data.data()), size); + if (dataio::is_big_endian()) { + for (int i = 0; i < data.size(); i++) { + data[i] = dataio::swap(data[i]); + } + } + return VertexAttribute {type, flags, std::move(data)}; +} + +static model::Mesh build_mesh( + const std::vector& attrs, + const util::Buffer& indices, + const std::string& texture +) { + const glm::vec3* coords = nullptr; + const glm::vec2* uvs = nullptr; + const glm::vec3* normals = nullptr; + + int coordsIndex, uvsIndex, normalsIndex; + + for (int i = 0; i < attrs.size(); i++) { + const auto& attr = attrs[i]; + switch (attr.type) { + case POSITION: + coords = reinterpret_cast(attr.data.data()); + coordsIndex = i; + break; + case UV: + uvs = reinterpret_cast(attr.data.data()); + uvsIndex = i; + break; + case NORMAL: + normals = reinterpret_cast(attr.data.data()); + normalsIndex = i; + break; + case COLOR: // unused + break; + } + } + std::vector vertices; + int attrsCount = attrs.size(); + int verticesCount = indices.size() / attrsCount; + for (int i = 0; i < verticesCount; i++) { + model::Vertex vertex {}; + if (coords) { + vertex.coord = coords[indices[i * attrsCount + coordsIndex]]; + } + if (uvs) { + vertex.uv = uvs[indices[i * attrsCount + uvsIndex]]; + } + if (normals) { + vertex.normal = normals[indices[i * attrsCount + normalsIndex]]; + } + } + return model::Mesh {texture, std::move(vertices)}; +} + +static model::Mesh load_mesh(ByteReader& reader) { + int triangleCount = reader.getInt32(); + int materialId = reader.getInt16(); + int flags = reader.getInt16(); + int attributeCount = reader.getInt16(); + if (flags == FLAG_ZLIB) { + throw std::runtime_error("compression is not supported yet"); + } + assert(flags == 0); + std::vector attributes; + for (int i = 0; i < attributeCount; i++) { + attributes.push_back(load_attribute(reader)); + } + + util::Buffer indices(triangleCount * 3 * attributeCount); + if ((flags & FLAG_16BIT_INDICES) == 0){ + util::Buffer smallIndices(indices.size()); + reader.get( + reinterpret_cast(smallIndices.data()), + indices.size() * sizeof(uint8_t) + ); + for (int i = 0; i < indices.size(); i++) { + indices[i] = smallIndices[i]; + } + } + if (dataio::is_big_endian()) { + for (int i = 0; i < indices.size(); i++) { + indices[i] = dataio::swap(indices[i]); + } + } + return build_mesh( + attributes, + indices, + // encode material index to UTF-8 because materials are not loaded yet + util::wstr2str_utf8(std::wstring({static_cast(materialId)})) + ); +} + +static Model load_model(ByteReader& reader) { + int nameLength = reader.getInt16(); + assert(nameLength >= 0); + float x = reader.getFloat32(); + float y = reader.getFloat32(); + float z = reader.getFloat32(); + int meshCount = reader.getInt32(); + assert(meshCount >= 0); + + std::vector meshes; + for (int i = 0; i < meshCount; i++) { + meshes.push_back(load_mesh(reader)); + } + util::Buffer chars(nameLength); + reader.get(chars.data(), nameLength); + std::string name(chars.data(), nameLength); + return Model {std::move(name), model::Model {std::move(meshes)}, {x, y, z}}; +} + +static Material load_material(ByteReader& reader) { + int flags = reader.getInt16(); + int nameLength = reader.getInt16(); + assert(nameLength >= 0); + util::Buffer chars(nameLength); + reader.get(chars.data(), nameLength); + std::string name(chars.data(), nameLength); + return Material {flags, std::move(name)}; +} + +File vec3::load( + const std::string_view file, const util::Buffer& src +) { + ByteReader reader(src.data(), src.size()); + + // Header + reader.checkMagic("\0\0VEC3\0\0", 8); + int version = reader.getInt16(); + int reserved = reader.getInt16(); + if (version > VERSION) { + throw std::runtime_error("unsupported VEC3 version"); + } + assert(reserved == 0); + + // Body + int modelCount = reader.getInt16(); + int materialCount = reader.getInt16(); + assert(modelCount >= 0); + assert(materialCount >= 0); + + std::unordered_map models; + for (int i = 0; i < modelCount; i++) { + Model model = load_model(reader); + models[model.name] = std::move(model); + } + std::vector materials; + for (int i = 0; i < materialCount; i++) { + materials.push_back(load_material(reader)); + } + + // Resolve textures + for (auto& [_, model] : models) { + for (auto& mesh : model.model.meshes) { + int materialId = util::str2wstr_utf8(mesh.texture).at(0); + mesh.texture = materials.at(materialId).name; + } + } + return File {std::move(models), std::move(materials)}; +} diff --git a/src/coders/vec3.hpp b/src/coders/vec3.hpp new file mode 100644 index 00000000..8a91250f --- /dev/null +++ b/src/coders/vec3.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "typedefs.hpp" +#include "util/Buffer.hpp" +#include "graphics/core/Model.hpp" + +/// See /doc/specs/vec3_model_spec.md +namespace vec3 { + struct Material { + int flags; + std::string name; + }; + + struct Model { + std::string name; + model::Model model; + glm::vec3 origin; + + Model& operator=(Model&&) = default; + + ~Model(); + }; + + struct File { + std::unordered_map models; + std::vector materials; + }; + + File load(const std::string_view file, const util::Buffer& src); +} diff --git a/test/coders/vec3.cpp b/test/coders/vec3.cpp new file mode 100644 index 00000000..e4643921 --- /dev/null +++ b/test/coders/vec3.cpp @@ -0,0 +1,12 @@ +#include + +#include "coders/vec3.hpp" +#include "files/files.hpp" + +TEST(VEC3, Decode) { + auto file = std::filesystem::u8path( + "../res/content/base/models/demo.vec3" + ); + auto bytes = files::read_bytes_buffer(file); + auto model = vec3::load(file.u8string(), bytes); +} From 39d9f269fae6b560ff0737d707a5a452302f3006 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 18:37:03 +0300 Subject: [PATCH 08/16] add 16 bit indices support --- src/coders/vec3.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coders/vec3.cpp b/src/coders/vec3.cpp index 286261d6..d6db56c3 100644 --- a/src/coders/vec3.cpp +++ b/src/coders/vec3.cpp @@ -131,6 +131,11 @@ static model::Mesh load_mesh(ByteReader& reader) { for (int i = 0; i < indices.size(); i++) { indices[i] = smallIndices[i]; } + } else { + reader.get( + reinterpret_cast(indices.data()), + indices.size() * sizeof(uint16_t) + ); } if (dataio::is_big_endian()) { for (int i = 0; i < indices.size(); i++) { From 30725c80c7774aa5a8164041e770645e12db7ac1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 24 Oct 2024 20:15:59 +0300 Subject: [PATCH 09/16] update VEC3 structure --- doc/specs/vec3_model_spec.md | 4 ++-- res/content/base/models/demo.vec3 | Bin 2116 -> 2116 bytes src/assets/assetload_funcs.cpp | 12 +++++++---- src/coders/vec3.cpp | 34 +++++++++++++----------------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/doc/specs/vec3_model_spec.md b/doc/specs/vec3_model_spec.md index 681fead6..b9c2868c 100644 --- a/doc/specs/vec3_model_spec.md +++ b/doc/specs/vec3_model_spec.md @@ -57,10 +57,10 @@ struct Header { sizeof(Header) == 12; struct Body { - uint16 model_count uint16 material_count - Model models[]; + uint16 model_count Material materials[]; + Model models[]; }; sizeof(Body) == 4; // + dynamic models array + dynamic materials array diff --git a/res/content/base/models/demo.vec3 b/res/content/base/models/demo.vec3 index 167be1e08bfda450c789a929f92693f848077f82..bd132c6e340abfd7c407200a60d2f4ce63a028d5 100644 GIT binary patch delta 29 jcmX>ia6~|Wfg#M**_eTWk%57M2}n*>;858pAix0tPpJfk delta 28 hcmX>ia6~|Wfg#M**_eTWkpYC67&e*+a7+|X0RT(a1dsp# diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 0f69e64e..41d50e43 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -181,8 +181,7 @@ assetload::postfunc assetload::sound( if (!fs::exists(variantFile)) { break; } - baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM) - ); + baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM)); } auto sound = baseSound.release(); @@ -191,8 +190,13 @@ assetload::postfunc assetload::sound( }; } -assetload::postfunc assetload:: - model(AssetsLoader* loader, const ResPaths* paths, const std::string& file, const std::string& name, const std::shared_ptr&) { +assetload::postfunc assetload::model( + AssetsLoader* loader, + const ResPaths* paths, + const std::string& file, + const std::string& name, + const std::shared_ptr& +) { auto path = paths->find(file + ".obj"); auto text = files::read_string(path); try { diff --git a/src/coders/vec3.cpp b/src/coders/vec3.cpp index d6db56c3..fbf40a11 100644 --- a/src/coders/vec3.cpp +++ b/src/coders/vec3.cpp @@ -107,7 +107,9 @@ static model::Mesh build_mesh( return model::Mesh {texture, std::move(vertices)}; } -static model::Mesh load_mesh(ByteReader& reader) { +static model::Mesh load_mesh( + ByteReader& reader, const std::vector& materials +) { int triangleCount = reader.getInt32(); int materialId = reader.getInt16(); int flags = reader.getInt16(); @@ -145,12 +147,13 @@ static model::Mesh load_mesh(ByteReader& reader) { return build_mesh( attributes, indices, - // encode material index to UTF-8 because materials are not loaded yet - util::wstr2str_utf8(std::wstring({static_cast(materialId)})) + materials.at(materialId).name ); } -static Model load_model(ByteReader& reader) { +static Model load_model( + ByteReader& reader, const std::vector& materials +) { int nameLength = reader.getInt16(); assert(nameLength >= 0); float x = reader.getFloat32(); @@ -161,7 +164,7 @@ static Model load_model(ByteReader& reader) { std::vector meshes; for (int i = 0; i < meshCount; i++) { - meshes.push_back(load_mesh(reader)); + meshes.push_back(load_mesh(reader, materials)); } util::Buffer chars(nameLength); reader.get(chars.data(), nameLength); @@ -194,27 +197,20 @@ File vec3::load( assert(reserved == 0); // Body - int modelCount = reader.getInt16(); int materialCount = reader.getInt16(); - assert(modelCount >= 0); + int modelCount = reader.getInt16(); assert(materialCount >= 0); + assert(modelCount >= 0); - std::unordered_map models; - for (int i = 0; i < modelCount; i++) { - Model model = load_model(reader); - models[model.name] = std::move(model); - } std::vector materials; for (int i = 0; i < materialCount; i++) { materials.push_back(load_material(reader)); } - - // Resolve textures - for (auto& [_, model] : models) { - for (auto& mesh : model.model.meshes) { - int materialId = util::str2wstr_utf8(mesh.texture).at(0); - mesh.texture = materials.at(materialId).name; - } + + std::unordered_map models; + for (int i = 0; i < modelCount; i++) { + Model model = load_model(reader, materials); + models[model.name] = std::move(model); } return File {std::move(models), std::move(materials)}; } From 5c019c53f0389d41c5ffd58e73a298599218545a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 25 Oct 2024 10:09:36 +0300 Subject: [PATCH 10/16] fix VEC3 loader --- src/coders/vec3.cpp | 1 + src/coders/vec3.hpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/coders/vec3.cpp b/src/coders/vec3.cpp index fbf40a11..61a08082 100644 --- a/src/coders/vec3.cpp +++ b/src/coders/vec3.cpp @@ -103,6 +103,7 @@ static model::Mesh build_mesh( if (normals) { vertex.normal = normals[indices[i * attrsCount + normalsIndex]]; } + vertices.push_back(std::move(vertex)); } return model::Mesh {texture, std::move(vertices)}; } diff --git a/src/coders/vec3.hpp b/src/coders/vec3.hpp index 8a91250f..1aa3ea9d 100644 --- a/src/coders/vec3.hpp +++ b/src/coders/vec3.hpp @@ -30,6 +30,10 @@ namespace vec3 { struct File { std::unordered_map models; std::vector materials; + + File(File&&) = default; + + File& operator=(File&&) = default; }; File load(const std::string_view file, const util::Buffer& src); From caf3376ef9c9a2d593b0b6149c741bf847004540 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 25 Oct 2024 10:10:30 +0300 Subject: [PATCH 11/16] add VEC3 models support --- src/assets/assetload_funcs.cpp | 44 +++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 41d50e43..6c4ed262 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -10,6 +10,7 @@ #include "coders/imageio.hpp" #include "coders/json.hpp" #include "coders/obj.hpp" +#include "coders/vec3.hpp" #include "constants.hpp" #include "debug/Logger.hpp" #include "files/engine_paths.hpp" @@ -23,6 +24,7 @@ #include "graphics/core/Texture.hpp" #include "graphics/core/TextureAnimation.hpp" #include "objects/rigging.hpp" +#include "util/stringutil.hpp" #include "Assets.hpp" #include "AssetsLoader.hpp" @@ -190,6 +192,17 @@ assetload::postfunc assetload::sound( }; } +static void request_textures(AssetsLoader* loader, const model::Model& model) { + for (auto& mesh : model.meshes) { + if (mesh.texture.find('$') == std::string::npos) { + auto filename = TEXTURES_FOLDER + "/" + mesh.texture; + loader->add( + AssetType::TEXTURE, filename, mesh.texture, nullptr + ); + } + } +} + assetload::postfunc assetload::model( AssetsLoader* loader, const ResPaths* paths, @@ -197,19 +210,32 @@ assetload::postfunc assetload::model( const std::string& name, const std::shared_ptr& ) { - auto path = paths->find(file + ".obj"); + auto path = paths->find(file + ".vec3"); + if (fs::exists(path)) { + auto bytes = files::read_bytes_buffer(path); + auto modelVEC3 = std::make_shared(vec3::load(path.u8string(), bytes)); + return [loader, name, modelVEC3=std::move(modelVEC3)](Assets* assets) { + for (auto& [modelName, model] : modelVEC3->models) { + request_textures(loader, model.model); + std::string fullName = name; + if (name != modelName) { + fullName += "." + modelName; + } + assets->store( + std::make_unique(model.model), + fullName + ); + logger.info() << "store model " << util::quote(modelName) + << " as " << util::quote(fullName); + } + }; + } + path = paths->find(file + ".obj"); auto text = files::read_string(path); try { auto model = obj::parse(path.u8string(), text).release(); return [=](Assets* assets) { - for (auto& mesh : model->meshes) { - if (mesh.texture.find('$') == std::string::npos) { - auto filename = TEXTURES_FOLDER + "/" + mesh.texture; - loader->add( - AssetType::TEXTURE, filename, mesh.texture, nullptr - ); - } - } + request_textures(loader, *model); assets->store(std::unique_ptr(model), name); }; } catch (const parsing_error& err) { From 9224d14d32f2d5339ecb2b4308798485a0cc67ad Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 25 Oct 2024 10:13:15 +0300 Subject: [PATCH 12/16] update demo model --- res/content/base/models/demo.vec3 | Bin 2116 -> 4132 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/res/content/base/models/demo.vec3 b/res/content/base/models/demo.vec3 index bd132c6e340abfd7c407200a60d2f4ce63a028d5..c780c71653647bd27ed61fed233ece6b22078912 100644 GIT binary patch literal 4132 zcmeH}X^dV|7{|}u-gowW=`5Y4`(ip>RJGcBhOU;XmQh7%Yo?T;=vZ3ROB88HBjp21 z5JHehe2@t4eQgm-Y>|jBB55Ne!Y3+0_&xXDGjsat7ZKmQx$~Ug``rI|{?B>tZD*7^ zf93K8N;yg?H;J`s_?pojqdSIo^!_EBRemi=G>Pu#eV@LO>Gg5g)@VmPfs0ZFoh->}bDI6<_w5=s zn|iXEyv>p`d9%tq*Nkl(&!UXS(h1nR?K`47M(a4rm2pe-JmvdN5IDJ<0}%6hCs*cr z7y^WNK03ClgBNpzVe z;cTy+*RtQ#JhblTX#bLRnl%(Ieso7EN0vr7%rpsSd+mHE8dLMTcw_Wsaj#|#gy>!Vlk!T(|bU3nl zjw~^E`G5%$&j?^k>voBr6GxgY9{P1;QvoBo>I?a!-sb2m!lYPb5>>J$YQcLN<<`1}M z)?%FI$Ip?Thx?iS&+Y?>{=C5%$+|pQi}BGUUyRo#^@_34rM^n1Io z38TpO7Ywx;0k~&i3i%0GbW&acH z>!>{xOI~!}^v#X4`kK9oPs!fc(!_OpS`rO)Vyn;CsxO+P2hmupt-ZFF-E&0GA-&05 zG`@0nsiiq@k`u;P`j_*_xso}_xtK$8R_Wi;Bs0tEC`)5|N&lu!YCa z=|^UU-=^f8+$#Ay(DNNPaQvJD5QDrEl(~Mno2zgEV!qPNCx81nH6B1zwVqe!2lalw z!3T({(f6AIw>fZH0)Uun4az2{@-|h`t^i_X2bWXP=~PX00Ah8QQ{C-W^|&>?E+@y{9$rqFXQ#O^8;p@~3D*)oO ziz)%)^oy&|AegbadgdiHvo5Wjy~R6WYhB;A`Zd{F9H%vbD z#wn-WGx6S6_>*;#~fDBAT25_;3TL8OYBBcN>%5n>! zGEBq{;KG_)0PZl6mH;kXxCPK5Or#Tlw(*4lP5~2X1K?=*!T=|QiL?Q5e0*VmX~0C< z02mRzFu=rMB5eSS5nmW!sxT3208?D>0+>8ZWWuR6E_mV2(}9URh141sym03U!bF}_ zYK;qCxbu`@B2P25=CJtU&YJ)ed58e72fi@C+X545BL#e6GQ3eRkv3Am7be472NP)n epfaT>2=HdYL|#Pz4J{!9NWw%q1crvM-uV}|2`|b3 literal 2116 zcmb_cOOI4V6h8Ip_q(Sb^Xyi9;A=)eK@qz~Mif*W(HUKlILO2hM=>%RT#$BwF>YA6 z(qN*j_zNb|_eK{cXb7(S1IG9Vkc~#qS9Qz2GXdhlN~+H}Uw!A)`KoRg(d&nn_7Djo zlJ2)k>+fxz+B~&>X5syfwX^FV5S8h~#_6}-IkRwL?ZdT=P0if#knEO{P_ccJXMLZF zP9EQ|usm(zBM!d9Z=An=CE-Mg?{ZSOP2-`Cps#orqM`NE1xfs0aOB;I=T~oEKjhxo zN;(`0xlThcjDqLwb`@b2ye(WINJ>&lXf2EpA|thynXHsKmB}kr;N#b-sEyItk}+kY zD~4-jtHg<%m8l_-v$9nra+atgk+Wn2iJV1EB+hdhz6jjskex3+pNuYFh!5aI;K!d` z8=<$9@9=~J+@?787C86;x4X_=jOKs%e#F-Kzkd971Pn9r(;VP7#j#0l8V5h%_Tuu5 zQT)?Kumkv??q7CT%6E9e8G)DL3~qsgAMnBN(Ho=Am)}{o&i^Go2Y-Z__-PLCL5ed- za??2Y0Urpu%zv}&&wnQ0dw-EA=C1>GX#e{9tn=HS{Dxbx-u4&GrEeVnkL$>bqraUU z+R7#H@zsSIu&4um9378;{1&mdSo7;44(qF}tIQ|6dS1GIyd}nbe<5C3-RXEYdl%Sp zAI_3J>hj0&Qo`HX7wTbuvVJ{|!oJ70?d$80>jqxvBlP7h+0akeH`L#{-}tNp&#%YP zZr86r8}2U|IM*=#FCX>BwfsHCQMku&Z$9oX`WSqFcm5pULrXZvpevkb9EI};_ZiO9 z>!hR}ENb6=hIensJ8mDqkhy&XES(cDJYWC5d%_5l!K0^hGMiVjpfW|JOInt-sAyF+ z*_ttRlWS1ENree2wnV8d@=a0h$Vyk1CS`R>6{c0Ct4cGvFst)(y4W+-zA1N1?Jla{ zO^qGYyhlvjE9&=&*8Q@*Q%*b}I}fVHL#q9-ns`Jv=5>8RHy<^fU8c3$bobEYV>I|FHh~-rKP{eX_4Jcwcl_OBZa&j*~5zA@52t_QX^%4}Z zoX$}wVmY;!p@`+Qm!XK|)LwximNR_}idfF Date: Fri, 25 Oct 2024 11:55:44 +0300 Subject: [PATCH 13/16] update textures loading --- src/assets/assetload_funcs.cpp | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 6c4ed262..68b0289c 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -41,18 +41,32 @@ static bool animation( Atlas* dstAtlas ); -assetload::postfunc assetload:: - texture(AssetsLoader*, const ResPaths* paths, const std::string& filename, const std::string& name, const std::shared_ptr&) { - std::shared_ptr image( - imageio::read(paths->find(filename + ".png").u8string()).release() - ); - return [name, image](auto assets) { - assets->store(Texture::from(image.get()), name); - }; +assetload::postfunc assetload::texture( + AssetsLoader*, + const ResPaths* paths, + const std::string& filename, + const std::string& name, + const std::shared_ptr& +) { + auto actualFile = paths->find(filename + ".png").u8string(); + try { + std::shared_ptr image(imageio::read(actualFile).release()); + return [name, image, actualFile](auto assets) { + assets->store(Texture::from(image.get()), name); + }; + } catch (const std::runtime_error& err) { + logger.error() << actualFile << ": " << err.what(); + return [](auto) {}; + } } -assetload::postfunc assetload:: - shader(AssetsLoader*, const ResPaths* paths, const std::string& filename, const std::string& name, const std::shared_ptr&) { +assetload::postfunc assetload::shader( + AssetsLoader*, + const ResPaths* paths, + const std::string& filename, + const std::string& name, + const std::shared_ptr& +) { fs::path vertexFile = paths->find(filename + ".glslv"); fs::path fragmentFile = paths->find(filename + ".glslf"); From 0c9fb987a13418a03f32094694077d48c799e3c7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 25 Oct 2024 13:30:12 +0300 Subject: [PATCH 14/16] update files::write_string signature --- src/files/files.cpp | 2 +- src/files/files.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/files/files.cpp b/src/files/files.cpp index 985cd2ba..f7d3f460 100644 --- a/src/files/files.cpp +++ b/src/files/files.cpp @@ -110,7 +110,7 @@ std::string files::read_string(const fs::path& filename) { return std::string((const char*)bytes.get(), size); } -bool files::write_string(const fs::path& filename, const std::string content) { +bool files::write_string(const fs::path& filename, std::string_view content) { std::ofstream file(filename); if (!file) { return false; diff --git a/src/files/files.hpp b/src/files/files.hpp index 99ccc936..4cf509d4 100644 --- a/src/files/files.hpp +++ b/src/files/files.hpp @@ -38,7 +38,7 @@ namespace files { uint append_bytes(const fs::path& file, const ubyte* data, size_t size); /// @brief Write string to the file - bool write_string(const fs::path& filename, const std::string content); + bool write_string(const fs::path& filename, std::string_view content); /// @brief Write dynamic data to the JSON file /// @param nice if true, human readable format will be used, otherwise From 87ba344401bdaf4f320364865e824d4d9654812a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 25 Oct 2024 15:57:50 +0300 Subject: [PATCH 15/16] add 'vec3model.modelname' notation support to skeletons --- src/assets/AssetsLoader.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 182bed6f..f3f575df 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -228,7 +228,11 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { for (auto& entry : content->getSkeletons()) { auto& skeleton = *entry.second; for (auto& bone : skeleton.getBones()) { - auto& model = bone->model.name; + std::string model = bone->model.name; + size_t pos = model.rfind('.'); + if (pos != std::string::npos) { + model = model.substr(0, pos); + } if (!model.empty()) { loader.add( AssetType::MODEL, MODELS_FOLDER + "/" + model, model From 75805642b5035b95d52a63cb263b635803af8a95 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 26 Oct 2024 13:13:47 +0300 Subject: [PATCH 16/16] feat: VEC3 automatic normals generation --- src/coders/vec3.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/coders/vec3.cpp b/src/coders/vec3.cpp index 61a08082..a6c68927 100644 --- a/src/coders/vec3.cpp +++ b/src/coders/vec3.cpp @@ -102,6 +102,13 @@ static model::Mesh build_mesh( } if (normals) { vertex.normal = normals[indices[i * attrsCount + normalsIndex]]; + } else if (coords) { + // Flat normal calculation + int idx = (i / 3) * 3; + auto a = coords[indices[idx * attrsCount + coordsIndex]]; + auto b = coords[indices[(idx + 1) * attrsCount + coordsIndex]]; + auto c = coords[indices[(idx + 2) * attrsCount + coordsIndex]]; + vertex.normal = glm::normalize(glm::cross(b - a, c - a)); } vertices.push_back(std::move(vertex)); }