Hello Johannes,
On 10/04/2010 07:42 AM, Johannes Brunen wrote:
1. If I use a gradient background it is not properly tiled (fbo_image*.png).
that is caused by the stage not passing on all tiling information to its
DrawEnv - fixed in r2489.
2. If I use sizes (factors fw and fh) which are not multiple of the window
size I get strange results (fbo_image.png).
this happens when you copy from the FBO image to col_image. The FBO
image is always the same size, but the target image is sometimes
smaller. However, Image::setSubData assumes that the source image has
the given size and writes to a rectangle of the target image - you can
use Image::subImage instead to copy a rectangle from a source image to a
destination image of the correct size.
3. Sometimes strange results on resizeng the GLUT window before actually
generating the image (fbo_image1.png).
hm, I've tried this a bunch of times, but I always got correct images,
perhaps fixing the above also fixed this?
Cheers,
Carsten
// OpenSG example: HiResImage.cpp
//
// This example shows how to create a high resolution screen shot from an OpenSG
// window.
// Two independent solution are provided. The first one does use a GrabForeground
// object and the second one a FrameBufferObject (FBO) for image generation.
//
// This example installs a tiled rendering mechanism which allows the generation
// of really huge images. The resulting hires image is not stored in the main
// memory but it is assembled on an extern disk.
//
// Especially demonstrated is the use of the following classes:
// - GrabForeground
// - FrameBufferObject,
// - RenderBuffer
// - SimpleStage
// - TileCameraDecorator
// - TileableBackground
// - VisitSubTree
//
// This examples renders a torus or if no model file is provided at the command
// line.
//
// User interface:
// a) mouse => standard navigator
// b) keyboard =>
// '1': take a high resolution snapshot with the GrabForeground class
// '2': take a high resolution snapshot using the FBO technique
//
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#ifdef OSG_BUILD_ACTIVE
// Headers
#include <OSGGLUT.h>
#include <OSGConfig.h>
#include <OSGSimpleGeometry.h>
#include <OSGGLUTWindow.h>
#include <OSGGradientBackground.h>
#include <OSGGrabForeground.h>
#include <OSGSimpleSceneManager.h>
#include <OSGSceneFileHandler.h>
#include <OSGAction.h>
#include <OSGFrameBufferObject.h>
#include <OSGRenderBuffer.h>
#include <OSGSimpleStage.h>
#include <OSGPassiveViewport.h>
#include <OSGTileCameraDecorator.h>
#include <OSGTileableBackground.h>
#include <OSGVisitSubTree.h>
#else
// Headers
#include <OpenSG/OSGGLUT.h>
#include <OpenSG/OSGConfig.h>
#include <OpenSG/OSGSimpleGeometry.h>
#include <OpenSG/OSGGLUTWindow.h>
#include <OpenSG/OSGGradientBackground.h>
#include <OpenSG/OSGGrabForeground.h>
#include <OpenSG/OSGSimpleSceneManager.h>
#include <OpenSG/OSGSceneFileHandler.h>
#include <OpenSG/OSGAction.h>
#include <OpenSG/OSGFrameBufferObject.h>
#include <OpenSG/OSGRenderBuffer.h>
#include <OpenSG/OSGSimpleStage.h>
#include <OpenSG/OSGPassiveViewport.h>
#include <OpenSG/OSGTileCameraDecorator.h>
#include <OpenSG/OSGTileableBackground.h>
#include <OpenSG/OSGVisitSubTree.h>
#endif
using namespace OSG; // just for convenience but not recommended
//
// function forward declarations
//
static void cleanup(void);
static void display(void);
static void reshape(int w, int h);
static void mouse(int button, int state, int x, int y);
static void motion(int x, int y);
static void keyboard(unsigned char k, int, int);
static void writeHiResScreenShot(const char* name, UInt32 width, UInt32 height);
static void writeHiResScreenShotFBO(const char* name, UInt32 width, UInt32 height);
static bool writePNMImagesHeader(const std::vector<ImageUnrecPtr>& vecImages, UInt32 width, UInt32 height, std::ostream& out);
static bool writePNMImagesData(const std::vector<ImageUnrecPtr>& vecImages, std::ostream& out);
static int setupGLUT(int *argc, char *argv[]);
static int doMain(int argc, char *argv[]);
//
// global state of example
//
SimpleSceneManager* mgr;
NodeRefPtr scene;
GLUTWindowRefPtr win;
const char* output_file = "grabforeground_image.ppm";
const char* output_file_fbo = "fbo_image.ppm";
//
// Size factors of the output image with respect to the window dimensions.
// The actual image size does account for the aspect ratio of the window.
//
float fw = 2.3; // multiplication factor for the window width
float fh = 4.7; // multiplication factor for the window height
static void cleanup(void)
{
delete mgr;
mgr = NULL;
scene = NULL;
}
static void display(void)
{
commitChanges();
mgr->redraw();
}
static void reshape(int w, int h)
{
mgr->resize(w,h);
glutPostRedisplay();
}
static void mouse(int button, int state, int x, int y)
{
if (state)
mgr->mouseButtonRelease(button, x, y);
else
mgr->mouseButtonPress(button, x, y);
glutPostRedisplay();
}
static void motion(int x, int y)
{
mgr->mouseMove(x, y);
glutPostRedisplay();
}
static void keyboard(unsigned char k, int, int)
{
UInt32 winWidth = win->getWidth();
UInt32 winHeight = win->getHeight();
UInt32 width = fw * winWidth;
UInt32 height = fh * winHeight;
switch(k)
{
case '1':
{
writeHiResScreenShot(output_file, width, height);
}
break;
case '2':
{
writeHiResScreenShotFBO(output_file_fbo, width, height);
}
break;
}
glutPostRedisplay();
}
//
// initialize GLUT
//
static int setupGLUT(int *argc, char *argv[])
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL | GLUT_DOUBLE);
int winid = glutCreateWindow("OpenSG");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutIdleFunc(display);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutKeyboardFunc(keyboard);
return winid;
}
//
// setup scene
//
static int doMain(int argc, char *argv[])
{
preloadSharedObject("OSGFileIO");
preloadSharedObject("OSGImageFileIO");
preloadSharedObject("OSGContribPLY");
osgInit(argc,argv);
int winid = setupGLUT(&argc, argv);
win = GLUTWindow::create();
win->setGlutId(winid);
win->init();
if(argc < 2)
{
FWARNING(("No file given!\n"));
FWARNING(("Supported file formats:\n"));
std::list<const char*> suffixes;
SceneFileHandler::the()->getSuffixList(suffixes);
for(std::list<const char*>::iterator it = suffixes.begin();
it != suffixes.end();
++it)
{
FWARNING(("%s\n", *it));
}
scene = makeTorus(.5, 2, 16, 16);
}
else
{
scene = SceneFileHandler::the()->read(argv[1]);
}
commitChanges();
mgr = new SimpleSceneManager;
mgr->setWindow(win);
mgr->setRoot (scene);
GradientBackgroundUnrecPtr background = GradientBackground::create();
background->addLine(Color3f(0,0,0), 0);
background->addLine(Color3f(1,1,1), 1);
Viewport* viewport = win->getPort(0);
viewport->setBackground(background);
mgr->showAll();
return 0;
}
//
// main entry point
//
int main(int argc, char *argv[])
{
int ret = doMain(argc, argv);
glutMainLoop();
cleanup();
osgExit();
return ret;
}
//
// scan for root node
//
static Node* rootNode(Node* node)
{
Node* root = NULL;
while (node) {
root = node;
node = node->getParent();
}
return root;
}
//
// GrabForeground based solution
//
static void writeHiResScreenShot(
const char* name,
UInt32 width,
UInt32 height)
{
size_t num_ports = win->getMFPort()->size();
if (num_ports == 0)
return;
//
// calc image dimensions
//
UInt32 winWidth = win->getWidth();
UInt32 winHeight = win->getHeight();
if (width < winWidth ) width = winWidth;
if (height < winHeight) height = winHeight;
Real32 a = Real32(winWidth) / Real32(winHeight);
width = UInt32(a*height);
//
// output stream for writing the final image
//
std::ofstream stream(name, std::ios::binary);
if (stream.good() == false)
return;
//
// Tile image used for foreground grabbing
//
ImageUnrecPtr grab_image = Image::create();
GrabForegroundUnrecPtr grabber = GrabForeground::create();
grabber->setImage (grab_image);
grabber->setActive (true);
grabber->setAutoResize(false);
//
// We tile the final image and render each tile with the screen resolution
// into the window. The more tiles we use the bigger the resolution of the
// final image gets with respect to a provided measure of length.
//
typedef std::pair<TileCameraDecoratorUnrecPtr, bool> PairT;
std::vector<PairT> decorators;
decorators.resize(num_ports);
//
// Setup the tile camera decorators for each viewport of the window and
// disable the tile property of tileable viewport backgrounds.
//
for (size_t i = 0; i < num_ports; ++i) {
Viewport* vp = win->getPort(i);
TileCameraDecoratorUnrecPtr decorator = TileCameraDecorator::create();
decorator->setFullSize (width, height);
decorator->setDecoratee(vp->getCamera());
vp->setCamera(decorator);
bool bTiled = false;
TileableBackground* tbg = dynamic_cast<TileableBackground*>(vp->getBackground());
if (tbg) {
bTiled = tbg->getTile();
tbg->setTile(false);
}
//
// remember the decorator and the background tile prop setting
//
decorators[i] = std::make_pair(decorator, bTiled);
}
//
// Add the grabber to the forgrounds of the first viewport
//
Viewport* vp0 = win->getPort(0);
vp0->addForeground(grabber);
//
// We write the image in simple ppm format. This one starts with a description
// header which we output once on first write.
//
bool write_header = true;
//
// Calc the max y start position (width). We process the tiles from bottom
// up and from left tp right as determined by the image format.
//
Int32 yPosLast = 0;
for (; yPosLast < height-winHeight; yPosLast += winHeight);
//
// Process from bottom to top
//
for (Int32 yPos = yPosLast; yPos >= 0; yPos -= winHeight)
{
UInt32 ySize = std::min(winHeight, height - yPos);
//
// Collect the tile images for each row, i.e. we write the
// image in row manner to disk. This way the main memory is
// only moderately stressed.
//
std::vector<ImageUnrecPtr> vecColImages;
//
// Process from left to right
//
for (UInt32 xPos = 0; xPos < width; xPos += winWidth)
{
UInt32 xSize = std::min(winWidth, width - xPos);
//
// The current tile image
//
ImageUnrecPtr col_image = Image::create();
col_image->set(Image::OSG_RGBA_PF, xSize, ySize);
//
// Adapt the tile camera decorator boxes to the current tile
//
for (size_t i = 0; i < num_ports; ++i) {
Viewport* vp = win->getPort(i);
vp->setSize(0, 0, xSize, ySize);
TileCameraDecorator* decorator = decorators[i].first;
decorator->setSize( xPos / float(width),
yPos / float(height),
(xPos + xSize) / float(width),
(yPos + ySize) / float(height) );
}
//
// Adapt the grabber image size to the current tile dimension
//
grab_image->set(Image::OSG_RGBA_PF, xSize, ySize);
//
// render the tile
//
mgr->redraw();
//
// Copy the image into the tile image stored for later processing
//
col_image->setSubData(0, 0, 0, xSize, ySize, 1, grabber->getImage()->getData());
vecColImages.push_back(col_image);
}
//
// Write the image format header once
//
if (write_header) {
write_header = false;
if (!writePNMImagesHeader(vecColImages, width, height, stream)) break;
}
//
// Write the current column
//
if (!writePNMImagesData(vecColImages, stream)) break;
//
// Forget the current column images
//
vecColImages.clear();
}
//
// restore window and cleanup
//
vp0->removeObjFromForegrounds(grabber);
for (size_t i = 0; i < num_ports; ++i) {
Viewport* vp = win->getPort(i);
vp->setCamera(decorators[i].first->getDecoratee());
vp->setSize(0, 0, 1, 1);
TileableBackground* tbg = dynamic_cast<TileableBackground*>(vp->getBackground());
if (tbg)
tbg->setTile(decorators[i].second);
}
}
//
// FBO solution
//
static void writeHiResScreenShotFBO(const char* name, UInt32 width, UInt32 height)
{
size_t num_ports = win->getMFPort()->size();
if (num_ports == 0)
return;
//
// calc image dimensions
//
UInt32 winWidth = win->getWidth();
UInt32 winHeight = win->getHeight();
if (width < winWidth ) width = winWidth;
if (height < winHeight) height = winHeight;
Real32 a = Real32(winWidth) / Real32(winHeight);
width = UInt32(a*height);
//
// output stream for writing the final image
//
std::ofstream stream(name, std::ios::binary);
if (stream.good() == false)
return;
//
// We tile the final image and render each tile with the screen resolution
// into the FBO. The more tiles we use the bigger the resolution of the
// final image gets with respect to a provided measure of length.
//
typedef std::pair<TileCameraDecoratorUnrecPtr, bool> PairT;
std::vector<PairT> decorators;
decorators.resize(num_ports);
//
// Setup the tile camera decorators for each viewport of the window and
// disable the tile property of tileable viewport backgrounds.
//
for (size_t i = 0; i < num_ports; ++i) {
Viewport* vp = win->getPort(i);
TileCameraDecoratorUnrecPtr decorator = TileCameraDecorator::create();
decorator->setFullSize (width, height);
decorator->setDecoratee(vp->getCamera());
vp->setCamera(decorator);
bool bTiled = false;
TileableBackground* tbg = dynamic_cast<TileableBackground*>(vp->getBackground());
if (tbg) {
bTiled = tbg->getTile();
tbg->setTile(false);
}
//
// remember the decorator and the background tile prop setting
//
decorators[i] = std::make_pair(decorator, bTiled);
}
//
// Setup the FBO
//
FrameBufferObjectUnrecPtr fbo = FrameBufferObject::create();
//
// We use two render buffers. One for the color buffer and one for the depth and
// stencil buffer. This example does not take credit of the stencil buffer. There-
// fore a depth buffer would suffice. However, the use of the combined depth and
// stencil buffer is useful in other contextes and hence used.
//
RenderBufferUnrecPtr colBuf = RenderBuffer::create();
RenderBufferUnrecPtr dsBuf = RenderBuffer::create();
//
// As we would like to read back the FBO color buffer, we must provide a fitting
// image.
//
ImageUnrecPtr buffer_image = Image::create();
buffer_image->set(Image::OSG_RGBA_PF, winWidth, winHeight);
colBuf->setImage(buffer_image);
//
// We must setup the internal image formats of the two render buffers accordingly.
//
colBuf->setInternalFormat(GL_RGBA);
dsBuf ->setInternalFormat(GL_DEPTH24_STENCIL8);
//
// we must inform the FBO about the actual used color render buffers.
//
fbo->editMFDrawBuffers()->push_back(GL_COLOR_ATTACHMENT0_EXT);
//
// The FBO takes responsibility of the render buffers. Notice, that the shared
// depth/stencil buffer is provided twice. As the depth render buffer and as the
// stencil render buffer.
//
fbo->setColorAttachment (colBuf, 0);
fbo->setDepthAttachment (dsBuf);
fbo->setStencilAttachment(dsBuf);
//
// Also the FBO must be sized correctly.
//
fbo->setWidth (winWidth );
fbo->setHeight(winHeight);
//
// In order to read the color buffer back next two statements are necessary.
//
fbo->setPostProcessOnDeactivate(true);
fbo->getColorAttachments(0)->setReadBack(true);
//
// The scene manager root node does not provide the illumination of the
// scene. This is governed internally by the manager. However, to take
// credit of the illumination we scan to the final parent of the scene
// graph.
//
Node* internalRoot = rootNode(mgr->getRoot());
//
// We would like to render the scene but won't detach it from its parent.
// The VisitSubTree allows just that.
//
VisitSubTreeUnrecPtr visitor = VisitSubTree::create();
visitor->setSubTreeRoot(internalRoot);
NodeUnrecPtr visit_node = makeNodeFor(visitor);
//
// We clone the camera of the first viewport and do not swap the buffer on later
// rendering. This way the image generation process is not noticable in the
// window.
//
Viewport* vp0 = win->getPort(0);
CameraUnrecPtr camera = dynamic_pointer_cast<Camera>(vp0->getCamera()->shallowCopy());
//
// The stage object does provide a render target for the frame buffer attachment.
// SimpleStage has a camera, a background and the left, right, top, bottom
// fields to let you restrict rendering to a sub-rectangle of your FBO, i.e.
// they give you a viewport.
//
SimpleStageUnrecPtr stage = SimpleStage::create();
stage->setRenderTarget(fbo);
stage->setCamera (decorators[0].first);
stage->setBackground (vp0->getBackground());
//
// Give the stage core a place to live
//
NodeUnrecPtr stage_node = makeNodeFor(stage);
stage_node->addChild(visit_node);
//
// root
// |
// +- SimpleStage
// |
// +- VisitSubTree -> ApplicationScene
//
NodeUnrecPtr root = makeCoredNode<Group>();
root->addChild(stage_node);
//
// Give the root node a place to live, i.e. create a passive
// viewport and add it to the window.
//
ViewportUnrecPtr stage_viewport = PassiveViewport::create();
stage_viewport->setRoot (root);
stage_viewport->setBackground(vp0->getBackground());
stage_viewport->setCamera (camera);
win->addPort(stage_viewport);
//
// We write the image in simple ppm format. This one starts with a description
// header which we output once on first write.
//
bool write_header = true;
//
// Calc the max y start position (width). We process the tiles from bottom
// up and from left tp right as determined by the image format.
//
Int32 yPosLast = 0;
for (; yPosLast < height-winHeight; yPosLast += winHeight);
//
// Process from bottom to top
//
for (Int32 yPos = yPosLast; yPos >= 0; yPos -= winHeight)
{
UInt32 ySize = std::min(winHeight, height - yPos);
//
// Collect the tile images for each row, i.e. we write the
// image in row manner to disk. This way the main memory is
// only moderately stressed.
//
std::vector<ImageUnrecPtr> vecColImages;
//
// Process from left to right
//
for (UInt32 xPos = 0; xPos < width; xPos += winWidth)
{
UInt32 xSize = std::min(winWidth, width - xPos);
//
// The current tile image
//
ImageUnrecPtr col_image = Image::create();
col_image->set(Image::OSG_RGBA_PF, xSize, ySize);
//
// Adapt the tile camera decorator boxes to the current tile
//
for (size_t i = 0; i < num_ports; ++i) {
// this tile does not fill the whole FBO - adjust to only render
// to a part of it
stage->setLeft (0.f);
stage->setRight (xSize / float(winWidth));
stage->setBottom(0.f);
stage->setTop (ySize / float(winHeight));
TileCameraDecorator* decorator = decorators[i].first;
decorator->setSize( xPos / float(width),
yPos / float(height),
(xPos + xSize) / float(width),
(yPos + ySize) / float(height) );
}
//
// render the tile
//
mgr->update();
win->renderNoFinish(mgr->getRenderAction());
win->frameExit();
win->deactivate ();
//
// Copy the image into the tile image stored for later processing
//
if (fbo) {
RenderBuffer* grabber = dynamic_cast<RenderBuffer*>(fbo->getColorAttachments(0));
if (grabber)
{
grabber->getImage()->subImage(0, 0, 0, xSize, ySize, 1, col_image);
}
}
vecColImages.push_back(col_image);
}
//
// Write the image format header once
//
if (write_header) {
write_header = false;
if (!writePNMImagesHeader(vecColImages, width, height, stream)) break;
}
//
// Write the current column
//
if (!writePNMImagesData(vecColImages, stream)) break;
//
// Forget the current column images
//
vecColImages.clear();
}
//
// restore window and cleanup
//
win->subPortByObj(stage_viewport);
for (size_t i = 0; i < num_ports; ++i) {
Viewport* vp = win->getPort(i);
vp->setCamera(decorators[i].first->getDecoratee());
vp->setSize(0, 0, 1, 1);
TileableBackground* tbg = dynamic_cast<TileableBackground*>(vp->getBackground());
if (tbg)
tbg->setTile(decorators[i].second);
}
}
//
// We write the image in PNM format (Portable Any Map).
// see http://netpbm.sourceforge.net/doc/ppm.html
// or http://en.wikipedia.org/wiki/Netpbm_format
//
static bool writePNMImagesHeader(
const std::vector<ImageUnrecPtr>& vecImages,
UInt32 width, UInt32 height,
std::ostream &out)
{
if (vecImages.empty()) return false;
ImageUnrecPtr col_image = vecImages[0];
UInt16 bpp = col_image->getBpp();
switch(bpp)
{
case 1:
case 2:
return false;
case 3:
case 4:
out << "P6" << std::endl;
break;
}
out << "# PNMImageFileType write" << std::endl;
out << width << " " << height << std::endl;
out << "255" << std::endl;
return true;
}
//
// Write tile images column wise
//
static bool writePNMImagesData(
const std::vector<ImageUnrecPtr>& vecImages,
std::ostream &out)
{
if (vecImages.empty()) return false;
ImageUnrecPtr first = vecImages[0];
UInt16 bpp = first->getBpp();
Int16 width = first->getWidth();
Int16 height = first->getHeight();
std::size_t num_images = vecImages.size();
for(Int16 y = height - 1; y >= 0; y--)
{
for (std::size_t img_idx = 0; img_idx < num_images; img_idx++)
{
ImageUnrecPtr image = vecImages[img_idx];
width = image->getWidth();
height = image->getHeight();
Int16 lineSize = bpp * width;
const UInt8* data = (image->getData() + (lineSize * y));
for(Int16 x = 0; x < width; x++)
{
for(Int16 p = bpp - 1; p--;)
out << *data++;
data++;
}
}
}
return true;
}
------------------------------------------------------------------------------
Virtualization is moving to the mainstream and overtaking non-virtualized
environment for deploying applications. Does it make network security
easier or more difficult to achieve? Read this whitepaper to separate the
two and get a better understanding.
http://p.sf.net/sfu/hp-phase2-d2d
_______________________________________________
Opensg-users mailing list
Opensg-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensg-users