Version 6 (modified by robert, 7 years ago)

--

osgText, HUD, RenderBins

(Wiki editing note: Add link to complete source code at bottom)

Goals

Add text to a scene - both HUD style text and text as part of the scene.

Overview

The Text class is derived from the Drawable class. This means that text instances can be added to Geode class instances and rendered as other geometry. A complete list of the methods associated with the Text class is listed *here*. The project 'osgExample Text' demonstrates many of these methods. This tutorial provides a sample of limited functionality of the Text class. Drawing a HUD involves two main concepts:

  1. Creating a subtree that has as its root the appropriate projection and model view matrices and...
  2. Assigning the geometry in the HUD subtree to the appropriate RenderBin? so that the HUD geometry is drawn after the rest of the scene with the correct state settings.

The subtree to render a HUD involves a a projection matrix and model view matrix. For the projection matrix we'll use an orthographic projection with a horizontal and vertical extent equivalent to the screen dimensions. Following this scheme, coordinates will equate to pixel coordinates. To keep things straightforward, the use the identity matrix for the model view matrix.
To render the HUD, we'll assign the geometry in it to a specific RenderBin. RenderBins allow users to specify the order in which geometry is drawn. This is helpful since the HUD geometry needs to be drawn last.

The code

First, declare the variables we will need - the 'osg::Text' and 'osg::Projection'.

       osg::Group* root = NULL;
       osg::Node* tankNode = NULL;
       osg::Node* terrainNode = NULL;
       osg::PositionAttitudeTransform* tankXform;

       // A geometry node for our HUD:
       osg::Geode* HUDGeode = new osg::Geode();
       // Text instance that wil show up in the HUD:
       osgText::Text* textOne = new osgText::Text();
       // Text instance for a label that will follow the tank:
       osgText::Text* tankLabel = new osgText::Text();
       // Projection node for defining view frustrum for HUD:
       osg::Projection* HUDProjectionMatrix = new osg::Projection;

Load the models from file and setup the scene graph structure as in the previous tutorials. (Nothing new here.)

       // Initialize root of scene:
       root = new osg::Group();

       osgDB::FilePathList pathList = osgDB::getDataFilePathList();
       pathList.push_back
       ("C:\\Projects\\OpenSceneGraph\\OpenSceneGraph-Data\\NPSData\\Models\\T72-Tank\\");
       pathList.push_back
       ("C:\\Projects\\OpenSceneGraph\\OpenSceneGraph-Data\\NPSData\\Models\\JoeDirt\\");
       pathList.push_back
       ("C:\\Projects\\OpenSceneGraph\\OpenSceneGraph-Data\\NPSData\\Textures\\");
       osgDB::setDataFilePathList(pathList);

       // Load models from files and assign to nodes:
       tankNode = osgDB::readNodeFile("t72-tank_des.flt");
       terrainNode = osgDB::readNodeFile("JoeDirt.flt");

       // Initialize transform to be used for positioning the tank
       tankXform = new osg::PositionAttitudeTransform());
       tankXform->setPosition( osg::Vec3d(5,5,8) );

       // Build the scene - add the terrain node directly to the root,
       // connect the tank node to the root via the transform node:
       root->addChild(terrainNode);
       root->addChild(tankXform);
       tankXform->addChild(tankNode);

Next, set up the scene to display the HUD components. Add a subtree that has as its root a projection and model view matrix.

       // Initialize the projection matrix for viewing everything we
       // will add as descendants of this node. Use screen coordinates
       // to define the horizontal and vertical extent of the projection
       // matrix. Positions described under this node will equate to
       // pixel coordinates.
       HUDProjectionMatrix->setMatrix(osg::Matrix::ortho2D(0,1024,0,768));
      
       // For the HUD model view matrix use an identity matrix:
       osg::MatrixTransform* HUDModelViewMatrix = new osg::MatrixTransform;
       HUDModelViewMatrix->setMatrix(osg::Matrix::identity());

       // Make sure the model view matrix is not affected by any transforms
       // above it in the scene graph:
       HUDModelViewMatrix->setReferenceFrame(osg::Transform::ABSOLUTE_RF);

       // Add the HUD projection matrix as a child of the root node
       // and the HUD model view matrix as a child of the projection matrix
       // Anything under this node will be viewed using this projection matrix
       // and positioned with this model view matrix.
       root->addChild(HUDProjectionMatrix);
       HUDProjectionMatrix->addChild(HUDModelViewMatrix);

