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 #include <PVLE/Physics/World.h>
00028 #include <PVLE/Physics/Space.h>
00029 #include <PVLE/Physics/Geom.h>
00030 #include <PVLE/Physics/Joint.h>
00031 #include <PVLE/Physics/Body.h>
00032 #include <PVLE/Physics/Contact.h>
00033 #include <boost/cast.hpp>
00034 #include <vector>
00035 #include <osg/Notify>
00036 #include <PVLE/Util/Util.h>
00037
00038 #include <PVLE/Entity/GeomCollisionContainer.h>
00039
00040 #define DISABLE_NULL_NORMAL_CONTACT_CHECK
00041 //#define DISABLE_CLOSE_CONTACT_CHECK
00042
00043
00044 namespace Physics {
00045
00046 void NearCallback::toPVLENearCallback(void * data, dGeomID o1, dGeomID o2) {
00047 World *This = static_cast<World *>(data);
00048 ASSERT(This);
00049
00050 NearCallback defaultCB;
00051 NearCallback * pBB = This->getNearCallback();
00052 if (!pBB) pBB = &defaultCB;
00053
00054 pBB->operator()(This, static_cast<AbstractGeom *>(dGeomGetData(o1)), static_cast<AbstractGeom *>(dGeomGetData(o2)));
00055 }
00056
00057 void NearCallback::operator()(World * pWorld, AbstractGeom * pG1, AbstractGeom * pG2) {
00058 DBG_TRY_BLOCK_START
00059
00060 if (pG1->isSpace() || pG2->isSpace()) {
00061
00062
00063 pWorld->spaceCollide2(pG1, pG2);
00064
00065 if (pG1->isSpace()) pWorld->spaceCollide(boost::polymorphic_downcast<Space*>(pG1));
00066 if (pG2->isSpace()) pWorld->spaceCollide(boost::polymorphic_downcast<Space*>(pG2));
00067 } else {
00068
00069 Geom * pCastG1 = boost::polymorphic_downcast<Geom*>(pG1);
00070 Geom * pCastG2 = boost::polymorphic_downcast<Geom*>(pG2);
00071 Body * pB1 = pCastG1->getBody();
00072 Body * pB2 = pCastG2->getBody();
00073
00074
00075 if (pB1 && pB2 &&
00076 !(pB1->getAllowCollideWithJointBodies() || pB1->getAllowCollideWithJointBodies()) &&
00077 pB1->isConnectedToExcludingContact(pB2)) return;
00078
00079
00080 const UINT nbMaxContacts = 10;
00081 static Contact pContacts[nbMaxContacts];
00082 UINT nbContacts = pCastG1->collide(pCastG2, nbMaxContacts, pContacts);
00083 UINT numContact;
00084
00085 if (nbContacts) {
00086 #ifdef _DEBUG
00087 static bool writtenOnce = false;
00088 if (!writtenOnce && nbContacts == nbMaxContacts) { osg::notify(osg::DEBUG_INFO) << "Max contacts reached (Message displayed only once)" << std::endl; writtenOnce = true; }
00089 #endif
00090
00091
00092 std::vector<osg::Vec3>::const_iterator it, itEnd;
00093 osg::Vec3 tempPos;
00094 bool deleted = false;
00095 for(numContact=0; numContact<nbContacts; ) {
00096 deleted = false;
00097
00098 #ifndef DISABLE_NULL_NORMAL_CONTACT_CHECK
00099
00100 if (!osg::equivalent(pContacts[numContact].getNormal().length2(), 1)) {
00101 pContacts[numContact] = pContacts[nbContacts-1];
00102 --nbContacts;
00103 deleted = true;
00104 continue;
00105 }
00106 #endif
00107 #ifndef DISABLE_CLOSE_CONTACT_CHECK
00108
00109 tempPos = pContacts[numContact].getPos();
00110
00111 for(UINT i=numContact+1; i<nbContacts; ++i) {
00112
00113 if ((pContacts[i].getPos() - tempPos).length() < 1e-5f) { pContacts[i] = pContacts[nbContacts-1]; --nbContacts; }
00114 }
00115
00116
00117 if (pB1) {
00118 BOOST_FOREACH(osg::Vec3 & pos, pB1->contactPos) {
00119 if ((pos - tempPos).length() < 1e-5f) {
00120 if (pContacts[numContact].geom.g2 == NULL) { pContacts[numContact] = pContacts[nbContacts-1]; --nbContacts; deleted = true; break; }
00121 else pContacts[numContact].geom.g1 = NULL;
00122 }
00123 }
00124 if (deleted) continue;
00125 }
00126 if (pB2) {
00127 BOOST_FOREACH(osg::Vec3 & pos, pB2->contactPos) {
00128 if ((pos - tempPos).length() < 1e-5f) {
00129 if (pContacts[numContact].geom.g1 == NULL) { pContacts[numContact] = pContacts[nbContacts-1]; --nbContacts; deleted = true; break; }
00130 else pContacts[numContact].geom.g2 = NULL;
00131 }
00132 }
00133
00134 }
00135 #endif
00136 if (!deleted) ++numContact;
00137 }
00138 }
00139 if (nbContacts) {
00140
00141 Contact* itContact = pContacts, * itEndContact = pContacts+nbContacts;
00142
00143
00144 float maxEnergy = 0;
00145 Contact * pMaxEnergyContact = pContacts;
00146
00147 for(numContact=0; itContact!=itEndContact; ++itContact, ++numContact) {
00148
00149
00150 osg::Vec3 relVel;
00151 itContact->arithmeticCineticEnergy = 0;
00152 if (pB1) {
00153 relVel = dBodyGetPointVelV(*pB1, itContact->getPos());
00154 itContact->arithmeticCineticEnergy = pB1->getMassData().getMass();
00155 }
00156 if (pB2) {
00157 relVel -= dBodyGetPointVelV(*pB2, itContact->getPos());
00158 itContact->arithmeticCineticEnergy += pB2->getMassData().getMass();
00159 }
00160 double relNormalVelocity = relVel * itContact->getNormal();
00161 itContact->arithmeticCineticEnergy *= 0.5 * (relNormalVelocity*relNormalVelocity);
00162 if (relNormalVelocity>0) itContact->arithmeticCineticEnergy *= -1;
00163
00164 if (itContact->arithmeticCineticEnergy > maxEnergy) { pMaxEnergyContact = itContact; maxEnergy = pMaxEnergyContact->arithmeticCineticEnergy; }
00165 }
00166
00167
00168 bool createContact = pB1 || pB2;
00169 Body * pHitB1 = pB1;
00170 Body * pHitB2 = pB2;
00171 if (pCastG1->getCollisionContainer()) createContact &= pCastG1->getCollisionContainer()->hitBefore(pContacts, numContact, pMaxEnergyContact, *pCastG1, *pCastG2, pHitB1, pHitB2);
00172 if (pCastG2->getCollisionContainer()) createContact &= pCastG2->getCollisionContainer()->hitBefore(pContacts, numContact, pMaxEnergyContact, *pCastG2, *pCastG1, pHitB2, pHitB1);
00173
00174
00175 if (createContact && pCastG1->getHandler()->type() != Physics::GeometryHandler::RAY) {
00176 for(itContact=pContacts; itContact!=itEndContact; ++itContact) {
00177
00178
00180 itContact->getSurfaceParams().mergeInto(pCastG1->surface, pCastG2->surface);
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190 Joint * pJ = pWorld->getContactGroup()->createAndAdd(pWorld, itContact);
00191
00192 pJ->attach(pHitB1, pHitB2);
00193
00194
00195 if (pHitB1) pHitB1->contactPos.push_back(itContact->getPos());
00196 if (pHitB2) pHitB2->contactPos.push_back(itContact->getPos());
00197 }
00198 }
00199
00200
00201
00202 if (pCastG1->getCollisionContainer()) pCastG1->getCollisionContainer()->hitAfter(pContacts, numContact, pMaxEnergyContact, *pCastG1, *pCastG2, createContact);
00203 if (pCastG2->getCollisionContainer()) pCastG2->getCollisionContainer()->hitAfter(pContacts, numContact, pMaxEnergyContact, *pCastG2, *pCastG1, createContact);
00204 }
00205 }
00206
00207 DBG_TRY_BLOCK_END
00208 }
00209
00210
00211
00212
00213 World::World() : id(0), bUseQuick(true), pNcb(NULL) {
00214 pSpace = new Space();
00215 init();
00216 }
00217
00218 World::World(const osg::Vec3 & worldBox) : id(0), bUseQuick(true), pNcb(NULL) {
00219 pSpace = new Space(worldBox);
00220 init();
00221 }
00222
00223 World::World(Space * space) : id(0), bUseQuick(true), pNcb(NULL) {
00224 pSpace = space ? space : new Space();
00225 init();
00226 }
00227
00228 void World::init() {
00229 #if defined(_DEBUG) && defined(PVLE_UNIT_TESTS)
00230 Util::UnitTestSingleton::testPart2();
00231 #endif
00232 pContactGroup = new JointGroup();
00233
00234 id = dWorldCreate();
00235
00236
00237 dWorldSetContactSurfaceLayer(id, 0.001f);
00238 dWorldSetAutoDisableFlag(id, 1);
00239
00240 dWorldSetGravity(id, osg::Vec3(0,0,-9.81f));
00241
00242 ODEHolder::instance();
00243 }
00244
00245 World::~World() {
00246 if (pContactGroup.valid()) pContactGroup->clear();
00247 pContactGroup = NULL;
00248 pSpace = NULL;
00249 if (id) dWorldDestroy(id);
00250 }
00251
00252 void World::step(dReal stepSize) {
00253 DBG_TRY_BLOCK_START
00254
00255
00256 pContactGroup->clear();
00257
00258
00260
00261
00262
00263
00264 resetSpaceCollideFlag(pSpace.get());
00265 spaceCollide(pSpace.get());
00266
00267
00268 if (bUseQuick) dWorldQuickStep(id, stepSize);
00269 else dWorldStep(id, stepSize);
00270
00271 DBG_TRY_BLOCK_END
00272 }
00273
00274 void World::spaceCollide2(AbstractGeom * pAGeom1, AbstractGeom * pAGeom2) {
00275 DBG_TRY_BLOCK_START
00276 dSpaceCollide2(pAGeom1->id, pAGeom2->id, this, &NearCallback::toPVLENearCallback);
00277 DBG_TRY_BLOCK_END
00278 }
00279
00280 void World::spaceCollide(Space * pCurSpace) {
00281 DBG_TRY_BLOCK_START
00282
00283
00284 if (pCurSpace->collided) return;
00285 dSpaceCollide(pCurSpace->asSpaceId(), this, &NearCallback::toPVLENearCallback);
00286 pCurSpace->collided = true;
00287
00288
00289 Space * pCurSpaceSub;
00290 BOOST_FOREACH(AbstractGeom * pAGeom, pCurSpace->vGeoms) {
00291 pCurSpaceSub = pAGeom->asSpace();
00292 if (pCurSpaceSub && !pCurSpaceSub->collided) spaceCollide(pCurSpaceSub);
00293 }
00294
00295 DBG_TRY_BLOCK_END
00296 }
00297
00298 void World::resetSpaceCollideFlag(Space * pCurSpace) {
00299 DBG_TRY_BLOCK_START
00300
00301
00302 pCurSpace->collided = false;
00303
00304
00305 Space * pCurSpaceSub;
00306 BOOST_FOREACH(AbstractGeom * pAGeom, pCurSpace->vGeoms) {
00307 pCurSpaceSub = pAGeom->asSpace();
00308 if (pCurSpaceSub) resetSpaceCollideFlag(pCurSpaceSub);
00309 }
00310
00311 DBG_TRY_BLOCK_END
00312 }
00313
00314 }