fixed: tess shader source typos (thanks to M. Weiler for the report)
[opensg.git] / Examples / Simple / hiresimage.cpp
blob6df067b085672c9c36cba578671ac49c70754fcf
1 // OpenSG example: HiResImage.cpp
2 //
3 // This example shows how to create a high resolution screen shot from an OpenSG
4 // window.
5 // Two independent solutions are provided. The first one uses a GrabForeground
6 // object and the second one a FrameBufferObject (FBO) for image generation.
7 //
8 // This example installs a tiled rendering mechanism which allows the generation
9 // of really huge images. The resulting hires image is not stored in the main
10 // memory but is written to disk in blocks.
12 // Specifically demonstrated is the use of the following classes:
13 // - GrabForeground
14 // - FrameBufferObject,
15 // - RenderBuffer
16 // - SimpleStage
17 // - TileCameraDecorator
18 // - TileableBackground
19 // - VisitSubTree
21 // This examples a model file provided on the command line or a torus if no file
22 // is provided.
24 // User interface:
25 // a) mouse => standard navigator
26 // b) keyboard =>
27 // '1': take a high resolution snapshot with the GrabForeground class
28 // '2': take a high resolution snapshot using the FBO technique
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string>
34 #include <sstream>
35 #include <fstream>
36 #include <vector>
37 #include <boost/tuple/tuple.hpp>
39 #ifdef OSG_BUILD_ACTIVE
40 // Headers
41 #include <OSGGLUT.h>
42 #include <OSGConfig.h>
43 #include <OSGSimpleGeometry.h>
44 #include <OSGGLUTWindow.h>
45 #include <OSGGradientBackground.h>
46 #include <OSGGrabForeground.h>
47 #include <OSGSimpleSceneManager.h>
48 #include <OSGSceneFileHandler.h>
49 #include <OSGAction.h>
50 #include <OSGFrameBufferObject.h>
51 #include <OSGRenderBuffer.h>
52 #include <OSGSimpleStage.h>
53 #include <OSGPassiveViewport.h>
54 #include <OSGTileCameraDecorator.h>
55 #include <OSGTileableBackground.h>
56 #include <OSGVisitSubTree.h>
57 #include <OSGImage.h>
58 #else
59 // Headers
60 #include <OpenSG/OSGGLUT.h>
61 #include <OpenSG/OSGConfig.h>
62 #include <OpenSG/OSGSimpleGeometry.h>
63 #include <OpenSG/OSGGLUTWindow.h>
64 #include <OpenSG/OSGGradientBackground.h>
65 #include <OpenSG/OSGGrabForeground.h>
66 #include <OpenSG/OSGSimpleSceneManager.h>
67 #include <OpenSG/OSGSceneFileHandler.h>
68 #include <OpenSG/OSGAction.h>
69 #include <OpenSG/OSGFrameBufferObject.h>
70 #include <OpenSG/OSGRenderBuffer.h>
71 #include <OpenSG/OSGSimpleStage.h>
72 #include <OpenSG/OSGPassiveViewport.h>
73 #include <OpenSG/OSGTileCameraDecorator.h>
74 #include <OpenSG/OSGTileableBackground.h>
75 #include <OpenSG/OSGVisitSubTree.h>
76 #include <OpenSG/OSGImage.h>
77 #endif
79 OSG_USING_NAMESPACE; // just for convenience but not recommended
82 // function forward declarations
84 static void cleanup(void);
85 static void display(void);
86 static void reshape(int w, int h);
87 static void mouse(int button, int state, int x, int y);
88 static void motion(int x, int y);
89 static void keyboard(unsigned char k, int, int);
90 static void writeHiResScreenShot(const char* name, UInt32 width, UInt32 height);
91 static void writeHiResScreenShotFBO(const char* name, UInt32 width, UInt32 height);
92 static bool writePNMImagesHeader(const std::vector<ImageUnrecPtr>& vecImages, UInt32 width, UInt32 height, std::ostream& out);
93 static bool writePNMImagesData(const std::vector<ImageUnrecPtr>& vecImages, std::ostream& out);
94 static int setupGLUT(int *argc, char *argv[]);
95 static int doMain(int argc, char *argv[]);
98 // global state of example
100 SimpleSceneManagerRefPtr mgr;
101 NodeRefPtr scene;
102 GLUTWindowRefPtr win;
103 const char* output_file = "grabforeground_image.ppm";
104 const char* output_file_fbo = "fbo_image.ppm";
107 // Size factors of the output image with respect to the window dimensions.
108 // The actual image size does account for the aspect ratio of the window.
110 float fw = 2.5f; // multiplication factor for the window width
111 float fh = 2.3f; // multiplication factor for the window height
113 static void cleanup(void)
115 mgr = NULL;
117 scene = NULL;
118 win = NULL;
121 static void display(void)
123 commitChanges();
124 mgr->redraw();
127 static void reshape(int w, int h)
129 mgr->resize(w,h);
130 glutPostRedisplay();
133 static void mouse(int button, int state, int x, int y)
135 if (state)
136 mgr->mouseButtonRelease(button, x, y);
137 else
138 mgr->mouseButtonPress(button, x, y);
140 glutPostRedisplay();
143 static void motion(int x, int y)
145 mgr->mouseMove(x, y);
146 glutPostRedisplay();
149 static void keyboard(unsigned char k, int, int)
151 UInt32 winWidth = win->getWidth();
152 UInt32 winHeight = win->getHeight();
154 UInt32 width = fw * winWidth;
155 UInt32 height = fh * winHeight;
157 switch(k)
159 case 27:
160 case 'q':
161 case 'Q':
163 cleanup();
164 osgExit();
166 std::exit(EXIT_SUCCESS);
168 break;
170 case '1':
172 std::cout << "Creating screenshot (Grab FG) "
173 << width << "x" << height << " in '" << output_file
174 << std::endl;
175 writeHiResScreenShot(output_file, width, height);
177 break;
178 case '2':
180 std::cout << "Creating screenshot (FBO) "
181 << width << "x" << height << " in '" << output_file_fbo
182 << std::endl;
183 writeHiResScreenShotFBO(output_file_fbo, width, height);
185 break;
187 glutPostRedisplay();
191 // initialize GLUT
193 static int setupGLUT(int *argc, char *argv[])
195 glutInit(argc, argv);
197 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL | GLUT_DOUBLE);
199 int winid = glutCreateWindow("OpenSG");
201 glutReshapeFunc(reshape);
202 glutDisplayFunc(display);
203 glutIdleFunc(display);
204 glutMouseFunc(mouse);
205 glutMotionFunc(motion);
206 glutKeyboardFunc(keyboard);
208 return winid;
212 // setup scene
214 static int doMain(int argc, char *argv[])
216 preloadSharedObject("OSGFileIO");
217 preloadSharedObject("OSGImageFileIO");
219 osgInit(argc,argv);
221 int winid = setupGLUT(&argc, argv);
223 win = GLUTWindow::create();
224 win->setGlutId(winid);
225 win->init();
227 if(argc < 2)
229 FWARNING(("No file given!\n"));
230 FWARNING(("Supported file formats:\n"));
232 std::list<const char*> suffixes;
233 SceneFileHandler::the()->getSuffixList(suffixes);
235 for(std::list<const char*>::iterator it = suffixes.begin();
236 it != suffixes.end();
237 ++it)
239 FWARNING(("%s\n", *it));
242 scene = makeTorus(.5, 2, 16, 16);
244 else
246 scene = SceneFileHandler::the()->read(argv[1]);
249 commitChanges();
251 mgr = SimpleSceneManager::create();
253 mgr->setWindow(win);
254 mgr->setRoot (scene);
256 GradientBackgroundUnrecPtr background = GradientBackground::create();
257 background->addLine(Color3f(0,0,0), 0);
258 background->addLine(Color3f(1,1,1), 1);
260 Viewport* viewport = win->getPort(0);
261 viewport->setBackground(background);
263 mgr->showAll();
265 return 0;
269 // main entry point
271 int main(int argc, char *argv[])
273 int ret = doMain(argc, argv);
275 glutMainLoop();
277 cleanup();
279 osgExit();
281 return ret;
285 // scan for root node
287 static Node* rootNode(Node* node)
289 Node* root = NULL;
290 while (node) {
291 root = node;
292 node = node->getParent();
294 return root;
298 // GrabForeground based solution
300 static void writeHiResScreenShot(
301 const char* name,
302 UInt32 width,
303 UInt32 height)
305 size_t num_ports = win->getMFPort()->size();
306 if (num_ports == 0)
307 return;
309 // calc image dimensions
311 UInt32 winWidth = win->getWidth();
312 UInt32 winHeight = win->getHeight();
314 if (width < winWidth ) width = winWidth;
315 if (height < winHeight) height = winHeight;
317 Real32 a = Real32(winWidth) / Real32(winHeight);
318 width = UInt32(a*height);
321 // output stream for writing the final image
323 std::ofstream stream(name, std::ios::binary);
325 if (stream.good() == false)
326 return;
329 // Tile image used for foreground grabbing
331 ImageUnrecPtr grab_image = Image::create();
333 GrabForegroundUnrecPtr grabber = GrabForeground::create();
334 grabber->setImage (grab_image);
335 grabber->setActive (true);
336 grabber->setAutoResize(false);
339 // We tile the final image and render each tile with the screen resolution
340 // into the window. The more tiles we use the bigger the resolution of the
341 // final image gets with respect to a provided measure of length.
343 typedef boost::tuple<TileCameraDecoratorUnrecPtr, bool> TupleT;
344 std::vector<TupleT> decorators;
345 decorators.resize(num_ports);
348 // Setup the tile camera decorators for each viewport of the window and
349 // disable the tile property of tileable viewport backgrounds.
351 for (size_t i = 0; i < num_ports; ++i) {
352 Viewport* vp = win->getPort(i);
354 TileCameraDecoratorUnrecPtr decorator = TileCameraDecorator::create();
356 decorator->setFullSize (width, height);
357 decorator->setDecoratee(vp->getCamera());
359 vp->setCamera(decorator);
361 bool bTiled = false;
362 TileableBackground* tbg = dynamic_cast<TileableBackground*>(vp->getBackground());
363 if (tbg) {
364 bTiled = tbg->getTile();
365 tbg->setTile(false);
369 // remember the decorator and the background tile prop setting
371 decorators[i] = boost::make_tuple(decorator, bTiled);
375 // Add the grabber to the forgrounds of the first viewport
377 Viewport* vp0 = win->getPort(0);
378 vp0->addForeground(grabber);
381 // We write the image in simple ppm format. This one starts with a description
382 // header which we output once on first write.
384 bool write_header = true;
387 // Calc the max y start position (width). We process the tiles from bottom
388 // up and from left tp right as determined by the image format.
390 UInt32 yPosLast = 0;
391 for (; yPosLast < height-winHeight; yPosLast += winHeight);
394 // Process from bottom to top
396 for (Int32 yPos = yPosLast; yPos >= 0; yPos -= winHeight)
398 UInt32 ySize = std::min(winHeight, height - yPos);
401 // Collect the tile images for each row, i.e. we write the
402 // image in row manner to disk. This way the main memory is
403 // only moderately stressed.
405 std::vector<ImageUnrecPtr> vecColImages;
408 // Process from left to right
410 for (UInt32 xPos = 0; xPos < width; xPos += winWidth)
412 UInt32 xSize = std::min(winWidth, width - xPos);
414 // The current tile image
416 ImageUnrecPtr col_image = Image::create();
417 col_image->set(Image::OSG_RGBA_PF, xSize, ySize);
419 // Adapt the tile camera decorator boxes to the current tile
421 for (size_t i = 0; i < num_ports; ++i) {
422 Viewport* vp = win->getPort(i);
423 vp->setSize(0, 0, xSize, ySize);
425 TileCameraDecorator* decorator = decorators[i].get<0>();
427 decorator->setSize( xPos / float(width),
428 yPos / float(height),
429 (xPos + xSize) / float(width),
430 (yPos + ySize) / float(height) );
433 // Adapt the grabber image size to the current tile dimension
435 grab_image->set(Image::OSG_RGBA_PF, xSize, ySize);
437 // render the tile
439 mgr->redraw();
441 // Copy the image into the tile image stored for later processing
443 col_image->setSubData(0, 0, 0, xSize, ySize, 1, grabber->getImage()->getData());
445 vecColImages.push_back(col_image);
449 // Write the image format header once
451 if (write_header) {
452 write_header = false;
453 if (!writePNMImagesHeader(vecColImages, width, height, stream)) break;
456 // Write the current column
458 if (!writePNMImagesData(vecColImages, stream)) break;
460 // Forget the current column images
462 vecColImages.clear();
466 // restore window and cleanup
468 vp0->removeObjFromForegrounds(grabber);
470 for (size_t i = 0; i < num_ports; ++i) {
471 Viewport* vp = win->getPort(i);
473 vp->setCamera(decorators[i].get<0>()->getDecoratee());
474 vp->setSize(0, 0, 1, 1);
476 TileableBackground* tbg = dynamic_cast<TileableBackground*>(vp->getBackground());
477 if (tbg)
478 tbg->setTile(decorators[i].get<1>());
483 // FBO solution
485 static void writeHiResScreenShotFBO(const char* name, UInt32 width, UInt32 height)
487 size_t num_ports = win->getMFPort()->size();
488 if (num_ports == 0)
489 return;
491 // calc image dimensions
493 UInt32 winWidth = win->getWidth();
494 UInt32 winHeight = win->getHeight();
496 if (width < winWidth ) width = winWidth;
497 if (height < winHeight) height = winHeight;
499 Real32 a = Real32(winWidth) / Real32(winHeight);
500 width = UInt32(a*height);
503 // output stream for writing the final image
505 std::ofstream stream(name, std::ios::binary);
506 if (stream.good() == false)
507 return;
510 // Setup the FBO
512 FrameBufferObjectUnrecPtr fbo = FrameBufferObject::create();
514 // We use two render buffers. One for the color buffer and one for the depth and
515 // stencil buffer. This example does not take credit of the stencil buffer. There-
516 // fore a depth buffer would suffice. However, the use of the combined depth and
517 // stencil buffer is useful in other contextes and hence used.
519 RenderBufferUnrecPtr colBuf = RenderBuffer::create();
520 RenderBufferUnrecPtr dsBuf = RenderBuffer::create();
522 // As we would like to read back the FBO color buffer, we must provide a fitting
523 // image.
525 ImageUnrecPtr buffer_image = Image::create();
526 buffer_image->set(Image::OSG_RGBA_PF, winWidth, winHeight);
527 colBuf->setImage(buffer_image);
529 // We must setup the internal image formats of the two render buffers accordingly.
531 colBuf->setInternalFormat(GL_RGBA);
532 dsBuf ->setInternalFormat(GL_DEPTH24_STENCIL8_EXT);
534 // we must inform the FBO about the actual used color render buffers.
536 fbo->editMFDrawBuffers()->push_back(GL_COLOR_ATTACHMENT0_EXT);
538 // The FBO takes responsibility of the render buffers. Notice, that the shared
539 // depth/stencil buffer is provided twice. As the depth render buffer and as the
540 // stencil render buffer.
542 fbo->setColorAttachment (colBuf, 0);
543 fbo->setDepthAttachment (dsBuf);
544 fbo->setStencilAttachment(dsBuf);
546 // Also the FBO must be sized correctly.
548 fbo->setWidth (winWidth );
549 fbo->setHeight(winHeight);
551 // In order to read the color buffer back next two statements are necessary.
553 fbo->setPostProcessOnDeactivate(true);
554 fbo->getColorAttachments(0)->setReadBack(true);
557 // We tile the final image and render each tile with the screen resolution
558 // into the FBO. The more tiles we use the bigger the resolution of the
559 // final image gets with respect to a provided measure of length.
561 typedef boost::tuple<TileCameraDecoratorUnrecPtr, bool, SimpleStageUnrecPtr, ViewportUnrecPtr> TupleT;
562 std::vector<TupleT> decorators;
563 decorators.resize(num_ports);
566 // Remember the stage viewports for later cleanup
568 std::stack<ViewportUnrecPtr> stage_viewports;
571 // Setup the tile camera decorators for each viewport of the window and
572 // disable the tile property of tileable viewport backgrounds.
574 for (size_t i = 0; i < num_ports; ++i) {
575 Viewport* vp = win->getPort(i);
577 TileCameraDecoratorUnrecPtr decorator = TileCameraDecorator::create();
579 decorator->setFullSize (width, height);
580 decorator->setDecoratee(vp->getCamera());
582 vp->setCamera(decorator);
584 bool bTiled = false;
585 TileableBackground* tbg = dynamic_cast<TileableBackground*>(vp->getBackground());
586 if (tbg) {
587 bTiled = tbg->getTile();
588 tbg->setTile(false);
592 // The scene manager root node does not provide the illumination of the
593 // scene. This is governed internally by the manager. However, to take
594 // credit of the illumination we scan to the final parent of the scene
595 // graph.
597 Node* internalRoot = rootNode(mgr->getRoot());
599 // We would like to render the scene but won't detach it from its parent.
600 // The VisitSubTree allows just that.
602 VisitSubTreeUnrecPtr visitor = VisitSubTree::create();
603 visitor->setSubTreeRoot(internalRoot);
604 NodeUnrecPtr visit_node = makeNodeFor(visitor);
606 // We clone the camera of the first viewport and do not swap the buffer on later
607 // rendering. This way the image generation process is not noticable in the
608 // window.
610 CameraUnrecPtr camera = dynamic_pointer_cast<Camera>(vp->getCamera()->shallowCopy());
612 // The stage object does provide a render target for the frame buffer attachment.
613 // SimpleStage has a camera, a background and the left, right, top, bottom
614 // fields to let you restrict rendering to a sub-rectangle of your FBO, i.e.
615 // they give you a viewport.
617 SimpleStageUnrecPtr stage = SimpleStage::create();
618 stage->setRenderTarget(fbo);
619 stage->setCamera (decorator);
620 stage->setBackground (vp->getBackground());
622 // Give the stage core a place to live
624 NodeUnrecPtr stage_node = makeNodeFor(stage);
625 stage_node->addChild(visit_node);
627 // root
628 // |
629 // +- SimpleStage
630 // |
631 // +- VisitSubTree -> ApplicationScene
633 NodeUnrecPtr root = makeCoredNode<Group>();
634 root->addChild(stage_node);
636 // Give the root node a place to live, i.e. create a passive
637 // viewport and add it to the window.
639 ViewportUnrecPtr stage_viewport = PassiveViewport::create();
640 stage_viewport->setRoot (root);
641 stage_viewport->setBackground(vp->getBackground());
642 stage_viewport->setCamera (camera);
644 win->addPort(stage_viewport);
647 // remember the decorator, the background tile prop setting and the stage setup
649 decorators[i] = boost::make_tuple(decorator, bTiled, stage, stage_viewport);
653 // We write the image in simple ppm format. This one starts with a description
654 // header which we output once on first write.
656 bool write_header = true;
659 // Calc the max y start position (width). We process the tiles from bottom
660 // up and from left tp right as determined by the image format.
662 UInt32 yPosLast = 0;
663 for (; yPosLast < height-winHeight; yPosLast += winHeight);
666 // Process from bottom to top
668 for (Int32 yPos = yPosLast; yPos >= 0; yPos -= winHeight)
670 UInt32 ySize = std::min(winHeight, height - yPos);
673 // Collect the tile images for each row, i.e. we write the
674 // image in row manner to disk. This way the main memory is
675 // only moderately stressed.
677 std::vector<ImageUnrecPtr> vecColImages;
680 // Process from left to right
682 for (UInt32 xPos = 0; xPos < width; xPos += winWidth)
684 UInt32 xSize = std::min(winWidth, width - xPos);
686 // The current tile image
688 ImageUnrecPtr col_image = Image::create();
689 col_image->set(Image::OSG_RGBA_PF, xSize, ySize);
691 // Adapt the tile camera decorator boxes to the current tile
693 for (size_t i = 0; i < num_ports; ++i)
696 // this tile does not fill the whole FBO - adjust to only render
697 // to a part of it
699 decorators[i].get<2>()->setLeft (0.f);
700 decorators[i].get<2>()->setRight (xSize / float(winWidth));
701 decorators[i].get<2>()->setBottom(0.f);
702 decorators[i].get<2>()->setTop (ySize / float(winHeight));
704 TileCameraDecorator* decorator = decorators[i].get<0>();
706 decorator->setSize( xPos / float(width),
707 yPos / float(height),
708 (xPos + xSize) / float(width),
709 (yPos + ySize) / float(height) );
713 // render the tile
715 mgr->update();
716 win->renderNoFinish(mgr->getRenderAction());
717 win->frameExit();
718 win->deactivate ();
721 // Copy the image into the tile image stored for later processing
723 if(fbo)
725 RenderBuffer* grabber = dynamic_cast<RenderBuffer*>(fbo->getColorAttachments(0));
727 if(grabber)
729 grabber->getImage()->subImage(0, 0, 0, xSize, ySize, 1, col_image);
733 vecColImages.push_back(col_image);
737 // Write the image format header once
739 if (write_header) {
740 write_header = false;
741 if (!writePNMImagesHeader(vecColImages, width, height, stream)) break;
744 // Write the current column
746 if (!writePNMImagesData(vecColImages, stream)) break;
748 // Forget the current column images
750 vecColImages.clear();
754 // restore window and cleanup
756 for (size_t i = 0; i < num_ports; ++i) {
757 win->subPortByObj(decorators[i].get<3>());
759 Viewport* vp = win->getPort(i);
760 vp->setCamera(decorators[i].get<0>()->getDecoratee());
761 vp->setSize(0, 0, 1, 1);
763 TileableBackground* tbg = dynamic_cast<TileableBackground*>(vp->getBackground());
764 if (tbg)
765 tbg->setTile(decorators[i].get<1>());
770 // We write the image in PNM format (Portable Any Map).
771 // see http://netpbm.sourceforge.net/doc/ppm.html
772 // or http://en.wikipedia.org/wiki/Netpbm_format
774 static bool writePNMImagesHeader(
775 const std::vector<ImageUnrecPtr>& vecImages,
776 UInt32 width, UInt32 height,
777 std::ostream &out)
779 if (vecImages.empty()) return false;
781 ImageUnrecPtr col_image = vecImages[0];
783 UInt16 bpp = col_image->getBpp();
785 switch(bpp)
787 case 1:
788 case 2:
789 return false;
790 case 3:
791 case 4:
792 out << "P6" << std::endl;
793 break;
796 out << "# PNMImageFileType write" << std::endl;
797 out << width << " " << height << std::endl;
798 out << "255" << std::endl;
800 return true;
804 // Write tile images column wise
806 static bool writePNMImagesData(
807 const std::vector<ImageUnrecPtr>& vecImages,
808 std::ostream &out)
810 if (vecImages.empty()) return false;
812 ImageUnrecPtr first = vecImages[0];
814 UInt16 bpp = first->getBpp();
815 Int16 width = first->getWidth();
816 Int16 height = first->getHeight();
818 std::size_t num_images = vecImages.size();
820 for(Int16 y = height - 1; y >= 0; y--)
822 for (std::size_t img_idx = 0; img_idx < num_images; img_idx++)
824 ImageUnrecPtr image = vecImages[img_idx];
826 width = image->getWidth();
827 height = image->getHeight();
829 Int16 lineSize = bpp * width;
831 const UInt8* data = (image->getData() + (lineSize * y));
833 for(Int16 x = 0; x < width; x++)
835 for(Int16 p = bpp - 1; p--;)
836 out << *data++;
838 data++;
842 return true;