1 /*
2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2015 Google Inc. http://bulletphysics.org
4
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
10
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
14 */
15
16 #ifndef NN3D_WALKERS_TIME_WARP_BASE_H
17 #define NN3D_WALKERS_TIME_WARP_BASE_H
18
19 #include "btBulletDynamicsCommon.h"
20 #include "LinearMath/btVector3.h"
21 #include "LinearMath/btAlignedObjectArray.h"
22 #include "LinearMath/btQuickprof.h" // Use your own timer, this timer is only used as we lack another timer
23
24 #include "../CommonInterfaces/CommonRigidBodyBase.h"
25 #include "../CommonInterfaces/CommonParameterInterface.h"
26
27 //Solvers
28 #include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h"
29 #include "BulletDynamics/ConstraintSolver/btNNCGConstraintSolver.h"
30 #include "BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h"
31 #include "BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h"
32 #include "BulletDynamics/MLCPSolvers/btDantzigSolver.h"
33 #include "BulletDynamics/MLCPSolvers/btSolveProjectedGaussSeidel.h"
34 #include "BulletDynamics/MLCPSolvers/btLemkeSolver.h"
35 #include "BulletDynamics/MLCPSolvers/btMLCPSolver.h"
36
37 #include "../Utils/b3ERPCFMHelper.hpp" // ERP/CFM setting utils
38
39 static btScalar gSimulationSpeed = 1; // default simulation speed at startup
40
41 // the current simulation speeds to choose from (the slider will snap to those using a custom form of snapping)
42 namespace SimulationSpeeds
43 {
44 static double /*0*/ PAUSE = 0;
45 static double /*1*/ QUARTER_SPEED = 0.25;
46 static double /*2*/ HALF_SPEED = 0.5;
47 static double /*3*/ NORMAL_SPEED = 1;
48 static double /*4*/ DOUBLE_SPEED = 2;
49 static double /*5*/ QUADRUPLE_SPEED = 4;
50 static double /*6*/ DECUPLE_SPEED = 10;
51 static double /*7*/ CENTUPLE_SPEED = 100;
52 static double /*8*/ QUINCENTUPLE_SPEED = 500;
53 static double /*9*/ MILLITUPLE_SPEED = 1000;
54 static double /*0*/ MAX_SPEED = MILLITUPLE_SPEED;
55 static double /**/ NUM_SPEEDS = 10;
56 }; // namespace SimulationSpeeds
57
58 // add speeds from the namespace here
59 static double speeds[] = {
60 SimulationSpeeds::PAUSE,
61 SimulationSpeeds::QUARTER_SPEED, SimulationSpeeds::HALF_SPEED,
62 SimulationSpeeds::NORMAL_SPEED, SimulationSpeeds::DOUBLE_SPEED,
63 SimulationSpeeds::QUADRUPLE_SPEED, SimulationSpeeds::DECUPLE_SPEED,
64 SimulationSpeeds::CENTUPLE_SPEED, SimulationSpeeds::QUINCENTUPLE_SPEED,
65 SimulationSpeeds::MILLITUPLE_SPEED};
66
67 static btScalar gSolverIterations = 10; // default number of solver iterations for the iterative solvers
68
69 static bool gIsHeadless = false; // demo runs with graphics by default
70
71 static bool gChangeErpCfm = false; // flag to make recalculation of ERP/CFM
72
73 static int gMinSpeed = SimulationSpeeds::PAUSE; // the minimum simulation speed
74
75 static int gMaxSpeed = SimulationSpeeds::MAX_SPEED; // the maximum simulation speed
76
77 static bool gMaximumSpeed = false; // the demo does not try to achieve maximum stepping speed by default
78
79 static bool gInterpolate = false; // the demo does not use any bullet interpolated physics substeps
80
81 static bool useSplitImpulse = true; // split impulse fixes issues with restitution in Baumgarte stabilization
82 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7117&p=24631&hilit=Baumgarte#p24631
83 // disabling continuous collision detection can also fix issues with restitution, though CCD is disabled by default an only kicks in at higher speeds
84 // set CCD speed threshold and testing sphere radius per rigidbody (rb->setCCDSpeedThreshold())
85
86 // all supported solvers by bullet
87 enum SolverEnumType
88 {
89 SEQUENTIALIMPULSESOLVER = 0,
90 GAUSSSEIDELSOLVER = 1,
91 NNCGSOLVER = 2,
92 DANZIGSOLVER = 3,
93 LEMKESOLVER = 4,
94
95 NUM_SOLVERS = 6
96 };
97
98 // solvers can be changed by drop down menu
99 namespace SolverType
100 {
101 static char SEQUENTIALIMPULSESOLVER[] = "Sequential Impulse Solver";
102 static char GAUSSSEIDELSOLVER[] = "Gauss-Seidel Solver";
103 static char NNCGSOLVER[] = "NNCG Solver";
104 static char DANZIGSOLVER[] = "Danzig Solver";
105 static char LEMKESOLVER[] = "Lemke Solver";
106
107 }; // namespace SolverType
108
109 static const char* solverTypes[NUM_SOLVERS];
110
111 static SolverEnumType SOLVER_TYPE = SEQUENTIALIMPULSESOLVER; // You can switch the solver here
112
113 //TODO:s===
114 //TODO: Give specific explanations about solver values
115
116 /**
117 * Step size of the bullet physics simulator (solverAccuracy). Accuracy versus speed.
118 */
119 // Choose an appropriate number of steps per second for your needs
120 static btScalar gPhysicsStepsPerSecond = 60.0f; // Default number of steps
121 //static btScalar gPhysicsStepsPerSecond = 120.0f; // Double steps for more accuracy
122 //static btScalar gPhysicsStepsPerSecond = 240.0f; // For high accuracy
123 //static btScalar gPhysicsStepsPerSecond = 1000.0f; // Very high accuracy
124
125 // appropriate inverses for seconds and milliseconds
126 static double fixedPhysicsStepSizeSec = 1.0f / gPhysicsStepsPerSecond; // steps size in seconds
127 static double fixedPhysicsStepSizeMilli = 1000.0f / gPhysicsStepsPerSecond; // step size in milliseconds
128
129 static btScalar gApplicationFrequency = 60.0f; // number of internal application ticks per second
130 static int gApplicationTick = 1000.0f / gApplicationFrequency; //ms
131
132 static btScalar gFramesPerSecond = 30.0f; // number of frames per second
133
134 static btScalar gERPSpringK = 10;
135 static btScalar gERPDamperC = 1;
136
137 static btScalar gCFMSpringK = 10;
138 static btScalar gCFMDamperC = 1;
139 static btScalar gCFMSingularityAvoidance = 0;
140
141 //GUI related parameter changing helpers
142
twxChangePhysicsStepsPerSecond(float physicsStepsPerSecond,void *)143 inline void twxChangePhysicsStepsPerSecond(float physicsStepsPerSecond, void*)
144 { // function to change simulation physics steps per second
145 gPhysicsStepsPerSecond = physicsStepsPerSecond;
146 }
147
twxChangeFPS(float framesPerSecond,void *)148 inline void twxChangeFPS(float framesPerSecond, void*)
149 {
150 gFramesPerSecond = framesPerSecond;
151 }
152
twxChangeERPCFM(float notUsed,void *)153 inline void twxChangeERPCFM(float notUsed, void*)
154 { // function to change ERP/CFM appropriately
155 gChangeErpCfm = true;
156 }
157
changeSolver(int comboboxId,const char * item,void * userPointer)158 inline void changeSolver(int comboboxId, const char* item, void* userPointer)
159 { // function to change the solver
160 for (int i = 0; i < NUM_SOLVERS; i++)
161 {
162 if (strcmp(solverTypes[i], item) == 0)
163 { // if the strings are equal
164 SOLVER_TYPE = ((SolverEnumType)i);
165 b3Printf("=%s=\n Reset the simulation by double clicking it in the menu list.", item);
166 return;
167 }
168 }
169 b3Printf("No Change");
170 }
171
twxChangeSolverIterations(float notUsed,void * userPtr)172 inline void twxChangeSolverIterations(float notUsed, void* userPtr)
173 { // change the solver iterations
174 }
175
clampToCustomSpeedNotches(float speed,void *)176 inline void clampToCustomSpeedNotches(float speed, void*)
177 { // function to clamp to custom speed notches
178 double minSpeed = 0;
179 double minSpeedDist = SimulationSpeeds::MAX_SPEED;
180 for (int i = 0; i < SimulationSpeeds::NUM_SPEEDS; i++)
181 {
182 double speedDist = (speeds[i] - speed >= 0) ? speeds[i] - speed : speed - speeds[i]; // float absolute
183
184 if (minSpeedDist > speedDist)
185 {
186 minSpeedDist = speedDist;
187 minSpeed = speeds[i];
188 }
189 }
190 gSimulationSpeed = minSpeed;
191 }
192
switchInterpolated(int buttonId,bool buttonState,void * userPointer)193 inline void switchInterpolated(int buttonId, bool buttonState, void* userPointer)
194 { // toggle if interpolation steps are taken
195 gInterpolate = !gInterpolate;
196 // b3Printf("Interpolate substeps %s", gInterpolate?"on":"off");
197 }
198
switchHeadless(int buttonId,bool buttonState,void * userPointer)199 inline void switchHeadless(int buttonId, bool buttonState, void* userPointer)
200 { // toggle if the demo should run headless
201 gIsHeadless = !gIsHeadless;
202 // b3Printf("Run headless %s", gIsHeadless?"on":"off");
203 }
204
switchMaximumSpeed(int buttonId,bool buttonState,void * userPointer)205 inline void switchMaximumSpeed(int buttonId, bool buttonState, void* userPointer)
206 { // toggle it the demo should run as fast as possible
207 // b3Printf("Run maximum speed %s", gMaximumSpeed?"on":"off");
208 }
209
setApplicationTick(float frequency,void *)210 inline void setApplicationTick(float frequency, void*)
211 { // set internal application tick
212 gApplicationTick = 1000.0f / frequency;
213 }
214
215 /**
216 * @link: Gaffer on Games - Fix your timestep: http://gafferongames.com/game-physics/fix-your-timestep/
217 */
218 struct NN3DWalkersTimeWarpBase : public CommonRigidBodyBase
219 {
NN3DWalkersTimeWarpBaseNN3DWalkersTimeWarpBase220 NN3DWalkersTimeWarpBase(struct GUIHelperInterface* helper) : CommonRigidBodyBase(helper),
221 mPhysicsStepsPerSecondUpdated(false),
222 mFramesPerSecondUpdated(false),
223 mSolverIterationsUpdated(false)
224 {
225 // main frame timer initialization
226 mApplicationStart = mLoopTimer.getTimeMilliseconds(); /**!< Initialize when the application started running */
227 mInputClock = mApplicationStart; /**!< Initialize the last time the input was updated */
228 mPreviousModelIteration = mApplicationStart;
229 mThisModelIteration = mApplicationStart;
230 mApplicationRuntime = mThisModelIteration - mApplicationStart; /**!< Initialize the application runtime */
231
232 // sub frame time initializations
233 mGraphicsStart = mApplicationStart; /** !< Initialize the last graphics start */
234 mModelStart = mApplicationStart; /** !< Initialize the last model start */
235 mInputStart = mApplicationStart; /** !< Initialize the last input start */
236
237 mPhysicsStepStart = mApplicationStart; /**!< Initialize the physics step start */
238 mPhysicsStepEnd = mApplicationStart; /**!< Initialize the physics step end */
239
240 //durations
241 mLastGraphicsTick = 0;
242 mLastModelTick = 0;
243 mLastInputTick = 0;
244 mPhysicsTick = 0;
245
246 mInputDt = 0;
247 mModelAccumulator = 0;
248 mFrameTime = 0;
249
250 fpsTimeStamp = mLoopTimer.getTimeMilliseconds(); // to time the fps
251 fpsStep = 1000.0f / gFramesPerSecond;
252
253 // performance measurements for this demo
254 performanceTimestamp = 0;
255 performedTime = 0; // time the physics steps consumed
256 speedUpPrintTimeStamp = mLoopTimer.getTimeSeconds(); // timer to print the speed up periodically
257 mLoopTimer.reset();
258 }
259
~NN3DWalkersTimeWarpBaseNN3DWalkersTimeWarpBase260 ~NN3DWalkersTimeWarpBase()
261 {
262 }
263
initPhysicsNN3DWalkersTimeWarpBase264 void initPhysics()
265 { // initialize the demo
266
267 setupBasicParamInterface(); // setup adjustable sliders and buttons for parameters
268
269 m_guiHelper->setUpAxis(1); // Set Y axis as Up axis
270
271 createEmptyDynamicsWorld(); // create an empty dynamic world
272
273 m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld);
274 }
275
setupBasicParamInterfaceNN3DWalkersTimeWarpBase276 void setupBasicParamInterface()
277 { // setup the adjustable sliders and button for parameters
278
279 { // create a slider to adjust the simulation speed
280 // Force increase the simulation speed to run the simulation with the same accuracy but a higher speed
281 SliderParams slider("Simulation speed",
282 &gSimulationSpeed);
283 slider.m_minVal = gMinSpeed;
284 slider.m_maxVal = gMaxSpeed;
285 slider.m_callback = clampToCustomSpeedNotches;
286 slider.m_clampToNotches = false;
287 if (m_guiHelper->getParameterInterface())
288 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
289 slider);
290 }
291
292 { // create a button to switch to headless simulation
293 // This turns off the graphics update and therefore results in more time for the model update
294 ButtonParams button("Run headless", 0, true);
295 button.m_callback = switchHeadless;
296 if (m_guiHelper->getParameterInterface())
297 m_guiHelper->getParameterInterface()->registerButtonParameter(
298 button);
299 }
300
301 { // create a button to switch to maximum speed simulation (fully deterministic)
302 // Interesting to test the maximal achievable speed on this hardware
303 ButtonParams button("Run maximum speed", 0, true);
304 button.m_callback = switchMaximumSpeed;
305 if (m_guiHelper->getParameterInterface())
306 m_guiHelper->getParameterInterface()->registerButtonParameter(
307 button);
308 }
309
310 { // create a button to switch bullet to perform interpolated substeps to speed up simulation
311 // generally, interpolated steps are a good speed-up and should only be avoided if higher accuracy is needed (research purposes etc.)
312 ButtonParams button("Perform interpolated substeps", 0, true);
313 button.m_callback = switchInterpolated;
314 if (m_guiHelper->getParameterInterface())
315 m_guiHelper->getParameterInterface()->registerButtonParameter(
316 button);
317 }
318 }
319
setupAdvancedParamInterfaceNN3DWalkersTimeWarpBase320 void setupAdvancedParamInterface()
321 {
322 solverTypes[0] = SolverType::SEQUENTIALIMPULSESOLVER;
323 solverTypes[1] = SolverType::GAUSSSEIDELSOLVER;
324 solverTypes[2] = SolverType::NNCGSOLVER;
325 solverTypes[3] = SolverType::DANZIGSOLVER;
326 solverTypes[4] = SolverType::LEMKESOLVER;
327
328
329 {
330 ComboBoxParams comboParams;
331 comboParams.m_comboboxId = 0;
332 comboParams.m_numItems = NUM_SOLVERS;
333 comboParams.m_startItem = SOLVER_TYPE;
334 comboParams.m_callback = changeSolver;
335
336 comboParams.m_items = solverTypes;
337 m_guiHelper->getParameterInterface()->registerComboBox(comboParams);
338 }
339
340 { // create a slider to adjust the number of internal application ticks
341 // The set application tick should contain enough time to perform a full cycle of model update (physics and input)
342 // and view update (graphics) with average application load. The graphics and input update determine the remaining time
343 // for the physics update
344 SliderParams slider("Application Ticks",
345 &gApplicationFrequency);
346 slider.m_minVal = gMinSpeed;
347 slider.m_maxVal = gMaxSpeed;
348 slider.m_callback = setApplicationTick;
349 slider.m_clampToNotches = false;
350 if (m_guiHelper->getParameterInterface())
351 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
352 slider);
353 }
354
355 { // create a slider to adjust the number of physics steps per second
356 // The default number of steps is at 60, which is appropriate for most general simulations
357 // For simulations with higher complexity or if you experience undesired behavior, try increasing the number of steps per second
358 // Alternatively, try increasing the number of solver iterations if you experience jittering constraints due to non-converging solutions
359 SliderParams slider("Physics steps per second", &gPhysicsStepsPerSecond);
360 slider.m_minVal = 0;
361 slider.m_maxVal = 1000;
362 slider.m_callback = twxChangePhysicsStepsPerSecond;
363 slider.m_clampToNotches = false;
364 if (m_guiHelper->getParameterInterface())
365 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
366 slider);
367 }
368
369 { // create a slider to adjust the number of frames per second
370 SliderParams slider("Frames per second", &gFramesPerSecond);
371 slider.m_minVal = 0;
372 slider.m_maxVal = 200;
373 slider.m_callback = twxChangeFPS;
374 slider.m_clampToNotches = false;
375 if (m_guiHelper->getParameterInterface())
376 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
377 slider);
378 }
379
380 { // create a slider to adjust the number of solver iterations to converge to a solution
381 // more complex simulations might need a higher number of iterations to converge, it also
382 // depends on the type of solver.
383 SliderParams slider(
384 "Solver interations",
385 &gSolverIterations);
386 slider.m_minVal = 0;
387 slider.m_maxVal = 1000;
388 slider.m_callback = twxChangePhysicsStepsPerSecond;
389 slider.m_clampToIntegers = true;
390 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
391 slider);
392 }
393
394 // ERP/CFM sliders
395 // Advanced users: Check descriptions of ERP/CFM in BulletUtils.cpp
396
397 { // create a slider to adjust ERP Spring k constant
398 SliderParams slider("Global ERP Spring k (F=k*x)", &gERPSpringK);
399 slider.m_minVal = 0;
400 slider.m_maxVal = 10;
401 slider.m_callback = twxChangeERPCFM;
402 slider.m_clampToNotches = false;
403 if (m_guiHelper->getParameterInterface())
404 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
405 slider);
406 }
407
408 { // create a slider to adjust ERP damper c constant
409 SliderParams slider("Global ERP damper c (F=c*xdot)", &gERPDamperC);
410 slider.m_minVal = 0;
411 slider.m_maxVal = 10;
412 slider.m_callback = twxChangeERPCFM;
413 slider.m_clampToNotches = false;
414 if (m_guiHelper->getParameterInterface())
415 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
416 slider);
417 }
418
419 { // create a slider to adjust CFM Spring k constant
420 SliderParams slider("Global CFM Spring k (F=k*x)", &gCFMSpringK);
421 slider.m_minVal = 0;
422 slider.m_maxVal = 10;
423 slider.m_callback = twxChangeERPCFM;
424 slider.m_clampToNotches = false;
425 if (m_guiHelper->getParameterInterface())
426 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
427 slider);
428 }
429
430 { // create a slider to adjust CFM damper c constant
431 SliderParams slider("Global CFM damper c (F=c*xdot)", &gCFMDamperC);
432 slider.m_minVal = 0;
433 slider.m_maxVal = 10;
434 slider.m_callback = twxChangeERPCFM;
435 slider.m_clampToNotches = false;
436 if (m_guiHelper->getParameterInterface())
437 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
438 slider);
439 }
440
441 { // create a slider to adjust CFM damper c constant
442 SliderParams slider("Global CFM singularity avoidance", &gCFMSingularityAvoidance);
443 slider.m_minVal = 0;
444 slider.m_maxVal = 10;
445 slider.m_callback = twxChangeERPCFM;
446 slider.m_clampToNotches = false;
447 if (m_guiHelper->getParameterInterface())
448 m_guiHelper->getParameterInterface()->registerSliderFloatParameter(
449 slider);
450 }
451 }
452
createEmptyDynamicsWorldNN3DWalkersTimeWarpBase453 void createEmptyDynamicsWorld()
454 { // create an empty dynamics worlds according to the chosen settings via statics (top section of code)
455
456 ///collision configuration contains default setup for memory, collision setup
457 m_collisionConfiguration = new btDefaultCollisionConfiguration();
458 //m_collisionConfiguration->setConvexConvexMultipointIterations();
459
460 ///use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
461 m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
462
463 // default broadphase
464 m_broadphase = new btDbvtBroadphase();
465
466 // different solvers require different settings
467 switch (SOLVER_TYPE)
468 {
469 case SEQUENTIALIMPULSESOLVER:
470 {
471 // b3Printf("=%s=",SolverType::SEQUENTIALIMPULSESOLVER);
472 m_solver = new btSequentialImpulseConstraintSolver();
473 break;
474 }
475 case NNCGSOLVER:
476 {
477 // b3Printf("=%s=",SolverType::NNCGSOLVER);
478 m_solver = new btNNCGConstraintSolver();
479 break;
480 }
481 case DANZIGSOLVER:
482 {
483 // b3Printf("=%s=",SolverType::DANZIGSOLVER);
484 btDantzigSolver* mlcp = new btDantzigSolver();
485 m_solver = new btMLCPSolver(mlcp);
486 break;
487 }
488 case GAUSSSEIDELSOLVER:
489 {
490 // b3Printf("=%s=",SolverType::GAUSSSEIDELSOLVER);
491 btSolveProjectedGaussSeidel* mlcp = new btSolveProjectedGaussSeidel();
492 m_solver = new btMLCPSolver(mlcp);
493 break;
494 }
495 case LEMKESOLVER:
496 {
497 // b3Printf("=%s=",SolverType::LEMKESOLVER);
498 btLemkeSolver* mlcp = new btLemkeSolver();
499 m_solver = new btMLCPSolver(mlcp);
500 break;
501 }
502
503 default:
504 break;
505 }
506
507 if (1)
508 {
509 //TODO: Set parameters for other solvers
510
511 m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,
512 m_broadphase, m_solver, m_collisionConfiguration);
513
514 if (SOLVER_TYPE == DANZIGSOLVER || SOLVER_TYPE == GAUSSSEIDELSOLVER)
515 {
516 m_dynamicsWorld->getSolverInfo().m_minimumSolverBatchSize = 1; //for mlcp solver it is better to have a small A matrix
517 }
518 else
519 {
520 m_dynamicsWorld->getSolverInfo().m_minimumSolverBatchSize = 128; //for direct solver, it is better to solve multiple objects together, small batches have high overhead
521 }
522
523 m_dynamicsWorld->getDispatchInfo().m_useContinuous = true; // set continuous collision
524 }
525 else
526 {
527 //use btMultiBodyDynamicsWorld for Featherstone btMultiBody support
528 m_dynamicsWorld = new btMultiBodyDynamicsWorld(m_dispatcher,
529 m_broadphase, (btMultiBodyConstraintSolver*)m_solver,
530 m_collisionConfiguration);
531 }
532
533 changeERPCFM(); // set appropriate ERP/CFM values according to the string and damper properties of the constraint
534
535 if (useSplitImpulse)
536 { // If you experience strong repulsion forces in your constraints, it might help to enable the split impulse feature
537 m_dynamicsWorld->getSolverInfo().m_splitImpulse = 1; //enable split impulse feature
538 // m_dynamicsWorld->getSolverInfo().m_splitImpulsePenetrationThreshold =
539 // -0.02;
540 // m_dynamicsWorld->getSolverInfo().m_erp2 = BulletUtils::getERP(
541 // fixedPhysicsStepSizeSec, 10, 1);
542 // m_dynamicsWorld->getSolverInfo().m_splitImpulseTurnErp =
543 // BulletUtils::getERP(fixedPhysicsStepSizeSec, 10, 1);
544 // b3Printf("Using split impulse feature with ERP/TurnERP: (%f,%f)",
545 // m_dynamicsWorld->getSolverInfo().m_erp2,
546 // m_dynamicsWorld->getSolverInfo().m_splitImpulseTurnErp);
547 }
548
549 m_dynamicsWorld->getSolverInfo().m_numIterations = gSolverIterations; // set the number of solver iterations for iteration based solvers
550
551 m_dynamicsWorld->setGravity(btVector3(0, -9.81f, 0)); // set gravity to -9.81
552 }
553
calculatePerformedSpeedupNN3DWalkersTimeWarpBase554 btScalar calculatePerformedSpeedup()
555 { // calculate performed speedup
556 // we calculate the performed speed up
557 btScalar speedUp = ((double)performedTime * 1000.0) / ((double)(mLoopTimer.getTimeMilliseconds() - performanceTimestamp));
558 // b3Printf("Avg Effective speedup: %f",speedUp);
559 performedTime = 0;
560 performanceTimestamp = mLoopTimer.getTimeMilliseconds();
561 return speedUp;
562 }
563
timeWarpSimulationNN3DWalkersTimeWarpBase564 void timeWarpSimulation(float deltaTime) // Override this
565 {
566 }
567
stepSimulationNN3DWalkersTimeWarpBase568 void stepSimulation(float deltaTime)
569 { // customly step the simulation
570 do
571 {
572 // // settings
573 if (mPhysicsStepsPerSecondUpdated)
574 {
575 changePhysicsStepsPerSecond(gPhysicsStepsPerSecond);
576 mPhysicsStepsPerSecondUpdated = false;
577 }
578
579 if (mFramesPerSecondUpdated)
580 {
581 changeFPS(gFramesPerSecond);
582 mFramesPerSecondUpdated = false;
583 }
584
585 if (gChangeErpCfm)
586 {
587 changeERPCFM();
588 gChangeErpCfm = false;
589 }
590
591 if (mSolverIterationsUpdated)
592 {
593 changeSolverIterations(gSolverIterations);
594 mSolverIterationsUpdated = false;
595 }
596
597 // structure according to the canonical game loop
598 // http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Canonical_Game_Loop
599
600 //##############
601 // breaking conditions - if the loop should stop, then check it here
602
603 //#############
604 // model update - here you perform updates of your model, be it the physics model, the game or simulation state or anything not related to graphics and input
605
606 timeWarpSimulation(deltaTime);
607 if (mLoopTimer.getTimeSeconds() - speedUpPrintTimeStamp > 1)
608 {
609 // on reset, we calculate the performed speed up
610 //double speedUp = ((double)performedTime*1000.0)/((double)(mLoopTimer.getTimeMilliseconds()-performanceTimestamp));
611 // b3Printf("Avg Effective speedup: %f",speedUp);
612 performedTime = 0;
613 performanceTimestamp = mLoopTimer.getTimeMilliseconds();
614 speedUpPrintTimeStamp = mLoopTimer.getTimeSeconds();
615 }
616
617 // update timers
618 mThisModelIteration = mLoopTimer.getTimeMilliseconds();
619 mFrameTime = mThisModelIteration - mPreviousModelIteration; /**!< Calculate the frame time (in Milliseconds) */
620 mPreviousModelIteration = mThisModelIteration;
621
622 // b3Printf("Current Frame time: % u", mFrameTime);
623
624 mApplicationRuntime = mThisModelIteration - mApplicationStart; /**!< Update main frame timer (in Milliseconds) */
625
626 mModelStart = mLoopTimer.getTimeMilliseconds(); /**!< Begin with the model update (in Milliseconds)*/
627 mLastGraphicsTick = mModelStart - mGraphicsStart; /**!< Update graphics timer (in Milliseconds) */
628
629 if (gMaximumSpeed /** If maximum speed is enabled*/)
630 {
631 performMaxStep();
632 }
633 else
634 { /**!< This mode tries to progress as much time as it is expected from the game loop*/
635 performSpeedStep();
636 }
637
638 mInputStart = mLoopTimer.getTimeMilliseconds(); /**!< Start the input update */
639 mLastModelTick = mInputStart - mModelStart; /**!< Calculate the time the model update took */
640
641 //#############
642 // Input update - Game Clock part of the loop
643 /** This runs once every gApplicationTick milliseconds on average */
644 mInputDt = mThisModelIteration - mInputClock;
645 if (mInputDt >= gApplicationTick)
646 {
647 mInputClock = mThisModelIteration;
648 // mInputHandler.injectInput(); /**!< Inject input into handlers */
649 // mInputHandler.update(mInputClock); /**!< update elements that work on the current input state */
650 }
651
652 mGraphicsStart = mLoopTimer.getTimeMilliseconds(); /**!< Start the graphics update */
653 mLastInputTick = mGraphicsStart - mInputStart; /**!< Calculate the time the input injection took */
654
655 //#############
656 // Graphics update - Here you perform the representation of your model, meaning graphics rendering according to what your game or simulation model describes
657 // In the example browser, there is a separate method called renderScene() for this
658
659 // Uncomment this for some detailed output about the application ticks
660 // b3Printf(
661 // "Physics time: %u milliseconds / Graphics time: %u milliseconds / Input time: %u milliseconds / Total time passed: %u milliseconds",
662 // mLastModelTick, mLastGraphicsTick, mLastInputTick, mApplicationRuntime);
663
664 } while (mLoopTimer.getTimeMilliseconds() - fpsTimeStamp < fpsStep); // escape the loop if it is time to render
665 // Unfortunately, the input is not included in the loop, therefore the input update frequency is equal to the fps
666
667 fpsTimeStamp = mLoopTimer.getTimeMilliseconds();
668 }
669
keyboardCallbackNN3DWalkersTimeWarpBase670 virtual bool keyboardCallback(int key, int state)
671 {
672 switch (key)
673 {
674 case '1':
675 {
676 gSimulationSpeed = SimulationSpeeds::QUARTER_SPEED;
677 gMaximumSpeed = false;
678 return true;
679 }
680 case '2':
681 {
682 gSimulationSpeed = SimulationSpeeds::HALF_SPEED;
683 gMaximumSpeed = false;
684 return true;
685 }
686 case '3':
687 {
688 gSimulationSpeed = SimulationSpeeds::NORMAL_SPEED;
689 gMaximumSpeed = false;
690 return true;
691 }
692 case '4':
693 {
694 gSimulationSpeed = SimulationSpeeds::DOUBLE_SPEED;
695 gMaximumSpeed = false;
696 return true;
697 }
698 case '5':
699 {
700 gSimulationSpeed = SimulationSpeeds::QUADRUPLE_SPEED;
701 gMaximumSpeed = false;
702 return true;
703 }
704 case '6':
705 {
706 gSimulationSpeed = SimulationSpeeds::DECUPLE_SPEED;
707 gMaximumSpeed = false;
708 return true;
709 }
710 case '7':
711 {
712 gSimulationSpeed = SimulationSpeeds::CENTUPLE_SPEED;
713 gMaximumSpeed = false;
714 return true;
715 }
716 case '8':
717 {
718 gSimulationSpeed = SimulationSpeeds::QUINCENTUPLE_SPEED;
719 gMaximumSpeed = false;
720 return true;
721 }
722 case '9':
723 {
724 gSimulationSpeed = SimulationSpeeds::MILLITUPLE_SPEED;
725 gMaximumSpeed = false;
726 return true;
727 }
728 case '0':
729 {
730 gSimulationSpeed = SimulationSpeeds::MAX_SPEED;
731 gMaximumSpeed = true;
732 return true;
733 }
734 }
735 return CommonRigidBodyBase::keyboardCallback(key, state);
736 }
737
changePhysicsStepsPerSecondNN3DWalkersTimeWarpBase738 void changePhysicsStepsPerSecond(float physicsStepsPerSecond)
739 { // change the simulation accuracy
740 if (m_dynamicsWorld && physicsStepsPerSecond)
741 {
742 fixedPhysicsStepSizeSec = 1.0f / physicsStepsPerSecond;
743 fixedPhysicsStepSizeMilli = 1000.0f / physicsStepsPerSecond;
744
745 changeERPCFM();
746 }
747 }
748
changeERPCFMNN3DWalkersTimeWarpBase749 void changeERPCFM()
750 { // Change ERP/CFM appropriately to the timestep and the ERP/CFM parameters above
751 if (m_dynamicsWorld)
752 {
753 m_dynamicsWorld->getSolverInfo().m_erp = b3ERPCFMHelper::getERP( // set the error reduction parameter
754 fixedPhysicsStepSizeSec, // step size per second
755 gERPSpringK, // k of a spring in the equation F = k * x (x:position)
756 gERPDamperC); // k of a damper in the equation F = k * v (v:velocity)
757
758 m_dynamicsWorld->getSolverInfo().m_globalCfm = b3ERPCFMHelper::getCFM( // set the constraint force mixing according to the time step
759 gCFMSingularityAvoidance, // singularity avoidance (if you experience unsolvable constraints, increase this value
760 fixedPhysicsStepSizeSec, // steps size per second
761 gCFMSpringK, // k of a spring in the equation F = k * x (x:position)
762 gCFMDamperC); // k of a damper in the equation F = k * v (v:velocity)
763
764 // b3Printf("Bullet DynamicsWorld ERP: %f",
765 // m_dynamicsWorld->getSolverInfo().m_erp);
766
767 // b3Printf("Bullet DynamicsWorld CFM: %f",
768 // m_dynamicsWorld->getSolverInfo().m_globalCfm);
769 }
770 }
771
changeSolverIterationsNN3DWalkersTimeWarpBase772 void changeSolverIterations(int iterations)
773 { // change the number of iterations
774 m_dynamicsWorld->getSolverInfo().m_numIterations = iterations;
775 }
776
changeFPSNN3DWalkersTimeWarpBase777 void changeFPS(float framesPerSecond)
778 { // change the frames per second
779 fpsStep = 1000.0f / gFramesPerSecond;
780 }
781
performTrueStepsNN3DWalkersTimeWarpBase782 void performTrueSteps(btScalar timeStep)
783 { // physics stepping without interpolated substeps
784 int subSteps = floor((timeStep / fixedPhysicsStepSizeSec) + 0.5); /**!< Calculate the number of full normal time steps we can take */
785
786 for (int i = 0; i < subSteps; i++)
787 { /**!< Perform the number of substeps to reach the timestep*/
788 if (timeStep && m_dynamicsWorld)
789 {
790 // since we want to perform all proper steps, we perform no interpolated substeps
791 int subSteps = 1;
792
793 m_dynamicsWorld->stepSimulation(btScalar(timeStep),
794 btScalar(subSteps), btScalar(fixedPhysicsStepSizeSec));
795 }
796 }
797 }
798
performInterpolatedStepsNN3DWalkersTimeWarpBase799 void performInterpolatedSteps(btScalar timeStep)
800 { // physics stepping with interpolated substeps
801 int subSteps = 1 + floor((timeStep / fixedPhysicsStepSizeSec) + 0.5); /**!< Calculate the number of full normal time steps we can take, plus 1 for safety of not losing time */
802 if (timeStep && m_dynamicsWorld)
803 {
804 m_dynamicsWorld->stepSimulation(btScalar(timeStep), btScalar(subSteps),
805 btScalar(fixedPhysicsStepSizeSec)); /**!< Perform the number of substeps to reach the timestep*/
806 }
807 }
808
performMaxStepNN3DWalkersTimeWarpBase809 void performMaxStep()
810 { // perform as many steps as possible
811 if (gApplicationTick >= mLastGraphicsTick + mLastInputTick)
812 { // if the remaining time for graphics is going to be positive
813 mPhysicsTick = gApplicationTick /**!< calculate the remaining time for physics (in Milliseconds) */
814 - mLastGraphicsTick - mLastInputTick;
815 }
816 else
817 {
818 mPhysicsTick = 0; // no time for physics left / The internal application step is too high
819 }
820
821 // b3Printf("Application tick: %u",gApplicationTick);
822 // b3Printf("Graphics tick: %u",mLastGraphicsTick);
823 // b3Printf("Input tick: %u",mLastInputTick);
824 // b3Printf("Physics tick: %u",mPhysicsTick);
825
826 if (mPhysicsTick > 0)
827 { // with positive physics tick we perform as many update steps until the time for it is used up
828
829 mPhysicsStepStart = mLoopTimer.getTimeMilliseconds(); /**!< The physics updates start (in Milliseconds)*/
830 mPhysicsStepEnd = mPhysicsStepStart;
831
832 while (mPhysicsTick > mPhysicsStepEnd - mPhysicsStepStart)
833 { /**!< Update the physics until we run out of time (in Milliseconds) */
834 // b3Printf("Physics passed: %u", mPhysicsStepEnd - mPhysicsStepStart);
835 double timeStep = fixedPhysicsStepSizeSec; /**!< update the world (in Seconds) */
836
837 if (gInterpolate)
838 {
839 performInterpolatedSteps(timeStep);
840 }
841 else
842 {
843 performTrueSteps(timeStep);
844 }
845 performedTime += timeStep;
846 mPhysicsStepEnd = mLoopTimer.getTimeMilliseconds(); /**!< Update the last physics step end to stop updating in time (in Milliseconds) */
847 }
848 }
849 }
850
performSpeedStepNN3DWalkersTimeWarpBase851 void performSpeedStep()
852 { // force-perform the number of steps needed to achieve a certain speed (safe to too high speeds, meaning the application will lose time, not the physics)
853 if (mFrameTime > gApplicationTick)
854 { /** cap frametime to make the application lose time, not the physics (in Milliseconds) */
855 mFrameTime = gApplicationTick; // This prevents the physics time accumulator to sum up too much time
856 } // The simulation therefore gets slower, but still performs all requested physics steps
857
858 mModelAccumulator += mFrameTime; /**!< Accumulate the time the physics simulation has to perform in order to stay in real-time (in Milliseconds) */
859 // b3Printf("Model time accumulator: %u", mModelAccumulator);
860
861 int steps = floor(mModelAccumulator / fixedPhysicsStepSizeMilli); /**!< Calculate the number of time steps we can take */
862 // b3Printf("Next steps: %i", steps);
863
864 if (steps > 0)
865 { /**!< Update if we can take at least one step */
866
867 double timeStep = gSimulationSpeed * steps * fixedPhysicsStepSizeSec; /**!< update the universe (in Seconds) */
868
869 if (gInterpolate)
870 {
871 performInterpolatedSteps(timeStep); // perform interpolated steps
872 }
873 else
874 {
875 performTrueSteps(timeStep); // perform full steps
876 }
877 performedTime += timeStep; // sum up the performed time for measuring the speed up
878 mModelAccumulator -= steps * fixedPhysicsStepSizeMilli; /**!< Remove the time performed by the physics simulation from the accumulator, the remaining time carries over to the next cycle (in Milliseconds) */
879 }
880 }
881
renderSceneNN3DWalkersTimeWarpBase882 void renderScene()
883 { // render the scene
884 if (!gIsHeadless)
885 { // while the simulation is not running headlessly, render to screen
886 CommonRigidBodyBase::renderScene();
887
888 if (m_dynamicsWorld->getDebugDrawer())
889 {
890 debugDraw(m_dynamicsWorld->getDebugDrawer()->getDebugMode());
891 }
892 }
893 mIsHeadless = gIsHeadless;
894 }
resetCameraNN3DWalkersTimeWarpBase895 void resetCamera()
896 { // reset the camera to its original position
897 float dist = 41;
898 float pitch = 52;
899 float yaw = 35;
900 float targetPos[3] = {0, 0.46, 0};
901 m_guiHelper->resetCamera(dist, pitch, yaw, targetPos[0], targetPos[1],
902 targetPos[2]);
903 }
904
905 // loop timing components ###################
906 //# loop timestamps
907 btClock mLoopTimer; /**!< The loop timer to time the loop correctly */
908 unsigned long int mApplicationStart; /**!< The time the application was started (absolute, in Milliseconds) */
909 unsigned long int mPreviousModelIteration; /**!< The previous model iteration timestamp (absolute, in Milliseconds) */
910 unsigned long int mThisModelIteration; /**!< This model iteration timestamp (absolute, in Milliseconds) */
911
912 //# loop durations
913 long int mModelAccumulator; /**!< The time to forward the model in this loop iteration (relative, in Milliseconds) */
914 unsigned long int mFrameTime; /**!< The time to render a frame (relative, in Milliseconds) */
915 unsigned long int mApplicationRuntime; /**!< The total application runtime (relative, in Milliseconds) */
916
917 long int mInputDt; /**!< The time difference of input that has to be fed in */
918 unsigned long int mInputClock;
919
920 long int mLastGraphicsTick; /*!< The time it took the graphics rendering last time (relative, in Milliseconds) */
921 unsigned long int mGraphicsStart;
922
923 long int mLastInputTick; /**!< The time it took the input to process last time (relative, in Milliseconds) */
924 unsigned long int mInputStart;
925
926 long int mLastModelTick; /**!< The time it took the model to update last time
927 This includes the bullet physics update */
928 unsigned long int mModelStart; /**!< The timestamp the model started updating last (absolute, in Milliseconds)*/
929
930 long int mPhysicsTick; /**!< The time remaining in the loop to update the physics (relative, in Milliseconds)*/
931 unsigned long int mPhysicsStepStart; /**!< The physics start timestamp (absolute, in Milliseconds) */
932 unsigned long int mPhysicsStepEnd; /**!< The last physics step end (absolute, in Milliseconds) */
933
934 // to measure the performance of the demo
935 double performedTime;
936 unsigned long int performanceTimestamp;
937
938 unsigned long int speedUpPrintTimeStamp;
939
940 unsigned long int fpsTimeStamp; /**!< FPS timing variables */
941 double fpsStep;
942
943 //store old values
944 bool mPhysicsStepsPerSecondUpdated;
945 bool mFramesPerSecondUpdated;
946 bool mSolverIterationsUpdated;
947 bool mIsHeadless;
948 };
949
950 #endif //NN3D_WALKERS_TIME_WARP_BASE_H
951