383 righe
14 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright: (c) 2010-2012 Sam Hocevar <sam@hocevar.net>
  5. // (c) 2009-2012 Cédric Lecacheur <jordx@free.fr>
  6. // (c) 2009-2012 Benjamin Huet <huet.benjamin@gmail.com>
  7. // This program is free software; you can redistribute it and/or
  8. // modify it under the terms of the Do What The Fuck You Want To
  9. // Public License, Version 2, as published by Sam Hocevar. See
  10. // http://sam.zoy.org/projects/COPYING.WTFPL for more details.
  11. //
  12. #if defined HAVE_CONFIG_H
  13. # include "config.h"
  14. #endif
  15. #define USE_LOL_CTRLR_CHARAC
  16. #ifdef HAVE_PHYS_USE_BULLET
  17. #include "core.h"
  18. #include <stdio.h>
  19. #include "../Include/LolBtPhysicsIntegration.h"
  20. #include "../Include/LolPhysics.h"
  21. #include "../Include/EasyCharacterController.h"
  22. #include "../Include/BulletCharacterController.h"
  23. //#include "LinearMath/btIDebugDraw.h"
  24. //#include "BulletCollision/CollisionDispatch/btGhostObject.h"
  25. //#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
  26. //#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
  27. //#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
  28. //#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
  29. //#include "LinearMath/btDefaultMotionState.h"
  30. #endif //HAVE_PHYS_USE_BULLET
  31. namespace lol
  32. {
  33. namespace phys
  34. {
  35. #ifdef USE_LOL_CTRLR_CHARAC
  36. #ifdef HAVE_PHYS_USE_BULLET
  37. //SweepCallback used for Swweep Tests.
  38. class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
  39. {
  40. public:
  41. ClosestNotMeConvexResultCallback(btCollisionObject* NewMe, const vec3& NewUp, float MinSlopeDot) :
  42. btCollisionWorld::ClosestConvexResultCallback(LOL2BTU_VEC3(vec3(.0f)), LOL2BTU_VEC3(vec3(.0f))),
  43. m_me(NewMe),
  44. m_up(NewUp),
  45. m_min_slope_dot(MinSlopeDot) { }
  46. virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& ConvexResult, bool NormalInWorld)
  47. {
  48. //We hit ourselves, FAIL
  49. if (ConvexResult.m_hitCollisionObject == m_me)
  50. return btScalar(1.f);
  51. vec3 WorldHitNomal(.0f);
  52. if (NormalInWorld)
  53. WorldHitNomal = BT2LOL_VEC3(ConvexResult.m_hitNormalLocal);
  54. else //need to transform Normal into worldspace
  55. {
  56. btVector3 TmpWorldHitNormal = ConvexResult.m_hitCollisionObject->getWorldTransform().getBasis() * ConvexResult.m_hitNormalLocal;
  57. WorldHitNomal = BT2LOL_VEC3(TmpWorldHitNormal);
  58. }
  59. float DotUp = dot(m_up, WorldHitNomal);
  60. //We hit below the accepted slope_dot, FAIL
  61. if (DotUp < m_min_slope_dot)
  62. return btScalar(1.f);
  63. //Continue to next.
  64. return ClosestConvexResultCallback::addSingleResult(ConvexResult, NormalInWorld);
  65. }
  66. protected:
  67. btCollisionObject* m_me;
  68. const vec3 m_up;
  69. float m_min_slope_dot;
  70. };
  71. //When called, will try to remove Character controller from its collision.
  72. bool BulletKinematicCharacterController::RecoverFromPenetration(btCollisionWorld* CollisionWorld)
  73. {
  74. bool HasPenetration = false;
  75. //Retrieve all pair with us colliding.
  76. CollisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghost_object->getOverlappingPairCache(), CollisionWorld->getDispatchInfo(), CollisionWorld->getDispatcher());
  77. m_current_position = BT2LOLU_VEC3(m_ghost_object->getWorldTransform().getOrigin());
  78. float MaxPen = .0f;
  79. for (int i = 0; i < m_ghost_object->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
  80. {
  81. m_manifold_array.resize(0);
  82. //this is the equivalent of the "Touch algorithm". Maybe refactor ?
  83. btBroadphasePair* CollisionPair = &m_ghost_object->getOverlappingPairCache()->getOverlappingPairArray()[i];
  84. if (CollisionPair->m_algorithm)
  85. CollisionPair->m_algorithm->getAllContactManifolds(m_manifold_array);
  86. for (int j = 0; j < m_manifold_array.size(); ++j)
  87. {
  88. btPersistentManifold* CurMfold = m_manifold_array[j];
  89. //Normal direction differs if we're Body0
  90. float DirSign = CurMfold->getBody0() == m_ghost_object ? -1.f : 1.f;
  91. for (int k = 0; k < CurMfold->getNumContacts(); k++)
  92. {
  93. const btManifoldPoint& MfPoint = CurMfold->getContactPoint(k);
  94. float Dist = MfPoint.getDistance();
  95. if (Dist < .0f)
  96. {
  97. if (Dist < MaxPen)
  98. {
  99. MaxPen = Dist;
  100. m_touching_normal = BT2LOL_VEC3(MfPoint.m_normalWorldOnB) * DirSign;
  101. }
  102. m_current_position += BT2LOL_VEC3(MfPoint.m_normalWorldOnB) * DirSign * Dist * .2f;
  103. HasPenetration = true;
  104. }
  105. }
  106. }
  107. }
  108. btTransform GObjMx = m_ghost_object->getWorldTransform();
  109. GObjMx.setOrigin(LOL2BTU_VEC3(m_current_position));
  110. m_ghost_object->setWorldTransform(GObjMx);
  111. return HasPenetration;
  112. }
  113. //When the Controller hits a wall, we modify the target so the controller will MoveStep along the wall.
  114. void BulletKinematicCharacterController::UpdateTargetOnHit(const vec3& HitNormal, float TangentMag, float NormalMag)
  115. {
  116. vec3 Movedir = m_target_position - m_current_position;
  117. float MoveLength = (float)length(Movedir);
  118. if (MoveLength > SIMD_EPSILON)
  119. {
  120. Movedir = normalize(Movedir);
  121. vec3 ReflectDir = normalize(GetReflectedDir(Movedir, HitNormal));
  122. vec3 ParallelDir = ProjectDirOnNorm(ReflectDir, HitNormal);
  123. vec3 PerpindicularDir = ProjectDirOnNormPerpindicular(ReflectDir, HitNormal);
  124. m_target_position = m_current_position;
  125. if (NormalMag != .0f)
  126. m_target_position += PerpindicularDir * NormalMag * MoveLength;
  127. }
  128. }
  129. //Handles the Step-Up : Currently taking into account Stair step & Jump.
  130. void BulletKinematicCharacterController::StepUp(btCollisionWorld* world)
  131. {
  132. // phase 1: up
  133. vec3 UpDir = GetUpAxisDirections()[m_up_axis];
  134. btTransform SweepStart, SweepEnd;
  135. SweepStart.setIdentity();
  136. SweepEnd.setIdentity();
  137. m_target_position = m_current_position + UpDir * (m_step_height + (m_vertical_offset > 0.f ? m_vertical_offset : 0.f));
  138. /* FIXME: Handle HasPenetration properly */
  139. SweepStart.setOrigin(LOL2BTU_VEC3(m_current_position + UpDir * (m_convex_shape->getMargin() + m_added_margin)));
  140. SweepEnd.setOrigin(LOL2BTU_VEC3(m_target_position));
  141. ClosestNotMeConvexResultCallback SweepCallback(m_ghost_object, -UpDir, float(0.7071));
  142. SweepCallback.m_collisionFilterGroup = GetGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
  143. SweepCallback.m_collisionFilterMask = GetGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
  144. if (m_do_gobject_sweep_test)
  145. m_ghost_object->convexSweepTest(m_convex_shape, SweepStart, SweepEnd, SweepCallback, world->getDispatchInfo().m_allowedCcdPenetration);
  146. else
  147. world->convexSweepTest(m_convex_shape, SweepStart, SweepEnd, SweepCallback);
  148. if (SweepCallback.hasHit())
  149. {
  150. // Only modify the position if the hit was a slope and not a wall or ceiling.
  151. if(SweepCallback.m_hitNormalWorld.dot(LOL2BTU_VEC3(UpDir)) > .0f)
  152. {
  153. // we moved up only a Fraction of the step height
  154. m_current_step_offset = m_step_height * SweepCallback.m_closestHitFraction;
  155. btVector3 InterpolPos; //TODO : REPLACE BY INTERPOLATE3/LERP(VEC3)
  156. InterpolPos.setInterpolate3(LOL2BTU_VEC3(m_current_position), LOL2BTU_VEC3(m_target_position), SweepCallback.m_closestHitFraction);
  157. m_current_position = BT2LOLU_VEC3(InterpolPos);
  158. }
  159. m_vertical_velocity = .0f;
  160. m_vertical_offset = .0f;
  161. }
  162. else
  163. {
  164. m_current_step_offset = m_step_height;
  165. m_current_position = m_target_position;
  166. }
  167. }
  168. //Handles the actual Movement. It actually moves in the 3 dimensions, function name is confusing.
  169. void BulletKinematicCharacterController::StepForwardAndStrafe(btCollisionWorld* CollisionWorld, const vec3& MoveStep)
  170. {
  171. // phase 2: forward and strafe
  172. m_target_position = m_current_position + MoveStep;
  173. btTransform SweepStart, SweepEnd;
  174. SweepStart.setIdentity();
  175. SweepEnd.setIdentity();
  176. float Fraction = 1.f;
  177. float SqDist = .0f;
  178. if (m_touching_contact && dot(m_normalized_direction, m_touching_normal) > .0f)
  179. UpdateTargetOnHit(m_touching_normal);
  180. //Let's loop on movement, until Movement fraction if below 0.01, which means we've reached our destination.
  181. //Or until we'tried 10 times.
  182. int MaxMoveLoop = 10;
  183. while (Fraction > .01f && MaxMoveLoop-- > 0)
  184. {
  185. SweepStart.setOrigin(LOL2BTU_VEC3(m_current_position));
  186. SweepEnd.setOrigin(LOL2BTU_VEC3(m_target_position));
  187. vec3 SweepDirNeg(m_current_position - m_target_position);
  188. ClosestNotMeConvexResultCallback SweepCallback(m_ghost_object, SweepDirNeg, .0f);
  189. SweepCallback.m_collisionFilterGroup = GetGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
  190. SweepCallback.m_collisionFilterMask = GetGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
  191. //The sweep test is done with an added margin, so we use it and then discard it
  192. float SavedMargin = m_convex_shape->getMargin();
  193. m_convex_shape->setMargin(SavedMargin + m_added_margin); //Apply Added Margin
  194. if (m_do_gobject_sweep_test)
  195. m_ghost_object->convexSweepTest (m_convex_shape, SweepStart, SweepEnd, SweepCallback, CollisionWorld->getDispatchInfo().m_allowedCcdPenetration);
  196. else
  197. CollisionWorld->convexSweepTest (m_convex_shape, SweepStart, SweepEnd, SweepCallback, CollisionWorld->getDispatchInfo().m_allowedCcdPenetration);
  198. m_convex_shape->setMargin(SavedMargin); //Restore saved margin
  199. Fraction -= SweepCallback.m_closestHitFraction;
  200. if (SweepCallback.hasHit())
  201. {
  202. //We moved only a Fraction
  203. float HitDist = (float)length(BT2LOLU_VEC3(SweepCallback.m_hitPointWorld) - m_current_position);
  204. UpdateTargetOnHit(BT2LOL_VEC3(SweepCallback.m_hitNormalWorld));
  205. vec3 NewDir = m_target_position - m_current_position;
  206. SqDist = sqlength(NewDir);
  207. if (SqDist > SIMD_EPSILON)
  208. {
  209. NewDir = normalize(NewDir);
  210. //See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners."
  211. if (dot(NewDir, m_normalized_direction) <= .0f)
  212. break;
  213. }
  214. else
  215. break;
  216. }
  217. else //We moved whole way
  218. m_current_position = m_target_position;
  219. }
  220. }
  221. //Handles the Step-down : We go back on the ground at the end of the MoveStep.
  222. void BulletKinematicCharacterController::StepDown(btCollisionWorld* CollisionWorld, float DeltaTime)
  223. {
  224. // phase 3: down
  225. vec3 UpDir = GetUpAxisDirections()[m_up_axis];
  226. btTransform SweepStart, SweepEnd;
  227. SweepStart.setIdentity();
  228. SweepEnd.setIdentity();
  229. float DownVel = (m_vertical_velocity < 0.f ? -m_vertical_velocity : 0.f) * DeltaTime;
  230. if (DownVel > .0f && DownVel < m_step_height && (m_was_on_ground || !m_was_jumping))
  231. DownVel = m_step_height;
  232. vec3 StepDrop = UpDir * (m_current_step_offset + DownVel);
  233. m_target_position -= StepDrop;
  234. SweepStart.setOrigin(LOL2BTU_VEC3(m_current_position));
  235. SweepEnd.setOrigin(LOL2BTU_VEC3(m_target_position));
  236. ClosestNotMeConvexResultCallback SweepCallback(m_ghost_object, UpDir, m_max_slope_cosine);
  237. SweepCallback.m_collisionFilterGroup = GetGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
  238. SweepCallback.m_collisionFilterMask = GetGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
  239. if (m_do_gobject_sweep_test)
  240. m_ghost_object->convexSweepTest(m_convex_shape, SweepStart, SweepEnd, SweepCallback, CollisionWorld->getDispatchInfo().m_allowedCcdPenetration);
  241. else
  242. CollisionWorld->convexSweepTest(m_convex_shape, SweepStart, SweepEnd, SweepCallback, CollisionWorld->getDispatchInfo().m_allowedCcdPenetration);
  243. if (SweepCallback.hasHit())
  244. {
  245. // we dropped a Fraction of the height -> hit floor
  246. btVector3 InterpolPos; //TODO : REPLACE BY INTERPOLATE3/LERP(VEC3)
  247. InterpolPos.setInterpolate3(LOL2BTU_VEC3(m_current_position), LOL2BTU_VEC3(m_target_position), SweepCallback.m_closestHitFraction);
  248. m_current_position = BT2LOLU_VEC3(InterpolPos);
  249. m_vertical_velocity = .0f;
  250. m_vertical_offset = .0f;
  251. m_was_jumping = false;
  252. }
  253. else // we dropped the full height
  254. m_current_position = m_target_position;
  255. }
  256. //The PreStepis done in order to recover from any HasPenetration.
  257. void BulletKinematicCharacterController::PreStep(btCollisionWorld* CollisionWorld)
  258. {
  259. int MaxPenetrationLoop = 0;
  260. m_touching_contact = false;
  261. while (RecoverFromPenetration(CollisionWorld))
  262. {
  263. MaxPenetrationLoop++;
  264. m_touching_contact = true;
  265. if (MaxPenetrationLoop > 4)
  266. break;
  267. }
  268. m_current_position = BT2LOLU_VEC3(m_ghost_object->getWorldTransform().getOrigin());
  269. m_target_position = m_current_position;
  270. }
  271. //And so we step :
  272. //StepUpfirst, then movement, then StepDownon the ground.
  273. void BulletKinematicCharacterController::PlayerStep(btCollisionWorld* CollisionWorld, float DeltaTime)
  274. {
  275. // quick check...
  276. if (!m_use_walk_direction && m_velocity_time_interval <= .0f)
  277. return; // no motion
  278. m_was_on_ground = OnGround();
  279. // Update fall velocity.
  280. m_vertical_velocity -= m_gravity * DeltaTime;
  281. if(m_vertical_velocity > .0f && m_vertical_velocity > m_jump_speed)
  282. m_vertical_velocity = m_jump_speed;
  283. if(m_vertical_velocity < .0f && btFabs(m_vertical_velocity) > btFabs(m_fall_speed))
  284. m_vertical_velocity = -btFabs(m_fall_speed);
  285. m_vertical_offset = m_vertical_velocity * DeltaTime;
  286. btTransform NewTransform;
  287. NewTransform = m_ghost_object->getWorldTransform();
  288. vec3 MoveStep(.0f);
  289. if (m_use_walk_direction)
  290. MoveStep = m_walk_direction;
  291. else
  292. {
  293. //Still have some time left for moving!
  294. float dtMoving = (DeltaTime < m_velocity_time_interval) ? DeltaTime : m_velocity_time_interval;
  295. m_velocity_time_interval -= DeltaTime;
  296. // how far will we MoveStep while we are moving?
  297. MoveStep = m_walk_direction * dtMoving;
  298. }
  299. //Okay, step !
  300. StepUp(CollisionWorld);
  301. StepForwardAndStrafe(CollisionWorld, MoveStep);
  302. StepDown(CollisionWorld, DeltaTime);
  303. //Movement finished, update World transform
  304. NewTransform.setOrigin(LOL2BTU_VEC3(m_current_position));
  305. m_ghost_object->setWorldTransform(NewTransform);
  306. }
  307. //should MoveStep Jump logic in EasyCC
  308. void BulletKinematicCharacterController::Jump()
  309. {
  310. if (!CanJump())
  311. return;
  312. m_vertical_velocity = m_jump_speed;
  313. m_was_jumping = true;
  314. }
  315. #endif // HAVE_PHYS_USE_BULLET
  316. #endif // USE_LOL_CTRLR_CHARAC
  317. } /* namespace phys */
  318. } /* namespace lol */