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 4 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 #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 #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 #5453: Magic effect VFX are offset for creatures
Bug #5483: AutoCalc flag is not used to calculate spells cost Bug #5483: AutoCalc flag is not used to calculate spells cost
Bug #6101: Disarming trapped unlocked owned objects isn't considered a crime 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()) 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 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.

@ -17,16 +17,12 @@
namespace MWClass namespace MWClass
{ {
Actor::Actor() {}
Actor::~Actor() {}
void Actor::adjustPosition(const MWWorld::Ptr& ptr, bool force) const void Actor::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
{ {
MWBase::Environment::get().getWorld()->adjustPosition(ptr, force); 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()) if (!model.empty())
{ {

@ -15,16 +15,16 @@ namespace MWClass
{ {
protected: protected:
Actor(); Actor() = default;
public: public:
virtual ~Actor(); ~Actor() override = default;
void adjustPosition(const MWWorld::Ptr& ptr, bool force) const override; void adjustPosition(const MWWorld::Ptr& ptr, bool force) const override;
///< Adjust position to stand on ground. Must be called post model load ///< Adjust position to stand on ground. Must be called post model load
/// @param force do this even if the ptr is flying /// @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; bool useAnim() const override;
@ -46,8 +46,8 @@ namespace MWClass
float getCurrentSpeed(const MWWorld::Ptr& ptr) const override; float getCurrentSpeed(const MWWorld::Ptr& ptr) const override;
// not implemented // not implemented
Actor(const Actor&); Actor(const Actor&) = delete;
Actor& operator= (const Actor&); 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 std::string Apparatus::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string Armor::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string BodyPart::getName(const MWWorld::ConstPtr &ptr) const
{ {
return std::string(); return std::string();

@ -15,8 +15,6 @@ namespace MWClass
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string Book::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string Clothing::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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()) 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 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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()) 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 // Resume the door's opening/closing animation if it wasn't finished
if (ptr.getRefData().getCustomData()) 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; 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 std::string Ingredient::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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)); 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 = MWWorld::LiveCellRef<ESM::Light> *ref =
ptr.get<ESM::Light>(); ptr.get<ESM::Light>();
@ -41,7 +41,7 @@ namespace MWClass
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
if (!model.empty() && (ref->mBase->mData.mFlags & ESM::Light::Carry) == 0) 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)) 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, 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; 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 std::string Lockpick::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string Miscellaneous::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string Potion::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string Probe::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string Repair::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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()) 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 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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 std::string Weapon::getModel(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>(); 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; void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering ///< 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; std::string getName (const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \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(); updateScale();
if(!mRotationallyInvariant) if(!mRotationallyInvariant)
updateRotation(); setRotation(mPtr.getRefData().getBaseNode()->getAttitude());
updatePosition(); updatePosition();
addCollisionMask(getCollisionMask()); addCollisionMask(getCollisionMask());
@ -197,10 +197,10 @@ osg::Vec3f Actor::getPreviousPosition() const
return mPreviousPosition; return mPreviousPosition;
} }
void Actor::updateRotation () void Actor::setRotation(osg::Quat quat)
{ {
std::scoped_lock lock(mPositionMutex); std::scoped_lock lock(mPositionMutex);
mRotation = mPtr.getRefData().getBaseNode()->getAttitude(); mRotation = quat;
} }
bool Actor::isRotationallyInvariant() const bool Actor::isRotationallyInvariant() const

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

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

@ -26,12 +26,12 @@ namespace MWPhysics
class Object final : public PtrHolder class Object final : public PtrHolder
{ {
public: 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; ~Object() override;
const Resource::BulletShapeInstance* getShapeInstance() const; const Resource::BulletShapeInstance* getShapeInstance() const;
void setScale(float scale); void setScale(float scale);
void setRotation(const btQuaternion& quat); void setRotation(osg::Quat quat);
void setOrigin(const btVector3& vec); void setOrigin(const btVector3& vec);
void commitPositionChange(); void commitPositionChange();
btCollisionObject* getCollisionObject(); btCollisionObject* getCollisionObject();

@ -461,13 +461,18 @@ namespace MWPhysics
return heightField->second.get(); 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); osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);
if (!shapeInstance || !shapeInstance->getCollisionShape()) if (!shapeInstance || !shapeInstance->getCollisionShape())
return; 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); mObjects.emplace(ptr, obj);
if (obj->isAnimated()) if (obj->isAnimated())
@ -625,12 +630,12 @@ namespace MWPhysics
mTaskScheduler->updateSingleAabb(foundProjectile->second); 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); ObjectMap::iterator found = mObjects.find(ptr);
if (found != mObjects.end()) if (found != mObjects.end())
{ {
found->second->setRotation(Misc::Convert::toBullet(ptr.getRefData().getBaseNode()->getAttitude())); found->second->setRotation(rotate);
mTaskScheduler->updateSingleAabb(found->second); mTaskScheduler->updateSingleAabb(found->second);
return; return;
} }
@ -639,7 +644,7 @@ namespace MWPhysics
{ {
if (!foundActor->second->isRotationallyInvariant()) if (!foundActor->second->isRotationallyInvariant())
{ {
foundActor->second->updateRotation(); foundActor->second->setRotation(rotate);
mTaskScheduler->updateSingleAabb(foundActor->second); mTaskScheduler->updateSingleAabb(foundActor->second);
} }
return; return;

@ -122,7 +122,7 @@ namespace MWPhysics
void setWaterHeight(float height); void setWaterHeight(float height);
void disableWater(); 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); 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); 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 remove (const MWWorld::Ptr& ptr);
void updateScale (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 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); 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()) if (ptr.getRefData().getBaseNode())
{ {
ptr.getRefData().setBaseNode(nullptr); ptr.getRefData().setBaseNode(nullptr);
mObjects.push_back (ptr);
} }
mObjects.push_back (ptr);
return true; return true;
} }
}; };
struct ListObjectsVisitor
{
std::vector<MWWorld::Ptr> mObjects;
bool operator() (MWWorld::Ptr ptr)
{
mObjects.push_back (ptr);
return true;
}
};
} }
#endif #endif

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

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

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

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

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

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

Loading…
Cancel
Save