Now for setting up the geometry. Here we build a quad that is aligned with screen coordinates and set up its color and texture parameters.

       // Add the Geometry node to contain HUD geometry as a child of the
       // HUD model view matrix.
       HUDModelViewMatrix->addChild( HUDGeode );

       // Set up geometry for the HUD and add it to the HUD
       osg::Geometry* HUDBackgroundGeometry = new osg::Geometry();

       osg::Vec3Array* HUDBackgroundVertices = new osg::Vec3Array;
       HUDBackgroundVertices->push_back( osg::Vec3( 0,    0,-1) );
       HUDBackgroundVertices->push_back( osg::Vec3(1024,  0,-1) );
       HUDBackgroundVertices->push_back( osg::Vec3(1024,200,-1) );
       HUDBackgroundVertices->push_back( osg::Vec3(   0,200,-1) );

       osg::DrawElementsUInt* HUDBackgroundIndices =
          new osg::DrawElementsUInt(osg::PrimitiveSet::POLYGON, 0);
       HUDBackgroundIndices->push_back(0);
       HUDBackgroundIndices->push_back(1);
       HUDBackgroundIndices->push_back(2);
       HUDBackgroundIndices->push_back(3);

       osg::Vec4Array* HUDcolors = new osg::Vec4Array;
       HUDcolors->push_back(osg::Vec4(0.8f,0.8f,0.8f,0.8f));

       osg::Vec2Array* texcoords = new osg::Vec2Array(4);
       (*texcoords)[0].set(0.0f,0.0f);
       (*texcoords)[1].set(1.0f,0.0f);
       (*texcoords)[2].set(1.0f,1.0f);
       (*texcoords)[3].set(0.0f,1.0f);

       HUDBackgroundGeometry->setTexCoordArray(0,texcoords);
       osg::Texture2D* HUDTexture = new osg::Texture2D;
       HUDTexture->setDataVariance(osg::Object::DYNAMIC);
       osg::Image* hudImage;
       hudImage = osgDB::readImageFile("HUDBack2.tga");
       HUDTexture->setImage(hudImage);
       osg::Vec3Array* HUDnormals = new osg::Vec3Array;
       HUDnormals->push_back(osg::Vec3(0.0f,0.0f,1.0f));
       HUDBackgroundGeometry->setNormalArray(HUDnormals);
       HUDBackgroundGeometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
       HUDBackgroundGeometry->addPrimitiveSet(HUDBackgroundIndices);
       HUDBackgroundGeometry->setVertexArray(HUDBackgroundVertices);
       HUDBackgroundGeometry->setColorArray(HUDcolors);
       HUDBackgroundGeometry->setColorBinding(osg::Geometry::BIND_OVERALL);

       HUDGeode->addDrawable(HUDBackgroundGeometry);

To render the HUD correctly we need create an osg::StateSet with depth testing disabled (always draw) and alpha blending enabled (for a transparent HUD.) We also need to make sure the HUD geometry is drawn last. Render order can be controlled by specifying a numbered render bin to load the geometry into doing the cull traversal. The last line shows how:

       // Create and set up a state set using the texture from above:
       osg::StateSet* HUDStateSet = new osg::StateSet();
       HUDGeode->setStateSet(HUDStateSet);
       HUDStateSet->
          setTextureAttributeAndModes(0,HUDTexture,osg::StateAttribute::ON);

       // For this state set, turn blending on (so alpha texture looks right)
       HUDStateSet->setMode(GL_BLEND,osg::StateAttribute::ON);

       // Disable depth testing so geometry is draw regardless of depth values
       // of geometry already draw.
       HUDStateSet->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
       HUDStateSet->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );

       // Need to make sure this geometry is draw last. RenderBins are handled
       // in numerical order so set bin number to 11
       HUDStateSet->setRenderBinDetails( 11, "RenderBin");

Finally for working with text. Since the osg::Text class is derived from osg::Drawable, osg::Text instances can be added as a children of osg::Geode class instances.

       // Add the text (Text class is derived from drawable) to the geode:
       HUDGeode->addDrawable( textOne );

       // Set up the parameters for the text we'll add to the HUD:
       textOne->setCharacterSize(25);
       textOne->setFont("C:/WINDOWS/Fonts/impact.ttf");
       textOne->setText("Not so good");
       textOne->setAxisAlignment(osgText::Text::SCREEN);
       textOne->setPosition( osg::Vec3(360,165,-1.5) );
       textOne->setColor( osg::Vec4(199, 77, 15, 1) );

       // Declare a geode to contain the tank's text label:
       osg::Geode* tankLabelGeode = new osg::Geode();

       // Add the tank label to the scene:
       tankLabelGeode->addDrawable(tankLabel);
       tankXform->addChild(tankLabelGeode);

       // Set up the parameters for the text label for the tank
       // align text with tank's SCREEN.
       // (for Onder: use XZ_PLANE to align text with tank's XZ plane.)
       tankLabel->setCharacterSize(5);
       tankLabel->setFont("/fonts/arial.ttf");
       tankLabel->setText("Tank #1");
       tankLabel->setAxisAlignment(osgText::Text::XZ_PLANE);

       // Set the text to render with alignment anchor and bounding box around it:
       tankLabel->setDrawMode(osgText::Text::TEXT |
          osgText::Text::ALIGNMENT |
          osgText::Text::BOUNDINGBOX);
       tankLabel->setAlignment(osgText::Text::CENTER_TOP);
       tankLabel->setPosition( osg::Vec3(0,0,8) );
       tankLabel->setColor( osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f) );

Lastly, setup the viewer and enter a simulation loop:

       osgViewer::Viewer viewer;

       viewer.setSceneData( root );

       return viewer.run();

Continue with tutorial Finding nodes, DOFs and Switches?

Attachments