diff --git a/irr/include/SkinnedMesh.h b/irr/include/SkinnedMesh.h index 3ae6aa2a3..5a1a9c4c8 100644 --- a/irr/include/SkinnedMesh.h +++ b/irr/include/SkinnedMesh.h @@ -98,12 +98,13 @@ public: //! returns an axis aligned bounding box const core::aabbox3d &getBoundingBox() const override { - return BoundingBox; + // assert(false); // TODO refactor IMesh so that we don't have to implement this + return StaticBoundingBox; } //! set user axis aligned bounding box void setBoundingBox(const core::aabbox3df &box) override { - BoundingBox = box; + // assert(false); // TODO refactor } //! set the hardware mapping hint, for driver @@ -153,8 +154,6 @@ public: //! Moves the mesh into static position. void resetAnimation(); - void updateBoundingBox(); - //! Creates an array of joints from this mesh as children of node std::vector addJoints( IAnimatedMeshSceneNode *node, ISceneManager *smgr); @@ -339,6 +338,9 @@ public: //! Skin weights std::vector Weights; + //! Bounding box of all affected vertices, in local space + core::aabbox3df LocalBoundingBox{{0, 0, 0}}; + //! Unnecessary for loaders, will be overwritten on finalize core::matrix4 GlobalMatrix; // loaders may still choose to set this (temporarily) to calculate absolute vertex data. @@ -353,9 +355,10 @@ public: //! Animates joints based on frame input std::vector animateMesh(f32 frame); - //! TODO core::aabbox3df calculateBoundingBox( - const std::vector &transforms); + const std::vector &global_transforms); + + void recalculateBaseBoundingBoxes(); const std::vector &getAllJoints() const { return AllJoints; @@ -368,6 +371,10 @@ protected: void prepareForSkinning(); + void calculateStaticBoundingBox(); + void calculateJointBoundingBoxes(); + void calculateBufferBoundingBoxes(); + void normalizeWeights(); void calculateTangents(core::vector3df &normal, @@ -388,7 +395,8 @@ protected: // doesn't allow taking a reference to individual elements. std::vector> Vertices_Moved; - core::aabbox3d BoundingBox{{0, 0, 0}}; + //! Bounding box of just the static parts of the mesh + core::aabbox3d StaticBoundingBox{{0, 0, 0}}; f32 EndFrame; f32 FramesPerSecond; diff --git a/irr/src/CAnimatedMeshSceneNode.cpp b/irr/src/CAnimatedMeshSceneNode.cpp index 326781a36..a5cd190d8 100644 --- a/irr/src/CAnimatedMeshSceneNode.cpp +++ b/irr/src/CAnimatedMeshSceneNode.cpp @@ -148,8 +148,11 @@ void CAnimatedMeshSceneNode::OnRegisterSceneNode() IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame() { - if (Mesh->getMeshType() != EAMT_SKINNED) - return Mesh->getMesh(getFrameNr()); + if (Mesh->getMeshType() != EAMT_SKINNED) { + auto *res = Mesh->getMesh(getFrameNr()); + Box = res->getBoundingBox(); + return res; + } // As multiple scene nodes may be sharing the same skinned mesh, we have to // re-animate it every frame to ensure that this node gets the mesh that it needs. @@ -164,9 +167,9 @@ IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame() skinnedMesh->skinMesh(matrices); - skinnedMesh->updateBoundingBox(); - - Box = skinnedMesh->getBoundingBox(); + // TODO this should have happened *before* the skinning in OnAnimate; + // we should thus probably store the global matrices. + Box = skinnedMesh->calculateBoundingBox(matrices); return skinnedMesh; } @@ -206,7 +209,6 @@ void CAnimatedMeshSceneNode::render() scene::IMesh *m = getMeshForCurrentFrame(); _IRR_DEBUG_BREAK_IF(!m); - Box = m->getBoundingBox(); // HACK driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); diff --git a/irr/src/SkinnedMesh.cpp b/irr/src/SkinnedMesh.cpp index 940517802..2a05dae65 100644 --- a/irr/src/SkinnedMesh.cpp +++ b/irr/src/SkinnedMesh.cpp @@ -9,6 +9,7 @@ #include "MatrixBoneSceneNode.h" #include "SSkinMeshBuffer.h" #include "Transform.h" +#include "aabbox3d.h" #include "irrMath.h" #include "matrix4.h" #include "os.h" @@ -70,6 +71,28 @@ std::vector SkinnedMesh::animateMesh(f32 frame) return result; } +core::aabbox3df SkinnedMesh::calculateBoundingBox( + const std::vector &global_transforms) +{ + assert(global_transforms.size() == AllJoints.size()); + core::aabbox3df result = StaticBoundingBox; + // skeletal animation + for (u16 i = 0; i < AllJoints.size(); ++i) { + auto box = AllJoints[i]->LocalBoundingBox; + global_transforms[i].transformBoxEx(box); + result.addInternalBox(box); + } + // rigid animation + for (u16 i = 0; i < AllJoints.size(); ++i) { + for (u32 j : AllJoints[i]->AttachedMeshes) { + auto box = (*SkinningBuffers)[j]->BoundingBox; + global_transforms[i].transformBoxEx(box); + result.addInternalBox(box); + } + } + return result; +} + // Software Skinning void SkinnedMesh::skinMesh(const std::vector &global_matrices) @@ -131,8 +154,6 @@ void SkinnedMesh::skinMesh(const std::vector &global_matrices) //*(weight._Pos) += thisVertexMove * weight.strength; } - - buffersUsed[weight.buffer_id]->boundingBoxNeedsRecalculated(); } } @@ -314,8 +335,6 @@ void SkinnedMesh::prepareForSkinning() weight.Moved = &Vertices_Moved[buffer_id][vertex_id]; weight.StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos; weight.StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal; - - // weight._Pos=&Buffers[buffer_id]->getVertex(vertex_id)->Pos; } } @@ -326,6 +345,70 @@ void SkinnedMesh::prepareForSkinning() } } +void SkinnedMesh::calculateStaticBoundingBox() +{ + std::vector> animated(getMeshBufferCount()); + for (u32 mb = 0; mb < getMeshBufferCount(); mb++) + animated[mb] = std::vector(getMeshBuffer(mb)->getVertexCount()); + + for (auto *joint : AllJoints) { + for (auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; + animated[buffer_id][vertex_id] = true; + } + } + + bool first = true; + for (u16 mb = 0; mb < getMeshBufferCount(); mb++) { + for (u32 v = 0; v < getMeshBuffer(mb)->getVertexCount(); v++) { + if (!animated[mb][v]) { + auto pos = getMeshBuffer(mb)->getVertexBuffer()->getPosition(v); + if (!first) { + StaticBoundingBox.addInternalPoint(pos); + } else { + StaticBoundingBox.reset(pos); + first = false; + } + } + } + } +} + +void SkinnedMesh::calculateJointBoundingBoxes() +{ + for (auto *joint : AllJoints) { + bool first = true; + for (auto &weight : joint->Weights) { + if (weight.strength < 1e-6) + continue; + auto pos = weight.StaticPos; + joint->GlobalInversedMatrix.value().transformVect(pos); + if (!first) { + joint->LocalBoundingBox.addInternalPoint(pos); + } else { + joint->LocalBoundingBox.reset(pos); + first = false; + } + } + } +} + +void SkinnedMesh::calculateBufferBoundingBoxes() +{ + for (u32 j = 0; j < LocalBuffers.size(); ++j) { + // If we use skeletal animation, this will just be a bounding box of the static pose; + // if we use rigid animation, this will correctly transform the points first. + LocalBuffers[j]->recalculateBoundingBox(); + } +} + +void SkinnedMesh::recalculateBaseBoundingBoxes() { + calculateStaticBoundingBox(); + calculateJointBoundingBoxes(); + calculateBufferBoundingBoxes(); +} + void SkinnedMesh::topoSortJoints() { size_t n = AllJoints.size(); @@ -368,11 +451,6 @@ SkinnedMesh *SkinnedMeshBuilder::finalize() topoSortJoints(); - // Calculate bounding box - for (auto *buffer : LocalBuffers) { - buffer->recalculateBoundingBox(); - } - // Set array sizes for (u32 i = 0; i < LocalBuffers.size(); ++i) { Vertices_Moved.emplace_back(LocalBuffers[i]->getVertexCount()); @@ -382,7 +460,6 @@ SkinnedMesh *SkinnedMeshBuilder::finalize() std::vector matrices; matrices.reserve(AllJoints.size()); - // TODO populate with local matrices for (auto *joint : AllJoints) { if (const auto *matrix = std::get_if(&joint->transform)) matrices.push_back(*matrix); @@ -403,41 +480,11 @@ SkinnedMesh *SkinnedMeshBuilder::finalize() } } - // calculate bounding box - if (LocalBuffers.empty()) - BoundingBox.reset(0, 0, 0); - else { - irr::core::aabbox3df bb(LocalBuffers[0]->BoundingBox); - LocalBuffers[0]->Transformation.transformBoxEx(bb); - BoundingBox.reset(bb); - - for (u32 j = 1; j < LocalBuffers.size(); ++j) { - bb = LocalBuffers[j]->BoundingBox; - LocalBuffers[j]->Transformation.transformBoxEx(bb); - - BoundingBox.addInternalBox(bb); - } - } + recalculateBaseBoundingBoxes(); return this; } -void SkinnedMesh::updateBoundingBox() -{ - if (!SkinningBuffers) - return; - - BoundingBox.reset(0, 0, 0); - - for (auto *buffer : *SkinningBuffers) { - buffer->recalculateBoundingBox(); - core::aabbox3df bb = buffer->BoundingBox; - buffer->Transformation.transformBoxEx(bb); - - BoundingBox.addInternalBox(bb); - } -} - scene::SSkinMeshBuffer *SkinnedMeshBuilder::addMeshBuffer() { scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer();