Hi folks,

I would like to post my solution to the problem of rendering to a texture 
independently on the framerate of the viewer, the code is attached.

My solution is to derived a Viewer from osgViewer::Viewer, added a custom 
offScreenRender() function mimicking frame() and (offScreen)renderTraversal() 
function without statistics and swapbuffer. 
In the offScreenRender() function I exchange the child of the main camera 
 by the off screen rendering node containing only the RTT cameras and switch 
it back afterwards. This works great except that the shadows do not work 
correctly in the RTT camera. It seems that the light position is wrongly 
detected, which is done in the cull traversal as far as I could understand 
from looking at the code. How do I perform this properly in the 
offScreenRenderTraversal()  function?


Cheers,
        Georg


/* -*-c++-*- Taken from osgprerenderer.cpp
 *
 * This application is open source and may be redistributed and/or modified   
 * freely and without restriction, both in commericial and non commericial applications,
 * as long as this copyright notice is maintained.
 * 
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 g++ -losgShadow -losgText -losgUtil -losgViewer -losgGA -lOpenThreads -losg -lGL -lGLU -lglut async_test.cpp

*/
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

#include <osgUtil/Optimizer>
#include <osg/CoordinateSystemNode>

#include <osg/Switch>
#include <osgText/Text>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/Renderer>


#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgGA/StateSetManipulator>

#include <iostream>
#include <sstream>
#include <string.h>


class AsyncRTTViewer : public osgViewer::Viewer {
public:
    
  AsyncRTTViewer(){
    asyncRTTViewerConstructorInit();
  }

  AsyncRTTViewer(osg::ArgumentParser& arguments)
    : osgViewer::Viewer(arguments) {
    asyncRTTViewerConstructorInit();
  }

  AsyncRTTViewer(const osgViewer::Viewer& viewer, 
                 const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)  
    : osgViewer::Viewer(viewer,copyop) {
    asyncRTTViewerConstructorInit();
  }
    
  virtual ~AsyncRTTViewer() {}

  /// adds a render to texture camera 
  void addOffScreenRTTNode(osg::Node* node){
    offScreenNodes->addChild(node);
  }

  /// removes a render to texture camera 
  void removeOffScreenRTTNode(osg::Node* node){
    offScreenNodes->removeChild(node);  
  }
    
  /** call this function to render the off screen scene.
      If no off screen nodes (RTT) are supplied than nothing is done      
  */
  virtual void renderOffScreen( ) {
    if (_done || offScreenNodes->getNumChildren() == 0) return;
      
    osg::Node* origNode = _camera->getChild(0);
    _camera->setChild(0,offScreenNodes);
    //    printf("before offscreen\n");    
    offScreenRenderingTraversals();
    //printf("after offscreen\n");
    _camera->setChild(0,origNode);
  }

protected:
    

