Merge pull request #3 from Battlerax/master

tes3mp & openmw changes
pull/165/head
Battlerax 8 years ago committed by GitHub
commit 79e996aed3

@ -21,6 +21,7 @@
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/resource/stats.hpp>
#include <components/compiler/extensions0.hpp> #include <components/compiler/extensions0.hpp>
@ -184,7 +185,7 @@ void OMW::Engine::frame(float frametime)
mEnvironment.getWindowManager()->update(); mEnvironment.getWindowManager()->update();
} }
int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
osg::Stats* stats = mViewer->getViewerStats(); osg::Stats* stats = mViewer->getViewerStats();
stats->setAttribute(frameNumber, "script_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeScriptTick)); stats->setAttribute(frameNumber, "script_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeScriptTick));
stats->setAttribute(frameNumber, "script_time_taken", osg::Timer::instance()->delta_s(beforeScriptTick, afterScriptTick)); stats->setAttribute(frameNumber, "script_time_taken", osg::Timer::instance()->delta_s(beforeScriptTick, afterScriptTick));
@ -198,6 +199,14 @@ void OMW::Engine::frame(float frametime)
stats->setAttribute(frameNumber, "physics_time_taken", osg::Timer::instance()->delta_s(beforePhysicsTick, afterPhysicsTick)); stats->setAttribute(frameNumber, "physics_time_taken", osg::Timer::instance()->delta_s(beforePhysicsTick, afterPhysicsTick));
stats->setAttribute(frameNumber, "physics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterPhysicsTick)); stats->setAttribute(frameNumber, "physics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterPhysicsTick));
if (stats->collectStats("resource"))
{
mResourceSystem->reportStats(frameNumber, stats);
stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems());
stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads());
}
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -423,6 +432,8 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
camera->setViewport(0, 0, width, height); camera->setViewport(0, 0, width, height);
mViewer->realize(); mViewer->realize();
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, width, height);
} }
void OMW::Engine::setWindowIcon() void OMW::Engine::setWindowIcon()
@ -656,6 +667,8 @@ void OMW::Engine::go()
mViewer->addEventHandler(statshandler); mViewer->addEventHandler(statshandler);
mViewer->addEventHandler(new Resource::StatsHandler);
Settings::Manager settings; Settings::Manager settings;
std::string settingspath; std::string settingspath;

@ -99,8 +99,6 @@ namespace MWMechanics
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2f * actorWillpower + 0.1f * actorLuck) * stats.getFatigueTerm(); float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2f * actorWillpower + 0.1f * actorLuck) * stats.getFatigueTerm();
if (MWBase::Environment::get().getWorld()->getGodModeState() && actor == getPlayer())
castChance = 100;
if (!cap) if (!cap)
return std::max(0.f, castChance); return std::max(0.f, castChance);
@ -820,8 +818,14 @@ namespace MWMechanics
bool fail = false; bool fail = false;
// Check success // Check success
float successChance = getSpellSuccessChance(spell, mCaster);
// Major change done by tes3mp:
// Instead of checking whether the caster is a player or an NPC,
// check whether it's the LocalPlayer or a DedicatedPlayer and calculate
// calculate the success chance in clients' LocalPlayer::prepareAttack()
// TODO: Make this make sense for NPCs too
// TODO: See if LocalPlayer being the target and having godmode on
// can be accounted for like it is in OpenMW's corresponding code
mwmp::DedicatedPlayer *dedicatedPlayer = mwmp::Players::getPlayer(mCaster); mwmp::DedicatedPlayer *dedicatedPlayer = mwmp::Players::getPlayer(mCaster);
bool isDedicated = dedicatedPlayer != NULL; bool isDedicated = dedicatedPlayer != NULL;

@ -26,10 +26,8 @@
namespace MWRender namespace MWRender
{ {
ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem, ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem)
bool disableListener) : Animation(ptr, parentNode, resourceSystem)
: Animation(ptr, parentNode, resourceSystem),
mListenerDisabled(disableListener)
{ {
MWWorld::ContainerStore& store = mPtr.getClass().getContainerStore(mPtr); MWWorld::ContainerStore& store = mPtr.getClass().getContainerStore(mPtr);
@ -41,16 +39,10 @@ ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group>
addHiddenItemLight(*iter, light); addHiddenItemLight(*iter, light);
} }
} }
if (!mListenerDisabled)
store.setContListener(this);
} }
ActorAnimation::~ActorAnimation() ActorAnimation::~ActorAnimation()
{ {
if (!mListenerDisabled && mPtr.getRefData().getCustomData() && mPtr.getClass().getContainerStore(mPtr).getContListener() == this)
mPtr.getClass().getContainerStore(mPtr).setContListener(NULL);
for (ItemLightMap::iterator iter = mItemLights.begin(); iter != mItemLights.end(); ++iter) for (ItemLightMap::iterator iter = mItemLights.begin(); iter != mItemLights.end(); ++iter)
{ {
mInsert->removeChild(iter->second); mInsert->removeChild(iter->second);

@ -31,8 +31,7 @@ namespace MWRender
class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
{ {
public: public:
ActorAnimation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem, ActorAnimation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem);
bool disableListener=false);
virtual ~ActorAnimation(); virtual ~ActorAnimation();
virtual void itemAdded(const MWWorld::ConstPtr& item, int count); virtual void itemAdded(const MWWorld::ConstPtr& item, int count);
@ -44,8 +43,6 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
typedef std::map<MWWorld::ConstPtr, osg::ref_ptr<SceneUtil::LightSource> > ItemLightMap; typedef std::map<MWWorld::ConstPtr, osg::ref_ptr<SceneUtil::LightSource> > ItemLightMap;
ItemLightMap mItemLights; ItemLightMap mItemLights;
bool mListenerDisabled;
}; };
} }

@ -464,6 +464,11 @@ namespace MWRender
return mPtr; return mPtr;
} }
MWWorld::Ptr Animation::getPtr()
{
return mPtr;
}
void Animation::setActive(bool active) void Animation::setActive(bool active)
{ {
if (mSkeleton) if (mSkeleton)
@ -1668,9 +1673,4 @@ namespace MWRender
} }
} }
void PartHolder::unlink()
{
mNode = NULL;
}
} }

