diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5a74fa48f..1b71cf0ed 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -151,7 +151,7 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -set(BOOST_COMPONENTS system filesystem program_options thread wave) +set(BOOST_COMPONENTS system filesystem program_options thread) if(WIN32) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) endif(WIN32) @@ -202,7 +202,6 @@ target_link_libraries(openmw-cs ${OGRE_LIBRARIES} ${OPENSCENEGRAPH_LIBRARIES} ${Boost_LIBRARIES} - ${BULLET_LIBRARIES} ${QT_LIBRARIES} components ) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index bcfc24053..95875de10 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwworld ) add_openmw_dir (mwphysics - physicssystem + physicssystem trace collisiontype actor convert ) add_openmw_dir (mwclass @@ -101,9 +101,9 @@ add_openmw_dir (mwbase # Main executable if (ANDROID) - set(BOOST_COMPONENTS system filesystem program_options thread wave atomic) + set(BOOST_COMPONENTS system filesystem program_options thread atomic) else () - set(BOOST_COMPONENTS system filesystem program_options thread wave) + set(BOOST_COMPONENTS system filesystem program_options thread) endif () if(WIN32) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 4ccbfc784..7cc0a6e5e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -302,7 +302,7 @@ namespace MWBase virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0; ///< Convert position to cell numbers - virtual void queueMovement(const MWWorld::Ptr &ptr, const Ogre::Vector3 &velocity) = 0; + virtual void queueMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity) = 0; ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. @@ -555,7 +555,7 @@ namespace MWBase virtual bool isInStorm() const = 0; /// @see MWWorld::WeatherManager::getStormDirection - virtual Ogre::Vector3 getStormDirection() const = 0; + virtual osg::Vec3f getStormDirection() const = 0; /// Resets all actors in the current active cells to their original location within that cell. virtual void resetActors() = 0; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 55209764a..6f11a36c7 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -35,8 +35,7 @@ namespace MWClass void Apparatus::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index ec3658b3d..04c98e437 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -40,8 +40,7 @@ namespace MWClass void Armor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Armor::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index e71dd6769..2c20435b2 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -37,8 +37,7 @@ namespace MWClass void Book::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Book::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 39025227c..8964b65e0 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -37,8 +37,7 @@ namespace MWClass void Clothing::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Clothing::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 9b35b8530..fb409cb55 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -41,8 +41,7 @@ namespace MWClass void Ingredient::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 7ea5ae1be..4d0c0cba9 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -47,8 +47,9 @@ namespace MWClass ptr.get(); assert (ref->mBase != NULL); - if(!model.empty()) - physics.addObject(ptr, model, (ref->mBase->mData.mFlags & ESM::Light::Carry) != 0); + // TODO: add option somewhere to enable collision for placeable objects + if ((ref->mBase->mData.mFlags & ESM::Light::Carry) == 0) + physics.addObject(ptr, model); if (!ref->mBase->mSound.empty() && !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)) MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index e1e774755..8f22c3fa1 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -36,8 +36,7 @@ namespace MWClass void Lockpick::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index df28daee9..b7c39b50a 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -53,8 +53,7 @@ namespace MWClass void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 98bb703cb..647f83f67 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -39,8 +39,7 @@ namespace MWClass void Potion::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Potion::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index fb96bff5a..cb43ccce6 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -36,8 +36,7 @@ namespace MWClass void Probe::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Probe::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 9ddc36ff9..0bc64a99e 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -35,8 +35,7 @@ namespace MWClass void Repair::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Repair::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index bbe81f370..8c3d7fb10 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -39,8 +39,7 @@ namespace MWClass void Weapon::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { - if(!model.empty()) - physics.addObject(ptr, model, true); + // TODO: add option somewhere to enable collision for placeable objects } std::string Weapon::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index adcfb57a4..5769138fa 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1346,7 +1346,7 @@ void CharacterController::update(float duration) { MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Class &cls = mPtr.getClass(); - Ogre::Vector3 movement(0.0f); + osg::Vec3f movement(0.f, 0.f, 0.f); updateMagicEffects(); @@ -1403,23 +1403,23 @@ void CharacterController::update(float duration) } } - Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition); - vec.normalise(); + osg::Vec3f vec(cls.getMovementSettings(mPtr).asVec3()); + vec.normalize(); if(mHitState != CharState_None && mJumpState == JumpState_None) - vec = Ogre::Vector3(0.0f); + vec = osg::Vec3f(0.f, 0.f, 0.f); Ogre::Vector3 rot = cls.getRotationVector(mPtr); mMovementSpeed = cls.getSpeed(mPtr); - vec.x *= mMovementSpeed; - vec.y *= mMovementSpeed; + vec.x() *= mMovementSpeed; + vec.y() *= mMovementSpeed; CharacterState movestate = CharState_None; CharacterState idlestate = CharState_SpecialIdle; bool forcestateupdate = false; - mHasMovedInXY = std::abs(vec[0])+std::abs(vec[1]) > 0.0f; + mHasMovedInXY = std::abs(vec.x())+std::abs(vec.y()) > 0.0f; isrunning = isrunning && mHasMovedInXY; @@ -1482,7 +1482,7 @@ void CharacterController::update(float duration) cls.getCreatureStats(mPtr).setFatigue(fatigue); if(sneak || inwater || flying) - vec.z = 0.0f; + vec.z() = 0.0f; if (inwater || flying) cls.getCreatureStats(mPtr).land(); @@ -1505,22 +1505,23 @@ void CharacterController::update(float duration) static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat(); float factor = fJumpMoveBase + fJumpMoveMult * mPtr.getClass().getSkill(mPtr, ESM::Skill::Acrobatics)/100.f; factor = std::min(1.f, factor); - vec.x *= factor; - vec.y *= factor; - vec.z = 0.0f; + vec.x() *= factor; + vec.y() *= factor; + vec.z() = 0.0f; } - else if(vec.z > 0.0f && mJumpState == JumpState_None) + else if(vec.z() > 0.0f && mJumpState == JumpState_None) { // Started a jump. float z = cls.getJump(mPtr); if (z > 0) { - if(vec.x == 0 && vec.y == 0) - vec = Ogre::Vector3(0.0f, 0.0f, z); + if(vec.x() == 0 && vec.y() == 0) + vec = osg::Vec3f(0.0f, 0.0f, z); else { - Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy(); - vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f; + osg::Vec3f lat (vec.x(), vec.y(), 0.0f); + lat.normalize(); + vec = osg::Vec3f(lat.x(), lat.y(), 1.0f) * z * 0.707f; } // advance acrobatics @@ -1544,7 +1545,7 @@ void CharacterController::update(float duration) { forcestateupdate = true; mJumpState = JumpState_Landing; - vec.z = 0.0f; + vec.z() = 0.0f; float height = cls.getCreatureStats(mPtr).land(); float healthLost = getFallDamage(mPtr, height); @@ -1575,28 +1576,28 @@ void CharacterController::update(float duration) else { mJumpState = JumpState_None; - vec.z = 0.0f; + vec.z() = 0.0f; inJump = false; - if(std::abs(vec.x/2.0f) > std::abs(vec.y)) + if(std::abs(vec.x()/2.0f) > std::abs(vec.y())) { - if(vec.x > 0.0f) + if(vec.x() > 0.0f) movestate = (inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight))); - else if(vec.x < 0.0f) + else if(vec.x() < 0.0f) movestate = (inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft))); } - else if(vec.y != 0.0f) + else if(vec.y() != 0.0f) { - if(vec.y > 0.0f) + if(vec.y() > 0.0f) movestate = (inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward))); - else if(vec.y < 0.0f) + else if(vec.y() < 0.0f) movestate = (inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); @@ -1676,7 +1677,7 @@ void CharacterController::update(float duration) } else // We must always queue movement, even if there is none, to apply gravity. - world->queueMovement(mPtr, Ogre::Vector3(0.0f)); + world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); movement = vec; cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0; @@ -1688,7 +1689,7 @@ void CharacterController::update(float duration) } else if(cls.getCreatureStats(mPtr).isDead()) { - world->queueMovement(mPtr, Ogre::Vector3(0.0f)); + world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); } osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim ? 0.f : duration); @@ -1702,15 +1703,15 @@ void CharacterController::update(float duration) { float l = moved.length(); - if((movement.x < 0.0f && movement.x < moved.x()*2.0f) || - (movement.x > 0.0f && movement.x > moved.x()*2.0f)) - moved.x() = movement.x; - if((movement.y < 0.0f && movement.y < moved.y()*2.0f) || - (movement.y > 0.0f && movement.y > moved.y()*2.0f)) - moved.y() = movement.y; - if((movement.z < 0.0f && movement.z < moved.z()*2.0f) || - (movement.z > 0.0f && movement.z > moved.z()*2.0f)) - moved.z() = movement.z; + if((movement.x() < 0.0f && movement.x() < moved.x()*2.0f) || + (movement.x() > 0.0f && movement.x() > moved.x()*2.0f)) + moved.x() = movement.x(); + if((movement.y() < 0.0f && movement.y() < moved.y()*2.0f) || + (movement.y() > 0.0f && movement.y() > moved.y()*2.0f)) + moved.y() = movement.y(); + if((movement.z() < 0.0f && movement.z() < moved.z()*2.0f) || + (movement.z() > 0.0f && movement.z() > moved.z()*2.0f)) + moved.z() = movement.z(); // but keep the original speed float newLength = moved.length(); if (newLength > 0) @@ -1722,7 +1723,7 @@ void CharacterController::update(float duration) // Update movement if(mMovementAnimationControlled && mPtr.getClass().isActor()) - world->queueMovement(mPtr, Ogre::Vector3(moved.x(), moved.y(), moved.z())); + world->queueMovement(mPtr, moved); mSkipAnim = false; diff --git a/apps/openmw/mwmechanics/movement.hpp b/apps/openmw/mwmechanics/movement.hpp index 6c9a4b758..c12b61538 100644 --- a/apps/openmw/mwmechanics/movement.hpp +++ b/apps/openmw/mwmechanics/movement.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWMECHANICS_MOVEMENT_H #define GAME_MWMECHANICS_MOVEMENT_H +#include + namespace MWMechanics { /// Desired movement for an actor @@ -14,6 +16,11 @@ namespace MWMechanics mPosition[0] = mPosition[1] = mPosition[2] = 0.0f; mRotation[0] = mRotation[1] = mRotation[2] = 0.0f; } + + osg::Vec3f asVec3() + { + return osg::Vec3f(mPosition[0], mPosition[1], mPosition[2]); + } }; } diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp new file mode 100644 index 000000000..edad7e196 --- /dev/null +++ b/apps/openmw/mwphysics/actor.cpp @@ -0,0 +1,152 @@ +#include "actor.hpp" +#include + +#include + +#include + +#include "../mwworld/class.hpp" + +#include + +#include "convert.hpp" +#include "collisiontype.hpp" + +namespace MWPhysics +{ + + +Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btDynamicsWorld* world) + : mCanWaterWalk(false), mWalkingOnWater(false) + , mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false) + , mInternalCollisionMode(true) + , mExternalCollisionMode(true) + , mDynamicsWorld(world) +{ + mPtr = ptr; + + mHalfExtents = shape->mCollisionBoxHalfExtents; + mMeshTranslation = shape->mCollisionBoxTranslate; + + // Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it) + if (std::abs(mHalfExtents.x()-mHalfExtents.y())= mHalfExtents.x()) + { + // Could also be btCapsuleShapeZ, but the movement solver seems to have issues with it (jumping on slopes doesn't work) + mShape.reset(new btCylinderShapeZ(toBullet(mHalfExtents))); + } + else + mShape.reset(new btBoxShape(toBullet(mHalfExtents))); + + mCollisionObject.reset(new btCollisionObject); + mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); + mCollisionObject->setActivationState(DISABLE_DEACTIVATION); + mCollisionObject->setCollisionShape(mShape.get()); + mCollisionObject->setUserPointer(static_cast(this)); + + updateRotation(); + updateScale(); + // already called by updateScale() + //updatePosition(); + + updateCollisionMask(); +} + +Actor::~Actor() +{ + if (mCollisionObject.get()) + mDynamicsWorld->removeCollisionObject(mCollisionObject.get()); +} + +void Actor::enableCollisionMode(bool collision) +{ + mInternalCollisionMode = collision; +} + +void Actor::enableCollisionBody(bool collision) +{ + if (mExternalCollisionMode != collision) + { + mExternalCollisionMode = collision; + updateCollisionMask(); + } +} + +void Actor::updateCollisionMask() +{ + mDynamicsWorld->removeCollisionObject(mCollisionObject.get()); + int collisionMask = CollisionType_World | CollisionType_HeightMap; + if (mExternalCollisionMode) + collisionMask |= CollisionType_Actor | CollisionType_Projectile; + if (mCanWaterWalk) + collisionMask |= CollisionType_Water; + mDynamicsWorld->addCollisionObject(mCollisionObject.get(), CollisionType_Actor, collisionMask); +} + +void Actor::updatePosition() +{ + osg::Vec3f position = mPtr.getRefData().getPosition().asVec3(); + + btTransform tr = mCollisionObject->getWorldTransform(); + osg::Vec3f scaledTranslation = osg::componentMultiply(mMeshTranslation, mScale); + osg::Vec3f newPosition = scaledTranslation + position; + + tr.setOrigin(toBullet(newPosition)); + mCollisionObject->setWorldTransform(tr); +} + +void Actor::updateRotation () +{ + btTransform tr = mCollisionObject->getWorldTransform(); + tr.setRotation(toBullet(mPtr.getRefData().getBaseNode()->getAttitude())); + mCollisionObject->setWorldTransform(tr); +} + +void Actor::updateScale() +{ + float scale = mPtr.getCellRef().getScale(); + osg::Vec3f scaleVec(scale,scale,scale); + + if (!mPtr.getClass().isNpc()) + mPtr.getClass().adjustScale(mPtr, scaleVec); + + mScale = scaleVec; + mShape->setLocalScaling(toBullet(mScale)); + + updatePosition(); +} + +osg::Vec3f Actor::getHalfExtents() const +{ + return osg::componentMultiply(mHalfExtents, mScale); +} + +void Actor::setInertialForce(const osg::Vec3f &force) +{ + mForce = force; +} + +void Actor::setOnGround(bool grounded) +{ + mOnGround = grounded; +} + +bool Actor::isWalkingOnWater() const +{ + return mWalkingOnWater; +} + +void Actor::setWalkingOnWater(bool walkingOnWater) +{ + mWalkingOnWater = walkingOnWater; +} + +void Actor::setCanWaterWalk(bool waterWalk) +{ + if (waterWalk != mCanWaterWalk) + { + mCanWaterWalk = waterWalk; + updateCollisionMask(); + } +} + +} diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp new file mode 100644 index 000000000..ec1d81fd9 --- /dev/null +++ b/apps/openmw/mwphysics/actor.hpp @@ -0,0 +1,125 @@ +#ifndef OPENMW_MWPHYSICS_ACTOR_H +#define OPENMW_MWPHYSICS_ACTOR_H + +#include + +#include "../mwworld/ptr.hpp" + +#include +#include + +class btDynamicsWorld; +class btCollisionShape; +class btCollisionObject; + +namespace NifBullet +{ + class BulletShapeInstance; +} + +namespace MWPhysics +{ + + class PtrHolder + { + public: + virtual ~PtrHolder() {} + + protected: + MWWorld::Ptr mPtr; + }; + + class Actor : public PtrHolder + { + public: + Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btDynamicsWorld* world); + ~Actor(); + + /** + * Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry. + */ + void enableCollisionMode(bool collision); + + bool getCollisionMode() const + { + return mInternalCollisionMode; + } + + /** + * Enables or disables the *external* collision body. If disabled, other actors will not collide with this actor. + */ + void enableCollisionBody(bool collision); + + void updateScale(); + void updateRotation(); + void updatePosition(); + + /** + * Returns the (scaled) half extents + */ + osg::Vec3f getHalfExtents() const; + + /** + * Sets the current amount of inertial force (incl. gravity) affecting this physic actor + */ + void setInertialForce(const osg::Vec3f &force); + + /** + * Gets the current amount of inertial force (incl. gravity) affecting this physic actor + */ + const osg::Vec3f &getInertialForce() const + { + return mForce; + } + + void setOnGround(bool grounded); + + bool getOnGround() const + { + return mInternalCollisionMode && mOnGround; + } + + btCollisionObject* getCollisionObject() const + { + return mCollisionObject.get(); + } + + /// Sets whether this actor should be able to collide with the water surface + void setCanWaterWalk(bool waterWalk); + + /// Sets whether this actor has been walking on the water surface in the last frame + void setWalkingOnWater(bool walkingOnWater); + bool isWalkingOnWater() const; + + private: + /// Removes then re-adds the collision object to the dynamics world + void updateCollisionMask(); + + bool mCanWaterWalk; + bool mWalkingOnWater; + + std::auto_ptr mShape; + + std::auto_ptr mCollisionObject; + + osg::Vec3f mMeshTranslation; + osg::Vec3f mHalfExtents; + + osg::Vec3f mScale; + osg::Vec3f mPosition; + + osg::Vec3f mForce; + bool mOnGround; + bool mInternalCollisionMode; + bool mExternalCollisionMode; + + btDynamicsWorld* mDynamicsWorld; + + Actor(const Actor&); + Actor& operator=(const Actor&); + }; + +} + + +#endif diff --git a/apps/openmw/mwphysics/collisiontype.hpp b/apps/openmw/mwphysics/collisiontype.hpp new file mode 100644 index 000000000..0f083ab35 --- /dev/null +++ b/apps/openmw/mwphysics/collisiontype.hpp @@ -0,0 +1,17 @@ +#ifndef OPENMW_MWPHYSICS_COLLISIONTYPE_H +#define OPENMW_MWPHYSICS_COLLISIONTYPE_H + +namespace MWPhysics +{ + +enum CollisionType { + CollisionType_World = 1<<0, + CollisionType_Actor = 1<<1, + CollisionType_HeightMap = 1<<2, + CollisionType_Projectile = 1<<4, + CollisionType_Water = 1<<5 +}; + +} + +#endif diff --git a/apps/openmw/mwphysics/convert.hpp b/apps/openmw/mwphysics/convert.hpp new file mode 100644 index 000000000..c5075a2c3 --- /dev/null +++ b/apps/openmw/mwphysics/convert.hpp @@ -0,0 +1,35 @@ +#ifndef OPENMW_MWPHYSICS_CONVERT_H +#define OPENMW_MWPHYSICS_CONVERT_H + +#include +#include + +#include +#include + +namespace MWPhysics +{ + + inline btVector3 toBullet(const osg::Vec3f& vec) + { + return btVector3(vec.x(), vec.y(), vec.z()); + } + + inline btQuaternion toBullet(const osg::Quat& quat) + { + return btQuaternion(quat.x(), quat.y(), quat.z(), quat.w()); + } + + inline osg::Vec3f toOsg(const btVector3& vec) + { + return osg::Vec3f(vec.x(), vec.y(), vec.z()); + } + + inline osg::Quat toOsg(const btQuaternion& quat) + { + return osg::Quat(quat.x(), quat.y(), quat.z(), quat.w()); + } + +} + +#endif diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 8ae4fc853..6d429f3e5 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -3,15 +3,17 @@ #include #include +#include #include +#include #include #include #include -#include "../mwbase/world.hpp" // FIXME +#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -28,6 +30,11 @@ #include "../mwworld/class.hpp" +#include "collisiontype.hpp" +#include "actor.hpp" +#include "convert.hpp" +#include "trace.h" + namespace { @@ -89,16 +96,17 @@ namespace MWPhysics // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. static const int sMaxIterations = 8; + // FIXME: move to a separate file class MovementSolver { private: - static float getSlope(const Ogre::Vector3 &normal) + static float getSlope(const osg::Vec3f &normal) { - return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees(); + return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f))); } - static bool stepMove(btCollisionObject *colobj, Ogre::Vector3 &position, - const Ogre::Vector3 &toMove, float &remainingTime /*, OEngine::Physic::PhysicEngine *engine*/) + static bool stepMove(btCollisionObject *colobj, osg::Vec3f &position, + const osg::Vec3f &toMove, float &remainingTime, btDynamicsWorld* dynamicsWorld) { /* * Slide up an incline or set of stairs. Should be called only after a @@ -144,11 +152,9 @@ namespace MWPhysics * +--+ +-------- * ============================================== */ - return false; -#if 0 - OEngine::Physic::ActorTracer tracer, stepper; + ActorTracer tracer, stepper; - stepper.doTrace(colobj, position, position+Ogre::Vector3(0.0f,0.0f,sStepSizeUp), engine); + stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), dynamicsWorld); if(stepper.mFraction < std::numeric_limits::epsilon()) return false; // didn't even move the smallest representable amount // (TODO: shouldn't this be larger? Why bother with such a small amount?) @@ -166,7 +172,7 @@ namespace MWPhysics * +--+ * ============================================== */ - tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, engine); + tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, dynamicsWorld); if(tracer.mFraction < std::numeric_limits::epsilon()) return false; // didn't even move the smallest representable amount @@ -185,7 +191,7 @@ namespace MWPhysics * +--+ +--+ * ============================================== */ - stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-Ogre::Vector3(0.0f,0.0f,sStepSizeDown), engine); + stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), dynamicsWorld); if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) { // don't allow stepping up other actors @@ -201,41 +207,39 @@ namespace MWPhysics // moved between 0 and just under sStepSize distance but slope was too great, // or moved full sStepSize distance (FIXME: is this a bug?) -#endif return false; } ///Project a vector u on another vector v - static inline Ogre::Vector3 project(const Ogre::Vector3 u, const Ogre::Vector3 &v) + static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v) { - return v * u.dotProduct(v); + return v * (u * v); + // ^ dot product } ///Helper for computing the character sliding - static inline Ogre::Vector3 slide(Ogre::Vector3 direction, const Ogre::Vector3 &planeNormal) + static inline osg::Vec3f slide(const osg::Vec3f& direction, const osg::Vec3f &planeNormal) { return direction - project(direction, planeNormal); } + static inline osg::Vec3f reflect(const osg::Vec3& velocity, const osg::Vec3f& normal) + { + return (normal * (normal * velocity)) * 2 - velocity; + } + public: - static Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, /*OEngine::Physic::PhysicEngine *engine,*/ float maxHeight) + static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, Actor* actor, btDynamicsWorld* dynamicsWorld, float maxHeight) { - const ESM::Position &refpos = ptr.getRefData().getPosition(); - Ogre::Vector3 position(refpos.pos); - - return position; -#if 0 - OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); - if (!physicActor) - return position; + osg::Vec3f position(ptr.getRefData().getPosition().asVec3()); - OEngine::Physic::ActorTracer tracer; - tracer.findGround(physicActor, position, position-Ogre::Vector3(0,0,maxHeight), engine); + ActorTracer tracer; + tracer.findGround(actor, position, position-osg::Vec3f(0,0,maxHeight), dynamicsWorld); if(tracer.mFraction >= 1.0f) { - physicActor->setOnGround(false); + actor->setOnGround(false); return position; } else @@ -243,79 +247,73 @@ namespace MWPhysics // Check if we actually found a valid spawn point (use an infinitely thin ray this time). // Required for some broken door destinations in Morrowind.esm, where the spawn point // intersects with other geometry if the actor's base is taken into account - btVector3 from = BtOgre::Convert::toBullet(position); + btVector3 from = toBullet(position); btVector3 to = from - btVector3(0,0,maxHeight); btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); resultCallback1.m_collisionFilterGroup = 0xff; resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap; - engine->mDynamicsWorld->rayTest(from, to, resultCallback1); + dynamicsWorld->rayTest(from, to, resultCallback1); if (resultCallback1.hasHit() && - (BtOgre::Convert::toOgre(resultCallback1.m_hitPointWorld).distance(tracer.mEndPos) > 30 + ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length() > 30 || getSlope(tracer.mPlaneNormal) > sMaxSlope)) { - physicActor->setOnGround(getSlope(BtOgre::Convert::toOgre(resultCallback1.m_hitNormalWorld)) <= sMaxSlope); - return BtOgre::Convert::toOgre(resultCallback1.m_hitPointWorld) + Ogre::Vector3(0,0,1.f); + actor->setOnGround(getSlope(toOsg(resultCallback1.m_hitNormalWorld)) <= sMaxSlope); + return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f); } - physicActor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope); + actor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope); return tracer.mEndPos; } -#endif } - static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, - bool isFlying, float waterlevel, float slowFall /*, OEngine::Physic::PhysicEngine *engine*/ + static osg::Vec3f move(const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time, + bool isFlying, float waterlevel, float slowFall, btDynamicsWorld* dynamicsWorld , std::map& collisionTracker , std::map& standingCollisionTracker) { - const ESM::Position &refpos = ptr.getRefData().getPosition(); - Ogre::Vector3 position(refpos.pos); - return position; -#if 0 + const ESM::Position& refpos = ptr.getRefData().getPosition(); + osg::Vec3f position(refpos.asVec3()); + // Early-out for totally static creatures // (Not sure if gravity should still apply?) if (!ptr.getClass().isMobile(ptr)) return position; - OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); - if (!physicActor) - return position; - // Reset per-frame data physicActor->setWalkingOnWater(false); // Anything to collide with? if(!physicActor->getCollisionMode()) { - return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * - Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) - * movement * time; + return position + (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * + osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1)) + ) * movement * time; } - btCollisionObject *colobj = physicActor->getCollisionBody(); - Ogre::Vector3 halfExtents = physicActor->getHalfExtents(); - position.z += halfExtents.z; + btCollisionObject *colobj = physicActor->getCollisionObject(); + osg::Vec3f halfExtents = physicActor->getHalfExtents(); + position.z() += halfExtents.z(); static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get() .find("fSwimHeightScale")->getFloat(); - float swimlevel = waterlevel + halfExtents.z - (halfExtents.z * 2 * fSwimHeightScale); + float swimlevel = waterlevel + halfExtents.z() - (halfExtents.z() * 2 * fSwimHeightScale); - OEngine::Physic::ActorTracer tracer; - Ogre::Vector3 inertia = physicActor->getInertialForce(); - Ogre::Vector3 velocity; + ActorTracer tracer; + osg::Vec3f inertia = physicActor->getInertialForce(); + osg::Vec3f velocity; - if(position.z < swimlevel || isFlying) + if(position.z() < swimlevel || isFlying) { - velocity = (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)* - Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement; + velocity = (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * + osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; } else { - velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement; + velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; - if (velocity.z > 0.f) + if (velocity.z() > 0.f) inertia = velocity; if(!physicActor->getOnGround()) { @@ -327,16 +325,16 @@ namespace MWPhysics // Now that we have the effective movement vector, apply wind forces to it if (MWBase::Environment::get().getWorld()->isInStorm()) { - Ogre::Vector3 stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); - Ogre::Degree angle = stormDirection.angleBetween(velocity); + osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); + float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity)); static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fStromWalkMult")->getFloat(); - velocity *= 1.f-(fStromWalkMult * (angle.valueDegrees()/180.f)); + velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f)); } - Ogre::Vector3 origVelocity = velocity; + osg::Vec3f origVelocity = velocity; - Ogre::Vector3 newPosition = position; + osg::Vec3f newPosition = position; /* * A loop to find newPosition using tracer, if successful different from the starting position. * nextpos is the local variable used to find potential newPosition, using velocity and remainingTime @@ -345,27 +343,27 @@ namespace MWPhysics float remainingTime = time; for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) { - Ogre::Vector3 nextpos = newPosition + velocity * remainingTime; + osg::Vec3f nextpos = newPosition + velocity * remainingTime; // If not able to fly, don't allow to swim up into the air - if(newPosition.z < swimlevel && + if(newPosition.z() < swimlevel && !isFlying && // can't fly - nextpos.z > swimlevel && // but about to go above water - newPosition.z <= swimlevel) + nextpos.z() > swimlevel && // but about to go above water + newPosition.z() <= swimlevel) { - const Ogre::Vector3 down(0,0,-1); - Ogre::Real movelen = velocity.normalise(); - Ogre::Vector3 reflectdir = velocity.reflect(down); - reflectdir.normalise(); + const osg::Vec3f down(0,0,-1); + float movelen = velocity.normalize(); + osg::Vec3f reflectdir = reflect(velocity, down); + reflectdir.normalize(); velocity = slide(reflectdir, down)*movelen; // NOTE: remainingTime is unchanged before the loop continues continue; // velocity updated, calculate nextpos again } - if(newPosition.squaredDistance(nextpos) > 0.0001) + if((newPosition - nextpos).length2() > 0.0001) { // trace to where character would go if there were no obstructions - tracer.doTrace(colobj, newPosition, nextpos, engine); + tracer.doTrace(colobj, newPosition, nextpos, dynamicsWorld); // check for obstructions if(tracer.mFraction >= 1.0f) @@ -375,11 +373,13 @@ namespace MWPhysics } else { + /* const btCollisionObject* standingOn = tracer.mHitObject; if (const OEngine::Physic::RigidBody* body = dynamic_cast(standingOn)) { collisionTracker[ptr.getRefData().getHandle()] = body->mName; } + */ } } else @@ -395,62 +395,68 @@ namespace MWPhysics } - Ogre::Vector3 oldPosition = newPosition; + osg::Vec3f oldPosition = newPosition; // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // NOTE: stepMove modifies newPosition if successful - bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, engine); + bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, dynamicsWorld); if (!result) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent - result = stepMove(colobj, newPosition, velocity.normalisedCopy()*10.f, remainingTime, engine); + { + osg::Vec3f normalizedVelocity = velocity; + normalizedVelocity.normalize(); + result = stepMove(colobj, newPosition, normalizedVelocity*10.f, remainingTime, dynamicsWorld); + } if(result) { // don't let pure water creatures move out of water after stepMove if (ptr.getClass().isPureWaterCreature(ptr) - && newPosition.z + halfExtents.z > waterlevel) + && newPosition.z() + halfExtents.z() > waterlevel) newPosition = oldPosition; } else { // Can't move this way, try to find another spot along the plane - Ogre::Vector3 direction = velocity; - Ogre::Real movelen = direction.normalise(); - Ogre::Vector3 reflectdir = velocity.reflect(tracer.mPlaneNormal); - reflectdir.normalise(); + osg::Vec3f direction = velocity; + float movelen = direction.normalize(); + osg::Vec3f reflectdir = reflect(velocity, tracer.mPlaneNormal); + reflectdir.normalize(); - Ogre::Vector3 newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; - if ((newVelocity-velocity).squaredLength() < 0.01) - break; - if (velocity.dotProduct(origVelocity) <= 0.f) + osg::Vec3f newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; + if ((newVelocity-velocity).length2() < 0.01) break; + if ((velocity * origVelocity) <= 0.f) + break; // ^ dot product velocity = newVelocity; // Do not allow sliding upward if there is gravity. Stepping will have taken // care of that. - if(!(newPosition.z < swimlevel || isFlying)) - velocity.z = std::min(velocity.z, 0.0f); + if(!(newPosition.z() < swimlevel || isFlying)) + velocity.z() = std::min(velocity.z(), 0.0f); } } bool isOnGround = false; - if (!(inertia.z > 0.f) && !(newPosition.z < swimlevel)) + if (!(inertia.z() > 0.f) && !(newPosition.z() < swimlevel)) { - Ogre::Vector3 from = newPosition; - Ogre::Vector3 to = newPosition - (physicActor->getOnGround() ? - Ogre::Vector3(0,0,sStepSizeDown+2.f) : Ogre::Vector3(0,0,2.f)); - tracer.doTrace(colobj, from, to, engine); + osg::Vec3f from = newPosition; + osg::Vec3f to = newPosition - (physicActor->getOnGround() ? + osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f)); + tracer.doTrace(colobj, from, to, dynamicsWorld); if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope && 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 == CollisionType_Water) physicActor->setWalkingOnWater(true); - + */ if (!isFlying) - newPosition.z = tracer.mEndPos.z + 1.0f; + newPosition.z() = tracer.mEndPos.z() + 1.0f; isOnGround = true; } @@ -461,13 +467,13 @@ namespace MWPhysics // so that we do not stay suspended in air indefinitely. 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) + if (osg::Vec3f(velocity.x(), velocity.y(), 0).length2() < 100.f*100.f) { btVector3 aabbMin, aabbMax; tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax); btVector3 center = (aabbMin + aabbMax) / 2.f; - inertia = Ogre::Vector3(position.x - center.x(), position.y - center.y(), 0); - inertia.normalise(); + inertia = osg::Vec3f(position.x() - center.x(), position.y() - center.y(), 0); + inertia.normalize(); inertia *= 100; } } @@ -476,20 +482,19 @@ namespace MWPhysics } } - if(isOnGround || newPosition.z < swimlevel || isFlying) - physicActor->setInertialForce(Ogre::Vector3(0.0f)); + if(isOnGround || newPosition.z() < swimlevel || isFlying) + physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f)); else { - inertia.z += time * -627.2f; - if (inertia.z < 0) - inertia.z *= slowFall; + inertia.z() += time * -627.2f; + if (inertia.z() < 0) + inertia.z() *= slowFall; physicActor->setInertialForce(inertia); } physicActor->setOnGround(isOnGround); - newPosition.z -= halfExtents.z; // remove what was added at the beginning + newPosition.z() -= halfExtents.z(); // remove what was added at the beginning return newPosition; -#endif } }; @@ -543,11 +548,62 @@ namespace MWPhysics btCollisionObject* mCollisionObject; }; - // --------------------------------------------------------------- + // -------------------------------------------------------------- + + class Object : public PtrHolder + { + public: + Object(const MWWorld::Ptr& ptr, osg::ref_ptr shapeInstance) + : mShapeInstance(shapeInstance) + { + mPtr = ptr; + mCollisionObject.reset(new btCollisionObject); + mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape()); + + mCollisionObject->setUserPointer(static_cast(this)); + + setScale(ptr.getCellRef().getScale()); + setRotation(toBullet(ptr.getRefData().getBaseNode()->getAttitude())); + const float* pos = ptr.getRefData().getPosition().pos; + setOrigin(btVector3(pos[0], pos[1], pos[2])); + } + + void updatePtr(const MWWorld::Ptr& updated) + { + mPtr = updated; + } - PhysicsSystem::PhysicsSystem(osg::ref_ptr parentNode) - : mTimeAccum(0.0f) + void setScale(float scale) + { + mShapeInstance->getCollisionShape()->setLocalScaling(btVector3(scale,scale,scale)); + } + + void setRotation(const btQuaternion& quat) + { + mCollisionObject->getWorldTransform().setRotation(quat); + } + + void setOrigin(const btVector3& vec) + { + mCollisionObject->getWorldTransform().setOrigin(vec); + } + + btCollisionObject* getCollisionObject() + { + return mCollisionObject.get(); + } + + private: + std::auto_ptr mCollisionObject; + osg::ref_ptr mShapeInstance; + }; + + // --------------------------------------------------------------- + + PhysicsSystem::PhysicsSystem(const VFS::Manager* vfs, osg::ref_ptr parentNode) + : mShapeManager(new NifBullet::BulletShapeManager(vfs)) + , mTimeAccum(0.0f) , mWaterEnabled(false) , mWaterHeight(0) , mDebugDrawEnabled(false) @@ -577,6 +633,17 @@ namespace MWPhysics delete it->second; } + for (ObjectMap::iterator it = mObjects.begin(); it != mObjects.end(); ++it) + { + mDynamicsWorld->removeCollisionObject(it->second->getCollisionObject()); + delete it->second; + } + + for (ActorMap::iterator it = mActors.begin(); it != mActors.end(); ++it) + { + delete it->second; + } + delete mDynamicsWorld; delete mSolver; delete mCollisionConfiguration; @@ -668,9 +735,13 @@ namespace MWPhysics return std::vector();//mEngine->getCollisions(ptr.getRefData().getBaseNodeOld()->getName(), collisionGroup, collisionMask); } - Ogre::Vector3 PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, float maxHeight) + osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, float maxHeight) { - return Ogre::Vector3();//MovementSolver::traceDown(ptr, mEngine, maxHeight); + ActorMap::iterator found = mActors.find(ptr); + if (found == mActors.end()) + return ptr.getRefData().getPosition().asVec3(); + else + return MovementSolver::traceDown(ptr, found->second, mDynamicsWorld, maxHeight); } void PhysicsSystem::addHeightField (float* heights, int x, int y, float triSize, float sqrtVerts) @@ -693,45 +764,115 @@ namespace MWPhysics } } - void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, bool placeable) + void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh) + { + osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); + if (!shapeInstance->getCollisionShape()) + return; + + Object *obj = new Object(ptr, shapeInstance); + mObjects.insert(std::make_pair(ptr, obj)); + + mDynamicsWorld->addCollisionObject(obj->getCollisionObject(), CollisionType_World, + CollisionType_Actor|CollisionType_HeightMap|CollisionType_Projectile); + } + + void PhysicsSystem::remove(const MWWorld::Ptr &ptr) + { + ObjectMap::iterator found = mObjects.find(ptr); + if (found != mObjects.end()) + { + mDynamicsWorld->removeCollisionObject(found->second->getCollisionObject()); + delete found->second; + mObjects.erase(found); + } + + ActorMap::iterator foundActor = mActors.find(ptr); + if (foundActor != mActors.end()) + { + delete foundActor->second; + mActors.erase(foundActor); + } + } + + void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr) { + ObjectMap::iterator found = mObjects.find(ptr); + float scale = ptr.getCellRef().getScale(); + if (found != mObjects.end()) + { + found->second->setScale(scale); + mDynamicsWorld->updateSingleAabb(found->second->getCollisionObject()); + return; + } + ActorMap::iterator foundActor = mActors.find(ptr); + if (foundActor != mActors.end()) + { + foundActor->second->updateScale(); + // no aabb update needed (DISABLE_DEACTIVATION) + return; + } + } + + void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr) + { + ObjectMap::iterator found = mObjects.find(ptr); + if (found != mObjects.end()) + { + found->second->setRotation(toBullet(ptr.getRefData().getBaseNode()->getAttitude())); + mDynamicsWorld->updateSingleAabb(found->second->getCollisionObject()); + return; + } + ActorMap::iterator foundActor = mActors.find(ptr); + if (foundActor != mActors.end()) + { + foundActor->second->updateRotation(); + // no aabb update needed (DISABLE_DEACTIVATION) + return; + } + } + void PhysicsSystem::updatePosition(const MWWorld::Ptr &ptr) + { + ObjectMap::iterator found = mObjects.find(ptr); + if (found != mObjects.end()) + { + found->second->setOrigin(toBullet(ptr.getRefData().getPosition().asVec3())); + mDynamicsWorld->updateSingleAabb(found->second->getCollisionObject()); + return; + } + ActorMap::iterator foundActor = mActors.find(ptr); + if (foundActor != mActors.end()) + { + foundActor->second->updatePosition(); + // no aabb update needed (DISABLE_DEACTIVATION) + return; + } } void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) { + osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); + Actor* actor = new Actor(ptr, shapeInstance, mDynamicsWorld); + mActors.insert(std::make_pair(ptr, actor)); } bool PhysicsSystem::toggleCollisionMode() { - /* - for(std::map::iterator it = mEngine->mActorMap.begin(); it != mEngine->mActorMap.end();++it) + ActorMap::iterator found = mActors.find(MWBase::Environment::get().getWorld()->getPlayerPtr()); + if (found != mActors.end()) { - if (it->first=="player") - { - OEngine::Physic::PhysicActor* act = it->second; - - bool cmode = act->getCollisionMode(); - if(cmode) - { - act->enableCollisionMode(false); - return false; - } - else - { - act->enableCollisionMode(true); - return true; - } - } + bool cmode = found->second->getCollisionMode(); + cmode = !cmode; + found->second->enableCollisionMode(cmode); + return cmode; } - throw std::logic_error ("can't find player"); - */ return false; } - void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement) + void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &movement) { PtrVelocityList::iterator iter = mMovementQueue.begin(); for(;iter != mMovementQueue.end();++iter) @@ -764,8 +905,6 @@ namespace MWPhysics mCollisions.clear(); mStandingCollisions.clear(); - /* - const MWBase::World *world = MWBase::Environment::get().getWorld(); PtrVelocityList::iterator iter = mMovementQueue.begin(); for(;iter != mMovementQueue.end();++iter) @@ -786,19 +925,20 @@ namespace MWPhysics Ogre::Vector3(iter->first.getRefData().getPosition().pos))) waterCollision = true; - OEngine::Physic::PhysicActor *physicActor = mEngine->getCharacter(iter->first.getRefData().getHandle()); - if (!physicActor) // actor was already removed from the scene + ActorMap::iterator foundActor = mActors.find(iter->first); + if (foundActor == mActors.end()) // actor was already removed from the scene continue; + Actor* physicActor = foundActor->second; physicActor->setCanWaterWalk(waterCollision); // Slow fall reduces fall speed by a factor of (effect magnitude / 200) float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f)); - Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, + osg::Vec3f newpos = MovementSolver::move(iter->first, physicActor, iter->second, mTimeAccum, world->isFlying(iter->first), - waterlevel, slowFall, mEngine, mCollisions, mStandingCollisions); + waterlevel, slowFall, mDynamicsWorld, mCollisions, mStandingCollisions); - float heightDiff = newpos.z - oldHeight; + float heightDiff = newpos.z() - oldHeight; if (heightDiff < 0) iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff); @@ -806,8 +946,6 @@ namespace MWPhysics mMovementResults.push_back(std::make_pair(iter->first, newpos)); } - */ - mTimeAccum = 0.0f; } mMovementQueue.clear(); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 4dbecdb7c..e6bfd7cd2 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MWWORLD_PHYSICSSYSTEM_H -#define GAME_MWWORLD_PHYSICSSYSTEM_H +#ifndef OPENMW_MWPHYSICS_PHYSICSSYSTEM_H +#define OPENMW_MWPHYSICS_PHYSICSSYSTEM_H #include @@ -21,34 +21,48 @@ namespace MWRender class DebugDrawer; } +namespace NifBullet +{ + class BulletShapeManager; +} + +namespace VFS +{ + class Manager; +} + 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 - }; + typedef std::vector > PtrVelocityList; class HeightField; + class Object; + class Actor; class PhysicsSystem { public: - PhysicsSystem (osg::ref_ptr parentNode); + PhysicsSystem (const VFS::Manager* vfs, osg::ref_ptr parentNode); ~PhysicsSystem (); void enableWater(float height); void setWaterHeight(float height); void disableWater(); - void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, bool placeable=false); + void addObject (const MWWorld::Ptr& ptr, const std::string& mesh); + + // Object or Actor + void remove (const MWWorld::Ptr& ptr); + + void updateScale (const MWWorld::Ptr& ptr); + void updateRotation (const MWWorld::Ptr& ptr); + void updatePosition (const MWWorld::Ptr& ptr); + + // TODO + //void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated); void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); @@ -61,7 +75,7 @@ namespace MWPhysics void stepSimulation(float dt); std::vector getCollisions(const MWWorld::Ptr &ptr, int collisionGroup, int collisionMask); ///< get handles this object collides with - Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, float maxHeight); + osg::Vec3f traceDown(const MWWorld::Ptr &ptr, float maxHeight); std::pair getHitContact(const std::string &name, const Ogre::Vector3 &origin, @@ -76,7 +90,7 @@ namespace MWPhysics /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. - void queueObjectMovement(const MWWorld::Ptr &ptr, const Ogre::Vector3 &velocity); + void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); /// Apply all queued movements, then clear the list. const PtrVelocityList& applyQueuedMovement(float dt); @@ -111,6 +125,14 @@ namespace MWPhysics btCollisionDispatcher* mDispatcher; btDiscreteDynamicsWorld* mDynamicsWorld; + std::auto_ptr mShapeManager; + + typedef std::map ObjectMap; + ObjectMap mObjects; + + typedef std::map ActorMap; + ActorMap mActors; + typedef std::map, HeightField*> HeightFieldMap; HeightFieldMap mHeightFields; @@ -121,9 +143,9 @@ namespace MWPhysics // Tracks all movement collisions happening during a single frame. // This will detect e.g. running against a vertical wall. It will not detect climbing up stairs, // stepping up small objects, etc. - std::map mCollisions; + std::map mCollisions; // FIXME: reimplement - std::map mStandingCollisions; + std::map mStandingCollisions; // FIXME: reimplement PtrVelocityList mMovementQueue; PtrVelocityList mMovementResults; diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp new file mode 100644 index 000000000..ddb984821 --- /dev/null +++ b/apps/openmw/mwphysics/trace.cpp @@ -0,0 +1,128 @@ +#include "trace.h" + +#include + +#include +#include + +#include "collisiontype.hpp" +#include "actor.hpp" +#include "convert.hpp" + +namespace MWPhysics +{ + +class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback +{ +public: + ClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot) + : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), + mMe(me), mUp(up), mMinSlopeDot(minSlopeDot) + { + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + if(convexResult.m_hitCollisionObject == mMe) + return btScalar( 1 ); + + btVector3 hitNormalWorld; + if(normalInWorldSpace) + hitNormalWorld = convexResult.m_hitNormalLocal; + else + { + ///need to transform normal into worldspace + hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; + } + + btScalar dotUp = mUp.dot(hitNormalWorld); + if(dotUp < mMinSlopeDot) + return btScalar(1); + + return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); + } + +protected: + btCollisionObject *mMe; + const btVector3 mUp; + const btScalar mMinSlopeDot; +}; + + +void ActorTracer::doTrace(btCollisionObject *actor, const osg::Vec3f& start, const osg::Vec3f& end, btDynamicsWorld* world) +{ + const btVector3 btstart = toBullet(start); + const btVector3 btend = toBullet(end); + + const btTransform &trans = actor->getWorldTransform(); + btTransform from(trans); + btTransform to(trans); + from.setOrigin(btstart); + to.setOrigin(btend); + + ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); + // Inherit the actor's collision group and mask + newTraceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup; + newTraceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask; + + btCollisionShape *shape = actor->getCollisionShape(); + assert(shape->isConvex()); + world->convexSweepTest(static_cast(shape), + from, to, newTraceCallback); + + // Copy the hit data over to our trace results struct: + if(newTraceCallback.hasHit()) + { + const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; + mFraction = newTraceCallback.m_closestHitFraction; + mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); + mEndPos = (end-start)*mFraction + start; + mHitObject = newTraceCallback.m_hitCollisionObject; + } + else + { + mEndPos = end; + mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f); + mFraction = 1.0f; + mHitObject = NULL; + } +} + +void ActorTracer::findGround(const Actor* actor, const osg::Vec3f& start, const osg::Vec3f& end, btDynamicsWorld* world) +{ + const btVector3 btstart(start.x(), start.y(), start.z()+1.0f); + const btVector3 btend(end.x(), end.y(), end.z()+1.0f); + + const btTransform &trans = actor->getCollisionObject()->getWorldTransform(); + btTransform from(trans.getBasis(), btstart); + btTransform to(trans.getBasis(), btend); + + ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionObject(), btstart-btend, btScalar(0.0)); + // Inherit the actor's collision group and mask + newTraceCallback.m_collisionFilterGroup = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterGroup; + newTraceCallback.m_collisionFilterMask = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterMask; + newTraceCallback.m_collisionFilterMask &= ~CollisionType_Actor; + + btVector3 halfExtents = toBullet(actor->getHalfExtents()); + + halfExtents[2] = 1.0f; + btCylinderShapeZ base(halfExtents); + + world->convexSweepTest(&base, from, to, newTraceCallback); + if(newTraceCallback.hasHit()) + { + const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; + mFraction = newTraceCallback.m_closestHitFraction; + mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); + mEndPos = (end-start)*mFraction + start; + mEndPos[2] += 1.0f; + } + else + { + mEndPos = end; + mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f); + mFraction = 1.0f; + } +} + +} diff --git a/apps/openmw/mwphysics/trace.h b/apps/openmw/mwphysics/trace.h new file mode 100644 index 000000000..02f9ebdd1 --- /dev/null +++ b/apps/openmw/mwphysics/trace.h @@ -0,0 +1,27 @@ +#ifndef OENGINE_BULLET_TRACE_H +#define OENGINE_BULLET_TRACE_H + +#include + +class btCollisionObject; +class btDynamicsWorld; + + +namespace MWPhysics +{ + class Actor; + + struct ActorTracer + { + osg::Vec3f mEndPos; + osg::Vec3f mPlaneNormal; + const btCollisionObject* mHitObject; + + float mFraction; + + void doTrace(btCollisionObject *actor, const osg::Vec3f& start, const osg::Vec3f& end, btDynamicsWorld* world); + void findGround(const Actor* actor, const osg::Vec3f& start, const osg::Vec3f& end, btDynamicsWorld* world); + }; +} + +#endif diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index f0cb8a967..4a7952c44 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -425,7 +425,7 @@ namespace MWScript MWWorld::Ptr targetPtr; if (creatureStats.getAiSequence().getCombatTarget (targetPtr)) { - if (targetPtr.getRefData().getHandle() == testedTargetId) + if (targetPtr.getCellRef().getRefId() == testedTargetId) targetsAreEqual = true; } runtime.push(int(targetsAreEqual)); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ce74c0c9f..9fe23ff14 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -811,10 +811,10 @@ namespace MWScript const std::string script = ptr.getClass().getScript(ptr); if(script.empty()) - str<< ptr.getCellRef().getRefId()<<" ("<getLocals(script); diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index cf6f7bf56..c7fdc793c 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -6,25 +6,21 @@ #include "ptr.hpp" -namespace ESM -{ - class CellRef; -} namespace MWWorld { - /// List all (Ogre-)handles, then reset RefData::mBaseNode to 0. - struct ListAndResetHandles + struct ListAndResetObjects { - std::vector mHandles; + std::vector mObjects; bool operator() (MWWorld::Ptr ptr) { - Ogre::SceneNode* handle = ptr.getRefData().getBaseNodeOld(); - if (handle) - mHandles.push_back (handle); + if (ptr.getRefData().getBaseNode()) + { + ptr.getRefData().setBaseNode(NULL); + mObjects.push_back (ptr); + } - ptr.getRefData().setBaseNodeOld(0); return true; } }; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 6a994f0cd..81e240698 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -60,7 +60,7 @@ namespace rot = rot * osg::Quat(y, osg::Vec3(0,-1,0)) * osg::Quat(x, osg::Vec3(-1,0,0)); rendering.rotateObject(ptr, rot * worldRotQuat); - //physics.rotateObject(ptr); + physics.updateRotation(ptr); } } @@ -190,14 +190,13 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; - ListAndResetHandles functor; + ListAndResetObjects functor; - (*iter)->forEach(functor); - for (std::vector::const_iterator iter2 (functor.mHandles.begin()); - iter2!=functor.mHandles.end(); ++iter2) + (*iter)->forEach(functor); + for (std::vector::const_iterator iter2 (functor.mObjects.begin()); + iter2!=functor.mObjects.end(); ++iter2) { - //Ogre::SceneNode* node = *iter2; - //mPhysics->removeObject (node->getName()); + mPhysics->remove(*iter2); } if ((*iter)->getCell()->isExterior()) @@ -258,17 +257,16 @@ namespace MWWorld insertCell (*cell, true, loadingListener); mRendering.addCell(cell); -#if 0 bool waterEnabled = cell->getCell()->hasWater(); - mRendering.setWaterEnabled(waterEnabled); + //mRendering.setWaterEnabled(waterEnabled); if (waterEnabled) { mPhysics->enableWater(cell->getWaterLevel()); - mRendering.setWaterHeight(cell->getWaterLevel()); + //mRendering.setWaterHeight(cell->getWaterLevel()); } else mPhysics->disableWater(); -#endif + if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) mRendering.configureAmbient(cell->getCell()); } @@ -564,8 +562,8 @@ namespace MWWorld { MWBase::Environment::get().getMechanicsManager()->remove (ptr); MWBase::Environment::get().getSoundManager()->stopSound3D (ptr); - //mPhysics->removeObject (ptr.getRefData().getHandle()); - //mRendering.removeObject (ptr); + mPhysics->remove(ptr); + //Rendering.removeObject (ptr); } bool Scene::isCellActive(const CellStore &cell) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index b9d16993f..b37a38353 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -415,11 +415,11 @@ void WeatherManager::update(float duration, bool paused) if (mIsStorm) { MWWorld::Ptr player = world->getPlayerPtr(); - Ogre::Vector3 playerPos (player.getRefData().getPosition().pos); - Ogre::Vector3 redMountainPos (19950, 72032, 27831); + osg::Vec3f playerPos (player.getRefData().getPosition().asVec3()); + osg::Vec3f redMountainPos (19950, 72032, 27831); mStormDirection = (playerPos - redMountainPos); - mStormDirection.z = 0; + mStormDirection.z() = 0; //mRendering->getSkyManager()->setStormDirection(mStormDirection); } @@ -844,7 +844,7 @@ bool WeatherManager::isInStorm() const return mIsStorm; } -Ogre::Vector3 WeatherManager::getStormDirection() const +osg::Vec3f WeatherManager::getStormDirection() const { return mStormDirection; } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 002f4355c..77d961057 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -184,7 +184,7 @@ namespace MWWorld /// Are we in an ash or blight storm? bool isInStorm() const; - Ogre::Vector3 getStormDirection() const; + osg::Vec3f getStormDirection() const; void advanceTime(double hours); @@ -205,7 +205,7 @@ namespace MWWorld float mHour; float mWindSpeed; bool mIsStorm; - Ogre::Vector3 mStormDirection; + osg::Vec3f mStormDirection; MWBase::SoundPtr mAmbientSound; std::string mPlayingSoundID; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 72423f476..add8d0373 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -7,8 +7,6 @@ #else #include #endif -#include "../mwbase/scriptmanager.hpp" -#include "../mwscript/globalscripts.hpp" #include #include @@ -19,6 +17,7 @@ #include #include #include +#include #include @@ -26,6 +25,7 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/movement.hpp" @@ -39,9 +39,12 @@ #include "../mwrender/renderingmanager.hpp" #include "../mwscript/interpretercontext.hpp" +#include "../mwscript/globalscripts.hpp" #include "../mwclass/door.hpp" +#include "../mwphysics/physicssystem.hpp" + #include "player.hpp" #include "manualref.hpp" #include "cellstore.hpp" @@ -51,7 +54,6 @@ #include "actionteleport.hpp" //#include "projectilemanager.hpp" #include "weather.hpp" -#include "../mwphysics/physicssystem.hpp" #include "contentloader.hpp" #include "esmloader.hpp" @@ -156,7 +158,7 @@ namespace MWWorld mStartCell (startCell), mStartupScript(startupScript), mScriptsEnabled(true) { - mPhysics = new MWPhysics::PhysicsSystem(rootNode); + mPhysics = new MWPhysics::PhysicsSystem(resourceSystem->getVFS(), rootNode); //mPhysEngine = mPhysics->getEngine(); #if 0 mProjectileManager.reset(new ProjectileManager(renderer.getScene(), *mPhysEngine)); @@ -1216,7 +1218,7 @@ namespace MWWorld if (haveToMove && ptr.getRefData().getBaseNode()) { mRendering->moveObject(ptr, osg::Vec3f(vec.x, vec.y, vec.z)); - //mPhysics->moveObject (ptr); + mPhysics->updatePosition(ptr); } if (isPlayer) { @@ -1343,9 +1345,9 @@ namespace MWWorld if (force || !isFlying(ptr)) { - //Ogre::Vector3 traced = mPhysics->traceDown(ptr, 500); - //if (traced.z < pos.pos[2]) - // pos.pos[2] = traced.z; + osg::Vec3f traced = mPhysics->traceDown(ptr, 500); + if (traced.z() < pos.pos[2]) + pos.pos[2] = traced.z(); } moveObject(ptr, ptr.getCell(), pos.pos[0], pos.pos[1], pos.pos[2]); @@ -1358,8 +1360,8 @@ namespace MWWorld pos.pos[2] += dist; actor.getRefData().setPosition(pos); - Ogre::Vector3 traced;// = mPhysics->traceDown(actor, dist*1.1f); - moveObject(actor, actor.getCell(), traced.x, traced.y, traced.z); + osg::Vec3f traced = mPhysics->traceDown(actor, dist*1.1f); + moveObject(actor, actor.getCell(), traced.x(), traced.y(), traced.z()); } void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) @@ -1397,34 +1399,32 @@ namespace MWWorld cellY = static_cast(std::floor(y / cellSize)); } - void World::queueMovement(const Ptr &ptr, const Ogre::Vector3 &velocity) + void World::queueMovement(const Ptr &ptr, const osg::Vec3f &velocity) { - //mPhysics->queueObjectMovement(ptr, velocity); + mPhysics->queueObjectMovement(ptr, velocity); } void World::doPhysics(float duration) { mPhysics->stepSimulation(duration); -#if 0 processDoors(duration); - mProjectileManager->update(duration); + //mProjectileManager->update(duration); - const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); - PtrVelocityList::const_iterator player(results.end()); - for(PtrVelocityList::const_iterator iter(results.begin());iter != results.end();++iter) + const MWPhysics::PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); + MWPhysics::PtrVelocityList::const_iterator player(results.end()); + for(MWPhysics::PtrVelocityList::const_iterator iter(results.begin());iter != results.end();++iter) { if(iter->first == getPlayerPtr()) { - /* Handle player last, in case a cell transition occurs */ + // Handle player last, in case a cell transition occurs player = iter; continue; } - moveObjectImp(iter->first, iter->second.x, iter->second.y, iter->second.z); + moveObjectImp(iter->first, iter->second.x(), iter->second.y(), iter->second.z()); } if(player != results.end()) - moveObjectImp(player->first, player->second.x, player->second.y, player->second.z); -#endif + moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z()); } bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) @@ -2312,12 +2312,12 @@ namespace MWWorld return false; } - Ogre::Vector3 World::getStormDirection() const + osg::Vec3f World::getStormDirection() const { if (isCellExterior() || isCellQuasiExterior()) return mWeatherManager->getStormDirection(); else - return Ogre::Vector3(0,1,0); + return osg::Vec3f(0,1,0); } void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index efb9b88c6..a7830da3a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -380,7 +380,7 @@ namespace MWWorld virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const; ///< Convert position to cell numbers - virtual void queueMovement(const Ptr &ptr, const Ogre::Vector3 &velocity); + virtual void queueMovement(const Ptr &ptr, const osg::Vec3f &velocity); ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. @@ -650,7 +650,7 @@ namespace MWWorld virtual bool isInStorm() const; /// @see MWWorld::WeatherManager::getStormDirection - virtual Ogre::Vector3 getStormDirection() const; + virtual osg::Vec3f getStormDirection() const; /// Resets all actors in the current active cells to their original location within that cell. virtual void resetActors(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 417164f2d..ef32e10dc 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -52,9 +52,9 @@ add_component_dir (nifosg nifloader controller particle userdata ) -#add_component_dir (nifbullet -# bulletnifloader -# ) +add_component_dir (nifbullet + bulletnifloader bulletshapemanager + ) add_component_dir (to_utf8 to_utf8 @@ -171,6 +171,7 @@ target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES} ${OPENSCENEGRAPH_LIBRARIES} + ${BULLET_LIBRARIES} # For MyGUI platform ${OPENGL_gl_LIBRARY} ) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index b80bbb83f..62f2d94d8 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -1,31 +1,11 @@ - /* -OpenMW - The completely unofficial reimplementation of Morrowind -Copyright (C) 2008-2010 Nicolay Korslund -Email: < korslund@gmail.com > -WWW: http://openmw.sourceforge.net/ - -This file (ogre_nif_loader.cpp) is part of the OpenMW package. - -OpenMW is distributed as free software: you can redistribute it -and/or modify it under the terms of the GNU General Public License -version 3, as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -version 3 along with this program. If not, see -http://www.gnu.org/licenses/ . - -*/ - #include "bulletnifloader.hpp" #include +#include +#include +#include +#include -#include #include #include "../nif/niffile.hpp" @@ -34,12 +14,7 @@ http://www.gnu.org/licenses/ . #include "../nif/property.hpp" #include "../nif/controller.hpp" #include "../nif/extra.hpp" -#include -#include -#include -// For warning messages -#include namespace { @@ -51,53 +26,40 @@ osg::Matrixf getWorldTransform(const Nif::Node *node) return node->trafo.toMatrix(); } +btVector3 getbtVector(const osg::Vec3f &v) +{ + return btVector3(v.x(), v.y(), v.z()); +} + } namespace NifBullet { -ManualBulletShapeLoader::~ManualBulletShapeLoader() +BulletNifLoader::BulletNifLoader() + : mCompoundShape(NULL) + , mStaticMesh(NULL) { } - -btVector3 ManualBulletShapeLoader::getbtVector(Ogre::Vector3 const &v) +BulletNifLoader::~BulletNifLoader() { - return btVector3(v[0], v[1], v[2]); } -void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) +osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr nif) { - mShape = static_cast(resource); - mResourceName = mShape->getName(); - mBoundingBox = NULL; - mShape->mBoxTranslation = osg::Vec3f(0,0,0); - mShape->mBoxRotation = osg::Quat(); + mShape = new BulletShape; + mCompoundShape = NULL; mStaticMesh = NULL; - Nif::NIFFilePtr pnif;// (Nif::Cache::getInstance().load(mResourceName.substr(0, mResourceName.length()-7))); - Nif::NIFFile & nif = *pnif.get (); - if (nif.numRoots() < 1) + if (nif->numRoots() < 1) { warn("Found no root nodes in NIF."); - return; - } - - // Have to load controlled nodes from the .kf - // FIXME: the .kf has to be loaded both for rendering and physics, ideally it should be opened once and then reused - mControlledNodes.clear(); - std::string kfname = mResourceName.substr(0, mResourceName.length()-7); - Misc::StringUtils::toLower(kfname); - if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) - kfname.replace(kfname.size()-4, 4, ".kf"); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(kfname)) - { - Nif::NIFFilePtr kf;// (Nif::Cache::getInstance().load(kfname)); - //extractControlledNodes(kf, mControlledNodes); + return mShape; } - Nif::Record *r = nif.getRoot(0); + Nif::Record *r = nif->getRoot(0); assert(r != NULL); Nif::Node *node = dynamic_cast(r); @@ -105,29 +67,40 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) { warn("First root in file was not a node, but a " + r->recName + ". Skipping file."); - return; + return mShape; } - mShape->mAutogenerated = hasAutoGeneratedCollision(node); + if (findBoundingBox(node)) + { + std::auto_ptr compound (new btCompoundShape); - //do a first pass - handleNode(node,0,false,false); + btBoxShape* boxShape = new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents)); + btTransform transform = btTransform::getIdentity(); + transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate)); + compound->addChildShape(transform, boxShape); - if(mBoundingBox != NULL) - { - mShape->mCollisionShape = mBoundingBox; - delete mStaticMesh; - if (mCompoundShape) - { - int n = mCompoundShape->getNumChildShapes(); - for(int i=0; i getChildShape(i)); - delete mCompoundShape; - mShape->mAnimatedShapes.clear(); - } + mShape->mCollisionShape = compound.release(); + return mShape; } else { + /* + // Have to load controlled nodes from the .kf + mControlledNodes.clear(); + std::string kfname = mResourceName.substr(0, mResourceName.length()-7); + Misc::StringUtils::toLower(kfname); + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + kfname.replace(kfname.size()-4, 4, ".kf"); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(kfname)) + { + Nif::NIFFilePtr kf;// (Nif::Cache::getInstance().load(kfname)); + //extractControlledNodes(kf, mControlledNodes); + } + */ + + bool autogenerated = hasAutoGeneratedCollision(node); + handleNode(node, 0, autogenerated, false, autogenerated); + if (mCompoundShape) { mShape->mCollisionShape = mCompoundShape; @@ -140,10 +113,46 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) } else if (mStaticMesh) mShape->mCollisionShape = new TriangleMeshShape(mStaticMesh,true); + + return mShape; } } -bool ManualBulletShapeLoader::hasAutoGeneratedCollision(Nif::Node const * rootNode) +// Find a boundingBox in the node hierarchy. +// Return: use bounding box for collision? +bool BulletNifLoader::findBoundingBox(const Nif::Node* node, int flags) +{ + flags |= node->flags; + + if (node->hasBounds) + { + mShape->mCollisionBoxHalfExtents = node->boundXYZ; + mShape->mCollisionBoxTranslate = node->boundPos; + + if (flags & Nif::NiNode::Flag_BBoxCollision) + { + return true; + } + } + + const Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + const Nif::NodeList &list = ninode->children; + for(size_t i = 0;i < list.length();i++) + { + if(!list[i].empty()) + { + bool found = findBoundingBox (list[i].getPtr()); + if (found) + return true; + } + } + } + return false; +} + +bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode) { const Nif::NiNode *ninode = dynamic_cast(rootNode); if(ninode) @@ -161,8 +170,8 @@ bool ManualBulletShapeLoader::hasAutoGeneratedCollision(Nif::Node const * rootNo return true; } -void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, - bool isCollisionNode, bool isAnimated) +void BulletNifLoader::handleNode(const Nif::Node *node, int flags, + bool isCollisionNode, bool isAnimated, bool autogenerated) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. @@ -201,24 +210,21 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, // No collision. Use an internal flag setting to mark this. flags |= 0x800; } + else if (sd->string == "MRK" && autogenerated) + { + // Marker can still have collision if the model explicitely specifies it via a RootCollisionNode. + return; + } + } } - if (isCollisionNode || (mShape->mAutogenerated)) + if (isCollisionNode) { // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // It must be ignored completely. // (occurs in tr_ex_imp_wall_arch_04.nif) - if(node->hasBounds) - { - if (flags & Nif::NiNode::Flag_BBoxCollision) - { - mShape->mBoxTranslation = node->boundPos; - //mShape->mBoxRotation = node->boundRot; - //mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ)); - } - } - else if(node->recType == Nif::RC_NiTriShape) + if(!node->hasBounds && node->recType == Nif::RC_NiTriShape) { handleNiTriShape(static_cast(node), flags, getWorldTransform(node), isAnimated); } @@ -237,7 +243,7 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, } } -void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf &transform, bool isAnimated) +void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf &transform, bool isAnimated) { assert(shape != NULL); @@ -261,6 +267,7 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int if (!shape->skin.empty()) isAnimated = false; + /* if (isAnimated) { if (!mCompoundShape) @@ -281,7 +288,7 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int osg::Vec3f b1 = vertices[triangles[i+0]]; osg::Vec3f b2 = vertices[triangles[i+1]]; osg::Vec3f b3 = vertices[triangles[i+2]]; - childMesh->addTriangle(btVector3(b1.x(),b1.y(),b1.z()),btVector3(b2.x(),b2.y(),b2.z()),btVector3(b3.x(),b3.y(),b3.z())); + childMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } TriangleMeshShape* childShape = new TriangleMeshShape(childMesh,true); @@ -304,9 +311,10 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int mCompoundShape->addChildShape(trans, childShape); } else + */ { if (!mStaticMesh) - mStaticMesh = new btTriangleMesh(); + mStaticMesh = new btTriangleMesh(false); // Static shape, just transform all vertices into position const Nif::NiTriShapeData *data = shape->data.getPtr(); @@ -315,61 +323,99 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int for(size_t i = 0;i < data->triangles.size();i+=3) { - osg::Vec3f b1 = transform*vertices[triangles[i+0]]; - osg::Vec3f b2 = transform*vertices[triangles[i+1]]; - osg::Vec3f b3 = transform*vertices[triangles[i+2]]; - mStaticMesh->addTriangle(btVector3(b1.x(),b1.y(),b1.z()),btVector3(b2.x(),b2.y(),b2.z()),btVector3(b3.x(),b3.y(),b3.z())); + osg::Vec3f b1 = vertices[triangles[i+0]]*transform; + osg::Vec3f b2 = vertices[triangles[i+1]]*transform; + osg::Vec3f b3 = vertices[triangles[i+2]]*transform; + mStaticMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } } } -bool findBoundingBox (const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation) +BulletShape::BulletShape() + : mCollisionShape(NULL) { - if(node->hasBounds) + +} + +BulletShape::~BulletShape() +{ + deleteShape(mCollisionShape); +} + +void BulletShape::deleteShape(btCollisionShape* shape) +{ + if(shape!=NULL) { - if (!(node->flags & Nif::NiNode::Flag_Hidden)) + if(shape->isCompound()) { - //translation = node->boundPos; - //orientation = node->boundRot; - //halfExtents = node->boundXYZ; - return true; + btCompoundShape* ms = static_cast(shape); + int a = ms->getNumChildShapes(); + for(int i=0; i getChildShape(i)); } + delete shape; } +} - const Nif::NiNode *ninode = dynamic_cast(node); - if(ninode) +btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape) const +{ + if(shape->isCompound()) { - const Nif::NodeList &list = ninode->children; - for(size_t i = 0;i < list.length();i++) + btCompoundShape *comp = static_cast(shape); + btCompoundShape *newShape = new btCompoundShape; + + int numShapes = comp->getNumChildShapes(); + for(int i = 0;i < numShapes;++i) { - if(!list[i].empty()) - if (findBoundingBox(list[i].getPtr(), halfExtents, translation, orientation)) - return true; + btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i)); + btTransform trans = comp->getChildTransform(i); + newShape->addChildShape(trans, child); } - } - return false; -} -bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation) -{ - Nif::NIFFilePtr pnif;// (Nif::Cache::getInstance().load(nifFile)); - Nif::NIFFile & nif = *pnif.get (); + return newShape; + } - if (nif.numRoots() < 1) + if(btBvhTriangleMeshShape* trishape = dynamic_cast(shape)) { - return false; +#if BT_BULLET_VERSION >= 283 + btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(trishape, btVector3(1.f, 1.f, 1.f)); +#else + // work around btScaledBvhTriangleMeshShape bug ( https://code.google.com/p/bullet/issues/detail?id=371 ) in older bullet versions + btTriangleMesh* oldMesh = static_cast(trishape->getMeshInterface()); + btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh); + NifBullet::TriangleMeshShape* newShape = new NifBullet::TriangleMeshShape(newMesh, true); +#endif + return newShape; } - Nif::Record *r = nif.getRoot(0); - assert(r != NULL); - - Nif::Node *node = dynamic_cast(r); - if (node == NULL) + if (btBoxShape* boxshape = dynamic_cast(shape)) { - return false; + return new btBoxShape(*boxshape); } - return findBoundingBox(node, halfExtents, translation, orientation); + throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName()); +} + +btCollisionShape *BulletShape::getCollisionShape() +{ + return mCollisionShape; +} + +osg::ref_ptr BulletShape::makeInstance() +{ + osg::ref_ptr instance (new BulletShapeInstance(this)); + return instance; +} + +BulletShapeInstance::BulletShapeInstance(osg::ref_ptr source) + : BulletShape() + , mSource(source) +{ + mCollisionBoxHalfExtents = source->mCollisionBoxHalfExtents; + mCollisionBoxTranslate = source->mCollisionBoxTranslate; + + if (source->mCollisionShape) + mCollisionShape = duplicateCollisionShape(source->mCollisionShape); } } // namespace NifBullet diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 2a2e914e3..d80ab77bd 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -1,41 +1,22 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2010 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.sourceforge.net/ - - This file (ogre_nif_loader.h) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - #ifndef OPENMW_COMPONENTS_NIFBULLET_BULLETNIFLOADER_HPP #define OPENMW_COMPONENTS_NIFBULLET_BULLETNIFLOADER_HPP #include #include #include +#include +#include #include #include #include #include #include +#include +#include +#include -// For warning messages -#include +#include namespace Nif { @@ -47,6 +28,48 @@ namespace Nif namespace NifBullet { +class BulletShapeInstance; +class BulletShape : public osg::Referenced +{ +public: + BulletShape(); + virtual ~BulletShape(); + + btCollisionShape* mCollisionShape; + + // Used for actors. Note, ideally actors would use a separate loader - as it is + // we have to keep a redundant copy of the actor model around in mCollisionShape, which isn't used. + // For now, use one file <-> one resource for simplicity. + osg::Vec3f mCollisionBoxHalfExtents; + osg::Vec3f mCollisionBoxTranslate; + + // Stores animated collision shapes. If any collision nodes in the NIF are animated, then mCollisionShape + // will be a btCompoundShape (which consists of one or more child shapes). + // In this map, for each animated collision shape, + // we store the node's record index mapped to the child index of the shape in the btCompoundShape. + std::map mAnimatedShapes; + + osg::ref_ptr makeInstance(); + + btCollisionShape* duplicateCollisionShape(btCollisionShape* shape) const; + + btCollisionShape* getCollisionShape(); + +private: + void deleteShape(btCollisionShape* shape); +}; + +// An instance of a BulletShape that may have its own unique scaling set on the mCollisionShape. +// Vertex data is shallow-copied where possible. A ref_ptr to the original shape needs to be held to keep vertex pointers intact. +class BulletShapeInstance : public BulletShape +{ +public: + BulletShapeInstance(osg::ref_ptr source); + +private: + osg::ref_ptr mSource; +}; + // Subclass btBhvTriangleMeshShape to auto-delete the meshInterface struct TriangleMeshShape : public btBvhTriangleMeshShape { @@ -66,18 +89,12 @@ struct TriangleMeshShape : public btBvhTriangleMeshShape /** *Load bulletShape from NIF files. */ -class ManualBulletShapeLoader +class BulletNifLoader { public: - ManualBulletShapeLoader(bool showMarkers=false) - : mCompoundShape(NULL) - , mStaticMesh(NULL) - , mBoundingBox(NULL) - , mShowMarkers(showMarkers) - { - } + BulletNifLoader(); - virtual ~ManualBulletShapeLoader(); + virtual ~BulletNifLoader(); void warn(const std::string &msg) { @@ -90,44 +107,26 @@ public: abort(); } + osg::ref_ptr load(const Nif::NIFFilePtr file); + private: - btVector3 getbtVector(Ogre::Vector3 const &v); + bool findBoundingBox(const Nif::Node* node, int flags = 0); - /** - *Parse a node. - */ - void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false); + void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); - /** - *Helper function - */ bool hasAutoGeneratedCollision(const Nif::Node *rootNode); - /** - *convert a NiTriShape to a bullet trishape. - */ void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated); - std::string mResourceName; - - //OEngine::Physic::BulletShape* mShape;//current shape - btCompoundShape* mCompoundShape; btTriangleMesh* mStaticMesh; - btBoxShape *mBoundingBox; - std::set mControlledNodes; - bool mShowMarkers; + osg::ref_ptr mShape; }; - -bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation); - -bool findBoundingBox(const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation); - } #endif diff --git a/components/nifbullet/bulletshapemanager.cpp b/components/nifbullet/bulletshapemanager.cpp new file mode 100644 index 000000000..6acfdd408 --- /dev/null +++ b/components/nifbullet/bulletshapemanager.cpp @@ -0,0 +1,47 @@ +#include "bulletshapemanager.hpp" + +#include + +#include + +namespace NifBullet +{ + +BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs) + : mVFS(vfs) +{ + +} + +BulletShapeManager::~BulletShapeManager() +{ + +} + +osg::ref_ptr BulletShapeManager::createInstance(const std::string &name) +{ + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + osg::ref_ptr shape; + Index::iterator it = mIndex.find(normalized); + if (it == mIndex.end()) + { + Files::IStreamPtr file = mVFS->get(normalized); + + // TODO: add support for non-NIF formats + + BulletNifLoader loader; + // might be worth sharing NIFFiles with SceneManager in some way + shape = loader.load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized))); + + mIndex[normalized] = shape; + } + else + shape = it->second; + + osg::ref_ptr instance = shape->makeInstance(); + return instance; +} + +} diff --git a/components/nifbullet/bulletshapemanager.hpp b/components/nifbullet/bulletshapemanager.hpp new file mode 100644 index 000000000..d9ba3ffbb --- /dev/null +++ b/components/nifbullet/bulletshapemanager.hpp @@ -0,0 +1,37 @@ +#ifndef OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H +#define OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H + +#include +#include + +#include + +namespace VFS +{ + class Manager; +} + +namespace NifBullet +{ + + class BulletShape; + class BulletShapeInstance; + + class BulletShapeManager + { + public: + BulletShapeManager(const VFS::Manager* vfs); + ~BulletShapeManager(); + + osg::ref_ptr createInstance(const std::string& name); + + private: + const VFS::Manager* mVFS; + + typedef std::map > Index; + Index mIndex; + }; + +} + +#endif