This commit is contained in:
Alexander Deynichenko
2013-02-25 07:23:44 +04:00
parent 35ff18a6a1
commit 03b62f3315
809 changed files with 211135 additions and 0 deletions

315
doc/ssg/LoaderWriter.html Normal file
View File

@@ -0,0 +1,315 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<H2>How to write plib-loaders and writers</H2>
<H2>1.1 Introduction</H2>
This page is intended for those who wish to write loaders and writers
for plib. If you just use plib (using the included loaders and writers), you don't
really need to read this.
It is quite easy to adapt 3D-file-loaders and writers to be used by ssg. Plib
already has many loaders and writers that may be used as examples.<p>
A brief note on cross platform compilation: Plib comes with
both Makefiles (for Unix and for the CygWin-system under Windows)
and workspace/project-files for Micro$oft Visual C++. If you
have both, please update, test and commit both.
Otherwise, when committing, please tell the people that something is
missing (for example: you didn't update the workspace files) and that some
kind soul should do so.
<p>
Both loaders and writers convert between ssg's internal geometry
representation and that of the file format. One key difference is that when
loading, you should support all possibile
geometry-representations of the file format. This is to ensure that
plib can handle all the possible variations of a file format that may be generated
by the tools that export to it. On the other hand, when writing a format, you can
pretty safely only write to one geometry format (unless you need features peculiar
to more than one geometry representation), because you have the final say on how the
data is to be written.
<p>
Regarding plib's geometry-representations: there are only two on the highest
level: <code>ssgVtxTable</code> and <code>ssgVtxArray</code>. Actually, there is also
ssgVTable, but that is deprecated.
<code>ssgVtxArray</code> is newer, is derived
from ssgVtxTable and uses a index-list. Apart from that they are quite
similar, and both have an interface <code>getNumTriangles () ; </code> and
<code>getTriangle ( i, ...); </code>
For this reasons, it is easier to write a writer than a loader.
For both ssgVtxTable and ssgVtxArray you need to choose a
GL-type. Currently ssgLoaderWriterMesh (more on this in the
next section) uses <code>GL_TRIANGLES</code>.
<H2>1.2 class ssgLoaderWriterMesh - an overview</H2>
The two main parts of writing a loader are writing the actual parser (coding
the syntax of the format) and transferring the contents
to ssg. The second task is fairly trivial if your contents
obey the restrictions of ssg (which follow from OpenGL).
<BR><BR>
These are:
<BR><BR>
1. Each ssg-node can (currently) have only one texture.
<BR>
2. Only one polygon or strip or fan per node. So you can't have a 3-, a 4- and a 5-sided
poly inside one node (without subdividing the polys into triangles)
<BR>
3. Currently, you may have only one texture coordinate per vertex.
<BR>
4. You may have only one normal per vertex.
<BR><BR>
To modularize the two steps parsing the format and transfering it to ssg
and to reduce redundant work, there is an intermediatory structure, the class
ssgLoaderWriterMesh. For example, this has a member function
<pre>
void ssgLoaderWriterMesh::addFaceFromIntegerArray( int numVertices, int *vertices );
</pre>
With several calls, you can add several n-sided polys to one mesh ("one node").
When you are done constructing the mesh from the file, you call
<pre>
void ssgLoaderWriterMesh::addToSSG(
class ssgSimpleState *currentState,
class ssgLoaderOptions* current_options,
class ssgBranch *curr_branch_)
</pre>
and the class adds the information into the scene graph. It handles
ssgs' restrictions. For example, if the polygons of the mesh use
5 textures then at least 5 nodes will be added to the scene graph.<p>
Unfortunately, this class isn't completely finished. As of this
writing, Wolfram Kuss (w_kuss@rz-online.de) has implemented those
parts that were needed for the loaders he has finished thus far. Hopefully
people will contribute more features as time goes on.<p>
If you are writing a new loader for a file format that doesn't hold to
all restrictions of ssg (and virtually none do), you are urged to use this class.
Your loader will be more consistent and easier to maintain and read.<p>
Further, there are many optimizations that can be done (For example: "If the state is
different, but not the texture, do we need several nodes?" or "When we have multitexturing,
can we use that?" or "Is there an optimal strip length?" or "How do I subdivide polys into
triangles so that the stripifier will work well?"-- the list goes on
forever). Once we have good answers to these questions (and the will
to implement them), it will be easier to do them once in the ssgLoaderWriterMesh than in all the
loaders seperately. It is noteable that most loaders written before
ssgLoaderWriterMesh have had some sort of intermediatory mesh
structure.<p>
In the future, ssgLoaderWriterMesh should also be used for writers, doing the opposite
job: It takes the information from ssg with the restrictions and then looks whether
it can optimize (for example merging nodes) by relaxing the restrictions.
<H2>1.3 The Class ssgLoaderWriterMesh - A Deeper Look</H2>
At the start of your loader, you create a new <code>ssgLoaderWriterMesh</code>
or do a <code>reInit()</code>. To insert the data into the <code>ssgLoaderWriterMesh</code>,
you have to add vertices, faces, materials, materialindexes (saying what face uses what material)
and, if applicable texture coordinates. For all of these, you <B>can</B> say in advance how many you have.
If you know that you have 3712 vertices, call <code>createVertices(3712)</code> and everything is allocated
at once and <code>addVertex</code> will be very fast. If you don't know in advance how many you have, you
still have to call <code>createVertices()</code>, as this also allocates the vertices. In this case, a
certain amount of vertices will be reserved and the list will dynamically grow as more are added.<p>
Vertices are simply sgVec3s. Faces are simply lists/arrays of vertex indexes.
For adding faces, use <code>addFace</code> if you already have a <code>ssgIndexArray</code> or use
<code>addFaceFromIntegerArray</code> if you have the vertex indexes in a C(++) array.
You need to add at least one material (<code>ssgSimpleState</code>). For each face, you tell
ssg which material to use via <code>addMaterialIndex</code>. Here is code from ssgLoadOFF, which tells
ssg to use the <code>ssgSimpleState</code> ss for all faces:
<BR>
<pre>
theMesh.createMaterials( 1 );
theMesh.addMaterial( &amp;ss );
theMesh.createMaterialIndices( _ssgNoFacesToRead ) ;
for(i=0;i<_ssgNoFacesToRead ;i++)
theMesh.addMaterialIndex ( 0 ) ;
</pre>
<BR>
If the file format has texture coordinates, you have to find out whether it has them
per vertex or per vertex and face. If you look at a "straight forward" cube,
having texture coordinates per vertex means you can have 8, but if you have
texture coordinates per vertex and face, you can have 24 (each vertex is part of 3 faces).
The two functions corresponding to these cases are:
<pre>
void createPerFaceAndVertexTextureCoordinates2( int numReservedTextureCoordinate2Lists = 3 );
void addPerFaceAndVertexTextureCoordinate2( ssgTexCoordArray **textureCoordinateArray );
</pre>
or
<pre>
void createPerVertexTextureCoordinates2( int numReservedTextureCoordinates2 = 3 );
void addPerVertexTextureCoordinate2( sgVec2 textureCoordinate );
</pre>
<H2>2. Loaders</H2>
<H2>2.1 class ssgLoaderOptions</H2>
ssgLoaderOptions is a class that is defined in ssg.h.
It is used to tell the loader some options.
It is NOT used for user-setable options, although
this may be nice to have at some point. For example,
one COULD create a member-variable in it
telling the unit that one wants. The loader would then
be responsible to scale the object in such a way that
the sizes are in that unit (for example: meter, millimeter, etc).
<BR>
<BR>
Regarding the reason for the callbacks in ssgLoaderOptions, Steve
wrote:
<BR>
<BR>
<blockquote>
<i>"Whenever a branch node is created. The deal is that most file formats
are
missing important features at the Branch level - but many support
comment
fields - or long ASCII name strings or something. The idea was to allow the artists to attach an ARBITARY comment string
in their modeller - and to have the loader trap these strings and pass
them on to the application.</i><p>
<i>Hence, if the hook function is defined then when a branch node needs
to
be created, we call the application's callback with the ASCII string
that was embedded in the file and let the application construct the
ssgBranch
node. Hence, you could put the string "~LOD: RANGE=100 meters"
into the comment field in (say) the AC3D modeller. (AC3D calls this a
"Data"
field)...the application could then say to itself: "Any comment that
starts
with a tilde ('~') is a command to the loader" and parse such
'comments'
as commands. In this case, it would construct an ssgRangeSelector and
set
the transition range to 100m and return the application back to the
loader.</i><p>
<i>Check the <a href="http://tuxkart.sf.net">Tux Kart</a> sources to see this in action."</i>
</blockquote>
So much for the quote from Steve.
<p>
As of this writing, the ssgLoaderOptions code has been copied from
another loader into ssgLoaderWriterMesh.
<H2>2.2 ASCII file formats</H2>
A lexical analyzer for ascii-files is available in ssgParser.cxx and
ssgParser.h. It converts the file into a stream of tokens and
handles comments. In a way, it has two APIs.
One hides the line structure from the loader. The loader just has
to say "getNextToken".
The other API operates in terms of exact line structure.
You do a getLine which reads all the tokens of that line into a
buffer and then you do a parseToken to get each token.<p>
The formats which currently use the parser are .X (which uses the
line-independant API), .ase, .scenery and .off (which use the
line-by-line-API).<p>
Some functions are used by both APIs. For example:<p>
<pre>
void openFile( const char* fname, const _ssgParserSpec* spec = 0 );
</pre><p>
In ssgParserSpec, you give the parser the specification of the format.
You say which characters start a comment, which characters are skipable,
which characters are used for braces (which are used to determine the
parser's level-- useful for parser's which work recursively).
Most important are the delimiters. These determine where one token ends and the
next one begins. For example, the first token of the line
<pre>
1234,567
</pre>
is <code>1234</code> if <code>","</code> is a delimiter and
<code>1234,567</code> otherwise.
The parser differentiates between skipable delimiters that
are "swallowed" by the parser and non-skipable ones that are
passed to the loader. So, regarding the example-line there
are three possibilities:
<BR>
<code>","</code> is not a delimiter => The line contains one token, namely
<code>"1234,567"</code>
<BR>
<code>","</code> is a skipable delimiter => The line contains two tokens, namely
<code>"1234"</code> and <code>"567"</code>
<BR>
<code>","</code> is a non-skipable delimiter => The line contains three tokens,
namely <code>"1234"</code>, <code>","</code> and <code>"567"</code>
<BR>
<H2>3. Writers</H2>
For an example how to write out geometry and material, look at
ssgSaveASE.
For an easy example (geometry only), look at <code>ssgSaveDXF</code> or <code>ssgSaveTRI</code>.
The function
<pre>
int ssgSaveXYZ ( const char *filename, ssgEntity *ent )
</pre>
normally calls a function
<pre>
static void save_entities ( ssgEntity *e )
</pre>
which just recursively walks the scene graph.
You should be able to use this function and just
write a
<pre>
static void save_vtx_table ( ssgVtxTable *vt )
</pre>
which writes a <code>ssgVtxTable</code>.<p>
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"><a href="non_class.html">&lt;= previous =</a></td>
<td width="34%" align="center"><a href="index.html">Return to SSG Index</a></td>
<td width="33%" align="right"></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>

319
doc/ssg/branches.html Normal file
View File

@@ -0,0 +1,319 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<H2><code>class ssgBranch</code> - A basic branch node.</H2>
The basic ssgBranch node simply handles a node in the tree,
with zero or more child nodes which can be any kind of ssgEntity
except ssgRoot.
<p>
There are a rich set of functions for adding, deleting and
replacing child nodes:
<pre>
class ssgBranch : public ssgEntity
{
int getNumKids (void) ;
ssgEntity *getKid ( int n ) ;
ssgEntity *getNextKid (void) ;
int searchForKid ( ssgEntity *entity ) ;
void addKid ( ssgEntity *entity ) ;
void removeKid ( int n ) ;
void removeKid ( ssgEntity *entity ) ;
void removeAllKids (void) ;
void replaceKid ( ssgEntity *old_entity, ssgEntity *new_entity ) ;
} ;
</pre>
Most of these are pretty self-explanatory.
<code>ssgBranch::getNumKids()</code> returns the number of child nodes
beneath this branch.
<code>ssgBranch::getKid(n)</code> returns the address of the n'th child ssgEntity.
<code>ssgBranch::getNextKid()</code> returns the address of the child entity following
the last one returned by getKid or getNextKid - returning NULL when all child nodes
have been exhausted.
<code>ssgBranch::searchForKid(entity)</code> searches for the specified entity
in the list of child nodes and returns it's index (ie the inverse of getKid).
<code>ssgBranch::addKid(entity)</code> adds the specified entity to the list of
child nodes - the new node is added at the end of the list and will therefore
have the highest numbered index.
<code>ssgBranch::removeKid(n)</code> removes the n'th child node and renumbers
any higher numbered children so there are never any gaps in the number range.
<code>ssgBranch::removeKid(entity)</code> same as removeKid(searchForKid(entity)).
<code>ssgBranch::removeAllKids()</code> remove ALL child entities.
<code>ssgBranch::replaceKid(old, new)</code> replaces <code>old</code> with <code>new</code>.
If the entity removed by any of these commands has a ref count of
zero, it will be deleted.
<H2><code>class ssgInvisible</code> - Invisible parts of a Scene Graph.</H2>
It's sometimes useful to have sections of the scene graph that are never
rendered to the screen. These are frequently used for collision detection
and other non-graphical operations.
<H2><code>class ssgRoot</code> - The Root of the Scene Graph,</H2>
The node at the root of the scene graph is special. At present,
it resembles an ssgBranch externally.
<pre>
class ssgRoot : public ssgBranch
{
} ;
</pre>
<H2><code>class ssgTweenController</code> - A morph controller,</H2>
This is essentially identical to an ssgBranch - but adds API to
set the current 'bank' settings of all ssgTween leaf nodes beneath it.
<p>
An ssgTween is a leaf node that can hold multiple geometric
representations that it smoothly interpolates between.
<pre>
ssgTweenController::selectBank ( float b ) ;
float ssgTweenController::getCurrBank () ;
</pre>
This allows you to set which of the banks of it's daughter ssgTweens
will be rendered. That's a "float" quantity - so if you selectBank(2.4)
then the ssgTweens will render their vertices in a position (colour, etc)
that's 40% of the way between the bank 2 and bank 3.
<p>
You can also set the behaviour when the selected bank number is larger
than the number of banks in the model. This tends to happen when you
simply add some amount of to the bank selector each frame in order
to keep the animation running forever:
<pre>
ssgTweenController::setMode ( int mode ) ;
</pre>
'mode' can either be SSGTWEEN_STOP_AT_END or SSGTWEEN_REPEAT.
The default is STOP_AT_END - where anytime the bank selector
is larger than the actual number of banks in the ssgTween node,
it is clamped to the largest possible number. If you choose
REPEAT, then the bank number will be taken modulo the number of
banks.
<H2><code>class ssgSelector</code> - A switch point,</H2>
Most ssgBranch nodes represent a collection of objects that
are all present in the scene at the same time. ssgSelector
nodes (and derived classes) typically represent a single
object that can be represented in more than one way.
<p>
A selector contains up to 32 daughter objects and a
32 bit unsigned integer mask. Where there is a one bit
in the mask, that child object will be drawn, where
there is a zero, it will not.
<p>
<code>ssgSelector::select(mask)</code> sets the mask,
<code>ssgSelector::getSelect()</code> returns the current
state of the mask, <code>ssgSelector::selectStep(n)</code>
sets the n'th mask bit and zeroes out all the others - effectively
causing only the n'th child object to be displayed.
<p>
It is quite common to have an ssgSelector with just one
child object that can be enabled with select(1) and disabled
with select(0).
<H2><code>class ssgTimedSelector</code> - An animation node,</H2>
This is a selector in which the selection is made as a function
of the amount of time elapsed.
<p>
SSG will draw each of the child objects in turn for some amount
of time before going onto the next node in the sequence. You
set the time for each child using:
<pre>
ssgTimedSelector::setDuration ( float time, int which_child ) ;
</pre>
...or you can set the same time for each of the child nodes using:
<pre>
ssgTimedSelector::setDuration ( float time ) ;
</pre>
(At present, times are measured in SSG update cycles - ultimately,
there will be an option to set the times in seconds - but until
I have a reasonably accurate portable timer library, I can't
easily implement this).
<p>
The animation doesn't have to start at the first child node and
end at the last. It is sometimes useful to be able to replay
just a subset of them.
<pre>
ssgTimedSelector::setLimits ( int start_child, int end_child ) ;
</pre>
You can also choose between a number of animation algorithms:
<pre>
ssgTimedSelector::setMode ( ssgAnimDirection mode ) ;
Where mode is one of:
SSG_ANIM_ONESHOT, SSG_ANIM_SHUTTLE, SSG_ANIM_SWING
</pre>
<ul>
<li>In SSG_ANIM_ONESHOT mode, the animation starts at the first child
you specified with setLimits and ends at the last child you
specified. Once the last child has been displayed, the animation
will automatically go into STOP mode and continue to display
that last object until the application intervenes.
<li>In SSG_ANIM_SHUTTLE mode, the animation goes from the start
child to the end child - but unlike SSG_ANIM_ONESHOT, when
the animation has finished displaying the last child object,
it resets to the start child and does it all over again.
SSG_ANIM_SHUTTLE animations don't stop running unless the
application stops them.
<li>In SSG_ANIM_SWING mode, the animation goes from the start
child to the end child - but when the end is reached, the
animation reverses direction and heads back to the start
again. The animation oscillates like a swing until the
application stops it.
</ul>
<p>
When all this preparation is done, the application must
control the animation:
<pre>
ssgTimedSelector::control ( ssgAnimEnum cntrl ) ;
Where 'cntrl' is one of:
SSG_ANIM_START, SSG_ANIM_STOP,
SSG_ANIM_PAUSE, SSG_ANIM_RESUME
</pre>
These controls work just like you'd expect. Start, Stop,
Pause, Resume. SSG_ANIM_START resets the animation to
the 'start' child and lets it rip. SSG_ANIM_STOP causes
it to stop wherever it is and continue to display that
child node indefinitely. SSG_ANIM_PAUSE pauses the
animation wherever it is right now and SSG_ANIM_RESUME
allows it to continue from whatever point it was paused.
<p>
When the animation is in SSG_ANIM_STOP mode, you can
use this node just like a normal ssgSelector node
using the ssgSelector API.
<p>
If you need to know which child object is currently being
displayed, you cannot call ssgSelector::getSelect() because
SSG computes the animation step only if the node is actually
on-screen. Instead call ssgTimedSelector::getStep() which
returns the currently selected child node.
<H2><code>class ssgRangeSelector</code> - A level of detail node,</H2>
This is a selector in which the selection is made automatically
based on the range to the object. This is principally used to
allow you to save time by drawing simpler versions of objects
at long ranges and more complex ones close up.
<p>
Since an <code>ssgRangeSelector</code> is a kind of
<code>ssgSelector</code>, you can find
out which version of the object was most recently drawn using
<code>getSelect()</code>. However, if the object was not drawn recently, that
may not be a very useful thing to know. Mostly, it's useful in
the post-cull callback.
<p>
You set the ranges at which each daughter object will be drawn
by passing an array of lengths to
<code>ssgRangeSelector::setRanges(float *ranges,int nranges)</code>.
The first parameter is an array of ranges and the second is the
number of elements in that array. Note that element N of the
array is the range beyond which child object N will be drawn.
The array should contain one more range than there are child
objects - and beyond the last range, nothing will be drawn.
setRanges takes a copy of your array so you can delete it after
the call. If nranges is less than the number required, the
remaining ranges will be set to infinity. You can query the
current range array with <code>float getRange(int n)</code> which
returns the n'th range in the array.
<p>
In some cases, you'd like to build several complete versions
of an object such that just one of those versions will be
drawn - and that is the default behaviour. In other cases,
you'd like some basic object to be rendered at long range
and for additional parts to be added to it at shorter ranges.
This is called 'additive' mode and it is set using
<code>setAdditive(int additive)</code>
and queried with <code>isAdditive()</code>.
<H2><code>class ssgBaseTransform</code> - Nodes with transformations.</H2>
It is common to wish to move objects around in the scene, scale and rotate
them, move their texture maps, etc. All of these operations entail
manipulating a matrix associated with the branch node and the
ssgBaseTransform contains the functionality to store and manipulate
that matrix. Applications use one of the derived classes of ssgBaseTransform
to actually do something with that matrix.
<pre>
class ssgBaseTransform : ssgBranch
{
void getTransform ( sgMat4 xform ) ;
virtual void setTransform ( sgVec3 xyz ) ;
virtual void setTransform ( sgCoord *xform ) ;
virtual void setTransform ( sgCoord *xform, float sx, float sy, float sz ) ;
virtual void setTransform ( sgMat4 xform ) ;
} ;
</pre>
You can set up the transformation matrix using <code>ssgBaseTransform::setTransform()</code>
which has versions that allow you to pass either a full-blown
4x4 matrix, a simple translation, an 'sgCoord' (which is an xyz translation
and a hpr rotation) or an sgCoord and scale factors in each of X, Y and Z
directions.
<p>
<code>ssgBaseTransform::getTransform(matrix)</code> copies the current
transform into the matrix that you provide.
<H2><code>class ssgTransform</code> - Nodes with spatial transformations.</H2>
An ssgTransform is derived from ssgBaseTransform and uses the base classes'
transform to transform all the spatial vertices and normals of it's
child nodes. This is done by applying the current transform to the
GL_MODELVIEW stack each frame.
<H2><code>class ssgTexTrans</code> - Nodes with moving texture</H2>
ssgTexTrans nodes are just like ssgTransform nodes except that the
resulting matrix is applied to the GL_TEXTURE stack rather than
the modelview stack. Hence, altering the transform moves the texture
map(s) on the descendent leaf nodes.
<H2><code>class ssgCutout</code> - turn-to-face-the-viewer nodes.</H2>
Cutout nodes will normally contain only leaf nodes that are
modelled with polygons in the X/Z plane. The ssgCutout will rotate
those polygons such that they turn to continually face the viewer.
<p>
There are actually two distinct forms of ssgCutout - depending on
what value is passed as a parameter to the constructor function.
ssgCutout(TRUE) produces an object that rotates around it's
origin such as to keep the X/Z plane parallel to the screen and
ssgCutout(FALSE) produces one that tries to stay parallel to the
screen - but which is only allowed to rotate about the Z axis.
The latter form is useful for objects with cylindrical symmetry
and the former for those with spherical symmetry.
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"><a href="ssgLeaf.html">&lt;= previous =</a></td>
<td width="34%" align="center"><a href="index.html">Return to SSG Index</a></td>
<td width="33%" align="right"><a href="state.html">= next =&gt;</a></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>

157
doc/ssg/index.html Normal file
View File

@@ -0,0 +1,157 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<center>
<H1>SSG: A Simple Scene Graph API</H1>
<H1>for OpenGL</H1>
by Steve Baker
</center>
<H2>Introduction</H2>
Simple Scene Graph (SSG) is intended to be a really simple,
low-impact, scene graph API that layers nicely on top
of OpenGL using C++ and which works with or without GLUT.
<p>
SSG is a part of <A HREF="../index.html">PLIB</A>.
<p>
This document assumes a certain degree of knowledge of
OpenGL.
<p>
SSG includes a subsidiary library of simple matix and vector
math with support for some intersection testing, field-of-view
culling and such like. This is called
<A HREF="../sg/index.html">'Simple Geometry' (SG)</A>.
SG is used extensively by SSG - but is also useful as a
standalone library.
<p>
A Scene Graph is essentially just a tree-structured database
containing a hierarchy of branches - and a bunch of leaf nodes.
Each leaf node does some OpenGL rendering - the branch nodes
are intended to manage things like: field of view (FOV) culling,
level of detail (LOD) management, transformations, and animation.
<p>
In addition, each leaf node has a structure tacked on to it
to encapsulate OpenGL state information - and that in turn
may optionally have a texture applied to it.
<p>
In addition to managing the scene graph, SSG contains code
to manage the positions of cameras, lights and other rendering
aspects of OpenGL.
<H2>Symbol Conventions.</H2>
Both SSG an SG follow conventions for symbols and tokens that
are the conventions used by OpenGL and GLUT.
<p>
Hence, all SSG symbols for classes and functions start with <code>ssg</code>
and all <code>#define</code> tokens start with <code>SSG</code>. Functions and symbols
that belong to the SG library similarly start with <code>sg</code> or <code>SG</code>.
<p>
Words within a class or function name are Capitalised and NOT
separated with underscores. Words within <code>#define</code> tokens may
be separated with underscores to make them readable.
<H2>Initialisation.</H2>
The first SSG call in any program must always be ssgInit(). Call
ssgInit only after you have obtained an OpenGL rendering context
(or called glutInit() and created a rendering window if you are
using glut).
<H2>Classes</H2>
The following class hierarchy makes up the core package - which
can be extended to add functionality or to change some underlying
mechanisms.
<pre>
class ssgBase
|__ class ssgSimpleList
| |
| |__ class ssgVertexArray
| |__ class ssgNormalArray
| |__ class ssgTexCoordArray
| |__ class ssgColourArray
| |__ class ssgIndexArray
|
|__ class ssgEntity
| |
| |__ class ssgLeaf
| | |__ class ssgVTable (deprecated)
| | |__ class ssgVtxTable
| | |__ class ssgTween
| | |__ class ssgVtxArray
| |
| |__ class ssgBranch
| |__ class ssgRoot
| |__ class ssgInvisible
| |__ class ssgSelector
| | |__ class ssgTimedSelector
| | |__ class ssgRangeSelector
| |
| |__ class ssgBaseTransform
| | |__ class ssgTransform
| | |__ class ssgTexTrans
| |
| |__ class ssgCutout
| |__ class ssgTweenController
|
|___ class ssgState
| |__ class ssgSimpleState
| |__ class ssgStateSelector
|
|___ class ssgTexture
</pre>
The general idea is that, all geometry is contained in ssgLeaf classes,
all data heirarchy is in a ssgBranch classes and all OpenGL state information
is in ssgStates.
<p>
You may not declare instances of ssgBase, ssgEntity, ssgBaseTransform,
ssgLeaf or ssgState since they are all abstract classes.
<p>
It is presumed that applications will add new kinds of leaves, branches,
states and textures to customise SSG to their needs.
<BR><BR>
Here are the further chapters:
<BR><BR>
<A HREF="ssgBase.html">The base class</A>
<BR>
<A HREF="ssgEntity.html">An entity of the graph</A>
<BR>
<A HREF="ssgLeaf.html">A leaf of the graph</A>
<BR>
<A HREF="branches.html">A branch of the graph</A>
<BR>
<A HREF="state.html">States (material etc)</A>
<BR>
<A HREF="ssgContext.html">ssgContext</A>
<BR>
<A HREF="non_class.html">Non-class functions: Loading, saving and optimizing databases</A>
<BR>
<A HREF="LoaderWriter.html">How to write plib-loaders and writers</A>
<BR><BR>
You can go through all the SSG-documentation by clicking on the "= next =&gt"-links at the bottom of each page.
<BR><BR>
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"></td>
<td width="34%" align="center"></td>
<td width="33%" align="right"><a href="ssgBase.html">= next =&gt;</a></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>

936
doc/ssg/non_class.html Normal file
View File

@@ -0,0 +1,936 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<H2>Non-class Functions.</H2>
So, with the class functions described above, it is
fairly simple to construct a scene graph. So, now that
you have one, what can you do with it?
<pre>
void ssgCullAndDraw ( ssgRoot *root ) ;
</pre>
This call deals with the entire process of rendering the database.
Your application need only call ssgInit(), build a database,
create a current context and call ssgCullAndDraw using the root node
of that database.
<H3>Intersection Testing.</H3>
Most applications need to test the scenery to see if moving
objects have collided with it - there are several ways to do
that - but they all share the same mechanisms:
<pre>
int ssgIsect ( ssgRoot *root, sgSphere *s, sgMat4 m, ssgHit **results ) ;
int ssgHOT ( ssgRoot *root, sgVec3 s, sgMat4 m, ssgHit **results ) ;
int ssgLOS ( ssgRoot *root, sgVec3 s, sgMat4 m, ssgHit **results ) ;
</pre>
These three calls implement various ways to test the database for collisions,
weapon impacts and such like. In each case, the search for a collision
starts at 'root', and the database is transformed by the matrix 'm'
before the test is evaluated - hence, 'm' is ususally the inverse of
the matrix describing the test object's location.
<ul>
<li>ssgIsect intersects a sphere against the scene.
<li>ssgHOT intersects a vertical line starting at the point 's' (remember,
in SSG, positive Z is 'up'). This is often used to compute
the Height-of-Terrain - hence the name 'HOT'.
<li>ssgLOS intersects an arbitary vector whose direction is defined by 's'.
</ul>
The result in either case is an integer telling
you how many triangles impacted the sphere/vector. If you need to know more
about these intersections, pass the address of a ssgHit * variable as
the last parameter and it will be returned pointing at a STATIC array
of ssgHit structures. Thats a confusing explanation - and an example
will help:
<pre>
ie:
ssgHit *results ;
int num_hits = ssgIsect ( root, &amp;sphere, mat, &amp;results ) ;
for ( int i = 0 ; i < num_hits ; i++ )
{
ssgHit *h = &(results [ i ]) ;
/* Do something with 'h' */
}
</pre>
Remember, you must finish using the results array before you do
another ssgIsect/ssgHOT/ssgLOS because all three functions share
the same results array.
<p>
By default, these functions only detect collisions with the front face
of polygons. You can make them detect only the back faces using:
<pre>
void ssgSetBackFaceCollisions ( bool b ) ;
</pre>
<p>
An ssgHit looks like this:
<pre>
class ssgHit
{
ssgLeaf *leaf ;
int triangle ;
sgVec4 plane ;
sgMat4 matrix ;
ssgHit ()
int getNumPathEntries () ;
ssgEntity *getPathEntry ( int i ) ;
} ;
</pre>
The 'leaf' member points at the leaf node that impacted the sphere.
The 'triangle' member tells you which triangle within the leaf
did the impacting. The 'plane' member contains the plane equation
of the impacting triangle and the 'matrix' element tells you
the net result of concatenating all the transform nodes from
the root to the leaf to the matrix you provided in the ssgIsect call.
<p>
It's possible for there to be multiple paths through the
scene graph to the leaf node. Sometimes you'll need to
look back up the tree to see nodes above the one that we actually
impacted with. Hence, you can read all the ssgEntities that were
traversed on the path from the root down to the leaf. Calling
the 'getNumPathEntries' function to find the number of nodes
along the path - and then 'getPathEntry(n)' to get the n'th entry
in the path. The 'root' node will always be the zeroth entry
in the path - and the leaf node will always be the last.
<H3>Lights.</H3>
SSG supports the eight standard OpenGL light sources as class 'ssgLight'.
Since there are only a finite number of these, they all exist all the
time - you just call:
<pre>
ssgLight *ssgGetLight ( int i ) ;
</pre>
...to get the i'th light should you need to manipulate it.
<pre>
class ssgLight
{
int isOn () ;
void on () ;
void off () ;
void setPosition ( const sgVec3 pos ) ;
void setPosition ( float x, float y, float z ) ;
void setColour ( GLenum which , const sgVec4 colour ) ;
void setColour ( GLenum which , float r, float g, float b ) ;
void setHeadlight ( int head ) ;
int isHeadlight () ;
void setSpotlight ( int spot ) ;
int isSpotlight () ;
void setSpotDirection ( const sgVec3 dir ) ;
void setSpotDirection ( float x, float y, float z ) ;
void setSpotDiffusion ( float exponent, float cutoff = 90.0f ) ;
void setSpotAttenuation ( float constant, float linear, float quadratic ) ;
} ;
</pre>
Each light can be turned on or off - or tested to see if it's on or off.
<p>
Lights are positioned with 'setPosition()' - which can be relative
to the origin of the world - or relative to the SSG camera (in 'headlight'
mode).
<p>
If the 'spotlight' mode is enabled,
then the light intensity has a certain distribution and attenuation.
These parameters can be set with the 'setSpotXxx' methods.
Otherwise, the light source is considered to be directional,
or infinitely far away (in the direction of its position).
The 'spotlight' mode is initially disabled.
<H3>Miscellany.</H3>
It's convenient to find out how much texture memory has been
consumed:
<pre>
int ssgGetNumTexelsLoaded () ;
</pre>
(Bear in mind that a texel could be 16 or 32 bits depending on
the hardware - and with MIPmapping enabled, 25% of the texels
will be in the MIPmaps - so ssgGetNumTexelsLoaded will return
a larger number than the total of the sizes of the input images
might suggest.
<H3>Loading Database Files.</H3>
To load a model file into SSG, you can either call a loader
function that is specific to the format of the file you
wish to load - or you can call 'ssgLoad' - which parses
the filename extension and calls the appropriate format-specific
loader.
<p>
At time or writing, there are MANY loaders for SSG:
<ul>
<li> ssgLoadSSG - for '.ssg' files - the 'native' SSG format.
<li> ssgLoadAC - for '.ac' files - a somewhat obscure format produced
by the 'AC3D' modelling tool - reasonably well tried
and tested.
<li> ssgLoad3ds - for '.3ds' files as produced by 3DStudio.
<li> ssgLoadASE - 3DSMAX ASCII EXPORT Version 2.00, well tested
<li> ssgLoadDXF - AutoCADs famous DXF format. well tested
<li> ssgLoadFLT - OpenFlight. Works for OpenFlight files generated by
recent versions of MultiGen - but not for those generated by some other
tools such as Designers' Workbench.
<li> ssgLoadMD2 - Quake MD2
<li> ssgLoadOBJ - Wavefront, works well
<li> ssgLoadTRI - simple Tri format from "Andy Colbournes Editor".
<li> ssgLoadX - Microsofts DirectX-Format. Most features work.
<li> ssgLoadOFF - Geomview's OFF
<li> ssgLoadM - ???
<li> ssgLoadATG - Ascii TerraGear. Used by Flight Gear Flight Sim.
<li> ssgLoadVRML1 - VRML1 format, only partially implemented.
<li> ssgLoadIV - Inventor format, only partially implemented.
<li> ssgLoadStrip - The format of a stripifier.
</ul>
<pre>
typedef ssgBranch *(*ssgHookFunc)(char *) ;
ssgEntity *ssgLoad ( const char *fname, const ssgLoaderOptions *options = NULL ) ;
ssgEntity *ssgLoadSSG ( const char *fname, const ssgLoaderOptions *options = NULL ) ;
ssgEntity *ssgLoadAC ( const char *fname, const ssgLoaderOptions *options = NULL ) ;
ssgEntity *ssgLoad3ds ( const char *fname, const ssgLoaderOptions *options = NULL ) ;
...etc...
</pre>
Minimally, all you need to do is to call ssgLoadAC/ssgLoad3ds with the
name of the file to load. However, most file formats (AC3D's
and 3Dstudio's included) lack many desirable features, and it is also often
necessary to store application-specific information in the
file.
<p>
SSG's loaders will decode the comment fields found in the
nodes of many common file formats and pass these onto the
application via 'hookfunc'. This function should decode
the string and construct whatever kind of SSG node it
considers appropriate.
<p>
Similarly, the application may wish to embellish the
ssgState of a loaded node - and since state information
rarely has a comment field in most file formats, we
pass the texture filename instead and expect the application
to construct the entire ssgState:
<pre>
void ssgSetAppStateCallback ( ssgState *(*cb)(char *) ) ;
</pre>
One common problem with file loaders is that it's
often possible to refer to a second file from inside
the first - but the <b>path</b> to that file is often
not adequately defined by the original file. Hence,
the application can specify a file path to be prepended
to all model or texture file names.
<pre>
void ssgModelPath ( char *path ) ;
void ssgTexturePath ( char *path ) ;
</pre>
You can only supply one path. If you need additional features, use the
function <code>ulFindFile</code> (for more see util-library-doc).
The last three functions simply set values in the _ssgCurrentOptions
(type ssgLoaderOptions), for example:
<pre>
inline void ssgModelPath ( const char *path )
{
_ssgCurrentOptions -> setModelDir ( path ) ;
}
</pre>
For more on ssgLoaderOptions see also the next page of this doc.
<p>
Some loaders for file formats that use texture formats not
supported by ssg use this functions to find textures:
<pre>
void ssgFindOptConvertTexture( char * filepath, char * tfname )
</pre>
It finds and optionally (= if necessary) converts the texture.
This is only really implemented for Windo$ :-(, for all others it
should just be the two lines you will find in the comment. I didn't
test it, that's the reason I commented it out. But it will warn you
when you have to convert something, so even for non-Windo$-users the
new function is already a step forward.
For the actual conversion, at first I wanted to use GIMP. So, in Deja,
I looked for "+gimp +convert +batch" and similar strings. I found
several people asking, but almost all the answer were to use
ImageMagick instead. ImageMagick is free as in beer. Also, the docs I
got with the newest gimp is extremely sparse :-(. Therefore, I tried
ImageMagick and this worked straight away. You should find ImageMagick
under
<p>
http://www.wizards.dupont.com/cristy/ImageMagick.html
<p>
Plib uses the ImageMagick application "convert". Since there are other
convert.exe-programs on my computer, I had to copy the ImageMagick
stuff into the directory of my plib-application, so that it uses the correct
convert.exe.
<p>
Most file formats contain considerable numbers of redundant
nodes (because of the way people build using these tools).
This function walks a database sub-tree multiplying
out any ssgTransform nodes and replacing them with
ssgBranch'ed - unless they have userdata associated with them.
Any branch nodes with zero kids are deleted - any with just
one kid are eliminated and the child node pushed up one level.
<pre>
void ssgFlatten ( ssgEntity *ent ) ;
</pre>
It's important for 3D performance to optimise
triangles into triangle strips or fans. Since most
file formats don't record strip/fan information,
it's useful to call:
<pre>
void ssgStripify ( ssgEntity *ent ) ;
</pre>
<H3>Saving Database Files.</H3>
Most SSG programs will simply load a file and display it in
some way - but occasionally, it's useful to be able to write
a file back out again.
<p>
To write a model file from SSG, you can either call a writer
function that is specific to the format of the file you
wish to save - or you can call 'ssgSave' - which parses
the filename extension and calls the appropriate format-specific
writer.
<p>
Saving into ssg-format is done with these functions:
<ul>
<li> ssgSaveSSG - for '.ssg' files - the 'native' SSG format.
</ul>
<pre>
int ssgSave ( char *fname, ssgEntity *ent ) ;
int ssgSaveSSG ( char *fname, ssgEntity *ent ) ;
</pre>
This returns TRUE if the operation worked - FALSE if it failed.
You will also find writers for the 3DS, AC, ASE, ATG, DXF, M, OBJ, OFF, QHI, TRI and
X formats.
<H3>Features of the file formats and status of the loaders/writers</H3>
Here comes a table of the features of all the file formats and loaders and writers.
VRML doesn't really work, so you won't find it here.
The "QHI" (QHull Input - a format used by a toll to create convex hulls)
writer only writes a point cloud and suppoorts no other features, so I also didn't add it to the table.
BTW, if you want to create convex hulls of existing geometry, you just need a few lines. Look
into PPE (prettypoly editor, on SourceForge as well), into the file ppeCoreFuncs.cxx,
function addConvexHull.
The SSG loaders/writers support all
features of the ssg-lib, so missing features mean they miss in the library (normaly because they miss in OpenGL)
The letters mean:
<BR><BR>
A = feature not in file format
<BR>
B = feature in file format and not implemented, not planned.
<BR>
C = feature in file format and not implemented, but planned.
<BR>
D = feature in file format and partly implemented or not tested.
<BR>
E = feature in file format and implemented.
<BR><BR>
And here are the features with some explanaitions:
<BR><BR>
<ul>
<li>Filled polys - Planar polygons. In the ssg-lib, this is the only primitive you can use to make "solid" looking models. This is a 2D primitive; it has no volume but an area.
<li># sides - No of sides per poly. This is of course equal to number of vertices per poly. Some formats support only triangles, some
also quads and some support n-sided polys, that is they have no restrictions.
<li>Lines - for example used for high voltage lines in a flight simulator.
This is a 1D primitive; it has no area but a length.
<li>Points - for example used for small lights.
This is a "0D" primitive; it has no length.
<li>Sub objects - The complete model may consist of several sub models.
There are several reasons one wants this, for example to make the structure of the model
clearer to humans, to easily enable humans or programs to move, colour, texture, animate etc
one part, for example one wheel of the complete model.
<li>Hierarchie - You not only have sub objects, but there is a parent-kid relation between some,
for example, the upper arm of a human model may be "under" the body and the lower arm "under" the upper arm etc.
<li>DAG - direct acyclic graph means that the hierarchie may not only be a tree where
each node has exactly one parent (apart from the root, which has zero parents), but that nodes may have more than one parent.
Used for example in cars to have the wheel geometry only once. If the one wheel-node has four parents, this means
it will be rendered four times when the scene is drawn.
<li>Colours - Things can have a colour. Uncoloured objects are always white.
<li>Textures - Texture things with 2D bitmaps.
<li>Texture coordinates - The texture mapping is arbitrary.
<li>Texture coord. per face AND vertex - If a vertex is part of three faces, then this feature
allows it to have three texture coordinates. For example, a cube may have 18 texture coordinates, even if it has much fewer vertices.
<li>Texture per face - each face may have another texture bitmap. Formats (and the ssg lib) that
don't have this have to increase the number of nodes to get this effect.
<li>Transparency - Some polys may be partially or completely transparent.
<li>Animation - There may be many different sorts of animation.
<li>Billboards - geometry that automatically rotates so it always faces the viewer. Normaly flat.
As an example, can be used to model trees in a driving simulator.
<li>LOD - stands for "Level of Detail" and means there can be geometries of varying
complexity for the model, so for example, if it is near the viewer you use many polys
and if it is far away you use few polys so that the impact on rendering speed is low.
<li>Other switches - switch means you can switch the visibility of some nodes on and off.
<li>Ascii or Binary - A stands for Ascii, B for Binary files.
</ul>
<table width="100%" border="1" cellspacing="0" cellpadding="0">
<tr>
<td>&nbsp;Feature</td>
<td>&nbsp;3DS<BR>load</td>
<td>&nbsp;AC<BR>load/<BR>save</td>
<td>&nbsp;ASC<BR>save</td>
<td>&nbsp;ASE<BR>load/<BR>save</td>
<td>&nbsp;ATG<BR>load/<BR>save</td>
<td>&nbsp;DXF<BR>load</td>
<td>&nbsp;DXF<BR>save</td>
<td>&nbsp;FLT<BR>load</td>
<td>&nbsp;M<BR>load/<BR>save</td>
<td>&nbsp;MD2<BR>load</td>
<td>&nbsp;OBJ<BR>load</td>
<td>&nbsp;OBJ<BR>save</td>
<td>&nbsp;OFF<BR>load/<BR>save</td>
<td>&nbsp;SSG<BR>load/<BR>save</td>
<td>&nbsp;Strip<BR>load</td>
<td>&nbsp;TRI<BR>load/<BR>save</td>
<td>&nbsp;X<BR>load</td>
<td>&nbsp;IV<BR>load</td>
<td>&nbsp;VRML1<BR>load</td>
</tr>
<tr>
<td>&nbsp;Filled polys</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E(5)</td>
<td>&nbsp;E</td>
<td>&nbsp;E/D</td>
<td>&nbsp;?</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
</tr>
<tr>
<td>&nbsp;# sides</td>
<td>&nbsp;</td>
<td>&nbsp;n/3</td>
<td>&nbsp;3</td>
<td>&nbsp;3</td>
<td>&nbsp;n</td>
<td>&nbsp;3,4(4)</td>
<td>&nbsp;3</td>
<td>&nbsp;n</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;n?</td>
<td>&nbsp;3</td>
<td>&nbsp;n</td>
<td>&nbsp;n(4)</td>
<td>&nbsp;</td>
<td>&nbsp;3?</td>
<td>&nbsp;n</td>
<td>&nbsp;3,4</td>
<td>&nbsp;3,4</td>
</tr>
<tr>
<td>&nbsp;Lines</td>
<td>&nbsp;A</td>
<td>&nbsp;D/C</td>
<td>&nbsp;C</td>
<td>&nbsp;E</td>
<td>&nbsp;A(?)</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;A?</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;A</td>
<td>&nbsp;B</td>
<td>&nbsp;B</td>
</tr>
<tr>
<td>&nbsp;Points</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;C</td>
<td>&nbsp;A(?)</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;B</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;A</td>
<td>&nbsp;B</td>
<td>&nbsp;B</td>
</tr>
<tr>
<td>&nbsp;Sub objects</td>
<td>&nbsp;E</td>
<td>&nbsp;E/D</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;A(?)</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;C-D</td>
<td>&nbsp;C-D</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
</tr>
<tr>
<td>&nbsp;Hierarchie</td>
<td>&nbsp;A</td>
<td>&nbsp;E/D</td>
<td>&nbsp;E?</td>
<td>&nbsp;E</td>
<td>&nbsp;A(?)</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;C</td>
<td>&nbsp;C</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;C</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
</tr>
<tr>
<td>&nbsp;DAG</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A(?)</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;A(?)</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
</tr>
<tr>
<td>&nbsp;Colours</td>
<td>&nbsp;E</td>
<td>&nbsp;E/D</td>
<td>&nbsp;E?</td>
<td>&nbsp;E</td>
<td>&nbsp;?</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;D(1)</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;D</td>
<td>&nbsp;C</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;E</td>
<td>&nbsp;B</td>
<td>&nbsp;B</td>
</tr>
<tr>
<td>&nbsp;Textures</td>
<td>&nbsp;D</td>
<td>&nbsp;E/D</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;D</td>
<td>&nbsp;C</td>
<td>&nbsp;B</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
</tr>
<tr>
<td>&nbsp;Texture coord.</td>
<td>&nbsp;D</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;E</td>
<td>&nbsp;C</td>
<td>&nbsp;B</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
</tr>
<tr>
<td>&nbsp;Texture coord.<BR>per face<BR>AND vertex</td>
<td>&nbsp;D(?)</td>
<td>&nbsp;D(?)</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;E</td>
<td>&nbsp;C</td>
<td>&nbsp;B(?)</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;C</td>
<td>&nbsp;E</td>
<td>&nbsp;E</td>
</tr>
<tr>
<td>&nbsp;Texture per face</td>
<td>&nbsp;</td>
<td>&nbsp;D(?)/-</td>
<td>&nbsp;C</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;B(?)</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;C</td>
<td>&nbsp;B</td>
<td>&nbsp;A</td>
</tr>
<tr>
<td>&nbsp;Transparency</td>
<td>&nbsp;D</td>
<td>&nbsp;D</td>
<td>&nbsp;D</td>
<td>&nbsp;D</td>
<td>&nbsp;A(?)</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;A?</td>
<td>&nbsp;A?</td>
<td>&nbsp;B(?)</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;E</td>
<td>&nbsp;B</td>
<td>&nbsp;B</td>
</tr>
<tr>
<td>&nbsp;Animation</td>
<td>&nbsp;B</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;B(2)</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;A?</td>
<td>&nbsp;A?</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;B or C</td>
<td>&nbsp;B</td>
<td>&nbsp;A</td>
</tr>
<tr>
<td>&nbsp;Billboards</td>
<td>&nbsp;</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;A</td>
<td>&nbsp;B</td>
<td>&nbsp;A</td>
</tr>
<tr>
<td>&nbsp;LOD</td>
<td>&nbsp;</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;A</td>
<td>&nbsp;B</td>
<td>&nbsp;B</td>
</tr>
<tr>
<td>&nbsp;Other switches</td>
<td>&nbsp;</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;D</td>
<td>&nbsp;A</td>
<td>&nbsp;?</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;E</td>
<td>&nbsp;A</td>
<td>&nbsp;</td>
<td>&nbsp;A</td>
<td>&nbsp;D</td>
<td>&nbsp;D</td>
</tr>
<tr>
<td>&nbsp;A=Ascii or<BR>B=Binary</td>
<td>&nbsp;B</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;B</td>
<td>&nbsp;A</td>
<td>&nbsp;B</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
<td>&nbsp;B</td>
<td>&nbsp;?</td>
<td>&nbsp;A</td>
<td>&nbsp;A+B(3)</td>
<td>&nbsp;A</td>
<td>&nbsp;A</td>
</tr>
</table>
footnotes:
<BR><BR>
(1) problematic for ancient files
<BR>
(2) DOFs are not implemented
<BR>
(3) There is an ascii- and a binary X-file-format.
I only implemented the ascii-one and probably wont implement the
binary one. There is a free (as in beer) converter by Microsoft
running under Windo$.
<BR>
(4) It is possible to have polys with an arbitrary number of vertices in ssg.
But then you may only use one poly per node. Also, you can not mix 3 and 4 sided polys in one node.
<BR>
(5) Some OpenGL modes, like for example Quads, are ignored. This means some parts of the model
may dissappear when saving into DXF files.
<BR><BR>
Additional info on some formats:
<BR><BR>
ATG:
<BR>
ATG stands for ascii TerraGear. Loading of TriStrips and TriFans not implemented yet.
Untextured parts are lost, for example when you save and load
Steve Bakers Tuxedo, you will loose the feet, since they are not
textured.
The files written are not optimal, they use no strips/fans,
only have triangles etc.
Also, one vertex that needs several texture coords is written out several
times.
<BR><BR>
3ds-writer:
<BR>
I (Per) have one basicly working, but since there seems to be so much else to do
with plib right now, I'm a bit reluctant to put my energy into it. Also, the
only 3ds-loader I've tested files written by it with is the one in ssg :-)
<BR><BR>
OFF loader:
<BR>
Warning: There are two formats called OFF! We support the OFF from GeomView,
not the one from DEC.
We support 2D and 3D, but no higher dimensions.
<BR><BR>
.SSG file loader/writer:
<BR>
We number .SSG file formats so that for example the loaders knows
what to expect. Until now, there is
<BR>
format "0" - Used by plib 1.2.0, 1.3.x
<BR>
format "1" - Will be used by 1.4.0
<BR>
Currently, we are between "0" and "1". Normally, we want to keep at least our loader
compatible, but the changes from zero to one would bloat the code so much and it seems
.SSG file version zero were only used by people for temporary files, so we decided to make an incompatible break.
If you need to read or write version zero, either get plib from SVN with the date 14.1.2001, or, if you are a Windo$ person, get
<a href="prettypoly.sourceforge.net/download/ppewinbin_ssg_version_zero.zip">prettypoly.sourceforge.net/download/ppewinbin_ssg_version_zero.zip</a>
This allows you to save and load .ssg version zero.
<BR>
One of the new features in format "1" might be worth mentioning.
It is now possible to load and save *any* class derived from ssgBase (notably
the ssgAux node types).
All that is needed for saving to work is an implementation of the virtual save() method.
Loading requires the corresponing load() method, and also means for creating
an instance of the class, which is accomplished
by a call to ssgRegisterType().
See the ssgAux implementation for example usage;
the convenience function ssgaInit() registers the ssgAux classes.
<BR><BR>
.X file writer:
<BR>
Planned.
<BR><BR>
.X file loader:
<BR>
The .X-entity "Frame" is not yet implemented.
If you convert files into .X with "3D Exploration", you can check the
option "without frames".
<BR><BR>
VRML1/IV file loaders:
<BR>
These are only partially implemented, and are useful primarily for
mesh-based models (textured or untextured). It would be useful to add
material support to these loaders, as well as explicit normal definition.
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"><a href="ssgContext.html">&lt;= previous =</a></td>
<td width="34%" align="center"><a href="index.html">Return to SSG Index</a></td>
<td width="33%" align="right"><a href="LoaderWriter.html">= next =&gt;</a></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>

233
doc/ssg/ssgBase.html Normal file
View File

@@ -0,0 +1,233 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<H2><code>class ssgBase</code> - The Universal Abstract Base Class.</H2>
All significant SSG classes are derived from ssgBase - which offers a type testing
mechanism and a means to print out the tree hierarchy in human-readable form,
or to save/load it to/from disk.
<pre>
class ssgBase
{
void ref () ;
void deRef () ;
int getRef () ;
int isA ( int ty ) ;
int isAKindOf ( int ty ) ;
int getType (void) ;
virtual char *getTypeName(void) ;
ssgBase *getUserData () ;
void setUserData ( ssgBase *user_data ) ;
void setName ( char *nm ) ;
char *getName () ;
const char *getPrintableName () ;
virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ;
virtual int load ( FILE *fd ) ;
virtual int save ( FILE *fd = stderr ) ;
ssgBase *clone ( int clone_flags ) ;
} ;
</pre>
<H3>Reference Counting.</H3>
All SSG classes are reference counted - that means that whenever you
connect a node into the scene graph with <code>ssgBranch::addKid()</code>, we
increment its reference count and each time we remove a node from
the graph with <code>ssgBranch::removeKid()</code>, we decrement the count -
and if it's zero, we'll delete the node to recover memory.
<p>
Sometimes, you need a node to stay in memory even though it may be
be disconnected from the scene graph. You can achieve that by
calling <code>ssgBase::ref()</code> to increment the reference count.
If you later find you don't need that node anymore then you may
<code>ssgBase::deRef()</code> it. If you <code>ssgBase::deRef()</code>
a node to zero, SSG won't automatically delete it - you still need to use
<code>delete</code> to do that. Since such deletions are recursive,
you may delete an entire sub-branch with a single call.
<p>
Instead of using <code>ssgBase::deRef()</code> directly, you should use
<code>ssgDeRefDelete()</code> which automatically deletes the node if
the reference count drops to zero.
<p>
You can read the current ref count for a node
using <code>ssgBase::getRef()</code>.
<H3>Names.</H3>
It's often useful to attach an ASCII name to a node in the
scene graph - this is often derived from a name field in
whatever modelling tool was used to create the object.
<code>ssgBase::setName(s)</code> sets the name,
<code>ssgBase::getName()</code> returns it.
<H3>User Data</H3>
Although one can derive a new C++ class from an SSG class and thereby customise
it's behavior, it's often more convenient to simply attach application-specific
data structures to a basic entity. Two functions are provided:
<code>ssgBase::getUserData()</code> and <code>ssgBase::setUserData(data)</code>.
<p>
Notice that user data is of class ssgBase - which means that user data
can be named, ref-counted - and can in turn have user data of it's own.
This allows user data to be formed into linked lists when multiple
user data items need to be attached to a single node.
<H3>Destructor Functions</H3>
When an SSG entity is NOT connected into the scene graph in any
way, then the correct way to get rid of it and free up memory
is to call it's destructor function. However, when the entity
is included into the scene graph, you should disconnect it
from the tree and let the reference count mechanism take care
of the cleanup.
<H3>Type Names</H3>
SSG frequently needs to know what kind of an object an ssgBase is.
Since C++ programs may create new classes that inherit from SSG
classes, we provide several functions to make run time type determination
possible. There is an external function for each type that
returns the type token for that type:
<pre>
int ssgTypeBase () ;
int ssgTypeEntity () ;
int ssgTypeLeaf () ;
int ssgTypeVTable () ;
int ssgTypeVtxTable () ;
int ssgTypeDisplayList() ;
int ssgTypeBranch () ;
int ssgTypeBaseTransform ();
int ssgTypeTransform () ;
int ssgTypeTexTrans () ;
int ssgTypeSelector () ;
int ssgTypeTimedSelector () ;
int ssgTypeRangeSelector () ;
int ssgTypeRoot () ;
int ssgTypeCutout () ;
</pre>
Now, you can use the <code>ssgBase::isA(type)</code> or <code>ssgBase::isAKindOf</code>
to test the type of the node. For example, if you want to test
whether a node is a Leaf node or a Branch node, you can do this:
<pre>
if ( mynode -> isAKindOf ( ssgTypeLeaf() ) )
printf ( "Leaf node\n" ) ;
else
if ( mynode -> isAKindOf ( ssgTypeBranch() ) )
printf ( "Branch node\n" ) ;
else
printf ( "Something else\n" ) ;
</pre>
Notice that if you ran that code on (say) an ssgSelector, then it'll
print "Branch node" since the Selector class is derived from the Branch
class. If you wanted to tell if a node was *exactly* a Branch node -
and not from a derived class, then you could use:
<pre>
if ( mynode -> isA ( ssgTypeBranch() ) )
printf ( "Branch node\n" ) ;
</pre>
Finally, you can actually read the type of a node - either as a
token (using <code>ssgBase::getType()</code>) or as an ASCII string
(using <code>ssgBase::getTypeName()</code>). The latter is very useful
for debug routines:
<pre>
printf ( "ERROR - something wrong with my '%s' node.\n",
mynode -> getTypeName () ) ;
</pre>
<H3>Printing</H3>
It's sometimes useful during debug to print a section of the
scene graph so you can examine it. <code>ssgBase::print(fd, indent, how_much)</code>
does that for you - it prints out the node itself - and anything
connected beneath it in the scene graph. <code>fd</code> is the
file descriptor to print to (defaults to stderr) and
<code>indent</code> is a string that will prefix all output lines
- and is used internally within SSG to make printout of tree
structures more legible by indenting them.
<p>
The values for the parameter <code>how_much</code> may be 0,1,2,3 or 4
and determine how much is printed, with 0 meaning little
and 4 meaning much.
<BR>
For how_much = 0, basic information of the branches is printed
<BR>
For 1, additionally there is: basic leaf info, state pointers
<BR>
For 2, additionally there is: states, user data, number of parents,
bSphere
<BR>
For 3, additionally there is: Reference count
<BR>
For 4, additionally there is: contents of vertex-, normal-, colour-,
texCoord- and index-arrays
<BR>
Experience tells that for <code>how_much = 0</code> you get very little
out put, with 1, 2 and 3 a manageable amount and with 4 a huge amount,
up to 100 MB for a medium sized model.
<p>
It would be unwise
to attempt to parse the output of <code>ssgBase::print</code> into another
program since it is only intended for human consumption and the
format may change dramatically between revisions of SSG.
<H3>Cloning:</H3>
All classes derived from ssgBase have a member function
<code>ssgBase *clone(int clone_flags)</code> which new's a
new object of that class as a copy of the calling object.
<p>
The 'clone_flags' is a set of tokens that you 'OR' together to
specify how 'deep' you want the cloning to go. By default
(with clone_flags==0), only the object itself is cloned and
the clone will simply point to the same child structures as
the original object. However, if you OR in 'SSG_CLONE_RECURSIVE',
then all ssgEntities beneath this one will also be cloned.
ORing in SSG_CLONE_GEOMETRY will cause all the per-vertex data
at the leaf nodes to also be cloned. SSG_CLONE_STATE causes
ssgState objects to be cloned also, SSG_CLONE_STATE_RECURSIVE
also causes states pointed to by other states to be copied,
SSG_CLONE_USERDATA causes user data attached to
the original node to be referenced (NOT copied) by the clone -
otherwise, user data for the clone is set to NULL. Finally,
SSG_CLONE_TEXTURE causes texture maps to be replicated also.
<p>
Most copy operations will typically use either zero, SSG_CLONE_RECURSIVE
or (SSG_CLONE_RECURSIVE | SSG_CLONE_GEOMETRY)
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"><a href="index.html">&lt;= previous =</a></td>
<td width="34%" align="center"><a href="index.html">Return to SSG Index</a></td>
<td width="33%" align="right"><a href="ssgEntity.html">= next =&gt;</a></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>

183
doc/ssg/ssgContext.html Normal file
View File

@@ -0,0 +1,183 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<H2>ssgContext</H2>
A 'context' is a set of parameters that relate to rendering
one SSG image. It contains the camera parameters and some
default state information.
<pre>
class ssgContext
{
ssgContext () ;
void forceBasicState () { basicState -> force () ; }
void makeCurrent () ;
int isCurrent () ;
void overrideTexture ( int on_off ) ;
void overrideCullface ( int on_off ) ;
int textureOverridden () { return ovTexture ; }
int cullfaceOverridden () { return ovCullface ; }
void setCullface ( int on_off ) { cullFace = on_off ; }
int cullfaceIsEnabled () { return cullFace ; }
sgFrustum *getFrustum () { return frustum ; }
void getNearFar ( float *n, float *f ) ;
void getFOV ( float *w, float *h ) ;
void getOrtho ( float *w, float *h ) ;
void setNearFar ( float n, float f ) ;
void setOrtho ( float w, float h ) ;
void setFOV ( float w, float h ) ;
int isOrtho () { return orthographic ; }
void getCameraPosition ( sgVec3 pos ) ;
void setCamera ( sgMat4 mat ) ;
void setCamera ( sgCoord *coord ) ;
void loadProjectionMatrix () ;
void loadModelviewMatrix () ;
void getProjectionMatrix ( sgMat4 dst ) ;
void getModelviewMatrix ( sgMat4 dst ) ;
void clrClipPlane ( int i ) ;
void setClipPlane ( int i, sgVec4 plane ) ;
int isSetClipPlane ( int i ) ;
float *getClipPlane ( int i ) ;
} ;
</pre>
In all 3D rendering, you need the concept of a virtual camera - or
eyepoint. This is set up in SSG with the following calls:
<pre>
void ssgContext::setFOV ( float w, float h ) ;
void ssgContext::setNearFar( float n, float f ) ;
void ssgContext::setCamera ( sgCoord *coord ) ;
</pre>
The setFOV call sets up the vertical and horizontal fields
of view (in degrees), setNearFar sets the near and far
clip planes (in whatever units your model is built in).
Finally, you can position the virtual camera relative to
the database origin using setCamera.
<p>
Very often, an SSG program contains other non-SSG routines that
are accessing OpenGL state. Even a menu drawn using PUI or an
overlay in raw OpenGL or using FNT will change the current
state of OpenGL. Since SSG does 'lazy' state changes (only
updating those states that need to be changed) - it will be
fooled into no setting some things that some ssgLeaf nodes
really need.
<p>
This isn't a problem so long as the application doesn't change states
DURING ssgCullAndDraw (by using derived class member functions, callbacks,
etc). If you need to do that then be sure to call forceBasicState()
before you return to SSG.
<pre>
void ssgContext::forceBasicState ( void ) ;
</pre>
<p>
During testing, you sometimes need to disable texture rendering,
or backface culling:
<pre>
void ssgContext::overrideTexture ( int on_off ) ;
void ssgContext::overrideCullface ( int on_off ) ;
</pre>
The six standard OpenGL user-defined clipping planes
can be setup and managed automatically using these
calls:
<pre>
void clrClipPlane ( int i ) ;
void setClipPlane ( int i, sgVec4 plane ) ;
int isSetClipPlane ( int i ) ;
float *getClipPlane ( int i ) ;
</pre>
setClipPlane takes the number of the plane (0 through 5) and
a plane equation which will be applied at the next cull/draw
cycle. clrClipPlane removes a clip plane. isSetClipPlane inquires
about whether the i'th plane is turned on and getClipPlane returns
the plane that's currently set as the i'th plane.
<p>
Once you have your context set up as you'd like, you have to
make it become the 'current' context.
<pre>
void ssgContext::makeCurrent () ;
</pre>
There is a non-class function:
<pre>
ssgContext *ssgGetCurrentContext() ;
</pre>
...which returns the current context.
<p>
The 'ssgContext' class was not present in PLIB 1.0.xx, so (for
backwards compatibility) there is an initial context assigned
inside ssgInit(). In addition to the member functions of ssgContext,
there are a number of equivelent global functions that are
equivelent to ssgGetCurrentContext()->member_function(). These
functions are deprecated for new code:
<pre>
inline void ssgForceBasicState () ;
inline void ssgGetCameraPosition ( sgVec3 pos ) ;
inline void ssgOverrideTexture ( int on_off ) ;
inline void ssgOverrideCullface ( int on_off ) ;
inline void ssgGetNearFar ( float *n, float *f ) ;
inline void ssgGetFOV ( float *w, float *h ) ;
inline void ssgSetFOV ( float w, float h ) ;
inline void ssgSetOrtho ( float w, float h ) ;
inline void ssgSetNearFar ( float n, float f ) ;
inline void ssgSetCamera ( sgMat4 mat ) ;
inline void ssgSetCamera ( sgCoord *coord ) ;
inline void ssgLoadProjectionMatrix () ;
inline void ssgLoadProjectionMatrix ( sgFrustum *f ) ;
inline void ssgGetProjectionMatrix ( sgMat4 dst ) ;
inline void ssgGetModelviewMatrix ( sgMat4 dst ) ;
inline void ssgLoadModelviewMatrix () ;
inline void ssgLoadModelviewMatrix ( sgMat4 mat ) ;
</pre>
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"><a href="state.html">&lt;= previous =</a></td>
<td width="34%" align="center"><a href="index.html">Return to SSG Index</a></td>
<td width="33%" align="right"><a href="non_class.html">= next =&gt;</a></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>

146
doc/ssg/ssgEntity.html Normal file
View File

@@ -0,0 +1,146 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<H2><code>class ssgEntity</code> - A Node in the Tree.</H2>
All nodes in the SSG scene graph are ssgEntities.
<pre>
clas ssgEntity : public ssgBase
{
public:
ssgEntity (void) ;
virtual ~ssgEntity (void) ;
int getTraversalMask () ;
void setTraversalMask ( int t ) ;
void setTraversalMaskBits ( int t ) ;
void clrTraversalMaskBits ( int t ) ;
virtual void recalcBSphere (void) ;
int isDirtyBSphere (void) ;
void dirtyBSphere () ;
sgSphere *getBSphere () ;
virtual int getNumKids (void) ;
int getNumParents () ;
ssgBranch *getParent ( int p ) ;
ssgBranch *getNextParent () ;
virtual void cull ( sgFrustum *f, sgMat4 m, int test_needed ) ;
virtual void isect ( sgSphere *s, sgMat4 m, int test_needed ) ;
virtual void hot ( sgVec3 s, sgMat4 m, int test_needed ) ;
} ;
</pre>
<H3>The tree structure.</H3>
Every entity has a list of parent entities and (conceptually), a list of
child entities ("kids"). In practice, ssgRoot nodes never have parents
and ssgLeaf nodes never have kids.
<p>
The structure of the scene graph permits the same node to be inserted
into the graph in multiple locations. This is useful for saving
space when the same object is needed many times in the scene.
Hence, any given node may have more than one parent node.
<p>
You can traverse the list of parent nodes
using <code>ssgEntity::getNumParents()</code> to find out the number
of parents this node has and
<code>ssgEntity::getParent(n)</code> to locate the n'th parent.
<p>
Alternatively, after calling getParent, you can call getNextParent
to get the N+1'th parent node - it returns NULL when no more parents
are available.
<p>
As a convenience for general tree-walking routines, there is a
<code>ssgEntity::getNumKids()</code> call - which will always
return zero on leaf nodes. You cannot actually get kid nodes unless
the node is some kind of ssgBranch.
<H3>Traversals</H3>
Much of the work done on an SSG scene graph entails 'traversing'
the tree structure. This is done most commonly to display the
scene using OpenGL - but is also done when doing intersection
testing and other operations.
<p>
It's quite useful to be able to limit the traversal so that
certain nodes do not get tested. This can save time - or
prevent undesirable side-effects.
<p>
Each entity has a 'traveral mask' - which is a simple integer
with one bit per kind of traversal. At present, there are
three kinds of traversal:
<pre>
SSGTRAV_CULL -- Culling to the field of view.
SSGTRAV_ISECT -- General intersection testing.
SSGTRAV_HOT -- Height-over-terrain testing.
</pre>
You can directly set or get the traversal mask with <code>ssgEntity::setTraversalMask(m)</code>
<code>ssgEntity::getTraversalMask()</code>. You can set an individual traversal bit using
<code>ssgEntity::setTraversalMaskBits(m)</code> or clear one using
<code>ssgEntity::clrTraversalMaskBits(m)</code>.
<H3>Bounding Sphere</H3>
Quite a few graphics algorithms can be accellerated using a
bounding sphere. The standard ssgEntity uses bounding
spheres to do field-of-view and intersection testing.
<p>
Clearly one does not want to recompute the bounding sphere
every frame - just some objects do change their size over
time. Hence, the bounding sphere is lazily evaluated.
<p>
Whenever you do something to change the size or shape of an entity,
you should call <code>ssgEntity::dirtyBSphere()</code>. This will
mark this entity's sphere as invalid ("dirty") and also, walk
backwards up the scene graph tree making all the nodes above
this one dirty too. The next time SSG needs to know the bounding
sphere size, it'll recompute it.
<p>
If you'd prefer for the bounding sphere recalculation to be
done immediately, then you can call <code>ssgEntity::recalcBSphere()</code>
and it will be done immediately. Branch nodes like ssgTransforms will
automatically dirty their bounding spheres when necessary. Leaf nodes
generally do not.
<p>
When anyone needs to know the bounding sphere size for a node,
they'll call <code>ssgEntity::getBSphere()</code> - which will
recaclulate the Bsphere if it needs to.
<H3>Culling and Drawing</H3>
The actual tree-traversal, culling and rendering is handled by
a virtual function <code>ssgEntity::cull()</code> - calling
this on the root node in the scene graph causes the entire
scene to be rendered in an efficient manner.
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"><a href="ssgBase.html">&lt;= previous =</a></td>
<td width="34%" align="center"><a href="index.html">Return to SSG Index</a></td>
<td width="33%" align="right"><a href="ssgLeaf.html">= next =&gt;</a></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>

305
doc/ssg/ssgLeaf.html Normal file
View File

@@ -0,0 +1,305 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<H2><code>class ssgLeaf</code> - Leaf nodes.</h2>
Leaf nodes are those that actually make OpenGL calls
or take other 'rendering' actions - they contain
all of the geometric information in the scene.
<pre>
class ssgLeaf : public ssgEntity
{
public:
int getExternalPropertyIndex ()
int isTranslucent ()
int hasState ()
ssgState *getState ()
void setState ( ssgState *st )
virtual float *getVertex ( int i )
virtual float *getNormal ( int i )
virtual float *getColour ( int i )
virtual float *getTexCoord ( int i )
virtual int getNumTriangles() ;
virtual void getTriangle ( int n, short *v1, short *v2, short *v3 )
virtual int getNumLines () ;
virtual void getLine ( int n, short *v1, short *v2 ) ;
int getNumLines () ;
void getLine ( int n, short *v1, short *v2 ) ;
virtual void transform ( sgMat4 m )
void setCullFace ( int cf )
int getCullFace ()
void makeDList () ;
void deleteDList () ;
GLuint getDListIndex () ;
} ;
</pre>
<H3>Pre- and Post-Draw Callbacks.</H3>
It's frequently useful to have an application function called
just before and/or just after a leaf node is rendered.
<pre>
typedef int (*ssgCallback)( ssgEntity * ) ;
ssgCallback getCallback ( int which ) ;
void setCallback ( int which, ssgCallback cb ) ;
</pre>
(Where 'which' is either SSG_CALLBACK_PREDRAW for a function that's
to be called before the node is drawn or SSG_CALLBACK_POSTDRAW for
one that's called after rendering.) In both cases, the function will
be passed a pointer to the leaf node that's about to be drawn, and
(in the case of PREDRAW callbacks), may return FALSE to prevent the
node from being drawn, or TRUE to have it draw normally.
<H3>Display Lists</H3>
A leaf node is normally rendered in 'immediate' mode in OpenGL,
so that changes you make to the leaf will be reflected on the
screen on the next occasion that it's drawn. However, on some
graphics hardware, it's more efficient to create an OpenGL
display list for each leaf node. This can be managed by
calling <code>ssgLeaf::makeDList()</code>. If you want to
make changes to the Leaf, you'll have to call makeDList again
since OpenGL does not support the editing of display lists.
<p>
You can call <code>ssgLeaf::deleteDList()</code> to stop
this leaf from being display listed from now on and to
free up the display list memory.
<code>ssgLeaf::getDListIndex()</code> returns the OpenGL
display list handle - or zero if no display list exists
for this leaf.
<p>
If you change a leaf node's geometry when it has an
active display list without calling either deleteDList
or makeDList again, then any subsequent operation
involving rendering this node could fail.
<H3>Face Culling.</H3>
By default, ssgLeaf nodes are back-face culled, you can change
that for any given node using <code>ssgLeaf::setCullFace(cf)</code>
where <code>cf</code> is TRUE to enable backface culling,
FALSE to disable it. You can test the state of face culling
using <code>ssgLeaf::getCullFace()</code>.
<H3>State Management.</H3>
OpenGL supports a wide selection of state information - things like
texture, materials and such. All of this information is held in a
separate SSG class hierarchy: 'ssgState'. Each leaf has a state
which it sets up before drawing the geometry that the leaf contains.
<p>
Nodes may also be stateless - but that isn't useful for any
of the existing SSG leaf node types.
<p>
You can set the state for a node using <code>ssgLeaf::setState(state)</code>
and query it using <code>ssgLeaf::getState()</code>. You can ask if
a node has state information attached using <code>ssgLeaf::hasState()</code>.
<p>
Since OpenGL does not render translucent object well when Z-buffering
is enabled, it's often useful to know if an object is translucent.
<code>ssgLeaf::isTranslucent()</code> handles that test.
<p>
It is often useful to tag ssgState's with external properties - and
you can retrieve the property of a leaf's state using
<code>ssgLeaf::getExternalPropertyIndex()</code>.
<H3>Querying Geometry</H3>
The actual storage format for geometry in classes derived from
ssgLeaf varies from class to class. However, it's very useful
to be able to query the geometry in an implementation-independent
manner.
<p>
Although classes derived from ssgLeaf are entitled to store their
geometry in any form, all of them are required to respond to
queries about basic triangle primitives.
<p>
Firstly, you can get a count of the number of triangles in this
leaf using <code>ssgLeaf::getNumTriangles()</code>. Each triangle
has an index number for each vertex which can be queried using
<code>ssgLeaf::getTriangle(n,&amp;v1,&amp;v2,&amp;v3)</code> which copies
the 'short' indices for the n'th triangle's three vertices into
v1, v2 and v3.
<p>
Once you know the indices of a triangle's vertices, you can ask for
more information about that vertex using
<code>ssgLeaf::getVertex(i)</code>,
<code>ssgLeaf::getNormal(i)</code>,
<code>ssgLeaf::getColour(i)</code>, and
<code>ssgLeaf::getTexCoord(i)</code>. These calls allow you to
retrieve the i'th vertex, normal, colour or texture coordinate as
a short floating point array. (3 elements for Vertex and Normal,
4 elements for Colour (RGBA) and two elements for a texture coordinate.
<p>
Analogous to getting the triangles,
you may use <code>ssgLeaf::getNumLines ()</code>
and <code>ssgLeaf::getLine ( int n, short *v1, short *v2 ) ;</code>
to get the number of lines and to get the n. line.
<p>
You can transform all the vertices of a leaf each frame by placing
an ssgTransform node above the leaf in the scene graph - but for
transformations that never change, it's more efficient to pre-transform
the vertices in the leaf node.
<code>ssgLeaf::transform(matrix)</code> permenantly transforms all
the vertices and normals of this leaf by multiplying them by the
matrix. (In the case of the normals, the translation part of the
matrix is ignored).
<p>
It's inadvisable to repeatedly transform a leaf using <code>transform</code>
since roundoff error will be accumulated with bad consequences (eventually).
In those cases, use an ssgTransform node.
<H2><code>class ssgVtxTable</code> - A Vertex-table leaf node.</H2>
This class allows one to represent leaf geometry as arrays of vertex,
normal, texture coordinate and colour data. As a derived class of
ssgLeaf, Vertex Tables add a constructor function to take the
pre-computed vertex data:
<pre>
ssgVtxTable ( GLenum ty, ssgVertexArray *vl,
ssgNormalArray *nl,
ssgTexCoordArray *tl,
ssgColourArray *cl ) ;
</pre>
'ty' is an OpenGL primitive type (such as one might pass to glBegin):
<pre>
GL_POLYGON
GL_TRIANGLE_FAN
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_QUAD_STRIP
GL_QUADS
</pre>
the remaining arguments are lists of (x,y,z) vertices, (nx,ny,nz)
normals, (s,t) texture coordinates and (r,g,b,a) colours.
<H2><code>class ssgVertexArray/ssgNormalArray/ssgTexCoordArray/ssgColourArray</code> - Vertex data storage classes.</H2>
These four classes are used for storing vertex data for ssgVtxTable nodes.
Each class implements an extensible array that grows as you add data to
it.
<pre>
ssg*Array::ssg*Array ( int init = 3 ) ;
</pre>
When you construct the array, you may specify an initial size for it,
the default is three elements. If you don't know how big the array
is, don't worry about it - but if you do know, there are time and
storage space advantages to telling the class the exact number.
<pre>
void ssgVertexArray ::add ( sgVec3 data ) ;
void ssgNormalArray ::add ( sgVec3 data ) ;
void ssgTexCoordArray::add ( sgVec2 data ) ;
void ssgColourArray ::add ( sgVec4 data ) ;
</pre>
These functions tack another data element onto the end of the array.
<pre>
float *ssg*Array::get ( int i ) ;
</pre>
Returns the address of the i'th element of the array.
<pre>
int ssg*Array::getNum () ;
</pre>
Returns the number of data elements currently stored in the array.
<pre>
void ssg*Array::removeAll () ;
</pre>
Empties the array.
<H2><code>class ssgTween</code> - A Vertex-table morphing node.</H2>
This node is derived from ssgVtxTable. Where VtxTable can retain
a set of arrays, one each for Vertex coordinates, Normals, Texture
Coordinates and Colours, the ssgTween node can store an unlimited
number of 'banks' of these arrays.
<p>
Hence, one constructs an ssgTween by specifying it's OpenGL primitive
type:
<pre>
ssgTween::ssgTween ( GLenum ty ) ;
</pre>
And then create some number of 'banks' of data arrays by calling:
<pre>
int ssgTween::newBank ( ssgVertexArray *vl,
ssgNormalArray *nl,
ssgTexCoordArray *tl,
ssgColourArray *cl ) ;
</pre>
...once for each bank. All other ssgVtxTable calls operate ONLY
on the 'current bank' - which is either the most recent one you
created with newBank - or you can go back to an older bank by
calling:
<pre>
void ssgTween::setBank ( int bankID ) ;
int ssgTween::getBank () ;
</pre>
(newBank returns the number of the bank it created in case you
lose count!).
<p>
ssgTween's idea of the 'current bank' is reset to zero after
rendering it...so be sure to always call setBank immediately
before working on it's component arrays.
<p>
When the ssgTween node is rendered, it works cooperatively
with whichever ssgTweenController node is above it in the
scene graph. See the section on ssgTweenController for more
details.
<p>
IMPORTANT NOTE: There must be an identical number of vertices,
normals, etc in each bank. If you get that wrong, SSG will
exit with an 'abort'. Note that there is a specific optimisation
in ssgTween that avoids the computational cost of interpolating
between two sets of vertex data if EITHER:
<ul>
<li>The ssgTweenController's selected bank number happens to
be an integer.
<li>The previous and next banks have the same ssg*Array pointer.
Since one generally doesn't wish to interpolate normals, colours
or texture coordinates, it's important to pay attention to this.
</ul>
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"><a href="ssgEntity.html">&lt;= previous =</a></td>
<td width="34%" align="center"><a href="index.html">Return to SSG Index</a></td>
<td width="33%" align="right"><a href="branches.html">= next =&gt;</a></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>

255
doc/ssg/state.html Normal file
View File

@@ -0,0 +1,255 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>A Simple Scene Graph API for OpenGL.</TITLE>
</HEAD>
<BODY text="#B5A642" link="#8FFF8F" vlink="#18A515" alink="#20336B"
bgcolor="#005000" background="../marble.png">
<H2><code>class ssgState</code> - OpenGL state representation.</H2>
Each leaf node will have some kind of ssgState node associated
with it that contains all relevent OpenGL state information.
<p>
There can (in principal) be a number of different ways to represent
OpenGL state - but all must be derived from an ssgState:
<pre>
class ssgState : public ssgBase
{
int getExternalPropertyIndex () ;
void setExternalPropertyIndex ( int i ) ;
ssgStateCallback getStateCallback ( int cb_type ) ;
void setStateCallback ( int cb_type, ssgStateCallback cb ) ;
virtual void force () ;
virtual void apply () ;
} ;
</pre>
<H3>Callbacks.</H3>
States are 'applied' immediately before the geometry they are attached
to is rendered. Applying the state causes all of it's properties to
be set up in OpenGL.
<p>
Each state has three optional callback function hooks. The application
program can have a function called before the state is applied, after
it is applied (but before the geometry is drawn) and another after the
geometry is rendered - immediately before the next state is applied.
<p>
You can set those three callbacks with setStateCallback - passing
either SSG_CALLBACK_PREAPPLY, SSG_CALLBACK_PREDRAW or SSG_CALLBACK_POSTDRAW
as the first parameter and the address of your function as the second.
<H3>External Properties.</H3>
Each state entity can have an external property - which is a simple
integer that can be set using <code>ssgState::setExternalPropertyIndex(i)</code>
or queried using <code>ssgState::getExternalPropertyIndex()</code>.
External properties are of use to certain sorts of applications programs,
for example, a game might want to encode the set of OpenGL state information
that represents Lava as something that is hot and Ice as something that
is cold by encoding the temperature of the material in the External
property field. Most applications will probably use this field as an
index into a table of material properties inside the application itself.
<H3>Applying a State.</H3>
Whilst it's rare for an application to need to deal with an ssgState
once it has been defined, there may be occasions when the application
wishes to draw objects of it's own without using SSG's scene graph.
This often is the case with on-screeen symbology.
<p>
In such cases, it is important to bear in mind that SSG changes the
OpenGL state as little as possible - in order to save time. Hence,
when a leaf node has just been drawn with one set of state information,
and another leaf node is about to be drawn using another, SSG carefully
compares the two states to see how they differ and arranges to make
only the fewest possible OpenGL state change calls. If the application
goes in "behind SSG's back" and changes state then SSG will be confused.
<p>
There are two ways to achive this. One is to use SSG state classes to
change the state by calling <code>ssgState::apply()</code>. That call
will ensure that OpenGL's state matches the desired state using the
minimum of calls. However, if your application absolutely MUST make
it's own state calls then you should call <code>ssgState::force()</code>
to force all aspects of a specified state to be set in OpenGL so that
SSG can be certain about how things are set up.
<H2><code>class ssgSimpleState</code> - Simple State class.</H2>
ssgSimpleState is currently the only concrete class derived from
ssgState. It has so far proved adequate for all state management.
<pre>
class ssgSimpleState : ssgState
{
void disable ( GLenum mode ) ;
void enable ( GLenum mode ) ;
void set ( GLenum mode, int val ) { val ? enable(mode) : disable(mode) ; }
void setTexture ( char *fname, int wrapu = TRUE, int wrapv = TRUE )
void setTexture ( ssgTexture *tex )
void setTexture ( GLuint tex )
void setColourMaterial ( GLenum which )
void setMaterial ( GLenum which, float r, float g, float b, float a = 1.0f )
void setMaterial ( GLenum which, sgVec4 rgba )
void setShininess ( float sh )
void setShadeModel ( GLenum model )
void setAlphaClamp ( float clamp )
} ;
</pre>
These calls mostly correspond to similarly named OpenGL functions.
<H3>Enable and Disable calls:</H3>
<code>ssgSimpleState:: disable ( mode )</code>,
<code>ssgSimpleState:: enable ( mode )</code> and
<code>ssgSimpleState:: set ( mode, val )</code> provide
the same services as glEnable and glDisable ('set' is a convenience function
that is a 'disable' if 'val' is FALSE, 'enable' otherwise). The 'mode'
parameter uses tokens that are similarly named to those in OpenGL:
<pre>
SSG_GL_TEXTURE_EN
SSG_GL_CULL_FACE_EN
SSG_GL_COLOR_MATERIAL_EN
SSG_GL_BLEND_EN
SSG_GL_ALPHA_TEST_EN
SSG_GL_LIGHTING_EN
</pre>
<H3>Texture states.</H3>
There are three ways to attach a texture to an ssgSimpleState:
<code>ssgSimpleState::setTexture ( fname, wrapu, wrapv )</code>,
<code>ssgSimpleState::setTexture ( ssgtexture )</code>, and
<code>ssgSimpleState::setTexture ( texture_handle )</code>.
In the form that takes a filename, U-axis wrap and V-axis wrap flags, the
texture is loaded from a texture file on disk (see ssgTexture
below for details on how this is done). The map will be MIPmapped and set with
a texture environment that is GL_LINEAR_MIPMAP_LINEAR and GL_MODULATE.
<p>
If you need something fancier, then declare an 'ssgTexture' class and
pass that to the setTexture function. You can also load your own
texture and pass the OpenGL glBindTexture handle to setTexture.
<p> If you need the texture file name, first check that
isEnabled ( GL_TEXTURE_2D ) returns true, then you may call
getTextureFilename(). But you should still check the return value for NULL and for "".
<H3>Materials.</H3>
These calls are all very similar to OpenGL calls - and take the same
parameters:
<code>ssgSimpleState::setColourMaterial(which)</code>
<code>ssgSimpleState::setMaterial(which,r,g,b,a)</code>
<code>ssgSimpleState::setMaterial(which,rgba)</code>
<code>ssgSimpleState::setShininess(sh)</code>
<code>ssgSimpleState::setShadeModel(model)</code>
<code>ssgSimpleState::setAlphaClamp(clamp)</code>
<H2><code>class ssgTexture</code> - Storing texture maps.</H2>
An ssgTexture loads a texture map for you with the minimum possible
fuss - but offers less flexibility than if you did so yourself.
<p>
The ssgTexture constructor function
<code>ssgTexture::ssgTexture( char *fname,
int wrapu = TRUE, int wrapv = TRUE )</code> does all the work,
presuming that you require GL_LINEAR_MIPMAP_LINEAR filtering and a
GL_MODULATE texture environment.
<p>
You can obtain the OpenGL glBindTexture handle for the texture
using <code>ssgTexture::getHandle()</code>.
<p>
When ssgTexture loads a map from disk, it uses the filename
extension to determine which image format the file is in.
<p>
Currently, only SGI format and <strong>uncompressed</strong>
8 or 24 bit BMP images are supported - but more
formats are planned for the future. Filenames ending with
'.rgb', '.rgba', '.int', '.inta', '.bw' are assumed to be
SGI formatted files, '.bmp' are in Microsoft's BMP format
and '.png' are in Portable Network Graphics format.
<p>
If for any reason ssgTexture cannot load the requested file,
it creates a 2x2 texel red and white chequerboard map to
enable the program to continue running. This is often very
useful for debugging and to enable program development to
continue when texture maps are not yet painted.
<H2><code>class ssgStateSelector</code> - Switchable State class.</H2>
There are cases where you would like to be able to switch between a
number of different states for a given leaf node.
<pre>
class ssgStateSelector : ssgSimpleState
{
ssgStateSelector ( int nstates ) ;
void selectStep ( unsigned int s ) ;
int getSelectStep (void) ;
ssgSimpleState *getCurrentStep (void) ;
void setStep ( int i, ssgSimpleState *step ) ;
ssgSimpleState *getStep ( int i ) ;
} ;
</pre>
This class is used to switch between some number of <code>ssgSimpleStates</code>.
You could (for example) draw the ground for your game as either
grass or snow - depending on the season. Construct objects of this
class with the number of alternative representations you will require
as it's parameter, then create a number of <code>ssgSimpleState</code>s - passing
them to <code>ssgStateSelector::setStep()</code>. You can then select which
step you wish to render using <code>ssgStateSelector::selectStep()</code>.
<p>
<code>ssgStateSelector::getSelectStep()</code> returns the number of the currently
selected step. <code>ssgStateSelector::getCurrentStep()</code> returns a pointer to
the currently selected <code>ssgSimpleState</code>. <code>ssgStateSelector::getStep()</code>
returns a pointer to the i'th <code>ssgSimpleState</code>.
<p>
Since <code>ssgStateSelector</code> is derived from <code>ssgSimpleState</code>,
all the other <code>ssgSimpleState</code> calls work - and the effect is to
operate on the currently selected <code>ssgSimpleState</code>.
<p>
Example:
<pre>
ssgStateSelector *s = new ssgStateSelector ( 2 ) ;
ssgSimpleState *grass = new ssgSimpleState () ;
ssgSimpleState *snow = new ssgSimpleState () ;
...set up grass and snow states appropriately...
s -> setStep ( 0, grass ) ;
s -> setStep ( 1, snow ) ;
leafnode1 -> setState ( s ) ;
leafnode2 -> setState ( s ) ;
leafnode3 -> setState ( s ) ;
...choose grass or snow...
if ( ! winter )
s -> selectStep ( 0 ) ;
else
s -> selectStep ( 1 ) ;
...render the scene...
</pre>
Notice that one selectStep call changes the material on all
leaf nodes that use that ssgStateSelector.
<hr>
<table width="100%">
<tr>
<td width="33%" align="left"><a href="branches.html">&lt;= previous =</a></td>
<td width="34%" align="center"><a href="index.html">Return to SSG Index</a></td>
<td width="33%" align="right"><a href="ssgContext.html">= next =&gt;</a></td>
</tr>
</table>
<hr>
<table>
<tr>
<td>
<a href="http://validator.w3.org/check/referer"><img border="0" src="../valid-html40.png" alt="Valid HTML 4.0!" height="31" width="88"></a>
<td>
<ADDRESS>
<A HREF="http://www.sjbaker.org">
Steve J. Baker.</A>
&lt;<A HREF="mailto:sjbaker1@airmail.net">sjbaker1@airmail.net</A>&gt;
</ADDRESS>
</table>
</BODY>
</HTML>