Developer Documentation
Loading...
Searching...
No Matches
SkeletalAnimationPlugin.cc
1/*===========================================================================*\
2* *
3* OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39* *
40\*===========================================================================*/
41
42
43#include "SkeletalAnimationPlugin.hh"
44
46#include <ObjectTypes/Skeleton/SkeletonObjectData.hh>
47#include <ObjectTypes/Skeleton/SkinT.hh>
48
49#include <QInputDialog>
50#include <QMessageBox>
51
52#include "dialogs/AnimationToolbox.hh"
53#include "dialogs/AddAnimationDialog.hh"
54
55using namespace std;
56
57//------------------------------------------------------------------------------
58
63 pToolbox_(nullptr),
64 toolIcon_(nullptr),
65 bGuiUpdating_(false),
66 animationOffset_(0)
67{
68}
69
74
75//------------------------------------------------------------------------------
76
81{
82 return "SkeletalAnimation";
83}
84
85//------------------------------------------------------------------------------
86
91{
92 return "Plugin to control skeletal animations";
93}
94
95//------------------------------------------------------------------------------
96
101{
102 bGuiUpdating_ = false;
103
105 QSize size(300, 300);
106 pToolbox_->resize(size);
107
108 connect( pToolbox_->pbAttachSkin, SIGNAL(clicked()), this, SLOT(slotAttachSkin()) );
109 connect( pToolbox_->pbClearSkins, SIGNAL(clicked()), this, SLOT(slotClearSkins()) );
110
111 connect( pToolbox_->cbAnimation, SIGNAL(currentIndexChanged(int)), this, SLOT(slotAnimationIndexChanged(int)) );
112 connect( pToolbox_->hsFrame, SIGNAL(valueChanged(int)), this, SLOT(slotFrameChanged(int)) );
113 connect( pToolbox_->pbPlay, SIGNAL(clicked()), this, SLOT( playAnimation() ) );
114 connect( pToolbox_->pbStop, SIGNAL(clicked()), this, SLOT( stopAnimation() ) );
115 connect( pToolbox_->pbPrevFrame, SIGNAL(clicked()), this, SLOT( prevFrame() ) );
116 connect( pToolbox_->pbNextFrame, SIGNAL(clicked()), this, SLOT( nextFrame() ) );
117 connect( pToolbox_->sbFPS, SIGNAL(valueChanged ( int )), this, SLOT( changeFPS(int) ) );
118 connect( pToolbox_->cbSkipFrames, SIGNAL(stateChanged(int)), this, SLOT(slotSkipFramesChanged(int)) );
119 connect( pToolbox_->pbAddAnimation, SIGNAL(clicked()), this, SLOT(slotAddAnimation()) );
120 connect( pToolbox_->pbDeleteAnimation, SIGNAL(clicked()), this, SLOT(slotDeleteAnimation()) );
121
122 connect( pToolbox_->pbEditAnimation, SIGNAL(clicked()), this, SLOT(slotAnimationNameChanged()));
123
124 pToolbox_->pbAddAnimation->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"addAnimation.png") );
125 pToolbox_->pbDeleteAnimation->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"deleteAnimation.png") );
126 pToolbox_->pbEditAnimation->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"editAnimation.png") );
127
128 pToolbox_->cbMethod->addItem("Linear Blend Skinning");
129 pToolbox_->cbMethod->addItem("Dual Quaternion Blend Skinning");
130
131 pToolbox_->cbMethod->setCurrentIndex(0);
132 connect( pToolbox_->cbMethod, SIGNAL(currentIndexChanged(int)), this, SLOT(slotMethodChanged(int)) );
133 method_ = Blending::M_LBS;
134
135 toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"skeletalAnimation.png");
136 emit addToolbox( tr("Skeletal Animation") , pToolbox_, toolIcon_ );
137}
138
139//------------------------------------------------------------------------------
140
148
149//------------------------------------------------------------------------------
150
157
158//------------------------------------------------------------------------------
159
169
170//------------------------------------------------------------------------------
171
179
180//------------------------------------------------------------------------------
181
188
189//------------------------------------------------------------------------------
190
198
199//------------------------------------------------------------------------------
200
205
207
208 // Check for skin that is to be cleared
209 BaseObjectData* bod = 0;
210 if (!PluginFunctions::getObject(_id, bod)) {
211 return;
212 }
213
214 if (bod->hasObjectData(OBJECTDATA_SKIN)) {
215
216 BaseSkin* baseSkin = 0;
217
219 baseSkin = dynamic_cast<BaseSkin*> (bod->objectData(OBJECTDATA_SKIN));
220 else if (bod->dataType(DATA_POLY_MESH))
221 baseSkin = dynamic_cast<BaseSkin*> (bod->objectData(OBJECTDATA_SKIN));
222
223 if (baseSkin) {
225
226 if(skeletonObj) {
227 // Detach skin from skeleton
228 detachSkin(bod, skeletonObj);
229 }
230 }
231 }
232}
233
234//------------------------------------------------------------------------------
235
240
241 BaseObject* object;
242 PluginFunctions::getObject(_objectId,object);
243
244 if ( !object ) {
245 std::cerr << "SkeletalAnimationPlugin::checkObjectSelection : unable to get object! " << std::endl;
246 return;
247 }
248
249 if ( object->dataType() == DataType(DATA_SKELETON) ) {
250 activeSkeletons_.clear();
251
252 // get target skeletons
254 activeSkeletons_.push_back( o_it->id() );
255
256 // if no target skeleton there check if there is only one skeleton
257 if ( activeSkeletons_.empty() ){
259 activeSkeletons_.push_back( o_it->id() );
260
261 if (activeSkeletons_.size() != 1)
262 activeSkeletons_.clear();
263 }
264
265 }
266
267 UpdateUI();
268}
269
270//------------------------------------------------------------------------------
271
273
274 if(pToolbox_->cbAnimation->currentText() == "Reference Pose") {
275
276 QMessageBox::warning(0, "Not editable!", "You cannot change the reference pose's name!");
277 return;
278 }
279
280 QString newName = QInputDialog::getText(0, tr("Change Animation's Name"), tr("New Name:"),
281 QLineEdit::Normal, pToolbox_->cbAnimation->currentText());
282
283 // Set animation's name
284 for (unsigned int i=0; i < activeSkeletons_.size(); i++){
285
286 // Get active skeleton
287 BaseObjectData* baseObject = 0;
289
290 if ( baseObject == 0 )
291 continue;
292
293 SkeletonObject* skeletonObject = dynamic_cast<SkeletonObject*>(baseObject);
294 if(!skeletonObject) continue;
295 Skeleton* skeleton = PluginFunctions::skeleton(skeletonObject);
296 if(!skeleton) continue;
297
298 AnimationHandle h = skeleton->animationHandle(pToolbox_->cbAnimation->currentText().toStdString());
299 if(skeleton != 0 && h.isValid()) {
300 skeleton->animation(h)->setName(newName.toStdString());
301 skeleton->replaceAnimationName(pToolbox_->cbAnimation->currentText().toStdString(), newName.toStdString());
302 } else {
303 return;
304 }
305 }
306
307 pToolbox_->cbAnimation->setItemText(pToolbox_->cbAnimation->currentIndex(), newName);
308}
309
310//------------------------------------------------------------------------------
311
316{
317
318 for (unsigned int i=0; i < activeSkeletons_.size(); i++){
319
320 //get active skeleton
321 BaseObjectData* baseObject = 0;
323
324 if ( baseObject == 0 )
325 continue;
326
327 SkeletonObject* skeletonObject = dynamic_cast<SkeletonObject*>(baseObject);
328 Skeleton* skeleton = PluginFunctions::skeleton(skeletonObject);
329
330 if(skeleton != 0 && skeleton->animation( currentAnimationHandle() ) != 0)
331 return skeleton->animation( currentAnimationHandle() )->frameCount();
332 }
333
334 return 0;
335}
336
337//------------------------------------------------------------------------------
338
343{
344 pToolbox_->hsFrame->setValue(_iFrame);
345}
346
347//------------------------------------------------------------------------------
348
353 return pToolbox_->hsFrame->value();
354}
355
356//------------------------------------------------------------------------------
357
362 pToolbox_->sbFPS->blockSignals(true);
363 pToolbox_->sbFPS->setValue(_fps);
364 pToolbox_->sbFPS->blockSignals(false);
365
366 animationTimer_.setInterval(1000/_fps);
367}
368
369//------------------------------------------------------------------------------
370
378{
379
380 if( !_skeletonObject->hasObjectData(OBJECTDATA_SKELETON) )
381 return;
382
383 SkeletonObjectData* skeletonData = dynamic_cast< SkeletonObjectData* >( _skeletonObject->objectData(OBJECTDATA_SKELETON) );
384
385 for (unsigned int i=0; i < skeletonData->skinCount(); i++){
386
387 // deform all attached skin meshes
388 int meshId = skeletonData->skin(i);
389
390 BaseObjectData* object = 0;
391 PluginFunctions::getObject(meshId, object);
392
393 if (object == 0)
394 continue;
395
396 if ( !object->hasObjectData(OBJECTDATA_SKIN) ){
397 emit log(LOGERR, tr("Error: Attached skin mesh has no skin-object-data."));
398 continue;
399 }
400
401 BaseSkin* skin = dynamic_cast< BaseSkin* > ( object->objectData(OBJECTDATA_SKIN) );
402 skin->deformSkin(_hAni, method_ );
403
404 emit updatedObject(object->id(), UPDATE_GEOMETRY);
405 }
406}
407
408//------------------------------------------------------------------------------
409
414{
415 // do nothing during animation
416 if ( animationTimer_.isActive() )
417 return;
418
419 BaseObjectData* obj = 0;
420
422
423 if ( (obj != 0) && (obj->dataType(DATA_SKELETON)) ){
424
426
427 //check if pose changed
428 if ( sObj->activePose() != currentAnimationHandle() ){
429
430 AnimationHandle newHandle = sObj->activePose();
431
432 if ( !newHandle.isValid() ) //refPose
433 pToolbox_->cbAnimation->setCurrentIndex( 0 );
434 else
436 }
437
438 //check if animationCount changed
439 if ( (int)PluginFunctions::skeleton(sObj)->animationCount() != pToolbox_->cbAnimation->count()-1 )
440 UpdateUI();
441 }
442}
443
444//------------------------------------------------------------------------------
445
450{
451 if(bGuiUpdating_) // do not update while the gui is updating
452 return;
453
454 if ( activeSkeletons_.empty() ){
455 pToolbox_->hsFrame->setRange( 0, 0 );
456 pToolbox_->hsFrame->setTickInterval(1);
457 pToolbox_->hsFrame->setValue(0);
458
459 pToolbox_->cbAnimation->clear();
460
461 } else {
462
463 //get first active skeleton
464 BaseObjectData* skelObject = 0;
466
467 if ( skelObject == 0 )
468 return;
469
470 Skeleton* skeleton = PluginFunctions::skeleton(skelObject);
471
472 // equip the frame slider with the new range
474 if(pAnimation != 0){
475 pToolbox_->hsFrame->setRange( 0, pAnimation->frameCount() - 1 );
476 changeFPS( pAnimation->fps() );
477 }else
478 pToolbox_->hsFrame->setRange( 0, 0 );
479
480 pToolbox_->hsFrame->setTickInterval(1);
481 pToolbox_->hsFrame->setValue(0);
482
483 // pass the current frame
485 dynamic_cast<SkeletonObject*>(skelObject)->setActivePose(hAni);
486
487 emit updatedObject(skelObject->id(), UPDATE_GEOMETRY);
488
489 // update skins if available
490 UpdateSkins(skelObject, hAni);
491
493 }
494}
495
496//------------------------------------------------------------------------------
497
502{
503 // do not update while the gui is updating
504 if(bGuiUpdating_)
505 return;
506
507 for (unsigned int i=0; i < activeSkeletons_.size(); i++){
508
509 //get active skeleton
510 BaseObjectData* skelObject = 0;
512
513 if ( skelObject == 0 )
514 return;
515
516 // pass the current frame
519
520 // and update the skin if available
521 UpdateSkins(skelObject, hAni);
522
523 emit updatedObject(skelObject->id(), UPDATE_GEOMETRY);
524 }
525}
526
527//------------------------------------------------------------------------------
528
533 disconnect(&animationTimer_, 0, 0, 0);
534 animationTimer_.stop();
535 animationTimer_.setSingleShot(false);
536 animationTimer_.setInterval(1000 / pToolbox_->sbFPS->value());
537 connect(&animationTimer_,SIGNAL(timeout()),this,SLOT(animate()));
538 animationTimer_.start();
539 animationTime_.start();
540 animationOffset_ = pToolbox_->hsFrame->value();
541
542 //The Play button will just toggle between play and pause mode
543 connect( pToolbox_->pbPlay, SIGNAL(clicked()), this, SLOT( pauseAnimation() ) );
544}
545
546//------------------------------------------------------------------------------
547
552 animationTimer_.stop();
553 disconnect(&animationTimer_, 0, 0, 0);
554
555 connect( pToolbox_->pbPlay, SIGNAL(clicked()), this, SLOT( playAnimation() ) );
556}
557
558//------------------------------------------------------------------------------
559
565 pToolbox_->hsFrame->setSliderPosition(0); //Reset the playback
566}
567
568//------------------------------------------------------------------------------
569
575 pToolbox_->hsFrame->setSliderPosition(pToolbox_->hsFrame->sliderPosition() - 1);
576}
577
578//------------------------------------------------------------------------------
579
585 pToolbox_->hsFrame->setSliderPosition(pToolbox_->hsFrame->sliderPosition() + 1);
586}
587
588//------------------------------------------------------------------------------
589
597{
598 unsigned long frameCount = getNumberOfFrames();
599 if(frameCount <= 1) // 0: invalid animation, 1: just a pose
600 {
601 setFrame(0);
603 return;
604 }
605 --frameCount;
606
607 if(pToolbox_->cbSkipFrames->isChecked())
608 {
609 int fps = pToolbox_->sbFPS->value();
610 unsigned long currentFrame = animationOffset_ + (unsigned long)floor(double(animationTime_.elapsed()) / 1000.0 * fps);
611 setFrame(currentFrame % frameCount);
612 }else{
613 setFrame((getFrame() + 1) % frameCount);
614 }
615}
616
617//------------------------------------------------------------------------------
618
625{
626 if(_state == Qt::Checked)
627 {
628 // changing to skip frames as necessary
629 animationTime_.start();
630 animationOffset_ = pToolbox_->hsFrame->value();
631 }else{
632 // changing to display all frames
633 }
634}
635
636//------------------------------------------------------------------------------
637
644{
645 // change the method
646 switch(_index)
647 {
648 case 0:
649 method_ = Blending::M_LBS;
650 break;
651 default:
652 case 1:
653 method_ = Blending::M_DBS;
654 break;
655 }
656
657 // deform the skin(s) using the new method
658 for (unsigned int i=0; i < activeSkeletons_.size(); i++){
659
660 //get active skeleton
661 BaseObjectData* baseObject = 0;
663
664 if ( baseObject == 0 )
665 return;
666
667 SkeletonObject* skeletonObject = dynamic_cast<SkeletonObject*>(baseObject);
668 AnimationHandle hAni = skeletonObject->skeletonNode()->activePose();
669
670 // and update the skin
671 UpdateSkins(skeletonObject, hAni);
672 }
673
674 emit updateView();
675}
676
677//------------------------------------------------------------------------------
678
684{
685 bool& v_;
686public:
687 explicit GuiUpdatingScopeGuard(bool &_in):v_(_in){v_ = true;}
688 ~GuiUpdatingScopeGuard(){v_ = false;}
689};
690
695{
696 if(bGuiUpdating_) // gui updates object -> object is updated so gui gets updated -> loop forever
697 return;
698
700
701 if( ! activeSkeletons_.empty() )
702 {
703 //get first active skeleton
704 BaseObjectData* baseObject = 0;
706
707 if ( baseObject == 0 )
708 return;
709
710 SkeletonObject* skeletonObj = PluginFunctions::skeletonObject(baseObject);
711 Skeleton* skeleton = PluginFunctions::skeleton(baseObject);
712
713 // update the rigging and skinning group
714 // enable UI
715 if(skeletonObj->objectData(OBJECTDATA_SKELETON) == 0)
716 {
717 pToolbox_->pbAttachSkin->setEnabled(true);
718 pToolbox_->pbClearSkins->setEnabled(false);
719 pToolbox_->skinningBox->setTitle(tr("Attached Skins"));
720 }else{
721 pToolbox_->pbAttachSkin->setEnabled(true);
722 pToolbox_->pbClearSkins->setEnabled(true);
723
724 SkeletonObjectData* skelData = dynamic_cast<SkeletonObjectData*>( skeletonObj->objectData(OBJECTDATA_SKELETON) );
725 pToolbox_->skinningBox->setTitle(tr("Attached Skins (Currently: %1)").arg(skelData->skinCount()) );
726 }
727
728 // update the Skeleton group
729 AnimationHandle hAni = skeletonObj->skeletonNode()->activePose();
730
731 pToolbox_->pbAddAnimation->setEnabled(true);
732 pToolbox_->cbAnimation->setEnabled(true);
733 pToolbox_->cbAnimation->clear();
734
735 // create the reference pose
736 pToolbox_->cbAnimation->addItem("Reference Pose");
737
738 Skeleton::AnimationIterator animations = skeleton->animationsBegin();
739
740 while ( animations ) {
741 AnimationHandle anim = *animations;
742 pToolbox_->cbAnimation->addItem(skeleton->animationName(anim.animationIndex()).c_str(),QVariant::fromValue(anim.animationIndex()));
743 ++animations;
744 }
745
747
748 // get the number of frames in the animation
749 pToolbox_->hsFrame->setEnabled(true);
750 if(skeleton->animation(hAni) != 0)
751 pToolbox_->hsFrame->setRange( 0, skeleton->animation(hAni)->frameCount() - 1 );
752 pToolbox_->hsFrame->setTickInterval(1);
753 pToolbox_->hsFrame->setValue(hAni.frame());
754
755 }else{
756 // disable UI
757 pToolbox_->pbAddAnimation->setEnabled(false);
758 pToolbox_->cbAnimation->setEnabled(false);
759 pToolbox_->cbAnimation->clear();
760 pToolbox_->hsFrame->setEnabled(false);
761 pToolbox_->hsFrame->setRange(0, 0);
762 pToolbox_->hsFrame->setTickInterval(1);
763
764 pToolbox_->pbAttachSkin->setEnabled(false);
765 pToolbox_->pbClearSkins->setEnabled(false);
766 pToolbox_->skinningBox->setTitle(tr("Attached Skins"));
767 }
768}
769
770//------------------------------------------------------------------------------
771
776{
777
778 if( activeSkeletons_.size() != 1 ){
779 emit log(LOGERR, tr("Cannot bind mesh. Please select only one skeleton."));
780 return;
781 }
782
783 int meshCount = 0;
784
786 {
787 attachSkin(activeSkeletons_[0], o_it->id());
788 meshCount++;
789 }
790
791 if (meshCount == 0){
792 emit log(LOGERR, tr("Cannot bind mesh. Please select at least one mesh as target."));
793 return;
794 }
795}
796
797//------------------------------------------------------------------------------
798
800
801 // get the skeleton and make it prepare the mesh
802 Skeleton* skeleton = dynamic_cast<SkeletonObject*>(_skeletonObj)->skeleton();
803
804 //check if mesh is already a skin
805 if (_skin->hasObjectData(OBJECTDATA_SKIN) ){
806 emit log(LOGERR, tr("Cannot bind mesh as skin. Mesh is already a skin."));
807 return;
808 }
809
810 // prepare the skin template class used to deform the skin
811 SkeletonObjectData* skelData = 0;
812
813 if ( _skeletonObj->hasObjectData(OBJECTDATA_SKELETON) )
814 skelData = dynamic_cast< SkeletonObjectData* >(_skeletonObj->objectData(OBJECTDATA_SKELETON));
815 else {
816 skelData = new SkeletonObjectData();
817 _skeletonObj->setObjectData(OBJECTDATA_SKELETON, skelData);
818 }
819
820 skelData->addSkin( _skin->id() );
821
822 BaseSkin* baseSkin = 0;
823
825 bool hasSkinWeights = true;
826
827 if(_skin->dataType(DATA_TRIANGLE_MESH)){
828 hasSkinWeights = PluginFunctions::triMesh(_skin)->get_property_handle(propWeights, SKIN_WEIGHTS_PROP);
829 baseSkin = dynamic_cast<BaseSkin*>( new TriMeshSkin(skeleton, PluginFunctions::triMesh(_skin),_skeletonObj->id()) );
830 }else if(_skin->dataType(DATA_POLY_MESH)){
831 hasSkinWeights = PluginFunctions::polyMesh(_skin)->get_property_handle(propWeights, SKIN_WEIGHTS_PROP);
832 baseSkin = dynamic_cast<BaseSkin*>( new PolyMeshSkin(skeleton, PluginFunctions::polyMesh(_skin) ,_skeletonObj->id() ));
833 }
834
835 baseSkin->attachSkin();
836
837 if (hasSkinWeights)
838 baseSkin->deformSkin(currentAnimationHandle(), method_);
839
840 emit updatedObject(_skin->id(), UPDATE_GEOMETRY);
841 _skin->setObjectData(OBJECTDATA_SKIN, baseSkin);
842
843 _skeletonObj->target(true);
844 _skeletonObj->source(false);
845
846 if( !hasSkinWeights ){
847 //ask if they should be computed automatically
848 bool canCompute;
849 emit pluginExists("skinningplugin", canCompute);
850
851 if (canCompute){
852 QMessageBox msgBox;
853 msgBox.setText("The mesh is not equipped with skin weights.");
854 msgBox.setInformativeText("Do you want to compute them automatically?");
855 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
856 msgBox.setDefaultButton(QMessageBox::Yes);
857 int ret = msgBox.exec();
858
859 if (ret == QMessageBox::Yes)
860 RPC::callFunction("skinningplugin", "computeSkinWeights");
861 }
862 }
863
864 UpdateUI();
865}
866
867//------------------------------------------------------------------------------
868
873{
874 for (unsigned int i=0; i < activeSkeletons_.size(); i++)
876}
877
878//------------------------------------------------------------------------------
879
881
882 // prepare the skin template class used to deform the skin
883 SkeletonObjectData* skelData = 0;
884
885 if ( !_skeletonObj->hasObjectData(OBJECTDATA_SKELETON) )
886 return;
887
888 skelData = dynamic_cast< SkeletonObjectData* >(_skeletonObj->objectData(OBJECTDATA_SKELETON));
889
890 for (int i=skelData->skinCount()-1; i >= 0; i--){
891 //deform all attached skin meshes
892 int meshId = skelData->skin(i);
893
894 BaseObjectData* object = 0;
895 PluginFunctions::getObject(meshId, object);
896
897 if (object == 0)
898 continue;
899
900 detachSkin(object, _skeletonObj);
901 }
902}
903
904//------------------------------------------------------------------------------
905
907
908 // get the skeleton and make it prepare the mesh
909 Skeleton* skeleton = dynamic_cast<SkeletonObject*>(_skeletonObj)->skeleton();
910
911 if ( !_skeletonObj->hasObjectData(OBJECTDATA_SKELETON) ){
912 emit log(LOGERR, tr("Cannot detach skin. Skeleton has no object data."));
913 return;
914 }
915
916 //first try to remove the skin from the mesh
917 if (_skin->hasObjectData(OBJECTDATA_SKIN) ){
918
919 BaseSkin* baseSkin = 0;
920
921 if(_skin->dataType(DATA_TRIANGLE_MESH))
922 baseSkin = dynamic_cast<BaseSkin*>( new TriMeshSkin(skeleton, PluginFunctions::triMesh(_skin) ,_skeletonObj->id()));
923 else if(_skin->dataType(DATA_POLY_MESH))
924 baseSkin = dynamic_cast<BaseSkin*>( new PolyMeshSkin(skeleton, PluginFunctions::polyMesh(_skin) ,_skeletonObj->id()));
925
926 baseSkin->releaseSkin();
927 _skin->clearObjectData(OBJECTDATA_SKIN);
928 delete baseSkin;
929
930 emit updatedObject(_skin->id(), UPDATE_GEOMETRY);
931 }
932
933 // then remove the skin from the skeleton data
934 SkeletonObjectData* skelData = dynamic_cast< SkeletonObjectData* >(_skeletonObj->objectData(OBJECTDATA_SKELETON));
935
936 skelData->removeSkin( _skin->id() );
937
938 //remove the objectData if all skins are removed
939 if ( skelData->skinCount() == 0 ){
940 _skeletonObj->clearObjectData(OBJECTDATA_SKELETON);
941 delete skelData;
942 }
943
944 UpdateUI();
945}
946
947//------------------------------------------------------------------------------
948
950{
951
952 if( activeSkeletons_.size() != 1 ){
953 emit log(LOGERR, tr("Cannot add animation. Please select only one skeleton."));
954 return;
955 }
956
957 AddAnimationDialog dialog;
958 dialog.animationName->selectAll();
959 dialog.animationName->setFocus();
960
961 if ( dialog.exec() == QDialog::Accepted ){
962 if ( dialog.animationName->text() == "" ){
963 emit log(LOGERR, tr("Cannot add animation with empty name"));
964 return;
965 }
966
967 BaseObjectData* obj = 0;
968
970
971 if (obj == 0){
972 emit log(LOGERR, tr("Unable to get object"));
973 return;
974 }
975
977
978 if (skeletonObj == 0){
979 emit log(LOGERR, tr("Unable to get skeletonObject"));
980 return;
981 }
982
983 Skeleton* skeleton = PluginFunctions::skeleton(obj);
984
985 std::string stdName = dialog.animationName->text().toStdString();
986
987 if ( skeleton->animation(stdName) != 0 ){
988 emit log(LOGERR, tr("Animation with this name already exists"));
989 return;
990 }
991
992 FrameAnimationT<ACG::Vec3d>* animation = new FrameAnimationT<ACG::Vec3d>(skeleton, dialog.frames->value());
993 AnimationHandle handle = skeleton->addAnimation(stdName, animation);
994
995 //init pose to refPose
996 for (unsigned int i=0; i < skeleton->animation(handle)->frameCount(); i++){
997 handle.setFrame(i);
998 Skeleton::Pose* pose = skeleton->pose(handle);
999
1000 for (unsigned int j=0; j < skeleton->jointCount(); j++)
1001 pose->setGlobalMatrix(j, skeleton->referencePose()->globalMatrix(j) );
1002 }
1003
1004 emit updatedObject(activeSkeletons_[0], UPDATE_ALL);
1005
1006 //select the new animation
1008 }
1009}
1010
1011//------------------------------------------------------------------------------
1012
1014{
1015 int iAnimation = pToolbox_->cbAnimation->currentIndex();
1016 unsigned int animationIndex = pToolbox_->cbAnimation->itemData(iAnimation).toUInt();
1017
1018 if ( iAnimation == 0 ) {
1019 emit log(LOGERR,"Reference pose could never be removed!");
1020 } else {
1021 pToolbox_->cbAnimation->removeItem(iAnimation);
1022
1024
1025 SkeletonObject* skeletonObject = dynamic_cast<SkeletonObject*>(*o_it);
1026 Skeleton* skeleton = PluginFunctions::skeleton(skeletonObject);
1027
1028 skeleton->removeAnimation(AnimationHandle(animationIndex));
1029
1030 }
1031
1032 UpdateUI();
1033 }
1034}
1035
1036//------------------------------------------------------------------------------
1037
1044{
1045 int iAnimation = pToolbox_->cbAnimation->currentIndex();
1046 unsigned int animationId = pToolbox_->cbAnimation->itemData(iAnimation).toUInt();
1047
1048 if(iAnimation == 0)
1049 return AnimationHandle(); //This will be the reference pose, i.e. an empty animation
1050 else if(iAnimation > 0)
1051 return AnimationHandle(animationId, pToolbox_->hsFrame->value());
1052 return AnimationHandle(); // should not happen
1053}
1054
1055//------------------------------------------------------------------------------
1056
1061void SkeletalAnimationPlugin::setComboBoxPosition(unsigned int _animationIndex)
1062{
1063 for ( int i = 0 ; i < pToolbox_->cbAnimation->count(); ++i ) {
1064 unsigned int animationId = pToolbox_->cbAnimation->itemData(i).toUInt();
1065
1066 if ( animationId == _animationIndex ) {
1067 pToolbox_->cbAnimation->setCurrentIndex(i);
1068 return;
1069 }
1070
1071 }
1072
1073}
1074
1075//------------------------------------------------------------------------------
1076
1077
1078
@ LOGERR
#define DATA_POLY_MESH
Definition PolyMesh.hh:59
#define DATA_SKELETON
Definition Skeleton.hh:64
#define DATA_TRIANGLE_MESH
A handle used to refer to an animation or to a specific frame in an animation.
bool isValid() const
Returns true if the handle is valid.
size_t frame() const
Returns the selected frame (zero based)
void setFrame(size_t _iFrame)
Sets the current animation frame (not failsafe)
size_t animationIndex() const
Returns the animation index (zero based)
Stores a single animation.
Definition AnimationT.hh:59
void setObjectData(QString _dataName, PerObjectData *_data)
PerObjectData * objectData(QString _dataName)
Returns the object data pointer.
bool dataType(DataType _type) const
bool hasObjectData(QString _dataName)
Checks if object data with given name is available.
bool source()
void clearObjectData(QString _dataName)
Clear the object data pointer ( this will not delete the object!! )
bool target()
int id() const
Abstract base class for the skin template, wrapping all template versions of the skin.
Definition BaseSkin.hh:63
int skeletonId()
Holds the associated skeleton.
Definition BaseSkin.hh:122
Predefined datatypes.
Definition DataTypes.hh:83
Helper Class for UpdateUI. assigns a bool value and set it to "true". after leaving the scope,...
A general pose, used to store the frames of the animation.
Definition PoseT.hh:59
const Matrix & globalMatrix(unsigned int _joint) const
Returns the global matrix for the given joint.
void setGlobalMatrix(unsigned int _joint, const Matrix &_global, bool _keepGlobalChildPositions=true)
Sets the global coordinate system.
void exit()
Plugin gets closed.
bool bGuiUpdating_
Used to drop a few messages while the gui is being updated.
void slotAddAnimation()
Returns the number of frames in the currently active animation.
void playAnimation()
Called by the ui and starts an automatic animation.
int getFrame()
Gets the current frame number.
int animationOffset_
This frame was selected as the animation was started.
std::vector< int > activeSkeletons_
A pointer to the toolbox widget.
void slotObjectUpdated(int _id, const UpdateType &_type)
Check activePose if a skeleton was updated.
QString name()
returns the plugin name
void stopAnimation()
Called by the ui and stops the current animation.
void setFrame(int _iFrame)
Displays the given frame from the current animation and updates the view.
void pauseAnimation()
Called by the ui and stops the current animation.
void addedEmptyObject(int _id)
Update ui when the object is added.
QElapsedTimer animationTime_
Time since the animation was started, used to meet the given fps.
void slotAttachSkin()
Called by Qt as the user is trying to connect a mesh to a skeleton.
void slotSkipFramesChanged(int _state)
Called as the skip frames check box changes state.
void initializePlugin()
initialize the plugin
bool clearSkins(int skeletonId)
Returns the number of frames in the currently active animation.
void UpdateUI()
Called when the active object changes and the interface needs to be updated.
void pluginsInitialized()
final initializations
void slotAnimationNameChanged()
Returns the number of frames in the currently active animation.
void setDescriptions()
Returns the number of frames in the currently active animation.
void slotMethodChanged(int _index)
Called as the skin deformation method changed.
void slotDeleteAnimation()
Returns the number of frames in the currently active animation.
void slotAllCleared()
clear all occurred
void slotObjectSelectionChanged(int _id)
Update ui when the object selection changes.
void UpdateSkins(BaseObjectData *_pSkeletonObject, AnimationHandle &_hAni)
Changes the mesh's pose to represent the frame given by the animation handle.
void setComboBoxPosition(unsigned int _animationIndex)
Sets the animations combo box to the right entry.
void checkObjectSelection(const int _objectId)
Check source/target selection of objects.
void slotClearSkins()
Called by Qt as the user is trying to unbind a mesh from as a skeleton.
void prevFrame()
Called by the ui and goes to previous frame of the current animation.
~SkeletalAnimationPlugin()
Returns the number of frames in the currently active animation.
Blending::Method method_
The current blending method for the skin.
void changeFPS(int _fps)
Change the frames per second (FPS)
void fileOpened(int _id)
Update ui when the object is loaded.
void animate()
Iterates the animation.
AnimationHandle currentAnimationHandle()
Returns a handle describing the current frame in the active animation.
void objectDeleted(int _id)
Update ui when the object is deleted.
bool detachSkin(int skeletonId, int skinId)
Returns the number of frames in the currently active animation.
bool attachSkin(int skeletonId, int skinId)
Returns the number of frames in the currently active animation.
QTimer animationTimer_
Timer used to control animations.
QString description()
returns a plugin description
AnimationToolboxWidget * pToolbox_
A pointer to the toolbox widget.
void nextFrame()
Called by the ui and goes to next frame of the current animation.
void slotAnimationIndexChanged(int)
Called by the framework when the animation index changed.
void slotFrameChanged(int)
Called by the framework when a different frame was selected.
QIcon * toolIcon_
A pointer to the toolbox widget.
int getNumberOfFrames()
Returns the number of frames in the currently active animation.
Data object attached to the skeleton.
unsigned int skinCount()
Get the number of associated skins.
void removeSkin(int _objectId)
Remove a skin from the skeleton.
int skin(unsigned int _index)
Get the skin with given index (0 <= _index < skinCount())
void addSkin(int _objectId)
Add a skin to the skeleton.
AnimationHandle activePose()
Returns true if the picked node given by _node_idx is this objects scenegraph node.
ACG::SceneGraph::SkeletonNodeT< Skeleton > * skeletonNode()
Returns the skeleton scenegraph node.
void setActivePose(const AnimationHandle &_hAni)
Call this to set the active pose.
Iterator class for the animations attached to a skeleton.
Definition SkeletonT.hh:114
Pose * referencePose()
Returns a pointer to the reference pose.
size_t jointCount()
Returns the number of joints.
AnimationHandle addAnimation(std::string _name, Animation *_animation)
Adds a new animation to the list.
AnimationHandle animationHandle(std::string _name)
Get an AnimationHandle to the animation with the given name.
Animation * animation(std::string _name)
Returns a pointer to the animation to the given name.
void replaceAnimationName(const std::string &_strOld, const std::string &_strNew)
Returns a pointer to the pose with the given animation handle.
Definition SkeletonT.hh:190
void removeAnimation(std::string _name)
Removes an animation from the list.
Pose * pose(const AnimationHandle &_hAni)
Returns a pointer to the pose with the given animation handle.
const std::string & animationName(size_t _index)
Returns the name of the animation with the given index.
AnimationIterator animationsBegin()
Iterator over the animations.
Update type class.
Definition UpdateType.hh:59
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(4))
Geometry updated.
SkeletonObject * skeletonObject(BaseObjectData *_object)
Cast an BaseObject to a SkeletonObject if possible.
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
Skeleton * skeleton(BaseObjectData *_object)
Get a skeleton from an object.
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
const QStringList ALL_OBJECTS
Iterable object range.
void callFunction(QString _plugin, QString _functionName)
call a function in another plugin