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