diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 03ec412d1c..ed0616ae2b 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 705a57367f..ee32bc9563 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 c1261fd2b1..2414e49eec 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 3be01931a7..b9852dafbf 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 e19d7a1ad2..851a6a29c5 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 1830be693b..cc64c18c60 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 2d1e2b1392..199cfe6a70 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 4bf27162ba..1fd3ce787c 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 3f3e1dbde1..48a414b389 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 d8eeb05b50..eb3e1e4eee 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 773ac68f1f..b2333032b3 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 906da5967b..28f9a3a624 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 635f58540c..c8a0572636 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 0000000000..23567353cc --- /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 0000000000..7dc59605eb --- /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 0000000000..19e8dd73b5 --- /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 0000000000..307251fe80 --- /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