Google

GLASS tutorial

A GLASS object

A GLASS object is a structured 3D object. Its shape is made up of components, which are the solid parts of the object. These are linked together by a structure which is fixed when it is created (by hand or using an editor), but the objects shape can be modified by using variables, that change "joints" in the object. Feedback is available by using active points that give the position and direction of specific places in the object.

These three usable parts, the components, variables, and active points are explained in more detail below.

Components

A component is a collection of triangles. It is the smallest unit in a GLASS object, and is what is linked together to make the object. The triangles are stored internally in the GLASS object structure, or by using openGL display lists. Each component has a unique name, allowing its visibility to be modified, or for it to be drawn on its own.

Variables

Variables control the transforms that link the components together. Each variable has a unique name, a value, and the minimum and maximum values the variable can take. By modifying one variable, the shape of the GLASS object can be changed.

Active Points

Active points can be present in the object. An active point allows the program using the GLASS object to find the position and direction of a point in the object, after it has been moved by the various transforms. Each active point has a unique name.

Using GLASS in a project

To use a GLASS object the following things must be done:
  • Set up openGL
  • Get the version of GLASS
  • Load the object
  • Find the components/variables/active points
  • Modify variables
  • Modify component visibility
  • Draw the components/object
  • Read active points
  • Remove the object
  • Optionally, libraries may be used.

    Setting up openGL

    For GLASS to work, openGL must be correctly set up. The display must be set up (using GLUT for instance), lighting must be prepared if it is to be used, depth testing must be enabled, back face culling (if wanted), and the desired blending function set up. As is with openGL if the display is correctly set up then the default settings will enable most GLASS objects to be displayed fine, but you will want to consider these settings for efficient/correct display.

    Getting the version of GLASS

    The version of glass can be obtained using the function:

    char *glassGetVersion(void);
    

    This returns a string that describes the current version of glass. For example, for version 1.0.0 this returns "1.0.0" (like you'd never expect). The string is used internally by GLASS so don't free it!

    Loading a GLASS object

    First your program will need to include the GLASS header file glass.h, with the following standard command.

    #include <glass.h>
    

    A GLASS object is stored in the GlassObject structure. The internals of the structure are not relevant, all that is required is that this structure is passed to the GLASS functions. An empty GLASS object is then declared as follows:

    GlassObject *foo = NULL
    

    Where foo is the name of the object you want to create.

    To actually load a glass object all you need to use the first GLASS function. Keep in mind that loading the object will create textures, and display lists.

    The prototype for the loading function is:

    GlassObject *glassLoadObject(const char *fname)
    

    Where fname is the path of the object file. This returns a pointer to the GLASS object.

    Using the previous example, the GLASS object in the file models/hello.model would be loaded using:

    #include <glass.h>
    
    int main(int argc, char **argv)
    {
      GlassObject *hello = NULL;
    
      hello = glassLoadObject("models/hello.model");
      if(hello == NULL)
        printf("Error loading GLASS object!\n");
    }
    

    So now you have a loaded GLASS object (assuming the object was valid).

    GLASS Libraries

    Objects can be grouped together into libraries, which allow objects to share textures/materials/components/variables/active points. The prototype of the function that creates GLASS libraries is:

    GlassLibrary *glassLibraryNew(const char *name)
    

    Where name is the name of the new library. This returns a pointer to the GLASS library. The GLASS library pointer is dealt with in the same way as a GLASS object pointer.

    To add objects to this library, the following function is used:

    GlassObject *glassLibraryLoadObject(GlassLibrary *library, const char *fname)
    

    Where library is the pointer to the library created (as above), fname is the path of the GLASS object file. This returns a pointer to the added GLASS object.

    The difference between this function, and the previous loading function is that the loaded object is added to the library. When the object is assembling itself while loading, it first looks inside itself for items (variables, active points etc), and then in the other objects in the library. This means it is important that objects are loaded into the library in the correct order, with objects not linking to anything else first.

    Libraries are useful in programs where the GLASS objects have an underlining theme. For example in a space game a fleet of ships might all have the same gun turrets. By having the turret components stored in a model file, and this being part of a library, many GLASS objects can access this component without it having to be defined in each model. This also means you can change it once, and all ships in the fleet will have the new turret automatically.

    Finding Components/Variables/Active Points

    For components, variables or active points to be modified/accessed, they first need to be found. Each of these have a unique name, and it is assumed you know what these are (for a particular object). Each component/variable/active point has an integer index which is what you want to know. The functions that turn the names into indices are:

    int glassFindComponent   (GlassObject *object, const char *name)
    int glassFindVariable    (GlassObject *object, const char *name)
    int glassFindActivePoint (GlassObject *object, const char *name)
    

    Where object is a valid GLASS object, and name is the name of the component/variable/active point. The return values are the index of that component/variable/active point. -1 is returned if the component/variable/active point doesn't exist. The names are case sensitive.

    For example if you had loaded a model of a tank, which was made up of two components, the hull, and the turret, which were linked by a rotation in the y-axis controlled by a variable turret heading, and the end of the turret had an active point, barrel; These would be found by:

    #include <glass.h>
    
    GlassObject *tank = NULL;
    int com_hull, com_turret, com_fish; /* The components */
    int var_turret_head;                /* The variables */
    int apt_barrel;                     /* The active points */
    
    int main(int argc, char **argv)
    {
      tank = glassLoadObject("models/tank.model");
    
      com_hull = glassFindComponent(tank, "hull");
      com_turret = glassFindComponent(tank, "turret");
      com_fish = glassFindComponent(tank, "fish");
    
      var_turret_head = glassFindVariable(tank, "turret heading");
    
      apt_barrel = glassFindActivePoint(tank, "barrel");
    }
    

    The values of the variables would all be >= zero, but the value for com_fish would be -1, since there is no component fish in the object.

    Modifying Variables

    Each variable has a value, and a maximum, and minimum value. The value of a variable may be set to a particular value, or increased by a delta value. GLASS will clip these values internally so the value lies in the range min <= value <= max.

    The prototypes for retrieving the properties of a variable are:

    GLfloat glassGetVariableValue(GlassObject *object, int variable)
    GLfloat *glassGetVariableValuev(GlassObject *object, int variable)
    GLfloat glassGetVariableMin(GlassObject *object, int variable)
    GLfloat glassGetVariableMax(GlassObject *object, int variable)
    

    Where object is a valid GLASS object, and variable is a valid index of a variable in that object. The first and last two functions return the value, minimum value, and maximum value respectively for that variable. The values are stored in the GLfloat format to be compatible with openGL. If the variable doesn't exist the returned values are undefined. The second function returns a pointer to the value of the variable. This is particularly useful as this is a pointer to the actual stored value, and so changes as the variable changes. Since it is used internally it shouldn't be freed or modified. If variable doesn't exist in this case the function returns NULL.

    The prototypes for the functions that modify variables are:

    int glassSetVariable(GlassObject *object, int variable, GLfloat value)
    int glassIncVariable(GlassObject *object, int variable, GLfloat dvalue)
    

    Where object is a valid GLASS object and variable is the index of a variable for that object. value is the new value of the variable, and dvalue is the change in value for a variable. Both functions return FALSE if the variable doesn't exist, and TRUE otherwise.

    For example with the tank, the following code would rotate the turret by 5 degrees:

    void animate(void)
    {
      ...
    
      glassIncVariableInc(tank, var_turret_head, 5.0);
    
      ...
    }
    

    Modifying a components visibility

    A component can be hidden, or shown. Note that this affects all uses of this component, as components can be reused in one or more models. The prototype is:

    int glassSetComponentVisibility(GlassObject *object, int component, int visible)
    

    Where object is a valid GLASS object, component is the index of a component in that object, and visible is either TRUE or FALSE and is the new visibility state of that component. This function returns FALSE if the component doesn't exist, otherwise TRUE.

    For example if the tanks turret exploded, so it was no longer visible, but you still wanted to draw the tank object, the following code would be used:

    void explode_tank(void)
    {
      ...
    
      glassSetComponentVisibility(tank, com_turret, FALSE);
    
      ...
    }
    

    Drawing components/objects

    Either single components can be drawn, or the whole object. Drawing a single component displays only that component, without any transforms. Drawing the object draws the components, by transforming as the structure and variables define, and updating the active points position and direction (if they have changed). These functions just draw triangles to openGL, so make sure that the screen is cleared etc. The prototypes are:

    int glassDrawComponent(GlassObject *object, int component)
    int glassDrawObject(GlassObject *object)
    

    Where object is a valid GLASS object and component is the index of a component in that object. The first function returns FALSE if the component doesn't exist, and the second returns FALSE if an error occurs while drawing the object.

    With the tank again... Say that the turret had exploded (see above), and you wanted to draw the tank, with the turret rising above the tank, then you would do the following:

    void display(void)
    {
      GLfloat explosion_height;
      ...
    
      glassDrawObject(tank);
      if(tank_exploded)
      {
        glTranslatef(0.0, explosion_height, 0.0);
        glassDrawComponent(tank, com_turret);
      }
    
      ...
    }
    

    Reading active points

    Active points are only updated when the object is drawn, so you'll want to access them after that has happened. There are two functions for active points, one to return its current position, and one to return the current direction. The prototypes for these are:

    GLfloat *glassGetActivePointPos(GlassObject *object, int apoint)
    GLfloat *glassGetActivePointDir(GlassObject *object, int apoint)
    

    Where object is a valid GLASS object and apoint is the index of an active point in that object. These functions return an array of 3 GLfloats for the current position, or direction of the active point. They both return FALSE if the active point doesn't exist. These arrays (like for the variables), are used internally by GLASS and shouldn't be freed or modified, they do however continue to be updated, and can be reused like for the variables.

    With the tank example, active points are useful to find where the end of the turrets barrel is, so projectiles can be created at the correct position, and fired in the correct direction. By getting the position of the active point apt_barrel, the position of the barrel (relative to the tank) is found. And the direction of the active point is the direction the barrel is pointing. So when the tank is fired:

    /* The position of the tank (in the world) */
    GLfloat tank_pos[3];
    
    void fire_tank(void)
    {
      GLfloat *barrel_pos, *barrel_dir;
      /* The position and velocity of a new projectile */
      GLfloat projectile_pos[3], projectile_vel[3];
      int i;
      ...
    
      barrel_pos = glassGetActivePointPos(tank, apt_barrel);
      barrel_dir = glassGetActivePointDir(tank, apt_barrel);
    
      /* Create a projectile at the end of the barrel, with a speed
         of 10 m/s */
      for(i = 0; i < 3; i++)
      {
        projectile_pos[i] = barrel_pos[i] + tank_pos[i];
        projectile_dir[i] = 10.0 * barrel_dir[i];
      }
      ...
    }
    

    Removing GLASS objects/Libraries

    When you've finished with a GLASS object, it can be freed using the following function:

    void glassFreeObject(GlassObject *object);
    

    Where object is a valid GLASS object.

    This function removes the object, and everything it used (e.g. textures). Note that now object is an invalid pointer, so setting it to NULL would be a good idea.

    To free a library, use:

    void glassFreeLibrary(GlassLibrary *library);
    

    This will remove the library, and all the objects who are members of it.

    And that's it!

    That's all there is, as you can see (unless this isn't very obvious... :) ), the idea was to keep it simple. If you have any questions/comments feel free to email me at bob27@users.sourceforge.net. All feedback is appreciated. Feel free to modify the tutorial if you think it necessary, and give me the new version.
    Robert Cleaver Ancell
    Last modified: Fri Aug 17 18:18:48 NZST 2001