XP 1.0 New Features
(as of 28 February 2000)

This is a summary of the things that have changed (so far) in XP version 1.0, relative to the previous version, 0.4.

Scene File

Basic syntax

The main changes to the scene file are the naming of nodes and a more function-style syntax for messages. Node attributes & messages are now basically the same - attributes which are listed for a given node are simply messages which will be sent at initialization.

An example scene in the new format is the following:

	light sun
	light sun2 (position(1 -1 1), diffuse(.9 .7 .2))

	switch theSwitch (on)
		object (file(duck.pfb), position(-2 5 3), size(3))

The syntax for a node is, roughly:

nodeType [name] [(messageList)] [{ children... }]

The big change here is that the name is pulled out of the attribute list and given right after the node type (similar to Inventor); this will make some things a little easier in the C++ coding of new classes, and should make the scene file a little clearer. Note that both the name and parenthesized message list are optional; however, at least one of them should be given for any node, or the parsing will fail due to an ambiguity. The most likely way you might encounter this problem is with something like:

	object foo (file(foo.pfb))

in which case the parser would interpret "object" as the name of the light, and then try to create a node of type "foo".

The messageList is a comma-separated list of messages to send to the node at initialization. The syntax for a message is:

[nodeName.]message[(arg, arg, ...)][+delay]

Some possible messages are:

	position(1 2 3)
	light.diffuse(1 0 0)

The first two messages above (on and on()) are identical. The delay value for a message is in seconds. Since the initialization options for nodes are just messages, these can also in fact be messages sent to other nodes; e.g.:

	light foo (bar.off)
	light bar ()

(Note: this isn't a particularly interesting example; I don't have any simple examples yet where this capability might actually be useful.)

Message arguments, in the default XP classes, can be comma-separated or not. e.g. a light will parse the messages position(-2 0 1 1) and position(-2, 0, 1, 1) equivalently. Whether new user-provided nodes do this will depend on their implementation, although standard message parsing functions provided by XP try to make it simple to accept both.

Event/message linking

The old "eventMessage" option has now been renamed to the command when. The arguments for when are the name of the event and the message to send in response to that event, in the format described above. A couple example uses are:

	switch sw (when(switchOn, sun.diffuse(0 1 0)))
	light sun (when(lightOff, on+2))

Other changes

The options for a Transform node have been renamed to position, orientation, and size. This should be clearer than the old translate/rotate/scale, since the messages are setting values, rather than accumulating them.

I will soon start using the C pre-processor to read scene files. This means that you will be able to use #define's, #if's, and C/C++ style comments (/**/ and //). There will therefore no longer be a "Scene" node, instead simply use #include, with a Switch around it if necessary. e.g.:

	switch scene1 (on)
		#include "scene1"

C++ Coding

The most significant changes which affect the coding of new nodes in C++ are that there are fewer functions to implement, the Standard Template Library (STL) is now used for a number of things, including strings, and new node classes can be loaded as shared objects (DSO's).


All strings used by XP code are now objects of class xpString, which is an instance of the STL basic_string template using case-insensitive comparisons. In other words, you can write code such as

	if (str == "message") { ... }
	str3 = str2 + ".aiff";

instead of

	if (STR_EQUALS(str,"message")) { ... }

(The xpString member function c_str() will return a char* pointer if you need it for things such as printf()).

Node functions

There are now just 3 important functions for implementing a new xpNode class: init(), app(), and message(). The old parseOption() function is now effectively merged with message(), since all options are just messages. The old postInit() function should not be necessary - most of what it was used for can now be handled by init() and message() (a common use was to find other nodes which were named in scene file options; in the new parsing system, all nodes will have been created and named before any option messages are sent, so message() can immediately search for any needed nodes). There is currently no switchOff() function - we will have to see if it actually becomes necessary again. The original reason for switchOff() was to stop playing sounds which were under switches; the new implementation of sound nodes will solve this problem a different way.

The init function (void init(void)) takes the place of a reset function. It is called for all nodes at the beginning of the program, once the full scene has been loaded, and in response to "init" or "initTree" messages. I am also finding it useful to call init() from a node class's constructor, since both functions would typically initialize all of a node's member variables; this saves repeating some code.

The app function (void app(void)) is unchanged - it is called for each active node once per frame.

The message function (void message(xpMessage *m)) is the same, except that its argument is now an xpMessage object. The xpMessage object contains the name of the message and an array of arguments, all as xpStrings. These data members are declared as:

	xpString msg;
	vector<xpString*> args;

(There are also members node, nodeName, and delay, but these are used in delivering the message, and should not be important when processing it in your message() function.) The arguments to the message will have been split up into the vector args based on commas, so for a message "position(1 2 3)", m->args[0] will be "1 2 3", while for a message "position(1,2,3)", m->args[0] will be "1", m->args[1] will be "2", and m->args[2] will be "3". However, xpMessage also includes some convenience functions for parsing which will handle either of these cases transparently. The xpMessage parsing functions are floatArg(i) to return argument #i as a float, intArg(i) to return argument #i as a int, and getVec2Args(v), getVec3Args(v), and getVec4Args(v) to parse all the arguments as a pfVec2/pfVec3/pfVec4.

An example message() function is the following:

	void spinner::message(xpMessage *m)
	 if (m->msg == "axis")
	 else if (m->msg == "speed")
		speed_ = m->floatArg(0);

(where axis_ has been declared as a pfVec3, and speed_ is a float.)


Node classes can now be built as dynamic shared objects (DSOs), which means that you can add new nodes to an application without having to extend xpWorld or recompile the xp program itself. Any time the scene parser encounters the name of a node type which it does not recognize, it will try to load a DSO "nodetype.so" (where nodetype is the type name found in the scene file) and create the node using that.

The procedure involves two or three steps (assume we are creating a new node class named "myNode"):

  1. In the code for your class, define the function:
    	extern "C" xpNode* construct_myNode(void) { return new myNode; }
    (this does not need to be declared in the header file).
  2. Compile the class and convert it to a DSO:
    	% CC -c myNode.o
    	% ld -shared -o myNode.so myNode.o
  3. If your class is not derived from one of the core XP classes (such as xpNode or xpTransform), but from some other class which will also be dynamically loaded, create a dependency file for it:
    	% cat > myNode.xpdep
    (Assume that myParentNode is the name of the class that myNode is derived from.) If your class uses any other dynamically loaded classes, you should also list them in the dependency file, one per line.

Currently, the DSO and ".xpdep" files should be in the directory that the xp program is run from. At some point I will provide a method for specifying a search path via an environment variable.

Last modified 28 February 2000.
Dave Pape, pape@evl.uic.edu