summaryrefslogtreecommitdiff
path: root/libs/assimp/tools/assimp_view/SceneAnimator.cpp
blob: c8b20229ea9016e13f21b8c1bcd9bbb3eac936c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------

Copyright (c) 2006-2022, assimp team

All rights reserved.

Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.

* Redistributions in binary form must reproduce the above
  copyright notice, this list of conditions and the
  following disclaimer in the documentation and/or other
  materials provided with the distribution.

* Neither the name of the assimp team, nor the names of its
  contributors may be used to endorse or promote products
  derived from this software without specific prior
  written permission of the assimp team.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/

/** @file SceneAnimator.cpp
 *  @brief Implementation of the utility class SceneAnimator
 */

#include "assimp_view.h"

using namespace AssimpView;

const aiMatrix4x4 IdentityMatrix;

// ------------------------------------------------------------------------------------------------
// Constructor for a given scene.
SceneAnimator::SceneAnimator(const aiScene *pScene, size_t pAnimIndex) :
        mScene(pScene),
        mCurrentAnimIndex(-1),
        mAnimEvaluator(nullptr),
        mRootNode(nullptr) {
    // build the nodes-for-bones table
    for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
        const aiMesh *mesh = pScene->mMeshes[i];
        for (unsigned int n = 0; n < mesh->mNumBones; ++n) {
            const aiBone *bone = mesh->mBones[n];

            mBoneNodesByName[bone->mName.data] = pScene->mRootNode->FindNode(bone->mName);
        }
    }

    // changing the current animation also creates the node tree for this animation
    SetAnimIndex(pAnimIndex);
}

// ------------------------------------------------------------------------------------------------
// Destructor
SceneAnimator::~SceneAnimator() {
    delete mRootNode;
    delete mAnimEvaluator;
}

// ------------------------------------------------------------------------------------------------
// Sets the animation to use for playback.
void SceneAnimator::SetAnimIndex(size_t pAnimIndex) {
    // no change
    if (pAnimIndex == static_cast<unsigned int>(mCurrentAnimIndex)) {
        return;
    }

    // kill data of the previous anim
    delete mRootNode;
    mRootNode = nullptr;
    delete mAnimEvaluator;
    mAnimEvaluator = nullptr;
    mNodesByName.clear();

    mCurrentAnimIndex = static_cast<int>(pAnimIndex);

    // create the internal node tree. Do this even in case of invalid animation index
    // so that the transformation matrices are properly set up to mimic the current scene
    mRootNode = CreateNodeTree(mScene->mRootNode, nullptr);

    // invalid anim index
    if (static_cast<unsigned int>(mCurrentAnimIndex) >= mScene->mNumAnimations) {
        return;
    }

    // create an evaluator for this animation
    mAnimEvaluator = new AnimEvaluator(mScene->mAnimations[mCurrentAnimIndex]);
}

// ------------------------------------------------------------------------------------------------
// Calculates the node transformations for the scene.
void SceneAnimator::Calculate(double pTime) {
    // invalid anim
    if (!mAnimEvaluator) {
        return;
    }

    // calculate current local transformations
    mAnimEvaluator->Evaluate(pTime);

    // and update all node transformations with the results
    UpdateTransforms(mRootNode, mAnimEvaluator->GetTransformations());
}

// ------------------------------------------------------------------------------------------------
// Retrieves the most recent local transformation matrix for the given node.
const aiMatrix4x4 &SceneAnimator::GetLocalTransform(const aiNode *node) const {
    NodeMap::const_iterator it = mNodesByName.find(node);
    if (it == mNodesByName.end()) {
        return IdentityMatrix;
    }

    return it->second->mLocalTransform;
}

// ------------------------------------------------------------------------------------------------
// Retrieves the most recent global transformation matrix for the given node.
const aiMatrix4x4 &SceneAnimator::GetGlobalTransform(const aiNode *node) const {
    NodeMap::const_iterator it = mNodesByName.find(node);
    if (it == mNodesByName.end()) {
        return IdentityMatrix;
    }

    return it->second->mGlobalTransform;
}

