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