-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathskeleton.cpp
More file actions
200 lines (168 loc) · 5.76 KB
/
Copy pathskeleton.cpp
File metadata and controls
200 lines (168 loc) · 5.76 KB
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
////////////////////////////////////////////////////////////////////////////////
// Distributed under the Boost Software License, Version 1.0. //
// (See accompanying file LICENSE or copy at //
// https://www.boost.org/LICENSE_1_0.txt) //
////////////////////////////////////////////////////////////////////////////////
#include "graphics/skeleton.h"
#include <cassert>
#include <cstddef>
#include <queue>
#include <string>
#include <string_view>
#include <vector>
#include "core/error_handling.h"
#include "core/matrix4.h"
#include "graphics/animation/animation.h"
#include "graphics/animation/bone_query.h"
#include "graphics/bone.h"
#include "graphics/weight.h"
namespace
{
/**
* Helper function to update transformation matrices. This is done by walking
* the bone hierarchy and applying the bone transformations (optionally also
* applied animation transformations).
*
* @param transforms
* Collection to update.
*
* @param bones
* Bones to calculate.
*
* @param parents
* Index of parents of bones.
*
* @param query
* Optional object to query bone transforms (for example from an animation).
*/
void update_transforms(
std::vector<iris::Matrix4> &transforms,
const std::vector<iris::Bone> &bones,
const std::vector<std::size_t> &parents,
iris::BoneQuery *query)
{
// get inverse transform of root node
const auto inverse = iris::Matrix4::invert(bones.front().transform());
// we need some scratch space to save bone transformations as we calculate
// them, this allows us to look up a parents transform
// we don't want to update the actual bones transform as this causes issues
// when we change animation
std::vector<iris::Matrix4> cache(transforms.size());
transforms[0] = bones.front().transform();
// walk remaining bones - these are in hierarchal order so we will always
// update a parent before its children
for (auto i = 1u; i < bones.size(); ++i)
{
auto &bone = bones[i];
if (bone.is_manual())
{
// if a bone is manual then its transform is absolute, so no need
// to apply parents transform
cache[i] = bone.transform();
transforms[i] = inverse * cache[i] * bone.offset();
}
else if (query != nullptr)
{
// check if our bone exists in the supplied animation
if (const auto transform = query->transform(bone.name()); transform)
{
// apply parent transform with animation transform
cache[i] = cache[parents[i]] * transform->matrix();
transforms[i] = inverse * cache[i] * bone.offset();
}
}
else
{
// apply parent transform
cache[i] = cache[parents[i]] * bone.transform();
transforms[i] = inverse * cache[i] * bone.offset();
}
}
}
}
namespace iris
{
Skeleton::Skeleton()
: Skeleton({{"root", {}, {}, {}}})
{
}
Skeleton::Skeleton(std::vector<Bone> bones)
: bones_()
, parents_()
, transforms_(100)
{
// a root bone is one without a parent, only support one
ensure(
std::count_if(std::cbegin(bones), std::cend(bones), [](const Bone &bone) { return bone.parent().empty(); }) ==
1,
"only support one root bones");
auto root =
std::find_if(std::begin(bones), std::end(bones), [](const Bone &bone) { return bone.parent().empty(); });
// we need to copy the supplied bones in a specific order
// the aim is to flatten the hierarchy so that the root node is first,
// then its children, then grand children etc
// this ordering guarantees that a nodes always precedes its children, which
// makes updating transforms much simpler (as we can just iterate through
// the list as we know a parents transformation will be updated by the time
// we reach the child)
std::queue<std::vector<Bone>::iterator> queue;
queue.emplace(root);
parents_.emplace_back(std::numeric_limits<std::size_t>::max());
// breadth-first walk the hierarchy
do
{
const auto iter = queue.front();
queue.pop();
// move the bone to the back of our list
std::move(iter, iter + 1u, std::back_inserter(bones_));
const auto name = bones_.back().name();
// have to search all bones for children every time, not the most
// efficient but its a one off cost
for (auto i = std::begin(bones); i != std::end(bones); ++i)
{
if (i->parent() == name)
{
queue.emplace(i);
parents_.emplace_back(bones_.size() - 1u);
}
}
} while (!queue.empty());
}
const std::vector<Bone> &Skeleton::bones() const
{
return bones_;
}
const std::vector<Matrix4> &Skeleton::transforms() const
{
return transforms_;
}
void Skeleton::update(BoneQuery *query)
{
update_transforms(transforms_, bones_, parents_, query);
}
bool Skeleton::has_bone(std::string_view name) const
{
const auto bone =
std::find_if(std::cbegin(bones_), std::cend(bones_), [&name](const Bone &bone) { return bone.name() == name; });
return bone != std::cend(bones_);
}
std::size_t Skeleton::bone_index(std::string_view name) const
{
const auto bone =
std::find_if(std::cbegin(bones_), std::cend(bones_), [&name](const Bone &bone) { return bone.name() == name; });
expect(bone != std::cend(bones_), "unknown bone");
return std::distance(std::cbegin(bones_), bone);
}
Bone &Skeleton::bone(std::size_t index)
{
return bones_[index];
}
Matrix4 Skeleton::transform(std::size_t index) const
{
return transforms_[index];
}
const Bone &Skeleton::bone(std::size_t index) const
{
return bones_[index];
}
}