Home

Download
Install
Extend

Use

Conformance
Examples
Contact

      

General Coding Notes for FreeWRL

This document will outline some of the ideosyncracies inherrent to the code behind FreeWRL. It has a companion document containing specific examples; Adding new nodes to FreeWRL/FreeX3D

Notes are not in any particular order; just numbered as they were entered into this page.

Last change: October 14, 2009.


1) Adding Pointers

There are a lot of times when we use absolute pointers. A "node" exists in memory, and we have a pointer to it. If we want to get to a specific field, we have to add an offset to a pointer. For instance, a TimeSensor node has the following C structure:
struct X3D_TimeSensor {
       struct X3D_Virt *v;
       int _renderFlags; /*sensitive, etc */
       int _hit;
       int _change;
       void **_parents;
       int _nparents;
       int _nparalloc;
       int _ichange;
       double _dist; /*sorting for blending */
       float _extent[6]; /* used for boundingboxes - +-x, +-y, +-z */
       void *_intern;
       int _nodeType; /* unique integer for each type */
       int _defaultContainer; /* holds the container */
        /*** node specific data: *****/
        double __ctflag;
        double __inittime;
        int __oldEnabled;
        void *__oldmetadata;
        double cycleInterval;
        double cycleTime;
        double elapsedTime;
        int enabled;
        float fraction_changed;
        int isActive;
        double isPaused;
        int loop;
        void *metadata;
        double pauseTime;
        double resumeTime;
        double startTime;
        double stopTime;
        double time;
};
If we wish to get the startTime field, we might say:
	struct X3D_TimeSensor *ts = createNewX3DNode(NODE_TimeSensor);
	int offs = offsetof (struct X3D_TimeSensor, startTime);
	double *finalPointer;
	
	finalPointer = offsetPointer_deref(double *, ts, offs);
The offsetPointer_deref will add the pointer, and the offset, and cast the results for us. This gets around problems and coding inconsistencies that we had that caused havoc on different platforms.

2) Just what are the fields in a node structure?

Each node type has two different sections; the first section is standard amongst ALL nodes, the second varies between the nodes. For instance, we have in the above TimeSensor node:
struct X3D_TimeSensor {
       struct X3D_Virt *v;
       int _renderFlags; /*sensitive, etc */
       int _hit;
       int _change;
       void **_parents;
       int _nparents;
       int _nparalloc;
       int _ichange;
       double _dist; /*sorting for blending */
       float _extent[6]; /* used for boundingboxes - +-x, +-y, +-z */
       void *_intern;
       int _nodeType; /* unique integer for each type */
       int _defaultContainer; /* holds the container */
	...
This section is the same amongst all X3D internal node representations. These fields are for keeping track of parents of this node (_parents, _nparents, _nparalloc), whether the node has changed and whether the change has been acted upon (_ichange, _change), distance from viewer and bounding box size (_dist, _extent), the node type (_nodeType, eg, "NODE_TimeSensor") and the Container Field of the parent (_defaultContainer).

There are some other fields for rendering the node (_intern and v) and whether this node and its children contain certain node types (_renderFlags) and whether the node is under the mouses gaze (_hit).

Now, if we look at the TimeSensor in the X3D Specification, we will notice a 1:1 mapping. We will also node that there are some other nodes that start off with an underscore, these are "internal" fields that are invocation specific and are used for enabling/disabling and for determining when to send events.

Here are the remainder of the fields:

	...
        /*** node specific data: *****/
        double __ctflag;
        double __inittime;
        int __oldEnabled;
        void *__oldmetadata;
        double cycleInterval;
        double cycleTime;
        double elapsedTime;
        int enabled;
        float fraction_changed;
        int isActive;
        double isPaused;
        int loop;
        void *metadata;
        double pauseTime;
        double resumeTime;
        double startTime;
        double stopTime;
        double time;
};
Check out the spec and the code for their operation; and, no Virginia, you can not ROUTE to a node starting with an underscore.

3) Getting Files

Sometimes your code needs to go out and resolve a URL. Michel Briand coded up the following and wrote the notes. From Michel:
Benefits:

- centralize file/url operations in one/or two (new) source files
	- url manipulation
	- file / directory manipulation
	- management of openned files and cached files (this will improve the shadowed files mgt)

- requests anywhere in the FreeWRL code for some external resources are handled like this :
(request will be handled asynchronously in the future (with a download multi threaded using libcurl))

- create a request item with the original request string
	example : in someTexture { url "test.frag" } => create a request with "test.frag"
	same principle for protos and for images....
- parse the request
- when the request is processed its status is "available"
- fetch / download / load of the specified resource is automaticaly done in one function
- the file is automatically read / openned and a pointer to the data is available through the request structure
- the function prototypes are not finalized but, as a rough example, you'll write :

Example:
resource_item_t *res;
bool status;

res = create_request_item("http://xxxxxx/yyyyyy/myextern.wrl");
resource_parse(res);
status = resource_fetch(res); // you can stop here if you only need to know if the resource is valid/not
status = resource_load(res); // you really ask for the resource to be loaded in memory

you can use status to check for succes / failure after _fetch and _load.
you can use res as long as you need it

you release it (and all the files openned, downloaded, ...) with the call :

resource_destroy(res);

res->request is the original request
res->actual_file is the path to the downloaded file
res->of is the openned file

res->of->data is the pointer to the data loaded in memory

