Rob and Raj,

Recently, I have developed a rudimentary geometry loader that parses
geometry files exported from 3DStudio Max (3DS) in ASCII format.  When 3DS
exports the geometry, it does so by exporting what it calls a Tri-mesh, or
an indexed triangular mesh, exactly what is required by the
IndexedTriangleArray class to create an object using Java3D.  Knowing a bit
about the exported file helped me to understand how Java uses
IndexedGeometryArrays.

In the following discussion, I use '>' to denote sample file text.

1.) First, 3DS exports the totals:

> Tri-mesh, Vertices: 92   Faces: 180

2.) Then, 3DS announces that it is about to export the vertices:

> Vertex list:

3.) This follows with 92 lines of 92 vertex specifications of the format:

> Vertex 0:  X:-15.5000 Y:5.0000 Z:6.0000
> Vertex 1:  X:-15.5000 Y:-5.0000 Z:6.0000
> Vertex 2:  X: 15.5000 Y:5.0000 Z:6.0000
> ...

4.) Then, 3DS announces that it is about to export the faces:

> Face list:

5.) This follows with 180 lines of 180 face specifications of the format:

> Face 0:    A:7 B:6 C:4 AB:1 BC:1 CA:0
> Face 1:    A:4 B:5 C:7 AB:1 BC:1 CA:0
> Face 2:    A:5 B:4 C:0 AB:1 BC:1 CA:0
> ...

Now, to explain the vertices.  The 92 lines of vertices make up the points
that will be referenced by the 180 lines of faces to create the triangle
array.  Each of the 92 vertices is of course defined by the three
dimensional cartesian coordinate system of the format ( X, Y, Z ).  So, for
example, Vertex 0 defines the point at ( -15.5, 5.0, 6.0 ).  In other words,
( -15.5, 5.0, 6.0 ) can be referenced by index '0'.

Now, to explain the faces.  The 180 lines of faces encompass the
specifications for building 180 triangles, the vertices of which are defined
by A, B, and C, and the edges of which are defined by AB, BC, and CA.  So,
for example, Face 0 defines the face specified by the vertices referenced by
index numbers 7, 6, and 4.  (I make no further mention of the edges that 3DS
exports below, as I have not yet deduced for what reason they are exported,
except maybe for the generation of surface normals.  Fortunately, I have not
needed to parse them in order to import my geometry to Java.)

So, how does one go about creating an IndexedTriangleArray from this data?

First, it is necessary to import all of the vertices into a vertex array.
You can do this by using a Point3f or Point3d object to contain the data,
but these are just "wrappers" for a 3-item array of float or double values,
respectively.  I find it easier to import the vertices into one large array
of floats or doubles.

For example, with 'vertexCount = 92':

