Example Application

There are a number of examples included to get started developing using Shade. What follows is the fourth tutorial for Shade, Offscreen Render. This example is overly commented to give an idea of what is going on with the program and meant to give an idea on what an application built in Shade will resemble.

ShadeOffscreenRenderApplication.hpp

#ifndef _SHADE_OFFSCREEN_RENDER_APPLICATION_HPP_
#define _SHADE_OFFSCREEN_RENDER_APPLICATION_HPP_

// Includes all the Shade classes for the Application
#include "Shade.hpp"

#include "ShadeLightColorController.hpp"

namespace Shade
{
  /**
   * A simple offscreen render application for Shade.
   *
   * In the fifth tutorial for Shade we create two scenes with
   * one of the scene drawn to a Dynamic Texture.
   *
   * This tutorial discusses: Creating a FrameBuffer, a 
   * DynamicTexture and using a FrameBufferRenderTarget.
   *
   * \note The code for this is meant as a tutorial and contains a 
   *       ridiculous amount of commenting.
   * \author Don Olmstead
   * \version 1.0
   */
  class OffscreenRenderApplication 
    : public SHADE_WINDOWED_APPLICATION_CLASS
  {
    public:
      OffscreenRenderApplication();
      virtual ~OffscreenRenderApplication();

      virtual void onOpen();
      virtual void onClose();
      virtual void onIdle(double time);

      virtual void onMotion(int x, int y);

    protected:
      /** The root node of the main scene */
      NodePtr rootNode;
      /** The root node of the offscreen scene */
      NodePtr offscreenRootNode;
      /** The box in the main scene */
      BoxPtr box;
      /** The box in the offscreen scene */
      BoxPtr offscreenBox;
      /** The DynamicTexture produced by the offscreen render */
      DynamicTexturePtr dynamicTexture;

      void createMainScene();
      void createOffscreenScene(int width, int height);
  } ;
}

#endif

ShadeOffscreenRenderApplication.cpp

#include "ShadeOffscreenRenderApplication.hpp"
using namespace Shade;

/**
 * Implements the WindowedApplication.
 *
 * This macro creates the main entrypoint into the application. Each 
 * WindowedApplication class you make will require this macro in order
 * to start the application.
 *
 * This macro expands to the int main() function and hooks the 
 * application.
 */
SHADE_IMPLEMENT_WINDOWED_APPLICATION(OffscreenRenderApplication);

/**
 * Default constructor for OffscreenRenderApplication.
 */
OffscreenRenderApplication::OffscreenRenderApplication()
{
  // Set any of the member variables to NULL
  // Do NOT initialize any member variables in the constructor
  // Do all initialization in the onOpen() method.
  box            = 0;
  offscreenBox   = 0;
  dynamicTexture = 0;
}

/**
 * Destructor for OffscreenRenderApplication.
 */
OffscreenRenderApplication::~OffscreenRenderApplication()
{
  // All the member variables should have been removed
  // by the time we hit this method. So for the most part
  // this method will be left blank.
}

/**
 * Initializes the application.
 *
 * In onOpen we open up any windows we require and initialize
 * our member variables. We also setup the scene and the render
 * target(s) in this method.
 */
void OffscreenRenderApplication::onOpen()
{
  // We want to call the parent method just in case there
  // are any tasks that need to be done by the parent class.
  // The parent method should be called at the start of the
  // child's method.
  SHADE_CALL_PARENT_APPLICATION_METHOD(onOpen());

  // Open a Window to display to.
  openWindow("Offscreen Render", 10, 10, 1433, 501);

  // Create the offscreen rendering scene. The offscreen rendering
  // produces a DynamicTexture which is used in the main scene
  // drawn to the screen so we create this first.
  createOffscreenScene(1024, 1024);

  // Create the main scene of the application. This is the scene
  // drawn to the screen.
  createMainScene();

  // Whenever you want to see what Objects are currently created
  // the Object::printObjectsInUse method can be used. This method
  // is called at startup and at termination to show what Objects
  // were created during initialization and what Objects still exist
  // at termination. We'll call it here to show what Objects we've
  // created at this point.
  Object::printObjectsInUse(std::cout);
}

/**
 * Closes the application.
 *
 * In onClose we cleanup any objects we've created during the
 * course of the application.
 */
void OffscreenRenderApplication::onClose()
{
  // Remove any member variables from the application.
  // Since we use smart pointers for our Objects all we need
  // to do is to set all of them to NULL (0) and they will 
  // delete themselves.
  box            = 0;
  offscreenBox   = 0;
  dynamicTexture = 0;

  // We want to call the parent method just in case there
  // are any tasks that need to be done by the parent class.
  // The parent method should be called at the end of the
  // child's method.
  SHADE_CALL_PARENT_APPLICATION_METHOD(onClose());

  // If there are any Objects that are left over by the time
  // the program is being terminated an assert will be thrown
  // when compiled in debug mode.
}

