Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

OpenSkelGlut Animation Software, Study notes of Mathematics

The openskelglut animation software, a real-time interactive c/opengl/glut animator developed at the university of illinois. The software includes functions for managing command line arguments, setting up the scene, and steering the animation. It also includes functions for drawing various shapes such as a torus and a cube, as well as stars in the background. The animation can be controlled through keyboard inputs and the use of binocular stereo.

Typology: Study notes

Pre 2010

Uploaded on 03/16/2009

koofers-user-uke
koofers-user-uke 🇺🇸

10 documents

1 / 22

Toggle sidebar

Related documents


Partial preview of the text

Download OpenSkelGlut Animation Software and more Study notes Mathematics in PDF only on Docsity! illiSkel in C/OpenGL/GLUT George Francis 3 January 2002 1 History of the program. The real-time interactive computer animation (RTICA ) illiSkeleton.c is a practical derivative of the classic 1994 illiShell.c. It maintains the general structure of its parent but is restricted to animating the monitor of the console. The illiShell.c is a fully functional prototype application for the CAVE which also compiles and runs on the console without proprietary CAVE libraries. Originally written in C and IrisGL, the illiShell and its evolutes have gone through many linguistic permutations, including C++ and OpenGL, with and without the GLUT extensions. Its C/OpenGL/GLUT derivative skel.chas been the basis of almost all RTICAs written since 1997 by students in illiMath courses and workshops. As such, the illiSkel lends some kind of minimal structural unity to illiMath, without resorting to proprietary libraries and packages which impede portability and hamper creativity. The 2002 version of illiSkel is intro- duced here. A subsequent chapter treats the extension to a CAVE and a CUBE application. 2 Programming with illiSkel. To minimize the time it takes to master the illiSkel, and maximize the freedom to modify, extend or rewrite it, the program consists of a single source file of fewer than 365 lines of code. We assume only the most rudimentary programming skills, and have kept things brief and simple. The main() function appears at the end of the program, in keeping with the (now unpopular) philosophy of not working with a concept before knowing exaclty what it means. In particular, factors of a function precede it in the linear order of the code. Like listing the ingredients of a recipe before the instructions on how to cook the soup, you have to be able to read the program backwards as well as forward. Thus keystroke editors with nimble searching (vi or emacs) are better for studying skel.cthan menu-driven scrolling editors. The reader displeased with this design is encouraged to rewrite the skel in a more conventional style, but with an agreement that every feature will work at least as well in the improved version. To work with the illiSkel means to subtract and add functions, factor func- tions that are getting too verbose, and divide the functions into affinity groups 1 by what they do and how they relate to each other. Such “arithmetic” also leads to a better understanding of the illiMath system of writing real-time interactive computer animation. By subtracting functions you produce a derivative RTICA which is more easily understood and built up again into a different one. You add functions to produce an extension of the original, integrating new features, better graphics, subtler navigation, etc. When a concept has grown too big to keep in mind all at once, we factor it into smaller concepts. Similarly, a function that has grown so big that it no longer fits on a page or a screen should be factored into more manageable bites. But also when you plan to make many experimental modifications to the same piece of a longer function, it wise to factor that section out. Finally, functions fit into groups by how they share variables and purposes. 2 rewritten to (((foo)<(5)?(5):(foo)). The extra parentheses prevent possible misunderstandings in more complicated contexts.5 Macros make subsequent code shorter and more readable. It is cutomary to use all caps for their names to make them easier to spot in the source code. Do not expect to find them in compiler error messages, because the compiler sees only only their expansions. Though replacing macros with function calls is an alternative, it makes the program run more slowly and leads to verbose, tortured code. Take the macro IFCLICK, which uses the function getbutton() to set a switch bypassing a segement of code. The argument of getbutton() getbutton() is the name of a key, and the function returns 1 if the key had been pressed.6 Inserting an IFCLICK macro anywhere in a function creates a block with a static flag whose value persists from one call to the next. A keypress toggles the flag. If the flag is down, the code segment is bypassed. In the idle() function, for example, pressing the (=)-key will freeze navigation because the function chaptrack(), which updates navigation, is not called again until you press (=). Try the (–)-key and see what the OpenGL depth test does for a living.7 Exercise. Since all interrupts from the keyboard or the mouse are medi- ated by the GLUT mainloop, you cannot bypass a callback function as easily. But you can prevent the effect of a function by setting a flag right at its be- ginning which makes it return empty-handed, as it were. For instance, write IFCLICK(’+’, return;) as the first line of mousemoved() and mouse motion will be ignored when you toggle the (+)-key. This won’t work right for the keyboard callback. Why? Sometimes you’ll really want to write text into one window while watching the RTICA in another, and wish that the RTICA were deaf. Modify the code of keyboard() which solves this problem. Exercise. Some macros are temporary constants, which you wish to change between recompilations. For instance, the default skel.chas 32 meridians and latitudes on its torus. After you have understood how illiGadget work, write one which makes it possible to change how many strings make up the mesh of the torus while it is running. 5 Parameter management functions. We next discuss the those functions which are involved with the management of control parameters. They are concerned with how something is rendered 5And, for the reader meeting the expression B?T:F for the first time, know that the compiler evaluates it to the expression F if the value of B is 0, and otherwise to T. 6In IrisGL, getbutton() was a system function which really interrupted the program flow and told the truth. In OpenGL, such interrupts are not allowed for reasons of inter-system portability. So we must simulate this function as explained further on. 7Using IFCLICK transgresses the very tidy habit of processing all keypresses in the same (two) input funtions keyboard(), specialkeyo(). Moreover, some C++ evangelists say that one should not use such tricks at all. Too many IFCLICKs scattered throughout your code will make the operation of your RTICA amusing, if not aggravating. Use it sparingly during development or your code and for analysing someone elses code. Use it for hacking, debugging and experimenting, especially when it is not clear yet what features should become permanent options. 5 by the RTICA , not with what is rendered. The clockwork animation function autotymer() appears first in the source code only for convenience of editing. The first factor of of main() is arguments, which picks up the input you wrote on the command line. It is not possible to settle on a best linear ordering of the functions in the source code, so we keep the historical order for easier comparison with RTICAs based on older versions. Use your editor to find the functions in the code, and to trace its variables forward and backward. A typical factor of main(int argc, char **argv) is a function that man- arguments() ages the command line arguments(). The system reads the entire command line and provides main() with a counter, argc, and a pointer of pointers, argv. Initially, argc equals the number of words on your command line, argv[0][0] is the the first character in the first word. The arguments() function we use in skel.cwas designed by Pat Hanrahan to make it easy for you to add and subtract command line switches. These are items marked with a single “dash- letter” followed by one or more integers or floats.8 Entering a command line that looks like this, iris % skel.x -w 0 -L 0. 0. 10. -g .8 sets the global flag win to 0. This specifies a particular size and location of the drawing window, as specified by the calls to the GLUT library initialization in main(). The function atoi() converts alphameric strings to integers, while atof() does the same thing for floats. Thus, the final -g .8 sets the default gap0 size to .8.9 The light direction vector also comes twice, lux[], luxx[], but for different reasons. The default direction is (1,2,3), but it can be changed on the command line with the -L option. We have capitalized the flag to remind you that is a vector, not a number. In order for the bright spot (signifying the light source) to move across a rotating object correctly, our lighting method, illiLight rotates the light direction appropriately with luxx[]. All parameters should be assigned their default values in the deFault() function.10 It is also possible to assign constant parameters at the time of their definition. This was done for the the light direction vector, lux[]. Note that we make sure that lux[] is really a unit vector. In deFault() we also reset the two navigation matrices, aff[], starmat[], to the identity.11 The clockwork autotymer() is also reset by deFault(). As you hack an RTICA you should resist the temptation of neglecting to update deFault(). 8Hanrahan’s ingenious code is not for the fainthearted. It also does not suffere fools gladly. It expects to be used by people who know what they’re doing. Therefore, it had been superceded in illiMath RTICAs by the much more robust but different getopt() command line argument handlers. Hanrahan’s code was revived when we found just how awkward it was to make the “getoptery” work in Windows. You are welcome to replace replace Hanrahan’s arguments with something more foolproof as long as the functionality remains. 9The reason we need two gap variable is this. The deFault() functions resets all variables to their current default values. Most of these are numbers. But some can be managed from the command line. 10The capital F distinguishes this function from one with a similar name in some Iris header files. 11Note the clever way someone in the past discovered how to get a 1 exactly for ii={0,5,10,15}. Integer division yields the quotient, the mod function yields the remainder. 6 The keyboard() function converts the user’s wishes into the current state of the RTICA by means of illiGadgets.12 The code here is heavily aliased with keyboard() macros to make it easier for you to add and subtract gadgets. Since the pre- compiler handles these, their effective scope is global, but you can wait to define them to just before their first use. The arguments of keyboard() are the key name and location of the mouse (which we don’t use yet). The first thing we do is raise a flag in the key-vector clefs[] corresponding to the pressed key.13 This way functions other than the current one can know which keys were pressed. We can even register that several keys were pressed. It is the duty of the clefs[] readers, such as getbutton() to unset the flags. Five classes of illiGadgets are defined here by macros, though we use only the first three here. If you press the key described by the string K, then PRESS(K,A,b) will execute code fragment A if the key is capitalized, and other- wise b. The TOGGLE, CYCLE macros do as they are named. There is an integer and a float slider wannabe, which you can use advisedly. Be sure to experiment on a running illiSkel before reading further. Note, in particular, that repeater keys also repeat the action. This can cause trouble on particularly fast comput- ers and other measures have to be introduced into the macros to unpredictable values. This particular keyboard() has Stuart Levy’s advanced gadgetry partially installed. With it, you do not need to hunt for a particular value, say of the speed, torq parameters14 by pressing keys. You type a decimal number, such as 0.0, or .9, before pressing the (Q)-key, and that is the value of the parameter. To preserve the ability of changing a value quickly by a keypress, the bump() function multiplies the current value by (1 + ). Dividing by that sum is close to multiplying by 1− ). Exercise. The ways of wizards are not lightly to be tampered with. You could bump the nose, focal, mysiz, wfar, gap, amb, and pwr gadgets. But the multiplicative nature of bump suggests that it is inappropriate for some of these. Which? To demonstrate your understanding of SLevy’s gadgets, write an additive bump, which is useful in integral gadgets, like cyclers. The function of the keys is discussed later, at the place where their parame- ters and flags are implemented. But the last two gadgets merit some discussion. The ZKEY here simply calls the deFault() function. There is room here for a second set of defaults on the shifted-ZKEY. However, it is easily converted to a cycler that permits an even larger choice alternative initial conditions.15 12We call these objects “gadgets” to distinguish them from “widgets”, which serve the same purpose. Unlike widgeteers, we discourage pull-down menus, scroll-bars, simulated dashboards and the like, all of which distract you from using your eyes to look at the RTICA , and your hands from controlling its performance. Imagine puppeteers or airline pilots applying their hands and eyes to pull-down menus. 13We also guard against overrunning the array by making sure that key-numbers higher than 127 are registered in clef[0]. 14These influence the power behind your mouse. Slow computers need higher values of speed, torq and vice-versa. 15We encourage this and similar programming devices to reduce the waste of time in re- 7 some ambient light, and we take this into account by not letting lmb drop below the amb parameter. You can interactively play with amb and the pseudo-specular pwr. The specular point of a light-source on a surface is where the normal bisects the light and the viewing directions. We save ourselves extra computation by using the caustic point, which depends only on lmb. Furthermore, Phong light- ing uses a power of the specular cosine. We use only the tangent of that power function at 1, since that’s all that really matters. You should draw the graph of spec as a function of lmb, pwr to see what I mean. After deciding on the color of the vertex with Chris Hartman’s illiPaint, we take the larger of the diffuse and the specular components.19 The illiPaint method is a poor-man’s texture map. Each vertex is given a color whose RGB components are linear function of two variables dog, cat. These, in turn, are fractions of the way across the source patch [τ0, τ1]× [θ0, θ1]. Thus, the red component varies linearly with dog, and the blue component varies contra-linearly with cat. Can you explain what the green component does? Exercise. Knowing this little about drawvert() you should be able to cre- ate remarkable surfaces and painting effects. How about a toroidal surface both of whose radii vary with the parameter angles. For now only the autotymer() can change the source patch. Make this interactive with new gadgets. Make the surface fine and coarse meshed on demand. All the drawtor() function has left to do is create the mesh of triangles which constitutes the numerical torus. Depending on how fine we want this mesh (good colors) and how nimbly we want it to move (few vertices) we will choose the number of MERIDIANS, LATITUDES. The torus is actually drawn as a series of parallel strips. And that’s why we can have such a cheap gap making gadget. Exercise. Install a second object in the same world as the torus. For instance, a cube or a hypercube in drawcube(). The collection of all objects in the world (not the stars, though) are packed into drawall(). Once you have several objects you can practice your IFCLICK skills to place, size, and color them properly. The final function is the display callback of the GLUT-libary, here called drawcons(). This is where we integrate our own functions with those of Op- drawcons() neGL and GLUT. Line by line, this is what happens each time through the glut-mainloop. The color buffer bit and the depth buffer bit are cleared. By default, the color is black. But you can put a nicer background in here with glClearColor(0.1,0.2,0.3,0). We set one or two viewports, depending on whether the binocular flag is up or not. The viewport is usually coextensive with the drawing window. Here is an illustration why there is a difference: the same window has two viewports. 19Standard lighting models, such as the one proper to OpenGL, uses a polynomial of frac- tions rather than the maximal envelope of ambient, diffuse and specular components. This difference makes illiLight simpler to understand, easier to handle, and more robust, but all at the expense of sublety and versatility. 10 First we call for a perspective20 projection with glFrustum(xmin, xmax, ymin, ymax, near, far). In the raw, the six arguments of this funtion describe the frustum of a cone. The cone is produced by looking from the origin through a rectangular window21 lo- cated a distance near down the negative z-axis, and clipped in -far < z < -near < 0. This eternally confusing convention guaranteess a right-handed coordinate sys- tem with the x-axis pointing to the right, and the y-axis pointing upward. You may notice how we have rewired the conical viewing box to be controlled by more geometrical parameters. First we make sure that the aspect ratio remains constant so that when you re-mouse the window, the object doesn’t become oblongated. Second, we have a fraction mysiz, which couples the focal distance near=mysize*focal with the size of the rectangular canvas the view is painted on at near. The name derives from the task of making yourself so small that you can fly into an object without having it clipped by a large value of near. Try it! Play with the f(O)cal and the my s(I)ze gadgets. We next modify affine (or GL_MODEL_VIEW ) matrix, which will be multiplied into the projection matrix eventually. Starting with the identity we draw the stars. (Recall, the stars manage their own place with the starmat[].) We then shift a little off center to account for the left-eye right-eye displacement, equal to twice the nose to eye distance. Now multiply by aff[], the matrix that places the world relative to the origin in the way it would look if you moved about a stable world.22 We next draw the entire scene into the other eye, widen the viewport fully for the graffiti, and swap graphics buffers. This last call to GLUT draws all that is currently in the frame buffer to the screen, repeatedly, until a new frame complete and ready to replace the old one. 7 Navigation in Euclidean space. Since we are construcing a graphics program, the management of the geometry pipeline for each frame is truly the main activity. The principle of OpenGL is very simple but its ramifications are extensive and can be mysterious and confusing without a clear mathematical idea to guide us. A geometrical figure is constructed out of points, line segments and planar patches. Even is the lines and patches appear curved (and they are intended to so appear), they are nevertheless made up of subliminally small straight lines and flat patches. A polylateral23 curve is completely described the succession of vertices along it. 20Photographers and computer graphics systems that are less versatile than OpenGL favor on-axis or centered perspective with parameters based on the aperture angle, aspect ration, near and far. It is simple to implement this in the Frustum command. 21Indeed, this command is more aptly named “window” in IrisGL. It is unfortunate that this aptly named function had to be renamed in the OpenGL vocabulary. The term “window” had become too strongly tied to a resizeable viewport. So, avoid misunderstanding, OpenGL adopted the hardly euphonic word “fructrum”, meaning a section of any solid between two parallel planes, often mispelled as “frutrum”. 22The Copernican versus the Ptolemaic weltanschauung. OpenGL favors the latter. 23There is some difference between common math speak, and common computer graphics lingo. In graphics, a “polygon” means a patch enclosed by a polylateral. In geometry, a 11 Moreover, these vertices can be placed anywhere in space, so the polytaerals aren’t planar, or convex, and we won’t call them polygons. The first problem to be solved in drawing surfaces is how to reduce their description to vertex streams. A surface made up of planar, polylateral patches is a polyhedral surface. Only a triangle (or “trilateral”) is unambiguously planar. Consider four non-coplanar vertices in space and tetrahedron so formed. A particular succesion of the vertices separates the skin of the tetrahedron into two dihedrals corresponding to the two ways of triangulating a quadrilateral. Thus the triangle is the geometrical primitive, and all surfaces in computer graphics are triangulated. There are two useful ways a succession of vertices V0, V1, ..., Vn describes a succession of triangles. The GL_TRIANGLE_STRIP produces a ribbon with with two rails, one traced out by the even vertices, V0, V2, V4, ..., and the other by the odd vertices, V1, V3, V5, .... The polylateral following the original vertex stream zig-zags back and forth between the rails, like the rungs of a bridge tressle. Of course, vertices may be repeated in the list. For example, if V4 = V0, V5 = V1 then the triangle strip covers a tetrahedron. Exercise. An intriguing geometrical question is which polyhedral surfaces can be drawn by a single triangle strips, without covering a facet twice. Maybe you can so “bandage” every polyhedral surface. For instance, how would you “bandage” a cube, an octahedron, dodecahedron, and icosahedron? What about the graph z = f(x, y) over a rectangular grid in the xy-plane? Hint: There is a “Columbus Egg” solution to this problem. So, being vertex based, the geometry pipeline24 feeds on a stream of vertices, bracketed by the appropriate glBegin() and glEnd(), performing a series of geometric transformations on each vertex, and producing the desired graphic on the screen. Regardless how these transformations are implemented (hard- ware, high or low level library functions, etc) they are represented by a 4x4 matrix of real numbers. The composition of operations is represented by matrix multiplication. Each vertex stream, X, encounters three matrices at the top of their respec- tive pushdown stacks: the current modelling (a.k.a. affine) matrix A, followed by the current projection matrix Π, followed by the viewport matrix, Ψ, about which we say very little. Thus, column mode25 we see the effect of ΨΠAX. A useful way to relate the viewport to the window is to imagine looking There is some expository redundancy here. Maybe saying it again isn’t so bad for now. at the scene with the camera at the origin, and looking through a rectangular window with corners as specified, and a distance near from the camera, all in “polylateral” is synonymous with a polygon. We will deviate from common speech only to avoid misunderstanding. 24Strictly speaking, a pipeline refers to a process which accepts new input before having finished its job on the previous input. Think of Ford’s assembly line. But whether the pipeline is real or virtual, the conceptual structure is the same in OpenGL. 25After ten years of writing vectors as rows on the left of their matrix transformations in IrigGL, OpenGL reverted to the scientific notation which writes vectors as columns on the right of their matrix multipliers. This way, linear transformations compose like functions, from right-to left. You’ll have to learn how to handle this source of confusion and errors effectively. Algebra is the right language for this. 12 Thus once through chaptrack() in TURNMODE with dA = dTdU , has the effect of replacing A by A1 = T dA T−1 A = T dT dU T−1 T U = dT T dU U. Consequently, after n iterations, the net change is, An = dTn dTn−1...dT1 T dUn dUn−1 ... dU1 U. and the illiRotor has smoothly accumulated the translation and rotation in- tended by the mouse displacements. The last thing chaptrack() does is to rotate the light source direction by the inverse of the rotation component of aff[]. The the inverse of an affine matrix is (T (m)U(a))−1 = U(a)−1T (m)−1 = U(a)T T (−m) . Since we don’t translate the stars, we need only the matrix multiplication by the transpose of to top-left 3x3 minor of aff[]. Exercise. Six-way Navigator. Build a navigator which has all six translations. For example, let a click of the middle mouse button represent a switch into translation mode, with the mouse deviation from the center representing translation. Note that this way it is not possible to fly because the mouse manages two modes consecutively instead of simultaneously. To recover the simultaneity, use buttons to manage translations. All that remains is to assemble all of these components into main(). In the overture of main() we set up the state of the RTICA . These routines are done only once, and the order in which they occur does matter somewhat. We read the arguments() from the command line after the deFault() in case the user wishes some changes. Since deFault() is called by the (Z)ap-key, and at present, arguments() does not change the default values, zapping reverts to the hard coded defaults. Note that a useful modification would be for (Z)ap to become a cycler which moves through various default sets, including the one from the command line (exercise!) Since the star-object was factored into initstars() and drawstars(), the former is called here, the latter in the eternal loop. Recall that the other object, drawcube(), initializes ‘itself’ the first time it is called. The switch on the window flag defaults to a voluntary window for win=0, to a borderless window in the correct position for shipping to an NTSC32 projector for win=1, and a full screen forwin=2. Since we now use the GLUT library, the remainder of main() has already been described a the beginning of this chapter. Exercise. illiOctahedron Put the octahedron from oc1.cl into skel.c to obtain illiOctahedron. 32To fit into a standard NTSC video screen we use 640×480 pixels 15 /*****************************2002*************************************/ /**** skel.c = OpenSkelGlut.c = Noosh97 with CAVE removed ****/ /**** (C) 1994--2002 Board of Trustees University of Illinois ****/ /**** A Model Real-Time Interactive C/OpenGL/GLUT Animator ****/ /**** George Francis, Stuart Levy, Glenn Chappell, Chris Hartman****/ /**** e-mail gfrancis@math.uiuc.edu : revised 2jan02 by gkf ****/ /**********************************************************************/ #include <stdlib.h> #include <stdio.h> #include <gl\glut.h> /* sgi <glut.h> */ #include <sys/timeb.h> /* sgi <sys/time.h> */ #include <math.h> #pragma warning (disable:4305) /* constant double-to-float */ #define MAX(x,y) (((x)<(y))?(y):(x)) #define MIN(x,y) (((x)<(y))?(x):(y)) #define CLAMP(x,u,v) (x<u? u : (x>v ? v: x)) #define ABS(u) ((u)<0 ? -(u): (u)) #define FOR(a,b,c) for((a)=(b);(a)<(c);(a)++) #define DOT(p,q) ((p)[0]*(q)[0]+(p)[1]*(q)[1]+(p)[2]*(q)[2]) #define NRM(p) sqrt(DOT((p),(p))) #define M_PI (355./113) /* sgi already defins \pi */ #define DG M_PI/180 #define S(u) sin(u*DG) #define C(u) cos(u*DG) #define CLAMP(x,u,v) (x<u? u : (x>v ? v: x)) #define random rand /* library dependent name */ #define IFCLICK(K,a){static ff=1; if(getbutton(K))ff=1-ff; if(ff){a} } #define MERIDIANS 32 #define LATITUDES 32 #define MANYSTARS 10000 /************************** global variables **************************/ int win=1; /* used once to choose window size */ float gap, gap0=1.; /* deFault() uses gap0 set by arguments() */ float lux[3]={1.,2.,3.}; /* world light non unit vector */ float luxx[3]; /* object space direction vector */ float amb, pwr; /*ambient fraction, pseudo-specular power */ float mysiz,speed, torq, focal, wfar; /* navigation control variables */ unsigned int PAW,XX,YY,SHIF; /* used in chaptrack gluttery */ int xwide,yhigh; /* viewportery width and height */ int mode,morph,msg,binoc; /* viewing globals */ int th0, th1, dth, ta0, ta1, dta; /* torus parameters */ #define FLYMODE (0) /* yellow: turns around head as center */ #define TURNMODE (1) /* purple: turns around object center */ int ii, jj, kk; float tmp, temp; /* saves gray hairs later */ float aff[16], starmat[16], mat[16]; /* OpenGL placement matrices */ int binoc; /* flag for binocular stereo */ 16 float nose; /* to eye distance in console */ char clefs[128]; /* which keys were pressed last */ /*********************** steering *************************************/ void autotymer(int reset){ /* cheap animator */ #define TYME(cnt,max,act) {static cnt; if(first)cnt=max; else\ if(cnt?cnt--:0){ act ; goto Break;}} static first = 1; /* the first time autymer is called */ if(reset)first=1; /* or if it is reset to start over */ TYME( shrink , 150 , th0++;th1--;ta0++;ta1--) TYME( pause , 20, 0 ) TYME( grow , 150,th0--;th1++;ta0--;ta1++) TYME( dwell , 30, 0 ) TYME(finish , 1 , first = 1 ) /* this TYME must be the last one */ first = 0; Break: ; /* yes Virginia, C has gotos */ } /**********************************************************************/ void deFault(void){ /* (Z)ap also restores these assignments */ th0=5; th1=355; ta0=5; ta1=355; gap = gap0; /* torus parameters */ msg=1; binoc=0; nose=.06; mode=TURNMODE; /* gadget parameters */ speed=.02; torq=.02; focal = 2.; wfar=13; mysiz=.01; morph=0; FOR(ii,0,16) starmat[ii]=aff[ii] = (ii/4==ii%4); /* identity matrix */ amb = .3; pwr = 10; /* lighting params */ tmp=NRM(lux); FOR(ii,0,3)lux[ii] /= tmp; /* normalize light vector */ aff[12]=0; aff[13]= 0; aff[14]= -4.2; /* place where we can see it */ autotymer(1); /* reset autotymer to start at the beginning */ } /*************************** scenery **********************************/ void drawvert(int th, int ta){ /* make one properly lighted vertex */ float bb,gg,rr; float lmb,spec,nn[3], dog, cat; /* radius of unit sphere is also unit normal to the torus */ nn[0] = C(th)*C(ta); nn[1] = S(th)*C(ta); nn[2] = S(ta); /* illiLight by Ray Idaszak 1989 uses max{amb*lmb, rgb*lmb, spec} */ lmb = DOT(nn,luxx); lmb =(lmb<0 ? .2 : lmb); lmb = MAX(amb, lmb); spec = CLAMP((1.1 - pwr+pwr*lmb), 0., 1.); /* illiPaint by Chris Hartman 1993 maps R2(cat,dog)->R3(r,g,b) */ dog = (ta-ta0)/(float)(ta1-ta0); cat = (th-th0)/(float)(th1-th0); rr = MAX(spec, lmb*dog); gg = MAX(spec, lmb*(.25 + ABS(cat -.5))); bb = MAX(spec, lmb*(1 - cat)); glColor3f(rr,gg,bb); /* torus has unit small diameter and unit big radius */ glVertex3f( C(th) + .5*nn[0], S(th) + .5*nn[1], 0 + .5*nn[2]); } /* end drawvert */ /**********************************************************************/ void drawtor(void){ /* illiTorus with gaps */ int th, ta; dth = (int)((th1-th0)/MERIDIANS); /* this many meridian strips */ 17 case GLUT_KEY_F1: th0++; th1--; break; case GLUT_KEY_F2: th0--; th1++; break; /*default: fprintf(stderr,"nonASCII char [%d] was pressed.\n", key);*/ } } /**********************************************************************/ float speedometer(void){ /* this one is for win32*/ double dbl; static double rate; static int ii=0; static struct _timeb lnow, lthen; if(++ii % 8 == 0){ /* 8 times around measure time */ _ftime(&lnow); dbl = (double)(lnow.time - lthen.time) +(double)(lnow.millitm - lthen.millitm)/1000; lthen = lnow; rate = 8/dbl; } return((float)rate); } /**********************************************************************/ void char2wall(float x,float y,float z, char buf[]){ char *p; glRasterPos3f(x,y,z); for(p = buf;*p;p++) glutBitmapCharacter(GLUT_BITMAP_9_BY_15,*p); } /**********************************************************************/ void messages(void){char buf[256]; /* console messages are done differently from cave */ #define LABEL(x,y,W,u) {sprintf(buf,(W),(u));char2wall(x,y,0.,buf);} glMatrixMode(GL_PROJECTION); glPushMatrix(); /* new projection matrix */ glLoadIdentity(); gluOrtho2D(0,3000,0,3000); /* new 2D coordinates */ glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); if(mode==TURNMODE) glColor3f(1.,0.,1.); else glColor3f(1.,1.,0.); LABEL(1500,1500,"%s","o"); /* place a bullseye dead center */ LABEL(80,80,"%4.1f fps",speedometer()); if(msg==2)return; //try this LABEL(80,2840,\ "(ESC)ape (V)Binoc (MAUS2)Fore (BAR)%s (H)omotopy (W)riting", mode?"TURNMODE":"FLYMODE"); LABEL(10,10,"illiSkel-2002 by Francis, Levy, Bourd, Hartman,\ & Chappell, U Illinois, 1995..2002 %s",""); LABEL(80,2770,"(N)ose %0.3f",nose); LABEL(80,2700,"[S]peed %0.4f",speed); LABEL(80,2630," tor[Q] %0.4f",torq); LABEL(80,2560,"near clipper %g", mysiz*focal); LABEL(80,2490,"f(O)cal factor %g",focal); LABEL(80,2420,"my s(I)ze %.2g",mysiz); LABEL(80,2350,"far cli(P)per= %.2g",wfar); LABEL(80,2280,"(Z)ap %s",""); LABEL(80,2210,"(G)ap %.2g",gap); LABEL(80,2140,"(A)mb %.2g",amb); LABEL(80,2070,"pw(R) %.2g",pwr); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } /************************ navigation **********************************/ 20 void chaptrack(int paw,int xx,int yy,int shif){/* Glenn Chappell 1992 */ long dx,dy; dx = xx -.5*xwide; dx = abs(dx)>5?dx:0; /* 5 pixel latency */ dy = yy -.5*yhigh; dy = abs(dy)>5?dy:0; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); if(mode==TURNMODE) glTranslatef(aff[12],aff[13],aff[14]); glRotatef(dx*torq,0.,1.,0.); glRotatef(dy*torq,1.,0.,0.); if(paw&(1<<GLUT_RIGHT_BUTTON ))glRotatef(shif?-10:-1,0.,0.,1.); if(paw&(1<<GLUT_LEFT_BUTTON ))glRotatef(shif?10:1,0.,0.,1.); if(mode==FLYMODE){ glPushMatrix(); glMultMatrixf(starmat); glGetFloatv(GL_MODELVIEW_MATRIX,starmat); glPopMatrix(); } if(paw&(1<<GLUT_MIDDLE_BUTTON))glTranslatef(0.,0.,shif?-speed:speed); if(clefs[0]==GLUT_KEY_UP) glTranslatef(0.,0., speed); if(clefs[0]==GLUT_KEY_DOWN) glTranslatef(0.,0., -speed); if(clefs[0]==GLUT_KEY_LEFT) glTranslatef(-speed,0.,0.); if(clefs[0]==GLUT_KEY_RIGHT) glTranslatef(speed,0.,0.); if(clefs[0]==GLUT_KEY_PAGE_UP) glTranslatef(0., speed,0.); if(clefs[0]==GLUT_KEY_PAGE_DOWN) glTranslatef(0.,-speed,0.); if(mode==TURNMODE) glTranslatef(-aff[12],-aff[13],-aff[14]); glMultMatrixf(aff); glGetFloatv(GL_MODELVIEW_MATRIX,aff); FOR(ii,0,3){luxx[ii]=0; FOR(jj,0,3)luxx[ii] +=aff[ii*4+jj]*lux[jj];} glPopMatrix(); } /************************* scenery ************************************/ void reshaped(int xx, int yy){xwide=xx ; yhigh=yy;} /*win width,height*/ /**********************************************************************/ void drawcons(void){ float asp =(float)xwide/yhigh; /* aspect ratio */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(0,0,0,0); /* base color, try (.1,.2,.3,0.) */ if(binoc) glViewport(0,yhigh/4,xwide/2,yhigh/2); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-mysiz*asp,mysiz*asp,-mysiz,mysiz,mysiz*focal,wfar); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); drawstars(); glTranslatef(-binoc*nose,0.0,0.0); glMultMatrixf(aff); drawall(); if(binoc){ glViewport(xwide/2,yhigh/4,xwide/2,yhigh/2); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); drawstars(); glTranslatef(binoc*nose,0.0,0.0); glMultMatrixf(aff); drawall(); } glViewport(0,0,xwide,yhigh); if(msg) messages(); glutSwapBuffers(); 21 } /************************** steering **********************************/ void idle(void){ /* do this when nothing else is happening */ if(morph) autotymer(0); /* advance autotymer */ glutPostRedisplay(); /* redraw the window */ IFCLICK(’=’,chaptrack(PAW,XX,YY,SHIF);) /* bypass navigation */ glDisable(GL_DEPTH_TEST); /* bypass depth buffer */ IFCLICK(’-’,glEnable(GL_DEPTH_TEST); ) /* bypass depth buffer */ } /**********************************************************************/ void mousepushed(int but,int stat,int x,int y){ if(stat==GLUT_DOWN) PAW |= (1<<but); /*key came down and called back*/ else PAW &= (-1 ^ (1<<but)); /* on the wayup erase flag*/ XX=x; YY=y; /* position in window coordinates (pos integers) */ SHIF=(glutGetModifiers()==GLUT_ACTIVE_SHIFT)?1:0; /* shift down too*/ } /**********************************************************************/ void mousemoved(int x,int y){ XX=x; YY=y; } /***************** one ring to rule the all ***************************/ int main(int argc, char **argv){ arguments(argc,argv); /* from the commandline */ deFault(); /* values of control parameters */ glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH); switch(win){ case 0: break; /* manage your own window */ case 1: glutInitWindowSize(640, 480); glutInitWindowPosition(100,100); break; case 2: glutInitWindowPosition(0,0); break; } glutCreateWindow("<* illiSkel 2002 in C/OpenGL/GLUT *>"); if(win==2) glutFullScreen(); glEnable(GL_DEPTH_TEST); /* enable z-buffer */ glutDisplayFunc(drawcons); /* the following are optional for interactive control */ glutKeyboardFunc(keyboard); glutSpecialFunc(specialkeybo); glutMouseFunc(mousepushed); glutMotionFunc(mousemoved); glutPassiveMotionFunc(mousemoved); /* beyond here all are needed */ glutReshapeFunc(reshaped); glutIdleFunc(idle); glutMainLoop(); } /**********************************************************************/ 22
Docsity logo



Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved