Merge pull request #167 from OpenMW/master

Add OpenMW commits up to 24 Feb 2017
coverity_scan^2
David Cernat 8 years ago committed by GitHub
commit 328e3b6618

@ -160,11 +160,12 @@ static void gdb_info(pid_t pid)
printf("Executing: %s\n", cmd_buf);
fflush(stdout);
{ /* another special exception for "ignoring return value..." */
int unused;
unused = system(cmd_buf);
UNUSED(unused);
}
int ret = system(cmd_buf);
if (ret != 0)
printf("\nFailed to create a crash report. Please install 'gdb' and crash again!\n");
fflush(stdout);
/* Clean up */
remove(respfile);
}

@ -32,9 +32,14 @@ Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape>
if (std::abs(mHalfExtents.x()-mHalfExtents.y())<mHalfExtents.x()*0.05 && mHalfExtents.z() >= mHalfExtents.x())
{
mShape.reset(new btCapsuleShapeZ(mHalfExtents.x(), 2*mHalfExtents.z() - 2*mHalfExtents.x()));
mRotationallyInvariant = true;
}
else
{
mShape.reset(new btBoxShape(toBullet(mHalfExtents)));
mRotationallyInvariant = false;
}
mConvexShape = static_cast<btConvexShape*>(mShape.get());
mCollisionObject.reset(new btCollisionObject);
@ -144,6 +149,11 @@ void Actor::updateRotation ()
updateCollisionObjectPosition();
}
bool Actor::isRotationallyInvariant() const
{
return mRotationallyInvariant;
}
void Actor::updateScale()
{
float scale = mPtr.getCellRef().getScale();

@ -72,6 +72,11 @@ namespace MWPhysics
void updateScale();
void updateRotation();
/**
* Return true if the collision shape looks the same no matter how its Z rotated.
*/
bool isRotationallyInvariant() const;
/**
* Set mPosition and mPreviousPosition to the position in the Ptr's RefData. This should be used
* when an object is "instantly" moved/teleported as opposed to being moved by the physics simulation.
@ -155,6 +160,8 @@ namespace MWPhysics
bool mCanWaterWalk;
bool mWalkingOnWater;
bool mRotationallyInvariant;
std::auto_ptr<btCollisionShape> mShape;
btConvexShape* mConvexShape;

@ -1294,8 +1294,11 @@ namespace MWPhysics
ActorMap::iterator foundActor = mActors.find(ptr);
if (foundActor != mActors.end())
{
foundActor->second->updateRotation();
mCollisionWorld->updateSingleAabb(foundActor->second->getCollisionObject());
if (!foundActor->second->isRotationallyInvariant())
{
foundActor->second->updateRotation();
mCollisionWorld->updateSingleAabb(foundActor->second->getCollisionObject());
}
return;
}
}
@ -1416,13 +1419,18 @@ namespace MWPhysics
osg::Vec3f position = physicActor->getPosition();
float oldHeight = position.z();
bool positionChanged = false;
for (int i=0; i<numSteps; ++i)
{
position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, physicsDt,
world->isFlying(iter->first),
waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
physicActor->setPosition(position);
if (position != physicActor->getPosition())
positionChanged = true;
physicActor->setPosition(position); // always set even if unchanged to make sure interpolation is correct
}
if (positionChanged)
mCollisionWorld->updateSingleAabb(physicActor->getCollisionObject());
float interpolationFactor = mTimeAccum / physicsDt;
osg::Vec3f interpolated = position * interpolationFactor + physicActor->getPreviousPosition() * (1.f - interpolationFactor);

@ -122,7 +122,6 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
if (found == nodeMap.end())
throw std::runtime_error("Can't find attachment node " + bonename);
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(node, mObjectRoot, bonename, found->second.get());
mResourceSystem->getSceneManager()->notifyAttached(attached);
scene.reset(new PartHolder(attached));

@ -52,7 +52,6 @@ void EffectManager::addEffect(const std::string &model, const std::string& textu
overrideTexture(textureOverride, mResourceSystem, node);
mParentNode->addChild(trans);
mResourceSystem->getSceneManager()->notifyAttached(node);
mEffects[trans] = effect;
}

@ -269,6 +269,7 @@ const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap();
NpcAnimation::~NpcAnimation()
{
mAmmunition.reset();
}
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
@ -662,7 +663,6 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st
throw std::runtime_error("Can't find attachment node " + bonename);
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, found->second);
mResourceSystem->getSceneManager()->notifyAttached(attached);
if (enchantedGlow)
addGlow(attached, *glowColor);
@ -880,6 +880,7 @@ void NpcAnimation::addControllers()
void NpcAnimation::showWeapons(bool showWeapon)
{
mShowWeapons = showWeapon;
mAmmunition.reset();
if(showWeapon)
{
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
@ -898,11 +899,7 @@ void NpcAnimation::showWeapons(bool showWeapon)
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt)
attachArrow();
else
mAmmunition.reset();
}
else
mAmmunition.reset();
}
}
else

@ -136,15 +136,14 @@ namespace MWWorld
template<typename T>
const T *Store<T>::search(const std::string &id) const
{
T item;
item.mId = Misc::StringUtils::lowerCase(id);
std::string idLower = Misc::StringUtils::lowerCase(id);
typename Dynamic::const_iterator dit = mDynamic.find(item.mId);
typename Dynamic::const_iterator dit = mDynamic.find(idLower);
if (dit != mDynamic.end()) {
return &dit->second;
}
typename std::map<std::string, T>::const_iterator it = mStatic.find(item.mId);
typename std::map<std::string, T>::const_iterator it = mStatic.find(idLower);
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
return &(it->second);
@ -274,10 +273,9 @@ namespace MWWorld
template<typename T>
bool Store<T>::eraseStatic(const std::string &id)
{
T item;
item.mId = Misc::StringUtils::lowerCase(id);
std::string idLower = Misc::StringUtils::lowerCase(id);
typename std::map<std::string, T>::iterator it = mStatic.find(item.mId);
typename std::map<std::string, T>::iterator it = mStatic.find(idLower);
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
// delete from the static part of mShared
@ -285,7 +283,7 @@ namespace MWWorld
typename std::vector<T *>::iterator end = sharedIter + mStatic.size();
while (sharedIter != mShared.end() && sharedIter != end) {
if((*sharedIter)->mId == item.mId) {
if((*sharedIter)->mId == idLower) {
mShared.erase(sharedIter);
break;
}

@ -53,7 +53,7 @@ add_component_dir (shader
add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
)
add_component_dir (nif

@ -106,6 +106,14 @@ public:
lowerCaseInPlace(out);
return out;
}
struct CiComp
{
bool operator()(const std::string& left, const std::string& right) const
{
return ciLess(left, right);
}
};
};
}

@ -356,7 +356,7 @@ namespace NifOsg
osg::ref_ptr<TextKeyMapHolder> textkeys (new TextKeyMapHolder);
osg::ref_ptr<osg::Node> created = handleNode(nifNode, NULL, imageManager, std::vector<int>(), 0, false, &textkeys->mTextKeys);
osg::ref_ptr<osg::Node> created = handleNode(nifNode, NULL, imageManager, std::vector<int>(), 0, false, false, &textkeys->mTextKeys);
if (nif->getUseSkinning())
{
@ -416,44 +416,6 @@ namespace NifOsg
toSetup->setFunction(boost::shared_ptr<ControllerFunction>(new ControllerFunction(ctrl)));
}
void optimize (const Nif::Node* nifNode, osg::Group* node, bool skipMeshes)
{
// For nodes with an identity transform, remove the redundant Transform node
if (node->getDataVariance() == osg::Object::STATIC
// For TriShapes, we can only collapse the node, but not completely remove it,
// if the link to animated collision shapes is supposed to stay intact.
&& (nifNode->recType != Nif::RC_NiTriShape || !skipMeshes)
// Don't optimize drawables with controllers, that creates issues when we want to deep copy controllers without deep copying the drawable that holds the controller.
// A deep copy of controllers may be needed to independently animate multiple copies of the same mesh.
&& !node->getUpdateCallback())
{
if (node->getNumParents() && nifNode->trafo.isIdentity())
{
osg::Group* parent = node->getParent(0);
// can be multiple children in case of ParticleSystems, with the extra ParticleSystemUpdater node
for (unsigned int i=0; i<node->getNumChildren(); ++i)
{
osg::Node* child = node->getChild(i);
if (i == node->getNumChildren()-1) // FIXME: some nicer way to determine where our actual Drawable resides...
{
child->addUpdateCallback(node->getUpdateCallback());
child->setStateSet(node->getStateSet());
child->setName(node->getName());
// make sure to copy the UserDataContainer with the record index, so that connections to an animated collision shape don't break
child->setUserDataContainer(node->getUserDataContainer());
}
parent->addChild(child);
}
node->removeChildren(0, node->getNumChildren());
parent->removeChild(node);
}
}
// For NiTriShapes *with* a valid transform, perhaps we could apply the transform to the vertices.
// Need to make sure that won't break transparency sorting. Check what the original engine is doing?
}
osg::ref_ptr<osg::LOD> handleLodNode(const Nif::NiLODNode* niLodNode)
{
osg::ref_ptr<osg::LOD> lod (new osg::LOD);
@ -549,15 +511,12 @@ namespace NifOsg
stateset->addUniform(new osg::Uniform("envMapColor", osg::Vec4f(1,1,1,1)));
}
osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager,
std::vector<int> boundTextures, int animflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL)
// Get a default dataVariance for this node to be used as a hint by optimization (post)routines
osg::Object::DataVariance getDataVariance(const Nif::Node* nifNode)
{
if (rootNode != NULL && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box"))
return NULL;
if (nifNode->boneTrafo || nifNode->boneIndex != -1)
return osg::Object::DYNAMIC;
osg::ref_ptr<osg::Group> node = new osg::MatrixTransform(nifNode->trafo.toMatrix());
// Set a default DataVariance (used as hint by optimization routines).
switch (nifNode->recType)
{
case Nif::RC_NiTriShape:
@ -565,13 +524,29 @@ namespace NifOsg
case Nif::RC_NiRotatingParticles:
// Leaf nodes in the NIF hierarchy, so won't be able to dynamically attach children.
// No support for keyframe controllers (just crashes in the original engine).
node->setDataVariance(osg::Object::STATIC);
break;
return osg::Object::STATIC;
default:
// could have new children attached at any time, or added external keyframe controllers from .kf files
node->setDataVariance(osg::Object::DYNAMIC);
break;
return osg::Object::DYNAMIC;
}
}
osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager,
std::vector<int> boundTextures, int animflags, bool skipMeshes, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=NULL)
{
if (rootNode != NULL && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box"))
return NULL;
osg::Object::DataVariance dataVariance = getDataVariance(nifNode);
osg::ref_ptr<osg::Group> node;
if (dataVariance == osg::Object::STATIC && nifNode->trafo.isIdentity())
node = new osg::Group;
else
node = new osg::MatrixTransform(nifNode->trafo.toMatrix());
node->setDataVariance(dataVariance);
if (nifNode->controller.empty())
node->setDataVariance(osg::Object::STATIC);
if (nifNode->recType == Nif::RC_NiBillboardNode)
{
@ -586,6 +561,9 @@ namespace NifOsg
node->setDataVariance(osg::Object::STATIC);
}
if (!nifNode->controller.empty() && nifNode->controller->recType == Nif::RC_NiKeyframeController)
isAnimated = true;
node->setName(nifNode->name);
if (parentNode)
@ -635,6 +613,11 @@ namespace NifOsg
node->setNodeMask(0x1);
}
if (skipMeshes && isAnimated) // make sure the empty node is not optimized away so the physicssystem can find it.
{
node->setDataVariance(osg::Object::DYNAMIC);
}
// We can skip creating meshes for hidden nodes if they don't have a VisController that
// might make them visible later
if (nifNode->flags & Nif::NiNode::Flag_Hidden)
@ -678,9 +661,6 @@ namespace NifOsg
if (!nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC)
handleNodeControllers(nifNode, static_cast<osg::MatrixTransform*>(node.get()), animflags);
// Optimization pass
optimize(nifNode, node, skipMeshes);
if (nifNode->recType == Nif::RC_NiLODNode)
{
@ -704,7 +684,7 @@ namespace NifOsg
for(size_t i = 0;i < children.length();++i)
{
if(!children[i].empty())
handleNode(children[i].getPtr(), node, imageManager, boundTextures, animflags, skipMeshes, textKeys, rootNode);
handleNode(children[i].getPtr(), node, imageManager, boundTextures, animflags, skipMeshes, isAnimated, textKeys, rootNode);
}
}

@ -14,11 +14,14 @@
#include <components/nifosg/nifloader.hpp>
#include <components/nif/niffile.hpp>
#include <components/misc/stringops.hpp>
#include <components/vfs/manager.hpp>
#include <components/sceneutil/clone.hpp>
#include <components/sceneutil/util.hpp>
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/optimizer.hpp>
#include <components/shader/shadervisitor.hpp>
#include <components/shader/shadermanager.hpp>
@ -105,6 +108,20 @@ namespace
namespace Resource
{
class SharedStateManager : public osgDB::SharedStateManager
{
public:
unsigned int getNumSharedTextures() const
{
return _sharedTextureList.size();
}
unsigned int getNumSharedStateSets() const
{
return _sharedStateSetList.size();
}
};
/// Set texture filtering settings on textures contained in a FlipController.
class SetFilterSettingsControllerVisitor : public SceneUtil::ControllerVisitor
{
@ -195,7 +212,7 @@ namespace Resource
, mAutoUseNormalMaps(false)
, mAutoUseSpecularMaps(false)
, mInstanceCache(new MultiObjectCache)
, mSharedStateManager(new osgDB::SharedStateManager)
, mSharedStateManager(new SharedStateManager)
, mImageManager(imageManager)
, mNifFileManager(nifFileManager)
, mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)
@ -359,6 +376,61 @@ namespace Resource
}
}
class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback
{
public:
bool isReservedName(const std::string& name) const
{
static std::set<std::string, Misc::StringUtils::CiComp> reservedNames;
if (reservedNames.empty())
{
const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm", "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle", "Left Clavicle", "Weapon Bone", "Tail",
"Bip01 L Hand", "Bip01 R Hand", "Bip01 Head", "Bip01 Spine1", "Bip01 Spine2", "Bip01 L Clavicle", "Bip01 R Clavicle", "bip01", "Root Bone", "Bip01 Neck",
"BoneOffset", "AttachLight", "ArrowBone", "Camera"};
reservedNames = std::set<std::string, Misc::StringUtils::CiComp>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
}
return reservedNames.find(name) != reservedNames.end();
}
virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const
{
if (option & SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS)
{
if (node->asGeometry() && node->className() == std::string("Geometry"))
return true;
else
return false; //ParticleSystem would have to convert space of all the processors, RigGeometry would have to convert bones... theoretically possible, but very complicated
}
return (option & optimizer->getPermissibleOptimizationsForObject(node))!=0;
}
virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const
{
if (node->getNumDescriptions()>0) return false;
if (node->getDataVariance() == osg::Object::DYNAMIC) return false;
if (isReservedName(node->getName())) return false;
return (option & optimizer->getPermissibleOptimizationsForObject(node))!=0;
}
};
bool canOptimize(const std::string& filename)
{
// xmesh.nif can not be optimized because there are keyframes added in post
size_t slashpos = filename.find_last_of("\\/");
if (slashpos != std::string::npos && slashpos+1 < filename.size())
{
std::string basename = filename.substr(slashpos+1);
if (!basename.empty() && basename[0] == 'x')
return false;
}
// For spell VFX, DummyXX nodes must remain intact. Not adding those to reservedNames to avoid being overly cautious - instead, decide on filename
if (filename.find("vfx_pattern") != std::string::npos)
return false;
return true;
}
osg::ref_ptr<const osg::Node> SceneManager::getTemplate(const std::string &name)
{
std::string normalized = name;
@ -414,10 +486,20 @@ namespace Resource
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.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);
optimizer.optimize(loaded, SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY);
}
if (mIncrementalCompileOperation)
mIncrementalCompileOperation->add(loaded);
@ -458,6 +540,13 @@ namespace Resource
// add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache
cloned->getOrCreateUserDataContainer()->addUserObject(new TemplateRef(scene));
// we can skip any scene graphs without update callbacks since we know that particle emitters will have an update callback set
if (cloned->getNumChildrenRequiringUpdateTraversal() > 0)
{
InitParticlesVisitor visitor (mParticleSystemMask);
cloned->accept(visitor);
}
return cloned;
}
@ -484,7 +573,6 @@ namespace Resource
void SceneManager::attachTo(osg::Node *instance, osg::Group *parentNode) const
{
parentNode->addChild(instance);
notifyAttached(instance);
}
void SceneManager::releaseGLObjects(osg::State *state)
@ -498,16 +586,6 @@ namespace Resource
mIncrementalCompileOperation = ico;
}
void SceneManager::notifyAttached(osg::Node *node) const
{
// 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)
{
InitParticlesVisitor visitor (mParticleSystemMask);
node->accept(visitor);
}
}
Resource::ImageManager* SceneManager::getImageManager()
{
return mImageManager;
@ -592,6 +670,12 @@ namespace Resource
stats->setAttribute(frameNumber, "Compiling", mIncrementalCompileOperation->getToCompile().size());
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSharedStateMutex);
stats->setAttribute(frameNumber, "Texture", mSharedStateManager->getNumSharedTextures());
stats->setAttribute(frameNumber, "StateSet", mSharedStateManager->getNumSharedStateSets());
}
stats->setAttribute(frameNumber, "Node", mCache->getCacheSize());
stats->setAttribute(frameNumber, "Node Instance", mInstanceCache->getCacheSize());
}

@ -15,6 +15,7 @@ namespace Resource
{
class ImageManager;
class NifFileManager;
class SharedStateManager;
}
namespace osgUtil
@ -116,9 +117,6 @@ namespace Resource
/// Set up an IncrementalCompileOperation for background compiling of loaded scenes.
void setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico);
/// @note SceneManager::attachTo calls this method automatically, only needs to be called by users if manually attaching
void notifyAttached(osg::Node* node) const;
Resource::ImageManager* getImageManager();
/// @param mask The node mask to apply to loaded particle system nodes.
@ -157,7 +155,7 @@ namespace Resource
osg::ref_ptr<MultiObjectCache> mInstanceCache;
osg::ref_ptr<osgDB::SharedStateManager> mSharedStateManager;
osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;
OpenThreads::Mutex mSharedStateMutex;
Resource::ImageManager* mImageManager;

@ -259,7 +259,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer)
_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"};
const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Cell", "Terrain Texture", "", "UnrefQueue"};
int numLines = sizeof(statNames) / sizeof(statNames[0]);

@ -121,6 +121,10 @@ namespace SceneUtil
trans->setPosition(boneOffset->getMatrix().getTrans());
// The BoneOffset rotation seems to be incorrect
trans->setAttitude(osg::Quat(osg::DegreesToRadians(-90.f), osg::Vec3f(1,0,0)));
// Now that we used it, get rid of the redundant node.
if (boneOffset->getNumChildren() == 0 && boneOffset->getNumParents() == 1)
boneOffset->getParent(0)->removeChild(boneOffset);
}
if (attachNode->getName().find("Left") != std::string::npos)

File diff suppressed because it is too large Load Diff

@ -0,0 +1,432 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
/* Modified for OpenMW */
#ifndef OPENMW_OSGUTIL_OPTIMIZER
#define OPENMW_OSGUTIL_OPTIMIZER
#include <osg/NodeVisitor>
#include <osg/Matrix>
#include <osg/Geometry>
#include <osg/Transform>
#include <osg/Texture2D>
//#include <osgUtil/Export>
#include <set>
//namespace osgUtil {
namespace SceneUtil {
// forward declare
class Optimizer;
/** Helper base class for implementing Optimizer techniques.*/
class BaseOptimizerVisitor : public osg::NodeVisitor
{
public:
BaseOptimizerVisitor(Optimizer* optimizer, unsigned int operation):
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_optimizer(optimizer),
_operationType(operation)
{
setNodeMaskOverride(0xffffffff);
}
inline bool isOperationPermissibleForObject(const osg::StateSet* object) const;
inline bool isOperationPermissibleForObject(const osg::StateAttribute* object) const;
inline bool isOperationPermissibleForObject(const osg::Drawable* object) const;
inline bool isOperationPermissibleForObject(const osg::Node* object) const;
protected:
Optimizer* _optimizer;
unsigned int _operationType;
};
/** Traverses scene graph to improve efficiency. See OptimizationOptions.
* For example of usage see examples/osgimpostor or osgviewer.
*/
class Optimizer
{
public:
Optimizer() {}
virtual ~Optimizer() {}
enum OptimizationOptions
{
FLATTEN_STATIC_TRANSFORMS = (1 << 0),
REMOVE_REDUNDANT_NODES = (1 << 1),
REMOVE_LOADED_PROXY_NODES = (1 << 2),
COMBINE_ADJACENT_LODS = (1 << 3),
SHARE_DUPLICATE_STATE = (1 << 4),
MERGE_GEOMETRY = (1 << 5),
CHECK_GEOMETRY = (1 << 6), // deprecated, currently no-op
MAKE_FAST_GEOMETRY = (1 << 7),
SPATIALIZE_GROUPS = (1 << 8),
COPY_SHARED_NODES = (1 << 9),
TRISTRIP_GEOMETRY = (1 << 10),
TESSELLATE_GEOMETRY = (1 << 11),
OPTIMIZE_TEXTURE_SETTINGS = (1 << 12),
MERGE_GEODES = (1 << 13),
FLATTEN_BILLBOARDS = (1 << 14),
TEXTURE_ATLAS_BUILDER = (1 << 15),
STATIC_OBJECT_DETECTION = (1 << 16),
FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS = (1 << 17),
INDEX_MESH = (1 << 18),
VERTEX_POSTTRANSFORM = (1 << 19),
VERTEX_PRETRANSFORM = (1 << 20),
DEFAULT_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS |
REMOVE_REDUNDANT_NODES |
REMOVE_LOADED_PROXY_NODES |
COMBINE_ADJACENT_LODS |
SHARE_DUPLICATE_STATE |
MERGE_GEOMETRY |
MAKE_FAST_GEOMETRY |
CHECK_GEOMETRY |
OPTIMIZE_TEXTURE_SETTINGS |
STATIC_OBJECT_DETECTION,
ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS |
REMOVE_REDUNDANT_NODES |
REMOVE_LOADED_PROXY_NODES |
COMBINE_ADJACENT_LODS |
SHARE_DUPLICATE_STATE |
MERGE_GEODES |
MERGE_GEOMETRY |
MAKE_FAST_GEOMETRY |
CHECK_GEOMETRY |
SPATIALIZE_GROUPS |
COPY_SHARED_NODES |
TRISTRIP_GEOMETRY |
OPTIMIZE_TEXTURE_SETTINGS |
TEXTURE_ATLAS_BUILDER |
STATIC_OBJECT_DETECTION
};
/** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/
void reset();
/** Traverse the node and its subgraph with a series of optimization
* visitors, specified by the OptimizationOptions.*/
virtual void optimize(osg::Node* node, unsigned int options);
/** Callback for customizing what operations are permitted on objects in the scene graph.*/
struct IsOperationPermissibleForObjectCallback : public osg::Referenced
{
virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::StateSet* stateset,unsigned int option) const
{
return optimizer->isOperationPermissibleForObjectImplementation(stateset,option);
}
virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::StateAttribute* attribute,unsigned int option) const
{
return optimizer->isOperationPermissibleForObjectImplementation(attribute,option);
}
virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::Drawable* drawable,unsigned int option) const
{
return optimizer->isOperationPermissibleForObjectImplementation(drawable,option);
}
virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::Node* node,unsigned int option) const
{
return optimizer->isOperationPermissibleForObjectImplementation(node,option);
}
};
/** Set the callback for customizing what operations are permitted on objects in the scene graph.*/
void setIsOperationPermissibleForObjectCallback(IsOperationPermissibleForObjectCallback* callback) { _isOperationPermissibleForObjectCallback=callback; }
/** Get the callback for customizing what operations are permitted on objects in the scene graph.*/
IsOperationPermissibleForObjectCallback* getIsOperationPermissibleForObjectCallback() { return _isOperationPermissibleForObjectCallback.get(); }
/** Get the callback for customizing what operations are permitted on objects in the scene graph.*/
const IsOperationPermissibleForObjectCallback* getIsOperationPermissibleForObjectCallback() const { return _isOperationPermissibleForObjectCallback.get(); }
inline void setPermissibleOptimizationsForObject(const osg::Object* object, unsigned int options)
{
_permissibleOptimizationsMap[object] = options;
}
inline unsigned int getPermissibleOptimizationsForObject(const osg::Object* object) const
{
PermissibleOptimizationsMap::const_iterator itr = _permissibleOptimizationsMap.find(object);
if (itr!=_permissibleOptimizationsMap.end()) return itr->second;
else return 0xffffffff;
}
inline bool isOperationPermissibleForObject(const osg::StateSet* object, unsigned int option) const
{
if (_isOperationPermissibleForObjectCallback.valid())
return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);
else
return isOperationPermissibleForObjectImplementation(object,option);
}
inline bool isOperationPermissibleForObject(const osg::StateAttribute* object, unsigned int option) const
{
if (_isOperationPermissibleForObjectCallback.valid())
return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);
else
return isOperationPermissibleForObjectImplementation(object,option);
}
inline bool isOperationPermissibleForObject(const osg::Drawable* object, unsigned int option) const
{
if (_isOperationPermissibleForObjectCallback.valid())
return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);
else
return isOperationPermissibleForObjectImplementation(object,option);
}
inline bool isOperationPermissibleForObject(const osg::Node* object, unsigned int option) const
{
if (_isOperationPermissibleForObjectCallback.valid())
return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);
else
return isOperationPermissibleForObjectImplementation(object,option);
}
bool isOperationPermissibleForObjectImplementation(const osg::StateSet* stateset, unsigned int option) const
{
return (option & getPermissibleOptimizationsForObject(stateset))!=0;
}
bool isOperationPermissibleForObjectImplementation(const osg::StateAttribute* attribute, unsigned int option) const
{
return (option & getPermissibleOptimizationsForObject(attribute))!=0;
}
bool isOperationPermissibleForObjectImplementation(const osg::Drawable* drawable, unsigned int option) const
{
if (option & (REMOVE_REDUNDANT_NODES|MERGE_GEOMETRY))
{
if (drawable->getUserData()) return false;
if (drawable->getUpdateCallback()) return false;
if (drawable->getEventCallback()) return false;
if (drawable->getCullCallback()) return false;
}
return (option & getPermissibleOptimizationsForObject(drawable))!=0;
}
bool isOperationPermissibleForObjectImplementation(const osg::Node* node, unsigned int option) const
{
if (option & (REMOVE_REDUNDANT_NODES|COMBINE_ADJACENT_LODS|FLATTEN_STATIC_TRANSFORMS))
{
if (node->getUserData()) return false;
if (node->getUpdateCallback()) return false;
if (node->getEventCallback()) return false;
if (node->getCullCallback()) return false;
if (node->getNumDescriptions()>0) return false;
if (node->getStateSet()) return false;
if (node->getNodeMask()!=0xffffffff) return false;
// if (!node->getName().empty()) return false;
}
return (option & getPermissibleOptimizationsForObject(node))!=0;
}
protected:
osg::ref_ptr<IsOperationPermissibleForObjectCallback> _isOperationPermissibleForObjectCallback;
typedef std::map<const osg::Object*,unsigned int> PermissibleOptimizationsMap;
PermissibleOptimizationsMap _permissibleOptimizationsMap;
public:
/** Flatten Static Transform nodes by applying their transform to the
* geometry on the leaves of the scene graph, then removing the
* now redundant transforms. Static transformed subgraphs that have multiple
* parental paths above them are not flattened, if you require this then
* the subgraphs have to be duplicated - for this use the
* FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor. */
class FlattenStaticTransformsVisitor : public BaseOptimizerVisitor
{
public:
FlattenStaticTransformsVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS) {}
virtual void apply(osg::Node& geode);
virtual void apply(osg::Drawable& drawable);
virtual void apply(osg::Billboard& geode);
virtual void apply(osg::Transform& transform);
bool removeTransforms(osg::Node* nodeWeCannotRemove);
protected:
typedef std::vector<osg::Transform*> TransformStack;
typedef std::set<osg::Drawable*> DrawableSet;
typedef std::set<osg::Billboard*> BillboardSet;
typedef std::set<osg::Node* > NodeSet;
typedef std::set<osg::Transform*> TransformSet;
TransformStack _transformStack;
NodeSet _excludedNodeSet;
DrawableSet _drawableSet;
BillboardSet _billboardSet;
TransformSet _transformSet;
};
/** Combine Static Transform nodes that sit above one another.*/
class CombineStaticTransformsVisitor : public BaseOptimizerVisitor
{
public:
CombineStaticTransformsVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS) {}
virtual void apply(osg::MatrixTransform& transform);
bool removeTransforms(osg::Node* nodeWeCannotRemove);
protected:
typedef std::set<osg::MatrixTransform*> TransformSet;
TransformSet _transformSet;
};
/** Remove rendundant nodes, such as groups with one single child.*/
class RemoveEmptyNodesVisitor : public BaseOptimizerVisitor
{
public:
typedef std::set<osg::Node*> NodeList;
NodeList _redundantNodeList;
RemoveEmptyNodesVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {}
virtual void apply(osg::Group& group);
void removeEmptyNodes();
};
/** Remove redundant nodes, such as groups with one single child.*/
class RemoveRedundantNodesVisitor : public BaseOptimizerVisitor
{
public:
typedef std::set<osg::Node*> NodeList;
NodeList _redundantNodeList;
RemoveRedundantNodesVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {}
virtual void apply(osg::Group& group);
virtual void apply(osg::Transform& transform);
bool isOperationPermissible(osg::Node& node);
void removeRedundantNodes();
};
/** Merge adjacent Groups that have the same StateSet. */
class MergeGroupsVisitor : public SceneUtil::BaseOptimizerVisitor
{
public:
MergeGroupsVisitor(SceneUtil::Optimizer* optimizer)
: BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES)
{
}
bool isOperationPermissible(osg::Group& node);
virtual void apply(osg::Group& group);
};
class MergeGeometryVisitor : public BaseOptimizerVisitor
{
public:
/// default to traversing all children.
MergeGeometryVisitor(Optimizer* optimizer=0) :
BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY),
_targetMaximumNumberOfVertices(10000), _allowedToMerge(true) {}
void setTargetMaximumNumberOfVertices(unsigned int num)
{
_targetMaximumNumberOfVertices = num;
}
unsigned int getTargetMaximumNumberOfVertices() const
{
return _targetMaximumNumberOfVertices;
}
void pushStateSet(osg::StateSet* stateSet);
void popStateSet();
void checkAllowedToMerge();
virtual void apply(osg::Group& group);
virtual void apply(osg::Billboard&) { /* don't do anything*/ }
bool mergeGroup(osg::Group& group);
static bool geometryContainsSharedArrays(osg::Geometry& geom);
static bool mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs);
static bool mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs);
static bool mergePrimitive(osg::DrawArrayLengths& lhs,osg::DrawArrayLengths& rhs);
static bool mergePrimitive(osg::DrawElementsUByte& lhs,osg::DrawElementsUByte& rhs);
static bool mergePrimitive(osg::DrawElementsUShort& lhs,osg::DrawElementsUShort& rhs);
static bool mergePrimitive(osg::DrawElementsUInt& lhs,osg::DrawElementsUInt& rhs);
protected:
unsigned int _targetMaximumNumberOfVertices;
std::vector<osg::StateSet*> _stateSetStack;
bool _allowedToMerge;
};
};
inline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::StateSet* object) const
{
return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) : true;
}
inline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::StateAttribute* object) const
{
return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) : true;
}
inline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::Drawable* object) const
{
return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) : true;
}
inline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::Node* object) const
{
return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) : true;
}
}
#endif