@ -58,9 +58,6 @@ public:
~PartHolder(); ~PartHolder();
/// Unreferences mNode *without* detaching it from the graph. Only use if you know what you are doing.
void unlink();
osg::ref_ptr<osg::Node> getNode() osg::ref_ptr<osg::Node> getNode()
{ {
return mNode; return mNode;
@ -74,7 +71,7 @@ private:
}; };
typedef boost::shared_ptr<PartHolder> PartHolderPtr; typedef boost::shared_ptr<PartHolder> PartHolderPtr;
class Animation class Animation : public osg::Referenced
{ {
public: public:
enum BoneGroup { enum BoneGroup {
@ -339,10 +336,14 @@ protected:
public: public:
Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem); Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem);
/// Must be thread safe
virtual ~Animation(); virtual ~Animation();
MWWorld::ConstPtr getPtr() const; MWWorld::ConstPtr getPtr() const;
MWWorld::Ptr getPtr();
/// Set active flag on the object skeleton, if one exists. /// Set active flag on the object skeleton, if one exists.
/// @see SceneUtil::Skeleton::setActive /// @see SceneUtil::Skeleton::setActive
void setActive(bool active); void setActive(bool active);

@ -3,6 +3,7 @@
#include <iostream> #include <iostream>
#include <osg/Fog> #include <osg/Fog>
#include <osg/BlendFunc>
#include <osg/Texture2D> #include <osg/Texture2D>
#include <osg/Camera> #include <osg/Camera>
#include <osg/PositionAttitudeTransform> #include <osg/PositionAttitudeTransform>
@ -42,7 +43,16 @@ namespace MWRender
mRendered = true; mRendered = true;
mLastRenderedFrame = nv->getTraversalNumber(); mLastRenderedFrame = nv->getTraversalNumber();
osg::ref_ptr<osg::FrameStamp> previousFramestamp = const_cast<osg::FrameStamp*>(nv->getFrameStamp());
osg::FrameStamp* fs = new osg::FrameStamp(*previousFramestamp);
fs->setSimulationTime(0.0);
nv->setFrameStamp(fs);
traverse(node, nv); traverse(node, nv);
nv->setFrameStamp(previousFramestamp);
} }
else else
{ {
@ -65,6 +75,35 @@ namespace MWRender
unsigned int mLastRenderedFrame; unsigned int mLastRenderedFrame;
}; };
// Set up alpha blending to Additive mode to avoid issues caused by transparent objects writing onto the alpha value of the FBO
class SetUpBlendVisitor : public osg::NodeVisitor
{
public:
SetUpBlendVisitor(): osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
{
}
virtual void apply(osg::Node& node)
{
if (osg::StateSet* stateset = node.getStateSet())
{
if (stateset->getAttribute(osg::StateAttribute::BLENDFUNC) || stateset->getBinNumber() == osg::StateSet::TRANSPARENT_BIN)
{
osg::ref_ptr<osg::StateSet> newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY);
osg::BlendFunc* blendFunc = static_cast<osg::BlendFunc*>(stateset->getAttribute(osg::StateAttribute::BLENDFUNC));
osg::ref_ptr<osg::BlendFunc> newBlendFunc = blendFunc ? new osg::BlendFunc(*blendFunc) : new osg::BlendFunc;
newBlendFunc->setDestinationAlpha(osg::BlendFunc::ONE);
newBlendFunc->setDestinationRGB(osg::BlendFunc::ONE);
newStateSet->setAttribute(newBlendFunc, osg::StateAttribute::ON);
node.setStateSet(newStateSet);
}
}
traverse(node);
}
};
CharacterPreview::CharacterPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem, CharacterPreview::CharacterPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem,
MWWorld::Ptr character, int sizeX, int sizeY, const osg::Vec3f& position, const osg::Vec3f& lookAt) MWWorld::Ptr character, int sizeX, int sizeY, const osg::Vec3f& position, const osg::Vec3f& lookAt)
: mParent(parent) : mParent(parent)
@ -161,8 +200,15 @@ namespace MWRender
return mSizeY; return mSizeY;
} }
void CharacterPreview::setBlendMode()
{
SetUpBlendVisitor visitor;
mNode->accept(visitor);
}
void CharacterPreview::onSetup() void CharacterPreview::onSetup()
{ {
setBlendMode();
} }
osg::ref_ptr<osg::Texture2D> CharacterPreview::getTexture() osg::ref_ptr<osg::Texture2D> CharacterPreview::getTexture()
@ -172,10 +218,10 @@ namespace MWRender
void CharacterPreview::rebuild() void CharacterPreview::rebuild()
{ {
mAnimation.reset(NULL); mAnimation = NULL;
mAnimation.reset(new NpcAnimation(mCharacter, mNode, mResourceSystem, true, true, mAnimation = new NpcAnimation(mCharacter, mNode, mResourceSystem, true,
(renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal))); (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));
onSetup(); onSetup();
@ -271,6 +317,8 @@ namespace MWRender
mAnimation->runAnimation(0.0f); mAnimation->runAnimation(0.0f);
setBlendMode();
redraw(); redraw();
} }
@ -310,6 +358,7 @@ namespace MWRender
void InventoryPreview::onSetup() void InventoryPreview::onSetup()
{ {
CharacterPreview::onSetup();
osg::Vec3f scale (1.f, 1.f, 1.f); osg::Vec3f scale (1.f, 1.f, 1.f);
mCharacter.getClass().adjustScale(mCharacter, scale, true); mCharacter.getClass().adjustScale(mCharacter, scale, true);
@ -383,6 +432,7 @@ namespace MWRender
void RaceSelectionPreview::onSetup () void RaceSelectionPreview::onSetup ()
{ {
CharacterPreview::onSetup();
mAnimation->play("idle", 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->play("idle", 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
mAnimation->runAnimation(0.f); mAnimation->runAnimation(0.f);

@ -47,6 +47,7 @@ namespace MWRender
protected: protected:
virtual bool renderHeadOnly() { return false; } virtual bool renderHeadOnly() { return false; }
void setBlendMode();
virtual void onSetup(); virtual void onSetup();
osg::ref_ptr<osg::Group> mParent; osg::ref_ptr<osg::Group> mParent;
@ -60,7 +61,7 @@ namespace MWRender
MWWorld::Ptr mCharacter; MWWorld::Ptr mCharacter;
std::auto_ptr<MWRender::NpcAnimation> mAnimation; osg::ref_ptr<MWRender::NpcAnimation> mAnimation;
osg::ref_ptr<osg::PositionAttitudeTransform> mNode; osg::ref_ptr<osg::PositionAttitudeTransform> mNode;
std::string mCurrentAnimGroup; std::string mCurrentAnimGroup;

@ -269,25 +269,11 @@ const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap();
NpcAnimation::~NpcAnimation() NpcAnimation::~NpcAnimation()
{ {
if (!mListenerDisabled
// No need to getInventoryStore() to reset, if none exists
// This is to avoid triggering the listener via ensureCustomData()->autoEquip()->fireEquipmentChanged()
// all from within this destructor. ouch!
&& mPtr.getRefData().getCustomData() && mPtr.getClass().getInventoryStore(mPtr).getInvListener() == this)
mPtr.getClass().getInventoryStore(mPtr).setInvListener(NULL, mPtr);
// do not detach (delete) parts yet, this is done so the background thread can handle the deletion
for(size_t i = 0;i < ESM::PRT_Count;i++)
{
if (mObjectParts[i].get())
mObjectParts[i]->unlink();
}
} }
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem, NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
bool disableListener, bool disableSounds, ViewMode viewMode, float firstPersonFieldOfView) bool disableSounds, ViewMode viewMode, float firstPersonFieldOfView)
: ActorAnimation(ptr, parentNode, resourceSystem, disableListener), : ActorAnimation(ptr, parentNode, resourceSystem),
mListenerDisabled(disableListener),
mViewMode(viewMode), mViewMode(viewMode),
mShowWeapons(false), mShowWeapons(false),
mShowCarriedLeft(true), mShowCarriedLeft(true),
@ -309,9 +295,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> par
} }
updateNpcBase(); updateNpcBase();
if (!disableListener)
mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr);
} }
void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)

