Unravel Engine C++ Reference
Loading...
Searching...
No Matches
model_component.cpp
Go to the documentation of this file.
1#include "model_component.h"
5
6
7namespace unravel
8{
9namespace
10{
11
12auto get_bone_entity(const std::string& bone_id, const std::vector<entt::handle>& entities) -> entt::handle
13{
14 for(const auto& e : entities)
15 {
16 if(e)
17 {
18 const auto& tag = e.get<tag_component>();
19 if(tag.name == bone_id)
20 {
21 return e;
22 }
23 }
24 }
25
26 return {};
27}
28
29auto process_node_impl(const std::unique_ptr<mesh::armature_node>& node,
30 const skin_bind_data& bind_data,
31 entt::handle& parent,
32 std::vector<entt::handle>& nodes,
33 animation_pose& ref_pose) -> entt::handle
34{
35 auto entity_node = parent;
36
37 if(entity_node == parent)
38 {
39 auto& parent_trans_comp = parent.get<transform_component>();
40 const auto& children = parent_trans_comp.get_children();
41 auto found_node = get_bone_entity(node->name, children);
42 if(found_node)
43 {
44 entity_node = found_node;
45 }
46 else
47 {
48 auto& reg = *entity_node.registry();
49 entity_node = scene::create_entity(reg, node->name, parent);
50 }
51 auto& trans_comp = entity_node.get<transform_component>();
52 trans_comp.set_transform_local(node->local_transform);
53
54 nodes.emplace_back(entity_node);
55
56 if(!node->submeshes.empty())
57 {
58 auto& comp = entity_node.get_or_emplace<submesh_component>();
59 comp.submeshes = node->submeshes;
60 }
61
62 auto query = bind_data.find_bone_by_id(node->name);
63 if(query.bone && query.index >= 0)
64 {
65 auto& comp = entity_node.get_or_emplace<bone_component>();
66 comp.bone_index = query.index;
67 }
68
69 // Instead of storing anything in a bone_component,
70 // immediately add this node to the reference pose.
71 animation_pose::node ref_node;
72 ref_node.desc.index = node->index; // Use the node's index
73 ref_node.transform = node->local_transform; // Save its local (bind) transform
74 ref_pose.nodes.push_back(ref_node);
75 }
76
77 return entity_node;
78}
79
80void process_node(const std::unique_ptr<mesh::armature_node>& node,
81 const skin_bind_data& bind_data,
82 entt::handle parent,
83 std::vector<entt::handle>& nodes,
84 animation_pose& ref_pose)
85{
86 if(!parent)
87 return;
88
89 auto entity_node = process_node_impl(node, bind_data, parent, nodes, ref_pose);
90 for(auto& child : node->children)
91 {
92 process_node(child, bind_data, entity_node, nodes, ref_pose);
93 }
94}
95
96auto process_armature(const mesh& render_mesh,
97 entt::handle parent,
98 std::vector<entt::handle>& nodes,
99 animation_pose& ref_pose) -> bool
100{
101 const auto& root = render_mesh.get_armature();
102 if(!root)
103 {
104 return false;
105 }
106
107 const auto& skin_data = render_mesh.get_skin_bind_data();
108 process_node(root, skin_data, parent, nodes, ref_pose);
109
110 return true;
111}
112
113void get_transforms_for_entities(const std::vector<entt::handle>& entities,
114 size_t submesh_count,
115 pose_mat4& submesh_pose,
116 size_t bone_count,
117 pose_mat4& bone_pose)
118{
119 size_t entities_count = entities.size();
120
121 submesh_pose.transforms.clear();
122 submesh_pose.transforms.reserve(submesh_count);
123 bone_pose.transforms.resize(bone_count);
124
125 // Use std::for_each with the view's iterators
126 std::for_each(entities.begin(),
127 entities.end(),
128 [&](entt::handle e)
129 {
130 auto&& [transform_comp, submesh_comp, bone_comp] =
131 e.try_get<transform_component, submesh_component, bone_component>();
132 if(transform_comp)
133 {
134 const auto& transform_global = transform_comp->get_transform_global().get_matrix();
135
136 if(submesh_comp)
137 {
138 submesh_pose.transforms.emplace_back(transform_global);
139 }
140
141 if(bone_comp)
142 {
143 auto bone_index = bone_comp->bone_index;
144 if(bone_index < bone_pose.transforms.size())
145 {
146 bone_pose.transforms[bone_index] = transform_global;
147 }
148 }
149 }
150 });
151}
152
153} // namespace
154
155auto model_component::create_armature(bool force) -> bool
156{
157 bool has_processed_armature = !get_armature_entities().empty();
158
159 if(force || !has_processed_armature)
160 {
161 auto lod = model_.get_lod(0);
162 if(!lod)
163 {
164 return false;
165 }
166 auto mesh = lod.get();
167
168 auto owner = get_owner();
169
170 std::vector<entt::handle> armature_entities;
171 if(process_armature(*mesh, owner, armature_entities, bind_pose_))
172 {
173 set_armature_entities(armature_entities);
174
175 const auto& skin_data = mesh->get_skin_bind_data();
176 // Has skinning data?
177 if(skin_data.has_bones())
178 {
179 set_static(false);
180 }
181
182 return true;
183 }
184 }
185
186 return false;
187}
188
189auto model_component::update_armature() -> bool
190{
191 auto lod = model_.get_lod(0);
192 if(!lod)
193 {
194 return false;
195 }
196
197 auto mesh = lod.get();
198
199 const auto& armature_entities = get_armature_entities();
200 const auto& skin_data = mesh->get_skin_bind_data();
201
202 auto bones_count = skin_data.get_bones().size();
203 auto submeshes_count = mesh->get_submeshes_count();
204
205 get_transforms_for_entities(armature_entities, submeshes_count, submesh_pose_, bones_count, bone_pose_);
206
207 // Has skinning data?
208 if(skin_data.has_bones())
209 {
210 const auto& palettes = mesh->get_bone_palettes();
211 skinning_pose_.resize(palettes.size());
212 for(size_t i = 0; i < palettes.size(); ++i)
213 {
214 const auto& palette = palettes[i];
215 // Apply the bone palette.
216 skinning_pose_[i].transforms = palette.get_skinning_matrices(bone_pose_.transforms, skin_data);
217 }
218 }
219
220 return true;
221}
222
223auto model_component::init_armature(bool force) -> bool
224{
225 auto lod = model_.get_lod(0);
226 if(!lod)
227 {
228 return false;
229 }
230
231 auto mesh = lod.get();
232 const auto& skin_data = mesh->get_skin_bind_data();
233 const auto& armature = mesh->get_armature();
234
235 bool recreate_armature = force;
236 recreate_armature |= armature && submesh_pose_.transforms.empty();
237 recreate_armature |= skin_data.has_bones() && skinning_pose_.empty();
238
239 if(recreate_armature)
240 {
241 if(create_armature(force))
242 {
243 return update_armature();
244 }
245 }
246
247 return false;
248}
249
250void model_component::update_world_bounds(const math::transform& world_transform)
251{
252 auto lod = model_.get_lod(0);
253 if(!lod)
254 {
255 return;
256 }
257
258 auto mesh = lod.get();
259 if(mesh)
260 {
261 const auto& bounds = mesh->get_bounds();
262
263 world_bounds_ = math::bbox::mul(bounds, world_transform);
264 world_bounds_transform_ = world_transform;
265 }
266}
267
268auto model_component::get_world_bounds() const -> const math::bbox&
269{
270 return world_bounds_;
271}
272
273auto model_component::get_world_bounds_transform() const -> const math::transform&
274{
275 return world_bounds_transform_;
276}
277
278auto model_component::get_local_bounds() const -> const math::bbox&
279{
280 auto lod = model_.get_lod(0);
281 if(!lod)
282 {
283 return math::bbox::empty;
284 }
285
286 auto mesh = lod.get();
287 if(mesh)
288 {
289 return mesh->get_bounds();
290 }
291
292 return math::bbox::empty;
293}
294
295void model_component::set_last_render_frame(uint64_t frame)
296{
297 last_render_frame_ = frame;
298}
299
300auto model_component::get_last_render_frame() const noexcept -> uint64_t
301{
302 return last_render_frame_;
303}
304
305auto model_component::was_used_last_frame() const noexcept -> bool
306{
307 auto current_frame = gfx::get_render_frame();
308 bool is_newly_created = last_render_frame_ == 0;
309 bool was_used_recently = current_frame - last_render_frame_ <= 1;
310 return is_newly_created || was_used_recently;
311}
312
313auto model_component::is_skinned() const -> bool
314{
315 auto lod = model_.get_lod(0);
316 if(!lod)
317 {
318 return false;
319 }
320
321 auto mesh = lod.get();
322 if(mesh)
323 {
324 return mesh->get_skinned_submeshes_count() > 0;
325 }
326
327 return false;
328}
329
330auto model_component::get_bind_pose() const -> const animation_pose&
331{
332 return bind_pose_;
333}
334
335void model_component::on_create_component(entt::registry& r, entt::entity e)
336{
337 entt::handle entity(r, e);
338
339 auto& component = entity.get<model_component>();
340 component.set_owner(entity);
341
342 component.set_armature_entities({});
343}
344
345void model_component::on_destroy_component(entt::registry& r, entt::entity e)
346{
347}
348
349void model_component::set_enabled(bool enabled)
350{
351 if(enabled_ == enabled)
352 {
353 return;
354 }
355
356 touch();
357
358 enabled_ = enabled;
359}
360
361void model_component::set_casts_shadow(bool cast_shadow)
362{
363 if(casts_shadow_ == cast_shadow)
364 {
365 return;
366 }
367
368 touch();
369
370 casts_shadow_ = cast_shadow;
371}
372
373void model_component::set_static(bool is_static)
374{
375 if(static_ == is_static)
376 {
377 return;
378 }
379
380 touch();
381
382 static_ = is_static;
383}
384
385void model_component::set_casts_reflection(bool casts_reflection)
386{
387 if(casts_reflection_ == casts_reflection)
388 {
389 return;
390 }
391
392 touch();
393
394 casts_reflection_ = casts_reflection;
395}
396
397auto model_component::is_enabled() const -> bool
398{
399 return enabled_;
400}
401
402auto model_component::casts_shadow() const -> bool
403{
404 return casts_shadow_;
405}
406
407auto model_component::is_static() const -> bool
408{
409 return static_;
410}
411
412auto model_component::get_model() const -> const model&
413{
414 return model_;
415}
416
417void model_component::set_model(const model& model)
418{
419 model_ = model;
420
421 touch();
422}
423
424auto model_component::casts_reflection() const -> bool
425{
426 return casts_reflection_;
427}
428
429auto model_component::get_bone_transforms() const -> const pose_mat4&
430{
431 return bone_pose_;
432}
433
434auto model_component::get_skinning_transforms() const -> const std::vector<pose_mat4>&
435{
436 return skinning_pose_;
437}
438
439auto model_component::get_submesh_transforms() const -> const pose_mat4&
440{
441 return submesh_pose_;
442}
443
444void model_component::set_armature_entities(const std::vector<entt::handle>& entities)
445{
446 armature_entities_ = entities;
447
448 touch();
449}
450
451auto model_component::get_armature_entities() const -> const std::vector<entt::handle>&
452{
453 return armature_entities_;
454}
455
456auto model_component::get_armature_by_id(const std::string& node_id) const -> entt::handle
457{
458 for(const auto& e : armature_entities_)
459 {
460 const auto& tag_comp = e.get<tag_component>();
461 if(tag_comp.name == node_id)
462 {
463 return e;
464 }
465 }
466
467 return {};
468}
469
470auto model_component::get_armature_index_by_id(const std::string& node_id) const -> int
471{
472 int result = 0;
473 for(const auto& e : armature_entities_)
474 {
475 const auto& tag_comp = e.get<tag_component>();
476 if(tag_comp.name == node_id)
477 {
478 return result;
479 }
480
481 result++;
482 }
483
484 return -1;
485}
486
487auto model_component::get_armature_by_index(size_t index) const -> entt::handle
488{
489 if(index >= armature_entities_.size())
490 {
491 return {};
492 }
493
494 return armature_entities_[index];
495}
496
497} // namespace unravel
General purpose transformation class designed to maintain each component of the transformation separa...
Definition transform.hpp:27
Main class representing a 3D mesh with support for different LODs, submeshes, and skinning.
Definition mesh.h:310
auto get_bone_palettes() const -> const bone_palette_array_t &
Retrieves the compiled bone combination palette data if this mesh has been bound as a skin.
Definition mesh.cpp:1404
auto get_submeshes_count() const -> size_t
Gets the number of submeshes for this mesh.
Definition mesh.cpp:1448
auto get_bounds() const -> const math::bbox &
Gets the local bounding box for this mesh.
Definition mesh.cpp:1507
auto get_armature() const -> const std::unique_ptr< armature_node > &
Retrieves the armature tree of the mesh.
Definition mesh.cpp:1409
auto get_skin_bind_data() const -> const skin_bind_data &
Retrieves the skin bind data if this mesh has been bound as a skin.
Definition mesh.cpp:1399
auto get_skinned_submeshes_count() const -> size_t
Definition mesh.cpp:1473
Class that contains core data for meshes.
Structure describing a LOD group (set of meshes), LOD transitions, and their materials.
Definition model.h:42
void set_owner(entt::handle owner)
Sets the owner of the component.
uint32_t frame
Definition graphics.cpp:21
std::string tag
Definition hub.cpp:26
uint32_t get_render_frame()
Definition bbox.cpp:5
entt::entity entity
bbox & mul(const transform &t)
Transforms an axis aligned bounding box by the specified matrix.
Definition bbox.cpp:876
static bbox empty
An empty bounding box.
Definition bbox.h:316
auto create_entity(const std::string &tag={}, entt::handle parent={}) -> entt::handle
Creates an entity in the scene with an optional tag and parent.
Definition scene.cpp:225
Component that provides a tag (name or label) for an entity.
gfx::uniform_handle handle
Definition uniform.cpp:9