Some actors are supposed to spawn on an object that belongs to an adjacent cell.

Since actors can be active in 3x3 grid around the player, we need to
first load all objects in a 5x5 grid around the player.

Split load and unloading in 2 phases. Add an mInactiveCells set into the
scene, which contains all cells inside the aforementioned 5x5 grid.
These cells contains only heightfields and non-animated physics objects.

Animated objects are tied to the scene graph, which doesn't exists yet
in these cells, so we skip them.
dont-compose-content
fredzio 3 years ago
parent df3a47187b
commit c795e0bce6

@ -3,6 +3,7 @@
Bug #3737: Scripts from The Underground 2 .esp do not play (all patched versions)
Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes
Bug #5379: Wandering NPCs falling through cantons
Bug #5453: Magic effect VFX are offset for creatures
Bug #5483: AutoCalc flag is not used to calculate spells cost
Bug #6101: Disarming trapped unlocked owned objects isn't considered a crime

@ -38,10 +38,10 @@ namespace MWClass
}
}
void Activator::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
void Activator::insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const
{
if(!model.empty())
physics.addObject(ptr, model);
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World, skipAnimated);
}
std::string Activator::getModel(const MWWorld::ConstPtr &ptr) const

@ -17,7 +17,7 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -17,16 +17,12 @@
namespace MWClass
{
Actor::Actor() {}
Actor::~Actor() {}
void Actor::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
{
MWBase::Environment::get().getWorld()->adjustPosition(ptr, force);
}
void Actor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
void Actor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const
{
if (!model.empty())
{

@ -15,16 +15,16 @@ namespace MWClass
{
protected:
Actor();
Actor() = default;
public:
virtual ~Actor();
~Actor() override = default;
void adjustPosition(const MWWorld::Ptr& ptr, bool force) const override;
///< Adjust position to stand on ground. Must be called post model load
/// @param force do this even if the ptr is flying
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override;
bool useAnim() const override;
@ -46,8 +46,8 @@ namespace MWClass
float getCurrentSpeed(const MWWorld::Ptr& ptr) const override;
// not implemented
Actor(const Actor&);
Actor& operator= (const Actor&);
Actor(const Actor&) = delete;
Actor& operator= (const Actor&) = delete;
};
}

@ -26,11 +26,6 @@ namespace MWClass
}
}
void Apparatus::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Apparatus::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();

@ -17,8 +17,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -34,11 +34,6 @@ namespace MWClass
}
}
void Armor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Armor::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();

@ -16,8 +16,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -22,10 +22,6 @@ namespace MWClass
}
}
void BodyPart::insertObject(const MWWorld::Ptr &ptr, const std::string &model, MWPhysics::PhysicsSystem &physics) const
{
}
std::string BodyPart::getName(const MWWorld::ConstPtr &ptr) const
{
return std::string();

@ -15,8 +15,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -31,11 +31,6 @@ namespace MWClass
}
}
void Book::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Book::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();

@ -14,8 +14,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -29,11 +29,6 @@ namespace MWClass
}
}
void Clothing::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Clothing::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();

@ -14,8 +14,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -106,10 +106,10 @@ namespace MWClass
}
}
void Container::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
void Container::insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const
{
if(!model.empty())
physics.addObject(ptr, model);
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World, skipAnimated);
}
std::string Container::getModel(const MWWorld::ConstPtr &ptr) const

@ -42,7 +42,7 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -55,10 +55,10 @@ namespace MWClass
}
}
void Door::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
void Door::insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const
{
if(!model.empty())
physics.addObject(ptr, model, MWPhysics::CollisionType_Door);
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_Door, skipAnimated);
// Resume the door's opening/closing animation if it wasn't finished
if (ptr.getRefData().getCustomData())

@ -18,7 +18,7 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override;
bool isDoor() const override;

@ -28,11 +28,6 @@ namespace MWClass
}
}
void Ingredient::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Ingredient::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();

@ -14,8 +14,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -33,7 +33,7 @@ namespace MWClass
renderingInterface.getObjects().insertModel(ptr, model, true, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault));
}
void Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
void Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const
{
MWWorld::LiveCellRef<ESM::Light> *ref =
ptr.get<ESM::Light>();
@ -41,7 +41,7 @@ namespace MWClass
// TODO: add option somewhere to enable collision for placeable objects
if (!model.empty() && (ref->mBase->mData.mFlags & ESM::Light::Carry) == 0)
physics.addObject(ptr, model);
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World, skipAnimated);
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,

@ -14,7 +14,7 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override;
bool useAnim() const override;

@ -28,11 +28,6 @@ namespace MWClass
}
}
void Lockpick::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Lockpick::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();

