You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/components/misc/stereo.hpp

207 lines
7.7 KiB
C++

#ifndef MISC_STEREO_H
#define MISC_STEREO_H
#include <osg/Matrix>
#include <osg/Vec3>
#include <osg/Camera>
#include <osg/StateSet>
#include <memory>
#include <components/sceneutil/mwshadowtechnique.hpp>
// 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<UpdateViewCallback> cb);
//! Set the cull callback on the appropriate camera object
void setCullCallback(osg::ref_ptr<osg::NodeCallback> 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<osgViewer::Viewer> mViewer;
osg::ref_ptr<osg::Camera> mMainCamera;
osg::ref_ptr<osg::Group> mRoot;
osg::ref_ptr<osg::Group> mScene;
osg::ref_ptr<osg::Group> mStereoRoot;
osg::ref_ptr<osg::Callback> mUpdateCallback;
Technique mTechnique;
// Keeps state relevant to doing stereo via the geometry shader
osg::ref_ptr<osg::Group> 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<osg::Group> mStereoBruteForceRoot{ new osg::Group };
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
osg::ref_ptr<osg::Camera> mRightCamera{ new osg::Camera };
using SharedShadowMapConfig = SceneUtil::MWShadowTechnique::SharedShadowMapConfig;
osg::ref_ptr<SharedShadowMapConfig> mMasterConfig;
osg::ref_ptr<SharedShadowMapConfig> mSlaveConfig;
bool mSharedShadowMaps;
// Camera viewports
bool flipViewOrder{ true };
// Updates stereo configuration during the update pass
std::shared_ptr<UpdateViewCallback> mUpdateViewCallback;
// OSG camera callbacks set using set*callback. StereoView manages that these are always set on the appropriate camera(s);
osg::ref_ptr<osg::NodeCallback> 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