123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- //-----------------------------------------------------------------------------
- // 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/physX/pxWorld.h"
- #include "T3D/physics/physX/px.h"
- #include "T3D/physics/physX/pxPlugin.h"
- #include "T3D/physics/physX/pxMaterial.h"
- #include "T3D/physics/physX/pxContactReporter.h"
- #include "T3D/physics/physX/pxStream.h"
- #include "T3D/physics/physX/pxCasts.h"
- #include "T3D/physics/physicsUserData.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 "T3D/tsstatic.h"
- #include "T3D/gameBase/gameProcess.h"
- #include "gfx/sim/debugDraw.h"
- #include "gfx/primBuilder.h"
- #include <NXU_helper.h>
- static const F32 PhysicsStepTime = (F32)TickMs / 1000.0f;
- static const U32 PhysicsMaxIterations = 8;
- static const F32 PhysicsMaxTimeStep = PhysicsStepTime / 2.0f;
- NxPhysicsSDK *gPhysicsSDK = NULL;
- NxCookingInterface *PxWorld::smCooking = NULL;
- PxConsoleStream *PxWorld::smConsoleStream = NULL;
- PxWorld::PxWorld() :
- mScene( NULL ),
- mConactReporter( NULL ),
- mProcessList( NULL ),
- mIsSimulating( false ),
- mErrorReport( false ),
- mTickCount( 0 ),
- mIsEnabled( false ),
- mEditorTimeScale( 1.0f )
- {
- if ( !CCTAllocator::mAllocator )
- CCTAllocator::mAllocator = new NxUserAllocatorDefault();
- mControllerManager = new CharacterControllerManager( CCTAllocator::mAllocator );
- }
- PxWorld::~PxWorld()
- {
- delete mControllerManager;
- }
- NxCookingInterface* PxWorld::getCooking()
- {
- if ( !smCooking )
- smCooking = NxGetCookingLib( NX_PHYSICS_SDK_VERSION );
- return smCooking;
- }
- bool PxWorld::_init( bool isServer, ProcessList *processList )
- {
- if ( !gPhysicsSDK )
- {
- Con::errorf( "PhysXWorld::init - PhysXSDK not initialized!" );
- return false;
- }
- // Create the scene description.
- NxSceneDesc sceneDesc;
- sceneDesc.userData = this;
- // Set up default gravity.
- sceneDesc.gravity.set( mGravity.x, mGravity.y, mGravity.z );
- // The master scene is always on the CPU and is used
- // mostly for static shapes.
- sceneDesc.simType = NX_SIMULATION_SW; // [9/28/2009 Pat] Why is this software? Should be software server, hardware client?
- // Threading... seems to improve performance.
- //
- // TODO: I was getting random crashes in debug when doing
- // edit and continue... lets see if i still get them with
- // the threading disabled.
- //
- sceneDesc.flags |= NX_SF_ENABLE_MULTITHREAD | NX_SF_DISABLE_SCENE_MUTEX;
- sceneDesc.threadMask = 0xfffffffe;
- sceneDesc.internalThreadCount = PHYSICSMGR->getThreadCount();
- // Create the scene.
- mScene = gPhysicsSDK->createScene(sceneDesc);
- if ( !mScene )
- {
- Con::errorf( "PhysXWorld - %s world createScene returned a null scene!", isServer ? "Server" : "Client" );
- return false;
- }
- /*
- // Make note of what we've created.
- String simType = sceneDesc.simType == NX_SIMULATION_SW ? "software" : "hardware";
- String clientOrServer = this == isServer ? "server" : "client";
- Con::printf( "PhysXWorld::init() - Created %s %s simulation!",
- clientOrServer.c_str(),
- simType.c_str() );
- */
- mScene->setTiming( PhysicsMaxTimeStep, PhysicsMaxIterations, NX_TIMESTEP_FIXED );
- // TODO: Add dummy actor with scene name!
- // Set the global contact reporter.
- mConactReporter = new PxContactReporter();
- mScene->setUserContactReport( mConactReporter );
- // Set the global PxUserNotify
- mUserNotify = new PxUserNotify();
- mScene->setUserNotify( mUserNotify );
- // Now create the dynamic rigid body compartment which
- // can reside on the hardware when hardware is present.
- /*
- NxCompartmentDesc compartmentDesc;
- compartmentDesc.type = NX_SCT_RIGIDBODY;
- compartmentDesc.deviceCode = NX_DC_PPU_AUTO_ASSIGN;
- mRigidCompartment = mScene->createCompartment( compartmentDesc );
- if ( !mRigidCompartment )
- {
- Con::errorf( "PhysXWorld - Creation of rigid body compartment failed!" );
- return false;
- }
- */
- // Hook up the tick processing signals for advancing physics.
- //
- // First an overview of how physics and the game ticks
- // interact with each other.
- //
- // In Torque you normally tick the server and then the client
- // approximately every 32ms. So before the game tick we call
- // getPhysicsResults() to get the new physics state and call
- // tickPhysics() when the game tick is done to start processing
- // the next physics state. This means PhysX is happily doing
- // physics in a separate thread while we're doing rendering,
- // sound, input, networking, etc.
- //
- // Because your frame rate is rarely perfectly even you can
- // get cases where you may tick the server or the client
- // several times in a row. This happens most often in debug
- // mode, but can also happen in release.
- //
- // The simple implementation is to do a getPhysicsResults() and
- // tickPhysics() for each tick. But this very bad! It forces
- // immediate results from PhysX which blocks the primary thread
- // and further slows down processing. It leads to slower and
- // slower frame rates as the simulation is never able to catch
- // up to the current tick.
- //
- // The trick is processing physics once for backlogged ticks
- // with the total of the elapsed tick time. This is a huge
- // performance gain and keeps you from blocking on PhysX.
- //
- // This does have a side effect that when it occurs you'll get
- // ticks where the physics state hasn't advanced, but this beats
- // single digit frame rates.
- //
- AssertFatal( processList, "PxWorld::init() - We need a process list to create the world!" );
- mProcessList = processList;
- mProcessList->preTickSignal().notify( this, &PxWorld::getPhysicsResults );
- mProcessList->postTickSignal().notify( this, &PxWorld::tickPhysics, 1000.0f );
- // Setup the default material.
- NxMaterial *dmat = mScene->getMaterialFromIndex( 0 );
- dmat->setRestitution( 0.2f );
- dmat->setStaticFriction( 0.6f );
- dmat->setDynamicFriction( 0.4f );
- // Setup dominance groups.
- // Group 31 is for debris and other objects which can be pushed but cannot push back.
- // Group 0 is for everything else.
-
- NxConstraintDominance debrisDominance( 0.0f, 1.0f );
- mScene->setDominanceGroupPair( 0, 31, debrisDominance );
- return true;
- }
- void PxWorld::_destroy()
- {
- // Make sure the simulation is stopped!
- getPhysicsResults();
- _releaseQueues();
- #ifdef TORQUE_DEBUG
- U32 actorCount = mScene->getNbActors();
- U32 jointCount = mScene->getNbJoints();
-
- if ( actorCount != 0 || jointCount != 0 )
- {
- // Dump the names of any actors or joints that
- // were not released before the destruction of
- // this scene.
- for ( U32 i=0; i < actorCount; i++ )
- {
- const NxActor *actor = mScene->getActors()[i];
- Con::errorf( "Orphan NxActor - '%s'!", actor->getName() );
- }
- mScene->resetJointIterator();
- for ( ;; )
- {
- const NxJoint *joint = mScene->getNextJoint();
- if ( !joint )
- break;
- Con::errorf( "Orphan NxJoint - '%s'!", joint->getName() );
- }
- AssertFatal( false, "PhysXWorld::_destroy() - Some actors and/or joints were not released!" );
- }
- #endif // TORQUE_DEBUG
- //NxCloseCooking();
- // Release the tick processing signals.
- if ( mProcessList )
- {
- mProcessList->preTickSignal().remove( this, &PxWorld::getPhysicsResults );
- mProcessList->postTickSignal().remove( this, &PxWorld::tickPhysics );
- mProcessList = NULL;
- }
- // Destroy the scene.
- if ( mScene )
- {
- // Delete the contact reporter.
- mScene->setUserContactReport( NULL );
- SAFE_DELETE( mConactReporter );
- // First shut down threads... this makes it
- // safe to release the scene.
- mScene->shutdownWorkerThreads();
- // Release the scene.
- gPhysicsSDK->releaseScene( *mScene );
- mScene = NULL;
- }
- // Try to restart the sdk if we can.
- //restartSDK();
- }
- bool PxWorld::restartSDK( bool destroyOnly, PxWorld *clientWorld, PxWorld *serverWorld )
- {
- // If either the client or the server still exist
- // then we cannot reset the SDK.
- if ( clientWorld || serverWorld )
- return false;
- // Destroy the existing SDK.
- if ( gPhysicsSDK )
- {
- NXU::releasePersistentMemory();
- gPhysicsSDK->release();
- gPhysicsSDK = NULL;
- smCooking = NULL;
- SAFE_DELETE( smConsoleStream );
- }
- // If we're not supposed to restart... return.
- if ( destroyOnly )
- return true;
- smConsoleStream = new PxConsoleStream();
- NxPhysicsSDKDesc sdkDesc;
- sdkDesc.flags |= NX_SDKF_NO_HARDWARE; // [9/28/2009 Pat] Why is this disabled?
- NxSDKCreateError error;
- gPhysicsSDK = NxCreatePhysicsSDK( NX_PHYSICS_SDK_VERSION,
- NULL,
- smConsoleStream,
- sdkDesc,
- &error );
- if ( !gPhysicsSDK )
- {
- Con::errorf( "PhysX failed to initialize! Error code: %d", error );
- Platform::messageBox( Con::getVariable( "$appName" ),
- avar("PhysX could not be started!\r\n"
- "Please be sure you have the latest version of PhysX installed.\r\n"
- "Error Code: %d", error),
- MBOk, MIStop );
- Platform::forceShutdown( -1 );
-
- // We shouldn't get here, but this shuts up
- // source diagnostic tools.
- return false;
- }
- // Set the default skin width for all actors.
- gPhysicsSDK->setParameter( NX_SKIN_WIDTH, 0.01f );
- return true;
- }
- void PxWorld::tickPhysics( U32 elapsedMs )
- {
- if ( !mScene || !mIsEnabled )
- return;
- // Did we forget to call getPhysicsResults somewhere?
- AssertFatal( !mIsSimulating, "PhysXWorld::tickPhysics() - Already simulating!" );
- // The elapsed time should be non-zero and
- // a multiple of TickMs!
- AssertFatal( elapsedMs != 0 &&
- ( elapsedMs % TickMs ) == 0 , "PhysXWorld::tickPhysics() - Got bad elapsed time!" );
- PROFILE_SCOPE(PxWorld_TickPhysics);
- // Convert it to seconds.
- const F32 elapsedSec = (F32)elapsedMs * 0.001f;
- // For some reason this gets reset all the time
- // and it must be called before the simulate.
- mScene->setFilterOps( NX_FILTEROP_OR,
- NX_FILTEROP_OR,
- NX_FILTEROP_AND );
- mScene->setFilterBool( false );
- NxGroupsMask zeroMask;
- zeroMask.bits0=zeroMask.bits1=zeroMask.bits2=zeroMask.bits3=0;
- mScene->setFilterConstant0( zeroMask );
- mScene->setFilterConstant1( zeroMask );
- mScene->simulate( elapsedSec * mEditorTimeScale );
- mScene->flushStream();
- mIsSimulating = true;
- //Con::printf( "%s PhysXWorld::tickPhysics!", this == smClientWorld ? "Client" : "Server" );
- }
- void PxWorld::releaseWriteLocks()
- {
- PxWorld *world = dynamic_cast<PxWorld*>( PHYSICSMGR->getWorld( "server" ) );
- if ( world )
- world->releaseWriteLock();
- world = dynamic_cast<PxWorld*>( PHYSICSMGR->getWorld( "client" ) );
- if ( world )
- world->releaseWriteLock();
- }
- void PxWorld::releaseWriteLock()
- {
- if ( !mScene || !mIsSimulating )
- return;
- PROFILE_SCOPE(PxWorld_ReleaseWriteLock);
- // We use checkResults here to release the write lock
- // but we do not change the simulation flag or increment
- // the tick count... we may have gotten results, but the
- // simulation hasn't really ticked!
- mScene->checkResults( NX_RIGID_BODY_FINISHED, true );
- AssertFatal( mScene->isWritable(), "PhysXWorld::releaseWriteLock() - We should have been writable now!" );
- }
- void PxWorld::getPhysicsResults()
- {
- if ( !mScene || !mIsSimulating )
- return;
- PROFILE_SCOPE(PxWorld_GetPhysicsResults);
- // Get results from scene.
- mScene->fetchResults( NX_RIGID_BODY_FINISHED, true );
- mIsSimulating = false;
- mTickCount++;
- // Release any joints/actors that were waiting
- // for the scene to become writable.
- _releaseQueues();
- //Con::printf( "%s PhysXWorld::getPhysicsResults!", this == smClientWorld ? "Client" : "Server" );
- }
- NxMaterial* PxWorld::createMaterial( NxMaterialDesc &material )
- {
- if ( !mScene )
- return NULL;
- // We need the writelock to create a material!
- releaseWriteLock();
- NxMaterial *mat = mScene->createMaterial( material );
- if ( !mat )
- return NULL;
- return mat;
- }
- NxController* PxWorld::createController( NxControllerDesc &desc )
- {
- if ( !mScene )
- return NULL;
- // We need the writelock!
- releaseWriteLock();
- return mControllerManager->createController( mScene, desc );
- }
- void PxWorld::releaseActor( NxActor &actor )
- {
- AssertFatal( &actor.getScene() == mScene, "PhysXWorld::releaseActor() - Bad scene!" );
- // Clear the userdata.
- actor.userData = NULL;
- // If the scene is not simulating then we have the
- // write lock and can safely delete it now.
- if ( !mIsSimulating )
- {
- mScene->releaseActor( actor );
- }
- else
- {
- mReleaseActorQueue.push_back( &actor );
- }
- }
- void PxWorld::releaseMaterial( NxMaterial &mat )
- {
- AssertFatal( &mat.getScene() == mScene, "PhysXWorld::releaseMaterial() - Bad scene!" );
- // If the scene is not simulating then we have the
- // write lock and can safely delete it now.
- if ( !mIsSimulating )
- mScene->releaseMaterial( mat );
- else
- mReleaseMaterialQueue.push_back( &mat );
- }
- void PxWorld::releaseHeightField( NxHeightField &heightfield )
- {
- // Always delay releasing a heightfield, for whatever reason,
- // it causes lots of deadlock asserts if we do it here, even if
- // the scene "says" its writable.
- //
- // Actually this is probably because a heightfield is owned by the "sdk" and
- // not an individual scene so if either the client "or" server scene are
- // simulating it asserts, thats just my theory.
- mReleaseHeightFieldQueue.push_back( &heightfield );
- }
- void PxWorld::releaseJoint( NxJoint &joint )
- {
- AssertFatal( &joint.getScene() == mScene, "PhysXWorld::releaseJoint() - Bad scene!" );
- AssertFatal( !mReleaseJointQueue.contains( &joint ),
- "PhysXWorld::releaseJoint() - Joint already exists in the release queue!" );
- // Clear the userdata.
- joint.userData = NULL;
- // If the scene is not simulating then we have the
- // write lock and can safely delete it now.
- if ( !mIsSimulating )
- mScene->releaseJoint( joint );
- else
- mReleaseJointQueue.push_back( &joint );
- }
- void PxWorld::releaseCloth( NxCloth &cloth )
- {
- AssertFatal( &cloth.getScene() == mScene, "PhysXWorld::releaseCloth() - Bad scene!" );
- // Clear the userdata.
- cloth.userData = NULL;
- // If the scene is not simulating then we have the
- // write lock and can safely delete it now.
- if ( !mIsSimulating )
- mScene->releaseCloth( cloth );
- else
- mReleaseClothQueue.push_back( &cloth );
- }
- void PxWorld::releaseFluid( NxFluid &fluid )
- {
- AssertFatal( &fluid.getScene() == mScene, "PhysXWorld::releaseFluid() - Bad scene!" );
- // Clear the userdata.
- fluid.userData = NULL;
- // If the scene is not simulating then we have the
- // write lock and can safely delete it now.
- if ( !mIsSimulating )
- mScene->releaseFluid( fluid );
- else
- mReleaseFluidQueue.push_back( &fluid );
- }
- void PxWorld::releaseClothMesh( NxClothMesh &clothMesh )
- {
- // We need the writelock to release.
- releaseWriteLock();
- gPhysicsSDK->releaseClothMesh( clothMesh );
- }
- void PxWorld::releaseController( NxController &controller )
- {
- // TODO: This isn't safe to do with actors and
- // joints, so we probably need a queue like we
- // do for them.
- // We need the writelock to release.
- releaseWriteLock();
- mControllerManager->releaseController( controller );
- }
- void PxWorld::_releaseQueues()
- {
- AssertFatal( mScene, "PhysXWorld::_releaseQueues() - The scene is null!" );
- // We release joints still pending in the queue
- // first as they depend on the actors.
- for ( S32 i = 0; i < mReleaseJointQueue.size(); i++ )
- {
- NxJoint *currJoint = mReleaseJointQueue[i];
- mScene->releaseJoint( *currJoint );
- }
-
- // All the joints should be released, clear the queue.
- mReleaseJointQueue.clear();
- // Now release any actors still pending in the queue.
- bool staticChanged = false;
- for ( S32 i = 0; i < mReleaseActorQueue.size(); i++ )
- {
- NxActor *currActor = mReleaseActorQueue[i];
- staticChanged |= !currActor->isDynamic();
- mScene->releaseActor( *currActor );
- }
- // All the actors should be released, clear the queue.
- mReleaseActorQueue.clear();
- // Now release any materials still pending in the queue.
- for ( S32 i = 0; i < mReleaseMaterialQueue.size(); i++ )
- {
- NxMaterial *currMat = mReleaseMaterialQueue[i];
- mScene->releaseMaterial( *currMat );
- }
- // All the materials should be released, clear the queue.
- mReleaseMaterialQueue.clear();
- // Now release any cloth still pending in the queue.
- for ( S32 i = 0; i < mReleaseClothQueue.size(); i++ )
- {
- NxCloth *currCloth = mReleaseClothQueue[i];
- mScene->releaseCloth( *currCloth );
- }
- // All the actors should be released, clear the queue.
- mReleaseClothQueue.clear();
- // Release heightfields that don't still have references.
- for ( S32 i = 0; i < mReleaseHeightFieldQueue.size(); i++ )
- {
- NxHeightField *currHeightfield = mReleaseHeightFieldQueue[i];
-
- if ( currHeightfield->getReferenceCount() == 0 )
- {
- gPhysicsSDK->releaseHeightField( *currHeightfield );
- mReleaseHeightFieldQueue.erase_fast( i );
- i--;
- }
- }
- // Clear fluid queue
- for ( S32 i = 0; i < mReleaseFluidQueue.size(); i++ )
- {
- NxFluid *currFluid = mReleaseFluidQueue[i];
- mScene->releaseFluid( *currFluid );
- }
- mReleaseFluidQueue.clear();
- if ( staticChanged )
- mStaticChangedSignal.trigger();
- }
- void PxWorld::setEnabled( bool enabled )
- {
- mIsEnabled = enabled;
- if ( !mIsEnabled )
- getPhysicsResults();
- }
- bool PxWorld::initWorld( bool isServer, ProcessList *processList )
- {
- /* This stuff is handled outside.
- PxWorld* world = PxWorld::getWorld( isServer );
- if ( world )
- {
- Con::errorf( "PhysXWorld::initWorld - %s world already exists!", isServer ? "Server" : "Client" );
- return false;
- }
- */
-
- if ( !_init( isServer, processList ) )
- return false;
- return true;
- }
- void PxWorld::destroyWorld()
- {
- //PxWorld* world = PxWorld::getWorld( serverWorld );
- /*
- if ( !world )
- {
- Con::errorf( "PhysXWorld::destroyWorld - %s world already destroyed!", serverWorld ? "Server" : "Client" );
- return;
- }
- */
- //world->_destroy();
- //delete world;
- _destroy();
- }
- bool PxWorld::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse )
- {
- NxRay worldRay;
- worldRay.orig = pxCast<NxVec3>( startPnt );
- worldRay.dir = pxCast<NxVec3>( endPnt - startPnt );
- NxF32 maxDist = worldRay.dir.magnitude();
- worldRay.dir.normalize();
- U32 groups = 0xffffffff;
- groups &= ~( 1<<31 ); // No trigger shapes!
- NxRaycastHit hitInfo;
- NxShape *hitShape = mScene->raycastClosestShape( worldRay, NX_ALL_SHAPES, hitInfo, groups, maxDist );
- if ( !hitShape )
- return false;
- //if ( hitShape->userData != NULL )
- // return false;
- NxActor &actor = hitShape->getActor();
- PhysicsUserData *userData = PhysicsUserData::cast( actor.userData );
- if ( ri )
- {
- ri->object = ( userData != NULL ) ? userData->getObject() : NULL;
-
- // If we were passed a RayInfo, we can only return true signifying a collision
- // if we hit an object that actually has a torque object associated with it.
- //
- // In some ways this could be considered an error, either a physx object
- // has raycast-collision enabled that shouldn't or someone did not set
- // an object in this actor's userData.
- //
- if ( ri->object == NULL )
- return false;
- ri->distance = hitInfo.distance;
- ri->normal = pxCast<Point3F>( hitInfo.worldNormal );
- ri->point = pxCast<Point3F>( hitInfo.worldImpact );
- ri->t = maxDist / hitInfo.distance;
- }
- if ( impulse.isZero() ||
- !actor.isDynamic() ||
- actor.readBodyFlag( NX_BF_KINEMATIC ) )
- return true;
-
- NxVec3 force = pxCast<NxVec3>( impulse );//worldRay.dir * forceAmt;
- actor.addForceAtPos( force, hitInfo.worldImpact, NX_IMPULSE );
- return true;
- }
- PhysicsBody* PxWorld::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes )
- {
- NxRay worldRay;
- worldRay.orig = pxCast<NxVec3>( start );
- worldRay.dir = pxCast<NxVec3>( end - start );
- F32 maxDist = worldRay.dir.normalize();
- U32 groups = 0xFFFFFFFF;
- if ( !( bodyTypes & BT_Player ) )
- groups &= ~( 1<<29 );
- // TODO: For now always skip triggers and debris,
- // but we should consider how game specifc this API
- // should be in the future.
- groups &= ~( 1<<31 ); // triggers
- groups &= ~( 1<<30 ); // debris
- U32 shapesType = 0;
- if ( bodyTypes & BT_Static )
- shapesType |= NX_STATIC_SHAPES;
- if ( bodyTypes & BT_Dynamic )
- shapesType |= NX_DYNAMIC_SHAPES;
- NxRaycastHit hitInfo;
- NxShape *hitShape = mScene->raycastClosestShape( worldRay, (NxShapesType)shapesType, hitInfo, groups, maxDist );
- if ( !hitShape )
- return NULL;
- NxActor &actor = hitShape->getActor();
- PhysicsUserData *userData = PhysicsUserData::cast( actor.userData );
- if ( !userData )
- return NULL;
- return userData->getBody();
- }
- void PxWorld::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude )
- {
- // Find Actors at the position within the radius
- // and apply force to them.
- NxVec3 nxPos = pxCast<NxVec3>( pos );
- NxShape **shapes = (NxShape**)NxAlloca(10*sizeof(NxShape*));
- NxSphere worldSphere( nxPos, radius );
- NxU32 numHits = mScene->overlapSphereShapes( worldSphere, NX_ALL_SHAPES, 10, shapes, NULL );
- for ( NxU32 i = 0; i < numHits; i++ )
- {
- NxActor &actor = shapes[i]->getActor();
-
- bool dynamic = actor.isDynamic();
-
- if ( !dynamic )
- continue;
- bool kinematic = actor.readBodyFlag( NX_BF_KINEMATIC );
-
- if ( kinematic )
- continue;
- NxVec3 force = actor.getGlobalPosition() - nxPos;
- force.normalize();
- force *= forceMagnitude;
- actor.addForceAtPos( force, nxPos, NX_IMPULSE, true );
- }
- }
- static ColorI getDebugColor( NxU32 packed )
- {
- ColorI col;
- col.blue = (packed)&0xff;
- col.green = (packed>>8)&0xff;
- col.red = (packed>>16)&0xff;
- col.alpha = 255;
- return col;
- }
- void PxWorld::onDebugDraw( const SceneRenderState *state )
- {
- if ( !mScene )
- return;
- // We need the write lock to be able to request
- // the NxDebugRenderable object.
- releaseWriteLock();
- // TODO: We need to expose the different types of visualization
- // options to script and add a GUI for toggling them!
- gPhysicsSDK->setParameter( NX_VISUALIZATION_SCALE, 1.0f );
- //gPhysicsSDK->setParameter( NX_VISUALIZE_BODY_MASS_AXES, 0.0f );
- gPhysicsSDK->setParameter( NX_VISUALIZE_BODY_AXES, 1.0f );
- gPhysicsSDK->setParameter( NX_VISUALIZE_COLLISION_SHAPES, 1.0f );
- const NxDebugRenderable *data = mScene->getDebugRenderable();
- if ( !data )
- return;
- // Render points
- {
- NxU32 numPoints = data->getNbPoints();
- const NxDebugPoint *points = data->getPoints();
- PrimBuild::begin( GFXPointList, numPoints );
-
- while ( numPoints-- )
- {
- PrimBuild::color( getDebugColor(points->color) );
- PrimBuild::vertex3fv( &points->p.x );
- points++;
- }
- PrimBuild::end();
- }
- // Render lines
- {
- NxU32 numLines = data->getNbLines();
- const NxDebugLine *lines = data->getLines();
- PrimBuild::begin( GFXLineList, numLines * 2 );
- while ( numLines-- )
- {
- PrimBuild::color( getDebugColor( lines->color ) );
- PrimBuild::vertex3fv( &lines->p0.x );
- PrimBuild::vertex3fv( &lines->p1.x );
- lines++;
- }
- PrimBuild::end();
- }
- // Render triangles
- {
- NxU32 numTris = data->getNbTriangles();
- const NxDebugTriangle *triangles = data->getTriangles();
- PrimBuild::begin( GFXTriangleList, numTris * 3 );
-
- while ( numTris-- )
- {
- PrimBuild::color( getDebugColor( triangles->color ) );
- PrimBuild::vertex3fv( &triangles->p0.x );
- PrimBuild::vertex3fv( &triangles->p1.x );
- PrimBuild::vertex3fv( &triangles->p2.x );
- triangles++;
- }
- PrimBuild::end();
- }
- }
|