btWorld.cpp

Engine/source/T3D/physics/bullet/btWorld.cpp

More...

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