world.cpp
1 /********************************************************************************
2  * WorldSim -- library for robot simulations *
3  * Copyright (C) 2008-2011 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "world.h"
21 #include "phyobject.h"
22 #include "phyjoint.h"
23 #include "private/phyobjectprivate.h"
24 #include "private/worldprivate.h"
25 #include "motorcontrollers.h"
26 
27 #ifdef __GNUC__
28 #pragma GCC diagnostic ignored "-Wunused-parameter"
29 #endif
30 #ifdef FARSA_USE_YARP_AND_ICUB
31  #include "yarp/os/Network.h"
32 #endif
33 #ifdef __GNUC__
34 #pragma GCC diagnostic warning "-Wunused-parameter"
35 #endif
36 
37 #include <QPair>
38 
39 namespace farsa {
40 
41 WObject::WObject( World* w, QString name, const wMatrix& tm, bool addToWorld ) {
42  this->worldv = w;
43  this->tm = tm;
44  namev = name;
45  texturev = "tile2";
46  colorv = Qt::white;
47  invisible = false;
49  if ( addToWorld ) {
50  worldv->pushObject( this );
51  }
52 }
53 
55  worldv->popObject( this );
56 }
57 
58 MaterialDB::MaterialDB( World* w ) : mats(), pmap() {
59  this->worldv = w;
60  createMaterial( "nonCollidable" );
61  createMaterial( "default" );
62 }
63 
64 bool MaterialDB::createMaterial( QString name ) {
65  if ( mats.contains( name ) ) {
66  return false;
67  }
68 #ifdef WORLDSIM_USE_NEWTON
69  NewtonWorld* ngdWorld = worldv->priv->world;
70  int newm;
71  if ( name == "default" ) {
72  newm = NewtonMaterialGetDefaultGroupID( ngdWorld );
73  } else {
74  newm = NewtonMaterialCreateGroupID( ngdWorld );
75  }
76  worldv->priv->matIDs[name] = newm;
77  NewtonMaterialSetCollisionCallback( ngdWorld, newm, newm, (void*)worldv, NULL, (WorldPrivate::processCollisionHandler) );
78  // --- setting callbacks
79  foreach( QString k, mats.values() ) {
80  int kid = worldv->priv->matIDs[k];
81  NewtonMaterialSetCollisionCallback( ngdWorld, newm, kid, (void*)worldv, NULL, (WorldPrivate::processCollisionHandler) );
82  }
83  // --- setting nonCollidable material
84  NewtonMaterialSetDefaultCollidable( ngdWorld, worldv->priv->matIDs["nonCollidable"], newm, 0 );
85 #endif
86  mats.insert( name );
87  return true;
88 }
89 
90 void MaterialDB::setFrictions( QString mat1, QString mat2, real st, real kn ) {
91  if ( !mats.contains( mat1 ) ) return;
92  if ( !mats.contains( mat2 ) ) return;
93  QString pkey = createKey( mat1, mat2 );
94  pmap[pkey].staticFriction = st;
95  pmap[pkey].dynamicFriction = kn;
96 #ifdef WORLDSIM_USE_NEWTON
97  int id1 = worldv->priv->matIDs[mat1];
98  int id2 = worldv->priv->matIDs[mat2];
99  NewtonMaterialSetDefaultFriction( worldv->priv->world, id1, id2, st, kn );
100 #endif
101  return;
102 }
103 
104 void MaterialDB::setElasticity( QString mat1, QString mat2, real el ) {
105  if ( !mats.contains( mat1 ) ) return;
106  if ( !mats.contains( mat2 ) ) return;
107  QString pkey = createKey( mat1, mat2 );
108  pmap[pkey].elasticity = el;
109 #ifdef WORLDSIM_USE_NEWTON
110  int id1 = worldv->priv->matIDs[mat1];
111  int id2 = worldv->priv->matIDs[mat2];
112  NewtonMaterialSetDefaultElasticity( worldv->priv->world, id1, id2, el );
113 #endif
114  return;
115 }
116 
117 void MaterialDB::setSoftness( QString mat1, QString mat2, real sf ) {
118  if ( !mats.contains( mat1 ) ) return;
119  if ( !mats.contains( mat2 ) ) return;
120  QString pkey = createKey( mat1, mat2 );
121  pmap[pkey].softness = sf;
122 #ifdef WORLDSIM_USE_NEWTON
123  int id1 = worldv->priv->matIDs[mat1];
124  int id2 = worldv->priv->matIDs[mat2];
125  NewtonMaterialSetDefaultSoftness( worldv->priv->world, id1, id2, sf );
126 #endif
127  return;
128 }
129 
130 void MaterialDB::setGravityForce( QString mat, real force ) {
131  gravities[mat] = force;
132 }
133 
134 real MaterialDB::gravityForce( QString mat ) {
135  if ( gravities.contains( mat ) ) {
136  return gravities[mat];
137  } else {
138  return worldv->gravityForce();
139  }
140 }
141 
142 
143 void MaterialDB::enableCollision( QString mat1, QString mat2, bool enable ) {
144  if ( !mats.contains( mat1 ) ) return;
145  if ( !mats.contains( mat2 ) ) return;
146  QString pkey = createKey( mat1, mat2 );
147  pmap[pkey].collisions = enable;
148 #ifdef WORLDSIM_USE_NEWTON
149  int id1 = worldv->priv->matIDs[mat1];
150  int id2 = worldv->priv->matIDs[mat2];
151  NewtonMaterialSetDefaultCollidable( worldv->priv->world, id1, id2, enable );
152 #endif
153 }
154 
155 void MaterialDB::setProperties( QString mat1, QString mat2, real fs, real fk, real el, real sf, bool en ) {
156  setFrictions( mat1, mat2, fs, fk );
157  setElasticity( mat1, mat2, el );
158  setSoftness( mat1, mat2, sf );
159  enableCollision( mat1, mat2, en );
160 }
161 
162 QString MaterialDB::createKey( QString mat1, QString mat2 ) {
163  if ( mat1 < mat2 ) {
164  return mat1 + ":" + mat2;
165  } else {
166  return mat1 + ":" + mat2;
167  }
168 }
169 
170 World::World( QString name, bool LocalYarpPorts )
171  : QObject(), minP(-100,-100,-100), maxP(100,100,100), objs(), jointsv(), mapObjJoints(),
172  cmap(), nobjs() {
173 #ifdef FARSA_USE_YARP_AND_ICUB
174  static yarp::os::Network networkYarp;
175  if ( LocalYarpPorts ) {
176  networkYarp.setLocalMode( LocalYarpPorts );
177  networkYarp.setVerbosity( -1 );
178  }
179 #else
180  // This is to avoid the warning regarding the unsed parameter LocalYarpPorts
181  Q_UNUSED( LocalYarpPorts )
182 #endif
183  timerId = 0;
184  state = stoppedS;
185  namev = name;
186  time = 0.0;
187  timestepv = 0.0150f; //--- about 66.66666 frame per second
188  isrealtimev = false;
189  isInit = false;
190  isFinish = true;
191  gforce = -9.8f;
192  priv = new WorldPrivate();
193 
194 #ifdef WORLDSIM_USE_NEWTON
195  priv->world = NewtonCreate();
196  NewtonInvalidateCache( priv->world );
197  NewtonSetWorldSize( priv->world, &minP[0], &maxP[0] );
198  // keep at least to 100 for stability
199  NewtonSetMinimumFrameRate( priv->world, 100 );
200  // exact model as default
201  NewtonSetSolverModel( priv->world, 0 );
202  NewtonSetFrictionModel( priv->world, 0 );
203  // enable multi-thread
204  //--- for works we before check that all callbacks implementation are thread-safe
205  //--- REMEMBER: connect, signal-slot of non-QT data needs to register the type with
206  //--- qRegisterMetaType
207  //NewtonSetThreadsCount( priv->world, 2 );
208 #endif
209 
210  mats = new MaterialDB( this );
211  return;
212 }
213 
215  // Here we can't use foreach because lists could be modified while we are iterating on them
216  // by the popObject(), popJoint() or popMotorController() functions. See the comment in the
217  // declaration of objs for more information
218  while ( !jointsv.isEmpty() ) {
219  PhyJoint* js = jointsv.takeFirst();
220  if ( js->owner() == NULL ) {
221  delete js;
222  }
223  }
224  while ( !objs.isEmpty() ) {
225  WObject* obj = objs.takeFirst();
226  if ( obj->owner() == NULL ) {
227  delete obj;
228  }
229  }
230 #ifdef WORLDSIM_USE_NEWTON
231  NewtonDestroy( priv->world );
232 #endif
233  delete priv;
234 }
235 
236 QString World::name() const {
237  return namev;
238 }
239 
241  if ( isInit ) return;
242  if ( !isFinish ) {
243  finalize();
244  }
245  time = 0.0;
246  timer.tic();
247  state = stoppedS;
248  isInit = true;
249  isFinish = false;
250  emit initialized();
251 }
252 
254  if ( !isInit ) {
255  initialize();
256  }
257  while( isrealtimev && timer.tac()/1000000.0 < timestepv ) { };
258 
259  timer.tic();
260  cmap.clear();
261  //--- preUpdate Objects
262  for( QLinkedList<WObject*>::iterator it = objs.begin(); it != objs.end(); it++ ) {
263  (*it)->preUpdate();
264  }
265  //--- preUpdate Joints
266  for( QLinkedList<PhyJoint*>::iterator it = jointsv.begin(); it != jointsv.end(); it++ ) {
267  (*it)->preUpdate();
268  }
269  //--- simulate physic
270 #ifdef WORLDSIM_USE_NEWTON
271  NewtonUpdate( priv->world, timestepv );
272 #endif
273  //--- postUpdate Objects
274  for( QLinkedList<WObject*>::iterator it = objs.begin(); it != objs.end(); it++ ) {
275  (*it)->postUpdate();
276  }
277  //--- postUpdate Joints
278  for( QLinkedList<PhyJoint*>::iterator it = jointsv.begin(); it != jointsv.end(); it++ ) {
279  (*it)->postUpdate();
280  }
281  time += timestepv;
282  emit advanced();
283 }
284 
286  if ( isFinish ) return;
287  /* nothing else to do ?!?! */
288  isFinish = true;
289  isInit = false;
290  emit finished();
291 }
292 
293 int World::play() {
294  if ( state == playingS ) return timerId;
295  if ( timerId != 0 ) return timerId;
296  state = playingS;
297  timerId = startTimer( 0 );
298  return timerId;
299 }
300 
301 void World::pause() {
302  state = pausedS;
303  if ( timerId != 0 ) killTimer( timerId );
304  timerId = 0;
305  emit paused();
306  return;
307 }
308 
309 void World::stop() {
310  finalize();
311  state = stoppedS;
312  if ( timerId != 0 ) killTimer( timerId );
313  timerId = 0;
314  emit stopped();
315  return;
316 }
317 
318 void World::timerEvent( QTimerEvent* ) {
319  advance();
320  return;
321 }
322 
323 void World::customEvent( QEvent* e ) {
324  switch( e->type() ) {
325  case E_Advance:
326  advance();
327  e->accept();
328  break;
329  case E_Play:
330  play();
331  e->accept();
332  break;
333  case E_Stop:
334  stop();
335  e->accept();
336  break;
337  case E_Pause:
338  pause();
339  e->accept();
340  break;
341  default:
342  e->ignore();
343  break;
344  }
345  return;
346 }
347 
348 real World::elapsedTime() const {
349  return time;
350 }
351 
353  time = 0.0;
354 }
355 
356 void World::setIsRealTime( bool b ) {
357  isrealtimev = b;
358 }
359 
360 bool World::isRealTime() const {
361  return isrealtimev;
362 }
363 
364 void World::setTimeStep( real ts ) {
365  timestepv = ts;
366 }
367 
368 void World::setMinimumFrameRate( unsigned int frames ) {
369 #ifdef WORLDSIM_USE_NEWTON
370  NewtonSetMinimumFrameRate( priv->world, frames );
371 #endif
372 }
373 
374 real World::timeStep() const {
375  return timestepv;
376 }
377 
378 void World::setGravityForce( real g ) {
379  gforce = g;
380 }
381 
382 real World::gravityForce() const {
383  return gforce;
384 }
385 
386 void World::advanceUntil( real t ) {
387  blockSignals( true );
388  bool b = isrealtimev;
389  isrealtimev = false;
390  while ( time < t ) {
391  advance();
392  }
393  isrealtimev = b;
394  blockSignals( false );
395 }
396 
397 const QLinkedList<WObject*> World::objects() {
398  return objs;
399 }
400 
401 WObject* World::getObject( QString name ) {
402  //--- not very efficient, FIX adding a QMap
403  foreach( WObject* obj, objs ) {
404  if ( obj->name() == name ) return obj;
405  }
406  return NULL;
407 }
408 
409 const QLinkedList<PhyJoint*> World::joints() {
410  return jointsv;
411 }
412 
413 const QHash<WObject*, QList<PhyJoint*> > World::mapObjectsToJoints() {
414  return mapObjJoints;
415 }
416 
418  nobjs.insert( qMakePair( obj1, obj2 ) );
419  nobjs.insert( qMakePair( obj2, obj1 ) );
420 }
421 
423  nobjs.remove( qMakePair( obj1, obj2 ) );
424  nobjs.remove( qMakePair( obj2, obj1 ) );
425 }
426 
427 void World::setSolverModel( QString model ) {
428 #ifdef WORLDSIM_USE_NEWTON
429  if ( model =="exact" ) {
430  NewtonSetSolverModel( priv->world, 0 );
431  } else if ( model == "linear" ) {
432  NewtonSetSolverModel( priv->world, 1 );
433  }
434 #endif
435 }
436 
437 void World::setFrictionModel( QString model ) {
438 #ifdef WORLDSIM_USE_NEWTON
439  if ( model =="exact" ) {
440  NewtonSetFrictionModel( priv->world, 0 );
441  } else if ( model == "linear" ) {
442  NewtonSetFrictionModel( priv->world, 1 );
443  }
444 #endif
445 }
446 
447 void World::setMultiThread( int numThreads ) {
448 #ifdef WORLDSIM_USE_NEWTON
449  numThreads = max( 1, numThreads );
450  NewtonSetThreadsCount( priv->world, numThreads );
451 #endif
452 }
453 
454 void World::setSize( const wVector& minPoint, const wVector& maxPoint ) {
455  minP = minPoint;
456  maxP = maxPoint;
457 #ifdef WORLDSIM_USE_NEWTON
458  NewtonSetWorldSize( priv->world, &minP[0], &maxP[0] );
459 #endif
460  emit resized();
461 }
462 
463 void World::size( wVector &minPoint, wVector &maxPoint ) {
464  minPoint = minP;
465  maxPoint = maxP;
466 }
467 
468 void World::pushObject( WObject* phy ) {
469  objs.push_back( phy );
470  emit addedObject( phy );
471 }
472 
473 void World::popObject( WObject* phy ) {
474  const int numRemoved = objs.removeAll( phy );
475  if ( numRemoved == 1 ) {
476  emit removedObject( phy );
477  }
478 #ifdef FARSA_DEBUG
479  else if (numRemoved > 1) {
480  qDebug( "INTERNAL ERROR: more than one object with the same address found in the World::objs list" );
481  }
482 #endif
483 }
484 
485 void World::pushJoint( PhyJoint* phy ) {
486  jointsv.push_back( phy );
487  mapObjJoints[ phy->child() ].push_back( phy );
488  if ( phy->parent() ) {
489  mapObjJoints[ phy->parent() ].push_back( phy );
490  }
491  emit addedJoint( phy );
492 }
493 
494 void World::popJoint( PhyJoint* phy ) {
495  const int numRemoved = jointsv.removeAll( phy );
496  if ( numRemoved == 1 ) {
497  mapObjJoints[ phy->child() ].removeAll( phy );
498  mapObjJoints.remove( phy->child() );
499  if ( phy->parent() ) {
500  mapObjJoints[ phy->parent() ].removeAll( phy );
501  mapObjJoints.remove( phy->parent() );
502  }
503  emit removedJoint( phy );
504  }
505 #ifdef FARSA_DEBUG
506  else if (numRemoved > 1) {
507  qDebug( "INTERNAL ERROR: more than one joint with the same address found in the World::jointsv list" );
508  }
509 #endif
510 }
511 
513 #ifdef WORLDSIM_USE_NEWTON
514  //qDebug() << "Memory Used by" << name() << NewtonGetMemoryUsed();
515  NewtonInvalidateCache( priv->world );
516  cmap.clear();
517  /*
518  qDebug() << "Objects: " << objs.size();
519  qDebug() << "Joints: " << jointsv.size();
520  qDebug() << "Map Obj-Joints: " << mapObjJoints.size();
521  qDebug() << "Contacts: " << cmap.size();
522  qDebug() << "Non-Collidable Objs: " << nobjs.size();
523  */
524  /*
525  qDebug() << "Materials: " << mats->mats.size();
526  qDebug() << "Gravities: " << mats->gravities.size();
527  qDebug() << "Material Infos: " << mats->pmap.size();
528  */
529 #endif
530 }
531 
532 const contactMap& World::contacts() {
533  return cmap;
534 }
535 
536 bool World::checkContacts( PhyObject* obj1, PhyObject* obj2, int maxContacts, QVector<wVector>* contacts, QVector<wVector>* normals, QVector<real>* penetra ) {
537 #ifdef WORLDSIM_USE_NEWTON
538  // Allocating the vector we use to get data from NewtonCollisionCollide
539  real *const tmp_data = new real[2 * 3 * maxContacts + maxContacts];
540  real *const tmp_contacts = &tmp_data[0 * 3 * maxContacts];
541  real *const tmp_normals = &tmp_data[1 * 3 * maxContacts];
542  real *const tmp_penetra = &tmp_data[2 * 3 * maxContacts];
543 
544  // Computing contacts
545  wMatrix t1 = obj1->matrix();
546  wMatrix t2 = obj2->matrix();
547  const int numContacts = NewtonCollisionCollide( priv->world, maxContacts, obj1->priv->collision, &t1[0][0], obj2->priv->collision, &t2[0][0], tmp_contacts, tmp_normals, tmp_penetra, 0 );
548 
549  // Now copying contacts information into user vectors
550  if (contacts != NULL) {
551  contacts->resize(numContacts);
552  for (int i = 0; i < numContacts; i++) {
553  (*contacts)[i].x = tmp_contacts[0 + i * 3];
554  (*contacts)[i].y = tmp_contacts[1 + i * 3];
555  (*contacts)[i].z = tmp_contacts[2 + i * 3];
556  }
557  }
558  if (normals != NULL) {
559  normals->resize(numContacts);
560  for (int i = 0; i < numContacts; i++) {
561  (*normals)[i].x = tmp_normals[0 + i * 3];
562  (*normals)[i].y = tmp_normals[1 + i * 3];
563  (*normals)[i].z = tmp_normals[2 + i * 3];
564  }
565  }
566  if (penetra != NULL) {
567  penetra->resize(numContacts);
568  for (int i = 0; i < numContacts; i++) {
569  (*penetra)[i] = tmp_penetra[i];
570  }
571  }
572 
573  delete[] tmp_data;
574  return (numContacts != 0);
575 #endif
576 }
577 
578 bool World::smartCheckContacts( PhyObject* obj1, PhyObject* obj2, int maxContacts, QVector<wVector>* contacts )
579 {
580  if (obj1->getKinematic() || obj2->getKinematic() || (obj1->getStatic() && obj2->getStatic())) {
581  return checkContacts( obj1, obj2, maxContacts, contacts, NULL, NULL);
582  } else {
583  if (!cmap.contains(obj1)) {
584  return false;
585  }
586 
587  // Taking the vector of contacts
588  const contactVec& c = cmap[obj1];
589  bool collision = false;
590  if (contacts != NULL) {
591  contacts->clear();
592  }
593 
594  // Now extracting all collisions between obj1 and obj2
595  for (int i = 0; i < c.size(); i++) {
596  if (c[i].collide == obj2) {
597  collision = true;
598  if ((contacts != NULL) && (contacts->size() < maxContacts)) {
599  // Adding contact point
600  contacts->append(c[i].worldPos);
601  }
602  }
603  }
604 
605  return collision;
606  }
607 }
608 
609 real World::collisionRayCast( PhyObject* obj, wVector start, wVector end, wVector* normal )
610 {
611 #ifdef WORLDSIM_USE_NEWTON
612  // The vector storing the normal to the contact point and the attribute (unused)
613  dFloat n[3];
614  int attribute;
615  wVector localStart = obj->matrix().inverse().transformVector(start);
616  wVector localEnd = obj->matrix().inverse().transformVector(end);
617 
618  // Computing the contact
619  const real contact = NewtonCollisionRayCast(obj->priv->collision, &localStart[0], &localEnd[0], n, &attribute);
620 
621  if (normal != NULL) {
622  (*normal)[0] = n[0];
623  (*normal)[1] = n[1];
624  (*normal)[2] = n[2];
625  }
626 
627  return contact;
628 #else
629  return 2.0;
630 #endif
631 }
632 
633 rayCastHitVector World::worldRayCast( wVector start, wVector end, bool onlyClosest, const QSet<PhyObject*>& ignoredObjs )
634 {
635 #ifdef WORLDSIM_USE_NEWTON
636  WorldPrivate::WorldRayCastCallbackUserData data(start, end, onlyClosest, ignoredObjs);
637 
638  // Casting the ray
639  NewtonWorldRayCast(priv->world, &start[0], &end[0], WorldPrivate::worldRayFilterCallback, &data, NULL);
640 
641  return data.vector;
642 #else
643  return rayCastHitVector();
644 #endif
645 }
646 
647 // bool World::checkContacts( PhyObject* obj1, PhyObject* obj2 ) {
648 // #ifdef WORLDSIM_USE_NEWTON
649 // const int maxsize = 4;
650 // real contacts[3*maxsize];
651 // real normals[3*maxsize];
652 // real penetra[3*maxsize];
653 // wMatrix t1 = obj1->matrix();
654 // wMatrix t2 = obj2->matrix();
655 // bool ret = ( NewtonCollisionCollide( priv->world, maxsize, obj1->priv->collision, &t1[0][0], obj2->priv->collision, &t2[0][0], contacts, normals, penetra, 0 ) != 0 );
656 // return ret;
657 // #endif
658 // }
659 
660 bool World::closestPoints( PhyObject* objA, PhyObject* objB, wVector& pointA, wVector& pointB ) {
661 #ifdef WORLDSIM_USE_NEWTON
662  wVector normal;
663  wMatrix t1 = objA->matrix();
664  wMatrix t2 = objB->matrix();
665  int ret = NewtonCollisionClosestPoint( priv->world,
666  objA->priv->collision, &t1[0][0],
667  objB->priv->collision, &t2[0][0],
668  &pointA[0], &pointB[0], &normal[0], 0 );
669  if ( ret == 0 ) {
670  pointA = wVector(0,0,0);
671  pointB = wVector(0,0,0);
672  return false;
673  }
674  return true;
675 #endif
676 }
677 
678 } // end namespace farsa