From e29794d24f786427311a97df1a6b904381e4f5d4 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Sun, 28 Feb 2016 21:39:39 +0100 Subject: [PATCH] Working on new GUI using nanogui --- testbed/src/Gui.cpp | 702 ++++++++++++++--------------- testbed/src/Gui.h | 53 ++- testbed/src/TestbedApplication.cpp | 145 +----- testbed/src/TestbedApplication.h | 25 +- 4 files changed, 419 insertions(+), 506 deletions(-) diff --git a/testbed/src/Gui.cpp b/testbed/src/Gui.cpp index 438441b7..2387dfc8 100644 --- a/testbed/src/Gui.cpp +++ b/testbed/src/Gui.cpp @@ -26,8 +26,6 @@ // Libraries #include "Gui.h" #include -#include -#include #include "TestbedApplication.h" using namespace nanogui; @@ -41,9 +39,8 @@ double Gui::mCachedUpdateTime = 0; double Gui::mCachedPhysicsUpdateTime = 0; // Constructor -Gui::Gui(Screen* screen) { +Gui::Gui(TestbedApplication* app) : mApp(app) { - mScreen = screen; } // Destructor @@ -56,30 +53,18 @@ Gui::~Gui() { /// Initialize the GUI void Gui::init() { - bool bvar = true; - int ivar = 12345678; - double dvar = 3.1415926; - float fvar = (float)dvar; - std::string strval = "A string"; - nanogui::Color colval(0.5f, 0.5f, 0.7f, 1.f); + // Create the Simulation panel + createSimulationPanel(); - FormHelper *gui = new FormHelper(mScreen); - ref window = gui->addWindow(Eigen::Vector2i(10, 10), "Form helper example"); - gui->addGroup("Basic types"); - gui->addVariable("bool", bvar); - gui->addVariable("string", strval); + // Create the Settings panel + createSettingsPanel(); - gui->addGroup("Validating fields"); - gui->addVariable("int", ivar); - gui->addVariable("float", fvar); - gui->addVariable("double", dvar); + // Create the Profiling panel + createProfilingPanel(); - gui->addGroup("Other widgets"); - gui->addButton("A button", [](){ std::cout << "Button pressed." << std::endl; }); - - mScreen->setVisible(true); - mScreen->performLayout(); - window->center(); + mApp->setVisible(true); + mApp->performLayout(); + //window->center(); // Init UI @@ -91,348 +76,361 @@ void Gui::init() { mTimeSinceLastProfilingDisplay = glfwGetTime(); } -void Gui::displayLeftPane() { - - /* - TestbedApplication& app = TestbedApplication::getInstance(); - - const float scalingX = app.mWindowToFramebufferRatio.x; - const float scalingY = app.mWindowToFramebufferRatio.y; - - int windowWidth, windowHeight; - glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); - - int scrollarea = 0; - imguiBeginScrollArea(NULL, 0, app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), - app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, - app.mWindowToFramebufferRatio.y * LEFT_PANE_HEADER_HEIGHT, &scrollarea); - - // ------ Header ----- // - - imguiHorizontalSpace(scalingX * 2.5); - imguiVerticalSpace(scalingY * 5); - imguiStartLine(); - - const int button_width = app.mWindowToFramebufferRatio.x * 75.0f; - const int button_height = app.mWindowToFramebufferRatio.y * 20.0f; - - // Play/Pause - if (imguiButton(app.mTimer.isRunning() ? "Pause" : "Play", true, button_width, button_height, scalingX, scalingY)) { - app.togglePlayPauseSimulation(); - } - imguiHorizontalSpace(scalingX * 2.5); - - // Step - if (imguiButton("Step", !app.mTimer.isRunning(), button_width, button_height, scalingX, scalingY)) { - app.toggleTakeSinglePhysicsStep(); - } - imguiHorizontalSpace(scalingX * 2.5); - - // Restart - if (imguiButton("Restart", true, button_width, button_height, scalingX, scalingY)) { - app.restartSimulation(); - } - - imguiEndLine(); - imguiVerticalSpace(scalingY * 35); - - imguiSeparatorLine(); - imguiVerticalSpace(scalingY * 2.5); - imguiStartLine(); - - // ----- Left Pane Tabs ----- // - - int widthButton = app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH / 4.3; - if (imguiButton("Scenes", true, widthButton, button_height, scalingX, scalingY)) { - mLeftPane = SCENES; - } - imguiHorizontalSpace(scalingX * 2.5); - - if (imguiButton("Physics", true, widthButton, button_height, scalingX, scalingY)) { - mLeftPane = PHYSICS; - } - imguiHorizontalSpace(scalingX * 2.5); - - if (imguiButton("Rendering", true, widthButton, button_height, scalingX, scalingY)) { - mLeftPane = RENDERING; - } - imguiHorizontalSpace(scalingX * 2.5); - - if (imguiButton("Profiling", true, widthButton, button_height, scalingX, scalingY)) { - mLeftPane = PROFILING; - } - - imguiEndLine(); - imguiVerticalSpace(scalingY * (BUTTON_HEIGHT/2 + 15)); - imguiSeparatorLine(); - imguiEndScrollArea(); - - // Display the left pane content - switch(mLeftPane) { - case SCENES: displayScenesPane(); break; - case PHYSICS: displayPhysicsPane(); break; - case RENDERING: displayRenderingPane(); break; - case PROFILING: displayProfilingPane(); break; - } - */ -} - -// Display the list of scenes -void Gui::displayScenesPane() { - - /* - TestbedApplication& app = TestbedApplication::getInstance(); - - const float scalingX = app.mWindowToFramebufferRatio.x; - const float scalingY = app.mWindowToFramebufferRatio.y; - - static int choice = 0; - int startChoice = choice; - int scrollarea = 1; - int windowWidth, windowHeight; - glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); - - std::vector scenes = app.getScenes(); - - imguiBeginScrollArea("Scenes", 0, 0, - app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, - app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), - &scrollarea); - - imguiVerticalSpace(15); - - // For each scene - for (int i=0; igetName().c_str(), choice == i, true, scalingX, scalingY)) { - choice = i; - } - } - if (choice != startChoice) { - app.switchScene(scenes[choice]); - } - - imguiEndScrollArea(); - */ -} - -void Gui::displayPhysicsPane() { - - /* - TestbedApplication& app = TestbedApplication::getInstance(); - - const float scalingX = app.mWindowToFramebufferRatio.x; - const float scalingY = app.mWindowToFramebufferRatio.y; - - int windowWidth, windowHeight; - glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); - - int scrollarea = 2; - imguiBeginScrollArea("Physics Engine Parameters", 0, 0, - app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, - app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), - &scrollarea); - - imguiVerticalSpace(10 * scalingY); - - // Enabled/Disable Sleeping - bool toggle = imguiCheck("Sleeping enabled", app.mEngineSettings.isSleepingEnabled, true, scalingX, scalingY); - if (toggle) { - app.mEngineSettings.isSleepingEnabled = !app.mEngineSettings.isSleepingEnabled; - } - - // Enabled/Disable Gravity - toggle = imguiCheck("Gravity enabled", app.mEngineSettings.isGravityEnabled, true, scalingX, scalingY); - if (toggle) { - app.mEngineSettings.isGravityEnabled = !app.mEngineSettings.isGravityEnabled; - } - - imguiVerticalSpace(10 * scalingY); - - // Timestep - float timeStep = app.mEngineSettings.timeStep; - if (imguiSlider("Timestep", &timeStep, 0.001f, 1.0f, 0.001f, true, scalingX, scalingY)) { - app.mEngineSettings.timeStep = timeStep; - } - - // Nb velocity solver iterations - float nbVelocityIterations = static_cast(app.mEngineSettings.nbVelocitySolverIterations); - if (imguiSlider("Velocity Solver Iterations", &nbVelocityIterations, 1.0f, 100.0f, 1.0f, true, scalingX, scalingY)) { - app.mEngineSettings.nbVelocitySolverIterations = static_cast(nbVelocityIterations); - } - - // Nb position solver iterations - float nbPositionIterations = static_cast(app.mEngineSettings.nbPositionSolverIterations); - if (imguiSlider("Position Solver Iterations", &nbPositionIterations, 1.0f, 100.0f, 1.0f, true, scalingX, scalingY)) { - app.mEngineSettings.nbPositionSolverIterations = static_cast(nbPositionIterations); - } - - // Time before sleep - float timeBeforeSleep = app.mEngineSettings.timeBeforeSleep; - if (imguiSlider("Time before sleep", &timeBeforeSleep, 0.0f, 60.0f, 0.5f, true, scalingX, scalingY)) { - app.mEngineSettings.timeBeforeSleep = timeBeforeSleep; - } - - // Sleep linear velocity - float sleepLinearVelocity = app.mEngineSettings.sleepLinearVelocity; - if (imguiSlider("Sleep linear velocity", &sleepLinearVelocity, 0.0f, 30.0f, 0.5f, true, scalingX, scalingY)) { - app.mEngineSettings.sleepLinearVelocity = sleepLinearVelocity; - } - - // Sleep angular velocity - float sleepAngularVelocity = app.mEngineSettings.sleepAngularVelocity; - if (imguiSlider("Sleep angular velocity", &sleepAngularVelocity, 0.0f, 30.0f, 0.5f, true, scalingX, scalingY)) { - app.mEngineSettings.sleepAngularVelocity = sleepAngularVelocity; - } - - // Gravity vector - openglframework::Vector3 gravity = app.mEngineSettings.gravity; - float gravityX = gravity.x, gravityY = gravity.y, gravityZ = gravity.z; - if (imguiSlider("Gravity X", &gravityX, -50.0f, 50.0f, 0.5f, true, scalingX, scalingY)) { - app.mEngineSettings.gravity.x = gravityX; - } - if (imguiSlider("Gravity Y", &gravityY, -50.0f, 50.0f, 0.5f, true, scalingX, scalingY)) { - app.mEngineSettings.gravity.y = gravityY; - } - if (imguiSlider("Gravity Z", &gravityZ, -50.0f, 50.0f, 0.5f, true, scalingX, scalingY)) { - app.mEngineSettings.gravity.z = gravityZ; - } - - imguiEndScrollArea(); - - */ -} - -void Gui::displayRenderingPane() { - - /* - TestbedApplication& app = TestbedApplication::getInstance(); - - const float scalingX = app.mWindowToFramebufferRatio.x; - const float scalingY = app.mWindowToFramebufferRatio.y; - - int windowWidth, windowHeight; - glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); - - int scrollarea = 2; - imguiBeginScrollArea("Rendering Parameters", 0, 0, - app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, - app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), - &scrollarea); - - imguiVerticalSpace(15); - - // Display/Hide contact points - bool toggleContactPoints = imguiCheck("Contacts", app.mIsContactPointsDisplayed, true, scalingX, scalingY); - if (toggleContactPoints) { - app.displayContactPoints(!app.mIsContactPointsDisplayed); - } - - // Enabled/Disable VSync - bool toggleVSync = imguiCheck("V Sync", app.mIsVSyncEnabled, true, scalingX, scalingY); - if (toggleVSync) { - app.enableVSync(!app.mIsVSyncEnabled); - } - - // Enabled/Disable Shadows - bool toggleShadows = imguiCheck("Shadows", app.mIsShadowMappingEnabled, true, scalingX, scalingY); - if (toggleShadows) { - app.enableShadows(!app.mIsShadowMappingEnabled); - } - - imguiEndScrollArea(); - */ -} - -void Gui::displayProfilingPane() { - - /* - TestbedApplication& app = TestbedApplication::getInstance(); - - const float scalingX = app.mWindowToFramebufferRatio.x; - const float scalingY = app.mWindowToFramebufferRatio.y; +// Update the GUI +void Gui::update() { double currentTime = glfwGetTime(); if ((currentTime - mTimeSinceLastProfilingDisplay) > TIME_INTERVAL_DISPLAY_PROFILING_INFO) { mTimeSinceLastProfilingDisplay = currentTime; - mCachedFPS = app.mFPS; - mCachedUpdateTime = app.mUpdateTime; - mCachedPhysicsUpdateTime = app.mPhysicsUpdateTime; + mCachedFPS = mApp->mFPS; + mCachedUpdateTime = mApp->mUpdateTime; + mCachedPhysicsUpdateTime = mApp->mPhysicsUpdateTime; } - int windowWidth, windowHeight; - glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); - - int scrollarea = 2; - imguiBeginScrollArea("Profiling", 0, 0, - app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, - app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), - &scrollarea); - - imguiVerticalSpace(15); - // Framerate (FPS) - std::stringstream ss; - ss << std::setprecision(4) << mCachedFPS; - std::string fps = std::string("FPS : ") + ss.str(); - imguiItem(fps.c_str(), true, scalingX, scalingY); + mFPSLabel->setCaption(std::string("FPS : ") + floatToString(mCachedFPS, 0)); // Update time - std::stringstream ss1; - double updateTime = mCachedUpdateTime * 1000.0; - ss1 << std::setprecision(4) << updateTime; - std::string updateTimeStr = std::string("Update time (ms) : ") + ss1.str(); - imguiItem(updateTimeStr.c_str(), true, scalingX, scalingY); + mUpdateTimeLabel->setCaption(std::string("Update time (ms) : ") + floatToString(mCachedUpdateTime * 1000.0, 1)); - // Update time (physics) - std::stringstream ss2; - ss2 << std::setprecision(4) << (mCachedPhysicsUpdateTime * 1000.0); - std::string updatePhysicsTimeStr = std::string("Update physics time (ms) : ") + ss2.str(); - imguiItem(updatePhysicsTimeStr.c_str(), true, scalingX, scalingY); + // Update time + mUpdatePhysicsTimeLabel->setCaption("Update physics time (ms) : " + floatToString(mCachedPhysicsUpdateTime * 1000.0, 1)); - imguiEndScrollArea(); - */ } -// Display the GUI -void Gui::render() { +void Gui::createSimulationPanel() { - /* - TestbedApplication& app = TestbedApplication::getInstance(); + mSimulationPanel = new Window(mApp, "Simulation"); + mSimulationPanel->setPosition(Vector2i(15, 15)); + mSimulationPanel->setLayout(new GroupLayout(10, 5, 10 , 20)); + mSimulationPanel->setId("SimulationPanel"); + mSimulationPanel->setFixedWidth(220); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_DEPTH_TEST); + // Scenes/Physics/Rendering buttons + Label* labelControls = new Label(mSimulationPanel, "Controls","sans-bold"); + Widget* panelControls = new Widget(mSimulationPanel); + panelControls->setLayout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 0, 5)); + ToolButton* buttonPlay = new ToolButton(panelControls, ENTYPO_ICON_PLAY); + buttonPlay->setFlags(Button::NormalButton); + buttonPlay->setCallback([&] { + mApp->playSimulation(); + }); + ToolButton* buttonPause = new ToolButton(panelControls, ENTYPO_ICON_PAUS); + buttonPause->setFlags(Button::NormalButton); + buttonPause->setCallback([&] { + mApp->pauseSimulation(); + }); + ToolButton* buttonPlayStep = new ToolButton(panelControls, ENTYPO_ICON_TO_END); + buttonPlayStep->setFlags(Button::NormalButton); + buttonPlayStep->setCallback([&] { + mApp->toggleTakeSinglePhysicsStep(); + }); + ToolButton* buttonRestart = new ToolButton(panelControls, ENTYPO_ICON_CCW); + buttonRestart->setFlags(Button::NormalButton); + buttonRestart->setCallback([&] { + mApp->restartSimulation(); + }); - int display_w, display_h; - glfwGetFramebufferSize(mWindow, &display_w, &display_h); - - int windowWidth, windowHeight; - glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); - - // Mouse position - double mouseX, mouseY; - glfwGetCursorPos(mWindow, &mouseX, &mouseY); - - // Mouse buttons - int leftButton = glfwGetMouseButton(mWindow, GLFW_MOUSE_BUTTON_LEFT); - unsigned char mousebutton = 0; - if(leftButton == GLFW_PRESS ) { - mousebutton |= IMGUI_MBUT_LEFT; + // Scenes + std::vector scenes = mApp->getScenes(); + std::vector scenesNames; + for (int i=0; igetName().c_str()); } - - imguiBeginFrame(app.mWindowToFramebufferRatio.x * mouseX, - app.mWindowToFramebufferRatio.y * (windowHeight - mouseY), mousebutton, - - app.mWindowToFramebufferRatio.y * mScrollY); - resetScroll(); - - //displayHeader(); - displayLeftPane(); - - imguiEndFrame(); - - imguiRenderGLDraw(display_w, display_h); - - */ + Label* labelScenes = new Label(mSimulationPanel, "Scene","sans-bold"); + ComboBox* comboBoxScenes = new ComboBox(mSimulationPanel, scenesNames); + comboBoxScenes->setFixedWidth(150); + comboBoxScenes->setCallback([&, scenes](int index){ + mApp->switchScene(scenes[index]); + }); } + +void Gui::createSettingsPanel() { + + mSettingsPanel = new Window(mApp, "Settings"); + mSettingsPanel->setPosition(Vector2i(15, 180)); + mSettingsPanel->setLayout(new BoxLayout(Orientation::Vertical, Alignment::Middle, 10, 5)); + mSettingsPanel->setId("SettingsPanel"); + mSettingsPanel->setFixedWidth(220); + + // Scenes/Physics/Rendering buttons + Widget* buttonsPanel = new Widget(mSettingsPanel); + buttonsPanel->setLayout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 5, 5)); + Button* buttonPhysics = new Button(buttonsPanel, "Physics"); + buttonPhysics->setFlags(Button::RadioButton); + buttonPhysics->setPushed(true); + buttonPhysics->setChangeCallback([&](bool state) { + mPhysicsPanel->setVisible(true); + mRenderingPanel->setVisible(false); + mApp->performLayout(); + }); + Button* buttonRendering = new Button(buttonsPanel, "Rendering"); + buttonRendering->setFlags(Button::RadioButton); + buttonRendering->setChangeCallback([&](bool state) { + mRenderingPanel->setVisible(true); + mPhysicsPanel->setVisible(false); + mApp->performLayout(); + }); + + // ---------- Physics Panel ---------- + mPhysicsPanel = new Widget(mSettingsPanel); + mPhysicsPanel->setLayout(new BoxLayout(Orientation::Vertical, Alignment::Fill, 0, 5)); + + // Enable/Disable sleeping + CheckBox* checkboxSleeping = new CheckBox(mPhysicsPanel, "Sleeping enabled"); + checkboxSleeping->setChecked(mApp->mEngineSettings.isSleepingEnabled); + checkboxSleeping->setCallback([&](bool value) { + mApp->mEngineSettings.isSleepingEnabled = value; + }); + + // Enabled/Disable Gravity + CheckBox* checkboxGravity = new CheckBox(mPhysicsPanel, "Gravity enabled"); + checkboxGravity->setChecked(mApp->mEngineSettings.isGravityEnabled); + checkboxGravity->setCallback([&](bool value) { + mApp->mEngineSettings.isGravityEnabled = value; + }); + + // Timestep + Widget* panelTimeStep = new Widget(mPhysicsPanel); + panelTimeStep->setLayout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 0, 5)); + Label* labelTimeStep = new Label(panelTimeStep, "Time step","sans-bold"); + labelTimeStep->setFixedWidth(120); + TextBox* textboxTimeStep = new TextBox(panelTimeStep); + textboxTimeStep->setFixedSize(Vector2i(70, 25)); + textboxTimeStep->setEditable(true); + std::ostringstream out; + out << std::setprecision(1) << std::fixed << (mApp->mEngineSettings.timeStep * 1000); + textboxTimeStep->setValue(out.str()); + textboxTimeStep->setUnits("ms"); + textboxTimeStep->setCallback([&, textboxTimeStep](const std::string &str) { + + try { + float value = std::stof(str); + std::ostringstream out; + out << std::setprecision(1) << std::fixed << std::showpoint << value; + float finalValue = std::stof(out.str()); + + if (finalValue < 1 || finalValue > 1000) return false; + + mApp->mEngineSettings.timeStep = finalValue / 1000.0f; + textboxTimeStep->setValue(out.str()); + } + catch (...) { + return false; + } + + return true; + }); + textboxTimeStep->setFontSize(16); + textboxTimeStep->setAlignment(TextBox::Alignment::Right); + + // Velocity solver iterations + Widget* panelVelocityIterations = new Widget(mPhysicsPanel); + panelVelocityIterations->setLayout(new BoxLayout(Orientation::Horizontal, Alignment::Fill, 0, 5)); + Label* labelVelocityIterations = new Label(panelVelocityIterations, "Velocity solver","sans-bold"); + labelVelocityIterations->setFixedWidth(120); + TextBox* textboxVelocityIterations = new TextBox(panelVelocityIterations); + textboxVelocityIterations->setFixedSize(Vector2i(70, 25)); + textboxVelocityIterations->setEditable(true); + textboxVelocityIterations->setValue(std::to_string(mApp->mEngineSettings.nbVelocitySolverIterations)); + textboxVelocityIterations->setUnits("iter"); + textboxVelocityIterations->setCallback([&, textboxVelocityIterations](const std::string &str) { + + try { + float value = std::stof(str); + + if (value < 1 || value > 1000) return false; + + mApp->mEngineSettings.nbVelocitySolverIterations = value; + textboxVelocityIterations->setValue(out.str()); + } + catch (...) { + return false; + } + + return true; + }); + textboxVelocityIterations->setFontSize(16); + textboxVelocityIterations->setAlignment(TextBox::Alignment::Right); + + // Position solver iterations + Widget* panelPositionIterations = new Widget(mPhysicsPanel); + panelPositionIterations->setLayout(new BoxLayout(Orientation::Horizontal, Alignment::Fill, 0, 5)); + Label* labelPositionIterations = new Label(panelPositionIterations, "Position solver","sans-bold"); + labelPositionIterations->setFixedWidth(120); + TextBox* textboxPositionIterations = new TextBox(panelPositionIterations); + textboxPositionIterations->setFixedSize(Vector2i(70, 25)); + textboxPositionIterations->setEditable(true); + textboxPositionIterations->setValue(std::to_string(mApp->mEngineSettings.nbPositionSolverIterations)); + textboxPositionIterations->setUnits("iter"); + textboxPositionIterations->setCallback([&, textboxPositionIterations](const std::string &str) { + + try { + float value = std::stof(str); + + if (value < 1 || value > 1000) return false; + + mApp->mEngineSettings.nbPositionSolverIterations = value; + textboxPositionIterations->setValue(out.str()); + } + catch (...) { + return false; + } + + return true; + }); + textboxPositionIterations->setFontSize(16); + textboxPositionIterations->setAlignment(TextBox::Alignment::Right); + + // Time before sleep + Widget* panelTimeSleep = new Widget(mPhysicsPanel); + panelTimeSleep->setLayout(new BoxLayout(Orientation::Horizontal, Alignment::Fill, 0, 5)); + Label* labelTimeSleep = new Label(panelTimeSleep, "Time before sleep","sans-bold"); + labelTimeSleep->setFixedWidth(120); + out.str(""); + out << std::setprecision(0) << std::fixed << (mApp->mEngineSettings.timeBeforeSleep * 1000); + TextBox* textboxTimeSleep = new TextBox(panelTimeSleep); + textboxTimeSleep->setFixedSize(Vector2i(70, 25)); + textboxTimeSleep->setEditable(true); + textboxTimeSleep->setValue(out.str()); + textboxTimeSleep->setUnits("ms"); + textboxTimeSleep->setCallback([&, textboxTimeSleep](const std::string &str) { + + try { + float value = std::stof(str); + std::ostringstream out; + out << std::setprecision(0) << std::fixed << std::showpoint << value; + float finalValue = std::stof(out.str()); + + if (finalValue < 1 || finalValue > 100000) return false; + + mApp->mEngineSettings.timeBeforeSleep = finalValue / 1000.0f; + textboxTimeSleep->setValue(out.str()); + } + catch (...) { + return false; + } + + return true; + }); + textboxTimeSleep->setFontSize(16); + textboxTimeSleep->setAlignment(TextBox::Alignment::Right); + + + // Sleep linear velocity + Widget* panelSleepLinearVel = new Widget(mPhysicsPanel); + panelSleepLinearVel->setLayout(new GridLayout(Orientation::Horizontal, 2, Alignment::Middle, 5, 5)); + Label* labelSleepLinearVel = new Label(panelSleepLinearVel, "Sleep linear velocity","sans-bold"); + labelSleepLinearVel->setFixedWidth(120); + out.str(""); + out << std::setprecision(1) << std::fixed << (mApp->mEngineSettings.sleepLinearVelocity); + TextBox* textboxSleepLinearVel = new TextBox(panelSleepLinearVel); + textboxSleepLinearVel->setFixedSize(Vector2i(70, 25)); + textboxSleepLinearVel->setEditable(true); + textboxSleepLinearVel->setValue(out.str()); + textboxSleepLinearVel->setUnits("m/s"); + textboxSleepLinearVel->setCallback([&, textboxSleepLinearVel](const std::string &str) { + + try { + float value = std::stof(str); + std::ostringstream out; + out << std::setprecision(1) << std::fixed << std::showpoint << value; + float finalValue = std::stof(out.str()); + + if (finalValue < 0 || finalValue > 10000) return false; + + mApp->mEngineSettings.sleepLinearVelocity = finalValue; + textboxSleepLinearVel->setValue(out.str()); + } + catch (...) { + return false; + } + + return true; + }); + textboxSleepLinearVel->setFontSize(16); + textboxSleepLinearVel->setAlignment(TextBox::Alignment::Right); + + // Sleep angular velocity + Widget* panelSleepAngularVel = new Widget(mPhysicsPanel); + panelSleepAngularVel->setLayout(new GridLayout(Orientation::Horizontal, 2, Alignment::Fill, 5, 5)); + Label* labelSleepAngularVel = new Label(panelSleepAngularVel, "Sleep angular velocity","sans-bold"); + labelSleepAngularVel->setFixedWidth(120); + out.str(""); + out << std::setprecision(0) << std::fixed << (mApp->mEngineSettings.sleepAngularVelocity); + TextBox* textboxSleepAngularVel = new TextBox(panelSleepAngularVel); + textboxSleepAngularVel->setFixedSize(Vector2i(70, 25)); + textboxSleepAngularVel->setEditable(true); + textboxSleepAngularVel->setValue(out.str()); + textboxSleepAngularVel->setUnits("m/s"); + textboxSleepAngularVel->setCallback([&, textboxSleepAngularVel](const std::string &str) { + + try { + float value = std::stof(str); + std::ostringstream out; + out << std::setprecision(0) << std::fixed << std::showpoint << value; + float finalValue = std::stof(out.str()); + + if (finalValue < 0 || finalValue > 10000) return false; + + mApp->mEngineSettings.sleepAngularVelocity = finalValue; + textboxSleepAngularVel->setValue(out.str()); + } + catch (...) { + return false; + } + + return true; + }); + textboxSleepAngularVel->setFontSize(16); + textboxSleepAngularVel->setAlignment(TextBox::Alignment::Right); + + // ---------- Rendering Panel ---------- + mRenderingPanel = new Widget(mSettingsPanel); + mRenderingPanel->setLayout(new BoxLayout(Orientation::Vertical, Alignment::Fill, 0, 5)); + + // Display/Hide contact points + CheckBox* checkboxContactPoints = new CheckBox(mRenderingPanel, "Contact points"); + checkboxContactPoints->setChecked(mApp->mIsContactPointsDisplayed); + checkboxContactPoints->setCallback([&](bool value) { + mApp->mIsContactPointsDisplayed = value; + }); + + // Enabled/Disable VSync + CheckBox* checkboxVSync = new CheckBox(mRenderingPanel, "V-Sync"); + checkboxVSync->setChecked(mApp->mIsVSyncEnabled); + checkboxVSync->setCallback([&](bool value) { + mApp->mIsVSyncEnabled = value; + }); + + // Enabled/Disable Shadows + CheckBox* checkboxShadows = new CheckBox(mRenderingPanel, "Shadows"); + checkboxShadows->setChecked(mApp->mIsShadowMappingEnabled); + checkboxShadows->setCallback([&](bool value) { + mApp->mIsShadowMappingEnabled = value; + }); + + mPhysicsPanel->setVisible(true); + mRenderingPanel->setVisible(false); +} + +void Gui::createProfilingPanel() { + + Widget* profilingPanel = new Window(mApp, "Profiling"); + profilingPanel->setPosition(Vector2i(15, 530)); + profilingPanel->setLayout(new BoxLayout(Orientation::Vertical, Alignment::Fill, 10, 5)); + profilingPanel->setId("SettingsPanel"); + profilingPanel->setFixedWidth(220); + + // Framerate (FPS) + mFPSLabel = new Label(profilingPanel, std::string("FPS : ") + floatToString(mCachedFPS, 0),"sans-bold"); + + // Update time + mUpdateTimeLabel = new Label(profilingPanel, std::string("Update time (ms) : ") + floatToString(mCachedUpdateTime * 1000.0, 1),"sans-bold"); + + // Update time + mUpdatePhysicsTimeLabel = new Label(profilingPanel, "Update physics time (ms) : " + floatToString(mCachedPhysicsUpdateTime * 1000.0, 1),"sans-bold"); + + profilingPanel->setVisible(true); +} + diff --git a/testbed/src/Gui.h b/testbed/src/Gui.h index 4b4d66cb..26fc5bcc 100644 --- a/testbed/src/Gui.h +++ b/testbed/src/Gui.h @@ -30,11 +30,14 @@ #include #include #include "openglframework.h" - +#include +#include using namespace openglframework; using namespace nanogui; +const double TIME_INTERVAL_DISPLAY_PROFILING_INFO = 1; + // Declarations class TestbedApplication; @@ -50,21 +53,28 @@ class Gui { // -------------------- Attributes -------------------- // - Screen* mScreen; + // Pointer to the application + TestbedApplication* mApp; static double mScrollX, mScrollY; + // Simulation panel + Widget* mSimulationPanel; + + // Settings Panel + Widget* mSettingsPanel; + Widget* mPhysicsPanel; + Widget* mRenderingPanel; + + // Profiling panel + Label* mFPSLabel; + Label* mUpdateTimeLabel; + Label* mUpdatePhysicsTimeLabel; + + std::vector mCheckboxesScenes; + // -------------------- Methods -------------------- // - static void displayLeftPane(); - - /// Display the list of scenes - static void displayScenesPane(); - - static void displayPhysicsPane(); - static void displayRenderingPane(); - static void displayProfilingPane(); - static void resetScroll(); /// Current time (in seconds) from last profiling time display @@ -79,13 +89,23 @@ class Gui { // Cached update physics time static double mCachedPhysicsUpdateTime; + // -------------------- Methods -------------------- // + + void createSimulationPanel(); + + void createSettingsPanel(); + + void createProfilingPanel(); + + // Convert float value to string + std::string floatToString(float value, int precision); public : // -------------------- Methods -------------------- // /// Constructor - Gui(Screen* screen); + Gui(TestbedApplication* app); /// Destructor ~Gui(); @@ -93,6 +113,9 @@ class Gui { /// Initialize the GUI void init(); + /// Update the GUI + void update(); + /// Display the GUI void render(); @@ -109,4 +132,10 @@ inline void Gui::setScroll(double scrollX, double scrollY) { mScrollY = scrollY; } +inline std::string Gui::floatToString(float value, int precision) { + std::stringstream ss; + ss << std::fixed << std::setprecision(precision) << value; + return ss.str(); +} + #endif diff --git a/testbed/src/TestbedApplication.cpp b/testbed/src/TestbedApplication.cpp index d560b26a..eca42a78 100644 --- a/testbed/src/TestbedApplication.cpp +++ b/testbed/src/TestbedApplication.cpp @@ -83,83 +83,12 @@ TestbedApplication::~TestbedApplication() { // Initialize the viewer void TestbedApplication::init() { - mGui.init(); - - /* - - { - - // Set the GLFW error callback method - glfwSetErrorCallback(error_callback); - - // Initialize the GLFW library - if (!glfwInit()) { - std::exit(EXIT_FAILURE); - } - - // 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); - - // 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); - - // Vertical Synchronization - enableVSync(mIsVSyncEnabled); - - // Initialize the GLEW library - glewExperimental = GL_TRUE; - 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) - glClearColor(0, 0, 0, 1.0); - - // Create all the scenes - createScenes(); - - Gui::getInstance().setWindow(mScreen); - - // Init the GUI - Gui::getInstance().init(); - - mTimer.start(); - - } - */ - // Create all the scenes createScenes(); + // Initialize the GUI + mGui.init(); + mTimer.start(); mIsInitialized = true; @@ -296,6 +225,8 @@ void TestbedApplication::drawContents() { // Compute the current framerate computeFPS(); + mGui.update(); + // Check the OpenGL errors checkOpenGLErrors(); } @@ -320,72 +251,12 @@ bool TestbedApplication::resizeEvent(const Vector2i& size) { return true; } -// Render -/* -void TestbedApplication::render() { - - int bufferWidth, bufferHeight; - glfwGetFramebufferSize(mGLFWWindow, &bufferWidth, &bufferHeight); - - int windowWidth, windowHeight; - glfwGetWindowSize(mGLFWWindow, &windowWidth, &windowHeight); - - // Compute the window to framebuffer ratio - mWindowToFramebufferRatio.x = float(bufferWidth) / float(windowWidth); - mWindowToFramebufferRatio.y = float(bufferHeight) / float(windowHeight); - - // Set the viewport of the scene - mCurrentScene->setViewport(mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, - 0, - bufferWidth - mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, - bufferHeight); - - // Render the scene - mCurrentScene->render(); - - // Display the GUI - Gui::getInstance().render(); - - // Check the OpenGL errors - checkOpenGLErrors(); -} -*/ - // Set the dimension of the camera viewport void TestbedApplication::reshape() { } -/* -// Start the main loop where rendering occur -void TestbedApplication::startMainLoop() { - - // Loop until the user closes the window - while (!glfwWindowShouldClose(mGLFWWindow)) { - - checkOpenGLErrors(); - - // Call the update function - update(); - - // Render the application - //render(); - - // Swap front and back buffers - glfwSwapBuffers(mGLFWWindow); - - // Process events - glfwPollEvents(); - - // Compute the current framerate - computeFPS(); - - checkOpenGLErrors(); - } -} -*/ - // Change the current scene void TestbedApplication::switchScene(Scene* newScene) { @@ -474,6 +345,12 @@ bool TestbedApplication::keyboardEvent(int key, int scancode, int action, int mo return true; } + // Close application on escape key + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { + glfwSetWindowShouldClose(mGLFWWindow, GL_TRUE); + return true; + } + return mCurrentScene->keyboardEvent(key, scancode, action, modifiers); } diff --git a/testbed/src/TestbedApplication.h b/testbed/src/TestbedApplication.h index 117cc31d..dec37de3 100644 --- a/testbed/src/TestbedApplication.h +++ b/testbed/src/TestbedApplication.h @@ -42,7 +42,6 @@ using namespace nanogui; const float DEFAULT_TIMESTEP = 1.0f / 60.0f; /// Class TestbedApplication -/// Singleton class representing the application. class TestbedApplication : public Screen { private : @@ -130,9 +129,6 @@ class TestbedApplication : public Screen { /// Called when the windows is reshaped void reshape(); - /// Render - //void render(); - /// Check the OpenGL errors static void checkOpenGLErrorsInternal(const char* file, int line); @@ -166,6 +162,12 @@ class TestbedApplication : public Screen { /// Start/stop the simulation void togglePlayPauseSimulation(); + /// Play the simulation + void playSimulation(); + + /// Pause the simulation + void pauseSimulation(); + /// Restart the simulation void restartSimulation(); @@ -208,9 +210,6 @@ class TestbedApplication : public Screen { /// Initialize the application void init(); - /// Start the main loop where rendering occur - //void startMainLoop(); - /// Change the current scene void switchScene(Scene* newScene); @@ -227,7 +226,7 @@ inline std::vector TestbedApplication::getScenes() { return mScenes; } -// Start the simulation +// Toggle play/pause for the simulation inline void TestbedApplication::togglePlayPauseSimulation() { if (mTimer.isRunning()) { @@ -238,6 +237,16 @@ inline void TestbedApplication::togglePlayPauseSimulation() { } } +// Play the simulation +inline void TestbedApplication::playSimulation() { + if (!mTimer.isRunning()) mTimer.start(); +} + +// Pause the simulation +inline void TestbedApplication::pauseSimulation() { + if (mTimer.isRunning()) mTimer.stop(); +} + // Restart the simulation inline void TestbedApplication::restartSimulation() { mCurrentScene->reset();