diff --git a/examples/ExampleBrowser/CMakeLists.txt b/examples/ExampleBrowser/CMakeLists.txt index b517a43c6..da15ba32b 100644 --- a/examples/ExampleBrowser/CMakeLists.txt +++ b/examples/ExampleBrowser/CMakeLists.txt @@ -114,7 +114,10 @@ SET(ExtendedTutorialsSources ../ExtendedTutorials/SimpleCloth.cpp ../ExtendedTutorials/Chain.cpp ../ExtendedTutorials/Bridge.cpp - ../ExtendedTutorials/RigidBodyFromObj.cpp + ../ExtendedTutorials/RigidBodyFromObj.cpp + ../ExtendedTutorials/InclinedPlane.cpp + ../ExtendedTutorials/InclinedPlane.h + ../ExtendedTutorials/NewtonsCradle.cpp ) SET(BulletExampleBrowser_SRCS @@ -158,6 +161,22 @@ SET(BulletExampleBrowser_SRCS ../Tutorial/Tutorial.h ../Tutorial/Dof6ConstraintTutorial.cpp ../Tutorial/Dof6ConstraintTutorial.h + ../ExtendedTutorials/SimpleBox.cpp + ../ExtendedTutorials/SimpleBox.h + ../ExtendedTutorials/MultipleBoxes.cpp + ../ExtendedTutorials/MultipleBoxes.h + ../ExtendedTutorials/SimpleCloth.cpp + ../ExtendedTutorials/SimpleCloth.h + ../ExtendedTutorials/SimpleJoint.cpp + ../ExtendedTutorials/SimpleJoint.h + ../ExtendedTutorials/NewtonsCradle.cpp + ../ExtendedTutorials/NewtonsCradle.h + ../ExtendedTutorials/NewtonsRopeCradle.cpp + ../ExtendedTutorials/NewtonsRopeCradle.h + ../ExtendedTutorials/InclinedPlane.cpp + ../ExtendedTutorials/InclinedPlane.h + ../ExtendedTutorials/MultiPendulum.cpp + ../ExtendedTutorials/MultiPendulum.h ../Collision/CollisionSdkC_Api.cpp ../Collision/CollisionSdkC_Api.h ../Collision/CollisionTutorialBullet2.cpp diff --git a/examples/ExampleBrowser/ExampleEntries.cpp b/examples/ExampleBrowser/ExampleEntries.cpp index 152ddff76..16185efe4 100644 --- a/examples/ExampleBrowser/ExampleEntries.cpp +++ b/examples/ExampleBrowser/ExampleEntries.cpp @@ -58,7 +58,7 @@ #endif #endif //B3_USE_CLEW -//Extended Tutorial Includes Added by Mobeen +//Extended Tutorial Includes Added by Mobeen and Benelot #include "../ExtendedTutorials/SimpleBox.h" #include "../ExtendedTutorials/MultipleBoxes.h" #include "../ExtendedTutorials/SimpleJoint.h" @@ -66,6 +66,10 @@ #include "../ExtendedTutorials/Chain.h" #include "../ExtendedTutorials/Bridge.h" #include "../ExtendedTutorials/RigidBodyFromObj.h" +#include "../ExtendedTutorials/InclinedPlane.h" +#include "../ExtendedTutorials/NewtonsCradle.h" +#include "../ExtendedTutorials/NewtonsRopeCradle.h" +#include "../ExtendedTutorials/MultiPendulum.h" struct ExampleEntry { @@ -273,11 +277,16 @@ static ExampleEntry gDefaultExamples[]= //Extended Tutorials Added by Mobeen ExampleEntry(0,"Extended Tutorials"), ExampleEntry(1,"Simple Box", "Simplest possible demo creating a single box rigid body that falls under gravity", ET_SimpleBoxCreateFunc), - ExampleEntry(1,"Multiple Boxes", "Adding multiple box rigid bodies that fall under gravity", ET_MultipleBoxesCreateFunc), - ExampleEntry(1,"Simple Joint", "Creating a single distance constraint between two box rigid bodies", ET_SimpleJointCreateFunc), - ExampleEntry(1,"Simple Cloth", "Creating a simple piece of cloth", ET_SimpleClothCreateFunc), - ExampleEntry(1,"Simple Chain", "Creating a simple chain using a pair of point2point/distance constraints. You may click and drag any box to see the chain respond.", ET_ChainCreateFunc), - ExampleEntry(1,"Simple Bridge", "Creating a simple bridge using a pair of point2point/distance constraints. You may click and drag any plank to see the bridge respond.", ET_BridgeCreateFunc), + ExampleEntry(1,"Multiple Boxes", "Add multiple box rigid bodies that fall under gravity", ET_MultipleBoxesCreateFunc), + ExampleEntry(1,"Simple Joint", "Create a single distance constraint between two box rigid bodies", ET_SimpleJointCreateFunc), + ExampleEntry(1,"Simple Cloth", "Create a simple piece of cloth", ET_SimpleClothCreateFunc), + ExampleEntry(1,"Simple Chain", "Create a simple chain using a pair of point2point/distance constraints. You may click and drag any box to see the chain respond.", ET_ChainCreateFunc), + ExampleEntry(1,"Simple Bridge", "Create a simple bridge using a pair of point2point/distance constraints. You may click and drag any plank to see the bridge respond.", ET_BridgeCreateFunc), + ExampleEntry(1,"Inclined Plane", "Create an inclined plane to show restitution and different types of friction. Use the sliders to vary restitution and friction and press space to reset the scene.", ET_InclinedPlaneCreateFunc), + ExampleEntry(1,"Newton's Cradle", "Create a Newton's Cradle using a pair of point2point/slider constraints. Press 1/2 to lengthen/shorten the pendula, press 3 to displace pendula. Use the sliders to select the number (reset simulation), length and restitution of pendula, the number of displaced pendula and apply the displacement force.", ET_NewtonsCradleCreateFunc), + ExampleEntry(1,"Newton's Rope Cradle", "Create a Newton's Cradle using ropes. Press 3 to displace pendula. Use the sliders to select the number (reset simulation), length and restitution of pendula and the number of displaced pendula and apply the displacement force.",ET_NewtonsRopeCradleCreateFunc), + ExampleEntry(1,"Multi-Pendulum", "Create a Multi-Pendulum using point2point/slider constraints. Press 1/2 to lengthen/shorten the pendula, press 3 to displace pendula. Use the sliders to select the number (reset simulation), length and restitution of pendula, the number of displaced pendula and apply the displacement force.",ET_MultiPendulumCreateFunc), + //todo: create a category/tutorial about advanced topics, such as optimizations, using different collision detection algorithm, different constraint solvers etc. //ExampleEntry(0,"Advanced"), diff --git a/examples/ExampleBrowser/premake4.lua b/examples/ExampleBrowser/premake4.lua index 522fffdaa..7d11eaed9 100644 --- a/examples/ExampleBrowser/premake4.lua +++ b/examples/ExampleBrowser/premake4.lua @@ -86,13 +86,7 @@ project "App_BulletExampleBrowser" "../InverseDynamics/InverseDynamicsExample.h", "../BasicDemo/BasicExample.*", "../Tutorial/*", - "../ExtendedTutorials/SimpleBox.cpp", - "../ExtendedTutorials/MultipleBoxes.cpp", - "../ExtendedTutorials/SimpleJoint.cpp", - "../ExtendedTutorials/SimpleCloth.cpp", - "../ExtendedTutorials/Chain.cpp", - "../ExtendedTutorials/Bridge.cpp", - "../ExtendedTutorials/RigidBodyFromObj.cpp", + "../ExtendedTutorials/*", "../Collision/*", "../RoboticsLearning/*", "../Collision/Internal/*", diff --git a/examples/ExtendedTutorials/InclinedPlane.cpp b/examples/ExtendedTutorials/InclinedPlane.cpp new file mode 100644 index 000000000..dceafdbe8 --- /dev/null +++ b/examples/ExtendedTutorials/InclinedPlane.cpp @@ -0,0 +1,372 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2015 Google Inc. http://bulletphysics.org + +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. +*/ + + + +#include "InclinedPlane.h" + +#include "btBulletDynamicsCommon.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "../CommonInterfaces/CommonRigidBodyBase.h" +#include "../CommonInterfaces/CommonParameterInterface.h" + +static btScalar gTilt = 20.0f/180.0f*SIMD_PI; // tilt the ramp 20 degrees + +static btScalar gRampFriction = 1; // set ramp friction to 1 + +static btScalar gRampRestitution = 0; // set ramp restitution to 0 (no restitution) + +static btScalar gBoxFriction = 1; // set box friction to 1 + +static btScalar gBoxRestitution = 0; // set box restitution to 0 + +static btScalar gSphereFriction = 1; // set sphere friction to 1 + +static btScalar gSphereRollingFriction = 1; // set sphere rolling friction to 1 + +static btScalar gSphereRestitution = 0; // set sphere restitution to 0 + +// handles for changes +static btRigidBody* ramp = NULL; +static btRigidBody* gBox = NULL; +static btRigidBody* gSphere = NULL; + +struct InclinedPlaneExample : public CommonRigidBodyBase +{ + InclinedPlaneExample(struct GUIHelperInterface* helper) + :CommonRigidBodyBase(helper) + { + } + virtual ~InclinedPlaneExample(){} + virtual void initPhysics(); + virtual void resetScene(); + virtual void renderScene(); + virtual void stepSimulation(float deltaTime); + virtual bool keyboardCallback(int key, int state); + void resetCamera() + { + float dist = 41; + float pitch = 52; + float yaw = 35; + float targetPos[3]={0,0.46,0}; + m_guiHelper->resetCamera(dist,pitch,yaw,targetPos[0],targetPos[1],targetPos[2]); + } + + + +}; + +void onBoxFrictionChanged(float friction); + +void onBoxRestitutionChanged(float restitution); + +void onSphereFrictionChanged(float friction); + +void onSphereRestitutionChanged(float restitution); + +void onRampInclinationChanged(float inclination); + +void onRampFrictionChanged(float friction); + +void onRampRestitutionChanged(float restitution); + +void InclinedPlaneExample::initPhysics() +{ + + { // create slider to change the ramp tilt + SliderParams slider("Ramp Tilt",&gTilt); + slider.m_minVal=0; + slider.m_maxVal=SIMD_PI/2.0f; + slider.m_clampToNotches = false; + slider.m_callback = onRampInclinationChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider); + } + + { // create slider to change the ramp friction + SliderParams slider("Ramp Friction",&gRampFriction); + slider.m_minVal=0; + slider.m_maxVal=10; + slider.m_clampToNotches = false; + slider.m_callback = onRampFrictionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider); + } + + { // create slider to change the ramp restitution + SliderParams slider("Ramp Restitution",&gRampRestitution); + slider.m_minVal=0; + slider.m_maxVal=1; + slider.m_clampToNotches = false; + slider.m_callback = onRampRestitutionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider); + } + + { // create slider to change the box friction + SliderParams slider("Box Friction",&gBoxFriction); + slider.m_minVal=0; + slider.m_maxVal=10; + slider.m_clampToNotches = false; + slider.m_callback = onBoxFrictionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider); + } + + { // create slider to change the box restitution + SliderParams slider("Box Restitution",&gBoxRestitution); + slider.m_minVal=0; + slider.m_maxVal=1; + slider.m_clampToNotches = false; + slider.m_callback = onBoxRestitutionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider); + } + + { // create slider to change the sphere friction + SliderParams slider("Sphere Friction",&gSphereFriction); + slider.m_minVal=0; + slider.m_maxVal=10; + slider.m_clampToNotches = false; + slider.m_callback = onSphereFrictionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider); + } + + { // create slider to change the sphere rolling friction + SliderParams slider("Sphere Rolling Friction",&gSphereRollingFriction); + slider.m_minVal=0; + slider.m_maxVal=10; + slider.m_clampToNotches = false; + slider.m_callback = onSphereRestitutionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider); + } + + { // create slider to change the sphere restitution + SliderParams slider("Sphere Restitution",&gSphereRestitution); + slider.m_minVal=0; + slider.m_maxVal=1; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider); + } + + m_guiHelper->setUpAxis(1); // set Y axis as up axis + + createEmptyDynamicsWorld(); + + // create debug drawer + m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld); + if (m_dynamicsWorld->getDebugDrawer()) + m_dynamicsWorld->getDebugDrawer()->setDebugMode(btIDebugDraw::DBG_DrawWireframe+btIDebugDraw::DBG_DrawContactPoints); + + + { // create a static ground + btBoxShape* groundShape = createBoxShape(btVector3(btScalar(50.),btScalar(50.),btScalar(50.))); + m_collisionShapes.push_back(groundShape); + + btTransform groundTransform; + groundTransform.setIdentity(); + groundTransform.setOrigin(btVector3(0,-50,0)); + + btScalar mass(0.); + createRigidBody(mass,groundTransform,groundShape, btVector4(0,0,1,1)); + } + + { //create a static inclined plane + btBoxShape* inclinedPlaneShape = createBoxShape(btVector3(btScalar(20.),btScalar(1.),btScalar(10.))); + m_collisionShapes.push_back(inclinedPlaneShape); + + btTransform startTransform; + startTransform.setIdentity(); + + // position the inclined plane above ground + startTransform.setOrigin(btVector3( + btScalar(0), + btScalar(15), + btScalar(0))); + + btQuaternion incline; + incline.setRotation(btVector3(0,0,1),gTilt); + startTransform.setRotation(incline); + + btScalar mass(0.); + ramp = createRigidBody(mass,startTransform,inclinedPlaneShape); + ramp->setFriction(gRampFriction); + ramp->setRestitution(gRampRestitution); + } + + + { //create a cube above the inclined plane + btBoxShape* boxShape = createBoxShape(btVector3(1,1,1)); + + m_collisionShapes.push_back(boxShape); + + btTransform startTransform; + startTransform.setIdentity(); + + btScalar boxMass(1.f); + + startTransform.setOrigin( + btVector3(btScalar(0), btScalar(20), btScalar(2))); + + gBox = createRigidBody(boxMass, startTransform, boxShape); + gBox->forceActivationState(DISABLE_DEACTIVATION); // to prevent the box on the ramp from disabling + gBox->setFriction(gBoxFriction); + gBox->setRestitution(gBoxRestitution); + } + + { //create a sphere above the inclined plane + btSphereShape* sphereShape = new btSphereShape(btScalar(1)); + + m_collisionShapes.push_back(sphereShape); + + btTransform startTransform; + startTransform.setIdentity(); + + btScalar sphereMass(1.f); + + startTransform.setOrigin( + btVector3(btScalar(0), btScalar(20), btScalar(4))); + + gSphere = createRigidBody(sphereMass, startTransform, sphereShape); + gSphere->forceActivationState(DISABLE_DEACTIVATION); // to prevent the sphere on the ramp from disabling + gSphere->setFriction(gSphereFriction); + gSphere->setRestitution(gSphereRestitution); + gSphere->setRollingFriction(gSphereRollingFriction); + } + + m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld); +} + +void InclinedPlaneExample::resetScene() { + { //reset a cube above the inclined plane + + btTransform startTransform; + startTransform.setIdentity(); + + startTransform.setOrigin( + btVector3(btScalar(0), btScalar(20), btScalar(2))); + + gBox->setWorldTransform(startTransform); + btVector3 zero(0, 0, 0); + gBox->setAngularVelocity(zero); + gBox->setLinearVelocity(zero); + gBox->clearForces(); + } + + { //reset a sphere above the inclined plane + btTransform startTransform; + startTransform.setIdentity(); + + startTransform.setOrigin( + btVector3(btScalar(0), btScalar(20), btScalar(4))); + + gSphere->setWorldTransform(startTransform); + btVector3 zero(0, 0, 0); + gSphere->setAngularVelocity(zero); + gSphere->setLinearVelocity(zero); + gSphere->clearForces(); + } +} + +void InclinedPlaneExample::stepSimulation(float deltaTime) +{ + if (m_dynamicsWorld) + { + m_dynamicsWorld->stepSimulation(deltaTime); + } + +} + + +void InclinedPlaneExample::renderScene() +{ + CommonRigidBodyBase::renderScene(); +} + +bool InclinedPlaneExample::keyboardCallback(int key, int state) { +// b3Printf("Key pressed: %d in state %d \n",key,state); + + switch (key) { + case 32 /*ASCII for space*/: { + resetScene(); + break; + } + } + + return false; +} + + +// GUI parameter modifiers +void onBoxFrictionChanged(float friction){ + if(gBox){ + gBox->setFriction(friction); +// b3Printf("Friction of box changed to %f",friction ); + } +} + +void onBoxRestitutionChanged(float restitution){ + if(gBox){ + gBox->setRestitution(restitution); + //b3Printf("Restitution of box changed to %f",restitution); + } +} + +void onSphereFrictionChanged(float friction){ + if(gSphere){ + gSphere->setFriction(friction); + //b3Printf("Friction of sphere changed to %f",friction ); + } +} + +void onSphereRestitutionChanged(float restitution){ + if(gSphere){ + gSphere->setRestitution(restitution); + //b3Printf("Restitution of sphere changed to %f",restitution); + } +} + +void onRampInclinationChanged(float inclination){ + if(ramp){ + btTransform startTransform; + startTransform.setIdentity(); + + // position the inclined plane above ground + startTransform.setOrigin( + btVector3(btScalar(0), btScalar(15), btScalar(0))); + + btQuaternion incline; + incline.setRotation(btVector3(0,0,1),gTilt); + startTransform.setRotation(incline); + ramp->setWorldTransform(startTransform); + //b3Printf("Inclination of ramp changed to %f",inclination ); + } +} + +void onRampFrictionChanged(float friction){ + if(ramp){ + ramp->setFriction(friction); + //b3Printf("Friction of ramp changed to %f \n",friction ); + } +} + +void onRampRestitutionChanged(float restitution){ + if(ramp){ + ramp->setRestitution(restitution); + //b3Printf("Restitution of ramp changed to %f \n",restitution); + } +} + + +CommonExampleInterface* ET_InclinedPlaneCreateFunc(CommonExampleOptions& options) +{ + return new InclinedPlaneExample(options.m_guiHelper); +} diff --git a/examples/ExtendedTutorials/InclinedPlane.h b/examples/ExtendedTutorials/InclinedPlane.h new file mode 100644 index 000000000..23ea92dfd --- /dev/null +++ b/examples/ExtendedTutorials/InclinedPlane.h @@ -0,0 +1,22 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2015 Google Inc. http://bulletphysics.org + +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. +*/ + +#ifndef ET_INCLINED_PLANE_EXAMPLE_H +#define ET_INCLINED_PLANE_EXAMPLE_H + +class CommonExampleInterface* ET_InclinedPlaneCreateFunc(struct CommonExampleOptions& options); + + +#endif //ET_INCLINED_PLANE_EXAMPLE_H diff --git a/examples/ExtendedTutorials/MultiPendulum.cpp b/examples/ExtendedTutorials/MultiPendulum.cpp new file mode 100644 index 000000000..038aab3ec --- /dev/null +++ b/examples/ExtendedTutorials/MultiPendulum.cpp @@ -0,0 +1,435 @@ +/* + Bullet Continuous Collision Detection and Physics Library + Copyright (c) 2015 Google Inc. http://bulletphysics.org + + 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. + */ + +#include "MultiPendulum.h" + +#include // TODO: Should I use another data structure? +#include + +#include "btBulletDynamicsCommon.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "../CommonInterfaces/CommonRigidBodyBase.h" +#include "../CommonInterfaces/CommonParameterInterface.h" + +static btScalar gPendulaQty = 2; //TODO: This would actually be an Integer, but the Slider does not like integers, so I floor it when changed + +static btScalar gDisplacedPendula = 1; //TODO: This is an int as well + +static btScalar gPendulaRestitution = 1; // Default pendulum restitution is 1 to restore all force + +static btScalar gSphereRadius = 1; // The sphere radius + +static btScalar gCurrentPendulumLength = 8; + +static btScalar gInitialPendulumLength = 8; // Default pendulum length (distance between two spheres) + +static btScalar gDisplacementForce = 30; // The default force with which we move the pendulum + +static btScalar gForceScalar = 0; // default force scalar to apply a displacement + +struct MultiPendulumExample: public CommonRigidBodyBase { + MultiPendulumExample(struct GUIHelperInterface* helper) : + CommonRigidBodyBase(helper) { + } + + virtual ~MultiPendulumExample() { + } + + virtual void initPhysics(); // build a multi pendulum + virtual void renderScene(); // render the scene to screen + virtual void createMultiPendulum(btSphereShape* colShape, btScalar pendulaQty, const btVector3& position, btScalar length, btScalar mass); // create a multi pendulum at the indicated x and y position, the specified number of pendula formed into a chain, each with indicated length and mass + virtual void changePendulaLength(btScalar length); // change the pendulum length + virtual void changePendulaRestitution(btScalar restitution); // change the pendula restitution + virtual void stepSimulation(float deltaTime); // step the simulation + virtual bool keyboardCallback(int key, int state); // handle keyboard callbacks + virtual void applyPendulumForce(btScalar pendulumForce); + void resetCamera() { + float dist = 41; + float pitch = 52; + float yaw = 35; + float targetPos[3] = { 0, 0.46, 0 }; + m_guiHelper->resetCamera(dist, pitch, yaw, targetPos[0], targetPos[1], + targetPos[2]); + } + + std::vector constraints; // keep a handle to the slider constraints + std::vector pendula; // keep a handle to the pendula +}; + +static MultiPendulumExample* mex = NULL; // Handle to the example to access it via functions. Do not use this in your simulation! + +void onMultiPendulaLengthChanged(float pendulaLength); // Change the pendula length + +void onMultiPendulaRestitutionChanged(float pendulaRestitution); // change the pendula restitution + +void floorMSliderValue(float notUsed); // floor the slider values which should be integers + +void applyMForceWithForceScalar(float forceScalar); + +void MultiPendulumExample::initPhysics() { // Setup your physics scene + + { // create a slider to change the number of pendula + SliderParams slider("Number of Pendula", &gPendulaQty); + slider.m_minVal = 1; + slider.m_maxVal = 50; + slider.m_callback = floorMSliderValue; // hack to get integer values + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the number of displaced pendula + SliderParams slider("Number of Displaced Pendula", &gDisplacedPendula); + slider.m_minVal = 0; + slider.m_maxVal = 49; + slider.m_callback = floorMSliderValue; // hack to get integer values + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the pendula restitution + SliderParams slider("Pendula Restitution", &gPendulaRestitution); + slider.m_minVal = 0; + slider.m_maxVal = 1; + slider.m_clampToNotches = false; + slider.m_callback = onMultiPendulaRestitutionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the pendulum length + SliderParams slider("Pendula Length", &gCurrentPendulumLength); + slider.m_minVal = 0; + slider.m_maxVal = 49; + slider.m_clampToNotches = false; + slider.m_callback = onMultiPendulaLengthChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the force to displace the lowest pendulum + SliderParams slider("Displacement force", &gDisplacementForce); + slider.m_minVal = 0.1; + slider.m_maxVal = 200; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to apply the force by slider + SliderParams slider("Apply displacement force", &gForceScalar); + slider.m_minVal = -1; + slider.m_maxVal = 1; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + m_guiHelper->setUpAxis(1); + + createEmptyDynamicsWorld(); + + // create a debug drawer + m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld); + if (m_dynamicsWorld->getDebugDrawer()) + m_dynamicsWorld->getDebugDrawer()->setDebugMode( + btIDebugDraw::DBG_DrawWireframe + + btIDebugDraw::DBG_DrawContactPoints + + btIDebugDraw::DBG_DrawConstraints + + btIDebugDraw::DBG_DrawConstraintLimits); + + { // create the multipendulum starting at the indicated position below and where each pendulum has the following mass + btScalar pendulumMass(1.f); + + btVector3 position(0.0f,15.0f,0.0f); // initial top-most pendulum position + + // Re-using the same collision is better for memory usage and performance + btSphereShape* pendulumShape = new btSphereShape(gSphereRadius); + m_collisionShapes.push_back(pendulumShape); + + // create multi-pendulum + createMultiPendulum(pendulumShape, floor(gPendulaQty), position, + gInitialPendulumLength, pendulumMass); + } + + m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld); +} + +void MultiPendulumExample::stepSimulation(float deltaTime) { + + applyMForceWithForceScalar(gForceScalar); // apply force defined by apply force slider + + if (m_dynamicsWorld) { + m_dynamicsWorld->stepSimulation(deltaTime); + } +} + +void MultiPendulumExample::createMultiPendulum(btSphereShape* colShape, + btScalar pendulaQty, const btVector3& position, + btScalar length, btScalar mass) { + + // The multi-pendulum looks like this (names when built): + //..........0......./.......1...../.......2......./..etc...:pendulum build iterations + // O parentSphere + // | + // O childSphere / parentSphere + // | + // O ............./ childSphere / parentSphere + // | + // O .........................../ childSphere + // etc. + + //create the top element of the pendulum + btTransform startTransform; + startTransform.setIdentity(); + + // position the top sphere + startTransform.setOrigin(position); + + startTransform.setRotation(btQuaternion(0, 0, 0, 1)); // zero rotation + + btRigidBody* topSphere = createRigidBody(mass, startTransform, colShape); + + // disable the deactivation when object does not move anymore + topSphere->setActivationState(DISABLE_DEACTIVATION); + + //make top sphere position "fixed" in the world by attaching it to a the world with a point to point constraint + // The pivot is defined in the reference frame of topSphere, so the attachment should be exactly at the center of topSphere + btVector3 constraintPivot(0.0f, 0.0f, 0.0f); + btPoint2PointConstraint* p2pconst = new btPoint2PointConstraint( + *topSphere, constraintPivot); + + p2pconst->setDbgDrawSize(btScalar(5.f)); // set the size of the debug drawing + + // add the constraint to the world + m_dynamicsWorld->addConstraint(p2pconst, true); + + btRigidBody* parentSphere = topSphere; // set the top sphere as the parent sphere for the next sphere to be created + + for (int i = 0; i < pendulaQty; i++) { // produce the number of pendula + + // create joint element to make the pendulum rotate it + + // position the joint sphere at the same position as the top sphere + startTransform.setOrigin(position - btVector3(0,length*(i),0)); + + startTransform.setRotation(btQuaternion(0, 0, 0, 1)); // zero rotation + + btRigidBody* jointSphere = createRigidBody(mass, startTransform, + colShape); + jointSphere->setFriction(0); // we do not need friction here + + // disable the deactivation when object does not move anymore + jointSphere->setActivationState(DISABLE_DEACTIVATION); + + //create constraint between parentSphere and jointSphere + // this is represented by the constraint pivot in the local frames of reference of both constrained spheres + btTransform constraintPivotInParentSphereRF, constraintPivotInJointSphereRF; + + constraintPivotInParentSphereRF.setIdentity(); + constraintPivotInJointSphereRF.setIdentity(); + + // the orientation of a point-to-point constraint does not matter, as is has no rotational limits + + //Obtain the position of parentSphere in local reference frame of the jointSphere (the pivot is therefore in the center of parentSphere) + btVector3 parentSphereInJointSphereRF = + (jointSphere->getWorldTransform().inverse()( + parentSphere->getWorldTransform().getOrigin())); + constraintPivotInJointSphereRF.setOrigin(parentSphereInJointSphereRF); + + btPoint2PointConstraint* p2pconst = new btPoint2PointConstraint( + *parentSphere,*jointSphere,constraintPivotInParentSphereRF.getOrigin(), constraintPivotInJointSphereRF.getOrigin()); + + p2pconst->setDbgDrawSize(btScalar(5.f)); // set the size of the debug drawing + + // add the constraint to the world + m_dynamicsWorld->addConstraint(p2pconst, true); + + // create a slider constraint to change the length of the pendula while it swings + + startTransform.setIdentity(); // reset start transform + + // position the child sphere below the joint sphere + startTransform.setOrigin(position - btVector3(0,length*(i+1),0)); + + startTransform.setRotation(btQuaternion(0, 0, 0, 1)); // zero rotation + + btRigidBody* childSphere = createRigidBody(mass, startTransform, + colShape); + childSphere->setFriction(0); // we do not need friction here + pendula.push_back(childSphere); + + // disable the deactivation when object does not move anymore + childSphere->setActivationState(DISABLE_DEACTIVATION); + + //create slider constraint between jointSphere and childSphere + // this is represented by the constraint pivot in the local frames of reference of both constrained spheres + // furthermore we need to rotate the constraint appropriately to orient it correctly in space + btTransform constraintPivotInChildSphereRF; + + constraintPivotInJointSphereRF.setIdentity(); + constraintPivotInChildSphereRF.setIdentity(); + + // the orientation of a point-to-point constraint does not matter, as is has no rotational limits + + //Obtain the position of jointSphere in local reference frame of the childSphere (the pivot is therefore in the center of jointSphere) + btVector3 jointSphereInChildSphereRF = + (childSphere->getWorldTransform().inverse()( + jointSphere->getWorldTransform().getOrigin())); + constraintPivotInChildSphereRF.setOrigin(jointSphereInChildSphereRF); + + // the slider constraint is x aligned per default, but we want it to be y aligned, therefore we rotate it + btQuaternion qt; + qt.setEuler(0, 0, -SIMD_HALF_PI); + constraintPivotInJointSphereRF.setRotation(qt); //we use Y like up Axis + constraintPivotInChildSphereRF.setRotation(qt); //we use Y like up Axis + + btSliderConstraint* sliderConst = new btSliderConstraint(*jointSphere, + *childSphere, constraintPivotInJointSphereRF, constraintPivotInChildSphereRF, true); + + sliderConst->setDbgDrawSize(btScalar(5.f)); // set the size of the debug drawing + + // set limits + // the initial setup of the constraint defines the origins of the limit dimensions, + // therefore we set both limits directly to the current position of the parentSphere + sliderConst->setLowerLinLimit(btScalar(0)); + sliderConst->setUpperLinLimit(btScalar(0)); + sliderConst->setLowerAngLimit(btScalar(0)); + sliderConst->setUpperAngLimit(btScalar(0)); + constraints.push_back(sliderConst); + + // add the constraint to the world + m_dynamicsWorld->addConstraint(sliderConst, true); + parentSphere = childSphere; + } +} + +void MultiPendulumExample::changePendulaLength(btScalar length) { + btScalar lowerLimit = -gInitialPendulumLength; + for (std::vector::iterator sit = constraints.begin(); + sit != constraints.end(); sit++) { + btAssert((*sit) && "Null constraint"); + + // if the pendulum is being shortened beyond it's own length, we don't let the lower sphere to go past the upper one + if (lowerLimit <= length) { + (*sit)->setLowerLinLimit(length + lowerLimit); + (*sit)->setUpperLinLimit(length + lowerLimit); + } + } +} + +void MultiPendulumExample::changePendulaRestitution(btScalar restitution) { + for (std::vector::iterator rit = pendula.begin(); + rit != pendula.end(); rit++) { + btAssert((*rit) && "Null constraint"); + + (*rit)->setRestitution(restitution); + } +} + +void MultiPendulumExample::renderScene() { + CommonRigidBodyBase::renderScene(); +} + +bool MultiPendulumExample::keyboardCallback(int key, int state) { + + //b3Printf("Key pressed: %d in state %d \n",key,state); + + //key 1, key 2, key 3 + switch (key) { + case '1' /*ASCII for 1*/: { + + //assumption: Sphere are aligned in Z axis + btScalar newLimit = btScalar(gCurrentPendulumLength + 0.1); + + changePendulaLength(newLimit); + gCurrentPendulumLength = newLimit; + + b3Printf("Increase pendulum length to %f", gCurrentPendulumLength); + return true; + } + case '2' /*ASCII for 2*/: { + + //assumption: Sphere are aligned in Z axis + btScalar newLimit = btScalar(gCurrentPendulumLength - 0.1); + + //is being shortened beyond it's own length, we don't let the lower sphere to go over the upper one + if (0 <= newLimit) { + changePendulaLength(newLimit); + gCurrentPendulumLength = newLimit; + } + + b3Printf("Decrease pendulum length to %f", gCurrentPendulumLength); + return true; + } + case '3' /*ASCII for 3*/: { + applyPendulumForce(gDisplacementForce); + return true; + } + } + + return false; +} + +void MultiPendulumExample::applyPendulumForce(btScalar pendulumForce){ + if(pendulumForce != 0){ + b3Printf("Apply %f to pendulum",pendulumForce); + for (int i = 0; i < gDisplacedPendula; i++) { + if (gDisplacedPendula >= 0 && gDisplacedPendula <= gPendulaQty) + pendula[i]->applyCentralForce(btVector3(pendulumForce, 0, 0)); + } + } +} + +// GUI parameter modifiers + +void onMultiPendulaLengthChanged(float pendulaLength) { // Change the pendula length + if (mex){ + mex->changePendulaLength(pendulaLength); + } + //b3Printf("Pendula length changed to %f \n",sliderValue ); + +} + +void onMultiPendulaRestitutionChanged(float pendulaRestitution) { // change the pendula restitution + if (mex){ + mex->changePendulaRestitution(pendulaRestitution); + } + +} + +void floorMSliderValue(float notUsed) { // floor the slider values which should be integers + gPendulaQty = floor(gPendulaQty); + gDisplacedPendula = floor(gDisplacedPendula); +} + +void applyMForceWithForceScalar(float forceScalar) { + if(mex){ + btScalar appliedForce = forceScalar * gDisplacementForce; + + if(fabs(gForceScalar) < 0.2f) + gForceScalar = 0; + + mex->applyPendulumForce(appliedForce); + } +} + +CommonExampleInterface* ET_MultiPendulumCreateFunc( + CommonExampleOptions& options) { + mex = new MultiPendulumExample(options.m_guiHelper); + return mex; +} diff --git a/examples/ExtendedTutorials/MultiPendulum.h b/examples/ExtendedTutorials/MultiPendulum.h new file mode 100644 index 000000000..336c1be3c --- /dev/null +++ b/examples/ExtendedTutorials/MultiPendulum.h @@ -0,0 +1,22 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2015 Google Inc. http://bulletphysics.org + +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. +*/ + +#ifndef ET_MULTI_PENDULUM_EXAMPLE_H +#define ET_MULTI_PENDULUM_EXAMPLE_H + +class CommonExampleInterface* ET_MultiPendulumCreateFunc(struct CommonExampleOptions& options); + + +#endif //ET_MULTI_PENDULUM_EXAMPLE_H diff --git a/examples/ExtendedTutorials/NewtonsCradle.cpp b/examples/ExtendedTutorials/NewtonsCradle.cpp new file mode 100644 index 000000000..0d4c52095 --- /dev/null +++ b/examples/ExtendedTutorials/NewtonsCradle.cpp @@ -0,0 +1,380 @@ +/* + Bullet Continuous Collision Detection and Physics Library + Copyright (c) 2015 Google Inc. http://bulletphysics.org + + 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. + */ + +#include "NewtonsCradle.h" + +#include // TODO: Should I use another data structure? +#include + +#include "btBulletDynamicsCommon.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "../CommonInterfaces/CommonRigidBodyBase.h" +#include "../CommonInterfaces/CommonParameterInterface.h" + +static btScalar gPendulaQty = 5; // Number of pendula in newton's cradle +//TODO: This would actually be an Integer, but the Slider does not like integers, so I floor it when changed + +static btScalar gDisplacedPendula = 1; // number of displaced pendula +//TODO: This is an int as well + +static btScalar gPendulaRestitution = 1; // pendula restitution when hitting against each other + +static btScalar gSphereRadius = 1; // pendula radius + +static btScalar gCurrentPendulumLength = 8; // current pendula length + +static btScalar gInitialPendulumLength = 8; // default pendula length + +static btScalar gDisplacementForce = 30; // default force to displace the pendula + +static btScalar gForceScalar = 0; // default force scalar to apply a displacement + +struct NewtonsCradleExample: public CommonRigidBodyBase { + NewtonsCradleExample(struct GUIHelperInterface* helper) : + CommonRigidBodyBase(helper) { + } + virtual ~NewtonsCradleExample() { + } + virtual void initPhysics(); + virtual void renderScene(); + virtual void createPendulum(btSphereShape* colShape, const btVector3& position, btScalar length, btScalar mass); + virtual void changePendulaLength(btScalar length); + virtual void changePendulaRestitution(btScalar restitution); + virtual void stepSimulation(float deltaTime); + virtual bool keyboardCallback(int key, int state); + virtual void applyPendulumForce(btScalar pendulumForce); + void resetCamera() { + float dist = 41; + float pitch = 52; + float yaw = 35; + float targetPos[3] = { 0, 0.46, 0 }; + m_guiHelper->resetCamera(dist, pitch, yaw, targetPos[0], targetPos[1], + targetPos[2]); + } + + std::vector constraints; // keep a handle to the slider constraints + std::vector pendula; // keep a handle to the pendula +}; + +static NewtonsCradleExample* nex = NULL; + +void onPendulaLengthChanged(float pendulaLength); // Change the pendula length + +void onPendulaRestitutionChanged(float pendulaRestitution); // change the pendula restitution + +void floorSliderValue(float notUsed); // floor the slider values which should be integers + +void applyForceWithForceScalar(float forceScalar); + +void NewtonsCradleExample::initPhysics() { + + { // create a slider to change the number of pendula + SliderParams slider("Number of Pendula", &gPendulaQty); + slider.m_minVal = 1; + slider.m_maxVal = 50; + slider.m_callback = floorSliderValue; // hack to get integer values + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the number of displaced pendula + SliderParams slider("Number of Displaced Pendula", &gDisplacedPendula); + slider.m_minVal = 0; + slider.m_maxVal = 49; + slider.m_callback = floorSliderValue; // hack to get integer values + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the pendula restitution + SliderParams slider("Pendula Restitution", &gPendulaRestitution); + slider.m_minVal = 0; + slider.m_maxVal = 1; + slider.m_clampToNotches = false; + slider.m_callback = onPendulaRestitutionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the pendulum length + SliderParams slider("Pendula Length", &gCurrentPendulumLength); + slider.m_minVal = 0; + slider.m_maxVal = 49; + slider.m_clampToNotches = false; + slider.m_callback = onPendulaLengthChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the force to displace the lowest pendulum + SliderParams slider("Displacement force", &gDisplacementForce); + slider.m_minVal = 0.1; + slider.m_maxVal = 200; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to apply the force by slider + SliderParams slider("Apply displacement force", &gForceScalar); + slider.m_minVal = -1; + slider.m_maxVal = 1; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + m_guiHelper->setUpAxis(1); + + createEmptyDynamicsWorld(); + + // create a debug drawer + m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld); + if (m_dynamicsWorld->getDebugDrawer()) + m_dynamicsWorld->getDebugDrawer()->setDebugMode( + btIDebugDraw::DBG_DrawWireframe + + btIDebugDraw::DBG_DrawContactPoints + + btIDebugDraw::DBG_DrawConstraints + + btIDebugDraw::DBG_DrawConstraintLimits); + + { // create the pendula starting at the indicated position below and where each pendulum has the following mass + btScalar pendulumMass(1.f); + + btVector3 position(0.0f,15.0f,0.0f); // initial left-most pendulum position + btQuaternion orientation(0,0,0,1); // orientation of the pendula + + // Re-using the same collision is better for memory usage and performance + btSphereShape* pendulumShape = new btSphereShape(gSphereRadius); + m_collisionShapes.push_back(pendulumShape); + + for (int i = 0; i < floor(gPendulaQty); i++) { + + // create pendulum + createPendulum(pendulumShape, position, gInitialPendulumLength, pendulumMass); + + // displace the pendula 1.05 sphere size, so that they all nearly touch (small spacings in between + position.setX(position.x()-2.1f * gSphereRadius); + } + } + + m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld); +} + +void NewtonsCradleExample::stepSimulation(float deltaTime) { + + applyForceWithForceScalar(gForceScalar); // apply force defined by apply force slider + + if (m_dynamicsWorld) { + m_dynamicsWorld->stepSimulation(deltaTime); + } +} + +void NewtonsCradleExample::createPendulum(btSphereShape* colShape, const btVector3& position, btScalar length, btScalar mass) { + + // The pendulum looks like this (names when built): + // O topSphere + // | + // O bottomSphere + + //create a dynamic pendulum + btTransform startTransform; + startTransform.setIdentity(); + + // position the top sphere above ground with a moving x position + startTransform.setOrigin(position); + startTransform.setRotation(btQuaternion(0, 0, 0, 1)); // zero rotation + btRigidBody* topSphere = createRigidBody(mass, startTransform, colShape); + + // position the bottom sphere below the top sphere + startTransform.setOrigin( + btVector3(position.x(), btScalar(position.y() - length), + position.z())); + + startTransform.setRotation(btQuaternion(0, 0, 0, 1)); // zero rotation + btRigidBody* bottomSphere = createRigidBody(mass, startTransform, colShape); + bottomSphere->setFriction(0); // we do not need friction here + pendula.push_back(bottomSphere); + + // disable the deactivation when objects do not move anymore + topSphere->setActivationState(DISABLE_DEACTIVATION); + bottomSphere->setActivationState(DISABLE_DEACTIVATION); + + bottomSphere->setRestitution(gPendulaRestitution); // set pendula restitution + + //make the top sphere position "fixed" to the world by attaching with a point to point constraint + // The pivot is defined in the reference frame of topSphere, so the attachment is exactly at the center of the topSphere + btVector3 constraintPivot(btVector3(0.0f, 0.0f, 0.0f)); + btPoint2PointConstraint* p2pconst = new btPoint2PointConstraint(*topSphere, + constraintPivot); + + p2pconst->setDbgDrawSize(btScalar(5.f)); // set the size of the debug drawing + + // add the constraint to the world + m_dynamicsWorld->addConstraint(p2pconst, true); + + //create constraint between spheres + // this is represented by the constraint pivot in the local frames of reference of both constrained spheres + // furthermore we need to rotate the constraint appropriately to orient it correctly in space + btTransform constraintPivotInTopSphereRF, constraintPivotInBottomSphereRF; + + constraintPivotInTopSphereRF.setIdentity(); + constraintPivotInBottomSphereRF.setIdentity(); + + // the slider constraint is x aligned per default, but we want it to be y aligned, therefore we rotate it + btQuaternion qt; + qt.setEuler(0, 0, -SIMD_HALF_PI); + constraintPivotInTopSphereRF.setRotation(qt); //we use Y like up Axis + constraintPivotInBottomSphereRF.setRotation(qt); //we use Y like up Axis + + //Obtain the position of topSphere in local reference frame of bottomSphere (the pivot is therefore in the center of topSphere) + btVector3 topSphereInBottomSphereRF = + (bottomSphere->getWorldTransform().inverse()( + topSphere->getWorldTransform().getOrigin())); + constraintPivotInBottomSphereRF.setOrigin(topSphereInBottomSphereRF); + + btSliderConstraint* sliderConst = new btSliderConstraint(*topSphere, + *bottomSphere, constraintPivotInTopSphereRF, constraintPivotInBottomSphereRF, true); + + sliderConst->setDbgDrawSize(btScalar(5.f)); // set the size of the debug drawing + + // set limits + // the initial setup of the constraint defines the origins of the limit dimensions, + // therefore we set both limits directly to the current position of the topSphere + sliderConst->setLowerLinLimit(btScalar(0)); + sliderConst->setUpperLinLimit(btScalar(0)); + sliderConst->setLowerAngLimit(btScalar(0)); + sliderConst->setUpperAngLimit(btScalar(0)); + constraints.push_back(sliderConst); + + // add the constraint to the world + m_dynamicsWorld->addConstraint(sliderConst, true); +} + +void NewtonsCradleExample::changePendulaLength(btScalar length) { + btScalar lowerLimit = -gInitialPendulumLength; + for (std::vector::iterator sit = constraints.begin(); + sit != constraints.end(); sit++) { + btAssert((*sit) && "Null constraint"); + + //if the pendulum is being shortened beyond it's own length, we don't let the lower sphere to go past the upper one + if (lowerLimit <= length) { + (*sit)->setLowerLinLimit(length + lowerLimit); + (*sit)->setUpperLinLimit(length + lowerLimit); + } + } +} + +void NewtonsCradleExample::changePendulaRestitution(btScalar restitution) { + for (std::vector::iterator rit = pendula.begin(); + rit != pendula.end(); rit++) { + btAssert((*rit) && "Null constraint"); + + (*rit)->setRestitution(restitution); + } +} + +void NewtonsCradleExample::renderScene() { + CommonRigidBodyBase::renderScene(); +} + +bool NewtonsCradleExample::keyboardCallback(int key, int state) { + //b3Printf("Key pressed: %d in state %d \n",key,state); + + //key 1, key 2, key 3 + switch (key) { + case '1' /*ASCII for 1*/: { + + //assumption: Sphere are aligned in Z axis + btScalar newLimit = btScalar(gCurrentPendulumLength + 0.1); + + changePendulaLength(newLimit); + gCurrentPendulumLength = newLimit; + + b3Printf("Increase pendulum length to %f", gCurrentPendulumLength); + return true; + } + case '2' /*ASCII for 2*/: { + + //assumption: Sphere are aligned in Z axis + btScalar newLimit = btScalar(gCurrentPendulumLength - 0.1); + + //is being shortened beyond it's own length, we don't let the lower sphere to go over the upper one + if (0 <= newLimit) { + changePendulaLength(newLimit); + gCurrentPendulumLength = newLimit; + } + + b3Printf("Decrease pendulum length to %f", gCurrentPendulumLength); + return true; + } + case '3' /*ASCII for 3*/: { + applyPendulumForce(gDisplacementForce); + return true; + } + } + + return false; +} + +void NewtonsCradleExample::applyPendulumForce(btScalar pendulumForce){ + if(pendulumForce != 0){ + b3Printf("Apply %f to pendulum",pendulumForce); + for (int i = 0; i < gDisplacedPendula; i++) { + if (gDisplacedPendula >= 0 && gDisplacedPendula <= gPendulaQty) + pendula[i]->applyCentralForce(btVector3(pendulumForce, 0, 0)); + } + } +} + +// GUI parameter modifiers + +void onPendulaLengthChanged(float pendulaLength) { + if (nex){ + nex->changePendulaLength(pendulaLength); + //b3Printf("Pendula length changed to %f \n",sliderValue ); + } +} + +void onPendulaRestitutionChanged(float pendulaRestitution) { + if (nex){ + nex->changePendulaRestitution(pendulaRestitution); + } +} + +void floorSliderValue(float notUsed) { + gPendulaQty = floor(gPendulaQty); + gDisplacedPendula = floor(gDisplacedPendula); + +} + +void applyForceWithForceScalar(float forceScalar) { + if(nex){ + btScalar appliedForce = forceScalar * gDisplacementForce; + + if(fabs(gForceScalar) < 0.2f) + gForceScalar = 0; + + nex->applyPendulumForce(appliedForce); + } +} + +CommonExampleInterface* ET_NewtonsCradleCreateFunc( + CommonExampleOptions& options) { + nex = new NewtonsCradleExample(options.m_guiHelper); + return nex; +} diff --git a/examples/ExtendedTutorials/NewtonsCradle.h b/examples/ExtendedTutorials/NewtonsCradle.h new file mode 100644 index 000000000..028754f7e --- /dev/null +++ b/examples/ExtendedTutorials/NewtonsCradle.h @@ -0,0 +1,22 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2015 Google Inc. http://bulletphysics.org + +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. +*/ + +#ifndef ET_NEWTONS_CRADLE_EXAMPLE_H +#define ET_NEWTONS_CRADLE_EXAMPLE_H + +class CommonExampleInterface* ET_NewtonsCradleCreateFunc(struct CommonExampleOptions& options); + + +#endif //ET_NEWTONS_CRADLE_EXAMPLE_H diff --git a/examples/ExtendedTutorials/NewtonsRopeCradle.cpp b/examples/ExtendedTutorials/NewtonsRopeCradle.cpp new file mode 100644 index 000000000..94c96a71f --- /dev/null +++ b/examples/ExtendedTutorials/NewtonsRopeCradle.cpp @@ -0,0 +1,387 @@ +/* + Bullet Continuous Collision Detection and Physics Library + Copyright (c) 2015 Google Inc. http://bulletphysics.org + + 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. + */ + +#include "NewtonsRopeCradle.h" + +#include // TODO: Should I use another data structure? +#include + +#include "btBulletDynamicsCommon.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "../CommonInterfaces/CommonRigidBodyBase.h" + +#include "BulletSoftBody/btSoftRigidDynamicsWorld.h" +#include "BulletSoftBody/btSoftBodyHelpers.h" +#include "BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h" +#include "../CommonInterfaces/CommonParameterInterface.h" + +static btScalar gPendulaQty = 5; // Number of pendula in newton's cradle +//TODO: This would actually be an Integer, but the Slider does not like integers, so I floor it when changed + +static btScalar gDisplacedPendula = 1; // number of displaced pendula +//TODO: This is an int as well + +static btScalar gPendulaRestitution = 1; // pendula restition when hitting against each other + +static btScalar gSphereRadius = 1; // pendula radius + +static btScalar gInitialPendulumWidth = 4; // default pendula width + +static btScalar gInitialPendulumHeight = 8; // default pendula height + +static btScalar gRopeResolution = 1; // default rope resolution (number of links as in a chain) + +static btScalar gDisplacementForce = 30; // default force to displace the pendula + +static btScalar gForceScalar = 0; // default force scalar to apply a displacement + +struct NewtonsRopeCradleExample : public CommonRigidBodyBase { + NewtonsRopeCradleExample(struct GUIHelperInterface* helper) : + CommonRigidBodyBase(helper) { + } + virtual ~NewtonsRopeCradleExample(){} + virtual void initPhysics(); + virtual void stepSimulation(float deltaTime); + virtual void renderScene(); + virtual void applyPendulumForce(btScalar pendulumForce); + void createEmptyDynamicsWorld() + { + m_collisionConfiguration = new btSoftBodyRigidBodyCollisionConfiguration(); + m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration); + + m_broadphase = new btDbvtBroadphase(); + + m_solver = new btSequentialImpulseConstraintSolver; + + m_dynamicsWorld = new btSoftRigidDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration); + m_dynamicsWorld->setGravity(btVector3(0, -10, 0)); + + softBodyWorldInfo.m_broadphase = m_broadphase; + softBodyWorldInfo.m_dispatcher = m_dispatcher; + softBodyWorldInfo.m_gravity = m_dynamicsWorld->getGravity(); + softBodyWorldInfo.m_sparsesdf.Initialize(); + } + + virtual void createRopePendulum(btSphereShape* colShape, + const btVector3& position, const btQuaternion& pendulumOrientation, btScalar width, btScalar height, btScalar mass); + virtual void changePendulaRestitution(btScalar restitution); + virtual void connectWithRope(btRigidBody* body1, btRigidBody* body2); + virtual bool keyboardCallback(int key, int state); + + virtual btSoftRigidDynamicsWorld* getSoftDynamicsWorld() + { + ///just make it a btSoftRigidDynamicsWorld please + ///or we will add type checking + return (btSoftRigidDynamicsWorld*) m_dynamicsWorld; + } + void resetCamera() + { + float dist = 41; + float pitch = 52; + float yaw = 35; + float targetPos[3]={0,0.46,0}; + m_guiHelper->resetCamera(dist,pitch,yaw,targetPos[0],targetPos[1],targetPos[2]); + } + + std::vector constraints; + std::vector pendula; + + btSoftBodyWorldInfo softBodyWorldInfo; + +}; + +static NewtonsRopeCradleExample* nex = NULL; + +void onRopePendulaRestitutionChanged(float pendulaRestitution); + +void floorRSliderValue(float notUsed); + +void applyRForceWithForceScalar(float forceScalar); + +void NewtonsRopeCradleExample::initPhysics() +{ + + { // create a slider to change the number of pendula + SliderParams slider("Number of Pendula", &gPendulaQty); + slider.m_minVal = 1; + slider.m_maxVal = 50; + slider.m_callback = floorRSliderValue; // hack to get integer values + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the number of displaced pendula + SliderParams slider("Number of Displaced Pendula", &gDisplacedPendula); + slider.m_minVal = 0; + slider.m_maxVal = 49; + slider.m_callback = floorRSliderValue; // hack to get integer values + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the pendula restitution + SliderParams slider("Pendula Restitution", &gPendulaRestitution); + slider.m_minVal = 0; + slider.m_maxVal = 1; + slider.m_clampToNotches = false; + slider.m_callback = onRopePendulaRestitutionChanged; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the rope resolution + SliderParams slider("Rope Resolution", &gRopeResolution); + slider.m_minVal = 1; + slider.m_maxVal = 20; + slider.m_clampToNotches = false; + slider.m_callback = floorRSliderValue; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the pendulum width + SliderParams slider("Pendulum Width", &gInitialPendulumWidth); + slider.m_minVal = 0; + slider.m_maxVal = 40; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the pendulum height + SliderParams slider("Pendulum Height", &gInitialPendulumHeight); + slider.m_minVal = 0; + slider.m_maxVal = 40; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to change the force to displace the lowest pendulum + SliderParams slider("Displacement force", &gDisplacementForce); + slider.m_minVal = 0.1; + slider.m_maxVal = 200; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + { // create a slider to apply the force by slider + SliderParams slider("Apply displacement force", &gForceScalar); + slider.m_minVal = -1; + slider.m_maxVal = 1; + slider.m_clampToNotches = false; + m_guiHelper->getParameterInterface()->registerSliderFloatParameter( + slider); + } + + m_guiHelper->setUpAxis(1); + + createEmptyDynamicsWorld(); + + // create a debug drawer + m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld); + if (m_dynamicsWorld->getDebugDrawer()) + m_dynamicsWorld->getDebugDrawer()->setDebugMode( + btIDebugDraw::DBG_DrawWireframe + + btIDebugDraw::DBG_DrawContactPoints + + btIDebugDraw::DBG_DrawConstraints + + btIDebugDraw::DBG_DrawConstraintLimits); + + { // create the pendula starting at the indicated position below and where each pendulum has the following mass + btScalar pendulumMass(1.0f); + + btVector3 position(0.0f,15.0f,0.0f); // initial left-most pendulum position + btQuaternion orientation(0,0,0,1); // orientation of the pendula + + // Re-using the same collision is better for memory usage and performance + btSphereShape* pendulumShape = new btSphereShape(gSphereRadius); + m_collisionShapes.push_back(pendulumShape); + + for (int i = 0; i < floor(gPendulaQty); i++) { + + // create pendulum + createRopePendulum(pendulumShape, position, orientation,gInitialPendulumWidth, + gInitialPendulumHeight, pendulumMass); + + // displace the pendula 1.05 sphere size, so that they all nearly touch (small spacings in between) + position.setX(position.x()-2.1f * gSphereRadius); + } + } + + m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld); +} + +void NewtonsRopeCradleExample::connectWithRope(btRigidBody* body1, btRigidBody* body2) +{ + btSoftBody* softBodyRope0 = btSoftBodyHelpers::CreateRope(softBodyWorldInfo,body1->getWorldTransform().getOrigin(),body2->getWorldTransform().getOrigin(),gRopeResolution,0); + softBodyRope0->setTotalMass(0.1f); + + softBodyRope0->appendAnchor(0,body1); + softBodyRope0->appendAnchor(softBodyRope0->m_nodes.size()-1,body2); + + softBodyRope0->m_cfg.piterations = 5; + softBodyRope0->m_cfg.kDP = 0.005f; + softBodyRope0->m_cfg.kSHR = 1; + softBodyRope0->m_cfg.kCHR = 1; + softBodyRope0->m_cfg.kKHR = 1; + + getSoftDynamicsWorld()->addSoftBody(softBodyRope0); +} + +void NewtonsRopeCradleExample::stepSimulation(float deltaTime) { + + applyRForceWithForceScalar(gForceScalar); // apply force defined by apply force slider + + if (m_dynamicsWorld) { + m_dynamicsWorld->stepSimulation(deltaTime); + } +} + +void NewtonsRopeCradleExample::createRopePendulum(btSphereShape* colShape, + const btVector3& position, const btQuaternion& pendulumOrientation, btScalar width, btScalar height, btScalar mass) { + + // The pendulum looks like this (names when built): + // O O topSphere1 topSphere2 + // \ / + // O bottomSphere + + //create a dynamic pendulum + btTransform startTransform; + startTransform.setIdentity(); + + // calculate sphere positions + btVector3 topSphere1RelPosition(0,0,width); + btVector3 topSphere2RelPosition(0,0,-width); + btVector3 bottomSphereRelPosition(0,-height,0); + + + // position the top sphere above ground with appropriate orientation + startTransform.setOrigin(btVector3(0,0,0)); // no translation intitially + startTransform.setRotation(pendulumOrientation); // pendulum rotation + startTransform.setOrigin(startTransform * topSphere1RelPosition); // rotate this position + startTransform.setOrigin(position + startTransform.getOrigin()); // add non-rotated position to the relative position + btRigidBody* topSphere1 = createRigidBody(0, startTransform, colShape); // make top sphere static + + // position the top sphere above ground with appropriate orientation + startTransform.setOrigin(btVector3(0,0,0)); // no translation intitially + startTransform.setRotation(pendulumOrientation); // pendulum rotation + startTransform.setOrigin(startTransform * topSphere2RelPosition); // rotate this position + startTransform.setOrigin(position + startTransform.getOrigin()); // add non-rotated position to the relative position + btRigidBody* topSphere2 = createRigidBody(0, startTransform, colShape); // make top sphere static + + // position the bottom sphere below the top sphere + startTransform.setOrigin(btVector3(0,0,0)); // no translation intitially + startTransform.setRotation(pendulumOrientation); // pendulum rotation + startTransform.setOrigin(startTransform * bottomSphereRelPosition); // rotate this position + startTransform.setOrigin(position + startTransform.getOrigin()); // add non-rotated position to the relative position + btRigidBody* bottomSphere = createRigidBody(mass, startTransform, colShape); + bottomSphere->setFriction(0); // we do not need friction here + pendula.push_back(bottomSphere); + + // disable the deactivation when objects do not move anymore + topSphere1->setActivationState(DISABLE_DEACTIVATION); + topSphere2->setActivationState(DISABLE_DEACTIVATION); + bottomSphere->setActivationState(DISABLE_DEACTIVATION); + + bottomSphere->setRestitution(gPendulaRestitution); // set pendula restitution + + // add ropes between spheres + connectWithRope(topSphere1, bottomSphere); + connectWithRope(topSphere2, bottomSphere); +} + +void NewtonsRopeCradleExample::renderScene() +{ + CommonRigidBodyBase::renderScene(); + btSoftRigidDynamicsWorld* softWorld = getSoftDynamicsWorld(); + + for ( int i=0;igetSoftBodyArray().size();i++) + { + btSoftBody* psb=(btSoftBody*)softWorld->getSoftBodyArray()[i]; + //if (softWorld->getDebugDrawer() && !(softWorld->getDebugDrawer()->getDebugMode() & (btIDebugDraw::DBG_DrawWireframe))) + { + btSoftBodyHelpers::DrawFrame(psb,softWorld->getDebugDrawer()); + btSoftBodyHelpers::Draw(psb,softWorld->getDebugDrawer(),softWorld->getDrawFlags()); + } + } +} + +void NewtonsRopeCradleExample::changePendulaRestitution(btScalar restitution) { + for (std::vector::iterator rit = pendula.begin(); + rit != pendula.end(); rit++) { + btAssert((*rit) && "Null constraint"); + + (*rit)->setRestitution(restitution); + } +} + +bool NewtonsRopeCradleExample::keyboardCallback(int key, int state) { + //b3Printf("Key pressed: %d in state %d \n",key,state); + + // key 3 + switch (key) { + case '3' /*ASCII for 3*/: { + applyPendulumForce(gDisplacementForce); + return true; + } + } + + return false; +} + +void NewtonsRopeCradleExample::applyPendulumForce(btScalar pendulumForce){ + if(pendulumForce != 0){ + b3Printf("Apply %f to pendulum",pendulumForce); + for (int i = 0; i < gDisplacedPendula; i++) { + if (gDisplacedPendula >= 0 && gDisplacedPendula <= gPendulaQty) + pendula[i]->applyCentralForce(btVector3(pendulumForce, 0, 0)); + } + } +} + +// GUI parameter modifiers + +void onRopePendulaRestitutionChanged(float pendulaRestitution) { + if (nex){ + nex->changePendulaRestitution(pendulaRestitution); + } +} + +void floorRSliderValue(float notUsed) { + gPendulaQty = floor(gPendulaQty); + gDisplacedPendula = floor(gDisplacedPendula); + gRopeResolution = floor(gRopeResolution); +} + +void applyRForceWithForceScalar(float forceScalar) { + if(nex){ + btScalar appliedForce = forceScalar * gDisplacementForce; + + if(fabs(gForceScalar) < 0.2f) + gForceScalar = 0; + + nex->applyPendulumForce(appliedForce); + } +} + +CommonExampleInterface* ET_NewtonsRopeCradleCreateFunc( + CommonExampleOptions& options) { + nex = new NewtonsRopeCradleExample(options.m_guiHelper); + return nex; +} diff --git a/examples/ExtendedTutorials/NewtonsRopeCradle.h b/examples/ExtendedTutorials/NewtonsRopeCradle.h new file mode 100644 index 000000000..3edbcd5af --- /dev/null +++ b/examples/ExtendedTutorials/NewtonsRopeCradle.h @@ -0,0 +1,22 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2015 Google Inc. http://bulletphysics.org + +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. +*/ + +#ifndef ET_NEWTONS_ROPE_CRADLE_EXAMPLE_H +#define ET_NEWTONS_ROPE_CRADLE_EXAMPLE_H + +class CommonExampleInterface* ET_NewtonsRopeCradleCreateFunc(struct CommonExampleOptions& options); + + +#endif //ET_NEWTONS_ROPE_CRADLE_EXAMPLE_H