1 /* winduprobot, Copyright (c) 2014 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Draws a robot wind-up toy.
13 * I've had this little robot since I was about six years old! When the time
14 * came for us to throw the Cocktail Robotics Grand Challenge at DNA Lounge, I
15 * photographed this robot (holding a tiny martini glass) to make a flyer for
16 * the event. You can see that photo here:
17 * http://www.dnalounge.com/flyers/2014/09/14.html
19 * Then I decided to try and make award statues for the contest by modeling
20 * this robot and 3D-printing it (a robot on a post, with the DNA Lounge
21 * grommet around it.) So I learned Maya and built a model.
23 * Well, that 3D printing idea didn't work out, but since I had the model
24 * already, I exported it and turned it into a screen saver.
26 * The DXF files that Maya exports aren't simple enough for my dxf2gl.pl
27 * script to process, so the exporting process went:
29 * - Save from Maya to OBJ;
30 * - Import OBJ into SketchUp using
31 * http://www.scriptspot.com/sketchup/scripts/obj-importer
32 * - Clean up the model a little bit more;
33 * - Export to DXF with "Millimeters", "Triangles", using
34 * http://www.guitar-list.com/download-software/convert-sketchup-skp-files-dxf-or-stl
37 #define LABEL_FONT "-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*"
39 #define DEFAULTS "*delay: 20000 \n" \
41 "*showFPS: False \n" \
42 "*wireframe: False \n" \
43 "*labelFont: " LABEL_FONT "\n" \
44 "*legColor: #AA2222" "\n" \
45 "*armColor: #AA2222" "\n" \
46 "*handColor: #AA2222" "\n" \
47 "*crankColor: #444444" "\n" \
48 "*bodyColor: #7777AA" "\n" \
49 "*domeColor: #7777AA" "\n" \
50 "*insideColor: #DDDDDD" "\n" \
51 "*gearboxColor: #444488" "\n" \
52 "*gearColor: #008877" "\n" \
53 "*wheelColor: #007788" "\n" \
54 "*wireColor: #006600" "\n" \
55 "*groundColor: #0000FF" "\n" \
56 "*textColor: #FFFFFF""\n" \
57 "*textBackground: #444444""\n" \
58 "*textBorderColor: #FFFF88""\n" \
60 "*program: xscreensaver-text\n" \
66 # define refresh_robot 0
68 #define countof(x) (sizeof((x))/sizeof((*x)))
70 #define DEF_SPEED "1.0"
71 #define DEF_ROBOT_SIZE "1.0"
72 #define DEF_TEXTURE "True"
73 #define DEF_FADE "True"
74 #define DEF_OPACITY "1.0"
75 #define DEF_TALK "0.2"
77 #include "xlockmore.h"
78 #include "gltrackball.h"
79 #include "xpm-ximage.h"
84 # include "textclient.h"
90 #ifndef HAVE_JWZGLES /* No SPHERE_MAP on iPhone */
95 # include "../images/chromesphere.xpm"
98 #ifdef USE_GL /* whole file */
102 extern const struct gllist
103 *robot_arm_half, *robot_body_half_outside, *robot_body_half_inside,
104 *robot_crank_full, *robot_gearbox_half, *robot_hand_half,
105 *robot_leg_half, *robot_rotator_half, *robot_wireframe;
107 static struct gllist *robot_dome = 0, *robot_gear = 0, *ground = 0;
109 static const struct gllist * const *all_objs[] = {
110 &robot_arm_half, &robot_body_half_outside, &robot_body_half_inside,
111 &robot_crank_full, &robot_gearbox_half, &robot_hand_half,
112 &robot_leg_half, &robot_rotator_half, &robot_wireframe,
113 (const struct gllist * const *) &robot_dome,
114 (const struct gllist * const *) &robot_gear,
115 (const struct gllist * const *) &ground
119 #define ROBOT_BODY_1 1
120 #define ROBOT_BODY_2 2
121 #define ROBOT_CRANK 3
122 #define ROBOT_GEARBOX 4
125 #define ROBOT_ROTATOR 7
126 #define ROBOT_WIREFRAME 8
128 #define ROBOT_GEAR 10
132 GLfloat x, y, z; /* position */
133 GLfloat facing; /* direction of front of robot, degrees */
134 GLfloat pitch; /* front/back tilt angle, degrees */
135 GLfloat roll; /* left/right tilt angle, degrees */
136 GLfloat speed; /* some robots are faster */
137 GLfloat crank_rot; /* gear state, degrees */
138 GLfloat hand_rot[2]; /* rotation of the hands, degrees */
139 GLfloat hand_pos[2]; /* openness of the hands, ratio */
140 GLfloat balance; /* how off-true does it walk? degrees */
141 GLfloat body_transparency; /* ratio */
142 int fading_p; /* -1, 0, 1 */
147 GLXContext *glx_context;
148 trackball_state *user_trackball;
152 GLfloat component_colors[countof(all_objs)][4];
157 GLfloat looking_x, looking_y, looking_z; /* Where camera is aimed */
158 GLfloat olooking_x, olooking_y, olooking_z; /* Where camera was aimed */
159 Bool camera_tracking_p; /* Whether camera in motion */
160 GLfloat tracking_ratio;
163 GLuint chrome_texture;
167 texture_font_data *font_data;
171 int lines, max_lines;
173 GLfloat text_color[4], text_bg[4], text_bd[4];
175 # endif /* WORDBUBBLES */
179 GLfloat debug_x, debug_y, debug_z;
182 } robot_configuration;
184 static robot_configuration *bps = NULL;
186 static GLfloat speed, size, opacity;
187 static int do_texture, do_fade;
189 static GLfloat talk_chance;
195 static XrmOptionDescRec opts[] = {
196 { "-speed", ".speed", XrmoptionSepArg, 0 },
197 { "-size", ".robotSize", XrmoptionSepArg, 0 },
198 { "-opacity", ".opacity", XrmoptionSepArg, 0 },
199 { "-talk", ".talk", XrmoptionSepArg, 0 },
200 {"-texture", ".texture", XrmoptionNoArg, "True" },
201 {"+texture", ".texture", XrmoptionNoArg, "False" },
202 {"-fade", ".fade", XrmoptionNoArg, "True" },
203 {"+fade", ".fade", XrmoptionNoArg, "False" },
205 {"-debug", ".debug", XrmoptionNoArg, "True" },
206 {"+debug", ".debug", XrmoptionNoArg, "False" },
210 static argtype vars[] = {
211 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
212 {&size, "robotSize", "RobotSize", DEF_ROBOT_SIZE, t_Float},
213 {&opacity, "opacity", "Opacity", DEF_OPACITY, t_Float},
214 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
215 {&do_fade, "fade", "Fade", DEF_FADE, t_Bool},
217 {&talk_chance, "talk", "Talk", DEF_TALK, t_Float},
220 {&debug_p, "debug", "Debug", "False", t_Bool},
224 ENTRYPOINT ModeSpecOpt robot_opts = {
225 countof(opts), opts, countof(vars), vars, NULL};
228 /* Window management, etc
231 reshape_robot (ModeInfo *mi, int width, int height)
233 GLfloat h = (GLfloat) height / (GLfloat) width;
235 glViewport (0, 0, (GLint) width, (GLint) height);
237 glMatrixMode(GL_PROJECTION);
239 gluPerspective (40.0, 1/h, 1.0, 250);
241 glMatrixMode(GL_MODELVIEW);
243 gluLookAt( 0, 20, 30,
247 glClear(GL_COLOR_BUFFER_BIT);
251 robot_configuration *bp = &bps[MI_SCREEN(mi)];
252 int w = (width < 800 ? 25 : 40);
255 textclient_reshape (bp->tc, w, h, w, h,
256 /* Passing bp->max_lines isn't actually necessary */
265 robot_handle_event (ModeInfo *mi, XEvent *event)
267 robot_configuration *bp = &bps[MI_SCREEN(mi)];
269 if (gltrackball_event_handler (event, bp->user_trackball,
270 MI_WIDTH (mi), MI_HEIGHT (mi),
274 else if (event->xany.type == KeyPress && debug_p)
278 double n[3] = { 1.0, 0.1, 0.1 };
279 int s = (event->xkey.state & ShiftMask ? 10 : 1);
281 XLookupString (&event->xkey, &c, 1, &keysym, 0);
283 if (keysym == XK_Right) bp->debug_x += n[0] * s;
284 else if (keysym == XK_Left) bp->debug_x -= n[0] * s;
285 else if (keysym == XK_Up) bp->debug_y += n[1] * s;
286 else if (keysym == XK_Down) bp->debug_y -= n[1] * s;
287 else if (c == '=' || c == '+') bp->debug_z += n[2] * s;
288 else if (c == '-' || c == '_') bp->debug_z -= n[2] * s;
289 else if (c == ' ') bp->debug_x = bp->debug_y = bp->debug_z = 0;
290 else if (c == '\n' || c == '\r')
291 fprintf (stderr, "%.4f %.4f %.4f\n",
292 bp->debug_x, bp->debug_y, bp->debug_z);
305 load_textures (ModeInfo *mi)
307 robot_configuration *bp = &bps[MI_SCREEN(mi)];
310 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
314 glGenTextures (1, &bp->chrome_texture);
315 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
316 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
317 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
318 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
319 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
320 xi->width, xi->height, 0,
323 GL_UNSIGNED_INT_8_8_8_8_REV,
328 check_gl_error("texture");
330 glEnable(GL_TEXTURE_GEN_S);
331 glEnable(GL_TEXTURE_GEN_T);
332 glEnable(GL_TEXTURE_2D);
335 #endif /* HAVE_TEXTURE */
338 static int unit_gear (ModeInfo *, GLfloat color[4]);
339 static int draw_ground (ModeInfo *, GLfloat color[4]);
340 static void init_walker (ModeInfo *, walker *);
343 parse_color (ModeInfo *mi, char *key, GLfloat color[4])
346 char *string = get_string_resource (mi->dpy, key, "RobotColor");
347 if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
349 fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
354 color[0] = xcolor.red / 65536.0;
355 color[1] = xcolor.green / 65536.0;
356 color[2] = xcolor.blue / 65536.0;
362 init_robot (ModeInfo *mi)
364 robot_configuration *bp;
365 int wire = MI_IS_WIREFRAME(mi);
368 bps = (robot_configuration *)
369 calloc (MI_NUM_SCREENS(mi), sizeof (robot_configuration));
371 fprintf(stderr, "%s: out of memory\n", progname);
376 bp = &bps[MI_SCREEN(mi)];
378 bp->glx_context = init_GL(mi);
380 reshape_robot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
382 glShadeModel(GL_SMOOTH);
384 glEnable(GL_DEPTH_TEST);
385 glEnable(GL_NORMALIZE);
386 glEnable(GL_CULL_FACE);
390 GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
391 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
392 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
393 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
394 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
396 glEnable(GL_LIGHTING);
398 glEnable(GL_DEPTH_TEST);
399 glEnable(GL_CULL_FACE);
401 glLightfv(GL_LIGHT0, GL_POSITION, pos);
402 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
403 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
404 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
406 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
410 if (!wire && do_texture)
414 bp->user_trackball = gltrackball_init (False);
416 bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
417 for (i = 0; i < countof(all_objs); i++)
418 bp->dlists[i] = glGenLists (1);
420 for (i = 0; i < countof(all_objs); i++)
422 const struct gllist *gll = *all_objs[i];
424 GLfloat spec1[4] = {1.00, 1.00, 1.00, 1.0};
425 GLfloat spec2[4] = {0.40, 0.40, 0.70, 1.0};
426 GLfloat *spec = spec1;
429 glNewList (bp->dlists[i], GL_COMPILE);
431 glMatrixMode(GL_MODELVIEW);
433 glMatrixMode(GL_TEXTURE);
435 glMatrixMode(GL_MODELVIEW);
437 glRotatef (-90, 1, 0, 0);
438 glRotatef (180, 0, 0, 1);
441 glBindTexture (GL_TEXTURE_2D, 0);
451 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
452 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
453 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
464 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
465 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
466 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
506 key = "gearboxColor";
510 case ROBOT_WIREFRAME:
524 parse_color (mi, key, bp->component_colors[i]);
526 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
527 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
532 robot_dome = (struct gllist *) calloc (1, sizeof(*robot_dome));
533 robot_dome->points = unit_dome (32, 32, MI_IS_WIREFRAME(mi));
537 robot_gear = (struct gllist *) calloc (1, sizeof(*robot_gear));
538 robot_gear->points = unit_gear (mi, bp->component_colors[i]);
542 ground = (struct gllist *) calloc (1, sizeof(*ground));
543 ground->points = draw_ground (mi, bp->component_colors[i]);
545 case ROBOT_WIREFRAME:
547 renderList (gll, True);
550 renderList (gll, wire);
551 /* glColor3f (1, 1, 1); renderListNormals (gll, 100, True); */
552 /* glColor3f (1, 1, 0); renderListNormals (gll, 100, False); */
556 glMatrixMode(GL_TEXTURE);
558 glMatrixMode(GL_MODELVIEW);
565 if (debug_p) MI_COUNT(mi) = 1;
568 bp->nwalkers = MI_COUNT(mi);
569 bp->walkers = (walker *) calloc (bp->nwalkers, sizeof (walker));
571 for (i = 0; i < bp->nwalkers; i++)
572 init_walker (mi, &bp->walkers[i]);
574 /* Since #0 is the one we track, make sure it doesn't walk too straight.
576 bp->walkers[0].balance *= 1.5;
579 bp->font_data = load_texture_font (mi->dpy, "labelFont");
580 bp->max_lines = get_integer_resource (mi->dpy, "textLines", "TextLines");
581 bp->tc = textclient_open (MI_DISPLAY (mi));
583 parse_color (mi, "textColor", bp->text_color);
584 parse_color (mi, "textBackground", bp->text_bg);
585 parse_color (mi, "textBorderColor", bp->text_bd);
587 reshape_robot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
589 # endif /* WORDBUBBLES */
591 /* Let's tilt the floor a little. */
596 gltrackball_start (bp->user_trackball, 0, 500, 1000, 1000);
597 gltrackball_track (bp->user_trackball,
598 0, 500 + (random() % 200) - 100,
605 draw_component (ModeInfo *mi, int i)
607 robot_configuration *bp = &bps[MI_SCREEN(mi)];
608 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
609 bp->component_colors[i]);
610 glCallList (bp->dlists[i]);
611 return (*all_objs[i])->points / 3;
615 draw_transparent_component (ModeInfo *mi, int i, GLfloat alpha)
617 robot_configuration *bp = &bps[MI_SCREEN(mi)];
618 int wire = MI_IS_WIREFRAME(mi);
621 if (alpha < 0) return 0;
622 if (alpha > 1) alpha = 1;
623 bp->component_colors[i][3] = alpha;
625 if (wire || alpha >= 1)
626 return draw_component (mi, i);
628 /* Draw into the depth buffer but not the frame buffer */
629 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
630 count += draw_component (mi, i);
632 /* Now draw into the frame buffer only where there's already depth */
633 glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
634 glDepthFunc (GL_EQUAL);
636 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
638 count += draw_component (mi, i);
639 glDepthFunc (GL_LESS);
640 glDisable (GL_BLEND);
647 draw_arm_half (ModeInfo *mi, walker *f)
651 count += draw_transparent_component (mi, ROBOT_ARM, f->body_transparency);
657 draw_hand_half (ModeInfo *mi, walker *f)
661 count += draw_transparent_component (mi, ROBOT_HAND, f->body_transparency);
667 /* rotation of arm: 0-360.
668 openness of fingers: 0.0 - 1.0.
671 draw_arm (ModeInfo *mi, walker *f, GLfloat left_p, GLfloat rot, GLfloat open)
674 GLfloat arm_x = 4766; /* distance from origin to arm axis */
675 GLfloat arm_y = 12212;
677 open *= 5.5; /* scale of finger range */
680 if (debug_p) rot = 0;
686 glTranslatef (0, 0, arm_x * 2);
688 glTranslatef (0, arm_y, -arm_x); /* move to origin */
689 glRotatef (rot, 1, 0, 0);
690 glTranslatef (0, -arm_y, arm_x); /* move back */
693 count += draw_arm_half (mi, f);
696 glTranslatef (0, -arm_y * 2, 0);
698 count += draw_arm_half (mi, f);
701 glTranslatef (0, 0, -arm_x * 2);
704 count += draw_arm_half (mi, f);
707 glTranslatef (0, -arm_y * 2, 0);
709 count += draw_arm_half (mi, f);
712 glTranslatef (0, 0, open);
714 count += draw_hand_half (mi, f);
716 glTranslatef (0, 0, -open);
718 glTranslatef (0, 0, arm_x * 2);
720 glTranslatef (0, 0, open);
721 count += draw_hand_half (mi, f);
729 draw_body_half (ModeInfo *mi, walker *f, Bool inside_p)
732 int which = (inside_p ? ROBOT_BODY_2 : ROBOT_BODY_1);
734 count += draw_transparent_component (mi, which, f->body_transparency);
741 draw_body (ModeInfo *mi, walker *f, Bool inside_p)
747 count += draw_body_half (mi, f, inside_p);
751 count += draw_body_half (mi, f, inside_p);
760 draw_gearbox_half (ModeInfo *mi)
764 count += draw_component (mi, ROBOT_GEARBOX);
771 draw_gearbox (ModeInfo *mi)
777 count += draw_gearbox_half (mi);
781 count += draw_gearbox_half (mi);
789 unit_gear (ModeInfo *mi, GLfloat color[4])
791 int wire = MI_IS_WIREFRAME(mi);
799 g->thickness2 = g->thickness * 0.5;
800 g->thickness3 = g->thickness;
801 g->inner_r = g->r * 0.7;
802 g->inner_r2 = g->r * 0.4;
803 g->inner_r3 = g->r * 0.1;
804 g->size = INVOLUTE_LARGE;
806 g->color[0] = g->color2[0] = color[0];
807 g->color[1] = g->color2[1] = color[1];
808 g->color[2] = g->color2[2] = color[2];
809 g->color[3] = g->color2[3] = color[3];
811 return draw_involute_gear (g, wire);
816 draw_gear (ModeInfo *mi)
821 count += draw_component (mi, ROBOT_GEAR);
827 draw_crank (ModeInfo *mi, walker *f, GLfloat rot)
830 GLfloat origin = 12210.0;
836 glTranslatef (0, origin, 0); /* position at origin */
837 glRotatef (rot, 0, 0, 1);
840 glRotatef (90, 1, 0, 0);
841 count += draw_gear (mi);
844 glTranslatef (0, -origin, 0); /* move back */
847 count += draw_component (mi, ROBOT_CRANK);
856 draw_rotator_half (ModeInfo *mi)
860 count += draw_component (mi, ROBOT_ROTATOR);
867 draw_rotator (ModeInfo *mi, walker *f, GLfloat rot)
870 GLfloat origin = 10093.0;
874 glTranslatef (0, origin, 0); /* position at origin */
875 glRotatef (rot, 0, 0, 1);
878 glRotatef (90, 1, 0, 0);
879 count += draw_gear (mi);
885 glDisable(GL_LIGHTING);
887 glVertex3f(0, 0, -3000);
888 glVertex3f(0, 0, 3000);
890 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
894 glTranslatef (0, -origin, 0); /* move back */
897 count += draw_rotator_half (mi);
901 glRotatef (180, 0, 0, 1);
902 glTranslatef (0, -origin * 2, 0); /* move back */
903 count += draw_rotator_half (mi);
911 draw_leg_half (ModeInfo *mi)
915 count += draw_component (mi, ROBOT_LEG);
922 draw_wireframe (ModeInfo *mi, walker *f)
924 int wire = MI_IS_WIREFRAME(mi);
926 GLfloat alpha = 0.6 - f->body_transparency;
927 if (alpha < 0) return 0;
929 if (!wire) glDisable (GL_LIGHTING);
931 count += draw_transparent_component (mi, ROBOT_WIREFRAME, alpha);
933 if (!wire) glEnable (GL_LIGHTING);
939 draw_leg (ModeInfo *mi, GLfloat rot, Bool left_p)
943 GLfloat leg_distance = 9401; /* distance from ground to leg axis */
944 GLfloat rot_distance = 10110; /* distance from ground to rotator axis */
945 GLfloat pin_distance = 14541; /* distance from ground to stop pin */
946 GLfloat orbit_r = rot_distance - leg_distance; /* radius of rotation */
948 /* Actually it's the bottom of the pin minus its diameter, or something. */
954 glRotatef (180, 0, 1, 0);
956 if (!left_p) rot = -(rot + 180);
960 x = orbit_r * cos (-rot * M_PI / 180);
961 y = orbit_r * sin (-rot * M_PI / 180);
964 /* Rotate the leg by angle B of the right A
965 triangle ABC, where: /:
967 A = position of stop pin / :
968 D = position of rotator wheel's axis , - ~ ~ ~ - ,
969 C = D + y , ' / : ' ,
970 B = D + xy (leg's axis) , / : ,
973 H = dist(A,B) , / D ,
974 O = dist(A,C) , / : ,
975 sin(th) = O/H , / : ,
976 th = asin(O/H) B ~ ~ ~ ~ ~ C .
980 GLfloat Ay = pin_distance - leg_distance;
981 GLfloat Cx = 0, Cy = y;
983 GLfloat dBC = Cx - Bx;
984 GLfloat dAC = Cy - Ay;
985 GLfloat dAB = sqrt (dBC*dBC + dAC*dAC);
986 GLfloat th = asin (dAC / dAB);
987 rot = th / (M_PI / 180.0);
989 if (dBC > 0) rot = 360-rot;
992 glTranslatef (0, orbit_r, 0); /* position on rotator axis */
993 glTranslatef (x, y, 0);
995 glTranslatef (0, leg_distance, 0); /* position on leg axis */
996 glRotatef(rot, 0, 0, 1);
997 glTranslatef (0, -leg_distance, 0); /* move back */
1000 count += draw_leg_half (mi);
1002 glScalef (-1, 1, 1);
1004 count += draw_leg_half (mi);
1012 draw_dome (ModeInfo *mi, walker *f)
1014 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1015 int wire = MI_IS_WIREFRAME(mi);
1016 int which = ROBOT_DOME;
1019 GLfloat trans = f->body_transparency;
1021 GLfloat dome_y = 15290;
1023 if (trans < 0) trans = 0;
1024 if (trans > max) trans = max;
1026 if (!wire) glEnable (GL_BLEND);
1029 glTranslatef (0, dome_y, 0);
1030 glScalef (100, 100, 100);
1031 glRotatef (90, 1, 0, 0);
1032 glTranslatef (0.35, 0, 0);
1034 glFrontFace(GL_CCW);
1035 bp->component_colors[which][3] = trans;
1036 count += draw_component (mi, which);
1039 if (!wire) glDisable (GL_BLEND);
1045 /* Is this robot overlapping any other?
1048 collision_p (ModeInfo *mi, walker *w, GLfloat extra_space)
1050 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1052 if (MI_COUNT(mi) <= 1) return False;
1054 for (i = 0; i < MI_COUNT(mi); i++)
1056 walker *w2 = &bp->walkers[i];
1057 GLfloat min = 0.75 + extra_space;
1059 if (w == w2) continue;
1062 d = (dx*dx + dy*dy);
1063 if (d <= min*min) return True;
1069 /* I couldn't figure out a straightforward trig solution to the
1070 forward/backward tilting that happens as weight passes from one
1071 foot to another, so I just built a tool to eyeball it manually.
1072 { vertical_translation, rotation, forward_translation }
1074 static const struct { GLfloat up, rot, fwd; } wobble_profile[360] = {
1075 { 0.0000, 0.00, 0.0000 }, /* 0 */
1076 { 0.0000, -0.01, 0.0025 },
1077 { 0.0000, -0.25, 0.0040 },
1078 { 0.0000, -0.41, 0.0060 },
1079 { 0.0000, -0.62, 0.0080 },
1080 { 0.0000, -0.80, 0.0095 },
1081 { 0.0000, -0.90, 0.0120 },
1082 { 0.0000, -1.10, 0.0135 },
1083 { 0.0000, -1.25, 0.0150 },
1084 { 0.0000, -1.40, 0.0175 },
1085 { 0.0000, -1.50, 0.0195 }, /* 10 */
1086 { 0.0000, -1.70, 0.0215 },
1087 { 0.0000, -1.80, 0.0230 },
1088 { 0.0000, -2.00, 0.0250 },
1089 { 0.0000, -2.10, 0.0270 },
1090 { -0.0005, -2.30, 0.0290 },
1091 { -0.0005, -2.50, 0.0305 },
1092 { -0.0005, -2.60, 0.0330 },
1093 { -0.0005, -2.70, 0.0330 },
1094 { -0.0005, -2.90, 0.0350 },
1095 { -0.0005, -3.00, 0.0365 }, /* 20 */
1096 { -0.0010, -3.20, 0.0380 },
1097 { -0.0005, -3.30, 0.0400 },
1098 { -0.0010, -3.50, 0.0420 },
1099 { -0.0010, -3.70, 0.0440 },
1100 { -0.0010, -3.80, 0.0460 },
1101 { -0.0010, -3.90, 0.0470 },
1102 { -0.0015, -4.10, 0.0500 },
1103 { -0.0015, -4.20, 0.0515 },
1104 { -0.0015, -4.40, 0.0535 },
1105 { -0.0015, -4.50, 0.0550 }, /* 30 */
1106 { -0.0015, -4.60, 0.0565 },
1107 { -0.0020, -4.80, 0.0585 },
1108 { -0.0020, -5.00, 0.0600 },
1109 { -0.0020, -5.10, 0.0620 },
1110 { -0.0025, -5.20, 0.0635 },
1111 { -0.0025, -5.40, 0.0655 },
1112 { -0.0025, -5.50, 0.0675 },
1113 { -0.0025, -5.60, 0.0690 },
1114 { -0.0030, -5.80, 0.0710 },
1115 { -0.0030, -5.90, 0.0720 }, /* 40 */
1116 { -0.0030, -6.00, 0.0740 },
1117 { -0.0035, -6.10, 0.0760 },
1118 { -0.0035, -6.30, 0.0790 },
1119 { -0.0040, -6.40, 0.0805 },
1120 { -0.0040, -6.50, 0.0820 },
1121 { -0.0040, -6.60, 0.0835 },
1122 { -0.0045, -6.80, 0.0855 },
1123 { -0.0045, -6.90, 0.0870 },
1124 { -0.0050, -7.00, 0.0885 },
1125 { -0.0050, -7.20, 0.0900 }, /* 50 */
1126 { -0.0050, -7.20, 0.0915 },
1127 { -0.0055, -7.40, 0.0930 },
1128 { -0.0055, -7.50, 0.0945 },
1129 { -0.0060, -7.60, 0.0960 },
1130 { -0.0060, -7.70, 0.0970 },
1131 { -0.0065, -7.80, 0.0985 },
1132 { -0.0060, -7.70, 0.0995 },
1133 { -0.0060, -7.60, 0.1010 },
1134 { -0.0060, -7.50, 0.1020 },
1135 { -0.0055, -7.30, 0.1030 }, /* 60 */
1136 { -0.0050, -7.10, 0.1040 },
1137 { -0.0050, -6.90, 0.1050 },
1138 { -0.0045, -6.80, 0.1065 },
1139 { -0.0045, -6.50, 0.1075 },
1140 { -0.0040, -6.40, 0.1085 },
1141 { -0.0040, -6.20, 0.1095 },
1142 { -0.0040, -6.00, 0.1105 },
1143 { -0.0035, -5.80, 0.1115 },
1144 { -0.0030, -5.50, 0.1125 },
1145 { -0.0030, -5.40, 0.1135 }, /* 70 */
1146 { -0.0030, -5.10, 0.1145 },
1147 { -0.0030, -4.90, 0.1150 },
1148 { -0.0025, -4.70, 0.1160 },
1149 { -0.0025, -4.40, 0.1165 },
1150 { -0.0025, -4.20, 0.1175 },
1151 { -0.0020, -3.90, 0.1180 },
1152 { -0.0020, -3.70, 0.1185 },
1153 { -0.0020, -3.40, 0.1190 },
1154 { -0.0020, -3.10, 0.1195 },
1155 { -0.0020, -2.90, 0.1200 }, /* 80 */
1156 { -0.0015, -2.60, 0.1200 },
1157 { -0.0015, -2.30, 0.1205 },
1158 { -0.0015, -2.00, 0.1210 },
1159 { -0.0015, -1.80, 0.1215 },
1160 { -0.0015, -1.50, 0.1215 },
1161 { -0.0015, -1.20, 0.1215 },
1162 { -0.0015, -0.90, 0.1215 },
1163 { -0.0015, -0.60, 0.1215 },
1164 { -0.0015, -0.30, 0.1215 },
1165 { -0.0010, 0.00, 0.1215 }, /* 90 */
1166 { -0.0010, 0.30, 0.1215 },
1167 { -0.0010, 0.60, 0.1215 },
1168 { -0.0010, 0.90, 0.1215 },
1169 { -0.0010, 1.20, 0.1215 },
1170 { -0.0015, 1.40, 0.1215 },
1171 { -0.0015, 1.70, 0.1215 },
1172 { -0.0015, 2.00, 0.1215 },
1173 { -0.0015, 2.30, 0.1215 },
1174 { -0.0015, 2.60, 0.1215 },
1175 { -0.0015, 2.80, 0.1220 }, /* 100 */
1176 { -0.0020, 3.10, 0.1225 },
1177 { -0.0020, 3.30, 0.1230 },
1178 { -0.0020, 3.60, 0.1235 },
1179 { -0.0020, 3.90, 0.1240 },
1180 { -0.0025, 4.10, 0.1245 },
1181 { -0.0025, 4.40, 0.1250 },
1182 { -0.0025, 4.60, 0.1260 },
1183 { -0.0025, 4.90, 0.1265 },
1184 { -0.0030, 5.10, 0.1275 },
1185 { -0.0030, 5.30, 0.1285 }, /* 110 */
1186 { -0.0035, 5.60, 0.1290 },
1187 { -0.0035, 5.80, 0.1300 },
1188 { -0.0035, 6.00, 0.1310 },
1189 { -0.0040, 6.20, 0.1325 },
1190 { -0.0040, 6.40, 0.1335 },
1191 { -0.0045, 6.60, 0.1345 },
1192 { -0.0045, 6.70, 0.1355 },
1193 { -0.0050, 6.90, 0.1365 },
1194 { -0.0050, 7.10, 0.1375 },
1195 { -0.0055, 7.30, 0.1390 }, /* 120 */
1196 { -0.0055, 7.40, 0.1400 },
1197 { -0.0060, 7.50, 0.1415 },
1198 { -0.0065, 8.00, 0.1425 },
1199 { -0.0065, 7.80, 0.1440 },
1200 { -0.0060, 7.80, 0.1455 },
1201 { -0.0060, 7.60, 0.1470 },
1202 { -0.0055, 7.50, 0.1485 },
1203 { -0.0055, 7.40, 0.1500 },
1204 { -0.0050, 7.30, 0.1515 },
1205 { -0.0050, 7.20, 0.1530 }, /* 130 */
1206 { -0.0050, 7.00, 0.1545 },
1207 { -0.0045, 6.90, 0.1560 },
1208 { -0.0045, 6.80, 0.1575 },
1209 { -0.0040, 6.70, 0.1590 },
1210 { -0.0040, 6.50, 0.1605 },
1211 { -0.0040, 6.40, 0.1625 },
1212 { -0.0035, 6.30, 0.1640 },
1213 { -0.0035, 6.10, 0.1655 },
1214 { -0.0030, 6.10, 0.1670 },
1215 { -0.0030, 5.90, 0.1690 }, /* 140 */
1216 { -0.0030, 5.70, 0.1705 },
1217 { -0.0025, 5.70, 0.1720 },
1218 { -0.0025, 5.50, 0.1740 },
1219 { -0.0025, 5.40, 0.1755 },
1220 { -0.0025, 5.20, 0.1775 },
1221 { -0.0020, 5.10, 0.1790 },
1222 { -0.0020, 5.00, 0.1810 },
1223 { -0.0020, 4.80, 0.1825 },
1224 { -0.0015, 4.70, 0.1840 },
1225 { -0.0015, 4.60, 0.1860 }, /* 150 */
1226 { -0.0015, 4.40, 0.1880 },
1227 { -0.0015, 4.20, 0.1900 },
1228 { -0.0015, 4.10, 0.1915 },
1229 { -0.0010, 4.00, 0.1935 },
1230 { -0.0010, 3.80, 0.1955 },
1231 { -0.0010, 3.70, 0.1970 },
1232 { -0.0010, 3.50, 0.1990 },
1233 { -0.0005, 3.40, 0.2010 },
1234 { -0.0010, 3.20, 0.2025 },
1235 { -0.0005, 3.10, 0.2045 }, /* 160 */
1236 { -0.0005, 2.90, 0.2065 },
1237 { -0.0005, 2.80, 0.2085 },
1238 { -0.0005, 2.60, 0.2105 },
1239 { -0.0005, 2.50, 0.2120 },
1240 { -0.0005, 2.30, 0.2140 },
1241 { -0.0005, 2.20, 0.2160 },
1242 { -0.0005, 2.00, 0.2180 },
1243 { 0.0000, 1.90, 0.2200 },
1244 { 0.0000, 1.70, 0.2220 },
1245 { 0.0000, 1.60, 0.2235 }, /* 170 */
1246 { 0.0000, 1.40, 0.2255 },
1247 { 0.0000, 1.30, 0.2275 },
1248 { 0.0000, 1.10, 0.2295 },
1249 { 0.0000, 0.90, 0.2315 },
1250 { 0.0000, 0.80, 0.2335 },
1251 { 0.0000, 0.60, 0.2355 },
1252 { 0.0000, 0.50, 0.2375 },
1253 { 0.0000, 0.30, 0.2395 },
1254 { 0.0000, 0.10, 0.2415 },
1255 { 0.0000, 0.00, 0.2430 }, /* 180 */
1256 { 0.0000, -0.10, 0.2450 },
1257 { 0.0000, -0.30, 0.2470 },
1258 { 0.0000, -0.40, 0.2490 },
1259 { 0.0000, -0.60, 0.2510 },
1260 { 0.0000, -0.80, 0.2530 },
1261 { 0.0000, -0.90, 0.2550 },
1262 { 0.0000, -1.10, 0.2570 },
1263 { 0.0000, -1.20, 0.2590 },
1264 { 0.0000, -1.40, 0.2610 },
1265 { 0.0000, -1.50, 0.2625 }, /* 190 */
1266 { 0.0000, -1.70, 0.2645 },
1267 { 0.0000, -1.80, 0.2665 },
1268 { -0.0005, -2.00, 0.2685 },
1269 { -0.0005, -2.10, 0.2705 },
1270 { -0.0005, -2.30, 0.2725 },
1271 { -0.0005, -2.40, 0.2740 },
1272 { -0.0005, -2.60, 0.2760 },
1273 { -0.0005, -2.80, 0.2780 },
1274 { -0.0005, -2.90, 0.2800 },
1275 { -0.0005, -3.00, 0.2820 }, /* 200 */
1276 { -0.0010, -3.20, 0.2835 },
1277 { -0.0005, -3.30, 0.2855 },
1278 { -0.0010, -3.50, 0.2875 },
1279 { -0.0010, -3.70, 0.2895 },
1280 { -0.0010, -3.80, 0.2910 },
1281 { -0.0010, -3.90, 0.2930 },
1282 { -0.0010, -4.00, 0.2950 },
1283 { -0.0015, -4.20, 0.2965 },
1284 { -0.0015, -4.40, 0.2985 },
1285 { -0.0015, -4.50, 0.3000 }, /* 210 */
1286 { -0.0015, -4.60, 0.3020 },
1287 { -0.0020, -4.80, 0.3040 },
1288 { -0.0020, -5.00, 0.3055 },
1289 { -0.0020, -5.00, 0.3075 },
1290 { -0.0025, -5.20, 0.3090 },
1291 { -0.0025, -5.30, 0.3110 },
1292 { -0.0025, -5.50, 0.3125 },
1293 { -0.0025, -5.60, 0.3140 },
1294 { -0.0030, -5.70, 0.3160 },
1295 { -0.0030, -5.90, 0.3175 }, /* 220 */
1296 { -0.0030, -6.00, 0.3190 },
1297 { -0.0035, -6.10, 0.3210 },
1298 { -0.0035, -6.30, 0.3225 },
1299 { -0.0040, -6.40, 0.3240 },
1300 { -0.0040, -6.50, 0.3255 },
1301 { -0.0040, -6.60, 0.3270 },
1302 { -0.0045, -6.80, 0.3290 },
1303 { -0.0045, -6.90, 0.3305 },
1304 { -0.0050, -7.00, 0.3320 },
1305 { -0.0050, -7.20, 0.3335 }, /* 230 */
1306 { -0.0050, -7.20, 0.3350 },
1307 { -0.0055, -7.40, 0.3365 },
1308 { -0.0055, -7.50, 0.3380 },
1309 { -0.0060, -7.60, 0.3390 },
1310 { -0.0060, -7.70, 0.3405 },
1311 { -0.0065, -7.80, 0.3420 },
1312 { -0.0060, -7.60, 0.3425 },
1313 { -0.0060, -7.50, 0.3440 },
1314 { -0.0055, -7.40, 0.3455 },
1315 { -0.0055, -7.20, 0.3470 }, /* 240 */
1316 { -0.0050, -7.10, 0.3480 },
1317 { -0.0050, -6.90, 0.3490 },
1318 { -0.0045, -6.80, 0.3500 },
1319 { -0.0045, -6.50, 0.3510 },
1320 { -0.0040, -6.40, 0.3520 },
1321 { -0.0040, -6.10, 0.3535 },
1322 { -0.0035, -6.00, 0.3545 },
1323 { -0.0035, -5.80, 0.3550 },
1324 { -0.0030, -5.50, 0.3560 },
1325 { -0.0030, -5.30, 0.3570 }, /* 250 */
1326 { -0.0030, -5.10, 0.3580 },
1327 { -0.0030, -4.90, 0.3585 },
1328 { -0.0025, -4.70, 0.3595 },
1329 { -0.0025, -4.40, 0.3600 },
1330 { -0.0020, -4.10, 0.3610 },
1331 { -0.0020, -3.90, 0.3615 },
1332 { -0.0020, -3.70, 0.3620 },
1333 { -0.0020, -3.30, 0.3625 },
1334 { -0.0020, -3.10, 0.3630 },
1335 { -0.0015, -2.80, 0.3635 }, /* 260 */
1336 { -0.0015, -2.60, 0.3640 },
1337 { -0.0015, -2.40, 0.3645 },
1338 { -0.0015, -2.00, 0.3645 },
1339 { -0.0015, -1.80, 0.3650 },
1340 { -0.0015, -1.40, 0.3650 },
1341 { -0.0015, -1.20, 0.3655 },
1342 { -0.0010, -0.90, 0.3655 },
1343 { -0.0010, -0.60, 0.3655 },
1344 { -0.0010, -0.30, 0.3655 },
1345 { -0.0010, 0.00, 0.3655 }, /* 270 */
1346 { -0.0010, 0.30, 0.3655 },
1347 { -0.0010, 0.60, 0.3655 },
1348 { -0.0010, 0.90, 0.3655 },
1349 { -0.0015, 1.10, 0.3655 },
1350 { -0.0015, 1.40, 0.3655 },
1351 { -0.0015, 1.70, 0.3655 },
1352 { -0.0015, 1.90, 0.3660 },
1353 { -0.0015, 2.20, 0.3660 },
1354 { -0.0015, 2.50, 0.3665 },
1355 { -0.0015, 2.80, 0.3670 }, /* 280 */
1356 { -0.0015, 3.10, 0.3675 },
1357 { -0.0020, 3.40, 0.3680 },
1358 { -0.0020, 3.70, 0.3685 },
1359 { -0.0020, 3.90, 0.3690 },
1360 { -0.0025, 4.10, 0.3695 },
1361 { -0.0025, 4.40, 0.3700 },
1362 { -0.0025, 4.60, 0.3710 },
1363 { -0.0025, 4.80, 0.3715 },
1364 { -0.0025, 5.00, 0.3730 },
1365 { -0.0030, 5.40, 0.3735 }, /* 290 */
1366 { -0.0035, 5.60, 0.3745 },
1367 { -0.0035, 5.80, 0.3755 },
1368 { -0.0035, 6.00, 0.3765 },
1369 { -0.0040, 6.20, 0.3775 },
1370 { -0.0045, 6.50, 0.3785 },
1371 { -0.0045, 6.60, 0.3795 },
1372 { -0.0045, 6.80, 0.3805 },
1373 { -0.0050, 7.00, 0.3815 },
1374 { -0.0050, 7.10, 0.3825 },
1375 { -0.0055, 7.20, 0.3840 }, /* 300 */
1376 { -0.0055, 7.40, 0.3850 },
1377 { -0.0060, 7.50, 0.3865 },
1378 { -0.0060, 7.70, 0.3875 },
1379 { -0.0065, 7.80, 0.3890 },
1380 { -0.0060, 7.80, 0.3900 },
1381 { -0.0060, 7.60, 0.3915 },
1382 { -0.0055, 7.60, 0.3930 },
1383 { -0.0055, 7.40, 0.3945 },
1384 { -0.0050, 7.30, 0.3960 },
1385 { -0.0050, 7.20, 0.3975 }, /* 310 */
1386 { -0.0050, 7.00, 0.3990 },
1387 { -0.0045, 6.90, 0.4005 },
1388 { -0.0045, 6.80, 0.4020 },
1389 { -0.0040, 6.70, 0.4035 },
1390 { -0.0040, 6.60, 0.4050 },
1391 { -0.0040, 6.40, 0.4065 },
1392 { -0.0035, 6.30, 0.4085 },
1393 { -0.0035, 6.20, 0.4100 },
1394 { -0.0030, 6.10, 0.4115 },
1395 { -0.0030, 5.90, 0.4130 }, /* 320 */
1396 { -0.0030, 5.80, 0.4150 },
1397 { -0.0025, 5.70, 0.4165 },
1398 { -0.0025, 5.50, 0.4180 },
1399 { -0.0025, 5.40, 0.4200 },
1400 { -0.0025, 5.20, 0.4215 },
1401 { -0.0020, 5.10, 0.4235 },
1402 { -0.0020, 5.00, 0.4250 },
1403 { -0.0020, 4.80, 0.4270 },
1404 { -0.0015, 4.70, 0.4285 },
1405 { -0.0015, 4.60, 0.4305 }, /* 330 */
1406 { -0.0015, 4.40, 0.4325 },
1407 { -0.0015, 4.20, 0.4340 },
1408 { -0.0015, 4.10, 0.4360 },
1409 { -0.0010, 4.00, 0.4375 },
1410 { -0.0010, 3.80, 0.4395 },
1411 { -0.0010, 3.70, 0.4415 },
1412 { -0.0010, 3.50, 0.4435 },
1413 { -0.0005, 3.40, 0.4450 },
1414 { -0.0010, 3.20, 0.4470 },
1415 { -0.0005, 3.10, 0.4490 }, /* 340 */
1416 { -0.0005, 2.90, 0.4510 },
1417 { -0.0005, 2.80, 0.4525 },
1418 { -0.0005, 2.60, 0.4545 },
1419 { -0.0005, 2.40, 0.4565 },
1420 { -0.0005, 2.30, 0.4585 },
1421 { -0.0005, 2.20, 0.4605 },
1422 { -0.0005, 2.00, 0.4620 },
1423 { 0.0000, 1.90, 0.4640 },
1424 { 0.0000, 1.70, 0.4660 },
1425 { 0.0000, 1.60, 0.4680 }, /* 350 */
1426 { 0.0000, 1.40, 0.4700 },
1427 { 0.0000, 1.20, 0.4720 },
1428 { 0.0000, 1.10, 0.4740 },
1429 { 0.0000, 0.90, 0.4760 },
1430 { 0.0000, 0.80, 0.4780 },
1431 { 0.0000, 0.60, 0.4795 },
1432 { 0.0000, 0.50, 0.4815 },
1433 { 0.0000, 0.30, 0.4835 },
1434 { 0.0000, 0.20, 0.4855 }, /* 359 */
1438 /* Turn the crank by 1 degree, which moves the legs and displaces the robot.
1441 tick_walker (ModeInfo *mi, walker *f)
1443 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1447 if (bp->button_down_p) return;
1450 deg = ((int) (f->crank_rot + 0.5)) % 360;
1455 f->crank_rot = bp->debug_x;
1456 f->pitch = wobble_profile[deg].rot;
1457 f->z = wobble_profile[deg].up;
1464 fwd = wobble_profile[deg].fwd;
1465 f->pitch = wobble_profile[deg].rot;
1466 f->z = wobble_profile[deg].up;
1470 fwd = (wobble_profile[deg].fwd - wobble_profile[deg-1].fwd);
1471 f->pitch += (wobble_profile[deg].rot - wobble_profile[deg-1].rot);
1472 f->z += (wobble_profile[deg].up - wobble_profile[deg-1].up);
1475 /* Lean slightly toward the foot that is raised off the ground. */
1476 f->roll = -2.5 * sin ((deg - 90) * M_PI / 180);
1478 if (!(random() % 10))
1480 GLfloat b = f->balance / 10.0;
1481 int s = (f->balance > 0 ? 1 : -1);
1483 f->facing += s * frand (b);
1487 if (debug_p) fwd = 0;
1493 th = f->facing * M_PI / 180.0;
1494 f->x += fwd * cos (th);
1495 f->y += fwd * sin (th);
1497 /* If moving this robot would collide with another, undo the move,
1498 recoil, and randomly turn.
1500 if (collision_p (mi, f, 0))
1503 f->x = ox + fwd * cos (th);
1504 f->y = oy + fwd * sin (th);
1505 f->facing += frand(10) - 5;
1506 if (! random() % 30)
1507 f->facing += frand(90) - 45;
1513 opacity > 0.5) /* Don't bother fading if it's already transparent. */
1515 GLfloat tick = 0.002;
1518 /* If we're not fading, maybe start fading out. */
1519 if (f->fading_p == 0 && ! (random() % 40000))
1523 if (debug_p) f->fading_p = 0;
1526 if (f->fading_p < 0)
1528 f->body_transparency -= tick;
1529 if (f->body_transparency <= -linger)
1531 f->body_transparency = -linger;
1535 else if (f->fading_p > 0)
1537 f->body_transparency += tick;
1538 if (f->body_transparency >= opacity)
1540 f->body_transparency = opacity;
1549 init_walker (ModeInfo *mi, walker *f)
1551 int i, start_tick = random() % 360;
1554 f->pitch = wobble_profile[0].rot;
1555 f->z = wobble_profile[0].up;
1557 f->body_transparency = opacity;
1559 f->hand_rot[0] = frand(180);
1560 f->hand_pos[0] = 0.6 + frand(0.4);
1561 f->hand_rot[1] = 180 - f->hand_rot[0];
1562 f->hand_pos[1] = f->hand_pos[0];
1564 if (! (random() % 30)) f->hand_rot[1] += frand(10);
1565 if (! (random() % 30)) f->hand_pos[1] = 0.6 + frand(0.4);
1567 f->facing = frand(360);
1568 f->balance = frand(10) - 5;
1570 if (MI_COUNT(mi) == 1)
1573 f->speed = 0.6 + frand(0.8);
1584 for (i = 0; i < start_tick; i++)
1585 tick_walker (mi, f);
1587 /* Place them randomly, but non-overlapping. */
1588 for (i = 0; i < 1000; i++)
1591 if (MI_COUNT(mi) > 10) range += MI_COUNT(mi) / 10.0;
1592 f->x = frand(range) - range/2;
1593 f->y = frand(range) - range/2;
1594 if (! collision_p (mi, f, 1.5))
1599 if (debug_p) f->x = f->y = 0;
1605 /* Draw a robot standing in the right place, 1 unit tall.
1608 draw_walker (ModeInfo *mi, walker *f, const char *tag)
1610 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1611 int wire = MI_IS_WIREFRAME(mi);
1615 glTranslatef (f->y, f->z, f->x);
1622 glRotatef (90, 0, 1, 0);
1623 glRotatef (f->facing, 0, 1, 0);
1624 glRotatef (f->pitch, 0, 0, 1);
1625 glRotatef (f->roll, 1, 0, 0);
1628 GLfloat n = 0.00484; /* make it 1 unit tall */
1632 count += draw_gearbox (mi);
1633 count += draw_crank (mi, f, f->crank_rot);
1634 count += draw_rotator (mi, f, f->crank_rot);
1635 count += draw_leg (mi, f->crank_rot, False);
1636 count += draw_leg (mi, f->crank_rot, True);
1637 count += draw_wireframe (mi, f);
1639 /* Draw these last, and outer shell first, to make transparency work.
1640 The order in which things hit the depth buffer matters.
1642 if (f->body_transparency >= 0.001)
1644 count += draw_arm (mi, f, True, f->hand_rot[0], f->hand_pos[0]);
1645 count += draw_arm (mi, f, False, f->hand_rot[1], f->hand_pos[1]);
1646 count += draw_body (mi, f, False);
1647 count += draw_body (mi, f, True);
1648 count += draw_dome (mi, f);
1651 if (tag) /* For debugging depth sorting: label each robot */
1654 if (! wire) glDisable (GL_DEPTH_TEST);
1655 glColor3f (1, 1, 1);
1658 /* Billboard rotation */
1659 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]);
1660 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
1661 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
1662 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
1664 glMultMatrixf (&m[0][0]);
1665 glScalef (0.04, 0.04, 0.04);
1667 print_texture_string (bp->font_data, tag);
1669 if (! wire) glEnable (GL_DEPTH_TEST);
1678 draw_ground (ModeInfo *mi, GLfloat color[4])
1680 int wire = MI_IS_WIREFRAME(mi);
1682 GLfloat cell_size = 0.9;
1683 int cells = 1000 * size;
1687 if (debug_p) return 0;
1692 glRotatef (frand(90), 0, 0, 1);
1696 GLfloat fog_color[4] = { 0, 0, 0, 1 };
1699 glEnable (GL_LINE_SMOOTH);
1700 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
1701 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1702 glEnable (GL_BLEND);
1704 glFogi (GL_FOG_MODE, GL_EXP2);
1705 glFogfv (GL_FOG_COLOR, fog_color);
1706 glFogf (GL_FOG_DENSITY, 0.017);
1707 glFogf (GL_FOG_START, -cells/2 * cell_size);
1708 # ifndef USE_IPHONE /* #### Not working on iOS for some reason */
1714 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
1717 for (i = -cells/2; i < cells/2; i++)
1719 GLfloat a = i * cell_size;
1720 GLfloat b = cells/2 * cell_size;
1721 glVertex3f (a, -b, 0); glVertex3f (a, b, 0); points++;
1722 glVertex3f (-b, a, 0); glVertex3f (b, a, 0); points++;
1728 glDisable (GL_LINE_SMOOTH);
1729 glDisable (GL_BLEND);
1739 /* If the target robot (robot #0) has moved too far from the point at which
1740 the camera is aimed, then initiate an animation to move the observer to
1743 Because of the jerky forward motion of the robots, just always focusing
1744 on the center of the robot looks terrible, so instead we let them walk
1745 a little out of the center of the frame, and then catch up.
1748 look_at_center (ModeInfo *mi)
1750 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1751 GLfloat target_x = bp->walkers[0].x;
1752 GLfloat target_y = bp->walkers[0].y;
1753 GLfloat target_z = 0.8; /* Look a little bit above his head */
1754 GLfloat max_dist = 2.5 / size;
1757 if (debug_p) return;
1760 if (max_dist < 1) max_dist = 1;
1761 if (max_dist > 10) max_dist = 10;
1763 if (bp->camera_tracking_p)
1765 GLfloat r = (1 - cos (bp->tracking_ratio * M_PI)) / 2;
1766 bp->looking_x = bp->olooking_x + r * (target_x - bp->olooking_x);
1767 bp->looking_y = bp->olooking_y + r * (target_y - bp->olooking_y);
1768 bp->looking_z = bp->olooking_z + r * (target_z - bp->olooking_z);
1770 bp->tracking_ratio += 0.02;
1771 if (bp->tracking_ratio >= 1)
1773 bp->camera_tracking_p = False;
1774 bp->olooking_x = bp->looking_x;
1775 bp->olooking_y = bp->looking_y;
1776 bp->olooking_z = bp->looking_z;
1780 if (! bp->camera_tracking_p)
1783 sqrt ((target_x - bp->looking_x) * (target_x - bp->looking_x) +
1784 (target_y - bp->looking_y) * (target_y - bp->looking_y) +
1785 (target_z - bp->looking_z) * (target_z - bp->looking_z));
1787 if (dist > max_dist)
1789 bp->camera_tracking_p = True;
1790 bp->tracking_ratio = 0;
1791 bp->olooking_x = bp->looking_x;
1792 bp->olooking_y = bp->looking_y;
1793 bp->olooking_z = bp->looking_z;
1797 glTranslatef (-bp->looking_y, -bp->looking_z, -bp->looking_x);
1804 glTranslatef (target_y, target_z, target_x);
1806 glVertex3f(0, -target_z, 0);
1807 glVertex3f(0, 1, 0);
1808 glVertex3f(-0.1, 0, -0.1);
1809 glVertex3f( 0.1, 0, 0.1);
1810 glVertex3f(-0.1, 0, 0.1);
1811 glVertex3f( 0.1, 0, -0.1);
1817 glTranslatef (bp->looking_y, bp->looking_z, bp->looking_x);
1818 glRotatef (30, 0, 1, 0);
1820 glVertex3f(0, -bp->looking_z, 0);
1821 glVertex3f(0, 1, 0);
1822 glVertex3f(-0.1, 0, -0.1);
1823 glVertex3f( 0.1, 0, 0.1);
1824 glVertex3f(-0.1, 0, 0.1);
1825 glVertex3f( 0.1, 0, -0.1);
1831 glTranslatef (bp->olooking_y, bp->olooking_z, bp->olooking_x);
1832 glRotatef (60, 0, 1, 0);
1834 glVertex3f(0, -bp->olooking_z, 0);
1835 glVertex3f(0, 1, 0);
1836 glVertex3f(-0.1, 0, -0.1);
1837 glVertex3f( 0.1, 0, 0.1);
1838 glVertex3f(-0.1, 0, 0.1);
1839 glVertex3f( 0.1, 0, -0.1);
1842 glTranslatef (0, -bp->olooking_z, 0);
1843 glBegin (GL_LINE_LOOP);
1844 for (th = 0; th < M_PI * 2; th += 0.1)
1845 glVertex3f (bp->olooking_y + max_dist * cos(th), 0,
1846 bp->olooking_x + max_dist * sin(th));
1856 /* Draw a cartoony word bubble.
1857 W and H are the inside size, for text.
1858 Origin is at bottom left.
1859 The bubble frame and arrow are outside that.
1862 draw_bubble_box (ModeInfo *mi,
1863 GLfloat width, GLfloat height,
1864 GLfloat corner_radius,
1865 GLfloat arrow_h, GLfloat arrow_x,
1866 GLfloat fg[4], GLfloat bg[4])
1869 # define CORNER_POINTS 16
1870 GLfloat outline_points[ (CORNER_POINTS + 2) * 4 + 8 ][3];
1873 GLfloat tick = M_PI / 2 / CORNER_POINTS;
1875 GLfloat arrow_w = arrow_h / 2;
1876 GLfloat arrow_x2 = MAX(0, MIN(width - arrow_w, arrow_x));
1878 GLfloat w2 = MAX(arrow_w, width - corner_radius * 1.10);
1879 GLfloat h2 = MAX(0, height - corner_radius * 1.28);
1880 GLfloat x2 = (width - w2) / 2;
1881 GLfloat y2 = (height - h2) / 2;
1883 GLfloat xa = x2 -corner_radius; /* E _------------_ */
1884 GLfloat xb = x2; /* D /__| |__\ */
1885 GLfloat xc = xb + w2; /* | | | | */
1886 GLfloat xd = xc + corner_radius; /* C |__| EF |__| */
1887 GLfloat xe = xb + arrow_x2; /* B \_|_________|_/ */
1888 GLfloat xf = xe + arrow_w; /* A \| */
1890 GLfloat ya = y2 - (corner_radius + arrow_h);
1891 GLfloat yb = y2 - corner_radius;
1893 GLfloat yd = yc + h2;
1894 GLfloat ye = yd + corner_radius;
1898 /* Let the lines take precedence over the fills. */
1899 glEnable (GL_POLYGON_OFFSET_FILL);
1900 glPolygonOffset (1.0, 1.0);
1905 /* top left corner */
1907 glBegin (GL_TRIANGLE_FAN);
1908 glVertex3f (xb, yd, 0);
1909 for (th = 0; th < M_PI/2 + tick; th += tick)
1911 GLfloat x = xb - corner_radius * cos(th);
1912 GLfloat y = yd + corner_radius * sin(th);
1913 glVertex3f (x, y, z);
1914 outline_points[i][0] = x;
1915 outline_points[i][1] = y;
1916 outline_points[i][2] = z;
1922 outline_points[i][0] = xc;
1923 outline_points[i][1] = ye;
1924 outline_points[i][2] = z;
1927 /* top right corner */
1929 glBegin (GL_TRIANGLE_FAN);
1930 glVertex3f (xc, yd, 0);
1931 for (th = M_PI/2; th > -tick; th -= tick)
1933 GLfloat x = xc + corner_radius * cos(th);
1934 GLfloat y = yd + corner_radius * sin(th);
1935 glVertex3f (x, y, z);
1936 outline_points[i][0] = x;
1937 outline_points[i][1] = y;
1938 outline_points[i][2] = z;
1944 outline_points[i][0] = xd;
1945 outline_points[i][1] = yc;
1946 outline_points[i][2] = z;
1949 /* bottom right corner */
1951 glBegin (GL_TRIANGLE_FAN);
1952 glVertex3f (xc, yc, 0);
1953 for (th = 0; th < M_PI/2 + tick; th += tick)
1955 GLfloat x = xc + corner_radius * cos(th);
1956 GLfloat y = yc - corner_radius * sin(th);
1957 glVertex3f (x, y, z);
1958 outline_points[i][0] = x;
1959 outline_points[i][1] = y;
1960 outline_points[i][2] = z;
1965 /* bottom right edge */
1966 outline_points[i][0] = xf;
1967 outline_points[i][1] = yb;
1968 outline_points[i][2] = z;
1971 /* arrow triangle */
1973 glBegin (GL_TRIANGLES);
1975 /* bottom arrow point */
1976 outline_points[i][0] = xf;
1977 outline_points[i][1] = yb;
1978 outline_points[i][2] = z;
1979 glVertex3f (outline_points[i][0],
1980 outline_points[i][1],
1981 outline_points[i][2]);
1984 /* bottom right edge */
1985 outline_points[i][0] = xf;
1986 outline_points[i][1] = ya;
1987 outline_points[i][2] = z;
1988 glVertex3f (outline_points[i][0],
1989 outline_points[i][1],
1990 outline_points[i][2]);
1993 outline_points[i][0] = xe;
1994 outline_points[i][1] = yb;
1995 outline_points[i][2] = z;
1996 glVertex3f (outline_points[i][0],
1997 outline_points[i][1],
1998 outline_points[i][2]);
2003 /* bottom left corner */
2005 glBegin (GL_TRIANGLE_FAN);
2006 glVertex3f (xb, yc, 0);
2007 for (th = M_PI/2; th > -tick; th -= tick)
2009 GLfloat x = xb - corner_radius * cos(th);
2010 GLfloat y = yc - corner_radius * sin(th);
2011 glVertex3f (x, y, z);
2012 outline_points[i][0] = x;
2013 outline_points[i][1] = y;
2014 outline_points[i][2] = z;
2019 glFrontFace(GL_CCW);
2022 outline_points[i][0] = xa;
2023 outline_points[i][1] = yd;
2024 outline_points[i][2] = z;
2030 glVertex3f (xa, yd, z);
2031 glVertex3f (xb, yd, z);
2032 glVertex3f (xb, yc, z);
2033 glVertex3f (xa, yc, z);
2036 glVertex3f (xb, ye, z);
2037 glVertex3f (xc, ye, z);
2038 glVertex3f (xc, yb, z);
2039 glVertex3f (xb, yb, z);
2042 glVertex3f (xc, yd, z);
2043 glVertex3f (xd, yd, z);
2044 glVertex3f (xd, yc, z);
2045 glVertex3f (xc, yc, z);
2052 glBegin (GL_LINE_LOOP);
2054 glVertex3fv (outline_points[--i]);
2057 glDisable (GL_POLYGON_OFFSET_FILL);
2062 draw_label (ModeInfo *mi, walker *f, GLfloat y_off, GLfloat scale,
2065 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2066 int wire = MI_IS_WIREFRAME(mi);
2069 if (scale == 0) return;
2072 glDisable (GL_LIGHTING); /* don't light fonts */
2076 /* First, we translate the origin to the center of the robot.
2078 Then we retrieve the prevailing modelview matrix, which
2079 includes any rotation, wandering, and user-trackball-rolling
2082 We set the top 3x3 cells of that matrix to be the identity
2083 matrix. This removes all rotation from the matrix, while
2084 leaving the translation alone. This has the effect of
2085 leaving the prevailing coordinate system perpendicular to
2086 the camera view: were we to draw a square face, it would
2087 be in the plane of the screen.
2089 Now we translate by `size' toward the viewer -- so that the
2090 origin is *just in front* of the ball.
2092 Then we draw the label text, allowing the depth buffer to
2093 do its work: that way, labels on atoms will be occluded
2094 properly when other atoms move in front of them.
2096 This technique (of neutralizing rotation relative to the
2097 observer, after both rotations and translations have been
2098 applied) is known as "billboarding".
2102 glTranslatef(f->y, 0, f->x); /* get matrix */
2104 glTranslatef (0, y_off, 0);
2106 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]); /* load rot. identity */
2107 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
2108 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
2109 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
2110 glLoadIdentity(); /* reset modelview */
2111 glMultMatrixf (&m[0][0]); /* replace with ours */
2113 glTranslatef (0, 0, 0.1); /* move toward camera */
2115 glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
2120 GLfloat max = 24; /* max point size to avoid pixellated text */
2122 /* Let the font be much larger on iPhone */
2123 if (mi->xgwa.height <= 640 || mi->xgwa.width <= 640)
2126 cw = texture_string_width (bp->font_data, "X", &ch); /* line height */
2128 if (ch > max) s *= max/ch;
2132 w = texture_string_width (bp->font_data, label, &h);
2135 glTranslatef (-w/2, h*2/3 + (cw * 7), 0);
2138 glTranslatef (0, -h + (ch * 1.2), -0.1);
2139 draw_bubble_box (mi, w, h,
2140 ch * 2, /* corner radius */
2141 ch * 2.5, /* arrow height */
2142 w / 2 - cw * 8, /* arrow x */
2143 bp->text_bd, bp->text_bg);
2146 glColor4fv (bp->text_color);
2147 print_texture_string (bp->font_data, label);
2152 /* More efficient to always call glEnable() with correct values
2153 than to call glPushAttrib()/glPopAttrib(), since reading
2154 attributes from GL does a round-trip and stalls the pipeline.
2157 glEnable (GL_LIGHTING);
2162 fill_words (ModeInfo *mi)
2164 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2165 char *p = bp->words + strlen(bp->words);
2168 int max = bp->max_lines;
2170 /* Fewer lines on iPhone */
2171 if ((mi->xgwa.height <= 640 || mi->xgwa.width <= 640) &&
2175 for (c = bp->words; c < p; c++)
2179 while (p < bp->words + sizeof(bp->words) - 1 &&
2182 int c = textclient_getc (bp->tc);
2197 bubble (ModeInfo *mi)
2199 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2201 GLfloat fade = 0.015;
2202 int chance = (talk_chance <= 0.0 ? 0 :
2203 talk_chance >= 0.99 ? 1 :
2204 (1-talk_chance) * 1000);
2206 char *s0 = strdup (bp->words);
2210 while (*s == '\n') s++;
2212 while (L > 0 && (s[L-1] == '\n' || s[L-1] == ' ' || s[L-1] == '\t'))
2214 if (! *s) goto DONE;
2217 if (debug_p) goto DONE;
2220 if (chance <= 0) goto DONE;
2222 if (bp->bubble_tick > 0)
2225 if (! bp->bubble_tick)
2229 if (! bp->bubble_tick)
2231 if (!(random() % chance))
2232 bp->bubble_tick = duration;
2237 scale = (bp->bubble_tick < duration * fade
2238 ? bp->bubble_tick / (duration * fade)
2239 : (bp->bubble_tick > duration * (1 - fade)
2240 ? 1 - ((bp->bubble_tick - duration * (1 - fade))
2241 / (duration * fade))
2244 draw_label (mi, &bp->walkers[0], 1.5, scale, s);
2249 #endif /* WORDBUBBLES */
2259 cmp_depth_sorter (const void *aa, const void *bb)
2261 const depth_sorter *a = (depth_sorter *) aa;
2262 const depth_sorter *b = (depth_sorter *) bb;
2263 return (a->d == b->d ? 0 : a->d < b->d ? -1 : 1);
2268 draw_robot (ModeInfo *mi)
2270 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2271 Display *dpy = MI_DISPLAY(mi);
2272 Window window = MI_WINDOW(mi);
2274 depth_sorter *sorted;
2277 if (!bp->glx_context)
2280 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
2282 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2285 glRotatef(current_device_rotation(), 0, 0, 1);
2286 gltrackball_rotate (bp->user_trackball);
2288 robot_size = size * 7;
2289 glScalef (robot_size, robot_size, robot_size);
2290 look_at_center (mi);
2293 glScalef (1/robot_size, 1/robot_size, 1/robot_size);
2294 glCallList (bp->dlists[GROUND]);
2305 glTranslatef (0, -1.2, 0);
2307 glRotatef (-43.5, 0, 0, 1);
2308 glRotatef (-90, 0, 1, 0);
2310 /* glTranslatef (bp->debug_z, bp->debug_y, 0); */
2313 if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
2314 if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
2317 glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
2318 glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
2319 glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
2322 glTranslatef (-0.5, 0, -0.5);
2324 glColor3f (1, 0, 0);
2325 glBegin (GL_LINE_LOOP);
2326 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2327 glVertex3f (0, 1, 1); glVertex3f (0, 1, 0);
2330 glBegin (GL_LINE_LOOP);
2331 glVertex3f (1, 0, 0); glVertex3f (1, 0, 1);
2332 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2336 glColor3f (0.5, 0.5, 0.5);
2337 glFrontFace (GL_CCW);
2339 /* glVertex3f (0, 1, 0); glVertex3f (0, 1, 1); */
2340 /* glVertex3f (1, 1, 1); glVertex3f (1, 1, 0); */
2341 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2342 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2345 glFrontFace (GL_CW);
2347 glVertex3f (0, 1, 0); glVertex3f (0, 1, 1);
2348 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2349 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2350 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2354 glColor3f (1, 0, 0);
2356 glVertex3f (0, 0, 0); glVertex3f (1, 0, 0);
2357 glVertex3f (0, 0, 1); glVertex3f (1, 0, 1);
2358 glVertex3f (0, 1, 0); glVertex3f (1, 1, 0);
2359 glVertex3f (0, 1, 1); glVertex3f (1, 1, 1);
2361 glVertex3f (0, 0, 0); glVertex3f (1, 0, 1);
2362 glVertex3f (0, 0, 1); glVertex3f (1, 0, 0);
2363 glVertex3f (0, 1, 0); glVertex3f (1, 1, 1);
2364 glVertex3f (0, 1, 1); glVertex3f (1, 1, 0);
2367 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
2368 if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
2375 /* For transparency to work right, we have to draw the robots from
2376 back to front, from the perspective of the observer. So project
2377 the origin of the robot to screen coordinates, and sort that by Z.
2379 sorted = (depth_sorter *) calloc (bp->nwalkers, sizeof (*sorted));
2381 GLdouble m[16], p[16];
2383 glGetDoublev (GL_MODELVIEW_MATRIX, m);
2384 glGetDoublev (GL_PROJECTION_MATRIX, p);
2385 glGetIntegerv (GL_VIEWPORT, v);
2387 for (i = 0; i < bp->nwalkers; i++)
2390 walker *f = &bp->walkers[i];
2391 gluProject (f->y, f->z, f->x, m, p, v, &x, &y, &z);
2395 qsort (sorted, i, sizeof(*sorted), cmp_depth_sorter);
2399 mi->polygon_count = 0;
2400 for (i = 0; i < bp->nwalkers; i++)
2402 int ii = sorted[i].i;
2403 walker *f = &bp->walkers[ii];
2404 int ticks = 22 * speed * f->speed;
2409 if (ticks < 1) ticks = 1;
2410 if (ticks > max) ticks = max;
2414 sprintf (tag, "%.4f, %.4f, %.4f",
2415 bp->debug_x, bp->debug_y, bp->debug_z);
2419 /* sprintf (tag + strlen(tag), " %d\n", ii); */
2420 sprintf (tag + strlen(tag), " %d\n", bp->nwalkers - i - 1);
2421 /* sprintf (tag + strlen(tag), "%.03f\n", sorted[i].d); */
2427 mi->polygon_count += draw_walker (mi, f, tag);
2429 for (ii = 0; ii < ticks; ii++)
2430 tick_walker (mi, f);
2437 if (mi->fps_p) do_fps (mi);
2440 glXSwapBuffers(dpy, window);
2444 release_robot (ModeInfo *mi)
2447 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2448 textclient_close (bp->tc);
2452 XSCREENSAVER_MODULE_2 ("WindupRobot", winduprobot, robot)