
Lib3d Programmers Documentation
-------------------------------

The documentation currently takes the form of a large scale annotation
of a working program.

There are currently quite a few include files to add to the top of
your program.  You will need most of these at least in the places
where you create or modify the overall structure of your 3d hierarchy.

> #include "Visual.H"
> #include "Model.H"
> #include "ModelBuilder.H"
> #include "World.H"
> #include "Camera.H"
> #include "Light.H"
> #include "SmoothPipeline.H"

You'll need this to use 'cout' and the like.

> #include <iostream.h>

> main()
> {

In designing lib3d, I have tried to minimize the exposure of client
applications to the underlying hardware and/or windowing system.  The
following line is the extent to which exposure remains:

>     Visual *visual = Visual::create(Device::create(300, 250, 8));

Two things have happened.  Firstly we attempt to create a Device - an
object providing framebuffer access - for the current environment.  We
have specified that that Device should be at least 300x250 and be at
least 8 bits deep (256 colour or better).

There are currently only two possible Devices, one typically for
console X displays (XShmDevice), and one for X displays on other
computers or X Terminals (XImageDevice).  XShmDevice is a good deal
faster and will be used if possible.  

The XDevice classes are the only places where lib3d touches X
directly, and it is a minimal point of contact.  This means that ports
to other environments and operating systems should be very simple,
both for lib3d and programs that use it.  If an SVGA device were to
become available, this program would not require any changes to take
advantage of it.  Indeed, if lib3d was linked as a dynamic library,
the program shouldn't even need to be recompiled.

The second thing which has occurred is that we have created an
appropriate Visual - an object which performs drawing to the device
and manages the z-buffer - for the Device that was created.  There are
currently visuals for 8, 16 and 32 bit displays.  Visuals are
independent of windowing system as they write directly to a memory
mapped buffer provided by the Device object.  It is also the task of
the Device object to move the contents of the buffer to the screen.
 
>     ifstream in("teapot.nff");
>     if (in.bad()) {
> 	cerr << "Couldn't open teapot.nff" << endl;
> 	exit(-1);
>     }

The hierarchy of objects, lights and cameras maintained by every lib3d
program is rooted at a World object.  When you render the hierarchy,
you will ask the World object to do that for you.  The position and
orientation of every object is defined (ultimately) to relative to the
World object.  

>     World world;

So at this point we have a hierarchy that looks like this:

	World

This is the starting point.  We will now create a model (a visible 3d
polyhedron object) from the teapot.nff file opened above.  This
currently acheived with the help of an object of class ModelBuilder.
This is a utility class, which really just serves to keep the noise
down in the Model class itself.  All model creation occurs through
this class, even if you want to create one polygon by polygon.  You
can think of this as analogous to the case of a commercial renderer
with a proprietry file format and some ad-hoc conversion utilities
bundled into the package.  

>     ModelBuilder ob;
>     ob.startModel();
>     ob.readNFF( in );
>     Model *model = ob.endModel();

So now we have a teapot model and an empty hierarchy.  To connect the
two, we ask the World object to add the teapot to its list of
descendents.

>     world.adopt(model);

Now the hierarchy looks like this:

	World
	  |
	  +---- Model (teapot.nff)


To display the contents of the hierarcy, we must define a point of
view - a Camera.  We put the camera into the hierarcy, too.  This has
powerful implications as we will discuss later.

>     Camera *camera = new Camera(world);

Now the hierarchy looks like this:

	World
	  |
	  +---- Model (teapot.nff)
	  |
	  +---- Camera

Define the camera to have a near clipping plane at z=3, a far clipping
plane at z=100, a field of view of 15 degrees, and an aspect ratio of
1:1.

>     camera->setParameters(3,100,15,1);

Tell the world that this is the camera to use when rendering.  This
allows us to switch between several cameras implanted in diferent
places in the hierarcy at appropriate moments.  For example one camera
could be located in the driver's position in a car, and another in a
trailing helicopter for cut-away shots.

>     world.setActiveCamera( *camera );

Next, a light to clarify the shape of objects.

>     Light *light = new Light(world);

Predictably, the hierarchy now looks like this:

	World
	  |
	  +---- Model (teapot.nff)
	  |
	  +---- Camera
	  |
	  +---- Light

This is really the minimal setup.  In any real application or game,
the hierarcy is going to be considerably more complex than this.
Particularly, cameras are likely to be descendents of player's
ships/cars/characters so that the point of view will move
automatically whenever the position or orientation of the player is
changed.

Here we set the direction and ambient and diffuse colours of the light.

>     Vector3 colour(1,1,1);	
>     Vector3 direction(0,1,.5);
>     light->setParameters(colour, colour, direction);

And turn the light on.

>     world.registerLight( *light );

If you want to use Gouraud shading for your teapot, you need to
explicitly override the default pipeline, which performs flat shading.
Models can and should share pipelines, so you should never create more
than one of any type of pipeline.  There are currently two pipelines,
FlatPipeline and SmoothPipeline.  If you try to render any models that
don't have a pipeline explicitly stated, a single additional
FlatPipeline will be created.

>     Pipeline *pipeline = new SmoothPipeline;
>     model->usePipeline(*pipeline);

Now we are all set up with our hierarchy.  All that is left to do is move
the bits around and take photos of them.

>     Matrix34 tmp;
>     Matrix34 scale;
>     Matrix34 transform;

This is the incremental rotation to be applied each frame.

>     scale.setRotation(4, 0,1,0);

This is necessary to get the teapot facing upwards...

>     transform.setRotation(-90, 1,0,0);

Currently the camera and teapot are coincident at the origin in world
space.  Move the camera back a bit so we can see the teapot.  The
transformation matrix in every case specifies the mapping between the
object's 3d coordinate system and that of its parent.  Tranformations
are inherited down the hierarchy, so if you move, scale, shear or
rotate a parent object, all of the children are likewise affected.

>     tmp.setTranslation(0,-1,-10);
>     camera->setTransform(tmp);

Loop 1000 times:

>     for ( int i = 0 ; i < 1000 ; i++ ) {

   Add a small rotation to the teapot's translation matrix.

>        transform.premul(scale);
>        model->setTransform(transform);

   Render the hierarchy onto the back buffer.

>        world.renderHierarchy( *visual );

   Move the image to the screen and clear the back buffer.

>        visual->swapBuffers();
>     }

All done.  

>     delete visual;
> }


From here on, you are entering undocumented territory.  



