dpiHorizontal = dpiVertical = 96;
};
+int TestRenderSurface::GetOrientation() const
+{
+ return 0;
+};
+
void TestRenderSurface::InitializeGraphics()
{
}
virtual void GetDpi( unsigned int& dpiHorizontal, unsigned int& dpiVertical );
/**
+ * @copydoc Dali::Integration::RenderSurface::GetOrientation
+ */
+ virtual int GetOrientation() const;
+
+ /**
* @copydoc Dali::Integration::RenderSurface::InitializeGraphics
*/
virtual void InitializeGraphics();
Vector2 newSize( 1000.0f, 2000.0f );
DALI_TEST_CHECK( stage.GetSize() != newSize );
defaultSurface->MoveResize( PositionSize( 0, 0, newSize.width, newSize.height ) );
- defaultScene.SurfaceResized();
+ defaultScene.SurfaceResized( false );
DALI_TEST_EQUALS( stage.GetSize(), newSize, TEST_LOCATION );
DALI_TEST_EQUALS( defaultScene.GetSize(), newSize, TEST_LOCATION );
Vector2 originalSurfaceSize( 500.0f, 1000.0f );
TestRenderSurface surface( PositionSize( 0.0f, 0.0f, originalSurfaceSize.width, originalSurfaceSize.height ) );
+
auto scene = Integration::Scene::New( surface );
// Ensure stage size does NOT match the surface size
Vector2 newSize( 1000.0f, 2000.0f );
DALI_TEST_CHECK( stage.GetSize() != newSize );
surface.MoveResize( PositionSize( 0, 0, newSize.width, newSize.height ) );
- scene.SurfaceResized();
+ scene.SurfaceResized( false );
// Ensure the stage hasn't been resized
DALI_TEST_EQUALS( stage.GetSize(), stageSize, TEST_LOCATION );
virtual void GetDpi( unsigned int& dpiHorizontal, unsigned int& dpiVertical ) = 0;
/**
+ * @brief Return the orientation of the surface.
+ * @return The orientation
+ */
+ virtual int GetOrientation() const = 0;
+
+ /**
* @brief InitializeGraphics the platform specific graphics surface interfaces
*/
virtual void InitializeGraphics() = 0;
GetImplementation(*this).SetSurface( surface );
}
-void Scene::SurfaceResized()
+void Scene::SurfaceResized( bool forceUpdate )
{
- GetImplementation( *this ).SurfaceResized();
+ GetImplementation( *this ).SurfaceResized( forceUpdate );
}
Integration::RenderSurface* Scene::GetSurface() const
/**
* @brief Informs the scene that the set surface has been resized.
*/
- void SurfaceResized();
+ void SurfaceResized( bool forceUpdate );
/**
* @brief Gets the rendering surface bound to the scene
return mSceneObject;
}
+void CameraActor::RotateProjection( int rotationAngle )
+{
+ // sceneObject is being used in a separate thread; queue a message to set
+ RotateProjectionMessage( GetEventThreadServices(), *mSceneObject, rotationAngle );
+}
+
void CameraActor::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
{
if(index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT)
*/
const SceneGraph::Camera* GetCamera() const;
+ /**
+ * Rotate the projection.
+ * It is used in case that the target buffer direction is different from the window direction.
+ * @param [in] rotationAngle The rotation angle
+ */
+ void RotateProjection( int rotationAngle );
+
public: // properties
/**
mSize(), // Don't set the proper value here, this will be set when the surface is set later
mDpi(),
mBackgroundColor( DEFAULT_BACKGROUND_COLOR ),
+ mSurfaceOrientation( 0 ),
mDepthTreeDirty( false ),
mEventProcessor( *this, ThreadLocalStorage::GetInternal()->GetGestureEventProcessor() )
{
mFrameBuffer = Dali::Internal::FrameBuffer::New( surface, Dali::FrameBuffer::Attachment::NONE );
defaultRenderTask->SetFrameBuffer( mFrameBuffer );
- SurfaceResized();
+ SurfaceResized( false );
}
}
-void Scene::SurfaceResized()
+void Scene::SurfaceResized( bool forceUpdate )
{
if( mSurface )
{
const PositionSize surfacePositionSize = mSurface->GetPositionSize();
const float fWidth = static_cast< float >( surfacePositionSize.width );
const float fHeight = static_cast< float >( surfacePositionSize.height );
+ const int orientation = mSurface->GetOrientation();
- if( ( fabsf( mSize.width - fWidth ) > Math::MACHINE_EPSILON_1 ) || ( fabsf( mSize.height - fHeight ) > Math::MACHINE_EPSILON_1 ) )
+ if( ( ( fabsf( mSize.width - fWidth ) > Math::MACHINE_EPSILON_1 ) || ( fabsf( mSize.height - fHeight ) > Math::MACHINE_EPSILON_1 ) )
+ || ( orientation != mSurfaceOrientation )
+ || ( forceUpdate ) )
{
Rect< int32_t > newSize( 0, 0, static_cast< int32_t >( surfacePositionSize.width ), static_cast< int32_t >( surfacePositionSize.height ) ); // truncated
mSize.width = fWidth;
mSize.height = fHeight;
+ mSurfaceOrientation = orientation;
// Calculates the aspect ratio, near and far clipping planes, field of view and camera Z position.
mDefaultCamera->SetPerspectiveProjection( mSize );
+ mDefaultCamera->RotateProjection( mSurfaceOrientation );
mRootLayer->SetSize( mSize.width, mSize.height );
ThreadLocalStorage* tls = ThreadLocalStorage::GetInternal();
SceneGraph::UpdateManager& updateManager = tls->GetUpdateManager();
SetDefaultSurfaceRectMessage( updateManager, newSize );
+ SetDefaultSurfaceOrientationMessage( updateManager, mSurfaceOrientation );
// set default render-task viewport parameters
RenderTaskPtr defaultRenderTask = mRenderTaskList->GetTask( 0u );
/**
* Notify the surface has been resized.
*/
- void SurfaceResized();
+ void SurfaceResized( bool forceUpdate );
/**
* Notify the surface has been deleted.
Vector4 mBackgroundColor;
+ // The SurfaceOrientation
+ int mSurfaceOrientation;
+
LayerPtr mRootLayer;
// Ordered list of currently on-stage layers
* @param[in] item The current RenderItem about to be rendered
* @param[in] context The context
*/
-inline void RenderAlgorithms::SetupScissorClipping( const RenderItem& item, Context& context )
+inline void RenderAlgorithms::SetupScissorClipping( const RenderItem& item, Context& context, int orientation )
{
// Get the number of child scissors in the stack (do not include layer or root box).
size_t childStackDepth = mScissorStack.size() - 1u;
if( scissorEnabled )
{
ClippingBox useScissorBox( mScissorStack.back() );
- context.Scissor( useScissorBox.x, useScissorBox.y, useScissorBox.width, useScissorBox.height );
+ GLint x = useScissorBox.x;
+ GLint y = useScissorBox.y;
+ if( orientation == 90 )
+ {
+ x = mViewportRectangle.height - (useScissorBox.y + useScissorBox.height);
+ y = useScissorBox.x;
+ context.Scissor( x, y, useScissorBox.height, useScissorBox.width );
+ }
+ else if( orientation == 180 )
+ {
+ x = mViewportRectangle.width - (useScissorBox.x + useScissorBox.width);
+ y = mViewportRectangle.height - (useScissorBox.y + useScissorBox.height);
+ context.Scissor( x, y, useScissorBox.width, useScissorBox.height );
+ }
+ else if( orientation == 270 )
+ {
+ x = useScissorBox.y;
+ y = mViewportRectangle.width - (useScissorBox.x + useScissorBox.width);
+ context.Scissor( x, y, useScissorBox.height, useScissorBox.width );
+ }
+ else
+ {
+ context.Scissor( x, y, useScissorBox.width, useScissorBox.height );
+ }
}
}
}
bool& usedStencilBuffer,
uint32_t& lastClippingDepth,
uint32_t& lastClippingId,
- Integration::StencilBufferAvailable stencilBufferAvailable )
+ Integration::StencilBufferAvailable stencilBufferAvailable,
+ int orientation )
{
RenderMode::Type renderMode = RenderMode::AUTO;
const Renderer *renderer = item.mRenderer;
// As both scissor and stencil clips can be nested, we may be simultaneously traversing up the scissor tree, requiring a scissor to be un-done. Whilst simultaneously adding a new stencil clip.
// We process both based on our current and old clipping depths for each mode.
// Both methods with return rapidly if there is nothing to be done for that type of clipping.
- SetupScissorClipping( item, context );
+ SetupScissorClipping( item, context, orientation );
if( stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE )
{
const Matrix& projectionMatrix,
Integration::DepthBufferAvailable depthBufferAvailable,
Integration::StencilBufferAvailable stencilBufferAvailable,
- Vector<GLuint>& boundTextures )
+ Vector<GLuint>& boundTextures,
+ int orientation )
{
DALI_PRINT_RENDER_LIST( renderList );
mViewportRectangle = context.GetViewport();
mHasLayerScissor = false;
+ if( orientation == 90 || orientation == 270 )
+ {
+ int temp = mViewportRectangle.width;
+ mViewportRectangle.width = mViewportRectangle.height;
+ mViewportRectangle.height = temp;
+ }
+
// Setup Scissor testing (for both viewport and per-node scissor)
mScissorStack.clear();
if( renderList.IsClipping() )
{
context.SetScissorTest( true );
const ClippingBox& layerScissorBox = renderList.GetClippingBox();
- context.Scissor( layerScissorBox.x, layerScissorBox.y, layerScissorBox.width, layerScissorBox.height );
+ GLint x = layerScissorBox.x;
+ GLint y = layerScissorBox.y;
+
+ if( orientation == 90 )
+ {
+ x = mViewportRectangle.height - (layerScissorBox.y + layerScissorBox.height);
+ y = layerScissorBox.x;
+ context.Scissor( x, y, layerScissorBox.height, layerScissorBox.width );
+ }
+ else if( orientation == 180 )
+ {
+ x = mViewportRectangle.width - (layerScissorBox.x + layerScissorBox.width);
+ y = mViewportRectangle.height - (layerScissorBox.y + layerScissorBox.height);
+ context.Scissor( x, y, layerScissorBox.width, layerScissorBox.height );
+ }
+ else if( orientation == 270 )
+ {
+ x = layerScissorBox.y;
+ y = mViewportRectangle.width - (layerScissorBox.x + layerScissorBox.width);
+ context.Scissor( x, y, layerScissorBox.height, layerScissorBox.width );
+ }
+ else
+ {
+ context.Scissor( x, y, layerScissorBox.width, layerScissorBox.height );
+ }
+
mScissorStack.push_back( layerScissorBox );
mHasLayerScissor = true;
}
// Set up clipping based on both the Renderer and Actor APIs.
// The Renderer API will be used if specified. If AUTO, the Actors automatic clipping feature will be used.
- SetupClipping( item, context, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable );
+ SetupClipping( item, context, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable, orientation );
if( DALI_LIKELY( item.mRenderer ) )
{
BufferIndex bufferIndex,
Integration::DepthBufferAvailable depthBufferAvailable,
Integration::StencilBufferAvailable stencilBufferAvailable,
- Vector<GLuint>& boundTextures )
+ Vector<GLuint>& boundTextures,
+ int orientation )
{
DALI_PRINT_RENDER_INSTRUCTION( instruction, bufferIndex );
*projectionMatrix,
depthBufferAvailable,
stencilBufferAvailable,
- boundTextures );
+ boundTextures,
+ orientation );
}
}
}
BufferIndex bufferIndex,
Integration::DepthBufferAvailable depthBufferAvailable,
Integration::StencilBufferAvailable stencilBufferAvailable,
- Vector<GLuint>& boundTextures );
+ Vector<GLuint>& boundTextures,
+ int orientation);
private:
* @param[in] item The current RenderItem (about to be rendered)
* @param[in] context The current Context
*/
- inline void SetupScissorClipping( const Dali::Internal::SceneGraph::RenderItem& item, Context& context );
+ inline void SetupScissorClipping( const Dali::Internal::SceneGraph::RenderItem& item, Context& context, int orientation );
/**
* @brief Set up the clipping based on the specified clipping settings.
bool& usedStencilBuffer,
uint32_t& lastClippingDepth,
uint32_t& lastClippingId,
- Integration::StencilBufferAvailable stencilBufferAvailable );
+ Integration::StencilBufferAvailable stencilBufferAvailable,
+ int orientation );
/**
* @brief Process a render-list.
const Matrix& projectionMatrix,
Integration::DepthBufferAvailable depthBufferAvailable,
Integration::StencilBufferAvailable stencilBufferAvailable,
- Vector<GLuint>& boundTextures );
+ Vector<GLuint>& boundTextures,
+ int orientation );
// Prevent copying:
RenderAlgorithms( RenderAlgorithms& rhs );
const Matrix* GetProjectionMatrix( BufferIndex index ) const
{
// inlined as this is called once per frame per render instruction
- return &mCamera->GetProjectionMatrix( index );
+ return &mCamera->GetFinalProjectionMatrix( index );
}
private:
lastFrameWasRendered( false ),
programController( glAbstraction ),
depthBufferAvailable( depthBufferAvailableParam ),
- stencilBufferAvailable( stencilBufferAvailableParam )
+ stencilBufferAvailable( stencilBufferAvailableParam ),
+ defaultSurfaceOrientation( 0 )
{
// Create thread pool with just one thread ( there may be a need to create more threads in the future ).
threadPool = std::unique_ptr<Dali::ThreadPool>( new Dali::ThreadPool() );
std::unique_ptr<Dali::ThreadPool> threadPool; ///< The thread pool
Vector<GLuint> boundTextures; ///< The textures bound for rendering
Vector<GLuint> textureDependencyList; ///< The dependency list of binded textures
+ int defaultSurfaceOrientation; ///< defaultSurfaceOrientation for the default surface we are rendering to
+
};
RenderManager* RenderManager::New( Integration::GlAbstraction& glAbstraction,
mImpl->defaultSurfaceRect = rect;
}
+void RenderManager::SetDefaultSurfaceOrientation( int orientation )
+{
+ mImpl->defaultSurfaceOrientation = orientation;
+}
+
void RenderManager::AddRenderer( OwnerPointer< Render::Renderer >& renderer )
{
// Initialize the renderer as we are now in render thread
}
Rect<int32_t> surfaceRect = mImpl->defaultSurfaceRect;
+ int surfaceOrientation = mImpl->defaultSurfaceOrientation;
Integration::DepthBufferAvailable depthBufferAvailable = mImpl->depthBufferAvailable;
Integration::StencilBufferAvailable stencilBufferAvailable = mImpl->stencilBufferAvailable;
// For glViewport the lower-left corner is (0,0)
// For glViewport the lower-left corner is (0,0)
const int32_t y = ( surfaceRect.height - instruction.mViewport.height ) - instruction.mViewport.y;
- viewportRect.Set( instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height );
+ viewportRect.Set( instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height );
}
else
{
{
viewportRect.Set( 0, 0, instruction.mFrameBuffer->GetWidth(), instruction.mFrameBuffer->GetHeight() );
}
+ surfaceOrientation = 0;
}
}
else // No Offscreen frame buffer rendering
{
// For glViewport the lower-left corner is (0,0)
const int32_t y = ( instruction.mFrameBuffer->GetHeight() - instruction.mViewport.height ) - instruction.mViewport.y;
- viewportRect.Set( instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height );
+ viewportRect.Set( instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height );
}
else
{
clearFullFrameRect = ( surfaceRect == viewportRect );
}
+ if ( surfaceOrientation == 90 || surfaceOrientation == 270 )
+ {
+ int temp = viewportRect.width;
+ viewportRect.width = viewportRect.height;
+ viewportRect.height = temp;
+ }
+
mImpl->currentContext->Viewport(viewportRect.x, viewportRect.y, viewportRect.width, viewportRect.height);
if( instruction.mIsClearColorSet )
mImpl->renderBufferIndex,
depthBufferAvailable,
stencilBufferAvailable,
- mImpl->boundTextures );
+ mImpl->boundTextures,
+ surfaceOrientation );
// Synchronise the FBO/Texture access when there are multiple contexts
if ( mImpl->currentContext->IsSurfacelessContextSupported() )
void SetDefaultSurfaceRect( const Rect<int>& rect );
/**
+ * Returns the orintation for the default surface (probably the application window).
+ * @return Orientation for the surface.
+ */
+ void SetDefaultSurfaceOrientation( int orientation );
+
+ /**
* Add a Renderer to the render manager.
* @param[in] renderer The renderer to add.
* @post renderer is owned by RenderManager
new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetDefaultSurfaceRect, rect );
}
+void UpdateManager::SetDefaultSurfaceOrientation( int orientation )
+{
+ typedef MessageValue1< RenderManager, int > DerivedType;
+
+ // Reserve some memory inside the render queue
+ unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
+
+ // Construct message in the render queue memory; note that delete should not be called on the return value
+ new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetDefaultSurfaceOrientation, orientation );
+}
+
void UpdateManager::KeepRendering( float durationSeconds )
{
mImpl->keepRenderingSeconds = std::max( mImpl->keepRenderingSeconds, durationSeconds );
void SetDefaultSurfaceRect( const Rect<int>& rect );
/**
+ * Set the default surface orientation.
+ * @param[in] orientation The orientation value representing the surface.
+ */
+ void SetDefaultSurfaceOrientation( int orientation );
+
+ /**
* @copydoc Dali::Stage::KeepRendering()
*/
void KeepRendering( float durationSeconds );
new (slot) LocalType( &manager, &UpdateManager::SetDefaultSurfaceRect, rect );
}
+inline void SetDefaultSurfaceOrientationMessage( UpdateManager& manager, int orientation )
+{
+ typedef MessageValue1< UpdateManager, int > LocalType;
+
+ // Reserve some memory inside the message queue
+ unsigned int* slot = manager.ReserveMessageSlot( sizeof( LocalType ) );
+
+ // Construct message in the message queue memory; note that delete should not be called on the return value
+ new (slot) LocalType( &manager, &UpdateManager::SetDefaultSurfaceOrientation, orientation );
+}
+
inline void KeepRenderingMessage( UpdateManager& manager, float durationSeconds )
{
typedef MessageValue1< UpdateManager, float > LocalType;
Camera::Camera()
: mUpdateViewFlag( UPDATE_COUNT ),
mUpdateProjectionFlag( UPDATE_COUNT ),
+ mProjectionRotation( 0 ),
mNode( NULL ),
mType( DEFAULT_TYPE ),
mProjectionMode( DEFAULT_MODE ),
mTargetPosition( DEFAULT_TARGET_POSITION ),
mViewMatrix(),
mProjectionMatrix(),
- mInverseViewProjection( Matrix::IDENTITY )
+ mInverseViewProjection( Matrix::IDENTITY ),
+ mFinalProjection( Matrix::IDENTITY )
{
}
mUpdateViewFlag = UPDATE_COUNT;
}
+void Camera::RotateProjection( int rotationAngle )
+{
+ mProjectionRotation = rotationAngle;
+ mUpdateViewFlag = UPDATE_COUNT;
+}
+
const Matrix& Camera::GetProjectionMatrix( BufferIndex bufferIndex ) const
{
return mProjectionMatrix[ bufferIndex ];
return mInverseViewProjection[ bufferIndex ];
}
+const Matrix& Camera::GetFinalProjectionMatrix( BufferIndex bufferIndex ) const
+{
+ return mFinalProjection[ bufferIndex ];
+}
+
const PropertyInputImpl* Camera::GetProjectionMatrix() const
{
return &mProjectionMatrix;
}
mProjectionMatrix.SetDirty( updateBufferIndex );
+
+ Matrix &finalProjection = mFinalProjection[ updateBufferIndex ];
+ finalProjection.SetIdentity();
+
+ Quaternion rotationAngle;
+ switch( mProjectionRotation )
+ {
+ case 90:
+ {
+ rotationAngle = Quaternion( Dali::ANGLE_90, Vector3::ZAXIS );
+ break;
+ }
+ case 180:
+ {
+ rotationAngle = Quaternion( Dali::ANGLE_180, Vector3::ZAXIS );
+ break;
+ }
+ case 270:
+ {
+ rotationAngle = Quaternion( Dali::ANGLE_270, Vector3::ZAXIS );
+ break;
+ }
+ default:
+ rotationAngle = Quaternion( Dali::ANGLE_0, Vector3::ZAXIS );
+ break;
+ }
+
+ Matrix rotation;
+ rotation.SetIdentity();
+ rotation.SetTransformComponents( Vector3( 1.0f, 1.0f, 1.0f ), rotationAngle, Vector3( 0.0f, 0.0f, 0.0f ) );
+
+ Matrix::Multiply( finalProjection, mProjectionMatrix.Get( updateBufferIndex ), rotation );
}
--mUpdateProjectionFlag;
}
void SetFarClippingPlane( float farClippingPlane );
/**
+ * @copydoc Dali::Internal::CameraActor::RotateProjection
+ */
+ void RotateProjection( int rotationAngle );
+
+ /**
* @copydoc Dali::Internal::CameraActor::SetTarget
*/
void SetTargetPosition( const Vector3& targetPosition );
const Matrix& GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const;
/**
+ * Retrieve the final projection-matrix; this is double buffered for input handling.
+ * @param[in] bufferIndex The buffer to read from.
+ * @return The projection-matrix that should be used to render.
+ */
+ const Matrix& GetFinalProjectionMatrix( BufferIndex bufferIndex ) const;
+
+ /**
* Retrieve the projection-matrix property querying interface.
* @pre The camera is on-stage.
* @return The projection-matrix property querying interface.
uint32_t mUpdateViewFlag; ///< This is non-zero if the view matrix requires an update
uint32_t mUpdateProjectionFlag; ///< This is non-zero if the projection matrix requires an update
+ int mProjectionRotation; ///< The rotaion angle of the projection
const Node* mNode; ///< The node this scene graph camera belongs to
public: // PROPERTIES
DoubleBuffered< FrustumPlanes > mFrustum; ///< Clipping frustum; double buffered for input handling
DoubleBuffered< Matrix > mInverseViewProjection; ///< Inverted viewprojection; double buffered for input handling
+ DoubleBuffered< Matrix > mFinalProjection; ///< Final projection matrix; double buffered for input handling
};
new (slot) LocalType( &camera, &Camera::SetInvertYAxis, parameter );
}
+inline void RotateProjectionMessage( EventThreadServices& eventThreadServices, const Camera& camera, int parameter )
+{
+ typedef MessageValue1< Camera, int > LocalType;
+
+ // Reserve some memory inside the message queue
+ unsigned int* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
+
+ // Construct message in the message queue memory; note that delete should not be called on the return value
+ new (slot) LocalType( &camera, &Camera::RotateProjection, parameter );
+}
+
} // namespace SceneGraph
} // namespace Internal