diff --git a/shards/gfx/gltf/gltf.cpp b/shards/gfx/gltf/gltf.cpp index 1262207c79c..763ca70b894 100644 --- a/shards/gfx/gltf/gltf.cpp +++ b/shards/gfx/gltf/gltf.cpp @@ -646,7 +646,7 @@ struct Loader { loadMeshes(); loadNodes(); - // Unify everyhing into a single node per scene + // Unify everything into a single node per scene // needs to be done before computing animation paths in skins size_t numScenes = model.scenes.size(); sceneMap.resize(numScenes); @@ -674,6 +674,54 @@ struct Loader { } }; +// Compute the model matrix for a node +linalg::mat getModelMatrix(const tinygltf::Node &node) { + auto translation = node.translation.empty() + ? linalg::vec{0, 0, 0} + : linalg::vec{node.translation[0], node.translation[1], node.translation[2]}; + auto rotation = node.rotation.empty() + ? linalg::vec{0, 0, 0, 1} + : linalg::vec{node.rotation[0], node.rotation[1], node.rotation[2], node.rotation[3]}; + auto scale = + node.scale.empty() ? linalg::vec{1, 1, 1} : linalg::vec{node.scale[0], node.scale[1], node.scale[2]}; + auto matrix = node.matrix.empty() ? linalg::identity_t{4} : convertNodeTransform(node).getMatrix(); + + // Combine transformations + auto T = linalg::translation_matrix(translation); + auto R = linalg::rotation_matrix(rotation); + auto S = linalg::scaling_matrix(scale); + return linalg::mul(linalg::mul(linalg::mul(matrix, T), R), S); +} + +void computeBoundingBox(const tinygltf::Node &node, const tinygltf::Model &model, linalg::vec &global_min, + linalg::vec &global_max, + const linalg::mat &parentTransform = linalg::identity_t{4}) { + linalg::mat modelMatrix = linalg::mul(parentTransform, getModelMatrix(node)); + + if (node.mesh != -1) { + const tinygltf::Mesh &mesh = model.meshes[node.mesh]; + for (const auto &primitive : mesh.primitives) { + const tinygltf::Accessor &accessor = model.accessors[primitive.attributes.find("POSITION")->second]; + const tinygltf::BufferView &bufferView = model.bufferViews[accessor.bufferView]; + const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer]; + + // Adjusting buffer data interpretation to float instead of double + const float *positions = reinterpret_cast(&(buffer.data[bufferView.byteOffset + accessor.byteOffset])); + for (size_t i = 0; i < accessor.count; ++i) { + linalg::vec vertex(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]); + linalg::vec vertex_homogeneous(vertex, 1.0f); // Homogenize the vertex for transformation + vertex = linalg::mul(modelMatrix, vertex_homogeneous).xyz(); // Apply transformation and reduce back to 3D + global_min = linalg::min(global_min, vertex); + global_max = linalg::max(global_max, vertex); + } + } + } + + for (int child : node.children) { + computeBoundingBox(model.nodes[child], model, global_min, global_max, modelMatrix); + } +} + template glTF load(T loader) { tinygltf::Model model; loader(model); @@ -693,6 +741,11 @@ template glTF load(T loader) { result.root = std::move(gfxLoader.sceneMap[model.defaultScene]); result.animations = std::move(gfxLoader.animations); result.materials = std::move(gfxLoader.materials); + + // compute bounding box + if (model.scenes[model.defaultScene].nodes.size() > 0) + computeBoundingBox(model.nodes[model.scenes[model.defaultScene].nodes[0]], model, result.boundsMin, result.boundsMax); + return result; } diff --git a/shards/gfx/gltf/gltf.hpp b/shards/gfx/gltf/gltf.hpp index a62fdb6bf13..b22f5dc0c2a 100644 --- a/shards/gfx/gltf/gltf.hpp +++ b/shards/gfx/gltf/gltf.hpp @@ -13,6 +13,8 @@ struct glTF { MeshTreeDrawable::Ptr root; std::unordered_map animations; std::unordered_map materials; + linalg::vec boundsMin = {0, 0, 0}; + linalg::vec boundsMax = {0, 0, 0}; glTF() = default; diff --git a/shards/modules/gfx/gltf.cpp b/shards/modules/gfx/gltf.cpp index a44408da91a..569e3f08e11 100644 --- a/shards/modules/gfx/gltf.cpp +++ b/shards/modules/gfx/gltf.cpp @@ -555,6 +555,8 @@ struct GLTFShard { _model->root = std::static_pointer_cast(other->clone()); _model->animations = shOther.animations; _model->materials = shOther.materials; + _model->boundsMin = shOther.boundsMin; + _model->boundsMax = shOther.boundsMax; rootNodeWrapped = shOther.rootNodeWrapped; } break; case LoadMode::LoadFileStatic: @@ -573,6 +575,8 @@ struct GLTFShard { _drawable->drawable = _model->root; _drawable->animations = _model->animations; _drawable->materials = _model->materials; + _drawable->boundsMin = _model->boundsMin; + _drawable->boundsMax = _model->boundsMax; if (hasAnimationController()) { shardifyAnimationData(); diff --git a/shards/modules/gfx/shards_types.hpp b/shards/modules/gfx/shards_types.hpp index fcac4bbfc58..4bca29ad1a8 100644 --- a/shards/modules/gfx/shards_types.hpp +++ b/shards/modules/gfx/shards_types.hpp @@ -1,6 +1,7 @@ #ifndef E452293C_6700_4675_8B6E_5293674E0A33 #define E452293C_6700_4675_8B6E_5293674E0A33 +#include "shards/shards.h" #include #include #include @@ -28,6 +29,9 @@ struct SHDrawable { Variant drawable; std::unordered_map animations; std::unordered_map materials; + linalg::vec boundsMin = {0, 0, 0}; + linalg::vec boundsMax = {0, 0, 0}; + bool rootNodeWrapped{}; void assign(const std::shared_ptr &generic) {