Hi all,

I'm hoping someone has a good suggestion of what direction to take this problem.
I've been working on integrating the guts of osgposter into an large existing
application. This application has a "live" graphics window. The osgposter portion is intended to produce a high-resolution snap shot file on demand.

The GUIEventHandler model doesn't work so well in this application, so I managed
to recast the osgposter stages into a 2-pass DrawCallback - 1st pass for setup
and bindCameraToImage, 2nd pass for recordImages and osgDB::writeImageFile).

This is all working fine for the main scene graph.  My problem has to do with
my HUD cameras.  I have a pre-render camera to manage a background image and
2 post render cameras for axes markers and annotations.

I'm changing setRenderOrder for the HUD cameras to NESTED_RENDER to render
to get them to appear at all in the osgposter image. The remaining problem here is that the HUD contents are always the size of a single tile. The image file ends up looking like a sheet of stamps. Also, the size of the HUDs in the "live" graphics window are inadvertently changed to the size of a single tile.

The HUD cameras need the offsetMatrix that is being applied to the poster cameras. Is this something that I should expect a slave camera to do?
Is there some other way to communicate the offsetMatrix to the HUD cameras?

Thanks,

Don Leich


/* OpenSceneGraph example, osgposter.
*
*  Permission is hereby granted, free of charge, to any person obtaining a copy
*  of this software and associated documentation files (the "Software"), to deal
*  in the Software without restriction, including without limitation the rights
*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the Software is
*  furnished to do so, subject to the following conditions:
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*  THE SOFTWARE.
*/



struct PosterDrawCB : public osg::Camera::DrawCallback
{
public:
    typedef std::pair<unsigned int, unsigned int> TilePosition;
    typedef std::map< TilePosition, osg::ref_ptr<osg::Image> > TileImages;

    PosterDrawCB()
    :   _isRunning(false), _isFinished(false),
        _outputPoster(true), _outputPosterName("poster.bmp"),
        _outputTiles(false), _outputTileExt("bmp"),
        _currentRow(0), _currentColumn(0),
        _cameraIndex(0), _cameraRoot(0), _finalPoster(0),
        _pass(0), _written(false)
    {}

    bool getWasWritten() { return _written; }

    inline void setOutputPoster( bool b ) { _outputPoster= b; }
    inline bool getOutputPoster() const { return _outputPoster; }

    inline void setOutputPosterName( const std::string& name ) { 
_outputPosterName = name; }
    inline const std::string& getOutputPosterName() const { return 
_outputPosterName; }

    inline void setOutputTiles( bool b ) { _outputTiles = b; }
    inline bool getOutputTiles() const { return _outputTiles; }

    inline void setOutputTileExtension( const std::string& ext ) { 
_outputTileExt = ext; }
    inline const std::string& getOutputTileExtension() const { return 
_outputTileExt; }

    inline void setTileSize( int w, int h ) { _tileSize.set(w, h); }
    inline const osg::Vec2& getTileSize() const { return _tileSize; }

    inline void setPosterSize( int w, int h ) { _posterSize.set(w, h); }
    inline const osg::Vec2& getPosterSize() const { return _posterSize; }

    inline void setCameraRoot( osg::Group* root ) { _cameraRoot = root; }
    inline const osg::Group* getCameraRoot() const { return _cameraRoot.get(); }

    inline void setFinalPoster( osg::Image* image ) { _finalPoster = image; }
    inline const osg::Image* getFinalPoster() const { return 
_finalPoster.get(); }


