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