Programming Course

Chapter 9

Inventor III - More C++

9.1 More Inventor Nodes and Functions

9.1.1 Lights

Light is important in architecture. Many architects would like to be able to design the light situation as carefully as the solid parts of the building. To do this a computer simulation can be of great help. Three kinds of lights are available in Inventor:

SoPointLight, SoDirectionalLight, and SoSpotLight.

from "Inventor Mentor" published by Addison Wesley

All lights are derived from SoLight and all have variables for:

on 1 = on, 0 = off.

intensity Brightness of the light, values range from 0.0 to 1.0, default is 1.0.

color Color of the light as an rgb value.

SoPointLight has an additional field for:

location a 3D point, default is (1.0 1.0 1.0).

SoDirectionalLight has an additional field for:

direction a 3D vector, default is (0.0, 0.0, -1.0)

SoSpotLight has additional fields for:

location a 3D point, default is (1.0 1.0 1.0).

direction a 3D vector, default is (0.0, 0.0, -1.0)

dropOffRate rate at which the light intensity decreases, 1.0 = immediate drop off, 0.0 = constant intensity, default is 0.0.

cutOffAngle Total angle of light, outside of it the intensity is 0.0, values in radians range from 0 to ¹, default is ¹/4.

For a PointLight the C++ code would look like this:

SoPointLight *mylight = new SoPointLight;
mylight->set("location 0 2.2 0");
root->addChild(mylight);
or
SoPointLight *light = new SoPointLight;
light->location.setValue( 0 2.2 0);
root->addChild(mylight);

9.1.2 Text

Text can be used to display additional information in a Scene. There are two kinds of text available in Inventor: 2D-Text (Text2) and 3D-Text (Text3). A font node has to be placed before the text node in the Scenegraph.

SoFont is used to specify a font type and size for the subsequent 2D or 3D text nodes. This node contains the following fields:

name a font name, (in our environment 'ls /usr/lib/DPS/outline/base' can be used to list the available fonts).

size size in points, default is 10.0.

SoText2 defines text strings that are rendered as 2D screen aligned text. It has the following fields:

string the text to display.

spacing between the lines of text, the default is 1.0.

justification alignment relative to the text origin, values are LEFT (default), RIGHT, or CENTER.

SoText3 In contrast to 2D text 3D text behaves like a 3D object in space, it scales according to the distance from the camera, it does not stay parallel to the screen, and it can have depth. SoText3 has the following fields:

string the text to display.

spacing between the lines of text, the default is 1.0.

justification alignment relative to the text origin, values are LEFT (default), RIGHT, or CENTER.

parts visible parts of the 3D text, values are FRONT (default), SIDES, BACK, ALL. Turning on all parts of Text can make your application very slow, because about three times more polygons have to be drawn.


The following is a code example to produce 2D Text:

SoFont *myfont = new SoFont;
myfont->name.setValue("Helvetica-Bold");
myfont->size.setValue(32.0);
root->addChild(myfont);
SoText2 *mytext = new SoText2;
mytext->string.setValue("This is my text");
root->addChild(mytext);

9.1.3 Callback Functions

Callback functions are a way to add new functionality to Inventor. A callback function is a user written function, that gets called under certain conditions.

An important callback function is the SelectionCallback. It requires a selection node to be placed in the Scenegraph and a function which should be executed. A selection callback is always added to a SoSelection node.

These are the pieces of code necessary for a selection callback:

void mySelectionCB(void *, SoPath *selectionPath)
{
 SoSelection *selected = (SoSelection *)selectionPath->getHead();
 ... // add or modify children of selection
}

void myDeSelectionCB(void *, SoPath *deselectionPath)
{
 SoSelection *selected = (SoSelection *)deselectionPath->getHead();
 ... // remove or reset children of selection
}

SoSelection *myselection = SoSelection;
myselection->policy = SoSelection::SINGLE;
myselection->addSelectionCallback(mySelectionCB);
myselection->addDeselectionCallback(myDeSelectionCB);
root->addChild(myselection);
For your exercises, a callback function has been prepared. In exercise 9 it will execute a prepared callback function. In exercise 10 you will have to write the code, that it should execute. The selection node is already provided, you do not have to write it, just understand it.

9.2 C++, more about Variables and Classes

9.2.1 Characteristics of Variables

Besides of their type variables can have a different scope (global, local), and different kinds of storage behavior(const, static).

global - local

Variables can be defined globally for one file. This is done by declaring them outside of any functions at the beginning of the file after the include statements. All the variables listed here, can be accessed and modified from every function in the program. It is not a good programming style to use a lot of global variables. It makes code hard to read and it is difficult to track the functions that change the global variable's value. A few globals are sometimes inevitable, but it is better to avoid them.

Local variables are the ones defined inside a function. They are not accessible by other functions. The same variable name can be used in different functions without causing conflicts.

const - static

Usually variables are not kept in memory when a function has finished execution, exceptions are constant and static variables.

Constant variables never change their value. Therefore it is a good advice to declare variables, which should never change as const. Because it cannot be changed, a constant variable has to have a value assigned, when it gets declared. For example: A constant variable can be used to remember the time, when a function was called for the first time.

Static variables are permanently stored, when re-entering a function they still have their previous value assigned. For example: Static variables can be used to count how many times a function has been called. Static variables can sometimes be used instead of global ones.

9.2.2 Definition of a Class

Defining a class means to give it a name and to define every method (=function) and field (= variable) of the class. The form is:

class newclass {

 // methods
 // fields
};

Definition of Methods

