0026025: Visualization, TKOpenGl - stereoscopic output does not work
authorkgv <kgv@opencascade.com>
Wed, 15 Apr 2015 13:25:38 +0000 (16:25 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 24 Apr 2015 10:16:27 +0000 (13:16 +0300)
OpenGl_View::Render() - pass target FBO as parameter.
OpenGl_Context - revise Read/Write buffers management logic, taking into account FBOs.

Graphic3d_Camera::UpdateProjection() - setup LProjection and RProjection
the same as MProjection in case of Projection_MonoLeftEye/Projection_MonoRightEye
for API consistency.

17 files changed:
src/Graphic3d/Graphic3d_Camera.cxx
src/Graphic3d/Graphic3d_Camera.hxx
src/OpenGl/OpenGl_Context.cxx
src/OpenGl/OpenGl_Context.hxx
src/OpenGl/OpenGl_FrameBuffer.cxx
src/OpenGl/OpenGl_FrameBuffer.hxx
src/OpenGl/OpenGl_ShaderManager.cxx
src/OpenGl/OpenGl_ShaderManager.hxx
src/OpenGl/OpenGl_View.cxx
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_View_2.cxx
src/OpenGl/OpenGl_View_Raytrace.cxx
src/OpenGl/OpenGl_Window.cxx
src/OpenGl/OpenGl_Window.hxx
src/OpenGl/OpenGl_Workspace.cxx
src/OpenGl/OpenGl_Workspace.hxx
src/OpenGl/OpenGl_Workspace_2.cxx

index edf72cd..198aa0e 100644 (file)
@@ -734,6 +734,7 @@ Graphic3d_Camera::TransformMatrices<Elem_t>&
       StereoEyeProj (aLeft, aRight, aBot, aTop,
                      aZNear, aZFar, aIOD, aFocus,
                      Standard_True, *theMatrices.MProjection);
+      *theMatrices.LProjection = *theMatrices.MProjection;
       break;
     }
 
@@ -742,6 +743,7 @@ Graphic3d_Camera::TransformMatrices<Elem_t>&
       StereoEyeProj (aLeft, aRight, aBot, aTop,
                      aZNear, aZFar, aIOD, aFocus,
                      Standard_False, *theMatrices.MProjection);
+      *theMatrices.RProjection = *theMatrices.MProjection;
       break;
     }
 
index 92e9000..56620af 100644 (file)
@@ -90,7 +90,7 @@ public:
   //! Enumerates supported monographic projections.
   //! - Projection_Orthographic : orthographic projection.
   //! - Projection_Perspective  : perspective projection.
-  //! - Projection_Stere        : stereographic projection.
+  //! - Projection_Stereo       : stereographic projection.
   //! - Projection_MonoLeftEye  : mono projection for stereo left eye.
   //! - Projection_MonoRightEye : mono projection for stereo right eye.
   enum Projection
index 10e7a42..7e53da0 100644 (file)
@@ -123,6 +123,7 @@ OpenGl_Context::OpenGl_Context (const Handle(OpenGl_Caps)& theCaps)
 #else
   myRenderMode (0),
 #endif
+  myReadBuffer (0),
   myDrawBuffer (0),
   myDefaultVao (0),
   myIsGlDebugCtx (Standard_False)
@@ -288,93 +289,58 @@ Standard_Integer OpenGl_Context::MaxClipPlanes() const
   return myMaxClipPlanes;
 }
 
-// =======================================================================
-// function : SetDrawBufferLeft
-// purpose  :
-// =======================================================================
-void OpenGl_Context::SetDrawBufferLeft()
-{
 #if !defined(GL_ES_VERSION_2_0)
-  switch (myDrawBuffer)
+inline Standard_Integer stereoToMonoBuffer (const Standard_Integer theBuffer)
+{
+  switch (theBuffer)
   {
-    case GL_BACK_RIGHT :
-    case GL_BACK :
-      glDrawBuffer (GL_BACK_LEFT);
-      myDrawBuffer = GL_BACK_LEFT;
-      break;
-
-    case GL_FRONT_RIGHT :
-    case GL_FRONT :
-      glDrawBuffer (GL_FRONT_LEFT);
-      myDrawBuffer = GL_FRONT_LEFT;
-      break;
-
-    case GL_FRONT_AND_BACK :
-    case GL_RIGHT :
-      glDrawBuffer (GL_LEFT);
-      myDrawBuffer = GL_LEFT;
-      break;
+    case GL_BACK_LEFT:
+    case GL_BACK_RIGHT:
+      return GL_BACK;
+    case GL_FRONT_LEFT:
+    case GL_FRONT_RIGHT:
+      return GL_FRONT;
+    default:
+      return theBuffer;
   }
-#endif
 }
+#endif
 
 // =======================================================================
-// function : SetDrawBufferRight
+// function : SetReadBuffer
 // purpose  :
 // =======================================================================
-void OpenGl_Context::SetDrawBufferRight()
+void OpenGl_Context::SetReadBuffer (const Standard_Integer theReadBuffer)
 {
 #if !defined(GL_ES_VERSION_2_0)
-  switch (myDrawBuffer)
+  myReadBuffer = !myIsStereoBuffers ? stereoToMonoBuffer (theReadBuffer) : theReadBuffer;
+  if (myReadBuffer < GL_COLOR_ATTACHMENT0
+   && arbFBO != NULL)
   {
-    case GL_BACK_LEFT :
-    case GL_BACK :
-      glDrawBuffer (GL_BACK_RIGHT);
-      myDrawBuffer = GL_BACK_RIGHT;
-      break;
-
-    case GL_FRONT_LEFT :
-    case GL_FRONT :
-      glDrawBuffer (GL_FRONT_RIGHT);
-      myDrawBuffer = GL_FRONT_RIGHT;
-      break;
-
-    case GL_FRONT_AND_BACK :
-    case GL_LEFT :
-      glDrawBuffer (GL_RIGHT);
-      myDrawBuffer = GL_RIGHT;
-      break;
+    arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
   }
+  ::glReadBuffer (myReadBuffer);
+#else
+  (void )theReadBuffer;
 #endif
 }
 
 // =======================================================================
-// function : SetDrawBufferMono
+// function : SetDrawBuffer
 // purpose  :
 // =======================================================================
-void OpenGl_Context::SetDrawBufferMono()
+void OpenGl_Context::SetDrawBuffer (const Standard_Integer theDrawBuffer)
 {
 #if !defined(GL_ES_VERSION_2_0)
-  switch (myDrawBuffer)
+  myDrawBuffer = !myIsStereoBuffers ? stereoToMonoBuffer (theDrawBuffer) : theDrawBuffer;
+  if (myDrawBuffer < GL_COLOR_ATTACHMENT0
+   && arbFBO != NULL)
   {
-    case GL_BACK_LEFT :
-    case GL_BACK_RIGHT :
-      glDrawBuffer (GL_BACK);
-      myDrawBuffer = GL_BACK;
-      break;
-
-    case GL_FRONT_LEFT :
-    case GL_FRONT_RIGHT :
-      glDrawBuffer (GL_FRONT);
-      myDrawBuffer = GL_FRONT;
-      break;
-
-    case GL_LEFT :
-    case GL_RIGHT :
-      glDrawBuffer (GL_FRONT_AND_BACK);
-      myDrawBuffer = GL_FRONT_AND_BACK;
-      break;
+    arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
   }
+  ::glDrawBuffer (myDrawBuffer);
+#else
+  (void )theDrawBuffer;
 #endif
 }
 
@@ -391,8 +357,9 @@ void OpenGl_Context::FetchState()
     ::glGetIntegerv (GL_RENDER_MODE, &myRenderMode);
   }
 
-  // cache draw buffer state
-  glGetIntegerv (GL_DRAW_BUFFER, &myDrawBuffer);
+  // cache buffers state
+  ::glGetIntegerv (GL_READ_BUFFER, &myReadBuffer);
+  ::glGetIntegerv (GL_DRAW_BUFFER, &myDrawBuffer);
 #endif
 }
 
index 7a8a58c..ce67d60 100644 (file)
@@ -487,20 +487,24 @@ public:
 
 public: //! @name methods to alter or retrieve current state
 
-  //! Switch to left stereographic rendering buffer.
-  //! This method can be used to keep unchanged choise
-  //! of front/back/both buffer rendering.
-  Standard_EXPORT void SetDrawBufferLeft();
+  //! Return active read buffer.
+  Standard_Integer ReadBuffer() { return myReadBuffer; }
 
-  //! Switch to right stereographic rendering buffer.
-  //! This method can be used to keep unchanged choise
-  //! of front/back/both buffer rendering.
-  Standard_EXPORT void SetDrawBufferRight();
+  //! Switch read buffer, wrapper for ::glReadBuffer().
+  Standard_EXPORT void SetReadBuffer (const Standard_Integer theReadBuffer);
 
-  //! Switch to non-stereographic rendering buffer.
-  //! This method can be used to keep unchanged choise
-  //! of front/back/both buffer rendering.
-  Standard_EXPORT void SetDrawBufferMono();
+  //! Return active draw buffer.
+  Standard_Integer DrawBuffer() { return myDrawBuffer; }
+
+  //! Switch draw buffer, wrapper for ::glDrawBuffer().
+  Standard_EXPORT void SetDrawBuffer (const Standard_Integer theDrawBuffer);
+
+  //! Switch read/draw buffers.
+  void SetReadDrawBuffer (const Standard_Integer theBuffer)
+  {
+    SetReadBuffer (theBuffer);
+    SetDrawBuffer (theBuffer);
+  }
 
   //! Fetch OpenGl context state. This class tracks value of several OpenGl
   //! state variables. Consulting the cached values is quicker than
