2015-04-08 18:47:55 +00:00
|
|
|
/********************************************************************************
|
|
|
|
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
|
|
|
|
* Copyright (c) 2010-2015 Daniel Chappuis *
|
|
|
|
*********************************************************************************
|
|
|
|
* *
|
|
|
|
* This software is provided 'as-is', without any express or implied warranty. *
|
|
|
|
* In no event will the authors be held liable for any damages arising from the *
|
|
|
|
* use of this software. *
|
|
|
|
* *
|
|
|
|
* Permission is granted to anyone to use this software for any purpose, *
|
|
|
|
* including commercial applications, and to alter it and redistribute it *
|
|
|
|
* freely, subject to the following restrictions: *
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
* 2. Altered source versions must be plainly marked as such, and must not be *
|
|
|
|
* misrepresented as being the original software. *
|
|
|
|
* *
|
|
|
|
* 3. This notice may not be removed or altered from any source distribution. *
|
|
|
|
* *
|
|
|
|
********************************************************************************/
|
|
|
|
|
|
|
|
// Libraries
|
|
|
|
#include "TestbedApplication.h"
|
|
|
|
#include "openglframework.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <sstream>
|
2015-04-15 21:11:27 +00:00
|
|
|
#include "cubes/CubesScene.h"
|
|
|
|
#include "joints/JointsScene.h"
|
|
|
|
#include "collisionshapes/CollisionShapesScene.h"
|
|
|
|
#include "raycast/RaycastScene.h"
|
2015-04-08 18:47:55 +00:00
|
|
|
|
|
|
|
using namespace openglframework;
|
2015-04-15 21:11:27 +00:00
|
|
|
using namespace jointsscene;
|
|
|
|
using namespace cubesscene;
|
|
|
|
using namespace raycastscene;
|
|
|
|
using namespace collisionshapesscene;
|
2015-04-08 18:47:55 +00:00
|
|
|
|
|
|
|
// Initialization of static variables
|
|
|
|
const float TestbedApplication::SCROLL_SENSITIVITY = 0.02f;
|
|
|
|
|
|
|
|
// Create and return the singleton instance of this class
|
|
|
|
TestbedApplication& TestbedApplication::getInstance() {
|
|
|
|
static TestbedApplication instance;
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constructor
|
2015-06-30 17:50:17 +00:00
|
|
|
TestbedApplication::TestbedApplication() : mFPS(0), mNbFrames(0), mPreviousTime(0) {
|
2015-04-08 18:47:55 +00:00
|
|
|
|
2015-04-22 18:54:17 +00:00
|
|
|
mCurrentScene = NULL;
|
2015-04-08 18:47:55 +00:00
|
|
|
mIsMultisamplingActive = true;
|
2015-05-24 15:23:55 +00:00
|
|
|
mWidth = 1280;
|
|
|
|
mHeight = 720;
|
2015-06-30 17:50:17 +00:00
|
|
|
mSinglePhysicsStepEnabled = false;
|
|
|
|
mSinglePhysicsStepDone = false;
|
2015-07-11 16:35:58 +00:00
|
|
|
mWindowToFramebufferRatio = Vector2(1, 1);
|
2015-04-08 18:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
TestbedApplication::~TestbedApplication() {
|
|
|
|
|
|
|
|
// TODO : Check that this method is called at the end
|
|
|
|
|
2015-04-15 21:11:27 +00:00
|
|
|
// Destroy all the scenes
|
|
|
|
destroyScenes();
|
|
|
|
|
2015-04-08 18:47:55 +00:00
|
|
|
// Destroy the window
|
|
|
|
glfwDestroyWindow(mWindow);
|
|
|
|
|
|
|
|
// Terminate GLFW
|
|
|
|
glfwTerminate();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the viewer
|
|
|
|
void TestbedApplication::init() {
|
|
|
|
|
|
|
|
// Set the GLFW error callback method
|
|
|
|
glfwSetErrorCallback(error_callback);
|
|
|
|
|
|
|
|
// Initialize the GLFW library
|
|
|
|
if (!glfwInit()) {
|
|
|
|
std::exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2015-05-24 15:23:55 +00:00
|
|
|
// OpenGL version required
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
|
|
|
|
2015-04-08 18:47:55 +00:00
|
|
|
// Active the multi-sampling by default
|
|
|
|
if (mIsMultisamplingActive) {
|
|
|
|
glfwWindowHint(GLFW_SAMPLES, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the GLFW window
|
|
|
|
mWindow = glfwCreateWindow(mWidth, mHeight,
|
|
|
|
"ReactPhysics3D Testbed", NULL, NULL);
|
|
|
|
if (!mWindow) {
|
|
|
|
glfwTerminate();
|
|
|
|
std::exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
glfwMakeContextCurrent(mWindow);
|
|
|
|
|
|
|
|
// Disable Vertical Synchronization
|
|
|
|
glfwSwapInterval(0);
|
|
|
|
|
|
|
|
// Initialize the GLEW library
|
2015-04-28 21:47:29 +00:00
|
|
|
glewExperimental = GL_TRUE;
|
2015-04-08 18:47:55 +00:00
|
|
|
GLenum errorGLEW = glewInit();
|
|
|
|
if (errorGLEW != GLEW_OK) {
|
|
|
|
|
|
|
|
// Problem: glewInit failed, something is wrong
|
|
|
|
std::cerr << "GLEW Error : " << glewGetErrorString(errorGLEW) << std::endl;
|
|
|
|
assert(false);
|
|
|
|
std::exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mIsMultisamplingActive) {
|
|
|
|
glEnable(GL_MULTISAMPLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
glfwSetKeyCallback(mWindow, keyboard);
|
|
|
|
glfwSetMouseButtonCallback(mWindow, mouseButton);
|
|
|
|
glfwSetCursorPosCallback(mWindow, mouseMotion);
|
|
|
|
glfwSetScrollCallback(mWindow, scroll);
|
|
|
|
|
|
|
|
// Define the background color (black)
|
2015-07-11 22:05:49 +00:00
|
|
|
glClearColor(0, 0, 0, 1.0);
|
2015-04-15 21:11:27 +00:00
|
|
|
|
|
|
|
// Create all the scenes
|
|
|
|
createScenes();
|
2015-04-20 21:23:46 +00:00
|
|
|
|
2015-04-28 21:47:29 +00:00
|
|
|
Gui::getInstance().setWindow(mWindow);
|
|
|
|
|
|
|
|
// Init the GUI
|
2015-06-01 21:40:44 +00:00
|
|
|
Gui::getInstance().init();
|
2015-04-28 21:47:29 +00:00
|
|
|
|
2015-04-20 21:23:46 +00:00
|
|
|
mTimer.start();
|
2015-04-15 21:11:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create all the scenes
|
|
|
|
void TestbedApplication::createScenes() {
|
|
|
|
|
|
|
|
// Cubes scene
|
|
|
|
CubesScene* cubeScene = new CubesScene("Cubes");
|
|
|
|
mScenes.push_back(cubeScene);
|
|
|
|
|
|
|
|
// Joints scene
|
|
|
|
JointsScene* jointsScene = new JointsScene("Joints");
|
|
|
|
mScenes.push_back(jointsScene);
|
|
|
|
|
|
|
|
// Collision shapes scene
|
|
|
|
CollisionShapesScene* collisionShapesScene = new CollisionShapesScene("Collision Shapes");
|
|
|
|
mScenes.push_back(collisionShapesScene);
|
|
|
|
|
|
|
|
// Raycast scene
|
|
|
|
RaycastScene* raycastScene = new RaycastScene("Raycast");
|
|
|
|
mScenes.push_back(raycastScene);
|
|
|
|
|
|
|
|
assert(mScenes.size() > 0);
|
2015-07-11 22:05:49 +00:00
|
|
|
mCurrentScene = mScenes[0];
|
2015-07-11 16:35:58 +00:00
|
|
|
|
|
|
|
// Get the engine settings from the scene
|
|
|
|
mEngineSettings = mCurrentScene->getEngineSettings();
|
|
|
|
mEngineSettings.timeStep = DEFAULT_TIMESTEP;
|
2015-04-15 21:11:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all the scenes
|
|
|
|
void TestbedApplication::destroyScenes() {
|
|
|
|
|
|
|
|
for (int i=0; i<mScenes.size(); i++) {
|
|
|
|
delete mScenes[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
mCurrentScene = NULL;
|
2015-04-08 18:47:55 +00:00
|
|
|
}
|
|
|
|
|
2015-06-30 17:50:17 +00:00
|
|
|
void TestbedApplication::updateSinglePhysicsStep() {
|
|
|
|
|
|
|
|
assert(!mTimer.isRunning());
|
|
|
|
|
|
|
|
mCurrentScene->updatePhysics();
|
|
|
|
}
|
|
|
|
|
2015-04-20 21:23:46 +00:00
|
|
|
// Update the physics of the current scene
|
|
|
|
void TestbedApplication::updatePhysics() {
|
|
|
|
|
|
|
|
// Set the engine settings
|
|
|
|
mEngineSettings.elapsedTime = mTimer.getPhysicsTime();
|
|
|
|
mCurrentScene->setEngineSettings(mEngineSettings);
|
|
|
|
|
|
|
|
if (mTimer.isRunning()) {
|
|
|
|
|
|
|
|
// Compute the time since the last update() call and update the timer
|
|
|
|
mTimer.update();
|
|
|
|
|
|
|
|
// While the time accumulator is not empty
|
2015-04-22 18:54:17 +00:00
|
|
|
while(mTimer.isPossibleToTakeStep(mEngineSettings.timeStep)) {
|
2015-04-20 21:23:46 +00:00
|
|
|
|
|
|
|
// Take a physics simulation step
|
|
|
|
mCurrentScene->updatePhysics();
|
|
|
|
|
|
|
|
// Update the timer
|
2015-04-22 18:54:17 +00:00
|
|
|
mTimer.nextStep(mEngineSettings.timeStep);
|
2015-04-20 21:23:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-08 18:47:55 +00:00
|
|
|
void TestbedApplication::update() {
|
|
|
|
|
2015-04-22 18:54:17 +00:00
|
|
|
// Update the physics
|
2015-06-30 17:50:17 +00:00
|
|
|
if (mSinglePhysicsStepEnabled && !mSinglePhysicsStepDone) {
|
|
|
|
updateSinglePhysicsStep();
|
|
|
|
mSinglePhysicsStepDone = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
updatePhysics();
|
|
|
|
}
|
2015-04-22 18:54:17 +00:00
|
|
|
|
2015-04-20 21:23:46 +00:00
|
|
|
// Compute the interpolation factor
|
|
|
|
float factor = mTimer.computeInterpolationFactor(mEngineSettings.timeStep);
|
|
|
|
assert(factor >= 0.0f && factor <= 1.0f);
|
|
|
|
|
|
|
|
// Notify the scene about the interpolation factor
|
|
|
|
mCurrentScene->setInterpolationFactor(factor);
|
|
|
|
|
|
|
|
// Update the scene
|
2015-04-08 18:47:55 +00:00
|
|
|
mCurrentScene->update();
|
|
|
|
|
|
|
|
// Compute the current framerate
|
|
|
|
//application->computeFPS();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render
|
|
|
|
void TestbedApplication::render() {
|
|
|
|
|
2015-07-11 16:35:58 +00:00
|
|
|
int bufferWidth, bufferHeight;
|
|
|
|
glfwGetFramebufferSize(mWindow, &bufferWidth, &bufferHeight);
|
|
|
|
|
|
|
|
int windowWidth, windowHeight;
|
|
|
|
glfwGetWindowSize(mWindow, &windowWidth, &windowHeight);
|
|
|
|
|
|
|
|
// Compute the window to framebuffer ratio
|
|
|
|
mWindowToFramebufferRatio.x = float(bufferWidth) / float(windowWidth);
|
|
|
|
mWindowToFramebufferRatio.y = float(bufferHeight) / float(windowHeight);
|
2015-06-01 21:40:44 +00:00
|
|
|
|
2015-07-25 00:26:26 +00:00
|
|
|
// Set the viewport of the scene
|
|
|
|
mCurrentScene->setViewport(mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH,
|
|
|
|
0,
|
|
|
|
bufferWidth - mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH,
|
|
|
|
bufferHeight - mWindowToFramebufferRatio.y * HEADER_HEIGHT);
|
2015-06-01 21:40:44 +00:00
|
|
|
|
2015-04-08 18:47:55 +00:00
|
|
|
// Render the scene
|
|
|
|
mCurrentScene->render();
|
|
|
|
|
|
|
|
// Display the GUI
|
2015-06-01 21:40:44 +00:00
|
|
|
Gui::getInstance().render();
|
2015-04-08 18:47:55 +00:00
|
|
|
|
|
|
|
// Check the OpenGL errors
|
|
|
|
checkOpenGLErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the dimension of the camera viewport
|
|
|
|
void TestbedApplication::reshape() {
|
|
|
|
|
|
|
|
// Get the framebuffer dimension
|
|
|
|
int width, height;
|
|
|
|
glfwGetFramebufferSize(mWindow, &width, &height);
|
|
|
|
|
2015-04-15 21:11:27 +00:00
|
|
|
// Resize the camera viewport
|
2015-06-01 21:40:44 +00:00
|
|
|
mCurrentScene->reshape(width - LEFT_PANE_WIDTH, height - HEADER_HEIGHT);
|
2015-04-08 18:47:55 +00:00
|
|
|
|
2015-04-15 21:11:27 +00:00
|
|
|
// Update the window size of the scene
|
|
|
|
int windowWidth, windowHeight;
|
|
|
|
glfwGetWindowSize(mWindow, &windowWidth, &windowHeight);
|
|
|
|
mCurrentScene->setWindowDimension(windowWidth, windowHeight);
|
2015-04-08 18:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Start the main loop where rendering occur
|
|
|
|
void TestbedApplication::startMainLoop() {
|
|
|
|
|
|
|
|
// Loop until the user closes the window
|
|
|
|
while (!glfwWindowShouldClose(mWindow)) {
|
|
|
|
|
2015-05-24 15:23:55 +00:00
|
|
|
checkOpenGLErrors();
|
|
|
|
|
2015-04-08 18:47:55 +00:00
|
|
|
// Reshape the viewport
|
|
|
|
reshape();
|
|
|
|
|
|
|
|
// Call the update function
|
|
|
|
update();
|
|
|
|
|
2015-04-15 21:11:27 +00:00
|
|
|
// Render the application
|
|
|
|
render();
|
|
|
|
|
2015-04-08 18:47:55 +00:00
|
|
|
// Swap front and back buffers
|
|
|
|
glfwSwapBuffers(mWindow);
|
|
|
|
|
|
|
|
// Process events
|
|
|
|
glfwPollEvents();
|
2015-05-24 15:23:55 +00:00
|
|
|
|
|
|
|
checkOpenGLErrors();
|
2015-04-08 18:47:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-01 21:40:44 +00:00
|
|
|
// Change the current scene
|
|
|
|
void TestbedApplication::switchScene(Scene* newScene) {
|
|
|
|
|
|
|
|
if (newScene == mCurrentScene) return;
|
|
|
|
|
|
|
|
mCurrentScene = newScene;
|
|
|
|
|
2015-07-11 16:35:58 +00:00
|
|
|
// Get the engine settings of the scene
|
|
|
|
float currentTimeStep = mEngineSettings.timeStep;
|
|
|
|
mEngineSettings = mCurrentScene->getEngineSettings();
|
|
|
|
mEngineSettings.timeStep = currentTimeStep;
|
|
|
|
|
2015-06-01 21:40:44 +00:00
|
|
|
// Reset the scene
|
|
|
|
mCurrentScene->reset();
|
|
|
|
}
|
|
|
|
|
2015-04-08 18:47:55 +00:00
|
|
|
// Check the OpenGL errors
|
|
|
|
void TestbedApplication::checkOpenGLErrors() {
|
|
|
|
GLenum glError;
|
|
|
|
|
|
|
|
// Get the OpenGL errors
|
|
|
|
glError = glGetError();
|
|
|
|
|
|
|
|
// While there are errors
|
|
|
|
while (glError != GL_NO_ERROR) {
|
|
|
|
|
|
|
|
// Get the error string
|
|
|
|
const GLubyte* stringError = gluErrorString(glError);
|
|
|
|
|
|
|
|
// Display the error
|
|
|
|
if (stringError)
|
2015-05-24 15:23:55 +00:00
|
|
|
std::cerr << "OpenGL Error #" << glError << "(" << gluErrorString(glError) << ")" << std::endl;
|
2015-04-08 18:47:55 +00:00
|
|
|
else
|
|
|
|
std::cerr << "OpenGL Error #" << glError << " (no message available)" << std::endl;
|
|
|
|
|
|
|
|
// Get the next error
|
|
|
|
glError = glGetError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Compute the FPS
|
|
|
|
void TestbedApplication::computeFPS() {
|
|
|
|
|
|
|
|
mNbFrames++;
|
|
|
|
|
|
|
|
// Get the number of milliseconds since glutInit called
|
|
|
|
mCurrentTime = glfwGetTime();
|
|
|
|
|
|
|
|
// Calculate time passed
|
|
|
|
double timeInterval = mCurrentTime - mPreviousTime;
|
|
|
|
|
|
|
|
// Update the FPS counter each second
|
|
|
|
if(timeInterval > 1.0) {
|
|
|
|
|
|
|
|
// calculate the number of frames per second
|
|
|
|
mFPS = static_cast<double>(mNbFrames) / timeInterval;
|
|
|
|
|
|
|
|
// Set time
|
|
|
|
mPreviousTime = mCurrentTime;
|
|
|
|
|
|
|
|
// Reset frame count
|
|
|
|
mNbFrames = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GLFW error callback method
|
|
|
|
void TestbedApplication::error_callback(int error, const char* description) {
|
|
|
|
fputs(description, stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display the GUI
|
|
|
|
void TestbedApplication::displayGUI() {
|
|
|
|
|
|
|
|
// Display the FPS
|
|
|
|
displayFPS();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display the FPS
|
|
|
|
void TestbedApplication::displayFPS() {
|
|
|
|
|
|
|
|
/*
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << mFPS;
|
|
|
|
std::string fpsString = ss.str();
|
|
|
|
std::string windowTitle = mWindowTitle + " | FPS : " + fpsString;
|
|
|
|
glfwSetWindowTitle(mWindow, windowTitle.c_str());
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callback method to receive keyboard events
|
|
|
|
void TestbedApplication::keyboard(GLFWwindow* window, int key, int scancode,
|
|
|
|
int action, int mods) {
|
|
|
|
getInstance().mCurrentScene->keyboardEvent(key, scancode, action, mods);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callback method to receive scrolling events
|
|
|
|
void TestbedApplication::scroll(GLFWwindow* window, double xAxis, double yAxis) {
|
2015-07-11 16:35:58 +00:00
|
|
|
|
|
|
|
// Update scroll on the GUI
|
|
|
|
Gui::getInstance().setScroll(xAxis, yAxis);
|
|
|
|
|
2015-04-15 21:11:27 +00:00
|
|
|
getInstance().mCurrentScene->scrollingEvent(xAxis, yAxis, SCROLL_SENSITIVITY);
|
2015-04-08 18:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Called when a mouse button event occurs
|
|
|
|
void TestbedApplication::mouseButton(GLFWwindow* window, int button, int action, int mods) {
|
2015-04-15 21:11:27 +00:00
|
|
|
|
|
|
|
// Get the mouse cursor position
|
|
|
|
double x, y;
|
|
|
|
glfwGetCursorPos(window, &x, &y);
|
|
|
|
|
|
|
|
getInstance().mCurrentScene->mouseButtonEvent(button, action, mods, x, y);
|
2015-04-08 18:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Called when a mouse motion event occurs
|
|
|
|
void TestbedApplication::mouseMotion(GLFWwindow* window, double x, double y) {
|
2015-04-15 21:11:27 +00:00
|
|
|
|
|
|
|
int leftButtonState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT);
|
|
|
|
int rightButtonState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT);
|
|
|
|
int middleButtonState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE);
|
|
|
|
int altKeyState = glfwGetKey(window, GLFW_KEY_LEFT_ALT);
|
|
|
|
|
|
|
|
getInstance().mCurrentScene->mouseMotionEvent(x, y, leftButtonState, rightButtonState,
|
|
|
|
middleButtonState, altKeyState);
|
2015-04-08 18:47:55 +00:00
|
|
|
}
|