forked from teamnwah/openmw-tes3coop
Readded collision objects and movement physics
This commit is contained in:
parent
54c1f19c18
commit
47758c11cd
38 changed files with 1238 additions and 473 deletions
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,8 +47,9 @@ namespace MWClass
|
|||
ptr.get<ESM::Light>();
|
||||
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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef GAME_MWMECHANICS_MOVEMENT_H
|
||||
#define GAME_MWMECHANICS_MOVEMENT_H
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
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]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
152
apps/openmw/mwphysics/actor.cpp
Normal file
152
apps/openmw/mwphysics/actor.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
#include "actor.hpp"
|
||||
#include <osg/io_utils>
|
||||
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
|
||||
#include <btBulletCollisionCommon.h>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include <components/nifbullet/bulletnifloader.hpp>
|
||||
|
||||
#include "convert.hpp"
|
||||
#include "collisiontype.hpp"
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
|
||||
|
||||
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<NifBullet::BulletShapeInstance> 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()*0.05 && mHalfExtents.z() >= 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<PtrHolder*>(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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
125
apps/openmw/mwphysics/actor.hpp
Normal file
125
apps/openmw/mwphysics/actor.hpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
#ifndef OPENMW_MWPHYSICS_ACTOR_H
|
||||
#define OPENMW_MWPHYSICS_ACTOR_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
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<NifBullet::BulletShapeInstance> 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<btCollisionShape> mShape;
|
||||
|
||||
std::auto_ptr<btCollisionObject> 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
|
17
apps/openmw/mwphysics/collisiontype.hpp
Normal file
17
apps/openmw/mwphysics/collisiontype.hpp
Normal file
|
@ -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
|
35
apps/openmw/mwphysics/convert.hpp
Normal file
35
apps/openmw/mwphysics/convert.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef OPENMW_MWPHYSICS_CONVERT_H
|
||||
#define OPENMW_MWPHYSICS_CONVERT_H
|
||||
|
||||
#include <LinearMath/btVector3.h>
|
||||
#include <LinearMath/btQuaternion.h>
|
||||
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/Quat>
|
||||
|
||||
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
|
|
@ -3,15 +3,17 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#include <osg/Group>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
|
||||
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
|
||||
|
||||
#include <components/nifbullet/bulletshapemanager.hpp>
|
||||
#include <components/nifbullet/bulletnifloader.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include <components/esm/loadgmst.hpp>
|
||||
|
||||
#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<float>::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<float>::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);
|
||||
osg::Vec3f position(ptr.getRefData().getPosition().asVec3());
|
||||
|
||||
return position;
|
||||
#if 0
|
||||
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
|
||||
if (!physicActor)
|
||||
return position;
|
||||
|
||||
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<std::string, std::string>& collisionTracker
|
||||
, std::map<std::string, std::string>& standingCollisionTracker)
|
||||
{
|
||||
const ESM::Position& refpos = ptr.getRefData().getPosition();
|
||||
Ogre::Vector3 position(refpos.pos);
|
||||
return position;
|
||||
#if 0
|
||||
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<ESM::GameSetting>()
|
||||
.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<ESM::GameSetting>()
|
||||
.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<const OEngine::Physic::RigidBody*>(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<const OEngine::Physic::RigidBody*>(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<NifBullet::BulletShapeInstance> shapeInstance)
|
||||
: mShapeInstance(shapeInstance)
|
||||
{
|
||||
mPtr = ptr;
|
||||
|
||||
mCollisionObject.reset(new btCollisionObject);
|
||||
mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape());
|
||||
|
||||
mCollisionObject->setUserPointer(static_cast<PtrHolder*>(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;
|
||||
}
|
||||
|
||||
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<btCollisionObject> mCollisionObject;
|
||||
osg::ref_ptr<NifBullet::BulletShapeInstance> mShapeInstance;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
|
||||
PhysicsSystem::PhysicsSystem(osg::ref_ptr<osg::Group> parentNode)
|
||||
: mTimeAccum(0.0f)
|
||||
PhysicsSystem::PhysicsSystem(const VFS::Manager* vfs, osg::ref_ptr<osg::Group> 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<std::string>();//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<NifBullet::BulletShapeInstance> 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<NifBullet::BulletShapeInstance> shapeInstance = mShapeManager->createInstance(mesh);
|
||||
|
||||
Actor* actor = new Actor(ptr, shapeInstance, mDynamicsWorld);
|
||||
mActors.insert(std::make_pair(ptr, actor));
|
||||
}
|
||||
|
||||
bool PhysicsSystem::toggleCollisionMode()
|
||||
{
|
||||
/*
|
||||
for(std::map<std::string,OEngine::Physic::PhysicActor*>::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();
|
||||
|
|
|
@ -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 <memory>
|
||||
|
||||
|
@ -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<std::pair<MWWorld::Ptr,Ogre::Vector3> > 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<std::pair<MWWorld::Ptr,osg::Vec3f> > PtrVelocityList;
|
||||
|
||||
class HeightField;
|
||||
class Object;
|
||||
class Actor;
|
||||
|
||||
class PhysicsSystem
|
||||
{
|
||||
public:
|
||||
PhysicsSystem (osg::ref_ptr<osg::Group> parentNode);
|
||||
PhysicsSystem (const VFS::Manager* vfs, osg::ref_ptr<osg::Group> 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<std::string> 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<std::string,Ogre::Vector3> 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<NifBullet::BulletShapeManager> mShapeManager;
|
||||
|
||||
typedef std::map<MWWorld::Ptr, Object*> ObjectMap;
|
||||
ObjectMap mObjects;
|
||||
|
||||
typedef std::map<MWWorld::Ptr, Actor*> ActorMap;
|
||||
ActorMap mActors;
|
||||
|
||||
typedef std::map<std::pair<int, int>, HeightField*> HeightFieldMap;
|
||||
HeightFieldMap mHeightFields;
|
||||
|
||||
|
@ -121,9 +143,9 @@ namespace MWPhysics
|
|||
// Tracks all movement collisions happening during a single frame. <actor handle, collided handle>
|
||||
// This will detect e.g. running against a vertical wall. It will not detect climbing up stairs,
|
||||
// stepping up small objects, etc.
|
||||
std::map<std::string, std::string> mCollisions;
|
||||
std::map<std::string, std::string> mCollisions; // FIXME: reimplement
|
||||
|
||||
std::map<std::string, std::string> mStandingCollisions;
|
||||
std::map<std::string, std::string> mStandingCollisions; // FIXME: reimplement
|
||||
|
||||
PtrVelocityList mMovementQueue;
|
||||
PtrVelocityList mMovementResults;
|
||||
|
|
128
apps/openmw/mwphysics/trace.cpp
Normal file
128
apps/openmw/mwphysics/trace.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include "trace.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <btBulletCollisionCommon.h>
|
||||
|
||||
#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<btConvexShape*>(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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
27
apps/openmw/mwphysics/trace.h
Normal file
27
apps/openmw/mwphysics/trace.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef OENGINE_BULLET_TRACE_H
|
||||
#define OENGINE_BULLET_TRACE_H
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
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
|
|
@ -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));
|
||||
|
|
|
@ -811,10 +811,10 @@ namespace MWScript
|
|||
|
||||
const std::string script = ptr.getClass().getScript(ptr);
|
||||
if(script.empty())
|
||||
str<< ptr.getCellRef().getRefId()<<" ("<<ptr.getRefData().getHandle()<<") does not have a script.";
|
||||
str<< ptr.getCellRef().getRefId()<<" does not have a script.";
|
||||
else
|
||||
{
|
||||
str<< "Local variables for "<<ptr.getCellRef().getRefId()<<" ("<<ptr.getRefData().getHandle()<<")";
|
||||
str<< "Local variables for "<<ptr.getCellRef().getRefId();
|
||||
|
||||
const Locals &locals = ptr.getRefData().getLocals();
|
||||
const Compiler::Locals &complocals = MWBase::Environment::get().getScriptManager()->getLocals(script);
|
||||
|
|
|
@ -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<Ogre::SceneNode*> mHandles;
|
||||
std::vector<MWWorld::Ptr> 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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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<ListAndResetHandles>(functor);
|
||||
for (std::vector<Ogre::SceneNode*>::const_iterator iter2 (functor.mHandles.begin());
|
||||
iter2!=functor.mHandles.end(); ++iter2)
|
||||
(*iter)->forEach<ListAndResetObjects>(functor);
|
||||
for (std::vector<MWWorld::Ptr>::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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#else
|
||||
#include <tr1/unordered_map>
|
||||
#endif
|
||||
#include "../mwbase/scriptmanager.hpp"
|
||||
#include "../mwscript/globalscripts.hpp"
|
||||
#include <OgreSceneNode.h>
|
||||
|
||||
#include <osg/Group>
|
||||
|
@ -19,6 +17,7 @@
|
|||
#include <components/compiler/locals.hpp>
|
||||
#include <components/esm/cellid.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
#include <boost/math/special_functions/sign.hpp>
|
||||
|
||||
|
@ -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<int>(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<MWWorld::Ptr>& out)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -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 <cstdio>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <OgreMatrix4.h>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#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 <libs/platform/strings.h>
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
// For warning messages
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -51,41 +26,66 @@ 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<BulletShape> BulletNifLoader::load(const Nif::NIFFilePtr nif)
|
||||
{
|
||||
mShape = static_cast<OEngine::Physic::BulletShape *>(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;
|
||||
return mShape;
|
||||
}
|
||||
|
||||
Nif::Record *r = nif->getRoot(0);
|
||||
assert(r != NULL);
|
||||
|
||||
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
|
||||
if (node == NULL)
|
||||
{
|
||||
warn("First root in file was not a node, but a " +
|
||||
r->recName + ". Skipping file.");
|
||||
return mShape;
|
||||
}
|
||||
|
||||
if (findBoundingBox(node))
|
||||
{
|
||||
std::auto_ptr<btCompoundShape> compound (new btCompoundShape);
|
||||
|
||||
btBoxShape* boxShape = new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents));
|
||||
btTransform transform = btTransform::getIdentity();
|
||||
transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate));
|
||||
compound->addChildShape(transform, boxShape);
|
||||
|
||||
mShape->mCollisionShape = compound.release();
|
||||
return mShape;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
// 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);
|
||||
|
@ -96,38 +96,11 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
|
|||
Nif::NIFFilePtr kf;// (Nif::Cache::getInstance().load(kfname));
|
||||
//extractControlledNodes(kf, mControlledNodes);
|
||||
}
|
||||
*/
|
||||
|
||||
Nif::Record *r = nif.getRoot(0);
|
||||
assert(r != NULL);
|
||||
bool autogenerated = hasAutoGeneratedCollision(node);
|
||||
handleNode(node, 0, autogenerated, false, autogenerated);
|
||||
|
||||
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
|
||||
if (node == NULL)
|
||||
{
|
||||
warn("First root in file was not a node, but a " +
|
||||
r->recName + ". Skipping file.");
|
||||
return;
|
||||
}
|
||||
|
||||
mShape->mAutogenerated = hasAutoGeneratedCollision(node);
|
||||
|
||||
//do a first pass
|
||||
handleNode(node,0,false,false);
|
||||
|
||||
if(mBoundingBox != NULL)
|
||||
{
|
||||
mShape->mCollisionShape = mBoundingBox;
|
||||
delete mStaticMesh;
|
||||
if (mCompoundShape)
|
||||
{
|
||||
int n = mCompoundShape->getNumChildShapes();
|
||||
for(int i=0; i <n;i++)
|
||||
delete (mCompoundShape->getChildShape(i));
|
||||
delete mCompoundShape;
|
||||
mShape->mAnimatedShapes.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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<const Nif::NiNode*>(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<const Nif::NiNode*>(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<const Nif::NiTriShape*>(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()
|
||||
{
|
||||
if (!(node->flags & Nif::NiNode::Flag_Hidden))
|
||||
deleteShape(mCollisionShape);
|
||||
}
|
||||
|
||||
void BulletShape::deleteShape(btCollisionShape* shape)
|
||||
{
|
||||
//translation = node->boundPos;
|
||||
//orientation = node->boundRot;
|
||||
//halfExtents = node->boundXYZ;
|
||||
return true;
|
||||
if(shape!=NULL)
|
||||
{
|
||||
if(shape->isCompound())
|
||||
{
|
||||
btCompoundShape* ms = static_cast<btCompoundShape*>(shape);
|
||||
int a = ms->getNumChildShapes();
|
||||
for(int i=0; i <a;i++)
|
||||
deleteShape(ms->getChildShape(i));
|
||||
}
|
||||
delete shape;
|
||||
}
|
||||
}
|
||||
|
||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
|
||||
if(ninode)
|
||||
btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape) const
|
||||
{
|
||||
const Nif::NodeList &list = ninode->children;
|
||||
for(size_t i = 0;i < list.length();i++)
|
||||
if(shape->isCompound())
|
||||
{
|
||||
if(!list[i].empty())
|
||||
if (findBoundingBox(list[i].getPtr(), halfExtents, translation, orientation))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
btCompoundShape *comp = static_cast<btCompoundShape*>(shape);
|
||||
btCompoundShape *newShape = new btCompoundShape;
|
||||
|
||||
int numShapes = comp->getNumChildShapes();
|
||||
for(int i = 0;i < numShapes;++i)
|
||||
{
|
||||
btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i));
|
||||
btTransform trans = comp->getChildTransform(i);
|
||||
newShape->addChildShape(trans, child);
|
||||
}
|
||||
|
||||
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 ();
|
||||
|
||||
if (nif.numRoots() < 1)
|
||||
{
|
||||
return false;
|
||||
return newShape;
|
||||
}
|
||||
|
||||
Nif::Record *r = nif.getRoot(0);
|
||||
assert(r != NULL);
|
||||
|
||||
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
|
||||
if (node == NULL)
|
||||
if(btBvhTriangleMeshShape* trishape = dynamic_cast<btBvhTriangleMeshShape*>(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<btTriangleMesh*>(trishape->getMeshInterface());
|
||||
btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh);
|
||||
NifBullet::TriangleMeshShape* newShape = new NifBullet::TriangleMeshShape(newMesh, true);
|
||||
#endif
|
||||
return newShape;
|
||||
}
|
||||
|
||||
return findBoundingBox(node, halfExtents, translation, orientation);
|
||||
if (btBoxShape* boxshape = dynamic_cast<btBoxShape*>(shape))
|
||||
{
|
||||
return new btBoxShape(*boxshape);
|
||||
}
|
||||
|
||||
throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName());
|
||||
}
|
||||
|
||||
btCollisionShape *BulletShape::getCollisionShape()
|
||||
{
|
||||
return mCollisionShape;
|
||||
}
|
||||
|
||||
osg::ref_ptr<BulletShapeInstance> BulletShape::makeInstance()
|
||||
{
|
||||
osg::ref_ptr<BulletShapeInstance> instance (new BulletShapeInstance(this));
|
||||
return instance;
|
||||
}
|
||||
|
||||
BulletShapeInstance::BulletShapeInstance(osg::ref_ptr<BulletShape> source)
|
||||
: BulletShape()
|
||||
, mSource(source)
|
||||
{
|
||||
mCollisionBoxHalfExtents = source->mCollisionBoxHalfExtents;
|
||||
mCollisionBoxTranslate = source->mCollisionBoxTranslate;
|
||||
|
||||
if (source->mCollisionShape)
|
||||
mCollisionShape = duplicateCollisionShape(source->mCollisionShape);
|
||||
}
|
||||
|
||||
} // namespace NifBullet
|
||||
|
|
|
@ -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 <cassert>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
|
||||
#include <BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h>
|
||||
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
|
||||
#include <osg/Matrixf>
|
||||
#include <osg/BoundingBox>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Referenced>
|
||||
|
||||
// For warning messages
|
||||
#include <iostream>
|
||||
#include <components/nif/niffile.hpp>
|
||||
|
||||
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<int, int> mAnimatedShapes;
|
||||
|
||||
osg::ref_ptr<BulletShapeInstance> 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<BulletShape> source);
|
||||
|
||||
private:
|
||||
osg::ref_ptr<BulletShape> 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<BulletShape> 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<std::string> mControlledNodes;
|
||||
|
||||
bool mShowMarkers;
|
||||
osg::ref_ptr<BulletShape> 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
|
||||
|
|
47
components/nifbullet/bulletshapemanager.cpp
Normal file
47
components/nifbullet/bulletshapemanager.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "bulletshapemanager.hpp"
|
||||
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include <components/nifbullet/bulletnifloader.hpp>
|
||||
|
||||
namespace NifBullet
|
||||
{
|
||||
|
||||
BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs)
|
||||
: mVFS(vfs)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BulletShapeManager::~BulletShapeManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::createInstance(const std::string &name)
|
||||
{
|
||||
std::string normalized = name;
|
||||
mVFS->normalizeFilename(normalized);
|
||||
|
||||
osg::ref_ptr<BulletShape> 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<BulletShapeInstance> instance = shape->makeInstance();
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
37
components/nifbullet/bulletshapemanager.hpp
Normal file
37
components/nifbullet/bulletshapemanager.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H
|
||||
#define OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
namespace VFS
|
||||
{
|
||||
class Manager;
|
||||
}
|
||||
|
||||
namespace NifBullet
|
||||
{
|
||||
|
||||
class BulletShape;
|
||||
class BulletShapeInstance;
|
||||
|
||||
class BulletShapeManager
|
||||
{
|
||||
public:
|
||||
BulletShapeManager(const VFS::Manager* vfs);
|
||||
~BulletShapeManager();
|
||||
|
||||
osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);
|
||||
|
||||
private:
|
||||
const VFS::Manager* mVFS;
|
||||
|
||||
typedef std::map<std::string, osg::ref_ptr<BulletShape> > Index;
|
||||
Index mIndex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue