pxPlayer.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "T3D/physics/physX/pxPlayer.h"
  24. #include "T3D/physics/physicsPlugin.h"
  25. #include "T3D/physics/physX/pxWorld.h"
  26. #include "T3D/physics/physX/pxCasts.h"
  27. #include "collision/collision.h"
  28. //#include "gfx/gfxDrawUtil.h"
  29. //#include "sim/netConnection.h"
  30. PxPlayer::PxPlayer()
  31. : PhysicsPlayer(),
  32. mController( NULL ),
  33. mWorld( NULL ),
  34. mObject( NULL ),
  35. mSkinWidth( 0.1f ),
  36. mOriginOffset( 0.0f )
  37. {
  38. PHYSICSMGR->getPhysicsResetSignal().notify( this, &PxPlayer::_onPhysicsReset );
  39. }
  40. PxPlayer::~PxPlayer()
  41. {
  42. _releaseController();
  43. PHYSICSMGR->getPhysicsResetSignal().remove( this, &PxPlayer::_onPhysicsReset );
  44. }
  45. void PxPlayer::_releaseController()
  46. {
  47. if ( mController )
  48. {
  49. mController->getActor()->userData = NULL;
  50. mWorld->getStaticChangedSignal().remove( this, &PxPlayer::_onStaticChanged );
  51. mWorld->releaseController( *mController );
  52. mController = NULL;
  53. }
  54. }
  55. void PxPlayer::init( const char *type,
  56. const Point3F &size,
  57. F32 runSurfaceCos,
  58. F32 stepHeight,
  59. SceneObject *obj,
  60. PhysicsWorld *world )
  61. {
  62. AssertFatal( obj, "PxPlayer::init - Got a null scene object!" );
  63. AssertFatal( world, "PxPlayer::init - Got a null world!" );
  64. AssertFatal( dynamic_cast<PxWorld*>( world ), "PxPlayer::init - The world is the wrong type!" );
  65. // Cleanup any previous controller.
  66. _releaseController();
  67. mObject = obj;
  68. mWorld = (PxWorld*)world;
  69. mOriginOffset = size.z * 0.5f;
  70. //if ( dStricmp( type, "Capsule" ) == 0 )
  71. {
  72. NxCapsuleControllerDesc desc;
  73. desc.skinWidth = 0.05f; // Expose?
  74. desc.radius = getMax( size.x, size.y ) * 0.5f;
  75. desc.radius -= desc.skinWidth;
  76. desc.height = size.z - ( desc.radius * 2.0f );
  77. desc.height -= desc.skinWidth * 2.0f;
  78. desc.climbingMode = CLIMB_CONSTRAINED;
  79. desc.position.set( 0, 0, 0 );
  80. desc.upDirection = NX_Z;
  81. desc.callback = this; // TODO: Fix this as well!
  82. desc.slopeLimit = runSurfaceCos;
  83. desc.stepOffset = stepHeight;
  84. mController = mWorld->createController( desc );
  85. }
  86. //else
  87. {
  88. //mColShape = new btBoxShape( btVector3( 0.5f, 0.5f, 1.0f ) );
  89. //mOriginOffset = 1.0f;
  90. }
  91. mWorld->getStaticChangedSignal().notify( this, &PxPlayer::_onStaticChanged );
  92. // Put the kinematic actor on group 29.
  93. NxActor *kineActor = mController->getActor();
  94. kineActor->setGroup( 29 );
  95. NxShape *const *shapes = kineActor->getShapes();
  96. for ( U32 i=0; i < kineActor->getNbShapes(); i++ )
  97. shapes[i]->setGroup( 29 );
  98. mUserData.setObject( obj );
  99. kineActor->userData = &mUserData;
  100. }
  101. void PxPlayer::_onStaticChanged()
  102. {
  103. mController->reportSceneChanged();
  104. }
  105. void PxPlayer::_onPhysicsReset( PhysicsResetEvent reset )
  106. {
  107. // The PhysX controller will crash out if it doesn't clear its
  108. // list of static elements when they are deleted. By calling this
  109. // on physics events we clear the cache and we don't get crashes.
  110. //
  111. // This all depends on not doing moves and sweeps when the
  112. // simulation is paused... we need to stop operating.
  113. if ( mController )
  114. mController->reportSceneChanged();
  115. }
  116. Point3F PxPlayer::move( const VectorF &disp, CollisionList &outCol )
  117. {
  118. AssertFatal( mController, "PxPlayer::move - The controller is null!" );
  119. // Return the last position if the simulation is stopped.
  120. //
  121. // See PxPlayer::_onPhysicsReset
  122. if ( !mWorld->isEnabled() )
  123. {
  124. Point3F newPos = pxCast<Point3F>( mController->getDebugPosition() );
  125. newPos.z -= mOriginOffset;
  126. //outCol->point = newPos;
  127. //outCol->normal.set( 0, 0, 1 );
  128. return newPos;
  129. }
  130. mWorld->releaseWriteLock();
  131. mCollisionList = &outCol;
  132. // PhysX 2.8.4 checks up an up displacement and if found will assume
  133. // the player is flying and remove the step offset. If we have a small
  134. // z displacement here, zero it out.
  135. NxVec3 dispNx( disp.x, disp.y, disp.z );
  136. if (mIsZero(disp.z))
  137. dispNx.z = 0.0f;
  138. NxU32 activeGroups = 0xFFFFFFFF;
  139. activeGroups &= ~( 1<<31 ); // Skip activeGroup for triggers ( 31 )
  140. activeGroups &= ~( 1<<30 ); // Skip activeGroup for debris / non interactive dynamics ( 30 )
  141. NxU32 collisionFlags = NXCC_COLLISION_SIDES | NXCC_COLLISION_DOWN | NXCC_COLLISION_UP;
  142. mController->move( dispNx, activeGroups, 0.0001f, collisionFlags );
  143. Point3F newPos = pxCast<Point3F>( mController->getDebugPosition() );
  144. newPos.z -= mOriginOffset;
  145. mCollisionList = NULL;
  146. return newPos;
  147. }
  148. NxControllerAction PxPlayer::onShapeHit( const NxControllerShapeHit& hit )
  149. {
  150. if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions)
  151. return NX_ACTION_NONE;
  152. NxActor *actor = &hit.shape->getActor();
  153. PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
  154. if ( actor->readActorFlag( NX_AF_DISABLE_RESPONSE ) )
  155. return NX_ACTION_NONE;
  156. // Fill out the Collision
  157. // structure for use later.
  158. Collision &col = mCollisionList->increment();
  159. dMemset( &col, 0, sizeof( col ) );
  160. col.normal = pxCast<Point3F>( hit.worldNormal );
  161. col.point.set( hit.worldPos.x, hit.worldPos.y, hit.worldPos.z );
  162. col.distance = hit.length;
  163. if ( userData )
  164. col.object = userData->getObject();
  165. // If the collision direction is sideways then modify the collision normal
  166. // to remove any z component. This takes care of any sideways collisions
  167. // with the round bottom of the capsule when it comes to the Player class
  168. // velocity calculations. We want all sideways collisions to be treated
  169. // as if they hit the side of a cylinder.
  170. if (mIsZero(hit.dir.z))
  171. {
  172. if (col.normal.z > 0.0f)
  173. {
  174. // This will only remove the z component of the collision normal
  175. // for the bottom of the character controller, which would hit during
  176. // a step. We'll leave the top hemisphere of the character's capsule
  177. // alone as bumping one's head is an entirely different story. This
  178. // helps with low doorways.
  179. col.normal.z = 0.0f;
  180. col.normal.normalizeSafe();
  181. }
  182. }
  183. else
  184. {
  185. // PhysX doesn't perform callbacks in its upwards collision check so if
  186. // this isn't a sideways collision then it must be a downwards one. In this
  187. // case we want to have the collision normal only point in the opposite direction.
  188. // i.e. up If we include the sideways part of the normal then the Player class
  189. // velocity calculations using this normal will affect the player's forwards
  190. // momentum. This is especially noticable on stairs as the rounded bottom of
  191. // the capsule slides up the corner of a stair.
  192. col.normal.set(0.0f, 0.0f, 1.0f);
  193. }
  194. /*
  195. if ( userData &&
  196. userData->mCanPush &&
  197. actor->isDynamic() &&
  198. !actor->readBodyFlag( NX_BF_KINEMATIC ) &&
  199. !mDummyMove )
  200. {
  201. NxActor *ctrlActor = mController->getActor();
  202. // So the object is neither
  203. // a static or a kinematic,
  204. // meaning we need to figure out
  205. // if we have enough force to push it.
  206. // Get the hit object's force
  207. // and scale it by the amount
  208. // that it's acceleration is going
  209. // against our acceleration.
  210. const Point3F &hitObjLinVel = pxCast<Point3F>( actor->getLinearVelocity() );
  211. F32 hitObjMass = actor->getMass();
  212. VectorF hitObjDeltaVel = hitObjLinVel * TickSec;
  213. VectorF hitObjAccel = hitObjDeltaVel / TickSec;
  214. VectorF controllerLinVel = pxCast<Point3F>( controllerActor->getLinearVelocity() );
  215. VectorF controllerDeltaVel = controllerLinVel * TickSec;
  216. VectorF controllerAccel = controllerDeltaVel / TickSec;
  217. Point3F hitObjForce = (hitObjMass * hitObjAccel);
  218. Point3F playerForce = (controllerActor->getMass() * controllerAccel);
  219. VectorF normalizedObjVel( hitObjLinVel );
  220. normalizedObjVel.normalizeSafe();
  221. VectorF normalizedPlayerVel( pxCast<Point3F>( controllerActor->getLinearVelocity() ) );
  222. normalizedPlayerVel.normalizeSafe();
  223. F32 forceDot = mDot( normalizedObjVel, normalizedPlayerVel );
  224. hitObjForce *= forceDot;
  225. playerForce = playerForce - hitObjForce;
  226. if ( playerForce.x > 0.0f || playerForce.y > 0.0f || playerForce.z > 0.0f )
  227. actor->addForceAtPos( NxVec3( playerForce.x, playerForce.y, playerForce.z ), actor->getCMassGlobalPosition() );
  228. //Con::printf( "onShapeHit: %f %f %f", playerForce.x, playerForce.y, playerForce.z );
  229. }
  230. */
  231. return NX_ACTION_PUSH;
  232. }
  233. NxControllerAction PxPlayer::onControllerHit( const NxControllersHit& hit )
  234. {
  235. if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions)
  236. return NX_ACTION_NONE;
  237. NxActor *actor = hit.other->getActor();
  238. PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
  239. if ( actor->readActorFlag( NX_AF_DISABLE_RESPONSE ) )
  240. return NX_ACTION_NONE;
  241. // For controller-to-controller hit we don't have an actual hit point, so all
  242. // we can do is set the hit object.
  243. Collision &col = mCollisionList->increment();
  244. dMemset( &col, 0, sizeof( col ) );
  245. if ( userData )
  246. col.object = userData->getObject();
  247. return NX_ACTION_NONE;
  248. }
  249. void PxPlayer::findContact( SceneObject **contactObject,
  250. VectorF *contactNormal,
  251. Vector<SceneObject*> *outOverlapObjects ) const
  252. {
  253. AssertFatal( mController, "PxPlayer::findContact - The controller is null!" );
  254. // See PxPlayer::_onPhysicsReset
  255. if ( !mWorld->isEnabled() )
  256. return;
  257. // Calculate the sweep motion...
  258. F32 halfCapSize = mOriginOffset;
  259. F32 halfSmallCapSize = halfCapSize * 0.8f;
  260. F32 diff = halfCapSize - halfSmallCapSize;
  261. const F32 mSkinWidth = 0.1f;
  262. F32 offsetDist = diff + mSkinWidth + 0.01f;
  263. NxVec3 motion(0,0,-offsetDist);
  264. /*
  265. // Construct the capsule...
  266. F32 radius = mCapsuleController->getRadius();
  267. F32 halfHeight = mCapsuleController->getHeight() * 0.5f;
  268. NxCapsule capsule;
  269. capsule.p0 = capsule.p1 = pxCast<NxVec3>( mCapsuleController->getDebugPosition() );
  270. capsule.p0.z -= halfHeight;
  271. capsule.p1.z += halfHeight;
  272. capsule.radius = radius;
  273. */
  274. NxSweepQueryHit sweepHit;
  275. NxU32 hitCount = mController->getActor()->linearSweep( motion, NX_SF_STATICS | NX_SF_DYNAMICS, NULL, 1, &sweepHit, NULL );
  276. if ( hitCount > 0 )
  277. {
  278. PhysicsUserData *data = PhysicsUserData::cast( sweepHit.hitShape->getActor().userData );
  279. if ( data )
  280. {
  281. *contactObject = data->getObject();
  282. *contactNormal = pxCast<Point3F>( sweepHit.normal );
  283. }
  284. }
  285. // Check for overlapped objects ( triggers )
  286. if ( !outOverlapObjects )
  287. return;
  288. NxCapsuleShape *shape = reinterpret_cast<NxCapsuleShape*>( mController->getActor()->getShapes()[0] );
  289. NxCapsule worldCapsule;
  290. shape->getWorldCapsule( worldCapsule );
  291. // Test only against activeGroup with triggers ( 31 ).
  292. NxU32 activeGroups = 1 << 31;
  293. NxShape *shapes[10];
  294. hitCount = mWorld->getScene()->overlapCapsuleShapes( worldCapsule, NX_ALL_SHAPES, 10, shapes, NULL, activeGroups );
  295. for ( S32 i = 0; i < hitCount; i++ )
  296. {
  297. PhysicsUserData *data = PhysicsUserData::cast( shapes[i]->getActor().userData );
  298. if ( data )
  299. outOverlapObjects->push_back( data->getObject() );
  300. }
  301. }
  302. void PxPlayer::enableCollision()
  303. {
  304. AssertFatal( mController, "PxPlayer::enableCollision - The controller is null!" );
  305. mWorld->releaseWriteLock();
  306. mController->setCollision( true );
  307. }
  308. void PxPlayer::disableCollision()
  309. {
  310. AssertFatal( mController, "PxPlayer::disableCollision - The controller is null!" );
  311. mWorld->releaseWriteLock();
  312. mController->setCollision( false );
  313. }
  314. PhysicsWorld* PxPlayer::getWorld()
  315. {
  316. return mWorld;
  317. }
  318. void PxPlayer::setTransform( const MatrixF &transform )
  319. {
  320. AssertFatal( mController, "PxPlayer::setTransform - The controller is null!" );
  321. mWorld->releaseWriteLock();
  322. Point3F newPos = transform.getPosition();
  323. newPos.z += mOriginOffset;
  324. const Point3F &curPos = pxCast<Point3F>(mController->getDebugPosition());
  325. if ( !(newPos - curPos ).isZero() )
  326. mController->setPosition( pxCast<NxExtendedVec3>(newPos) );
  327. }
  328. MatrixF& PxPlayer::getTransform( MatrixF *outMatrix )
  329. {
  330. AssertFatal( mController, "PxPlayer::getTransform - The controller is null!" );
  331. Point3F newPos = pxCast<Point3F>( mController->getDebugPosition() );
  332. newPos.z -= mOriginOffset;
  333. outMatrix->setPosition( newPos );
  334. return *outMatrix;
  335. }
  336. void PxPlayer::setScale( const Point3F &scale )
  337. {
  338. }
  339. Box3F PxPlayer::getWorldBounds()
  340. {
  341. Con::warnf( "PxPlayer::getWorldBounds - not implemented" );
  342. return Box3F::Invalid;
  343. }