From 70b5d6857a182de419fc2faf63a0f7e177da8f7f Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 4 Nov 2014 20:52:28 +1100 Subject: [PATCH] Add missing files. --- apps/opencs/view/world/physicsengine.cpp | 441 +++++++++++++++++++++++ apps/opencs/view/world/physicsengine.hpp | 253 +++++++++++++ 2 files changed, 694 insertions(+) create mode 100644 apps/opencs/view/world/physicsengine.cpp create mode 100644 apps/opencs/view/world/physicsengine.hpp diff --git a/apps/opencs/view/world/physicsengine.cpp b/apps/opencs/view/world/physicsengine.cpp new file mode 100644 index 000000000..cdfc818a5 --- /dev/null +++ b/apps/opencs/view/world/physicsengine.cpp @@ -0,0 +1,441 @@ +#include "physicsengine.hpp" + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +// PLEASE NOTE: +// +// This file is based on libs/openengine/bullet/physic.cpp. The commit history and +// credits for the code below stem from that file. + +namespace CSVWorld +{ + RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo &CI, + const std::string &referenceId) : btRigidBody(CI) , mReferenceId(referenceId) + { + } + + RigidBody::~RigidBody() + { + delete getMotionState(); + } + + // In OpenCS one document has one PhysicsSystem. Each PhysicsSystem contains one + // PhysicsEngine. One document can have 0..n SceneWidgets, each with its own + // Ogre::SceneManager. + // + // These relationships are managed by the PhysicsManager. + // + // - When a view is created its document is registered with the PhysicsManager in + // View's constructor. If the document is new a PhysicSystem is created and + // associated with that document. A null list of SceneWidgets are assigned to + // that document. + // + // - When all views for a given document is closed, ViewManager will notify the + // PhysicsManager to destroy the PhysicsSystem associated with the document. + // + // - Each time a WorldspaceWidget (or its subclass) is created, it gets the + // PhysicsSystem associates with the widget's document from the PhysicsManager. + // The list of widgets are then maintained, but is not necessary and may be + // removed in future code updates. + // + // Each WorldspaceWidget can have objects (References) and terrain (Land) loaded + // from its document. There may be several views of the object, however there + // is only one corresponding physics object. i.e. each Reference can have 1..n + // SceneNodes + // + // These relationships are managed by the PhysicsSystem for the document. + // + // - Each time a WorldspaceWidget (or its subclass) is created, it registers + // itself and its Ogre::SceneManager with the PhysicsSystem assigned by the + // PhysicsManager. + // + // - Each time an object is added, the object's Ogre::SceneNode name is registered + // with the PhysicsSystem. Ogre itself maintains which SceneNode belongs to + // which SceneManager. + // + // - Each time an terrain is added, its cell coordinates and the SceneManager is + // registered with the PhysicsSystem. + // + PhysicsEngine::PhysicsEngine() + { + // Set up the collision configuration and dispatcher + mCollisionConfiguration = new btDefaultCollisionConfiguration(); + mDispatcher = new btCollisionDispatcher(mCollisionConfiguration); + + // The actual physics solver + mSolver = new btSequentialImpulseConstraintSolver; + + mBroadphase = new btDbvtBroadphase(); + + // The world. + mDynamicsWorld = new btDiscreteDynamicsWorld(mDispatcher, + mBroadphase, mSolver, mCollisionConfiguration); + + // Don't update AABBs of all objects every frame. Most objects in MW are static, + // so we don't need this. Should a "static" object ever be moved, we have to + // update its AABB manually using DynamicsWorld::updateSingleAabb. + mDynamicsWorld->setForceUpdateAllAabbs(false); + + mDynamicsWorld->setGravity(btVector3(0, 0, -10)); + + if(OEngine::Physic::BulletShapeManager::getSingletonPtr() == NULL) + { + new OEngine::Physic::BulletShapeManager(); + } + + mShapeLoader = new NifBullet::ManualBulletShapeLoader(); + } + + PhysicsEngine::~PhysicsEngine() + { + HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); + for(; hf_it != mHeightFieldMap.end(); ++hf_it) + { + mDynamicsWorld->removeRigidBody(hf_it->second.mBody); + delete hf_it->second.mShape; + delete hf_it->second.mBody; + } + + RigidBodyContainer::iterator rb_it = mCollisionObjectMap.begin(); + for(; rb_it != mCollisionObjectMap.end(); ++rb_it) + { + if (rb_it->second != NULL) + { + mDynamicsWorld->removeRigidBody(rb_it->second); + + delete rb_it->second; + rb_it->second = NULL; + } + } + + rb_it = mRaycastingObjectMap.begin(); + for (; rb_it != mRaycastingObjectMap.end(); ++rb_it) + { + if (rb_it->second != NULL) + { + mDynamicsWorld->removeRigidBody(rb_it->second); + + delete rb_it->second; + rb_it->second = NULL; + } + } + + delete mDynamicsWorld; // FIXME: need to reference count?? + delete mSolver; + delete mCollisionConfiguration; + delete mDispatcher; + delete mBroadphase; + delete mShapeLoader; + + delete OEngine::Physic::BulletShapeManager::getSingletonPtr(); // FIXME: need to reference count + } + + int PhysicsEngine::toggleDebugRendering(Ogre::SceneManager *sceneMgr) + { + if(!sceneMgr) + return 0; + + std::map::iterator iter = + mDebugDrawers.find(sceneMgr); + if(iter != mDebugDrawers.end()) // found scene manager + { + if((*iter).second) + { + // set a new drawer each time (maybe with a different scene manager) + mDynamicsWorld->setDebugDrawer(mDebugDrawers[sceneMgr]); + if(!mDebugDrawers[sceneMgr]->getDebugMode()) + mDebugDrawers[sceneMgr]->setDebugMode(1 /*mDebugDrawFlags*/); + else + mDebugDrawers[sceneMgr]->setDebugMode(0); + + mDynamicsWorld->debugDrawWorld(); // FIXME: call this now? + return mDebugDrawers[sceneMgr]->getDebugMode(); + } + } + return 0; + } + + void PhysicsEngine::stepDebug(Ogre::SceneManager *sceneMgr) + { + if(!sceneMgr) + return; + + std::map::iterator iter = + mDebugDrawers.find(sceneMgr); + if(iter != mDebugDrawers.end()) // found scene manager + { + if((*iter).second) + (*iter).second->step(); + else + return; + } + } + + void PhysicsEngine::createDebugDraw(Ogre::SceneManager *sceneMgr) + { + if(mDebugDrawers.find(sceneMgr) == mDebugDrawers.end()) + { + mDebugSceneNodes[sceneMgr] = sceneMgr->getRootSceneNode()->createChildSceneNode(); + mDebugDrawers[sceneMgr] = new BtOgre::DebugDrawer(mDebugSceneNodes[sceneMgr], mDynamicsWorld); + mDebugDrawers[sceneMgr]->setDebugMode(0); + } + } + + void PhysicsEngine::removeDebugDraw(Ogre::SceneManager *sceneMgr) + { + std::map::iterator iter = + mDebugDrawers.find(sceneMgr); + if(iter != mDebugDrawers.end()) + { + delete (*iter).second; + mDebugDrawers.erase(iter); + } + + std::map::iterator it = + mDebugSceneNodes.find(sceneMgr); + if(it != mDebugSceneNodes.end()) + { + std::string sceneNodeName = (*it).second->getName(); + if(sceneMgr->hasSceneNode(sceneNodeName)) + sceneMgr->destroySceneNode(sceneNodeName); + } + } + + void PhysicsEngine::addHeightField(float* heights, + int x, int y, float yoffset, + float triSize, float sqrtVerts) + { + const std::string name = "HeightField_" + + boost::lexical_cast(x) + "_" + + boost::lexical_cast(y); + + // find the minimum and maximum heights (needed for bullet) + float minh = heights[0]; + float maxh = heights[0]; + for (int i=0; imaxh) maxh = h; + if (hsetUseDiamondSubdivision(true); + + btVector3 scl(triSize, triSize, 1); + hfShape->setLocalScaling(scl); + + btRigidBody::btRigidBodyConstructionInfo CI = + btRigidBody::btRigidBodyConstructionInfo(0,0,hfShape); + RigidBody* body = new RigidBody(CI, name); + body->getWorldTransform().setOrigin(btVector3( + (x+0.5)*triSize*(sqrtVerts-1), + (y+0.5)*triSize*(sqrtVerts-1), + (maxh+minh)/2.f)); + + HeightField hf; + hf.mBody = body; + hf.mShape = hfShape; + + mHeightFieldMap [name] = hf; + + mDynamicsWorld->addRigidBody(body, CollisionType_HeightMap, + CollisionType_Actor|CollisionType_Raycasting|CollisionType_Projectile); + } + + void PhysicsEngine::removeHeightField(int x, int y) + { + const std::string name = "HeightField_" + + boost::lexical_cast(x) + "_" + + boost::lexical_cast(y); + + HeightField hf = mHeightFieldMap [name]; + + mDynamicsWorld->removeRigidBody(hf.mBody); + delete hf.mShape; + delete hf.mBody; + + mHeightFieldMap.erase(name); + } + + void PhysicsEngine::adjustRigidBody(RigidBody* body, + const Ogre::Vector3 &position, const Ogre::Quaternion &rotation) + { + btTransform tr; + //Ogre::Quaternion boxrot = rotation * boxRotation; + Ogre::Quaternion boxrot = rotation * Ogre::Quaternion::IDENTITY; + //Ogre::Vector3 transrot = boxrot * scaledBoxTranslation; + Ogre::Vector3 transrot = boxrot * Ogre::Vector3::ZERO; + Ogre::Vector3 newPosition = transrot + position; + + tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); + tr.setRotation(btQuaternion(boxrot.x,boxrot.y,boxrot.z,boxrot.w)); + body->setWorldTransform(tr); + } + + RigidBody* PhysicsEngine::createAndAdjustRigidBody(const std::string &mesh, + const std::string &name, // referenceId, assumed unique per OpenCS document + float scale, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + bool raycasting, + bool placeable) // indicates whether the object can be picked up by the player + { + std::string sid = (boost::format("%07.3f") % scale).str(); + std::string outputstring = mesh + sid; + + //get the shape from the .nif + mShapeLoader->load(outputstring, "General"); + OEngine::Physic::BulletShapeManager::getSingletonPtr()->load(outputstring, "General"); + OEngine::Physic::BulletShapePtr shape = + OEngine::Physic::BulletShapeManager::getSingleton().getByName(outputstring, "General"); + + if (placeable && !raycasting && shape->mCollisionShape && !shape->mHasCollisionNode) + return NULL; + + if (!shape->mCollisionShape && !raycasting) + return NULL; + + if (!shape->mRaycastingShape && raycasting) + return NULL; + + btCollisionShape* collisionShape = raycasting ? shape->mRaycastingShape : shape->mCollisionShape; + collisionShape->setLocalScaling(btVector3(scale, scale, scale)); + + //create the real body + btRigidBody::btRigidBodyConstructionInfo CI = + btRigidBody::btRigidBodyConstructionInfo(0, 0, collisionShape); + + RigidBody* body = new RigidBody(CI, name); + + adjustRigidBody(body, position, rotation); + + if (!raycasting) + { + assert (mCollisionObjectMap.find(name) == mCollisionObjectMap.end()); + mCollisionObjectMap[name] = body; + mDynamicsWorld->addRigidBody(body, + CollisionType_World, CollisionType_Actor|CollisionType_HeightMap); + } + else + { + assert (mRaycastingObjectMap.find(name) == mRaycastingObjectMap.end()); + mRaycastingObjectMap[name] = body; + mDynamicsWorld->addRigidBody(body, + CollisionType_Raycasting, CollisionType_Raycasting|CollisionType_Projectile); + } + + return body; + } + + void PhysicsEngine::removeRigidBody(const std::string &name) + { + RigidBodyContainer::iterator it = mCollisionObjectMap.find(name); + if (it != mCollisionObjectMap.end() ) + { + RigidBody* body = it->second; + if(body != NULL) + { + mDynamicsWorld->removeRigidBody(body); + } + } + + it = mRaycastingObjectMap.find(name); + if (it != mRaycastingObjectMap.end() ) + { + RigidBody* body = it->second; + if(body != NULL) + { + mDynamicsWorld->removeRigidBody(body); + } + } + } + + void PhysicsEngine::deleteRigidBody(const std::string &name) + { + RigidBodyContainer::iterator it = mCollisionObjectMap.find(name); + if (it != mCollisionObjectMap.end() ) + { + RigidBody* body = it->second; + + if(body != NULL) + { + delete body; + } + mCollisionObjectMap.erase(it); + } + + it = mRaycastingObjectMap.find(name); + if (it != mRaycastingObjectMap.end() ) + { + RigidBody* body = it->second; + + if(body != NULL) + { + delete body; + } + mRaycastingObjectMap.erase(it); + } + } + + RigidBody* PhysicsEngine::getRigidBody(const std::string &name, bool raycasting) + { + RigidBodyContainer* map = raycasting ? &mRaycastingObjectMap : &mCollisionObjectMap; + RigidBodyContainer::iterator it = map->find(name); + if (it != map->end() ) + { + RigidBody* body = (*map)[name]; + return body; + } + else + { + return NULL; + } + } + + std::pair PhysicsEngine::rayTest(const btVector3 &from, + const btVector3 &to, bool raycastingObjectOnly, bool ignoreHeightMap, Ogre::Vector3* normal) + { + std::string referenceId = ""; + float d = -1; + + btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); + resultCallback1.m_collisionFilterGroup = 0xff; + + if(raycastingObjectOnly) + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor; + else + resultCallback1.m_collisionFilterMask = CollisionType_World; + + if(!ignoreHeightMap) + resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap; + + mDynamicsWorld->rayTest(from, to, resultCallback1); + if (resultCallback1.hasHit()) + { + referenceId = static_cast(*resultCallback1.m_collisionObject).getReferenceId(); + d = resultCallback1.m_closestHitFraction; + if (normal) + *normal = Ogre::Vector3(resultCallback1.m_hitNormalWorld.x(), + resultCallback1.m_hitNormalWorld.y(), + resultCallback1.m_hitNormalWorld.z()); + } + + return std::pair(referenceId, d); + } +} diff --git a/apps/opencs/view/world/physicsengine.hpp b/apps/opencs/view/world/physicsengine.hpp new file mode 100644 index 000000000..9d60fe5f1 --- /dev/null +++ b/apps/opencs/view/world/physicsengine.hpp @@ -0,0 +1,253 @@ +#ifndef CSV_WORLD_PHYSICSENGINE_H +#define CSV_WORLD_PHYSICSENGINE_H + +//#include +#include + +#include +//#include "BulletCollision/CollisionDispatch/btGhostObject.h" +//#include +//#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" +//#include +#include +#include // needs Ogre::SceneNode defined + +//#include +//#include + +//class btRigidBody; +class btBroadphaseInterface; +class btDefaultCollisionConfiguration; +class btSequentialImpulseConstraintSolver; +class btCollisionDispatcher; +class btDiscreteDynamicsWorld; +class btHeightfieldTerrainShape; +//class btCollisionObject; + +namespace BtOgre +{ + class DebugDrawer; +} + +namespace Ogre +{ + class Vector3; + class SceneNode; + class SceneManager; + class Quaternion; +} + +namespace OEngine +{ + namespace Physic + { + class BulletShapeLoader; + } +} + +namespace CSVWorld +{ + + // enum btIDebugDraw::DebugDrawModes + // { + // DBG_NoDebug=0, + // DBG_DrawWireframe = 1, + // DBG_DrawAabb=2, + // DBG_DrawFeaturesText=4, + // DBG_DrawContactPoints=8, + // DBG_NoDeactivation=16, + // DBG_NoHelpText = 32, + // DBG_DrawText=64, + // DBG_ProfileTimings = 128, + // DBG_EnableSatComparison = 256, + // DBG_DisableBulletLCP = 512, + // DBG_EnableCCD = 1024, + // DBG_DrawConstraints = (1 << 11), + // DBG_DrawConstraintLimits = (1 << 12), + // DBG_FastWireframe = (1<<13), + // DBG_DrawNormals = (1<<14), + // DBG_MAX_DEBUG_DRAW_MODE + // }; + // +#if 0 + class CSVDebugDrawer : public BtOgre::DebugDrawer + { + BtOgre::DebugDrawer *mDebugDrawer; + Ogre::SceneManager *mSceneMgr; + Ogre::SceneNode *mSceneNode; + int mDebugMode; + + public: + + CSVDebugDrawer(Ogre::SceneManager *sceneMgr, btDiscreteDynamicsWorld *dynamicsWorld); + ~CSVDebugDrawer(); + + void setDebugMode(int mode); + bool toggleDebugRendering(); + }; +#endif + + // This class is just an extension of normal btRigidBody in order to add extra info. + // When bullet give back a btRigidBody, you can just do a static_cast to RigidBody, + // so one never should use btRigidBody directly! + class RigidBody: public btRigidBody + { + std::string mReferenceId; + + public: + + RigidBody(btRigidBody::btRigidBodyConstructionInfo &CI, const std::string &referenceId); + virtual ~RigidBody(); + + std::string getReferenceId() const { return mReferenceId; } + }; + + struct HeightField + { + btHeightfieldTerrainShape* mShape; + RigidBody* mBody; + }; + + /** + * The PhysicsEngine class contain everything which is needed for Physic. + * It's needed that Ogre Resources are set up before the PhysicsEngine is created. + * Note:deleting it WILL NOT delete the RigidBody! + * TODO:unload unused resources? + */ + class PhysicsEngine + { + //Bullet Stuff + btBroadphaseInterface *mBroadphase; + btDefaultCollisionConfiguration *mCollisionConfiguration; + btSequentialImpulseConstraintSolver *mSolver; + btCollisionDispatcher *mDispatcher; + btDiscreteDynamicsWorld *mDynamicsWorld; + + //the NIF file loader. + OEngine::Physic::BulletShapeLoader *mShapeLoader; + + typedef std::map HeightFieldContainer; + HeightFieldContainer mHeightFieldMap; + + typedef std::map RigidBodyContainer; + RigidBodyContainer mCollisionObjectMap; + + RigidBodyContainer mRaycastingObjectMap; + + std::map mDebugDrawers; + std::map mDebugSceneNodes; + +#if 0 + // from bullet + enum CollisionFilterGroups + { + DefaultFilter = 1, + StaticFilter = 2, + KinematicFilter = 4, + DebrisFilter = 8, + SensorTrigger = 16, + CharacterFilter = 32, + AllFilter = -1 //all bits sets: DefaultFilter | StaticFilter | KinematicFilter | DebrisFilter | SensorTrigger + }; +#endif + + enum CollisionType { + CollisionType_Nothing = 0, // rayTest(const btVector3 &from, + const btVector3 &to, bool raycastingObjectOnly = true, + bool ignoreHeightMap = false, Ogre::Vector3* normal = NULL); + + private: + + PhysicsEngine(const PhysicsEngine&); + PhysicsEngine& operator=(const PhysicsEngine&); + + // Create a debug rendering. It is called by setDebgRenderingMode if it's + // not created yet. + // Important Note: this will crash if the Render is not yet initialised! + void createDebugRendering(); + + // Set the debug rendering mode. 0 to turn it off. + // Important Note: this will crash if the Render is not yet initialised! + void setDebugRenderingMode(int mode); +#if 0 + void getObjectAABB(const std::string &mesh, + float scale, btVector3 &min, btVector3 &max); + + /** + * Return all objects hit by a ray. + */ + std::vector< std::pair > rayTest2(const btVector3 &from, const btVector3 &to, int filterGroup=0xff); + + std::pair sphereCast (float radius, btVector3 &from, btVector3 &to); + ///< @return (hit, relative distance) + + std::vector getCollisions(const std::string &name, int collisionGroup, int collisionMask); + + // Get the nearest object that's inside the given object, filtering out objects of the + // provided name + std::pair getFilteredContact(const std::string &filter, + const btVector3 &origin, + btCollisionObject *object); +#endif + }; +} +#endif // CSV_WORLD_PHYSICSENGINE_H