//////////////////////////////////////////////////////////////////////////////// // Scorched3D (c) 2000-2003 // // This file is part of Scorched3D. // // Scorched3D is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Scorched3D is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Scorched3D; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// #include #include #include PhysicsEngine::PhysicsEngine() : world_(0), targetSpace_(0), groundSpace_(0), particleSpace_(0), tankSpace_(0), contactgroup_(0), handler_(0), contactGeomsCount_(0), contactInfosCount_(0), maxContacts_(1000) { contactGeoms_ = new dContactGeom[maxContacts_]; contactInfos_ = new dContactInfo[maxContacts_]; create(); } PhysicsEngine::~PhysicsEngine() { destroy(); delete [] contactGeoms_; delete [] contactInfos_; } void PhysicsEngine::setCollisionHandler(PhysicsEngineCollision *handler) { handler_ = handler; } bool PhysicsEngine::create() { // Create the simultion world world_ = dWorldCreate(); dVector3 staticCenter; staticCenter[0] = 128.0; staticCenter[1] = 128.0; staticCenter[2] = 0.0; staticCenter[3] = 0.0; dVector3 staticSize; staticSize[0] = 256.0; staticSize[1] = 256.0; staticSize[2] = 128.0; staticSize[3] = 0.0; typedef dReal dVector3[4]; targetSpace_ = dHashSpaceCreate(0);/*dQuadTreeSpaceCreate( 0, // Existing space id staticCenter, // Center staticSize, // size, 5); // Depth*/ tankSpace_ = dHashSpaceCreate(0); particleSpace_ = dHashSpaceCreate(0); groundSpace_ = dHashSpaceCreate(0); contactgroup_ = dJointGroupCreate(0); // Setup the world's settings dWorldSetGravity(world_,0,0,-10.0); // Set a default gravity (this will change) dWorldSetCFM(world_,1e-5); return true; } void PhysicsEngine::setGravity(Vector &gravity) { dWorldSetGravity(world_, gravity[0], gravity[1], gravity[2]); } void PhysicsEngine::setWind(Vector &wind) { wind_ = wind; } void PhysicsEngine::destroy() { dJointGroupDestroy(contactgroup_); dSpaceDestroy(targetSpace_); dSpaceDestroy(tankSpace_); dSpaceDestroy(particleSpace_); dSpaceDestroy(groundSpace_); dWorldDestroy(world_); } void PhysicsEngine::nearCallback(void *data, dGeomID o1, dGeomID o2) { PhysicsEngine *engine = (PhysicsEngine *) data; engine->realNearCallback(data, o1, o2); } void PhysicsEngine::realNearCallback(void *data, dGeomID o1, dGeomID o2) { if (dGeomIsEnabled(o1) == 0 || dGeomIsEnabled(o1) == 0) { // Check that both bodies should actualy count in the collision // Does it seem odd that I have to do this, I think so! return; } if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) { // colliding a space with something dSpaceCollide2(o1,o2,data,&nearCallback); // collide all geoms internal to the space(s) if (dGeomIsSpace(o1)) { dSpaceID s1 = (dSpaceID) o1; dSpaceCollide(s1,data,&nearCallback); } if (dGeomIsSpace(o2)) { dSpaceID s2 = (dSpaceID) o2; dSpaceCollide(s2,data,&nearCallback); } } else { // exit without doing anything if the two bodies are connected by a joint dBodyID b1 = dGeomGetBody(o1); dBodyID b2 = dGeomGetBody(o2); if (b1 && b2 && dAreConnectedExcluding(b1,b2,dJointTypeContact)) return; // Find how many contacts we are allowed from the pool unsigned int contactsLeft = maxContacts_ - contactGeomsCount_; if (contactsLeft > 0) { // We have contacts left in the pool // Get the contacts for these two objects int numc = dCollide(o1, o2, contactsLeft, &contactGeoms_[contactGeomsCount_], sizeof(dContactGeom)); if (numc > 0) { // We have a collision // Record this collision for processing dContactInfo &info = contactInfos_[contactInfosCount_]; info.o1 = o1; info.o2 = o2; info.contacts = &contactGeoms_[contactGeomsCount_]; info.contactCount = numc; contactInfosCount_ += 1; // We've used one collision contactGeomsCount_ += numc; // We've used numc contacts } } } } void PhysicsEngine::stepSimulation(float stepSize) { // Find collisions contactGeomsCount_ = 0; contactInfosCount_ = 0; dSpaceCollide2((dGeomID) targetSpace_, (dGeomID) particleSpace_, this, &nearCallback); dSpaceCollide2((dGeomID) groundSpace_, (dGeomID) particleSpace_, this, &nearCallback); dSpaceCollide2((dGeomID) tankSpace_, (dGeomID) particleSpace_, this, &nearCallback); dSpaceCollide2((dGeomID) targetSpace_, (dGeomID) tankSpace_, this, &nearCallback); // Perform collision actions if (handler_ && contactInfosCount_ > 0) { for (unsigned int c=0; ccollision(info.o1, info.o2, info.contacts, info.contactCount); } } // Simulate the world dWorldQuickStep(world_, stepSize); // remove all contact joints dJointGroupEmpty(contactgroup_); // Destroy all dead geoms while (!deadGeoms_.empty()) { dGeomID geom = deadGeoms_.back(); deadGeoms_.pop_back(); dGeomDestroy(geom); } } void PhysicsEngine::destroyGeom(dGeomID geom) { dGeomDisable(geom); deadGeoms_.push_back(geom); }