    virtual void operator () (osg::RenderInfo& renderInfo) const
    {
        osg::Camera* camera = renderInfo.getCurrentCamera();

        switch ( _pass++ )
        {
            case 0:
            {
                startPosterRecording( camera );

                osg::Camera* subCamera = 0;
                while ( (subCamera=getAvailableCamera())!=NULL )
                {
                    std::cout << "Binding sub-camera " << _currentRow 
                        << "_" << _currentColumn
                        << " to image..." << std::endl;
                    bindCameraToImage( subCamera, _currentRow, _currentColumn );
                    if ( _currentColumn<_tileColumns-1 )
                        _currentColumn++;
                    else
                    {
                        if ( _currentRow<_tileRows-1 )
                        {
                            _currentRow++;
                            _currentColumn = 0;
                        }
                        else
                        {
                            _isRunning = false;
                            _isFinished = true;
                            std::cout << "Sub-cameras dispatching finished." << 
std::endl;
                            break;
                        }
                    }
 
                }
                _cameraIndex = _cameraRoot->getNumChildren();
                break;
            }
            case 1:
            {
                cout << "recordImages"<<endl;
                // Record images and unref them to free memory
                recordImages();
                _isFinished = false;

                if ( _outputPoster )
                {
                    std::cout << "Writing final result to file..." << std::endl;
                    _written = osgDB::writeImageFile( *_finalPoster, 
_outputPosterName );
                    std::cout << "Writing done." << std::endl;
                }
                break;
            }
        }
    }

protected:
    void startPosterRecording( osg::Camera *camera ) const
    {

        if ( !_isRunning && _cameraRoot.valid() )
        {
            cout << "startPosterRecording" << endl;
            _tileRows = (int)(_posterSize.y() / _tileSize.y());
            _tileColumns = (int)(_posterSize.x() / _tileSize.x());
            _currentRow = 0;
            _currentColumn = 0;
            _cameraIndex = _cameraRoot->getNumChildren();
            _currentViewMatrix = camera->getViewMatrix();
            _currentProjectionMatrix = camera->getProjectionMatrix();
            _images.clear();

            _isRunning = true;
            _isFinished = false;

            if ( _outputPoster )
            {
                _finalPoster = new osg::Image;
                _finalPoster->allocateImage( (int)(_posterSize.x()), 
                        (int)(_posterSize.y()),
                        1, GL_RGBA, GL_UNSIGNED_BYTE );
            }
        }
    }

    osg::Camera* getAvailableCamera() const
    {
        // Find an available camera for rendering current tile image.
        if ( !_cameraIndex || !_cameraRoot.valid() ) return NULL;
        return dynamic_cast<osg::Camera*>( 
_cameraRoot->getChild(--_cameraIndex) );
    }

    void bindCameraToImage( osg::Camera* camera, int row, int col ) const
    {
        std::stringstream stream;
        stream << "image_" << row << "_" << col;

        osg::ref_ptr<osg::Image> image = new osg::Image;
        image->setName( stream.str() );
        image->allocateImage( (int)_tileSize.x(), (int)_tileSize.y(), 
                1, GL_RGBA, GL_UNSIGNED_BYTE );
        _images[TilePosition(row,col)] = image.get();

        // Calculate projection matrix offset of each tile
        osg::Matrix offsetMatrix =
            osg::Matrix::scale(_tileColumns, _tileRows, 1.0) *
            osg::Matrix::translate(_tileColumns-1-2*col, _tileRows-1-2*row, 
0.0);
        camera->setViewMatrix( _currentViewMatrix );
        camera->setProjectionMatrix( _currentProjectionMatrix * offsetMatrix );

        // Reattach cameras and new allocated images
        // FIXME: Uses for reattaching camera with image, maybe inefficient?
        camera->setRenderingCache( NULL );  
        camera->detach( osg::Camera::COLOR_BUFFER );
        // camera->attach( osg::Camera::COLOR_BUFFER, image.get(), 0, 0 );
        camera->attach( osg::Camera::COLOR_BUFFER, image.get(), 8, 8 );  // 
multi-sampling
    }

    void recordImages() const
    {
        for ( TileImages::iterator itr=_images.begin(); itr!=_images.end(); 
++itr )
        {
            osg::Image* image = (itr->second).get();
            if ( _finalPoster.valid() )
            {
                unsigned int row = itr->first.first, col = itr->first.second;
                for ( int t=0; t<image->t(); ++t )
                {
                    unsigned char* source = image->data(0, t);
                    unsigned char* target = 
_finalPoster->data(col*(int)_tileSize.x(), 
                                                t + row*(int)_tileSize.y());
                    memcpy(target, source, image->s() * 4 * sizeof(unsigned 
char));
                }
            }

            if ( _outputTiles )
                osgDB::writeImageFile( *image, 
image->getName()+"."+_outputTileExt );
        }
        _images.clear();
    }

