How to use and extend the modeler

The modeler project includes one executable file (modeler.exe), one loadable library for modeler primitives (modeler.dll) and the loadable library that defines the model itself (myModel.dll, or whatever).   The executable and the library are provided, along with a few sample models.  You build your own model in .dll format and invoke modeler.exe to view and manipulate it.

The executable must be able to find the modeler library when it starts.  The NT skeleton project always copies modeler.dll to the Debug folder when the modeler library is rebuilt. On Linux, you should rebuild libmodeler.so in the modeler directory (make), then add the directory containing libmodeler.so into the environment variable LD_LIBRARY_PATH using setenv.  Also, the Linux executable needs to have the access permissions set to allow you to execute it: chmod ugo+x animator.

You must have the FLTK include and library files available.  The lab machines now all have FLTK installed in D:\app\fltk-1.0.9, so that is where the sample projects point to.  If you have set up your own machine, you can get the basic files or the full fltk distribution.

Setting up a new model project

After unzipping the skeleton code, you will get six subdirectories: Debug, LibModeler, MdlClassarm, MdlCylinder, MdlRobotarm, and MdlSphere.  Debug contains the executable modeler.exe and the modeler.dll.  Together they implement the framework for this project.  The sources for the modeler library are included in the LibModeler directory in case you want to extend the set of primitives.  The last four subdirectories contain the sample model projects.  The sources for classarm, cylinder, robotarm, and sphere are included for your information.  The project settings and basic structure of the code are a good beginning point for your own model.

An easy way to build a new project is to copy the robotarm folder and start modifying that.  This way all the project settings are established for you.

To start from scratch, do the following.

  1. In MSVC, select File > New.... Click the Projects tab and select "Win32 Dynamic-Link Library". Give your project a name in the Project name: entry box, for example, "sphere". Click OK, then Finish, then OK to create an empty DLL project.
  2. Use your favorite text editor to create the model source file - for example "sphere.cpp". Enter the following outline:
  3. #include ?modelerdll.h>
    
    #define SPHERE_RADIUS   1
    
    MODEL("sphere");
    
    bool init(void *args)
    {
        scale( SPHERE_RADIUS, "radius", 0.1f, 3.0f, 0.01f, 1.0f );    
        return true;
    }
    
    void redraw(int drawmode)
    {
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();
        glTranslatef( 0.0, 0.0, -15.0 );
        ambient_color( 0.0, 0.0, 0.2 );
        diffuse_color( 0.0, 0.3, 0.8 );
        sphere( get_control_d( SPHERE_RADIUS ) );
    }
    The argument to the MODEL macro on the third line will the name of your model. Remember to put this macro at the beginning of your file.
     
  4. Save the file. In MSVC, add the file to the project by selecting Project > Add To Project > Files....
  5. Now configure the project. Choose Project > Settings... to bring up the configuration dialog.
    1. At the upper left there is a dropdown labeled Settings For:. Select "All configurations."
    2. Under the General tab, set "Intermediate files" to c:\temp
    3. Under the C/C++ tab, select the "Preprocessor" category. In the "Additional include directories" box, enter:
    4. "<where to find fltk>", "<where to find modelerdll.h>"
      The sample projects initially set this to: d:\app\fltk-1.0.9,..\LibModeler
       
    5. Under the Link tab, select the "General" category. In the "Object/library modules:" box, add (don't delete what's already there!) the following:
    6. modeler.lib fltkd.lib wsock32.lib opengl32.lib glu32.lib
      Select the "Input" category. In the "Additional Library Path" box, enter
      "<where to find fltk library>", "<where to find modeler.lib>"
      The sample projects initially set this to d:\app\fltk-1.0.9\lib,..\Debug

      In the "Ignore Libraries" box, add 'libcmt'.
       

    7. Click OK.
  6. You can set the project settings so that when you select  run application, it runs modeler.exe and loads your model automatically.  One way to do this is to choose Project>Settings>General, and set executable for debug session to ..\Debug\modeler.exe, and working directory to ..\Debug.  Then choose the Post-build Step (way at the end of the options tabs) and copy your dll to the ..\Debug directory every time you rebuild it using a command like "copy Debug\sphere.dll ..\Debug\myModel.dll"

  7.  
  8. Now you can build the project. You'll get a "Debug" subdirectory with your model DLL in it ("sphere.dll" in this example). If you start the modeler and load the model in, you'll see a blue sphere in the center.

  9.  
  10. If you copy the model dll to the main Debug directory and name it myModel.dll, then when you double click modeler.exe it will be loaded automatically.  You can also load the model "by hand" from within the program, or you can specify it on the command line as the second parameter following the name of the modeler library modeler.dll.

