btWorld.cpp
Engine/source/T3D/physics/bullet/btWorld.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "T3D/physics/bullet/btWorld.h" 26 27#include "T3D/physics/bullet/btPlugin.h" 28#include "T3D/physics/bullet/btCasts.h" 29#include "T3D/physics/physicsUserData.h" 30#include "core/stream/bitStream.h" 31#include "platform/profiler.h" 32#include "sim/netConnection.h" 33#include "console/console.h" 34#include "console/consoleTypes.h" 35#include "scene/sceneRenderState.h" 36#include "T3D/gameBase/gameProcess.h" 37 38BtWorld::BtWorld() : 39 mProcessList( NULL ), 40 mIsSimulating( false ), 41 mErrorReport( false ), 42 mTickCount( 0 ), 43 mIsEnabled( false ), 44 mEditorTimeScale( 1.0f ), 45 mDynamicsWorld( NULL ) 46{ 47} 48 49BtWorld::~BtWorld() 50{ 51} 52 53bool BtWorld::initWorld( bool isServer, ProcessList *processList ) 54{ 55 // Collision configuration contains default setup for memory, collision setup. 56 mCollisionConfiguration = new btDefaultCollisionConfiguration(); 57 mDispatcher = new btCollisionDispatcher( mCollisionConfiguration ); 58 59 btVector3 worldMin( -2000, -2000, -1000 ); 60 btVector3 worldMax( 2000, 2000, 1000 ); 61 btAxisSweep3 *sweepBP = new btAxisSweep3( worldMin, worldMax ); 62 mBroadphase = sweepBP; 63 sweepBP->getOverlappingPairCache()->setInternalGhostPairCallback( new btGhostPairCallback() ); 64 65 // The default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded). 66 mSolver = new btSequentialImpulseConstraintSolver; 67 68 mDynamicsWorld = new btDiscreteDynamicsWorld( mDispatcher, mBroadphase, mSolver, mCollisionConfiguration ); 69 if ( !mDynamicsWorld ) 70 { 71 Con::errorf( "BtWorld - %s failed to create dynamics world!", isServer ? "Server" : "Client" ); 72 return false; 73 } 74 75 // Removing the randomization in the solver is required 76 // to make the simulation deterministic. 77 mDynamicsWorld->getSolverInfo().m_solverMode &= ~SOLVER_RANDMIZE_ORDER; 78 79 mDynamicsWorld->setGravity( btCast<btVector3>( mGravity ) ); 80 81 AssertFatal( processList, "BtWorld::init() - We need a process list to create the world!" ); 82 mProcessList = processList; 83 mProcessList->preTickSignal().notify( this, &BtWorld::getPhysicsResults ); 84 mProcessList->postTickSignal().notify( this, &BtWorld::tickPhysics, 1000.0f ); 85 86 return true; 87} 88 89void BtWorld::_destroy() 90{ 91 // Release the tick processing signals. 92 if ( mProcessList ) 93 { 94 mProcessList->preTickSignal().remove( this, &BtWorld::getPhysicsResults ); 95 mProcessList->postTickSignal().remove( this, &BtWorld::tickPhysics ); 96 mProcessList = NULL; 97 } 98 99 // TODO: Release any remaining 100 // orphaned rigid bodies here. 101 102 SAFE_DELETE( mDynamicsWorld ); 103 SAFE_DELETE( mSolver ); 104 SAFE_DELETE( mBroadphase ); 105 SAFE_DELETE( mDispatcher ); 106 SAFE_DELETE( mCollisionConfiguration ); 107} 108 109void BtWorld::tickPhysics( U32 elapsedMs ) 110{ 111 if ( !mDynamicsWorld || !mIsEnabled ) 112 return; 113 114 // Did we forget to call getPhysicsResults somewhere? 115 AssertFatal( !mIsSimulating, "BtWorld::tickPhysics() - Already simulating!" ); 116 117 // The elapsed time should be non-zero and 118 // a multiple of TickMs! 119 AssertFatal( elapsedMs != 0 && 120 ( elapsedMs % TickMs ) == 0 , "BtWorld::tickPhysics() - Got bad elapsed time!" ); 121 122 PROFILE_SCOPE(BtWorld_TickPhysics); 123 124 // Convert it to seconds. 125 const F32 elapsedSec = (F32)elapsedMs * 0.001f; 126 127 // Simulate 128 mDynamicsWorld->stepSimulation( elapsedSec * mEditorTimeScale, smPhysicsMaxSubSteps, smPhysicsStepTime); 129 130 mIsSimulating = true; 131 132 //Con::printf( "%s BtWorld::tickPhysics!", this == smClientWorld ? "Client" : "Server" ); 133} 134 135void BtWorld::getPhysicsResults() 136{ 137 if ( !mDynamicsWorld || !mIsSimulating ) 138 return; 139 140 PROFILE_SCOPE(BtWorld_GetPhysicsResults); 141 142 // Get results from scene. 143 // mScene->fetchResults( NX_RIGID_BODY_FINISHED, true ); 144 mIsSimulating = false; 145 mTickCount++; 146} 147 148void BtWorld::setEnabled( bool enabled ) 149{ 150 mIsEnabled = enabled; 151 152 if ( !mIsEnabled ) 153 getPhysicsResults(); 154} 155 156void BtWorld::destroyWorld() 157{ 158 _destroy(); 159} 160 161bool BtWorld::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ) 162{ 163 btCollisionWorld::ClosestRayResultCallback result( btCast<btVector3>( startPnt ), btCast<btVector3>( endPnt ) ); 164 mDynamicsWorld->rayTest( btCast<btVector3>( startPnt ), btCast<btVector3>( endPnt ), result ); 165 166 if ( !result.hasHit() || !result.m_collisionObject ) 167 return false; 168 169 if ( ri ) 170 { 171 ri->object = PhysicsUserData::getObject( result.m_collisionObject->getUserPointer() ); 172 173 // If we were passed a RayInfo, we can only return true signifying a collision 174 // if we hit an object that actually has a torque object associated with it. 175 // 176 // In some ways this could be considered an error, either a physx object 177 // has raycast-collision enabled that shouldn't or someone did not set 178 // an object in this actor's userData. 179 // 180 if ( ri->object == NULL ) 181 return false; 182 183 ri->distance = ( endPnt - startPnt ).len() * result.m_closestHitFraction; 184 ri->normal = btCast<Point3F>( result.m_hitNormalWorld ); 185 ri->point = btCast<Point3F>( result.m_hitPointWorld ); 186 ri->t = result.m_closestHitFraction; 187 } 188 189 /* 190 if ( impulse.isZero() || 191 !actor.isDynamic() || 192 actor.readBodyFlag( NX_BF_KINEMATIC ) ) 193 return true; 194 195 NxVec3 force = pxCast<NxVec3>( impulse );//worldRay.dir * forceAmt; 196 actor.addForceAtPos( force, hitInfo.worldImpact, NX_IMPULSE ); 197 */ 198 199 return true; 200} 201 202PhysicsBody* BtWorld::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes ) 203{ 204 btVector3 startPt = btCast<btVector3>( start ); 205 btVector3 endPt = btCast<btVector3>( end ); 206 207 btCollisionWorld::ClosestRayResultCallback result( startPt, endPt ); 208 mDynamicsWorld->rayTest( startPt, endPt, result ); 209 210 if ( !result.hasHit() || !result.m_collisionObject ) 211 return NULL; 212 213 PhysicsUserData *userData = PhysicsUserData::cast( result.m_collisionObject->getUserPointer() ); 214 if ( !userData ) 215 return NULL; 216 217 return userData->getBody(); 218} 219 220void BtWorld::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ) 221{ 222 /* 223 // Find Actors at the position within the radius 224 // and apply force to them. 225 226 NxVec3 nxPos = pxCast<NxVec3>( pos ); 227 NxShape **shapes = (NxShape**)NxAlloca(10*sizeof(NxShape*)); 228 NxSphere worldSphere( nxPos, radius ); 229 230 NxU32 numHits = mScene->overlapSphereShapes( worldSphere, NX_ALL_SHAPES, 10, shapes, NULL ); 231 232 for ( NxU32 i = 0; i < numHits; i++ ) 233 { 234 NxActor &actor = shapes[i]->getActor(); 235 236 bool dynamic = actor.isDynamic(); 237 238 if ( !dynamic ) 239 continue; 240 241 bool kinematic = actor.readBodyFlag( NX_BF_KINEMATIC ); 242 243 if ( kinematic ) 244 continue; 245 246 NxVec3 force = actor.getGlobalPosition() - nxPos; 247 force.normalize(); 248 force *= forceMagnitude; 249 250 actor.addForceAtPos( force, nxPos, NX_IMPULSE, true ); 251 } 252 */ 253} 254 255void BtWorld::onDebugDraw( const SceneRenderState *state ) 256{ 257 mDebugDraw.setCuller( &state->getCullingFrustum() ); 258 259 mDynamicsWorld->setDebugDrawer( &mDebugDraw ); 260 mDynamicsWorld->debugDrawWorld(); 261 mDynamicsWorld->setDebugDrawer( NULL ); 262 263 mDebugDraw.flush(); 264} 265 266void BtWorld::reset() 267{ 268 if ( !mDynamicsWorld ) 269 return; 270 271 ///create a copy of the array, not a reference! 272 btCollisionObjectArray copyArray = mDynamicsWorld->getCollisionObjectArray(); 273 274 S32 numObjects = mDynamicsWorld->getNumCollisionObjects(); 275 for ( S32 i=0; i < numObjects; i++ ) 276 { 277 btCollisionObject* colObj = copyArray[i]; 278 btRigidBody* body = btRigidBody::upcast(colObj); 279 280 if (body) 281 { 282 if (body->getMotionState()) 283 { 284 //btDefaultMotionState* myMotionState = (btDefaultMotionState*)body->getMotionState(); 285 //myMotionState->m_graphicsWorldTrans = myMotionState->m_startWorldTrans; 286 //body->setCenterOfMassTransform( myMotionState->m_graphicsWorldTrans ); 287 //colObj->setInterpolationWorldTransform( myMotionState->m_startWorldTrans ); 288 colObj->forceActivationState(ACTIVE_TAG); 289 colObj->activate(); 290 colObj->setDeactivationTime(0); 291 //colObj->setActivationState(WANTS_DEACTIVATION); 292 } 293 294 //removed cached contact points (this is not necessary if all objects have been removed from the dynamics world) 295 //m_dynamicsWorld->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(colObj->getBroadphaseHandle(),getDynamicsWorld()->getDispatcher()); 296 297 btRigidBody* body = btRigidBody::upcast(colObj); 298 if (body && !body->isStaticObject()) 299 { 300 btRigidBody::upcast(colObj)->setLinearVelocity(btVector3(0,0,0)); 301 btRigidBody::upcast(colObj)->setAngularVelocity(btVector3(0,0,0)); 302 } 303 } 304 305 } 306 307 // reset some internal cached data in the broadphase 308 mDynamicsWorld->getBroadphase()->resetPool( mDynamicsWorld->getDispatcher() ); 309 mDynamicsWorld->getConstraintSolver()->reset(); 310} 311 312/* 313ConsoleFunction( castForceRay, const char*, 4, 4, "( Point3F startPnt, Point3F endPnt, VectorF impulseVec )" ) 314{ 315 PhysicsWorld *world = PHYSICSPLUGIN->getWorld( "server" ); 316 if ( !world ) 317 return NULL; 318 319 char *returnBuffer = Con::getReturnBuffer(256); 320 321 Point3F impulse; 322 Point3F startPnt, endPnt; 323 dSscanf( argv[1], "%f %f %f", &startPnt.x, &startPnt.y, &startPnt.z ); 324 dSscanf( argv[2], "%f %f %f", &endPnt.x, &endPnt.y, &endPnt.z ); 325 dSscanf( argv[3], "%f %f %f", &impulse.x, &impulse.y, &impulse.z ); 326 327 Point3F hitPoint; 328 329 RayInfo rinfo; 330 331 bool hit = world->castRay( startPnt, endPnt, &rinfo, impulse ); 332 333 DebugDrawer *ddraw = DebugDrawer::get(); 334 if ( ddraw ) 335 { 336 ddraw->drawLine( startPnt, endPnt, hit ? ColorF::RED : ColorF::GREEN ); 337 ddraw->setLastTTL( 3000 ); 338 } 339 340 if ( hit ) 341 { 342 dSprintf(returnBuffer, 256, "%g %g %g", 343 rinfo.point.x, rinfo.point.y, rinfo.point.z ); 344 return returnBuffer; 345 } 346 else 347 return NULL; 348} 349*/ 350