From e90941a8b3bccc0d2c93f4a63ee940624c94123b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 30 Jan 2017 18:59:04 +0100 Subject: [PATCH 1/7] Wrap the scene template reference in another object for const correctness and to avoid it from being serialized with the osgDB serializer --- components/resource/scenemanager.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index e4fc459ac..cc719e231 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -413,13 +413,27 @@ namespace Resource return node; } + class TemplateRef : public osg::Object + { + public: + TemplateRef(const Object* object) + : mObject(object) {} + TemplateRef() {} + TemplateRef(const TemplateRef& copy, const osg::CopyOp&) : mObject(copy.mObject) {} + + META_Object(Resource, TemplateRef) + + private: + osg::ref_ptr mObject; + }; + osg::ref_ptr SceneManager::createInstance(const std::string& name) { osg::ref_ptr scene = getTemplate(name); osg::ref_ptr cloned = osg::clone(scene.get(), SceneUtil::CopyOp()); // 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(const_cast(scene.get())); + cloned->getOrCreateUserDataContainer()->addUserObject(new TemplateRef(scene)); return cloned; } From 34deb6e7b189a79e14696bf6dc0cb2a65a318de7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Feb 2017 03:00:33 +0100 Subject: [PATCH 2/7] Add 'showSceneGraph' command to export the scene or a particular object to .osgt for debugging purposes --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/world.hpp | 4 + apps/openmw/mwrender/renderingmanager.cpp | 10 ++ apps/openmw/mwrender/renderingmanager.hpp | 2 + apps/openmw/mwscript/docs/vmformat.txt | 4 +- apps/openmw/mwscript/miscextensions.cpp | 27 +++++ apps/openmw/mwworld/worldimp.cpp | 11 +- apps/openmw/mwworld/worldimp.hpp | 8 +- components/CMakeLists.txt | 2 +- components/compiler/extensions0.cpp | 2 + components/compiler/opcodes.hpp | 2 + components/sceneutil/lightmanager.cpp | 2 +- components/sceneutil/lightmanager.hpp | 4 +- components/sceneutil/serialize.cpp | 119 ++++++++++++++++++++++ components/sceneutil/serialize.hpp | 12 +++ components/sceneutil/writescene.cpp | 26 +++++ components/sceneutil/writescene.hpp | 18 ++++ 17 files changed, 246 insertions(+), 9 deletions(-) create mode 100644 components/sceneutil/serialize.cpp create mode 100644 components/sceneutil/serialize.hpp create mode 100644 components/sceneutil/writescene.cpp create mode 100644 components/sceneutil/writescene.hpp diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 03ec412d1..ed0616ae2 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -505,7 +505,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create the world mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mFileCollections, mContentFiles, mEncoder, mFallbackMap, - mActivationDistanceOverride, mCellName, mStartupScript, mResDir.string())); + mActivationDistanceOverride, mCellName, mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string())); mEnvironment.getWorld()->setupPlayer(); input->setPlayer(&mEnvironment.getWorld()->getPlayer()); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 705a57367..ee32bc956 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -567,6 +567,10 @@ namespace MWBase /// Return physical or rendering half extents of the given actor. virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering=false) const = 0; + + /// Export scene graph to a file and return the filename. + /// \param ptr object to export scene graph for (if empty, export entire scene graph) + virtual std::string exportSceneGraph(const MWWorld::Ptr& ptr) = 0; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c1261fd2b..2414e49ee 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -1002,8 +1003,17 @@ namespace MWRender if (mFieldOfViewOverridden == true) { mFieldOfViewOverridden = false; + updateProjectionMatrix(); } } + void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format) + { + osg::Node* node = mViewer->getSceneData(); + if (!ptr.isEmpty()) + node = ptr.getRefData().getBaseNode(); + + SceneUtil::writeScene(node, filename, format); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 3be01931a..b9852dafb 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -188,6 +188,8 @@ namespace MWRender /// reset a previous overrideFieldOfView() call, i.e. revert to field of view specified in the settings file. void resetFieldOfView(); + void exportSceneGraph(const MWWorld::Ptr& ptr, const std::string& filename, const std::string& format); + private: void updateProjectionMatrix(); void updateTextureFiltering(); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index e19d7a1ad..851a6a29c 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -60,7 +60,9 @@ op 0x2002b: PCJoinFaction, explicit reference op 0x2002c: MenuTest op 0x2002d: BetaComment op 0x2002e: BetaComment, explicit reference -opcodes 0x2002d-0x3ffff unused +op 0x2002f: ShowSceneGraph +op 0x20030: ShowSceneGraph, explicit +opcodes 0x20031-0x3ffff unused Segment 4: (not implemented yet) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 1830be693..cc64c18c6 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1252,6 +1252,31 @@ namespace MWScript } }; + template + class OpShowSceneGraph : public Interpreter::Opcode1 + { + public: + virtual void execute(Interpreter::Runtime &runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime, false); + + int confirmed = 0; + if (arg0==1) + { + confirmed = runtime[0].mInteger; + runtime.pop(); + } + + if (ptr.isEmpty() && !confirmed) + runtime.getContext().report("Exporting the entire scene graph will result in a large file. Confirm this action using 'showscenegraph 1' or select an object instead."); + else + { + const std::string& filename = MWBase::Environment::get().getWorld()->exportSceneGraph(ptr); + runtime.getContext().report("Wrote '" + filename + "'"); + } + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -1348,6 +1373,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevCreature, new OpRemoveFromLevCreature); interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevItem, new OpAddToLevItem); interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevItem, new OpRemoveFromLevItem); + interpreter.installSegment3 (Compiler::Misc::opcodeShowSceneGraph, new OpShowSceneGraph); + interpreter.installSegment3 (Compiler::Misc::opcodeShowSceneGraphExplicit, new OpShowSceneGraph); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2d1e2b139..199cfe6a7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -145,10 +145,10 @@ namespace MWWorld const std::vector& contentFiles, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int activationDistanceOverride, const std::string& startCell, const std::string& startupScript, - const std::string& resourcePath) + const std::string& resourcePath, const std::string& userDataPath) : mResourceSystem(resourceSystem), mFallback(fallbackMap), mPlayer (0), mLocalScripts (mStore), mSky (true), mCells (mStore, mEsm), - mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), + mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath), mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0) @@ -3146,6 +3146,13 @@ namespace MWWorld return mPhysics->getHalfExtents(actor); } + std::string World::exportSceneGraph(const Ptr &ptr) + { + std::string file = mUserDataPath + "/openmw.osgt"; + mRendering->exportSceneGraph(ptr, file, "Ascii"); + return file; + } + void World::spawnRandomCreature(const std::string &creatureList) { const ESM::CreatureLevList* list = getStore().get().find(creatureList); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 4bf27162b..1fd3ce787 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -101,6 +101,8 @@ namespace MWWorld bool mScriptsEnabled; std::vector mContentFiles; + std::string mUserDataPath; + // not implemented World (const World&); World& operator= (const World&); @@ -182,7 +184,7 @@ namespace MWWorld const Files::Collections& fileCollections, const std::vector& contentFiles, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, - int activationDistanceOverride, const std::string& startCell, const std::string& startupScript, const std::string& resourcePath); + int activationDistanceOverride, const std::string& startCell, const std::string& startupScript, const std::string& resourcePath, const std::string& userDataPath); virtual ~World(); @@ -668,6 +670,10 @@ namespace MWWorld /// Return physical or rendering half extents of the given actor. virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering=false) const; + + /// Export scene graph to a file and return the filename. + /// \param ptr object to export scene graph for (if empty, export entire scene graph) + virtual std::string exportSceneGraph(const MWWorld::Ptr& ptr); }; } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3f3e1dbde..48a414b38 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -50,7 +50,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 + lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize ) add_component_dir (nif diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index d8eeb05b5..eb3e1e4ee 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -312,6 +312,8 @@ namespace Compiler extensions.registerInstruction ("betacomment", "/S", opcodeBetaComment, opcodeBetaCommentExplicit); extensions.registerInstruction ("bc", "/S", opcodeBetaComment, opcodeBetaCommentExplicit); extensions.registerInstruction ("ori", "/S", opcodeBetaComment, opcodeBetaCommentExplicit); // 'ori' stands for 'ObjectReferenceInfo' + extensions.registerInstruction ("showscenegraph", "/l", opcodeShowSceneGraph, opcodeShowSceneGraphExplicit); + extensions.registerInstruction ("ssg", "/l", opcodeShowSceneGraph, opcodeShowSceneGraphExplicit); extensions.registerInstruction ("addtolevcreature", "ccl", opcodeAddToLevCreature); extensions.registerInstruction ("removefromlevcreature", "ccl", opcodeRemoveFromLevCreature); extensions.registerInstruction ("addtolevitem", "ccl", opcodeAddToLevItem); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 773ac68f1..b2333032b 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -292,6 +292,8 @@ namespace Compiler const int opcodeRemoveFromLevCreature = 0x20002fc; const int opcodeAddToLevItem = 0x20002fd; const int opcodeRemoveFromLevItem = 0x20002fe; + const int opcodeShowSceneGraph = 0x2002f; + const int opcodeShowSceneGraphExplicit = 0x20030; } namespace Sky diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 906da5967..28f9a3a62 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -132,7 +132,7 @@ namespace SceneUtil : osg::NodeCallback(copy, copyop) { } - META_Object(SceneUtil, SceneUtil::LightManagerUpdateCallback) + META_Object(SceneUtil, LightManagerUpdateCallback) virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index 635f58540..c8a057263 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -32,7 +32,7 @@ namespace SceneUtil public: - META_Node(SceneUtil, SceneUtil::LightSource) + META_Node(SceneUtil, LightSource) LightSource(); @@ -79,7 +79,7 @@ namespace SceneUtil { public: - META_Node(SceneUtil, SceneUtil::LightManager) + META_Node(SceneUtil, LightManager) LightManager(); diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp new file mode 100644 index 000000000..23567353c --- /dev/null +++ b/components/sceneutil/serialize.cpp @@ -0,0 +1,119 @@ +#include "serialize.hpp" + +#include +#include + +#include +#include +#include + +namespace SceneUtil +{ + +template +static osg::Object* createInstanceFunc() { return new Cls; } + +class PositionAttitudeTransformSerializer : public osgDB::ObjectWrapper +{ +public: + PositionAttitudeTransformSerializer() + : osgDB::ObjectWrapper(createInstanceFunc, "SceneUtil::PositionAttitudeTransform", "osg::Object osg::Node osg::Group osg::Transform SceneUtil::PositionAttitudeTransform") + { + addSerializer( new osgDB::PropByRefSerializer< SceneUtil::PositionAttitudeTransform, osg::Vec3f >( + "position", osg::Vec3f(), &SceneUtil::PositionAttitudeTransform::getPosition, &SceneUtil::PositionAttitudeTransform::setPosition), osgDB::BaseSerializer::RW_VEC3F ); + addSerializer( new osgDB::PropByRefSerializer< SceneUtil::PositionAttitudeTransform, osg::Quat >( + "attitude", osg::Quat(), &SceneUtil::PositionAttitudeTransform::getAttitude, &SceneUtil::PositionAttitudeTransform::setAttitude), osgDB::BaseSerializer::RW_QUAT ); + addSerializer( new osgDB::PropByRefSerializer< SceneUtil::PositionAttitudeTransform, osg::Vec3f >( + "scale", osg::Vec3f(), &SceneUtil::PositionAttitudeTransform::getScale, &SceneUtil::PositionAttitudeTransform::setScale), osgDB::BaseSerializer::RW_VEC3F ); + } +}; + +class SkeletonSerializer : public osgDB::ObjectWrapper +{ +public: + SkeletonSerializer() + : osgDB::ObjectWrapper(createInstanceFunc, "SceneUtil::Skeleton", "osg::Object osg::Node osg::Group SceneUtil::Skeleton") + { + } +}; + +class FrameSwitchSerializer : public osgDB::ObjectWrapper +{ +public: + FrameSwitchSerializer() + : osgDB::ObjectWrapper(createInstanceFunc, "NifOsg::FrameSwitch", "osg::Object osg::Node osg::Group NifOsg::FrameSwitch") + { + } +}; + +class RigGeometrySerializer : public osgDB::ObjectWrapper +{ +public: + RigGeometrySerializer() + : osgDB::ObjectWrapper(createInstanceFunc, "SceneUtil::RigGeometry", "osg::Object osg::Node osg::Drawable osg::Geometry SceneUtil::RigGeometry") + { + } +}; + +class LightManagerSerializer : public osgDB::ObjectWrapper +{ +public: + LightManagerSerializer() + : osgDB::ObjectWrapper(createInstanceFunc, "SceneUtil::LightManager", "osg::Object osg::Node osg::Group SceneUtil::LightManager") + { + } +}; + +osgDB::ObjectWrapper* makeDummySerializer(const std::string& classname) +{ + return new osgDB::ObjectWrapper(createInstanceFunc, classname, "osg::Object"); +} + + +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); + + // ignore the below for now to avoid warning spam + const char* ignore[] = { + "MWRender::PtrHolder", + "MWRender::CameraRelativeTransform", + "Resource::TemplateRef", + "SceneUtil::LightListCallback", + "SceneUtil::LightManagerUpdateCallback", + "SceneUtil::UpdateRigBounds", + "SceneUtil::UpdateRigGeometry", + "SceneUtil::LightSource", + "NifOsg::NodeUserData", + "NifOsg::FlipController", + "NifOsg::KeyframeController", + "NifOsg::TextKeyMapHolder", + "NifOsg::Emitter", + "NifOsg::ParticleSystem", + "NifOsg::GrowFadeAffector", + "NifOsg::InverseWorldMatrix", + "NifOsg::StaticBoundingBoxCallback", + "NifOsg::GeomMorpherController", + "NifOsg::UpdateMorphGeometry", + "osgMyGUI::Drawable", + "osg::DrawCallback", + "osg::DummyObject" + }; + for (size_t i=0; igetObjectWrapperManager()->addWrapper(makeDummySerializer(ignore[i])); + } + + + done = true; + } +} + +} diff --git a/components/sceneutil/serialize.hpp b/components/sceneutil/serialize.hpp new file mode 100644 index 000000000..7dc59605e --- /dev/null +++ b/components/sceneutil/serialize.hpp @@ -0,0 +1,12 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_SERIALIZE_H +#define OPENMW_COMPONENTS_SCENEUTIL_SERIALIZE_H + +namespace SceneUtil +{ + + /// Register osg node serializers for certain SceneUtil classes if not already done so + void registerSerializers(); + +} + +#endif diff --git a/components/sceneutil/writescene.cpp b/components/sceneutil/writescene.cpp new file mode 100644 index 000000000..19e8dd73b --- /dev/null +++ b/components/sceneutil/writescene.cpp @@ -0,0 +1,26 @@ +#include "writescene.hpp" + +#include + +#include + +#include + +#include "serialize.hpp" + +void SceneUtil::writeScene(osg::Node *node, const std::string& filename, const std::string& format) +{ + registerSerializers(); + + osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension("osgt"); + if (!rw) + throw std::runtime_error("can not find readerwriter for " + format); + + boost::filesystem::ofstream stream; + stream.open(filename); + + osg::ref_ptr options = new osgDB::Options; + options->setPluginStringData("fileType", format); + + rw->writeNode(*node, stream, options); +} diff --git a/components/sceneutil/writescene.hpp b/components/sceneutil/writescene.hpp new file mode 100644 index 000000000..307251fe8 --- /dev/null +++ b/components/sceneutil/writescene.hpp @@ -0,0 +1,18 @@ +#ifndef OPENMW_COMPONENTS_WRITESCENE_H +#define OPENMW_COMPONENTS_WRITESCENE_H + +#include + +namespace osg +{ + class Node; +} + +namespace SceneUtil +{ + + void writeScene(osg::Node* node, const std::string& filename, const std::string& format); + +} + +#endif From e399e10607bdc40a7ade1d4df6b10116fded7daa Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Feb 2017 03:06:59 +0100 Subject: [PATCH 3/7] Add missing lock in shareMaterial --- components/nifosg/nifloader.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 66575699f..c758e8736 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1643,10 +1643,12 @@ namespace NifOsg osg::Material* shareMaterial(osg::Material* mat) { typedef std::set, CompareMaterial> MatCache; - static MatCache mats; - MatCache::iterator found = mats.find(mat); - if (found == mats.end()) - found = mats.insert(mat).first; + static MatCache sMats; + static OpenThreads::Mutex sMutex; + OpenThreads::ScopedLock lock(sMutex); + MatCache::iterator found = sMats.find(mat); + if (found == sMats.end()) + found = sMats.insert(mat).first; return *found; } From 960d4a96c3ad8d00ced4fcb2ec1ad3583ce1a134 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Feb 2017 03:18:17 +0100 Subject: [PATCH 4/7] Reduce the number of osg::Material state by setting the default state on the graph root --- apps/opencs/view/render/scenewidget.cpp | 7 +++++++ apps/openmw/mwrender/renderingmanager.cpp | 7 +++++++ components/nifosg/nifloader.cpp | 9 +++++++++ 3 files changed, 23 insertions(+) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index e5b61f5d3..a8618923f 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,12 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) mView->getCamera()->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); mView->getCamera()->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON); + osg::ref_ptr defaultMat (new osg::Material); + defaultMat->setColorMode(osg::Material::OFF); + defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); + defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); + defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); + mView->getCamera()->getOrCreateStateSet()->getOrCreateStateSet()->setAttribute(defaultMat); mView->setSceneData(mRootNode); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2414e49ee..417f33567 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -221,6 +222,12 @@ namespace MWRender sceneRoot->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON); sceneRoot->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON); sceneRoot->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); + osg::ref_ptr defaultMat (new osg::Material); + defaultMat->setColorMode(osg::Material::OFF); + defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); + defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); + defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); + sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); sceneRoot->setNodeMask(Mask_Scene); sceneRoot->setName("Scene Root"); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index c758e8736..baba022ea 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1758,6 +1758,15 @@ namespace NifOsg mat->setColorMode(osg::Material::AMBIENT); } + if (mat->getColorMode() == osg::Material::OFF + && mat->getDiffuse(osg::Material::FRONT_AND_BACK) == osg::Vec4f(1,1,1,1) + && mat->getAmbient(osg::Material::FRONT_AND_BACK) == osg::Vec4f(1,1,1,1) + && mat->getSpecular(osg::Material::FRONT_AND_BACK) == osg::Vec4f(0.f, 0.f, 0.f, 0.f)) + { + // default state, skip + return; + } + // TODO: this could be replaced by a more generic mechanism of sharing any type of State Attribute // apply only for Materials for now mat = shareMaterial(mat); From 9180089a3b3641bf8f689e9c37999f2fb6769b2f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Feb 2017 04:20:46 +0100 Subject: [PATCH 5/7] Add sharing for more State Attributes, most notably BlendFunc and AlphaFunc, to improve the state tracking in osg::State and reduce the number of GL calls --- components/nifosg/nifloader.cpp | 51 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index baba022ea..07b92ef2a 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1551,7 +1551,7 @@ namespace NifOsg case Nif::RC_NiStencilProperty: { const Nif::NiStencilProperty* stencilprop = static_cast(property); - osg::FrontFace* frontFace = new osg::FrontFace; + osg::ref_ptr frontFace = new osg::FrontFace; switch (stencilprop->data.drawMode) { case 2: @@ -1563,6 +1563,7 @@ namespace NifOsg frontFace->setMode(osg::FrontFace::COUNTER_CLOCKWISE); break; } + frontFace = shareAttribute(frontFace); osg::StateSet* stateset = node->getOrCreateStateSet(); stateset->setAttribute(frontFace, osg::StateAttribute::ON); @@ -1571,11 +1572,12 @@ namespace NifOsg if (stencilprop->data.enabled != 0) { - osg::Stencil* stencil = new osg::Stencil; + osg::ref_ptr stencil = new osg::Stencil; stencil->setFunction(getStencilFunction(stencilprop->data.compareFunc), stencilprop->data.stencilRef, stencilprop->data.stencilMask); stencil->setStencilFailOperation(getStencilOperation(stencilprop->data.failAction)); stencil->setStencilPassAndDepthFailOperation(getStencilOperation(stencilprop->data.zFailAction)); stencil->setStencilPassAndDepthPassOperation(getStencilOperation(stencilprop->data.zPassAction)); + stencil = shareAttribute(stencil); stateset->setAttributeAndModes(stencil, osg::StateAttribute::ON); } @@ -1584,9 +1586,10 @@ namespace NifOsg case Nif::RC_NiWireframeProperty: { const Nif::NiWireframeProperty* wireprop = static_cast(property); - osg::PolygonMode* mode = new osg::PolygonMode; + osg::ref_ptr mode = new osg::PolygonMode; mode->setMode(osg::PolygonMode::FRONT_AND_BACK, wireprop->flags == 0 ? osg::PolygonMode::FILL : osg::PolygonMode::LINE); + mode = shareAttribute(mode); node->getOrCreateStateSet()->setAttributeAndModes(mode, osg::StateAttribute::ON); break; } @@ -1594,8 +1597,12 @@ namespace NifOsg { const Nif::NiZBufferProperty* zprop = static_cast(property); // VER_MW doesn't support a DepthFunction according to NifSkope - osg::Depth* depth = new osg::Depth; - depth->setWriteMask((zprop->flags>>1)&1); + static osg::ref_ptr depth; + if (!depth) + { + depth = new osg::Depth; + depth->setWriteMask((zprop->flags>>1)&1); + } node->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); break; } @@ -1632,23 +1639,25 @@ namespace NifOsg } } - struct CompareMaterial + struct CompareStateAttribute { - bool operator() (const osg::ref_ptr& left, const osg::ref_ptr& right) const + bool operator() (const osg::ref_ptr& left, const osg::ref_ptr& right) const { return left->compare(*right) < 0; } }; - osg::Material* shareMaterial(osg::Material* mat) + // global sharing of State Attributes will reduce the number of GL calls as the osg::State will check by pointer to see if state is the same + template + Attribute* shareAttribute(const osg::ref_ptr& attr) { - typedef std::set, CompareMaterial> MatCache; - static MatCache sMats; + typedef std::set, CompareStateAttribute> Cache; + static Cache sCache; static OpenThreads::Mutex sMutex; OpenThreads::ScopedLock lock(sMutex); - MatCache::iterator found = sMats.find(mat); - if (found == sMats.end()) - found = sMats.insert(mat).first; + typename Cache::iterator found = sCache.find(attr); + if (found == sCache.end()) + found = sCache.insert(attr).first; return *found; } @@ -1715,9 +1724,10 @@ namespace NifOsg const Nif::NiAlphaProperty* alphaprop = static_cast(property); if (alphaprop->flags&1) { - stateset->setAttributeAndModes(new osg::BlendFunc(getBlendMode((alphaprop->flags>>1)&0xf), - getBlendMode((alphaprop->flags>>5)&0xf)), - osg::StateAttribute::ON); + osg::ref_ptr blendFunc (new osg::BlendFunc(getBlendMode((alphaprop->flags>>1)&0xf), + getBlendMode((alphaprop->flags>>5)&0xf))); + blendFunc = shareAttribute(blendFunc); + stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); bool noSort = (alphaprop->flags>>13)&1; if (!noSort) @@ -1734,8 +1744,9 @@ namespace NifOsg if((alphaprop->flags>>9)&1) { - stateset->setAttributeAndModes(new osg::AlphaFunc(getTestMode((alphaprop->flags>>10)&0x7), - alphaprop->data.threshold/255.f), osg::StateAttribute::ON); + osg::ref_ptr alphaFunc (new osg::AlphaFunc(getTestMode((alphaprop->flags>>10)&0x7), alphaprop->data.threshold/255.f)); + alphaFunc = shareAttribute(alphaFunc); + stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); } else { @@ -1767,9 +1778,7 @@ namespace NifOsg return; } - // TODO: this could be replaced by a more generic mechanism of sharing any type of State Attribute - // apply only for Materials for now - mat = shareMaterial(mat); + mat = shareAttribute(mat); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); } From fcb4129aee517375055da989a780c15d88afa1d4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Feb 2017 05:38:59 +0100 Subject: [PATCH 6/7] Add 'small feature culling pixel size' setting --- apps/openmw/mwrender/renderingmanager.cpp | 3 +++ docs/source/openmw-mods/settings/camera.rst | 15 +++++++++++++-- files/settings-default.cfg | 4 +++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 417f33567..08eb89499 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -244,7 +244,10 @@ namespace MWRender if (!Settings::Manager::getBool("small feature culling", "Camera")) cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING); else + { + mViewer->getCamera()->setSmallFeatureCullingPixelSize(Settings::Manager::getFloat("small feature culling pixel size", "Camera")); cullingMode |= osg::CullStack::SMALL_FEATURE_CULLING; + } mViewer->getCamera()->setCullingMode( cullingMode ); diff --git a/docs/source/openmw-mods/settings/camera.rst b/docs/source/openmw-mods/settings/camera.rst index 796e117cb..41dabee40 100644 --- a/docs/source/openmw-mods/settings/camera.rst +++ b/docs/source/openmw-mods/settings/camera.rst @@ -19,10 +19,21 @@ small feature culling :Range: True/False :Default: True -This boolean setting determines whether objects that render to a few pixels or smaller will be culled (not drawn). It generally improves performance to enable this feature, and by definition the culled objects will be very small on screen. It appears that the default definition of "small" in OpenSceneGraph is 2x2 pixels. +This boolean setting determines whether objects that render to a few pixels or smaller will be culled (not drawn). It generally improves performance to enable this feature, and by definition the culled objects will be very small on screen. The size in pixels for an object to be considered 'small' is controlled by a separate setting. The default value is true. This setting can only be configured by editing the settings configuration file. +small feature culling pixel size +--------------------- + +:Type: floating point +:Range: > 0 +:Default: 2.0 + +Controls the cutoff in pixels for the 'small feature culling' setting which will have no effect if 'small feature culling' is disabled. + +This setting can only be configured by editing the settings configuration file. + viewing distance ---------------- @@ -72,4 +83,4 @@ first person field of view The floating point setting controls the field of view for first person meshes such as the player's hands and held objects. It is not recommended to change this value from its default value because the Bethesda provided Morrowind assets do not adapt well to large values, while small values can result in the hands not being visible. -The default value is 55.0. This setting can only be configured by editing the settings configuration file. \ No newline at end of file +The default value is 55.0. This setting can only be configured by editing the settings configuration file. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index e17f0235b..6b98d5599 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -16,9 +16,11 @@ # Near clipping plane (>0.0, e.g. 0.01 to 18.0). near clip = 1 -# Cull objects smaller than one pixel. +# Cull objects that occupy less than 'small feature culling pixel size' on the screen. small feature culling = true +small feature culling pixel size = 2.0 + # Maximum visible distance (e.g. 2000.0 to 6666.0). Caution: this setting # can dramatically affect performance, see documentation for details. viewing distance = 6666.0 From 1808b8567e5cb5eeb806c8d9e7993ba51c4e866e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Feb 2017 05:55:33 +0100 Subject: [PATCH 7/7] Add 'small feature culling pixel size' setting specifically for water RTTs, by default set higher than the one in [Camera] --- apps/openmw/mwrender/water.cpp | 2 ++ docs/source/openmw-mods/settings/water.rst | 17 ++++++++++++++++- files/settings-default.cfg | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index b99c128a3..cb5f68943 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -222,6 +222,7 @@ public: setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); setReferenceFrame(osg::Camera::RELATIVE_RF); + setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting); setNodeMask(Mask_RenderToTexture); @@ -303,6 +304,7 @@ public: setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); setReferenceFrame(osg::Camera::RELATIVE_RF); + setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); bool reflectActors = Settings::Manager::getBool("reflect actors", "Water"); diff --git a/docs/source/openmw-mods/settings/water.rst b/docs/source/openmw-mods/settings/water.rst index b2b9a00c2..379da1de2 100644 --- a/docs/source/openmw-mods/settings/water.rst +++ b/docs/source/openmw-mods/settings/water.rst @@ -40,4 +40,19 @@ This boolean setting enables the refraction rendering feature of the water shade This setting has no effect if the shader setting is false. -The default setting is false. This setting can be toggled with the Refraction button in the Water tab of the Video panel of the Options menu. \ No newline at end of file +The default setting is false. This setting can be toggled with the Refraction button in the Water tab of the Video panel of the Options menu. + +small feature culling pixel size +-------------------------------- + +:Type: floating point +:Range: > 0 +:Default: 20.0 + +Controls the cutoff in pixels for small feature culling - see the 'Camera' section for more details, however this setting in the 'Water' section applies specifically to objects rendered in water reflection and refraction textures. + +The setting 'rtt size' interacts with this setting because it controls how large a pixel on the water texture (technically called a texel) is in pixels on the screen. + +This setting will have no effect if the shader setting is false, or the 'small feature culling' (in the 'Camera' section) is disabled. + +This setting can only be configured by editing the settings configuration file. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6b98d5599..d9d40bb0b 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -340,6 +340,9 @@ refraction = false # Draw NPCs and creatures on water reflections. reflect actors = false +# Overrides the value in '[Camera] small feature culling pixel size' specifically for water reflection/refraction textures. +small feature culling pixel size = 20.0 + [Windows] # Location and sizes of windows as a fraction of the OpenMW window or