home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / GLUT / progs / contrib / worms.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-11  |  13.5 KB  |  511 lines

  1. #if 0
  2. From jallen@cs.hmc.edu  Fri Feb 17 00:49:59 1995
  3. Received: from giraffe.asd.sgi.com by hoot.asd.sgi.com via SMTP (940816.SGI.8.6.9/940406.SGI.AUTO)
  4.     for <mjk@hoot.asd.sgi.com> id AAA13591; Fri, 17 Feb 1995 00:49:33 -0800
  5. Received: from sgi.sgi.com by giraffe.asd.sgi.com via SMTP (920330.SGI/920502.SGI)
  6.     for mjk@hoot.asd.sgi.com id AA09774; Fri, 17 Feb 95 00:52:30 -0800
  7. Received: from cs.hmc.edu by sgi.sgi.com via SMTP (950215.405.SGI.8.6.10/910110.SGI)
  8.     for <mjk@sgi.com> id AAA06439; Fri, 17 Feb 1995 00:52:28 -0800
  9. Received: by cs.hmc.edu (5.0/SMI-SVR4)
  10.     id AA13309; Fri, 17 Feb 1995 00:52:10 -0800
  11. Date: Fri, 17 Feb 1995 00:52:10 -0800
  12. From: jallen@cs.hmc.edu (Jeff R. Allen)
  13. Message-Id: <9502170852.AA13309@cs.hmc.edu>
  14. To: nate@cs.hmc.edu (Nathan Tuck), mjk@sgi.sgi.com, hadas@cs.hmc.edu
  15. Subject: Re: GLUT demos
  16. In-Reply-To: <9502100805.AA08487@cs.hmc.edu>
  17. References: <9502100805.AA08487@cs.hmc.edu>
  18. Reply-To: Jeff Allen <jeff@hmc.edu>
  19. Content-Length: 12851
  20. Status: RO
  21.  
  22. Below is a program I wrote for the Graphics class at Harvey Mudd. As
  23. the comments explain, I am currently working on a version in 3D with
  24. lighting, and a pre-programmed camera flight-path. I also added a
  25. checker-board-type-thing for the worms to crawl around on, so that
  26. there is some reference for the viewer.
  27.  
  28. For now, here is the program.
  29.  
  30. -- 
  31. Jeff R. Allen  |     Senior CS major    |    Support your local
  32. (fnord)        |    South 351d, x4940   |        unicyclist!
  33.  
  34. -------------------------  begin worms.c   -------------------------
  35. #endif
  36.  
  37. /* worms.c -- demos OpenGL in 2D using the GLUT interface to the
  38.               underlying window system.
  39.  
  40.    Compile with: [g]cc -O3 -o worms worms.c -lm -lGLU -lglut -lXmu -lX11 -lGL
  41.  
  42.    This is a fun little demo that actually makes very little use of
  43.    OpenGL and GLUT. It generates a bunch of worms and animates them as
  44.    they crawl around your screen. When you click in the screen with
  45.    the left mouse button, the worms converge on the spot for a while,
  46.    then go back to their business. The animation is incredibly simple:
  47.    we erase the tail, then draw a new head, repeatedly. It is so
  48.    simple, actually, we don't even need double-buffering!
  49.  
  50.    The behavior of the worms can be controlled via the compile-time
  51.    constants below. Enterprising indiviuals wil want to add GLUT menus
  52.    to control these constants at run time. This is left as an exercise
  53.    to the reader. The only thing that can currently be controlled is
  54.    wether or not the worms are filled. Use the right button to get a popup
  55.    menu.
  56.  
  57.    A future version of this program will make more use of OpenGL by
  58.    rendering 3d worms crawling in 3-space (or possibly just around on
  59.    a plane) and it will allow the user to manipulate the viewpoint
  60.    using the mouse. This will require double-buffering and less
  61.    optimal updates.
  62.  
  63.    This program is Copyright 1995 by Jeff R. Allen <jeff@hmc.edu>.
  64.    Permission is hereby granted to use and modify this code freely,
  65.    provided it is not sold or redistibuted in any way for profit. This
  66.    is copyrighted material, and is NOT in the Public Domain.
  67.  
  68.    $Id: worms.c,v 1.1 1996/02/28 03:22:11 dave Exp $
  69.  
  70.  */
  71.  
  72. #include <math.h>
  73. #include <stdlib.h>
  74. #include <sys/types.h>
  75. #include <time.h>
  76. #include <string.h>
  77. #include <GL/glut.h>
  78.  
  79. /* operational constants */
  80. #define RADIAN .0174532
  81. #define CIRCLE_POINTS 25
  82. #define PI 3.1415926535897
  83. #define SIDETOLERANCE .01
  84. #define INITH 500
  85. #define INITW 500
  86.  
  87. /* worm options */
  88. #define SEGMENTS 20
  89. #define SEG_RADIUS 0.01
  90. #define STEPSIZE 0.01
  91. #define MAXTURN (20 * RADIAN)        /* in radians */
  92. #define MAXWORMS 400
  93. #define INITWORMS 40
  94. #define MARKTICKS 100
  95.  
  96. typedef struct worm_s {
  97.   float dir;                   /* direction in radians */
  98.   float segx[SEGMENTS];        /* location of segments. */
  99.   float segy[SEGMENTS];
  100.   GLfloat *color;              /* pointer to the RGB color of the worm */
  101.   int head;                    /* which elt of seg[xy] is currently head */
  102.                                /* the tail is always (head+1 % SEGMENTS) */
  103. } worm_t;
  104.  
  105. /* colors available for worms... this is a huge mess because I
  106.    originally brought these colors in from rgb.txt as integers,
  107.    but they have to be normalized into floats. And C is stupid
  108.    and truncates them unless I add the annoying .0's
  109.  */
  110.  
  111. const GLfloat colors[][3] = {
  112.   { 255.0/255.0,   0.0/255.0,   0.0/255.0},
  113.   { 238.0/255.0,   0.0/255.0,   0.0/255.0},
  114.   { 205.0/255.0,   0.0/255.0,   0.0/255.0},
  115.   {   0.0/255.0, 255.0/255.0,   0.0/255.0},
  116.   {   0.0/255.0, 238.0/255.0,   0.0/255.0},
  117.   {   0.0/255.0, 205.0/255.0,   0.0/255.0},
  118.   {   0.0/255.0,   0.0/255.0, 255.0/255.0},
  119.   {   0.0/255.0,   0.0/255.0, 238.0/255.0},
  120.   {   0.0/255.0,   0.0/255.0, 205.0/255.0},
  121.   { 255.0/255.0, 255.0/255.0,   0.0/255.0},
  122.   { 238.0/255.0, 238.0/255.0,   0.0/255.0},
  123.   { 205.0/255.0, 205.0/255.0,   0.0/255.0},
  124.   {   0.0/255.0, 255.0/255.0, 255.0/255.0},
  125.   {   0.0/255.0, 238.0/255.0, 238.0/255.0},
  126.   {   0.0/255.0, 205.0/255.0, 205.0/255.0},
  127.   { 255.0/255.0,   0.0/255.0, 255.0/255.0},
  128.   { 238.0/255.0,   0.0/255.0, 238.0/255.0},
  129.   { 205.0/255.0,   0.0/255.0, 205.0/255.0},
  130. };
  131.  
  132. #define COLORS 18
  133.  
  134. /* define's for the menu item numbers */
  135. #define MENU_NULL          0
  136. #define MENU_FILLED        1
  137. #define MENU_UNFILLED      2
  138. #define MENU_QUIT          3
  139.  
  140. /* flag to determine how to draw worms; set by popup menu -- starts out
  141.    filled in
  142.  */
  143. int filled = 1;
  144.  
  145. /* the global worm array */
  146. worm_t worms[MAXWORMS];
  147. int curworms = 0;
  148.  
  149. /* global window extent variables */
  150. GLfloat gleft = -1.0, gright = 1.0, gtop = 1.0, gbottom = -1.0;
  151. GLint wsize, hsize;
  152.  
  153. /* globals for marking */
  154. float markx, marky;
  155. int marktime;
  156.  
  157. /* prototypes */
  158. void mydisplay(void);
  159.  
  160. void drawCircle(float x0, float y0, float radius)
  161. {
  162.   int i;
  163.   float angle;
  164.  
  165.   /* a table of offsets for a circle (used in drawCircle) */
  166.   static float circlex[CIRCLE_POINTS];
  167.   static float circley[CIRCLE_POINTS];
  168.   static int   inited = 0;
  169.  
  170.   if (! inited) {
  171.     for (i = 0; i < CIRCLE_POINTS; i++) {
  172.       angle = 2.0 * PI * i / CIRCLE_POINTS;
  173.       circlex[i] = cos(angle);
  174.       circley[i] = sin(angle);
  175.     }
  176.     inited++;
  177.   };
  178.  
  179.   if (filled)
  180.     glBegin(GL_POLYGON);
  181.   else
  182.     glBegin(GL_LINE_LOOP);
  183.   for(i = 0; i < CIRCLE_POINTS; i++)
  184.     glVertex2f((radius * circlex[i]) + x0, (radius * circley[i]) + y0);
  185.   glEnd();
  186.  
  187.   return;
  188. }
  189.  
  190. void drawWorm(worm_t *theworm)
  191. {
  192.   int i;
  193.  
  194.   glColor3fv(theworm->color);
  195.   for (i = 0; i < SEGMENTS; i++)
  196.     drawCircle(theworm->segx[i], theworm->segy[i], SEG_RADIUS);
  197.  
  198.   return;
  199. }
  200.  
  201. void myinit(void)
  202. {
  203.   int i, j, thecolor;
  204.   float thedir;
  205.  
  206.   srand48(time(NULL));
  207.  
  208.   curworms = INITWORMS;
  209.   
  210.   for (j = 0; j < curworms; j++) {
  211.     /* divide the circle up into a number of pieces, and send one worm
  212.        each direction.
  213.      */
  214.     worms[j].dir = ((2.0 * PI) / curworms) * j;
  215.     thedir = worms[j].dir;
  216.  
  217.     worms[j].segx[0] = 0.0;
  218.     worms[j].segy[0] = 0.0;
  219.  
  220.     for (i = 1; i < SEGMENTS; i++) {
  221.       worms[j].segx[i] = worms[j].segx[i-1] + (STEPSIZE * cos(thedir));
  222.       worms[j].segy[i] = worms[j].segx[i-1] + (STEPSIZE * sin(thedir));
  223.     };
  224.     worms[j].head = (SEGMENTS - 1);
  225.  
  226.     /* make this worm one of the predefined colors */
  227.     thecolor = (int) COLORS * drand48();
  228.     worms[j].color = (GLfloat *) colors[thecolor];
  229.   };
  230.  
  231.   /* now that they are all set, draw them as though they have just been
  232.      uncovered
  233.    */
  234.   mydisplay();
  235. }
  236.  
  237.  
  238.  
  239. /* this routine is called after the coordinates are changed to make sure
  240.    worms outside the window come back into view right away. (This behavior
  241.    is arbitrary, but they are my worms, and they'll do what I please!)
  242.  */
  243.  
  244. void warpWorms(void)
  245. {
  246.   register int j, head;
  247.  
  248.   for (j = 0; j < curworms; j++) {
  249.     head = worms[j].head;
  250.  
  251.     if (worms[j].segx[head] < gleft)
  252.       worms[j].segx[head] = gleft;
  253.     if (worms[j].segx[head] > gright)
  254.       worms[j].segx[head] = gright;
  255.     if (worms[j].segx[head] > gtop)
  256.       worms[j].segx[head] = gtop;
  257.     if (worms[j].segx[head] < gbottom)
  258.       worms[j].segx[head] = gbottom;      
  259.   }
  260. }
  261.  
  262. /* a bunch of extra hoopla goes on here to change the Global coordinate
  263.    space at teh same rate that the window itself changes. This give the
  264.    worms more space to play in when the window gets bigger, and vice versa.
  265.    The alternative would be to end up with big worms when the window gets
  266.    big, and that looks silly.
  267.  */
  268.  
  269. void myreshape (GLsizei w, GLsizei h)
  270. {
  271.   float ratiow = (float) w/INITW;
  272.   float ratioh = (float) h/INITH;
  273.  
  274.   glViewport(0,0,w,h);
  275.   glMatrixMode(GL_PROJECTION);
  276.   glLoadIdentity();
  277.  
  278.   gleft = -1 * ratiow;
  279.   gright = 1 * ratiow;
  280.   gbottom = -1 * ratioh;
  281.   gtop = 1 * ratioh;
  282.  
  283.   gluOrtho2D(gleft, gright, gbottom, gtop);
  284.   warpWorms();
  285.  
  286.   glMatrixMode(GL_MODELVIEW);
  287.   glLoadIdentity();
  288.  
  289.   wsize = w; hsize = h;
  290.  
  291.   return;
  292. }
  293.  
  294.  
  295. /* given a pointer to a worm, this routine will decide on the next
  296.    place to put a head and will advance the head pointer
  297.  */
  298.  
  299. void updateWorm(worm_t *theworm)
  300. {
  301.   int newhead;
  302.   float prevx, prevy;
  303.   float newh = -1, newv = -1;
  304.   float num, denom;
  305.  
  306.   /* make an easy to reference local copy of head, and update it in
  307.      the worm structure. The new head replaces the old tail.
  308.    */
  309.   newhead = (theworm->head + 1) % SEGMENTS;
  310.  
  311.   prevx = theworm->segx[theworm->head];
  312.   prevy = theworm->segy[theworm->head];
  313.  
  314.   /* if there is a mark, home in on it. After this, we still allow
  315.      the random adjustment so that the worms play around a bit on the
  316.      way to the mark.
  317.    */
  318.   if (marktime) {
  319.     num = marky - prevy;
  320.     denom = markx - prevx;
  321.     theworm->dir = atan2(num,denom);
  322.   };
  323.  
  324.   /* make a bit of a turn: between -MAXTURN and MAXTURN degrees change
  325.      to dir (actualy theworm->dir is in radians for later use with
  326.      cosf().
  327.    */
  328.   theworm->dir += (MAXTURN - (2 * MAXTURN * (float) drand48()));
  329.  
  330.   theworm->segx[newhead] = prevx + (STEPSIZE * cos(theworm->dir));
  331.   theworm->segy[newhead] = prevy + (STEPSIZE * sin(theworm->dir));
  332.  
  333.   /* if we are at an edge, change direction so that we are heading away
  334.      from the edge in question. There might be a problem here handling
  335.      corner cases, but I have never seen a worm get stuck, so what the
  336.      heck...
  337.    */
  338.   if (theworm->segx[newhead] <= gleft)
  339.     theworm->dir = 0;
  340.   if (theworm->segx[newhead] >= gright)
  341.     theworm->dir = (180 * RADIAN);
  342.   if (theworm->segy[newhead] >= gtop)
  343.     theworm->dir = (270 * RADIAN);
  344.   if (theworm->segy[newhead] <= gbottom)
  345.     theworm->dir = (90 * RADIAN);
  346.  
  347.   if ((newv >= 0) || (newh >= 0)) {
  348.     newh = (newh<0) ? 0 : newh;
  349.     newv = (newv<0) ? 0 : newv;
  350.   };
  351.  
  352.   /* update the permanent copy of the new head index */
  353.   theworm->head = newhead;
  354. }  
  355.  
  356. /* updates the worms -- drawing takes place here, which may actually
  357.    be a bad idea. It will probably be better to update the internal
  358.    state only here, then post a redisplay using GLUT.
  359. */
  360.  
  361. void myidle (void)
  362. {
  363.   register int i, tail;
  364.  
  365.   if (marktime)
  366.     marktime--;
  367.  
  368.   for (i = 0; i < curworms; i++) {
  369.     /* first find tail */
  370.     tail = (worms[i].head + 1) % SEGMENTS;
  371.   
  372.     /* erase tail */
  373.     glColor3f(0.0, 0.0, 0.0);
  374.     drawCircle(worms[i].segx[tail], worms[i].segy[tail], SEG_RADIUS);
  375.  
  376.     /* update head segment position and head pointer */
  377.     updateWorm(&worms[i]);
  378.  
  379.     /* draw head */
  380.     glColor3fv(worms[i].color);
  381.     drawCircle(worms[i].segx[worms[i].head], worms[i].segy[worms[i].head],
  382.            SEG_RADIUS);
  383.   };
  384.  
  385.   glFlush();
  386.   return;
  387. }
  388.  
  389. /* redraws the worms from scratch -- called after a window gets obscured */
  390.  
  391. void mydisplay(void)
  392. {
  393.   int i;
  394.  
  395. #ifndef WORMS_EAT_BACKGROUND
  396.   glClearColor(0.0, 0.0, 0.0, 0.0);
  397.   glClear(GL_COLOR_BUFFER_BIT);
  398. #endif
  399.  
  400.   for (i = 0; i < curworms; i++)
  401.     drawWorm(&worms[i]);
  402.  
  403.   glFlush();
  404.   return;
  405. }
  406.  
  407. /* this routine gets called when the mouse is clicked. The incoming
  408.    coordinates are in screen coordinates relative to the upper-left corner
  409.    of the window, and oriented according to X, not to GL. So, here we
  410.    convert the given coordinates into worm-world coordinates, and set the
  411.    mark.
  412.  */
  413.  
  414. void markSpot(int x, int y)
  415. {
  416.   /* map into the corridinate space I am using */
  417.   markx = (float)((x - wsize/2)*(gright - gleft)/wsize);
  418.   marky = -(float)((y - hsize/2)*(gtop - gbottom)/hsize);
  419.  
  420.   marktime = MARKTICKS;
  421. }
  422.  
  423. void handleMouse(int btn, int state, int x, int y)
  424. {
  425.   switch (btn) {
  426.  
  427.   case (GLUT_LEFT_BUTTON):
  428.     if (state == GLUT_UP)
  429.       markSpot(x,y);
  430.     break;
  431.  
  432.   default:
  433.     /* do nothing */
  434.     break;
  435.   }
  436.  
  437.   return;
  438. }
  439.  
  440. void menuSelect(int value)
  441. {
  442.   switch (value) {
  443.     case MENU_FILLED:
  444.       filled = 1;
  445.       break;
  446.  
  447.     case MENU_UNFILLED:
  448.       filled = 0;
  449.       break;
  450.  
  451.     case MENU_QUIT:
  452.       exit(0);
  453.       break;
  454.  
  455.     case MENU_NULL:
  456.       return;
  457.  
  458.     default:
  459.       break;
  460.     };
  461.  
  462.   glutPostRedisplay();
  463.   return;
  464. }
  465.  
  466. void visibility(int status)
  467. {
  468.   if (status == GLUT_VISIBLE)
  469.     glutIdleFunc(myidle);
  470.   else
  471.     glutIdleFunc(NULL);
  472. }
  473.  
  474. /* this is where GLUT is initialized, and the whole thing starts up.
  475.    All animation and redisplay happens via the callbacks registered below.
  476.  */
  477.  
  478. int main(int argc, char **argv)
  479. {
  480.   int fillmenu = 0;
  481.  
  482.   glutInit(&argc, argv);
  483.   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  484.   glutInitWindowSize(INITW, INITH);
  485.   glutCreateWindow("Worms");
  486.  
  487.   myinit();
  488.  
  489.   glutDisplayFunc(mydisplay);
  490.   glutVisibilityFunc(visibility);
  491.   glutReshapeFunc(myreshape);
  492.   glutMouseFunc(handleMouse);
  493.  
  494.   /* popup menu, courtsey of GLUT */
  495.   fillmenu = glutCreateMenu(menuSelect);
  496.   glutAddMenuEntry("Filled", MENU_FILLED);
  497.   glutAddMenuEntry("Unfilled", MENU_UNFILLED);
  498.  
  499.   glutCreateMenu(menuSelect);
  500.   glutAddMenuEntry("     WORMS", MENU_NULL);
  501.   glutAddSubMenu("Drawing Mode", fillmenu);
  502.   glutAddMenuEntry("Quit", MENU_QUIT);
  503.  
  504.   glutAttachMenu(GLUT_RIGHT_BUTTON);
  505.  
  506.   glutMainLoop();
  507.   return 0;
  508. }
  509.  
  510.  
  511.