@@ -667,6 +671,7 @@ private: //! @name fields tracking current state
   Handle(OpenGl_Sampler)       myTexSampler;    //!< currently active sampler object
   Handle(OpenGl_FrameBuffer)   myDefaultFbo;    //!< default Frame Buffer Object
   Standard_Integer             myRenderMode;    //!< value for active rendering mode
+  Standard_Integer             myReadBuffer;    //!< current read buffer
   Standard_Integer             myDrawBuffer;    //!< current draw buffer
   unsigned int                 myDefaultVao;    //!< default Vertex Array Object
   Standard_Boolean             myIsGlDebugCtx;  //!< debug context initialization state
index 6667238..59f99c8 100644 (file)
@@ -99,6 +99,23 @@ Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlCo
   return Standard_True;
 }
 
+// =======================================================================
+// function : Init
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_FrameBuffer::InitLazy (const Handle(OpenGl_Context)& theGlContext,
+                                               const GLsizei                 theViewportSizeX,
+                                               const GLsizei                 theViewportSizeY)
+{
+  if (myVPSizeX == theViewportSizeX
+   && myVPSizeY == theViewportSizeY)
+  {
+    return IsValid();
+  }
+
+  return Init (theGlContext, theViewportSizeX, theViewportSizeY);
+}
+
 // =======================================================================
 // function : InitWithRB
 // purpose  :
index 65bc08b..799b292 100644 (file)
@@ -88,6 +88,11 @@ public:
                                          const GLsizei                 theViewportSizeX,
                                          const GLsizei                 theViewportSizeY);
 
+  //! (Re-)initialize FBO with specified dimensions.
+  Standard_EXPORT Standard_Boolean InitLazy (const Handle(OpenGl_Context)& theGlCtx,
+                                             const GLsizei                 theViewportSizeX,
+                                             const GLsizei                 theViewportSizeY);
+
   //! (Re-)initialize FBO with specified dimensions.
   //! The Render Buffer Objects will be used for Color, Depth and Stencil attachments (as opposite to textures).
   //! @param theGlCtx         currently bound OpenGL context
index 01ca672..a5d22af 100644 (file)
@@ -235,6 +235,7 @@ void OpenGl_ShaderManager::clear()
   myMapOfLightPrograms.Clear();
   myFontProgram.Nullify();
   myBlitProgram.Nullify();
+  myAnaglyphProgram.Nullify();
   switchLightPrograms();
 }
 
@@ -1578,6 +1579,60 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_Sha
   return Standard_True;
 }
 
+// =======================================================================
+// function : prepareStdProgramAnaglyph
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderManager::prepareStdProgramAnaglyph()
+{
+  Handle(Graphic3d_ShaderProgram) aProgramSrc = new Graphic3d_ShaderProgram();
+  TCollection_AsciiString aSrcVert =
+      EOL"THE_SHADER_OUT vec2 TexCoord;"
+      EOL"void main()"
+      EOL"{"
+      EOL"  TexCoord    = occVertex.zw;"
+      EOL"  gl_Position = vec4(occVertex.x, occVertex.y, 0.0, 1.0);"
+      EOL"}";
+
+  TCollection_AsciiString aSrcFrag =
+      EOL"uniform sampler2D uLeftSampler;"
+      EOL"uniform sampler2D uRightSampler;"
+      EOL
+      EOL"THE_SHADER_IN vec2 TexCoord;"
+      EOL
+      EOL"void main()"
+      EOL"{"
+      EOL"  vec4 aColorL = occTexture2D (uLeftSampler,  TexCoord);"
+      EOL"  vec4 aColorR = occTexture2D (uRightSampler, TexCoord);"
+      EOL"  aColorL.b = 0.0;"
+      EOL"  aColorL.g = 0.0;"
+      EOL"  aColorR.r = 0.0;"
+      EOL"  occFragColor = aColorL + aColorR;"
+      EOL"}";
+
+#if !defined(GL_ES_VERSION_2_0)
+  if (myContext->core32 != NULL)
+  {
+    aProgramSrc->SetHeader ("#version 150");
+  }
+#endif
+
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_VERTEX,   aSrcVert));
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_FRAGMENT, aSrcFrag));
+  TCollection_AsciiString aKey;
+  if (!Create (aProgramSrc, aKey, myAnaglyphProgram))
+  {
+    myAnaglyphProgram = new OpenGl_ShaderProgram(); // just mark as invalid
+    return Standard_False;
+  }
+
+  myContext->BindProgram (myAnaglyphProgram);
+  myAnaglyphProgram->SetSampler (myContext, "uLeftSampler",  0);
+  myAnaglyphProgram->SetSampler (myContext, "uRightSampler", 1);
+  myContext->BindProgram (NULL);
+  return Standard_True;
+}
+
 // =======================================================================
 // function : bindProgramWithState
 // purpose  :
index 780b76f..b08d866 100644 (file)
@@ -156,6 +156,17 @@ public:
          && myContext->BindProgram (myBlitProgram);
   }
 
+  //! Bind program for rendering Anaglyph image.
+  Standard_Boolean BindAnaglyphProgram()
+  {
+    if (myAnaglyphProgram.IsNull())
+    {
+      prepareStdProgramAnaglyph();
+    }
+    return !myAnaglyphProgram.IsNull()
+         && myContext->BindProgram (myAnaglyphProgram);
+  }
+
 public:
 
   //! Returns current state of OCCT light sources.
@@ -354,6 +365,9 @@ protected:
   //! Set pointer myLightPrograms to active lighting programs set from myMapOfLightPrograms
   Standard_EXPORT void switchLightPrograms();
 
+  //! Prepare standard GLSL program for Anaglyph image.
+  Standard_EXPORT Standard_Boolean prepareStdProgramAnaglyph();
+
 protected:
 
   Visual3d_TypeOfModel               myShadingModel;       //!< lighting shading model
@@ -364,6 +378,8 @@ protected:
   Handle(OpenGl_ShaderProgram)       myBlitProgram;        //!< standard program for FBO blit emulation
   OpenGl_MapOfShaderPrograms         myMapOfLightPrograms; //!< map of lighting programs depending on shading model and lights configuration
 
+  Handle(OpenGl_ShaderProgram)       myAnaglyphProgram;    //!< standard program for Anaglyph image
+
   OpenGl_Context*                    myContext;            //!< OpenGL context
 
 protected:
index d1c77aa..44926c3 100644 (file)
@@ -500,13 +500,3 @@ const TEL_TRANSFORM_PERSISTENCE* OpenGl_View::BeginTransformPersistence (const H
   theCtx->ApplyProjectionMatrix();
   return aTransPersPrev;
 }
