px3World.cpp

Engine/source/T3D/physics/physx3/px3World.cpp

More...

Public Variables

physx::PxPhysics *

Public Functions

getDebugColor(physx::PxU32 packed)

Detailed Description

Public Variables

physx::PxPhysics * gPhysics3SDK 

Public Functions

getDebugColor(physx::PxU32 packed)

  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/physx3/px3World.h"
 26
 27#include "T3D/physics/physx3/px3.h"
 28#include "T3D/physics/physx3/px3Plugin.h"
 29#include "T3D/physics/physx3/px3Casts.h"
 30#include "T3D/physics/physx3/px3Stream.h"
 31#include "T3D/physics/physicsUserData.h"
 32
 33#include "console/engineAPI.h"
 34#include "core/stream/bitStream.h"
 35#include "platform/profiler.h"
 36#include "sim/netConnection.h"
 37#include "console/console.h"
 38#include "console/consoleTypes.h"
 39#include "core/util/safeDelete.h"
 40#include "collision/collision.h"
 41#include "T3D/gameBase/gameProcess.h"
 42#include "gfx/sim/debugDraw.h"
 43#include "gfx/primBuilder.h"
 44
 45physx::PxPhysics* gPhysics3SDK = NULL;
 46physx::PxCooking* Px3World::smCooking = NULL;
 47physx::PxFoundation* Px3World::smFoundation = NULL;
 48physx::PxDefaultCpuDispatcher* Px3World::smCpuDispatcher = NULL;
 49#ifndef TORQUE_OS_MAC
 50physx::PxCudaContextManager* Px3World::smCudaContextManager = NULL;
 51#endif
 52Px3ConsoleStream* Px3World::smErrorCallback = NULL;
 53physx::PxPvd* Px3World::smPvdConnection = NULL;
 54physx::PxPvdTransport* Px3World::smPvdTransport = NULL;
 55physx::PxDefaultAllocator Px3World::smMemoryAlloc;
 56
 57Px3World::Px3World() :
 58   mScene( NULL ),
 59   mIsEnabled( false ),
 60   mIsSimulating( false ),
 61   mIsServer( false ),
 62   mIsSceneLocked( false ),
 63   mTickCount( 0 ),
 64   mProcessList( NULL ),
 65   mEditorTimeScale( 1.0f ),
 66   mErrorReport( false ),
 67   mControllerManager(NULL),
 68   mRenderBuffer(NULL),
 69   mAccumulator( 0 )
 70{
 71}
 72
 73Px3World::~Px3World()
 74{
 75}
 76
 77physx::PxCooking *Px3World::getCooking()
 78{
 79   return smCooking;
 80}
 81
 82bool Px3World::restartSDK( bool destroyOnly, Px3World *clientWorld, Px3World *serverWorld)
 83{
 84   // If either the client or the server still exist
 85   // then we cannot reset the SDK.
 86   if ( clientWorld || serverWorld )
 87      return false;
 88#ifndef TORQUE_OS_MAC
 89   SafeReleasePhysx(smCudaContextManager);
 90#endif
 91   SafeReleasePhysx(smCpuDispatcher);
 92   SafeReleasePhysx(smCooking);
 93   smGpuEnabled = false;
 94
 95   // Destroy the existing SDK.
 96   if ( gPhysics3SDK )
 97   {
 98      PxCloseExtensions();
 99      SafeReleasePhysx(gPhysics3SDK);
100   }
101
102   SafeReleasePhysx(smPvdConnection);
103   SafeReleasePhysx(smPvdTransport);
104
105   SAFE_DELETE(smErrorCallback);
106   SafeReleasePhysx(smFoundation);
107
108   // If we're not supposed to restart... return.
109   if ( destroyOnly )
110      return true;
111
112   bool memTrack = false;
113 #ifdef TORQUE_DEBUG
114   memTrack = false;
115 #endif
116   
117   smErrorCallback  = new Px3ConsoleStream;
118   smFoundation = PxCreateFoundation(PX_FOUNDATION_VERSION, smMemoryAlloc, *smErrorCallback);
119   smPvdConnection = PxCreatePvd(*smFoundation);
120   gPhysics3SDK = PxCreatePhysics(PX_PHYSICS_VERSION, *smFoundation, physx::PxTolerancesScale(),memTrack, smPvdConnection);
121
122   if ( !gPhysics3SDK )
123   {
124      Con::errorf( "PhysX3 failed to initialize!" );
125      Platform::messageBox( Con::getVariable( "$appName" ), avar("PhysX3 could not be started!\r\n"), MBOk, MIStop );
126      Platform::forceShutdown( -1 );
127      
128      // We shouldn't get here, but this shuts up
129      // source diagnostic tools.
130      return false;
131   }
132
133   if(!PxInitExtensions(*gPhysics3SDK, smPvdConnection))
134   {
135      Con::errorf( "PhysX3 failed to initialize extensions!" );
136      Platform::messageBox(   Con::getVariable( "$appName" ), avar("PhysX3 could not be started!\r\n"), MBOk, MIStop );
137      Platform::forceShutdown( -1 );
138      return false;
139   }
140//no gpu support on macOS
141#ifndef TORQUE_OS_MAC
142   //check if we are allowed to use gpu acceleration
143   if (PhysicsPlugin::gpuAccelerationAllowed())
144   {
145      // attempt to create a cuda context manager - only works on nvidia gpu (SM 3.0+ i.e kepler or better)
146      if (!smCpuDispatcher)
147      {
148         //check we have capable gpu, -1 means none found
149         S32 suggestedGpu = PxGetSuggestedCudaDeviceOrdinal(*smErrorCallback);
150         if (suggestedGpu != -1)
151         {
152            physx::PxCudaContextManagerDesc cudaContextManagerDesc;
153            smCudaContextManager = PxCreateCudaContextManager(*smFoundation, cudaContextManagerDesc);
154            if (smCudaContextManager)
155               smGpuEnabled = true;
156         }
157      }
158   }
159#endif
160
161   //cpu dispatcher
162   if (!smCpuDispatcher)
163      smCpuDispatcher = physx::PxDefaultCpuDispatcherCreate(PHYSICSMGR->getThreadCount());
164
165   physx::PxCookingParams params = physx::PxCookingParams(physx::PxTolerancesScale());
166   params.meshWeldTolerance = 0.001f;
167   params.meshPreprocessParams = physx::PxMeshPreprocessingFlags(physx::PxMeshPreprocessingFlag::eWELD_VERTICES);
168#ifndef TORQUE_OS_MAC
169   if(smGpuEnabled)
170      params.buildGPUData = true;
171#endif
172
173   smCooking = PxCreateCooking(PX_PHYSICS_VERSION, *smFoundation, params);
174   if(!smCooking)
175   {
176      Con::errorf( "PhysX3 failed to initialize cooking!" );
177      Platform::messageBox( Con::getVariable( "$appName" ), avar("PhysX3 could not be started!\r\n"), MBOk, MIStop );
178      Platform::forceShutdown( -1 );      
179      return false;
180   }
181
182   //TODO: enable/disable this from script
183#ifdef TORQUE_DEBUG
184   if(!smPvdTransport)
185      smPvdTransport = physx::PxDefaultPvdSocketTransportCreate("localhost", 5425, 100);
186
187   smPvdConnection->connect(*smPvdTransport, physx::PxPvdInstrumentationFlag::eALL);
188#endif
189
190   //use legacy heightfield
191   //TODO: new method causing crashes on collision in debug build (unified HeightFields)
192   PxRegisterLegacyHeightFields(*gPhysics3SDK);
193
194   return true;
195}
196
197void Px3World::destroyWorld()
198{
199   getPhysicsResults();
200
201   // Release the tick processing signals.
202   if ( mProcessList )
203   {
204      mProcessList->preTickSignal().remove( this, &Px3World::getPhysicsResults );
205      mProcessList->postTickSignal().remove( this, &Px3World::tickPhysics );
206      mProcessList = NULL;
207   }
208
209   SafeReleasePhysx(mControllerManager);
210   // Destroy the scene.
211   SafeReleasePhysx(mScene);
212}
213
214bool Px3World::initWorld( bool isServer, ProcessList *processList )
215{
216   if ( !gPhysics3SDK )
217   {
218      Con::errorf( "Physx3World::init - PhysXSDK not initialized!" );
219      return false;
220   }
221
222   mIsServer = isServer;
223   
224   physx::PxSceneDesc sceneDesc(gPhysics3SDK->getTolerancesScale());
225
226   sceneDesc.gravity = px3Cast<physx::PxVec3>(mGravity);
227   sceneDesc.userData = this;
228 
229   sceneDesc.cpuDispatcher = smCpuDispatcher;
230   Con::printf("PhysX3 using Cpu: %d workers", smCpuDispatcher->getWorkerCount());
231
232#ifndef TORQUE_OS_MAC
233   if (smGpuEnabled)
234   {
235      sceneDesc.flags |= physx::PxSceneFlag::eENABLE_GPU_DYNAMICS;
236      sceneDesc.flags |= physx::PxSceneFlag::eENABLE_PCM;
237      sceneDesc.broadPhaseType = physx::PxBroadPhaseType::eGPU;
238      sceneDesc.gpuDispatcher = smCudaContextManager->getGpuDispatcher();
239      Con::printf("PhysX3 using Gpu: %s", smCudaContextManager->getDeviceName());
240   }
241#endif
242
243   sceneDesc.flags |= physx::PxSceneFlag::eENABLE_CCD;
244   sceneDesc.flags |= physx::PxSceneFlag::eENABLE_ACTIVETRANSFORMS;
245   sceneDesc.filterShader  = physx::PxDefaultSimulationFilterShader;
246
247   mScene = gPhysics3SDK->createScene(sceneDesc);
248
249   mRenderBuffer = const_cast<physx::PxRenderBuffer*>(&mScene->getRenderBuffer());
250
251   physx::PxDominanceGroupPair debrisDominance( 0.0f, 1.0f );
252   mScene->setDominanceGroupPair(0,31,debrisDominance);
253
254   mControllerManager = PxCreateControllerManager(*mScene);
255
256   AssertFatal( processList, "Px3World::init() - We need a process list to create the world!" );
257   mProcessList = processList;
258   mProcessList->preTickSignal().notify( this, &Px3World::getPhysicsResults );
259   mProcessList->postTickSignal().notify( this, &Px3World::tickPhysics, 1000.0f );
260
261   return true;
262}
263// Most of this borrowed from bullet physics library, see btDiscreteDynamicsWorld.cpp
264bool Px3World::_simulate(const F32 dt)
265{
266   S32 numSimulationSubSteps = 0;
267   //fixed timestep with interpolation
268   mAccumulator += dt;
269   if (mAccumulator >= smPhysicsStepTime)
270   {
271      numSimulationSubSteps = S32(mAccumulator / smPhysicsStepTime);
272      mAccumulator -= numSimulationSubSteps * smPhysicsStepTime;
273   }
274   if (numSimulationSubSteps)
275   {
276      //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt
277      S32 clampedSimulationSteps = (numSimulationSubSteps > smPhysicsMaxSubSteps) ? smPhysicsMaxSubSteps : numSimulationSubSteps;
278      
279      for (S32 i=0;i<clampedSimulationSteps;i++)
280      {
281         if(i > 0)
282            mScene->fetchResults(true);
283         mScene->simulate(smPhysicsStepTime);
284      }
285   }
286   
287   mIsSimulating = true;
288
289   return true;
290}
291
292void Px3World::tickPhysics( U32 elapsedMs )
293{
294   if ( !mScene || !mIsEnabled )
295      return;
296
297   // Did we forget to call getPhysicsResults somewhere?
298   AssertFatal( !mIsSimulating, "PhysX3World::tickPhysics() - Already simulating!" );
299
300   // The elapsed time should be non-zero and 
301   // a multiple of TickMs!
302   AssertFatal(   elapsedMs != 0 &&
303                  ( elapsedMs % TickMs ) == 0 , "PhysX3World::tickPhysics() - Got bad elapsed time!" );
304
305   PROFILE_SCOPE(Px3World_TickPhysics);
306
307   // Convert it to seconds.
308   const F32 elapsedSec = (F32)elapsedMs * 0.001f;
309   mIsSimulating = _simulate(elapsedSec * mEditorTimeScale);
310
311   //Con::printf( "%s PhysX3World::tickPhysics!", mIsServer ? "Client" : "Server" );
312}
313
314void Px3World::getPhysicsResults()
315{
316   if ( !mScene || !mIsSimulating ) 
317      return;
318
319   PROFILE_SCOPE(Px3World_GetPhysicsResults);
320
321   // Get results from scene.
322   mScene->fetchResults(true);
323   mIsSimulating = false;
324   mTickCount++;
325}
326
327void Px3World::lockScenes()
328{
329   Px3World *world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("server"));
330
331   if (world)
332      world->lockScene();
333
334   world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("client"));
335
336   if (world)
337      world->lockScene();
338}
339
340void Px3World::unlockScenes()
341{
342   Px3World *world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("server"));
343
344   if (world)
345      world->unlockScene();
346
347   world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("client"));
348
349   if (world)
350      world->unlockScene();
351}
352
353void Px3World::lockScene()
354{
355   if (!mScene)
356      return;
357
358   if (mIsSceneLocked)
359   {
360      Con::printf("Px3World: Attempting to lock a scene that is already locked.");
361      return;
362   }
363
364   mScene->lockWrite();
365   mIsSceneLocked = true;
366}
367
368void Px3World::unlockScene()
369{
370   if (!mScene)
371      return;
372
373   if (!mIsSceneLocked)
374   {
375      Con::printf("Px3World: Attempting to unlock a scene that is not locked.");
376      return;
377   }
378
379   mScene->unlockWrite();
380   mIsSceneLocked = false;
381}
382
383bool Px3World::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse )
384{    
385   physx::PxVec3 orig = px3Cast<physx::PxVec3>( startPnt );
386   physx::PxVec3 dir = px3Cast<physx::PxVec3>( endPnt - startPnt );
387   physx::PxF32 maxDist = dir.magnitude();
388   dir.normalize();
389
390   U32 groups = 0xffffffff;
391   groups &= ~( PX3_TRIGGER ); // No trigger shapes!
392
393   physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::ePOSITION | physx::PxHitFlag::eNORMAL);
394   physx::PxQueryFilterData filterData(physx::PxQueryFlag::eSTATIC|physx::PxQueryFlag::eDYNAMIC);
395   filterData.data.word0 = groups;
396   physx::PxRaycastBuffer buf;
397
398   if(!mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData))
399      return false;
400   if(!buf.hasBlock)
401      return false;
402
403   const physx::PxRaycastHit hit = buf.block;
404   physx::PxRigidActor *actor = hit.actor;
405   PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
406
407   if ( ri )
408   {
409      ri->object = ( userData != NULL ) ? userData->getObject() : NULL;
410      
411      if ( ri->object == NULL )
412
413      ri->distance = hit.distance;
414      ri->normal = px3Cast<Point3F>( hit.normal );
415      ri->point = px3Cast<Point3F>( hit.position );
416      ri->t = maxDist / hit.distance;
417   }
418
419   if ( impulse.isZero() ||
420        !actor->is<physx::PxRigidDynamic>() ||
421        actor->is<physx::PxRigidDynamic>()->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC )
422      return true;
423  
424   physx::PxRigidBody *body = actor->is<physx::PxRigidBody>();
425   physx::PxVec3 force = px3Cast<physx::PxVec3>( impulse );
426   physx::PxRigidBodyExt::addForceAtPos(*body,force,hit.position,physx::PxForceMode::eIMPULSE);
427
428   return true;
429}
430
431PhysicsBody* Px3World::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes )
432{
433   physx::PxVec3 orig = px3Cast<physx::PxVec3>( start );
434   physx::PxVec3 dir = px3Cast<physx::PxVec3>( end - start );
435   physx::PxF32 maxDist = dir.magnitude();
436   dir.normalize();
437
438   U32 groups = 0xFFFFFFFF;
439   if ( !( bodyTypes & BT_Player ) )
440      groups &= ~( PX3_PLAYER );
441
442   // TODO: For now always skip triggers and debris,
443   // but we should consider how game specifc this API
444   // should be in the future.
445   groups &= ~( PX3_TRIGGER ); // triggers
446   groups &= ~( PX3_DEBRIS ); // debris
447
448   physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::ePOSITION | physx::PxHitFlag::eNORMAL);
449   physx::PxQueryFilterData filterData;
450   if(bodyTypes & BT_Static)
451      filterData.flags |= physx::PxQueryFlag::eSTATIC;
452   if(bodyTypes & BT_Dynamic)
453      filterData.flags |= physx::PxQueryFlag::eDYNAMIC;
454
455   filterData.data.word0 = groups;
456   physx::PxRaycastBuffer buf;  
457
458   if( !mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData) )
459      return NULL;
460   if(!buf.hasBlock)
461      return NULL;
462
463   physx::PxRigidActor *actor = buf.block.actor;
464   PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
465   if( !userData )
466      return NULL;
467
468   return userData->getBody();
469}
470
471void Px3World::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude )
472{
473   physx::PxVec3 nxPos = px3Cast<physx::PxVec3>( pos );
474   const physx::PxU32 bufferSize = 10;
475   physx::PxSphereGeometry worldSphere(radius);
476   physx::PxTransform pose(nxPos);
477   physx::PxOverlapBufferN<bufferSize> buffer;
478  
479   if(!mScene->overlap(worldSphere,pose,buffer))
480      return;
481
482   for ( physx::PxU32 i = 0; i < buffer.nbTouches; i++ )
483   {
484      physx::PxRigidActor *actor = buffer.touches[i].actor;
485      
486      bool dynamic = actor->is<physx::PxRigidDynamic>();
487      
488      if ( !dynamic )
489         continue;
490
491      bool kinematic = actor->is<physx::PxRigidDynamic>()->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC;
492      
493      if ( kinematic )
494         continue;
495
496      physx::PxVec3 force = actor->getGlobalPose().p - nxPos;
497      force.normalize();
498      force *= forceMagnitude;
499
500      physx::PxRigidBody *body = actor->is<physx::PxRigidBody>();
501      physx::PxRigidBodyExt::addForceAtPos(*body,force,nxPos,physx::PxForceMode::eIMPULSE);
502   }
503}
504
505void Px3World::setEnabled( bool enabled )
506{
507   mIsEnabled = enabled;
508
509   if ( !mIsEnabled )
510      getPhysicsResults();
511}
512
513physx::PxController* Px3World::createController( physx::PxControllerDesc &desc )
514{
515   if ( !mScene )
516      return NULL;
517
518   physx::PxController* pController = mControllerManager->createController(desc);
519   AssertFatal( pController, "Px3World::createController - Got a null!" );
520   return pController;
521}
522
523static ColorI getDebugColor( physx::PxU32 packed )
524{
525   ColorI col;
526   col.blue = (packed)&0xff;
527   col.green = (packed>>8)&0xff;
528   col.red = (packed>>16)&0xff;
529   col.alpha = 255;
530
531   return col;
532}
533
534void Px3World::onDebugDraw( const SceneRenderState *state )
535{
536   if ( !mScene || !mRenderBuffer)
537      return;
538
539   mScene->setVisualizationParameter(physx::PxVisualizationParameter::eSCALE,1.0f);
540   mScene->setVisualizationParameter(physx::PxVisualizationParameter::eBODY_AXES,1.0f);
541   mScene->setVisualizationParameter(physx::PxVisualizationParameter::eCOLLISION_SHAPES,1.0f);
542
543   // Render points
544   {
545      physx::PxU32 numPoints = mRenderBuffer->getNbPoints();
546      const physx::PxDebugPoint *points = mRenderBuffer->getPoints();
547
548      PrimBuild::begin( GFXPointList, numPoints );
549      
550      while ( numPoints-- )
551      {
552         PrimBuild::color( getDebugColor(points->color) );
553         PrimBuild::vertex3fv(px3Cast<Point3F>(points->pos));
554         points++;
555      }
556
557      PrimBuild::end();
558   }
559
560   // Render lines
561   {
562      physx::PxU32 numLines = mRenderBuffer->getNbLines();
563      const physx::PxDebugLine *lines = mRenderBuffer->getLines();
564
565      PrimBuild::begin( GFXLineList, numLines * 2 );
566
567      while ( numLines-- )
568      {
569         PrimBuild::color( getDebugColor( lines->color0 ) );
570         PrimBuild::vertex3fv( px3Cast<Point3F>(lines->pos0));
571         PrimBuild::color( getDebugColor( lines->color1 ) );
572         PrimBuild::vertex3fv( px3Cast<Point3F>(lines->pos1));
573         lines++;
574      }
575
576      PrimBuild::end();
577   }
578
579   // Render triangles
580   {
581      physx::PxU32 numTris = mRenderBuffer->getNbTriangles();
582      const physx::PxDebugTriangle *triangles = mRenderBuffer->getTriangles();
583
584      PrimBuild::begin( GFXTriangleList, numTris * 3 );
585      
586      while ( numTris-- )
587      {
588         PrimBuild::color( getDebugColor( triangles->color0 ) );
589         PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos0) );
590         PrimBuild::color( getDebugColor( triangles->color1 ) );
591         PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos1));
592         PrimBuild::color( getDebugColor( triangles->color2 ) );
593         PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos2) );
594         triangles++;
595      }
596
597      PrimBuild::end();
598   }
599
600}
601