1 
2 #ifndef COMMON_RIGID_BODY_BASE_H
3 #define COMMON_RIGID_BODY_BASE_H
4 
5 #include "btBulletDynamicsCommon.h"
6 #include "CommonExampleInterface.h"
7 #include "CommonGUIHelperInterface.h"
8 #include "CommonRenderInterface.h"
9 #include "CommonCameraInterface.h"
10 #include "CommonGraphicsAppInterface.h"
11 #include "CommonWindowInterface.h"
12 #include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h"
13 
14 struct CommonRigidBodyBase : public CommonExampleInterface
15 {
16 	//keep the collision shapes, for deletion/cleanup
17 	btAlignedObjectArray<btCollisionShape*> m_collisionShapes;
18 	btBroadphaseInterface* m_broadphase;
19 	btCollisionDispatcher* m_dispatcher;
20 	btConstraintSolver* m_solver;
21 	btDefaultCollisionConfiguration* m_collisionConfiguration;
22 	btDiscreteDynamicsWorld* m_dynamicsWorld;
23 
24 	//data for picking objects
25 	class btRigidBody* m_pickedBody;
26 	class btTypedConstraint* m_pickedConstraint;
27 	int m_savedState;
28 	btVector3 m_oldPickingPos;
29 	btVector3 m_hitPos;
30 	btScalar m_oldPickingDist;
31 	struct GUIHelperInterface* m_guiHelper;
32 
CommonRigidBodyBaseCommonRigidBodyBase33 	CommonRigidBodyBase(struct GUIHelperInterface* helper)
34 		: m_broadphase(0),
35 		  m_dispatcher(0),
36 		  m_solver(0),
37 		  m_collisionConfiguration(0),
38 		  m_dynamicsWorld(0),
39 		  m_pickedBody(0),
40 		  m_pickedConstraint(0),
41 		  m_guiHelper(helper)
42 	{
43 	}
~CommonRigidBodyBaseCommonRigidBodyBase44 	virtual ~CommonRigidBodyBase()
45 	{
46 	}
47 
getDynamicsWorldCommonRigidBodyBase48 	btDiscreteDynamicsWorld* getDynamicsWorld()
49 	{
50 		return m_dynamicsWorld;
51 	}
52 
createEmptyDynamicsWorldCommonRigidBodyBase53 	virtual void createEmptyDynamicsWorld()
54 	{
55 		///collision configuration contains default setup for memory, collision setup
56 		m_collisionConfiguration = new btDefaultCollisionConfiguration();
57 		//m_collisionConfiguration->setConvexConvexMultipointIterations();
58 
59 		///use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
60 		m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
61 
62 		m_broadphase = new btDbvtBroadphase();
63 
64 		///the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded)
65 		btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver;
66 		m_solver = sol;
67 
68 		m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration);
69 
70 		m_dynamicsWorld->setGravity(btVector3(0, -10, 0));
71 	}
72 
stepSimulationCommonRigidBodyBase73 	virtual void stepSimulation(float deltaTime)
74 	{
75 		if (m_dynamicsWorld)
76 		{
77 			m_dynamicsWorld->stepSimulation(deltaTime);
78 		}
79 	}
80 
physicsDebugDrawCommonRigidBodyBase81 	virtual void physicsDebugDraw(int debugFlags)
82 	{
83 		if (m_dynamicsWorld && m_dynamicsWorld->getDebugDrawer())
84 		{
85 			m_dynamicsWorld->getDebugDrawer()->setDebugMode(debugFlags);
86 			m_dynamicsWorld->debugDrawWorld();
87 		}
88 	}
89 
exitPhysicsCommonRigidBodyBase90 	virtual void exitPhysics()
91 	{
92 		removePickingConstraint();
93 		//cleanup in the reverse order of creation/initialization
94 
95 		//remove the rigidbodies from the dynamics world and delete them
96 
97 		if (m_dynamicsWorld)
98 		{
99 			int i;
100 			for (i = m_dynamicsWorld->getNumConstraints() - 1; i >= 0; i--)
101 			{
102 				m_dynamicsWorld->removeConstraint(m_dynamicsWorld->getConstraint(i));
103 			}
104 			for (i = m_dynamicsWorld->getNumCollisionObjects() - 1; i >= 0; i--)
105 			{
106 				btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i];
107 				btRigidBody* body = btRigidBody::upcast(obj);
108 				if (body && body->getMotionState())
109 				{
110 					delete body->getMotionState();
111 				}
112 				m_dynamicsWorld->removeCollisionObject(obj);
113 				delete obj;
114 			}
115 		}
116 		//delete collision shapes
117 		for (int j = 0; j < m_collisionShapes.size(); j++)
118 		{
119 			btCollisionShape* shape = m_collisionShapes[j];
120 			delete shape;
121 		}
122 		m_collisionShapes.clear();
123 
124 		delete m_dynamicsWorld;
125 		m_dynamicsWorld = 0;
126 
127 		delete m_solver;
128 		m_solver = 0;
129 
130 		delete m_broadphase;
131 		m_broadphase = 0;
132 
133 		delete m_dispatcher;
134 		m_dispatcher = 0;
135 
136 		delete m_collisionConfiguration;
137 		m_collisionConfiguration = 0;
138 	}
139 
debugDrawCommonRigidBodyBase140 	virtual void debugDraw(int debugDrawFlags)
141 	{
142 		if (m_dynamicsWorld)
143 		{
144 			if (m_dynamicsWorld->getDebugDrawer())
145 			{
146 				m_dynamicsWorld->getDebugDrawer()->setDebugMode(debugDrawFlags);
147 			}
148 			m_dynamicsWorld->debugDrawWorld();
149 		}
150 	}
151 
keyboardCallbackCommonRigidBodyBase152 	virtual bool keyboardCallback(int key, int state)
153 	{
154 		if ((key == B3G_F3) && state && m_dynamicsWorld)
155 		{
156 			btDefaultSerializer* serializer = new btDefaultSerializer();
157 			m_dynamicsWorld->serialize(serializer);
158 
159 			FILE* file = fopen("testFile.bullet", "wb");
160 			fwrite(serializer->getBufferPointer(), serializer->getCurrentBufferSize(), 1, file);
161 			fclose(file);
162 			//b3Printf("btDefaultSerializer wrote testFile.bullet");
163 			delete serializer;
164 			return true;
165 		}
166 		return false;  //don't handle this key
167 	}
168 
getRayToCommonRigidBodyBase169 	btVector3 getRayTo(int x, int y)
170 	{
171 		CommonRenderInterface* renderer = m_guiHelper->getRenderInterface();
172 
173 		if (!renderer)
174 		{
175 			btAssert(0);
176 			return btVector3(0, 0, 0);
177 		}
178 
179 		float top = 1.f;
180 		float bottom = -1.f;
181 		float nearPlane = 1.f;
182 		float tanFov = (top - bottom) * 0.5f / nearPlane;
183 		float fov = btScalar(2.0) * btAtan(tanFov);
184 
185 		btVector3 camPos, camTarget;
186 
187 		renderer->getActiveCamera()->getCameraPosition(camPos);
188 		renderer->getActiveCamera()->getCameraTargetPosition(camTarget);
189 
190 		btVector3 rayFrom = camPos;
191 		btVector3 rayForward = (camTarget - camPos);
192 		rayForward.normalize();
193 		float farPlane = 10000.f;
194 		rayForward *= farPlane;
195 
196 		btVector3 rightOffset;
197 		btVector3 cameraUp = btVector3(0, 0, 0);
198 		cameraUp[m_guiHelper->getAppInterface()->getUpAxis()] = 1;
199 
200 		btVector3 vertical = cameraUp;
201 
202 		btVector3 hor;
203 		hor = rayForward.cross(vertical);
204 		hor.safeNormalize();
205 		vertical = hor.cross(rayForward);
206 		vertical.safeNormalize();
207 
208 		float tanfov = tanf(0.5f * fov);
209 
210 		hor *= 2.f * farPlane * tanfov;
211 		vertical *= 2.f * farPlane * tanfov;
212 
213 		btScalar aspect;
214 		float width = float(renderer->getScreenWidth());
215 		float height = float(renderer->getScreenHeight());
216 
217 		aspect = width / height;
218 
219 		hor *= aspect;
220 
221 		btVector3 rayToCenter = rayFrom + rayForward;
222 		btVector3 dHor = hor * 1.f / width;
223 		btVector3 dVert = vertical * 1.f / height;
224 
225 		btVector3 rayTo = rayToCenter - 0.5f * hor + 0.5f * vertical;
226 		rayTo += btScalar(x) * dHor;
227 		rayTo -= btScalar(y) * dVert;
228 		return rayTo;
229 	}
230 
mouseMoveCallbackCommonRigidBodyBase231 	virtual bool mouseMoveCallback(float x, float y)
232 	{
233 		CommonRenderInterface* renderer = m_guiHelper->getRenderInterface();
234 
235 		if (!renderer)
236 		{
237 			btAssert(0);
238 			return false;
239 		}
240 
241 		btVector3 rayTo = getRayTo(int(x), int(y));
242 		btVector3 rayFrom;
243 		renderer->getActiveCamera()->getCameraPosition(rayFrom);
244 		movePickedBody(rayFrom, rayTo);
245 
246 		return false;
247 	}
248 
mouseButtonCallbackCommonRigidBodyBase249 	virtual bool mouseButtonCallback(int button, int state, float x, float y)
250 	{
251 		CommonRenderInterface* renderer = m_guiHelper->getRenderInterface();
252 
253 		if (!renderer)
254 		{
255 			btAssert(0);
256 			return false;
257 		}
258 
259 		CommonWindowInterface* window = m_guiHelper->getAppInterface()->m_window;
260 
261 #if 0
262 		if (window->isModifierKeyPressed(B3G_ALT))
263 		{
264 			printf("ALT pressed\n");
265 		} else
266 		{
267 			printf("NO ALT pressed\n");
268 		}
269 
270 		if (window->isModifierKeyPressed(B3G_SHIFT))
271 		{
272 			printf("SHIFT pressed\n");
273 		} else
274 		{
275 			printf("NO SHIFT pressed\n");
276 		}
277 
278 		if (window->isModifierKeyPressed(B3G_CONTROL))
279 		{
280 			printf("CONTROL pressed\n");
281 		} else
282 		{
283 			printf("NO CONTROL pressed\n");
284 		}
285 #endif
286 
287 		if (state == 1)
288 		{
289 			if (button == 0 && (!window->isModifierKeyPressed(B3G_ALT) && !window->isModifierKeyPressed(B3G_CONTROL)))
290 			{
291 				btVector3 camPos;
292 				renderer->getActiveCamera()->getCameraPosition(camPos);
293 
294 				btVector3 rayFrom = camPos;
295 				btVector3 rayTo = getRayTo(int(x), int(y));
296 
297 				pickBody(rayFrom, rayTo);
298 			}
299 		}
300 		else
301 		{
302 			if (button == 0)
303 			{
304 				removePickingConstraint();
305 				//remove p2p
306 			}
307 		}
308 
309 		//printf("button=%d, state=%d\n",button,state);
310 		return false;
311 	}
312 
pickBodyCommonRigidBodyBase313 	virtual bool pickBody(const btVector3& rayFromWorld, const btVector3& rayToWorld)
314 	{
315 		if (m_dynamicsWorld == 0)
316 			return false;
317 
318 		btCollisionWorld::ClosestRayResultCallback rayCallback(rayFromWorld, rayToWorld);
319 
320 		rayCallback.m_flags |= btTriangleRaycastCallback::kF_UseGjkConvexCastRaytest;
321 		m_dynamicsWorld->rayTest(rayFromWorld, rayToWorld, rayCallback);
322 		if (rayCallback.hasHit())
323 		{
324 			btVector3 pickPos = rayCallback.m_hitPointWorld;
325 			btRigidBody* body = (btRigidBody*)btRigidBody::upcast(rayCallback.m_collisionObject);
326 			if (body)
327 			{
328 				//other exclusions?
329 				if (!(body->isStaticObject() || body->isKinematicObject()))
330 				{
331 					m_pickedBody = body;
332 					m_savedState = m_pickedBody->getActivationState();
333 					m_pickedBody->setActivationState(DISABLE_DEACTIVATION);
334 					//printf("pickPos=%f,%f,%f\n",pickPos.getX(),pickPos.getY(),pickPos.getZ());
335 					btVector3 localPivot = body->getCenterOfMassTransform().inverse() * pickPos;
336 					btPoint2PointConstraint* p2p = new btPoint2PointConstraint(*body, localPivot);
337 					m_dynamicsWorld->addConstraint(p2p, true);
338 					m_pickedConstraint = p2p;
339 					btScalar mousePickClamping = 30.f;
340 					p2p->m_setting.m_impulseClamp = mousePickClamping;
341 					//very weak constraint for picking
342 					p2p->m_setting.m_tau = 0.001f;
343 				}
344 			}
345 
346 			//					pickObject(pickPos, rayCallback.m_collisionObject);
347 			m_oldPickingPos = rayToWorld;
348 			m_hitPos = pickPos;
349 			m_oldPickingDist = (pickPos - rayFromWorld).length();
350 			//					printf("hit !\n");
351 			//add p2p
352 		}
353 		return false;
354 	}
movePickedBodyCommonRigidBodyBase355 	virtual bool movePickedBody(const btVector3& rayFromWorld, const btVector3& rayToWorld)
356 	{
357 		if (m_pickedBody && m_pickedConstraint)
358 		{
359 			btPoint2PointConstraint* pickCon = static_cast<btPoint2PointConstraint*>(m_pickedConstraint);
360 			if (pickCon)
361 			{
362 				//keep it at the same picking distance
363 
364 				btVector3 newPivotB;
365 
366 				btVector3 dir = rayToWorld - rayFromWorld;
367 				dir.normalize();
368 				dir *= m_oldPickingDist;
369 
370 				newPivotB = rayFromWorld + dir;
371 				pickCon->setPivotB(newPivotB);
372 				return true;
373 			}
374 		}
375 		return false;
376 	}
removePickingConstraintCommonRigidBodyBase377 	virtual void removePickingConstraint()
378 	{
379 		if (m_pickedConstraint)
380 		{
381 			m_pickedBody->forceActivationState(m_savedState);
382 			m_pickedBody->activate();
383 			m_dynamicsWorld->removeConstraint(m_pickedConstraint);
384 			delete m_pickedConstraint;
385 			m_pickedConstraint = 0;
386 			m_pickedBody = 0;
387 		}
388 	}
389 
createBoxShapeCommonRigidBodyBase390 	btBoxShape* createBoxShape(const btVector3& halfExtents)
391 	{
392 		btBoxShape* box = new btBoxShape(halfExtents);
393 		return box;
394 	}
395 
deleteRigidBodyCommonRigidBodyBase396 	void deleteRigidBody(btRigidBody* body)
397 	{
398 		int graphicsUid = body->getUserIndex();
399 		m_guiHelper->removeGraphicsInstance(graphicsUid);
400 
401 		m_dynamicsWorld->removeRigidBody(body);
402 		btMotionState* ms = body->getMotionState();
403 		delete body;
404 		delete ms;
405 	}
406 
407 	btRigidBody* createRigidBody(float mass, const btTransform& startTransform, btCollisionShape* shape, const btVector4& color = btVector4(1, 0, 0, 1))
408 	{
409 		btAssert((!shape || shape->getShapeType() != INVALID_SHAPE_PROXYTYPE));
410 
411 		//rigidbody is dynamic if and only if mass is non zero, otherwise static
412 		bool isDynamic = (mass != 0.f);
413 
414 		btVector3 localInertia(0, 0, 0);
415 		if (isDynamic)
416 			shape->calculateLocalInertia(mass, localInertia);
417 
418 			//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
419 
420 #define USE_MOTIONSTATE 1
421 #ifdef USE_MOTIONSTATE
422 		btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
423 
424 		btRigidBody::btRigidBodyConstructionInfo cInfo(mass, myMotionState, shape, localInertia);
425 
426 		btRigidBody* body = new btRigidBody(cInfo);
427 		//body->setContactProcessingThreshold(m_defaultContactProcessingThreshold);
428 
429 #else
430 		btRigidBody* body = new btRigidBody(mass, 0, shape, localInertia);
431 		body->setWorldTransform(startTransform);
432 #endif  //
433 
434 		body->setUserIndex(-1);
435 		m_dynamicsWorld->addRigidBody(body);
436 		return body;
437 	}
438 
renderSceneCommonRigidBodyBase439 	virtual void renderScene()
440 	{
441 		if (m_dynamicsWorld)
442 		{
443 			{
444 				m_guiHelper->syncPhysicsToGraphics(m_dynamicsWorld);
445 			}
446 
447 			{
448 				m_guiHelper->render(m_dynamicsWorld);
449 			}
450 		}
451 	}
452 };
453 
454 #endif  //COMMON_RIGID_BODY_SETUP_H
455