/**
 * Idle loop for the application.
 *
 * Here we update the scene. Use this method to do any time
 * based animations.
 */
void OffscreenRenderApplication::onIdle(double time)
{
  // We want to update the global state of the scene. This
  // will update any controllers attached to the scene, and will
  // update any Camera's attached to the scene, among other things.
  // There are two separate root nodes so we want to update both
  // of them.
  rootNode->updateGS(time);
  offscreenRootNode->updateGS(time);

  // We want to call the parent method just in case there
  // are any tasks that need to be done by the parent class.
  // The parent method should be called at the end of the
  // child's method.
  SHADE_CALL_PARENT_APPLICATION_METHOD(onIdle(time));
}

/**
 * Motion callback for the application.
 *
 * Motion is defined as a mouse button being held while the mouse
 * pointer is moved. This method is used to handle those events.
 *
 * \param x The x position of the mouse pointer.
 * \param y The y position of the mouse pointer.
 */
void OffscreenRenderApplication::onMotion(int x, int y)
{
  // We want to call the parent method just in case there
  // are any tasks that need to be done by the parent class.
  // The parent method should be called at the start of the
  // child's method.
  SHADE_CALL_PARENT_APPLICATION_METHOD(onMotion(x, y));

  // A wrapper class is provided for the mouse. This allows
  // for state querying. In our case we want to see the change
  // in position of the Mouse since the last callback.
  int changeX, changeY;

  mouse->getChangeInPosition(changeX, changeY);

  // If the left button is pressed we want to rotate the main
  // scene's cube
  if (mouse->isPressed(Mouse::LEFT))
    box->rotate(changeY * 0.1f, changeX * 0.1f, 0.0f);

  // If the left button is pressed we want to rotate the offscreen
  // scene's cube
  if (mouse->isPressed(Mouse::RIGHT))
    offscreenBox->rotate(changeY * 0.1f, changeX * 0.1f, 0.0f);
}

/**
 * Create the main scene of the application.
 *
 * This code should look fairly familiar to those who've
 * gone through the tutorials thus far. This method just
 * sets up the scene that is displayed directly to the
 * screen.
 */
void OffscreenRenderApplication::createMainScene()
{
  // Create the root node of the scene.
  rootNode = new Node();

  // Create the Camera used to display the scene.
  CameraPtr camera = new Camera();

  // There are two different ways to set the frustum.
  // The first is by setting all the values yourself.
  // The second, which is the one we're using, sets the
  // frustum using a method that mirrors the OpenGL
  // function gluPerspective.
  camera->setFrustum(35.0, 14.0 / 5.0, 1.0, 100.0);

  // Create the CameraNode and attach the Camera to it.
  // The CameraNode class allows a Camera to be moved around
  // the scene the same as any other Object in the scene.
  CameraNodePtr cameraNode = new CameraNode(camera);
  cameraNode->moveInZ(5.0f);

  rootNode->attachChild(cameraNode);

  // Create the Light used in the scene.
  PointLightPtr light = new PointLight();

  ColorRGBA diffuse(0.9f, 0.9f, 0.9f, 1.0f);

  light->setDiffuse(diffuse);

  // Create the LightNode and attach the Light to it.
  // The LightNode class allows a Light to be moved around
  // the scene the same as any other Object in the scene.
  LightNodePtr lightNode = new LightNode(light);
  lightNode->moveInZ(10.0f);

  rootNode->attachChild(lightNode);

  // Create the box and attach it to the root scene.
  box = new Box(1.0f);

  // Attach lighting to the box.
  box->attachEffect(new LightingEffect());
  box->attachLight(light);

  // Here we add the dynamic texture. This is the same 
  // procedure as any use of TextureEffect.
  TextureEffectPtr textureEffect = new TextureEffect();
  textureEffect->setTexture(dynamicTexture);

  box->attachEffect(textureEffect);

  // We will be texturing and lighting the box. This requires
  // two seperate passes over the geometry so we need to let the
  // renderer know how to blend the passes together. We do this
  // by attaching an AlphaState to the Effect.
  AlphaStatePtr alphaState = new AlphaState();
  alphaState->setBlendEnabled(true);
  alphaState->setSourceFactor(AlphaState::SF_DESTINATION_COLOR);
  alphaState->setDestinationFactor(AlphaState::DF_ZERO);

  textureEffect->setAlphaState(alphaState);

  // Add the box to the root node.
  rootNode->attachChild(box);

  // Update the state of the scene. This method is used to update
  // the scene, which includes setting the location of the camera,
  // updating any bounding volumes, and anything else that has to
  // do with changes within the scene based on time.
  rootNode->updateGS();
  rootNode->updateRS();

  // Finally we create the ScreenRenderTarget and attach
  // all the necessary data to it.
  ScreenRenderTargetPtr renderTarget = new ScreenRenderTarget();
  renderTarget->setCamera(camera);
  renderTarget->setRootNode(rootNode);
}

