Browse Source

Merge master AnimatedModel bone bounding boxes from the other AnimatedModels in the same node to prevent culling errors. Closes #1179.

Lasse Öörni 10 years ago
parent
commit
f651101a28
2 changed files with 72 additions and 10 deletions
  1. 70 10
      Source/Urho3D/Graphics/AnimatedModel.cpp
  2. 2 0
      Source/Urho3D/Graphics/AnimatedModel.h

+ 70 - 10
Source/Urho3D/Graphics/AnimatedModel.cpp

@@ -718,17 +718,10 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
 
         skeleton_.Define(skeleton);
 
-        // Remove collision information from dummy bones that do not affect skinning, to prevent them from being merged
-        // to the bounding box
-        Vector<Bone>& bones = skeleton_.GetModifiableBones();
-        for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i)
-        {
-            if (i->collisionMask_ & BONECOLLISION_BOX && i->boundingBox_.Size().Length() < M_EPSILON)
-                i->collisionMask_ &= ~BONECOLLISION_BOX;
-            if (i->collisionMask_ & BONECOLLISION_SPHERE && i->radius_ < M_EPSILON)
-                i->collisionMask_ &= ~BONECOLLISION_SPHERE;
-        }
+        // Merge bounding boxes from non-master models
+        FinalizeBoneBoundingBoxes();
 
+        Vector<Bone>& bones = skeleton_.GetModifiableBones();
         // Create scene nodes for the bones
         if (createBones)
         {
@@ -762,6 +755,11 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
         // For non-master models: use the bone nodes of the master model
         skeleton_.Define(skeleton);
 
+        // Instruct the master model to refresh (merge) its bone bounding boxes
+        AnimatedModel* master = node_->GetComponent<AnimatedModel>();
+        if (master && master != this)
+            master->FinalizeBoneBoundingBoxes();
+
         if (createBones)
         {
             Vector<Bone>& bones = skeleton_.GetModifiableBones();
@@ -996,6 +994,68 @@ void AnimatedModel::AssignBoneNodes()
     }
 }
 
+void AnimatedModel::FinalizeBoneBoundingBoxes()
+{
+    Vector<Bone>& bones = skeleton_.GetModifiableBones();
+    PODVector<AnimatedModel*> models;
+    GetComponents<AnimatedModel>(models);
+
+    if (models.Size() > 1)
+    {
+        // Reset first to the model resource's original bone bounding information if available (should be)
+        if (model_)
+        {
+            const Vector<Bone>& modelBones = model_->GetSkeleton().GetBones();
+            for (unsigned i = 0; i < bones.Size() && i < modelBones.Size(); ++i)
+            {
+                bones[i].collisionMask_ = modelBones[i].collisionMask_;
+                bones[i].radius_ = modelBones[i].radius_;
+                bones[i].boundingBox_ = modelBones[i].boundingBox_;
+            }
+        }
+
+        // Get matching bones from all non-master models and merge their bone bounding information
+        // to prevent culling errors (master model may not have geometry in all bones, or the bounds are smaller)
+        for (PODVector<AnimatedModel*>::Iterator i = models.Begin(); i != models.End(); ++i)
+        {
+            if ((*i) == this)
+                continue;
+
+            Skeleton& otherSkeleton = (*i)->GetSkeleton();
+            for (Vector<Bone>::Iterator j = bones.Begin(); j != bones.End(); ++j)
+            {
+                Bone* otherBone = otherSkeleton.GetBone(j->nameHash_);
+                if (otherBone)
+                {
+                    if (otherBone->collisionMask_ & BONECOLLISION_SPHERE)
+                    {
+                        j->collisionMask_ |= BONECOLLISION_SPHERE;
+                        j->radius_ = Max(j->radius_, otherBone->radius_);
+                    }
+                    if (otherBone->collisionMask_ & BONECOLLISION_BOX)
+                    {
+                        j->collisionMask_ |= BONECOLLISION_BOX;
+                        if (j->boundingBox_.Defined())
+                            j->boundingBox_.Merge(otherBone->boundingBox_);
+                        else
+                            j->boundingBox_.Define(otherBone->boundingBox_);
+                    }
+                }
+            }
+        }
+    }
+
+    // Remove collision information from dummy bones that do not affect skinning, to prevent them from being merged
+    // to the bounding box and making it artificially large
+    for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i)
+    {
+        if (i->collisionMask_ & BONECOLLISION_BOX && i->boundingBox_.Size().Length() < M_EPSILON)
+            i->collisionMask_ &= ~BONECOLLISION_BOX;
+        if (i->collisionMask_ & BONECOLLISION_SPHERE && i->radius_ < M_EPSILON)
+            i->collisionMask_ &= ~BONECOLLISION_SPHERE;
+    }
+}
+
 void AnimatedModel::RemoveRootBone()
 {
     Bone* rootBone = skeleton_.GetRootBone();

+ 2 - 0
Source/Urho3D/Graphics/AnimatedModel.h

@@ -177,6 +177,8 @@ protected:
 private:
     /// Assign skeleton and animation bone node references as a postprocess. Called by ApplyAttributes.
     void AssignBoneNodes();
+    /// Finalize master model bone bounding boxes by merging from matching non-master bones.. Performed whenever any of the AnimatedModels in the same node changes its model.
+    void FinalizeBoneBoundingBoxes();
     /// Remove (old) skeleton root bone.
     void RemoveRootBone();
     /// Mark animation and skinning to require an update.