Creating a new node class

To start a new node class, make a copy of one of the prototype node classes. There are two prototypes - mySimpleNode (mySimpleNode.cxx & mySimpleNode.h) contains just the basic features that a typical class will have; myNode (myNode.cxx & myNode.h) contains all of the standard xpNode features.

First, do a global search-and-replace on both the header and source code files to replace the dummy name (mySimpleNode or myNode) by the name of your new node class. Second, if this new class is to be derived from something other than the basic class xpNode, replace occurrences of xpNode by the appropriate parent class, except for the return value of the function construct(). The most common alternative parent class is xpTransform, to define a class that moves things around.

The basic node member functions, which are found in mySimpleNode, are:

parseOption is for parsing node values which are given in a scene file. For each "tag=value" assignment found in a scene file, the function parseOption(tag,value) will be called.

message is used for receiving messages from other nodes. If an eventMessage in a certain node is triggered, message(messageString) will be called for the recipient node at the appropriate time. The message function should then parse the character string and perform the corresponding action.

app is used for per-frame updates. Once each frame, the XP scene graph is traversed and app is called for every active node in the scene (nodes under a switch which is off, or not under the selected child of a selector, are inactive and will not have their app function called).

Remember that these functions are extending ones that are inherited from the parent class. So, you should be sure to call the parent class's corresponding functions as appropriate - parseOption and message should call the parent's version for any options or messages which they do not recognize; app should call the parent app in order for other standard update actions to take place.


The first example class is Wanderer, which will be a transform that starts at the (local) origin and can move around randomly. A Wanderer node will take a speed value in the scene file to define how fast it should move, in feet per second. It will accept three messages - "start", to begin moving, "stop", to stop moving and remain at its current position, and "speed", to change its speed.

The full source code for the Wanderer class is here: Wanderer.cxx, Wanderer.h

To implement Wanderer's behavior, I added three functions - setSpeed, start, and stop - which will be used when handling messages and options. I also added three member variables - speed_ will hold the Wanderer's speed, isMoving_ is a flag to keep track of whether it is currently moving or not, and position_ holds the current x/y/z position, which will be changed randomly whenever the Wanderer is moving. I could have omitted the position_ variable and simply used an xpTransform function to find out the current position before modifying it, but that would have had a greater performance cost, as it would require some matrix manipulation (within the xpTransform code) every frame; on the other hand, by caching the position value like this, I risk other problems - any "translate" messages sent to the node when it is wandering will be ignored.

class Wanderer : public xpTransform
       	{
       	public:
       	 Wanderer(void);
       	 virtual void parseOption(char *tag,char *val);
       	 virtual void message(char *msgstring);
       	 virtual void app(void);
       	 static void init(void);
       	 static inline xpClass * xpClassObject(void) { return xpClassObject_; };
	 void setSpeed(float);
 	 void start(void);
 	 void stop(void); 
       	private:
	 float speed_;
       	 bool isMoving_;
       	 pfVec3 position_; 
      	 static xpClass * xpClassObject_;
       	};

In the constructor, make sure to assign default/initial values to any member variables:

Wanderer::Wanderer(void)
{
 setClassObject(xpClassObject_);
 speed_ = 1;
 isMoving_ = 0;
 position_.set(0,0,0);
}

In order to accept the speed option in the scene file, the parseOption function must check for it. All other, unrecognized options are passed on to xpTransform to handle.

void Wanderer::parseOption(char *tag,char *val)
{
 if (STR_EQUAL(tag,"speed"))
        setSpeed(atof(val));
 else
        xpTransform::parseOption(tag,val);
}

The message function must be modified to recognize the new messages that will be accepted. All other messages are passed on to xpTransform.

void Wanderer::message(char *msgstring)
{
 if (STR_EQUAL(msgstring,"start"))
        start();
 else if (STR_EQUAL(msgstring,"stop"))
        stop();
 else if (SUBSTR_EQUAL(msgstring,"speed"))
        {
        float s;
        sscanf(msgstring,"%*s%f",&s);
        setSpeed(s);
        }
 else
        xpTransform::message(msgstring);
}

Finally, the app function is modified to update the position if the node is currently moving.

void Wanderer::app(void)
{
 if (isMoving_)
        {
        pfVec3 direction;
        randomVector(direction);
        position_ += direction * speed_ * xpWorld::FrameDeltaTime;
        setTrans(position_[0],position_[1],position_[2]);
        }
 xpTransform::app();
}

To use the Wanderer class in an application, references to it must be added to the World class (such as the prototype myWorld.cxx). Add it in the three places marked "ADD NEW NODE CLASSES HERE". Wanderer.o will also need to be added to the list of classes in the Makefile.


The full set of xpNode functions which can be extended by a new node class are:

Again, remember that all these functions are extensions of those defined in xpNode or other parent classes, so they should all call the parent class's corresponding function at the appropriate point.


Last modified 21 February 2000.

Dave Pape, pape@evl.uic.edu