Developer Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 * *
44 * $Revision$ *
45 * $LastChangedBy$ *
46 * $Date$ *
47 * *
48 \*===========================================================================*/
49 
50 #include <QInputDialog>
51 #include <QMessageBox>
52 
53 #include "SkeletalAnimationPlugin.hh"
54 #include <ObjectTypes/Skeleton/SkeletonObjectData.hh>
55 #include <ObjectTypes/Skeleton/SkinT.hh>
56 
57 #include "AddAnimationDialog.hh"
58 
59 using namespace std;
60 
61 //------------------------------------------------------------------------------
62 
67  pToolbox_(0),
68  toolIcon_(0),
69  bGuiUpdating_(false),
70  animationOffset_(0)
71 {
72 }
73 
74 //------------------------------------------------------------------------------
75 
80 {
81  return "SkeletalAnimation";
82 }
83 
84 //------------------------------------------------------------------------------
85 
90 {
91  return "Plugin to control skeletal animations";
92 }
93 
94 //------------------------------------------------------------------------------
95 
100 {
101  bGuiUpdating_ = false;
102 
104  QSize size(300, 300);
105  pToolbox_->resize(size);
106 
107  connect( pToolbox_->pbAttachSkin, SIGNAL(clicked()), this, SLOT(slotAttachSkin()) );
108  connect( pToolbox_->pbClearSkins, SIGNAL(clicked()), this, SLOT(slotClearSkins()) );
109 
110  connect( pToolbox_->cbAnimation, SIGNAL(currentIndexChanged(int)), this, SLOT(slotAnimationIndexChanged(int)) );
111  connect( pToolbox_->hsFrame, SIGNAL(valueChanged(int)), this, SLOT(slotFrameChanged(int)) );
112  connect( pToolbox_->pbPlay, SIGNAL(clicked()), this, SLOT( playAnimation() ) );
113  connect( pToolbox_->pbStop, SIGNAL(clicked()), this, SLOT( stopAnimation() ) );
114  connect( pToolbox_->pbPrevFrame, SIGNAL(clicked()), this, SLOT( prevFrame() ) );
115  connect( pToolbox_->pbNextFrame, SIGNAL(clicked()), this, SLOT( nextFrame() ) );
116  connect( pToolbox_->sbFPS, SIGNAL(valueChanged ( int )), this, SLOT( changeFPS(int) ) );
117  connect( pToolbox_->cbSkipFrames, SIGNAL(stateChanged(int)), this, SLOT(slotSkipFramesChanged(int)) );
118  connect( pToolbox_->pbAddAnimation, SIGNAL(clicked()), this, SLOT(slotAddAnimation()) );
119  connect( pToolbox_->pbDeleteAnimation, SIGNAL(clicked()), this, SLOT(slotDeleteAnimation()) );
120 
121  connect( pToolbox_->pbEditAnimation, SIGNAL(clicked()), this, SLOT(slotAnimationNameChanged()));
122 
123  pToolbox_->pbAddAnimation->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"addAnimation.png") );
124  pToolbox_->pbDeleteAnimation->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"deleteAnimation.png") );
125  pToolbox_->pbEditAnimation->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"editAnimation.png") );
126 
127  pToolbox_->cbMethod->addItem("Linear Blend Skinning");
128  pToolbox_->cbMethod->addItem("Dual Quaternion Blend Skinning");
129 
130  pToolbox_->cbMethod->setCurrentIndex(0);
131  connect( pToolbox_->cbMethod, SIGNAL(currentIndexChanged(int)), this, SLOT(slotMethodChanged(int)) );
132  method_ = BaseSkin::M_LBS;
133 
134  toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"skeletalAnimation.png");
135  emit addToolbox( tr("Skeletal Animation") , pToolbox_, toolIcon_ );
136 }
137 
138 //------------------------------------------------------------------------------
139 
144 {
145  emit setDescriptions();
146 }
147 
148 //------------------------------------------------------------------------------
149 
154 {
155 }
156 
157 //------------------------------------------------------------------------------
158 
163 {
164  activeSkeletons_.clear();
165 
167 }
168 
169 //------------------------------------------------------------------------------
170 
175 {
177 }
178 
179 //------------------------------------------------------------------------------
180 
186 }
187 
188 //------------------------------------------------------------------------------
189 
194 
196 }
197 
198 //------------------------------------------------------------------------------
199 
204 
206 
207  // Check for skin that is to be cleared
208  BaseObjectData* bod = 0;
209  if (!PluginFunctions::getObject(_id, bod)) {
210  return;
211  }
212 
213  if (bod->hasObjectData(OBJECTDATA_SKIN)) {
214 
215  BaseSkin* baseSkin = 0;
216 
217  if (bod->dataType(DATA_TRIANGLE_MESH))
218  baseSkin = dynamic_cast<BaseSkin*> (bod->objectData(OBJECTDATA_SKIN));
219  else if (bod->dataType(DATA_POLY_MESH))
220  baseSkin = dynamic_cast<BaseSkin*> (bod->objectData(OBJECTDATA_SKIN));
221 
222  if (baseSkin) {
223  BaseObjectData* skeletonObj = PluginFunctions::skeletonObject(baseSkin->skeletonId());
224 
225  if(skeletonObj) {
226  // Detach skin from skeleton
227  detachSkin(bod, skeletonObj);
228  }
229  }
230  }
231 }
232 
233 //------------------------------------------------------------------------------
234 
239 
240  BaseObject* object;
241  PluginFunctions::getObject(_objectId,object);
242 
243  if ( !object ) {
244  std::cerr << "SkeletalAnimationPlugin::checkObjectSelection : unable to get object! " << std::endl;
245  return;
246  }
247 
248  if ( object->dataType() == DataType(DATA_SKELETON) ) {
249  activeSkeletons_.clear();
250 
251  // get target skeletons
253  activeSkeletons_.push_back( o_it->id() );
254 
255  // if no target skeleton there check if there is only one skeleton
256  if ( activeSkeletons_.empty() ){
258  activeSkeletons_.push_back( o_it->id() );
259 
260  if (activeSkeletons_.size() != 1)
261  activeSkeletons_.clear();
262  }
263 
264  }
265 
266  UpdateUI();
267 }
268 
269 //------------------------------------------------------------------------------
270 
271 void SkeletalAnimationPlugin::slotAnimationNameChanged() {
272 
273  if(pToolbox_->cbAnimation->currentText() == "Reference Pose") {
274 
275  QMessageBox::warning(0, "Not editable!", "You cannot change the reference pose's name!");
276  return;
277  }
278 
279  QString newName = QInputDialog::getText(0, tr("Change Animation's Name"), tr("New Name:"),
280  QLineEdit::Normal, pToolbox_->cbAnimation->currentText());
281 
282  // Set animation's name
283  for (unsigned int i=0; i < activeSkeletons_.size(); i++){
284 
285  // Get active skeleton
286  BaseObjectData* baseObject = 0;
287  PluginFunctions::getObject(activeSkeletons_[i], baseObject);
288 
289  if ( baseObject == 0 )
290  continue;
291 
292  SkeletonObject* skeletonObject = dynamic_cast<SkeletonObject*>(baseObject);
293  if(!skeletonObject) continue;
294  Skeleton* skeleton = PluginFunctions::skeleton(skeletonObject);
295  if(!skeleton) continue;
296 
297  AnimationHandle h = skeleton->animationHandle(pToolbox_->cbAnimation->currentText().toStdString());
298  if(skeleton != 0 && h.isValid()) {
299  skeleton->animation(h)->setName(newName.toStdString());
300  skeleton->replaceAnimationName(pToolbox_->cbAnimation->currentText().toStdString(), newName.toStdString());
301  } else {
302  return;
303  }
304  }
305 
306  pToolbox_->cbAnimation->setItemText(pToolbox_->cbAnimation->currentIndex(), newName);
307 }
308 
309 //------------------------------------------------------------------------------
310 
315 {
316 
317  for (unsigned int i=0; i < activeSkeletons_.size(); i++){
318 
319  //get active skeleton
320  BaseObjectData* baseObject = 0;
321  PluginFunctions::getObject(activeSkeletons_[i], baseObject);
322 
323  if ( baseObject == 0 )
324  continue;
325 
326  SkeletonObject* skeletonObject = dynamic_cast<SkeletonObject*>(baseObject);
327  Skeleton* skeleton = PluginFunctions::skeleton(skeletonObject);
328 
329  if(skeleton != 0 && skeleton->animation( currentAnimationHandle() ) != 0)
330  return skeleton->animation( currentAnimationHandle() )->frameCount();
331  }
332 
333  return 0;
334 }
335 
336 //------------------------------------------------------------------------------
337 
342 {
343  pToolbox_->hsFrame->setValue(_iFrame);
344 }
345 
346 //------------------------------------------------------------------------------
347 
352  return pToolbox_->hsFrame->value();
353 }
354 
355 //------------------------------------------------------------------------------
356 
361  pToolbox_->sbFPS->blockSignals(true);
362  pToolbox_->sbFPS->setValue(_fps);
363  pToolbox_->sbFPS->blockSignals(false);
364 
365  animationTimer_.setInterval(1000/_fps);
366 }
367 
368 //------------------------------------------------------------------------------
369 
377 {
378 
379  if( !_skeletonObject->hasObjectData(OBJECTDATA_SKELETON) )
380  return;
381 
382  SkeletonObjectData* skeletonData = dynamic_cast< SkeletonObjectData* >( _skeletonObject->objectData(OBJECTDATA_SKELETON) );
383 
384  for (unsigned int i=0; i < skeletonData->skinCount(); i++){
385 
386  // deform all attached skin meshes
387  int meshId = skeletonData->skin(i);
388 
389  BaseObjectData* object = 0;
390  PluginFunctions::getObject(meshId, object);
391 
392  if (object == 0)
393  continue;
394 
395  if ( !object->hasObjectData(OBJECTDATA_SKIN) ){
396  emit log(LOGERR, tr("Error: Attached skin mesh has no skin-object-data."));
397  continue;
398  }
399 
400  BaseSkin* skin = dynamic_cast< BaseSkin* > ( object->objectData(OBJECTDATA_SKIN) );
401  skin->deformSkin(_hAni, method_ );
402 
403  emit updatedObject(object->id(), UPDATE_GEOMETRY);
404  }
405 }
406 
407 //------------------------------------------------------------------------------
408 
413 {
414  // do nothing during animation
415  if ( animationTimer_.isActive() )
416  return;
417 
418  BaseObjectData* obj = 0;
419 
420  PluginFunctions::getObject(_id, obj);
421 
422  if ( (obj != 0) && (obj->dataType(DATA_SKELETON)) ){
423 
425 
426  //check if pose changed
427  if ( sObj->activePose() != currentAnimationHandle() ){
428 
429  AnimationHandle newHandle = sObj->activePose();
430 
431  if ( !newHandle.isValid() ) //refPose
432  pToolbox_->cbAnimation->setCurrentIndex( 0 );
433  else
434  setComboBoxPosition(newHandle.animationIndex());
435  }
436 
437  //check if animationCount changed
438  if ( (int)PluginFunctions::skeleton(sObj)->animationCount() != pToolbox_->cbAnimation->count()-1 )
439  UpdateUI();
440  }
441 }
442 
443 //------------------------------------------------------------------------------
444 
449 {
450  if(bGuiUpdating_) // do not update while the gui is updating
451  return;
452 
453  if ( activeSkeletons_.empty() ){
454  pToolbox_->hsFrame->setRange( 0, 0 );
455  pToolbox_->hsFrame->setTickInterval(1);
456  pToolbox_->hsFrame->setValue(0);
457 
458  pToolbox_->cbAnimation->clear();
459 
460  } else {
461 
462  //get first active skeleton
463  BaseObjectData* skelObject = 0;
464  PluginFunctions::getObject(activeSkeletons_[0], skelObject);
465 
466  if ( skelObject == 0 )
467  return;
468 
469  Skeleton* skeleton = PluginFunctions::skeleton(skelObject);
470 
471  // equip the frame slider with the new range
472  AnimationT<ACG::Vec3d> *pAnimation = skeleton->animation(currentAnimationHandle());
473  if(pAnimation != 0){
474  pToolbox_->hsFrame->setRange( 0, pAnimation->frameCount() - 1 );
475  changeFPS( pAnimation->fps() );
476  }else
477  pToolbox_->hsFrame->setRange( 0, 0 );
478 
479  pToolbox_->hsFrame->setTickInterval(1);
480  pToolbox_->hsFrame->setValue(0);
481 
482  // pass the current frame
484  dynamic_cast<SkeletonObject*>(skelObject)->setActivePose(hAni);
485 
486  emit updatedObject(skelObject->id(), UPDATE_GEOMETRY);
487 
488  // update skins if available
489  UpdateSkins(skelObject, hAni);
490 
491  slotFrameChanged(0);
492  }
493 }
494 
495 //------------------------------------------------------------------------------
496 
501 {
502  // do not update while the gui is updating
503  if(bGuiUpdating_)
504  return;
505 
506  for (unsigned int i=0; i < activeSkeletons_.size(); i++){
507 
508  //get active skeleton
509  BaseObjectData* skelObject = 0;
510  PluginFunctions::getObject(activeSkeletons_[i], skelObject);
511 
512  if ( skelObject == 0 )
513  return;
514 
515  // pass the current frame
518 
519  // and update the skin if available
520  UpdateSkins(skelObject, hAni);
521 
522  emit updatedObject(skelObject->id(), UPDATE_GEOMETRY);
523  }
524 }
525 
526 //------------------------------------------------------------------------------
527 
532  disconnect(&animationTimer_, 0, 0, 0);
533  animationTimer_.stop();
534  animationTimer_.setSingleShot(false);
535  animationTimer_.setInterval(1000 / pToolbox_->sbFPS->value());
536  connect(&animationTimer_,SIGNAL(timeout()),this,SLOT(animate()));
537  animationTimer_.start();
538  animationTime_.start();
539  animationOffset_ = pToolbox_->hsFrame->value();
540 
541  //The Play button will just toggle between play and pause mode
542  connect( pToolbox_->pbPlay, SIGNAL(clicked()), this, SLOT( pauseAnimation() ) );
543 }
544 
545 //------------------------------------------------------------------------------
546 
551  animationTimer_.stop();
552  disconnect(&animationTimer_, 0, 0, 0);
553 
554  connect( pToolbox_->pbPlay, SIGNAL(clicked()), this, SLOT( playAnimation() ) );
555 }
556 
557 //------------------------------------------------------------------------------
558 
563  pauseAnimation();
564  pToolbox_->hsFrame->setSliderPosition(0); //Reset the playback
565 }
566 
567 //------------------------------------------------------------------------------
568 
573  pauseAnimation();
574  pToolbox_->hsFrame->setSliderPosition(pToolbox_->hsFrame->sliderPosition() - 1);
575 }
576 
577 //------------------------------------------------------------------------------
578 
583  pauseAnimation();
584  pToolbox_->hsFrame->setSliderPosition(pToolbox_->hsFrame->sliderPosition() + 1);
585 }
586 
587 //------------------------------------------------------------------------------
588 
596 {
597  unsigned long frameCount = getNumberOfFrames();
598  if(frameCount <= 1) // 0: invalid animation, 1: just a pose
599  {
600  setFrame(0);
601  stopAnimation();
602  return;
603  }
604  --frameCount;
605 
606  if(pToolbox_->cbSkipFrames->isChecked())
607  {
608  int fps = pToolbox_->sbFPS->value();
609  unsigned long currentFrame = animationOffset_ + (unsigned long)floor(double(animationTime_.elapsed()) / 1000.0 * fps);
610  setFrame(currentFrame % frameCount);
611  }else{
612  setFrame((getFrame() + 1) % frameCount);
613  }
614 }
615 
616 //------------------------------------------------------------------------------
617 
624 {
625  if(_state == Qt::Checked)
626  {
627  // changing to skip frames as necessary
628  animationTime_.start();
629  animationOffset_ = pToolbox_->hsFrame->value();
630  }else{
631  // changing to display all frames
632  }
633 }
634 
635 //------------------------------------------------------------------------------
636 
643 {
644  // change the method
645  switch(_index)
646  {
647  case 0:
648  method_ = BaseSkin::M_LBS;
649  break;
650  default:
651  case 1:
652  method_ = BaseSkin::M_DBS;
653  break;
654  }
655 
656  // deform the skin(s) using the new method
657  for (unsigned int i=0; i < activeSkeletons_.size(); i++){
658 
659  //get active skeleton
660  BaseObjectData* baseObject = 0;
661  PluginFunctions::getObject(activeSkeletons_[i], baseObject);
662 
663  if ( baseObject == 0 )
664  return;
665 
666  SkeletonObject* skeletonObject = dynamic_cast<SkeletonObject*>(baseObject);
667  AnimationHandle hAni = skeletonObject->skeletonNode()->activePose();
668 
669  // and update the skin
670  UpdateSkins(skeletonObject, hAni);
671  }
672 
673  emit updateView();
674 }
675 
676 //------------------------------------------------------------------------------
677 
683 {
684  bool& v_;
685 public:
686  GuiUpdatingScopeGuard(bool &_in):v_(_in){v_ = true;}
687  ~GuiUpdatingScopeGuard(){v_ = false;}
688 };
689 
694 {
695  if(bGuiUpdating_) // gui updates object -> object is updated so gui gets updated -> loop forever
696  return;
697 
699 
700  if( ! activeSkeletons_.empty() )
701  {
702  //get first active skeleton
703  BaseObjectData* baseObject = 0;
704  PluginFunctions::getObject(activeSkeletons_[0], baseObject);
705 
706  if ( baseObject == 0 )
707  return;
708 
709  SkeletonObject* skeletonObj = PluginFunctions::skeletonObject(baseObject);
710  Skeleton* skeleton = PluginFunctions::skeleton(baseObject);
711 
712  // update the rigging and skinning group
713  // enable UI
714  if(skeletonObj->objectData(OBJECTDATA_SKELETON) == 0)
715  {
716  pToolbox_->pbAttachSkin->setEnabled(true);
717  pToolbox_->pbClearSkins->setEnabled(false);
718  pToolbox_->skinningBox->setTitle(tr("Attached Skins"));
719  }else{
720  pToolbox_->pbAttachSkin->setEnabled(true);
721  pToolbox_->pbClearSkins->setEnabled(true);
722 
723  SkeletonObjectData* skelData = dynamic_cast<SkeletonObjectData*>( skeletonObj->objectData(OBJECTDATA_SKELETON) );
724  pToolbox_->skinningBox->setTitle(tr("Attached Skins (Currently: %1)").arg(skelData->skinCount()) );
725  }
726 
727  // update the Skeleton group
728  AnimationHandle hAni = skeletonObj->skeletonNode()->activePose();
729 
730  pToolbox_->pbAddAnimation->setEnabled(true);
731  pToolbox_->cbAnimation->setEnabled(true);
732  pToolbox_->cbAnimation->clear();
733 
734  // create the reference pose
735  pToolbox_->cbAnimation->addItem("Reference Pose");
736 
737  Skeleton::AnimationIterator animations = skeleton->animationsBegin();
738 
739  while ( animations ) {
740  AnimationHandle anim = *animations;
741  pToolbox_->cbAnimation->addItem(skeleton->animationName(anim.animationIndex()).c_str(),QVariant(anim.animationIndex()));
742  ++animations;
743  }
744 
746 
747  // get the number of frames in the animation
748  pToolbox_->hsFrame->setEnabled(true);
749  if(skeleton->animation(hAni) != 0)
750  pToolbox_->hsFrame->setRange( 0, skeleton->animation(hAni)->frameCount() - 1 );
751  pToolbox_->hsFrame->setTickInterval(1);
752  pToolbox_->hsFrame->setValue(hAni.frame());
753 
754  }else{
755  // disable UI
756  pToolbox_->pbAddAnimation->setEnabled(false);
757  pToolbox_->cbAnimation->setEnabled(false);
758  pToolbox_->cbAnimation->clear();
759  pToolbox_->hsFrame->setEnabled(false);
760  pToolbox_->hsFrame->setRange(0, 0);
761  pToolbox_->hsFrame->setTickInterval(1);
762 
763  pToolbox_->pbAttachSkin->setEnabled(false);
764  pToolbox_->pbClearSkins->setEnabled(false);
765  pToolbox_->skinningBox->setTitle(tr("Attached Skins"));
766  }
767 }
768 
769 //------------------------------------------------------------------------------
770 
775 {
776 
777  if( activeSkeletons_.size() != 1 ){
778  emit log(LOGERR, tr("Cannot bind mesh. Please select only one skeleton."));
779  return;
780  }
781 
782  int meshCount = 0;
783 
785  {
786  attachSkin(activeSkeletons_[0], o_it->id());
787  meshCount++;
788  }
789 
790  if (meshCount == 0){
791  emit log(LOGERR, tr("Cannot bind mesh. Please select at least one mesh as target."));
792  return;
793  }
794 }
795 
796 //------------------------------------------------------------------------------
797 
799 
800  // get the skeleton and make it prepare the mesh
801  Skeleton* skeleton = dynamic_cast<SkeletonObject*>(_skeletonObj)->skeleton();
802 
803  //check if mesh is already a skin
804  if (_skin->hasObjectData(OBJECTDATA_SKIN) ){
805  emit log(LOGERR, tr("Cannot bind mesh as skin. Mesh is already a skin."));
806  return;
807  }
808 
809  // prepare the skin template class used to deform the skin
810  SkeletonObjectData* skelData = 0;
811 
812  if ( _skeletonObj->hasObjectData(OBJECTDATA_SKELETON) )
813  skelData = dynamic_cast< SkeletonObjectData* >(_skeletonObj->objectData(OBJECTDATA_SKELETON));
814  else {
815  skelData = new SkeletonObjectData();
816  _skeletonObj->setObjectData(OBJECTDATA_SKELETON, skelData);
817  }
818 
819  skelData->addSkin( _skin->id() );
820 
821  BaseSkin* baseSkin = 0;
822 
824  bool hasSkinWeights = true;
825 
826  if(_skin->dataType(DATA_TRIANGLE_MESH)){
827  hasSkinWeights = PluginFunctions::triMesh(_skin)->get_property_handle(propWeights, SKIN_WEIGHTS_PROP);
828  baseSkin = dynamic_cast<BaseSkin*>( new TriMeshSkin(skeleton, PluginFunctions::triMesh(_skin),_skeletonObj->id()) );
829  }else if(_skin->dataType(DATA_POLY_MESH)){
830  hasSkinWeights = PluginFunctions::polyMesh(_skin)->get_property_handle(propWeights, SKIN_WEIGHTS_PROP);
831  baseSkin = dynamic_cast<BaseSkin*>( new PolyMeshSkin(skeleton, PluginFunctions::polyMesh(_skin) ,_skeletonObj->id() ));
832  }
833 
834  baseSkin->attachSkin();
835 
836  if (hasSkinWeights)
837  baseSkin->deformSkin(currentAnimationHandle(), method_);
838 
839  emit updatedObject(_skin->id(), UPDATE_GEOMETRY);
840  _skin->setObjectData(OBJECTDATA_SKIN, baseSkin);
841 
842  _skeletonObj->target(true);
843  _skeletonObj->source(false);
844 
845  if( !hasSkinWeights ){
846  //ask if they should be computed automatically
847  bool canCompute;
848  emit pluginExists("skinningplugin", canCompute);
849 
850  if (canCompute){
851  QMessageBox msgBox;
852  msgBox.setText("The mesh is not equipped with skin weights.");
853  msgBox.setInformativeText("Do you want to compute them automatically?");
854  msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
855  msgBox.setDefaultButton(QMessageBox::Yes);
856  int ret = msgBox.exec();
857 
858  if (ret == QMessageBox::Yes)
859  RPC::callFunction("skinningplugin", "computeSkinWeights");
860  }
861  }
862 
863  UpdateUI();
864 }
865 
866 //------------------------------------------------------------------------------
867 
872 {
873  for (unsigned int i=0; i < activeSkeletons_.size(); i++)
874  clearSkins( activeSkeletons_[i] );
875 }
876 
877 //------------------------------------------------------------------------------
878 
880 
881  // prepare the skin template class used to deform the skin
882  SkeletonObjectData* skelData = 0;
883 
884  if ( !_skeletonObj->hasObjectData(OBJECTDATA_SKELETON) )
885  return;
886 
887  skelData = dynamic_cast< SkeletonObjectData* >(_skeletonObj->objectData(OBJECTDATA_SKELETON));
888 
889  for (int i=skelData->skinCount()-1; i >= 0; i--){
890  //deform all attached skin meshes
891  int meshId = skelData->skin(i);
892 
893  BaseObjectData* object = 0;
894  PluginFunctions::getObject(meshId, object);
895 
896  if (object == 0)
897  continue;
898 
899  detachSkin(object, _skeletonObj);
900  }
901 }
902 
903 //------------------------------------------------------------------------------
904 
906 
907  // get the skeleton and make it prepare the mesh
908  Skeleton* skeleton = dynamic_cast<SkeletonObject*>(_skeletonObj)->skeleton();
909 
910  if ( !_skeletonObj->hasObjectData(OBJECTDATA_SKELETON) ){
911  emit log(LOGERR, tr("Cannot detach skin. Skeleton has no object data."));
912  return;
913  }
914 
915  //first try to remove the skin from the mesh
916  if (_skin->hasObjectData(OBJECTDATA_SKIN) ){
917 
918  BaseSkin* baseSkin = 0;
919 
920  if(_skin->dataType(DATA_TRIANGLE_MESH))
921  baseSkin = dynamic_cast<BaseSkin*>( new TriMeshSkin(skeleton, PluginFunctions::triMesh(_skin) ,_skeletonObj->id()));
922  else if(_skin->dataType(DATA_POLY_MESH))
923  baseSkin = dynamic_cast<BaseSkin*>( new PolyMeshSkin(skeleton, PluginFunctions::polyMesh(_skin) ,_skeletonObj->id()));
924 
925  baseSkin->releaseSkin();
926  _skin->clearObjectData(OBJECTDATA_SKIN);
927  delete baseSkin;
928 
929  emit updatedObject(_skin->id(), UPDATE_GEOMETRY);
930  }
931 
932  // then remove the skin from the skeleton data
933  SkeletonObjectData* skelData = dynamic_cast< SkeletonObjectData* >(_skeletonObj->objectData(OBJECTDATA_SKELETON));
934 
935  skelData->removeSkin( _skin->id() );
936 
937  //remove the objectData if all skins are removed
938  if ( skelData->skinCount() == 0 ){
939  _skeletonObj->clearObjectData(OBJECTDATA_SKELETON);
940  delete skelData;
941  }
942 
943  UpdateUI();
944 }
945 
946 //------------------------------------------------------------------------------
947 
948 void SkeletalAnimationPlugin::slotAddAnimation()
949 {
950 
951  if( activeSkeletons_.size() != 1 ){
952  emit log(LOGERR, tr("Cannot add animation. Please select only one skeleton."));
953  return;
954  }
955 
956  AddAnimationDialog dialog;
957  dialog.animationName->selectAll();
958  dialog.animationName->setFocus();
959 
960  if ( dialog.exec() == QDialog::Accepted ){
961  if ( dialog.animationName->text() == "" ){
962  emit log(LOGERR, tr("Cannot add animation with empty name"));
963  return;
964  }
965 
966  BaseObjectData* obj = 0;
967 
968  PluginFunctions::getObject(activeSkeletons_[0], obj);
969 
970  if (obj == 0){
971  emit log(LOGERR, tr("Unable to get object"));
972  return;
973  }
974 
976 
977  if (skeletonObj == 0){
978  emit log(LOGERR, tr("Unable to get skeletonObject"));
979  return;
980  }
981 
982  Skeleton* skeleton = PluginFunctions::skeleton(obj);
983 
984  std::string stdName = dialog.animationName->text().toStdString();
985 
986  if ( skeleton->animation(stdName) != 0 ){
987  emit log(LOGERR, tr("Animation with this name already exists"));
988  return;
989  }
990 
991  FrameAnimationT<ACG::Vec3d>* animation = new FrameAnimationT<ACG::Vec3d>(skeleton, dialog.frames->value());
992  AnimationHandle handle = skeleton->addAnimation(stdName, animation);
993 
994  //init pose to refPose
995  for (unsigned int i=0; i < skeleton->animation(handle)->frameCount(); i++){
996  handle.setFrame(i);
997  Skeleton::Pose* pose = skeleton->pose(handle);
998 
999  for (unsigned int j=0; j < skeleton->jointCount(); j++)
1000  pose->setGlobalMatrix(j, skeleton->referencePose()->globalMatrix(j) );
1001  }
1002 
1003  emit updatedObject(activeSkeletons_[0], UPDATE_ALL);
1004 
1005  //select the new animation
1006  setComboBoxPosition(handle.animationIndex());
1007  }
1008 }
1009 
1010 //------------------------------------------------------------------------------
1011 
1012 void SkeletalAnimationPlugin::slotDeleteAnimation()
1013 {
1014  int iAnimation = pToolbox_->cbAnimation->currentIndex();
1015  unsigned int animationIndex = pToolbox_->cbAnimation->itemData(iAnimation).toUInt();
1016 
1017  if ( iAnimation == 0 ) {
1018  emit log(LOGERR,"Reference pose could never be removed!");
1019  } else {
1020  pToolbox_->cbAnimation->removeItem(iAnimation);
1021 
1023 
1024  SkeletonObject* skeletonObject = dynamic_cast<SkeletonObject*>(*o_it);
1025  Skeleton* skeleton = PluginFunctions::skeleton(skeletonObject);
1026 
1027  skeleton->removeAnimation(AnimationHandle(animationIndex));
1028 
1029  }
1030 
1031  UpdateUI();
1032  }
1033 }
1034 
1035 //------------------------------------------------------------------------------
1036 
1043 {
1044  int iAnimation = pToolbox_->cbAnimation->currentIndex();
1045  unsigned int animationId = pToolbox_->cbAnimation->itemData(iAnimation).toUInt();
1046 
1047  if(iAnimation == 0)
1048  return AnimationHandle(); //This will be the reference pose, i.e. an empty animation
1049  else if(iAnimation > 0)
1050  return AnimationHandle(animationId, pToolbox_->hsFrame->value());
1051  return AnimationHandle(); // should not happen
1052 }
1053 
1054 //------------------------------------------------------------------------------
1055 
1060 void SkeletalAnimationPlugin::setComboBoxPosition(unsigned int _animationIndex)
1061 {
1062  for ( int i = 0 ; i < pToolbox_->cbAnimation->count(); ++i ) {
1063  unsigned int animationId = pToolbox_->cbAnimation->itemData(i).toUInt();
1064 
1065  if ( animationId == _animationIndex ) {
1066  pToolbox_->cbAnimation->setCurrentIndex(i);
1067  return;
1068  }
1069 
1070  }
1071 
1072 }
1073 
1074 //------------------------------------------------------------------------------
1075 
1076 #if QT_VERSION < 0x050000
1077  Q_EXPORT_PLUGIN2(skeletalAnimationplugin, SkeletalAnimationPlugin);
1078 #endif
1079 
1080 
General skin class, used to bind skeleton and mesh and deform the mesh.
Definition: SkinT.hh:12
bool isValid() const
Returns true if the handle is valid.
void stopAnimation()
Called by the ui and stops the current animation.
void setGlobalMatrix(unsigned int _joint, const Matrix &_global, bool _keepGlobalChildPositions=true)
Sets the global coordinate system.
Definition: PoseT.cc:217
BaseSkin::Method method_
The current blending method for the skin.
void pauseAnimation()
Called by the ui and stops the current animation.
SkeletonObject * skeletonObject(BaseObjectData *_object)
Cast an BaseObject to a SkeletonObject if possible.
QString description()
returns a plugin description
void initializePlugin()
initialize the plugin
const QStringList ALL_OBJECTS
Iterable object range.
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(1)<< 2)
Geometry updated.
void setActivePose(const AnimationHandle &_hAni)
Call this to set the active pose.
QString name()
returns the plugin name
PerObjectData * objectData(QString _dataName)
Returns the object data pointer.
Definition: BaseObject.cc:814
void objectDeleted(int _id)
Update ui when the object is deleted.
void pluginsInitialized()
final initializations
void slotFrameChanged(int)
Called by the framework when a different frame was selected.
void slotMethodChanged(int _index)
Called as the skin deformation method changed.
Iterator class for the animations attached to a skeleton.
Definition: SkeletonT.hh:120
bool attachSkin(int skeletonId, int skinId)
Returns the number of frames in the currently active animation.
Update type class.
Definition: UpdateType.hh:70
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
void slotObjectUpdated(int _id, const UpdateType &_type)
Check activePose if a skeleton was updated.
Helper Class for UpdateUI. assigns a bool value and set it to "true". after leaving the scope...
bool getObject(int _identifier, BSplineCurveObject *&_object)
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
void exit()
Plugin gets closed.
void UpdateSkins(BaseObjectData *_pSkeletonObject, AnimationHandle &_hAni)
Changes the mesh's pose to represent the frame given by the animation handle.
Animation * animation(std::string _name)
Returns a pointer to the animation to the given name.
Definition: SkeletonT.cc:858
void removeAnimation(std::string _name)
Removes an animation from the list.
Definition: SkeletonT.cc:886
void fileOpened(int _id)
Update ui when the object is loaded.
A general pose, used to store the frames of the animation.
Definition: PoseT.hh:68
unsigned int animationIndex() const
Returns the animation index (zero based)
void slotObjectSelectionChanged(int _id)
Update ui when the object selection changes.
Abstract base class for the skin template, wrapping all template versions of the skin.
Definition: BaseSkin.hh:67
const QStringList TARGET_OBJECTS("target")
Iterable object range.
void nextFrame()
Called by the ui and goes to next frame of the current animation.
bool dataType(DataType _type) const
Definition: BaseObject.cc:232
void checkObjectSelection(const int _objectId)
Check source/target selection of objects.
AnimationHandle animationHandle(std::string _name)
Get an AnimationHandle to the animation with the given name.
Definition: SkeletonT.cc:843
int animationOffset_
This frame was selected as the animation was started.
bool source()
Definition: BaseObject.cc:302
void removeSkin(int _objectId)
Remove a skin from the skeleton.
void slotAllCleared()
clear all occurred
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
void addedEmptyObject(int _id)
Update ui when the object is added.
bool hasObjectData(QString _dataName)
Checks if object data with given name is available.
Definition: BaseObject.cc:806
void prevFrame()
Called by the ui and goes to previous frame of the current animation.
void setObjectData(QString _dataName, PerObjectData *_data)
Definition: BaseObject.cc:792
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
void replaceAnimationName(const std::string &_strOld, const std::string &_strNew)
Returns a pointer to the pose with the given animation handle.
Definition: SkeletonT.hh:196
unsigned int skinCount()
Get the number of associated skins.
Pose * pose(unsigned int _iFrame)
Returns a pointer to the pose stored in the given frame.
void slotSkipFramesChanged(int _state)
Called as the skip frames check box changes state.
unsigned int animationCount()
Returns the number of animations stored in this skeleton.
Definition: SkeletonT.cc:971
QScriptValue callFunction(QString _plugin, QString _functionName, std::vector< QScriptValue > _parameters)
Call a function provided by a plugin getting multiple parameters.
Definition: RPCWrappers.cc:63
#define DATA_POLY_MESH
Definition: PolyMesh.hh:65
void clearObjectData(QString _dataName)
Clear the object data pointer ( this will not delete the object!! )
Definition: BaseObject.cc:798
void setComboBoxPosition(unsigned int _animationIndex)
Sets the animations combo box to the right entry.
unsigned int frame() const
Returns the selected frame (zero based)
QTime animationTime_
Time since the animation was started, used to meet the given fps.
int getFrame()
Gets the current frame number.
The skeletal animation plugin is used to interact with the skeleton.
void addSkin(int _objectId)
Add a skin to the skeleton.
bool detachSkin(int skeletonId, int skinId)
Returns the number of frames in the currently active animation.
void slotAnimationIndexChanged(int)
Called by the framework when the animation index changed.
Add normals to mesh item (vertices/faces)
Definition: Attributes.hh:87
void setFrame(int _iFrame)
Displays the given frame from the current animation and updates the view.
A handle used to refer to an animation or to a specific frame in an animation.
AnimationHandle currentAnimationHandle()
Returns a handle describing the current frame in the active animation.
void changeFPS(int _fps)
Change the frames per second (FPS)
STL namespace.
int skin(unsigned int _index)
Get the skin with given index (0 <= _index < skinCount())
void UpdateUI()
Called when the active object changes and the interface needs to be updated.
Predefined datatypes.
Definition: DataTypes.hh:96
#define DATA_SKELETON
Definition: Skeleton.hh:70
const std::string & animationName(unsigned int _index)
Returns the name of the animation with the given index.
Definition: SkeletonT.cc:984
bool bGuiUpdating_
Used to drop a few messages while the gui is being updated.
bool target()
Definition: BaseObject.cc:284
Skeleton * skeleton(BaseObjectData *_object)
Get a skeleton from an object.
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:66
void slotAttachSkin()
Called by Qt as the user is trying to connect a mesh to a skeleton.
void slotClearSkins()
Called by Qt as the user is trying to unbind a mesh from as a skeleton.
ACG::SceneGraph::SkeletonNodeT< Skeleton > * skeletonNode()
Returns the skeleton scenegraph node.
bool clearSkins(int skeletonId)
Returns the number of frames in the currently active animation.
Data object attached to the skeleton.
AnimationIterator animationsBegin()
Iterator over the animations.
Definition: SkeletonT.cc:945
int id() const
Definition: BaseObject.cc:201
int getNumberOfFrames()
Returns the number of frames in the currently active animation.
void animate()
Iterates the animation.
QTimer animationTimer_
Timer used to control animations.
void playAnimation()
Called by the ui and starts an automatic animation.
AnimationToolboxWidget * pToolbox_
A pointer to the toolbox widget.
unsigned int frameCount()
Returns the number of frames stored in this pose.