> float[] vertices;
>
> vertices = new float[ vertexCount * 3];   // 92 vertices of 3 values (X,
Y, Z) each.
>
> for ( int index = 0; index < vertexCount; index++ ) {
>     float[] coordinates;
>
>     coordinates = new float[ 3 ];
>
>     // ... code to parse the X:-15.5000 Y:5.0000 Z:6.0000 to
>     // ... the 'coordinates' float array.
>     // ... After this is done, then, coordinates = { -15.5f, 5.0f, 6.0f }.
>
>     /* The following code then copies the 'coordinates' float array
>     /* into the 'vertices' float array at the specified
>     /* 'index * 3' + offset.  Thus Vertex 0 would be stored in
>     /* vertices[0], vertices[1], and vertices[2].  Just like Vertex 4
>     /* would be stored in vertices[12], vertices[13], and vertices[14].
>     /* And since we initialized vertices to a length of 92 * 3, no
>     /* ArrayIndexOutOfBounds message will occur.
>     /*/
>     for ( int offset = 0; offset < 3; offset++ ) {
>         vertices[ ( index * 3 ) + offset ] = coordinates[ offset ];
>     }
>

The same code can be reused to import the indices of the triangle faces, but
instead use an int array.

These two arrays, one of vertices and one of indices, are then added to an
IndexedTriangleArray.  With 'vertexCount = 92' and 'faceCount = 180':

> IndexedTriangleArray array = new IndexedTriangleArray(
>     vertexCount,
>     IndexedTriangleArray.COORDINATES,
>     faceCount * 3 );

Note the 'faceCount * 3' above.  Why don't we tell the IndexedTriangleArray
to make 'vertexCount * 3' as well?  That's because the IndexedTriangleArray
already knows that in order to create an array that will hold, for example,
92 vertices, it has to multiply 92 * 3 to set the length of the array.  It
stores the array of vertices as an array of floats.  On the contrary, the
IndexedTriangleArray doesn't store an array of indexed faces; instead, it
stores an array of indices to reference vertices, and since one face is made
up of 3 vertices, we need to tell the constructor of IndexedTriangleArray to
generate an array to hold 'faceCount * 3' number of indices.

Interestingly, the effort to put our vertex array and index array each into
two big arrays of floats and ints, respectively, reaps dividends when we go
to add it to the IndexedTriangleArray:

> array.setCoordinates( 0, vertices );
> array.setCoordinateIndices( 0, indices );

And that's all there is to it!

After creating the array, clean it up by setting normals.  Generating
normals is required for lighting effects.

> GeometryInfo geometryInfo = new GeometryInfo( array );
> NormalGenerator normalGenerator = new NormalGenerator();
> normalGenerator.generateNormals( geometryInfo );
> array = geometryInfo.getGeometryArray();

At this point, you could also stripify it to speed up rendering.  Thus, you
don't need to create a IndexedTriangleStripArray from scratch.  Just create
an IndexedTriangleArray, set the normals, then stripify it.

In reference to your problem, Rob, your polygon:

>  A*---*B  ( 0, 4, 0 )   ( 4, 4, 0 )
>    |\   |
>    | \  |
>    |  \ |
>    |   \|
>  C*---*D  ( 0, 0, 0 )   ( 4, 0, 0 )

with specified vertices A, B, C, and D, would have vertices:

> (A) Vertex 0: X:0 Y:4 Z:0
> (B) Vertex 1: X:4 Y:4 Z:0
> (C) Vertex 2: X:0 Y:0 Z:0
> (D) Vertex 3: X:4 Y:0 Z:0

which would specify the float array:

> { 0.0f, 4.0f, 0.0f, 4.0f, 4.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f, 0.0f }

Thus your vertex array would be 12-items long as stored within
IndexedTriangleArray, but would contain 4 indexed vertices.  (Each is 3
floats long).

The same polygon would have the triangular faces:

> Face 0: A:0 B:2 C:3
> Face 1: A:3 B:0 C:1

which would specify the int array:

> { 0, 2, 3, 3, 0, 1 }

Thus your index array would be 6 items long as stored within
IndexedTriangleArray, and would contain 2 triangles.  (Each is 3 indexed
vertices long).

Notice the order of the int array, as well.  I chose it carefully.  In order
for normals to be generated appropriately for the IndexedTriangleArray, the
order A, B, C has to obey the 'right-hand' rule.  That is, the vertices of
the front face or visible face of the first, third, fifth, (odd) ...
triangles must proceed counterclockwise.  Furthermore, the second, fourth,
sixth, (even) ... triangles must proceed clockwise.  Otherwise, correct
normals will not be generated and those faces (if backFaceCulling is
enabled) will disappear!

Thus, when you said you were using faces of type:

> Face 0: A:0 B:2 C:3
> Face 1: A:3 B:1 C:0

the reason you were having problems was because you were setting all of the
faces to counterclockwise rendering.  Thus, your normals were flip-flopping
front to back, the faces with reversed normals were culled, and your
rendering was compromised.

Trying to figure out which direction to go is tedious, at best, but those
are the rules.  That's why I use 3DStudio Max.  Just remember, for your
index array:

> indices[0], [1], [2], ... [6], [7], [8], ... [12], [13], [14], ...

must be arranged counter-clockwise to have the face appear and:

> indices[3], [4], [5], ... [9], [10], [11], ... [15], [16], [17], ...

must be arranged clockwise to have the face appear.

Hope this helps!

G. Scott Anderson

===========================================================================
To unsubscribe, send email to [EMAIL PROTECTED] and include in the body
of the message "signoff JAVA3D-INTEREST".  For general help, send email to
[EMAIL PROTECTED] and include in the body of the message "help".

Reply via email to