From 54c1f19c183350c2b55965ff0f197db52c7559ae Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 10 May 2015 02:08:25 +0200 Subject: [PATCH] Readded height fields --- apps/openmw/mwphysics/physicssystem.cpp | 132 ++++++++++++++++++++---- apps/openmw/mwphysics/physicssystem.hpp | 26 ++++- apps/openmw/mwworld/scene.cpp | 10 +- 3 files changed, 137 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index f57a8fbff..8ae4fc853 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -4,6 +4,8 @@ #include +#include + #include #include @@ -187,7 +189,7 @@ namespace MWPhysics if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) { // don't allow stepping up other actors - if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Actor) + if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) return false; // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing @@ -246,7 +248,7 @@ namespace MWPhysics btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); resultCallback1.m_collisionFilterGroup = 0xff; - resultCallback1.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap; + resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap; engine->mDynamicsWorld->rayTest(from, to, resultCallback1); if (resultCallback1.hasHit() && @@ -437,14 +439,14 @@ namespace MWPhysics Ogre::Vector3(0,0,sStepSizeDown+2.f) : Ogre::Vector3(0,0,2.f)); tracer.doTrace(colobj, from, to, engine); if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope - && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != OEngine::Physic::CollisionType_Actor) + && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor) { const btCollisionObject* standingOn = tracer.mHitObject; if (const OEngine::Physic::RigidBody* body = dynamic_cast(standingOn)) { standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName; } - if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Water) + if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water) physicActor->setWalkingOnWater(true); if (!isFlying) @@ -457,7 +459,7 @@ namespace MWPhysics // standing on actors is not allowed (see above). // in addition to that, apply a sliding effect away from the center of the actor, // so that we do not stay suspended in air indefinitely. - if (tracer.mFraction < 1.0f && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Actor) + if (tracer.mFraction < 1.0f && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) { if (Ogre::Vector3(velocity.x, velocity.y, 0).squaredLength() < 100.f*100.f) { @@ -492,15 +494,94 @@ namespace MWPhysics }; - PhysicsSystem::PhysicsSystem(osg::ref_ptr parentNode) : - mTimeAccum(0.0f), mWaterEnabled(false), mWaterHeight(0), mDebugDrawEnabled(false), mParentNode(parentNode) + // --------------------------------------------------------------- + + class HeightField { + public: + HeightField(float* heights, int x, int y, float triSize, float sqrtVerts) + { + // find the minimum and maximum heights (needed for bullet) + float minh = heights[0]; + float maxh = heights[0]; + for(int i = 1;i < sqrtVerts*sqrtVerts;++i) + { + float h = heights[i]; + if(h > maxh) maxh = h; + if(h < minh) minh = h; + } + + mShape = new btHeightfieldTerrainShape( + sqrtVerts, sqrtVerts, heights, 1, + minh, maxh, 2, + PHY_FLOAT, true + ); + mShape->setUseDiamondSubdivision(true); + mShape->setLocalScaling(btVector3(triSize, triSize, 1)); + + btTransform transform(btQuaternion::getIdentity(), + btVector3((x+0.5f) * triSize * (sqrtVerts-1), + (y+0.5f) * triSize * (sqrtVerts-1), + (maxh+minh)*0.5f)); + + mCollisionObject = new btCollisionObject; + mCollisionObject->setCollisionShape(mShape); + mCollisionObject->setWorldTransform(transform); + } + ~HeightField() + { + delete mCollisionObject; + delete mShape; + } + btCollisionObject* getCollisionObject() + { + return mCollisionObject; + } + + private: + btHeightfieldTerrainShape* mShape; + btCollisionObject* mCollisionObject; + }; + + // --------------------------------------------------------------- + + + PhysicsSystem::PhysicsSystem(osg::ref_ptr parentNode) + : mTimeAccum(0.0f) + , mWaterEnabled(false) + , mWaterHeight(0) + , mDebugDrawEnabled(false) + , mParentNode(parentNode) + { + mCollisionConfiguration = new btDefaultCollisionConfiguration(); + mDispatcher = new btCollisionDispatcher(mCollisionConfiguration); + mSolver = new btSequentialImpulseConstraintSolver; + mBroadphase = new btDbvtBroadphase(); + 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)); } PhysicsSystem::~PhysicsSystem() { - //if (mWaterCollisionObject.get()) - // mEngine->mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get()); + if (mWaterCollisionObject.get()) + mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get()); + + for (HeightFieldMap::iterator it = mHeightFields.begin(); it != mHeightFields.end(); ++it) + { + mDynamicsWorld->removeCollisionObject(it->second->getCollisionObject()); + delete it->second; + } + + delete mDynamicsWorld; + delete mSolver; + delete mCollisionConfiguration; + delete mDispatcher; + delete mBroadphase; } bool PhysicsSystem::toggleDebugRendering() @@ -509,9 +590,9 @@ namespace MWPhysics if (mDebugDrawEnabled && !mDebugDrawer.get()) { - //mDebugDrawer.reset(new MWRender::DebugDrawer(mParentNode, mEngine->mDynamicsWorld)); - //mEngine->mDynamicsWorld->setDebugDrawer(mDebugDrawer.get()); - //mDebugDrawer->setDebugMode(mDebugDrawEnabled); + mDebugDrawer.reset(new MWRender::DebugDrawer(mParentNode, mDynamicsWorld)); + mDynamicsWorld->setDebugDrawer(mDebugDrawer.get()); + mDebugDrawer->setDebugMode(mDebugDrawEnabled); } else if (mDebugDrawer.get()) mDebugDrawer->setDebugMode(mDebugDrawEnabled); @@ -592,14 +673,24 @@ namespace MWPhysics return Ogre::Vector3();//MovementSolver::traceDown(ptr, mEngine, maxHeight); } - void PhysicsSystem::addHeightField (float* heights, - int x, int y, float yoffset, - float triSize, float sqrtVerts) + void PhysicsSystem::addHeightField (float* heights, int x, int y, float triSize, float sqrtVerts) { + HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts); + mHeightFields[std::make_pair(x,y)] = heightfield; + + mDynamicsWorld->addCollisionObject(heightfield->getCollisionObject(), CollisionType_HeightMap, + CollisionType_Actor|CollisionType_Projectile); } void PhysicsSystem::removeHeightField (int x, int y) { + HeightFieldMap::iterator heightfield = mHeightFields.find(std::make_pair(x,y)); + if(heightfield != mHeightFields.end()) + { + mDynamicsWorld->removeCollisionObject(heightfield->second->getCollisionObject()); + delete heightfield->second; + mHeightFields.erase(heightfield); + } } void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, bool placeable) @@ -726,9 +817,10 @@ namespace MWPhysics void PhysicsSystem::stepSimulation(float dt) { - //animateCollisionShapes(mEngine->mAnimatedShapes, mEngine->mDynamicsWorld); + //animateCollisionShapes(mEngine->mAnimatedShapes, mDynamicsWorld); - //mEngine->stepSimulation(dt); + // We have nothing to simulate, but character controllers aren't working without this call. Might be related to updating AABBs. + mDynamicsWorld->stepSimulation(static_cast(dt), 1, 1 / 60.0f); if (mDebugDrawer.get()) mDebugDrawer->step(); @@ -818,7 +910,7 @@ namespace MWPhysics { if (mWaterCollisionObject.get()) { - //mEngine->mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get()); + mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get()); } if (!mWaterEnabled) @@ -827,7 +919,7 @@ namespace MWPhysics mWaterCollisionObject.reset(new btCollisionObject()); mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight)); mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get()); - //mEngine->mDynamicsWorld->addCollisionObject(mWaterCollisionObject.get(), OEngine::Physic::CollisionType_Water, - // OEngine::Physic::CollisionType_Actor); + mDynamicsWorld->addCollisionObject(mWaterCollisionObject.get(), CollisionType_Water, + CollisionType_Actor); } } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index e04ac1b51..4dbecdb7c 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -21,10 +21,23 @@ namespace MWRender class DebugDrawer; } +class btSequentialImpulseConstraintSolver; +class btDiscreteDynamicsWorld; + namespace MWPhysics { typedef std::vector > PtrVelocityList; + enum CollisionType { + CollisionType_World = 1<<0, + CollisionType_Actor = 1<<1, + CollisionType_HeightMap = 1<<2, + CollisionType_Projectile = 1<<4, + CollisionType_Water = 1<<5 + }; + + class HeightField; + class PhysicsSystem { public: @@ -39,9 +52,7 @@ namespace MWPhysics void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); - void addHeightField (float* heights, - int x, int y, float yoffset, - float triSize, float sqrtVerts); + void addHeightField (float* heights, int x, int y, float triSize, float sqrtVerts); void removeHeightField (int x, int y); @@ -94,6 +105,15 @@ namespace MWPhysics void updateWater(); + btBroadphaseInterface* mBroadphase; + btDefaultCollisionConfiguration* mCollisionConfiguration; + btSequentialImpulseConstraintSolver* mSolver; + btCollisionDispatcher* mDispatcher; + btDiscreteDynamicsWorld* mDynamicsWorld; + + typedef std::map, HeightField*> HeightFieldMap; + HeightFieldMap mHeightFields; + bool mDebugDrawEnabled; std::map handleToMesh; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0e17dad74..6a994f0cd 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -246,14 +246,8 @@ namespace MWWorld const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; if (!land->isDataLoaded(flags)) land->loadData(flags); - mPhysics->addHeightField ( - land->mLandData->mHeights, - cell->getCell()->getGridX(), - cell->getCell()->getGridY(), - 0, - worldsize / (verts-1), - verts) - ; + mPhysics->addHeightField (land->mLandData->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(), + worldsize / (verts-1), verts); } }