diff --git a/testbed/src/Gui.cpp b/testbed/src/Gui.cpp index 2d844c7c..db36a49c 100644 --- a/testbed/src/Gui.cpp +++ b/testbed/src/Gui.cpp @@ -139,6 +139,7 @@ void Gui::createSimulationPanel() { new Label(mSimulationPanel, "Scene","sans-bold"); ComboBox* comboBoxScenes = new ComboBox(mSimulationPanel, scenesNames); comboBoxScenes->set_fixed_width(150); + comboBoxScenes->popup()->child_at(0)->set_fixed_height(800); comboBoxScenes->set_callback([&, scenes](int index) { mApp->switchScene(scenes[index]); }); diff --git a/testbed/src/Scene.cpp b/testbed/src/Scene.cpp index fe2b8b71..9c6ec92f 100644 --- a/testbed/src/Scene.cpp +++ b/testbed/src/Scene.cpp @@ -91,8 +91,7 @@ bool Scene::mapMouseCoordinatesToSphere(double xMouse, double yMouse, } // Called when a mouse button event occurs -bool Scene::mouseButtonEvent(int button, bool down, int mods, - double mousePosX, double mousePosY) { +bool Scene::mouseButtonEvent(int button, bool down, int mods, double mousePosX, double mousePosY) { // If the mouse button is pressed if (down) { diff --git a/testbed/src/Scene.h b/testbed/src/Scene.h index 58e31f83..14e7bfbb 100644 --- a/testbed/src/Scene.h +++ b/testbed/src/Scene.h @@ -199,8 +199,7 @@ class Scene : public rp3d::EventListener { virtual bool keyboardEvent(int key, int scancode, int action, int mods); /// Called when a mouse button event occurs - virtual bool mouseButtonEvent(int button, bool down, int mods, - double mousePosX, double mousePosY); + virtual bool mouseButtonEvent(int button, bool down, int mods, double mousePosX, double mousePosY); /// Called when a mouse motion event occurs virtual bool mouseMotionEvent(double xMouse, double yMouse, int leftButtonState, diff --git a/testbed/src/SceneDemo.cpp b/testbed/src/SceneDemo.cpp index 19bf4466..d547cce8 100644 --- a/testbed/src/SceneDemo.cpp +++ b/testbed/src/SceneDemo.cpp @@ -47,7 +47,8 @@ SceneDemo::SceneDemo(const std::string& name, EngineSettings& settings, bool isP mColorShader("shaders/color.vert", "shaders/color.frag"), mQuadShader("shaders/quad.vert", "shaders/quad.frag"), mVBOQuad(GL_ARRAY_BUFFER), mDebugVBOLinesVertices(GL_ARRAY_BUFFER), mDebugVBOTrianglesVertices(GL_ARRAY_BUFFER), - mMeshFolderPath("meshes/"), mPhysicsWorld(nullptr), mIsPhysicsWorldSimulated(isPhysicsWorldSimulated) { + mMeshFolderPath("meshes/"), mPhysicsWorld(nullptr), mIsPhysicsWorldSimulated(isPhysicsWorldSimulated), + mIsMovingBody(false), mMovingBody(nullptr) { shadowMapTextureLevel++; @@ -612,6 +613,75 @@ void SceneDemo::removeAllVisualContactPoints() { mVisualContactPoints.clear(); } +// Called when the user is moving a body with the mouse +void SceneDemo::moveBodyWithMouse(double mousePosX, double mousePosY) { + + if (!mIsMovingBody) { + + // Find the body and the position of the mouse on that body (with raycasting) + openglframework::Vector4 screenPoint1((mousePosX / mWindowWidth) * 2.0 - 1.0, ((mWindowHeight - mousePosY) / mWindowHeight) * 2.0 - 1.0, -1, 1); + openglframework::Vector4 screenPoint2((mousePosX / mWindowWidth) * 2.0 - 1.0, ((mWindowHeight - mousePosY) / mWindowHeight) * 2.0 - 1.0, 1, 1); + openglframework::Vector4 worldP1 = (mCamera.getTransformMatrix() * mCamera.getProjectionMatrix().getInverse()) * screenPoint1; + openglframework::Vector4 worldP2 = (mCamera.getTransformMatrix() * mCamera.getProjectionMatrix().getInverse()) * screenPoint2; + openglframework::Vector3 cameraPos = mCamera.getOrigin(); + rp3d::Vector3 worldPoint1(worldP1.x, worldP1.y, worldP1.z); + rp3d::Vector3 worldPoint2(worldP2.x, worldP2.y, worldP2.z); + rp3d::Ray ray(worldPoint1, worldPoint2); + mPhysicsWorld->raycast(ray, this); + } + + if (mMovingBody != nullptr) { + openglframework::Vector4 previousScreenPos(mLastMouseX / mWindowWidth, (mWindowHeight - mLastMouseY) / mWindowHeight, 0, 0); + openglframework::Vector4 currentScreenPos(mousePosX / mWindowWidth, (mWindowHeight - mousePosY) / mWindowHeight, 0, 0); + openglframework::Vector4 forceScreen = currentScreenPos - previousScreenPos; + openglframework::Vector4 f = mCamera.getTransformMatrix() * forceScreen * MOUSE_MOVE_BODY_FORCE; + rp3d::Vector3 force(f.x, f.y, f.z); + mMovingBody->applyForceAtLocalPosition(force, mMovingBodyLocalPoint); + } + + mLastMouseX = mousePosX; + mLastMouseY = mousePosY; + mIsMovingBody = true; +} + +// Called when a mouse button event occurs +bool SceneDemo::mouseButtonEvent(int button, bool down, int mods, double mousePosX, double mousePosY) { + + // Left mouse click with CTRL key pressed on keyboard (moving a body) + if (down && (mods & GLFW_MOD_CONTROL)) { + + moveBodyWithMouse(mousePosX, mousePosY); + return true; + } + + mIsMovingBody = false; + mMovingBody = nullptr; + + return Scene::mouseButtonEvent(button, down, mods, mousePosX, mousePosY); +} + +// Called when a mouse motion event occurs +bool SceneDemo::mouseMotionEvent(double xMouse, double yMouse, int leftButtonState, int rightButtonState, int middleButtonState, int altKeyState) { + + if (mIsMovingBody) { + moveBodyWithMouse(xMouse, yMouse); + return true; + } + + return Scene::mouseMotionEvent(xMouse, yMouse, leftButtonState, rightButtonState, middleButtonState, altKeyState); +} + +// Called when a raycast hit occurs (used to move a body with the mouse) +rp3d::decimal SceneDemo::notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) { + + rp3d::RigidBody* body = dynamic_cast<rp3d::RigidBody*>(raycastInfo.body); + mMovingBody = body; + const rp3d::Transform localToWorldTransform = raycastInfo.collider->getLocalToWorldTransform(); + mMovingBodyLocalPoint = localToWorldTransform.getInverse() * raycastInfo.worldPoint; + + return raycastInfo.hitFraction; +} + // Update the engine settings void SceneDemo::updateEngineSettings() { diff --git a/testbed/src/SceneDemo.h b/testbed/src/SceneDemo.h index c30868fa..05d9e78c 100644 --- a/testbed/src/SceneDemo.h +++ b/testbed/src/SceneDemo.h @@ -35,11 +35,12 @@ // Constants const int SHADOWMAP_WIDTH = 2048; const int SHADOWMAP_HEIGHT = 2048; +const float MOUSE_MOVE_BODY_FORCE = 100000.0f; // Class SceneDemo // Abstract class that represents a 3D scene for the ReactPhysics3D examples. // This scene has a single light source with shadow mapping. -class SceneDemo : public Scene { +class SceneDemo : public Scene, rp3d::RaycastCallback { protected: @@ -121,6 +122,15 @@ class SceneDemo : public Scene { /// True if we need to step the physics simulation each frame bool mIsPhysicsWorldSimulated; + /// True if the user is currently moving a body with the mouse + bool mIsMovingBody; + + /// Local-space point on the body use to move it with the mouse + rp3d::Vector3 mMovingBodyLocalPoint; + + /// Pointer to the body that is currently moved with the mouse by the user + rp3d::RigidBody* mMovingBody; + // -------------------- Methods -------------------- // /// Create the Shadow map FBO and texture @@ -150,6 +160,9 @@ class SceneDemo : public Scene { /// Remove all contact points void removeAllVisualContactPoints(); + /// Called when the user is moving a body with the mouse + void moveBodyWithMouse(double mousePosX, double mousePosY); + public: // -------------------- Methods -------------------- // @@ -181,6 +194,16 @@ class SceneDemo : public Scene { /// Enable/disable debug rendering virtual void setIsDebugRendererEnabled(bool isEnabled) override; + + /// Called when a mouse button event occurs + virtual bool mouseButtonEvent(int button, bool down, int mods, double mousePosX, double mousePosY) override; + + /// Called when a mouse motion event occurs + virtual bool mouseMotionEvent(double xMouse, double yMouse, int leftButtonState, + int rightButtonState, int middleButtonState, int altKeyState) override; + + /// Called when a raycast hit occurs (used to move a body with the mouse) + virtual rp3d::decimal notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) override; }; // Enabled/Disable the shadow mapping diff --git a/testbed/src/TestbedApplication.cpp b/testbed/src/TestbedApplication.cpp index 8c0f9f6a..a582d93e 100644 --- a/testbed/src/TestbedApplication.cpp +++ b/testbed/src/TestbedApplication.cpp @@ -110,7 +110,7 @@ void TestbedApplication::createScenes() { mScenes.push_back(cubeStackScene); // Joints scene - JointsScene* jointsScene = new JointsScene("Joints", mEngineSettings); + JointsScene* jointsScene = new JointsScene("HingeJoint - Bridge", mEngineSettings); mScenes.push_back(jointsScene); // Collision shapes scene @@ -356,6 +356,19 @@ bool TestbedApplication::keyboard_event(int key, int scancode, int action, int m return true; } + // Close application on escape key + if (key == GLFW_KEY_P && action == GLFW_PRESS) { + + if (mTimer.isRunning()) { + pauseSimulation(); + } + else { + playSimulation(); + } + + return true; + } + return mCurrentScene->keyboardEvent(key, scancode, action, modifiers); }