Creating a model

Your model consists of 2 C functions. Let's look at what the two functions do.

bool init(void *args) gets called once, when your model is first loaded. It should return true if the initialization is successful. The args parameter is not used in our implementation and will be NULL. Note:  you cannot do OpenGL initialization in this function.  See the occasionally asked questions or the robotarm project code for a suggestion on how to do OpenGL initialization.

void redraw(int drawmode) is where the action is. It gets called every time the window needs to be redrawn.  The context in which it is called is established by the calling routine CGLView::draw.

To draw something, you call one of primitive object functions provided in the modeler library:

sphere( r )
draws a sphere of radius r.
box( x, y, z )
draws a box with corners at (0,0,0) and (x,y,z).
cylinder( h, r1, r2 )
draws a cylinder with axis from the origin to (0,0,h). The radius at the origin is r1; the radius at z=h is r2. If r1 and r2 are different, you get a frustum. If r1 or r2 is zero, you get a cone.
triangle( x1, y1, z1, x2, y2, z2, x3, y3, z3 )
draws a triangle with vertices at the three given points.
We draw a sphere here.

Since the sphere is centered at the origin, which is exactly where the camera is. We use the following code fragment,

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    glTranslatef( 0.0, 0.0, -15.0 );
to move the sphere so that we can see it. The default camera is looking down the -z axis, so translate the sphere in that direction.

We can set material properties with four other functions:

ambient_color( r, g, b );
diffuse_color( r, g, b );
specular_color( r, g, b );
shininess( s );
The code fragment
ambient_color( 0.0, 0.0, 0.2 );
diffuse_color( 0.0, 0.3, 0.8 );
is used to set the material properties of the object. Now we get:

Adding interactive controls

The sample models have UI widgets that you can interact with to change the model. Now we'll see how to add those, allowing the user to vary the radius of the sphere in our simple example. Each control widget that you want must have a unique integer ID. To keep these straight, we recommend using #define'd constants. Stick
#define SPHERE_RADIUS  1
somewhere at the top of your file. Note that this doesn't mean the radius of the sphere is 1 unit, it means that control #1 is going to control the radius. We can use any number as the ID, as long as it is unique in the model.

init is where we create the widgets. The available commands to create widgets are listed below.

scale( id, name, min, max, res, start )
Create a slider widget. res is the resolution of the slider (the smallest amount you can move it). start is the initial value. name, as in all these calls, is a string that labels the widget.
checkbox( id, name, start )
Create a checkbox. start is the initial setting.
menu( id, name, start, ... )
Create a selection widget - either a set of radio buttons or a menu of choices. The "..." arguments are one or more strings, terminated with NULL - these are the available choices. They are numbered choice 0, 1, 2, and so on. start is the initial selection.
curve( id, name, min, max, start )
This will be used in project 4 when we do the animation.  It creates a control variable in the curve editor which doesn't appear in the user interface.
 
Let's create a slider for the sphere. In init, we have the line:
scale( SPHERE_RADIUS, "radius", 0.1, 3.0, 0.01, 1.0 );
Now, in the model drawing function, we need to read the current value of the slider. Widget settings can be read with the get_control_* functions, where "*" is one of "d" (to read a double), "i" (to read an int), or "b" (to read a boolean). You pass the function the ID of the control whose value you want.

In redraw, we use

sphere( get_control_d( SPHERE_RADIUS ) );
for the purpose.

Now when you load the model, there will be a slider added to the main window. Moving the slider will change the radius of the sphere:

Animation

The modeler has a simple animation mode, in which it will automatically redraw the model periodically (instead of just when a control widget is changed). You can use the function get_time() to retrieve the current time in seconds since the start of the animation. Look at the robotarm project for an example of how to use this.  If your computer can't keep up with the redraw rate in animation mode, try lowering the quality on the draw menu, so that fewer polygons are generated.