@ -78,18 +78,31 @@ osgDB::ObjectWrapper* makeDummySerializer(const std::string& classname)
return new osgDB::ObjectWrapper(createInstanceFunc<osg::DummyObject>, classname, "osg::Object");
}
class GeometrySerializer : public osgDB::ObjectWrapper
{
public:
GeometrySerializer()
: osgDB::ObjectWrapper(createInstanceFunc<osg::Drawable>, "osg::Geometry", "osg::Object osg::Drawable osg::Geometry")
{
}
};
void registerSerializers()
{
static bool done = false;
if (!done)
{
osgDB::Registry::instance()->getObjectWrapperManager()->addWrapper(new PositionAttitudeTransformSerializer);
osgDB::Registry::instance()->getObjectWrapperManager()->addWrapper(new SkeletonSerializer);
osgDB::Registry::instance()->getObjectWrapperManager()->addWrapper(new FrameSwitchSerializer);
osgDB::Registry::instance()->getObjectWrapperManager()->addWrapper(new RigGeometrySerializer);
osgDB::Registry::instance()->getObjectWrapperManager()->addWrapper(new LightManagerSerializer);
osgDB::Registry::instance()->getObjectWrapperManager()->addWrapper(new CameraRelativeTransformSerializer);
osgDB::ObjectWrapperManager* mgr = osgDB::Registry::instance()->getObjectWrapperManager();
mgr->addWrapper(new PositionAttitudeTransformSerializer);
mgr->addWrapper(new SkeletonSerializer);
mgr->addWrapper(new FrameSwitchSerializer);
mgr->addWrapper(new RigGeometrySerializer);
mgr->addWrapper(new LightManagerSerializer);
mgr->addWrapper(new CameraRelativeTransformSerializer);
// Don't serialize Geometry data as we are more interested in the overall structure rather than tons of vertex data that would make the file large and hard to read.
mgr->removeWrapper(mgr->findWrapper("osg::Geometry"));
mgr->addWrapper(new GeometrySerializer);
// ignore the below for now to avoid warning spam
const char* ignore[] = {
@ -120,7 +133,7 @@ void registerSerializers()
};
for (size_t i=0; i<sizeof(ignore)/sizeof(ignore[0]); ++i)
{
osgDB::Registry::instance()->getObjectWrapperManager()->addWrapper(makeDummySerializer(ignore[i]));
mgr->addWrapper(makeDummySerializer(ignore[i]));
}

Loading…
Cancel
Save