px3World.cpp
Engine/source/T3D/physics/physx3/px3World.cpp
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