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