1 #include "ui_SkeletonBasedAnimationUI.h"
2 #include <Gui/SkeletonBasedAnimation/SkeletonBasedAnimationUI.hpp>
10 #include <Core/Animation/KeyFramedValueController.hpp>
11 #include <Core/Animation/KeyFramedValueInterpolators.hpp>
12 #include <Core/CoreMacros.hpp>
13 #include <Core/Utils/StringUtils.hpp>
15 #include <Engine/RadiumEngine.hpp>
16 #include <Engine/Scene/Entity.hpp>
17 #include <Engine/Scene/SkeletonBasedAnimationSystem.hpp>
18 #include <Engine/Scene/SkeletonComponent.hpp>
19 #include <Engine/Scene/SkinningComponent.hpp>
21 #include <Gui/Timeline/Timeline.hpp>
23 using namespace Ra::Core::Animation;
27 SkeletonBasedAnimationUI::SkeletonBasedAnimationUI(
28 Engine::Scene::SkeletonBasedAnimationSystem* system,
32 ui( new Ui::SkeletonBasedAnimationUI ),
34 m_timeline( timeline ) {
37 ui->actionXray, &QAction::toggled,
this, &SkeletonBasedAnimationUI::on_m_xray_clicked );
38 connect( ui->actionXray,
41 &SkeletonBasedAnimationUI::on_m_showSkeleton_toggled );
44 SkeletonBasedAnimationUI::~SkeletonBasedAnimationUI() {
48 void SkeletonBasedAnimationUI::selectionChanged(
const Engine::Scene::ItemEntry& entry ) {
50 m_currentSkeleton =
nullptr;
51 m_currentSkinnings.clear();
52 ui->actionLBS->setEnabled(
false );
53 ui->actionDQS->setEnabled(
false );
54 ui->actionCoR->setEnabled(
false );
55 ui->tabWidget->setEnabled(
false );
56 ui->m_skinning->setEnabled(
false );
57 if ( m_selection.m_entity ==
nullptr ) {
return; }
58 for (
auto& comp : m_selection.m_entity->getComponents() ) {
59 if (
auto curSkel =
dynamic_cast<Engine::Scene::SkeletonComponent*
>( comp.get() ) ) {
61 m_currentSkeleton = curSkel;
63 ui->tabWidget->setEnabled(
true );
64 ui->actionXray->setChecked( m_currentSkeleton->isXray() );
65 ui->m_speed->setValue(
double( m_currentSkeleton->getSpeed() ) );
66 ui->m_autoRepeat->setChecked( m_currentSkeleton->isAutoRepeat() );
67 ui->m_pingPong->setChecked( m_currentSkeleton->isPingPong() );
68 ui->m_currentAnimation->blockSignals(
true );
69 ui->m_currentAnimation->clear();
70 for (
size_t i = 0; i < m_currentSkeleton->getAnimationCount(); ++i ) {
71 ui->m_currentAnimation->addItem(
"#" + QString::number( i ) );
73 ui->m_currentAnimation->blockSignals(
false );
74 ui->m_currentAnimation->setCurrentIndex(
int( m_currentSkeleton->getAnimationId() ) );
75 ui->m_xray->setChecked( m_currentSkeleton->isXray() );
76 ui->m_showSkeleton->setChecked( m_currentSkeleton->isShowingSkeleton() );
77 ui->m_manipulation->setCurrentIndex(
78 int( m_currentSkeleton->getManipulationScheme() ) );
80 if (
auto skinComp =
dynamic_cast<Engine::Scene::SkinningComponent*
>( comp.get() ) ) {
82 m_currentSkinnings.emplace_back( skinComp );
84 ui->m_skinning->setEnabled(
true );
85 ui->actionLBS->setEnabled(
true );
86 ui->actionDQS->setEnabled(
true );
87 ui->actionCoR->setEnabled(
true );
88 ui->m_skinningMethod->setEnabled(
true );
89 ui->m_skinningMethod->setCurrentIndex(
int( skinComp->getSkinningType() ) );
90 on_m_skinningMethod_currentIndexChanged(
int( skinComp->getSkinningType() ) );
91 ui->m_showWeights->setEnabled(
true );
92 ui->m_showWeights->setChecked( skinComp->isShowingWeights() );
93 ui->m_weightsType->setEnabled(
true );
94 ui->m_weightsType->setCurrentIndex(
int( skinComp->getWeightsType() ) );
95 ui->m_normalSkinning->setEnabled(
true );
96 ui->m_normalSkinning->setCurrentIndex(
int( skinComp->getNormalSkinning() ) );
101 if ( m_currentSkeleton !=
nullptr ) {
102 auto BM = *m_currentSkeleton->getBoneRO2idx();
103 auto b_it = BM.find( m_selection.m_roIndex );
104 if ( b_it != BM.end() ) {
105 for (
auto skin : m_currentSkinnings ) {
106 skin->setWeightBone( b_it->second );
114 int SkeletonBasedAnimationUI::getActionNb() {
118 QAction* SkeletonBasedAnimationUI::getAction(
int i ) {
121 return ui->actionXray;
123 return ui->actionLBS;
125 return ui->actionDQS;
127 return ui->actionCoR;
133 void SkeletonBasedAnimationUI::postLoadFile( Engine::Scene::Entity* entity ) {
135 m_selection = Engine::Scene::ItemEntry();
136 m_currentSkeleton =
nullptr;
137 m_currentSkinnings.clear();
138 ui->tabWidget->setEnabled(
false );
139 ui->m_skinning->setEnabled(
false );
140 if ( !m_timeline ) {
return; }
142 auto c = std::find_if(
143 entity->getComponents().begin(), entity->getComponents().end(), [](
const auto& cmpt ) {
144 return ( dynamic_cast<Ra::Engine::Scene::SkeletonComponent*>( cmpt.get() ) != nullptr );
146 if ( c != entity->getComponents().end() ) {
148 auto& anim = skel->
getAnimation( skel->getAnimationId() );
149 const auto& boneMap = skel->getBoneRO2idx();
150 for (
size_t j = 0; j < anim.size(); ++j ) {
151 auto it = std::find_if(
152 boneMap->begin(), boneMap->end(), [j](
const auto& b ) { return b.second == j; } );
153 if ( it == boneMap->end() ) {
continue; }
155 m_timeline->registerKeyFramedValue(
159 "Animation_" + skel->getSkeleton()->getLabel( uint( j ) ),
160 [&anim, j, skel](
const Scalar& t ) {
161 anim[j].insertKeyFrame(
162 t, skel->getSkeleton()->getPose( HandleArray::SpaceType::LOCAL )[j] );
167 Scalar startTime = std::numeric_limits<Scalar>::max();
169 for (
const auto& animComp : entity->getComponents() ) {
171 auto [s, e] = skel->getAnimationTimeInterval();
172 startTime = std::min( startTime, s );
173 endTime = std::max( endTime, e );
176 m_timeline->onChangeStart( startTime );
177 m_timeline->onChangeEnd( endTime );
180 void SkeletonBasedAnimationUI::on_actionXray_triggered(
bool checked ) {
181 ui->m_xray->setChecked( checked );
182 on_m_xray_clicked( checked );
185 void SkeletonBasedAnimationUI::on_m_speed_valueChanged(
double arg1 ) {
186 CORE_ASSERT( m_currentSkeleton,
"Null SkeletonComponent." );
187 m_currentSkeleton->setSpeed( Scalar( arg1 ) );
191 void SkeletonBasedAnimationUI::on_m_pingPong_toggled(
bool checked ) {
192 CORE_ASSERT( m_currentSkeleton,
"Null SkeletonComponent." );
193 m_currentSkeleton->pingPong( checked );
197 void SkeletonBasedAnimationUI::on_m_autoRepeat_toggled(
bool checked ) {
198 CORE_ASSERT( m_currentSkeleton,
"Null SkeletonComponent." );
199 m_currentSkeleton->autoRepeat( checked );
203 void SkeletonBasedAnimationUI::on_m_currentAnimation_currentIndexChanged(
int index ) {
204 if ( !m_timeline ) {
return; }
206 m_currentSkeleton->useAnimation( uint( index ) );
207 auto& anim = m_currentSkeleton->getAnimation( uint( index ) );
208 const auto& boneMap = m_currentSkeleton->getBoneRO2idx();
209 auto skel = m_currentSkeleton->getSkeleton();
210 for (
size_t j = 0; j < anim.size(); ++j ) {
211 auto it = std::find_if(
212 boneMap->begin(), boneMap->end(), [j](
const auto& b ) { return b.second == j; } );
213 if ( it == boneMap->end() ) {
continue; }
214 m_timeline->unregisterKeyFramedValue( it->first,
215 "Animation_" + skel->getLabel( uint( j ) ) );
216 m_timeline->registerKeyFramedValue(
220 "Animation_" + skel->getLabel( uint( j ) ),
221 [&anim, j, skel](
const Scalar& t ) {
222 anim[j].insertKeyFrame( t, skel->getPose( HandleArray::SpaceType::LOCAL )[j] );
226 m_system->enforceUpdate();
228 m_timeline->selectionChanged( m_selection );
233 void SkeletonBasedAnimationUI::on_m_newAnim_clicked() {
234 CORE_ASSERT( m_currentSkeleton,
"Null SkeletonComponent." );
235 const int num = ui->m_currentAnimation->count();
236 m_currentSkeleton->addNewAnimation();
237 ui->m_currentAnimation->addItem(
"#" + QString::number( num ) );
238 ui->m_currentAnimation->setCurrentIndex( num );
242 void SkeletonBasedAnimationUI::on_m_removeAnim_clicked() {
243 if ( ui->m_currentAnimation->count() == 1 ) {
return; }
245 CORE_ASSERT( m_currentSkeleton,
"Null SkeletonComponent." );
246 const int removeIndex = ui->m_currentAnimation->currentIndex();
247 m_currentSkeleton->removeAnimation(
size_t( removeIndex ) );
248 ui->m_currentAnimation->removeItem( removeIndex );
249 ui->m_currentAnimation->setCurrentIndex(
int( m_currentSkeleton->getAnimationId() ) );
253 void SkeletonBasedAnimationUI::on_m_loadAnim_clicked() {
255 QString path = settings.value(
"Animation::path", QDir::homePath() ).toString();
256 path = QFileDialog::getOpenFileName(
nullptr,
"Load Animation", path,
"*.rdma" );
257 if ( path.size() == 0 ) {
return; }
258 settings.setValue(
"Animation::path", path );
259 ui->m_animFile->setText( path.split(
'/' ).last() );
262 auto& anim = m_currentSkeleton->addNewAnimation();
263 std::ifstream file( path.toStdString(), std::ios::binary );
266 Ra::Core::Transform T;
267 for (
auto& bAnim : anim ) {
268 file.read(
reinterpret_cast<char*
>( &n ),
sizeof( n ) );
269 for (
size_t i = 0; i < n; ++i ) {
270 file.read(
reinterpret_cast<char*
>( &t ),
sizeof( t ) );
271 file.read(
reinterpret_cast<char*
>( &T ),
sizeof( T ) );
272 bAnim.insertKeyFrame( t, T );
277 const int num = ui->m_currentAnimation->count();
278 ui->m_currentAnimation->addItem(
"#" + QString::number( num ) );
279 ui->m_currentAnimation->setCurrentIndex( num );
285 void SkeletonBasedAnimationUI::on_m_saveAnim_clicked() {
287 QString path = settings.value(
"Animation::path", QDir::homePath() ).toString();
288 path = QFileDialog::getSaveFileName(
nullptr,
"Load Animation", path,
"*.rdma" );
289 if ( path.size() == 0 ) {
return; }
290 settings.setValue(
"Animation::path", path );
291 ui->m_animFile->setText( path.split(
'/' ).last() );
293 const auto& anim = m_currentSkeleton->getAnimation( m_currentSkeleton->getAnimationId() );
294 std::ofstream file( path.toStdString(), std::ios::trunc | std::ios::binary );
297 for (
const auto& bAnim : anim ) {
299 file.write(
reinterpret_cast<const char*
>( &n ),
sizeof( n ) );
300 for (
const auto t : bAnim.getTimes() ) {
301 file.write(
reinterpret_cast<const char*
>( &t ),
sizeof( t ) );
302 auto kf = bAnim.at( t, linearInterpolate<Ra::Core::Transform> );
303 file.write(
reinterpret_cast<const char*
>( &kf ),
sizeof( kf ) );
308 void SkeletonBasedAnimationUI::on_m_xray_clicked(
bool checked ) {
309 m_system->setXray( checked );
313 void SkeletonBasedAnimationUI::on_m_showSkeleton_toggled(
bool checked ) {
314 m_system->toggleSkeleton( checked );
318 void SkeletonBasedAnimationUI::on_m_manipulation_currentIndexChanged(
int index ) {
323 void SkeletonBasedAnimationUI::on_m_skinningMethod_currentIndexChanged(
int newType ) {
324 CORE_ASSERT( newType >= 0 && newType < 5,
"Invalid Skinning Type" );
325 using SkinningType = Engine::Scene::SkinningComponent::SkinningType;
326 auto type = SkinningType( newType );
327 for (
auto skin : m_currentSkinnings ) {
328 skin->setSkinningType( type );
331 case SkinningType::LBS: {
332 on_actionLBS_triggered();
335 case SkinningType::DQS: {
336 on_actionDQS_triggered();
339 case SkinningType::COR: {
340 on_actionCoR_triggered();
349 void SkeletonBasedAnimationUI::on_m_showWeights_toggled(
bool checked ) {
350 for (
auto skin : m_currentSkinnings ) {
351 skin->showWeights( checked );
356 void SkeletonBasedAnimationUI::on_m_weightsType_currentIndexChanged(
int newType ) {
357 for (
auto skin : m_currentSkinnings ) {
358 skin->showWeightsType( Engine::Scene::SkinningComponent::WeightType( newType ) );
363 void SkeletonBasedAnimationUI::on_m_normalSkinning_currentIndexChanged(
int newType ) {
364 for (
auto skin : m_currentSkinnings ) {
365 skin->setNormalSkinning( Engine::Scene::SkinningComponent::NormalSkinning( newType ) );
370 void SkeletonBasedAnimationUI::on_actionLBS_triggered() {
372 ui->m_skinningMethod->setCurrentIndex(
int( SkinningType::LBS ) );
373 ui->actionLBS->setChecked(
true );
374 ui->actionDQS->setChecked(
false );
375 ui->actionCoR->setChecked(
false );
379 void SkeletonBasedAnimationUI::on_actionDQS_triggered() {
381 ui->m_skinningMethod->setCurrentIndex(
int( SkinningType::DQS ) );
382 ui->actionLBS->setChecked(
false );
383 ui->actionDQS->setChecked(
true );
384 ui->actionCoR->setChecked(
false );
388 void SkeletonBasedAnimationUI::on_actionCoR_triggered() {
390 ui->m_skinningMethod->setCurrentIndex(
int( SkinningType::COR ) );
391 ui->actionLBS->setChecked(
false );
392 ui->actionDQS->setChecked(
false );
393 ui->actionCoR->setChecked(
true );
const Animation & getAnimation(const size_t i) const
Return the i -th animation.
SkinningType
The Geometric Skinning Method.