Last updated: 15 February 2005
Contact us: 60hz@csse.uwa.edu.au
Real-time Rendering Group
Department of Computer Science and Software Engineering, The University of Western Australia
"If you can't do it in realtime, it's not worth doing"

Existing Workshops

If you are very new to Clean, the best place to start is to take a look at An Introduction to Real-time Rendering Using OpenGL. This workshop will take you all the way from the basics of OpenGL, through adding Clean to your codebase and also contains a simple example using Clean to load a 3D object from an existing model file.

Once you have completed that workshop, you may need some further information on typical use of the clean data structures and functions. This page is intended to fulfill that need. It will take you through creating some of the data structures in order to get some data of your own up on the screen.

Clean Hierarchy

While Clean is written in plain C, it is intended to be used in a somewhat object-oriented fashion. It helps to understand the hierarchy of the data structures when writing code using Clean.

The hierarchy is, from smallest to largest, CLprimitiveset < CLmesh < CLmodel < CLcontext. In practice, the best way to create this hierarchy in you code is as follows:

  1. Create a CLmesh
  2. Fill the mesh with vertex information
  3. Create a CLprimitiveset
  4. Fill the primitiveset with topology information (triangles)
  5. Add the CLprimitiveset to the CLmesh
  6. Create a CLmodel
  7. Add CLmesh to CLmodel
  8. Create a CLcontext
  9. Add CLmodel to CLcontext

Then you end up with a thing that is renderable by clean, a CLcontext.

CLmesh

Create a CLmesh using the Default and New methods, you'll get used to these.

CLmesh* mesh;
mesh = clDefaultMesh(clNewMesh());

Basically, the CLmesh is the most important structure. The definition of the CLmesh struct is here and more detailed information is here.

Of particular interest are the parallel arrays, defined below:

GLuint num_vertices;
CLvertex* vertices;
CLcolour* colours;
CLnormal* normals;
CLtexcoord* texcoords;
CLedgeflag* edgeflags;

Obviously you need to have the vertices array full of the points in your data set. All the other arrays are optional, for example, if you arent doing lighting, then just set the normals array to NULL and the rendering code will know not to try reading normals from there. On the other hand, if you want to specify other information about the vertices, for example, the vertex colours, then the associated array must be the same length as the vertices array.

So, if num_vertices is 10, then the arrays must have length 10 or be set to NULL.

CLprimitiveset

Create a CLprimitiveset using the Default and New methods.

CLprimitiveset* primitiveset;
primitiveset = clDefaultPrimitiveSet(clNewPrimitiveSet());

Where the CLmesh stored the location of all the vertices in your object, the CLprimitiveset stores how to join the verts together into triangles or quads or whatever. The definition of the CLprimitiveset is here and more detailed information is here

Since you're probably using triangles, set the primitiveset mode to triangles.

primitiveset->mode = GL_TRIANGLES;

Here it gets a little interesting, but sensible, once you understand. The rest of the primitiveset looks as follows:

GLuint num_indices;
GLuint* indices;

num_indices is simply the number of primitives multiplied by the number of indices per primitive. For example, if you are using 10 triangles, then the length of the indices array is 10 * 3. The first three elements describe the first triangle, elements 3-5 describe the second triangle, and so on.

The indices array are indices into the CLmesh->vertices array. For example, if primitive 0 connects vertices 10, 11, 12 to form a triangle, then indices[0] = 10, indices[1] = 11 and indices[2] = 12. Once you have filled the primitiveset->indices array with all your triangles, then add the primitiveset to the mesh you made earlier.

clMeshAddPrimitiveSet(mesh, primitiveset);

CLmodel

Since 3d objects can be quite complex, different parts of the same object may require different materials and textures. A CLmesh may only have one material, so complex objects must be made from several meshes. This is the reason we have a CLmodel, it groups together meshes that are part of the same logical object.

Its not necessary to understand this in order to render something. Just create a CLmodel, shown below:

CLmodel* model;
model = clDefaultModel(clNewModel());

Then add the existing mesh to your new model

clModelAddMesh(model, mesh);

CLcontext

The context groups all the geometry in the scene together with all the materials and all the textures. Once again, dont worry about it if all you're doing in rendering stuff. Just create a context like so:

CLcontext* context;
context = clDefaultContext(clNewContext());

Then add the existing model to your new context

clContextAddModel(context, model);

Extras

Now the CLcontext in ready to render, but you can calculate normals for your data using a function in CLU.

cluMeshGenerateNormals(mesh);

Rendering

Now you can render the object as easily as you would anything loaded from a 3D model file. Check out the later stages of the clean workshop to read more about rendering in Clean.

Hopefully this helped out, if you have any troubles or want more stuff to do, mail 60hz