Skip to content

Commit

Permalink
libgui|ModelDrawable: Optionally always transform the skeleton nodes
Browse files Browse the repository at this point in the history
By default, if there is no animation sequences active, the
transformations would revert to the bone defaults. The new flag
forces ModelDrawable to re-evaluate each node's transformation,
applying any extra custom rotations.
  • Loading branch information
skyjake committed Feb 23, 2016
1 parent bec86d7 commit a7955b5
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 13 deletions.
22 changes: 21 additions & 1 deletion doomsday/sdk/libgui/include/de/graphics/modeldrawable.h
Expand Up @@ -164,6 +164,18 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup
/// Referenced node or animation was not found in the model. @ingroup errors
DENG2_ERROR(InvalidError);

enum Flag
{
/**
* Node transformations always done, even when there are no
* animation sequences.
*/
AlwaysTransformNodes = 0x1,

DefaultFlags = 0
};
Q_DECLARE_FLAGS(Flags, Flag)

public:
Animator(Constructor sequenceConstructor = OngoingSequence::make);
Animator(ModelDrawable const &model,
Expand All @@ -173,6 +185,10 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup

void setModel(ModelDrawable const &model);

void setFlags(Flags const &flags, FlagOp op = SetFlags);

Flags flags() const;

/**
* Returns the model with which this animation is being used.
*/
Expand Down Expand Up @@ -278,6 +294,9 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup
/**
* Rendering pass. When no rendering passes are specified, all the meshes
* of the model are rendered in one pass with regular alpha blending.
*
* @todo Use GLState instead of than having individual GL parameters?
* The state must be set up to not touch viewport, clipping, etc., though.
*/
struct LIBGUI_PUBLIC Pass
{
Expand Down Expand Up @@ -559,8 +578,9 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup
DENG2_PRIVATE(d)
};

Q_DECLARE_OPERATORS_FOR_FLAGS(ModelDrawable::Appearance::Flags)
Q_DECLARE_OPERATORS_FOR_FLAGS(ModelDrawable::Animator::Flags)
Q_DECLARE_OPERATORS_FOR_FLAGS(ModelDrawable::Animator::OngoingSequence::Flags)
Q_DECLARE_OPERATORS_FOR_FLAGS(ModelDrawable::Appearance::Flags)

} // namespace de

Expand Down
60 changes: 48 additions & 12 deletions doomsday/sdk/libgui/src/graphics/modeldrawable.cpp
Expand Up @@ -737,6 +737,7 @@ DENG2_PIMPL(ModelDrawable)
void buildNodeLookup(aiNode const &node)
{
String const name = node.mName.C_Str();
qDebug() << "Node:" << name;
if(!name.isEmpty())
{
nodeNameToPtr.insert(name, &node);
Expand Down Expand Up @@ -1053,19 +1054,18 @@ DENG2_PIMPL(ModelDrawable)
struct AccumData
{
Animator const &animator;
ddouble time;
aiAnimation const *anim;
ddouble time = 0.0;
aiAnimation const *anim = nullptr;
QVector<Matrix4f> finalTransforms;

AccumData(Animator const &animator, int boneCount)
: animator(animator)
, time(0)
, anim(0)
, finalTransforms(boneCount)
{}

aiNodeAnim const *findNodeAnim(aiNode const &node) const
{
if(!anim) return nullptr;
for(duint i = 0; i < anim->mNumChannels; ++i)
{
aiNodeAnim const *na = anim->mChannels[i];
Expand All @@ -1074,18 +1074,19 @@ DENG2_PIMPL(ModelDrawable)
return na;
}
}
return 0;
return nullptr;
}
};

void accumulateAnimationTransforms(Animator const &animator,
ddouble time,
aiAnimation const &animSeq,
aiAnimation const *animSeq,
aiNode const &rootNode) const
{
AccumData data(animator, boneCount());
data.anim = &animSeq;
data.time = std::fmod(secondsToTicks(time, animSeq), animSeq.mDuration); // wrap animation
data.anim = animSeq;
// Wrap animation time.
data.time = animSeq? std::fmod(secondsToTicks(time, *animSeq), animSeq->mDuration) : time;

accumulateTransforms(rootNode, data);

Expand All @@ -1101,22 +1102,34 @@ DENG2_PIMPL(ModelDrawable)
{
Matrix4f nodeTransform = convertMatrix(node.mTransformation);

// Additional rotation?
Vector4f const axisAngle = data.animator.extraRotationForNode(node.mName.C_Str());

// Transform according to the animation sequence.
if(aiNodeAnim const *anim = data.findNodeAnim(node))
{
// Interpolate for this point in time.
Matrix4f const translation = Matrix4f::translate(interpolatePosition(data.time, *anim));
Matrix4f const scaling = Matrix4f::scale(interpolateScaling(data.time, *anim));
Matrix4f rotation = convertMatrix(aiMatrix4x4(interpolateRotation(data.time, *anim).GetMatrix()));

// Check for an additional rotation.
Vector4f const axisAngle = data.animator.extraRotationForNode(node.mName.C_Str());
if(!fequal(axisAngle.w, 0))
{
// Include the custom extra rotation.
rotation = Matrix4f::rotate(axisAngle.w, axisAngle) * rotation;
}

nodeTransform = translation * rotation * scaling;
}
else
{
// Model does not specify animation information for this node.
// Only apply the possible additional rotation.
if(!fequal(axisAngle.w, 0))
{
nodeTransform = Matrix4f::rotate(axisAngle.w, axisAngle) * nodeTransform;
}
}

Matrix4f globalTransform = parentTransform * nodeTransform;

Expand Down Expand Up @@ -1198,7 +1211,19 @@ DENG2_PIMPL(ModelDrawable)

void updateMatricesFromAnimation(Animator const *animator) const
{
if(!scene->HasAnimations() || !animator) return;
// Cannot do anything without an Animator.
if(!animator) return;

if(!scene->HasAnimations() || !animator->count())
{
// If requested, run through the bone transformations even when
// no animations are active.
if(animator->flags().testFlag(Animator::AlwaysTransformNodes))
{
accumulateAnimationTransforms(*animator, 0, nullptr, *scene->mRootNode);
return;
}
}

// Apply all current animations.
for(int i = 0; i < animator->count(); ++i)
Expand All @@ -1211,7 +1236,7 @@ DENG2_PIMPL(ModelDrawable)

accumulateAnimationTransforms(*animator,
animator->currentTime(i),
*scene->mAnimations[animSeq.animId],
scene->mAnimations[animSeq.animId],
*nodeNameToPtr[animSeq.node]);
}
}
Expand Down Expand Up @@ -1669,6 +1694,7 @@ DENG2_PIMPL_NOREF(ModelDrawable::Animator)
Constructor constructor;
ModelDrawable const *model = nullptr;
QList<OngoingSequence *> anims;
Flags flags = DefaultFlags;

Instance(Constructor ctr, ModelDrawable const *mdl = 0)
: constructor(ctr)
Expand Down Expand Up @@ -1768,6 +1794,16 @@ void ModelDrawable::Animator::setModel(ModelDrawable const &model)
d->setModel(&model);
}

void ModelDrawable::Animator::setFlags(Flags const &flags, FlagOp op)
{
applyFlagOperation(d->flags, flags, op);
}

ModelDrawable::Animator::Flags ModelDrawable::Animator::flags() const
{
return d->flags;
}

ModelDrawable const &ModelDrawable::Animator::model() const
{
DENG2_ASSERT(d->model != 0);
Expand Down

0 comments on commit a7955b5

Please sign in to comment.