Open topic with navigation

# Updating the Scene

This section breaks updating into two steps:

- Updating the Stereo Viewport
- Rendering the Scene

## Updating the Stereo Viewport

For head tracking and stereo to work properly, your application must track the application window's position and size. If the window has moved or resized, your application must update the viewport position and size. Use zsSetViewportPosition() and zsSetViewportSize() to accomplish this. Next, call zsUpdate() to cache the latest tracking information and update the stereo frustum with the latest head pose. The following is a partial code example, with error handling omitted for brevity:

      // Get the application window's most recent position and size.
      // Implementation omitted for brevity.
      int windowX      = ...;
      int windowY      = ...;
      int windowWidth  = ...;
      int windowHeight = ...;

      // Update the zSpace viewport position and size based
      // on the position and size of the application window.
      zsSetViewportPosition(g_viewportHandle, windowX, windowY);
      zsSetViewportSize(g_viewportHandle, windowWidth, windowHeight);

      // Update the OpenGl viewport size.
      glViewport(0, 0, windowWidth, windowHeight);

      // Update the zSpace SDK. This updates both tracking information
      // as well as the head poses for any frustums that have been created.
      zsUpdate(g_zSpaceContext);

Although we discussed zsUpdate() with regards to stereo only, this function does quite a bit more. zsUpdate() is important for both stereo and tracking devices.

## Rendering the Scene

In your application's main loop, every frame, you will need to render your scene once for each eye. At a high level, you will need to:

- Call zsBeginStereoBufferFrame() to handle the left/right frame detection. Call this once per frame.

- For each eye:
  1. Save the monoscopic model-view and projection matrices for restoration.
  2. Call zsGetFrustumViewMatrix() to obtain the specified eye's view matrix.
  3. Set the eye's view matrix (eye view matrix multiplied by the monoscopic model-view matrix).
  4. Call zsGetFrustumProjectionMatrix() to obtain the specified eye's projection matrix.
  5. Set the eye's projection matrix.
  6. Select the correct render target based on specified eye (e.g., GL_BACK_LEFT or GL_BACK_RIGHT).
  7. Render the scene.
  8. Restore the model-view and projection matrix stacks.
  9. Flush the render buffers.

The following code example is based on the Basic Stereo sample application for OpenGL and illustrates the above sequence of steps:

    GLfloat g_modelViewMatrix[16];

    void draw(HDC hDC, HGLRC hRC)
    {
      // This must be called every frame on the rendering thread in order
      // to handle the initial sync and any subsequent pending sync requests
      // for left/right frame detection.
      zsBeginStereoBufferFrame(g_bufferHandle);

      // Cache the monoscopic model-view matrix for later use when setting
      // the model-view matrices for each eye.
      glMatrixMode(GL_MODELVIEW);
      glGetFloatv(GL_MODELVIEW_MATRIX, g_modelViewMatrix);

      // Set the application window's rendering context as the current rendering context.
      wglMakeCurrent(hDC, hRC);

      // Draw the scene for each eye.
      drawSceneForEye(ZS_EYE_LEFT);
      drawSceneForEye(ZS_EYE_RIGHT);

      // Flush the render buffers.
      SwapBuffers(hDC);
    }

Note: Generally, left/right frame detection is handled automatically by calling zsBeginStereoBufferFrame(). However, if needed, you can force re-synchronization by calling zsSyncStereoBuffer().

The rest of the steps will occur twice, once for each eye. The following code examples belong to the implementation of drawSceneForEye().

First, save the monoscopic model-view and projection matrices for later:

      // Push the stereo view and projection matrices onto the OpenGl matrix
      // stack so that we can pop them off after we're done rendering the
      // scene for a specified eye.  This will allow us to restore the mono
      // (non-stereoscopic) model-view and projection matrices.
      glMatrixMode(GL_MODELVIEW);
      glPushMatrix();
      glMatrixMode(GL_PROJECTION);
      glPushMatrix();

Next, set the model-view matrix for the specified eye:

      // Set the view matrix.
      ZSMatrix4 viewMatrix;
      zsGetFrustumViewMatrix(g_frustumHandle, eye, &viewMatrix);

      // Set the model-view matrix for the specified eye and multiply it by the
      // mono (non-stereoscopic) model-view matrix.
      glMatrixMode(GL_MODELVIEW);
      glLoadMatrixf(viewMatrix.f);
      glMultMatrixf(g_modelViewMatrix);

Set the projection matrix for the specified eye:

      // Get the projection matrix from the zSpace StereoFrustum for a specified eye.
      ZSMatrix4 projectionMatrix;
      zsGetFrustumProjectionMatrix(g_frustumHandle, eye, &projectionMatrix);

      // Set OpenGl MatrixMode to GL_PROJECTION and set the projection matrix.
      glMatrixMode(GL_PROJECTION);
      glLoadMatrixf(projectionMatrix.f);

Set the render target for the specified eye:

      // Select appropriate back buffer to render to based on the specified eye.
      switch (eye)
      {
      case ZS_EYE_LEFT:
        glDrawBuffer(GL_BACK_LEFT);
        break;
      case ZS_EYE_RIGHT:
        glDrawBuffer(GL_BACK_RIGHT);
        break;
      default:
        break;
      }

Render the scene. Refer to the Basic Stereo sample application for an example of rendering the scene itself. This does not require the zSpace SDK.

Finally, restore the monoscopic model-view and projection matrices:

      // Restore the mono (non-stereoscopic) model-view and projection matrices.
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);
      glPopMatrix();

Open topic with navigation