mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-25 16:43:33 +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 #6197: Infinite Casting Loop
|
||||
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 #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 #2780: A way to see current OpenMW version in the console
|
||||
Feature #3616: Allow Zoom levels on the World Map
|
||||
|
@ -71,7 +71,7 @@ opencs_units (view/world
|
||||
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
|
||||
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
|
||||
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
|
||||
bodypartcreator landtexturecreator landcreator
|
||||
bodypartcreator landtexturecreator landcreator tableheadermouseeventhandler
|
||||
)
|
||||
|
||||
opencs_units (view/world
|
||||
|
@ -32,7 +32,6 @@ namespace CSVWidget
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class Table;
|
||||
class TableBottomBox;
|
||||
class CreatorFactoryBase;
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "../../model/prefs/shortcut.hpp"
|
||||
|
||||
#include "tableeditidaction.hpp"
|
||||
#include "tableheadermouseeventhandler.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
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 *)),
|
||||
this, SLOT (settingChanged (const CSMPrefs::Setting *)));
|
||||
CSMPrefs::get()["ID Tables"].update();
|
||||
|
||||
new TableHeaderMouseEventHandler(this);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
||||
}
|
||||
@ -686,7 +686,7 @@ namespace MWPhysics
|
||||
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);
|
||||
assert(shapeInstance);
|
||||
@ -694,7 +694,7 @@ namespace MWPhysics
|
||||
|
||||
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));
|
||||
|
||||
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 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 updateProjectile(const int projectileId, const osg::Vec3f &position) const;
|
||||
void removeProjectile(const int projectileId);
|
||||
|
@ -15,12 +15,10 @@
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
||||
: mCanCrossWaterSurface(canCrossWaterSurface)
|
||||
, mCrossedWaterSurface(false)
|
||||
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
||||
: mHitWater(false)
|
||||
, mActive(true)
|
||||
, mHitTarget(nullptr)
|
||||
, mWaterHitPosition(std::nullopt)
|
||||
, mPhysics(physicssystem)
|
||||
, mTaskScheduler(scheduler)
|
||||
{
|
||||
@ -75,11 +73,6 @@ osg::Vec3f Projectile::getPosition() const
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
bool Projectile::canTraverseWater() const
|
||||
{
|
||||
return mCanCrossWaterSurface;
|
||||
}
|
||||
|
||||
void Projectile::hit(const btCollisionObject* target, btVector3 pos, btVector3 normal)
|
||||
{
|
||||
bool active = true;
|
||||
@ -143,17 +136,4 @@ bool Projectile::isValidTarget(const btCollisionObject* target) const
|
||||
[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 <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include <LinearMath/btVector3.h>
|
||||
|
||||
@ -32,7 +31,7 @@ namespace MWPhysics
|
||||
class Projectile final : public PtrHolder
|
||||
{
|
||||
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;
|
||||
|
||||
btConvexShape* getConvexShape() const { return mConvexShape; }
|
||||
@ -56,15 +55,25 @@ namespace MWPhysics
|
||||
return mCasterColObj;
|
||||
}
|
||||
|
||||
bool canTraverseWater() const;
|
||||
void setHitWater()
|
||||
{
|
||||
mHitWater = true;
|
||||
}
|
||||
|
||||
bool getHitWater() const
|
||||
{
|
||||
return mHitWater;
|
||||
}
|
||||
|
||||
void hit(const btCollisionObject* target, btVector3 pos, btVector3 normal);
|
||||
|
||||
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
|
||||
bool isValidTarget(const btCollisionObject* target) const;
|
||||
|
||||
std::optional<btVector3> getWaterHitPosition();
|
||||
void setWaterHitPosition(btVector3 pos);
|
||||
btVector3 getHitPosition() const
|
||||
{
|
||||
return mHitPosition;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@ -72,13 +81,11 @@ namespace MWPhysics
|
||||
btConvexShape* mConvexShape;
|
||||
|
||||
bool mTransformUpdatePending;
|
||||
bool mCanCrossWaterSurface;
|
||||
bool mCrossedWaterSurface;
|
||||
bool mHitWater;
|
||||
std::atomic<bool> mActive;
|
||||
MWWorld::Ptr mCaster;
|
||||
const btCollisionObject* mCasterColObj;
|
||||
const btCollisionObject* mHitTarget;
|
||||
std::optional<btVector3> mWaterHitPosition;
|
||||
osg::Vec3f mPosition;
|
||||
btVector3 mHitPosition;
|
||||
btVector3 mHitNormal;
|
||||
|
@ -49,9 +49,7 @@ namespace MWPhysics
|
||||
}
|
||||
case CollisionType_Water:
|
||||
{
|
||||
mProjectile->setWaterHitPosition(m_hitPointWorld);
|
||||
if (mProjectile->canTraverseWater())
|
||||
return 1.f;
|
||||
mProjectile->setHitWater();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -651,7 +651,7 @@ namespace MWRender
|
||||
}
|
||||
optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);
|
||||
unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY;
|
||||
mSceneManager->shareState(mergeGroup);
|
||||
|
||||
optimizer.optimize(mergeGroup, options);
|
||||
|
||||
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
|
||||
if (state.mIdMagic.size() > 1)
|
||||
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;
|
||||
mMagicBolts.push_back(state);
|
||||
}
|
||||
@ -342,7 +342,7 @@ namespace MWWorld
|
||||
if (!ptr.getClass().getEnchantment(ptr).empty())
|
||||
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;
|
||||
mProjectiles.push_back(state);
|
||||
}
|
||||
@ -493,9 +493,6 @@ namespace MWWorld
|
||||
|
||||
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||
|
||||
if (const auto hitWaterPos = projectile->getWaterHitPosition())
|
||||
mRendering->emitWaterRipple(Misc::Convert::toOsg(*hitWaterPos));
|
||||
|
||||
const auto pos = projectile->getPosition();
|
||||
projectileState.mNode->setPosition(pos);
|
||||
|
||||
@ -519,6 +516,8 @@ namespace MWWorld
|
||||
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))
|
||||
bow = *invIt;
|
||||
}
|
||||
if (projectile->getHitWater())
|
||||
mRendering->emitWaterRipple(pos);
|
||||
|
||||
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
||||
projectileState.mToDelete = true;
|
||||
@ -663,7 +662,7 @@ namespace MWWorld
|
||||
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
||||
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(...)
|
||||
{
|
||||
@ -716,7 +715,7 @@ namespace MWWorld
|
||||
|
||||
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
||||
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();
|
||||
for (const std::string &soundid : state.mSoundIds)
|
||||
|
@ -200,13 +200,12 @@ namespace
|
||||
struct InsertVisitor
|
||||
{
|
||||
MWWorld::CellStore& mCell;
|
||||
Loading::Listener& mLoadingListener;
|
||||
Loading::Listener* mLoadingListener;
|
||||
bool mOnlyObjects;
|
||||
bool mTest;
|
||||
|
||||
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);
|
||||
|
||||
@ -214,8 +213,8 @@ namespace
|
||||
void insert(AddObject&& addObject);
|
||||
};
|
||||
|
||||
InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool onlyObjects, bool test)
|
||||
: mCell (cell), mLoadingListener (loadingListener), mOnlyObjects(onlyObjects), mTest(test)
|
||||
InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener* loadingListener, bool onlyObjects)
|
||||
: mCell(cell), mLoadingListener(loadingListener), mOnlyObjects(onlyObjects)
|
||||
{}
|
||||
|
||||
bool InsertVisitor::operator() (const MWWorld::Ptr& ptr)
|
||||
@ -244,8 +243,8 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
if (!mTest)
|
||||
mLoadingListener.increaseProgress (1);
|
||||
if (mLoadingListener != nullptr)
|
||||
mLoadingListener->increaseProgress(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,12 +324,12 @@ namespace MWWorld
|
||||
mRendering.update (duration, paused);
|
||||
}
|
||||
|
||||
void Scene::unloadInactiveCell (CellStore* cell, bool test)
|
||||
void Scene::unloadInactiveCell (CellStore* cell)
|
||||
{
|
||||
assert(mActiveCells.find(cell) == mActiveCells.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;
|
||||
|
||||
@ -351,13 +350,13 @@ namespace MWWorld
|
||||
mInactiveCells.erase(cell);
|
||||
}
|
||||
|
||||
void Scene::deactivateCell(CellStore* cell, bool test)
|
||||
void Scene::deactivateCell(CellStore* cell)
|
||||
{
|
||||
assert(mInactiveCells.find(cell) != mInactiveCells.end());
|
||||
if (mActiveCells.find(cell) == mActiveCells.end())
|
||||
return;
|
||||
if (!test)
|
||||
Log(Debug::Info) << "Deactivate cell " << cell->getCell()->getDescription();
|
||||
|
||||
Log(Debug::Info) << "Deactivate cell " << cell->getCell()->getDescription();
|
||||
|
||||
ListAndResetObjectsVisitor visitor;
|
||||
|
||||
@ -409,7 +408,7 @@ namespace MWWorld
|
||||
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;
|
||||
|
||||
@ -417,17 +416,14 @@ namespace MWWorld
|
||||
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();
|
||||
Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription();
|
||||
|
||||
const auto world = MWBase::Environment::get().getWorld();
|
||||
|
||||
const int cellX = cell->getCell()->getGridX();
|
||||
const int cellY = cell->getCell()->getGridY();
|
||||
|
||||
if (!test && cell->getCell()->isExterior())
|
||||
if (cell->getCell()->isExterior())
|
||||
{
|
||||
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
|
||||
{
|
||||
@ -466,69 +462,64 @@ namespace MWWorld
|
||||
if (respawn)
|
||||
cell->respawn();
|
||||
|
||||
insertCell (*cell, loadingListener, false, test);
|
||||
insertCell(*cell, loadingListener, false);
|
||||
|
||||
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,
|
||||
osg::Vec3f(static_cast<float>(transform.getOrigin().x()),
|
||||
static_cast<float>(transform.getOrigin().y()),
|
||||
waterLevel));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mNavigator.addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),
|
||||
osg::Vec3f(0, 0, waterLevel));
|
||||
const btTransform& transform =heightField->getCollisionObject()->getWorldTransform();
|
||||
mNavigator.addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE,
|
||||
osg::Vec3f(static_cast<float>(transform.getOrigin().x()),
|
||||
static_cast<float>(transform.getOrigin().y()),
|
||||
waterLevel));
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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(mInactiveCells.find(cell) == mInactiveCells.end());
|
||||
mInactiveCells.insert(cell);
|
||||
|
||||
if (test)
|
||||
Log(Debug::Info) << "Testing inactive cell " << cell->getCell()->getDescription();
|
||||
else
|
||||
Log(Debug::Info) << "Loading inactive cell " << cell->getCell()->getDescription();
|
||||
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 worldsize = ESM::Land::REAL_SIZE;
|
||||
@ -550,7 +541,7 @@ namespace MWWorld
|
||||
}
|
||||
}
|
||||
|
||||
insertCell (*cell, loadingListener, true, test);
|
||||
insertCell(*cell, loadingListener, true);
|
||||
}
|
||||
|
||||
void Scene::clear()
|
||||
@ -746,8 +737,8 @@ namespace MWWorld
|
||||
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);
|
||||
loadInactiveCell (cell, loadingListener, true);
|
||||
activateCell (cell, loadingListener, false, true);
|
||||
loadInactiveCell(cell, nullptr);
|
||||
activateCell(cell, nullptr, false);
|
||||
|
||||
auto iter = mInactiveCells.begin();
|
||||
while (iter != mInactiveCells.end())
|
||||
@ -755,8 +746,8 @@ namespace MWWorld
|
||||
if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() &&
|
||||
it->mData.mY == (*iter)->getCell()->getGridY())
|
||||
{
|
||||
deactivateCell(*iter, true);
|
||||
unloadInactiveCell (*iter, true);
|
||||
deactivateCell(*iter);
|
||||
unloadInactiveCell(*iter);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -794,8 +785,8 @@ namespace MWWorld
|
||||
loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")...");
|
||||
|
||||
CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName);
|
||||
loadInactiveCell (cell, loadingListener, true);
|
||||
activateCell (cell, loadingListener, false, true);
|
||||
loadInactiveCell(cell, nullptr);
|
||||
activateCell(cell, nullptr, false);
|
||||
|
||||
auto iter = mInactiveCells.begin();
|
||||
while (iter != mInactiveCells.end())
|
||||
@ -804,8 +795,8 @@ namespace MWWorld
|
||||
|
||||
if (it->mName == (*iter)->getCell()->mName)
|
||||
{
|
||||
deactivateCell(*iter, true);
|
||||
unloadInactiveCell (*iter, true);
|
||||
deactivateCell(*iter);
|
||||
unloadInactiveCell(*iter);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -988,9 +979,9 @@ namespace MWWorld
|
||||
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);
|
||||
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs, onlyObjects); });
|
||||
if (!onlyObjects)
|
||||
|
@ -100,7 +100,7 @@ namespace MWWorld
|
||||
|
||||
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;
|
||||
|
||||
// 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::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);
|
||||
void unloadInactiveCell(CellStore* cell);
|
||||
void deactivateCell(CellStore* cell);
|
||||
void activateCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn);
|
||||
void loadInactiveCell(CellStore *cell, Loading::Listener* loadingListener);
|
||||
|
||||
public:
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/misc/mathutil.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/misc/convert.hpp>
|
||||
@ -76,21 +77,6 @@
|
||||
#include "contentloader.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
|
||||
{
|
||||
struct GameContentLoader : public ContentLoader
|
||||
@ -1290,8 +1276,6 @@ namespace MWWorld
|
||||
|
||||
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();
|
||||
float *objRot = pos.rot;
|
||||
if (flags & MWBase::RotationFlag_adjust)
|
||||
@ -1313,13 +1297,9 @@ namespace MWWorld
|
||||
* currently it's done so for rotating the camera, which needs
|
||||
* clamping.
|
||||
*/
|
||||
const float half_pi = pi/2.f;
|
||||
|
||||
if(objRot[0] < -half_pi) objRot[0] = -half_pi;
|
||||
else if(objRot[0] > half_pi) objRot[0] = half_pi;
|
||||
|
||||
wrap(objRot[1]);
|
||||
wrap(objRot[2]);
|
||||
objRot[0] = osg::clampBetween(objRot[0], -osg::PIf / 2, osg::PIf / 2);
|
||||
objRot[1] = Misc::normalizeAngle(objRot[1]);
|
||||
objRot[2] = Misc::normalizeAngle(objRot[2]);
|
||||
}
|
||||
|
||||
ptr.getRefData().setPosition(pos);
|
||||
@ -3145,6 +3125,7 @@ namespace MWWorld
|
||||
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos);
|
||||
if (underwater)
|
||||
{
|
||||
MWMechanics::projectileHit(actor, Ptr(), bow, projectile, worldPos, attackStrength);
|
||||
mRendering->emitWaterRipple(worldPos);
|
||||
return;
|
||||
}
|
||||
|
@ -164,8 +164,6 @@ namespace Compiler
|
||||
std::string value;
|
||||
c.appendTo(value);
|
||||
|
||||
bool error = false;
|
||||
|
||||
while (get (c))
|
||||
{
|
||||
if (c.isDigit())
|
||||
@ -174,16 +172,11 @@ namespace Compiler
|
||||
}
|
||||
else if (!c.isMinusSign() && isStringCharacter (c))
|
||||
{
|
||||
error = true;
|
||||
c.appendTo(value);
|
||||
/// workaround that allows names to begin with digits
|
||||
return scanName(c, parser, cont, value);
|
||||
}
|
||||
else if (c=='.')
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
putback (c);
|
||||
break;
|
||||
}
|
||||
return scanFloat (value, parser, cont);
|
||||
}
|
||||
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);
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
@ -268,9 +250,8 @@ namespace Compiler
|
||||
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);
|
||||
|
||||
if (!scanName (name))
|
||||
|
@ -236,7 +236,7 @@ namespace Compiler
|
||||
|
||||
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)
|
||||
bool scanName (std::string& name);
|
||||
|
@ -659,22 +659,18 @@ namespace Resource
|
||||
osg::ref_ptr<Shader::ShaderVisitor> shaderVisitor (createShaderVisitor());
|
||||
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))
|
||||
{
|
||||
SceneUtil::Optimizer optimizer;
|
||||
optimizer.setSharedStateManager(mSharedStateManager, &mSharedStateMutex);
|
||||
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);
|
||||
}
|
||||
else
|
||||
shareState(loaded);
|
||||
|
||||
if (compile && mIncrementalCompileOperation)
|
||||
mIncrementalCompileOperation->add(loaded);
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include <osg/io_utils>
|
||||
#include <osg/Depth>
|
||||
|
||||
#include <osgDB/SharedStateManager>
|
||||
|
||||
#include <osgUtil/TransformAttributeFunctor>
|
||||
#include <osgUtil/Statistics>
|
||||
#include <osgUtil/MeshOptimizers>
|
||||
@ -84,6 +86,13 @@ void Optimizer::optimize(osg::Node* node, unsigned int options)
|
||||
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)
|
||||
{
|
||||
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 &&
|
||||
transform->getChild(0)->asTransform()!=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.
|
||||
osg::MatrixTransform* child = transform->getChild(0)->asTransform()->asMatrixTransform();
|
||||
|
@ -25,6 +25,12 @@
|
||||
//#include <osgUtil/Export>
|
||||
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
|
||||
namespace osgDB
|
||||
{
|
||||
class SharedStateManager;
|
||||
}
|
||||
|
||||
//namespace osgUtil {
|
||||
namespace SceneUtil {
|
||||
@ -65,7 +71,7 @@ class Optimizer
|
||||
|
||||
public:
|
||||
|
||||
Optimizer() : _mergeAlphaBlending(false) {}
|
||||
Optimizer() : _mergeAlphaBlending(false), _sharedStateManager(nullptr), _sharedStateMutex(nullptr) {}
|
||||
virtual ~Optimizer() {}
|
||||
|
||||
enum OptimizationOptions
|
||||
@ -121,6 +127,8 @@ class Optimizer
|
||||
void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; }
|
||||
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.*/
|
||||
void reset();
|
||||
|
||||
@ -258,6 +266,9 @@ class Optimizer
|
||||
osg::Vec3f _viewPoint;
|
||||
bool _mergeAlphaBlending;
|
||||
|
||||
osgDB::SharedStateManager* _sharedStateManager;
|
||||
mutable std::mutex* _sharedStateMutex;
|
||||
|
||||
public:
|
||||
|
||||
/** Flatten Static Transform nodes by applying their transform to the
|
||||
|
@ -38,27 +38,41 @@ import termtables
|
||||
@click.option('--timeseries_sum', is_flag=True,
|
||||
help='Add a graph to timeseries for a sum per frame of all given timeseries metrics.')
|
||||
@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,
|
||||
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,
|
||||
help='Start processing from this frame.')
|
||||
@click.option('--end_frame', type=int, default=sys.maxsize,
|
||||
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)
|
||||
def main(print_keys, timeseries, hist, hist_ratio, stdev_hist, plot, stats,
|
||||
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))}
|
||||
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:
|
||||
for v in keys:
|
||||
print(v)
|
||||
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:
|
||||
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:
|
||||
draw_hists(sources=frames, keys=hist)
|
||||
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)
|
||||
if stats:
|
||||
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()
|
||||
|
||||
|
||||
@ -92,19 +109,26 @@ def read_data(path):
|
||||
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))
|
||||
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 frame in frames:
|
||||
for key in keys:
|
||||
if key in frame:
|
||||
result[name][key].append(frame[key])
|
||||
else:
|
||||
result[name][key].append(None)
|
||||
for name, sources in result.items():
|
||||
for key, values in sources.items():
|
||||
result[name][key] = numpy.array(values[begin_frame:end_frame])
|
||||
return result
|
||||
number = frame[frame_number_name]
|
||||
if begin_frame <= number < end_frame:
|
||||
index = number - begin_frame
|
||||
for key in keys:
|
||||
if key in frame:
|
||||
result[name][key][index] = frame[key]
|
||||
for name in result.keys():
|
||||
for key in keys:
|
||||
result[name][key] = numpy.array(result[name][key])
|
||||
return result, begin_frame, end_frame
|
||||
|
||||
|
||||
def collect_unique_keys(sources):
|
||||
@ -116,12 +140,11 @@ def collect_unique_keys(sources):
|
||||
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()
|
||||
x = numpy.array(range(begin_frame, end_frame))
|
||||
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:
|
||||
print(key, name)
|
||||
ax.plot(x, frames[key], label=f'{key}:{name}')
|
||||
if add_sum:
|
||||
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')
|
||||
|
||||
|
||||
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()
|
||||
x = numpy.array(range(begin_frame, end_frame))
|
||||
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:
|
||||
ax.plot(x, numpy.cumsum(frames[key]), label=f'{key}:{name}')
|
||||
if add_sum:
|
||||
@ -227,7 +250,6 @@ def print_stats(sources, keys, stats_sum):
|
||||
if stats_sum:
|
||||
stats.append(make_stats(source=name, key='sum', values=sum_multiple(frames, keys)))
|
||||
metrics = list(stats[0].keys())
|
||||
max_key_size = max(len(tuple(v.values())[0]) for v in stats)
|
||||
termtables.print(
|
||||
[list(v.values()) for v in stats],
|
||||
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):
|
||||
return [v for v in values if v is not None]
|
||||
|
||||
@ -269,5 +312,6 @@ def to_number(value):
|
||||
except ValueError:
|
||||
return float(value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user