/**
 * Create the offscreen scene of the application.
 *
 * This code will be similar to creating the main scene
 * with a few important differences.
 *
 * \param width The width of the DynamicTexture.
 * \param height The height of the DynamicTexture.
 */
void OffscreenRenderApplication::createOffscreenScene(int width,
                                                      int height)
{
  // Create the root node of the scene.
  offscreenRootNode = new Node();

  // Create the Camera used to display the scene.
  CameraPtr camera = new Camera();

  // The frustum will be different in this case.
  // We are creating a texture that is a square. So
  // The aspect ratio will be much different
  camera->setFrustum(45.0, 1.0, 1.0, 100.0);

  // Create the CameraNode and attach the Camera to it.
  // The CameraNode class allows a Camera to be moved around
  // the scene the same as any other Object in the scene.
  CameraNodePtr cameraNode = new CameraNode(camera);
  cameraNode->moveInZ(5.0f);

  offscreenRootNode->attachChild(cameraNode);

  // Create the Light used in the scene.
  PointLightPtr light = new PointLight();

  // To make things a little more colorful we're going
  // to add the LightColorController from the last
  // tutorial.
  LightColorControllerPtr lightController = new LightColorController();

  lightController->setRepeatType(Controller::WRAP);
  lightController->setMaxTime(88.0);

  light->setController(lightController);

  // Create the LightNode and attach the Light to it.
  // The LightNode class allows a Light to be moved around
  // the scene the same as any other Object in the scene.
  LightNodePtr lightNode = new LightNode(light);
  lightNode->moveInZ(10.0f);

  offscreenRootNode->attachChild(lightNode);

  // Create the box and attach it to the root scene.
  offscreenBox = new Box(1.0f);

  // Attach lighting to the box.
  offscreenBox->attachEffect(new LightingEffect());
  offscreenBox->attachLight(light);

  // Here we add the dynamic texture. This is the same 
  // procedure as any use of TextureEffect.
  TextureEffectPtr textureEffect = new TextureEffect();
  textureEffect->setTexture(new Texture("Images/Shade.png"));

  offscreenBox->attachEffect(textureEffect);

  // We will be texturing and lighting the box. This requires
  // two seperate passes over the geometry so we need to let the
  // renderer know how to blend the passes together. We do this
  // by attaching an AlphaState to the Effect.
  AlphaStatePtr alphaState = new AlphaState();
  alphaState->setBlendEnabled(true);
  alphaState->setSourceFactor(AlphaState::SF_DESTINATION_COLOR);
  alphaState->setDestinationFactor(AlphaState::DF_ZERO);

  textureEffect->setAlphaState(alphaState);

  // Add the box to the root node.
  offscreenRootNode->attachChild(offscreenBox);

  // Update the state of the scene. This method is used to update
  // the scene, which includes setting the location of the camera,
  // updating any bounding volumes, and anything else that has to
  // do with changes within the scene based on time.
  offscreenRootNode->updateGS();
  offscreenRootNode->updateRS();

  // Now we create the FrameBuffer which is the mechanism for
  // creating the offscreen render. A FrameBuffer requires a
  // RenderBuffer and a DynamicTexture to do offscreen rendering.
  FrameBufferPtr frameBuffer = new FrameBuffer();

  // First we create the render buffer.
  RenderBufferPtr renderBuffer = new RenderBuffer(width, height);

  frameBuffer->setRenderBuffer(renderBuffer);

  // Next we want to create the DyanmicTexture and
  // attach it to the frame buffer. The texture should be
  // the same dimensions as the RenderBuffer.
  dynamicTexture = new DynamicTexture(width, height, 1);

  frameBuffer->setTexture(dynamicTexture);

  // Finally we create the FrameBufferRenderTarget and attach
  // all the necessary data to it.
  FrameBufferRenderTargetPtr renderTarget = new 
                                            FrameBufferRenderTarget();
  renderTarget->setCamera(camera);
  renderTarget->setRootNode(offscreenRootNode);
  renderTarget->setFrameBuffer(frameBuffer);
  renderTarget->setClearColor(ColorRGBA::BLUE);
}

The Result

Result on a single display Result on a tiled display