diff --git a/include/sp2/graphics/mesh/glb.h b/include/sp2/graphics/mesh/glb.h new file mode 100644 index 00000000..0b7f1022 --- /dev/null +++ b/include/sp2/graphics/mesh/glb.h @@ -0,0 +1,51 @@ +#ifndef SP2_GRAPHICS_MESH_GLB_H +#define SP2_GRAPHICS_MESH_GLB_H + +#include +#include +#include + + +namespace sp { + +class GLBLoader +{ +public: + class GLBFile { + public: + struct Node { + string name; + + sp::Vector3d translation{}; + sp::Quaterniond rotation{}; + sp::MeshData::Vertices vertices; + sp::MeshData::Indices indices; + + std::vector children; + }; + std::vector roots; + + std::shared_ptr flatMesh() const; + private: + void addToFlat(sp::MeshData::Vertices& vertices, sp::MeshData::Indices& indices, const Node& node, Matrix4x4f transform) const; + }; + + static const GLBFile& get(string resource_name); +private: + GLBLoader(string resource_name); + void handleNode(int node_id, GLBFile::Node& node); + + template static sp::Vector3 swap_axis(sp::Vector3 v) { + return {-v.x, v.z, v.y}; + } + + GLBFile result; + nlohmann::json json; + std::vector bindata; + + static inline std::unordered_map files; +}; + +}//namespace sp + +#endif//SP2_GRAPHICS_MESH_GLB_H diff --git a/src/graphics/mesh/glb.cpp b/src/graphics/mesh/glb.cpp new file mode 100644 index 00000000..1bd6c8bd --- /dev/null +++ b/src/graphics/mesh/glb.cpp @@ -0,0 +1,136 @@ +#include +#include + +namespace sp { + +std::shared_ptr GLBLoader::GLBFile::flatMesh() const { + sp::MeshData::Vertices vertices; + sp::MeshData::Indices indices; + for(auto& root : roots) { + addToFlat(vertices, indices, root, Matrix4x4f::identity()); + } + return MeshData::create(std::move(vertices), std::move(indices)); +} + +void GLBLoader::GLBFile::addToFlat(sp::MeshData::Vertices& vertices, sp::MeshData::Indices& indices, const Node& node, Matrix4x4f transform) const { + transform = transform * Matrix4x4f::translate(Vector3f(node.translation)) * Matrix4x4f::fromQuaternion(Quaternionf(node.rotation)); + auto oldVertexCount = vertices.size(); + vertices.reserve(oldVertexCount + node.vertices.size()); + for(auto& v : node.vertices) { + vertices.emplace_back( + transform.multiply(v.position), + transform.applyDirection(v.normal), + v.uv + ); + } + indices.reserve(indices.size() + node.indices.size()); + for(auto i : node.indices) { + indices.push_back(i + oldVertexCount); + } + + for(auto& child : node.children) + addToFlat(vertices, indices, child, transform); +} + +const GLBLoader::GLBFile& GLBLoader::get(string resource_name) { + auto it = files.find(resource_name); + if (it != files.end()) + return it->second; + files[resource_name] = GLBLoader(resource_name).result; + return get(resource_name); +} + +GLBLoader::GLBLoader(string resource_name) +{ + auto resource = io::ResourceProvider::get(resource_name); + + uint32_t magic; + resource->read(&magic, sizeof(magic)); + if (magic != 0x46546C67) return; + uint32_t version; + uint32_t length; + resource->read(&version, sizeof(version)); + resource->read(&length, sizeof(length)); + + while(resource->tell() != resource->getSize()) { + uint32_t chunkLen; + resource->read(&chunkLen, sizeof(chunkLen)); + uint32_t chunkType; + resource->read(&chunkType, sizeof(chunkType)); + + if (chunkType == 0x4E4F534A) { + std::string json_string; + json_string.resize(chunkLen); + resource->read(json_string.data(), chunkLen); + json = nlohmann::json::parse(json_string, nullptr, false); + } else if (chunkType == 0x004E4942) { + bindata.resize(chunkLen); + resource->read(bindata.data(), chunkLen); + } else { + resource->seek(resource->tell() + chunkLen); + } + } + + for(auto& scene : json["scenes"]) { + for(auto& node_id : scene["nodes"]) { + result.roots.emplace_back(); + handleNode(static_cast(node_id), result.roots.back()); + } + } +} + +void GLBLoader::handleNode(int node_id, GLBFile::Node& node) +{ + auto& node_json = json["nodes"][node_id]; + if (node_json.find("translation") != node_json.end()) { + node.translation.x = node_json["translation"][0]; + node.translation.y = node_json["translation"][1]; + node.translation.z = node_json["translation"][2]; + node.translation = swap_axis(node.translation); + } + auto& mesh = json["meshes"][static_cast(node_json["mesh"])]; + for(auto& primitive : mesh["primitives"]) { + auto& p_a = json["accessors"][static_cast(primitive["attributes"]["POSITION"])]; + auto& p_b = json["bufferViews"][static_cast(p_a["bufferView"])]; + auto& n_a = json["accessors"][static_cast(primitive["attributes"]["NORMAL"])]; + auto& n_b = json["bufferViews"][static_cast(n_a["bufferView"])]; + auto& t_a = json["accessors"][static_cast(primitive["attributes"]["TEXCOORD_0"])]; + auto& t_b = json["bufferViews"][static_cast(t_a["bufferView"])]; + auto& i_a = json["accessors"][static_cast(primitive["indices"])]; + auto& i_b = json["bufferViews"][static_cast(i_a["bufferView"])]; + + node.vertices.reserve(p_a.value("count", 0)); + for(int vertex_offset=0; vertex_offset(bindata.data() + p_b.value("byteOffset", 0) + p_b.value("byteStride", sizeof(sp::Vector3f)) * vertex_offset)), + swap_axis(*reinterpret_cast(bindata.data() + n_b.value("byteOffset", 0) + n_b.value("byteStride", sizeof(sp::Vector3f)) * vertex_offset)), + *reinterpret_cast(bindata.data() + t_b.value("byteOffset", 0) + t_b.value("byteStride", sizeof(sp::Vector2f)) * vertex_offset) + ); + } + auto buffer = bindata.data() + i_b.value("byteOffset", 0); + node.indices.reserve(i_a.value("count", 0)); + for(int indice_offset=0; indice_offset(i_a["componentType"])) { + case 5121: + index = buffer[indice_offset]; + break; + case 5123: + index = buffer[indice_offset*2] | (buffer[indice_offset*2+1] << 8); + break; + } + node.indices.push_back(index); + } + } + + node.name = node_json.value("name", "no-name"); + + if (node_json.find("children") != node_json.end()) { + for(auto& child_id : node_json["children"]) { + node.children.emplace_back(); + handleNode(static_cast(child_id), node.children.back()); + } + } +} + +}//namespace sp