• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "skinning_system.h"
17 
18 #include <algorithm>
19 #include <charconv>
20 #include <limits>
21 
22 #include <3d/ecs/components/joint_matrices_component.h>
23 #include <3d/ecs/components/mesh_component.h>
24 #include <3d/ecs/components/node_component.h>
25 #include <3d/ecs/components/render_mesh_component.h>
26 #include <3d/ecs/components/skin_component.h>
27 #include <3d/ecs/components/skin_ibm_component.h>
28 #include <3d/ecs/components/skin_joints_component.h>
29 #include <3d/ecs/components/world_matrix_component.h>
30 #include <3d/implementation_uids.h>
31 #include <3d/util/intf_picking.h>
32 #include <base/containers/fixed_string.h>
33 #include <base/math/matrix_util.h>
34 #include <core/ecs/intf_ecs.h>
35 #include <core/implementation_uids.h>
36 #include <core/intf_engine.h>
37 #include <core/log.h>
38 #include <core/namespace.h>
39 #include <core/plugin/intf_plugin_register.h>
40 #include <core/property_tools/property_api_impl.inl>
41 #include <render/implementation_uids.h>
42 #include <render/intf_render_context.h>
43 
44 #include "ecs/components/previous_joint_matrices_component.h"
45 #include "ecs/systems/node_system.h"
46 #include "util/string_util.h"
47 
48 CORE3D_BEGIN_NAMESPACE()
49 using namespace BASE_NS;
50 using namespace CORE_NS;
51 using namespace RENDER_NS;
52 
53 namespace {
54 constexpr auto SKIN_INDEX = 0U;
55 constexpr auto SKIN_JOINTS_INDEX = 1U;
56 constexpr auto JOINT_MATS_INDEX = 2U;
57 constexpr auto PREV_JOINT_MATS_INDEX = 3U;
58 constexpr auto RENDER_MESH_INDEX = 4U;
59 
UpdateJointBounds(IPicking & pick,const array_view<const float> & jointBoundsData,const Math::Mat4X4 & skinEntityWorld,JointMatricesComponent & jointMatrices)60 void UpdateJointBounds(IPicking& pick, const array_view<const float>& jointBoundsData,
61     const Math::Mat4X4& skinEntityWorld, JointMatricesComponent& jointMatrices)
62 {
63     const size_t jointBoundsDataSize = jointBoundsData.size();
64     const size_t boundsCount = jointBoundsDataSize / 6U;
65 
66     CORE_ASSERT(jointBoundsData.size() % 6U == 0); // 6: should be multiple of 6
67     CORE_ASSERT(jointMatrices.count >= boundsCount);
68 
69     static constexpr float maxFloat = std::numeric_limits<float>::max();
70     static constexpr Math::Vec3 minDefault(maxFloat, maxFloat, maxFloat);
71     static constexpr Math::Vec3 maxDefault(-maxFloat, -maxFloat, -maxFloat);
72 
73     jointMatrices.jointsAabbMin = minDefault;
74     jointMatrices.jointsAabbMax = maxDefault;
75 
76     for (size_t j = 0; j < boundsCount; j++) {
77         // Bounds that don't have any vertices will be filled with maxFloat.
78         const float* boundsData = &jointBoundsData[j * 6U];
79         if (*boundsData != maxFloat) {
80             const Math::Vec3 min(boundsData);
81             const Math::Vec3 max(boundsData + 3);
82             const Math::Mat4X4& bbWorld = skinEntityWorld * jointMatrices.jointMatrices[j];
83             const auto mam = pick.GetWorldAABB(bbWorld, min, max);
84             // Only use bounding box if it's size is > ~zero.
85             if (Math::Distance2(mam.minAABB, mam.maxAABB) > Math::EPSILON) {
86                 jointMatrices.jointAabbMinArray[j] = mam.minAABB;
87                 jointMatrices.jointAabbMaxArray[j] = mam.maxAABB;
88                 // Update the combined min/max for all joints.
89                 jointMatrices.jointsAabbMin = Math::min(jointMatrices.jointsAabbMin, mam.minAABB);
90                 jointMatrices.jointsAabbMax = Math::max(jointMatrices.jointsAabbMax, mam.maxAABB);
91                 continue;
92             }
93         }
94 
95         // This joint is not referenced by any vertex or the bounding box size is zero.
96         jointMatrices.jointAabbMinArray[j] = minDefault;
97         jointMatrices.jointAabbMaxArray[j] = maxDefault;
98     }
99     for (size_t j = boundsCount; j < jointMatrices.count; j++) {
100         // This joint is not referenced by any vertex or the bounding box size is zero.
101         jointMatrices.jointAabbMinArray[j] = minDefault;
102         jointMatrices.jointAabbMaxArray[j] = maxDefault;
103     }
104 }
105 
GetPicking(IEcs & ecs)106 IPicking* GetPicking(IEcs& ecs)
107 {
108     if (IEngine* engine = ecs.GetClassFactory().GetInterface<IEngine>(); engine) {
109         if (auto renderContext =
110                 GetInstance<IRenderContext>(*engine->GetInterface<IClassRegister>(), UID_RENDER_CONTEXT);
111             renderContext) {
112             return GetInstance<IPicking>(*renderContext->GetInterface<IClassRegister>(), UID_PICKING);
113         }
114     }
115     return nullptr;
116 }
117 } // namespace
118 
119 class SkinningSystem::SkinTask final : public IThreadPool::ITask {
120 public:
SkinTask(SkinningSystem & system,array_view<const ComponentQuery::ResultRow> results)121     SkinTask(SkinningSystem& system, array_view<const ComponentQuery::ResultRow> results)
122         : system_(system), results_(results) {};
123 
operator ()()124     void operator()() override
125     {
126         for (const ComponentQuery::ResultRow& row : results_) {
127             system_.UpdateSkin(row);
128         }
129     }
130 
131 protected:
Destroy()132     void Destroy() override {}
133 
134 private:
135     SkinningSystem& system_;
136     array_view<const ComponentQuery::ResultRow> results_;
137 };
138 
SkinningSystem(IEcs & ecs)139 SkinningSystem::SkinningSystem(IEcs& ecs)
140     : active_(true), ecs_(ecs), picking_(*GetPicking(ecs)), skinManager_(*GetManager<ISkinComponentManager>(ecs)),
141       skinIbmManager_(*GetManager<ISkinIbmComponentManager>(ecs)),
142       skinJointsManager_(*GetManager<ISkinJointsComponentManager>(ecs)),
143       jointMatricesManager_(*GetManager<IJointMatricesComponentManager>(ecs)),
144       previousJointMatricesManager_(*GetManager<IPreviousJointMatricesComponentManager>(ecs)),
145       worldMatrixManager_(*GetManager<IWorldMatrixComponentManager>(ecs)),
146       nodeManager_(*GetManager<INodeComponentManager>(ecs)),
147       renderMeshManager_(*GetManager<IRenderMeshComponentManager>(ecs)),
148       meshManager_(*GetManager<IMeshComponentManager>(ecs)), threadPool_(ecs.GetThreadPool())
149 {}
150 
SetActive(bool state)151 void SkinningSystem::SetActive(bool state)
152 {
153     active_ = state;
154 }
155 
IsActive() const156 bool SkinningSystem::IsActive() const
157 {
158     return active_;
159 }
160 
Initialize()161 void SkinningSystem::Initialize()
162 {
163     nodeSystem_ = GetSystem<INodeSystem>(ecs_);
164     {
165         const ComponentQuery::Operation operations[] = {
166             { skinJointsManager_, ComponentQuery::Operation::REQUIRE },
167             { jointMatricesManager_, ComponentQuery::Operation::REQUIRE },
168             { previousJointMatricesManager_, ComponentQuery::Operation::OPTIONAL },
169             { renderMeshManager_, ComponentQuery::Operation::OPTIONAL },
170         };
171         componentQuery_.SetEcsListenersEnabled(true);
172         componentQuery_.SetupQuery(skinManager_, operations);
173     }
174 }
175 
GetName() const176 string_view SkinningSystem::GetName() const
177 {
178     return CORE3D_NS::GetName(this);
179 }
180 
GetUid() const181 Uid SkinningSystem::GetUid() const
182 {
183     return UID;
184 }
185 
Uninitialize()186 void SkinningSystem::Uninitialize()
187 {
188     componentQuery_.SetEcsListenersEnabled(false);
189 }
190 
GetProperties()191 IPropertyHandle* SkinningSystem::GetProperties()
192 {
193     return SKINNING_SYSTEM_PROPERTIES.GetData();
194 }
195 
GetProperties() const196 const IPropertyHandle* SkinningSystem::GetProperties() const
197 {
198     return SKINNING_SYSTEM_PROPERTIES.GetData();
199 }
200 
SetProperties(const IPropertyHandle &)201 void SkinningSystem::SetProperties(const IPropertyHandle&) {}
202 
GetECS() const203 const IEcs& SkinningSystem::GetECS() const
204 {
205     return ecs_;
206 }
207 
UpdateJointTransformations(bool isEnabled,const array_view<Entity const> & jointEntities,const array_view<Math::Mat4X4 const> & ibms,JointMatricesComponent & jointMatrices,const Math::Mat4X4 & skinEntityWorldInverse)208 void SkinningSystem::UpdateJointTransformations(bool isEnabled, const array_view<Entity const>& jointEntities,
209     const array_view<Math::Mat4X4 const>& ibms, JointMatricesComponent& jointMatrices,
210     const Math::Mat4X4& skinEntityWorldInverse)
211 {
212     auto matrices = array_view<Math::Mat4X4>(
213         jointMatrices.jointMatrices, std::min(jointMatrices.count, countof(jointMatrices.jointMatrices)));
214 
215     std::transform(jointEntities.begin(), jointEntities.end(), ibms.begin(), matrices.begin(),
216         [&worldMatrixManager = worldMatrixManager_, skinEntityWorldInverse, isEnabled](
217             auto const& jointEntity, auto const& ibm) {
218             if (isEnabled) {
219                 if (const auto worldMatrixId = worldMatrixManager.GetComponentId(jointEntity);
220                     worldMatrixId != IComponentManager::INVALID_COMPONENT_ID) {
221                     auto const& jointGlobal = worldMatrixManager.Get(worldMatrixId).matrix;
222                     return skinEntityWorldInverse * jointGlobal * ibm;
223                 }
224             }
225 
226             return Math::Mat4X4 { 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f };
227         });
228 }
229 
UpdateSkin(const ComponentQuery::ResultRow & row)230 void SkinningSystem::UpdateSkin(const ComponentQuery::ResultRow& row)
231 {
232     bool isEnabled = true;
233     Math::Mat4X4 skinEntityWorld(1.0f);
234     Math::Mat4X4 skinEntityWorldInverse(1.0f);
235 
236     const SkinComponent skinComponent = skinManager_.Get(row.components[SKIN_INDEX]);
237     if (const auto worldMatrixId = worldMatrixManager_.GetComponentId(skinComponent.skinRoot);
238         worldMatrixId != IComponentManager::INVALID_COMPONENT_ID) {
239         isEnabled = nodeManager_.Get(skinComponent.skinRoot).effectivelyEnabled;
240         skinEntityWorld = worldMatrixManager_.Get(worldMatrixId).matrix;
241         skinEntityWorldInverse = Math::Inverse(skinEntityWorld);
242     }
243     if (const auto worldMatrixId = worldMatrixManager_.GetComponentId(row.entity);
244         worldMatrixId != IComponentManager::INVALID_COMPONENT_ID) {
245         skinEntityWorld = worldMatrixManager_.Get(worldMatrixId).matrix;
246     }
247 
248     const auto skinIbmHandle = skinIbmManager_.Read(skinComponent.skin);
249     if (!skinIbmHandle) {
250 #if (CORE3D_VALIDATION_ENABLED == 1)
251         auto const onceId = to_hex(row.entity.id);
252         CORE_LOG_ONCE_W(onceId.c_str(), "Invalid skin resource for entity %s", onceId.c_str());
253 #endif
254         return;
255     }
256 
257     auto const skinJointsHandle = skinJointsManager_.Read(row.components[SKIN_JOINTS_INDEX]);
258     auto const jointEntities = array_view<Entity const>(
259         skinJointsHandle->jointEntities, std::min(skinJointsHandle->count, countof(skinJointsHandle->jointEntities)));
260 
261     auto const& ibmMatrices = skinIbmHandle->matrices;
262     if (jointEntities.size() != ibmMatrices.size()) {
263 #if (CORE3D_VALIDATION_ENABLED == 1)
264         auto const onceId = to_hex(row.entity.id);
265         CORE_LOG_ONCE_W(onceId.c_str(), "Entity (%zu) and description (%zu) counts don't match for entity %s",
266             jointEntities.size(), ibmMatrices.size(), onceId.c_str());
267 #endif
268         return;
269     }
270 
271     auto jointMatricesHandle = jointMatricesManager_.Write(row.components[JOINT_MATS_INDEX]);
272     auto& jointMatrices = *jointMatricesHandle;
273     jointMatrices.count = jointEntities.size();
274 
275     UpdateJointTransformations(isEnabled, jointEntities, ibmMatrices, jointMatrices, skinEntityWorldInverse);
276     if (row.IsValidComponentId(RENDER_MESH_INDEX)) {
277         if (const auto renderMeshHandle = renderMeshManager_.Read(row.components[RENDER_MESH_INDEX]);
278             renderMeshHandle) {
279             const RenderMeshComponent& renderMeshComponent = *renderMeshHandle;
280             if (const auto meshHandle = meshManager_.Read(renderMeshComponent.mesh); meshHandle) {
281                 auto& mesh = *meshHandle;
282                 UpdateJointBounds(picking_, mesh.jointBounds, skinEntityWorld, jointMatrices);
283             }
284         }
285     }
286 }
287 
Update(bool frameRenderingQueued,uint64_t,uint64_t)288 bool SkinningSystem::Update(bool frameRenderingQueued, uint64_t, uint64_t)
289 {
290     if (!active_) {
291         return false;
292     }
293 
294     componentQuery_.Execute();
295 
296     // copy joint matrices if they have changed
297     bool missingPrevJointMatrices = false;
298     if (jointMatricesGeneration_ != jointMatricesManager_.GetGenerationCounter()) {
299         jointMatricesGeneration_ = jointMatricesManager_.GetGenerationCounter();
300 
301         for (const auto& row : componentQuery_.GetResults()) {
302             const bool hasPrev = row.IsValidComponentId(PREV_JOINT_MATS_INDEX);
303             if (hasPrev && row.IsValidComponentId(JOINT_MATS_INDEX)) {
304                 auto prev = previousJointMatricesManager_.Write(row.components[PREV_JOINT_MATS_INDEX]);
305                 auto current = jointMatricesManager_.Read(row.components[JOINT_MATS_INDEX]);
306                 prev->count = current->count;
307                 std::copy(current->jointMatrices, current->jointMatrices + current->count, prev->jointMatrices);
308             } else if (!hasPrev) {
309                 missingPrevJointMatrices = true;
310             }
311         }
312     }
313 
314     if (worldMatrixGeneration_ == worldMatrixManager_.GetGenerationCounter()) {
315         return false;
316     }
317 
318     worldMatrixGeneration_ = worldMatrixManager_.GetGenerationCounter();
319 
320     const auto threadCount = threadPool_->GetNumberOfThreads();
321     if (threadCount == 0) {
322         return 0;
323     }
324     const auto queryResults = componentQuery_.GetResults();
325     const auto resultCount = queryResults.size();
326     constexpr size_t minTaskSize = 8U;
327     const auto taskSize = Math::max(minTaskSize, resultCount / threadCount);
328     const auto tasks = resultCount / taskSize;
329 
330     tasks_.clear();
331     tasks_.reserve(tasks);
332 
333     taskResults_.clear();
334     taskResults_.reserve(tasks);
335     for (size_t i = 0; i < tasks; ++i) {
336         auto& task = tasks_.emplace_back(*this, array_view(queryResults.data() + i * taskSize, taskSize));
337         taskResults_.push_back(threadPool_->Push(IThreadPool::ITask::Ptr { &task }));
338     }
339 
340     // Skin the tail in the main thread.
341     if (const auto remaining = resultCount - (tasks * taskSize); remaining) {
342         auto finalBatch = array_view(queryResults.data() + tasks * taskSize, remaining);
343         for (const ComponentQuery::ResultRow& row : finalBatch) {
344             UpdateSkin(row);
345         }
346     }
347 
348     for (const auto& result : taskResults_) {
349         result->Wait();
350     }
351 
352     if (missingPrevJointMatrices) {
353         for (const auto& row : componentQuery_.GetResults()) {
354             // Create missing PreviousJointMatricesComponents and initialize with current.
355             if (!row.IsValidComponentId(PREV_JOINT_MATS_INDEX) && row.IsValidComponentId(JOINT_MATS_INDEX)) {
356                 previousJointMatricesManager_.Create(row.entity);
357                 auto prev = previousJointMatricesManager_.Write(row.entity);
358                 auto current = jointMatricesManager_.Read(row.components[JOINT_MATS_INDEX]);
359                 prev->count = current->count;
360                 std::copy(current->jointMatrices, current->jointMatrices + current->count, prev->jointMatrices);
361             }
362         }
363     }
364 
365     return true;
366 }
367 
CreateInstance(Entity const & skinIbmEntity,array_view<const Entity> const & joints,Entity const & entity,Entity const & skeleton)368 void SkinningSystem::CreateInstance(
369     Entity const& skinIbmEntity, array_view<const Entity> const& joints, Entity const& entity, Entity const& skeleton)
370 {
371     if (!EntityUtil::IsValid(skinIbmEntity) ||
372         !std::all_of(joints.begin(), joints.end(), [](const Entity& entity) { return EntityUtil::IsValid(entity); }) ||
373         !EntityUtil::IsValid(entity)) {
374         return;
375     }
376     if (const auto skinIbmHandle = skinIbmManager_.Read(skinIbmEntity); skinIbmHandle) {
377         auto& skinIbm = *skinIbmHandle;
378         if (skinIbm.matrices.size() != joints.size()) {
379             CORE_LOG_E(
380                 "Skin bone count doesn't match the given joints (%zu, %zu)!", skinIbm.matrices.size(), joints.size());
381             return;
382         }
383 
384         // make sure the entity has the needed components
385         skinManager_.Create(entity);
386         skinJointsManager_.Create(entity);
387         jointMatricesManager_.Create(entity);
388 
389         {
390             // set the skin resource handle
391             auto skinComponent = skinManager_.Get(entity);
392             skinComponent.skin = skinIbmEntity;
393             skinComponent.skinRoot = entity;
394             skinComponent.skeleton = skeleton;
395             skinManager_.Set(entity, skinComponent);
396         }
397 
398         if (auto skinInstanceHandle = skinJointsManager_.Write(entity); skinInstanceHandle) {
399             auto& skinInstance = *skinInstanceHandle;
400             skinInstance.count = skinIbm.matrices.size();
401             auto jointEntities = array_view<Entity>(skinInstance.jointEntities, skinInstance.count);
402             std::copy(joints.begin(), joints.end(), jointEntities.begin());
403         }
404     }
405 }
406 
CreateInstance(Entity const & skinIbmEntity,Entity const & entity,Entity const & skeleton)407 void SkinningSystem::CreateInstance(Entity const& skinIbmEntity, Entity const& entity, Entity const& skeleton)
408 {
409     if (!EntityUtil::IsValid(entity) || !skinJointsManager_.HasComponent(skinIbmEntity) ||
410         !skinIbmManager_.HasComponent(skinIbmEntity)) {
411         return;
412     }
413 
414     // validate skin joints
415     if (const auto jointsHandle = skinJointsManager_.Read(skinIbmEntity); jointsHandle) {
416         const auto joints = array_view(jointsHandle->jointEntities, jointsHandle->count);
417         if (!std::all_of(
418             joints.begin(), joints.end(), [](const Entity& entity) { return EntityUtil::IsValid(entity); })) {
419             return;
420         }
421         if (const auto skinIbmHandle = skinIbmManager_.Read(skinIbmEntity); skinIbmHandle) {
422             if (skinIbmHandle->matrices.size() != joints.size()) {
423                 CORE_LOG_E("Skin bone count doesn't match the given joints (%zu, %zu)!", skinIbmHandle->matrices.size(),
424                     joints.size());
425                 return;
426             }
427         }
428     }
429 
430     skinManager_.Create(entity);
431     if (auto skinHandle = skinManager_.Write(entity); skinHandle) {
432         skinHandle->skin = skinIbmEntity;
433         skinHandle->skinRoot = entity;
434         skinHandle->skeleton = skeleton;
435     }
436 
437     skinJointsManager_.Create(entity);
438     const auto dstJointsHandle = skinJointsManager_.Write(entity);
439     const auto srcJointsHandle = skinJointsManager_.Read(skinIbmEntity);
440     if (dstJointsHandle && srcJointsHandle) {
441         dstJointsHandle->count = srcJointsHandle->count;
442         std::copy(srcJointsHandle->jointEntities,
443             srcJointsHandle->jointEntities + static_cast<ptrdiff_t>(srcJointsHandle->count),
444             dstJointsHandle->jointEntities);
445     }
446 
447     // joint matrices will be written during Update call
448     jointMatricesManager_.Create(entity);
449 }
450 
DestroyInstance(Entity const & entity)451 void SkinningSystem::DestroyInstance(Entity const& entity)
452 {
453     if (skinManager_.HasComponent(entity)) {
454         skinManager_.Destroy(entity);
455     }
456     if (skinJointsManager_.HasComponent(entity)) {
457         skinJointsManager_.Destroy(entity);
458     }
459     if (jointMatricesManager_.HasComponent(entity)) {
460         jointMatricesManager_.Destroy(entity);
461     }
462 }
463 
ISkinningSystemInstance(IEcs & ecs)464 ISystem* ISkinningSystemInstance(IEcs& ecs)
465 {
466     return new SkinningSystem(ecs);
467 }
468 
ISkinningSystemDestroy(ISystem * instance)469 void ISkinningSystemDestroy(ISystem* instance)
470 {
471     delete static_cast<SkinningSystem*>(instance);
472 }
473 CORE3D_END_NAMESPACE()
474