// ------------------------------------------------------------------------------------------------
// Calculates the bone matrices for the given mesh.
const std::vector<aiMatrix4x4> &SceneAnimator::GetBoneMatrices(const aiNode *pNode, size_t pMeshIndex /* = 0 */) {
    ai_assert(pMeshIndex < pNode->mNumMeshes);
    size_t meshIndex = pNode->mMeshes[pMeshIndex];
    ai_assert(meshIndex < mScene->mNumMeshes);
    const aiMesh *mesh = mScene->mMeshes[meshIndex];

    // resize array and initialize it with identity matrices
    mTransforms.resize(mesh->mNumBones, aiMatrix4x4());

    // calculate the mesh's inverse global transform
    aiMatrix4x4 globalInverseMeshTransform = GetGlobalTransform(pNode);
    globalInverseMeshTransform.Inverse();

    // Bone matrices transform from mesh coordinates in bind pose to mesh coordinates in skinned pose
    // Therefore the formula is offsetMatrix * currentGlobalTransform * inverseCurrentMeshTransform
    for (size_t a = 0; a < mesh->mNumBones; ++a) {
        const aiBone *bone = mesh->mBones[a];
        const aiMatrix4x4 &currentGlobalTransform = GetGlobalTransform(mBoneNodesByName[bone->mName.data]);
        mTransforms[a] = globalInverseMeshTransform * currentGlobalTransform * bone->mOffsetMatrix;
    }

    // and return the result
    return mTransforms;
}

// ------------------------------------------------------------------------------------------------
// Recursively creates an internal node structure matching the current scene and animation.
SceneAnimNode *SceneAnimator::CreateNodeTree(aiNode *pNode, SceneAnimNode *pParent) {
    // create a node
    SceneAnimNode *internalNode = new SceneAnimNode(pNode->mName.data);
    internalNode->mParent = pParent;
    mNodesByName[pNode] = internalNode;

    // copy its transformation
    internalNode->mLocalTransform = pNode->mTransformation;
    CalculateGlobalTransform(internalNode);

    // find the index of the animation track affecting this node, if any
    if (static_cast<unsigned int>(mCurrentAnimIndex) < mScene->mNumAnimations) {
        internalNode->mChannelIndex = -1;
        const aiAnimation *currentAnim = mScene->mAnimations[mCurrentAnimIndex];
        for (unsigned int a = 0; a < currentAnim->mNumChannels; a++) {
            if (currentAnim->mChannels[a]->mNodeName.data == internalNode->mName) {
                internalNode->mChannelIndex = a;
                break;
            }
        }
    }

    // continue for all child nodes and assign the created internal nodes as our children
    for (unsigned int a = 0; a < pNode->mNumChildren; ++a) {
        SceneAnimNode *childNode = CreateNodeTree(pNode->mChildren[a], internalNode);
        internalNode->mChildren.push_back(childNode);
    }

    return internalNode;
}

// ------------------------------------------------------------------------------------------------
// Recursively updates the internal node transformations from the given matrix array
void SceneAnimator::UpdateTransforms(SceneAnimNode *pNode, const std::vector<aiMatrix4x4> &pTransforms) {
    // update node local transform
    if (pNode->mChannelIndex != -1) {
        ai_assert(static_cast<unsigned int>(pNode->mChannelIndex) < pTransforms.size());
        pNode->mLocalTransform = pTransforms[pNode->mChannelIndex];
    }

    // update global transform as well
    CalculateGlobalTransform(pNode);

    // continue for all children
    for (std::vector<SceneAnimNode *>::iterator it = pNode->mChildren.begin(); it != pNode->mChildren.end(); ++it) {
        UpdateTransforms(*it, pTransforms);
    }
}

// ------------------------------------------------------------------------------------------------
// Calculates the global transformation matrix for the given internal node
void SceneAnimator::CalculateGlobalTransform(SceneAnimNode *pInternalNode) {
    // concatenate all parent transforms to get the global transform for this node
    pInternalNode->mGlobalTransform = pInternalNode->mLocalTransform;
    SceneAnimNode *node = pInternalNode->mParent;
    while (node) {
        pInternalNode->mGlobalTransform = node->mLocalTransform * pInternalNode->mGlobalTransform;
        node = node->mParent;
    }
}