If you unpack the distribution onto your Z: drive, you should only need to do this once, no matter which machine you use.
Create a variable "TCL" with the path where Tcl was installed (on the lab machines this is "C:\Program Files\Tcl").
Create a variable "MODELER" with the path where you unpack the distribution (where the files "modeler.dll" and "modeler.lib" are).
Once you can load and view the robot arm and stem thing models provided in the distribution, you're ready to create your own model.
#include <modeler.h> MODEL(Tralfaz); int Model_SetupCmd( ClientData clientdata, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] ) { return TCL_OK; } int Model_ResizeCmd( ClientData clientdata, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] ) { int width, height; if ( objc != 3 ) { Tcl_SetResult( interp, "wrong # args: should be \"width height\"", TCL_STATIC ); return TCL_ERROR; } if ( Tcl_GetIntFromObj( interp, objv[1], &width ) != TCL_OK ) return TCL_ERROR; if ( Tcl_GetIntFromObj( interp, objv[2], &height ) != TCL_OK ) return TCL_ERROR; glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 30.0, (double)width / height, 1.0, 100.0 ); glViewport( 0, 0, width, height ); glClearColor( 0.0, 0.0, 0.0, 0.0 ); return TCL_OK; } int Model_RedrawCmd( ClientData clientdata, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); draw_mode_setup(); return TCL_OK; }
The argument to the MODEL macro on the second line must match the name of your project. The first letter should be capitalized and the rest of the letters lowercase. If the case is wrong it won't work!
"$(TCL)\include", "$(MODELER)"
"$(TCL)\lib\tcl82.lib" "$(TCL)\lib\tk82.lib" "$(MODELER)\modeler.lib" opengl32.lib glu32.lib
c:\program files\tcl\bin\wish82.exe
-f z:\foo\modeler.tcl
The space between the "-f" and the pathname is significant.
-f z:\foo\modeler.tcl z:\foo\robotarm\debug\robotarm.dll
Now when you debug from within MSVC (Build > Start Debug > Go, or press F5), it will start the modeler for you (and, if you did step (d), load a model).
A black screen isn't a very interesting model, so let's draw something.
Your model consists of 3 C++ functions. You can largely ignore all the arguments, they are standard Tcl interface boilerplate. Let's look at what the three functions do.
Model_SetupCmd gets called once, when your model is first loaded. This is empty now, we'll see some things to do with this later.
Model_ResizeCmd gets called when the size of the output window, to set up the
OpenGL camera parameters. It gets passed the new width and height of the window, which is
what all the Tcl_GetIntFromObj
stuff is about. The code above sets up a plain
vanilla perspective projection. You can change this if you want but shouldn't really need
to.
Model_RedrawCmd is where the action is. It gets called every time the window needs to be redrawn.
First, you need to clear the window, and do some modeler initialization - this code is provided for you. 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.
Let's draw a sphere. Add this line to Model_RedrawCmd (between draw_mode_setup();
and the return):
sphere( 1.0 );
When you load this model, you'll still see nothing. Why? Because the sphere is centered at the origin, which is exactly where the camera is. We're inside the sphere, so we don't see anything. Let's move the sphere so we can see it. The default camera is looking down the -z axis, so translate the sphere in that direction.
glTranslatef( 0.0, 0.0, -15.0 ); sphere( 1.0 );
Now we get:
Experiment with the other primitives. We can set material properties with four other functions:
glTranslatef( 0.0, 0.0, -15.0 ); ambient_color( 0.0, 0.0, 0.2 ); diffuse_color( 0.0, 0.3, 0.8 ); sphere( 1.0 ); |
Both of 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.
Model_SetupCmd is where we create the widgets. The available commands to create widgets are listed below. The first argument to each one should be the interp argument passed to Model_SetupCmd; just pass it straight through.
Let's create a slider for the sphere. In Model_SetupCmd, add this line:
scale( interp, 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 Model_RedrawCmd, replace
sphere( 1.0 );
with
sphere( get_control_d( SPHERE_RADIUS ) );
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:
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.