mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-28 19:21:04 +00:00
Merge branch 'master' into 'undead_intelligence'
# Conflicts: # CHANGELOG.md
This commit is contained in:
commit
908d196fee
@ -37,8 +37,10 @@
|
|||||||
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
|
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
|
||||||
Bug #6197: Infinite Casting Loop
|
Bug #6197: Infinite Casting Loop
|
||||||
Bug #6273: Respawning NPCs rotation is inconsistent
|
Bug #6273: Respawning NPCs rotation is inconsistent
|
||||||
|
Bug #6282: Laura craft doesn't follow the player character
|
||||||
Bug #6283: Avis Dorsey follows you after her death
|
Bug #6283: Avis Dorsey follows you after her death
|
||||||
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters
|
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters
|
||||||
|
Feature #890: OpenMW-CS: Column filtering
|
||||||
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
|
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
|
||||||
Feature #2780: A way to see current OpenMW version in the console
|
Feature #2780: A way to see current OpenMW version in the console
|
||||||
Feature #3616: Allow Zoom levels on the World Map
|
Feature #3616: Allow Zoom levels on the World Map
|
||||||
|
@ -71,7 +71,7 @@ opencs_units (view/world
|
|||||||
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
|
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
|
||||||
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
|
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
|
||||||
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
|
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
|
||||||
bodypartcreator landtexturecreator landcreator
|
bodypartcreator landtexturecreator landcreator tableheadermouseeventhandler
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units (view/world
|
opencs_units (view/world
|
||||||
|
@ -32,7 +32,6 @@ namespace CSVWidget
|
|||||||
|
|
||||||
namespace CSVWorld
|
namespace CSVWorld
|
||||||
{
|
{
|
||||||
class Table;
|
|
||||||
class TableBottomBox;
|
class TableBottomBox;
|
||||||
class CreatorFactoryBase;
|
class CreatorFactoryBase;
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "../../model/prefs/shortcut.hpp"
|
#include "../../model/prefs/shortcut.hpp"
|
||||||
|
|
||||||
#include "tableeditidaction.hpp"
|
#include "tableeditidaction.hpp"
|
||||||
|
#include "tableheadermouseeventhandler.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
|
||||||
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
|
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
|
||||||
@ -422,6 +423,8 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
|||||||
connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),
|
connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),
|
||||||
this, SLOT (settingChanged (const CSMPrefs::Setting *)));
|
this, SLOT (settingChanged (const CSMPrefs::Setting *)));
|
||||||
CSMPrefs::get()["ID Tables"].update();
|
CSMPrefs::get()["ID Tables"].update();
|
||||||
|
|
||||||
|
new TableHeaderMouseEventHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVWorld::Table::setEditLock (bool locked)
|
void CSVWorld::Table::setEditLock (bool locked)
|
||||||
|
64
apps/opencs/view/world/tableheadermouseeventhandler.cpp
Normal file
64
apps/opencs/view/world/tableheadermouseeventhandler.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include "tableheadermouseeventhandler.hpp"
|
||||||
|
#include "dragrecordtable.hpp"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QPoint>
|
||||||
|
|
||||||
|
namespace CSVWorld
|
||||||
|
{
|
||||||
|
|
||||||
|
TableHeaderMouseEventHandler::TableHeaderMouseEventHandler(DragRecordTable * parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, table(*parent)
|
||||||
|
, header(*table.horizontalHeader())
|
||||||
|
{
|
||||||
|
header.setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||||
|
connect(
|
||||||
|
&header, &QHeaderView::customContextMenuRequested, [=](const QPoint & position) { showContextMenu(position); });
|
||||||
|
|
||||||
|
header.viewport()->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TableHeaderMouseEventHandler::eventFilter(QObject * tableWatched, QEvent * event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::Type::MouseButtonPress)
|
||||||
|
{
|
||||||
|
auto & clickEvent = static_cast<QMouseEvent &>(*event);
|
||||||
|
if ((clickEvent.button() == Qt::MiddleButton))
|
||||||
|
{
|
||||||
|
const auto & index = table.indexAt(clickEvent.pos());
|
||||||
|
table.setColumnHidden(index.column(), true);
|
||||||
|
clickEvent.accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TableHeaderMouseEventHandler::showContextMenu(const QPoint & position)
|
||||||
|
{
|
||||||
|
auto & menu{createContextMenu()};
|
||||||
|
menu.popup(header.viewport()->mapToGlobal(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu & TableHeaderMouseEventHandler::createContextMenu()
|
||||||
|
{
|
||||||
|
auto * menu = new QMenu(this);
|
||||||
|
for (int i = 0; i < table.model()->columnCount(); ++i)
|
||||||
|
{
|
||||||
|
const auto & name = table.model()->headerData(i, Qt::Horizontal, Qt::DisplayRole);
|
||||||
|
QAction * action{new QAction(name.toString(), this)};
|
||||||
|
action->setCheckable(true);
|
||||||
|
action->setChecked(!table.isColumnHidden(i));
|
||||||
|
menu->addAction(action);
|
||||||
|
|
||||||
|
connect(action, &QAction::triggered, [=]() {
|
||||||
|
table.setColumnHidden(i, !action->isChecked());
|
||||||
|
action->setChecked(!action->isChecked());
|
||||||
|
action->toggle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return *menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CSVWorld
|
25
apps/opencs/view/world/tableheadermouseeventhandler.hpp
Normal file
25
apps/opencs/view/world/tableheadermouseeventhandler.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
namespace CSVWorld
|
||||||
|
{
|
||||||
|
class DragRecordTable;
|
||||||
|
|
||||||
|
class TableHeaderMouseEventHandler : public QWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TableHeaderMouseEventHandler(DragRecordTable * parent);
|
||||||
|
|
||||||
|
void showContextMenu(const QPoint &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DragRecordTable & table;
|
||||||
|
QHeaderView & header;
|
||||||
|
|
||||||
|
QMenu & createContextMenu();
|
||||||
|
bool eventFilter(QObject *, QEvent *) override;
|
||||||
|
|
||||||
|
}; // class TableHeaderMouseEventHandler
|
||||||
|
} // namespace CSVWorld
|
@ -624,7 +624,7 @@ namespace MWPhysics
|
|||||||
|
|
||||||
mTaskScheduler->convexSweepTest(projectile->getConvexShape(), from_, to_, resultCallback);
|
mTaskScheduler->convexSweepTest(projectile->getConvexShape(), from_, to_, resultCallback);
|
||||||
|
|
||||||
const auto newpos = projectile->isActive() ? position : Misc::Convert::toOsg(resultCallback.m_hitPointWorld);
|
const auto newpos = projectile->isActive() ? position : Misc::Convert::toOsg(projectile->getHitPosition());
|
||||||
projectile->setPosition(newpos);
|
projectile->setPosition(newpos);
|
||||||
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
||||||
}
|
}
|
||||||
@ -686,7 +686,7 @@ namespace MWPhysics
|
|||||||
mActors.emplace(ptr.mRef, std::move(actor));
|
mActors.emplace(ptr.mRef, std::move(actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater)
|
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);
|
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);
|
||||||
assert(shapeInstance);
|
assert(shapeInstance);
|
||||||
@ -694,7 +694,7 @@ namespace MWPhysics
|
|||||||
|
|
||||||
mProjectileId++;
|
mProjectileId++;
|
||||||
|
|
||||||
auto projectile = std::make_shared<Projectile>(caster, position, radius, canTraverseWater, mTaskScheduler.get(), this);
|
auto projectile = std::make_shared<Projectile>(caster, position, radius, mTaskScheduler.get(), this);
|
||||||
mProjectiles.emplace(mProjectileId, std::move(projectile));
|
mProjectiles.emplace(mProjectileId, std::move(projectile));
|
||||||
|
|
||||||
return mProjectileId;
|
return mProjectileId;
|
||||||
|
@ -131,7 +131,7 @@ namespace MWPhysics
|
|||||||
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, osg::Quat rotation, int collisionType = CollisionType_World, bool skipAnimated = false);
|
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);
|
||||||
void setCaster(int projectileId, const MWWorld::Ptr& caster);
|
void setCaster(int projectileId, const MWWorld::Ptr& caster);
|
||||||
void updateProjectile(const int projectileId, const osg::Vec3f &position) const;
|
void updateProjectile(const int projectileId, const osg::Vec3f &position) const;
|
||||||
void removeProjectile(const int projectileId);
|
void removeProjectile(const int projectileId);
|
||||||
|
@ -15,12 +15,10 @@
|
|||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
||||||
: mCanCrossWaterSurface(canCrossWaterSurface)
|
: mHitWater(false)
|
||||||
, mCrossedWaterSurface(false)
|
|
||||||
, mActive(true)
|
, mActive(true)
|
||||||
, mHitTarget(nullptr)
|
, mHitTarget(nullptr)
|
||||||
, mWaterHitPosition(std::nullopt)
|
|
||||||
, mPhysics(physicssystem)
|
, mPhysics(physicssystem)
|
||||||
, mTaskScheduler(scheduler)
|
, mTaskScheduler(scheduler)
|
||||||
{
|
{
|
||||||
@ -75,11 +73,6 @@ osg::Vec3f Projectile::getPosition() const
|
|||||||
return mPosition;
|
return mPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Projectile::canTraverseWater() const
|
|
||||||
{
|
|
||||||
return mCanCrossWaterSurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Projectile::hit(const btCollisionObject* target, btVector3 pos, btVector3 normal)
|
void Projectile::hit(const btCollisionObject* target, btVector3 pos, btVector3 normal)
|
||||||
{
|
{
|
||||||
bool active = true;
|
bool active = true;
|
||||||
@ -143,17 +136,4 @@ bool Projectile::isValidTarget(const btCollisionObject* target) const
|
|||||||
[target](const btCollisionObject* actor) { return target == actor; });
|
[target](const btCollisionObject* actor) { return target == actor; });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<btVector3> Projectile::getWaterHitPosition()
|
|
||||||
{
|
|
||||||
return std::exchange(mWaterHitPosition, std::nullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Projectile::setWaterHitPosition(btVector3 pos)
|
|
||||||
{
|
|
||||||
if (mCrossedWaterSurface)
|
|
||||||
return;
|
|
||||||
mCrossedWaterSurface = true;
|
|
||||||
mWaterHitPosition = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <LinearMath/btVector3.h>
|
#include <LinearMath/btVector3.h>
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ namespace MWPhysics
|
|||||||
class Projectile final : public PtrHolder
|
class Projectile final : public PtrHolder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
|
Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
|
||||||
~Projectile() override;
|
~Projectile() override;
|
||||||
|
|
||||||
btConvexShape* getConvexShape() const { return mConvexShape; }
|
btConvexShape* getConvexShape() const { return mConvexShape; }
|
||||||
@ -56,15 +55,25 @@ namespace MWPhysics
|
|||||||
return mCasterColObj;
|
return mCasterColObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canTraverseWater() const;
|
void setHitWater()
|
||||||
|
{
|
||||||
|
mHitWater = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getHitWater() const
|
||||||
|
{
|
||||||
|
return mHitWater;
|
||||||
|
}
|
||||||
|
|
||||||
void hit(const btCollisionObject* target, btVector3 pos, btVector3 normal);
|
void hit(const btCollisionObject* target, btVector3 pos, btVector3 normal);
|
||||||
|
|
||||||
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
|
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
|
||||||
bool isValidTarget(const btCollisionObject* target) const;
|
bool isValidTarget(const btCollisionObject* target) const;
|
||||||
|
|
||||||
std::optional<btVector3> getWaterHitPosition();
|
btVector3 getHitPosition() const
|
||||||
void setWaterHitPosition(btVector3 pos);
|
{
|
||||||
|
return mHitPosition;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -72,13 +81,11 @@ namespace MWPhysics
|
|||||||
btConvexShape* mConvexShape;
|
btConvexShape* mConvexShape;
|
||||||
|
|
||||||
bool mTransformUpdatePending;
|
bool mTransformUpdatePending;
|
||||||
bool mCanCrossWaterSurface;
|
bool mHitWater;
|
||||||
bool mCrossedWaterSurface;
|
|
||||||
std::atomic<bool> mActive;
|
std::atomic<bool> mActive;
|
||||||
MWWorld::Ptr mCaster;
|
MWWorld::Ptr mCaster;
|
||||||
const btCollisionObject* mCasterColObj;
|
const btCollisionObject* mCasterColObj;
|
||||||
const btCollisionObject* mHitTarget;
|
const btCollisionObject* mHitTarget;
|
||||||
std::optional<btVector3> mWaterHitPosition;
|
|
||||||
osg::Vec3f mPosition;
|
osg::Vec3f mPosition;
|
||||||
btVector3 mHitPosition;
|
btVector3 mHitPosition;
|
||||||
btVector3 mHitNormal;
|
btVector3 mHitNormal;
|
||||||
|
@ -49,9 +49,7 @@ namespace MWPhysics
|
|||||||
}
|
}
|
||||||
case CollisionType_Water:
|
case CollisionType_Water:
|
||||||
{
|
{
|
||||||
mProjectile->setWaterHitPosition(m_hitPointWorld);
|
mProjectile->setHitWater();
|
||||||
if (mProjectile->canTraverseWater())
|
|
||||||
return 1.f;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -651,7 +651,7 @@ namespace MWRender
|
|||||||
}
|
}
|
||||||
optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);
|
optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);
|
||||||
unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY;
|
unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY;
|
||||||
mSceneManager->shareState(mergeGroup);
|
|
||||||
optimizer.optimize(mergeGroup, options);
|
optimizer.optimize(mergeGroup, options);
|
||||||
|
|
||||||
group->addChild(mergeGroup);
|
group->addChild(mergeGroup);
|
||||||
|
@ -317,7 +317,7 @@ namespace MWWorld
|
|||||||
// in case there are multiple effects, the model is a dummy without geometry. Use the second effect for physics shape
|
// in case there are multiple effects, the model is a dummy without geometry. Use the second effect for physics shape
|
||||||
if (state.mIdMagic.size() > 1)
|
if (state.mIdMagic.size() > 1)
|
||||||
model = "meshes\\" + MWBase::Environment::get().getWorld()->getStore().get<ESM::Weapon>().find(state.mIdMagic[1])->mModel;
|
model = "meshes\\" + MWBase::Environment::get().getWorld()->getStore().get<ESM::Weapon>().find(state.mIdMagic[1])->mModel;
|
||||||
state.mProjectileId = mPhysics->addProjectile(caster, pos, model, true, false);
|
state.mProjectileId = mPhysics->addProjectile(caster, pos, model, true);
|
||||||
state.mToDelete = false;
|
state.mToDelete = false;
|
||||||
mMagicBolts.push_back(state);
|
mMagicBolts.push_back(state);
|
||||||
}
|
}
|
||||||
@ -342,7 +342,7 @@ namespace MWWorld
|
|||||||
if (!ptr.getClass().getEnchantment(ptr).empty())
|
if (!ptr.getClass().getEnchantment(ptr).empty())
|
||||||
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
|
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
|
||||||
|
|
||||||
state.mProjectileId = mPhysics->addProjectile(actor, pos, model, false, true);
|
state.mProjectileId = mPhysics->addProjectile(actor, pos, model, false);
|
||||||
state.mToDelete = false;
|
state.mToDelete = false;
|
||||||
mProjectiles.push_back(state);
|
mProjectiles.push_back(state);
|
||||||
}
|
}
|
||||||
@ -493,9 +493,6 @@ namespace MWWorld
|
|||||||
|
|
||||||
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||||
|
|
||||||
if (const auto hitWaterPos = projectile->getWaterHitPosition())
|
|
||||||
mRendering->emitWaterRipple(Misc::Convert::toOsg(*hitWaterPos));
|
|
||||||
|
|
||||||
const auto pos = projectile->getPosition();
|
const auto pos = projectile->getPosition();
|
||||||
projectileState.mNode->setPosition(pos);
|
projectileState.mNode->setPosition(pos);
|
||||||
|
|
||||||
@ -519,6 +516,8 @@ namespace MWWorld
|
|||||||
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))
|
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))
|
||||||
bow = *invIt;
|
bow = *invIt;
|
||||||
}
|
}
|
||||||
|
if (projectile->getHitWater())
|
||||||
|
mRendering->emitWaterRipple(pos);
|
||||||
|
|
||||||
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
||||||
projectileState.mToDelete = true;
|
projectileState.mToDelete = true;
|
||||||
@ -663,7 +662,7 @@ namespace MWWorld
|
|||||||
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
||||||
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
|
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
|
||||||
|
|
||||||
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, false, true);
|
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, false);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
@ -716,7 +715,7 @@ namespace MWWorld
|
|||||||
|
|
||||||
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
||||||
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);
|
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);
|
||||||
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, true, false);
|
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, true);
|
||||||
|
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
for (const std::string &soundid : state.mSoundIds)
|
for (const std::string &soundid : state.mSoundIds)
|
||||||
|
@ -200,13 +200,12 @@ namespace
|
|||||||
struct InsertVisitor
|
struct InsertVisitor
|
||||||
{
|
{
|
||||||
MWWorld::CellStore& mCell;
|
MWWorld::CellStore& mCell;
|
||||||
Loading::Listener& mLoadingListener;
|
Loading::Listener* mLoadingListener;
|
||||||
bool mOnlyObjects;
|
bool mOnlyObjects;
|
||||||
bool mTest;
|
|
||||||
|
|
||||||
std::vector<MWWorld::Ptr> mToInsert;
|
std::vector<MWWorld::Ptr> mToInsert;
|
||||||
|
|
||||||
InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool onlyObjects, bool test);
|
InsertVisitor (MWWorld::CellStore& cell, Loading::Listener* loadingListener, bool onlyObjects);
|
||||||
|
|
||||||
bool operator() (const MWWorld::Ptr& ptr);
|
bool operator() (const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
@ -214,8 +213,8 @@ namespace
|
|||||||
void insert(AddObject&& addObject);
|
void insert(AddObject&& addObject);
|
||||||
};
|
};
|
||||||
|
|
||||||
InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool onlyObjects, bool test)
|
InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener* loadingListener, bool onlyObjects)
|
||||||
: mCell (cell), mLoadingListener (loadingListener), mOnlyObjects(onlyObjects), mTest(test)
|
: mCell(cell), mLoadingListener(loadingListener), mOnlyObjects(onlyObjects)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool InsertVisitor::operator() (const MWWorld::Ptr& ptr)
|
bool InsertVisitor::operator() (const MWWorld::Ptr& ptr)
|
||||||
@ -244,8 +243,8 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mTest)
|
if (mLoadingListener != nullptr)
|
||||||
mLoadingListener.increaseProgress (1);
|
mLoadingListener->increaseProgress(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,12 +324,12 @@ namespace MWWorld
|
|||||||
mRendering.update (duration, paused);
|
mRendering.update (duration, paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::unloadInactiveCell (CellStore* cell, bool test)
|
void Scene::unloadInactiveCell (CellStore* cell)
|
||||||
{
|
{
|
||||||
assert(mActiveCells.find(cell) == mActiveCells.end());
|
assert(mActiveCells.find(cell) == mActiveCells.end());
|
||||||
assert(mInactiveCells.find(cell) != mInactiveCells.end());
|
assert(mInactiveCells.find(cell) != mInactiveCells.end());
|
||||||
if (!test)
|
|
||||||
Log(Debug::Info) << "Unloading cell " << cell->getCell()->getDescription();
|
Log(Debug::Info) << "Unloading cell " << cell->getCell()->getDescription();
|
||||||
|
|
||||||
ListObjectsVisitor visitor;
|
ListObjectsVisitor visitor;
|
||||||
|
|
||||||
@ -351,13 +350,13 @@ namespace MWWorld
|
|||||||
mInactiveCells.erase(cell);
|
mInactiveCells.erase(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::deactivateCell(CellStore* cell, bool test)
|
void Scene::deactivateCell(CellStore* cell)
|
||||||
{
|
{
|
||||||
assert(mInactiveCells.find(cell) != mInactiveCells.end());
|
assert(mInactiveCells.find(cell) != mInactiveCells.end());
|
||||||
if (mActiveCells.find(cell) == mActiveCells.end())
|
if (mActiveCells.find(cell) == mActiveCells.end())
|
||||||
return;
|
return;
|
||||||
if (!test)
|
|
||||||
Log(Debug::Info) << "Deactivate cell " << cell->getCell()->getDescription();
|
Log(Debug::Info) << "Deactivate cell " << cell->getCell()->getDescription();
|
||||||
|
|
||||||
ListAndResetObjectsVisitor visitor;
|
ListAndResetObjectsVisitor visitor;
|
||||||
|
|
||||||
@ -409,7 +408,7 @@ namespace MWWorld
|
|||||||
mActiveCells.erase(cell);
|
mActiveCells.erase(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::activateCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test)
|
void Scene::activateCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn)
|
||||||
{
|
{
|
||||||
using DetourNavigator::HeightfieldShape;
|
using DetourNavigator::HeightfieldShape;
|
||||||
|
|
||||||
@ -417,17 +416,14 @@ namespace MWWorld
|
|||||||
assert(mInactiveCells.find(cell) != mInactiveCells.end());
|
assert(mInactiveCells.find(cell) != mInactiveCells.end());
|
||||||
mActiveCells.insert(cell);
|
mActiveCells.insert(cell);
|
||||||
|
|
||||||
if (test)
|
Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription();
|
||||||
Log(Debug::Info) << "Testing cell " << cell->getCell()->getDescription();
|
|
||||||
else
|
|
||||||
Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription();
|
|
||||||
|
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
const auto world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
const int cellX = cell->getCell()->getGridX();
|
const int cellX = cell->getCell()->getGridX();
|
||||||
const int cellY = cell->getCell()->getGridY();
|
const int cellY = cell->getCell()->getGridY();
|
||||||
|
|
||||||
if (!test && cell->getCell()->isExterior())
|
if (cell->getCell()->isExterior())
|
||||||
{
|
{
|
||||||
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
|
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
|
||||||
{
|
{
|
||||||
@ -466,69 +462,64 @@ namespace MWWorld
|
|||||||
if (respawn)
|
if (respawn)
|
||||||
cell->respawn();
|
cell->respawn();
|
||||||
|
|
||||||
insertCell (*cell, loadingListener, false, test);
|
insertCell(*cell, loadingListener, false);
|
||||||
|
|
||||||
mRendering.addCell(cell);
|
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);
|
|
||||||
|
|
||||||
if (cell->getCell()->isExterior())
|
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())
|
||||||
|
{
|
||||||
|
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
|
||||||
{
|
{
|
||||||
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
|
const btTransform& transform =heightField->getCollisionObject()->getWorldTransform();
|
||||||
{
|
mNavigator.addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE,
|
||||||
const btTransform& transform =heightField->getCollisionObject()->getWorldTransform();
|
osg::Vec3f(static_cast<float>(transform.getOrigin().x()),
|
||||||
mNavigator.addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE,
|
static_cast<float>(transform.getOrigin().y()),
|
||||||
osg::Vec3f(static_cast<float>(transform.getOrigin().x()),
|
waterLevel));
|
||||||
static_cast<float>(transform.getOrigin().y()),
|
|
||||||
waterLevel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mNavigator.addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),
|
|
||||||
osg::Vec3f(0, 0, waterLevel));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mPhysics->disableWater();
|
|
||||||
|
|
||||||
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
|
||||||
|
|
||||||
// The player is loaded before the scene and by default it 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);
|
mNavigator.addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),
|
||||||
|
osg::Vec3f(0, 0, waterLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
mNavigator.update(player.getRefData().getPosition().asVec3());
|
|
||||||
|
|
||||||
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
|
|
||||||
mRendering.configureAmbient(cell->getCell());
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
mPhysics->disableWater();
|
||||||
|
|
||||||
|
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
|
|
||||||
|
// The player is loaded before the scene and by default it 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
mNavigator.update(player.getRefData().getPosition().asVec3());
|
||||||
|
|
||||||
|
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
|
||||||
|
mRendering.configureAmbient(cell->getCell());
|
||||||
|
|
||||||
mPreloader->notifyLoaded(cell);
|
mPreloader->notifyLoaded(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::loadInactiveCell (CellStore *cell, Loading::Listener* loadingListener, bool test)
|
void Scene::loadInactiveCell(CellStore *cell, Loading::Listener* loadingListener)
|
||||||
{
|
{
|
||||||
assert(mActiveCells.find(cell) == mActiveCells.end());
|
assert(mActiveCells.find(cell) == mActiveCells.end());
|
||||||
assert(mInactiveCells.find(cell) == mInactiveCells.end());
|
assert(mInactiveCells.find(cell) == mInactiveCells.end());
|
||||||
mInactiveCells.insert(cell);
|
mInactiveCells.insert(cell);
|
||||||
|
|
||||||
if (test)
|
Log(Debug::Info) << "Loading inactive cell " << cell->getCell()->getDescription();
|
||||||
Log(Debug::Info) << "Testing inactive cell " << cell->getCell()->getDescription();
|
|
||||||
else
|
|
||||||
Log(Debug::Info) << "Loading inactive cell " << cell->getCell()->getDescription();
|
|
||||||
|
|
||||||
if (!test && cell->getCell()->isExterior())
|
if (cell->getCell()->isExterior())
|
||||||
{
|
{
|
||||||
float verts = ESM::Land::LAND_SIZE;
|
float verts = ESM::Land::LAND_SIZE;
|
||||||
float worldsize = ESM::Land::REAL_SIZE;
|
float worldsize = ESM::Land::REAL_SIZE;
|
||||||
@ -550,7 +541,7 @@ namespace MWWorld
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insertCell (*cell, loadingListener, true, test);
|
insertCell(*cell, loadingListener, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::clear()
|
void Scene::clear()
|
||||||
@ -746,8 +737,8 @@ namespace MWWorld
|
|||||||
loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")...");
|
loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")...");
|
||||||
|
|
||||||
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);
|
||||||
loadInactiveCell (cell, loadingListener, true);
|
loadInactiveCell(cell, nullptr);
|
||||||
activateCell (cell, loadingListener, false, true);
|
activateCell(cell, nullptr, false);
|
||||||
|
|
||||||
auto iter = mInactiveCells.begin();
|
auto iter = mInactiveCells.begin();
|
||||||
while (iter != mInactiveCells.end())
|
while (iter != mInactiveCells.end())
|
||||||
@ -755,8 +746,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())
|
||||||
{
|
{
|
||||||
deactivateCell(*iter, true);
|
deactivateCell(*iter);
|
||||||
unloadInactiveCell (*iter, true);
|
unloadInactiveCell(*iter);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,8 +785,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);
|
||||||
loadInactiveCell (cell, loadingListener, true);
|
loadInactiveCell(cell, nullptr);
|
||||||
activateCell (cell, loadingListener, false, true);
|
activateCell(cell, nullptr, false);
|
||||||
|
|
||||||
auto iter = mInactiveCells.begin();
|
auto iter = mInactiveCells.begin();
|
||||||
while (iter != mInactiveCells.end())
|
while (iter != mInactiveCells.end())
|
||||||
@ -804,8 +795,8 @@ namespace MWWorld
|
|||||||
|
|
||||||
if (it->mName == (*iter)->getCell()->mName)
|
if (it->mName == (*iter)->getCell()->mName)
|
||||||
{
|
{
|
||||||
deactivateCell(*iter, true);
|
deactivateCell(*iter);
|
||||||
unloadInactiveCell (*iter, true);
|
unloadInactiveCell(*iter);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -988,9 +979,9 @@ namespace MWWorld
|
|||||||
mCellChanged = false;
|
mCellChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener, bool onlyObjects, bool test)
|
void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener, bool onlyObjects)
|
||||||
{
|
{
|
||||||
InsertVisitor insertVisitor (cell, *loadingListener, onlyObjects, test);
|
InsertVisitor insertVisitor(cell, loadingListener, onlyObjects);
|
||||||
cell.forEach (insertVisitor);
|
cell.forEach (insertVisitor);
|
||||||
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs, onlyObjects); });
|
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs, onlyObjects); });
|
||||||
if (!onlyObjects)
|
if (!onlyObjects)
|
||||||
|
@ -100,7 +100,7 @@ namespace MWWorld
|
|||||||
|
|
||||||
std::vector<osg::ref_ptr<SceneUtil::WorkItem>> mWorkItems;
|
std::vector<osg::ref_ptr<SceneUtil::WorkItem>> mWorkItems;
|
||||||
|
|
||||||
void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool onlyObjects, bool test = false);
|
void insertCell(CellStore &cell, Loading::Listener* loadingListener, bool onlyObjects);
|
||||||
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
|
||||||
@ -116,10 +116,10 @@ namespace MWWorld
|
|||||||
osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const;
|
osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) 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 unloadInactiveCell(CellStore* cell);
|
||||||
void deactivateCell (CellStore* cell, bool test = false);
|
void deactivateCell(CellStore* cell);
|
||||||
void activateCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test = false);
|
void activateCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn);
|
||||||
void loadInactiveCell (CellStore *cell, Loading::Listener* loadingListener, bool test = false);
|
void loadInactiveCell(CellStore *cell, Loading::Listener* loadingListener);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <components/esm/cellref.hpp>
|
#include <components/esm/cellref.hpp>
|
||||||
|
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
|
#include <components/misc/mathutil.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/misc/convert.hpp>
|
#include <components/misc/convert.hpp>
|
||||||
@ -76,21 +77,6 @@
|
|||||||
#include "contentloader.hpp"
|
#include "contentloader.hpp"
|
||||||
#include "esmloader.hpp"
|
#include "esmloader.hpp"
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
// Wraps a value to (-PI, PI]
|
|
||||||
void wrap(float& rad)
|
|
||||||
{
|
|
||||||
const float pi = static_cast<float>(osg::PI);
|
|
||||||
if (rad>0)
|
|
||||||
rad = std::fmod(rad+pi, 2.0f*pi)-pi;
|
|
||||||
else
|
|
||||||
rad = std::fmod(rad-pi, 2.0f*pi)+pi;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
struct GameContentLoader : public ContentLoader
|
struct GameContentLoader : public ContentLoader
|
||||||
@ -1290,8 +1276,6 @@ namespace MWWorld
|
|||||||
|
|
||||||
void World::rotateObject(const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags)
|
void World::rotateObject(const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags)
|
||||||
{
|
{
|
||||||
const float pi = static_cast<float>(osg::PI);
|
|
||||||
|
|
||||||
ESM::Position pos = ptr.getRefData().getPosition();
|
ESM::Position pos = ptr.getRefData().getPosition();
|
||||||
float *objRot = pos.rot;
|
float *objRot = pos.rot;
|
||||||
if (flags & MWBase::RotationFlag_adjust)
|
if (flags & MWBase::RotationFlag_adjust)
|
||||||
@ -1313,13 +1297,9 @@ namespace MWWorld
|
|||||||
* currently it's done so for rotating the camera, which needs
|
* currently it's done so for rotating the camera, which needs
|
||||||
* clamping.
|
* clamping.
|
||||||
*/
|
*/
|
||||||
const float half_pi = pi/2.f;
|
objRot[0] = osg::clampBetween(objRot[0], -osg::PIf / 2, osg::PIf / 2);
|
||||||
|
objRot[1] = Misc::normalizeAngle(objRot[1]);
|
||||||
if(objRot[0] < -half_pi) objRot[0] = -half_pi;
|
objRot[2] = Misc::normalizeAngle(objRot[2]);
|
||||||
else if(objRot[0] > half_pi) objRot[0] = half_pi;
|
|
||||||
|
|
||||||
wrap(objRot[1]);
|
|
||||||
wrap(objRot[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr.getRefData().setPosition(pos);
|
ptr.getRefData().setPosition(pos);
|
||||||
@ -3145,6 +3125,7 @@ namespace MWWorld
|
|||||||
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos);
|
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos);
|
||||||
if (underwater)
|
if (underwater)
|
||||||
{
|
{
|
||||||
|
MWMechanics::projectileHit(actor, Ptr(), bow, projectile, worldPos, attackStrength);
|
||||||
mRendering->emitWaterRipple(worldPos);
|
mRendering->emitWaterRipple(worldPos);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -164,8 +164,6 @@ namespace Compiler
|
|||||||
std::string value;
|
std::string value;
|
||||||
c.appendTo(value);
|
c.appendTo(value);
|
||||||
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
while (get (c))
|
while (get (c))
|
||||||
{
|
{
|
||||||
if (c.isDigit())
|
if (c.isDigit())
|
||||||
@ -174,16 +172,11 @@ namespace Compiler
|
|||||||
}
|
}
|
||||||
else if (!c.isMinusSign() && isStringCharacter (c))
|
else if (!c.isMinusSign() && isStringCharacter (c))
|
||||||
{
|
{
|
||||||
error = true;
|
/// workaround that allows names to begin with digits
|
||||||
c.appendTo(value);
|
return scanName(c, parser, cont, value);
|
||||||
}
|
}
|
||||||
else if (c=='.')
|
else if (c=='.')
|
||||||
{
|
{
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
putback (c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return scanFloat (value, parser, cont);
|
return scanFloat (value, parser, cont);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -193,17 +186,6 @@ namespace Compiler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
/// workaround that allows names to begin with digits
|
|
||||||
/// \todo disable
|
|
||||||
TokenLoc loc (mLoc);
|
|
||||||
mLoc.mLiteral.clear();
|
|
||||||
cont = parser.parseName (value, loc, *this);
|
|
||||||
return true;
|
|
||||||
// return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenLoc loc (mLoc);
|
TokenLoc loc (mLoc);
|
||||||
mLoc.mLiteral.clear();
|
mLoc.mLiteral.clear();
|
||||||
|
|
||||||
@ -268,9 +250,8 @@ namespace Compiler
|
|||||||
nullptr
|
nullptr
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Scanner::scanName (MultiChar& c, Parser& parser, bool& cont)
|
bool Scanner::scanName (MultiChar& c, Parser& parser, bool& cont, std::string name)
|
||||||
{
|
{
|
||||||
std::string name;
|
|
||||||
c.appendTo(name);
|
c.appendTo(name);
|
||||||
|
|
||||||
if (!scanName (name))
|
if (!scanName (name))
|
||||||
|
@ -236,7 +236,7 @@ namespace Compiler
|
|||||||
|
|
||||||
bool scanFloat (const std::string& intValue, Parser& parser, bool& cont);
|
bool scanFloat (const std::string& intValue, Parser& parser, bool& cont);
|
||||||
|
|
||||||
bool scanName (MultiChar& c, Parser& parser, bool& cont);
|
bool scanName (MultiChar& c, Parser& parser, bool& cont, std::string name = {});
|
||||||
|
|
||||||
/// \param name May contain the start of the name (one or more characters)
|
/// \param name May contain the start of the name (one or more characters)
|
||||||
bool scanName (std::string& name);
|
bool scanName (std::string& name);
|
||||||
|
@ -659,22 +659,18 @@ namespace Resource
|
|||||||
osg::ref_ptr<Shader::ShaderVisitor> shaderVisitor (createShaderVisitor());
|
osg::ref_ptr<Shader::ShaderVisitor> shaderVisitor (createShaderVisitor());
|
||||||
loaded->accept(*shaderVisitor);
|
loaded->accept(*shaderVisitor);
|
||||||
|
|
||||||
// share state
|
|
||||||
// do this before optimizing so the optimizer will be able to combine nodes more aggressively
|
|
||||||
// note, because StateSets will be shared at this point, StateSets can not be modified inside the optimizer
|
|
||||||
mSharedStateMutex.lock();
|
|
||||||
mSharedStateManager->share(loaded.get());
|
|
||||||
mSharedStateMutex.unlock();
|
|
||||||
|
|
||||||
if (canOptimize(normalized))
|
if (canOptimize(normalized))
|
||||||
{
|
{
|
||||||
SceneUtil::Optimizer optimizer;
|
SceneUtil::Optimizer optimizer;
|
||||||
|
optimizer.setSharedStateManager(mSharedStateManager, &mSharedStateMutex);
|
||||||
optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);
|
optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);
|
||||||
|
|
||||||
static const unsigned int options = getOptimizationOptions();
|
static const unsigned int options = getOptimizationOptions()|SceneUtil::Optimizer::SHARE_DUPLICATE_STATE;
|
||||||
|
|
||||||
optimizer.optimize(loaded, options);
|
optimizer.optimize(loaded, options);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
shareState(loaded);
|
||||||
|
|
||||||
if (compile && mIncrementalCompileOperation)
|
if (compile && mIncrementalCompileOperation)
|
||||||
mIncrementalCompileOperation->add(loaded);
|
mIncrementalCompileOperation->add(loaded);
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include <osg/io_utils>
|
#include <osg/io_utils>
|
||||||
#include <osg/Depth>
|
#include <osg/Depth>
|
||||||
|
|
||||||
|
#include <osgDB/SharedStateManager>
|
||||||
|
|
||||||
#include <osgUtil/TransformAttributeFunctor>
|
#include <osgUtil/TransformAttributeFunctor>
|
||||||
#include <osgUtil/Statistics>
|
#include <osgUtil/Statistics>
|
||||||
#include <osgUtil/MeshOptimizers>
|
#include <osgUtil/MeshOptimizers>
|
||||||
@ -84,6 +86,13 @@ void Optimizer::optimize(osg::Node* node, unsigned int options)
|
|||||||
cstv.removeTransforms(node);
|
cstv.removeTransforms(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options & SHARE_DUPLICATE_STATE && _sharedStateManager)
|
||||||
|
{
|
||||||
|
if (_sharedStateMutex) _sharedStateMutex->lock();
|
||||||
|
_sharedStateManager->share(node);
|
||||||
|
if (_sharedStateMutex) _sharedStateMutex->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
if (options & REMOVE_REDUNDANT_NODES)
|
if (options & REMOVE_REDUNDANT_NODES)
|
||||||
{
|
{
|
||||||
OSG_INFO<<"Optimizer::optimize() doing REMOVE_REDUNDANT_NODES"<<std::endl;
|
OSG_INFO<<"Optimizer::optimize() doing REMOVE_REDUNDANT_NODES"<<std::endl;
|
||||||
@ -741,7 +750,8 @@ bool Optimizer::CombineStaticTransformsVisitor::removeTransforms(osg::Node* node
|
|||||||
if (transform->getNumChildren()==1 &&
|
if (transform->getNumChildren()==1 &&
|
||||||
transform->getChild(0)->asTransform()!=0 &&
|
transform->getChild(0)->asTransform()!=0 &&
|
||||||
transform->getChild(0)->asTransform()->asMatrixTransform()!=0 &&
|
transform->getChild(0)->asTransform()->asMatrixTransform()!=0 &&
|
||||||
transform->getChild(0)->asTransform()->getDataVariance()==osg::Object::STATIC)
|
(!transform->getChild(0)->getStateSet() || transform->getChild(0)->getStateSet()->referenceCount()==1) &&
|
||||||
|
transform->getChild(0)->getDataVariance()==osg::Object::STATIC)
|
||||||
{
|
{
|
||||||
// now combine with its child.
|
// now combine with its child.
|
||||||
osg::MatrixTransform* child = transform->getChild(0)->asTransform()->asMatrixTransform();
|
osg::MatrixTransform* child = transform->getChild(0)->asTransform()->asMatrixTransform();
|
||||||
|
@ -25,6 +25,12 @@
|
|||||||
//#include <osgUtil/Export>
|
//#include <osgUtil/Export>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace osgDB
|
||||||
|
{
|
||||||
|
class SharedStateManager;
|
||||||
|
}
|
||||||
|
|
||||||
//namespace osgUtil {
|
//namespace osgUtil {
|
||||||
namespace SceneUtil {
|
namespace SceneUtil {
|
||||||
@ -65,7 +71,7 @@ class Optimizer
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Optimizer() : _mergeAlphaBlending(false) {}
|
Optimizer() : _mergeAlphaBlending(false), _sharedStateManager(nullptr), _sharedStateMutex(nullptr) {}
|
||||||
virtual ~Optimizer() {}
|
virtual ~Optimizer() {}
|
||||||
|
|
||||||
enum OptimizationOptions
|
enum OptimizationOptions
|
||||||
@ -121,6 +127,8 @@ class Optimizer
|
|||||||
void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; }
|
void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; }
|
||||||
void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; }
|
void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; }
|
||||||
|
|
||||||
|
void setSharedStateManager(osgDB::SharedStateManager* sharedStateManager, std::mutex* sharedStateMutex) { _sharedStateMutex = sharedStateMutex; _sharedStateManager = sharedStateManager; }
|
||||||
|
|
||||||
/** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/
|
/** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
@ -258,6 +266,9 @@ class Optimizer
|
|||||||
osg::Vec3f _viewPoint;
|
osg::Vec3f _viewPoint;
|
||||||
bool _mergeAlphaBlending;
|
bool _mergeAlphaBlending;
|
||||||
|
|
||||||
|
osgDB::SharedStateManager* _sharedStateManager;
|
||||||
|
mutable std::mutex* _sharedStateMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Flatten Static Transform nodes by applying their transform to the
|
/** Flatten Static Transform nodes by applying their transform to the
|
||||||
|
@ -38,27 +38,41 @@ import termtables
|
|||||||
@click.option('--timeseries_sum', is_flag=True,
|
@click.option('--timeseries_sum', is_flag=True,
|
||||||
help='Add a graph to timeseries for a sum per frame of all given timeseries metrics.')
|
help='Add a graph to timeseries for a sum per frame of all given timeseries metrics.')
|
||||||
@click.option('--commulative_timeseries_sum', is_flag=True,
|
@click.option('--commulative_timeseries_sum', is_flag=True,
|
||||||
help='Add a graph to timeseries for a sum per frame of all given commulative timeseries.')
|
help='Add a graph to timeseries for a sum per frame of all given commulative timeseries.')
|
||||||
@click.option('--stats_sum', is_flag=True,
|
@click.option('--stats_sum', is_flag=True,
|
||||||
help='Add a row to stats table for a sum per frame of all given stats metrics.')
|
help='Add a row to stats table for a sum per frame of all given stats metrics.')
|
||||||
@click.option('--begin_frame', type=int, default=0,
|
@click.option('--begin_frame', type=int, default=0,
|
||||||
help='Start processing from this frame.')
|
help='Start processing from this frame.')
|
||||||
@click.option('--end_frame', type=int, default=sys.maxsize,
|
@click.option('--end_frame', type=int, default=sys.maxsize,
|
||||||
help='End processing at this frame.')
|
help='End processing at this frame.')
|
||||||
|
@click.option('--frame_number_name', type=str, default='FrameNumber',
|
||||||
|
help='Frame number metric name.')
|
||||||
|
@click.option('--hist_threshold', type=str, multiple=True,
|
||||||
|
help='Show a histogram for given metric only for frames with threshold_name metric over threshold_value.')
|
||||||
|
@click.option('--threshold_name', type=str, default='Frame duration',
|
||||||
|
help='Frame duration metric name.')
|
||||||
|
@click.option('--threshold_value', type=float, default=1.05/60,
|
||||||
|
help='Threshold for hist_over.')
|
||||||
@click.argument('path', type=click.Path(), nargs=-1)
|
@click.argument('path', type=click.Path(), nargs=-1)
|
||||||
def main(print_keys, timeseries, hist, hist_ratio, stdev_hist, plot, stats,
|
def main(print_keys, timeseries, hist, hist_ratio, stdev_hist, plot, stats,
|
||||||
timeseries_sum, stats_sum, begin_frame, end_frame, path,
|
timeseries_sum, stats_sum, begin_frame, end_frame, path,
|
||||||
commulative_timeseries, commulative_timeseries_sum):
|
commulative_timeseries, commulative_timeseries_sum, frame_number_name,
|
||||||
|
hist_threshold, threshold_name, threshold_value):
|
||||||
sources = {v: list(read_data(v)) for v in path} if path else {'stdin': list(read_data(None))}
|
sources = {v: list(read_data(v)) for v in path} if path else {'stdin': list(read_data(None))}
|
||||||
keys = collect_unique_keys(sources)
|
keys = collect_unique_keys(sources)
|
||||||
frames = collect_per_frame(sources=sources, keys=keys, begin_frame=begin_frame, end_frame=end_frame)
|
frames, begin_frame, end_frame = collect_per_frame(
|
||||||
|
sources=sources, keys=keys, begin_frame=begin_frame,
|
||||||
|
end_frame=end_frame, frame_number_name=frame_number_name,
|
||||||
|
)
|
||||||
if print_keys:
|
if print_keys:
|
||||||
for v in keys:
|
for v in keys:
|
||||||
print(v)
|
print(v)
|
||||||
if timeseries:
|
if timeseries:
|
||||||
draw_timeseries(sources=frames, keys=timeseries, add_sum=timeseries_sum)
|
draw_timeseries(sources=frames, keys=timeseries, add_sum=timeseries_sum,
|
||||||
|
begin_frame=begin_frame, end_frame=end_frame)
|
||||||
if commulative_timeseries:
|
if commulative_timeseries:
|
||||||
draw_commulative_timeseries(sources=frames, keys=commulative_timeseries, add_sum=commulative_timeseries_sum)
|
draw_commulative_timeseries(sources=frames, keys=commulative_timeseries, add_sum=commulative_timeseries_sum,
|
||||||
|
begin_frame=begin_frame, end_frame=end_frame)
|
||||||
if hist:
|
if hist:
|
||||||
draw_hists(sources=frames, keys=hist)
|
draw_hists(sources=frames, keys=hist)
|
||||||
if hist_ratio:
|
if hist_ratio:
|
||||||
@ -69,6 +83,9 @@ def main(print_keys, timeseries, hist, hist_ratio, stdev_hist, plot, stats,
|
|||||||
draw_plots(sources=frames, plots=plot)
|
draw_plots(sources=frames, plots=plot)
|
||||||
if stats:
|
if stats:
|
||||||
print_stats(sources=frames, keys=stats, stats_sum=stats_sum)
|
print_stats(sources=frames, keys=stats, stats_sum=stats_sum)
|
||||||
|
if hist_threshold:
|
||||||
|
draw_hist_threshold(sources=frames, keys=hist_threshold, begin_frame=begin_frame,
|
||||||
|
threshold_name=threshold_name, threshold_value=threshold_value)
|
||||||
matplotlib.pyplot.show()
|
matplotlib.pyplot.show()
|
||||||
|
|
||||||
|
|
||||||
@ -92,19 +109,26 @@ def read_data(path):
|
|||||||
frame[key] = to_number(value)
|
frame[key] = to_number(value)
|
||||||
|
|
||||||
|
|
||||||
def collect_per_frame(sources, keys, begin_frame, end_frame):
|
def collect_per_frame(sources, keys, begin_frame, end_frame, frame_number_name):
|
||||||
|
assert begin_frame < end_frame
|
||||||
result = collections.defaultdict(lambda: collections.defaultdict(list))
|
result = collections.defaultdict(lambda: collections.defaultdict(list))
|
||||||
|
begin_frame = max(begin_frame, min(v[0][frame_number_name] for v in sources.values()))
|
||||||
|
end_frame = min(end_frame, begin_frame + max(len(v) for v in sources.values()))
|
||||||
|
for name in sources.keys():
|
||||||
|
for key in keys:
|
||||||
|
result[name][key] = [0] * (end_frame - begin_frame)
|
||||||
for name, frames in sources.items():
|
for name, frames in sources.items():
|
||||||
for frame in frames:
|
for frame in frames:
|
||||||
for key in keys:
|
number = frame[frame_number_name]
|
||||||
if key in frame:
|
if begin_frame <= number < end_frame:
|
||||||
result[name][key].append(frame[key])
|
index = number - begin_frame
|
||||||
else:
|
for key in keys:
|
||||||
result[name][key].append(None)
|
if key in frame:
|
||||||
for name, sources in result.items():
|
result[name][key][index] = frame[key]
|
||||||
for key, values in sources.items():
|
for name in result.keys():
|
||||||
result[name][key] = numpy.array(values[begin_frame:end_frame])
|
for key in keys:
|
||||||
return result
|
result[name][key] = numpy.array(result[name][key])
|
||||||
|
return result, begin_frame, end_frame
|
||||||
|
|
||||||
|
|
||||||
def collect_unique_keys(sources):
|
def collect_unique_keys(sources):
|
||||||
@ -116,12 +140,11 @@ def collect_unique_keys(sources):
|
|||||||
return sorted(result)
|
return sorted(result)
|
||||||
|
|
||||||
|
|
||||||
def draw_timeseries(sources, keys, add_sum):
|
def draw_timeseries(sources, keys, add_sum, begin_frame, end_frame):
|
||||||
fig, ax = matplotlib.pyplot.subplots()
|
fig, ax = matplotlib.pyplot.subplots()
|
||||||
|
x = numpy.array(range(begin_frame, end_frame))
|
||||||
for name, frames in sources.items():
|
for name, frames in sources.items():
|
||||||
x = numpy.array(range(max(len(v) for k, v in frames.items() if k in keys)))
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
print(key, name)
|
|
||||||
ax.plot(x, frames[key], label=f'{key}:{name}')
|
ax.plot(x, frames[key], label=f'{key}:{name}')
|
||||||
if add_sum:
|
if add_sum:
|
||||||
ax.plot(x, numpy.sum(list(frames[k] for k in keys), axis=0), label=f'sum:{name}')
|
ax.plot(x, numpy.sum(list(frames[k] for k in keys), axis=0), label=f'sum:{name}')
|
||||||
@ -130,10 +153,10 @@ def draw_timeseries(sources, keys, add_sum):
|
|||||||
fig.canvas.set_window_title('timeseries')
|
fig.canvas.set_window_title('timeseries')
|
||||||
|
|
||||||
|
|
||||||
def draw_commulative_timeseries(sources, keys, add_sum):
|
def draw_commulative_timeseries(sources, keys, add_sum, begin_frame, end_frame):
|
||||||
fig, ax = matplotlib.pyplot.subplots()
|
fig, ax = matplotlib.pyplot.subplots()
|
||||||
|
x = numpy.array(range(begin_frame, end_frame))
|
||||||
for name, frames in sources.items():
|
for name, frames in sources.items():
|
||||||
x = numpy.array(range(max(len(v) for k, v in frames.items() if k in keys)))
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
ax.plot(x, numpy.cumsum(frames[key]), label=f'{key}:{name}')
|
ax.plot(x, numpy.cumsum(frames[key]), label=f'{key}:{name}')
|
||||||
if add_sum:
|
if add_sum:
|
||||||
@ -227,7 +250,6 @@ def print_stats(sources, keys, stats_sum):
|
|||||||
if stats_sum:
|
if stats_sum:
|
||||||
stats.append(make_stats(source=name, key='sum', values=sum_multiple(frames, keys)))
|
stats.append(make_stats(source=name, key='sum', values=sum_multiple(frames, keys)))
|
||||||
metrics = list(stats[0].keys())
|
metrics = list(stats[0].keys())
|
||||||
max_key_size = max(len(tuple(v.values())[0]) for v in stats)
|
|
||||||
termtables.print(
|
termtables.print(
|
||||||
[list(v.values()) for v in stats],
|
[list(v.values()) for v in stats],
|
||||||
header=metrics,
|
header=metrics,
|
||||||
@ -235,6 +257,27 @@ def print_stats(sources, keys, stats_sum):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_hist_threshold(sources, keys, begin_frame, threshold_name, threshold_value):
|
||||||
|
for name, frames in sources.items():
|
||||||
|
indices = [n for n, v in enumerate(frames[threshold_name]) if v > threshold_value]
|
||||||
|
numbers = [v + begin_frame for v in indices]
|
||||||
|
x = [v for v in range(0, len(indices))]
|
||||||
|
fig, ax = matplotlib.pyplot.subplots()
|
||||||
|
ax.set_title(f'Frames with "{threshold_name}" > {threshold_value} ({len(indices)})')
|
||||||
|
ax.bar(x, [frames[threshold_name][v] for v in indices], label=threshold_name, color='black', alpha=0.2)
|
||||||
|
prev = 0
|
||||||
|
for key in keys:
|
||||||
|
values = [frames[key][v] for v in indices]
|
||||||
|
ax.bar(x, values, bottom=prev, label=key)
|
||||||
|
prev = values
|
||||||
|
ax.hlines(threshold_value, x[0] - 1, x[-1] + 1, color='black', label='threshold', linestyles='dashed')
|
||||||
|
ax.xaxis.set_major_locator(matplotlib.pyplot.FixedLocator(x))
|
||||||
|
ax.xaxis.set_major_formatter(matplotlib.pyplot.FixedFormatter(numbers))
|
||||||
|
ax.grid(True)
|
||||||
|
ax.legend()
|
||||||
|
fig.canvas.set_window_title(f'hist_threshold:{name}')
|
||||||
|
|
||||||
|
|
||||||
def filter_not_none(values):
|
def filter_not_none(values):
|
||||||
return [v for v in values if v is not None]
|
return [v for v in values if v is not None]
|
||||||
|
|
||||||
@ -269,5 +312,6 @@ def to_number(value):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return float(value)
|
return float(value)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user