  virtual void offScreenRenderingTraversals() {        
      
    /*** This is copied from ViewerBase::renderingTraversals() and 
         statistics and swapbuffer and so on are removed.       
    */
      
    if (_done) return;
      
    offScreenNodes->getBound();
      
    Contexts contexts;
    getContexts(contexts);
      
    Cameras cameras;
    getCameras(cameras);
      
    Contexts::iterator itr;
      
    bool doneMakeCurrentInThisThread = false;
      
    if (_endDynamicDrawBlock.valid())
      {
        _endDynamicDrawBlock->reset();
      }
      
    // dispatch the rendering threads
    if (_startRenderingBarrier.valid()) _startRenderingBarrier->block();
      
    // reset any double buffer graphics objects
    for(Cameras::iterator camItr = cameras.begin();
        camItr != cameras.end();
        ++camItr)
      {
        osg::Camera* camera = *camItr;
        osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(camera->getRenderer());
        if (renderer)
          {
            if (!renderer->getGraphicsThreadDoesCull() && !(camera->getCameraThread()))
              {
                renderer->cull();
              }
          }
          
      }
      
    for(itr = contexts.begin();
        itr != contexts.end();
        ++itr)
      {
        if (_done) return;
        if (!((*itr)->getGraphicsThread()) && (*itr)->valid())
          {
            doneMakeCurrentInThisThread = true; 
            makeCurrent(*itr);
            (*itr)->runOperations();
          }
      }

    // osg::notify(osg::NOTICE)<<"Joing _endRenderingDispatchBarrier block "<<_endRenderingDispatchBarrier.get()<<std::endl;

    // wait till the rendering dispatch is done.
    if (_endRenderingDispatchBarrier.valid()) _endRenderingDispatchBarrier->block();

    // wait till the dynamic draw is complete.
    if (_endDynamicDrawBlock.valid()) 
      {
        // osg::Timer_t startTick = osg::Timer::instance()->tick();
        _endDynamicDrawBlock->block();
        // osg::notify(osg::NOTICE)<<"Time waiting "<<osg::Timer::instance()->delta_m(startTick, osg::Timer::instance()->tick())<<std::endl;;
      }
    
    if (_releaseContextAtEndOfFrameHint && doneMakeCurrentInThisThread)
      {
        //osg::notify(osg::NOTICE)<<"Doing release context"<<std::endl;
        releaseContext();
      }

  }

  void asyncRTTViewerConstructorInit(){
    offScreenNodes = new osg::Group();
  }
        
  osg::ref_ptr<osg::Group> offScreenNodes;
};




struct MyCameraPostDrawCallback : public osg::Camera::DrawCallback
{
  MyCameraPostDrawCallback(osg::Image* image):
    _image(image)
  {
  }

  virtual void operator () (const osg::Camera& /*camera*/) const
  {
    if (_image && _image->getPixelFormat()==GL_RGBA && _image->getDataType()==GL_UNSIGNED_BYTE)
      {
        printf("hello from image processing\n");
        // we'll pick out the center 1/2 of the whole image,
        int column_start = _image->s()/4;
        int column_end = 3*column_start;
            
        int row_start = _image->t()/4;
        int row_end = 3*row_start;
            
        // and then invert these pixels
        for(int r=row_start; r<row_end; ++r)
          {
            unsigned char* data = _image->data(column_start, r);
            for(int c=column_start; c<column_end; ++c)
              {
                (*data) = 255-(*data); ++data;
                (*data) = 255-(*data); ++data;
                (*data) = 255-(*data); ++data;
                (*data) = 255; ++data;
              }
          }

        // dirty the image (increments the modified count) so that any textures
        // using the image can be informed that they need to update.
        _image->dirty();
      }
       
  }    
  osg::Image* _image;
};


