1 /********************************************************************************
2 * ReactPhysics3D physics library, http://www.reactphysics3d.com *
3 * Copyright (c) 2010-2016 Daniel Chappuis *
4 *********************************************************************************
5 * *
6 * This software is provided 'as-is', without any express or implied warranty. *
7 * In no event will the authors be held liable for any damages arising from the *
8 * use of this software. *
9 * *
10 * Permission is granted to anyone to use this software for any purpose, *
11 * including commercial applications, and to alter it and redistribute it *
12 * freely, subject to the following restrictions: *
13 * *
14 * 1. The origin of this software must not be misrepresented; you must not claim *
15 * that you wrote the original software. If you use this software in a *
16 * product, an acknowledgment in the product documentation would be *
17 * appreciated but is not required. *
18 * *
19 * 2. Altered source versions must be plainly marked as such, and must not be *
20 * misrepresented as being the original software. *
21 * *
22 * 3. This notice may not be removed or altered from any source distribution. *
23 * *
24 ********************************************************************************/
25
26 // Libraries
27 #include "TestbedApplication.h"
28 #include "openglframework.h"
29 #include <iostream>
30 #include <cstdlib>
31 #include <sstream>
32 #include "cubes/CubesScene.h"
33 #include "collisiondetection/CollisionDetectionScene.h"
34 #include "joints/JointsScene.h"
35 #include "collisionshapes/CollisionShapesScene.h"
36 #include "heightfield/HeightFieldScene.h"
37 #include "raycast/RaycastScene.h"
38 #include "concavemesh/ConcaveMeshScene.h"
39 #include "cubestack/CubeStackScene.h"
40 #include "pile/PileScene.h"
41
42 using namespace openglframework;
43 using namespace jointsscene;
44 using namespace cubesscene;
45 using namespace raycastscene;
46 using namespace collisionshapesscene;
47 using namespace trianglemeshscene;
48 using namespace heightfieldscene;
49 using namespace collisiondetectionscene;
50 using namespace cubestackscene;
51 using namespace pilescene;
52
53 // Initialization of static variables
54 const float TestbedApplication::SCROLL_SENSITIVITY = 0.08f;
55
56 // Constructor
TestbedApplication(bool isFullscreen,int windowWidth,int windowHeight)57 TestbedApplication::TestbedApplication(bool isFullscreen, int windowWidth, int windowHeight)
58 : Screen(Vector2i(windowWidth, windowHeight), "Testbed ReactPhysics3D", true, isFullscreen, true, true, false, 4, 1),
59 mIsInitialized(false), mGui(this), mCurrentScene(nullptr),
60 mEngineSettings(EngineSettings::defaultSettings()),
61 mFPS(0), mNbFrames(0), mPreviousTime(0),
62 mLastTimeComputedFPS(0), mFrameTime(0), mTotalPhysicsTime(0), mPhysicsStepTime(0),
63 mWidth(windowWidth), mHeight(windowHeight),
64 mSinglePhysicsStepEnabled(false), mSinglePhysicsStepDone(false),
65 mWindowToFramebufferRatio(Vector2(1, 1)), mIsShadowMappingEnabled(true),
66 mAreContactPointsDisplayed(false), mAreContactNormalsDisplayed(false),
67 mAreBroadPhaseAABBsDisplayed(false), mAreCollidersAABBsDisplayed(false),
68 mAreCollisionShapesDisplayed(false), mAreObjectsWireframeEnabled(false),
69 mIsVSyncEnabled(false), mIsDebugRendererEnabled(false) {
70
71 init();
72
73 resize_event(Vector2i(0, 0));
74 }
75
76 // Destructor
~TestbedApplication()77 TestbedApplication::~TestbedApplication() {
78
79 // Destroy all the scenes
80 destroyScenes();
81 }
82
83 // Initialize the viewer
init()84 void TestbedApplication::init() {
85
86 // Create all the scenes
87 createScenes();
88
89 // Initialize the GUI
90 mGui.init();
91
92 mTimer.start();
93
94 // Enable OpenGL error reporting
95 glEnable(GL_DEBUG_OUTPUT);
96 glDebugMessageCallback(onOpenGLError, 0);
97
98 mIsInitialized = true;
99 }
100
101 // Create all the scenes
createScenes()102 void TestbedApplication::createScenes() {
103
104 // Cubes scene
105 CubesScene* cubeScene = new CubesScene("Cubes", mEngineSettings);
106 mScenes.push_back(cubeScene);
107
108 // Cube Stack scene
109 CubeStackScene* cubeStackScene = new CubeStackScene("Cube Stack", mEngineSettings);
110 mScenes.push_back(cubeStackScene);
111
112 // Joints scene
113 JointsScene* jointsScene = new JointsScene("Joints", mEngineSettings);
114 mScenes.push_back(jointsScene);
115
116 // Collision shapes scene
117 CollisionShapesScene* collisionShapesScene = new CollisionShapesScene("Collision Shapes", mEngineSettings);
118 mScenes.push_back(collisionShapesScene);
119
120 // Heightfield shape scene
121 HeightFieldScene* heightFieldScene = new HeightFieldScene("Heightfield", mEngineSettings);
122 mScenes.push_back(heightFieldScene);
123
124 // Raycast scene
125 RaycastScene* raycastScene = new RaycastScene("Raycast", mEngineSettings);
126 mScenes.push_back(raycastScene);
127
128 // Collision Detection scene
129 CollisionDetectionScene* collisionDetectionScene = new CollisionDetectionScene("Collision Detection", mEngineSettings);
130 mScenes.push_back(collisionDetectionScene);
131
132 // Concave Mesh scene
133 ConcaveMeshScene* concaveMeshScene = new ConcaveMeshScene("Concave Mesh", mEngineSettings);
134 mScenes.push_back(concaveMeshScene);
135
136 // Pile scene
137 PileScene* pileScene = new PileScene("Pile", mEngineSettings);
138 mScenes.push_back(pileScene);
139 assert(mScenes.size() > 0);
140
141 const int firstSceneIndex = 0;
142
143 switchScene(mScenes[firstSceneIndex]);
144 }
145
146 // Remove all the scenes
destroyScenes()147 void TestbedApplication::destroyScenes() {
148
149 for (uint i=0; i<mScenes.size(); i++) {
150 delete mScenes[i];
151 }
152
153 mCurrentScene = NULL;
154 }
155
updateSinglePhysicsStep()156 void TestbedApplication::updateSinglePhysicsStep() {
157
158 assert(!mTimer.isRunning());
159
160 mCurrentScene->updatePhysics();
161 }
162
163 // Update the physics of the current scene
updatePhysics()164 void TestbedApplication::updatePhysics() {
165
166 // Update the elapsed time
167 mEngineSettings.elapsedTime = mTimer.getPhysicsTime();
168
169 if (mTimer.isRunning()) {
170
171 // Compute the time since the last update() call and update the timer
172 mTimer.update();
173
174 // While the time accumulator is not empty
175 while(mTimer.isPossibleToTakeStep(mEngineSettings.timeStep)) {
176
177 double currentTime = glfwGetTime();
178
179 // Take a physics simulation step
180 mCurrentScene->updatePhysics();
181
182 mPhysicsStepTime = glfwGetTime() - currentTime;
183
184 // Update the timer
185 mTimer.nextStep(mEngineSettings.timeStep);
186 }
187 }
188 }
189
update()190 void TestbedApplication::update() {
191
192 double currentTime = glfwGetTime();
193
194 mCurrentScene->setIsDebugRendererEnabled(mIsDebugRendererEnabled);
195
196 // Update the physics
197 if (mSinglePhysicsStepEnabled && !mSinglePhysicsStepDone) {
198 updateSinglePhysicsStep();
199 mSinglePhysicsStepDone = true;
200 }
201 else {
202 updatePhysics();
203 }
204
205 // Compute the physics update time
206 mTotalPhysicsTime = glfwGetTime() - currentTime;
207
208 // Compute the interpolation factor
209 float factor = mTimer.computeInterpolationFactor(mEngineSettings.timeStep);
210 assert(factor >= 0.0f && factor <= 1.0f);
211
212 // Notify the scene about the interpolation factor
213 mCurrentScene->setInterpolationFactor(factor);
214
215 // Enable/Disable shadow mapping
216 mCurrentScene->setIsShadowMappingEnabled(mIsShadowMappingEnabled);
217
218 // Display/Hide contact points
219 mCurrentScene->setAreContactPointsDisplayed(mAreContactPointsDisplayed);
220
221 // Display/Hide contact normals
222 mCurrentScene->setAreContactNormalsDisplayed(mAreContactNormalsDisplayed);
223
224 // Display/Hide the broad phase AABBs
225 mCurrentScene->setAreBroadPhaseAABBsDisplayed(mAreBroadPhaseAABBsDisplayed);
226
227 // Display/Hide the colliders AABBs
228 mCurrentScene->setAreCollidersAABBsDisplayed(mAreCollidersAABBsDisplayed);
229
230 // Display/Hide the collision shapes
231 mCurrentScene->setAreCollisionShapesDisplayed(mAreCollisionShapesDisplayed);
232
233 // Enable/Disable wireframe mode
234 mCurrentScene->setIsWireframeEnabled(mAreObjectsWireframeEnabled);
235
236 // Update the scene
237 mCurrentScene->update();
238 }
239
draw_contents()240 void TestbedApplication::draw_contents() {
241
242 update();
243
244 int bufferWidth, bufferHeight;
245 glfwMakeContextCurrent(m_glfw_window);
246 glfwGetFramebufferSize(m_glfw_window, &bufferWidth, &bufferHeight);
247
248 // Set the viewport of the scene
249 mCurrentScene->setViewport(0, 0, bufferWidth, bufferHeight);
250
251 // Render the scene
252 mCurrentScene->render();
253
254 mGui.update();
255
256 // Compute the current framerate
257 computeFPS();
258 }
259
260 /// Window resize event handler
resize_event(const Vector2i & size)261 bool TestbedApplication::resize_event(const Vector2i &size) {
262
263 if (!mIsInitialized) return false;
264
265 // Get the framebuffer dimension
266 int width, height;
267 glfwGetFramebufferSize(m_glfw_window, &width, &height);
268
269 // Resize the camera viewport
270 mCurrentScene->reshape(width, height);
271
272 // Update the window size of the scene
273 int windowWidth, windowHeight;
274 glfwGetWindowSize(m_glfw_window, &windowWidth, &windowHeight);
275 mCurrentScene->setWindowDimension(windowWidth, windowHeight);
276
277 return true;
278 }
279
280 // Change the current scene
switchScene(Scene * newScene)281 void TestbedApplication::switchScene(Scene* newScene) {
282
283 if (newScene == mCurrentScene) return;
284
285 mCurrentScene = newScene;
286
287 // Reset the scene
288 mCurrentScene->reset();
289
290 mCurrentScene->updateEngineSettings();
291
292 resize_event(Vector2i(0, 0));
293 }
294
295 // Notify that the engine settings have changed
notifyEngineSetttingsChanged()296 void TestbedApplication::notifyEngineSetttingsChanged() {
297 mCurrentScene->updateEngineSettings();
298 }
299
onOpenGLError(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)300 void GLAPIENTRY TestbedApplication::onOpenGLError(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
301 const GLchar* message, const void* userParam ) {
302
303 if (type == GL_DEBUG_TYPE_ERROR) {
304 /*
305 fprintf( stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
306 ("** GL ERROR **" ),
307 type, severity, message );
308 */
309 }
310 }
311
312 // Compute the FPS
computeFPS()313 void TestbedApplication::computeFPS() {
314
315 // Note : By default the nanogui library is using glfwWaitEvents() to process
316 // events and sleep to target a framerate of 50 ms (using a thread
317 // sleeping). However, for games we prefer to use glfwPollEvents()
318 // instead and remove the update. Therefore the file common.cpp of the
319 // nanogui library has been modified to have a faster framerate
320
321 mNbFrames++;
322
323 // Get the number of seconds since start
324 mCurrentTime = glfwGetTime();
325
326 // Calculate time passed
327 mFrameTime = mCurrentTime - mPreviousTime;
328 double timeInterval = (mCurrentTime - mLastTimeComputedFPS) * 1000.0;
329
330 // Update the FPS counter each second
331 if(timeInterval > 1000) {
332
333 // calculate the number of frames per second
334 mFPS = static_cast<double>(mNbFrames) / timeInterval;
335 mFPS *= 1000.0;
336
337 // Reset frame count
338 mNbFrames = 0;
339
340 mLastTimeComputedFPS = mCurrentTime;
341 }
342
343 // Set time
344 mPreviousTime = mCurrentTime;
345 }
346
keyboard_event(int key,int scancode,int action,int modifiers)347 bool TestbedApplication::keyboard_event(int key, int scancode, int action, int modifiers) {
348
349 if (Screen::keyboard_event(key, scancode, action, modifiers)) {
350 return true;
351 }
352
353 // Close application on escape key
354 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
355 glfwSetWindowShouldClose(m_glfw_window, GL_TRUE);
356 return true;
357 }
358
359 return mCurrentScene->keyboardEvent(key, scancode, action, modifiers);
360 }
361
362 // Handle a mouse button event (default implementation: propagate to children)
mouse_button_event(const Vector2i & p,int button,bool down,int modifiers)363 bool TestbedApplication::mouse_button_event(const Vector2i &p, int button, bool down, int modifiers) {
364
365 if (Screen::mouse_button_event(p, button, down, modifiers)) {
366 return true;
367 }
368
369 // Get the mouse cursor position
370 double x, y;
371 glfwGetCursorPos(m_glfw_window, &x, &y);
372
373 return mCurrentScene->mouseButtonEvent(button, down, modifiers, x, y);
374 }
375
376 // Handle a mouse motion event (default implementation: propagate to children)
mouse_motion_event(const Vector2i & p,const Vector2i & rel,int button,int modifiers)377 bool TestbedApplication::mouse_motion_event(const Vector2i &p, const Vector2i &rel, int button, int modifiers) {
378
379 if (Screen::mouse_motion_event(p, rel, button, modifiers)) {
380 return true;
381 }
382
383 int leftButtonState = glfwGetMouseButton(m_glfw_window, GLFW_MOUSE_BUTTON_LEFT);
384 int rightButtonState = glfwGetMouseButton(m_glfw_window, GLFW_MOUSE_BUTTON_RIGHT);
385 int middleButtonState = glfwGetMouseButton(m_glfw_window, GLFW_MOUSE_BUTTON_MIDDLE);
386 int altKeyState = glfwGetKey(m_glfw_window, GLFW_KEY_LEFT_ALT);
387
388 return mCurrentScene->mouseMotionEvent(p[0], p[1], leftButtonState, rightButtonState,
389 middleButtonState, altKeyState);
390 }
391
392 // Handle a mouse scroll event (default implementation: propagate to children)
scroll_event(const Vector2i & p,const Vector2f & rel)393 bool TestbedApplication::scroll_event(const Vector2i &p, const Vector2f &rel) {
394
395 if (Screen::scroll_event(p, rel)) {
396 return true;
397 }
398
399 return mCurrentScene->scrollingEvent(rel[0], rel[1], SCROLL_SENSITIVITY);
400 }
401