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