PixelBullet  0.0.1
A C++ game engine
Loading...
Searching...
No Matches
morph_weights_animation_component.h
1#pragma once
2
3#include "pixelbullet/scene/components/animation_state_component.h"
4#include "pixelbullet/serialization/node.h"
5
6#include <algorithm>
7#include <cmath>
8#include <string>
9#include <string_view>
10#include <unordered_map>
11#include <vector>
12
13namespace pixelbullet
14{
16{
17 float time_seconds = 0.0f;
18 std::vector<float> weights;
19};
20
21inline bool operator==(const MorphWeightsKeyframe& lhs, const MorphWeightsKeyframe& rhs)
22{
23 return lhs.time_seconds == rhs.time_seconds && lhs.weights == rhs.weights;
24}
25
26inline bool operator!=(const MorphWeightsKeyframe& lhs, const MorphWeightsKeyframe& rhs)
27{
28 return !(lhs == rhs);
29}
30
31inline void NormalizeMorphWeightsAnimationKeys(std::vector<MorphWeightsKeyframe>& keys)
32{
33 for (auto& key : keys)
34 {
35 key.time_seconds = std::max(key.time_seconds, 0.0f);
36 }
37
38 std::stable_sort(keys.begin(), keys.end(),
39 [](const MorphWeightsKeyframe& lhs, const MorphWeightsKeyframe& rhs) { return lhs.time_seconds < rhs.time_seconds; });
40
41 std::vector<MorphWeightsKeyframe> normalized;
42 normalized.reserve(keys.size());
43 for (const MorphWeightsKeyframe& key : keys)
44 {
45 if (!normalized.empty() && std::abs(normalized.back().time_seconds - key.time_seconds) <= 0.0001f)
46 {
47 normalized.back() = key;
48 }
49 else
50 {
51 normalized.push_back(key);
52 }
53 }
54
55 keys = std::move(normalized);
56}
57
58inline float ResolveMorphWeightsAnimationClipLength(const std::vector<MorphWeightsKeyframe>& keys) noexcept
59{
60 return keys.empty() ? 0.0f : std::max(keys.back().time_seconds, 0.0f);
61}
62
64{
65 std::string name = "Default";
66 std::vector<MorphWeightsKeyframe> keys;
67};
68
69inline bool operator==(const MorphWeightsAnimationClip& lhs, const MorphWeightsAnimationClip& rhs)
70{
71 return lhs.name == rhs.name && lhs.keys == rhs.keys;
72}
73
74inline bool operator!=(const MorphWeightsAnimationClip& lhs, const MorphWeightsAnimationClip& rhs)
75{
76 return !(lhs == rhs);
77}
78
79inline std::string NormalizeMorphAnimationClipName(std::string_view name, std::string_view fallback)
80{
81 std::string normalized = name.empty() ? std::string(fallback) : std::string(name);
82 if (normalized.empty())
83 {
84 normalized = std::string(fallback);
85 }
86 return normalized;
87}
88
89inline void NormalizeMorphWeightsAnimationClip(MorphWeightsAnimationClip& clip)
90{
91 NormalizeMorphWeightsAnimationKeys(clip.keys);
92}
93
94inline float ResolveMorphWeightsAnimationClipLength(const MorphWeightsAnimationClip& clip) noexcept
95{
96 return ResolveMorphWeightsAnimationClipLength(clip.keys);
97}
98
100{
101 std::vector<MorphWeightsAnimationClip> clips;
102};
103
104inline bool operator==(const MorphWeightsAnimationComponent& lhs, const MorphWeightsAnimationComponent& rhs)
105{
106 return lhs.clips == rhs.clips;
107}
108
109inline bool operator!=(const MorphWeightsAnimationComponent& lhs, const MorphWeightsAnimationComponent& rhs)
110{
111 return !(lhs == rhs);
112}
113
114inline void NormalizeMorphWeightsAnimationComponent(MorphWeightsAnimationComponent& component)
115{
116 std::unordered_map<std::string, std::size_t> counts;
117 for (MorphWeightsAnimationClip& clip : component.clips)
118 {
119 NormalizeMorphWeightsAnimationClip(clip);
120 std::string normalized_name = NormalizeMorphAnimationClipName(clip.name, "Default");
121 const auto [it, inserted] = counts.emplace(normalized_name, 0u);
122 if (!inserted)
123 {
124 ++it->second;
125 normalized_name += " " + std::to_string(it->second + 1u);
126 }
127 clip.name = std::move(normalized_name);
128 }
129}
130
131inline const MorphWeightsAnimationClip* FindMorphWeightsAnimationClip(const MorphWeightsAnimationComponent& component,
132 const std::string_view name)
133{
134 for (const MorphWeightsAnimationClip& clip : component.clips)
135 {
136 if (clip.name == name)
137 {
138 return &clip;
139 }
140 }
141 return nullptr;
142}
143
144inline MorphWeightsAnimationClip* FindMorphWeightsAnimationClip(MorphWeightsAnimationComponent& component, const std::string_view name)
145{
146 for (MorphWeightsAnimationClip& clip : component.clips)
147 {
148 if (clip.name == name)
149 {
150 return &clip;
151 }
152 }
153 return nullptr;
154}
155
156inline const MorphWeightsAnimationClip* ResolveActiveMorphWeightsAnimationClip(const MorphWeightsAnimationComponent& component,
157 const AnimationStateComponent* state = nullptr)
158{
159 if (component.clips.empty())
160 {
161 return nullptr;
162 }
163
164 if (state != nullptr && !state->active_clip.empty())
165 {
166 if (const MorphWeightsAnimationClip* clip = FindMorphWeightsAnimationClip(component, state->active_clip))
167 {
168 return clip;
169 }
170 }
171
172 return &component.clips.front();
173}
174
175inline float ResolveMorphWeightsAnimationClipLength(const MorphWeightsAnimationComponent& component,
176 const AnimationStateComponent* state) noexcept
177{
178 if (const MorphWeightsAnimationClip* clip = ResolveActiveMorphWeightsAnimationClip(component, state))
179 {
180 return ResolveMorphWeightsAnimationClipLength(*clip);
181 }
182
183 return 0.0f;
184}
185
186inline float ResolveMorphWeightsAnimationClipLength(const MorphWeightsAnimationComponent& component) noexcept
187{
188 return ResolveMorphWeightsAnimationClipLength(component, nullptr);
189}
190
191inline Node& operator<<(Node& node, const MorphWeightsKeyframe& keyframe)
192{
193 node["timeSeconds"] << keyframe.time_seconds;
194 node["weights"] << keyframe.weights;
195 return node;
196}
197
198inline const Node& operator>>(const Node& node, MorphWeightsKeyframe& keyframe)
199{
200 node["timeSeconds"] >> keyframe.time_seconds;
201 if (node.HasProperty("weights"))
202 {
203 node["weights"] >> keyframe.weights;
204 }
205 else
206 {
207 keyframe.weights.clear();
208 }
209 keyframe.time_seconds = std::max(keyframe.time_seconds, 0.0f);
210 return node;
211}
212
213inline Node& operator<<(Node& node, const MorphWeightsAnimationClip& clip)
214{
215 node["name"] << clip.name;
216 node["keys"] << clip.keys;
217 return node;
218}
219
220inline const Node& operator>>(const Node& node, MorphWeightsAnimationClip& clip)
221{
222 if (node.HasProperty("name"))
223 {
224 node["name"] >> clip.name;
225 }
226 else
227 {
228 clip.name = "Default";
229 }
230
231 if (node.HasProperty("keys"))
232 {
233 node["keys"] >> clip.keys;
234 }
235 else
236 {
237 clip.keys.clear();
238 }
239
240 NormalizeMorphWeightsAnimationClip(clip);
241 return node;
242}
243
244inline Node& operator<<(Node& node, const MorphWeightsAnimationComponent& component)
245{
246 node["clips"] << component.clips;
247 return node;
248}
249
250inline const Node& operator>>(const Node& node, MorphWeightsAnimationComponent& component)
251{
252 component.clips.clear();
253 if (node.HasProperty("clips"))
254 {
255 node["clips"] >> component.clips;
256 }
257 else
258 {
260 clip.name = "Default";
261 if (node.HasProperty("keys"))
262 {
263 node["keys"] >> clip.keys;
264 }
265 NormalizeMorphWeightsAnimationClip(clip);
266 if (!clip.keys.empty() || node.HasProperty("enabled"))
267 {
268 component.clips.push_back(std::move(clip));
269 }
270 }
271
272 NormalizeMorphWeightsAnimationComponent(component);
273 return node;
274}
275} // namespace pixelbullet
Represents a hierarchical node capable of storing various data types and supporting YAML serializatio...
Definition node.h:45
Definition animation_state_component.h:10
Definition morph_weights_animation_component.h:64
Definition morph_weights_animation_component.h:100
Definition morph_weights_animation_component.h:16