@ -38,8 +38,6 @@ public:
private: private:
static const PartBoneMap sPartList; static const PartBoneMap sPartList;
bool mListenerDisabled;
// Bounded Parts // Bounded Parts
PartHolderPtr mObjectParts[ESM::PRT_Count]; PartHolderPtr mObjectParts[ESM::PRT_Count];
std::string mSoundIds[ESM::PRT_Count]; std::string mSoundIds[ESM::PRT_Count];
@ -105,7 +103,7 @@ public:
* @param disableSounds Same as \a disableListener but for playing items sounds * @param disableSounds Same as \a disableListener but for playing items sounds
* @param viewMode * @param viewMode
*/ */
NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem, bool disableListener = false, NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
bool disableSounds = false, ViewMode viewMode=VM_Normal, float firstPersonFieldOfView=55.f); bool disableSounds = false, ViewMode viewMode=VM_Normal, float firstPersonFieldOfView=55.f);
virtual ~NpcAnimation(); virtual ~NpcAnimation();

@ -29,8 +29,6 @@ Objects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Gro
Objects::~Objects() Objects::~Objects()
{ {
for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();++iter)
delete iter->second;
mObjects.clear(); mObjects.clear();
for (CellMap::iterator iter = mCellSceneNodes.begin(); iter != mCellSceneNodes.end(); ++iter) for (CellMap::iterator iter = mCellSceneNodes.begin(); iter != mCellSceneNodes.end(); ++iter)
@ -74,9 +72,9 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool
{ {
insertBegin(ptr); insertBegin(ptr);
std::auto_ptr<ObjectAnimation> anim (new ObjectAnimation(ptr, mesh, mResourceSystem, animated, allowLight)); osg::ref_ptr<ObjectAnimation> anim (new ObjectAnimation(ptr, mesh, mResourceSystem, animated, allowLight));
mObjects.insert(std::make_pair(ptr, anim.release())); mObjects.insert(std::make_pair(ptr, anim));
} }
void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, bool weaponsShields) void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, bool weaponsShields)
@ -85,14 +83,16 @@ void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, b
ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor); ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor);
// CreatureAnimation // CreatureAnimation
std::auto_ptr<Animation> anim; osg::ref_ptr<Animation> anim;
if (weaponsShields) if (weaponsShields)
anim.reset(new CreatureWeaponAnimation(ptr, mesh, mResourceSystem)); anim = new CreatureWeaponAnimation(ptr, mesh, mResourceSystem);
else else
anim.reset(new CreatureAnimation(ptr, mesh, mResourceSystem)); anim = new CreatureAnimation(ptr, mesh, mResourceSystem);
mObjects.insert(std::make_pair(ptr, anim.release())); ptr.getClass().getContainerStore(ptr).setContListener(static_cast<ActorAnimation*>(anim.get()));
mObjects.insert(std::make_pair(ptr, anim));
} }
void Objects::insertNPC(const MWWorld::Ptr &ptr) void Objects::insertNPC(const MWWorld::Ptr &ptr)
@ -100,9 +100,12 @@ void Objects::insertNPC(const MWWorld::Ptr &ptr)
insertBegin(ptr); insertBegin(ptr);
ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor); ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor);
std::auto_ptr<NpcAnimation> anim (new NpcAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), mResourceSystem)); osg::ref_ptr<NpcAnimation> anim (new NpcAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), mResourceSystem));
ptr.getClass().getInventoryStore(ptr).setInvListener(anim.get(), ptr);
ptr.getClass().getInventoryStore(ptr).setContListener(anim.get());
mObjects.insert(std::make_pair(ptr, anim.release())); mObjects.insert(std::make_pair(ptr, anim));
} }
bool Objects::removeObject (const MWWorld::Ptr& ptr) bool Objects::removeObject (const MWWorld::Ptr& ptr)
@ -114,11 +117,17 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr)
if(iter != mObjects.end()) if(iter != mObjects.end())
{ {
if (mUnrefQueue.get()) if (mUnrefQueue.get())
mUnrefQueue->push(iter->second->getObjectRoot()); mUnrefQueue->push(iter->second);
delete iter->second;
mObjects.erase(iter); mObjects.erase(iter);
if (ptr.getClass().isNpc())
{
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
store.setInvListener(NULL, ptr);
store.setContListener(NULL);
}
ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode()); ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode());
ptr.getRefData().setBaseNode(NULL); ptr.getRefData().setBaseNode(NULL);
@ -132,11 +141,19 @@ void Objects::removeCell(const MWWorld::CellStore* store)
{ {
for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();) for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();)
{ {
if(iter->first.getCell() == store) MWWorld::Ptr ptr = iter->second->getPtr();
if(ptr.getCell() == store)
{ {
if (mUnrefQueue.get()) if (mUnrefQueue.get())
mUnrefQueue->push(iter->second->getObjectRoot()); mUnrefQueue->push(iter->second);
delete iter->second;
if (ptr.getClass().isNpc() && ptr.getRefData().getCustomData())
{
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
store.setInvListener(NULL, ptr);
store.setContListener(NULL);
}
mObjects.erase(iter++); mObjects.erase(iter++);
} }
else else
@ -185,7 +202,7 @@ void Objects::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
PtrAnimationMap::iterator iter = mObjects.find(old); PtrAnimationMap::iterator iter = mObjects.find(old);
if(iter != mObjects.end()) if(iter != mObjects.end())
{ {
Animation *anim = iter->second; osg::ref_ptr<Animation> anim = iter->second;
mObjects.erase(iter); mObjects.erase(iter);
anim->updatePtr(cur); anim->updatePtr(cur);
mObjects[cur] = anim; mObjects[cur] = anim;

@ -62,7 +62,7 @@ public:
}; };
class Objects{ class Objects{
typedef std::map<MWWorld::ConstPtr,Animation*> PtrAnimationMap; typedef std::map<MWWorld::ConstPtr,osg::ref_ptr<Animation> > PtrAnimationMap;
typedef std::map<const MWWorld::CellStore*, osg::ref_ptr<osg::Group> > CellMap; typedef std::map<const MWWorld::CellStore*, osg::ref_ptr<osg::Group> > CellMap;
CellMap mCellSceneNodes; CellMap mCellSceneNodes;

@ -475,6 +475,8 @@ namespace MWRender
void RenderingManager::update(float dt, bool paused) void RenderingManager::update(float dt, bool paused)
{ {
reportStats();
mUnrefQueue->flush(mWorkQueue.get()); mUnrefQueue->flush(mWorkQueue.get());
if (!paused) if (!paused)
@ -819,8 +821,8 @@ namespace MWRender
void RenderingManager::renderPlayer(const MWWorld::Ptr &player) void RenderingManager::renderPlayer(const MWWorld::Ptr &player)
{ {
mPlayerAnimation.reset(new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, false, NpcAnimation::VM_Normal, mPlayerAnimation = new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, NpcAnimation::VM_Normal,
mFirstPersonFieldOfView)); mFirstPersonFieldOfView);
mCamera->setAnimation(mPlayerAnimation.get()); mCamera->setAnimation(mPlayerAnimation.get());
mCamera->attachTo(player); mCamera->attachTo(player);
@ -901,6 +903,18 @@ namespace MWRender
mStateUpdater->setFogColor(color); mStateUpdater->setFogColor(color);
} }
void RenderingManager::reportStats()
{
osg::Stats* stats = mViewer->getViewerStats();
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
if (stats->collectStats("resource"))
{
stats->setAttribute(frameNumber, "UnrefQueue", mUnrefQueue->getNumItems());
mTerrain->reportStats(frameNumber, stats);
}
}
void RenderingManager::processChangedSettings(const Settings::CategorySettingVector &changed) void RenderingManager::processChangedSettings(const Settings::CategorySettingVector &changed)
{ {
for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it)

@ -196,6 +196,8 @@ namespace MWRender
void updateAmbient(); void updateAmbient();
void setFogColor(const osg::Vec4f& color); void setFogColor(const osg::Vec4f& color);
void reportStats();
osg::ref_ptr<osgViewer::Viewer> mViewer; osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osg::Group> mRootNode; osg::ref_ptr<osg::Group> mRootNode;
osg::ref_ptr<osg::Group> mSceneRoot; osg::ref_ptr<osg::Group> mSceneRoot;
@ -212,7 +214,7 @@ namespace MWRender
std::auto_ptr<Terrain::World> mTerrain; std::auto_ptr<Terrain::World> mTerrain;
std::auto_ptr<SkyManager> mSky; std::auto_ptr<SkyManager> mSky;
std::auto_ptr<EffectManager> mEffectManager; std::auto_ptr<EffectManager> mEffectManager;
std::auto_ptr<NpcAnimation> mPlayerAnimation; osg::ref_ptr<NpcAnimation> mPlayerAnimation;
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode; osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
std::auto_ptr<Camera> mCamera; std::auto_ptr<Camera> mCamera;
osg::Vec3f mCurrentCameraPos; osg::Vec3f mCurrentCameraPos;

@ -38,6 +38,7 @@
#include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors #include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
#include "../mwrender/npcanimation.hpp"
#include "../mwrender/renderingmanager.hpp" #include "../mwrender/renderingmanager.hpp"
#include "../mwrender/camera.hpp" #include "../mwrender/camera.hpp"
#include "../mwrender/vismask.hpp" #include "../mwrender/vismask.hpp"
@ -2238,7 +2239,12 @@ namespace MWWorld
{ {
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr()); MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr());
mRendering->renderPlayer(getPlayerPtr()); MWWorld::Ptr player = getPlayerPtr();
mRendering->renderPlayer(player);
MWRender::NpcAnimation* anim = static_cast<MWRender::NpcAnimation*>(mRendering->getAnimation(player));
player.getClass().getInventoryStore(player).setInvListener(anim, player);
player.getClass().getInventoryStore(player).setContListener(anim);
scaleObject(getPlayerPtr(), 1.f); // apply race height scaleObject(getPlayerPtr(), 1.f); // apply race height
@ -2742,8 +2748,9 @@ namespace MWWorld
const ESM::Spell* spell = getStore().get<ESM::Spell>().find(selectedSpell); const ESM::Spell* spell = getStore().get<ESM::Spell>().find(selectedSpell);
// Check mana // Check mana
bool godmode = (isPlayer && getGodModeState());
MWMechanics::DynamicStat<float> magicka = stats.getMagicka(); MWMechanics::DynamicStat<float> magicka = stats.getMagicka();
if (magicka.getCurrent() < spell->mData.mCost && !(isPlayer && getGodModeState())) if (magicka.getCurrent() < spell->mData.mCost && !godmode)
{ {
message = "#{sMagicInsufficientSP}"; message = "#{sMagicInsufficientSP}";
fail = true; fail = true;
@ -2757,7 +2764,7 @@ namespace MWWorld
} }
// Reduce mana // Reduce mana
if (!fail) if (!fail && !godmode)
{ {
magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost);
stats.setMagicka(magicka); stats.setMagicka(magicka);

@ -44,7 +44,7 @@ add_component_dir (vfs
IF(BUILD_OPENMW OR BUILD_OPENCS) IF(BUILD_OPENMW OR BUILD_OPENCS)
add_component_dir (resource add_component_dir (resource
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager stats
) )
add_component_dir (shader add_component_dir (shader
@ -239,6 +239,7 @@ target_link_libraries(components
${OSGUTIL_LIBRARIES} ${OSGUTIL_LIBRARIES}
${OSGDB_LIBRARIES} ${OSGDB_LIBRARIES}
${OSGVIEWER_LIBRARIES} ${OSGVIEWER_LIBRARIES}
${OSGTEXT_LIBRARIES}
${OSGGA_LIBRARIES} ${OSGGA_LIBRARIES}
${OSGFX_LIBRARIES} ${OSGFX_LIBRARIES}
${OSGANIMATION_LIBRARIES} ${OSGANIMATION_LIBRARIES}

@ -1038,10 +1038,12 @@ namespace NifOsg
if (!(animflags & Nif::NiNode::ParticleFlag_AutoPlay)) if (!(animflags & Nif::NiNode::ParticleFlag_AutoPlay))
{ {
partsys->setFrozen(true); partsys->setFrozen(true);
// HACK: particle system will not render in Frozen state if there was no update
osg::NodeVisitor nv;
partsys->update(0.0, nv);
} }
// Due to odd code in the ParticleSystemUpdater, particle systems will not be updated in the first frame
// So do that update manually
osg::NodeVisitor nv;
partsys->update(0.0, nv);
} }
// affectors must be attached *after* the emitter in the scene graph for correct update order // affectors must be attached *after* the emitter in the scene graph for correct update order

@ -185,4 +185,10 @@ void BulletShapeManager::updateCache(double referenceTime)
mInstanceCache->removeUnreferencedObjectsInCache(); mInstanceCache->removeUnreferencedObjectsInCache();
} }
void BulletShapeManager::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
stats->setAttribute(frameNumber, "Shape", mCache->getCacheSize());
stats->setAttribute(frameNumber, "Shape Instance", mInstanceCache->getCacheSize());
}
} }

@ -42,6 +42,8 @@ namespace Resource
/// @see ResourceManager::updateCache /// @see ResourceManager::updateCache
virtual void updateCache(double referenceTime); virtual void updateCache(double referenceTime);
void reportStats(unsigned int frameNumber, osg::Stats *stats);
private: private:
osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name); osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);

@ -141,4 +141,9 @@ namespace Resource
return mWarningImage; return mWarningImage;
} }
void ImageManager::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
stats->setAttribute(frameNumber, "Image", mCache->getCacheSize());
}
} }

