00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "aesop-physics.h"
00037
00038 #include "bullet/bullet.h"
00039
00040 #include "common/wave_ex.h"
00041 #include "datahash/datahash_util.h"
00042 #include "hfield/heightfield.h"
00043 #include "perf/perf.h"
00044 #include "threadsafe/threadsafe_queue.h"
00045 #include "trimesh/trimesh.h"
00046
00047
00048 namespace aesop {
00049
00050
00051
00052 static const float s_stepHeight = 0.35;
00053 static const int s_ghostCollisionFlags =
00054 btCollisionObject::CF_KINEMATIC_OBJECT;
00055
00056
00057 PhysicsObject::~PhysicsObject(void) throw() { }
00058 PhysicsShape::~PhysicsShape(void) throw() { }
00059 PhysicsWorld::~PhysicsWorld(void) throw() { }
00060
00061
00062 typedef btTransform Transform3D;
00063
00064
00066
00067
00068
00070
00071 typedef std::map<dword_t, smart_ptr<PhysicsObject> > id_object_map_t;
00072 static id_object_map_t s_idMap;
00073 static smart_mutex s_idMapMutex;
00074
00075
00076 template <class T>
00077 long
00078 getNewIdForObject
00079 (
00080 IN smart_ptr<T>& in_obj
00081 )
00082 {
00083
00084 smart_ptr<PhysicsObject> obj = in_obj;
00085 ASSERT(obj, "failed to downcast?");
00086
00087
00088 mlock l(s_idMapMutex);
00089
00090
00091 for (;;) {
00092 long id = rand() + 1;
00093 ASSERT(id > 0, "bad id: %ld", id);
00094 if (s_idMap.end() == s_idMap.find(id)) {
00095
00096 s_idMap[id] = obj;
00097 return id;
00098 }
00099 }
00100 }
00101
00102
00103
00104 smart_ptr<PhysicsObject>
00105 getPhysicsObjectById
00106 (
00107 IN dword_t id
00108 )
00109 {
00110 ASSERT(id > 0, "Bad id: %ld", (long) id);
00111
00112
00113 mlock l(s_idMapMutex);
00114
00115 id_object_map_t::iterator i = s_idMap.find(id);
00116 if (s_idMap.end() == i) {
00117 DPRINTF("Object with id %ld not found!", (long) id);
00118 return NULL;
00119 }
00120
00121 return i->second;
00122 }
00123
00124
00125
00126 static void
00127 removeObjectFromIdMap
00128 (
00129 IN long id
00130 )
00131 {
00132 ASSERT(id > 0, "Bad id: %ld", id);
00133
00134
00135 mlock l(s_idMapMutex);
00136
00137 id_object_map_t::iterator i = s_idMap.find(id);
00138 if (s_idMap.end() != i) {
00139 s_idMap.erase(i);
00140 }
00141 }
00142
00143
00144
00146
00147
00148
00150
00151 static inline btVector3
00152 getBulletVectorFromPoint3d
00153 (
00154 IN const point3d_t& pos
00155 )
00156 throw()
00157 {
00158 return btVector3(pos.x, pos.y, pos.z);
00159 }
00160
00161
00162
00163 template <class T>
00164 static inline point3d_t
00165 getPoint3dFromBulletType
00166 (
00167 IN const T& v
00168 )
00169 throw()
00170 {
00171 return point3d_t(v.x(), v.y(), v.z());
00172 }
00173
00174
00175
00176 static inline point3d_t
00177 getNullPoint
00178 (
00179 void
00180 )
00181 throw()
00182 {
00183 point3d_t p;
00184 p.clear();
00185 return p;
00186 }
00187
00188
00189
00190 static point3d_t
00191 getCollisionObjectPosition
00192 (
00193 IN const btCollisionObject * obj
00194 )
00195 {
00196 ASSERT(obj, "null");
00197
00198 const btTransform& T = obj->getWorldTransform();
00199 btVector3 localOrigin(0, 0, 0);
00200 btVector3 position = T(localOrigin);
00201
00202 return point3d_t(position.x(), position.y(), position.z());
00203 }
00204
00205
00206
00207 static void
00208 dumpVector
00209 (
00210 IN const char * title,
00211 IN const btVector3& v
00212 )
00213 throw()
00214 {
00215 ASSERT(title, "null");
00216
00217 DPRINTF("%s: [ %5.1f, %5.1f, %5.1f ]", title,
00218 v[0], v[1], v[2]);
00219 }
00220
00221
00222
00223 static void
00224 dumpAabb
00225 (
00226 IN const char * title,
00227 IN const btVector3& aabbMin,
00228 IN const btVector3& aabbMax
00229 )
00230 throw()
00231 {
00232 ASSERT(title, "null");
00233
00234 DPRINTF("%s: ( %5.1f, %5.1f, %5.1f ) - ( %5.1f, %5.1f, %5.1f )",
00235 title, aabbMin[0], aabbMin[1], aabbMin[2],
00236 aabbMax[0], aabbMax[1], aabbMax[2]);
00237 }
00238
00239
00240
00241 static void
00242 dumpCollisionObject
00243 (
00244 IN const char * title,
00245 IN const btCollisionObject * obj
00246 )
00247 throw()
00248 {
00249 ASSERT(obj, "null");
00250 ASSERT(sizeof(btScalar) == sizeof(float), "mismatch!");
00251
00252
00253 const btCollisionShape * shape = obj->getCollisionShape();
00254 ASSERT(shape, "null shape for rigid body");
00255
00256 const char * shape_name = shape->getName();
00257 if (!title) {
00258 title = shape_name;
00259 }
00260
00261 DPRINTF(" %s", title);
00262 DPRINTF(" shape name = '%s'", shape->getName());
00263 DPRINTF(" shape type = %d", shape->getShapeType());
00264 DPRINTF(" Object flags: %s %s %s %s",
00265 shape->isPolyhedral() ? "polyhedral" : "",
00266 shape->isConvex() ? "convex" : "",
00267 shape->isConcave() ? "concave" : "",
00268 shape->isCompound() ? "compound" : "");
00269
00270
00271 point3d_t pos = getCollisionObjectPosition(obj);
00272 DPRINTF(" position: (%f, %f, %f)", pos.x, pos.y, pos.z);
00273
00274
00275 btTransform T = obj->getWorldTransform();
00276 btVector3 aabbMin, aabbMax;
00277 shape->getAabb(T, aabbMin, aabbMax);
00278 dumpAabb(" aabb from shape", aabbMin, aabbMax);
00279
00280 T.setIdentity();
00281 shape->getAabb(T, aabbMin, aabbMax);
00282 dumpAabb(" aabb (relative)", aabbMin, aabbMax);
00283
00284
00285 const btBroadphaseProxy * proxy = obj->getBroadphaseHandle();
00286 if (proxy) {
00287 DPRINTF(" unique ID = %d", proxy->getUid());
00288 } else {
00289 DPRINTF(" NULL proxy -- not in world?");
00290 }
00291
00292
00293 const btRigidBody * prb = dynamic_cast<const btRigidBody *>(obj);
00294 if (prb) {
00295 DPRINTF(" inverse mass = %f", prb->getInvMass());
00296
00297 btVector3 linVel = prb->getLinearVelocity();
00298 dumpVector(" linear velocity", linVel);
00299
00300 prb->getAabb(aabbMin, aabbMax);
00301 dumpAabb(" aabb from rigid body ", aabbMin, aabbMax);
00302 }
00303 }
00304
00305
00306
00308
00309
00310
00312
00313 class ObjectImpl : public PhysicsObject {
00314 public:
00315 ObjectImpl(void) throw();
00316 ~ObjectImpl(void) throw() { }
00317
00318
00319 dword_t getId(void) const throw() { return m_id; }
00320 int getFlags(void) const throw() { return m_flags; }
00321 void dump(IN const char * txt) const throw() {
00322 DPRINTF("Physics object: %s", txt);
00323 DPRINTF(" id: %ld", (long) m_id);
00324 dumpCollisionObject(txt, m_obj);
00325 }
00326 point3d_t getPosition(void) const throw()
00327 { return getCollisionObjectPosition(m_obj); }
00328 rect3d_t getBoundingBox(void) const throw();
00329 quaternion_t getOrientation(void) const throw();
00330 point3d_t getLinearVelocity(void) const throw();
00331 point3d_t getAngularVelocity(void) const throw();
00332 void setOrientation(IN const quaternion_t& orient);
00333 void setLinearVelocity(IN const point3d_t& linear);
00334 void setAngularVelocity(IN const point3d_t& angular);
00335
00336 void requestMove(IN const point3d_t& delta, IN float dt);
00337 void setPosition(IN const point3d_t& pos);
00338 float getMass(void) const throw() { return m_mass; }
00339 void iterateOverlappingObjects(IN object_iteration_fn fn,
00340 IN void * context);
00341 void setUserPointer(IN void * up) throw() { m_up = up; }
00342 void * getUserPointer(void) const throw() { return m_up; }
00343
00344
00345 void setId(IN dword_t id) throw() { m_id = id; }
00346 bool isMoveable(void) const throw() { return !!m_ghost; }
00347 btCollisionObject * getCollisionObject(void) throw() {
00348 ASSERT(m_obj, "null");
00349 return m_obj;
00350 }
00351 btKinematicCharacterController * getCharacter(void)
00352 { return m_character; }
00353 bool wantCollisionEvents(void) const throw()
00354 { return eFlag_Events & m_flags; }
00355
00356
00357 static smart_ptr<ObjectImpl> create(IN smart_ptr<btCollisionObject>& o,
00358 IN const physics_meta_t& meta);
00359 static smart_ptr<ObjectImpl> create(
00360 IN smart_ptr<btKinematicCharacterController>& c,
00361 IN smart_ptr<btPairCachingGhostObject>& ghost,
00362 IN const physics_meta_t& meta);
00363
00364 private:
00365
00366 void addContext(IN const physics_meta_t& meta);
00367
00368
00369 int m_flags;
00370 dword_t m_id;
00371 float m_mass;
00372 void * m_up;
00373 point3d_t m_linVel;
00374 btRigidBody * m_rigidBody;
00375 smart_ptr<btCollisionObject> m_obj;
00376 smart_ptr<btKinematicCharacterController> m_character;
00377 smart_ptr<btPairCachingGhostObject> m_ghost;
00378 };
00379
00380 typedef std::vector<ObjectImpl *> vec_objimpl_t;
00381
00382
00383
00384 ObjectImpl::ObjectImpl(void)
00385 throw()
00386 {
00387 m_id = 0;
00388 m_flags = 0;
00389 m_mass = 0.0;
00390 m_up = 0;
00391 m_linVel.clear();
00392 }
00393
00394
00395
00396 static ObjectImpl *
00397 getObject
00398 (
00399 IN PhysicsObject * obj
00400 )
00401 {
00402 ASSERT(obj, "null");
00403 ObjectImpl * o = dynamic_cast<ObjectImpl *>(obj);
00404 ASSERT(o, "PhysicsObject not implemented by ObjectImpl?");
00405 return o;
00406 }
00407
00408
00409
00410
00411
00412
00413
00414 static ObjectImpl *
00415 getPhysicsObject
00416 (
00417 IN void * pv
00418 )
00419 {
00420 ASSERT(pv, "null");
00421 btCollisionObject * obj = (btCollisionObject *) pv;
00422 ObjectImpl * poi = (ObjectImpl *) obj->getUserPointer();
00423 ASSERT(poi, "user pointer was never set?");
00424 return poi;
00425 }
00426
00427
00428
00429 rect3d_t
00430 ObjectImpl::getBoundingBox
00431 (
00432 void
00433 )
00434 const
00435 throw()
00436 {
00437 ASSERT(m_obj, "null collision object");
00438 const btCollisionShape * shape = m_obj->getCollisionShape();
00439
00440 btTransform T = m_obj->getWorldTransform();
00441 btVector3 aabbMin, aabbMax;
00442 shape->getAabb(T, aabbMin, aabbMax);
00443
00444 rect3d_t r;
00445 r.x0 = aabbMin.x();
00446 r.y0 = aabbMin.y();
00447 r.z0 = aabbMin.z();
00448 r.x1 = aabbMax.x();
00449 r.y1 = aabbMax.y();
00450 r.z1 = aabbMax.z();
00451 return r;
00452 }
00453
00454
00455
00456 quaternion_t
00457 ObjectImpl::getOrientation
00458 (
00459 void
00460 )
00461 const
00462 throw()
00463 {
00464 ASSERT(m_obj, "null collision object?");
00465
00466 const btTransform& T = m_obj->getWorldTransform();
00467 btQuaternion q = T.getRotation();
00468 quaternion_t retval;
00469 retval.x = q.x();
00470 retval.y = q.y();
00471 retval.z = q.z();
00472 retval.w = q.w();
00473 return retval;
00474 }
00475
00476
00477
00478 point3d_t
00479 ObjectImpl::getLinearVelocity
00480 (
00481 void
00482 )
00483 const
00484 throw()
00485 {
00486 ASSERT(m_obj, "null collision object?");
00487
00488
00489 if (this->isMoveable()) {
00490
00491
00492 return m_linVel;
00493 }
00494
00495
00496 if (m_rigidBody) {
00497 return getPoint3dFromBulletType(
00498 m_rigidBody->getLinearVelocity());
00499 }
00500
00501
00502
00503 return getNullPoint();
00504 }
00505
00506
00507
00508 point3d_t
00509 ObjectImpl::getAngularVelocity
00510 (
00511 void
00512 )
00513 const
00514 throw()
00515 {
00516 ASSERT(m_obj, "null collision object?");
00517
00518 if (m_rigidBody) {
00519 return getPoint3dFromBulletType(
00520 m_rigidBody->getAngularVelocity());
00521 }
00522 return getNullPoint();
00523 }
00524
00525
00526
00527 void
00528 ObjectImpl::setOrientation
00529 (
00530 IN const quaternion_t& orient
00531 )
00532 {
00533 ASSERT(m_obj, "requires object");
00534
00535
00536
00537
00538
00539
00540
00541
00542 point3d_t pos = this->getPosition();
00543 btTransform T;
00544 T.setOrigin(getBulletVectorFromPoint3d(pos));
00545 btQuaternion q(orient.x, orient.y, orient.z, orient.w);
00546 T.setRotation(q);
00547
00548 m_obj->setWorldTransform(T);
00549 }
00550
00551
00552
00553 void
00554 ObjectImpl::setLinearVelocity
00555 (
00556 IN const point3d_t& linear
00557 )
00558 {
00559 ASSERT(m_obj, "requires object");
00560
00561
00562 if (this->isMoveable()) {
00563 this->requestMove(linear, 1.0);
00564 return;
00565 }
00566
00567 if (!m_rigidBody) {
00568 return;
00569 }
00570
00571 m_rigidBody->setLinearVelocity(getBulletVectorFromPoint3d(linear));
00572 }
00573
00574
00575
00576 void
00577 ObjectImpl::setAngularVelocity
00578 (
00579 IN const point3d_t& angular
00580 )
00581 {
00582 ASSERT(m_obj, "requires object");
00583 if (!m_rigidBody) {
00584 return;
00585 }
00586
00587 m_rigidBody->setAngularVelocity(getBulletVectorFromPoint3d(angular));
00588 }
00589
00590
00591
00592 void
00593 ObjectImpl::requestMove
00594 (
00595 IN const point3d_t& delta,
00596 IN float dt
00597 )
00598 {
00599 ASSERT(dt > 0.0, "bad time delta: %f", dt);
00600 if (!this->isMoveable()) {
00601 DPRINTF("Asking a non-moveable object to move?");
00602 return;
00603 }
00604 ASSERT(m_character, "null");
00605
00606
00607
00608
00609
00610
00611 m_linVel = (1.0 / dt) * delta;
00612
00613 m_character->setVelocityForTimeInterval(
00614 getBulletVectorFromPoint3d(m_linVel), dt);
00615 }
00616
00617
00618
00619 void
00620 ObjectImpl::setPosition
00621 (
00622 IN const point3d_t& pos
00623 )
00624 {
00625
00626 btTransform T;
00627 T.setIdentity();
00628 T.setOrigin(getBulletVectorFromPoint3d(pos));
00629
00630
00631 m_obj->setWorldTransform(T);
00632 }
00633
00634
00635
00636 void
00637 ObjectImpl::iterateOverlappingObjects
00638 (
00639 IN object_iteration_fn fn,
00640 IN void * context
00641 )
00642 {
00643 ASSERT(fn, "null");
00644
00645
00646 btGhostObject * ghost =
00647 dynamic_cast<btGhostObject *>((btCollisionObject *) m_obj);
00648 if (!ghost) {
00649 DPRINTF("WARNING: can only iterate overlapping pairs for "
00650 "ghost objects!");
00651 return;
00652 }
00653
00654 int numObjects = ghost->getNumOverlappingObjects();
00655 for (int i = 0; i < numObjects; ++i) {
00656 btCollisionObject * cobj = ghost->getOverlappingObject(i);
00657 ASSERT(cobj, "null");
00658
00659 ObjectImpl * other = getPhysicsObject(cobj);
00660 ASSERT(other, "null");
00661
00662
00663
00664 long id = other->getId();
00665 smart_ptr<PhysicsObject> obj = getPhysicsObjectById(id);
00666 ASSERT(obj, "null");
00667
00668
00669 fn(obj, context);
00670 }
00671 }
00672
00673
00674
00675 void
00676 ObjectImpl::addContext
00677 (
00678 IN const physics_meta_t& meta
00679 )
00680 {
00681
00682 m_flags = meta.objectFlags;
00683
00684
00685 m_mass = meta.mass;
00686
00687
00688 m_rigidBody = dynamic_cast<btRigidBody *>((btCollisionObject *) m_obj);
00689
00690
00691 m_obj->setUserPointer((void *) this);
00692 }
00693
00694
00695
00696 smart_ptr<ObjectImpl>
00697 ObjectImpl::create
00698 (
00699 IN smart_ptr<btCollisionObject>& o,
00700 IN const physics_meta_t& meta
00701 )
00702 {
00703 ASSERT(o, "null");
00704
00705 smart_ptr<ObjectImpl> local = new ObjectImpl;
00706 ASSERT(local, "out of memory");
00707 local->m_obj = o;
00708
00709 local->addContext(meta);
00710
00711 return local;
00712 }
00713
00714
00715
00716 smart_ptr<ObjectImpl>
00717 ObjectImpl::create
00718 (
00719 IN smart_ptr<btKinematicCharacterController>& character,
00720 IN smart_ptr<btPairCachingGhostObject>& ghost,
00721 IN const physics_meta_t& meta
00722 )
00723 {
00724 ASSERT(character, "null");
00725 ASSERT(ghost, "null");
00726
00727 smart_ptr<ObjectImpl> local = new ObjectImpl;
00728 ASSERT(local, "out of memory");
00729
00730 local->m_character = character;
00731 local->m_ghost = ghost;
00732 local->m_obj = ghost;
00733
00734 local->addContext(meta);
00735
00736 return local;
00737 }
00738
00739
00740
00742
00743
00744
00746
00747 class ShapeImpl : public PhysicsShape {
00748 public:
00749 ~ShapeImpl(void) throw() { }
00750
00751
00752 struct private_context_t {
00753 smart_ptr<btTriangleIndexVertexArray> tiva;
00754 };
00755
00756
00757 void setContext(IN void * ctx) throw() {
00758 ASSERT(m_shape, "null");
00759 m_shape->setUserPointer((void *) ctx);
00760 }
00761
00762 void * getContext(void) throw() {
00763 ASSERT(m_shape, "null");
00764 return m_shape->getUserPointer();
00765 }
00766
00767
00768 btCollisionShape * getShape(void) throw() {
00769 ASSERT(m_shape, "null");
00770 return m_shape;
00771 }
00772
00773
00774 static smart_ptr<ShapeImpl> create(IN smart_ptr<btCollisionShape>& shape,
00775 IN private_context_t * ctx = NULL)
00776 {
00777 ASSERT(shape, "null");
00778 smart_ptr<ShapeImpl> local = new ShapeImpl;
00779 ASSERT(local, "out of memory");
00780 local->m_shape = shape;
00781 if (ctx) {
00782 local->m_ctx = *ctx;
00783 }
00784 return local;
00785 }
00786
00787 private:
00788
00789 smart_ptr<btCollisionShape> m_shape;
00790 private_context_t m_ctx;
00791 };
00792
00793
00794
00795 static ShapeImpl *
00796 getShape
00797 (
00798 IN const PhysicsShape * s
00799 )
00800 {
00801 ASSERT(s, "null");
00802
00803 const ShapeImpl * csi = dynamic_cast<const ShapeImpl *>(s);
00804 ASSERT(csi, "PhysicsShape not implemented with ShapeImpl?");
00805
00806
00807 return (ShapeImpl *) csi;
00808 }
00809
00810
00811
00813
00814
00815
00817
00818 class World : public PhysicsWorld {
00819 public:
00820 World(void) throw();
00821 ~World(void) throw() { this->removeAll(); }
00822
00823
00824 void initialize(IN const Datahash * params);
00825
00826
00827 void addObject(IN smart_ptr<PhysicsObject>&obj);
00828 void removeObject(IN PhysicsObject * obj);
00829 long getObjectCount(void) throw();
00830 void tick(IN float seconds);
00831
00832 void getObjectIterator(OUT object_iterator_t& i);
00833 bool getNextObject(IO object_iterator_t& i,
00834 OUT smart_ptr<PhysicsObject>& obj);
00835
00836 void getCollisionIterator(OUT collision_iterator_t& i);
00837 bool getNextCollision(IO collision_iterator_t& i,
00838 OUT collision_record_t& cr);
00839
00840 smart_ptr<PhysicsObject> rayTest(IN const point3d_t& from,
00841 IN const point3d_t& to);
00842
00843 private:
00844
00845 struct real_obj_iter_t {
00846 dword_t magic;
00847 dword_t rvn;
00848 id_object_map_t::iterator iter;
00849 };
00850
00851 struct real_collision_iter_t {
00852 dword_t magic;
00853 dword_t rvn;
00854 int iManifold;
00855 };
00856
00857
00858 static real_obj_iter_t * getRealObjectIterator(
00859 IN object_iterator_t& i) throw()
00860 { return (real_obj_iter_t *) &i; }
00861
00862 static real_collision_iter_t * getRealCollisionIterator(
00863 IN collision_iterator_t& i) throw()
00864 { return (real_collision_iter_t *) &i; }
00865
00866 void removeObjectInternal(IN ObjectImpl * o);
00867 void removeAll(void);
00868
00869
00870 id_object_map_t m_objects;
00871 int m_maxTimeSteps;
00872 float m_engineTimeStep;
00873 smart_ptr<btDefaultCollisionConfiguration> m_colcfg;
00874 smart_ptr<btCollisionDispatcher> m_dispatch;
00875 smart_ptr<btAxisSweep3> m_as3;
00876 smart_ptr<btSequentialImpulseConstraintSolver> m_solver;
00877 smart_ptr<btDiscreteDynamicsWorld> m_world;
00878 smart_mutex m_mutex;
00879 dword_t m_rvn;
00880 };
00881
00882 static const dword_t s_dwMagicObject = 0x34892199;
00883 static const dword_t s_dwMagicCollision = 0x55784923;
00884
00885
00886
00887 World::World
00888 (
00889 void
00890 )
00891 throw()
00892 {
00893 m_rvn = 777;
00894 }
00895
00896
00897
00898 void
00899 World::initialize
00900 (
00901 IN const Datahash * params
00902 )
00903 {
00904 ASSERT(params, "null");
00905
00906
00907 ASSERT2(sizeof(object_iterator_t) >= sizeof(real_obj_iter_t),
00908 "Public iterator is too small! " << sizeof(object_iterator_t)
00909 << " bytes vs. " << sizeof(real_obj_iter_t) << " bytes.");
00910 ASSERT2(sizeof(collision_iterator_t) >= sizeof(real_collision_iter_t),
00911 "Public iterator is too small! " << sizeof(collision_iterator_t)
00912 << " bytes vs. " << sizeof(real_collision_iter_t) << " bytes.");
00913
00914 smart_ptr<Datahash> phys = getSubhash(params, "physics");
00915 ASSERT(phys, "null physics subhash?");
00916
00917
00918 float minCoord = getFloat(phys, "minCoord");
00919 float maxCoord = getFloat(phys, "maxCoord");
00920 ASSERT(maxCoord > minCoord, "Bad min/max = %f / %f",
00921 minCoord, maxCoord);
00922
00923 m_maxTimeSteps = getInt(phys, "maxTimeSteps");
00924 ASSERT(m_maxTimeSteps > 0, "Bad maxTimeSteps: %d",
00925 m_maxTimeSteps);
00926 m_engineTimeStep = getFloat(phys, "engineTimeStep");
00927 ASSERT(m_engineTimeStep > 0.0, "Bad engineTimeStep: %f",
00928 m_engineTimeStep);
00929
00930 int gravAxis = getInt(phys, "gravityAxis");
00931 ASSERT(gravAxis > -2 && gravAxis < 3, "Bad gravity axis: %d",
00932 gravAxis);
00933 float gravity = getFloat(phys, "gravity");
00934
00935
00936
00937 DPRINTF("Initializing Bullet physics engine...");
00938 DPRINTF(" physics engine time step = %f seconds",
00939 m_engineTimeStep);
00940
00941
00942 m_colcfg = new btDefaultCollisionConfiguration();
00943 ASSERT(m_colcfg, "failed to create collision config");
00944
00945
00946 m_dispatch = new btCollisionDispatcher(m_colcfg);
00947 ASSERT(m_dispatch, "failed to create collision dispatcher");
00948
00949
00950 btVector3 min(minCoord, minCoord, minCoord);
00951 btVector3 max(maxCoord, maxCoord, maxCoord);
00952
00953 m_as3 = new btAxisSweep3(min, max);
00954 ASSERT(m_as3, "Failed to create axis sweep object");
00955
00956
00957 m_solver = new btSequentialImpulseConstraintSolver;
00958 ASSERT(m_solver, "failed to create constraint solver");
00959
00960
00961 m_world = new btDiscreteDynamicsWorld(m_dispatch, m_as3, m_solver,
00962 m_colcfg);
00963 ASSERT(m_world, "failed to create bullet physics world");
00964
00965
00966 m_as3->getOverlappingPairCache()->setInternalGhostPairCallback(
00967 new btGhostPairCallback);
00968
00969
00970 if (gravAxis >= 0) {
00971 btVector3 grav(0, 0, 0);
00972 grav[gravAxis] = gravity;
00973 dumpVector(" world gravity", grav);
00974 m_world->setGravity(grav);
00975 }
00976
00977
00978 DPRINTF("Finished initializing physics engine...");
00979 }
00980
00981
00982
00984
00985
00986
00988
00989 void
00990 World::addObject
00991 (
00992 IN smart_ptr<PhysicsObject>& obj
00993 )
00994 {
00995 ASSERT(obj, "null");
00996
00997
00998
00999 ObjectImpl * o = getObject(obj);
01000 ASSERT(o, "null");
01001
01002
01003 mlock l(m_mutex);
01004 ++m_rvn;
01005
01006
01007 long id = getNewIdForObject(obj);
01008 o->setId(id);
01009
01010 btCollisionObject * co = o->getCollisionObject();
01011 btRigidBody * rb = dynamic_cast<btRigidBody *>(co);
01012 if (rb) {
01013
01014 m_world->addRigidBody(rb);
01015 } else {
01016
01017
01018 int filterGroup = btBroadphaseProxy::DefaultFilter;
01019 int filterMask = btBroadphaseProxy::AllFilter;
01020 m_world->addCollisionObject(co, filterGroup, filterMask);
01021 }
01022
01023 if (o->isMoveable()) {
01024 DPRINTF(" Adding character...");
01025 m_world->addCharacter(o->getCharacter());
01026 }
01027
01028
01029 m_objects[id] = obj;
01030 }
01031
01032
01033
01034 void
01035 World::removeObject
01036 (
01037 IN PhysicsObject * obj
01038 )
01039 {
01040 ASSERT(obj, "null");
01041 ObjectImpl * o = getObject(obj);
01042 ASSERT(o, "null");
01043
01044
01045 mlock l(m_mutex);
01046 ++m_rvn;
01047 this->removeObjectInternal(o);
01048 }
01049
01050
01051
01052 long
01053 World::getObjectCount
01054 (
01055 void
01056 )
01057 throw()
01058 {
01059
01060 mlock l(m_mutex);
01061
01062 int n0 = m_world->getNumCollisionObjects();
01063 int n1 = m_objects.size();
01064 ASSERT(n0 == n1, "World has %d objects, we have %d", n0, n1);
01065 return n1;
01066 }
01067
01068
01069
01070 void
01071 World::tick
01072 (
01073 IN float seconds
01074 )
01075 {
01076 ASSERT(seconds > 0.0, "Bad tick duration: %f", seconds);
01077
01078
01079 mlock l(m_mutex);
01080 ++m_rvn;
01081
01082
01083 perf::Timer timer("PhysicsWorld::tick");
01084 long nSteps = (long) (seconds / m_engineTimeStep);
01085 if (nSteps > m_maxTimeSteps) {
01086 DPRINTF("WARNING: losing time");
01087 }
01088 m_world->stepSimulation(seconds, m_maxTimeSteps,
01089 m_engineTimeStep);
01090 }
01091
01092
01093
01094 void
01095 World::getObjectIterator
01096 (
01097 OUT object_iterator_t& i
01098 )
01099 {
01100 real_obj_iter_t * ri = getRealObjectIterator(i);
01101 ri->magic = s_dwMagicObject;
01102
01103
01104 mlock l(m_mutex);
01105 ri->rvn = m_rvn;
01106 ri->iter = m_objects.begin();
01107 }
01108
01109
01110
01111 bool
01112 World::getNextObject
01113 (
01114 IO object_iterator_t& i,
01115 OUT smart_ptr<PhysicsObject>& obj
01116 )
01117 {
01118 obj = NULL;
01119 real_obj_iter_t * ri = getRealObjectIterator(i);
01120 if (s_dwMagicObject != ri->magic) {
01121 WAVE_EX(wex);
01122 wex << "Invalid iterator passed to getNextObject()";
01123 }
01124
01125
01126 mlock l(m_mutex);
01127 if (ri->rvn != m_rvn)
01128 return false;
01129
01130 if (m_objects.end() == ri->iter)
01131 return false;
01132
01133
01134 obj = ri->iter->second;
01135 ASSERT(obj, "null object in our map");
01136 ++ri->iter;
01137 return true;
01138 }
01139
01140
01141
01142 void
01143 World::getCollisionIterator
01144 (
01145 OUT collision_iterator_t& i
01146 )
01147 {
01148 real_collision_iter_t * ri = getRealCollisionIterator(i);
01149 ri->magic = s_dwMagicCollision;
01150
01151
01152 mlock l(m_mutex);
01153 ri->rvn = m_rvn;
01154 ri->iManifold = 0;
01155 }
01156
01157
01158
01159 bool
01160 World::getNextCollision
01161 (
01162 IO collision_iterator_t& i,
01163 OUT collision_record_t& cr
01164 )
01165 {
01166 cr.clear();
01167 real_collision_iter_t * ri = getRealCollisionIterator(i);
01168 if (s_dwMagicCollision != ri->magic) {
01169 WAVE_EX(wex);
01170 wex << "Invalid collision iterator in getNextCollision()";
01171 }
01172
01173
01174 mlock l(m_mutex);
01175
01176
01177 if (m_rvn != ri->rvn)
01178 return false;
01179
01180 int N = m_dispatch->getNumManifolds();
01181 if (ri->iManifold >= N)
01182 return false;
01183
01184
01185 for (; ri->iManifold < N; ++ri->iManifold) {
01186 btPersistentManifold * m =
01187 m_dispatch->getManifoldByIndexInternal(ri->iManifold);
01188 if (!m || !m->getNumContacts())
01189 continue;
01190
01191 ObjectImpl * o0 = getPhysicsObject(m->getBody0());
01192 ObjectImpl * o1 = getPhysicsObject(m->getBody1());
01193
01194 if (o0->wantCollisionEvents() &&
01195 o1->wantCollisionEvents()) {
01196
01197 cr.obj1 = getPhysicsObjectById(o0->getId());
01198 cr.obj2 = getPhysicsObjectById(o1->getId());
01199 ASSERT(cr.obj1 && cr.obj2,
01200 "objects in collision not in global id map");
01201 return true;
01202 }
01203 }
01204
01205
01206 return false;
01207 }
01208
01209
01210
01211 smart_ptr<PhysicsObject>
01212 World::rayTest
01213 (
01214 IN const point3d_t& from,
01215 IN const point3d_t& to
01216 )
01217 {
01218 perf::Timer timer("PhysicsWorld::rayTest");
01219 ASSERT(m_world, "null");
01220
01221
01222 btVector3 btFrom = getBulletVectorFromPoint3d(from);
01223 btVector3 btTo = getBulletVectorFromPoint3d(to);
01224
01225
01226 btCollisionWorld::ClosestRayResultCallback rayCallback(btFrom, btTo);
01227
01228
01229 mlock m(m_mutex);
01230
01231
01232 m_world->rayTest(btFrom, btTo, rayCallback);
01233
01234
01235 if (!rayCallback.hasHit())
01236 return NULL;
01237
01238
01239 ASSERT(rayCallback.m_collisionObject, "null");
01240 ObjectImpl * poi = getPhysicsObject(rayCallback.m_collisionObject);
01241 ASSERT(poi, "null");
01242
01243
01244
01245
01246
01247 long id = poi->getId();
01248 ASSERT(id, "bad object ID");
01249 return getPhysicsObjectById(id);
01250 }
01251
01252
01253
01255
01256
01257
01259
01260 void
01261 World::removeObjectInternal
01262 (
01263 IN ObjectImpl * o
01264 )
01265 {
01266
01267
01268 ASSERT(o, "null");
01269
01270
01271 long id = o->getId();
01272 removeObjectFromIdMap(id);
01273 o->setId(0);
01274
01275
01276 btCollisionObject * bto = o->getCollisionObject();
01277 btRigidBody * btr = dynamic_cast<btRigidBody *>(bto);
01278 if (btr) {
01279 m_world->removeRigidBody(btr);
01280 } else {
01281 m_world->removeCollisionObject(bto);
01282 }
01283
01284 btKinematicCharacterController * btc = o->getCharacter();
01285 if (btc) {
01286 m_world->removeCharacter(btc);
01287 }
01288
01289
01290 id_object_map_t::iterator i = m_objects.find(id);
01291 if (m_objects.end() != i) {
01292 m_objects.erase(i);
01293 }
01294 }
01295
01296
01297
01298 void
01299 World::removeAll
01300 (
01301 void
01302 )
01303 {
01304
01305
01306 while (m_objects.size() > 0) {
01307 id_object_map_t::iterator i = m_objects.begin();
01308 smart_ptr<PhysicsObject> obj = i->second;
01309 ASSERT(obj, "null object in our id map");
01310
01311 ObjectImpl * o = getObject(obj);
01312 ASSERT(o, "null");
01313
01314 this->removeObjectInternal(o);
01315 }
01316 }
01317
01318
01319
01321
01322
01323
01325
01326 void
01327 getVectorFromEulerAngles
01328 (
01329 IN const point3d_t& euler,
01330 OUT point3d_t& vector
01331 )
01332 throw()
01333 {
01334 btQuaternion q;
01335 q.setEulerZYX(euler.z, euler.y, euler.x);
01336
01337 btTransform T(q);
01338
01339 btVector3 out = T * btVector3(0, 0, 1);
01340 vector.x = out.x();
01341 vector.y = out.y();
01342 vector.z = out.z();
01343 }
01344
01345
01346
01347 void
01348 getVectorFromQuaternion
01349 (
01350 IN const quaternion_t& q,
01351 OUT point3d_t& vector
01352 )
01353 throw()
01354 {
01355 btQuaternion Q(q.x, q.y, q.z, q.w);
01356 btTransform T(Q);
01357
01358 btVector3 out = T * btVector3(0, 0, 1);
01359 vector.x = out.x();
01360 vector.y = out.y();
01361 vector.z = out.z();
01362 }
01363
01364
01365
01366 void
01367 dumpWorld
01368 (
01369 IN PhysicsWorld * world
01370 )
01371 throw()
01372 {
01373 ASSERT(world, "null");
01374
01375 DPRINTF("Debug information for physics world:");
01376
01377 PhysicsWorld::object_iterator_t i;
01378 world->getObjectIterator(i);
01379 smart_ptr<PhysicsObject> obj;
01380 while (world->getNextObject(i, obj)) {
01381 ASSERT(obj, "null");
01382 obj->dump("");
01383 }
01384 }
01385
01386
01387
01388
01389 void
01390 getTransformFromPlacement
01391 (
01392 IN const placement_t& placement,
01393 OUT Transform3D& T
01394 )
01395 {
01396 T.setIdentity();
01397 T.setOrigin(getBulletVectorFromPoint3d(placement.position));
01398 btQuaternion q(placement.rotation.x, placement.rotation.y,
01399 placement.rotation.z, placement.rotation.w);
01400 T.setRotation(q);
01401 }
01402
01403
01404
01405 smart_ptr<PhysicsShape>
01406 createBoxShape
01407 (
01408 IN const point3d_t& dimensions
01409 )
01410 {
01411 ASSERT(dimensions.x >= 0.0, "Bad box x-dimension: %f", dimensions.x);
01412 ASSERT(dimensions.y >= 0.0, "Bad box y-dimension: %f", dimensions.y);
01413 ASSERT(dimensions.z >= 0.0, "Bad box z-dimension: %f", dimensions.z);
01414
01415 btVector3 halfExtent(0.5 * dimensions.x, 0.5 * dimensions.y,
01416 0.5 * dimensions.z);
01417 smart_ptr<btCollisionShape> shape = new btBoxShape(halfExtent);
01418 ASSERT(shape, "out of memory");
01419
01420 return ShapeImpl::create(shape);
01421 }
01422
01423
01424
01425 smart_ptr<PhysicsShape>
01426 createCubeShape
01427 (
01428 IN float edge_length
01429 )
01430 {
01431 ASSERT(edge_length > 0.0, "Bad cube edge length: %f", edge_length);
01432
01433 return createBoxShape(point3d_t(edge_length, edge_length, edge_length));
01434 }
01435
01436
01437
01438 smart_ptr<PhysicsShape>
01439 createTrimeshShape
01440 (
01441 IN const trimesh::Trimesh * trimesh
01442 )
01443 {
01444 ASSERT(trimesh, "null");
01445
01446 const int triangleStride = 3 * sizeof(int);
01447 const int vertexStride = sizeof(point3d_t);
01448
01449
01450
01451 ShapeImpl::private_context_t pctx;
01452 pctx.tiva = new btTriangleIndexVertexArray(
01453 trimesh->getTriangleCount(), (int *) trimesh->getTriangleArray(), triangleStride,
01454 trimesh->getVertexCount(), (btScalar *) trimesh->getVertexArray(), vertexStride);
01455 ASSERT(pctx.tiva, "out of memory");
01456
01457
01458 btBvhTriangleMeshShape * btmShape =
01459 new btBvhTriangleMeshShape(pctx.tiva, true);
01460 ASSERT(btmShape, "null");
01461
01462
01463 smart_ptr<btCollisionShape> shape = btmShape;
01464 return ShapeImpl::create(shape, &pctx);
01465 }
01466
01467
01468
01469 smart_ptr<PhysicsShape>
01470 createHeightfieldShape
01471 (
01472 IN const hfield::Heightfield * field
01473 )
01474 {
01475 ASSERT(field, "null");
01476
01477 DPRINTF("Constructing heightfield shape");
01478
01479
01480 long width = field->getWidth();
01481 long length = field->getLength();
01482 DPRINTF(" width = %ld", width);
01483 DPRINTF(" length= %ld", length);
01484 ASSERT(width > 1 && length > 1, "Bad width/length?");
01485
01486 long nSize = width * length;
01487 DPRINTF(" size = %ld", nSize);
01488
01489 float yScale = field->getYScale();
01490 DPRINTF(" yScale = %f", yScale);
01491 ASSERT(yScale > 0.0, "Bad y-scale: %f", yScale);
01492
01493 const short * raw = field->getHeightArray();
01494 ASSERT(raw, "null height array?");
01495
01496
01497 float minHeight = field->getMinHeight() * yScale;
01498 float maxHeight = field->getMaxHeight() * yScale;
01499 DPRINTF(" min = %f", minHeight);
01500 DPRINTF(" max = %f", maxHeight);
01501
01502 int upAxis = 1;
01503
01504 btHeightfieldTerrainShape * terrain =
01505 new btHeightfieldTerrainShape(width, length, (void *) raw, yScale,
01506 minHeight, maxHeight, upAxis,
01507 PHY_SHORT, false);
01508 ASSERT(terrain, "out of memory");
01509 DPRINTF("Successfully created a btHeightfieldTerrainShape!");
01510
01511
01512 float xzScale = field->getXZScale();
01513 ASSERT(xzScale > 0.0, "Bad xz scale: %f", xzScale);
01514 btVector3 localScaling(xzScale, 1.0, xzScale);
01515 terrain->setLocalScaling(localScaling);
01516
01517
01518 smart_ptr<btCollisionShape> shape = terrain;
01519 return ShapeImpl::create(shape);
01520 }
01521
01522
01523
01524 smart_ptr<PhysicsShape>
01525 createCapsuleShape
01526 (
01527 IN float height,
01528 IN float radius
01529 )
01530 {
01531 ASSERT(height > 0, "Bad capsule height: %f", height);
01532 ASSERT(radius > 0, "Bad capsule radius: %f", radius);
01533
01534 smart_ptr<btCapsuleShape> capsule = new btCapsuleShape(radius, height);
01535 ASSERT(capsule, "out of memory");
01536
01537 smart_ptr<btCollisionShape> shape = capsule;
01538 return ShapeImpl::create(shape);
01539 }
01540
01541
01542
01543 smart_ptr<PhysicsObject>
01544 createMoveableObject
01545 (
01546 IN const physics_meta_t& meta,
01547 IN const placement_t& placement
01548 )
01549 {
01550 ASSERT(meta.shape, "null");
01551 ASSERT(meta.mass >= 0.0, "Bad mass: %f", meta.mass);
01552
01553
01554 Transform3D T;
01555 getTransformFromPlacement(placement, T);
01556
01557
01558 ShapeImpl * si = getShape(meta.shape);
01559 ASSERT(si, "null");
01560 btCollisionShape * shape1 = si->getShape();
01561 ASSERT(shape1, "null");
01562 btConvexShape * shape = dynamic_cast<btConvexShape *>(shape1);
01563 ASSERT(shape, "moveable objects must use convex shapes!");
01564
01565
01566 smart_ptr<btPairCachingGhostObject> ghost =
01567 new btPairCachingGhostObject;
01568 ASSERT(ghost, "out of memory");
01569 ghost->setWorldTransform(T);
01570 ghost->setCollisionShape(shape);
01571 ghost->setCollisionFlags(s_ghostCollisionFlags);
01572
01573
01574 smart_ptr<btKinematicCharacterController> character =
01575 new btKinematicCharacterController(ghost, shape, s_stepHeight);
01576 ASSERT(character, "out of memory");
01577
01578
01579 return ObjectImpl::create(character, ghost, meta);
01580 }
01581
01582
01583
01584 smart_ptr<PhysicsObject>
01585 createObject
01586 (
01587 IN const physics_meta_t& meta,
01588 IN const placement_t& placement
01589 )
01590 {
01591 ASSERT(meta.shape, "null");
01592 ASSERT(meta.mass >= 0.0, "Bad mass: %f", meta.mass);
01593
01594
01595 if (PhysicsObject::eFlag_Moveable & meta.objectFlags) {
01596 return createMoveableObject(meta, placement);
01597 }
01598
01599
01600 Transform3D T;
01601 getTransformFromPlacement(placement, T);
01602
01603 bool isDynamic = (meta.mass > 0.0);
01604
01605
01606 ShapeImpl * si = getShape(meta.shape);
01607 ASSERT(si, "null");
01608 btCollisionShape * shape = si->getShape();
01609 ASSERT(shape, "null");
01610
01611 smart_ptr<btCollisionObject> obj;
01612 if (PhysicsObject::eFlag_Ghost & meta.objectFlags) {
01613 if (meta.mass > 0.0) {
01614 WAVE_EX(wex);
01615 wex << "Only immobile ghost objects allowed for now.";
01616 }
01617
01618
01619 smart_ptr<btPairCachingGhostObject> ghost =
01620 new btPairCachingGhostObject;
01621 ASSERT(ghost, "out of memory");
01622 ghost->setWorldTransform(T);
01623 ghost->setCollisionShape(shape);
01624
01625 int flags = s_ghostCollisionFlags;
01626 if (PhysicsObject::eFlag_NoCollisions & meta.objectFlags) {
01627
01628 DPRINTF("Updating flags!");
01629 flags = btCollisionObject::CF_NO_CONTACT_RESPONSE;
01630 }
01631 ghost->setCollisionFlags(flags);
01632 obj = ghost;
01633 } else {
01634
01635
01636
01637 btVector3 localInertia(0, 0, 0);
01638 if (isDynamic) {
01639 shape->calculateLocalInertia(meta.mass, localInertia);
01640 }
01641
01642
01643 btDefaultMotionState * motionState = new btDefaultMotionState(T);
01644 ASSERT(motionState, "null");
01645 motionState->setWorldTransform(T);
01646
01647
01648 btRigidBody::btRigidBodyConstructionInfo
01649 info(meta.mass, motionState, shape, localInertia);
01650 smart_ptr<btRigidBody> body = new btRigidBody(info);
01651 ASSERT(body, "out of memory");
01652 obj = body;
01653 }
01654
01655
01656 ASSERT(obj, "should have collision object now");
01657 return ObjectImpl::create(obj, meta);
01658 }
01659
01660
01661
01662 smart_ptr<PhysicsWorld>
01663 PhysicsWorld::create
01664 (
01665 IN const Datahash * params
01666 )
01667 {
01668 ASSERT(params, "null");
01669
01670 smart_ptr<World> local = new World;
01671 ASSERT(local, "out of memory");
01672
01673 local->initialize(params);
01674
01675 return local;
01676 }
01677
01678
01679
01680 smart_ptr<PhysicsObject>
01681 getObjectHitFromPlacement
01682 (
01683 IN PhysicsWorld * world,
01684 IN const placement_t& placement,
01685 IN float half_extent
01686 )
01687 {
01688 ASSERT(world, "null");
01689 ASSERT(half_extent > 0, "Bad half extent: %f", half_extent);
01690
01691 const float max_range = 256.0;
01692
01693
01694 point3d_t dir;
01695 getVectorFromQuaternion(placement.rotation, dir);
01696
01697 point3d_t from = placement.position + half_extent * dir;
01698 point3d_t to = from + max_range * dir;
01699
01700 return world->rayTest(from, to);
01701 }
01702
01703
01704
01705 };
01706