Ensure non-master models don't update animation or the bone bounding box. Use the model resource's transformed bounding box instead of actual animated bounding box for AnimatedModel LOD calculation to ensure animation does not affect the chosen LOD level. Copy the master model's bounding box for non-master AnimatedModels directly (may result in too large bounding box, but should prevent the non-master model skipping rendering because of a missing bounding box update)

This commit is contained in:
Lasse Öörni 2014-02-24 23:40:34 +02:00
parent 062dfcf2fb
commit 74620c74f2

View File

@ -216,9 +216,11 @@ void AnimatedModel::Update(const FrameInfo& frame)
void AnimatedModel::UpdateBatches(const FrameInfo& frame)
{
const BoundingBox& worldBoundingBox = GetWorldBoundingBox();
const Matrix3x4& worldTransform = node_->GetWorldTransform();
distance_ = frame.camera_->GetDistance(worldBoundingBox.Center());
// To prevent LOD changes during animation, do not use the actual world bounding box (which changes with animation),
// but the model's bounding box transformed to world space
BoundingBox transformedBoundingBox = boundingBox_.Transformed(worldTransform);
distance_ = frame.camera_->GetDistance(transformedBoundingBox.Center());
// Note: per-geometry distances do not take skinning into account
if (batches_.Size() > 1)
@ -229,7 +231,7 @@ void AnimatedModel::UpdateBatches(const FrameInfo& frame)
else if (batches_.Size() == 1)
batches_[0].distance_ = distance_;
float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE);
float scale = transformedBoundingBox.Size().DotProduct(DOT_SCALE);
float newLodDistance = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
// If model is rendered from several views, use the minimum LOD distance for animation LOD
@ -868,8 +870,19 @@ void AnimatedModel::OnMarkedDirty(Node* node)
void AnimatedModel::OnWorldBoundingBoxUpdate()
{
// Note: do not update bone bounding box here, instead do it in either of the threaded updates
worldBoundingBox_ = boneBoundingBox_.Transformed(node_->GetWorldTransform());
if (isMaster_)
{
// Note: do not update bone bounding box here, instead do it in either of the threaded updates
worldBoundingBox_ = boneBoundingBox_.Transformed(node_->GetWorldTransform());
}
else
{
// Non-master animated models get the bounding box from the master
/// \todo If it's a skinned attachment that does not cover the whole body, it will have unnecessarily large bounds
AnimatedModel* master = node_->GetComponent<AnimatedModel>();
if (master)
worldBoundingBox_ = master->GetWorldBoundingBox();
}
}
void AnimatedModel::AssignBoneNodes()
@ -1112,13 +1125,17 @@ void AnimatedModel::UpdateAnimation(const FrameInfo& frame)
animationOrderDirty_ = false;
}
// Reset skeleton, then apply all animations
skeleton_.Reset();
for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
(*i)->Apply();
// Calculate new bone bounding box
UpdateBoneBoundingBox();
// Reset skeleton, apply all animations, calculate bones' bounding box. Make sure this is only done for the master model
// (first AnimatedModel in a node)
if (isMaster_)
{
skeleton_.Reset();
for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
(*i)->Apply();
// Calculate new bone bounding box
UpdateBoneBoundingBox();
}
animationDirty_ = false;
}