@ -32,6 +32,8 @@ namespace Resource
osg::Image* getWarningImage(); osg::Image* getWarningImage();
void reportStats(unsigned int frameNumber, osg::Stats* stats);
private: private:
osg::ref_ptr<osg::Image> mWarningImage; osg::ref_ptr<osg::Image> mWarningImage;
osg::ref_ptr<osgDB::Options> mOptions; osg::ref_ptr<osgDB::Options> mOptions;

@ -34,6 +34,11 @@ namespace Resource
} }
} }
void KeyframeManager::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
stats->setAttribute(frameNumber, "Keyframe", mCache->getCacheSize());
}
} }

@ -22,6 +22,8 @@ namespace Resource
/// Retrieve a read-only keyframe resource by name (case-insensitive). /// Retrieve a read-only keyframe resource by name (case-insensitive).
/// @note Throws an exception if the resource is not found. /// @note Throws an exception if the resource is not found.
osg::ref_ptr<const NifOsg::KeyframeHolder> get(const std::string& name); osg::ref_ptr<const NifOsg::KeyframeHolder> get(const std::string& name);
void reportStats(unsigned int frameNumber, osg::Stats* stats);
}; };
} }

@ -76,4 +76,10 @@ namespace Resource
} }
} }
unsigned int MultiObjectCache::getCacheSize() const
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
return _objectCache.size();
}
} }

