123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "T3D/physics/physx3/px3World.h"
- #include "T3D/physics/physx3/px3.h"
- #include "T3D/physics/physx3/px3Plugin.h"
- #include "T3D/physics/physx3/px3Casts.h"
- #include "T3D/physics/physx3/px3Stream.h"
- #include "T3D/physics/physicsUserData.h"
- #include "console/engineAPI.h"
- #include "core/stream/bitStream.h"
- #include "platform/profiler.h"
- #include "sim/netConnection.h"
- #include "console/console.h"
- #include "console/consoleTypes.h"
- #include "core/util/safeDelete.h"
- #include "collision/collision.h"
- #include "T3D/gameBase/gameProcess.h"
- #include "gfx/sim/debugDraw.h"
- #include "gfx/primBuilder.h"
- physx::PxPhysics* gPhysics3SDK = NULL;
- physx::PxCooking* Px3World::smCooking = NULL;
- physx::PxFoundation* Px3World::smFoundation = NULL;
- physx::PxDefaultCpuDispatcher* Px3World::smCpuDispatcher = NULL;
- #ifndef TORQUE_OS_MAC
- physx::PxCudaContextManager* Px3World::smCudaContextManager = NULL;
- #endif
- Px3ConsoleStream* Px3World::smErrorCallback = NULL;
- physx::PxPvd* Px3World::smPvdConnection = NULL;
- physx::PxPvdTransport* Px3World::smPvdTransport = NULL;
- physx::PxDefaultAllocator Px3World::smMemoryAlloc;
- Px3World::Px3World() :
- mScene( NULL ),
- mIsEnabled( false ),
- mIsSimulating( false ),
- mIsServer( false ),
- mIsSceneLocked( false ),
- mTickCount( 0 ),
- mProcessList( NULL ),
- mEditorTimeScale( 1.0f ),
- mErrorReport( false ),
- mControllerManager(NULL),
- mRenderBuffer(NULL),
- mAccumulator( 0 )
- {
- }
- Px3World::~Px3World()
- {
- }
- physx::PxCooking *Px3World::getCooking()
- {
- return smCooking;
- }
- bool Px3World::restartSDK( bool destroyOnly, Px3World *clientWorld, Px3World *serverWorld)
- {
- // If either the client or the server still exist
- // then we cannot reset the SDK.
- if ( clientWorld || serverWorld )
- return false;
- #ifndef TORQUE_OS_MAC
- SafeReleasePhysx(smCudaContextManager);
- #endif
- SafeReleasePhysx(smCpuDispatcher);
- SafeReleasePhysx(smCooking);
- smGpuEnabled = false;
- // Destroy the existing SDK.
- if ( gPhysics3SDK )
- {
- PxCloseExtensions();
- SafeReleasePhysx(gPhysics3SDK);
- }
- SafeReleasePhysx(smPvdConnection);
- SafeReleasePhysx(smPvdTransport);
- SAFE_DELETE(smErrorCallback);
- SafeReleasePhysx(smFoundation);
- // If we're not supposed to restart... return.
- if ( destroyOnly )
- return true;
- bool memTrack = false;
- #ifdef TORQUE_DEBUG
- memTrack = false;
- #endif
-
- smErrorCallback = new Px3ConsoleStream;
- smFoundation = PxCreateFoundation(PX_FOUNDATION_VERSION, smMemoryAlloc, *smErrorCallback);
- smPvdConnection = PxCreatePvd(*smFoundation);
- gPhysics3SDK = PxCreatePhysics(PX_PHYSICS_VERSION, *smFoundation, physx::PxTolerancesScale(),memTrack, smPvdConnection);
- if ( !gPhysics3SDK )
- {
- Con::errorf( "PhysX3 failed to initialize!" );
- Platform::messageBox( Con::getVariable( "$appName" ), avar("PhysX3 could not be started!\r\n"), MBOk, MIStop );
- Platform::forceShutdown( -1 );
-
- // We shouldn't get here, but this shuts up
- // source diagnostic tools.
- return false;
- }
- if(!PxInitExtensions(*gPhysics3SDK, smPvdConnection))
- {
- Con::errorf( "PhysX3 failed to initialize extensions!" );
- Platform::messageBox( Con::getVariable( "$appName" ), avar("PhysX3 could not be started!\r\n"), MBOk, MIStop );
- Platform::forceShutdown( -1 );
- return false;
- }
- //no gpu support on macOS
- #ifndef TORQUE_OS_MAC
- //check if we are allowed to use gpu acceleration
- if (PhysicsPlugin::gpuAccelerationAllowed())
- {
- // attempt to create a cuda context manager - only works on nvidia gpu (SM 3.0+ i.e kepler or better)
- if (!smCpuDispatcher)
- {
- //check we have capable gpu, -1 means none found
- S32 suggestedGpu = PxGetSuggestedCudaDeviceOrdinal(*smErrorCallback);
- if (suggestedGpu != -1)
- {
- physx::PxCudaContextManagerDesc cudaContextManagerDesc;
- smCudaContextManager = PxCreateCudaContextManager(*smFoundation, cudaContextManagerDesc);
- if (smCudaContextManager)
- smGpuEnabled = true;
- }
- }
- }
- #endif
- //cpu dispatcher
- if (!smCpuDispatcher)
- smCpuDispatcher = physx::PxDefaultCpuDispatcherCreate(PHYSICSMGR->getThreadCount());
- physx::PxCookingParams params = physx::PxCookingParams(physx::PxTolerancesScale());
- params.meshWeldTolerance = 0.001f;
- params.meshPreprocessParams = physx::PxMeshPreprocessingFlags(physx::PxMeshPreprocessingFlag::eWELD_VERTICES);
- #ifndef TORQUE_OS_MAC
- if(smGpuEnabled)
- params.buildGPUData = true;
- #endif
- smCooking = PxCreateCooking(PX_PHYSICS_VERSION, *smFoundation, params);
- if(!smCooking)
- {
- Con::errorf( "PhysX3 failed to initialize cooking!" );
- Platform::messageBox( Con::getVariable( "$appName" ), avar("PhysX3 could not be started!\r\n"), MBOk, MIStop );
- Platform::forceShutdown( -1 );
- return false;
- }
- //TODO: enable/disable this from script
- #ifdef TORQUE_DEBUG
- if(!smPvdTransport)
- smPvdTransport = physx::PxDefaultPvdSocketTransportCreate("localhost", 5425, 100);
- smPvdConnection->connect(*smPvdTransport, physx::PxPvdInstrumentationFlag::eALL);
- #endif
- //use legacy heightfield
- //TODO: new method causing crashes on collision in debug build (unified HeightFields)
- PxRegisterLegacyHeightFields(*gPhysics3SDK);
- return true;
- }
- void Px3World::destroyWorld()
- {
- getPhysicsResults();
- // Release the tick processing signals.
- if ( mProcessList )
- {
- mProcessList->preTickSignal().remove( this, &Px3World::getPhysicsResults );
- mProcessList->postTickSignal().remove( this, &Px3World::tickPhysics );
- mProcessList = NULL;
- }
- SafeReleasePhysx(mControllerManager);
- // Destroy the scene.
- SafeReleasePhysx(mScene);
- }
- bool Px3World::initWorld( bool isServer, ProcessList *processList )
- {
- if ( !gPhysics3SDK )
- {
- Con::errorf( "Physx3World::init - PhysXSDK not initialized!" );
- return false;
- }
- mIsServer = isServer;
-
- physx::PxSceneDesc sceneDesc(gPhysics3SDK->getTolerancesScale());
- sceneDesc.gravity = px3Cast<physx::PxVec3>(mGravity);
- sceneDesc.userData = this;
-
- sceneDesc.cpuDispatcher = smCpuDispatcher;
- Con::printf("PhysX3 using Cpu: %d workers", smCpuDispatcher->getWorkerCount());
- #ifndef TORQUE_OS_MAC
- if (smGpuEnabled)
- {
- sceneDesc.flags |= physx::PxSceneFlag::eENABLE_GPU_DYNAMICS;
- sceneDesc.flags |= physx::PxSceneFlag::eENABLE_PCM;
- sceneDesc.broadPhaseType = physx::PxBroadPhaseType::eGPU;
- sceneDesc.gpuDispatcher = smCudaContextManager->getGpuDispatcher();
- Con::printf("PhysX3 using Gpu: %s", smCudaContextManager->getDeviceName());
- }
- #endif
- sceneDesc.flags |= physx::PxSceneFlag::eENABLE_CCD;
- sceneDesc.flags |= physx::PxSceneFlag::eENABLE_ACTIVETRANSFORMS;
- sceneDesc.filterShader = physx::PxDefaultSimulationFilterShader;
- mScene = gPhysics3SDK->createScene(sceneDesc);
- mRenderBuffer = const_cast<physx::PxRenderBuffer*>(&mScene->getRenderBuffer());
- physx::PxDominanceGroupPair debrisDominance( 0.0f, 1.0f );
- mScene->setDominanceGroupPair(0,31,debrisDominance);
- mControllerManager = PxCreateControllerManager(*mScene);
- AssertFatal( processList, "Px3World::init() - We need a process list to create the world!" );
- mProcessList = processList;
- mProcessList->preTickSignal().notify( this, &Px3World::getPhysicsResults );
- mProcessList->postTickSignal().notify( this, &Px3World::tickPhysics, 1000.0f );
- return true;
- }
- // Most of this borrowed from bullet physics library, see btDiscreteDynamicsWorld.cpp
- bool Px3World::_simulate(const F32 dt)
- {
- S32 numSimulationSubSteps = 0;
- //fixed timestep with interpolation
- mAccumulator += dt;
- if (mAccumulator >= smPhysicsStepTime)
- {
- numSimulationSubSteps = S32(mAccumulator / smPhysicsStepTime);
- mAccumulator -= numSimulationSubSteps * smPhysicsStepTime;
- }
- if (numSimulationSubSteps)
- {
- //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt
- S32 clampedSimulationSteps = (numSimulationSubSteps > smPhysicsMaxSubSteps) ? smPhysicsMaxSubSteps : numSimulationSubSteps;
-
- for (S32 i=0;i<clampedSimulationSteps;i++)
- {
- if(i > 0)
- mScene->fetchResults(true);
- mScene->simulate(smPhysicsStepTime);
- }
- }
-
- mIsSimulating = true;
- return true;
- }
- void Px3World::tickPhysics( U32 elapsedMs )
- {
- if ( !mScene || !mIsEnabled )
- return;
- // Did we forget to call getPhysicsResults somewhere?
- AssertFatal( !mIsSimulating, "PhysX3World::tickPhysics() - Already simulating!" );
- // The elapsed time should be non-zero and
- // a multiple of TickMs!
- AssertFatal( elapsedMs != 0 &&
- ( elapsedMs % TickMs ) == 0 , "PhysX3World::tickPhysics() - Got bad elapsed time!" );
- PROFILE_SCOPE(Px3World_TickPhysics);
- // Convert it to seconds.
- const F32 elapsedSec = (F32)elapsedMs * 0.001f;
- mIsSimulating = _simulate(elapsedSec * mEditorTimeScale);
- //Con::printf( "%s PhysX3World::tickPhysics!", mIsServer ? "Client" : "Server" );
- }
- void Px3World::getPhysicsResults()
- {
- if ( !mScene || !mIsSimulating )
- return;
- PROFILE_SCOPE(Px3World_GetPhysicsResults);
- // Get results from scene.
- mScene->fetchResults(true);
- mIsSimulating = false;
- mTickCount++;
- }
- void Px3World::lockScenes()
- {
- Px3World *world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("server"));
- if (world)
- world->lockScene();
- world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("client"));
- if (world)
- world->lockScene();
- }
- void Px3World::unlockScenes()
- {
- Px3World *world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("server"));
- if (world)
- world->unlockScene();
- world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("client"));
- if (world)
- world->unlockScene();
- }
- void Px3World::lockScene()
- {
- if (!mScene)
- return;
- if (mIsSceneLocked)
- {
- Con::printf("Px3World: Attempting to lock a scene that is already locked.");
- return;
- }
- mScene->lockWrite();
- mIsSceneLocked = true;
- }
- void Px3World::unlockScene()
- {
- if (!mScene)
- return;
- if (!mIsSceneLocked)
- {
- Con::printf("Px3World: Attempting to unlock a scene that is not locked.");
- return;
- }
- mScene->unlockWrite();
- mIsSceneLocked = false;
- }
- bool Px3World::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse )
- {
- physx::PxVec3 orig = px3Cast<physx::PxVec3>( startPnt );
- physx::PxVec3 dir = px3Cast<physx::PxVec3>( endPnt - startPnt );
- physx::PxF32 maxDist = dir.magnitude();
- dir.normalize();
- U32 groups = 0xffffffff;
- groups &= ~( PX3_TRIGGER ); // No trigger shapes!
- physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::ePOSITION | physx::PxHitFlag::eNORMAL);
- physx::PxQueryFilterData filterData(physx::PxQueryFlag::eSTATIC|physx::PxQueryFlag::eDYNAMIC);
- filterData.data.word0 = groups;
- physx::PxRaycastBuffer buf;
- if(!mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData))
- return false;
- if(!buf.hasBlock)
- return false;
- const physx::PxRaycastHit hit = buf.block;
- physx::PxRigidActor *actor = hit.actor;
- PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
- if ( ri )
- {
- ri->object = ( userData != NULL ) ? userData->getObject() : NULL;
-
- if ( ri->object == NULL )
- ri->distance = hit.distance;
- ri->normal = px3Cast<Point3F>( hit.normal );
- ri->point = px3Cast<Point3F>( hit.position );
- ri->t = maxDist / hit.distance;
- }
- if ( impulse.isZero() ||
- !actor->is<physx::PxRigidDynamic>() ||
- actor->is<physx::PxRigidDynamic>()->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC )
- return true;
-
- physx::PxRigidBody *body = actor->is<physx::PxRigidBody>();
- physx::PxVec3 force = px3Cast<physx::PxVec3>( impulse );
- physx::PxRigidBodyExt::addForceAtPos(*body,force,hit.position,physx::PxForceMode::eIMPULSE);
- return true;
- }
- PhysicsBody* Px3World::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes )
- {
- physx::PxVec3 orig = px3Cast<physx::PxVec3>( start );
- physx::PxVec3 dir = px3Cast<physx::PxVec3>( end - start );
- physx::PxF32 maxDist = dir.magnitude();
- dir.normalize();
- U32 groups = 0xFFFFFFFF;
- if ( !( bodyTypes & BT_Player ) )
- groups &= ~( PX3_PLAYER );
- // TODO: For now always skip triggers and debris,
- // but we should consider how game specifc this API
- // should be in the future.
- groups &= ~( PX3_TRIGGER ); // triggers
- groups &= ~( PX3_DEBRIS ); // debris
- physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::ePOSITION | physx::PxHitFlag::eNORMAL);
- physx::PxQueryFilterData filterData;
- if(bodyTypes & BT_Static)
- filterData.flags |= physx::PxQueryFlag::eSTATIC;
- if(bodyTypes & BT_Dynamic)
- filterData.flags |= physx::PxQueryFlag::eDYNAMIC;
- filterData.data.word0 = groups;
- physx::PxRaycastBuffer buf;
- if( !mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData) )
- return NULL;
- if(!buf.hasBlock)
- return NULL;
- physx::PxRigidActor *actor = buf.block.actor;
- PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
- if( !userData )
- return NULL;
- return userData->getBody();
- }
- void Px3World::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude )
- {
- physx::PxVec3 nxPos = px3Cast<physx::PxVec3>( pos );
- const physx::PxU32 bufferSize = 10;
- physx::PxSphereGeometry worldSphere(radius);
- physx::PxTransform pose(nxPos);
- physx::PxOverlapBufferN<bufferSize> buffer;
-
- if(!mScene->overlap(worldSphere,pose,buffer))
- return;
- for ( physx::PxU32 i = 0; i < buffer.nbTouches; i++ )
- {
- physx::PxRigidActor *actor = buffer.touches[i].actor;
-
- bool dynamic = actor->is<physx::PxRigidDynamic>();
-
- if ( !dynamic )
- continue;
- bool kinematic = actor->is<physx::PxRigidDynamic>()->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC;
-
- if ( kinematic )
- continue;
- physx::PxVec3 force = actor->getGlobalPose().p - nxPos;
- force.normalize();
- force *= forceMagnitude;
- physx::PxRigidBody *body = actor->is<physx::PxRigidBody>();
- physx::PxRigidBodyExt::addForceAtPos(*body,force,nxPos,physx::PxForceMode::eIMPULSE);
- }
- }
- void Px3World::setEnabled( bool enabled )
- {
- mIsEnabled = enabled;
- if ( !mIsEnabled )
- getPhysicsResults();
- }
- physx::PxController* Px3World::createController( physx::PxControllerDesc &desc )
- {
- if ( !mScene )
- return NULL;
- physx::PxController* pController = mControllerManager->createController(desc);
- AssertFatal( pController, "Px3World::createController - Got a null!" );
- return pController;
- }
- static ColorI getDebugColor( physx::PxU32 packed )
- {
- ColorI col;
- col.blue = (packed)&0xff;
- col.green = (packed>>8)&0xff;
- col.red = (packed>>16)&0xff;
- col.alpha = 255;
- return col;
- }
- void Px3World::onDebugDraw( const SceneRenderState *state )
- {
- if ( !mScene || !mRenderBuffer)
- return;
- mScene->setVisualizationParameter(physx::PxVisualizationParameter::eSCALE,1.0f);
- mScene->setVisualizationParameter(physx::PxVisualizationParameter::eBODY_AXES,1.0f);
- mScene->setVisualizationParameter(physx::PxVisualizationParameter::eCOLLISION_SHAPES,1.0f);
- // Render points
- {
- physx::PxU32 numPoints = mRenderBuffer->getNbPoints();
- const physx::PxDebugPoint *points = mRenderBuffer->getPoints();
- PrimBuild::begin( GFXPointList, numPoints );
-
- while ( numPoints-- )
- {
- PrimBuild::color( getDebugColor(points->color) );
- PrimBuild::vertex3fv(px3Cast<Point3F>(points->pos));
- points++;
- }
- PrimBuild::end();
- }
- // Render lines
- {
- physx::PxU32 numLines = mRenderBuffer->getNbLines();
- const physx::PxDebugLine *lines = mRenderBuffer->getLines();
- PrimBuild::begin( GFXLineList, numLines * 2 );
- while ( numLines-- )
- {
- PrimBuild::color( getDebugColor( lines->color0 ) );
- PrimBuild::vertex3fv( px3Cast<Point3F>(lines->pos0));
- PrimBuild::color( getDebugColor( lines->color1 ) );
- PrimBuild::vertex3fv( px3Cast<Point3F>(lines->pos1));
- lines++;
- }
- PrimBuild::end();
- }
- // Render triangles
- {
- physx::PxU32 numTris = mRenderBuffer->getNbTriangles();
- const physx::PxDebugTriangle *triangles = mRenderBuffer->getTriangles();
- PrimBuild::begin( GFXTriangleList, numTris * 3 );
-
- while ( numTris-- )
- {
- PrimBuild::color( getDebugColor( triangles->color0 ) );
- PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos0) );
- PrimBuild::color( getDebugColor( triangles->color1 ) );
- PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos1));
- PrimBuild::color( getDebugColor( triangles->color2 ) );
- PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos2) );
- triangles++;
- }
- PrimBuild::end();
- }
- }
|