    mutable bool _isRunning;
    mutable bool _isFinished;

    bool _outputPoster;
    std::string _outputPosterName;

    bool _outputTiles;
    std::string _outputTileExt;

    osg::Vec2 _tileSize;
    osg::Vec2 _posterSize;

    mutable int _tileRows;
    mutable int _tileColumns;

    mutable int _currentRow;
    mutable int _currentColumn;
    mutable unsigned int _cameraIndex;

    mutable osg::Matrixd _currentViewMatrix;
    mutable osg::Matrixd _currentProjectionMatrix;

    osg::ref_ptr<osg::Group> _cameraRoot;
    mutable osg::ref_ptr<osg::Image> _finalPoster;
    mutable TileImages _images;
    mutable int _pass;
    mutable bool _written;
};


osg::ref_ptr< PosterDrawCB > setupPoster( const std::string& filename )
{
    bool outputPoster = true, outputTiles = false;
    int tileWidth = 640, tileHeight = 512;
    // int tileWidth = 320, tileHeight = 256;
    int posterWidth = 1280, posterHeight = 1024;
    int numCameras = 4;
    // int numCameras = 16;
    osg::Camera::RenderTargetImplementation renderImplementation = 
                        osg::Camera::FRAME_BUFFER_OBJECT;


    // Create cameras for rendering tiles offscreen. 
    // FrameBuffer is recommended because it requires less memory.
    osg::ref_ptr<osg::Group> cameraRoot = new osg::Group;
    for ( int i=0; i<numCameras; ++i )
    {
        osg::ref_ptr<osg::Camera> camera = new osg::Camera;

        camera->setClearColor( gfx_getScene()->getClearColor() );
        camera->setClearMask( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        camera->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
        camera->setRenderOrder( osg::Camera::PRE_RENDER );
        camera->setRenderTargetImplementation( renderImplementation );
        camera->setViewport( 0, 0, tileWidth, tileHeight );

        camera->addChild( gfx_getScene() );

        cameraRoot->addChild( camera.get() );
    }
    gfx_getRoot()->addChild( cameraRoot.get() );

    osg::ref_ptr< PosterDrawCB > cb = new PosterDrawCB;
    cb->setOutputPosterName( filename );
    cb->setTileSize( tileWidth, tileHeight );
    cb->setPosterSize( posterWidth, posterHeight );
    cb->setCameraRoot( cameraRoot.get() );

// TEST
    cb->setOutputTiles( true );

    return cb.get();
}

boolean gfx_scenePosterImageFile( char *filename )
{

    osg::ref_ptr< PosterDrawCB > posterCB = setupPoster( filename );

    // Change render order settings.
    // When enabled, get a full HUD per tile in the print file.  
    // When disabled, no HUD in the print file.
    
gfx_getScene()->getBGImageProjection()->setRenderOrder(osg::CameraNode::NESTED_RENDER);
    gfx_getScene()->getHUD2D()->setRenderOrder(osg::CameraNode::NESTED_RENDER);
    gfx_getScene()->getHUD3D()->setRenderOrder(osg::CameraNode::NESTED_RENDER);

    // This comment from osgposter:
    // Every "copy-to-image" process seems to be finished in 2 frames.

    // 1st pass
    gfx_getViewer()->getGraphicsView()->getCamera()->setInitialDrawCallback( 
                        posterCB.get() ); 
    gfx_getViewer()->frame();
    gfx_getViewer()->getGraphicsView()->getCamera()->setInitialDrawCallback( 
NULL );

    // 2nd pass
    gfx_getViewer()->getGraphicsView()->getCamera()->setFinalDrawCallback( 
                        posterCB.get() ); 
    gfx_getViewer()->frame();
    gfx_getViewer()->getGraphicsView()->getCamera()->setFinalDrawCallback( NULL 
);

    // Change render order settings back.
    
gfx_getScene()->getBGImageProjection()->setRenderOrder(osg::CameraNode::PRE_RENDER);
    gfx_getScene()->getHUD2D()->setRenderOrder(osg::CameraNode::POST_RENDER);
    gfx_getScene()->getHUD3D()->setRenderOrder(osg::CameraNode::POST_RENDER);

    return posterCB->getWasWritten();
}

_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to