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>
23using namespace Ra::Core::Animation;
27SkeletonBasedAnimationUI::SkeletonBasedAnimationUI(
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 );
44SkeletonBasedAnimationUI::~SkeletonBasedAnimationUI() {
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() ) {
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() ) );
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 );
114int SkeletonBasedAnimationUI::getActionNb() {
118QAction* SkeletonBasedAnimationUI::getAction(
int i ) {
121 return ui->actionXray;
123 return ui->actionLBS;
125 return ui->actionDQS;
127 return ui->actionCoR;
136 m_currentSkeleton =
nullptr;
137 m_currentSkinnings.clear();
138 ui->tabWidget->setEnabled(
false );
139 ui->m_skinning->setEnabled(
false );
140 if ( !m_timeline ) {
return; }
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 ) {
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] );
169 for (
const auto& animComp : entity->getComponents() ) {
171 auto [s, e] = skel->getAnimationTimeInterval();
172 startTime =
std::min( startTime, s );
176 m_timeline->onChangeStart( startTime );
177 m_timeline->onChangeEnd( endTime );
180void SkeletonBasedAnimationUI::on_actionXray_triggered(
bool checked ) {
181 ui->m_xray->setChecked( checked );
182 on_m_xray_clicked( checked );
185void SkeletonBasedAnimationUI::on_m_speed_valueChanged(
double arg1 ) {
186 CORE_ASSERT( m_currentSkeleton,
"Null SkeletonComponent." );
187 m_currentSkeleton->setSpeed( Scalar( arg1 ) );
191void SkeletonBasedAnimationUI::on_m_pingPong_toggled(
bool checked ) {
192 CORE_ASSERT( m_currentSkeleton,
"Null SkeletonComponent." );
193 m_currentSkeleton->pingPong( checked );
197void SkeletonBasedAnimationUI::on_m_autoRepeat_toggled(
bool checked ) {
198 CORE_ASSERT( m_currentSkeleton,
"Null SkeletonComponent." );
199 m_currentSkeleton->autoRepeat( checked );
203void 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 ) {
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 );
233void 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 );
242void 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() ) );
253void 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();
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 );
285void 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 ) );
308void SkeletonBasedAnimationUI::on_m_xray_clicked(
bool checked ) {
309 m_system->setXray( checked );
313void SkeletonBasedAnimationUI::on_m_showSkeleton_toggled(
bool checked ) {
314 m_system->toggleSkeleton( checked );
318void SkeletonBasedAnimationUI::on_m_manipulation_currentIndexChanged(
int index ) {
323void SkeletonBasedAnimationUI::on_m_skinningMethod_currentIndexChanged(
int newType ) {
324 CORE_ASSERT( newType >= 0 && newType < 5,
"Invalid Skinning Type" );
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();
349void SkeletonBasedAnimationUI::on_m_showWeights_toggled(
bool checked ) {
350 for (
auto skin : m_currentSkinnings ) {
351 skin->showWeights( checked );
356void SkeletonBasedAnimationUI::on_m_weightsType_currentIndexChanged(
int newType ) {
357 for (
auto skin : m_currentSkinnings ) {
363void SkeletonBasedAnimationUI::on_m_normalSkinning_currentIndexChanged(
int newType ) {
364 for (
auto skin : m_currentSkinnings ) {
370void 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 );
379void 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 );
388void 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 );
An entity is an scene element. It ties together components with a transform.
const Animation & getAnimation(const size_t i) const
Return the i -th animation.
The SkinningComponent class is responsible for applying Geometric Skinning Methods on an animated obj...
SkinningType
The Geometric Skinning Method.
WeightType
The skinning weight type.
NormalSkinning
How to skin the normal, tangent and binormal vectors.
The Timeline class provides display and management of time, as well as keyframes.