1 | //
|
---|
2 | // LolPhysics
|
---|
3 | //
|
---|
4 | // Copyright: (c) 2009-2012 Benjamin Huet <huet.benjamin@gmail.com>
|
---|
5 | // (c) 2012 Sam Hocevar <sam@hocevar.net>
|
---|
6 | //
|
---|
7 |
|
---|
8 | #if !defined __LOLPHYSICS_H__
|
---|
9 | #define __LOLPHYSICS_H__
|
---|
10 |
|
---|
11 | #ifdef HAVE_PHYS_USE_BULLET
|
---|
12 | #include <cstring> |
---|
13 | #include <bullet/btBulletDynamicsCommon.h>
|
---|
14 | #include <bullet/btBulletCollisionCommon.h>
|
---|
15 | #include <BulletDynamics/Character/btKinematicCharacterController.h>
|
---|
16 | #include "LolBtPhysicsIntegration.h"
|
---|
17 | #include "EasyPhysics.h"
|
---|
18 | #include "EasyConstraint.h"
|
---|
19 | #endif
|
---|
20 |
|
---|
21 | namespace lol |
---|
22 | { |
---|
23 | |
---|
24 | namespace phys |
---|
25 | { |
---|
26 | |
---|
27 | enum eRaycastType
|
---|
28 | {
|
---|
29 | ERT_Closest,
|
---|
30 | ERT_AllHit,
|
---|
31 | ERT_AnyHit, //Will stop at the first hit. Hit data are supposed to be irrelevant
|
---|
32 |
|
---|
33 | ERT_MAX
|
---|
34 | };
|
---|
35 |
|
---|
36 | struct RayCastResult
|
---|
37 | {
|
---|
38 | RayCastResult(int CollisionFilterGroup=1, int CollisionFilterMask=(0xFF))
|
---|
39 | {
|
---|
40 | memset(this, 0, sizeof(RayCastResult)); |
---|
41 | |
---|
42 | m_collision_filter_group = CollisionFilterGroup;
|
---|
43 | m_collision_filter_mask = CollisionFilterMask;
|
---|
44 | }
|
---|
45 | void Reset()
|
---|
46 | {
|
---|
47 | m_collider_list.Empty();
|
---|
48 | m_hit_normal_list.Empty();
|
---|
49 | m_hit_point_list.Empty();
|
---|
50 | m_hit_fraction_list.Empty();
|
---|
51 | }
|
---|
52 |
|
---|
53 | Array<EasyPhysic*> m_collider_list;
|
---|
54 | Array<vec3> m_hit_normal_list;
|
---|
55 | Array<vec3> m_hit_point_list;
|
---|
56 | Array<float> m_hit_fraction_list;
|
---|
57 |
|
---|
58 | short int m_collision_filter_group;
|
---|
59 | short int m_collision_filter_mask;
|
---|
60 | unsigned int m_flags; //???
|
---|
61 | };
|
---|
62 |
|
---|
63 | class Simulation : public Entity
|
---|
64 | {
|
---|
65 | public:
|
---|
66 | Simulation() :
|
---|
67 | m_broadphase(0),
|
---|
68 | m_collision_configuration(0),
|
---|
69 | m_dispatcher(0),
|
---|
70 | m_solver(0),
|
---|
71 | m_dynamics_world(0),
|
---|
72 | m_timestep(1.f/60.f)
|
---|
73 | {
|
---|
74 | m_gamegroup = GAMEGROUP_SIMULATION;
|
---|
75 | }
|
---|
76 | ~Simulation()
|
---|
77 | {
|
---|
78 | Exit();
|
---|
79 | }
|
---|
80 |
|
---|
81 | char const *GetName() { return "<Simulation>"; }
|
---|
82 |
|
---|
83 | #ifdef HAVE_PHYS_USE_BULLET
|
---|
84 | public:
|
---|
85 | void Init()
|
---|
86 | {
|
---|
87 | // Build the broadphase
|
---|
88 | if (1)
|
---|
89 | {
|
---|
90 | m_Sweep_broadphase = new btAxisSweep3(LOL2BT_VEC3(m_world_min), LOL2BT_VEC3(m_world_max));
|
---|
91 | m_Sweep_broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
|
---|
92 | m_broadphase = m_Sweep_broadphase;
|
---|
93 | }
|
---|
94 | else
|
---|
95 | m_broadphase = new btDbvtBroadphase();
|
---|
96 |
|
---|
97 | // Set up the collision configuration and dispatcher
|
---|
98 | m_collision_configuration = new btDefaultCollisionConfiguration();
|
---|
99 | m_dispatcher = new btCollisionDispatcher(m_collision_configuration);
|
---|
100 |
|
---|
101 | // The actual physics solver
|
---|
102 | m_solver = new btSequentialImpulseConstraintSolver;
|
---|
103 |
|
---|
104 | // The world.
|
---|
105 | m_dynamics_world = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collision_configuration);
|
---|
106 | }
|
---|
107 |
|
---|
108 | virtual void TickGame(float seconds)
|
---|
109 | {
|
---|
110 | Entity::TickGame(seconds);
|
---|
111 |
|
---|
112 | //step the simulation
|
---|
113 | if (m_dynamics_world)
|
---|
114 | {
|
---|
115 | //the "+1" is to have at least one Timestep and to ensure float to int .5f conversion.
|
---|
116 | int steps = (int)(seconds / m_timestep) + 1;
|
---|
117 | m_dynamics_world->stepSimulation(seconds, steps, m_timestep);
|
---|
118 | }
|
---|
119 | }
|
---|
120 |
|
---|
121 | //Reap-Off of the btKinematicClosestNotMeRayResultCallback
|
---|
122 | class LolClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback |
---|
123 | { |
---|
124 | public: |
---|
125 | LolClosestNotMeRayResultCallback(btCollisionObject* Me, const btVector3& rayFromWorld, const btVector3& rayToWorld) : |
---|
126 | btCollisionWorld::ClosestRayResultCallback(rayFromWorld, rayToWorld) |
---|
127 | { |
---|
128 | m_me = Me; |
---|
129 | } |
---|
130 | |
---|
131 | virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) |
---|
132 | { |
---|
133 | if (rayResult.m_collisionObject == m_me) |
---|
134 | return 1.0; |
---|
135 | |
---|
136 | return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); |
---|
137 | } |
---|
138 | protected: |
---|
139 | btCollisionObject* m_me; |
---|
140 | }; |
---|
141 | |
---|
142 | //Will stop at the first hit. Hit data are supposed to be irrelevant
|
---|
143 | class AnyHitRayResultCallback : public btCollisionWorld::ClosestRayResultCallback |
---|
144 | { |
---|
145 | public: |
---|
146 | AnyHitRayResultCallback(const btVector3& rayFromWorld, const btVector3& rayToWorld) : |
---|
147 | btCollisionWorld::ClosestRayResultCallback(rayFromWorld, rayToWorld) |
---|
148 | { |
---|
149 | } |
---|
150 | |
---|
151 | virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) |
---|
152 | { |
---|
153 | return .0f; |
---|
154 | } |
---|
155 | }; |
---|
156 |
|
---|
157 | //Returns true when hitting something. If SourceCaster is set, it will be ignored by Raycast.
|
---|
158 | bool RayHits(RayCastResult& HitResult, eRaycastType RaycastType, const vec3& RayFrom, const vec3& RayTo, EasyPhysic* SourceCaster=NULL)
|
---|
159 | { |
---|
160 | bool bResult = false; |
---|
161 | |
---|
162 | btCollisionWorld::RayResultCallback* BtRayResult = NULL; |
---|
163 | btCollisionWorld::ClosestRayResultCallback* BtRayResult_Closest; |
---|
164 | btCollisionWorld::AllHitsRayResultCallback* BtRayResult_AllHits; |
---|
165 | |
---|
166 | switch (RaycastType) |
---|
167 | { |
---|
168 | case ERT_Closest:
|
---|
169 | {
|
---|
170 | if (SourceCaster)
|
---|
171 | BtRayResult_Closest = new LolClosestNotMeRayResultCallback(SourceCaster->m_collision_object, LOL2BTU_VEC3(RayFrom), LOL2BTU_VEC3(RayTo));
|
---|
172 | else
|
---|
173 | BtRayResult_Closest = new btCollisionWorld::ClosestRayResultCallback(LOL2BTU_VEC3(RayFrom), LOL2BTU_VEC3(RayTo));
|
---|
174 | BtRayResult = BtRayResult_Closest;
|
---|
175 | break;
|
---|
176 | }
|
---|
177 | case ERT_AllHit:
|
---|
178 | {
|
---|
179 | BtRayResult_AllHits = new btCollisionWorld::AllHitsRayResultCallback(LOL2BTU_VEC3(RayFrom), LOL2BTU_VEC3(RayTo));
|
---|
180 | BtRayResult = BtRayResult_AllHits;
|
---|
181 | break;
|
---|
182 | }
|
---|
183 | case ERT_AnyHit:
|
---|
184 | {
|
---|
185 | BtRayResult_Closest = new AnyHitRayResultCallback(LOL2BTU_VEC3(RayFrom), LOL2BTU_VEC3(RayTo));
|
---|
186 | BtRayResult = BtRayResult_Closest;
|
---|
187 | break;
|
---|
188 | }
|
---|
189 | } |
---|
190 |
|
---|
191 | m_dynamics_world->rayTest(LOL2BTU_VEC3(RayFrom), LOL2BTU_VEC3(RayTo), *BtRayResult);
|
---|
192 | if (BtRayResult->hasHit())
|
---|
193 | {
|
---|
194 | bResult = true;
|
---|
195 |
|
---|
196 | switch (RaycastType) |
---|
197 | { |
---|
198 | case ERT_Closest:
|
---|
199 | {
|
---|
200 | HitResult.m_collider_list << (EasyPhysic*)BtRayResult_Closest->m_collisionObject->getUserPointer();
|
---|
201 | HitResult.m_hit_normal_list << BT2LOLU_VEC3(BtRayResult_Closest->m_hitNormalWorld);
|
---|
202 | HitResult.m_hit_point_list << BT2LOLU_VEC3(BtRayResult_Closest->m_hitPointWorld);
|
---|
203 | HitResult.m_hit_fraction_list << BtRayResult_Closest->m_closestHitFraction;
|
---|
204 | break;
|
---|
205 | }
|
---|
206 | case ERT_AllHit:
|
---|
207 | {
|
---|
208 | for (int i = 0; i < BtRayResult_AllHits->m_collisionObjects.size(); i++)
|
---|
209 | {
|
---|
210 | HitResult.m_collider_list << (EasyPhysic*)BtRayResult_AllHits->m_collisionObjects[i]->getUserPointer();
|
---|
211 | HitResult.m_hit_normal_list << BT2LOLU_VEC3(BtRayResult_AllHits->m_hitNormalWorld[i]);
|
---|
212 | HitResult.m_hit_point_list << BT2LOLU_VEC3(BtRayResult_AllHits->m_hitPointWorld[i]);
|
---|
213 | HitResult.m_hit_fraction_list << BtRayResult_AllHits->m_hitFractions[i];
|
---|
214 | }
|
---|
215 | break;
|
---|
216 | }
|
---|
217 | } |
---|
218 | }
|
---|
219 |
|
---|
220 | delete BtRayResult;
|
---|
221 |
|
---|
222 | return bResult;
|
---|
223 | } |
---|
224 |
|
---|
225 |
|
---|
226 | void Exit()
|
---|
227 | {
|
---|
228 | delete m_dynamics_world;
|
---|
229 | delete m_solver;
|
---|
230 | delete m_dispatcher;
|
---|
231 | delete m_collision_configuration;
|
---|
232 | delete m_broadphase;
|
---|
233 | }
|
---|
234 |
|
---|
235 | btDiscreteDynamicsWorld* GetWorld()
|
---|
236 | {
|
---|
237 | return m_dynamics_world;
|
---|
238 | }
|
---|
239 |
|
---|
240 | private:
|
---|
241 | void CustomSetContinuousDetection(bool ShouldUseCCD)
|
---|
242 | {
|
---|
243 | if (m_dynamics_world)
|
---|
244 | m_dynamics_world->getDispatchInfo().m_useContinuous = ShouldUseCCD;
|
---|
245 | }
|
---|
246 |
|
---|
247 | void CustomSetGravity(vec3 &NewGravity)
|
---|
248 | {
|
---|
249 | if (m_dynamics_world)
|
---|
250 | m_dynamics_world->setGravity(LOL2BT_VEC3(NewGravity * LOL2BT_UNIT));
|
---|
251 | }
|
---|
252 |
|
---|
253 | void CustomSetWorldLimit(vec3 const &NewWorldMin, vec3 const &NewWorldMax)
|
---|
254 | {
|
---|
255 | }
|
---|
256 |
|
---|
257 | void CustomSetTimestep(float NewTimestep) { }
|
---|
258 |
|
---|
259 | //broadphase
|
---|
260 | btBroadphaseInterface* m_broadphase;
|
---|
261 | btAxisSweep3* m_Sweep_broadphase;
|
---|
262 | // Set up the collision configuration and dispatc
|
---|
263 | btDefaultCollisionConfiguration* m_collision_configuration;
|
---|
264 | btCollisionDispatcher* m_dispatcher;
|
---|
265 | // The actual physics solver
|
---|
266 | btSequentialImpulseConstraintSolver* m_solver;
|
---|
267 | // The world.
|
---|
268 | btDiscreteDynamicsWorld* m_dynamics_world;
|
---|
269 |
|
---|
270 | #else // NO PHYSIC IMPLEMENTATION
|
---|
271 |
|
---|
272 | public:
|
---|
273 | void Init() { }
|
---|
274 | void TickGame(float seconds) { }
|
---|
275 | bool RayHits(RayCastResult& HitResult, eRaycastType RaycastType, const vec3& RayFrom, const vec3& RayTo, EasyPhysic* SourceCaster=NULL) { return false; }
|
---|
276 | void Exit() { }
|
---|
277 | private:
|
---|
278 | void CustomSetContinuousDetection(bool ShouldUseCCD) { }
|
---|
279 | void CustomSetGravity(vec3 &NewGravity) { }
|
---|
280 | void CustomSetWorldLimit(vec3 &NewWorldMin, vec3 &NewWorldMax) { }
|
---|
281 | void CustomSetTimestep(float NewTimestep) { }
|
---|
282 |
|
---|
283 | #endif // PHYSIC IMPLEMENTATION
|
---|
284 |
|
---|
285 | public:
|
---|
286 | //Main logic :
|
---|
287 | //The Set*() functions do the all-lib-independent data storage.
|
---|
288 | //And then it calls the CustomSet*() which are the specialized versions.
|
---|
289 |
|
---|
290 | //Sets the continuous collision detection flag.
|
---|
291 | void SetContinuousDetection(bool ShouldUseCCD)
|
---|
292 | {
|
---|
293 | m_using_CCD = ShouldUseCCD;
|
---|
294 | CustomSetContinuousDetection(ShouldUseCCD);
|
---|
295 | }
|
---|
296 |
|
---|
297 | //Sets the simulation gravity.
|
---|
298 | void SetGravity(vec3 &NewGravity)
|
---|
299 | {
|
---|
300 | m_gravity = NewGravity;
|
---|
301 | CustomSetGravity(NewGravity);
|
---|
302 | }
|
---|
303 |
|
---|
304 | //Sets the simulation gravity.
|
---|
305 | void SetWorldLimit(vec3 const &NewWorldMin, vec3 const &NewWorldMax)
|
---|
306 | {
|
---|
307 | m_world_min = NewWorldMin;
|
---|
308 | m_world_max = NewWorldMax;
|
---|
309 | CustomSetWorldLimit(NewWorldMin, NewWorldMax);
|
---|
310 | }
|
---|
311 |
|
---|
312 | //Sets the simulation fixed timestep.
|
---|
313 | void SetTimestep(float NewTimestep)
|
---|
314 | {
|
---|
315 | if (NewTimestep > .0f)
|
---|
316 | {
|
---|
317 | m_timestep = NewTimestep;
|
---|
318 | CustomSetTimestep(NewTimestep);
|
---|
319 | }
|
---|
320 | }
|
---|
321 |
|
---|
322 | private:
|
---|
323 |
|
---|
324 | friend class EasyPhysic;
|
---|
325 | friend class EasyCharacterController;
|
---|
326 | friend class EasyConstraint;
|
---|
327 |
|
---|
328 | enum eEasyPhysicType
|
---|
329 | {
|
---|
330 | EEPT_Dynamic,
|
---|
331 | EEPT_Static,
|
---|
332 | EEPT_Ghost,
|
---|
333 | EEPT_CollisionObject,
|
---|
334 | EEPT_CharacterController,
|
---|
335 |
|
---|
336 | EEPT_MAX
|
---|
337 | };
|
---|
338 |
|
---|
339 | //m_owner_simulation
|
---|
340 | //Adds the given EasyPhysic to the correct list.
|
---|
341 | void ObjectRegistration(bool AddObject, EasyPhysic* NewEP, eEasyPhysicType CurType)
|
---|
342 | {
|
---|
343 | Array<EasyPhysic*>* SearchList = NULL;
|
---|
344 | switch(CurType)
|
---|
345 | {
|
---|
346 | case EEPT_Dynamic:
|
---|
347 | {
|
---|
348 | SearchList = &m_dynamic_list;
|
---|
349 | break;
|
---|
350 | }
|
---|
351 | case EEPT_Static:
|
---|
352 | {
|
---|
353 | SearchList = &m_static_list;
|
---|
354 | break;
|
---|
355 | }
|
---|
356 | case EEPT_Ghost:
|
---|
357 | {
|
---|
358 | SearchList = &m_ghost_list;
|
---|
359 | break;
|
---|
360 | }
|
---|
361 | case EEPT_CollisionObject:
|
---|
362 | {
|
---|
363 | SearchList = &m_collision_object_list;
|
---|
364 | break;
|
---|
365 | }
|
---|
366 | case EEPT_CharacterController:
|
---|
367 | {
|
---|
368 | SearchList = &m_character_controller_list;
|
---|
369 | break;
|
---|
370 | }
|
---|
371 | }
|
---|
372 |
|
---|
373 | if (AddObject)
|
---|
374 | {
|
---|
375 | NewEP->m_owner_simulation = this;
|
---|
376 | (*SearchList) << NewEP;
|
---|
377 | }
|
---|
378 | else
|
---|
379 | {
|
---|
380 | NewEP->m_owner_simulation = NULL;
|
---|
381 | for (int i = 0; i < SearchList->Count(); ++i)
|
---|
382 | {
|
---|
383 | if ((*SearchList)[i] == NewEP)
|
---|
384 | {
|
---|
385 | SearchList->Remove(i--);
|
---|
386 | break;
|
---|
387 | }
|
---|
388 | }
|
---|
389 | }
|
---|
390 | }
|
---|
391 | void ObjectRegistration(bool AddObject, EasyConstraint* NewEC)
|
---|
392 | {
|
---|
393 | Array<EasyConstraint*>* SearchList = NULL;
|
---|
394 | SearchList = &m_constraint_list;
|
---|
395 |
|
---|
396 | if (AddObject)
|
---|
397 | {
|
---|
398 | NewEC->m_owner_simulation = this;
|
---|
399 | (*SearchList) << NewEC;
|
---|
400 | }
|
---|
401 | else
|
---|
402 | {
|
---|
403 | NewEC->m_owner_simulation = NULL;
|
---|
404 | for (int i = 0; i < SearchList->Count(); ++i)
|
---|
405 | {
|
---|
406 | if ((*SearchList)[i] == NewEC)
|
---|
407 | {
|
---|
408 | SearchList->Remove(i--);
|
---|
409 | break;
|
---|
410 | }
|
---|
411 | }
|
---|
412 | }
|
---|
413 | }
|
---|
414 |
|
---|
415 | //Easy Physics body List
|
---|
416 | Array<EasyPhysic*> m_dynamic_list;
|
---|
417 | Array<EasyPhysic*> m_static_list;
|
---|
418 | Array<EasyPhysic*> m_ghost_list;
|
---|
419 | Array<EasyPhysic*> m_collision_object_list;
|
---|
420 | Array<EasyPhysic*> m_character_controller_list;
|
---|
421 | Array<EasyConstraint*> m_constraint_list;
|
---|
422 |
|
---|
423 | //Easy Physics data storage
|
---|
424 | float m_timestep;
|
---|
425 | bool m_using_CCD;
|
---|
426 | vec3 m_gravity;
|
---|
427 | vec3 m_world_min;
|
---|
428 | vec3 m_world_max;
|
---|
429 | };
|
---|
430 | |
---|
431 | } /* namespace phys */ |
---|
432 | |
---|
433 | } /* namespace lol */ |
---|
434 |
|
---|
435 | #endif // __LOLPHYSICS_H__
|
---|
436 |
|
---|