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/engine.cpp b/apps/openmw/engine.cpp index d82351550..bc0095894 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -526,7 +526,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 d19e2d7dc..37ea1524e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -570,6 +570,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..08eb89499 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include @@ -220,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"); @@ -236,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 ); @@ -1002,8 +1013,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/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/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 431e34fb4..f0ae17efb 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1310,6 +1310,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); @@ -1406,6 +1431,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 cf2caff42..83844a9b1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -152,10 +152,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) @@ -3229,6 +3229,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 94c43b5d3..f5e6950e7 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(); @@ -671,6 +673,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 8a449a680..7c6cd666b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -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 + 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/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 66575699f..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,21 +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 mats; - MatCache::iterator found = mats.find(mat); - if (found == mats.end()) - found = mats.insert(mat).first; + typedef std::set, CompareStateAttribute> Cache; + static Cache sCache; + static OpenThreads::Mutex sMutex; + OpenThreads::ScopedLock lock(sMutex); + typename Cache::iterator found = sCache.find(attr); + if (found == sCache.end()) + found = sCache.insert(attr).first; return *found; } @@ -1713,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) @@ -1732,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 { @@ -1756,9 +1769,16 @@ namespace NifOsg mat->setColorMode(osg::Material::AMBIENT); } - // 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); + 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; + } + + mat = shareAttribute(mat); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); } 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; } 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 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/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 e17f0235b..d9d40bb0b 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 @@ -338,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