Controller.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. On PhysX because Controller's rotation is frozen using joints,
  6. it may sometimes appear to "rotate" (be out of sync/jitter/jerky)
  7. PhysX may not report all faces of a mesh when sweeping.
  8. /******************************************************************************/
  9. void Controller::zero()
  10. {
  11. fall_control=false;
  12. height_crouched=step_height=0;
  13. _on_ground=_crouched=_jumping=_flying=false;
  14. _radius=_height=_height_cur=_time_in_air=_time_jump=0;
  15. _vel_prev=_step_normal=_shape_offset=_ground_plane.pos=_ground_plane.normal=0;
  16. }
  17. Controller::Controller() {zero();}
  18. /******************************************************************************/
  19. Controller& Controller::del()
  20. {
  21. actor.del();
  22. zero(); return T;
  23. }
  24. Controller& Controller::createCapsule(Flt radius, Flt height, C Vec &pos, C Vec *anchor)
  25. {
  26. del();
  27. T._radius =Max(Physics.minShapeRadius(), Min(radius, (height-Physics.minCapsuleEdge())*0.5f));
  28. T._height=T._height_cur=Max(2*T._radius+Physics.minCapsuleEdge(), height);
  29. height_crouched =T._height*0.67f;
  30. step_height =T._radius;
  31. _shape_offset =(anchor ? pos-*anchor : 0);
  32. actor.create (Capsule(T._radius, T._height, pos), 1, anchor)
  33. .freezeRot(true)
  34. .material (&Physics.mtrl_ctrl)
  35. .group (AG_CONTROLLER);
  36. return T;
  37. }
  38. /******************************************************************************/
  39. #if PHYSX
  40. void Controller::capsuleHeight(Flt height)
  41. {
  42. PxShape *shape; if(actor._actor)if(actor._actor->getShapes(&shape, 1))
  43. {
  44. PxCapsuleGeometry capsule; if(shape->getCapsuleGeometry(capsule))
  45. {
  46. T._height_cur=Max(2*T._radius+Physics.minCapsuleEdge(), height);
  47. capsule.halfHeight=T._height_cur*0.5f-T._radius;
  48. shape->setGeometry(capsule);
  49. PxTransform pose=shape->getLocalPose(); pose.p=Physx.vec(_shape_offset + Vec(0, (T._height_cur-T._height)*0.5f, 0)); shape->setLocalPose(pose);
  50. Physx.world->resetFiltering(*actor._actor); // this call is needed, because when standing up from crouched position, the controller jumps
  51. }
  52. }
  53. }
  54. void Controller::radius(Flt radius)
  55. {
  56. PxShape *shape; if(actor._actor)if(actor._actor->getShapes(&shape, 1))
  57. {
  58. PxCapsuleGeometry capsule; if(shape->getCapsuleGeometry(capsule))
  59. {
  60. T._radius=Mid(radius, Physics.minShapeRadius(), (T._height-Physics.minCapsuleEdge())*0.5f);
  61. capsule.radius=T._radius;
  62. shape->setGeometry(capsule);
  63. capsuleHeight(T._height_cur);
  64. }
  65. }
  66. }
  67. #else
  68. void Controller::capsuleHeight(Flt height)
  69. {
  70. if(actor._actor)if(btCapsuleShape *shape=CAST(btCapsuleShape, actor._actor->getCollisionShape()))
  71. {
  72. T._height_cur=Max(2*T._radius+Physics.minCapsuleEdge(), height);
  73. shape->setImplicitShapeDimensions(btVector3(T._radius, T._height_cur*0.5f-T._radius, T._radius));
  74. Flt com_offset =(T._height-T._height_cur)*0.5f, // center of mass offset (to match PhysX settings)
  75. new_actor_offset =-T._shape_offset.y+com_offset,
  76. delta =actor._actor->offset.pos.y-new_actor_offset;
  77. actor._actor->offset_com.y=com_offset;
  78. actor._actor->offset.pos.y=new_actor_offset;
  79. actor._actor->translate(btVector3(0, delta, 0));
  80. if(Bullet.world)
  81. {
  82. Bullet.world->updateSingleAabb(actor._actor);
  83. Bullet.world->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(actor._actor->getBroadphaseHandle(), Bullet.world->getDispatcher());
  84. }
  85. }
  86. }
  87. void Controller::radius(Flt radius)
  88. {
  89. if(actor._actor)if(btCapsuleShape *shape=CAST(btCapsuleShape, actor._actor->getCollisionShape()))
  90. {
  91. T._radius=Mid(radius, Physics.minShapeRadius(), (T._height-Physics.minCapsuleEdge())*0.5f);
  92. shape->setImplicitShapeDimensions(btVector3(T._radius, T._height_cur*0.5f-T._radius, T._radius));
  93. if(Bullet.world)
  94. {
  95. Bullet.world->updateSingleAabb(actor._actor);
  96. Bullet.world->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(actor._actor->getBroadphaseHandle(), Bullet.world->getDispatcher());
  97. }
  98. }
  99. }
  100. #endif
  101. /******************************************************************************/
  102. void Controller::flying(Bool on)
  103. {
  104. if(flying()!=on)
  105. {
  106. _flying=on;
  107. actor.gravity(!flying());
  108. }
  109. }
  110. /******************************************************************************/
  111. struct ControllerGroundCallback : PhysHitCallback
  112. {
  113. Bool on_ground, do_slide, test_slide;
  114. Flt min_y, max_y, slide_y, slide_radius2, ground_frac, slide_frac;
  115. Vec2 center_xz;
  116. Vec slide_normal;
  117. #if DEBUG // on debug also store slide position in case we want to display it
  118. Vec slide_pos;
  119. #endif
  120. Controller &ctrl;
  121. INLINE Bool closerGround(C PhysHit &phys_hit)C
  122. {
  123. if(!on_ground)return true;
  124. return phys_hit.frac<ground_frac; // just check which one is closer
  125. }
  126. INLINE Bool walkable(C PhysHit &phys_hit, UInt group_mask)C
  127. {
  128. return (group_mask&Physics.ctrl_ground_group_force) // always stand on this group
  129. ||((group_mask&Physics.ctrl_ground_group_allow) // stand only when angle is correct
  130. #if PHYSX // PhysX may not report all faces of a mesh when sweeping (but it was noticed that it has preference to vertical faces, so ignore 'face_nrm' in case it points to some tiny faces with horizontal 'plane.normal')
  131. && phys_hit.plane.normal.y>=Physics.ctrl_ground_slope // if angle is ok
  132. #else // Bullet reports all faces so we can use 'face_nrm'
  133. && phys_hit. face_nrm.y>=Physics.ctrl_ground_slope // if angle is ok
  134. #endif
  135. );
  136. }
  137. virtual Bool hit(PhysHit &phys_hit)
  138. {
  139. if(phys_hit.collision)
  140. {
  141. UInt group_mask=(1<<phys_hit.group);
  142. // detect ground plane
  143. if( phys_hit.plane.pos.y>=min_y
  144. && phys_hit.plane.pos.y<=max_y
  145. && walkable(phys_hit, group_mask)
  146. )
  147. {
  148. if(closerGround(phys_hit) // if closer than previous detection
  149. && !ctrl.actor.ignored(phys_hit))
  150. {
  151. on_ground =true;
  152. ctrl._ground_plane=phys_hit.plane;
  153. ground_frac =phys_hit.frac;
  154. }
  155. }else // detect if we should slide
  156. if(test_slide
  157. && (!phys_hit.dynamic || (group_mask&Physics.ctrl_slide_group_horizontal)) // don't slide against dynamic objects (or slide if it's specified for horizontal)
  158. && phys_hit.plane.pos.y>=slide_y // only if above lower ball
  159. && (!do_slide || phys_hit.frac<slide_frac) // if closer than previous detection
  160. && Dist2(phys_hit.plane.pos.xz(), center_xz)<=slide_radius2 // if within controller radius
  161. && !ctrl.actor.ignored(phys_hit)) // if not ignored
  162. {
  163. do_slide =true;
  164. slide_normal=phys_hit.plane.normal; if(group_mask&Physics.ctrl_slide_group_horizontal){slide_normal.y=0; slide_normal.normalize();} // slide only horizontally against desired groups
  165. slide_frac =phys_hit.frac;
  166. #if DEBUG
  167. slide_pos =phys_hit.plane.pos;
  168. #endif
  169. }
  170. }
  171. return true;
  172. }
  173. ControllerGroundCallback(Controller &ctrl, C Vec &center) : ctrl(ctrl)
  174. {
  175. Flt ctrl_bottom=center.y-ctrl.heightCur()*0.5f; // don't operate on 'ctrl_shape.h' because it was extended
  176. T.on_ground =false;
  177. T.do_slide =false;
  178. T.test_slide =true;
  179. T.center_xz =center.xz();
  180. T.slide_radius2=Sqr( ctrl.radius()+Physics.skin()*6);
  181. T.slide_y =ctrl_bottom+ctrl.radius()-Physics.skin()*2; // add ctrl radius (important when walking up the stairs)
  182. T.min_y =ctrl_bottom -Physics.skin()*PHYS_API(12, 8);
  183. T.max_y =ctrl_bottom+ctrl.step_height;
  184. }
  185. };
  186. struct ControllerCeilCallback : PhysHitCallback
  187. {
  188. Bool cuts;
  189. Controller &ctrl;
  190. virtual Bool hit(PhysHit &phys_hit)
  191. {
  192. if(phys_hit.collision
  193. && !ctrl.actor.ignored(phys_hit)){cuts=true; return false;}
  194. return true;
  195. }
  196. ControllerCeilCallback(Controller &ctrl) : ctrl(ctrl) {cuts=false;}
  197. };
  198. /******************************************************************************/
  199. void Controller::update(C Vec &velocity, Bool crouch, Flt jump)
  200. {
  201. if(actor.is())
  202. {
  203. Vec vel=velocity;
  204. if(!actor.sleep())
  205. {
  206. if(_jumping) // update jumping
  207. {
  208. if((_time_jump+=Time.d())>0.2f){_jumping=false; _time_jump=0;}
  209. }else
  210. if(Physics.updated()) // enter only when physics was updated
  211. {
  212. // detect if on ground
  213. Flt width=Physics.skin()*2;
  214. Capsule shape(radius(), heightCur(), center()); // faster than "actor.shape(false)"
  215. shape.pos.y-=(height()-heightCur())*0.5f; // align to bottom when crouching
  216. shape.extend(-width); // perform negative extend to make shape smaller, so it will properly detect collisions with contacting shapes (closer than skin width)
  217. Vec v(vel.x, 0, vel.z); v.normalize(); v*=width*16; v.y=width*-2-0.1f; // horizontal*16 because when moving almost perpendicular to wall (||) because of smaller radius collision with wall would not be detected, vertical*2 because we need to travel both the radius that we've decreased, and secondary radius for testing (also subtract extra in case the ground is going down, but we're moving forward, so we still can detect the decreasing ground)
  218. ControllerGroundCallback cgc(T, shape.pos);
  219. Physics.sweep(shape, v, cgc, Physics.collisionGroups(AG_CONTROLLER));
  220. if(onGround() && !cgc.on_ground) // if in the previous frame we were on ground but now no ground was detected, try performing additional sweep test in backwards direction
  221. {
  222. cgc.test_slide=false; // we don't want to test sliding in backwards dir
  223. CHS(v.x);
  224. CHS(v.z);
  225. Physics.sweep(shape, v, cgc, Physics.collisionGroups(AG_CONTROLLER));
  226. }
  227. if(cgc.do_slide)
  228. {
  229. Flt d=Dot(vel, cgc.slide_normal); if(d<0)vel-=d*cgc.slide_normal;
  230. }
  231. if(_on_ground=cgc.on_ground)
  232. {
  233. _step_normal=((_ground_plane.normal.y<=0.01f) ? !(_ground_plane.normal+Vec(0, 1.01f, 0)) : _ground_plane.normal); // +1.01 in case _step_normal.y==-1 so normalize will be vertical and not zero
  234. }
  235. }
  236. if(_on_ground)_time_in_air=0;else _time_in_air+=Time.d();
  237. }
  238. // update actor velocities
  239. if(flying())
  240. {
  241. actor.addVel(vel*Time.d());
  242. _vel_prev.zero();
  243. }else
  244. {
  245. if(onGround() || fall_control)
  246. {
  247. if(Physics.updated()) // process only when physics were updated, important because we may do 'Actor.addVel' instead of 'Actor.vel' to set new velocity, which in case physics didn't get simulated it would add delta multiple times (example: actor_vel(0, 0, 0), target_vel(1, 0, 0), addVel(target_vel-actor_vel) addVel(target_vel-actor_vel) addVel(target_vel-actor_vel) would add delta 3x times in case physics was not simulated between the calls)
  248. {
  249. // adjust velocity on ground plane
  250. if(onGround())
  251. {
  252. if(Flt length=vel.length())
  253. if(_step_normal.y) // if "_step_normal.y" would be zero then 'PointOnPlaneRay' would return NaN values
  254. {
  255. Vec v=PointOnPlaneRay(vel, _step_normal, Vec(0, 1, 0));
  256. if(!(jump && v.y<0)) // don't align to ground level if we're jumping and the ground is below us
  257. {
  258. v.setLength(length);
  259. vel=v;
  260. }
  261. }
  262. }
  263. // adjust velocity on want
  264. Vec v=actor.vel(), actor_vel=v;
  265. Int s=Sign(v.y); if(s==Sign(_vel_prev.y))if(Sign(v.y-=_vel_prev.y)!=s)v.y=0;
  266. {
  267. Vec2 vc_xz= v.xz(); if(Flt vc_xz_len=vc_xz.normalize()){ // current velocity of the actor
  268. Vec2 vp_xz= _vel_prev.xz(); Flt vp_xz_len=vp_xz.normalize(); // previously set velocity
  269. Flt dot =Dot(vc_xz, vp_xz);
  270. if( dot>0) // if they are in the same direction
  271. { // decrease only what we've manually set
  272. Flt power_of_decrease=vp_xz_len*dot,
  273. scale_factor =Max(0, vc_xz_len-power_of_decrease)/vc_xz_len;
  274. v.x*=scale_factor;
  275. v.z*=scale_factor;
  276. }}
  277. v+=vel;
  278. }
  279. // store previous velocity
  280. if(onGround())
  281. {
  282. _vel_prev=v; // =vel; this should be set to 'vel' however with PhysX and processing physics in background thread this won't work, because in codes above ("actor.vel()") we expect actual Actor physical velocity, however because we manually set 'vel' below, it will force velocity to the parameter that we give there, because PhysX queues that kind of commands if simulation is already running, after simulation has ended it will process the queue and override physical velocity with our given parameter (we can't use "actor.addVel(target-actor.vel())" to set relative change, because it was tested and it didn't work, worked badly when walking against a wall)
  283. actor.vel(v); // set desired velocity
  284. }else // fall control (here use relative velocity, it's less precise, however it won't cancel out the gravity)
  285. {
  286. _vel_prev=vel;
  287. actor.addVel(v-actor_vel);
  288. }
  289. }
  290. }else // falling
  291. {
  292. actor.addVel(vel*Time.d());
  293. _vel_prev.zero();
  294. }
  295. // jump
  296. if(jump && !_jumping && (onGround() || timeInAir()<=0.2f)) // if on ground or just lost ground (allow 0.2s tolerance)
  297. {
  298. actor.addVel(Vec(0, jump, 0));
  299. _on_ground=false;
  300. _jumping =true;
  301. }
  302. }
  303. // crouch
  304. if(crouch!=T.crouched())
  305. {
  306. if(crouch) // if want to crouch
  307. {
  308. capsuleHeight(height_crouched);
  309. T._crouched=true;
  310. }else // if want to stand up
  311. {
  312. ControllerCeilCallback ccc(T);
  313. Flt h_diff=height()-heightCur();
  314. Ball ball(radius(), center()); ball.pos.y+=height()*0.5f - radius() - h_diff; // set ball as crouched controller upper ball
  315. Physics.sweep(ball, Vec(0, h_diff, 0), ccc, Physics.collisionGroups(AG_CONTROLLER)); // check if there's anything blocking
  316. if(!ccc.cuts)
  317. {
  318. capsuleHeight(height());
  319. T._crouched=false;
  320. }
  321. }
  322. }
  323. // handle slope sliding
  324. if(Physics._css && Physics.updated())
  325. {
  326. Bool freeze=(onGround() && _ground_plane.normal.y<1-EPS && velocity.length2()<=Sqr(0.1f));
  327. switch(Physics._css)
  328. {
  329. case CSS_MATERIALS: actor.material ( freeze ? &Physics.mtrl_ctrl_stop : &Physics.mtrl_ctrl); break;
  330. case CSS_GRAVITY : actor.gravity ((freeze || flying()) ? false : true ); break;
  331. case CSS_FREEZE_XZ: actor.freezePosX(freeze).freezePosZ(freeze); break;
  332. }
  333. }
  334. }
  335. }
  336. /******************************************************************************/
  337. #pragma pack(push, 1)
  338. struct ControllerDesc
  339. {
  340. enum FLAG
  341. {
  342. CROUCHED =1<<0,
  343. ON_GROUND =1<<1,
  344. JUMPING =1<<2,
  345. FLYING =1<<3,
  346. FALL_CONTROL=1<<4,
  347. };
  348. Byte flags;
  349. Flt radius, height, height_cur, height_crouched, step_height, time_in_air, time_jump;
  350. Vec vel_prev, step_normal, shape_offset;
  351. Plane ground_plane;
  352. };
  353. #pragma pack(pop)
  354. Bool Controller::save(File &f)C
  355. {
  356. f.cmpUIntV(0); // version
  357. ControllerDesc desc;
  358. _Unaligned(desc.flags, (_crouched ? ControllerDesc::CROUCHED : 0)
  359. |(_on_ground ? ControllerDesc::ON_GROUND : 0)
  360. |(_jumping ? ControllerDesc::JUMPING : 0)
  361. |(_flying ? ControllerDesc::FLYING : 0)
  362. |( fall_control ? ControllerDesc::FALL_CONTROL : 0));
  363. Unaligned(desc.radius , _radius);
  364. Unaligned(desc.height , _height);
  365. Unaligned(desc.height_cur , _height_cur);
  366. Unaligned(desc.height_crouched, height_crouched);
  367. Unaligned(desc.step_height , step_height);
  368. Unaligned(desc.time_in_air , _time_in_air);
  369. Unaligned(desc.time_jump , _time_jump);
  370. Unaligned(desc.vel_prev , _vel_prev);
  371. Unaligned(desc.step_normal , _step_normal);
  372. Unaligned(desc.shape_offset, _shape_offset);
  373. Unaligned(desc.ground_plane, _ground_plane);
  374. f<<desc;
  375. if(actor.saveState(f))
  376. return f.ok();
  377. return false;
  378. }
  379. Bool Controller::load(File &f)
  380. {
  381. del();
  382. switch(f.decUIntV()) // version
  383. {
  384. case 0:
  385. {
  386. ControllerDesc desc; if(f.getFast(desc))
  387. {
  388. _crouched =FlagTest(Unaligned(desc.flags), ControllerDesc::CROUCHED );
  389. _on_ground =FlagTest(Unaligned(desc.flags), ControllerDesc::ON_GROUND );
  390. _jumping =FlagTest(Unaligned(desc.flags), ControllerDesc::JUMPING );
  391. _flying =FlagTest(Unaligned(desc.flags), ControllerDesc::FLYING );
  392. fall_control=FlagTest(Unaligned(desc.flags), ControllerDesc::FALL_CONTROL);
  393. Unaligned(_radius , desc.radius);
  394. Unaligned(_height , desc.height);
  395. Unaligned(_height_cur , desc.height_cur);
  396. Unaligned( height_crouched, desc.height_crouched);
  397. Unaligned( step_height , desc.step_height);
  398. Unaligned(_time_in_air , desc.time_in_air);
  399. Unaligned(_time_jump , desc.time_jump);
  400. Unaligned(_vel_prev , desc.vel_prev);
  401. Unaligned(_step_normal , desc.step_normal);
  402. Unaligned(_shape_offset, desc.shape_offset);
  403. Unaligned(_ground_plane, desc.ground_plane);
  404. actor.create(Capsule(_radius, _height, _shape_offset), 1, &VecZero);
  405. if(actor.loadState(f))
  406. {
  407. actor.group (AG_CONTROLLER); // reset group in case if the enum value was changed
  408. capsuleHeight(_height_cur);
  409. if(f.ok())return true;
  410. }
  411. }
  412. }break;
  413. }
  414. del(); return false;
  415. }
  416. /******************************************************************************/
  417. }
  418. /******************************************************************************/