====== LAB9: Physics ====== In this lab we will scratch the surface of using the Bullet physics blibrary with Ogre. {{ :public:t-gede-14-1:ogrebullet1.png?nolink |}}{{ :public:t-gede-14-1:ogrebullet2.png?nolink |}} ===== Discussion ===== Discussion thread for this lab is on [[https://piazza.com/class/ij2xbg3ztb41qt?cid=43|Piazza]] ===== Bullet ===== - Setup Bullet Project. - Downlaod [[https://github.com/bulletphysics/bullet3/releases|Bullet]] - Use version 2.83.7 (later versions are not supported). - Build Bullet - Run \build\vs2010.bat to generate the VS 2010 project files. - Open the \build\vs2010\0BulletSolution.sln With you VS of choice. - Visual studio should prompt you to update the project to visual studio, accept that. - We have confirmed that compiling using either the v110 version or the v141 version of the visual studio compiler works \\ **Note:** that you can select properties for one project, then multi select the projects you want to change the properties for to change the value in multiple projects at the same time. - Change the C/C++->Code Generation->Runtime Library to Multi Threaded DLL (/MDd (Debug) and /MD (Release)) - Build the solution for debug and release. **You can skip all projects starting with test_ or app_ if you like.** - **Note:** If your Ogre was compiled for 64 bit, your Bullet build will **also** need to be compiled for 64 bit! ===== Lab ===== And now for the Ogre project. **You will not be able to compile until you have finished step 8!** - **Add** ''\bin;'' to ''Linker->General->Additional Library Directories'' (Debug and Release) - **Add** ''\src;'' to ''C++->General->Additional Include Directories'' (Debug and Release) - **Add (note: file names may vary slightly, e.g. _64bit is appended for 64 bit builds, verify what files you have!)** ''BulletCollision_vs2010_debug.lib;BulletDynamics_vs2010_debug.lib;LinearMath_vs2010_debug.lib'' to the ''Linker->Input->Additional Dependencies'' (Debug) - **Add (note: file names may vary slightly, e.g. _64bit is appended for 64 bit builds, verify what files you have!)** ''BulletCollision_vs2010.lib;BulletDynamics_vs2010.lib;LinearMath_vs2010.lib'' to the ''Linker->Input->Additional Dependencies'' (Release) - **Create class Physics.** // Header! #ifndef PHYSICS_H #define PHYSICS_H #include "btBulletCollisionCommon.h" #include "btBulletDynamicsCommon.h" #include "OgreMotionState.h" #include #include class Physics{ btDefaultCollisionConfiguration* collisionConfiguration; btCollisionDispatcher* dispatcher; btBroadphaseInterface* overlappingPairCache; btSequentialImpulseConstraintSolver* solver; btDiscreteDynamicsWorld* dynamicsWorld; std::vector collisionShapes; std::map physicsAccessors; public: Physics(); void initObjects(); virtual ~Physics(); btDiscreteDynamicsWorld* getDynamicsWorld(); }; #endif //PHYSICS_H // Implementation! #include "Physics.h" Physics::Physics() { } Physics::~Physics() { delete dynamicsWorld; delete solver; delete overlappingPairCache; delete dispatcher; delete collisionConfiguration; } void Physics::initObjects() { collisionConfiguration = new btDefaultCollisionConfiguration(); dispatcher = new btCollisionDispatcher(collisionConfiguration); overlappingPairCache = new btDbvtBroadphase(); solver = new btSequentialImpulseConstraintSolver(); dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration); } btDiscreteDynamicsWorld* Physics::getDynamicsWorld() { return dynamicsWorld; } - **Create a pointer** to physics in your Application, and initialize it in the startup function. _physicsEngine = new Physics(); _physicsEngine->initObjects(); - **Before we start** there are some basic changes that have to made to the environment, in order to make the physics look more realist, we must make sure that our environmet is in a logical scale. If you have the handout code, Sinbad will be around 9.5 units high, Bullet library uses 1 unit as equal to 1 meter. - **You can check out Sinbad's actualt height** by doing ''_SinbadNode->getBoundingBox().getSize()'', this function will return the bounding box the the sceneNode, which is an axis aligned box that encompasses the entities of the SceneNode and is defined by the vertex extremas + an additional 2% scale. - **Now scale** the sinbad node down by 0.2 ''_SinbadNode->scale(Ogre::Vector3(0.2f,0.2f,0.2f));'' - **Change** the nearClippingPlaneDistance of the camera to 0.5 - **In the frameListener class,** change the movement speed of the camera to something around 10-20. - **Now to create Rigid Bodies we must** first let bullet know what form of rigid body we want, and we must also tell bullet the size, position and orientation of our RigidBody. - Add this function to your Physics class. btRigidBody* AddDynamicCubeRigidBoidy(Ogre::SceneNode* node, Ogre::Entity* ent, btScalar mass) btRigidBody* Physics::AddDynamicCubeRigidBoidy(Ogre::SceneNode* node, Ogre::Entity* ent, btScalar mass) { Ogre::Vector3 aabbSize = ent->getBoundingBox().getSize() / 1.02f; btScalar x = 0.5f * node->getScale().x * aabbSize.x; btScalar y = 0.5f * node->getScale().y * aabbSize.y; btScalar z = 0.5f * node->getScale().z * aabbSize.z; btCollisionShape* colShape = new btBoxShape(btVector3(x, y, z)); /// Create Dynamic Objects btTransform startTransform; startTransform.setIdentity(); //rigidbody is dynamic if and only if mass is non zero, otherwise static bool isDynamic = (mass != 0.f); btVector3 localInertia(0, 0, 0); if (isDynamic) colShape->calculateLocalInertia(mass, localInertia); startTransform.setOrigin(btVector3(node->getPosition().x, node->getPosition().y, node->getPosition().z)); btQuaternion initRotation(node->getOrientation().x, node->getOrientation().y, node->getOrientation().z, node->getOrientation().w); startTransform.setRotation(initRotation); OgreMotionState* motionState = new OgreMotionState(startTransform, node); btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, motionState, colShape, localInertia); btRigidBody* body = new btRigidBody(rbInfo); dynamicsWorld->addRigidBody(body); return body; } - As you might have noticed, that function uses a class called OgreMotionState, this is a class that we must implement in order for Bullet to automatically transoform our SceneNode according to the movement of the physics object. - Add the file OgreMotionState.h to your project.#ifndef OGREMOTIONSTATE_H #define OGREMOTIONSTATE_H #include "LinearMath\btMotionState.h" #include "OGRE\Ogre.h" class OgreMotionState : public btMotionState { public: OgreMotionState(const btTransform &initialpos, Ogre::SceneNode *node) { mVisibleobj = node; mPos1 = initialpos; } virtual ~OgreMotionState() { } void setNode(Ogre::SceneNode *node) { mVisibleobj = node; } virtual void getWorldTransform(btTransform &worldTrans) const { worldTrans = mPos1; } virtual void setWorldTransform(const btTransform &worldTrans) { if (NULL == mVisibleobj) return; btQuaternion rot = worldTrans.getRotation(); mVisibleobj->setOrientation(rot.w(), rot.x(), rot.y(), rot.z()); btVector3 pos = worldTrans.getOrigin(); mVisibleobj->setPosition(pos.x(), pos.y(), pos.z()); } protected: Ogre::SceneNode *mVisibleobj; btTransform mPos1; }; #endif //OGREMOTIONSTATE_H - **Now we are ready to create some rigid Bodies.** - **Lets start by setting the ground as a collision plane**. Make the following changes and add the bullet code to the definition of your ground plane. // Create the plane. Ogre::Plane plane(Ogre::Vector3::UNIT_Y, 0); Ogre::MeshManager::getSingleton().createPlane("plane", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane, 1500, 1500, 200, 200, true, 1, 5, 5, Ogre::Vector3::UNIT_Z); // Set the plane as the ground and add a texture to it. Ogre::Entity* ground = _sceneManager->createEntity("LightPlaneEntity", "plane"); Ogre::SceneNode* groundNode = _sceneManager->getRootSceneNode()->createChildSceneNode(); groundNode->attachObject(ground); ground->setMaterialName("Examples/BeachStones"); groundNode->setPosition(0, -1, 0); // Create the collision shape, and give it the ground plane normals. btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(plane.normal.x, plane.normal.y, plane.normal.z), 0); // Create the collision transform. btTransform groundTransform; // Set up the collision location and orientation. groundTransform.setIdentity(); groundTransform.setOrigin(btVector3(groundNode->getPosition().x, groundNode->getPosition().y, groundNode->getPosition().z)); btQuaternion initRotation(groundNode->getOrientation().x, groundNode->getOrientation().y, groundNode->getOrientation().z, groundNode->getOrientation().w); groundTransform.setRotation(initRotation); // Give the plane a mass of 0, because our plane will be static, thus will not moce. btScalar mass(0.0f); // Set the ground as a static object. bool isDynamic = false; // This plane isnt going to be moving so i dont care about setting the motion state btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, myMotionState, groundShape); btRigidBody* body = new btRigidBody(rbInfo); //add the body to the dynamics world _physicsEngine->getDynamicsWorld()->addRigidBody(body); - **Now lets add some cubes :)**, add this function to your create scene function. int ringCount = 16; int ringheight = 10; for (int i = 0; i < ringheight; ++i) { for (int j = 0; j < ringCount; ++j) { Ogre::Entity* cubeEnt = _sceneManager->createEntity("Cube.mesh"); Ogre::SceneNode* cubeNode = _sceneManager->getRootSceneNode()->createChildSceneNode(); cubeNode->setScale(0.006f, 0.004f, 0.006f); Ogre::Radian angle((Ogre::Math::TWO_PI / ringCount)*j + (Ogre::Math::TWO_PI / ringCount)*0.5f*(i%2) ); cubeNode->setOrientation(Ogre::Quaternion(-angle, Ogre::Vector3::UNIT_Y)); //cubeNode->setPosition(j*0.62+((i%2)*0.31f), i*0.405-(1-0.2), 0); cubeNode->setPosition(Ogre::Math::Cos(angle) * 1.9f, i*0.405f - (1 - 0.2f), Ogre::Math::Sin(angle) * 1.9f); cubeNode->attachObject(cubeEnt); btRigidBody* temp = _physicsEngine->AddDynamicCubeRigidBoidy(cubeNode, cubeEnt, 1); temp->setFriction(1); } } - **And finally we must update the dynamicWorld** and of course make the camera shoot some boxes! Add the RoamingCamera from Lab 3 // HEADER! #pragma once #include #include // A simple indepdendent camera that can be flown around the scene // using the mouse and WASD keys, while pressing and holding the // right mouse button. class RoamingCamera { public: // Constructor // sceneManager: contains the scene the camera will roam about // win: is a valid render window into which the camera view will get rendered RoamingCamera(Ogre::SceneManager* sceneManager, Ogre::RenderWindow* win, Physics* physicsEngine); // Destructor ~RoamingCamera(); // Update // dt: Elapsed time in seconds from the last time this method got called // state: Valid SDL keyboard state, as retrieved with SDL_GetKeyboardState(..) void update(Ogre::Real dt, const Uint8 * state); private: Ogre::Camera* _cam; // My actual camera Ogre::SceneNode* _node; // My scene graph node Physics* _physicsEngine; }; // Implementation! #include "RoamingCamera.h" RoamingCamera::RoamingCamera(Ogre::SceneManager* sceneManager, Ogre::RenderWindow* win, Physics* physicsEngine) { _physicsEngine = physicsEngine; // Attaching the camera _node = sceneManager->getRootSceneNode()->createChildSceneNode(); _cam = sceneManager->createCamera("myCam"); _node->attachObject(_cam); _node->setPosition(0, 0, 50); _node->lookAt(Ogre::Vector3(0, 0, 0), Ogre::Node::TransformSpace::TS_WORLD); // Configuring the camera _cam->setNearClipDistance(5); Ogre::Viewport* vp = win->addViewport(_cam); vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0)); _cam->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight())); } RoamingCamera::~RoamingCamera() { } void RoamingCamera::update(Ogre::Real dt, const Uint8 * state) { int x = 0, y = 0; // Leave if right mouse button is not being pressed // ...but also retrieve and store mouse movement if (!(SDL_GetRelativeMouseState(&x, &y) & SDL_BUTTON_RMASK)) return; // Construct displacement vector Ogre::Vector3 vec = Ogre::Vector3(0, 0, 0); if (state[SDL_SCANCODE_W]) { vec = Ogre::Vector3(0, 0, -1); } if (state[SDL_SCANCODE_S]) { vec = Ogre::Vector3(0, 0, 1); } if (state[SDL_SCANCODE_A]) { vec = Ogre::Vector3(-1, 0, 0); } if (state[SDL_SCANCODE_D]) { vec = Ogre::Vector3(1, 0, 0); } if (state[SDL_SCANCODE_Q]) { vec = Ogre::Vector3(0, 1, 0); } if (state[SDL_SCANCODE_E]) { vec = Ogre::Vector3(0, -1, 0); } // Construct view rotation float rotX = x * dt * -1 * 2.0f; float rotY = y * dt * -1 * 2.0f; // Update camera with new displacement and rotation _cam->yaw(Ogre::Radian(rotX)); _cam->pitch(Ogre::Radian(rotY)); _cam->moveRelative(dt * 4.0 * vec); } make it shoot boxes when some button is pressed, you can shoot boxes like this: // Create a cube entity. Ogre::Entity* cubeEnt = _sceneManager->createEntity("Cube.mesh"); Ogre::SceneNode* cubeNode = _sceneManager->getRootSceneNode()->createChildSceneNode(); cubeNode->setScale(0.005f, 0.005f, 0.005f); // Set the position of the cube to the front of the origin of the camera cubeNode->setPosition(_cam->getPosition() + _cam->getDirection()); cubeNode->attachObject(cubeEnt); // Now make the cube a rigid body, give it some linearVelocity and add it to the physics world btRigidBody* boxBody = _physicsEngine->AddDynamicCubeRigidBoidy(cubeNode, cubeEnt, 2); boxBody->setLinearVelocity(btVector3(_cam->getDirection().x, _cam->getDirection().y, _cam->getDirection().z) * 30); - **Finally** we update the physics world // Now update the physics world with the delta time. "Note: normally we would want to have the physics world update a little more independant of the framerate, but this will do for now :)" _physicsEngine->getDynamicsWorld()->stepSimulation(evt.timeSinceLastFrame); ** Compile and run using a Release built target!!! Very important, unless you plan to fry some eggs and bacon on your laptop :)** Just make sure to use the Release version of the plugins file when you run the application.It can be found under ''\bin\Release\plugins.cfg'' **Check out these pre-processor variables to help you out**#if _DEBUG cf.load("../LabFiles/OgreConfig/resources_d.cfg"); #elif NDEBUG cf.load("../LabFiles/OgreConfig/resources.cfg"); #endif #if _DEBUG _root = new Ogre::Root("../LabFiles/OgreConfig/plugins_d.cfg","../LabFiles/OgreConfig/ogre.cfg", "../LabFiles/ogre.log"); #elif NDEBUG _root = new Ogre::Root("../LabFiles/OgreConfig/plugins.cfg","../LabFiles/OgreConfig/ogre.cfg", "../LabFiles/ogre.log"); #endif ===== When You Are Finished ===== Upload your **commented source files** into Lab9 in MySchool (zip them up if more than one).