@ -14,8 +14,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -37,11 +37,6 @@ namespace MWClass
}
}
void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Miscellaneous::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();

@ -14,8 +14,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -30,11 +30,6 @@ namespace MWClass
}
}
void Potion::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Potion::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();

@ -14,8 +14,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -28,11 +28,6 @@ namespace MWClass
}
}
void Probe::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Probe::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();

@ -14,8 +14,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -25,11 +25,6 @@ namespace MWClass
}
}
void Repair::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Repair::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();

@ -14,8 +14,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -23,10 +23,10 @@ namespace MWClass
}
}
void Static::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
void Static::insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const
{
if(!model.empty())
physics.addObject(ptr, model);
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World, skipAnimated);
}
std::string Static::getModel(const MWWorld::ConstPtr &ptr) const

@ -14,7 +14,7 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -34,11 +34,6 @@ namespace MWClass
}
}
void Weapon::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{
// TODO: add option somewhere to enable collision for placeable objects
}
std::string Weapon::getModel(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();

@ -15,8 +15,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string.

@ -68,7 +68,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic
updateScale();
if(!mRotationallyInvariant)
updateRotation();
setRotation(mPtr.getRefData().getBaseNode()->getAttitude());
updatePosition();
addCollisionMask(getCollisionMask());
@ -197,10 +197,10 @@ osg::Vec3f Actor::getPreviousPosition() const
return mPreviousPosition;
}
void Actor::updateRotation ()
void Actor::setRotation(osg::Quat quat)
{
std::scoped_lock lock(mPositionMutex);
mRotation = mPtr.getRefData().getBaseNode()->getAttitude();
mRotation = quat;
}
bool Actor::isRotationallyInvariant() const

