1 #include "Dof6ConstraintTutorial.h"
2 
3 #include "btBulletDynamicsCommon.h"
4 #include "BulletDynamics/ConstraintSolver/btNNCGConstraintSolver.h"
5 #include "BulletDynamics/MLCPSolvers/btMLCPSolver.h"
6 #include "BulletDynamics/MLCPSolvers/btSolveProjectedGaussSeidel.h"
7 #include "BulletDynamics/MLCPSolvers/btLemkeSolver.h"
8 #include "BulletDynamics/MLCPSolvers/btDantzigSolver.h"
9 #include "../RenderingExamples/TimeSeriesCanvas.h"
10 
11 #include "BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h"
12 
13 #ifndef M_PI
14 #define M_PI 3.14159265358979323846
15 #endif
16 
17 #ifndef M_PI_2
18 #define M_PI_2 1.57079632679489661923
19 #endif
20 
21 #ifndef M_PI_4
22 #define M_PI_4 0.785398163397448309616
23 #endif
24 
25 extern float g_additionalBodyMass;
26 
27 //comment this out to compare with original spring constraint
28 #define USE_6DOF2
29 #ifdef USE_6DOF2
30 #define CONSTRAINT_TYPE btGeneric6DofSpring2Constraint
31 #define EXTRAPARAMS
32 #else
33 #define CONSTRAINT_TYPE btGeneric6DofSpringConstraint
34 #define EXTRAPARAMS , true
35 #endif
36 
37 #include "../CommonInterfaces/CommonRigidBodyBase.h"
38 
39 struct Dof6ConstraintTutorial : public CommonRigidBodyBase
40 {
41 	struct Dof6ConstraintTutorialInternalData* m_data;
42 
43 	Dof6ConstraintTutorial(struct GUIHelperInterface* helper);
44 	virtual ~Dof6ConstraintTutorial();
45 	virtual void initPhysics();
46 
47 	virtual void stepSimulation(float deltaTime);
48 
49 	void animate();
50 
resetCameraDof6ConstraintTutorial51 	virtual void resetCamera()
52 	{
53 		float dist = 5;
54 		float pitch = -35;
55 		float yaw = 722;
56 		float targetPos[3] = {4, 2, -11};
57 		m_guiHelper->resetCamera(dist, yaw, pitch, targetPos[0], targetPos[1], targetPos[2]);
58 	}
59 };
60 
61 struct Dof6ConstraintTutorialInternalData
62 {
63 	btRigidBody* m_TranslateSpringBody;
64 	btRigidBody* m_TranslateSpringBody2;
65 	btRigidBody* m_RotateSpringBody;
66 	btRigidBody* m_RotateSpringBody2;
67 	btRigidBody* m_BouncingTranslateBody;
68 	btRigidBody* m_MotorBody;
69 	btRigidBody* m_ServoMotorBody;
70 	btRigidBody* m_ChainLeftBody;
71 	btRigidBody* m_ChainRightBody;
72 	CONSTRAINT_TYPE* m_ServoMotorConstraint;
73 	CONSTRAINT_TYPE* m_ChainLeftConstraint;
74 	CONSTRAINT_TYPE* m_ChainRightConstraint;
75 
76 	TimeSeriesCanvas* m_timeSeriesCanvas;
77 
78 	float mDt;
79 
80 	unsigned int frameID;
Dof6ConstraintTutorialInternalDataDof6ConstraintTutorialInternalData81 	Dof6ConstraintTutorialInternalData()
82 		: mDt(1. / 60.), frameID(0)
83 	{
84 	}
85 };
86 
Dof6ConstraintTutorial(struct GUIHelperInterface * helper)87 Dof6ConstraintTutorial::Dof6ConstraintTutorial(struct GUIHelperInterface* helper)
88 	: CommonRigidBodyBase(helper)
89 {
90 	m_data = new Dof6ConstraintTutorialInternalData;
91 	m_data->m_timeSeriesCanvas = new TimeSeriesCanvas(helper->get2dCanvasInterface(), 256, 256, "Position and Velocity");
92 	m_data->m_timeSeriesCanvas->setupTimeSeries(20, 100, 0);
93 	m_data->m_timeSeriesCanvas->addDataSource("X position (m)", 255, 0, 0);
94 	m_data->m_timeSeriesCanvas->addDataSource("X velocity (m/s)", 0, 0, 255);
95 	m_data->m_timeSeriesCanvas->addDataSource("dX/dt (m/s)", 0, 0, 0);
96 }
~Dof6ConstraintTutorial()97 Dof6ConstraintTutorial::~Dof6ConstraintTutorial()
98 {
99 	delete m_data->m_timeSeriesCanvas;
100 	m_data->m_timeSeriesCanvas = 0;
101 	exitPhysics();
102 	delete m_data;
103 }
initPhysics()104 void Dof6ConstraintTutorial::initPhysics()
105 {
106 	// Setup the basic world
107 
108 	m_guiHelper->setUpAxis(1);
109 
110 	m_collisionConfiguration = new btDefaultCollisionConfiguration();
111 	m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
112 	btVector3 worldAabbMin(-10000, -10000, -10000);
113 	btVector3 worldAabbMax(10000, 10000, 10000);
114 	m_broadphase = new btAxisSweep3(worldAabbMin, worldAabbMax);
115 
116 	/////// uncomment the corresponding line to test a solver.
117 	//m_solver = new btSequentialImpulseConstraintSolver;
118 	m_solver = new btNNCGConstraintSolver;
119 	//m_solver = new btMLCPSolver(new btSolveProjectedGaussSeidel());
120 	//m_solver = new btMLCPSolver(new btDantzigSolver());
121 	//m_solver = new btMLCPSolver(new btLemkeSolver());
122 
123 	m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration);
124 	m_dynamicsWorld->getDispatchInfo().m_useContinuous = true;
125 	m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld);
126 
127 	m_dynamicsWorld->setGravity(btVector3(0, 0, 0));
128 
129 	// Setup a big ground box
130 	if (0)
131 	{
132 		btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(200.), btScalar(5.), btScalar(200.)));
133 		btTransform groundTransform;
134 		groundTransform.setIdentity();
135 		groundTransform.setOrigin(btVector3(0, -10, 0));
136 #define CREATE_GROUND_COLLISION_OBJECT 1
137 #ifdef CREATE_GROUND_COLLISION_OBJECT
138 		btCollisionObject* fixedGround = new btCollisionObject();
139 		fixedGround->setCollisionShape(groundShape);
140 		fixedGround->setWorldTransform(groundTransform);
141 		m_dynamicsWorld->addCollisionObject(fixedGround);
142 #else
143 		localCreateRigidBody(btScalar(0.), groundTransform, groundShape);
144 #endif  //CREATE_GROUND_COLLISION_OBJECT
145 	}
146 
147 	m_dynamicsWorld->getSolverInfo().m_numIterations = 100;
148 
149 	btCollisionShape* shape;
150 	btVector3 localInertia(0, 0, 0);
151 	btDefaultMotionState* motionState;
152 	btTransform bodyTransform;
153 	btScalar mass;
154 	btTransform localA;
155 	btTransform localB;
156 	CONSTRAINT_TYPE* constraint;
157 
158 	//static body centered in the origo
159 	mass = 0.0;
160 	shape = new btBoxShape(btVector3(0.5, 0.5, 0.5));
161 	localInertia = btVector3(0, 0, 0);
162 	bodyTransform.setIdentity();
163 	motionState = new btDefaultMotionState(bodyTransform);
164 	btRigidBody* staticBody = new btRigidBody(mass, motionState, shape, localInertia);
165 	m_dynamicsWorld->addRigidBody(staticBody);
166 
167 	/////////// box with undamped translate spring attached to static body
168 	/////////// the box should oscillate left-to-right forever
169 	{
170 		mass = 1.0;
171 		shape = new btBoxShape(btVector3(0.5, 0.5, 0.5));
172 		shape->calculateLocalInertia(mass, localInertia);
173 		bodyTransform.setIdentity();
174 		bodyTransform.setOrigin(btVector3(-2, 0, -5));
175 		motionState = new btDefaultMotionState(bodyTransform);
176 		m_data->m_TranslateSpringBody = new btRigidBody(mass, motionState, shape, localInertia);
177 		m_data->m_TranslateSpringBody->setActivationState(DISABLE_DEACTIVATION);
178 		m_dynamicsWorld->addRigidBody(m_data->m_TranslateSpringBody);
179 		localA.setIdentity();
180 		localA.getOrigin() = btVector3(0, 0, -5);
181 		localB.setIdentity();
182 		constraint = new CONSTRAINT_TYPE(*staticBody, *m_data->m_TranslateSpringBody, localA, localB EXTRAPARAMS);
183 		constraint->setLimit(0, 1, -1);
184 		constraint->setLimit(1, 0, 0);
185 		constraint->setLimit(2, 0, 0);
186 		constraint->setLimit(3, 0, 0);
187 		constraint->setLimit(4, 0, 0);
188 		constraint->setLimit(5, 0, 0);
189 		constraint->enableSpring(0, true);
190 		constraint->setStiffness(0, 100);
191 #ifdef USE_6DOF2
192 		constraint->setDamping(0, 0);
193 #else
194 		constraint->setDamping(0, 1);
195 #endif
196 		constraint->setEquilibriumPoint(0, 0);
197 		constraint->setDbgDrawSize(btScalar(2.f));
198 		m_dynamicsWorld->addConstraint(constraint, true);
199 	}
200 #if 0
201 /////////// box with rotate spring, attached to static body
202 /////////// box should swing (rotate) left-to-right forever
203 		{
204 			mass = 1.0;
205 			shape= new btBoxShape(btVector3(0.5,0.5,0.5));
206 			shape->calculateLocalInertia(mass,localInertia);
207 			bodyTransform.setIdentity();
208 			bodyTransform.getBasis().setEulerZYX(0,0,M_PI_2);
209 			motionState = new btDefaultMotionState(bodyTransform);
210 			m_data->m_RotateSpringBody = new btRigidBody(mass,motionState,shape,localInertia);
211 			m_data->m_RotateSpringBody->setActivationState(DISABLE_DEACTIVATION);
212 			m_dynamicsWorld->addRigidBody(m_data->m_RotateSpringBody);
213 			localA.setIdentity();localA.getOrigin() = btVector3(0,0,0);
214 			localB.setIdentity();localB.setOrigin(btVector3(0,0.5,0));
215 			constraint =  new CONSTRAINT_TYPE(*staticBody, *m_data->m_RotateSpringBody, localA, localB EXTRAPARAMS);
216 			constraint->setLimit(0, 0, 0);
217 			constraint->setLimit(1, 0, 0);
218 			constraint->setLimit(2, 0, 0);
219 			constraint->setLimit(3, 0, 0);
220 			constraint->setLimit(4, 0, 0);
221 			constraint->setLimit(5, 1, -1);
222 			constraint->enableSpring(5, true);
223 			constraint->setStiffness(5, 100);
224 #ifdef USE_6DOF2
225 			constraint->setDamping(5, 0);
226 #else
227 			constraint->setDamping(5, 1);
228 #endif
229 			constraint->setEquilibriumPoint(0, 0);
230 			constraint->setDbgDrawSize(btScalar(2.f));
231 			m_dynamicsWorld->addConstraint(constraint, true);
232 		}
233 
234 /////////// box with bouncing constraint, translation is bounced at the positive x limit, but not at the negative limit
235 /////////// bouncing can not be set independently at low and high limits, so two constraints will be created: one that defines the low (non bouncing) limit, and one that defines the high (bouncing) limit
236 /////////// the box should move to the left (as an impulse will be applied to it periodically) until it reaches its limit, then bounce back
237 		{
238 			mass = 1.0;
239 			shape= new btBoxShape(btVector3(0.5,0.5,0.5));
240 			shape->calculateLocalInertia(mass,localInertia);
241 			bodyTransform.setIdentity();
242 			bodyTransform.setOrigin(btVector3(0,0,-3));
243 			motionState = new btDefaultMotionState(bodyTransform);
244 			m_data->m_BouncingTranslateBody = new btRigidBody(mass,motionState,shape,localInertia);
245 			m_data->m_BouncingTranslateBody->setActivationState(DISABLE_DEACTIVATION);
246 			m_data->m_BouncingTranslateBody->setDeactivationTime(btScalar(20000000));
247 			m_dynamicsWorld->addRigidBody(m_data->m_BouncingTranslateBody);
248 			localA.setIdentity();localA.getOrigin() = btVector3(0,0,0);
249 			localB.setIdentity();
250 			constraint =  new CONSTRAINT_TYPE(*staticBody, *m_data->m_BouncingTranslateBody, localA, localB EXTRAPARAMS);
251 			constraint->setLimit(0, -2, SIMD_INFINITY);
252 			constraint->setLimit(1, 0, 0);
253 			constraint->setLimit(2, -3, -3);
254 			constraint->setLimit(3, 0, 0);
255 			constraint->setLimit(4, 0, 0);
256 			constraint->setLimit(5, 0, 0);
257 #ifdef USE_6DOF2
258 			constraint->setBounce(0,0);
259 #else  //bounce is named restitution in 6dofspring, but not implemented for translational limit motor, so the following line has no effect
260 			constraint->getTranslationalLimitMotor()->m_restitution = 0.0;
261 #endif
262 			constraint->setParam(BT_CONSTRAINT_STOP_ERP,0.995,0);
263 			constraint->setParam(BT_CONSTRAINT_STOP_CFM,0.0,0);
264 			constraint->setDbgDrawSize(btScalar(2.f));
265 			m_dynamicsWorld->addConstraint(constraint, true);
266 			constraint =  new CONSTRAINT_TYPE(*staticBody, *m_data->m_BouncingTranslateBody, localA, localB EXTRAPARAMS);
267 			constraint->setLimit(0, -SIMD_INFINITY, 2);
268 			constraint->setLimit(1, 0, 0);
269 			constraint->setLimit(2, -3, -3);
270 			constraint->setLimit(3, 0, 0);
271 			constraint->setLimit(4, 0, 0);
272 			constraint->setLimit(5, 0, 0);
273 #ifdef USE_6DOF2
274 			constraint->setBounce(0,1);
275 #else  //bounce is named restitution in 6dofspring, but not implemented for translational limit motor, so the following line has no effect
276 			constraint->getTranslationalLimitMotor()->m_restitution = 1.0;
277 #endif
278 			constraint->setParam(BT_CONSTRAINT_STOP_ERP,0.995,0);
279 			constraint->setParam(BT_CONSTRAINT_STOP_CFM,0.0,0);
280 			constraint->setDbgDrawSize(btScalar(2.f));
281 			m_dynamicsWorld->addConstraint(constraint, true);
282 		}
283 
284 /////////// box with rotational motor, attached to static body
285 /////////// the box should rotate around the y axis
286 		{
287 			mass = 1.0;
288 			shape= new btBoxShape(btVector3(0.5,0.5,0.5));
289 			shape->calculateLocalInertia(mass,localInertia);
290 			bodyTransform.setIdentity();
291 			bodyTransform.setOrigin(btVector3(4,0,0));
292 			motionState = new btDefaultMotionState(bodyTransform);
293 			m_data->m_MotorBody = new btRigidBody(mass,motionState,shape,localInertia);
294 			m_data->m_MotorBody->setActivationState(DISABLE_DEACTIVATION);
295 			m_dynamicsWorld->addRigidBody(m_data->m_MotorBody);
296 			localA.setIdentity();localA.getOrigin() = btVector3(4,0,0);
297 			localB.setIdentity();
298 			constraint =  new CONSTRAINT_TYPE(*staticBody, *m_data->m_MotorBody, localA, localB EXTRAPARAMS);
299 			constraint->setLimit(0, 0, 0);
300 			constraint->setLimit(1, 0, 0);
301 			constraint->setLimit(2, 0, 0);
302 			constraint->setLimit(3, 0, 0);
303 			constraint->setLimit(4, 0, 0);
304 			constraint->setLimit(5, 1,-1);
305 #ifdef USE_6DOF2
306 			constraint->enableMotor(5,true);
307 			constraint->setTargetVelocity(5,3.f);
308 			constraint->setMaxMotorForce(5,600.f);
309 #else
310 			constraint->getRotationalLimitMotor(2)->m_enableMotor = true;
311 			constraint->getRotationalLimitMotor(2)->m_targetVelocity = 3.f;
312 			constraint->getRotationalLimitMotor(2)->m_maxMotorForce = 600.f;
313 #endif
314 			constraint->setDbgDrawSize(btScalar(2.f));
315 			m_dynamicsWorld->addConstraint(constraint, true);
316 		}
317 
318 /////////// box with rotational servo motor, attached to static body
319 /////////// the box should rotate around the y axis until it reaches its target
320 /////////// the target will be negated periodically
321 		{
322 			mass = 1.0;
323 			shape= new btBoxShape(btVector3(0.5,0.5,0.5));
324 			shape->calculateLocalInertia(mass,localInertia);
325 			bodyTransform.setIdentity();
326 			bodyTransform.setOrigin(btVector3(7,0,0));
327 			motionState = new btDefaultMotionState(bodyTransform);
328 			m_data->m_ServoMotorBody = new btRigidBody(mass,motionState,shape,localInertia);
329 			m_data->m_ServoMotorBody->setActivationState(DISABLE_DEACTIVATION);
330 			m_dynamicsWorld->addRigidBody(m_data->m_ServoMotorBody);
331 			localA.setIdentity();localA.getOrigin() = btVector3(7,0,0);
332 			localB.setIdentity();
333 			constraint =  new CONSTRAINT_TYPE(*staticBody, *m_data->m_ServoMotorBody, localA, localB EXTRAPARAMS);
334 			constraint->setLimit(0, 0, 0);
335 			constraint->setLimit(1, 0, 0);
336 			constraint->setLimit(2, 0, 0);
337 			constraint->setLimit(3, 0, 0);
338 			constraint->setLimit(4, 0, 0);
339 			constraint->setLimit(5, 1,-1);
340 #ifdef USE_6DOF2
341 			constraint->enableMotor(5,true);
342 			constraint->setTargetVelocity(5,3.f);
343 			constraint->setMaxMotorForce(5,600.f);
344 			constraint->setServo(5,true);
345 			constraint->setServoTarget(5, M_PI_2);
346 #else
347 			constraint->getRotationalLimitMotor(2)->m_enableMotor = true;
348 			constraint->getRotationalLimitMotor(2)->m_targetVelocity = 3.f;
349 			constraint->getRotationalLimitMotor(2)->m_maxMotorForce = 600.f;
350 			//servo motor is not implemented in 6dofspring constraint
351 #endif
352 			constraint->setDbgDrawSize(btScalar(2.f));
353 			m_dynamicsWorld->addConstraint(constraint, true);
354 			m_data->m_ServoMotorConstraint = constraint;
355 		}
356 
357 ////////// chain of boxes linked together with fully limited rotational and translational constraints
358 ////////// the chain will be pulled to the left and to the right periodically. They should strictly stick together.
359 		{
360 			btScalar limitConstraintStrength = 0.6;
361 			int bodycount = 10;
362 			btRigidBody* prevBody = 0;
363 			for(int i = 0; i < bodycount; ++i)
364 			{
365 				mass = 1.0;
366 				shape= new btBoxShape(btVector3(0.5,0.5,0.5));
367 				shape->calculateLocalInertia(mass,localInertia);
368 				bodyTransform.setIdentity();
369 				bodyTransform.setOrigin(btVector3(- i,0,3));
370 				motionState = new btDefaultMotionState(bodyTransform);
371 				btRigidBody* body = new btRigidBody(mass,motionState,shape,localInertia);
372 				body->setActivationState(DISABLE_DEACTIVATION);
373 				m_dynamicsWorld->addRigidBody(body);
374 				if(prevBody != 0)
375 				{
376 					localB.setIdentity();
377 					localB.setOrigin(btVector3(0.5,0,0));
378 					btTransform localA;
379 					localA.setIdentity();
380 					localA.setOrigin(btVector3(-0.5,0,0));
381 					CONSTRAINT_TYPE* constraint =  new CONSTRAINT_TYPE(*prevBody, *body, localA, localB EXTRAPARAMS);
382 					constraint->setLimit(0, -0.01, 0.01);
383 					constraint->setLimit(1, 0, 0);
384 					constraint->setLimit(2, 0, 0);
385 					constraint->setLimit(3, 0, 0);
386 					constraint->setLimit(4, 0, 0);
387 					constraint->setLimit(5, 0, 0);
388 					for(int a = 0; a < 6; ++a)
389 					{
390 						constraint->setParam(BT_CONSTRAINT_STOP_ERP,0.9,a);
391 						constraint->setParam(BT_CONSTRAINT_STOP_CFM,0.0,a);
392 					}
393 					constraint->setDbgDrawSize(btScalar(1.f));
394 					m_dynamicsWorld->addConstraint(constraint, true);
395 
396 					if(i < bodycount - 1)
397 					{
398 						localA.setIdentity();localA.getOrigin() = btVector3(0,0,3);
399 						localB.setIdentity();
400 						CONSTRAINT_TYPE* constraintZY =  new CONSTRAINT_TYPE(*staticBody, *body, localA, localB EXTRAPARAMS);
401 						constraintZY->setLimit(0, 1, -1);
402 						constraintZY->setDbgDrawSize(btScalar(1.f));
403 						m_dynamicsWorld->addConstraint(constraintZY, true);
404 
405 					}
406 				}
407 				else
408 				{
409 					localA.setIdentity();localA.getOrigin() = btVector3(bodycount,0,3);
410 					localB.setIdentity();
411 					localB.setOrigin(btVector3(0,0,0));
412 					m_data->m_ChainLeftBody = body;
413 					m_data->m_ChainLeftConstraint = new CONSTRAINT_TYPE(*staticBody, *body, localA, localB EXTRAPARAMS);
414 					m_data->m_ChainLeftConstraint->setLimit(3,0,0);
415 					m_data->m_ChainLeftConstraint->setLimit(4,0,0);
416 					m_data->m_ChainLeftConstraint->setLimit(5,0,0);
417 					for(int a = 0; a < 6; ++a)
418 					{
419 						m_data->m_ChainLeftConstraint->setParam(BT_CONSTRAINT_STOP_ERP,limitConstraintStrength,a);
420 						m_data->m_ChainLeftConstraint->setParam(BT_CONSTRAINT_STOP_CFM,0.0,a);
421 					}
422 					m_data->m_ChainLeftConstraint->setDbgDrawSize(btScalar(1.f));
423 					m_dynamicsWorld->addConstraint(m_data->m_ChainLeftConstraint, true);
424 				}
425 				prevBody = body;
426 			}
427 			m_data->m_ChainRightBody = prevBody;
428 			localA.setIdentity();localA.getOrigin() = btVector3(-bodycount,0,3);
429 			localB.setIdentity();
430 			localB.setOrigin(btVector3(0,0,0));
431 			m_data->m_ChainRightConstraint = new CONSTRAINT_TYPE(*staticBody, *m_data->m_ChainRightBody, localA, localB EXTRAPARAMS);
432 			m_data->m_ChainRightConstraint->setLimit(3,0,0);
433 			m_data->m_ChainRightConstraint->setLimit(4,0,0);
434 			m_data->m_ChainRightConstraint->setLimit(5,0,0);
435 			for(int a = 0; a < 6; ++a)
436 			{
437 				m_data->m_ChainRightConstraint->setParam(BT_CONSTRAINT_STOP_ERP,limitConstraintStrength,a);
438 				m_data->m_ChainRightConstraint->setParam(BT_CONSTRAINT_STOP_CFM,0.0,a);
439 			}
440 		}
441 #endif
442 	m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld);
443 }
444 
animate()445 void Dof6ConstraintTutorial::animate()
446 {
447 /////// servo motor: flip its target periodically
448 #ifdef USE_6DOF2
449 	static float servoNextFrame = -1;
450 	//btScalar pos = m_data->m_ServoMotorConstraint->getRotationalLimitMotor(2)->m_currentPosition;
451 	//btScalar target = m_data->m_ServoMotorConstraint->getRotationalLimitMotor(2)->m_servoTarget;
452 	if (servoNextFrame < 0)
453 	{
454 		m_data->m_ServoMotorConstraint->getRotationalLimitMotor(2)->m_servoTarget *= -1;
455 		servoNextFrame = 3.0;
456 	}
457 	servoNextFrame -= m_data->mDt;
458 #endif
459 
460 	/////// constraint chain: pull the chain left and right periodically
461 	static float chainNextFrame = -1;
462 	static bool left = true;
463 	if (chainNextFrame < 0)
464 	{
465 		if (!left)
466 		{
467 			m_data->m_ChainRightBody->setActivationState(ACTIVE_TAG);
468 			m_dynamicsWorld->removeConstraint(m_data->m_ChainRightConstraint);
469 			m_data->m_ChainLeftConstraint->setDbgDrawSize(btScalar(2.f));
470 			m_dynamicsWorld->addConstraint(m_data->m_ChainLeftConstraint, true);
471 		}
472 		else
473 		{
474 			m_data->m_ChainLeftBody->setActivationState(ACTIVE_TAG);
475 			m_dynamicsWorld->removeConstraint(m_data->m_ChainLeftConstraint);
476 			m_data->m_ChainRightConstraint->setDbgDrawSize(btScalar(2.f));
477 			m_dynamicsWorld->addConstraint(m_data->m_ChainRightConstraint, true);
478 		}
479 		chainNextFrame = 3.0;
480 		left = !left;
481 	}
482 	chainNextFrame -= m_data->mDt;
483 
484 	/////// bouncing constraint: push the box periodically
485 	m_data->m_BouncingTranslateBody->setActivationState(ACTIVE_TAG);
486 	static float bounceNextFrame = -1;
487 	if (bounceNextFrame < 0)
488 	{
489 		m_data->m_BouncingTranslateBody->applyCentralImpulse(btVector3(10, 0, 0));
490 		bounceNextFrame = 3.0;
491 	}
492 	bounceNextFrame -= m_data->mDt;
493 
494 	m_data->frameID++;
495 }
496 
stepSimulation(float deltaTime)497 void Dof6ConstraintTutorial::stepSimulation(float deltaTime)
498 {
499 	//animate();
500 
501 	//float time = m_data->m_timeSeriesCanvas->getCurrentTime();
502 
503 	float prevPos = m_data->m_TranslateSpringBody->getWorldTransform().getOrigin().x();
504 	m_dynamicsWorld->stepSimulation(deltaTime);
505 	float xPos = m_data->m_TranslateSpringBody->getWorldTransform().getOrigin().x();
506 
507 	m_data->m_timeSeriesCanvas->insertDataAtCurrentTime(xPos, 0, true);
508 	m_data->m_timeSeriesCanvas->insertDataAtCurrentTime(m_data->m_TranslateSpringBody->getLinearVelocity().x(), 1, true);
509 
510 	if (deltaTime > 0)
511 	{
512 		m_data->m_timeSeriesCanvas->insertDataAtCurrentTime((xPos - prevPos) / deltaTime, 2, true);
513 	}
514 	prevPos = xPos;
515 	m_data->m_timeSeriesCanvas->nextTick();
516 }
517 
Dof6ConstraintTutorialCreateFunc(CommonExampleOptions & options)518 class CommonExampleInterface* Dof6ConstraintTutorialCreateFunc(CommonExampleOptions& options)
519 {
520 	return new Dof6ConstraintTutorial(options.m_guiHelper);
521 }
522