-
-/*----------------------------------------------------------------------*/
-
-void OpenGl_View::GetMatrices (OpenGl_Mat4& theOrientation,
-                               OpenGl_Mat4& theViewMapping) const
-{
-  theViewMapping = myCamera->ProjectionMatrixF();
-  theOrientation = myCamera->OrientationMatrixF();
-}
-/*----------------------------------------------------------------------*/
index d3c30a0..5f28f80 100644 (file)
@@ -170,6 +170,8 @@ class OpenGl_View : public MMgt_TShared
 
   void Render (const Handle(OpenGl_PrinterContext)& thePrintContext,
                const Handle(OpenGl_Workspace)&      theWorkspace,
+               OpenGl_FrameBuffer*                  theReadDrawFbo,
+               Graphic3d_Camera::Projection         theProjection,
                const Graphic3d_CView&               theCView,
                const Aspect_CLayer2d&               theCUnderLayer,
                const Aspect_CLayer2d&               theCOverLayer,
@@ -198,10 +200,6 @@ class OpenGl_View : public MMgt_TShared
   //! marks primitive set for rebuild.
   void InvalidateBVHData (const Standard_Integer theLayerId);
 
-  //! Returns view-mapping and orientation matrices.
-  void GetMatrices (OpenGl_Mat4& theOrientation,
-                    OpenGl_Mat4& theViewMapping) const;
-
   //! Returns list of immediate structures rendered on top of main presentation
   const OpenGl_SequenceOfStructure& ImmediateStructures() const
   {
@@ -218,6 +216,7 @@ class OpenGl_View : public MMgt_TShared
 protected:
 
   void RenderStructs (const Handle(OpenGl_Workspace)& theWorkspace,
+                      OpenGl_FrameBuffer*             theReadDrawFbo,
                       const Graphic3d_CView&          theCView,
                       const Standard_Boolean          theToDrawImmediate);
 
@@ -234,6 +233,7 @@ protected:
   //! matrices supplied by 3d view.
   void RedrawScene (const Handle(OpenGl_PrinterContext)& thePrintContext,
                     const Handle(OpenGl_Workspace)&      theWorkspace,
+                    OpenGl_FrameBuffer*                  theReadDrawFbo,
                     const Graphic3d_CView&               theCView,
                     const Standard_Boolean               theToDrawImmediate);
 
@@ -587,14 +587,14 @@ protected: //! @name methods related to ray-tracing
                                        const OpenGl_Vec3*            theOrigins,
                                        const OpenGl_Vec3*            theDirects,
                                        const OpenGl_Mat4&            theUnviewMat,
-                                       OpenGl_FrameBuffer*           theOutputFBO,
+                                       OpenGl_FrameBuffer*           theReadDrawFbo,
                                        const Handle(OpenGl_Context)& theGlContext);
 
   //! Redraws the window using OpenGL/GLSL ray-tracing.
   Standard_Boolean raytrace (const Graphic3d_CView&        theCView,
                              const Standard_Integer        theSizeX,
                              const Standard_Integer        theSizeY,
-                             OpenGl_FrameBuffer*           theOutputFBO,
+                             OpenGl_FrameBuffer*           theReadDrawFbo,
                              const Handle(OpenGl_Context)& theGlContext);
 
 protected: //! @name fields related to ray-tracing
index 4240be5..3abea51 100644 (file)
@@ -245,6 +245,8 @@ void OpenGl_View::DrawBackground (const Handle(OpenGl_Workspace)& theWorkspace)
 //call_func_redraw_all_structs_proc
 void OpenGl_View::Render (const Handle(OpenGl_PrinterContext)& thePrintContext,
                           const Handle(OpenGl_Workspace)&      theWorkspace,
+                          OpenGl_FrameBuffer*                  theOutputFBO,
+                          Graphic3d_Camera::Projection         theProjection,
                           const Graphic3d_CView&               theCView,
                           const Aspect_CLayer2d&               theCUnderLayer,
                           const Aspect_CLayer2d&               theCOverLayer,
@@ -433,42 +435,17 @@ void OpenGl_View::Render (const Handle(OpenGl_PrinterContext)& thePrintContext,
   }
 
   // Redraw 3d scene
-  if (!myCamera->IsStereo() || !aContext->HasStereoBuffers())
+  if (theProjection == Graphic3d_Camera::Projection_MonoLeftEye)
   {
-    // single-pass monographic rendering
-    // redraw scene with normal orientation and projection
-    RedrawScene (thePrintContext, theWorkspace, theCView, theToDrawImmediate);
-  }
-  else
-  {
-    // two stereographic passes
-
-    // safely switch to left Eye buffer
-    aContext->SetDrawBufferLeft();
-
     aContext->ProjectionState.SetCurrent (myCamera->ProjectionStereoLeftF());
     aContext->ApplyProjectionMatrix();
-
-    // redraw left Eye
-    RedrawScene (thePrintContext, theWorkspace, theCView, theToDrawImmediate);
-
-    // reset depth buffer of first rendering pass
-    if (theWorkspace->UseDepthTest())
-    {
-      glClear (GL_DEPTH_BUFFER_BIT);
-    }
-    // safely switch to right Eye buffer
-    aContext->SetDrawBufferRight();
-
+  }
+  else if (theProjection == Graphic3d_Camera::Projection_MonoRightEye)
+  {
     aContext->ProjectionState.SetCurrent (myCamera->ProjectionStereoRightF());
     aContext->ApplyProjectionMatrix();
-
-    // redraw right Eye
-    RedrawScene (thePrintContext, theWorkspace, theCView, theToDrawImmediate);
-
-    // switch back to monographic rendering
-    aContext->SetDrawBufferMono();
   }
+  RedrawScene (thePrintContext, theWorkspace, theOutputFBO, theCView, theToDrawImmediate);
 
   // ===============================
   //      Step 5: Trihedron
@@ -547,6 +524,7 @@ void OpenGl_View::InvalidateBVHData (const Graphic3d_ZLayerId theLayerId)
 
 //ExecuteViewDisplay
 void OpenGl_View::RenderStructs (const Handle(OpenGl_Workspace)& theWorkspace,
+                                 OpenGl_FrameBuffer*             theReadDrawFbo,
                                  const Graphic3d_CView&          theCView,
                                  const Standard_Boolean          theToDrawImmediate)
 {
@@ -601,21 +579,10 @@ void OpenGl_View::RenderStructs (const Handle(OpenGl_Workspace)& theWorkspace,
 
     if (!toRenderGL)
     {
-      OpenGl_FrameBuffer* anOutputFBO = NULL;
-
-      if (theWorkspace->ResultFBO()->IsValid())
-      {
-        anOutputFBO = theWorkspace->ResultFBO().operator->();
-      }
-      else if (theCView.ptrFBO != NULL)
-      {
-        anOutputFBO = (OpenGl_FrameBuffer* )theCView.ptrFBO;
-      }
-
-      const Standard_Integer aSizeX = anOutputFBO != NULL ?
-        anOutputFBO->GetVPSizeX() : theWorkspace->Width();
-      const Standard_Integer aSizeY = anOutputFBO != NULL ?
-        anOutputFBO->GetVPSizeY() : theWorkspace->Height();
+      const Standard_Integer aSizeX = theReadDrawFbo != NULL ?
+        theReadDrawFbo->GetVPSizeX() : theWorkspace->Width();
+      const Standard_Integer aSizeY = theReadDrawFbo != NULL ?
+        theReadDrawFbo->GetVPSizeY() : theWorkspace->Height();
 
       if (myOpenGlFBO.IsNull())
         myOpenGlFBO = new OpenGl_FrameBuffer;
@@ -631,8 +598,8 @@ void OpenGl_View::RenderStructs (const Handle(OpenGl_Workspace)& theWorkspace,
 
       myRaytraceFilter->SetPrevRenderFilter (theWorkspace->GetRenderFilter());
 
-      if (anOutputFBO != NULL)
-        anOutputFBO->UnbindBuffer (aCtx);
+      if (theReadDrawFbo != NULL)
+        theReadDrawFbo->UnbindBuffer (aCtx);
 
       // Prepare preliminary OpenGL output
       if (aCtx->arbFBOBlit != NULL)
@@ -642,9 +609,9 @@ void OpenGl_View::RenderStructs (const Handle(OpenGl_Workspace)& theWorkspace,
 
         theWorkspace->SetRenderFilter (myRaytraceFilter);
         {
-          if (anOutputFBO != NULL)
+          if (theReadDrawFbo != NULL)
           {
-            anOutputFBO->BindReadBuffer (aCtx);
+            theReadDrawFbo->BindReadBuffer (aCtx);
           }
           else
           {
@@ -664,9 +631,9 @@ void OpenGl_View::RenderStructs (const Handle(OpenGl_Workspace)& theWorkspace,
         theWorkspace->SetRenderFilter (myRaytraceFilter->PrevRenderFilter());
       }
 
-      if (anOutputFBO != NULL)
+      if (theReadDrawFbo != NULL)
       {
-        anOutputFBO->BindBuffer (aCtx);
+        theReadDrawFbo->BindBuffer (aCtx);
       }
       else
       {
@@ -674,7 +641,7 @@ void OpenGl_View::RenderStructs (const Handle(OpenGl_Workspace)& theWorkspace,
       }
 
       // Ray-tracing polygonal primitive arrays
-      raytrace (theCView, aSizeX, aSizeY, anOutputFBO, aCtx);
+      raytrace (theCView, aSizeX, aSizeY, theReadDrawFbo, aCtx);
 
       // Render upper (top and topmost) OpenGL layers
       myZLayers.Render (theWorkspace, theToDrawImmediate, OpenGl_LF_Upper);
@@ -1032,6 +999,7 @@ void OpenGl_View::ChangePriority (const OpenGl_Structure *theStructure,
 
 void OpenGl_View::RedrawScene (const Handle(OpenGl_PrinterContext)& thePrintContext,
                                const Handle(OpenGl_Workspace)&      theWorkspace,
+                               OpenGl_FrameBuffer*                  theReadDrawFbo,
                                const Graphic3d_CView&               theCView,
                                const Standard_Boolean               theToDrawImmediate)
 {
@@ -1165,14 +1133,14 @@ void OpenGl_View::RedrawScene (const Handle(OpenGl_PrinterContext)& thePrintCont
       theWorkspace->NamedStatus |= OPENGL_NS_FORBIDSETTEX;
       theWorkspace->DisableTexture();
       // Render the view
-      RenderStructs (theWorkspace, theCView, theToDrawImmediate);
+      RenderStructs (theWorkspace, theReadDrawFbo, theCView, theToDrawImmediate);
       break;
 
     case Visual3d_TOD_ENVIRONMENT:
       theWorkspace->NamedStatus |= OPENGL_NS_FORBIDSETTEX;
       theWorkspace->EnableTexture (myTextureEnv);
       // Render the view
-      RenderStructs (theWorkspace, theCView, theToDrawImmediate);
+      RenderStructs (theWorkspace, theReadDrawFbo, theCView, theToDrawImmediate);
       theWorkspace->DisableTexture();
       break;
 
@@ -1180,7 +1148,7 @@ void OpenGl_View::RedrawScene (const Handle(OpenGl_PrinterContext)& thePrintCont
       // First pass
       theWorkspace->NamedStatus &= ~OPENGL_NS_FORBIDSETTEX;
       // Render the view
-      RenderStructs (theWorkspace, theCView, theToDrawImmediate);
+      RenderStructs (theWorkspace, theReadDrawFbo, theCView, theToDrawImmediate);
       theWorkspace->DisableTexture();
 
       // Second pass
@@ -1213,7 +1181,7 @@ void OpenGl_View::RedrawScene (const Handle(OpenGl_PrinterContext)& thePrintCont
         theWorkspace->NamedStatus |= OPENGL_NS_FORBIDSETTEX;
 
         // Render the view
-        RenderStructs (theWorkspace, theCView, theToDrawImmediate);
+        RenderStructs (theWorkspace, theReadDrawFbo, theCView, theToDrawImmediate);
         theWorkspace->DisableTexture();
 
         // Restore properties back
index 757f0b2..4a0196e 100644 (file)
@@ -2208,7 +2208,7 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Graphic3d_CView&
                                                   const OpenGl_Vec3*            theOrigins,
                                                   const OpenGl_Vec3*            theDirects,
                                                   const OpenGl_Mat4&            theUnviewMat,
-                                                  OpenGl_FrameBuffer*           theOutputFBO,
+                                                  OpenGl_FrameBuffer*           theReadDrawFbo,
                                                   const Handle(OpenGl_Context)& theGlContext)
 {
   bindRaytraceTextures (theGlContext);
@@ -2296,8 +2296,8 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Graphic3d_CView&
       {
         glEnable (GL_BLEND);
 
-        if (theOutputFBO != NULL)
-          theOutputFBO->BindBuffer (theGlContext);
+        if (theReadDrawFbo != NULL)
+          theReadDrawFbo->BindBuffer (theGlContext);
       }
       else
       {
@@ -2328,7 +2328,7 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Graphic3d_CView&
 Standard_Boolean OpenGl_View::raytrace (const Graphic3d_CView&        theCView,
                                         const Standard_Integer        theSizeX,
                                         const Standard_Integer        theSizeY,
-                                        OpenGl_FrameBuffer*           theOutputFBO,
+                                        OpenGl_FrameBuffer*           theReadDrawFbo,
                                         const Handle(OpenGl_Context)& theGlContext)
 {
   if (!initRaytraceResources (theCView, theGlContext))
@@ -2347,15 +2347,11 @@ Standard_Boolean OpenGl_View::raytrace (const Graphic3d_CView&        theCView,
   }
 
   // Get model-view and projection matrices
-  OpenGl_Mat4 aOrientationMatrix;
-  OpenGl_Mat4 aViewMappingMatrix;
-  OpenGl_Mat4 aInverOrientMatrix;
-
-  GetMatrices (aOrientationMatrix,
-               aViewMappingMatrix);
+  OpenGl_Mat4 aOrientationMatrix = myCamera->OrientationMatrixF();
+  OpenGl_Mat4 aViewMappingMatrix = theGlContext->ProjectionState.Current();
 
+  OpenGl_Mat4 aInverOrientMatrix;
   aOrientationMatrix.Inverted (aInverOrientMatrix);
-
   if (!updateRaytraceLightSources (aInverOrientMatrix, theGlContext))
   {
     return Standard_False;
@@ -2375,9 +2371,9 @@ Standard_Boolean OpenGl_View::raytrace (const Graphic3d_CView&        theCView,
   glDisable (GL_DEPTH_TEST);
   glBlendFunc (GL_ONE, GL_SRC_ALPHA);
 
-  if (theOutputFBO != NULL)
+  if (theReadDrawFbo != NULL)
   {
-    theOutputFBO->BindBuffer (theGlContext);
+    theReadDrawFbo->BindBuffer (theGlContext);
   }
 
   // Generate ray-traced image
@@ -2397,7 +2393,7 @@ Standard_Boolean OpenGl_View::raytrace (const Graphic3d_CView&        theCView,
                                                    aOrigins,
                                                    aDirects,
                                                    anUnviewMat,
-                                                   theOutputFBO,
+                                                   theReadDrawFbo,
                                                    theGlContext);
 
     if (!aResult)
index 2b9649b..5fedea6 100644 (file)
@@ -896,25 +896,3 @@ void OpenGl_Window::DisableFeatures() const
   }
 #endif
 }
-
-// =======================================================================
-// function : MakeFrontBufCurrent
-// purpose  : TelMakeFrontBufCurrent
-// =======================================================================
-void OpenGl_Window::MakeFrontBufCurrent() const
-{
-#if !defined(GL_ES_VERSION_2_0)
-  glDrawBuffer (GL_FRONT);
-#endif
-}
-
-// =======================================================================
-// function : MakeBackBufCurrent
-// purpose  : TelMakeBackBufCurrent
-// =======================================================================
-void OpenGl_Window::MakeBackBufCurrent() const
-{
-#if !defined(GL_ES_VERSION_2_0)
-  glDrawBuffer (GL_BACK);
-#endif
-}
index 7f7dee2..6b0d224 100644 (file)
@@ -83,12 +83,6 @@ protected:
   void EnableFeatures() const;
   void DisableFeatures() const;
 
-  //! Draw directly to the FRONT buffer. Can cause artifacts on the screen.
-  void MakeFrontBufCurrent() const;
-
-  //! Draw to BACK buffer. Normal and default state.
-  void MakeBackBufCurrent() const;
-
 protected:
 
   Handle(OpenGl_Context) myGlContext;
index 606d308..237ece8 100644 (file)
@@ -176,7 +176,10 @@ OpenGl_Workspace::OpenGl_Workspace (const Handle(OpenGl_GraphicDriver)& theDrive
   PolygonOffset_applied (THE_DEFAULT_POFFSET)
 {
   myGlContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
-  myResultFBO = new OpenGl_FrameBuffer();
+  myMainSceneFbos[0]      = new OpenGl_FrameBuffer();
+  myMainSceneFbos[1]      = new OpenGl_FrameBuffer();
+  myImmediateSceneFbos[0] = new OpenGl_FrameBuffer();
+  myImmediateSceneFbos[1] = new OpenGl_FrameBuffer();
 
   if (!myGlContext->GetResource ("OpenGl_LineAttributes", myLineAttribs))
   {
@@ -224,6 +227,16 @@ Standard_Boolean OpenGl_Workspace::SetImmediateModeDrawToFront (const Standard_B
   return aPrevMode;
 }
 
+inline void nullifyGlResource (Handle(OpenGl_Resource)&      theResource,
+                               const Handle(OpenGl_Context)& theCtx)
+{
+  if (!theResource.IsNull())
+  {
+    theResource->Release (theCtx.operator->());
+    theResource.Nullify();
+  }
+}
+
 // =======================================================================
 // function : ~OpenGl_Workspace
 // purpose  :
@@ -236,15 +249,12 @@ OpenGl_Workspace::~OpenGl_Workspace()
     myGlContext->ReleaseResource ("OpenGl_LineAttributes", Standard_True);
   }
 
-  if (!myResultFBO.IsNull())
-  {
-    myResultFBO->Release (myGlContext.operator->());
-    myResultFBO.Nullify();
-  }
-  if (myFullScreenQuad.IsValid())
-  {
-    myFullScreenQuad.Release (myGlContext.operator->());
-  }
+  nullifyGlResource (myMainSceneFbos[0],      myGlContext);
+  nullifyGlResource (myMainSceneFbos[1],      myGlContext);
+  nullifyGlResource (myImmediateSceneFbos[0], myGlContext);
+  nullifyGlResource (myImmediateSceneFbos[1], myGlContext);
+
+  myFullScreenQuad.Release (myGlContext.operator->());
 }
 
 // =======================================================================
@@ -652,6 +662,211 @@ Handle(OpenGl_Texture) OpenGl_Workspace::EnableTexture (const Handle(OpenGl_Text
   return aPrevTexture;
 }
 
+// =======================================================================
+// function : bindDefaultFbo
+// purpose  :
+// =======================================================================
+void OpenGl_Workspace::bindDefaultFbo (OpenGl_FrameBuffer* theCustomFbo)
+{
+  OpenGl_FrameBuffer* anFbo = (theCustomFbo != NULL && theCustomFbo->IsValid())
+                            ?  theCustomFbo
+                            : (!myGlContext->DefaultFrameBuffer().IsNull()
+                             && myGlContext->DefaultFrameBuffer()->IsValid()
+                              ? myGlContext->DefaultFrameBuffer().operator->()
+                              : NULL);
+  if (anFbo != NULL)
+  {
+    anFbo->BindBuffer (myGlContext);
+  }
+  else
+  {
+  #if !defined(GL_ES_VERSION_2_0)
+    myGlContext->SetReadDrawBuffer (GL_BACK);
+  #else
+    if (myGlContext->arbFBO != NULL)
+    {
+      myGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
+    }
+  #endif
+  }
+  myGlContext->core11fwd->glViewport (0, 0, myWidth, myHeight);
+}
+
+// =======================================================================
+// function : blitBuffers
+// purpose  :
+// =======================================================================
+bool OpenGl_Workspace::blitBuffers (OpenGl_FrameBuffer* theReadFbo,
+                                    OpenGl_FrameBuffer* theDrawFbo)
+{
+  if (theReadFbo == NULL)
+  {
+    return false;
+  }
+
+  // clear destination before blitting
+  if (theDrawFbo != NULL
+  &&  theDrawFbo->IsValid())
+  {
+    theDrawFbo->BindBuffer (myGlContext);
+  }
+  else
+  {
+    myGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
+  }
+#if !defined(GL_ES_VERSION_2_0)
+  myGlContext->core20fwd->glClearDepth  (1.0);
+#else
+  myGlContext->core20fwd->glClearDepthf (1.0f);
+#endif
+  myGlContext->core20fwd->glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+/*#if !defined(GL_ES_VERSION_2_0)
+  if (myGlContext->arbFBOBlit != NULL)
+  {
+    theReadFbo->BindReadBuffer (myGlContext);
+    if (theDrawFbo != NULL
+     && theDrawFbo->IsValid())
+    {
+      theDrawFbo->BindDrawBuffer (myGlContext);
+    }
+    else
+    {
+      myGlContext->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
+    }
+    // we don't copy stencil buffer here... does it matter for performance?
+    myGlContext->arbFBOBlit->glBlitFramebuffer (0, 0, theReadFbo->GetVPSizeX(), theReadFbo->GetVPSizeY(),
+                                                0, 0, theReadFbo->GetVPSizeX(), theReadFbo->GetVPSizeY(),
+                                                GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+
+    if (theDrawFbo != NULL
+      && theDrawFbo->IsValid())
+    {
+      theDrawFbo->BindBuffer (myGlContext);
+    }
+    else
+    {
+      myGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
+    }
+  }
+  else
+#endif*/
+  {
+    myGlContext->core20fwd->glDepthFunc (GL_ALWAYS);
+    myGlContext->core20fwd->glDepthMask (GL_TRUE);
+    myGlContext->core20fwd->glEnable (GL_DEPTH_TEST);
+
+    DisableTexture();
+    if (!myFullScreenQuad.IsValid())
+    {
+      OpenGl_Vec4 aQuad[4] =
+      {
+        OpenGl_Vec4( 1.0f, -1.0f, 1.0f, 0.0f),
+        OpenGl_Vec4( 1.0f,  1.0f, 1.0f, 1.0f),
+        OpenGl_Vec4(-1.0f, -1.0f, 0.0f, 0.0f),
+        OpenGl_Vec4(-1.0f,  1.0f, 0.0f, 1.0f)
+      };
+      myFullScreenQuad.Init (myGlContext, 4, 4, aQuad[0].GetData());
+    }
+
+    const Handle(OpenGl_ShaderManager)& aManager = myGlContext->ShaderManager();
+    if (myFullScreenQuad.IsValid()
+     && aManager->BindFboBlitProgram())
+    {
+      theReadFbo->ColorTexture()       ->Bind   (myGlContext, GL_TEXTURE0 + 0);
+      theReadFbo->DepthStencilTexture()->Bind   (myGlContext, GL_TEXTURE0 + 1);
+      myFullScreenQuad.BindVertexAttrib (myGlContext, 0);
+
+      myGlContext->core20fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+      myFullScreenQuad.UnbindVertexAttrib (myGlContext, 0);
+      theReadFbo->DepthStencilTexture()->Unbind (myGlContext, GL_TEXTURE0 + 1);
+      theReadFbo->ColorTexture()       ->Unbind (myGlContext, GL_TEXTURE0 + 0);
+    }
+    else
+    {
+      TCollection_ExtendedString aMsg = TCollection_ExtendedString()
+        + "Error! FBO blitting has failed";
+      myGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
+                                GL_DEBUG_TYPE_ERROR_ARB,
+                                0,
+                                GL_DEBUG_SEVERITY_HIGH_ARB,
+                                aMsg);
+      myHasFboBlit = Standard_False;
+      theReadFbo->Release (myGlContext.operator->());
+      return true;
+    }
+  }
+  return true;
+}
+
+// =======================================================================
+// function : drawStereoPair
+// purpose  :
+// =======================================================================
+void OpenGl_Workspace::drawStereoPair()
+{
+  OpenGl_FrameBuffer* aPair[2] =
+  {
+    myImmediateSceneFbos[0]->IsValid() ? myImmediateSceneFbos[0].operator->() : NULL,
+    myImmediateSceneFbos[1]->IsValid() ? myImmediateSceneFbos[1].operator->() : NULL
+  };
+  if (aPair[0] == NULL
+   || aPair[1] == NULL)
+  {
+    aPair[0] = myMainSceneFbos[0]->IsValid() ? myMainSceneFbos[0].operator->() : NULL;
+    aPair[1] = myMainSceneFbos[1]->IsValid() ? myMainSceneFbos[1].operator->() : NULL;
+  }
+
+  if (aPair[0] == NULL
+   || aPair[1] == NULL)
+  {
+    return;
+  }
+
+  myGlContext->core20fwd->glDepthFunc (GL_ALWAYS);
+  myGlContext->core20fwd->glDepthMask (GL_TRUE);
+  myGlContext->core20fwd->glEnable (GL_DEPTH_TEST);
+
+  DisableTexture();
+  if (!myFullScreenQuad.IsValid())
+  {
+    OpenGl_Vec4 aQuad[4] =
+    {
+      OpenGl_Vec4( 1.0f, -1.0f, 1.0f, 0.0f),
+      OpenGl_Vec4( 1.0f,  1.0f, 1.0f, 1.0f),
+      OpenGl_Vec4(-1.0f, -1.0f, 0.0f, 0.0f),
+      OpenGl_Vec4(-1.0f,  1.0f, 0.0f, 1.0f)
+    };
+    myFullScreenQuad.Init (myGlContext, 4, 4, aQuad[0].GetData());
+  }
+
+  const Handle(OpenGl_ShaderManager)& aManager = myGlContext->ShaderManager();
+  if (myFullScreenQuad.IsValid()
+   && aManager->BindAnaglyphProgram())
+  {
+    aPair[0]->ColorTexture()->Bind (myGlContext, GL_TEXTURE0 + 0);
+    aPair[1]->ColorTexture()->Bind (myGlContext, GL_TEXTURE0 + 1);
+    myFullScreenQuad.BindVertexAttrib (myGlContext, 0);
+
+    myGlContext->core20fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+    myFullScreenQuad.UnbindVertexAttrib (myGlContext, 0);
+    aPair[1]->ColorTexture()->Unbind (myGlContext, GL_TEXTURE0 + 1);
+    aPair[0]->ColorTexture()->Unbind (myGlContext, GL_TEXTURE0 + 0);
+  }
+  else
+  {
+    TCollection_ExtendedString aMsg = TCollection_ExtendedString()
+      + "Error! Anaglyph has failed";
+    myGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
+                              GL_DEBUG_TYPE_ERROR_ARB,
+                              0,
+                              GL_DEBUG_SEVERITY_HIGH_ARB,
+                              aMsg);
+  }
+}
+
 // =======================================================================
 // function : Redraw
 // purpose  :
@@ -675,14 +890,6 @@ void OpenGl_Workspace::Redraw (const Graphic3d_CView& theCView,
   myGlContext->FetchState();
 
   OpenGl_FrameBuffer* aFrameBuffer = (OpenGl_FrameBuffer* )theCView.ptrFBO;
-  if (aFrameBuffer != NULL)
-  {
-    aFrameBuffer->SetupViewport (myGlContext);
-  }
-  else
-  {
-    myGlContext->core11fwd->glViewport (0, 0, myWidth, myHeight);
-  }
   bool toSwap = myGlContext->IsRender()
             && !myGlContext->caps->buffersNoSwap
             &&  aFrameBuffer == NULL;
@@ -690,60 +897,135 @@ void OpenGl_Workspace::Redraw (const Graphic3d_CView& theCView,
   Standard_Integer aSizeX = aFrameBuffer != NULL ? aFrameBuffer->GetVPSizeX() : myWidth;
   Standard_Integer aSizeY = aFrameBuffer != NULL ? aFrameBuffer->GetVPSizeY() : myHeight;
 
-  if (!myGlContext->DefaultFrameBuffer().IsNull()
-    && myGlContext->DefaultFrameBuffer()->IsValid())
+  if ( aFrameBuffer == NULL
+   && !myGlContext->DefaultFrameBuffer().IsNull()
+   &&  myGlContext->DefaultFrameBuffer()->IsValid())
   {
-    myGlContext->DefaultFrameBuffer()->BindBuffer (myGlContext);
+    aFrameBuffer = myGlContext->DefaultFrameBuffer().operator->();
   }
 
   if (myHasFboBlit
    && myTransientDrawToFront)
   {
-    if (myResultFBO->GetVPSizeX() != aSizeX
-     || myResultFBO->GetVPSizeY() != aSizeY)
+    if (myMainSceneFbos[0]->GetVPSizeX() != aSizeX
+     || myMainSceneFbos[0]->GetVPSizeY() != aSizeY)
     {
       // prepare FBOs containing main scene
       // for further blitting and rendering immediate presentations on top
       if (myGlContext->core20fwd != NULL)
       {
-        myResultFBO->Init (myGlContext, aSizeX, aSizeY);
+        myMainSceneFbos[0]->Init (myGlContext, aSizeX, aSizeY);
       }
     }
-
-    if (myResultFBO->IsValid())
-    {
-      myResultFBO->SetupViewport (myGlContext);
-    }
   }
   else
   {
-    myResultFBO->Release (myGlContext.operator->());
-    myResultFBO->ChangeViewport (0, 0);
+    myMainSceneFbos     [0]->Release (myGlContext.operator->());
+    myMainSceneFbos     [1]->Release (myGlContext.operator->());
+    myImmediateSceneFbos[0]->Release (myGlContext.operator->());
+    myImmediateSceneFbos[1]->Release (myGlContext.operator->());
+    myMainSceneFbos     [0]->ChangeViewport (0, 0);
+    myMainSceneFbos     [1]->ChangeViewport (0, 0);
+    myImmediateSceneFbos[0]->ChangeViewport (0, 0);
+    myImmediateSceneFbos[1]->ChangeViewport (0, 0);
   }
 
   // draw entire frame using normal OpenGL pipeline
-  if (myResultFBO->IsValid())
-  {
-    myResultFBO->BindBuffer (myGlContext);
-  }
-  else if (aFrameBuffer != NULL)
+  const Handle(Graphic3d_Camera)& aCamera      = myView->Camera();
+  Graphic3d_Camera::Projection    aProjectType = aCamera->ProjectionType();
+  if (aProjectType == Graphic3d_Camera::Projection_Stereo)
   {
-    aFrameBuffer->BindBuffer (myGlContext);
+    if (aFrameBuffer != NULL
+    || !myGlContext->IsRender())
+    {
+      // implicitly switch to mono camera for image dump
+      aProjectType = Graphic3d_Camera::Projection_Perspective;
+    }
+    else if (myMainSceneFbos[0]->IsValid())
+    {
+      myMainSceneFbos[1]->InitLazy (myGlContext, aSizeX, aSizeY);
+      if (!myMainSceneFbos[1]->IsValid())
+      {
+        // no enough memory?
+        aProjectType = Graphic3d_Camera::Projection_Perspective;
+      }
+      else if (!myGlContext->HasStereoBuffers())
+      {
+        myImmediateSceneFbos[0]->InitLazy (myGlContext, aSizeX, aSizeY);
+        myImmediateSceneFbos[1]->InitLazy (myGlContext, aSizeX, aSizeY);
+        if (!myImmediateSceneFbos[0]->IsValid()
+         || !myImmediateSceneFbos[1]->IsValid())
+        {
+          aProjectType = Graphic3d_Camera::Projection_Perspective;
+        }
+      }
+    }
   }
 
-  redraw1 (theCView, theCUnderLayer, theCOverLayer);
-  myBackBufferRestored = Standard_True;
-  myIsImmediateDrawn   = Standard_False;
-  if (!redrawImmediate (theCView, theCOverLayer, theCUnderLayer, aFrameBuffer))
+  if (aProjectType == Graphic3d_Camera::Projection_Stereo)
   {
-    toSwap = false;
-  }
+    OpenGl_FrameBuffer* aMainFbos[2] =
+    {
+      myMainSceneFbos[0]->IsValid() ? myMainSceneFbos[0].operator->() : NULL,
+      myMainSceneFbos[1]->IsValid() ? myMainSceneFbos[1].operator->() : NULL
+    };
+    OpenGl_FrameBuffer* anImmFbos[2] =
+    {
+      myImmediateSceneFbos[0]->IsValid() ? myImmediateSceneFbos[0].operator->() : NULL,
+      myImmediateSceneFbos[1]->IsValid() ? myImmediateSceneFbos[1].operator->() : NULL
+    };
 
-  if (aFrameBuffer != NULL)
+  #if !defined(GL_ES_VERSION_2_0)
+    myGlContext->SetReadDrawBuffer (GL_BACK_LEFT);
+  #endif
+    redraw1 (theCView, theCUnderLayer, theCOverLayer,
+             aMainFbos[0], Graphic3d_Camera::Projection_MonoLeftEye);
+    myBackBufferRestored = Standard_True;
+    myIsImmediateDrawn   = Standard_False;
+  #if !defined(GL_ES_VERSION_2_0)
+    myGlContext->SetReadDrawBuffer (GL_BACK_LEFT);
+  #endif
+    if (!redrawImmediate (theCView, theCOverLayer, theCUnderLayer, aMainFbos[0], aProjectType, anImmFbos[0]))
+    {
+      toSwap = false;
+    }
+
+  #if !defined(GL_ES_VERSION_2_0)
+    myGlContext->SetReadDrawBuffer (GL_BACK_RIGHT);
+  #endif
+    redraw1 (theCView, theCUnderLayer, theCOverLayer,
+             aMainFbos[1], Graphic3d_Camera::Projection_MonoRightEye);
+    myBackBufferRestored = Standard_True;
+    myIsImmediateDrawn   = Standard_False;
+    if (!redrawImmediate (theCView, theCOverLayer, theCUnderLayer, aMainFbos[1], aProjectType, anImmFbos[1]))
+    {
+      toSwap = false;
+    }
+
+    if (anImmFbos[0] != NULL)
+    {
+      bindDefaultFbo (aFrameBuffer);
+      drawStereoPair();
+    }
+  }
+  else
   {
-    aFrameBuffer->UnbindBuffer (myGlContext);
-    // move back original viewport
-    myGlContext->core11fwd->glViewport (0, 0, myWidth, myHeight);
+    OpenGl_FrameBuffer* aMainFbo = myMainSceneFbos[0]->IsValid() ? myMainSceneFbos[0].operator->() : NULL;
+  #if !defined(GL_ES_VERSION_2_0)
+    if (aMainFbo     == NULL
+     && aFrameBuffer == NULL)
+    {
+      myGlContext->SetReadDrawBuffer (GL_BACK);
+    }
+  #endif
+    redraw1 (theCView, theCUnderLayer, theCOverLayer,
+             aMainFbo != NULL ? aMainFbo : aFrameBuffer, aProjectType);
+    myBackBufferRestored = Standard_True;
+    myIsImmediateDrawn   = Standard_False;
+    if (!redrawImmediate (theCView, theCOverLayer, theCUnderLayer, aMainFbo, aProjectType, aFrameBuffer))
+    {
+      toSwap = false;
+    }
   }
 
 #if defined(_WIN32) && defined(HAVE_VIDEOCAPTURE)
@@ -764,11 +1046,14 @@ void OpenGl_Workspace::Redraw (const Graphic3d_CView& theCView,
   }
 #endif
 
+  // bind default FBO
+  bindDefaultFbo();
+
   // Swap the buffers
   if (toSwap)
   {
     GetGlContext()->SwapBuffers();
-    if (!myResultFBO->IsValid())
+    if (!myMainSceneFbos[0]->IsValid())
     {
       myBackBufferRestored = Standard_False;
     }
@@ -786,15 +1071,27 @@ void OpenGl_Workspace::Redraw (const Graphic3d_CView& theCView,
 // function : redraw1
 // purpose  :
 // =======================================================================
-void OpenGl_Workspace::redraw1 (const Graphic3d_CView& theCView,
-                                const Aspect_CLayer2d& theCUnderLayer,
-                                const Aspect_CLayer2d& theCOverLayer)
+void OpenGl_Workspace::redraw1 (const Graphic3d_CView&               theCView,
+                                const Aspect_CLayer2d&               theCUnderLayer,
+                                const Aspect_CLayer2d&               theCOverLayer,
+                                OpenGl_FrameBuffer*                  theReadDrawFbo,
+                                const Graphic3d_Camera::Projection   theProjection)
 {
   if (myView.IsNull())
   {
     return;
   }
 
+  if (theReadDrawFbo != NULL)
+  {
+    theReadDrawFbo->BindBuffer    (myGlContext);
+    theReadDrawFbo->SetupViewport (myGlContext);
+  }
+  else
+  {
+    myGlContext->core11fwd->glViewport (0, 0, myWidth, myHeight);
+  }
+
   // request reset of material
   NamedStatus |= OPENGL_NS_RESMAT;
 
@@ -838,7 +1135,7 @@ void OpenGl_Workspace::redraw1 (const Graphic3d_CView& theCView,
   glClear (toClear);
 
   Handle(OpenGl_Workspace) aWS (this);
-  myView->Render (myPrintContext, aWS, theCView, theCUnderLayer, theCOverLayer, Standard_False);
+  myView->Render (myPrintContext, aWS, theReadDrawFbo, theProjection, theCView, theCUnderLayer, theCOverLayer, Standard_False);
 }
 
 // =======================================================================
@@ -864,8 +1161,27 @@ void OpenGl_Workspace::copyBackToFront()
 
   DisableFeatures();
 
-  glDrawBuffer (GL_FRONT);
-  glReadBuffer (GL_BACK);
+  switch (myGlContext->DrawBuffer())
+  {
+    case GL_BACK_LEFT:
+    {
+      myGlContext->SetReadBuffer (GL_BACK_LEFT);
+      myGlContext->SetDrawBuffer (GL_FRONT_LEFT);
+      break;
+    }
+    case GL_BACK_RIGHT:
+    {
+      myGlContext->SetReadBuffer (GL_BACK_RIGHT);
+      myGlContext->SetDrawBuffer (GL_FRONT_RIGHT);
+      break;
+    }
+    default:
+    {
+      myGlContext->SetReadBuffer (GL_BACK);
+      myGlContext->SetDrawBuffer (GL_FRONT);
+      break;
+    }
+  }
 
   glRasterPos2i (0, 0);
   glCopyPixels  (0, 0, myWidth + 1, myHeight + 1, GL_COLOR);
@@ -876,8 +1192,9 @@ void OpenGl_Workspace::copyBackToFront()
   myGlContext->WorldViewState.Pop();
   myGlContext->ProjectionState.Pop();
   myGlContext->ApplyProjectionMatrix();
-  glDrawBuffer (GL_BACK);
 
+  // read/write from front buffer now
+  myGlContext->SetReadBuffer (myGlContext->DrawBuffer());
 #endif
   myIsImmediateDrawn = Standard_False;
 }
@@ -911,9 +1228,33 @@ void OpenGl_Workspace::RedrawImmediate (const Graphic3d_CView& theCView,
                                         const Aspect_CLayer2d& theCUnderLayer,
                                         const Aspect_CLayer2d& theCOverLayer)
 {
+  const Handle(Graphic3d_Camera)& aCamera      = myView->Camera();
+  Graphic3d_Camera::Projection    aProjectType = aCamera->ProjectionType();
+  OpenGl_FrameBuffer* aFrameBuffer = (OpenGl_FrameBuffer* )theCView.ptrFBO;
+  if ( aFrameBuffer == NULL
+   && !myGlContext->DefaultFrameBuffer().IsNull()
+   &&  myGlContext->DefaultFrameBuffer()->IsValid())
+  {
+    aFrameBuffer = myGlContext->DefaultFrameBuffer().operator->();
+  }
+
+  if (aProjectType == Graphic3d_Camera::Projection_Stereo)
+  {
+    if (aFrameBuffer != NULL)
+    {
+      // implicitly switch to mono camera for image dump
+      aProjectType = Graphic3d_Camera::Projection_Perspective;
+    }
+    else if (myMainSceneFbos[0]->IsValid()
+         && !myMainSceneFbos[1]->IsValid())
+    {
+      aProjectType = Graphic3d_Camera::Projection_Perspective;
+    }
+  }
+
   if (!myTransientDrawToFront
    || !myBackBufferRestored
-   || (myGlContext->caps->buffersNoSwap && !myResultFBO->IsValid()))
+   || (myGlContext->caps->buffersNoSwap && !myMainSceneFbos[0]->IsValid()))
   {
     Redraw (theCView, theCUnderLayer, theCOverLayer);
     return;
@@ -923,13 +1264,77 @@ void OpenGl_Workspace::RedrawImmediate (const Graphic3d_CView& theCView,
     return;
   }
 
-  if (!myGlContext->DefaultFrameBuffer().IsNull()
-   &&  myGlContext->DefaultFrameBuffer()->IsValid())
+  bool toSwap = false;
+  if (aProjectType == Graphic3d_Camera::Projection_Stereo)
+  {
+    OpenGl_FrameBuffer* aMainFbos[2] =
+    {
+      myMainSceneFbos[0]->IsValid() ? myMainSceneFbos[0].operator->() : NULL,
+      myMainSceneFbos[1]->IsValid() ? myMainSceneFbos[1].operator->() : NULL
+    };
+    OpenGl_FrameBuffer* anImmFbos[2] =
+    {
+      myImmediateSceneFbos[0]->IsValid() ? myImmediateSceneFbos[0].operator->() : NULL,
+      myImmediateSceneFbos[1]->IsValid() ? myImmediateSceneFbos[1].operator->() : NULL
+    };
+
+    if (myGlContext->arbFBO != NULL)
+    {
+      myGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
+    }
+  #if !defined(GL_ES_VERSION_2_0)
+    if (anImmFbos[0] == NULL)
+    {
+      myGlContext->SetReadDrawBuffer (GL_BACK_LEFT);
+    }
+  #endif
+    toSwap = redrawImmediate (theCView, theCUnderLayer, theCOverLayer,
+                              aMainFbos[0],
+                              Graphic3d_Camera::Projection_MonoLeftEye,
+                              anImmFbos[0],
+                              Standard_True) || toSwap;
+
+    if (myGlContext->arbFBO != NULL)
+    {
+      myGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
+    }
+  #if !defined(GL_ES_VERSION_2_0)
+    if (anImmFbos[1] == NULL)
+    {
+      myGlContext->SetReadDrawBuffer (GL_BACK_RIGHT);
+    }
+  #endif
+    toSwap = redrawImmediate (theCView, theCUnderLayer, theCOverLayer,
+                              aMainFbos[1],
+                              Graphic3d_Camera::Projection_MonoRightEye,
+                              anImmFbos[1],
+                              Standard_True) || toSwap;
+    if (anImmFbos[0] != NULL)
+    {
+      bindDefaultFbo (aFrameBuffer);
+      drawStereoPair();
+    }
+  }
+  else
   {
-    myGlContext->DefaultFrameBuffer()->BindBuffer (myGlContext);
+    OpenGl_FrameBuffer* aMainFbo = myMainSceneFbos[0]->IsValid() ? myMainSceneFbos[0].operator->() : NULL;
+  #if !defined(GL_ES_VERSION_2_0)
+    if (aMainFbo == NULL)
+    {
+      myGlContext->SetReadDrawBuffer (GL_BACK);
+    }
+  #endif
+    toSwap = redrawImmediate (theCView, theCUnderLayer, theCOverLayer,
+                              aMainFbo,
+                              aProjectType,
+                              aFrameBuffer,
+                              Standard_True) || toSwap;
   }
 
-  if (redrawImmediate (theCView, theCUnderLayer, theCOverLayer, NULL, Standard_True)
+  // bind default FBO
+  bindDefaultFbo();
+
+  if (toSwap
   && !myGlContext->caps->buffersNoSwap)
   {
     myGlContext->SwapBuffers();
@@ -937,7 +1342,6 @@ void OpenGl_Workspace::RedrawImmediate (const Graphic3d_CView& theCView,
   else
   {
     myGlContext->core11fwd->glFlush();
-    MakeBackBufCurrent();
   }
 }
 
@@ -948,7 +1352,9 @@ void OpenGl_Workspace::RedrawImmediate (const Graphic3d_CView& theCView,
 bool OpenGl_Workspace::redrawImmediate (const Graphic3d_CView& theCView,
                                         const Aspect_CLayer2d& theCUnderLayer,
                                         const Aspect_CLayer2d& theCOverLayer,
-                                        OpenGl_FrameBuffer*    theTargetFBO,
+                                        OpenGl_FrameBuffer*    theReadFbo,
+                                        const Graphic3d_Camera::Projection theProjection,
+                                        OpenGl_FrameBuffer*    theDrawFbo,
                                         const Standard_Boolean theIsPartialUpdate)
 {
   GLboolean toCopyBackToFront = GL_FALSE;
@@ -956,116 +1362,16 @@ bool OpenGl_Workspace::redrawImmediate (const Graphic3d_CView& theCView,
   {
     myBackBufferRestored = Standard_False;
   }
-  else if (myResultFBO->IsValid()
+  else if (theReadFbo != NULL
+        && theReadFbo->IsValid()
         && myGlContext->IsRender())
   {
-    // clear destination before blitting
-    if (theTargetFBO != NULL)
-    {
-      theTargetFBO->BindBuffer (myGlContext);
-    }
-    else if (!myGlContext->DefaultFrameBuffer().IsNull()
-          &&  myGlContext->DefaultFrameBuffer()->IsValid())
-    {
-      myGlContext->DefaultFrameBuffer()->BindBuffer (myGlContext);
-    }
-    else
-    {
-      myGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
-    }
-  #if !defined(GL_ES_VERSION_2_0)
-    myGlContext->core20fwd->glClearDepth  (1.0);
-  #else
-    myGlContext->core20fwd->glClearDepthf (1.0f);
-  #endif
-    myGlContext->core20fwd->glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-
-  /*#if !defined(GL_ES_VERSION_2_0)
-    if (myGlContext->arbFBOBlit != NULL)
-    {
-      myResultFBO->BindReadBuffer (myGlContext);
-      if (theTargetFBO != NULL)
-      {
-        theTargetFBO->BindDrawBuffer (myGlContext);
-      }
-      else if (!myGlContext->DefaultFrameBuffer().IsNull()
-            &&  myGlContext->DefaultFrameBuffer()->IsValid())
-      {
-        myGlContext->DefaultFrameBuffer()->BindDrawBuffer (myGlContext);
-      }
-      else
-      {
-        myGlContext->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
-      }
-      // we don't copy stencil buffer here... does it matter for performance?
-      myGlContext->arbFBOBlit->glBlitFramebuffer (0, 0, myResultFBO->GetVPSizeX(), myResultFBO->GetVPSizeY(),
-                                                  0, 0, myResultFBO->GetVPSizeX(), myResultFBO->GetVPSizeY(),
-                                                  GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
-
-      if (theTargetFBO != NULL)
-      {
-        theTargetFBO->BindBuffer (myGlContext);
-      }
-      else if (!myGlContext->DefaultFrameBuffer().IsNull()
-            &&  myGlContext->DefaultFrameBuffer()->IsValid())
-      {
-        myGlContext->DefaultFrameBuffer()->BindBuffer (myGlContext);
-      }
-      else
-      {
-        myGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
-      }
-    }
-    else
-  #endif*/
+    if (!blitBuffers (theReadFbo, theDrawFbo))
     {
-      myGlContext->core20fwd->glDepthFunc (GL_ALWAYS);
-      myGlContext->core20fwd->glDepthMask (GL_TRUE);
-      myGlContext->core20fwd->glEnable (GL_DEPTH_TEST);
-
-      DisableTexture();
-      if (!myFullScreenQuad.IsValid())
-      {
-        OpenGl_Vec4 aQuad[4] =
-        {
-          OpenGl_Vec4( 1.0f, -1.0f, 1.0f, 0.0f),
-          OpenGl_Vec4( 1.0f,  1.0f, 1.0f, 1.0f),
-          OpenGl_Vec4(-1.0f, -1.0f, 0.0f, 0.0f),
-          OpenGl_Vec4(-1.0f,  1.0f, 0.0f, 1.0f)
-        };
-        myFullScreenQuad.Init (myGlContext, 4, 4, aQuad[0].GetData());
-      }
-
-      const Handle(OpenGl_ShaderManager)& aManager = myGlContext->ShaderManager();
-      if (myFullScreenQuad.IsValid()
-       && aManager->BindFboBlitProgram())
-      {
-        myResultFBO->ColorTexture()       ->Bind   (myGlContext, GL_TEXTURE0 + 0);
-        myResultFBO->DepthStencilTexture()->Bind   (myGlContext, GL_TEXTURE0 + 1);
-        myFullScreenQuad.BindVertexAttrib (myGlContext, 0);
-
-        myGlContext->core20fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
-
-        myFullScreenQuad.UnbindVertexAttrib (myGlContext, 0);
-        myResultFBO->DepthStencilTexture()->Unbind (myGlContext, GL_TEXTURE0 + 1);
-        myResultFBO->ColorTexture()       ->Unbind (myGlContext, GL_TEXTURE0 + 0);
-      }
-      else
-      {
-        TCollection_ExtendedString aMsg = TCollection_ExtendedString()
-          + "Error! FBO blitting has failed";
-        myGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
-                                  GL_DEBUG_TYPE_ERROR_ARB,
-                                  0,
-                                  GL_DEBUG_SEVERITY_HIGH_ARB,
-                                  aMsg);
-        myHasFboBlit = Standard_False;
-        myResultFBO->Release (myGlContext.operator->());
-        return true;
-      }
+      return true;
     }
   }
-  else if (theTargetFBO == NULL)
+  else if (theDrawFbo == NULL)
   {
   #if !defined(GL_ES_VERSION_2_0)
     myGlContext->core11fwd->glGetBooleanv (GL_DOUBLEBUFFER, &toCopyBackToFront);
@@ -1079,7 +1385,6 @@ bool OpenGl_Workspace::redrawImmediate (const Graphic3d_CView& theCView,
         return true;
       }
       copyBackToFront();
-      MakeFrontBufCurrent();
     }
     else
     {
@@ -1118,7 +1423,8 @@ bool OpenGl_Workspace::redrawImmediate (const Graphic3d_CView& theCView,
     glDisable (GL_DEPTH_TEST);
   }
 
-  myView->Render (myPrintContext, aWS, theCView, theCUnderLayer, theCOverLayer, Standard_True);
+  myView->Render (myPrintContext, aWS, theDrawFbo, theProjection,
+                  theCView, theCUnderLayer, theCOverLayer, Standard_True);
   if (!myView->ImmediateStructures().IsEmpty())
   {
     glDisable (GL_DEPTH_TEST);
@@ -1135,12 +1441,7 @@ bool OpenGl_Workspace::redrawImmediate (const Graphic3d_CView& theCView,
     aStructure->Render (aWS);
   }
 
-  if (toCopyBackToFront)
-  {
-    MakeBackBufCurrent();
-    return false;
-  }
-  return true;
+  return !toCopyBackToFront;
 }
 
 IMPLEMENT_STANDARD_HANDLE (OpenGl_RaytraceFilter, OpenGl_RenderFilter)
index 2b1ba45..c135788 100644 (file)
@@ -265,22 +265,31 @@ public:
   //! @return true if clipping algorithm enabled
   inline Standard_Boolean IsCullingEnabled() const { return myIsCullingEnabled; }
 
-  //! Returns framebuffer storing cached main presentation of the view.
-  const Handle(OpenGl_FrameBuffer)& ResultFBO() const { return myResultFBO; }
-
 protected:
 
   //! Copy content of Back buffer to the Front buffer
   void copyBackToFront();
 
+  //! Blit image from/to specified buffers.
+  bool blitBuffers (OpenGl_FrameBuffer* theReadFbo,
+                    OpenGl_FrameBuffer* theDrawFbo);
+
   virtual Standard_Boolean Activate();
 
-  void redraw1 (const Graphic3d_CView& theCView,
-                const Aspect_CLayer2d& theCUnderLayer,
-                const Aspect_CLayer2d& theCOverLayer);
+  void redraw1 (const Graphic3d_CView&               theCView,
+                const Aspect_CLayer2d&               theCUnderLayer,
+                const Aspect_CLayer2d&               theCOverLayer,
+                OpenGl_FrameBuffer*                  theReadDrawFbo,
+                const Graphic3d_Camera::Projection   theProjection);
+
+  //! Setup default FBO.
+  void bindDefaultFbo (OpenGl_FrameBuffer* theCustomFbo = NULL);
+
+  //! Blend together views pair into stereo image.
+  void drawStereoPair();
 
-  //! Blit snapshot containing main scene (myResultFBO or BackBuffer)
-  //! into presentation buffer (myResultFBO->offscreen FBO or myResultFBO->BackBuffer or BackBuffer->FrontBuffer),
+  //! Blit snapshot containing main scene (myMainSceneFbos or BackBuffer)
+  //! into presentation buffer (myMainSceneFbos -> offscreen FBO or myMainSceneFbos -> BackBuffer or BackBuffer -> FrontBuffer),
   //! and redraw immediate structures on top.
   //!
   //! When scene caching is disabled (myTransientDrawToFront, no double buffer in window, etc.),
@@ -290,7 +299,9 @@ protected:
   bool redrawImmediate (const Graphic3d_CView& theCView,
                         const Aspect_CLayer2d& theCUnderLayer,
                         const Aspect_CLayer2d& theCOverLayer,
-                        OpenGl_FrameBuffer*    theTargetFBO,
+                        OpenGl_FrameBuffer*    theReadFbo,
+                        const Graphic3d_Camera::Projection theProjection,
+                        OpenGl_FrameBuffer*    theDrawFbo,
                         const Standard_Boolean theIsPartialUpdate = Standard_False);
 
   void updateMaterial (const int theFlag);
@@ -300,9 +311,12 @@ protected:
 
 protected: //! @name protected fields
 
-  //! Framebuffer stores cached main presentation of the view (without presentation of immediate layers).
-  Handle(OpenGl_FrameBuffer) myResultFBO;
-  //! Special flag which is invalidated when myResultFBO can not be blitted for some reason (e.g. driver bugs).
+  //! Two framebuffers (left and right views) store cached main presentation
+  //! of the view (without presentation of immediate layers).
+  Handle(OpenGl_FrameBuffer) myMainSceneFbos[2];
+  //! Additional buffers for immediate layer in stereo mode.
+  Handle(OpenGl_FrameBuffer) myImmediateSceneFbos[2];
+  //! Special flag which is invalidated when myMainSceneFbos can not be blitted for some reason (e.g. driver bugs).
   Standard_Boolean           myHasFboBlit;
 
   //! Vertices for full-screen quad rendering.
index 2bf1720..e4171b1 100644 (file)
@@ -550,9 +550,6 @@ Standard_Boolean OpenGl_Workspace::Print
     }
   }
 
-  // activate the offscreen buffer
-  aFrameBuffer->BindBuffer (GetGlContext());
-
   // calculate offset for centered printing
   int aDevOffx = (int)(devWidth  - width) /2;
   int aDevOffy = (int)(devHeight - height)/2;
@@ -564,18 +561,21 @@ Standard_Boolean OpenGl_Workspace::Print
   if (!showBackground)
     NamedStatus |= OPENGL_NS_WHITEBACK;
 
+  // switch to mono camera for image dump
+  const Graphic3d_Camera::Projection aProjectType = myView->Camera()->ProjectionType() != Graphic3d_Camera::Projection_Stereo
+                                                  ? myView->Camera()->ProjectionType()
+                                                  : Graphic3d_Camera::Projection_Perspective;
   if (!IsTiling)
   {
     myPrintContext->SetScale ((GLfloat )aFrameWidth /viewWidth,
                               (GLfloat )aFrameHeight/viewHeight);
-    aFrameBuffer->SetupViewport (GetGlContext());
-    redraw1 (ACView, ACUnderLayer, ACOverLayer);
+    redraw1 (ACView, ACUnderLayer, ACOverLayer, aFrameBuffer, aProjectType);
     if (!myTransientDrawToFront)
     {
       // render to FBO only if allowed to render to back buffer
       myBackBufferRestored = Standard_True;
       myIsImmediateDrawn   = Standard_False;
-      redrawImmediate (ACView, ACUnderLayer, ACOverLayer, aFrameBuffer);
+      redrawImmediate (ACView, ACUnderLayer, ACOverLayer, NULL, aProjectType, aFrameBuffer);
       myBackBufferRestored = Standard_False;
       myIsImmediateDrawn   = Standard_False;
     }
@@ -683,14 +683,13 @@ Standard_Boolean OpenGl_Workspace::Print
                                      aFrameHeight;
 
         // draw to the offscreen buffer and capture the result
-        aFrameBuffer->SetupViewport (GetGlContext());
-        redraw1 (ACView, ACUnderLayer, ACOverLayer);
+        redraw1 (ACView, ACUnderLayer, ACOverLayer, aFrameBuffer, aProjectType);
         if (!myTransientDrawToFront)
         {
           // render to FBO only if forces to render to back buffer
           myBackBufferRestored = Standard_True;
           myIsImmediateDrawn   = Standard_False;
-          redrawImmediate (ACView, ACUnderLayer, ACOverLayer, aFrameBuffer);
+          redrawImmediate (ACView, ACUnderLayer, ACOverLayer, NULL, aProjectType, aFrameBuffer);
           myBackBufferRestored = Standard_False;
           myIsImmediateDrawn   = Standard_False;
         }