@ -33,12 +33,14 @@ namespace Resource
/** call releaseGLObjects on all objects attached to the object cache.*/ /** call releaseGLObjects on all objects attached to the object cache.*/
void releaseGLObjects(osg::State* state); void releaseGLObjects(osg::State* state);
unsigned int getCacheSize() const;
protected: protected:
typedef std::multimap<std::string, osg::ref_ptr<osg::Object> > ObjectCacheMap; typedef std::multimap<std::string, osg::ref_ptr<osg::Object> > ObjectCacheMap;
ObjectCacheMap _objectCache; ObjectCacheMap _objectCache;
OpenThreads::Mutex _objectCacheMutex; mutable OpenThreads::Mutex _objectCacheMutex;
}; };

@ -1,6 +1,7 @@
#include "niffilemanager.hpp" #include "niffilemanager.hpp"
#include <osg/Object> #include <osg/Object>
#include <osg/Stats>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
@ -55,4 +56,9 @@ namespace Resource
} }
} }
void NifFileManager::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
stats->setAttribute(frameNumber, "Nif", mCache->getCacheSize());
}
} }

@ -22,6 +22,8 @@ namespace Resource
/// @note For performance reasons the NifFileManager does not handle case folding, needs /// @note For performance reasons the NifFileManager does not handle case folding, needs
/// to be done in advance by other managers accessing the NifFileManager. /// to be done in advance by other managers accessing the NifFileManager.
Nif::NIFFilePtr get(const std::string& name); Nif::NIFFilePtr get(const std::string& name);
void reportStats(unsigned int frameNumber, osg::Stats *stats);
}; };
} }

@ -150,4 +150,10 @@ void ObjectCache::accept(osg::NodeVisitor &nv)
} }
} }
unsigned int ObjectCache::getCacheSize() const
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
return _objectCache.size();
}
} }

@ -73,6 +73,9 @@ class ObjectCache : public osg::Referenced
/** call node->accept(nv); for all nodes in the objectCache. */ /** call node->accept(nv); for all nodes in the objectCache. */
void accept(osg::NodeVisitor& nv); void accept(osg::NodeVisitor& nv);
/** Get the number of objects in the cache. */
unsigned int getCacheSize() const;
protected: protected:
virtual ~ObjectCache(); virtual ~ObjectCache();
@ -81,7 +84,7 @@ class ObjectCache : public osg::Referenced
typedef std::map<std::string, ObjectTimeStampPair > ObjectCacheMap; typedef std::map<std::string, ObjectTimeStampPair > ObjectCacheMap;
ObjectCacheMap _objectCache; ObjectCacheMap _objectCache;
OpenThreads::Mutex _objectCacheMutex; mutable OpenThreads::Mutex _objectCacheMutex;
}; };

@ -8,6 +8,11 @@ namespace VFS
class Manager; class Manager;
} }
namespace osg
{
class Stats;
}
namespace Resource namespace Resource
{ {
class ObjectCache; class ObjectCache;
@ -28,6 +33,8 @@ namespace Resource
const VFS::Manager* getVFS() const; const VFS::Manager* getVFS() const;
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}
protected: protected:
const VFS::Manager* mVFS; const VFS::Manager* mVFS;
osg::ref_ptr<Resource::ObjectCache> mCache; osg::ref_ptr<Resource::ObjectCache> mCache;

@ -85,4 +85,10 @@ namespace Resource
return mVFS; return mVFS;
} }
void ResourceSystem::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
for (std::vector<ResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)
(*it)->reportStats(frameNumber, stats);
}
} }