Methods are declared in the class definition preceded by their return type and in parenthesis the types of the arguments. The constructor method has always the same name as the class and needs no type declaration. The constructor method gets called when a new object is initialized. At least one constructor must be defined. For the same class there can be several constructors with varying arguments.

class newclass { // methods newclass (); void writefun(int, char*); // fields }; After declaring the methods they have to be programmed, the name is always composed from the class name and the method name with two ':' in-between:

newclass::newclass()
{
 // set values
 // do something
 ...
}

void newclass::writefun(int num, char *string)
{
 // do something
 // use the arguments
 ...
}

Declaration of Fields

Fields are variables of an object, they are declared inside the class-definition in the same way as variables are declared in functions. Fields are preceded by their type and if they are pointers an asterisk (*) is placed before the name.

class newclass
{
 // methods
 // fields
 int mysize;
 SoFont *myfont;
};
In every function of a class the fields of the class can be used like variables of the function.
newclass::newclass()
{
 // set default values
 mysize = 12;
 myfont = new SoFont;
 // do something
 myfont->size.setValue(size);
 ...
}

public, private, protected

Methods and fields can have different degrees of accessibility. Some fields inside an object should not get modified directly, or some methods should only be used by the class itself. For example: A room class could have constraints for the minimum size, therefore every time the size gets changed, the object wants to check, if the new value is not too small. This can happen if the change of size happens by using a method of the room class instead of changing the value directly.

public a public member is accessible from anywhere.

private a private member can be accessed only be the functions of its class.

protected a protected member is like a public member to a subclass and like a private member to the rest of the program.

The following example defines a class for adding a string to a scene. The class has two methods, the creator: mynote::mynote(), and a callback method mynote::moveCB():

#include <Inventor/nodes/SoSelection.h>
#include <Inventor/nodes/SoFont.h>
#include <Inventor/nodes/SoText3.h>

class mynote {

 public:
  mynote(char *, SoSeparator *);

 protected:
  static void moveCB(void *, SoPath *);
};

mynote::mynote(char *thenote, SoSeparator *root)
{
 SoSelection *myself = new SoSelection;
 myself->policy = SoSelection::SINGLE; 
 myself->addSelectionCallback(moveCB);
 root->addChild(myself);
 SoFont *myfont = new SoFont;
 myfont->name.setValue("Helvetica-Bold");
 myfont->size.setValue(32.0);
 myself->addChild(myfont);
 SoText3 *mytext = new SoText3;
 mytext->string.setValue(thenote);
 myself->addChild(mytext); 
}

void mynote::moveCB(void *, SoPath *selectionpath)
{
 SoTransform *mytrans = new SoTransform;
 mytrans->translation.setValue(2.0, 2.0, 0.0);
 SoSelection *n = (SoSelection *) selectionpath->getHead();
 n->insertChild(mytrans, 1);
}

main()
{
 ...
 mynote *anote = new mynote("it's great",root);
 ...
}

9.3 Exercise 9 - Create an Object

The goal is to get used to the commands of a C++-based language and to gain insight into object-oriented programming. You are supposed to design an object (furniture, sculpture), which will be inserted into a space together with the objects of the other students. You will have to write a function that can create your object and design an icon for the button, that will invoke the creation of your object.

Look at the example:

Download the files for exercise 9.

Start by compiling the unmodified files with Ñmake exercise9". Then run 'exercise9'. On the left side you will discover the following new buttons:

To insert an object click the respective button on the left. To move or rotate, first select the mode on the left side, then select the object with the left mouse button and when the element is lifted use the middle button to do the transformation. Click the left button again to release the object.

Figure out how the objects and buttons relate:

Each object needs two different files:

- a "student.h"-file which contains the icon that is going to appear on the button in the viewer and defines what kind of classes and member-functions that you will use.

- a "student.c++"-file with the actual content of those member-functions.

To get an idea of how the two files work, take a look at the examples:

- three... an easy example.

- rietveld... a not difficult, but quite extensive example.

The template program is split up into two parts: One (MyViewer) that manages objects and one (student,rietveld,three,csphere) with the objects themselves. You do not have to understand the managing part in order to write an object for it.

Make an icon for your own object:

Startup Photoshop:

- press "new"

- set width and height to be measured in Pixels

enter "24" for width and height, set mode to "Bitmap", press "OK".

- press "Ctrl" + "+" to enlarge the drawing area and use the drawing tools to draw your button.

- then save your button as SGI Image file in your working directory for exercise 9 under the name "button.sgi".

- quit Photoshop

Program your own object:

Before you start, run the program addOne.pl this will convert your button into a suitable format and prepare exercise9.c++ for your object. (Only run it once)

Open the file student.c++ with a text editor:

- The function "student::mySelCB" should not be touched, it tells Inventor what to do if your object gets selected. The same with the lines marked with "////", they are responsible for the insertion point of the object, you can change that point but not change the position of the node in the Scenegraph

- the 'constructor' is the only function that gets called when a class is instantiated as an object. It is here where you have to start creating new objects and calling other functions.

- If you want to add your own functions, like for example:

void myFunction(int i, SoSelection *top)

open the "student.h"-file and add the following in the "class student":

private:

void myFunction(int, SoSelection *);

in "student.c++" you can then add:


void student::myFunction(int i, SoSelection *top)
 { 
  YOUR COMMANDS 
  ...
 }
Please focus on one object, and make it the best you can. To add a second object is not trivial, you would have to understand the exercise9-program very well.

Hand in:

Please upload three files:

student.h the header file with the declaration of the new objects
student.c++ the c++ programming code

and a GIF image with a snapshot of your object.