2020-09-14 20:17:07 +00:00
# include "stereo.hpp"
2020-09-19 18:55:06 +00:00
# include "stringops.hpp"
2020-09-14 20:17:07 +00:00
# include <osg/io_utils>
# include <osg/ViewportIndexed>
# include <osgUtil/CullVisitor>
2020-12-19 14:15:58 +00:00
# include <osgViewer/Renderer>
2020-09-14 20:17:07 +00:00
# include <osgViewer/Viewer>
# include <iostream>
2020-12-10 20:29:56 +00:00
# include <map>
# include <string>
2020-09-14 20:17:07 +00:00
# include <components/debug/debuglog.hpp>
2020-09-19 18:55:06 +00:00
2020-09-14 20:17:07 +00:00
# include <components/sceneutil/statesetupdater.hpp>
# include <components/sceneutil/visitor.hpp>
2020-09-19 18:55:06 +00:00
# include <components/settings/settings.hpp>
2020-09-14 20:17:07 +00:00
namespace Misc
{
Pose Pose : : operator + ( const Pose & rhs )
{
Pose pose = * this ;
pose . position + = this - > orientation * rhs . position ;
pose . orientation = rhs . orientation * this - > orientation ;
return pose ;
}
const Pose & Pose : : operator + = ( const Pose & rhs )
{
* this = * this + rhs ;
return * this ;
}
Pose Pose : : operator * ( float scalar )
{
Pose pose = * this ;
pose . position * = scalar ;
return pose ;
}
const Pose & Pose : : operator * = ( float scalar )
{
* this = * this * scalar ;
return * this ;
}
Pose Pose : : operator / ( float scalar )
{
Pose pose = * this ;
pose . position / = scalar ;
return pose ;
}
const Pose & Pose : : operator / = ( float scalar )
{
* this = * this / scalar ;
return * this ;
}
bool Pose : : operator = = ( const Pose & rhs ) const
{
return position = = rhs . position & & orientation = = rhs . orientation ;
}
osg : : Matrix Pose : : viewMatrix ( bool useGLConventions )
{
if ( useGLConventions )
{
// When applied as an offset to an existing view matrix,
// that view matrix will already convert points to a camera space
// with opengl conventions. So we need to convert offsets to opengl
// conventions.
float y = position . y ( ) ;
float z = position . z ( ) ;
position . y ( ) = z ;
position . z ( ) = - y ;
y = orientation . y ( ) ;
z = orientation . z ( ) ;
orientation . y ( ) = z ;
orientation . z ( ) = - y ;
osg : : Matrix viewMatrix ;
viewMatrix . setTrans ( - position ) ;
viewMatrix . postMultRotate ( orientation . conj ( ) ) ;
return viewMatrix ;
}
else
{
osg : : Vec3d forward = orientation * osg : : Vec3d ( 0 , 1 , 0 ) ;
osg : : Vec3d up = orientation * osg : : Vec3d ( 0 , 0 , 1 ) ;
osg : : Matrix viewMatrix ;
viewMatrix . makeLookAt ( position , position + forward , up ) ;
return viewMatrix ;
}
}
bool FieldOfView : : operator = = ( const FieldOfView & rhs ) const
{
return angleDown = = rhs . angleDown
& & angleUp = = rhs . angleUp
& & angleLeft = = rhs . angleLeft
& & angleRight = = rhs . angleRight ;
}
// near and far named with an underscore because of windows' headers galaxy brain defines.
osg : : Matrix FieldOfView : : perspectiveMatrix ( float near_ , float far_ )
{
const float tanLeft = tanf ( angleLeft ) ;
const float tanRight = tanf ( angleRight ) ;
const float tanDown = tanf ( angleDown ) ;
const float tanUp = tanf ( angleUp ) ;
const float tanWidth = tanRight - tanLeft ;
const float tanHeight = tanUp - tanDown ;
const float offset = near_ ;
float matrix [ 16 ] = { } ;
matrix [ 0 ] = 2 / tanWidth ;
matrix [ 4 ] = 0 ;
matrix [ 8 ] = ( tanRight + tanLeft ) / tanWidth ;
matrix [ 12 ] = 0 ;
matrix [ 1 ] = 0 ;
matrix [ 5 ] = 2 / tanHeight ;
matrix [ 9 ] = ( tanUp + tanDown ) / tanHeight ;
matrix [ 13 ] = 0 ;
if ( far_ < = near_ ) {
matrix [ 2 ] = 0 ;
matrix [ 6 ] = 0 ;
matrix [ 10 ] = - 1 ;
matrix [ 14 ] = - ( near_ + offset ) ;
}
else {
matrix [ 2 ] = 0 ;
matrix [ 6 ] = 0 ;
matrix [ 10 ] = - ( far_ + offset ) / ( far_ - near_ ) ;
matrix [ 14 ] = - ( far_ * ( near_ + offset ) ) / ( far_ - near_ ) ;
}
matrix [ 3 ] = 0 ;
matrix [ 7 ] = 0 ;
matrix [ 11 ] = - 1 ;
matrix [ 15 ] = 0 ;
return osg : : Matrix ( matrix ) ;
}
bool View : : operator = = ( const View & rhs ) const
{
return pose = = rhs . pose & & fov = = rhs . fov ;
}
std : : ostream & operator < < (
std : : ostream & os ,
const Pose & pose )
{
os < < " position= " < < pose . position < < " , orientation= " < < pose . orientation ;
return os ;
}
std : : ostream & operator < < (
std : : ostream & os ,
const FieldOfView & fov )
{
os < < " left= " < < fov . angleLeft < < " , right= " < < fov . angleRight < < " , down= " < < fov . angleDown < < " , up= " < < fov . angleUp ;
return os ;
}
std : : ostream & operator < < (
std : : ostream & os ,
const View & view )
{
os < < " pose=< " < < view . pose < < " >, fov=< " < < view . fov < < " > " ;
return os ;
}
2020-09-14 21:09:44 +00:00
// Update stereo view/projection during update
2020-09-14 20:17:07 +00:00
class StereoUpdateCallback : public osg : : Callback
{
public :
2020-09-14 21:09:44 +00:00
StereoUpdateCallback ( StereoView * stereoView ) : stereoView ( stereoView ) { }
2020-09-14 20:17:07 +00:00
bool run ( osg : : Object * object , osg : : Object * data ) override
{
auto b = traverse ( object , data ) ;
2020-09-14 21:09:44 +00:00
stereoView - > update ( ) ;
2020-09-14 20:17:07 +00:00
return b ;
}
2020-09-14 21:09:44 +00:00
StereoView * stereoView ;
2020-09-14 20:17:07 +00:00
} ;
2020-09-14 21:09:44 +00:00
// Update states during cull
class StereoStatesetUpdateCallback : public SceneUtil : : StateSetUpdater
2020-09-14 20:17:07 +00:00
{
public :
2020-09-14 21:09:44 +00:00
StereoStatesetUpdateCallback ( StereoView * view )
2020-09-14 20:17:07 +00:00
: stereoView ( view )
{
}
protected :
virtual void setDefaults ( osg : : StateSet * stateset )
{
auto stereoViewMatrixUniform = new osg : : Uniform ( osg : : Uniform : : FLOAT_MAT4 , " stereoViewMatrices " , 2 ) ;
stateset - > addUniform ( stereoViewMatrixUniform , osg : : StateAttribute : : OVERRIDE ) ;
auto stereoViewProjectionsUniform = new osg : : Uniform ( osg : : Uniform : : FLOAT_MAT4 , " stereoViewProjections " , 2 ) ;
stateset - > addUniform ( stereoViewProjectionsUniform ) ;
auto geometryPassthroughUniform = new osg : : Uniform ( " geometryPassthrough " , false ) ;
stateset - > addUniform ( geometryPassthroughUniform ) ;
}
virtual void apply ( osg : : StateSet * stateset , osg : : NodeVisitor * /*nv*/ )
{
2020-09-14 21:09:44 +00:00
stereoView - > updateStateset ( stateset ) ;
2020-09-14 20:17:07 +00:00
}
private :
StereoView * stereoView ;
} ;
2020-12-10 20:29:56 +00:00
StereoView * sInstance = nullptr ;
StereoView & StereoView : : instance ( )
{
return * sInstance ;
}
2020-12-16 19:59:10 +00:00
static osg : : Camera *
createCamera ( std : : string name , GLbitfield clearMask )
{
auto * camera = new osg : : Camera ;
camera - > setReferenceFrame ( osg : : Transform : : ABSOLUTE_RF ) ;
camera - > setProjectionResizePolicy ( osg : : Camera : : FIXED ) ;
camera - > setProjectionMatrix ( osg : : Matrix : : identity ( ) ) ;
camera - > setViewMatrix ( osg : : Matrix : : identity ( ) ) ;
camera - > setName ( name ) ;
camera - > setDataVariance ( osg : : Object : : STATIC ) ;
camera - > setRenderOrder ( osg : : Camera : : NESTED_RENDER ) ;
camera - > setClearMask ( clearMask ) ;
camera - > setUpdateCallback ( new SceneUtil : : StateSetUpdater ( ) ) ;
return camera ;
}
2020-12-19 14:15:58 +00:00
StereoView : : StereoView ( osg : : Node : : NodeMask noShaderMask , osg : : Node : : NodeMask sceneMask )
: mViewer ( nullptr )
, mMainCamera ( nullptr )
, mRoot ( nullptr )
2020-12-16 19:59:10 +00:00
, mStereoRoot ( new osg : : Group )
, mUpdateCallback ( new StereoUpdateCallback ( this ) )
, mTechnique ( Technique : : None )
2020-09-19 18:55:06 +00:00
, mNoShaderMask ( noShaderMask )
2020-12-16 19:59:10 +00:00
, mSceneMask ( sceneMask )
2020-12-19 14:15:58 +00:00
, mCullMask ( 0 )
2020-12-08 20:37:27 +00:00
, mMasterConfig ( new SharedShadowMapConfig )
, mSlaveConfig ( new SharedShadowMapConfig )
, mSharedShadowMaps ( Settings : : Manager : : getBool ( " shared shadow maps " , " Stereo " ) )
2020-09-14 20:17:07 +00:00
{
2020-12-08 20:37:27 +00:00
mMasterConfig - > _id = " STEREO " ;
mMasterConfig - > _master = true ;
mSlaveConfig - > _id = " STEREO " ;
mSlaveConfig - > _master = false ;
2020-12-16 19:59:10 +00:00
mStereoRoot - > setName ( " Stereo Root " ) ;
mStereoRoot - > setDataVariance ( osg : : Object : : STATIC ) ;
mStereoRoot - > addChild ( mStereoGeometryShaderRoot ) ;
mStereoRoot - > addChild ( mStereoBruteForceRoot ) ;
mStereoRoot - > addCullCallback ( new StereoStatesetUpdateCallback ( this ) ) ;
2020-09-14 20:17:07 +00:00
2020-12-10 20:29:56 +00:00
if ( sInstance )
throw std : : logic_error ( " Double instance og StereoView " ) ;
sInstance = this ;
2020-12-19 14:15:58 +00:00
auto * ds = osg : : DisplaySettings : : instance ( ) . get ( ) ;
ds - > setStereo ( true ) ;
ds - > setStereoMode ( osg : : DisplaySettings : : StereoMode : : HORIZONTAL_SPLIT ) ;
ds - > setUseSceneViewForStereoHint ( true ) ;
2020-09-19 18:55:06 +00:00
}
2020-12-19 14:15:58 +00:00
void StereoView : : initializeStereo ( osgViewer : : Viewer * viewer , Technique technique )
2020-09-19 18:55:06 +00:00
{
2020-12-19 14:15:58 +00:00
mViewer = viewer ;
mRoot = viewer - > getSceneData ( ) - > asGroup ( ) ;
mMainCamera = viewer - > getCamera ( ) ;
mCullMask = mMainCamera - > getCullMask ( ) ;
setStereoTechnique ( technique ) ;
}
2020-09-19 18:55:06 +00:00
2020-12-19 14:15:58 +00:00
void StereoView : : initializeScene ( )
{
SceneUtil : : FindByNameVisitor findScene ( " Scene Root " ) ;
mRoot - > accept ( findScene ) ;
mScene = findScene . mFoundNode ;
if ( ! mScene )
throw std : : logic_error ( " Couldn't find scene root " ) ;
if ( mTechnique = = Technique : : GeometryShader_IndexedViewports )
2020-12-08 20:37:27 +00:00
{
2020-12-19 14:15:58 +00:00
mLeftCamera - > addChild ( mScene ) ; // Use scene directly to avoid redundant shadow computation.
mRightCamera - > addChild ( mScene ) ;
2020-12-08 20:37:27 +00:00
}
2020-12-19 14:15:58 +00:00
}
2020-12-08 20:37:27 +00:00
2020-12-19 14:15:58 +00:00
void StereoView : : setupBruteForceTechnique ( )
{
auto * ds = osg : : DisplaySettings : : instance ( ) . get ( ) ;
ds - > setStereo ( true ) ;
ds - > setStereoMode ( osg : : DisplaySettings : : StereoMode : : HORIZONTAL_SPLIT ) ;
ds - > setUseSceneViewForStereoHint ( true ) ;
struct ComputeStereoMatricesCallback : public osgUtil : : SceneView : : ComputeStereoMatricesCallback
{
ComputeStereoMatricesCallback ( StereoView * sv )
: mStereoView ( sv )
{
2020-09-19 18:55:06 +00:00
2020-12-19 14:15:58 +00:00
}
2020-12-16 19:59:10 +00:00
2020-12-19 14:15:58 +00:00
osg : : Matrixd computeLeftEyeProjection ( const osg : : Matrixd & projection ) const override
{
return mStereoView - > computeLeftEyeProjection ( projection ) ;
}
2020-12-16 19:59:10 +00:00
2020-12-19 14:15:58 +00:00
osg : : Matrixd computeLeftEyeView ( const osg : : Matrixd & view ) const override
{
return mStereoView - > computeLeftEyeView ( view ) ;
}
osg : : Matrixd computeRightEyeProjection ( const osg : : Matrixd & projection ) const override
{
return mStereoView - > computeRightEyeProjection ( projection ) ;
}
osg : : Matrixd computeRightEyeView ( const osg : : Matrixd & view ) const override
{
return mStereoView - > computeRightEyeView ( view ) ;
}
StereoView * mStereoView ;
} ;
auto * renderer = static_cast < osgViewer : : Renderer * > ( mMainCamera - > getRenderer ( ) ) ;
// osgViewer::Renderer always has two scene views
for ( auto * sceneView : { renderer - > getSceneView ( 0 ) , renderer - > getSceneView ( 1 ) } )
{
sceneView - > setComputeStereoMatricesCallback ( new ComputeStereoMatricesCallback ( this ) ) ;
2020-12-19 14:47:16 +00:00
if ( mSharedShadowMaps )
{
sceneView - > getCullVisitorLeft ( ) - > setUserData ( mMasterConfig ) ;
sceneView - > getCullVisitorRight ( ) - > setUserData ( mSlaveConfig ) ;
}
}
2020-09-19 18:55:06 +00:00
}
void StereoView : : setupGeometryShaderIndexedViewportTechnique ( )
{
2020-12-16 19:59:10 +00:00
mLeftCamera = createCamera ( " Stereo Left " , GL_NONE ) ;
mRightCamera = createCamera ( " Stereo Right " , GL_NONE ) ;
2020-09-14 20:17:07 +00:00
mStereoBruteForceRoot - > addChild ( mLeftCamera ) ;
mStereoBruteForceRoot - > addChild ( mRightCamera ) ;
2020-09-14 21:09:44 +00:00
2020-09-19 18:55:06 +00:00
// Inject self as the root of the scene graph
2020-12-10 20:29:56 +00:00
mStereoGeometryShaderRoot - > addChild ( mRoot ) ;
2020-12-16 19:59:10 +00:00
mViewer - > setSceneData ( mStereoRoot ) ;
}
static void removeSlave ( osgViewer : : Viewer * viewer , osg : : Camera * camera )
{
for ( unsigned int i = 0 ; i < viewer - > getNumSlaves ( ) ; i + + )
{
auto & slave = viewer - > getSlave ( i ) ;
2020-12-19 14:15:58 +00:00
if ( slave . _camera = = camera )
2020-12-16 19:59:10 +00:00
{
viewer - > removeSlave ( i ) ;
return ;
}
}
}
void StereoView : : removeBruteForceTechnique ( )
{
2020-12-19 14:15:58 +00:00
auto * ds = osg : : DisplaySettings : : instance ( ) . get ( ) ;
ds - > setStereo ( false ) ;
if ( mMainCamera - > getUserData ( ) = = mMasterConfig )
mMainCamera - > setUserData ( nullptr ) ;
2020-12-16 19:59:10 +00:00
}
void StereoView : : removeGeometryShaderIndexedViewportTechnique ( )
{
mStereoGeometryShaderRoot - > removeChild ( mRoot ) ;
mViewer - > setSceneData ( mRoot ) ;
mStereoBruteForceRoot - > removeChild ( mLeftCamera ) ;
mStereoBruteForceRoot - > removeChild ( mRightCamera ) ;
mLeftCamera = nullptr ;
mRightCamera = nullptr ;
}
void StereoView : : disableStereo ( )
{
if ( mTechnique = = Technique : : None )
return ;
mMainCamera - > removeUpdateCallback ( mUpdateCallback ) ;
switch ( mTechnique )
{
case Technique : : GeometryShader_IndexedViewports :
removeGeometryShaderIndexedViewportTechnique ( ) ; break ;
case Technique : : BruteForce :
removeBruteForceTechnique ( ) ; break ;
default : break ;
}
mMainCamera - > setCullMask ( mCullMask ) ;
}
void StereoView : : enableStereo ( )
{
if ( mTechnique = = Technique : : None )
return ;
// Update stereo statesets/matrices, but after the main camera updates.
auto mainCameraCB = mMainCamera - > getUpdateCallback ( ) ;
mMainCamera - > removeUpdateCallback ( mainCameraCB ) ;
mMainCamera - > addUpdateCallback ( mUpdateCallback ) ;
mMainCamera - > addUpdateCallback ( mainCameraCB ) ;
switch ( mTechnique )
{
case Technique : : GeometryShader_IndexedViewports :
setupGeometryShaderIndexedViewportTechnique ( ) ; break ;
case Technique : : BruteForce :
setupBruteForceTechnique ( ) ; break ;
default : break ;
}
setCullMask ( mCullMask ) ;
}
void StereoView : : setStereoTechnique ( Technique technique )
{
if ( technique = = mTechnique )
return ;
auto cullCB = mCullCallback ;
auto initialDrawCB = mInitialDrawCallback ;
auto predrawCB = mPreDrawCallback ;
auto postDrawCB = mPostDrawCallback ;
setCullCallback ( nullptr ) ;
setInitialDrawCallback ( nullptr ) ;
setPostdrawCallback ( nullptr ) ;
setPredrawCallback ( nullptr ) ;
disableStereo ( ) ;
mTechnique = technique ;
enableStereo ( ) ;
setCullCallback ( cullCB ) ;
setInitialDrawCallback ( initialDrawCB ) ;
setPostdrawCallback ( predrawCB ) ;
setPredrawCallback ( postDrawCB ) ;
2020-09-14 20:17:07 +00:00
}
2020-09-14 21:09:44 +00:00
void StereoView : : update ( )
2020-09-14 20:17:07 +00:00
{
auto viewMatrix = mViewer - > getCamera ( ) - > getViewMatrix ( ) ;
auto projectionMatrix = mViewer - > getCamera ( ) - > getProjectionMatrix ( ) ;
View left { } ;
View right { } ;
2020-12-16 19:59:10 +00:00
double near_ = 1.f ;
double far_ = 10000.f ;
2021-01-01 18:50:52 +00:00
auto updateViewCallback = mUpdateViewCallback . lock ( ) ;
if ( ! updateViewCallback )
2020-09-14 20:17:07 +00:00
{
2021-01-01 18:50:52 +00:00
Log ( Debug : : Error ) < < " StereoView: No update view callback. Stereo rendering will not work. " ;
return ;
2020-09-14 20:17:07 +00:00
}
2021-01-01 18:50:52 +00:00
updateViewCallback - > updateView ( left , right ) ;
2020-12-16 19:59:10 +00:00
near_ = Settings : : Manager : : getFloat ( " near clip " , " Camera " ) ;
far_ = Settings : : Manager : : getFloat ( " viewing distance " , " Camera " ) ;
2020-09-14 20:17:07 +00:00
osg : : Vec3d leftEye = left . pose . position ;
osg : : Vec3d rightEye = right . pose . position ;
osg : : Matrix leftViewOffset = left . pose . viewMatrix ( true ) ;
osg : : Matrix rightViewOffset = right . pose . viewMatrix ( true ) ;
osg : : Matrix leftViewMatrix = viewMatrix * leftViewOffset ;
osg : : Matrix rightViewMatrix = viewMatrix * rightViewOffset ;
2020-12-16 19:59:10 +00:00
osg : : Matrix leftProjectionMatrix = left . fov . perspectiveMatrix ( near_ , far_ ) ;
osg : : Matrix rightProjectionMatrix = right . fov . perspectiveMatrix ( near_ , far_ ) ;
2020-09-14 20:17:07 +00:00
2020-09-14 21:09:44 +00:00
mRightCamera - > setViewMatrix ( rightViewMatrix ) ;
mLeftCamera - > setViewMatrix ( leftViewMatrix ) ;
mRightCamera - > setProjectionMatrix ( rightProjectionMatrix ) ;
mLeftCamera - > setProjectionMatrix ( leftProjectionMatrix ) ;
2020-09-14 20:17:07 +00:00
2020-09-19 18:55:06 +00:00
auto width = mMainCamera - > getViewport ( ) - > width ( ) ;
auto height = mMainCamera - > getViewport ( ) - > height ( ) ;
2020-12-08 20:37:27 +00:00
// To correctly cull when drawing stereo using the geometry shader, the main camera must
// draw a fake view+perspective that includes the full frustums of both the left and right eyes.
// This frustum will be computed as a perspective frustum from a position P slightly behind the eyes L and R
// where it creates the minimum frustum encompassing both eyes' frustums.
// NOTE: I make an assumption that the eyes lie in a horizontal plane relative to the base view,
// and lie mirrored around the Y axis (straight ahead).
// Re-think this if that turns out to be a bad assumption.
View frustumView ;
// Compute Frustum angles. A simple min/max.
/* Example values for reference:
Left :
angleLeft - 0.767549932 float
angleRight 0.620896876 float
angleDown - 0.837898076 float
angleUp 0.726982594 float
Right :
angleLeft - 0.620896876 float
angleRight 0.767549932 float
angleDown - 0.837898076 float
angleUp 0.726982594 float
*/
frustumView . fov . angleLeft = std : : min ( left . fov . angleLeft , right . fov . angleLeft ) ;
frustumView . fov . angleRight = std : : max ( left . fov . angleRight , right . fov . angleRight ) ;
frustumView . fov . angleDown = std : : min ( left . fov . angleDown , right . fov . angleDown ) ;
frustumView . fov . angleUp = std : : max ( left . fov . angleUp , right . fov . angleUp ) ;
// Check that the case works for this approach
auto maxAngle = std : : max ( frustumView . fov . angleRight - frustumView . fov . angleLeft , frustumView . fov . angleUp - frustumView . fov . angleDown ) ;
if ( maxAngle > osg : : PI )
2020-09-14 20:17:07 +00:00
{
2020-12-08 20:37:27 +00:00
Log ( Debug : : Error ) < < " Total FOV exceeds 180 degrees. Case cannot be culled in single-pass VR. Disabling culling to cope. Consider switching to dual-pass VR. " ;
mMainCamera - > setCullingActive ( false ) ;
return ;
// TODO: An explicit frustum projection could cope, so implement that later. Guarantee you there will be VR headsets with total horizontal fov > 180 in the future. Maybe already.
}
// Use the law of sines on the triangle spanning PLR to determine P
double angleLeft = std : : abs ( frustumView . fov . angleLeft ) ;
double angleRight = std : : abs ( frustumView . fov . angleRight ) ;
double lengthRL = ( rightEye - leftEye ) . length ( ) ;
double ratioRL = lengthRL / std : : sin ( osg : : PI - angleLeft - angleRight ) ;
double lengthLP = ratioRL * std : : sin ( angleRight ) ;
2020-09-19 18:55:06 +00:00
2020-12-08 20:37:27 +00:00
osg : : Vec3d directionLP = osg : : Vec3 ( std : : cos ( - angleLeft ) , std : : sin ( - angleLeft ) , 0 ) ;
osg : : Vec3d LP = directionLP * lengthLP ;
frustumView . pose . position = leftEye + LP ;
//frustumView.pose.position.x() += 1000;
2020-09-19 18:55:06 +00:00
2020-12-08 20:37:27 +00:00
// Base view position is 0.0, by definition.
// The length of the vector P is therefore the required offset to near/far.
auto nearFarOffset = frustumView . pose . position . length ( ) ;
2020-09-19 18:55:06 +00:00
2020-12-08 20:37:27 +00:00
// Generate the frustum matrices
auto frustumViewMatrix = viewMatrix * frustumView . pose . viewMatrix ( true ) ;
2020-12-16 19:59:10 +00:00
auto frustumProjectionMatrix = frustumView . fov . perspectiveMatrix ( near_ + nearFarOffset , far_ + nearFarOffset ) ;
2020-09-19 18:55:06 +00:00
2020-12-08 20:37:27 +00:00
if ( mTechnique = = Technique : : GeometryShader_IndexedViewports )
{
2020-09-19 18:55:06 +00:00
// Update camera with frustum matrices
mMainCamera - > setViewMatrix ( frustumViewMatrix ) ;
mMainCamera - > setProjectionMatrix ( frustumProjectionMatrix ) ;
mLeftCamera - > getOrCreateStateSet ( ) - > setAttribute ( new osg : : ViewportIndexed ( 0 , 0 , 0 , width / 2 , height ) , osg : : StateAttribute : : OVERRIDE ) ;
mRightCamera - > getOrCreateStateSet ( ) - > setAttribute ( new osg : : ViewportIndexed ( 0 , width / 2 , 0 , width / 2 , height ) , osg : : StateAttribute : : OVERRIDE ) ;
2020-09-14 20:17:07 +00:00
}
2020-09-19 18:55:06 +00:00
else
{
mLeftCamera - > setClearColor ( mMainCamera - > getClearColor ( ) ) ;
mRightCamera - > setClearColor ( mMainCamera - > getClearColor ( ) ) ;
2020-09-14 20:17:07 +00:00
2020-09-19 18:55:06 +00:00
mLeftCamera - > setViewport ( 0 , 0 , width / 2 , height ) ;
mRightCamera - > setViewport ( width / 2 , 0 , width / 2 , height ) ;
2020-12-08 20:37:27 +00:00
if ( mMasterConfig - > _projection = = nullptr )
mMasterConfig - > _projection = new osg : : RefMatrix ;
if ( mMasterConfig - > _modelView = = nullptr )
mMasterConfig - > _modelView = new osg : : RefMatrix ;
if ( mSharedShadowMaps )
{
mMasterConfig - > _referenceFrame = mMainCamera - > getReferenceFrame ( ) ;
mMasterConfig - > _modelView - > set ( frustumViewMatrix ) ;
mMasterConfig - > _projection - > set ( projectionMatrix ) ;
}
2020-09-19 18:55:06 +00:00
}
2020-09-14 21:09:44 +00:00
}
2020-09-14 20:17:07 +00:00
2020-12-16 19:59:10 +00:00
void StereoView : : updateStateset ( osg : : StateSet * stateset )
2020-09-14 21:09:44 +00:00
{
// Manage viewports in update to automatically catch window/resolution changes.
auto width = mMainCamera - > getViewport ( ) - > width ( ) ;
auto height = mMainCamera - > getViewport ( ) - > height ( ) ;
stateset - > setAttribute ( new osg : : ViewportIndexed ( 0 , 0 , 0 , width / 2 , height ) ) ;
stateset - > setAttribute ( new osg : : ViewportIndexed ( 1 , width / 2 , 0 , width / 2 , height ) ) ;
2020-09-14 20:17:07 +00:00
2020-09-14 21:09:44 +00:00
// Update stereo uniforms
auto frustumViewMatrixInverse = osg : : Matrix : : inverse ( mMainCamera - > getViewMatrix ( ) ) ;
//auto frustumViewProjectionMatrixInverse = osg::Matrix::inverse(mMainCamera->getProjectionMatrix()) * osg::Matrix::inverse(mMainCamera->getViewMatrix());
2020-09-14 20:17:07 +00:00
auto * stereoViewMatrixUniform = stateset - > getUniform ( " stereoViewMatrices " ) ;
auto * stereoViewProjectionsUniform = stateset - > getUniform ( " stereoViewProjections " ) ;
2020-09-14 21:09:44 +00:00
stereoViewMatrixUniform - > setElement ( 0 , frustumViewMatrixInverse * mLeftCamera - > getViewMatrix ( ) ) ;
stereoViewMatrixUniform - > setElement ( 1 , frustumViewMatrixInverse * mRightCamera - > getViewMatrix ( ) ) ;
stereoViewProjectionsUniform - > setElement ( 0 , frustumViewMatrixInverse * mLeftCamera - > getViewMatrix ( ) * mLeftCamera - > getProjectionMatrix ( ) ) ;
stereoViewProjectionsUniform - > setElement ( 1 , frustumViewMatrixInverse * mRightCamera - > getViewMatrix ( ) * mRightCamera - > getProjectionMatrix ( ) ) ;
2020-09-14 20:17:07 +00:00
}
2020-12-16 19:59:10 +00:00
void StereoView : : setUpdateViewCallback ( std : : shared_ptr < UpdateViewCallback > cb )
2020-09-14 20:17:07 +00:00
{
2020-12-16 19:59:10 +00:00
mUpdateViewCallback = cb ;
2020-09-14 20:17:07 +00:00
}
void disableStereoForCamera ( osg : : Camera * camera )
{
auto * viewport = camera - > getViewport ( ) ;
camera - > getOrCreateStateSet ( ) - > setAttribute ( new osg : : ViewportIndexed ( 0 , viewport - > x ( ) , viewport - > y ( ) , viewport - > width ( ) , viewport - > height ( ) ) , osg : : StateAttribute : : OVERRIDE ) ;
camera - > getOrCreateStateSet ( ) - > addUniform ( new osg : : Uniform ( " geometryPassthrough " , true ) , osg : : StateAttribute : : OVERRIDE ) ;
}
void enableStereoForCamera ( osg : : Camera * camera , bool horizontalSplit )
{
auto * viewport = camera - > getViewport ( ) ;
auto x1 = viewport - > x ( ) ;
auto y1 = viewport - > y ( ) ;
auto width = viewport - > width ( ) ;
auto height = viewport - > height ( ) ;
auto x2 = x1 ;
auto y2 = y1 ;
if ( horizontalSplit )
{
width / = 2 ;
x2 + = width ;
}
else
{
height / = 2 ;
y2 + = height ;
}
camera - > getOrCreateStateSet ( ) - > setAttribute ( new osg : : ViewportIndexed ( 0 , x1 , y1 , width , height ) ) ;
camera - > getOrCreateStateSet ( ) - > setAttribute ( new osg : : ViewportIndexed ( 1 , x2 , y2 , width , height ) ) ;
camera - > getOrCreateStateSet ( ) - > addUniform ( new osg : : Uniform ( " geometryPassthrough " , false ) ) ;
}
2020-09-19 18:55:06 +00:00
StereoView : : Technique getStereoTechnique ( void )
{
auto stereoMethodString = Settings : : Manager : : getString ( " stereo method " , " Stereo " ) ;
auto stereoMethodStringLowerCase = Misc : : StringUtils : : lowerCase ( stereoMethodString ) ;
if ( stereoMethodStringLowerCase = = " geometryshader " )
{
return Misc : : StereoView : : Technique : : GeometryShader_IndexedViewports ;
}
if ( stereoMethodStringLowerCase = = " bruteforce " )
{
return Misc : : StereoView : : Technique : : BruteForce ;
}
Log ( Debug : : Warning ) < < " Unknown stereo technique \" " < < stereoMethodString < < " \" , defaulting to BruteForce " ;
return StereoView : : Technique : : BruteForce ;
}
2020-12-16 19:59:10 +00:00
void StereoView : : DefaultUpdateViewCallback : : updateView ( View & left , View & right )
2020-09-14 20:17:07 +00:00
{
left . pose . position = osg : : Vec3 ( - 2.2 , 0 , 0 ) ;
right . pose . position = osg : : Vec3 ( 2.2 , 0 , 0 ) ;
left . fov = { - 0.767549932 , 0.620896876 , - 0.837898076 , 0.726982594 } ;
right . fov = { - 0.620896876 , 0.767549932 , - 0.837898076 , 0.726982594 } ;
}
2020-12-10 20:29:56 +00:00
void StereoView : : setInitialDrawCallback ( osg : : ref_ptr < osg : : Camera : : DrawCallback > cb )
{
2020-12-16 19:59:10 +00:00
mInitialDrawCallback = cb ;
2020-12-20 19:32:32 +00:00
mMainCamera - > setInitialDrawCallback ( cb ) ;
2020-12-10 20:29:56 +00:00
}
void StereoView : : setPredrawCallback ( osg : : ref_ptr < osg : : Camera : : DrawCallback > cb )
{
2020-12-16 19:59:10 +00:00
mPreDrawCallback = cb ;
2020-12-20 19:32:32 +00:00
mMainCamera - > setPreDrawCallback ( cb ) ;
2020-12-10 20:29:56 +00:00
}
void StereoView : : setPostdrawCallback ( osg : : ref_ptr < osg : : Camera : : DrawCallback > cb )
{
2020-12-16 19:59:10 +00:00
mPostDrawCallback = cb ;
2020-12-20 19:32:32 +00:00
mMainCamera - > setPostDrawCallback ( cb ) ;
2020-12-16 19:59:10 +00:00
}
void StereoView : : setCullCallback ( osg : : ref_ptr < osg : : NodeCallback > cb )
{
2020-12-20 19:32:32 +00:00
mMainCamera - > setCullCallback ( cb ) ;
2020-12-16 19:59:10 +00:00
}
void StereoView : : setCullMask ( osg : : Node : : NodeMask cullMask )
{
mCullMask = cullMask ;
2020-12-10 20:29:56 +00:00
if ( mTechnique = = Technique : : GeometryShader_IndexedViewports )
{
2020-12-16 19:59:10 +00:00
mMainCamera - > setCullMask ( cullMask & ~ mNoShaderMask ) ;
mLeftCamera - > setCullMask ( ( cullMask & mNoShaderMask ) | mSceneMask ) ;
mRightCamera - > setCullMask ( ( cullMask & mNoShaderMask ) | mSceneMask ) ;
2020-12-10 20:29:56 +00:00
}
else
{
2020-12-20 19:32:32 +00:00
mMainCamera - > setCullMask ( cullMask ) ;
2021-01-01 10:50:18 +00:00
mMainCamera - > setCullMaskLeft ( cullMask ) ;
mMainCamera - > setCullMaskRight ( cullMask ) ;
2020-12-10 20:29:56 +00:00
}
}
2020-12-16 19:59:10 +00:00
osg : : Node : : NodeMask StereoView : : getCullMask ( )
{
return mCullMask ;
}
2020-12-19 14:15:58 +00:00
osg : : Matrixd StereoView : : computeLeftEyeProjection ( const osg : : Matrixd & projection ) const
{
return mLeftCamera - > getProjectionMatrix ( ) ;
}
osg : : Matrixd StereoView : : computeLeftEyeView ( const osg : : Matrixd & view ) const
{
return mLeftCamera - > getViewMatrix ( ) ;
}
osg : : Matrixd StereoView : : computeRightEyeProjection ( const osg : : Matrixd & projection ) const
{
return mRightCamera - > getProjectionMatrix ( ) ;
}
osg : : Matrixd StereoView : : computeRightEyeView ( const osg : : Matrixd & view ) const
{
return mRightCamera - > getViewMatrix ( ) ;
}
2020-09-14 20:17:07 +00:00
}