Some other nice thing to note : all downloaded files are gathered in a directory named by the main world / main file. 
So that when you run FreeWRL on a remote world, all data will be automaticaly available to create a ZIP of that world.

4) That Polyrep Structure

Almost all visible shapes get funnelled through a PolyRep structure in order to get rendered. This means that, for instance, the "behind the scenes" for an IndexedFaceSet is identical to that of a TriangleSet.

The simple shapes, Box, Cone, Cylinder, Sphere do NOT go through this process. Some shapes, like the ArcClose2D node do not, as of this writing, use this process, but most likely will as FreeWRL progresses to OpenGL-3.1 and OpenGL-ES.

So, what happens? Lets look at a simple example, TriangleFanSet The X3D spec says:

"A TriangleFanSet represents a 3D shape composed of triangles that form a fan shape around the first vertex declared in each fan. The fanCount field describes how many vertices are to be used in each fan from the coordinate field. Coordinates are assigned to each strip by taking fanCount[n] vertices from the coordinate field. Each value of the fanCount array shall be greater than or equal to three. It shall be an error to have a value less than three."

A coding example might be:

	Shape {
		geometry TriangleFanSet {
			fanCount 5
			ccw FALSE
			coordinate Coordinate { point [
				0 0 0
				0 1 0
				0.5 1 0
				1 0.25 0
				0.75 -0.75 0 ]}
		}
	}
So this creates a series of triangles "fanning" around the first vertex (0,0,0).

When this TriangleFanSet is first encountered, or when it is changed, the function make_genericfaceset is called. This function funnels all of the "PolyRep" nodes together. Just about the first thing done is the following call:

	checkTriangleFanSetFields(X3D_TRIANGLEFANSET(node));
which simply creates a coordinateIndex into the vertexes, exactly as one would require for an IndexedFaceSet. You will also note that after the checkTriangleFanSetFields call, fields are extracted from the Node, and put into common variables, eg:
       orig_coordIndex= &X3D_TRIANGLEFANSET(node)->_coordIndex;
       cpv = X3D_TRIANGLEFANSET(node)->colorPerVertex;
       npv = X3D_TRIANGLEFANSET(node)->normalPerVertex;
       ccw = X3D_TRIANGLEFANSET(node)->ccw;
Note the use of the underscore in the "_coordIndex" field - this is a generated field; a TriangleFanSet does not have this field as an X3D field.

You will see that the fields are verified, and placed in the PolyRep structure, as located in the code as:

	struct X3D_PolyRep *rep_ = (struct X3D_PolyRep *)node->_intern;
--

So, lets see; we have effectively changed a TriangleFanSet into an IndexedFaceSet by creating a coordinateIndex field; and (if you look at the code) we ensure that there are no passed in Normals, textureCoordinates, colorIndexes, etc. You will see that "faces" are counted, tesselation performed if required; in essence, all these X3D shapes have their data put into a common form, and that common form is an IndexedFaceSet.

Step two of the rendering process is to "stream" that structure; this streaming process can change depending on OpenGL requirements; change one routine we can render all these shapes using different OpenGL calls. We could use OpenGL immediate mode to go through and render each vertex (which FreeWRL used to do) but this is slow and is being phased out by the OpenGL ARB. So, we take the data that would be ok for rendering in OpenGL immediate mode, and "stream" it so that we can make simple calls and have our shape rendered by the OpenGL driver.

Here is the header for "stream_polyrep" - I will not go through the actual process here as it will change for OpenGL-ES and OpenGL-3.1.


/********************************************************************
*
* stream_polyrep
*
*  convert a polyrep into a structure format that displays very
*  well, especially on fast graphics hardware
*
* many shapes go to a polyrep structure; Extrusions, ElevationGrids,
* IndexedFaceSets, and all of the Triangle nodes.
*
* This is stage 2 of the polyrep build process; the first stage is
* (for example) make_indexedfaceset(node); it creates a polyrep
* structure.
*
* This stage takes that polyrep structure, and finishes it in a
* generic fashion, and makes it "linear" so that it can be rendered
* very quickly by the GPU.
*
* we ALWAYS worry about texture coords, even if this geometry does not
* have a texture in the associated geometry node; you *never* know
* when that Appearance node will change. Some nodes, eg ElevationGrid,
* will automatically generate texture coordinates so they are outside
* of this scope.
*
*********************************************************************/

So, what about rendering?

	void render_polyrep(void *node) {
does the rendering for these shapes. Look at the code, and you will see some stuff for propagating BoundingBoxes, and so on, but basically you will see OpenGL calls to do the actual rendering.

render_polyrep will be called each time through the event loop for the X3D shape; it will have all of the data required in a read-only form, so it will be quick, efficient, and optimized for a specific set of OpenGL calls.


5) "changed_", "prep_" and other function prefixes




Trailing thoughts

If you want to help develop FreeWRL to use in an interesting project, join the mailing list (send mail to freewrl-join@crc.ca). Anyone is welcome.

Contact

If you want to help, please email John Stewart (freewrl-09 -at- rogers.com) - an employee of the Communications Research Centre (CRC), Canada.

FreeWRL is produced by dedicated volunteers, and employees of CRC, and is released under the LGPL License as Open Source to the world community.

DISCLAIMER: All information and programs presented on these pages is presented strictly on an as-is basis without an explicit or implicit warranty or guarantee of any kind, not even for fitness for any particular purpose. The FreeWRL logo is based on the Linux Penguin logo by Larry Ewing. All trademarks are owned by their respective owners.

SourceForge.net Logo