@ -9,6 +9,11 @@ namespace VFS
class Manager; class Manager;
} }
namespace osg
{
class Stats;
}
namespace Resource namespace Resource
{ {
@ -49,6 +54,8 @@ namespace Resource
/// @note May be called from any thread. /// @note May be called from any thread.
const VFS::Manager* getVFS() const; const VFS::Manager* getVFS() const;
void reportStats(unsigned int frameNumber, osg::Stats* stats);
private: private:
std::auto_ptr<SceneManager> mSceneManager; std::auto_ptr<SceneManager> mSceneManager;
std::auto_ptr<ImageManager> mImageManager; std::auto_ptr<ImageManager> mImageManager;

@ -31,12 +31,47 @@
namespace namespace
{ {
/// @todo Do this in updateCallback so that animations are accounted for. class InitWorldSpaceParticlesCallback : public osg::NodeCallback
class InitWorldSpaceParticlesVisitor : public osg::NodeVisitor {
public:
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osgParticle::ParticleSystem* partsys = static_cast<osgParticle::ParticleSystem*>(node);
// HACK: Ignore the InverseWorldMatrix transform the particle system is attached to
if (partsys->getNumParents() && partsys->getParent(0)->getNumParents())
transformInitialParticles(partsys, partsys->getParent(0)->getParent(0));
node->removeUpdateCallback(this);
}
void transformInitialParticles(osgParticle::ParticleSystem* partsys, osg::Node* node)
{
osg::NodePathList nodepaths = node->getParentalNodePaths();
if (nodepaths.empty())
return;
osg::Matrixf worldMat = osg::computeLocalToWorld(nodepaths[0]);
worldMat.orthoNormalize(worldMat); // scale is already applied on the particle node
for (int i=0; i<partsys->numParticles(); ++i)
{
partsys->getParticle(i)->transformPositionVelocity(worldMat);
}
// transform initial bounds to worldspace
osg::BoundingSphere sphere(partsys->getInitialBound());
SceneUtil::transformBoundingSphere(worldMat, sphere);
osg::BoundingBox box;
box.expandBy(sphere);
partsys->setInitialBound(box);
}
};
class InitParticlesVisitor : public osg::NodeVisitor
{ {
public: public:
/// @param mask The node mask to set on ParticleSystem nodes. /// @param mask The node mask to set on ParticleSystem nodes.
InitWorldSpaceParticlesVisitor(unsigned int mask) InitParticlesVisitor(unsigned int mask)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mMask(mask) , mMask(mask)
{ {
@ -56,33 +91,12 @@ namespace
{ {
if (isWorldSpaceParticleSystem(partsys)) if (isWorldSpaceParticleSystem(partsys))
{ {
// HACK: Ignore the InverseWorldMatrix transform the particle system is attached to partsys->addUpdateCallback(new InitWorldSpaceParticlesCallback);
if (partsys->getNumParents() && partsys->getParent(0)->getNumParents())
transformInitialParticles(partsys, partsys->getParent(0)->getParent(0));
} }
partsys->setNodeMask(mMask); partsys->setNodeMask(mMask);
} }
} }
void transformInitialParticles(osgParticle::ParticleSystem* partsys, osg::Node* node)
{
osg::NodePathList nodepaths = node->getParentalNodePaths();
if (nodepaths.empty())
return;
osg::Matrixf worldMat = osg::computeLocalToWorld(nodepaths[0]);
worldMat.orthoNormalize(worldMat); // scale is already applied on the particle node
for (int i=0; i<partsys->numParticles(); ++i)
{
partsys->getParticle(i)->transformPositionVelocity(worldMat);
}
// transform initial bounds to worldspace
osg::BoundingSphere sphere(partsys->getInitialBound());
SceneUtil::transformBoundingSphere(worldMat, sphere);
osg::BoundingBox box;
box.expandBy(sphere);
partsys->setInitialBound(box);
}
private: private:
unsigned int mMask; unsigned int mMask;
}; };
@ -489,7 +503,7 @@ namespace Resource
// we can skip any scene graphs without update callbacks since we know that particle emitters will have an update callback set // we can skip any scene graphs without update callbacks since we know that particle emitters will have an update callback set
if (node->getNumChildrenRequiringUpdateTraversal() > 0) if (node->getNumChildrenRequiringUpdateTraversal() > 0)
{ {
InitWorldSpaceParticlesVisitor visitor (mParticleSystemMask); InitParticlesVisitor visitor (mParticleSystemMask);
node->accept(visitor); node->accept(visitor);
} }
} }
@ -571,4 +585,15 @@ namespace Resource
mInstanceCache->removeUnreferencedObjectsInCache(); mInstanceCache->removeUnreferencedObjectsInCache();
} }
void SceneManager::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
stats->setAttribute(frameNumber, "Compiling", mIncrementalCompileOperation->getToCompile().size());
}
stats->setAttribute(frameNumber, "Node", mCache->getCacheSize());
stats->setAttribute(frameNumber, "Node Instance", mInstanceCache->getCacheSize());
}
} }

@ -139,6 +139,8 @@ namespace Resource
/// @see ResourceManager::updateCache /// @see ResourceManager::updateCache
virtual void updateCache(double referenceTime); virtual void updateCache(double referenceTime);
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats);
private: private:
osg::ref_ptr<osg::Node> createInstance(const std::string& name); osg::ref_ptr<osg::Node> createInstance(const std::string& name);

