#ifndef MISC_STEREO_H #define MISC_STEREO_H #include #include #include #include #include #include // Some cursed headers like to define these #if defined(near) || defined(far) #undef near #undef far #endif namespace osgViewer { class Viewer; } namespace Misc { //! Represents the relative pose in space of some object struct Pose { //! Position in space osg::Vec3 position{ 0,0,0 }; //! Orientation in space. osg::Quat orientation{ 0,0,0,1 }; //! Add one pose to another Pose operator+(const Pose& rhs); const Pose& operator+=(const Pose& rhs); //! Scale a pose (does not affect orientation) Pose operator*(float scalar); const Pose& operator*=(float scalar); Pose operator/(float scalar); const Pose& operator/=(float scalar); bool operator==(const Pose& rhs) const; osg::Matrix viewMatrix(bool useGLConventions); }; //! Fov that defines all 4 angles from center struct FieldOfView { float angleLeft{ 0.f }; float angleRight{ 0.f }; float angleUp{ 0.f }; float angleDown{ 0.f }; bool operator==(const FieldOfView& rhs) const; //! Generate a perspective matrix from this fov osg::Matrix perspectiveMatrix(float near, float far) const; }; //! Represents an eye including both pose and fov. struct View { Pose pose; FieldOfView fov; bool operator==(const View& rhs) const; }; //! Represent two eyes. The eyes are in relative terms, and are assumed to lie on the horizon plane. struct StereoView { struct UpdateViewCallback { //! Called during the update traversal of every frame to source updated stereo values. virtual void updateView(View& left, View& right) = 0; }; //! Default implementation of UpdateViewCallback that just provides some hardcoded values for debugging purposes struct DefaultUpdateViewCallback : public UpdateViewCallback { void updateView(View& left, View& right) override; }; enum class Technique { None = 0, //!< Stereo disabled (do nothing). BruteForce, //!< Two slave cameras culling and drawing everything. GeometryShader_IndexedViewports, //!< Frustum camera culls and draws stereo into indexed viewports using an automatically generated geometry shader. }; //! A draw callback that adds stereo information to the operator. //! The stereo information is an enum describing which of the views the callback concerns. //! With some stereo methods, there is only one callback, in which case the enum will be 'Both'. //! //! A typical use case of this callback is to prevent firing callbacks twice and correctly identifying the last/first callback. struct StereoDrawCallback : public osg::Camera::DrawCallback { public: enum class View { Both, Left, Right }; public: StereoDrawCallback() {} void operator()(osg::RenderInfo& info) const override; virtual void operator()(osg::RenderInfo& info, View view) const = 0; private: }; static StereoView& instance(); //! Adds two cameras in stereo to the mainCamera. //! All nodes matching the mask are rendered in stereo using brute force via two camera transforms, the rest are rendered in stereo via a geometry shader. //! \param noShaderMask mask in all nodes that do not use shaders and must be rendered brute force. //! \param sceneMask must equal MWRender::VisMask::Mask_Scene. Necessary while VisMask is still not in components/ //! \note the masks apply only to the GeometryShader_IndexdViewports technique and can be 0 for the BruteForce technique. StereoView(osg::Node::NodeMask noShaderMask, osg::Node::NodeMask sceneMask); //! Updates uniforms with the view and projection matrices of each stereo view, and replaces the camera's view and projection matrix //! with a view and projection that closely envelopes the frustums of the two eyes. void update(); void updateStateset(osg::StateSet* stateset); void initializeStereo(osgViewer::Viewer* viewer, Technique technique); //! Initialized scene. Call when the "scene root" node has been created void initializeScene(); void setStereoTechnique(Technique technique); //! Callback that updates stereo configuration during the update pass void setUpdateViewCallback(std::shared_ptr cb); //! Set the cull callback on the appropriate camera object void setCullCallback(osg::ref_ptr cb); //! Apply the cullmask to the appropriate camera objects void setCullMask(osg::Node::NodeMask cullMask); //! Get the last applied cullmask. osg::Node::NodeMask getCullMask(); osg::Matrixd computeLeftEyeProjection(const osg::Matrixd& projection) const; osg::Matrixd computeLeftEyeView(const osg::Matrixd& view) const; osg::Matrixd computeRightEyeProjection(const osg::Matrixd& projection) const; osg::Matrixd computeRightEyeView(const osg::Matrixd& view) const; private: void setupBruteForceTechnique(); void setupGeometryShaderIndexedViewportTechnique(); void removeBruteForceTechnique(); void removeGeometryShaderIndexedViewportTechnique(); void disableStereo(); void enableStereo(); osg::ref_ptr mViewer; osg::ref_ptr mMainCamera; osg::ref_ptr mRoot; osg::ref_ptr mScene; osg::ref_ptr mStereoRoot; osg::ref_ptr mUpdateCallback; Technique mTechnique; // Keeps state relevant to doing stereo via the geometry shader osg::ref_ptr mStereoGeometryShaderRoot{ new osg::Group }; osg::Node::NodeMask mNoShaderMask; osg::Node::NodeMask mSceneMask; osg::Node::NodeMask mCullMask; // Keeps state and cameras relevant to doing stereo via brute force osg::ref_ptr mStereoBruteForceRoot{ new osg::Group }; osg::ref_ptr mLeftCamera{ new osg::Camera }; osg::ref_ptr mRightCamera{ new osg::Camera }; using SharedShadowMapConfig = SceneUtil::MWShadowTechnique::SharedShadowMapConfig; osg::ref_ptr mMasterConfig; osg::ref_ptr mSlaveConfig; bool mSharedShadowMaps; // Camera viewports bool flipViewOrder{ true }; // Updates stereo configuration during the update pass std::shared_ptr mUpdateViewCallback; // OSG camera callbacks set using set*callback. StereoView manages that these are always set on the appropriate camera(s); osg::ref_ptr mCullCallback{ nullptr }; }; //! Overrides all stereo-related states/uniforms to disable stereo for the scene rendered by camera void disableStereoForCamera(osg::Camera* camera); //! Overrides all stereo-related states/uniforms to enable stereo for the scene rendered by camera void enableStereoForCamera(osg::Camera* camera, bool horizontalSplit); //! Reads settings to determine stereo technique StereoView::Technique getStereoTechnique(void); } #endif