int main(int argc, char** argv)
{

  bool useImage=true;

  // use an ArgumentParser object to manage the program arguments.
  osg::ArgumentParser arguments(&argc,argv);

  arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
  arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");

  //    osgViewer::Viewer viewer(arguments);
  AsyncRTTViewer viewer(arguments);
  viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);

  unsigned int helpType = 0;
  if ((helpType = arguments.readHelpType()))
    {
      arguments.getApplicationUsage()->write(std::cout, helpType);
      return 1;
    }
    
  // report any errors if they have occurred when parsing the program arguments.
  if (arguments.errors())
    {
      arguments.writeErrorMessages(std::cout);
      return 1;
    }
    
  if (arguments.argc()<=1)
    {
      arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
      return 1;
    }

  // set up the camera manipulators.
  {
    osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;

    keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
    keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
    keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
    viewer.setCameraManipulator( keyswitchManipulator.get() );
  }

  // add the state manipulator
  viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );    
  viewer.addEventHandler(new osgViewer::ThreadingHandler);
  viewer.addEventHandler(new osgViewer::WindowSizeHandler);
  viewer.addEventHandler(new osgViewer::StatsHandler);
  viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
  viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);
  viewer.addEventHandler(new osgViewer::LODScaleHandler);    
  viewer.realize();    


  // load the data
  osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
  if (!loadedModel) 
    {
      std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
      return 1;
    }

  // we have a root object that contains a scene and some overlayed textures to show
  // the content if a independently rendered texture
  osg::Group* root = new osg::Group();
  osg::Group* scene = new osg::Group();
  root->addChild(scene);
  scene->addChild(loadedModel.get());
  // any option left unread are converted into errors to write out later.
  arguments.reportRemainingOptionsAsUnrecognized();

  // optimize the scene graph, remove redundant nodes and state etc.
  osgUtil::Optimizer optimizer;
  optimizer.optimize(loadedModel.get());

  viewer.setSceneData(root);    
    
  // Now we create a normal Render to Texture camera 
  // Create the texture to render to
  osg::Texture2D* texture = new osg::Texture2D;
  texture->setTextureSize(256, 256);
  texture->setInternalFormat(GL_RGBA);
  texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
  texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);

  // set up the render to texture camera.
  osg::Camera* cam = new osg::Camera;
  cam->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // set up projection.
  cam->setProjectionMatrixAsPerspective(30, 1,0.1,30);    
  // set view
  cam->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
  cam->setViewport(0, 0, 256, 256);
  // Frame buffer objects are the best option
  cam->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);    
  // We need to render to the texture BEFORE we render to the screen
  cam->setRenderOrder(osg::Camera::PRE_RENDER);    

  if(useImage){
    osg::Image* image = new osg::Image;
    image->allocateImage(256, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE);
    //image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT);
    // attach the image so its copied on each frame.
    cam->attach(osg::Camera::COLOR_BUFFER, image);    
    cam->setPostDrawCallback(new MyCameraPostDrawCallback(image));
    texture->setImage(0, image);
  }else{
    // The camera will render into the texture that we created earlier
    cam->attach(osg::Camera::COLOR_BUFFER, texture);
  }

  // Add world to be drawn to the texture
  cam->addChild(loadedModel.get());

  // now we add the RTT camera to our custom viewer
  viewer.addOffScreenRTTNode(cam);

  // set up the place where the content of the texture is seen in the normal scene
  osg::ref_ptr<osg::Geometry> screenQuad;
  screenQuad = osg::createTexturedQuadGeometry(osg::Vec3(),
                                               osg::Vec3(256, 0.0, 0.0),
                                               osg::Vec3(0.0, 256, 0.0));
  osg::ref_ptr<osg::Geode> quadGeode = new osg::Geode;
  quadGeode->addDrawable(screenQuad.get());
  osg::StateSet *quadState = quadGeode->getOrCreateStateSet();
  quadState->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
  quadState->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);    
  osg::ref_ptr<osg::Camera> orthoCamera = new osg::Camera;
  // We don't want to apply perspective, just overlay using orthographic
  orthoCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 256, 0, 256));    
  orthoCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
  orthoCamera->setViewMatrix(osg::Matrix::identity());
        
  orthoCamera->setViewport(0, 0, 256,256);      
  orthoCamera->setRenderOrder(osg::Camera::POST_RENDER);
  orthoCamera->addChild(quadGeode.get());
  // and add it to the root note
  root->addChild(orthoCamera.get());    

 
  int frame_count=0;  
  const int swap_every=200; // the texture rendering occurs only every 200th frame
  bool swap=true;
  while(!viewer.done())
    {        
      if (swap && 0 == (frame_count % swap_every)) {
        osg::Vec3 eye; osg::Vec3 center; osg::Vec3 up; 
        viewer.getCamera()->getViewMatrixAsLookAt(eye,center,up);
        // simply turn the up vector around
        cam->setViewMatrixAsLookAt(eye, center, -up);           
        // do the offscreen rendering
        viewer.renderOffScreen();     
      }else{
        viewer.frame();
      }
      
      frame_count++;      
    }

  return 0;
}
_______________________________________________
osg-users mailing list
osg-users@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to