To print all or part of an Inventor scene graph, use the SoOffscreen-Renderer class, which in turn uses an SoGLRenderAction SoGLRenderAction SoGLRenderAction to render an image into an off-screen memory buffer. This rendering buffer can be used both to generate an image to send to a PostScript printer (see Example 13.1, “ Printing ”) and to generate an image to be used as a texture map (see Example 13.2, “ Generating a Texture Map ”).
The image rendered into the buffer can be one of four component types:
LUMINANCE | one component (grayscale) |
LUMINANCE_TRANSPARENCY | two components (grayscale with alpha value) |
RGB | three components (full color) |
RGB_TRANSPARENCY | four components (full color with alpha value) |
Use the setComponents() method of SoOffscreenRenderer to specify the components in the image generated before you render the image. To print black and white, use LUMINANCE. To print color, use RGB. To generate images with transparency information, use LUMINANCE_TRANSPARENCY or RGB_TRANSPARENCY.
![]() | |
Tip: If you want the output to go directly to a printer, use the SoXtPrintDialog , an Xt component. See the Open Inventor C++ Reference Manual for more information. |
To write a scene graph to a file in Encapsulated PostScript (EPS) format, you first render the scene with the off-screen renderer. Then you use the writeToPostScript() method to generate the PostScript output and write it to the given file.
For example, suppose you want to print a screen area that is 300 pixels by 400 pixels. Use the setWindowSize() method on SbViewportRegion SbViewportRegion to specify the size of the viewport to be printed:
SbViewportRegion vp; vp.setWindowSize(SbVec2s(300, 400)); rootNode = getMyScene(); SoOffscreenRenderer renderer(vp); renderer->render(rootNode); renderer->writeToPostScript(stdout);
SbViewportRegion vp = new SbViewportRegion(); vp.SetWindowSize(new SbVec2s(300, 400)); SoOffscreenRenderer renderer = new SoOffscreenRenderer(vp); renderer.Render(rootNode); renderer.WriteToPostScript("fileName.ps", new SbVec2f(8.0f, 10.0f));
SbViewportRegion vp = new SbViewportRegion(); vp.setWindowSize(new SbVec2s((short)300, (short)400)); SoOffscreenRenderer renderer = new SoOffscreenRenderer(vp); renderer.render(rootNode); renderer.writeToPostScript("fileName.ps", new SbVec2f(8.0f, 10.0f));
This code fragment assumes the default pixels per inch (approximately 72). To change the number of pixels per inch, use the setPixelsPerInch() method on SbViewportRegion SbViewportRegion . Typically, you use the resolution of the printer. For a 300 dots-per-inch (DPI) printer, you would specify the following:
vp.setPixelsPerInch(300);
vp.SetPixelsPerInch(300);
vp.setPixelsPerInch(300);
This resolution affects line width, the size of 2D text, and point size, which are all specified in pixels.
You may want the printed image to be the same size as the image rendered on the screen. To determine the size of the image on the screen, first use the getViewportSizePixels() method on SbViewportRegion SbViewportRegion to obtain the number of pixels (in x and y) of the viewport region. Then use the getScreenPixelsPerInch() method onSoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer to find out the screen resolution in pixels.
screenVp = renderArea->getViewportRegion(); SbVec2s screenSize = screenVp.getViewportSizePixels(); float screenPixelsPerInch = SoOffscreenRenderer::getScreenPixelsPerInch();
screenVp = renderArea.GetViewportRegion(); SbVec2s screenSize = screenVp.ViewportSizePixels; float screenPixelsPerInch = SoOffscreenRenderer.GetScreenPixelsPerInch();
screenVp = renderArea.getViewportRegion(); SbVec2s screenSize = screenVp.getViewportSizePixels(); float screenPixelsPerInch = SoOffscreenRenderer.getScreenPixelsPerInch();
Now you can calculate the size of the screen image in pixels by dividing x and y by screenPixelsPerInch. If you have a 300-by-400-pixel viewport on a screen with a resolution of 100 pixels per inch, your image is 3 by 4 inches.
To print this image at the same size, you specify the following:
vp.setWindowSize(SbVec2s(x_in_inches * printer_DPI, y_in_inches * printer_DPI)); vp.setPixelsPerInch(printer_DPI);
vp.SetWindowSize(new SbVec2s(x_in_inches * printer_DPI, y_in_inches * printer_DPI)); vp.SetPixelsPerInch(printer_DPI);
vp.setWindowSize(new SbVec2s((short)(x_in_inches * printer_DPI), (short)(y_in_inches * printer_DPI))); vp.setPixelsPerInch(printer_DPI);
Your OpenGL implementation may restrict the maximum viewport size. Use getMaximumResolution() to obtain the maximum resolution possible for a viewport in your window system.
Example 13.1, “ Printing ” shows a simple function that renders a given scene graph and then saves it in a file that can be sent to a printer.
Example 13.1. Printing
SbBool printToPostScript (SoNode *root, FILE *file, SoXtExaminerViewer *viewer, int printerDPI) { // Calculate size of the image in inches which is equal to // the size of the viewport in pixels divided by the number // of pixels per inch of the screen device. This size in // inches will be the size of the Postscript image that will // be generated. const SbViewportRegion &vp = viewer->getViewportRegion(); const SbVec2s &imagePixSize = vp.getViewportSizePixels(); SbVec2f imageInches; float pixPerInch; pixPerInch = SoOffscreenRenderer::getScreenPixelsPerInch(); imageInches.setValue((float)imagePixSize[0] / pixPerInch, (float)imagePixSize[1] / pixPerInch); // The resolution to render the scene for the printer // is equal to the size of the image in inches times // the printer DPI; SbVec2s postScriptRes; postScriptRes.setValue((short)(imageInches[0])*printerDPI, (short)(imageInches[1])*printerDPI); // Create a viewport to render the scene into. SbViewportRegion myViewport; myViewport.setWindowSize(postScriptRes); myViewport.setPixelsPerInch((float)printerDPI); // Render the scene SoOffscreenRenderer *myRenderer = new SoOffscreenRenderer(myViewport); if (!myRenderer->render(root)) { delete myRenderer; return FALSE; } // Generate PostScript and write it to the given file myRenderer->writeToPostScript(file); delete myRenderer; return TRUE; }
private static boolean printToFile(String file, SwSimpleViewer viewer, int printerDPI, int type) { // Calculate size of the images in inches which is equal to // the size of the viewport in pixels divided by the number // of pixels per inch of the screen device. This size in // inches will be the size of the Postscript image that will // be generated. SbViewportRegion vp = viewer.getArea().getViewportRegion(); SbVec2s imagePixSize = vp.getViewportSizePixels(); SbVec2f imageInches = new SbVec2f(); float pixPerInch = SoOffscreenRenderer.getScreenPixelsPerInch(); imageInches.setValue((float)imagePixSize.getX() / pixPerInch, (float)imagePixSize.getY() / pixPerInch); // The resolution to render the scene for the printer // is equal to the size of the image in inches times // the printer DPI; SbVec2s postScriptRes = new SbVec2s(); postScriptRes.setValue((short)(imageInches.getX()*printerDPI), (short)(imageInches.getY()*printerDPI)); // Create a viewport to render the scene into. SbViewportRegion myViewport = new SbViewportRegion(); myViewport.setWindowSize(postScriptRes); myViewport.setPixelsPerInch((float)printerDPI); // Render the scene SoOffscreenRenderer myRenderer = new SoOffscreenRenderer(myViewport); myRenderer.setBackgroundColor(new SbColor(.6f, .7f, .9f)); if (!myRenderer.render(viewer.getArea().getSceneManager().getSceneGraph())) return false; // Generate PostScript or BMP and write it to the given file if (type == 0) { try { myRenderer.writeToBMP(file); } catch (UnsupportedOperationException e) { System.err.println(); e.printStackTrace(); return false ; } } else myRenderer.writeToPostScript(file); System.gc(); return true; }
You can also use the off-screen renderer to render an image to be used as a texture map. In this case, use the render() method of SoOffscreenRenderer to render the image. Then use the getBuffer() method to obtain the buffer.
Example 13.2, “ Generating a Texture Map ” shows the typical sequence for using the rendering buffer to generate a texture map.
Example 13.2. Generating a Texture Map
#include <Inventor/SoDB.h> #include <Inventor/SoInput.h> #include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> #include <Inventor/SbViewportRegion.h> #include <Inventor/misc/SoOffscreenRenderer.h> #include <Inventor/nodes/SoCube.h> #include <Inventor/nodes/SoDirectionalLight.h> #include <Inventor/nodes/SoPerspectiveCamera.h> #include <Inventor/nodes/SoRotationXYZ.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoTexture2.h> SbBool generateTextureMap (SoNode *root, SoTexture2 *texture, short textureWidth, short textureHeight) { SbViewportRegion myViewport(textureWidth, textureHeight); // Render the scene SoOffscreenRenderer *myRenderer = new SoOffscreenRenderer(myViewport); myRenderer->setBackgroundColor(SbColor(0.3, 0.3, 0.3)); if (!myRenderer->render(root)) { delete myRenderer; return FALSE; } // Generate the texture texture->image.setValue(SbVec2s(textureWidth, textureHeight), SoOffscreenRenderer::RGB, myRenderer->getBuffer()); delete myRenderer; return TRUE; } main(int, char **argv) { // Initialize Inventor and Xt Widget appWindow = SoXt::init(argv[0]); if (appWindow == NULL) exit(1); // Make a scene from reading in a file SoSeparator *texRoot = new SoSeparator; SoInput in; SoNode *result; texRoot->ref(); in.openFile("jumpyMan.iv"); SoDB::read(&in, result); SoPerspectiveCamera *myCamera = new SoPerspectiveCamera; SoRotationXYZ *rot = new SoRotationXYZ; rot->axis = SoRotationXYZ::X; rot->angle = M_PI_2; myCamera->position.setValue(SbVec3f(-0.2, -0.2, 2.0)); myCamera->scaleHeight(0.4); texRoot->addChild(myCamera); texRoot->addChild(new SoDirectionalLight); texRoot->addChild(rot); texRoot->addChild(result); // Generate the texture map SoTexture2 *texture = new SoTexture2; texture->ref(); if (generateTextureMap(texRoot, texture, 64, 64)) printf ("Successfully generated texture map\n"); else printf ("Could not generate texture map\n"); texRoot->unref(); // Make a scene with a cube and apply the texture to it SoSeparator *root = new SoSeparator; root->ref(); root->addChild(texture); root->addChild(new SoCube); // Initialize an Examiner Viewer SoXtExaminerViewer *viewer = new SoXtExaminerViewer(appWindow); viewer->setSceneGraph(root); viewer->setTitle("Offscreen Rendered Texture"); viewer->show(); SoXt::show(appWindow); SoXt::mainLoop(); }
using OIV.Inventor.Nodes; using OIV.Inventor.Win; using OIV.Inventor.Win.Viewers; using OIV.Inventor; using System.IO; namespace _09_2_Texture { public partial class MainForm : Form { SoWinExaminerViewer viewer; public MainForm() { InitializeComponent(); CreateSample(); } public bool GenerateTextureMap(SoNode root, SoTexture2 texture, short textureWidth, short textureHeight) { SbViewportRegion myViewport = new SbViewportRegion(textureWidth, textureHeight); // Render the scene SoOffscreenRenderer myRenderer = new SoOffscreenRenderer(myViewport); myRenderer.SetBackgroundColor(new SbColor(0.3f, 0.3f, 0.3f)); if (!myRenderer.Render(root)) { return false; } // Copy from Native content SbNativeByteArray nativeData = myRenderer.GetBuffer(SoOffscreenRenderer.BufferTypes.RGB_BUFFER); byte[] data = new byte[nativeData.Length]; for (int i = 0; i < nativeData.Length; i++) data[i] = nativeData[i]; // Generate the texture texture.image.SetValue(new SbVec2s(textureWidth, textureHeight), (int)SoOffscreenRenderer.Components.RGB, data); return true; } public void CreateSample() { // Make a scene from reading in a file SoSeparator texRoot = new SoSeparator(); SoInput input = new SoInput(); SoNode result; string fileName = "../../../../../data/jumpyMan.iv"; if (!File.Exists(fileName)) { Console.Error.WriteLine("File not found:" + fileName); return; } input.OpenFile(fileName); result = SoDB.ReadAll(input); //SoDB.Read(input, out result); SoPerspectiveCamera myCamera = new SoPerspectiveCamera(); SoRotationXYZ rot = new SoRotationXYZ(); rot.axis.SetValue((int)SoRotationXYZ.AxisType.X); rot.angle.SetValue((float)Math.PI / 2f); myCamera.position.SetValue(new SbVec3f(-0.2f, -0.2f, 2.0f)); myCamera.ScaleHeight(0.4f); texRoot.AddChild(myCamera); texRoot.AddChild(new SoDirectionalLight()); texRoot.AddChild(rot); texRoot.AddChild(result); // Generate the texture map SoTexture2 texture = new SoTexture2(); if (GenerateTextureMap(texRoot, texture, 64, 64)) Console.WriteLine("Successfully generated texture map."); else Console.WriteLine("Could not generate texture map.\n"); // Make a scene with a cube and apply the texture to it SoSeparator root = new SoSeparator(); root.AddChild(texture); root.AddChild(new SoCube()); // Initialize an Examiner Viewer viewer = new SoWinExaminerViewer(this, "", true, SoWinFullViewer.BuildFlags.BUILD_ALL, SoWinViewer.Types.BROWSER); viewer.SetSceneGraph(root); viewer.SetTitle("Offscreen Rendered Texture"); viewer.SetDrawStyle(SoWinViewer.DrawTypes.STILL, SoWinViewer.DrawStyles.VIEW_AS_IS); } } }
import tools.*; import com.openinventor.inventor.awt.*; import com.openinventor.inventor.nodes.*; import com.openinventor.inventor.*; import com.openinventor.util.Scene.DrawStyle; import java.awt.*; public class Main extends DemoInventor { public static void main(String[] args) { Main applet = new Main(); applet.isAnApplet = false; applet.start(); demoMain(applet, "Offscreen Rendered Texture"); } public void start() { super.start(); SwSimpleViewer myViewer = new SwSimpleViewer(); // In Inventor 2.1, if the machine does not have hardware texture // mapping, we must override the default drawStyle to display textures. myViewer.getArea().setDrawStyle(SwActiveArea.STILL, DrawStyle.VIEW_AS_IS); setLayout(new BorderLayout()); add(myViewer, BorderLayout.CENTER); // Make a scene from reading in a file SoSeparator texRoot = new SoSeparator(); SoComplexity nodeComplexity = new SoComplexity(); nodeComplexity.textureQuality.setValue(0.0f); SoInput in = new SoInput(); in.openFile(m_prefix + "../../../../data/models/jumpyMan.iv"); SoNode result = SoDB.readNode(in); SoPerspectiveCamera myCamera = new SoPerspectiveCamera(); SoRotationXYZ rot = new SoRotationXYZ(); rot.axis.setValue(SoRotationXYZ.X); rot.angle.setValue((float)java.lang.Math.PI/2.f); myCamera.position.setValue(new SbVec3f( -0.2f, -0.2f, 2.0f)); myCamera.scaleHeight(0.4f); { texRoot.addChild(nodeComplexity); texRoot.addChild(myCamera); texRoot.addChild(new SoDirectionalLight()); texRoot.addChild(rot); texRoot.addChild(result); } // Generate the texture map SoTexture2 texture = new SoTexture2(); if (generateTextureMap(texRoot, texture, (short)64, (short)64)) System.out.println("Successfully generated texture map\n"); else System.out.println("Could not generate texture map\n"); // Make a scene with a cube and apply the texture to it SoSeparator root = new SoSeparator(); { // Assemble scene graph root.addChild(texture); root.addChild(new SoCube()); } myViewer.setSceneGraph(root); } private boolean generateTextureMap(final SoNode root, SoTexture2 texture, short textureWidth, short textureHeight) { SbViewportRegion myViewport = new SbViewportRegion(textureWidth, textureHeight); // Render the scene final SoOffscreenRenderer myRenderer = new SoOffscreenRenderer(myViewport); myRenderer.setBackgroundColor(new SbColor(0.3f, 0.3f, 0.3f)); if ( !myRenderer.render(root) ) return false; byte[] buffer_byte = myRenderer.getBuffer(); // Generate the texture texture.image.setValue(new SbVec2s(textureWidth, textureHeight), myRenderer.getComponents().getValue(), buffer_byte); //SoOffscreenRenderer.RGB, //myRenderer.getBuffer()); return true; } }