@ -0,0 +1,316 @@
#include "stats.hpp"
#include <sstream>
#include <iomanip>
#include <osg/PolygonMode>
#include <osgText/Text>
#include <osgViewer/Viewer>
#include <osgViewer/Renderer>
namespace Resource
{
StatsHandler::StatsHandler():
_key(osgGA::GUIEventAdapter::KEY_F4),
_initialized(false),
_statsType(false),
_statsWidth(1280.0f),
_statsHeight(1024.0f),
_font("fonts/arial.ttf"),
_characterSize(20.0f)
{
_camera = new osg::Camera;
_camera->getOrCreateStateSet()->setGlobalDefaults();
_camera->setRenderer(new osgViewer::Renderer(_camera.get()));
_camera->setProjectionResizePolicy(osg::Camera::FIXED);
_resourceStatsChildNum = 0;
}
bool StatsHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
{
if (ea.getHandled()) return false;
switch(ea.getEventType())
{
case(osgGA::GUIEventAdapter::KEYDOWN):
{
if (ea.getKey()== _key)
{
osgViewer::View* myview = dynamic_cast<osgViewer::View*>(&aa);
if (!myview) return false;
osgViewer::ViewerBase* viewer = myview->getViewerBase();
toggle(viewer);
aa.requestRedraw();
return true;
}
break;
}
case osgGA::GUIEventAdapter::RESIZE:
{
setWindowSize(ea.getWindowWidth(), ea.getWindowHeight());
break;
}
default:
break;
}
return false;
}
void StatsHandler::setWindowSize(int width, int height)
{
if (width <= 0 || height <= 0)
return;
_camera->setViewport(0, 0, width, height);
if (fabs(height*_statsWidth) <= fabs(width*_statsHeight))
{
_camera->setProjectionMatrix(osg::Matrix::ortho2D(_statsWidth - width*_statsHeight/height, _statsWidth,0.0,_statsHeight));
}
else
{
_camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0,_statsWidth,_statsHeight-height*_statsWidth/width,_statsHeight));
}
}
void StatsHandler::toggle(osgViewer::ViewerBase *viewer)
{
if (!_initialized)
{
setUpHUDCamera(viewer);
setUpScene(viewer);
}
_statsType = !_statsType;
if (!_statsType)
{
_camera->setNodeMask(0);
_switch->setAllChildrenOff();
viewer->getViewerStats()->collectStats("resource", false);
}
else
{
_camera->setNodeMask(0xffffffff);
_switch->setSingleChildOn(_resourceStatsChildNum);
viewer->getViewerStats()->collectStats("resource", true);
}
}
void StatsHandler::setUpHUDCamera(osgViewer::ViewerBase* viewer)
{
// Try GraphicsWindow first so we're likely to get the main viewer window
osg::GraphicsContext* context = dynamic_cast<osgViewer::GraphicsWindow*>(_camera->getGraphicsContext());
if (!context)
{
osgViewer::Viewer::Windows windows;
viewer->getWindows(windows);
if (!windows.empty()) context = windows.front();
else
{
// No GraphicsWindows were found, so let's try to find a GraphicsContext
context = _camera->getGraphicsContext();
if (!context)
{
osgViewer::Viewer::Contexts contexts;
viewer->getContexts(contexts);
if (contexts.empty()) return;
context = contexts.front();
}
}
}
_camera->setGraphicsContext(context);
_camera->setRenderOrder(osg::Camera::POST_RENDER, 11);
_camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
_camera->setViewMatrix(osg::Matrix::identity());
setWindowSize(context->getTraits()->width, context->getTraits()->height);
// only clear the depth buffer
_camera->setClearMask(0);
_camera->setAllowEventFocus(false);
_camera->setRenderer(new osgViewer::Renderer(_camera.get()));
_initialized = true;
}
osg::Geometry* createBackgroundRectangle(const osg::Vec3& pos, const float width, const float height, osg::Vec4& color)
{
osg::StateSet *ss = new osg::StateSet;
osg::Geometry* geometry = new osg::Geometry;
geometry->setUseDisplayList(false);
geometry->setStateSet(ss);
osg::Vec3Array* vertices = new osg::Vec3Array;
geometry->setVertexArray(vertices);
vertices->push_back(osg::Vec3(pos.x(), pos.y(), 0));
vertices->push_back(osg::Vec3(pos.x(), pos.y()-height,0));
vertices->push_back(osg::Vec3(pos.x()+width, pos.y()-height,0));
vertices->push_back(osg::Vec3(pos.x()+width, pos.y(),0));
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(color);
geometry->setColorArray(colors, osg::Array::BIND_OVERALL);
osg::DrawElementsUShort *base = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_FAN,0);
base->push_back(0);
base->push_back(1);
base->push_back(2);
base->push_back(3);
geometry->addPrimitiveSet(base);
return geometry;
}
class ResourceStatsTextDrawCallback : public osg::Drawable::DrawCallback
{
public:
ResourceStatsTextDrawCallback(osg::Stats* stats, const std::vector<std::string>& statNames)
: _stats(stats)
, _statNames(statNames)
{
}
virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const
{
if (!_stats) return;
osgText::Text* text = (osgText::Text*)(drawable);
std::ostringstream viewStr;
viewStr.setf(std::ios::left, std::ios::adjustfield);
viewStr.width(14);
// Used fixed formatting, as scientific will switch to "...e+.." notation for
// large numbers of vertices/drawables/etc.
viewStr.setf(std::ios::fixed);
viewStr.precision(0);
unsigned int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber()-1;
for (std::vector<std::string>::const_iterator it = _statNames.begin(); it != _statNames.end(); ++it)
{
if (it->empty())
viewStr << std::endl;
else
{
double value = 0.0;
if (_stats->getAttribute(frameNumber, *it, value))
viewStr << std::setw(8) << value << std::endl;
else
viewStr << std::setw(8) << "." << std::endl;
}
}
text->setText(viewStr.str());
text->drawImplementation(renderInfo);
}
osg::ref_ptr<osg::Stats> _stats;
std::vector<std::string> _statNames;
};
void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer)
{
_switch = new osg::Switch;
_camera->addChild(_switch);
osg::StateSet* stateset = _switch->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
#ifdef OSG_GL1_AVAILABLE
stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED);
#endif
osg::Vec3 pos(_statsWidth-300.f, _statsHeight-500.0f,0.0f);
osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3);
osg::Vec4 staticTextColor(1.0, 1.0, 0.0f, 1.0);
osg::Vec4 dynamicTextColor(1.0, 1.0, 1.0f, 1.0);
float backgroundMargin = 5;
float backgroundSpacing = 3;
// resource stats
{
osg::Group* group = new osg::Group;
group->setCullingActive(false);
_resourceStatsChildNum = _switch->getNumChildren();
_switch->addChild(group, false);
const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Cell", "Terrain Texture", "", "UnrefQueue"};
int numLines = sizeof(statNames) / sizeof(statNames[0]);
group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0),
10 * _characterSize + 2 * backgroundMargin,
numLines * _characterSize + 2 * backgroundMargin,
backgroundColor));
osg::ref_ptr<osgText::Text> staticText = new osgText::Text;
group->addChild( staticText.get() );
staticText->setColor(staticTextColor);
staticText->setFont(_font);
staticText->setCharacterSize(_characterSize);
staticText->setPosition(pos);
std::ostringstream viewStr;
viewStr.clear();
viewStr.setf(std::ios::left, std::ios::adjustfield);
viewStr.width(14);
for (size_t i = 0; i<sizeof(statNames)/sizeof(statNames[0]); ++i)
{
viewStr << statNames[i] << std::endl;
}
staticText->setText(viewStr.str());
pos.x() += 10 * _characterSize + 2 * backgroundMargin + backgroundSpacing;
group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0),
5 * _characterSize + 2 * backgroundMargin,
numLines * _characterSize + 2 * backgroundMargin,
backgroundColor));
osg::ref_ptr<osgText::Text> statsText = new osgText::Text;
group->addChild( statsText.get() );
statsText->setColor(dynamicTextColor);
statsText->setFont(_font);
statsText->setCharacterSize(_characterSize);
statsText->setPosition(pos);
statsText->setText("");
statsText->setDrawCallback(new ResourceStatsTextDrawCallback(viewer->getViewerStats(), std::vector<std::string>(statNames, statNames + numLines)));
}
}
void StatsHandler::getUsage(osg::ApplicationUsage &usage) const
{
usage.addKeyboardMouseBinding(_key, "On screen resource usage stats.");
}
}

@ -0,0 +1,59 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_STATS_H
#define OPENMW_COMPONENTS_RESOURCE_STATS_H
#include <osgGA/GUIEventHandler>
namespace osgViewer
{
class ViewerBase;
}
namespace osg
{
class Switch;
}
namespace Resource
{
class StatsHandler : public osgGA::GUIEventHandler
{
public:
StatsHandler();
void setKey(int key) { _key = key; }
int getKey() const { return _key; }
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
void setWindowSize(int w, int h);
void toggle(osgViewer::ViewerBase* viewer);
void setUpHUDCamera(osgViewer::ViewerBase* viewer);
void setUpScene(osgViewer::ViewerBase* viewer);
/** Get the keyboard and mouse usage of this manipulator.*/
virtual void getUsage(osg::ApplicationUsage& usage) const;
private:
osg::ref_ptr<osg::Switch> _switch;
int _key;
osg::ref_ptr<osg::Camera> _camera;
bool _initialized;
bool _statsType;
float _statsWidth;
float _statsHeight;
std::string _font;
float _leftPos;
float _characterSize;
int _resourceStatsChildNum;
};
}
#endif