@ -48,7 +48,7 @@ namespace MWPhysics
void enableCollisionBody(bool collision);
void updateScale();
void updateRotation();
void setRotation(osg::Quat quat);
/**
* Return true if the collision shape looks the same no matter how its Z rotated.

@ -14,7 +14,7 @@
namespace MWPhysics
{
Object::Object(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance, int collisionType, PhysicsTaskScheduler* scheduler)
Object::Object(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance, osg::Quat rotation, int collisionType, PhysicsTaskScheduler* scheduler)
: mShapeInstance(shapeInstance)
, mSolid(true)
, mTaskScheduler(scheduler)
@ -27,7 +27,7 @@ namespace MWPhysics
mCollisionObject->setUserPointer(this);
setScale(ptr.getCellRef().getScale());
setRotation(Misc::Convert::toBullet(ptr.getRefData().getBaseNode()->getAttitude()));
setRotation(rotation);
setOrigin(Misc::Convert::toBullet(ptr.getRefData().getPosition().asVec3()));
commitPositionChange();
@ -51,10 +51,10 @@ namespace MWPhysics
mScaleUpdatePending = true;
}
void Object::setRotation(const btQuaternion& quat)
void Object::setRotation(osg::Quat quat)
{
std::unique_lock<std::mutex> lock(mPositionMutex);
mLocalTransform.setRotation(quat);
mLocalTransform.setRotation(Misc::Convert::toBullet(quat));
mTransformUpdatePending = true;
}
@ -108,7 +108,7 @@ namespace MWPhysics
bool Object::isAnimated() const
{
return !mShapeInstance->mAnimatedShapes.empty();
return mShapeInstance->isAnimated();
}
bool Object::animateCollisionShapes()

@ -26,12 +26,12 @@ namespace MWPhysics
class Object final : public PtrHolder
{
public:
Object(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance, int collisionType, PhysicsTaskScheduler* scheduler);
Object(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance, osg::Quat rotation, int collisionType, PhysicsTaskScheduler* scheduler);
~Object() override;
const Resource::BulletShapeInstance* getShapeInstance() const;
void setScale(float scale);
void setRotation(const btQuaternion& quat);
void setRotation(osg::Quat quat);
void setOrigin(const btVector3& vec);
void commitPositionChange();
btCollisionObject* getCollisionObject();

@ -461,13 +461,18 @@ namespace MWPhysics
return heightField->second.get();
}
void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType)
void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, osg::Quat rotation, int collisionType, bool skipAnimated)
{
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);
if (!shapeInstance || !shapeInstance->getCollisionShape())
return;
auto obj = std::make_shared<Object>(ptr, shapeInstance, collisionType, mTaskScheduler.get());
if (skipAnimated && shapeInstance->isAnimated())
return;
assert(!getObject(ptr));
auto obj = std::make_shared<Object>(ptr, shapeInstance, rotation, collisionType, mTaskScheduler.get());
mObjects.emplace(ptr, obj);
if (obj->isAnimated())
@ -625,12 +630,12 @@ namespace MWPhysics
mTaskScheduler->updateSingleAabb(foundProjectile->second);
}
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr, osg::Quat rotate)
{
ObjectMap::iterator found = mObjects.find(ptr);
if (found != mObjects.end())
{
found->second->setRotation(Misc::Convert::toBullet(ptr.getRefData().getBaseNode()->getAttitude()));
found->second->setRotation(rotate);
mTaskScheduler->updateSingleAabb(found->second);
return;
}
@ -639,7 +644,7 @@ namespace MWPhysics
{
if (!foundActor->second->isRotationallyInvariant())
{
foundActor->second->updateRotation();
foundActor->second->setRotation(rotate);
mTaskScheduler->updateSingleAabb(foundActor->second);
}
return;

@ -122,7 +122,7 @@ namespace MWPhysics
void setWaterHeight(float height);
void disableWater();
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, osg::Quat rotation, int collisionType = CollisionType_World, bool skipAnimated = false);
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater);
@ -143,7 +143,7 @@ namespace MWPhysics
void remove (const MWWorld::Ptr& ptr);
void updateScale (const MWWorld::Ptr& ptr);
void updateRotation (const MWWorld::Ptr& ptr);
void updateRotation (const MWWorld::Ptr& ptr, osg::Quat rotate);
void updatePosition (const MWWorld::Ptr& ptr);
void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject);

@ -18,12 +18,23 @@ namespace MWWorld
if (ptr.getRefData().getBaseNode())
{
ptr.getRefData().setBaseNode(nullptr);
mObjects.push_back (ptr);
}
mObjects.push_back (ptr);
return true;
}
};
struct ListObjectsVisitor
{
std::vector<MWWorld::Ptr> mObjects;
bool operator() (MWWorld::Ptr ptr)
{
mObjects.push_back (ptr);
return true;
}
};
}
#endif

@ -25,16 +25,12 @@ namespace MWWorld
{
std::map<std::string, std::shared_ptr<Class> > Class::sClasses;
Class::Class() {}
Class::~Class() {}
void Class::insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const
{
}
void Class::insertObject(const Ptr& ptr, const std::string& mesh, MWPhysics::PhysicsSystem& physics) const
void Class::insertObject(const Ptr& ptr, const std::string& mesh, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const
{
}

@ -6,6 +6,7 @@
#include <string>
#include <vector>
#include <osg/Quat>
#include <osg/Vec4f>
#include "ptr.hpp"
@ -57,13 +58,9 @@ namespace MWWorld
std::string mTypeName;
// not implemented
Class (const Class&);
Class& operator= (const Class&);
protected:
Class();
Class() = default;
std::shared_ptr<Action> defaultItemActivate(const Ptr &ptr, const Ptr &actor) const;
///< Generate default action for activating inventory items
@ -72,14 +69,16 @@ namespace MWWorld
public:
virtual ~Class();
virtual ~Class() = default;
Class (const Class&) = delete;
Class& operator= (const Class&) = delete;
const std::string& getTypeName() const {
return mTypeName;
}
virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const;
virtual void insertObject(const Ptr& ptr, const std::string& mesh, MWPhysics::PhysicsSystem& physics) const;
virtual void insertObject(const Ptr& ptr, const std::string& mesh, osg::Quat rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const;
///< Add reference into a cell for rendering (default implementation: don't render anything).
virtual std::string getName (const ConstPtr& ptr) const = 0;

@ -73,18 +73,20 @@ namespace
* osg::Quat(xr, osg::Vec3(-1, 0, 0));
}
void setNodeRotation(const MWWorld::Ptr& ptr, MWRender::RenderingManager& rendering, RotationOrder order)
osg::Quat makeNodeRotation(const MWWorld::Ptr& ptr, RotationOrder order)
{
if (!ptr.getRefData().getBaseNode())
return;
const auto pos = ptr.getRefData().getPosition();
const auto rot = ptr.getClass().isActor() ? makeActorOsgQuat(pos)
: (order == RotationOrder::inverse ? makeInversedOrderObjectOsgQuat(pos) : makeObjectOsgQuat(pos));
rendering.rotateObject(ptr,
ptr.getClass().isActor()
? makeActorOsgQuat(ptr.getRefData().getPosition())
: (order == RotationOrder::inverse
? makeInversedOrderObjectOsgQuat(ptr.getRefData().getPosition())
: makeObjectOsgQuat(ptr.getRefData().getPosition()))
);
return rot;
}
void setNodeRotation(const MWWorld::Ptr& ptr, MWRender::RenderingManager& rendering, osg::Quat rotation)
{
if (ptr.getRefData().getBaseNode())
rendering.rotateObject(ptr, rotation);
}
std::string getModel(const MWWorld::Ptr &ptr, const VFS::Manager *vfs)
@ -101,7 +103,7 @@ namespace
}
void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
MWRender::RenderingManager& rendering, std::set<ESM::RefNum>& pagedRefs)
MWRender::RenderingManager& rendering, std::set<ESM::RefNum>& pagedRefs, bool onlyPhysics)
{
if (ptr.getRefData().getBaseNode() || physics.getActor(ptr))
{
@ -109,19 +111,23 @@ namespace
return;
}
bool useAnim = ptr.getClass().useAnim();
std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS());
const auto rotation = makeNodeRotation(ptr, RotationOrder::direct);
if (onlyPhysics && !physics.getObject(ptr) && !ptr.getClass().isActor())
{
// When we preload physics object we need to skip animated objects. They are dependant on the scene graph which doesn't yet exist.
ptr.getClass().insertObject (ptr, model, rotation, physics, true);
return;
}
const ESM::RefNum& refnum = ptr.getCellRef().getRefNum();
if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end())
ptr.getClass().insertObjectRendering(ptr, model, rendering);
else
ptr.getRefData().setBaseNode(new SceneUtil::PositionAttitudeTransform); // FIXME remove this when physics code is fixed not to depend on basenode
setNodeRotation(ptr, rendering, RotationOrder::direct);
setNodeRotation(ptr, rendering, rotation);
ptr.getClass().insertObject (ptr, model, physics);
if (useAnim)
if (ptr.getClass().useAnim())
MWBase::Environment::get().getMechanicsManager()->add(ptr);
if (ptr.getClass().isActor())
@ -129,6 +135,9 @@ namespace
// Restore effect particles
MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr);
if (!physics.getObject(ptr))
ptr.getClass().insertObject (ptr, model, rotation, physics);
}
void addObject(const MWWorld::Ptr& ptr, const MWPhysics::PhysicsSystem& physics, DetourNavigator::Navigator& navigator)
@ -199,11 +208,12 @@ namespace
{
MWWorld::CellStore& mCell;
Loading::Listener& mLoadingListener;
bool mOnlyObjects;
bool mTest;
std::vector<MWWorld::Ptr> mToInsert;
InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool test);
InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool onlyObjects, bool test);
bool operator() (const MWWorld::Ptr& ptr);
@ -211,8 +221,8 @@ namespace
void insert(AddObject&& addObject);
};
InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool test)
: mCell (cell), mLoadingListener (loadingListener), mTest(test)
InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool onlyObjects, bool test)
: mCell (cell), mLoadingListener (loadingListener), mOnlyObjects(onlyObjects), mTest(test)
{}
bool InsertVisitor::operator() (const MWWorld::Ptr& ptr)
@ -228,7 +238,7 @@ namespace
{
for (MWWorld::Ptr& ptr : mToInsert)
{
if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled())
if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled() && (!mOnlyObjects || !ptr.getClass().isActor()))
{
try
{
@ -261,6 +271,16 @@ namespace
return std::abs(cellPosition.first) + std::abs(cellPosition.second);
}
bool isCellInCollection(int x, int y, MWWorld::Scene::CellStoreCollection& collection)
{
for (auto *cell : collection)
{
assert(cell->getCell()->isExterior());
if (x == cell->getCell()->getGridX() && y == cell->getCell()->getGridY())
return true;
}
return false;
}
}
@ -274,7 +294,7 @@ namespace MWWorld
{
if (!ptr.getRefData().getBaseNode()) return;
ptr.getClass().insertObjectRendering(ptr, getModel(ptr, mRendering.getResourceSystem()->getVFS()), mRendering);
setNodeRotation(ptr, mRendering, RotationOrder::direct);
setNodeRotation(ptr, mRendering, makeNodeRotation(ptr, RotationOrder::direct));
reloadTerrain();
}
}
@ -290,8 +310,9 @@ namespace MWWorld
void Scene::updateObjectRotation(const Ptr &ptr, RotationOrder order)
{
setNodeRotation(ptr, mRendering, order);
mPhysics->updateRotation(ptr);
const auto rot = makeNodeRotation(ptr, order);
setNodeRotation(ptr, mRendering, rot);
mPhysics->updateRotation(ptr, rot);
}
void Scene::updateObjectScale(const Ptr &ptr)
@ -311,165 +332,213 @@ namespace MWWorld
mRendering.update (duration, paused);
}
void Scene::unloadCell (CellStoreCollection::iterator iter, bool test)
void Scene::unloadInactiveCell (CellStore* cell, bool test)
{
assert(mActiveCells.find(cell) == mActiveCells.end());
assert(mInactiveCells.find(cell) != mInactiveCells.end());
if (!test)
Log(Debug::Info) << "Unloading cell " << (*iter)->getCell()->getDescription();
Log(Debug::Info) << "Unloading cell " << cell->getCell()->getDescription();
ListObjectsVisitor visitor;
cell->forEach(visitor);
for (const auto& ptr : visitor.mObjects)
mPhysics->remove(ptr);
if (cell->getCell()->isExterior())
{
const auto cellX = cell->getCell()->getGridX();
const auto cellY = cell->getCell()->getGridY();
mPhysics->removeHeightField(cellX, cellY);
}
mInactiveCells.erase(cell);
}
void Scene::deactivateCell(CellStore* cell, bool test)
{
assert(mInactiveCells.find(cell) != mInactiveCells.end());
if (mActiveCells.find(cell) == mActiveCells.end())
return;
if (!test)
Log(Debug::Info) << "Deactivate cell " << cell->getCell()->getDescription();
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
ListAndResetObjectsVisitor visitor;
(*iter)->forEach(visitor);
cell->forEach(visitor);
const auto world = MWBase::Environment::get().getWorld();
for (const auto& ptr : visitor.mObjects)
{
if (const auto object = mPhysics->getObject(ptr))
{
navigator->removeObject(DetourNavigator::ObjectId(object));
if (object->isAnimated())
mPhysics->remove(ptr);
}
else if (mPhysics->getActor(ptr))
{
navigator->removeAgent(world->getPathfindingHalfExtents(ptr));
mRendering.removeActorPath(ptr);
mPhysics->remove(ptr);
}
mPhysics->remove(ptr);
}
const auto cellX = (*iter)->getCell()->getGridX();
const auto cellY = (*iter)->getCell()->getGridY();
const auto cellX = cell->getCell()->getGridX();
const auto cellY = cell->getCell()->getGridY();
if ((*iter)->getCell()->isExterior())
if (cell->getCell()->isExterior())
{
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->removeObject(DetourNavigator::ObjectId(heightField));
mPhysics->removeHeightField(cellX, cellY);
}
if ((*iter)->getCell()->hasWater())
if (cell->getCell()->hasWater())
navigator->removeWater(osg::Vec2i(cellX, cellY));
if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*(*iter)->getCell()))
if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell->getCell()))
navigator->removePathgrid(*pathgrid);
const auto player = world->getPlayerPtr();
navigator->update(player.getRefData().getPosition().asVec3());
MWBase::Environment::get().getMechanicsManager()->drop (*iter);
MWBase::Environment::get().getMechanicsManager()->drop (cell);
mRendering.removeCell(*iter);
MWBase::Environment::get().getWindowManager()->removeCell(*iter);
mRendering.removeCell(cell);
MWBase::Environment::get().getWindowManager()->removeCell(cell);
MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter);
MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (cell);
MWBase::Environment::get().getSoundManager()->stopSound (*iter);
mActiveCells.erase(*iter);
MWBase::Environment::get().getSoundManager()->stopSound (cell);
mActiveCells.erase(cell);
}
void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test)
void Scene::activateCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test)
{
std::pair<CellStoreCollection::iterator, bool> result = mActiveCells.insert(cell);
assert(mActiveCells.find(cell) == mActiveCells.end());
assert(mInactiveCells.find(cell) != mInactiveCells.end());
mActiveCells.insert(cell);
if (test)
Log(Debug::Info) << "Testing cell " << cell->getCell()->getDescription();
else
Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription();
if(result.second)
const auto world = MWBase::Environment::get().getWorld();
const auto navigator = world->getNavigator();
const int cellX = cell->getCell()->getGridX();
const int cellY = cell->getCell()->getGridY();
if (!test && cell->getCell()->isExterior())
{
if (test)
Log(Debug::Info) << "Testing cell " << cell->getCell()->getDescription();
else
Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription();
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->addObject(DetourNavigator::ObjectId(heightField), *heightField->getShape(),
heightField->getCollisionObject()->getWorldTransform());
}
float verts = ESM::Land::LAND_SIZE;
float worldsize = ESM::Land::REAL_SIZE;
if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell->getCell()))
navigator->addPathgrid(*cell->getCell(), *pathgrid);
const auto world = MWBase::Environment::get().getWorld();
const auto navigator = world->getNavigator();
// register local scripts
// do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
const int cellX = cell->getCell()->getGridX();
const int cellY = cell->getCell()->getGridY();
if (respawn)
cell->respawn();
// Load terrain physics first...
if (!test && cell->getCell()->isExterior())
insertCell (*cell, loadingListener, false, test);
mRendering.addCell(cell);
if (!test)
{
MWBase::Environment::get().getWindowManager()->addCell(cell);
bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior();
float waterLevel = cell->getWaterLevel();
mRendering.setWaterEnabled(waterEnabled);
if (waterEnabled)
{
osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY);
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
if (data)
mPhysics->enableWater(waterLevel);
mRendering.setWaterHeight(waterLevel);
if (cell->getCell()->isExterior())
{
mPhysics->addHeightField (data->mHeights, cellX, cellY, worldsize / (verts-1), verts, data->mMinHeight, data->mMaxHeight, land.get());
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE,
cell->getWaterLevel(), heightField->getCollisionObject()->getWorldTransform());
}
else
{
static std::vector<float> defaultHeight;
defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT);
mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts, ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get());
navigator->addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),
cell->getWaterLevel(), btTransform::getIdentity());
}
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->addObject(DetourNavigator::ObjectId(heightField), *heightField->getShape(),
heightField->getCollisionObject()->getWorldTransform());
}
else
mPhysics->disableWater();
if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell->getCell()))
navigator->addPathgrid(*cell->getCell(), *pathgrid);
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// register local scripts
// do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
// By default the player is grounded, with the scene fully loaded, we validate and correct this.
if (player.mCell == cell) // Only run once, during initial cell load.
{
mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f);
}
if (respawn)
cell->respawn();
navigator->update(player.getRefData().getPosition().asVec3());
// ... then references. This is important for adjustPosition to work correctly.
insertCell (*cell, loadingListener, test);
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
mRendering.configureAmbient(cell->getCell());
}
mRendering.addCell(cell);
if (!test)
{
MWBase::Environment::get().getWindowManager()->addCell(cell);
bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior();
float waterLevel = cell->getWaterLevel();
mRendering.setWaterEnabled(waterEnabled);
if (waterEnabled)
{
mPhysics->enableWater(waterLevel);
mRendering.setWaterHeight(waterLevel);
mPreloader->notifyLoaded(cell);
}
if (cell->getCell()->isExterior())
{
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE,
cell->getWaterLevel(), heightField->getCollisionObject()->getWorldTransform());
}
else
{
navigator->addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),
cell->getWaterLevel(), btTransform::getIdentity());
}
}
else
mPhysics->disableWater();
void Scene::loadInactiveCell (CellStore *cell, Loading::Listener* loadingListener, bool test)
{
assert(mActiveCells.find(cell) == mActiveCells.end());
assert(mInactiveCells.find(cell) == mInactiveCells.end());
mInactiveCells.insert(cell);
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (test)
Log(Debug::Info) << "Testing inactive cell " << cell->getCell()->getDescription();
else
Log(Debug::Info) << "Loading inactive cell " << cell->getCell()->getDescription();
// By default the player is grounded, with the scene fully loaded, we validate and correct this.
if (player.mCell == cell) // Only run once, during initial cell load.
{
mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f);
}
if (!test && cell->getCell()->isExterior())
{
float verts = ESM::Land::LAND_SIZE;
float worldsize = ESM::Land::REAL_SIZE;
navigator->update(player.getRefData().getPosition().asVec3());
const int cellX = cell->getCell()->getGridX();
const int cellY = cell->getCell()->getGridY();
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
{
mRendering.configureAmbient(cell->getCell());
}
osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY);
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
if (data)
{
mPhysics->addHeightField (data->mHeights, cellX, cellY, worldsize / (verts-1), verts, data->mMinHeight, data->mMaxHeight, land.get());
}
else
{
static std::vector<float> defaultHeight;
defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT);
mPhysics->addHeightField (&defaultHeight[0], cellX, cellY, worldsize / (verts-1), verts, ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get());
}
}
mPreloader->notifyLoaded(cell);
insertCell (*cell, loadingListener, true, test);
}
void Scene::clear()
{
CellStoreCollection::iterator active = mActiveCells.begin();
while (active!=mActiveCells.end())
unloadCell (active++);
for (auto iter = mInactiveCells.begin(); iter!=mInactiveCells.end(); )
{
auto* cell = *iter++;
deactivateCell(cell);
unloadInactiveCell (cell);
}
assert(mActiveCells.empty());
assert(mInactiveCells.empty());
mCurrentCell = nullptr;
mPreloader->clear();
@ -512,20 +581,24 @@ namespace MWWorld
void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent)
{
CellStoreCollection::iterator active = mActiveCells.begin();
while (active!=mActiveCells.end())
for (auto iter = mInactiveCells.begin(); iter != mInactiveCells.end(); )
{
if ((*active)->getCell()->isExterior())
auto* cell = *iter++;
if (cell->getCell()->isExterior())
{
if (std::abs (playerCellX-(*active)->getCell()->getGridX())<=mHalfGridSize &&
std::abs (playerCellY-(*active)->getCell()->getGridY())<=mHalfGridSize)
{
// keep cells within the new grid
++active;
continue;
}
const auto dx = std::abs(playerCellX - cell->getCell()->getGridX());
const auto dy = std::abs(playerCellY - cell->getCell()->getGridY());
if (dx > mHalfGridSize || dy > mHalfGridSize)
deactivateCell(cell);
if (dx > mHalfGridSize+1 || dy > mHalfGridSize+1)
unloadInactiveCell(cell);
}
else
{
deactivateCell(cell);
unloadInactiveCell(cell);
}
unloadCell (active++);
}
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
@ -537,32 +610,24 @@ namespace MWWorld
mRendering.getPagedRefnums(newGrid, mPagedRefs);
std::size_t refsToLoad = 0;
std::vector<std::pair<int, int>> cellsPositionsToLoad;
// get the number of refs to load
for (int x = playerCellX - mHalfGridSize; x <= playerCellX + mHalfGridSize; ++x)
const auto cellsToLoad = [&playerCellX,&playerCellY,&refsToLoad](CellStoreCollection& collection, int range) -> std::vector<std::pair<int,int>>
{
for (int y = playerCellY - mHalfGridSize; y <= playerCellY + mHalfGridSize; ++y)
std::vector<std::pair<int, int>> cellsPositionsToLoad;
for (int x = playerCellX - range; x <= playerCellX + range; ++x)
{
CellStoreCollection::iterator iter = mActiveCells.begin();
while (iter!=mActiveCells.end())
for (int y = playerCellY - range; y <= playerCellY + range; ++y)
{
assert ((*iter)->getCell()->isExterior());
if (x==(*iter)->getCell()->getGridX() &&
y==(*iter)->getCell()->getGridY())
break;
++iter;
}
if (iter==mActiveCells.end())
{
refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count();
cellsPositionsToLoad.emplace_back(x, y);
if (!isCellInCollection(x, y, collection))
{
refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count();
cellsPositionsToLoad.emplace_back(x, y);
}
}
}
}
return cellsPositionsToLoad;
};
auto cellsPositionsToLoad = cellsToLoad(mActiveCells,mHalfGridSize);
auto cellsPositionsToLoadInactive = cellsToLoad(mInactiveCells,mHalfGridSize+1);
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
@ -585,30 +650,26 @@ namespace MWWorld
return getCellPositionPriority(lhs) < getCellPositionPriority(rhs);
});
std::sort(cellsPositionsToLoadInactive.begin(), cellsPositionsToLoadInactive.end(),
[&] (const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) {
return getCellPositionPriority(lhs) < getCellPositionPriority(rhs);
});
// Load cells
for (const auto& cellPosition : cellsPositionsToLoad)
for (const auto& [x,y] : cellsPositionsToLoadInactive)
{
const auto x = cellPosition.first;
const auto y = cellPosition.second;
CellStoreCollection::iterator iter = mActiveCells.begin();
while (iter != mActiveCells.end())
if (!isCellInCollection(x, y, mInactiveCells))
{
assert ((*iter)->getCell()->isExterior());
if (x == (*iter)->getCell()->getGridX() &&
y == (*iter)->getCell()->getGridY())
break;
++iter;
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y);
loadInactiveCell (cell, loadingListener);
}
if (iter == mActiveCells.end())
}
for (const auto& [x,y] : cellsPositionsToLoad)
{
if (!isCellInCollection(x, y, mActiveCells))
{
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y);
loadCell (cell, loadingListener, changeEvent);
activateCell (cell, loadingListener, changeEvent);
}
}
@ -643,7 +704,8 @@ namespace MWWorld
CellStoreCollection::iterator iter = mActiveCells.begin();
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY);
loadCell (cell, loadingListener, false, true);
loadInactiveCell (cell, loadingListener, true);
activateCell (cell, loadingListener, false, true);
iter = mActiveCells.begin();
while (iter != mActiveCells.end())
@ -651,7 +713,8 @@ namespace MWWorld
if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() &&
it->mData.mY == (*iter)->getCell()->getGridY())
{
unloadCell(iter, true);
deactivateCell(*iter, true);
unloadInactiveCell (*iter, true);
break;
}
@ -689,7 +752,8 @@ namespace MWWorld
loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")...");
CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName);
loadCell (cell, loadingListener, false, true);
loadInactiveCell (cell, loadingListener, true);
activateCell (cell, loadingListener, false, true);
CellStoreCollection::iterator iter = mActiveCells.begin();
while (iter != mActiveCells.end())
@ -698,7 +762,8 @@ namespace MWWorld
if (it->mName == (*iter)->getCell()->mName)
{
unloadCell(iter, true);
deactivateCell(*iter, true);
unloadInactiveCell (*iter, true);
break;
}
@ -820,15 +885,21 @@ namespace MWWorld
Log(Debug::Info) << "Changing to interior";
// unload
CellStoreCollection::iterator active = mActiveCells.begin();
while (active!=mActiveCells.end())
unloadCell (active++);
for (auto iter = mInactiveCells.begin(); iter!=mInactiveCells.end(); )
{
auto* cell = *iter++;
deactivateCell(cell);
unloadInactiveCell(cell);
}
assert(mActiveCells.empty());
assert(mInactiveCells.empty());
loadingListener->setProgressRange(cell->count());
// Load cell.
mPagedRefs.clear();
loadCell (cell, loadingListener, changeEvent);
loadInactiveCell (cell, loadingListener);
activateCell (cell, loadingListener, changeEvent);
changePlayerCell(cell, position, adjustPlayerPos);
@ -878,23 +949,26 @@ namespace MWWorld
mCellChanged = false;
}
void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test)
void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener, bool onlyObjects, bool test)
{
InsertVisitor insertVisitor (cell, *loadingListener, test);
InsertVisitor insertVisitor (cell, *loadingListener, onlyObjects, test);
cell.forEach (insertVisitor);
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs); });
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); });
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs, onlyObjects); });
if (!onlyObjects)
{
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); });
// do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order
PositionVisitor posVisitor;
cell.forEach (posVisitor);
// do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order
PositionVisitor posVisitor;
cell.forEach (posVisitor);
}
}
void Scene::addObjectToScene (const Ptr& ptr)
{
try
{
addObject(ptr, *mPhysics, mRendering, mPagedRefs);
addObject(ptr, *mPhysics, mRendering, mPagedRefs, false);
addObject(ptr, *mPhysics, mNavigator);
MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale());
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();

@ -64,13 +64,13 @@ namespace MWWorld
class Scene
{
public:
typedef std::set<CellStore *> CellStoreCollection;
using CellStoreCollection = std::set<CellStore *>;
private:
CellStore* mCurrentCell; // the cell the player is in
CellStoreCollection mActiveCells;
CellStoreCollection mInactiveCells;
bool mCellChanged;
MWPhysics::PhysicsSystem *mPhysics;
MWRender::RenderingManager& mRendering;
@ -91,7 +91,7 @@ namespace MWWorld
std::set<ESM::RefNum> mPagedRefs;
void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false);
void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool onlyObjects, bool test = false);
osg::Vec2i mCurrentGridCenter;
// Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center
@ -107,6 +107,11 @@ namespace MWWorld
osg::Vec4i gridCenterToBounds(const osg::Vec2i &centerCell) const;
osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const;
void unloadInactiveCell (CellStore* cell, bool test = false);
void deactivateCell (CellStore* cell, bool test = false);
void activateCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test = false);
void loadInactiveCell (CellStore *cell, Loading::Listener* loadingListener, bool test = false);
public:
Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics,
@ -118,10 +123,6 @@ namespace MWWorld
void preloadTerrain(const osg::Vec3f& pos, bool sync=false);
void reloadTerrain();
void unloadCell (CellStoreCollection::iterator iter, bool test = false);
void loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test = false);
void playerMoved (const osg::Vec3f& pos);
void changePlayerCell (CellStore* newCell, const ESM::Position& position, bool adjustPlayerPos);

@ -1421,7 +1421,7 @@ namespace MWWorld
mWorldScene->removeFromPagedRefs(ptr);
mRendering->rotateObject(ptr, rotate);
mPhysics->updateRotation(ptr);
mPhysics->updateRotation(ptr, rotate);
if (const auto object = mPhysics->getObject(ptr))
updateNavigatorObject(object);

@ -95,6 +95,11 @@ void BulletShape::setLocalScaling(const btVector3& scale)
mAvoidCollisionShape->setLocalScaling(scale);
}
bool BulletShape::isAnimated() const
{
return !mAnimatedShapes.empty();
}
osg::ref_ptr<BulletShapeInstance> BulletShape::makeInstance() const
{
osg::ref_ptr<BulletShapeInstance> instance (new BulletShapeInstance(this));

@ -52,6 +52,8 @@ namespace Resource
void setLocalScaling(const btVector3& scale);
bool isAnimated() const;
private:
void deleteShape(btCollisionShape* shape);

Loading…
Cancel
Save