Chapter Objectives
After reading this chapter, you'll be able to do the following:
Add 2D text annotations to a scene
Add 3D text to a scene, using a variety of customized profiles and fonts
This chapter describes the use of 2D and 3D text. Inventor's 2D text provides you with a simple, quick method for annotating your graphics. For greater embellishment and flexibility, use 3D text, which offers you a wide range of possibilities for shaping the profiles of 3D fonts. Key concepts introduced in this chapter include justification, spacing, font type and size, and profiles. Although the topic of NURBS curves and surfaces is mentioned, that subject is explained fully in Chapter 8.
The first part of this chapter focuses on 2D text and introduces certain concepts common to both 2D and 3D text, such as justification, spacing, and font type and size. The second part of the chapter describes the use of 3D text. The main additional concept in the use of 3D text is defining the cross-sectional profile for the text. You can create profiles that are straight, curved, or a combination of the two.
The text node, SoText2, defines text strings that are rendered as 2D screen-aligned text. Just as other shape nodes cause their shape to be drawn when encountered during rendering traversal, an SoText2 node causes text to be drawn, using the current values for font and color. Text attributes used by SoText2 are specified in the SoFont node. These attributes include font type and point size. Two-dimensional text does not scale in size according to changes in distance from the camera.
SoText2 has the following fields:
The text origin is positioned at (0, 0, 0), transformed by the current geometric transformation. Text is drawn relative to the text origin, according to the specified justification. For example, if you specify RIGHT justification, the right side of the text aligns with the text origin.
Use the SoFont node to specify a font type and size for subsequent text nodes (both 2D and 3D) in the scene graph. This node contains the following fields:
For example, to specify 140-point Courier bold italic:
SoFont *font = new SoFont; font->name.setValue("Courier-BoldOblique"); font->size.setValue(140); |
Example 6-1 renders a globe and uses 2D text to label the continents Africa and Asia. The SoFont node specifies 24-point Times Roman as the current font. Figure 6-1 shows the scene graph for this example. “Simple Text” shows the image produced by this program.
#include <Inventor/nodes/SoFont.h> #include <Inventor/nodes/SoGroup.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoSphere.h> #include <Inventor/nodes/SoText2.h> #include <Inventor/nodes/SoTexture2.h> #include <Inventor/nodes/SoTranslation.h> #include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> main(int argc, char **argv) { Widget myWindow = SoXt::init(argv[0]); if(myWindow == NULL) exit(1); SoGroup *root = new SoGroup; root->ref(); // Choose a font. SoFont *myFont = new SoFont; myFont->name.setValue("Times-Roman"); myFont->size.setValue(24.0); root->addChild(myFont); // Add the globe, a sphere with a texture map. // Put it within a separator. SoSeparator *sphereSep = new SoSeparator; SoTexture2 *myTexture2 = new SoTexture2; root->addChild(sphereSep); sphereSep->addChild(myTexture2); sphereSep->addChild(new SoSphere); myTexture2->filename = "globe.rgb"; // Add Text2 for AFRICA, translated to proper location. SoSeparator *africaSep = new SoSeparator; SoTranslation *africaTranslate = new SoTranslation; SoText2 *africaText = new SoText2; africaTranslate->translation.setValue(.25,.0,1.25); africaText->string = "AFRICA"; root->addChild(africaSep); africaSep->addChild(africaTranslate); africaSep->addChild(africaText); |
// Add Text2 for ASIA, translated to proper location. SoSeparator *asiaSep = new SoSeparator; SoTranslation *asiaTranslate = new SoTranslation; SoText2 *asiaText = new SoText2; asiaTranslate->translation.setValue(.8,.8,0); asiaText->string = "ASIA"; root->addChild(asiaSep); asiaSep->addChild(asiaTranslate); asiaSep->addChild(asiaText); SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); myViewer->setSceneGraph(root); myViewer->setTitle("2D Text"); myViewer->setBackgroundColor(SbColor(0.35, 0.35, 0.35)); myViewer->show(); myViewer->viewAll(); SoXt::show(myWindow); SoXt::mainLoop(); } |
In contrast to 2D text, 3D text scales in size according to changes in distance from the camera and does not always stay parallel to the screen. Three-dimensional text has depth. The face of a 3D letter can join its sides at right angles (the default). Or you can bevel the edges of the letter by specifying your own text profile, as shown at the right of Figure 6-3, which shows a beveled letter A.
The chief advantages of 2D text are that it is faster than 3D text and, because it remains parallel to the screen, is always readable. Advantages of 3D text are that it can be scaled and is generally prettier than 2D text.
SoText3 has the following fields:
Three-dimensional text has three parts: front, sides, and back. Text uses the current material. If material binding is specified as PER_PART, the front uses the first material, the sides use the second material, and the back uses the third material.
Tip: Be aware that when you turn on SIDES and BACK of 3D text, you draw three times more polygons than with FRONT only, so performance is slower. |
The profile describes the cross-section of the letter, as shown in Figure 6-3. The profile is drawn in its own 2D plane. This plane is perpendicular to the face of the text, as shown in Figure 6-4. The origin of this plane is at the edge of the letter. In this coordinate system, capital letters are one unit high. The profile coordinates thus need to be in the range of 0.0 to about 0.3 or 0.4 times the size of the font.
Profiles are constructed from the current profile coordinates. If the profile is a collection of connected straight-line segments, use the SoLinearProfile node to specify how the coordinates are connected. The profile coordinates are specified in an SoProfileCoordinate2 node, which precedes the SoLinearProfile node in the scene graph (see Example 6-3).
If the profile is curved, use the SoNurbsProfile node to specify how the coordinates are used. If you are interested in creating curved profiles, first read Chapter 8 for detailed conceptual information on NURBS curves. The coordinates themselves are specified in the SoProfileCoordinate2 node or the SoProfileCoordinate3 node, depending on whether the curve is nonrational or rational. (The terms nonrational and rational are also explained in Chapter 8.)
If your text profile is a combination of linear and curved lines, you can join the linear profile to the curved profile. The base profile class, SoProfile, includes a linkage field that is inherited by both SoLinearProfile and SoNurbsProfile. This field indicates whether the profile is START_FIRST (begin the first profile for the text), START_NEW (begin a new profile; for NURBS trimming only), or ADD_TO_CURRENT (append this profile to the previous one).
Example 6-2 illustrates a simple use of 3D text. It renders a globe and then uses 3D text to label the continents Africa and Asia. The SoFont node specifies Times Roman as the current font. Figure 6-5 shows the scene graph for this example. “Simple 3D Text Example” shows the image produced by this program.
#include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> #include <Inventor/nodes/SoFont.h> #include <Inventor/nodes/SoGroup.h> #include <Inventor/nodes/SoMaterial.h> #include <Inventor/nodes/SoMaterialBinding.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoSphere.h> #include <Inventor/nodes/SoText3.h> #include <Inventor/nodes/SoTexture2.h> #include <Inventor/nodes/SoTransform.h> main(int, char **argv) { Widget myWindow = SoXt::init(argv[0]); if(myWindow == NULL) exit(1); SoGroup *root = new SoGroup; root->ref(); |
// Choose a font. SoFont *myFont = new SoFont; myFont->name.setValue("Times-Roman"); myFont->size.setValue(.2); root->addChild(myFont); // We'll color the front of the text white, and the sides // dark grey. So use a materialBinding of PER_PART and // two diffuseColor values in the material node. SoMaterial *myMaterial = new SoMaterial; SoMaterialBinding *myBinding = new SoMaterialBinding; myMaterial->diffuseColor.set1Value(0,SbColor(1,1,1)); myMaterial->diffuseColor.set1Value(1,SbColor(.1,.1,.1)); myBinding->value = SoMaterialBinding::PER_PART; root->addChild(myMaterial); root->addChild(myBinding); // Create the globe. SoSeparator *sphereSep = new SoSeparator; SoTexture2 *myTexture2 = new SoTexture2; root->addChild(sphereSep); sphereSep->addChild(myTexture2); sphereSep->addChild(new SoSphere); myTexture2->filename = "globe.rgb"; // Add Text3 for AFRICA, transformed to proper location. SoSeparator *africaSep = new SoSeparator; SoTransform *africaTransform = new SoTransform; SoText3 *africaText = new SoText3; africaTransform->rotation.setValue(SbVec3f(0,1,0),.4); africaTransform->translation.setValue(.25,.0,1.25); africaText->parts = SoText3::ALL; africaText->string = "AFRICA"; root->addChild(africaSep); africaSep->addChild(africaTransform); africaSep->addChild(africaText); // Add Text3 for ASIA, transformed to proper location. SoSeparator *asiaSep = new SoSeparator; SoTransform *asiaTransform = new SoTransform; SoText3 *asiaText = new SoText3; asiaTransform->rotation.setValue(SbVec3f(0,1,0),1.5); asiaTransform->translation.setValue(.8,.6,.5); asiaText->parts = SoText3::ALL; asiaText->string = "ASIA"; root->addChild(asiaSep); asiaSep->addChild(asiaTransform); asiaSep->addChild(asiaText); SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); myViewer->setSceneGraph(root); myViewer->setTitle("3D Text"); myViewer->setBackgroundColor(SbColor(0.35, 0.35, 0.35)); myViewer->show(); myViewer->viewAll(); SoXt::show(myWindow); SoXt::mainLoop(); } |
Example 6-3 illustrates additional features available with 3D text. It specifies a beveled cross-section for the text using the SoProfile-Coordinate2 and SoLinearProfile nodes. The text uses two different materials— one for the front of the text, and one for the back and sides. The font node specifies the Times Roman font. Figure 6-7 shows the scene graph for this figure. “Advanced 3D Text Example” shows the rendered image.
#include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> #include <Inventor/nodes/SoFont.h> #include <Inventor/nodes/SoGroup.h> #include <Inventor/nodes/SoLinearProfile.h> #include <Inventor/nodes/SoMaterial.h> #include <Inventor/nodes/SoMaterialBinding.h> #include <Inventor/nodes/SoPerspectiveCamera.h> #include <Inventor/nodes/SoProfileCoordinate2.h> #include <Inventor/nodes/SoText3.h> main(int argc, char **argv) { Widget myWindow = SoXt::init(argv[0]); if(myWindow == NULL) exit(1); SoGroup *root = new SoGroup; root->ref(); // Set up camera. SoPerspectiveCamera *myCamera = new SoPerspectiveCamera; myCamera->position.setValue(0, -(argc - 1) / 2, 10); myCamera->nearDistance.setValue(5.0); myCamera->farDistance.setValue(15.0); root->addChild(myCamera); |
// Let's make the front of the text white, // and the sides and back yellow. SoMaterial *myMaterial = new SoMaterial; SbColor colors[3]; // diffuse colors[0].setValue(1, 1, 1); colors[1].setValue(1, 1, 0); colors[2].setValue(1, 1, 0); myMaterial->diffuseColor.setValues(0, 3, colors); // specular colors[0].setValue(1, 1, 1); colors[1].setValue(1, 1, 0); colors[2].setValue(1, 1, 0); myMaterial->specularColor.setValues(0, 3, colors); myMaterial->shininess.setValue(.1); root->addChild(myMaterial); // Choose a font. SoFont *myFont = new SoFont; myFont->name.setValue("Times-Roman"); root->addChild(myFont); // Specify a beveled cross-section for the text. SoProfileCoordinate2 *myProfileCoords = new SoProfileCoordinate2; SbVec2f coords[4]; coords[0].setValue(.00, .00); coords[1].setValue(.25, .25); coords[2].setValue(1.25, .25); coords[3].setValue(1.50, .00); myProfileCoords->point.setValues(0, 4, coords); root->addChild(myProfileCoords); SoLinearProfile *myLinearProfile = new SoLinearProfile; long index[4] ; index[0] = 0; index[1] = 1; index[2] = 2; index[3] = 3; myLinearProfile->index.setValues(0, 4, index); root->addChild(myLinearProfile); // Set the material binding to PER_PART. SoMaterialBinding *myMaterialBinding = new SoMaterialBinding; myMaterialBinding-> value.setValue(SoMaterialBinding::PER_PART); root->addChild(myMaterialBinding); // Add the text. SoText3 *myText3 = new SoText3; myText3->string.setValue("Beveled Text"); myText3->justification.setValue(SoText3::CENTER); myText3->parts.setValue(SoText3::ALL); root->addChild(myText3); SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); myViewer->setSceneGraph(root); myViewer->setTitle("Complex 3D Text"); myViewer->show(); myViewer->viewAll(); SoXt::show(myWindow); SoXt::mainLoop(); } |