On Stereo horizontal mode, the left and right images are now rendered to be viewed in landscape mode
preserving aspect ratio. Also fixed bug which caused to incorrectly reuse render list when camera was
different to the one used to update the render list before.
To set STEREO_HORIZONTAL mode, use '--view 1' as a command-line argument.
Change-Id: I7943b31bc6349c2a70244e86b4b0762e6bdb7ea0
mInvertYAxis( SceneGraph::CameraAttachment::DEFAULT_INVERT_Y_AXIS ),
mFieldOfView( SceneGraph::CameraAttachment::DEFAULT_FIELD_OF_VIEW ),
mAspectRatio( SceneGraph::CameraAttachment::DEFAULT_ASPECT_RATIO ),
- mStereoBias( SceneGraph::CameraAttachment::DEFAULT_STEREO_BIAS ),
mLeftClippingPlane( SceneGraph::CameraAttachment::DEFAULT_LEFT_CLIPPING_PLANE ),
mRightClippingPlane( SceneGraph::CameraAttachment::DEFAULT_RIGHT_CLIPPING_PLANE ),
mTopClippingPlane( SceneGraph::CameraAttachment::DEFAULT_TOP_CLIPPING_PLANE ),
mBottomClippingPlane( SceneGraph::CameraAttachment::DEFAULT_BOTTOM_CLIPPING_PLANE ),
mNearClippingPlane( SceneGraph::CameraAttachment::DEFAULT_NEAR_CLIPPING_PLANE ),
mFarClippingPlane( SceneGraph::CameraAttachment::DEFAULT_FAR_CLIPPING_PLANE ),
+ mStereoBias( SceneGraph::CameraAttachment::DEFAULT_STEREO_BIAS ),
mTargetPosition( SceneGraph::CameraAttachment::DEFAULT_TARGET_POSITION )
{
}
return mAspectRatio;
}
-void CameraAttachment::SetStereoBias(float stereoBias)
+void CameraAttachment::SetStereoBias(const Vector2& stereoBias)
{
- if( ! Equals(stereoBias, mStereoBias) )
+ if( ! Equals(stereoBias.x, mStereoBias.x ) || ! Equals(stereoBias.y, mStereoBias.y ) )
{
mStereoBias = stereoBias;
}
}
-float CameraAttachment::GetStereoBias(float stereoBias) const
+Vector2 CameraAttachment::GetStereoBias() const
{
return mStereoBias;
}
* @copydoc Dali::Camera::SetLeftClippingPlane * Set stereo bias. The frustum offset for a 3D camera
* @param[in] stereoBias The frustum offset for the 3D camera
*/
- void SetStereoBias(float stereoBias);
+ void SetStereoBias(const Vector2& stereoBias);
/**
* Get stereo bias. The frustum offset for a 3D camera
* @return The frustum offset for the 3D camera
*/
- float GetStereoBias(float stereoBias) const;
+ Vector2 GetStereoBias() const;
/**
* @copydoc Dali::Camera::SetLeftClippingPlane
bool mInvertYAxis;
float mFieldOfView;
float mAspectRatio;
- float mStereoBias;
float mLeftClippingPlane;
float mRightClippingPlane;
float mTopClippingPlane;
float mBottomClippingPlane;
float mNearClippingPlane;
float mFarClippingPlane;
+ Vector2 mStereoBias;
Vector3 mTargetPosition;
};
return mCameraAttachment->GetInvertYAxis();
}
-void CameraActor::SetPerspectiveProjection( const Size& size, float stereoBias /* = 0.0f */ )
+void CameraActor::SetPerspectiveProjection( const Size& size, const Vector2& stereoBias /* = Vector2::ZERO */ )
{
float width = size.width;
float height = size.height;
/**
* @copydoc Dali::CameraActor::SetPerspectiveProjection()
- * @param[in] stereoBias The frustum horizontal offset for stereoscopic cameras
+ * @param[in] stereoBias The frustum horizontal and vertical offset for stereoscopic cameras
*/
- void SetPerspectiveProjection( const Size& size, float stereoBias = 0.0f );
+ void SetPerspectiveProjection( const Size& size, const Vector2& stereoBias = Vector2::ZERO );
/**
* @copydoc Dali::CameraActor::SetOrthographicProjection(const Vector2& size);
// EXTERNAL INCLUDES
#include <algorithm>
+#include <cmath>
// INTERNAL INCLUDES
#include <dali/integration-api/system-overlay.h>
{
DALI_LOG_INFO( Debug::Filter::gActor, Debug::Concise, "View mode changed from %d to %d\n", mViewMode, viewMode);
- const float stereoBase( ( (mStereoBase / 25.4f) * GetDpi().x ) * 0.5f );
-
if( mViewMode == MONO )
{
mDefaultCamera->SetRotation( Degree( 180.0f ), Vector3::YAXIS );
mRenderTaskList->GetTask(0).SetSourceActor( Dali::Actor() );
+ //Create camera and RenderTask for left eye
mLeftCamera = CameraActor::New( Size::ZERO );
mLeftCamera->SetParentOrigin( ParentOrigin::CENTER );
- mLeftCamera->SetPerspectiveProjection( mSize, stereoBase );
- mLeftCamera->SetPosition( Vector3( stereoBase, 0.0f, 0.0f ) );
mDefaultCamera->Add( *mLeftCamera.Get() );
mLeftRenderTask = mRenderTaskList->CreateTask();
mLeftRenderTask.SetCameraActor( Dali::CameraActor( mLeftCamera.Get() ) );
mLeftCamera->SetType( Dali::Camera::FREE_LOOK );
+ //Create camera and RenderTask for right eye
mRightCamera = CameraActor::New( Size::ZERO );
mRightCamera->SetParentOrigin( ParentOrigin::CENTER );
- mRightCamera->SetPerspectiveProjection( mSize, -stereoBase );
- mRightCamera->SetPosition( Vector3( -stereoBase, 0.0f, 0.0f ) );
mDefaultCamera->Add( *mRightCamera.Get() );
mRightRenderTask = mRenderTaskList->CreateTask();
+ mRightRenderTask.SetClearColor( Vector4( 1.0f,0.0f,0.0f,1.0f));
+
mRightRenderTask.SetCameraActor( Dali::CameraActor( mRightCamera.Get() ) );
mRightCamera->SetType( Dali::Camera::FREE_LOOK );
}
mDefaultCamera->SetRotation( Degree( 0.0f ), Vector3::YAXIS );
mDefaultCamera->SetType( Dali::Camera::LOOK_AT_TARGET );
mRenderTaskList->GetTask(0).SetSourceActor( Dali::Layer(mRootLayer.Get()) );
+
break;
}
case STEREO_HORIZONTAL:
{
- mLeftRenderTask.SetViewport( Viewport(0, 0, mSize.width, mSize.height * 0.5f ) );
- mRightRenderTask.SetViewport( Viewport(0, mSize.height * 0.5f, mSize.width, mSize.height * 0.5f) );
+ //Stereo mode with horizontal split is for landscape mode. That's the reason for the cameras being rotated
+ //Top camera renders the scene as seen from the right eye and bottom camera as seen from left.
+
+ //Calculate separation in pixels along vertical axis ( mStereoBase is defined in millimetres )
+ const float stereoBase( ( (mStereoBase / 25.4f) * GetDpi().y ) * 0.5f );
+
+ //Calculate aspect ratio
+ float aspect = mSize.width / (mSize.height * 0.5f);
+
+ mLeftCamera->SetPerspectiveProjection( mSize, Vector2( 0.0f,stereoBase) );
+ mLeftCamera->SetAspectRatio( aspect );
+ mLeftCamera->SetRotation( Degree(-90.0f), Vector3::ZAXIS );
+ mLeftCamera->SetPosition( Vector3( stereoBase, 0.0f, 0.0f ) );
+ mLeftRenderTask.SetViewport( Viewport(0, mSize.height * 0.5f, mSize.width, mSize.height * 0.5f) );
+
+ mRightCamera->SetPerspectiveProjection( mSize, Vector2( 0.0, -stereoBase) );
+ mRightCamera->SetAspectRatio( aspect );
+ mRightCamera->SetRotation( Degree(-90.0f), Vector3::ZAXIS );
+ mRightCamera->SetPosition( Vector3(-stereoBase, 0.0f, 0.0f ) );
+ mRightRenderTask.SetViewport( Viewport(0, 0, mSize.width, mSize.height * 0.5f ) );
+
break;
}
case STEREO_VERTICAL:
{
+ //Calculate separation in pixels along horizontal axis
+ const float stereoBase( ( (mStereoBase / 25.4f) * GetDpi().x ) * 0.5f );
+
+ //Recalculate fov based on viewport size
+ const float fov = 2.0f * std::atan( mSize.y / (2.0f * std::max( mSize.x*0.5f, mSize.y )) );
+
+ mLeftCamera->SetPerspectiveProjection( Size( mSize.x * 0.5f, mSize.y ), Vector2(stereoBase,0.0f) );
+ mLeftCamera->SetFieldOfView( fov );
+ mLeftCamera->SetRotation( Degree(0.0f), Vector3::ZAXIS );
+ mLeftCamera->SetPosition( Vector3( stereoBase, 0.0f, 0.0f ) );
mLeftRenderTask.SetViewport( Viewport(0, 0, mSize.width * 0.5f, mSize.height ) );
+
+ mRightCamera->SetPerspectiveProjection( Size( mSize.x * 0.5f, mSize.y ), Vector2(-stereoBase,0.0f) );
+ mRightCamera->SetFieldOfView( fov );
+ mRightCamera->SetRotation( Degree(0.0f), Vector3::ZAXIS );
+ mRightCamera->SetPosition( Vector3( -stereoBase, 0.0f, 0.0f ) );
mRightRenderTask.SetViewport( Viewport(mSize.width * 0.5f, 0, mSize.width * 0.5f, mSize.height ) );
+
break;
}
case STEREO_INTERLACED:
DALI_LOG_INFO( Debug::Filter::gActor, Debug::Concise, "old( %.2f) new(%.2f)", mStereoBase, stereoBase );
mStereoBase = stereoBase;
- if( mViewMode != MONO )
+ switch( mViewMode )
{
- stereoBase *= 0.5f;
- mLeftCamera->SetX( stereoBase );
- mLeftCamera->SetPerspectiveProjection( mSize, stereoBase );
- mRightCamera->SetX( -stereoBase );
- mRightCamera->SetPerspectiveProjection( mSize, -stereoBase );
+ case STEREO_VERTICAL:
+ {
+ stereoBase = mStereoBase / 25.4f * GetDpi().y * 0.5f;
+ float aspect = mSize.width / (mSize.height * 0.5f);
+
+ mLeftCamera->SetPerspectiveProjection( mSize, Vector2( 0.0, stereoBase) );
+ mLeftCamera->SetAspectRatio( aspect );
+ mLeftCamera->SetX( stereoBase );
+
+ mRightCamera->SetPerspectiveProjection( mSize, Vector2( 0.0, -stereoBase) );
+ mRightCamera->SetAspectRatio( aspect );
+ mRightCamera->SetX( -stereoBase );
+
+ break;
+ }
+ case STEREO_HORIZONTAL:
+ {
+ stereoBase = mStereoBase / 25.4f * GetDpi().x * 0.5f;
+ const float fov = 2.0f * std::atan( mSize.y / (2.0f * std::max( mSize.x*0.5f, mSize.y )) );
+
+ mLeftCamera->SetPerspectiveProjection( Size( mSize.x * 0.5f, mSize.y ), Vector2(stereoBase,0.0f) );
+ mLeftCamera->SetFieldOfView( fov );
+ mLeftCamera->SetX( stereoBase );
+
+ mRightCamera->SetPerspectiveProjection( Size( mSize.x * 0.5f, mSize.y ), Vector2(-stereoBase,0.0f) );
+ mRightCamera->SetFieldOfView( fov );
+ mRightCamera->SetX( -stereoBase );
+
+ break;
+ }
+ default:
+ break;
}
}
}
const bool opaqueRenderablesExist( !layer.opaqueRenderables.empty() );
const bool transparentRenderablesExist( !layer.transparentRenderables.empty() );
const bool overlayRenderablesExist( !layer.overlayRenderables.empty() );
- const bool tryReuseRenderList( layer.CanReuseRenderers() && viewMatrixHasNotChanged );
+ const bool tryReuseRenderList( viewMatrixHasNotChanged && layer.CanReuseRenderers(renderTask.GetCamera()) );
// Ignore stencils if there's nothing to test
if( stencilRenderablesExist &&
m[12] = m[13] = m[15] = 0.0f;
}
-void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis, float stereoBias )
+void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis, const Vector2& stereoBias )
{
float frustumH = tanf( fovy * 0.5f ) * near;
float frustumW = frustumH * aspect;
- float bias = stereoBias * 0.5f;
+ Vector2 bias = stereoBias * 0.5f;
- Frustum(result, -(frustumW + bias), frustumW - bias, -frustumH, frustumH, near, far, invertYAxis);
+ Frustum(result, -(frustumW + bias.x), frustumW - bias.x, -(frustumH + bias.y), frustumH - bias.y, near, far, invertYAxis);
}
void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
const bool CameraAttachment::DEFAULT_INVERT_Y_AXIS( false );
const float CameraAttachment::DEFAULT_FIELD_OF_VIEW( 45.0f*(M_PI/180.0f) );
const float CameraAttachment::DEFAULT_ASPECT_RATIO( 4.0f/3.0f );
-const float CameraAttachment::DEFAULT_STEREO_BIAS( 0.0f );
const float CameraAttachment::DEFAULT_LEFT_CLIPPING_PLANE(-240.0f);
const float CameraAttachment::DEFAULT_RIGHT_CLIPPING_PLANE(240.0f);
const float CameraAttachment::DEFAULT_TOP_CLIPPING_PLANE(-400.0f);
const float CameraAttachment::DEFAULT_BOTTOM_CLIPPING_PLANE(400.0f);
const float CameraAttachment::DEFAULT_NEAR_CLIPPING_PLANE( 800.0f ); // default height of the screen
const float CameraAttachment::DEFAULT_FAR_CLIPPING_PLANE( DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE );
+const Vector2 CameraAttachment::DEFAULT_STEREO_BIAS( 0.0f, 0.0f );
const Vector3 CameraAttachment::DEFAULT_TARGET_POSITION( 0.0f, 0.0f, 0.0f );
mInvertYAxis( DEFAULT_INVERT_Y_AXIS ),
mFieldOfView( DEFAULT_FIELD_OF_VIEW ),
mAspectRatio( DEFAULT_ASPECT_RATIO ),
- mStereoBias( DEFAULT_STEREO_BIAS ),
mLeftClippingPlane( DEFAULT_LEFT_CLIPPING_PLANE ),
mRightClippingPlane( DEFAULT_RIGHT_CLIPPING_PLANE ),
mTopClippingPlane( DEFAULT_TOP_CLIPPING_PLANE ),
mBottomClippingPlane( DEFAULT_BOTTOM_CLIPPING_PLANE ),
mNearClippingPlane( DEFAULT_NEAR_CLIPPING_PLANE ),
mFarClippingPlane( DEFAULT_FAR_CLIPPING_PLANE ),
+ mStereoBias( DEFAULT_STEREO_BIAS ),
mTargetPosition( DEFAULT_TARGET_POSITION ),
mViewMatrix( Matrix::IDENTITY ),
mProjectionMatrix( Matrix::IDENTITY ),
mUpdateProjectionFlag = UPDATE_COUNT;
}
-void CameraAttachment::SetStereoBias( float stereoBias )
+void CameraAttachment::SetStereoBias( const Vector2& stereoBias )
{
mStereoBias = stereoBias;
mUpdateProjectionFlag = UPDATE_COUNT;
static const bool DEFAULT_INVERT_Y_AXIS;
static const float DEFAULT_FIELD_OF_VIEW;
static const float DEFAULT_ASPECT_RATIO;
- static const float DEFAULT_STEREO_BIAS;
static const float DEFAULT_LEFT_CLIPPING_PLANE;
static const float DEFAULT_RIGHT_CLIPPING_PLANE;
static const float DEFAULT_TOP_CLIPPING_PLANE;
static const float DEFAULT_BOTTOM_CLIPPING_PLANE;
static const float DEFAULT_NEAR_CLIPPING_PLANE;
static const float DEFAULT_FAR_CLIPPING_PLANE;
+ static const Vector2 DEFAULT_STEREO_BIAS;
static const Vector3 DEFAULT_TARGET_POSITION;
/**
/**
* @copydoc Dali::Internal::CameraAttachment::SetStereoBias
*/
- void SetStereoBias(float stereoBias);
+ void SetStereoBias(const Vector2& stereoBias);
/**
* @copydoc Dali::Internal::CameraAttachment::SetLeftClippingPlane
float mFieldOfView;
float mAspectRatio;
- float mStereoBias;
float mLeftClippingPlane;
float mRightClippingPlane;
float mTopClippingPlane;
float mBottomClippingPlane;
float mNearClippingPlane;
float mFarClippingPlane;
+ Vector2 mStereoBias;
Vector3 mTargetPosition;
InheritedProperty<Matrix> mViewMatrix; ///< The view-matrix; this is double buffered for input handling.
new (slot) LocalType( &attachment, &CameraAttachment::SetAspectRatio, parameter );
}
-inline void SetStereoBiasMessage( EventToUpdate& eventToUpdate, const CameraAttachment& attachment, float parameter )
+inline void SetStereoBiasMessage( EventToUpdate& eventToUpdate, const CameraAttachment& attachment, const Vector2& parameter )
{
- typedef MessageValue1< CameraAttachment, float > LocalType;
+ typedef MessageValue1< CameraAttachment, Vector2 > LocalType;
// Reserve some memory inside the message queue
unsigned int* slot = eventToUpdate.ReserveMessageSlot( sizeof( LocalType ) );
Layer::Layer()
: mSortFunction( Dali::Layer::ZValue ),
mClippingBox( 0,0,0,0 ),
+ mLastCamera(0),
mIsClipping( false ),
mDepthTestDisabled( false ),
mIsDefaultSortFunction( true )
}
/**
- * @return True if all children have been clean for two consequtive frames
+ * Checks if it is ok to reuse renderers. Renderers can be reused if ModelView transform for all the renderers
+ * has not changed from previous use.
+ * @param[in] camera A pointer to the camera that we want to use to render the list.
+ * @return True if all children transforms have been clean for two consecutive frames and the camera we are going
+ * to use is the same than the one used before ( Otherwise View transform will be different )
+ *
*/
- bool CanReuseRenderers()
+ bool CanReuseRenderers(Node* camera)
{
- return mAllChildTransformsClean[ 0 ] && mAllChildTransformsClean[ 1 ];
+ bool bReturn( mAllChildTransformsClean[ 0 ] && mAllChildTransformsClean[ 1 ] && camera == mLastCamera );
+ mLastCamera = camera;
+
+ return bReturn;
}
/**
SortFunctionType mSortFunction; ///< Used to sort semi-transparent geometry
ClippingBox mClippingBox; ///< The clipping box, in window coordinates
+ Node* mLastCamera; ///< Pointer to the last camera that has rendered the layer
+
bool mAllChildTransformsClean[ 2 ]; ///< True if all child nodes transforms are clean,
/// double buffered as we need two clean frames before we can reuse N-1 for N+1
/// this allows us to cache render items when layer is "static"
return false;
}
+Node* RenderTask::GetCamera() const
+{
+ return mCameraNode;
+}
+
void RenderTask::ResetDefaultProperties( BufferIndex updateBufferIndex )
{
// Reset default properties
*/
void SetCompleteStatusManager( CompleteStatusManager* completeStatusManager );
+ /**
+ * @return A pointer to the camera used by the RenderTask
+ */
+ Node* GetCamera() const;
+
private:
/**