@ -44,4 +44,9 @@ namespace SceneUtil
mWorkItem = new UnrefWorkItem; mWorkItem = new UnrefWorkItem;
} }
unsigned int UnrefQueue::getNumItems() const
{
return mWorkItem->mObjects.size();
}
} }

@ -23,6 +23,8 @@ namespace SceneUtil
/// Call from the main thread. /// Call from the main thread.
void flush(SceneUtil::WorkQueue* workQueue); void flush(SceneUtil::WorkQueue* workQueue);
unsigned int getNumItems() const;
private: private:
osg::ref_ptr<UnrefWorkItem> mWorkItem; osg::ref_ptr<UnrefWorkItem> mWorkItem;
}; };

@ -100,6 +100,23 @@ osg::ref_ptr<WorkItem> WorkQueue::removeWorkItem()
return NULL; return NULL;
} }
unsigned int WorkQueue::getNumItems() const
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
return mQueue.size();
}
unsigned int WorkQueue::getNumActiveThreads() const
{
unsigned int count = 0;
for (unsigned int i=0; i<mThreads.size(); ++i)
{
if (mThreads[i]->isActive())
++count;
}
return count;
}
WorkThread::WorkThread(WorkQueue *workQueue) WorkThread::WorkThread(WorkQueue *workQueue)
: mWorkQueue(workQueue) : mWorkQueue(workQueue)
{ {
@ -112,9 +129,16 @@ void WorkThread::run()
osg::ref_ptr<WorkItem> item = mWorkQueue->removeWorkItem(); osg::ref_ptr<WorkItem> item = mWorkQueue->removeWorkItem();
if (!item) if (!item)
return; return;
mActive = true;
item->doWork(); item->doWork();
item->signalDone(); item->signalDone();
mActive = false;
} }
} }
bool WorkThread::isActive() const
{
return mActive;
}
} }

@ -61,11 +61,15 @@ namespace SceneUtil
/// @par Used internally by the WorkThread. /// @par Used internally by the WorkThread.
osg::ref_ptr<WorkItem> removeWorkItem(); osg::ref_ptr<WorkItem> removeWorkItem();
unsigned int getNumItems() const;
unsigned int getNumActiveThreads() const;
private: private:
bool mIsReleased; bool mIsReleased;
std::deque<osg::ref_ptr<WorkItem> > mQueue; std::deque<osg::ref_ptr<WorkItem> > mQueue;
OpenThreads::Mutex mMutex; mutable OpenThreads::Mutex mMutex;
OpenThreads::Condition mCondition; OpenThreads::Condition mCondition;
std::vector<WorkThread*> mThreads; std::vector<WorkThread*> mThreads;
@ -79,8 +83,11 @@ namespace SceneUtil
virtual void run(); virtual void run();
bool isActive() const;
private: private:
WorkQueue* mWorkQueue; WorkQueue* mWorkQueue;
volatile bool mActive;
}; };

@ -89,18 +89,16 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
if (!evt.key.repeat) if (!evt.key.repeat)
mKeyboardListener->keyPressed(evt.key); mKeyboardListener->keyPressed(evt.key);
// temporary for the stats viewer if (evt.key.keysym.sym >= SDLK_F1 && evt.key.keysym.sym <= SDLK_F12)
if (evt.key.keysym.sym == SDLK_F3) mViewer->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_F1 + (evt.key.keysym.sym - SDLK_F1));
mViewer->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_F3);
break; break;
case SDL_KEYUP: case SDL_KEYUP:
if (!evt.key.repeat) if (!evt.key.repeat)
mKeyboardListener->keyReleased(evt.key); mKeyboardListener->keyReleased(evt.key);
// temporary for the stats viewer if (evt.key.keysym.sym >= SDLK_F1 && evt.key.keysym.sym <= SDLK_F12)
if (evt.key.keysym.sym == SDLK_F3) mViewer->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_F1 + (evt.key.keysym.sym - SDLK_F1));
mViewer->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_F3);
break; break;
case SDL_TEXTEDITING: case SDL_TEXTEDITING:

@ -314,4 +314,16 @@ void TerrainGrid::updateTextureFiltering()
mResourceSystem->getSceneManager()->applyFilterSettings(it->second); mResourceSystem->getSceneManager()->applyFilterSettings(it->second);
} }
void TerrainGrid::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
stats->setAttribute(frameNumber, "Terrain Cell", mGridCache.size());
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mTextureCacheMutex);
stats->setAttribute(frameNumber, "Terrain Texture", mTextureCache.size());
}
}
} }

@ -49,6 +49,8 @@ namespace Terrain
/// @note Thread safe. /// @note Thread safe.
void updateTextureFiltering(); void updateTextureFiltering();
void reportStats(unsigned int frameNumber, osg::Stats *stats);
private: private:
osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter);

@ -9,6 +9,7 @@
namespace osg namespace osg
{ {
class Group; class Group;
class Stats;
} }
namespace osgUtil namespace osgUtil
@ -43,6 +44,8 @@ namespace Terrain
virtual void updateCache() {} virtual void updateCache() {}
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}
float getHeightAt (const osg::Vec3f& worldPos); float getHeightAt (const osg::Vec3f& worldPos);
virtual osg::ref_ptr<osg::Node> cacheCell(int x, int y) {return NULL;} virtual osg::ref_ptr<osg::Node> cacheCell(int x, int y) {return NULL;}

@ -26,6 +26,8 @@ preload enabled
Controls whether textures and objects will be pre-loaded in background threads. This setting being enabled should result in a reduced amount of loading screens, no impact on frame rate and a varying amount of additional RAM usage, depending on how the preloader was configured (see the below settings). The default preloading settings with vanilla game files should only use negligible amounts of RAM, however, when using high-res texture and model replacers it may be necessary to tweak these settings to prevent the game from running out of memory. Controls whether textures and objects will be pre-loaded in background threads. This setting being enabled should result in a reduced amount of loading screens, no impact on frame rate and a varying amount of additional RAM usage, depending on how the preloader was configured (see the below settings). The default preloading settings with vanilla game files should only use negligible amounts of RAM, however, when using high-res texture and model replacers it may be necessary to tweak these settings to prevent the game from running out of memory.
The effects of (pre-)loading can be observed on the in-game statistics panel brought up with the 'F4' key.
All settings starting with 'preload' in this section will have no effect if preloading is disabled, and can only be configured by editing the settings configuration file. All settings starting with 'preload' in this section will have no effect if preloading is disabled, and can only be configured by editing the settings configuration file.

Loading…
Cancel
Save