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