pxPlayer.cpp 14 KB

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