1 /* winduprobot, Copyright (c) 2014-2018 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 * https://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
36 * We did eventually end up with robotic award statues, but we constructed
37 * them out of mass-produced wind-up robots, rather than 3D printing them:
38 * https://www.dnalounge.com/gallery/2014/09-14/045.html
39 * https://www.youtube.com/watch?v=EZF4ZAAy49g
42 #define LABEL_FONT "-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*"
44 #define DEFAULTS "*delay: 20000 \n" \
46 "*showFPS: False \n" \
47 "*wireframe: False \n" \
48 "*labelFont: " LABEL_FONT "\n" \
49 "*legColor: #AA2222" "\n" \
50 "*armColor: #AA2222" "\n" \
51 "*handColor: #AA2222" "\n" \
52 "*crankColor: #444444" "\n" \
53 "*bodyColor: #7777AA" "\n" \
54 "*domeColor: #7777AA" "\n" \
55 "*insideColor: #DDDDDD" "\n" \
56 "*gearboxColor: #444488" "\n" \
57 "*gearColor: #008877" "\n" \
58 "*wheelColor: #007788" "\n" \
59 "*wireColor: #006600" "\n" \
60 "*groundColor: #0000FF" "\n" \
61 "*textColor: #FFFFFF""\n" \
62 "*textBackground: #444444""\n" \
63 "*textBorderColor: #FFFF88""\n" \
65 "*program: xscreensaver-text\n" \
71 # define release_robot 0
73 #define countof(x) (sizeof((x))/sizeof((*x)))
75 #define DEF_SPEED "1.0"
76 #define DEF_ROBOT_SIZE "1.0"
77 #define DEF_TEXTURE "True"
78 #define DEF_FADE "True"
79 #define DEF_OPACITY "1.0"
80 #define DEF_TALK "0.2"
82 #include "xlockmore.h"
83 #include "gltrackball.h"
84 #include "ximage-loader.h"
89 # include "textclient.h"
95 #ifndef HAVE_JWZGLES /* No SPHERE_MAP on iPhone */
100 # include "images/gen/chromesphere_png.h"
103 #ifdef USE_GL /* whole file */
107 extern const struct gllist
108 *robot_arm_half, *robot_body_half_outside, *robot_body_half_inside,
109 *robot_crank_full, *robot_gearbox_half, *robot_hand_half,
110 *robot_leg_half, *robot_rotator_half, *robot_wireframe;
112 static struct gllist *robot_dome = 0, *robot_gear = 0, *ground = 0;
114 static const struct gllist * const *all_objs[] = {
115 &robot_arm_half, &robot_body_half_outside, &robot_body_half_inside,
116 &robot_crank_full, &robot_gearbox_half, &robot_hand_half,
117 &robot_leg_half, &robot_rotator_half, &robot_wireframe,
118 (const struct gllist * const *) &robot_dome,
119 (const struct gllist * const *) &robot_gear,
120 (const struct gllist * const *) &ground
124 #define ROBOT_BODY_1 1
125 #define ROBOT_BODY_2 2
126 #define ROBOT_CRANK 3
127 #define ROBOT_GEARBOX 4
130 #define ROBOT_ROTATOR 7
131 #define ROBOT_WIREFRAME 8
133 #define ROBOT_GEAR 10
137 GLfloat x, y, z; /* position */
138 GLfloat facing; /* direction of front of robot, degrees */
139 GLfloat pitch; /* front/back tilt angle, degrees */
140 GLfloat roll; /* left/right tilt angle, degrees */
141 GLfloat speed; /* some robots are faster */
142 GLfloat crank_rot; /* gear state, degrees */
143 GLfloat hand_rot[2]; /* rotation of the hands, degrees */
144 GLfloat hand_pos[2]; /* openness of the hands, ratio */
145 GLfloat balance; /* how off-true does it walk? degrees */
146 GLfloat body_transparency; /* ratio */
147 int fading_p; /* -1, 0, 1 */
152 GLXContext *glx_context;
153 trackball_state *user_trackball;
157 GLfloat component_colors[countof(all_objs)][4];
162 GLfloat looking_x, looking_y, looking_z; /* Where camera is aimed */
163 GLfloat olooking_x, olooking_y, olooking_z; /* Where camera was aimed */
164 Bool camera_tracking_p; /* Whether camera in motion */
165 GLfloat tracking_ratio;
168 GLuint chrome_texture;
172 texture_font_data *font_data;
176 int lines, max_lines;
178 GLfloat text_color[4], text_bg[4], text_bd[4];
180 # endif /* WORDBUBBLES */
184 GLfloat debug_x, debug_y, debug_z;
187 } robot_configuration;
189 static robot_configuration *bps = NULL;
191 static GLfloat speed, size, opacity;
192 static int do_texture, do_fade;
194 static GLfloat talk_chance;
200 static XrmOptionDescRec opts[] = {
201 { "-speed", ".speed", XrmoptionSepArg, 0 },
202 { "-size", ".robotSize", XrmoptionSepArg, 0 },
203 { "-opacity", ".opacity", XrmoptionSepArg, 0 },
204 { "-talk", ".talk", XrmoptionSepArg, 0 },
205 {"-texture", ".texture", XrmoptionNoArg, "True" },
206 {"+texture", ".texture", XrmoptionNoArg, "False" },
207 {"-fade", ".fade", XrmoptionNoArg, "True" },
208 {"+fade", ".fade", XrmoptionNoArg, "False" },
210 {"-debug", ".debug", XrmoptionNoArg, "True" },
211 {"+debug", ".debug", XrmoptionNoArg, "False" },
215 static argtype vars[] = {
216 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
217 {&size, "robotSize", "RobotSize", DEF_ROBOT_SIZE, t_Float},
218 {&opacity, "opacity", "Opacity", DEF_OPACITY, t_Float},
219 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
220 {&do_fade, "fade", "Fade", DEF_FADE, t_Bool},
222 {&talk_chance, "talk", "Talk", DEF_TALK, t_Float},
225 {&debug_p, "debug", "Debug", "False", t_Bool},
229 ENTRYPOINT ModeSpecOpt robot_opts = {
230 countof(opts), opts, countof(vars), vars, NULL};
233 /* Window management, etc
236 reshape_robot (ModeInfo *mi, int width, int height)
238 GLfloat h = (GLfloat) height / (GLfloat) width;
241 if (width > height * 5) { /* tiny window: show middle */
242 height = width * 9/16;
244 h = height / (GLfloat) width;
247 glViewport (0, y, width, height);
249 glMatrixMode(GL_PROJECTION);
251 gluPerspective (40.0, 1/h, 1.0, 250);
253 glMatrixMode(GL_MODELVIEW);
259 glClear(GL_COLOR_BUFFER_BIT);
263 robot_configuration *bp = &bps[MI_SCREEN(mi)];
264 int w = (width < 800 ? 25 : 40);
267 textclient_reshape (bp->tc, w, h, w, h,
268 /* Passing bp->max_lines isn't actually necessary */
277 robot_handle_event (ModeInfo *mi, XEvent *event)
279 robot_configuration *bp = &bps[MI_SCREEN(mi)];
281 if (gltrackball_event_handler (event, bp->user_trackball,
282 MI_WIDTH (mi), MI_HEIGHT (mi),
286 else if (event->xany.type == KeyPress && debug_p)
290 double n[3] = { 1.0, 0.1, 0.1 };
291 int s = (event->xkey.state & ShiftMask ? 10 : 1);
293 XLookupString (&event->xkey, &c, 1, &keysym, 0);
295 if (keysym == XK_Right) bp->debug_x += n[0] * s;
296 else if (keysym == XK_Left) bp->debug_x -= n[0] * s;
297 else if (keysym == XK_Up) bp->debug_y += n[1] * s;
298 else if (keysym == XK_Down) bp->debug_y -= n[1] * s;
299 else if (c == '=' || c == '+') bp->debug_z += n[2] * s;
300 else if (c == '-' || c == '_') bp->debug_z -= n[2] * s;
301 else if (c == ' ') bp->debug_x = bp->debug_y = bp->debug_z = 0;
302 else if (c == '\n' || c == '\r')
303 fprintf (stderr, "%.4f %.4f %.4f\n",
304 bp->debug_x, bp->debug_y, bp->debug_z);
317 load_textures (ModeInfo *mi)
319 robot_configuration *bp = &bps[MI_SCREEN(mi)];
322 xi = image_data_to_ximage (mi->dpy, mi->xgwa.visual,
323 chromesphere_png, sizeof(chromesphere_png));
326 glGenTextures (1, &bp->chrome_texture);
327 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
328 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
329 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
330 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
331 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
332 xi->width, xi->height, 0,
333 GL_RGBA, GL_UNSIGNED_BYTE, xi->data);
334 check_gl_error("texture");
336 glEnable(GL_TEXTURE_GEN_S);
337 glEnable(GL_TEXTURE_GEN_T);
338 glEnable(GL_TEXTURE_2D);
341 #endif /* HAVE_TEXTURE */
344 static int unit_gear (ModeInfo *, GLfloat color[4]);
345 static int draw_ground (ModeInfo *, GLfloat color[4]);
346 static void init_walker (ModeInfo *, walker *);
349 parse_color (ModeInfo *mi, char *key, GLfloat color[4])
352 char *string = get_string_resource (mi->dpy, key, "RobotColor");
353 if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
355 fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
360 color[0] = xcolor.red / 65536.0;
361 color[1] = xcolor.green / 65536.0;
362 color[2] = xcolor.blue / 65536.0;
368 init_robot (ModeInfo *mi)
370 robot_configuration *bp;
371 int wire = MI_IS_WIREFRAME(mi);
375 bp = &bps[MI_SCREEN(mi)];
377 bp->glx_context = init_GL(mi);
379 reshape_robot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
381 glShadeModel(GL_SMOOTH);
383 glEnable(GL_DEPTH_TEST);
384 glEnable(GL_NORMALIZE);
385 glEnable(GL_CULL_FACE);
389 GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
390 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
391 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
392 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
393 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
395 glEnable(GL_LIGHTING);
397 glEnable(GL_DEPTH_TEST);
398 glEnable(GL_CULL_FACE);
400 glLightfv(GL_LIGHT0, GL_POSITION, pos);
401 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
402 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
403 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
405 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
409 if (!wire && do_texture)
413 bp->user_trackball = gltrackball_init (False);
415 bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
416 for (i = 0; i < countof(all_objs); i++)
417 bp->dlists[i] = glGenLists (1);
419 for (i = 0; i < countof(all_objs); i++)
421 const struct gllist *gll = *all_objs[i];
423 GLfloat spec1[4] = {1.00, 1.00, 1.00, 1.0};
424 GLfloat spec2[4] = {0.40, 0.40, 0.70, 1.0};
428 glNewList (bp->dlists[i], GL_COMPILE);
430 glMatrixMode(GL_MODELVIEW);
432 glMatrixMode(GL_TEXTURE);
434 glMatrixMode(GL_MODELVIEW);
436 glRotatef (-90, 1, 0, 0);
437 glRotatef (180, 0, 0, 1);
440 glBindTexture (GL_TEXTURE_2D, 0);
450 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
451 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
452 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
463 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
464 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
465 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
505 key = "gearboxColor";
509 case ROBOT_WIREFRAME:
523 parse_color (mi, key, bp->component_colors[i]);
525 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
526 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
531 robot_dome = (struct gllist *) calloc (1, sizeof(*robot_dome));
532 robot_dome->points = unit_dome (32, 32, MI_IS_WIREFRAME(mi));
536 robot_gear = (struct gllist *) calloc (1, sizeof(*robot_gear));
537 robot_gear->points = unit_gear (mi, bp->component_colors[i]);
541 ground = (struct gllist *) calloc (1, sizeof(*ground));
542 ground->points = draw_ground (mi, bp->component_colors[i]);
544 case ROBOT_WIREFRAME:
546 renderList (gll, True);
549 renderList (gll, wire);
550 /* glColor3f (1, 1, 1); renderListNormals (gll, 100, True); */
551 /* glColor3f (1, 1, 0); renderListNormals (gll, 100, False); */
555 glMatrixMode(GL_TEXTURE);
557 glMatrixMode(GL_MODELVIEW);
564 if (debug_p) MI_COUNT(mi) = 1;
567 bp->nwalkers = MI_COUNT(mi);
568 bp->walkers = (walker *) calloc (bp->nwalkers, sizeof (walker));
570 for (i = 0; i < bp->nwalkers; i++)
571 init_walker (mi, &bp->walkers[i]);
573 /* Since #0 is the one we track, make sure it doesn't walk too straight.
575 bp->walkers[0].balance *= 1.5;
578 bp->font_data = load_texture_font (mi->dpy, "labelFont");
579 bp->max_lines = get_integer_resource (mi->dpy, "textLines", "TextLines");
580 bp->tc = textclient_open (MI_DISPLAY (mi));
582 parse_color (mi, "textColor", bp->text_color);
583 parse_color (mi, "textBackground", bp->text_bg);
584 parse_color (mi, "textBorderColor", bp->text_bd);
586 reshape_robot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
588 # endif /* WORDBUBBLES */
593 /* Let's tilt the floor a little. */
594 gltrackball_reset (bp->user_trackball,
601 draw_component (ModeInfo *mi, int i)
603 robot_configuration *bp = &bps[MI_SCREEN(mi)];
604 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
605 bp->component_colors[i]);
606 glCallList (bp->dlists[i]);
607 return (*all_objs[i])->points / 3;
611 draw_transparent_component (ModeInfo *mi, int i, GLfloat alpha)
613 robot_configuration *bp = &bps[MI_SCREEN(mi)];
614 int wire = MI_IS_WIREFRAME(mi);
617 if (alpha < 0) return 0;
618 if (alpha > 1) alpha = 1;
619 bp->component_colors[i][3] = alpha;
621 if (wire || alpha >= 1)
622 return draw_component (mi, i);
624 /* Draw into the depth buffer but not the frame buffer */
625 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
626 count += draw_component (mi, i);
628 /* Now draw into the frame buffer only where there's already depth */
629 glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
630 glDepthFunc (GL_EQUAL);
632 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
634 count += draw_component (mi, i);
635 glDepthFunc (GL_LESS);
636 glDisable (GL_BLEND);
643 draw_arm_half (ModeInfo *mi, walker *f)
647 count += draw_transparent_component (mi, ROBOT_ARM, f->body_transparency);
653 draw_hand_half (ModeInfo *mi, walker *f)
657 count += draw_transparent_component (mi, ROBOT_HAND, f->body_transparency);
663 /* rotation of arm: 0-360.
664 openness of fingers: 0.0 - 1.0.
667 draw_arm (ModeInfo *mi, walker *f, GLfloat left_p, GLfloat rot, GLfloat open)
670 GLfloat arm_x = 4766; /* distance from origin to arm axis */
671 GLfloat arm_y = 12212;
673 open *= 5.5; /* scale of finger range */
676 if (debug_p) rot = 0;
682 glTranslatef (0, 0, arm_x * 2);
684 glTranslatef (0, arm_y, -arm_x); /* move to origin */
685 glRotatef (rot, 1, 0, 0);
686 glTranslatef (0, -arm_y, arm_x); /* move back */
689 count += draw_arm_half (mi, f);
692 glTranslatef (0, -arm_y * 2, 0);
694 count += draw_arm_half (mi, f);
697 glTranslatef (0, 0, -arm_x * 2);
700 count += draw_arm_half (mi, f);
703 glTranslatef (0, -arm_y * 2, 0);
705 count += draw_arm_half (mi, f);
708 glTranslatef (0, 0, open);
710 count += draw_hand_half (mi, f);
712 glTranslatef (0, 0, -open);
714 glTranslatef (0, 0, arm_x * 2);
716 glTranslatef (0, 0, open);
717 count += draw_hand_half (mi, f);
725 draw_body_half (ModeInfo *mi, walker *f, Bool inside_p)
728 int which = (inside_p ? ROBOT_BODY_2 : ROBOT_BODY_1);
730 count += draw_transparent_component (mi, which, f->body_transparency);
737 draw_body (ModeInfo *mi, walker *f, Bool inside_p)
743 count += draw_body_half (mi, f, inside_p);
747 count += draw_body_half (mi, f, inside_p);
756 draw_gearbox_half (ModeInfo *mi)
760 count += draw_component (mi, ROBOT_GEARBOX);
767 draw_gearbox (ModeInfo *mi)
773 count += draw_gearbox_half (mi);
777 count += draw_gearbox_half (mi);
785 unit_gear (ModeInfo *mi, GLfloat color[4])
787 int wire = MI_IS_WIREFRAME(mi);
795 g->thickness2 = g->thickness * 0.5;
796 g->thickness3 = g->thickness;
797 g->inner_r = g->r * 0.7;
798 g->inner_r2 = g->r * 0.4;
799 g->inner_r3 = g->r * 0.1;
800 g->size = INVOLUTE_LARGE;
802 g->color[0] = g->color2[0] = color[0];
803 g->color[1] = g->color2[1] = color[1];
804 g->color[2] = g->color2[2] = color[2];
805 g->color[3] = g->color2[3] = color[3];
807 return draw_involute_gear (g, wire);
812 draw_gear (ModeInfo *mi)
817 count += draw_component (mi, ROBOT_GEAR);
823 draw_crank (ModeInfo *mi, walker *f, GLfloat rot)
826 GLfloat origin = 12210.0;
832 glTranslatef (0, origin, 0); /* position at origin */
833 glRotatef (rot, 0, 0, 1);
836 glRotatef (90, 1, 0, 0);
837 count += draw_gear (mi);
840 glTranslatef (0, -origin, 0); /* move back */
843 count += draw_component (mi, ROBOT_CRANK);
852 draw_rotator_half (ModeInfo *mi)
856 count += draw_component (mi, ROBOT_ROTATOR);
863 draw_rotator (ModeInfo *mi, walker *f, GLfloat rot)
866 GLfloat origin = 10093.0;
870 glTranslatef (0, origin, 0); /* position at origin */
871 glRotatef (rot, 0, 0, 1);
874 glRotatef (90, 1, 0, 0);
875 count += draw_gear (mi);
881 glDisable(GL_LIGHTING);
883 glVertex3f(0, 0, -3000);
884 glVertex3f(0, 0, 3000);
886 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
890 glTranslatef (0, -origin, 0); /* move back */
893 count += draw_rotator_half (mi);
897 glRotatef (180, 0, 0, 1);
898 glTranslatef (0, -origin * 2, 0); /* move back */
899 count += draw_rotator_half (mi);
907 draw_leg_half (ModeInfo *mi)
911 count += draw_component (mi, ROBOT_LEG);
918 draw_wireframe (ModeInfo *mi, walker *f)
920 int wire = MI_IS_WIREFRAME(mi);
922 GLfloat alpha = 0.6 - f->body_transparency;
923 if (alpha < 0) return 0;
925 if (!wire) glDisable (GL_LIGHTING);
927 count += draw_transparent_component (mi, ROBOT_WIREFRAME, alpha);
929 if (!wire) glEnable (GL_LIGHTING);
935 draw_leg (ModeInfo *mi, GLfloat rot, Bool left_p)
939 GLfloat leg_distance = 9401; /* distance from ground to leg axis */
940 GLfloat rot_distance = 10110; /* distance from ground to rotator axis */
941 GLfloat pin_distance = 14541; /* distance from ground to stop pin */
942 GLfloat orbit_r = rot_distance - leg_distance; /* radius of rotation */
944 /* Actually it's the bottom of the pin minus its diameter, or something. */
950 glRotatef (180, 0, 1, 0);
952 if (!left_p) rot = -(rot + 180);
956 x = orbit_r * cos (-rot * M_PI / 180);
957 y = orbit_r * sin (-rot * M_PI / 180);
960 /* Rotate the leg by angle B of the right A
961 triangle ABC, where: /:
963 A = position of stop pin / :
964 D = position of rotator wheel's axis , - ~ ~ ~ - ,
965 C = D + y , ' / : ' ,
966 B = D + xy (leg's axis) , / : ,
969 H = dist(A,B) , / D ,
970 O = dist(A,C) , / : ,
971 sin(th) = O/H , / : ,
972 th = asin(O/H) B ~ ~ ~ ~ ~ C .
976 GLfloat Ay = pin_distance - leg_distance;
977 GLfloat Cx = 0, Cy = y;
979 GLfloat dBC = Cx - Bx;
980 GLfloat dAC = Cy - Ay;
981 GLfloat dAB = sqrt (dBC*dBC + dAC*dAC);
982 GLfloat th = asin (dAC / dAB);
983 rot = th / (M_PI / 180.0);
985 if (dBC > 0) rot = 360-rot;
988 glTranslatef (0, orbit_r, 0); /* position on rotator axis */
989 glTranslatef (x, y, 0);
991 glTranslatef (0, leg_distance, 0); /* position on leg axis */
992 glRotatef(rot, 0, 0, 1);
993 glTranslatef (0, -leg_distance, 0); /* move back */
996 count += draw_leg_half (mi);
1000 count += draw_leg_half (mi);
1008 draw_dome (ModeInfo *mi, walker *f)
1010 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1011 int wire = MI_IS_WIREFRAME(mi);
1012 int which = ROBOT_DOME;
1015 GLfloat trans = f->body_transparency;
1017 GLfloat dome_y = 15290;
1019 if (trans < 0) trans = 0;
1020 if (trans > max) trans = max;
1022 if (!wire) glEnable (GL_BLEND);
1025 glTranslatef (0, dome_y, 0);
1026 glScalef (100, 100, 100);
1027 glRotatef (90, 1, 0, 0);
1028 glTranslatef (0.35, 0, 0);
1030 glFrontFace(GL_CCW);
1031 bp->component_colors[which][3] = trans;
1032 count += draw_component (mi, which);
1035 if (!wire) glDisable (GL_BLEND);
1041 /* Is this robot overlapping any other?
1044 collision_p (ModeInfo *mi, walker *w, GLfloat extra_space)
1046 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1048 if (MI_COUNT(mi) <= 1) return False;
1050 for (i = 0; i < MI_COUNT(mi); i++)
1052 walker *w2 = &bp->walkers[i];
1053 GLfloat min = 0.75 + extra_space;
1055 if (w == w2) continue;
1058 d = (dx*dx + dy*dy);
1059 if (d <= min*min) return True;
1065 /* I couldn't figure out a straightforward trig solution to the
1066 forward/backward tilting that happens as weight passes from one
1067 foot to another, so I just built a tool to eyeball it manually.
1068 { vertical_translation, rotation, forward_translation }
1070 static const struct { GLfloat up, rot, fwd; } wobble_profile[360] = {
1071 { 0.0000, 0.00, 0.0000 }, /* 0 */
1072 { 0.0000, -0.01, 0.0025 },
1073 { 0.0000, -0.25, 0.0040 },
1074 { 0.0000, -0.41, 0.0060 },
1075 { 0.0000, -0.62, 0.0080 },
1076 { 0.0000, -0.80, 0.0095 },
1077 { 0.0000, -0.90, 0.0120 },
1078 { 0.0000, -1.10, 0.0135 },
1079 { 0.0000, -1.25, 0.0150 },
1080 { 0.0000, -1.40, 0.0175 },
1081 { 0.0000, -1.50, 0.0195 }, /* 10 */
1082 { 0.0000, -1.70, 0.0215 },
1083 { 0.0000, -1.80, 0.0230 },
1084 { 0.0000, -2.00, 0.0250 },
1085 { 0.0000, -2.10, 0.0270 },
1086 { -0.0005, -2.30, 0.0290 },
1087 { -0.0005, -2.50, 0.0305 },
1088 { -0.0005, -2.60, 0.0330 },
1089 { -0.0005, -2.70, 0.0330 },
1090 { -0.0005, -2.90, 0.0350 },
1091 { -0.0005, -3.00, 0.0365 }, /* 20 */
1092 { -0.0010, -3.20, 0.0380 },
1093 { -0.0005, -3.30, 0.0400 },
1094 { -0.0010, -3.50, 0.0420 },
1095 { -0.0010, -3.70, 0.0440 },
1096 { -0.0010, -3.80, 0.0460 },
1097 { -0.0010, -3.90, 0.0470 },
1098 { -0.0015, -4.10, 0.0500 },
1099 { -0.0015, -4.20, 0.0515 },
1100 { -0.0015, -4.40, 0.0535 },
1101 { -0.0015, -4.50, 0.0550 }, /* 30 */
1102 { -0.0015, -4.60, 0.0565 },
1103 { -0.0020, -4.80, 0.0585 },
1104 { -0.0020, -5.00, 0.0600 },
1105 { -0.0020, -5.10, 0.0620 },
1106 { -0.0025, -5.20, 0.0635 },
1107 { -0.0025, -5.40, 0.0655 },
1108 { -0.0025, -5.50, 0.0675 },
1109 { -0.0025, -5.60, 0.0690 },
1110 { -0.0030, -5.80, 0.0710 },
1111 { -0.0030, -5.90, 0.0720 }, /* 40 */
1112 { -0.0030, -6.00, 0.0740 },
1113 { -0.0035, -6.10, 0.0760 },
1114 { -0.0035, -6.30, 0.0790 },
1115 { -0.0040, -6.40, 0.0805 },
1116 { -0.0040, -6.50, 0.0820 },
1117 { -0.0040, -6.60, 0.0835 },
1118 { -0.0045, -6.80, 0.0855 },
1119 { -0.0045, -6.90, 0.0870 },
1120 { -0.0050, -7.00, 0.0885 },
1121 { -0.0050, -7.20, 0.0900 }, /* 50 */
1122 { -0.0050, -7.20, 0.0915 },
1123 { -0.0055, -7.40, 0.0930 },
1124 { -0.0055, -7.50, 0.0945 },
1125 { -0.0060, -7.60, 0.0960 },
1126 { -0.0060, -7.70, 0.0970 },
1127 { -0.0065, -7.80, 0.0985 },
1128 { -0.0060, -7.70, 0.0995 },
1129 { -0.0060, -7.60, 0.1010 },
1130 { -0.0060, -7.50, 0.1020 },
1131 { -0.0055, -7.30, 0.1030 }, /* 60 */
1132 { -0.0050, -7.10, 0.1040 },
1133 { -0.0050, -6.90, 0.1050 },
1134 { -0.0045, -6.80, 0.1065 },
1135 { -0.0045, -6.50, 0.1075 },
1136 { -0.0040, -6.40, 0.1085 },
1137 { -0.0040, -6.20, 0.1095 },
1138 { -0.0040, -6.00, 0.1105 },
1139 { -0.0035, -5.80, 0.1115 },
1140 { -0.0030, -5.50, 0.1125 },
1141 { -0.0030, -5.40, 0.1135 }, /* 70 */
1142 { -0.0030, -5.10, 0.1145 },
1143 { -0.0030, -4.90, 0.1150 },
1144 { -0.0025, -4.70, 0.1160 },
1145 { -0.0025, -4.40, 0.1165 },
1146 { -0.0025, -4.20, 0.1175 },
1147 { -0.0020, -3.90, 0.1180 },
1148 { -0.0020, -3.70, 0.1185 },
1149 { -0.0020, -3.40, 0.1190 },
1150 { -0.0020, -3.10, 0.1195 },
1151 { -0.0020, -2.90, 0.1200 }, /* 80 */
1152 { -0.0015, -2.60, 0.1200 },
1153 { -0.0015, -2.30, 0.1205 },
1154 { -0.0015, -2.00, 0.1210 },
1155 { -0.0015, -1.80, 0.1215 },
1156 { -0.0015, -1.50, 0.1215 },
1157 { -0.0015, -1.20, 0.1215 },
1158 { -0.0015, -0.90, 0.1215 },
1159 { -0.0015, -0.60, 0.1215 },
1160 { -0.0015, -0.30, 0.1215 },
1161 { -0.0010, 0.00, 0.1215 }, /* 90 */
1162 { -0.0010, 0.30, 0.1215 },
1163 { -0.0010, 0.60, 0.1215 },
1164 { -0.0010, 0.90, 0.1215 },
1165 { -0.0010, 1.20, 0.1215 },
1166 { -0.0015, 1.40, 0.1215 },
1167 { -0.0015, 1.70, 0.1215 },
1168 { -0.0015, 2.00, 0.1215 },
1169 { -0.0015, 2.30, 0.1215 },
1170 { -0.0015, 2.60, 0.1215 },
1171 { -0.0015, 2.80, 0.1220 }, /* 100 */
1172 { -0.0020, 3.10, 0.1225 },
1173 { -0.0020, 3.30, 0.1230 },
1174 { -0.0020, 3.60, 0.1235 },
1175 { -0.0020, 3.90, 0.1240 },
1176 { -0.0025, 4.10, 0.1245 },
1177 { -0.0025, 4.40, 0.1250 },
1178 { -0.0025, 4.60, 0.1260 },
1179 { -0.0025, 4.90, 0.1265 },
1180 { -0.0030, 5.10, 0.1275 },
1181 { -0.0030, 5.30, 0.1285 }, /* 110 */
1182 { -0.0035, 5.60, 0.1290 },
1183 { -0.0035, 5.80, 0.1300 },
1184 { -0.0035, 6.00, 0.1310 },
1185 { -0.0040, 6.20, 0.1325 },
1186 { -0.0040, 6.40, 0.1335 },
1187 { -0.0045, 6.60, 0.1345 },
1188 { -0.0045, 6.70, 0.1355 },
1189 { -0.0050, 6.90, 0.1365 },
1190 { -0.0050, 7.10, 0.1375 },
1191 { -0.0055, 7.30, 0.1390 }, /* 120 */
1192 { -0.0055, 7.40, 0.1400 },
1193 { -0.0060, 7.50, 0.1415 },
1194 { -0.0065, 8.00, 0.1425 },
1195 { -0.0065, 7.80, 0.1440 },
1196 { -0.0060, 7.80, 0.1455 },
1197 { -0.0060, 7.60, 0.1470 },
1198 { -0.0055, 7.50, 0.1485 },
1199 { -0.0055, 7.40, 0.1500 },
1200 { -0.0050, 7.30, 0.1515 },
1201 { -0.0050, 7.20, 0.1530 }, /* 130 */
1202 { -0.0050, 7.00, 0.1545 },
1203 { -0.0045, 6.90, 0.1560 },
1204 { -0.0045, 6.80, 0.1575 },
1205 { -0.0040, 6.70, 0.1590 },
1206 { -0.0040, 6.50, 0.1605 },
1207 { -0.0040, 6.40, 0.1625 },
1208 { -0.0035, 6.30, 0.1640 },
1209 { -0.0035, 6.10, 0.1655 },
1210 { -0.0030, 6.10, 0.1670 },
1211 { -0.0030, 5.90, 0.1690 }, /* 140 */
1212 { -0.0030, 5.70, 0.1705 },
1213 { -0.0025, 5.70, 0.1720 },
1214 { -0.0025, 5.50, 0.1740 },
1215 { -0.0025, 5.40, 0.1755 },
1216 { -0.0025, 5.20, 0.1775 },
1217 { -0.0020, 5.10, 0.1790 },
1218 { -0.0020, 5.00, 0.1810 },
1219 { -0.0020, 4.80, 0.1825 },
1220 { -0.0015, 4.70, 0.1840 },
1221 { -0.0015, 4.60, 0.1860 }, /* 150 */
1222 { -0.0015, 4.40, 0.1880 },
1223 { -0.0015, 4.20, 0.1900 },
1224 { -0.0015, 4.10, 0.1915 },
1225 { -0.0010, 4.00, 0.1935 },
1226 { -0.0010, 3.80, 0.1955 },
1227 { -0.0010, 3.70, 0.1970 },
1228 { -0.0010, 3.50, 0.1990 },
1229 { -0.0005, 3.40, 0.2010 },
1230 { -0.0010, 3.20, 0.2025 },
1231 { -0.0005, 3.10, 0.2045 }, /* 160 */
1232 { -0.0005, 2.90, 0.2065 },
1233 { -0.0005, 2.80, 0.2085 },
1234 { -0.0005, 2.60, 0.2105 },
1235 { -0.0005, 2.50, 0.2120 },
1236 { -0.0005, 2.30, 0.2140 },
1237 { -0.0005, 2.20, 0.2160 },
1238 { -0.0005, 2.00, 0.2180 },
1239 { 0.0000, 1.90, 0.2200 },
1240 { 0.0000, 1.70, 0.2220 },
1241 { 0.0000, 1.60, 0.2235 }, /* 170 */
1242 { 0.0000, 1.40, 0.2255 },
1243 { 0.0000, 1.30, 0.2275 },
1244 { 0.0000, 1.10, 0.2295 },
1245 { 0.0000, 0.90, 0.2315 },
1246 { 0.0000, 0.80, 0.2335 },
1247 { 0.0000, 0.60, 0.2355 },
1248 { 0.0000, 0.50, 0.2375 },
1249 { 0.0000, 0.30, 0.2395 },
1250 { 0.0000, 0.10, 0.2415 },
1251 { 0.0000, 0.00, 0.2430 }, /* 180 */
1252 { 0.0000, -0.10, 0.2450 },
1253 { 0.0000, -0.30, 0.2470 },
1254 { 0.0000, -0.40, 0.2490 },
1255 { 0.0000, -0.60, 0.2510 },
1256 { 0.0000, -0.80, 0.2530 },
1257 { 0.0000, -0.90, 0.2550 },
1258 { 0.0000, -1.10, 0.2570 },
1259 { 0.0000, -1.20, 0.2590 },
1260 { 0.0000, -1.40, 0.2610 },
1261 { 0.0000, -1.50, 0.2625 }, /* 190 */
1262 { 0.0000, -1.70, 0.2645 },
1263 { 0.0000, -1.80, 0.2665 },
1264 { -0.0005, -2.00, 0.2685 },
1265 { -0.0005, -2.10, 0.2705 },
1266 { -0.0005, -2.30, 0.2725 },
1267 { -0.0005, -2.40, 0.2740 },
1268 { -0.0005, -2.60, 0.2760 },
1269 { -0.0005, -2.80, 0.2780 },
1270 { -0.0005, -2.90, 0.2800 },
1271 { -0.0005, -3.00, 0.2820 }, /* 200 */
1272 { -0.0010, -3.20, 0.2835 },
1273 { -0.0005, -3.30, 0.2855 },
1274 { -0.0010, -3.50, 0.2875 },
1275 { -0.0010, -3.70, 0.2895 },
1276 { -0.0010, -3.80, 0.2910 },
1277 { -0.0010, -3.90, 0.2930 },
1278 { -0.0010, -4.00, 0.2950 },
1279 { -0.0015, -4.20, 0.2965 },
1280 { -0.0015, -4.40, 0.2985 },
1281 { -0.0015, -4.50, 0.3000 }, /* 210 */
1282 { -0.0015, -4.60, 0.3020 },
1283 { -0.0020, -4.80, 0.3040 },
1284 { -0.0020, -5.00, 0.3055 },
1285 { -0.0020, -5.00, 0.3075 },
1286 { -0.0025, -5.20, 0.3090 },
1287 { -0.0025, -5.30, 0.3110 },
1288 { -0.0025, -5.50, 0.3125 },
1289 { -0.0025, -5.60, 0.3140 },
1290 { -0.0030, -5.70, 0.3160 },
1291 { -0.0030, -5.90, 0.3175 }, /* 220 */
1292 { -0.0030, -6.00, 0.3190 },
1293 { -0.0035, -6.10, 0.3210 },
1294 { -0.0035, -6.30, 0.3225 },
1295 { -0.0040, -6.40, 0.3240 },
1296 { -0.0040, -6.50, 0.3255 },
1297 { -0.0040, -6.60, 0.3270 },
1298 { -0.0045, -6.80, 0.3290 },
1299 { -0.0045, -6.90, 0.3305 },
1300 { -0.0050, -7.00, 0.3320 },
1301 { -0.0050, -7.20, 0.3335 }, /* 230 */
1302 { -0.0050, -7.20, 0.3350 },
1303 { -0.0055, -7.40, 0.3365 },
1304 { -0.0055, -7.50, 0.3380 },
1305 { -0.0060, -7.60, 0.3390 },
1306 { -0.0060, -7.70, 0.3405 },
1307 { -0.0065, -7.80, 0.3420 },
1308 { -0.0060, -7.60, 0.3425 },
1309 { -0.0060, -7.50, 0.3440 },
1310 { -0.0055, -7.40, 0.3455 },
1311 { -0.0055, -7.20, 0.3470 }, /* 240 */
1312 { -0.0050, -7.10, 0.3480 },
1313 { -0.0050, -6.90, 0.3490 },
1314 { -0.0045, -6.80, 0.3500 },
1315 { -0.0045, -6.50, 0.3510 },
1316 { -0.0040, -6.40, 0.3520 },
1317 { -0.0040, -6.10, 0.3535 },
1318 { -0.0035, -6.00, 0.3545 },
1319 { -0.0035, -5.80, 0.3550 },
1320 { -0.0030, -5.50, 0.3560 },
1321 { -0.0030, -5.30, 0.3570 }, /* 250 */
1322 { -0.0030, -5.10, 0.3580 },
1323 { -0.0030, -4.90, 0.3585 },
1324 { -0.0025, -4.70, 0.3595 },
1325 { -0.0025, -4.40, 0.3600 },
1326 { -0.0020, -4.10, 0.3610 },
1327 { -0.0020, -3.90, 0.3615 },
1328 { -0.0020, -3.70, 0.3620 },
1329 { -0.0020, -3.30, 0.3625 },
1330 { -0.0020, -3.10, 0.3630 },
1331 { -0.0015, -2.80, 0.3635 }, /* 260 */
1332 { -0.0015, -2.60, 0.3640 },
1333 { -0.0015, -2.40, 0.3645 },
1334 { -0.0015, -2.00, 0.3645 },
1335 { -0.0015, -1.80, 0.3650 },
1336 { -0.0015, -1.40, 0.3650 },
1337 { -0.0015, -1.20, 0.3655 },
1338 { -0.0010, -0.90, 0.3655 },
1339 { -0.0010, -0.60, 0.3655 },
1340 { -0.0010, -0.30, 0.3655 },
1341 { -0.0010, 0.00, 0.3655 }, /* 270 */
1342 { -0.0010, 0.30, 0.3655 },
1343 { -0.0010, 0.60, 0.3655 },
1344 { -0.0010, 0.90, 0.3655 },
1345 { -0.0015, 1.10, 0.3655 },
1346 { -0.0015, 1.40, 0.3655 },
1347 { -0.0015, 1.70, 0.3655 },
1348 { -0.0015, 1.90, 0.3660 },
1349 { -0.0015, 2.20, 0.3660 },
1350 { -0.0015, 2.50, 0.3665 },
1351 { -0.0015, 2.80, 0.3670 }, /* 280 */
1352 { -0.0015, 3.10, 0.3675 },
1353 { -0.0020, 3.40, 0.3680 },
1354 { -0.0020, 3.70, 0.3685 },
1355 { -0.0020, 3.90, 0.3690 },
1356 { -0.0025, 4.10, 0.3695 },
1357 { -0.0025, 4.40, 0.3700 },
1358 { -0.0025, 4.60, 0.3710 },
1359 { -0.0025, 4.80, 0.3715 },
1360 { -0.0025, 5.00, 0.3730 },
1361 { -0.0030, 5.40, 0.3735 }, /* 290 */
1362 { -0.0035, 5.60, 0.3745 },
1363 { -0.0035, 5.80, 0.3755 },
1364 { -0.0035, 6.00, 0.3765 },
1365 { -0.0040, 6.20, 0.3775 },
1366 { -0.0045, 6.50, 0.3785 },
1367 { -0.0045, 6.60, 0.3795 },
1368 { -0.0045, 6.80, 0.3805 },
1369 { -0.0050, 7.00, 0.3815 },
1370 { -0.0050, 7.10, 0.3825 },
1371 { -0.0055, 7.20, 0.3840 }, /* 300 */
1372 { -0.0055, 7.40, 0.3850 },
1373 { -0.0060, 7.50, 0.3865 },
1374 { -0.0060, 7.70, 0.3875 },
1375 { -0.0065, 7.80, 0.3890 },
1376 { -0.0060, 7.80, 0.3900 },
1377 { -0.0060, 7.60, 0.3915 },
1378 { -0.0055, 7.60, 0.3930 },
1379 { -0.0055, 7.40, 0.3945 },
1380 { -0.0050, 7.30, 0.3960 },
1381 { -0.0050, 7.20, 0.3975 }, /* 310 */
1382 { -0.0050, 7.00, 0.3990 },
1383 { -0.0045, 6.90, 0.4005 },
1384 { -0.0045, 6.80, 0.4020 },
1385 { -0.0040, 6.70, 0.4035 },
1386 { -0.0040, 6.60, 0.4050 },
1387 { -0.0040, 6.40, 0.4065 },
1388 { -0.0035, 6.30, 0.4085 },
1389 { -0.0035, 6.20, 0.4100 },
1390 { -0.0030, 6.10, 0.4115 },
1391 { -0.0030, 5.90, 0.4130 }, /* 320 */
1392 { -0.0030, 5.80, 0.4150 },
1393 { -0.0025, 5.70, 0.4165 },
1394 { -0.0025, 5.50, 0.4180 },
1395 { -0.0025, 5.40, 0.4200 },
1396 { -0.0025, 5.20, 0.4215 },
1397 { -0.0020, 5.10, 0.4235 },
1398 { -0.0020, 5.00, 0.4250 },
1399 { -0.0020, 4.80, 0.4270 },
1400 { -0.0015, 4.70, 0.4285 },
1401 { -0.0015, 4.60, 0.4305 }, /* 330 */
1402 { -0.0015, 4.40, 0.4325 },
1403 { -0.0015, 4.20, 0.4340 },
1404 { -0.0015, 4.10, 0.4360 },
1405 { -0.0010, 4.00, 0.4375 },
1406 { -0.0010, 3.80, 0.4395 },
1407 { -0.0010, 3.70, 0.4415 },
1408 { -0.0010, 3.50, 0.4435 },
1409 { -0.0005, 3.40, 0.4450 },
1410 { -0.0010, 3.20, 0.4470 },
1411 { -0.0005, 3.10, 0.4490 }, /* 340 */
1412 { -0.0005, 2.90, 0.4510 },
1413 { -0.0005, 2.80, 0.4525 },
1414 { -0.0005, 2.60, 0.4545 },
1415 { -0.0005, 2.40, 0.4565 },
1416 { -0.0005, 2.30, 0.4585 },
1417 { -0.0005, 2.20, 0.4605 },
1418 { -0.0005, 2.00, 0.4620 },
1419 { 0.0000, 1.90, 0.4640 },
1420 { 0.0000, 1.70, 0.4660 },
1421 { 0.0000, 1.60, 0.4680 }, /* 350 */
1422 { 0.0000, 1.40, 0.4700 },
1423 { 0.0000, 1.20, 0.4720 },
1424 { 0.0000, 1.10, 0.4740 },
1425 { 0.0000, 0.90, 0.4760 },
1426 { 0.0000, 0.80, 0.4780 },
1427 { 0.0000, 0.60, 0.4795 },
1428 { 0.0000, 0.50, 0.4815 },
1429 { 0.0000, 0.30, 0.4835 },
1430 { 0.0000, 0.20, 0.4855 }, /* 359 */
1434 /* Turn the crank by 1 degree, which moves the legs and displaces the robot.
1437 tick_walker (ModeInfo *mi, walker *f)
1439 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1443 if (bp->button_down_p) return;
1446 deg = ((int) (f->crank_rot + 0.5)) % 360;
1451 f->crank_rot = bp->debug_x;
1452 f->pitch = wobble_profile[deg].rot;
1453 f->z = wobble_profile[deg].up;
1460 fwd = wobble_profile[deg].fwd;
1461 f->pitch = wobble_profile[deg].rot;
1462 f->z = wobble_profile[deg].up;
1466 fwd = (wobble_profile[deg].fwd - wobble_profile[deg-1].fwd);
1467 f->pitch += (wobble_profile[deg].rot - wobble_profile[deg-1].rot);
1468 f->z += (wobble_profile[deg].up - wobble_profile[deg-1].up);
1471 /* Lean slightly toward the foot that is raised off the ground. */
1472 f->roll = -2.5 * sin ((deg - 90) * M_PI / 180);
1474 if (!(random() % 10))
1476 GLfloat b = f->balance / 10.0;
1477 int s = (f->balance > 0 ? 1 : -1);
1479 f->facing += s * frand (b);
1483 if (debug_p) fwd = 0;
1489 th = f->facing * M_PI / 180.0;
1490 f->x += fwd * cos (th);
1491 f->y += fwd * sin (th);
1493 /* If moving this robot would collide with another, undo the move,
1494 recoil, and randomly turn.
1496 if (collision_p (mi, f, 0))
1499 f->x = ox + fwd * cos (th);
1500 f->y = oy + fwd * sin (th);
1501 f->facing += frand(10) - 5;
1502 if (! random() % 30)
1503 f->facing += frand(90) - 45;
1509 opacity > 0.5) /* Don't bother fading if it's already transparent. */
1511 GLfloat tick = 0.002;
1514 /* If we're not fading, maybe start fading out. */
1515 if (f->fading_p == 0 && ! (random() % 40000))
1519 if (debug_p) f->fading_p = 0;
1522 if (f->fading_p < 0)
1524 f->body_transparency -= tick;
1525 if (f->body_transparency <= -linger)
1527 f->body_transparency = -linger;
1531 else if (f->fading_p > 0)
1533 f->body_transparency += tick;
1534 if (f->body_transparency >= opacity)
1536 f->body_transparency = opacity;
1545 init_walker (ModeInfo *mi, walker *f)
1547 int i, start_tick = random() % 360;
1550 f->pitch = wobble_profile[0].rot;
1551 f->z = wobble_profile[0].up;
1553 f->body_transparency = opacity;
1555 f->hand_rot[0] = frand(180);
1556 f->hand_pos[0] = 0.6 + frand(0.4);
1557 f->hand_rot[1] = 180 - f->hand_rot[0];
1558 f->hand_pos[1] = f->hand_pos[0];
1560 if (! (random() % 30)) f->hand_rot[1] += frand(10);
1561 if (! (random() % 30)) f->hand_pos[1] = 0.6 + frand(0.4);
1563 f->facing = frand(360);
1564 f->balance = frand(10) - 5;
1566 if (MI_COUNT(mi) == 1)
1569 f->speed = 0.6 + frand(0.8);
1580 for (i = 0; i < start_tick; i++)
1581 tick_walker (mi, f);
1583 /* Place them randomly, but non-overlapping. */
1584 for (i = 0; i < 1000; i++)
1587 if (MI_COUNT(mi) > 10) range += MI_COUNT(mi) / 10.0;
1588 f->x = frand(range) - range/2;
1589 f->y = frand(range) - range/2;
1590 if (! collision_p (mi, f, 1.5))
1595 if (debug_p) f->x = f->y = 0;
1601 /* Draw a robot standing in the right place, 1 unit tall.
1604 draw_walker (ModeInfo *mi, walker *f, const char *tag)
1606 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1607 int wire = MI_IS_WIREFRAME(mi);
1611 glTranslatef (f->y, f->z, f->x);
1618 glRotatef (90, 0, 1, 0);
1619 glRotatef (f->facing, 0, 1, 0);
1620 glRotatef (f->pitch, 0, 0, 1);
1621 glRotatef (f->roll, 1, 0, 0);
1624 GLfloat n = 0.00484; /* make it 1 unit tall */
1628 count += draw_gearbox (mi);
1629 count += draw_crank (mi, f, f->crank_rot);
1630 count += draw_rotator (mi, f, f->crank_rot);
1631 count += draw_leg (mi, f->crank_rot, False);
1632 count += draw_leg (mi, f->crank_rot, True);
1633 count += draw_wireframe (mi, f);
1635 /* Draw these last, and outer shell first, to make transparency work.
1636 The order in which things hit the depth buffer matters.
1638 if (f->body_transparency >= 0.001)
1640 count += draw_arm (mi, f, True, f->hand_rot[0], f->hand_pos[0]);
1641 count += draw_arm (mi, f, False, f->hand_rot[1], f->hand_pos[1]);
1642 count += draw_body (mi, f, False);
1643 count += draw_body (mi, f, True);
1644 count += draw_dome (mi, f);
1647 if (tag) /* For debugging depth sorting: label each robot */
1650 if (! wire) glDisable (GL_DEPTH_TEST);
1651 glColor3f (1, 1, 1);
1654 /* Billboard rotation */
1655 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]);
1656 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
1657 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
1658 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
1660 glMultMatrixf (&m[0][0]);
1661 glScalef (0.04, 0.04, 0.04);
1663 print_texture_string (bp->font_data, tag);
1665 if (! wire) glEnable (GL_DEPTH_TEST);
1674 draw_ground (ModeInfo *mi, GLfloat color[4])
1676 int wire = MI_IS_WIREFRAME(mi);
1679 /* When using fog, iOS apparently doesn't like lines or quads that are
1680 really long, and extend very far outside of the scene. Maybe? If the
1681 length of the line (cells * cell_size) is greater than 25 or so, lines
1682 that are oriented steeply away from the viewer tend to disappear
1683 (whether implemented as GL_LINES or as GL_QUADS).
1685 So we do a bunch of smaller grids instead of one big one.
1688 GLfloat cell_size = 0.8;
1693 if (debug_p) return 0;
1698 glRotatef (frand(90), 0, 0, 1);
1702 GLfloat fog_color[4] = { 0, 0, 0, 1 };
1705 glEnable (GL_LINE_SMOOTH);
1706 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
1707 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1708 glEnable (GL_BLEND);
1710 glFogi (GL_FOG_MODE, GL_EXP2);
1711 glFogfv (GL_FOG_COLOR, fog_color);
1712 glFogf (GL_FOG_DENSITY, 0.015);
1713 glFogf (GL_FOG_START, -cells/2 * cell_size * grids);
1718 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
1720 glTranslatef (-cells * grids * cell_size / 2,
1721 -cells * grids * cell_size / 2, 0);
1723 for (j = 0; j < grids; j++)
1726 for (k = 0; k < grids; k++)
1729 for (i = -cells/2; i < cells/2; i++)
1731 GLfloat a = i * cell_size;
1732 GLfloat b = cells/2 * cell_size;
1733 glVertex3f (a, -b, 0); glVertex3f (a, b, 0); points++;
1734 glVertex3f (-b, a, 0); glVertex3f (b, a, 0); points++;
1737 glTranslatef (cells * cell_size, 0, 0);
1740 glTranslatef (0, cells * cell_size, 0);
1745 glDisable (GL_LINE_SMOOTH);
1746 glDisable (GL_BLEND);
1756 /* If the target robot (robot #0) has moved too far from the point at which
1757 the camera is aimed, then initiate an animation to move the observer to
1760 Because of the jerky forward motion of the robots, just always focusing
1761 on the center of the robot looks terrible, so instead we let them walk
1762 a little out of the center of the frame, and then catch up.
1765 look_at_center (ModeInfo *mi)
1767 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1768 GLfloat target_x = bp->walkers[0].x;
1769 GLfloat target_y = bp->walkers[0].y;
1770 GLfloat target_z = 0.8; /* Look a little bit above his head */
1771 GLfloat max_dist = 2.5 / size;
1774 if (debug_p) return;
1777 if (max_dist < 1) max_dist = 1;
1778 if (max_dist > 10) max_dist = 10;
1780 if (bp->camera_tracking_p)
1782 GLfloat r = (1 - cos (bp->tracking_ratio * M_PI)) / 2;
1783 bp->looking_x = bp->olooking_x + r * (target_x - bp->olooking_x);
1784 bp->looking_y = bp->olooking_y + r * (target_y - bp->olooking_y);
1785 bp->looking_z = bp->olooking_z + r * (target_z - bp->olooking_z);
1787 bp->tracking_ratio += 0.02;
1788 if (bp->tracking_ratio >= 1)
1790 bp->camera_tracking_p = False;
1791 bp->olooking_x = bp->looking_x;
1792 bp->olooking_y = bp->looking_y;
1793 bp->olooking_z = bp->looking_z;
1797 if (! bp->camera_tracking_p)
1800 sqrt ((target_x - bp->looking_x) * (target_x - bp->looking_x) +
1801 (target_y - bp->looking_y) * (target_y - bp->looking_y) +
1802 (target_z - bp->looking_z) * (target_z - bp->looking_z));
1804 if (dist > max_dist)
1806 bp->camera_tracking_p = True;
1807 bp->tracking_ratio = 0;
1808 bp->olooking_x = bp->looking_x;
1809 bp->olooking_y = bp->looking_y;
1810 bp->olooking_z = bp->looking_z;
1814 glTranslatef (-bp->looking_y, -bp->looking_z, -bp->looking_x);
1821 glTranslatef (target_y, target_z, target_x);
1823 glVertex3f(0, -target_z, 0);
1824 glVertex3f(0, 1, 0);
1825 glVertex3f(-0.1, 0, -0.1);
1826 glVertex3f( 0.1, 0, 0.1);
1827 glVertex3f(-0.1, 0, 0.1);
1828 glVertex3f( 0.1, 0, -0.1);
1834 glTranslatef (bp->looking_y, bp->looking_z, bp->looking_x);
1835 glRotatef (30, 0, 1, 0);
1837 glVertex3f(0, -bp->looking_z, 0);
1838 glVertex3f(0, 1, 0);
1839 glVertex3f(-0.1, 0, -0.1);
1840 glVertex3f( 0.1, 0, 0.1);
1841 glVertex3f(-0.1, 0, 0.1);
1842 glVertex3f( 0.1, 0, -0.1);
1848 glTranslatef (bp->olooking_y, bp->olooking_z, bp->olooking_x);
1849 glRotatef (60, 0, 1, 0);
1851 glVertex3f(0, -bp->olooking_z, 0);
1852 glVertex3f(0, 1, 0);
1853 glVertex3f(-0.1, 0, -0.1);
1854 glVertex3f( 0.1, 0, 0.1);
1855 glVertex3f(-0.1, 0, 0.1);
1856 glVertex3f( 0.1, 0, -0.1);
1859 glTranslatef (0, -bp->olooking_z, 0);
1860 glBegin (GL_LINE_LOOP);
1861 for (th = 0; th < M_PI * 2; th += 0.1)
1862 glVertex3f (bp->olooking_y + max_dist * cos(th), 0,
1863 bp->olooking_x + max_dist * sin(th));
1873 /* Draw a cartoony word bubble.
1874 W and H are the inside size, for text.
1875 Origin is at bottom left.
1876 The bubble frame and arrow are outside that.
1879 draw_bubble_box (ModeInfo *mi,
1880 GLfloat width, GLfloat height,
1881 GLfloat corner_radius,
1882 GLfloat arrow_h, GLfloat arrow_x,
1883 GLfloat fg[4], GLfloat bg[4])
1886 # define CORNER_POINTS 16
1887 GLfloat outline_points[ (CORNER_POINTS + 2) * 4 + 8 ][3];
1890 GLfloat tick = M_PI / 2 / CORNER_POINTS;
1892 GLfloat arrow_w = arrow_h / 2;
1893 GLfloat arrow_x2 = MAX(0, MIN(width - arrow_w, arrow_x));
1895 GLfloat w2 = MAX(arrow_w, width - corner_radius * 1.10);
1896 GLfloat h2 = MAX(0, height - corner_radius * 1.28);
1897 GLfloat x2 = (width - w2) / 2;
1898 GLfloat y2 = (height - h2) / 2;
1900 GLfloat xa = x2 -corner_radius; /* E _------------_ */
1901 GLfloat xb = x2; /* D /__| |__\ */
1902 GLfloat xc = xb + w2; /* | | | | */
1903 GLfloat xd = xc + corner_radius; /* C |__| EF |__| */
1904 GLfloat xe = xb + arrow_x2; /* B \_|_________|_/ */
1905 GLfloat xf = xe + arrow_w; /* A \| */
1907 GLfloat ya = y2 - (corner_radius + arrow_h);
1908 GLfloat yb = y2 - corner_radius;
1910 GLfloat yd = yc + h2;
1911 GLfloat ye = yd + corner_radius;
1915 /* Let the lines take precedence over the fills. */
1916 glEnable (GL_POLYGON_OFFSET_FILL);
1917 glPolygonOffset (1.0, 1.0);
1922 /* top left corner */
1924 glBegin (GL_TRIANGLE_FAN);
1925 glVertex3f (xb, yd, 0);
1926 for (th = 0; th < M_PI/2 + tick; th += tick)
1928 GLfloat x = xb - corner_radius * cos(th);
1929 GLfloat y = yd + corner_radius * sin(th);
1930 glVertex3f (x, y, z);
1931 outline_points[i][0] = x;
1932 outline_points[i][1] = y;
1933 outline_points[i][2] = z;
1939 outline_points[i][0] = xc;
1940 outline_points[i][1] = ye;
1941 outline_points[i][2] = z;
1944 /* top right corner */
1946 glBegin (GL_TRIANGLE_FAN);
1947 glVertex3f (xc, yd, 0);
1948 for (th = M_PI/2; th > -tick; th -= tick)
1950 GLfloat x = xc + corner_radius * cos(th);
1951 GLfloat y = yd + corner_radius * sin(th);
1952 glVertex3f (x, y, z);
1953 outline_points[i][0] = x;
1954 outline_points[i][1] = y;
1955 outline_points[i][2] = z;
1961 outline_points[i][0] = xd;
1962 outline_points[i][1] = yc;
1963 outline_points[i][2] = z;
1966 /* bottom right corner */
1968 glBegin (GL_TRIANGLE_FAN);
1969 glVertex3f (xc, yc, 0);
1970 for (th = 0; th < M_PI/2 + tick; th += tick)
1972 GLfloat x = xc + corner_radius * cos(th);
1973 GLfloat y = yc - corner_radius * sin(th);
1974 glVertex3f (x, y, z);
1975 outline_points[i][0] = x;
1976 outline_points[i][1] = y;
1977 outline_points[i][2] = z;
1982 /* bottom right edge */
1983 outline_points[i][0] = xf;
1984 outline_points[i][1] = yb;
1985 outline_points[i][2] = z;
1988 /* arrow triangle */
1990 glBegin (GL_TRIANGLES);
1992 /* bottom arrow point */
1993 outline_points[i][0] = xf;
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]);
2001 /* bottom right edge */
2002 outline_points[i][0] = xf;
2003 outline_points[i][1] = ya;
2004 outline_points[i][2] = z;
2005 glVertex3f (outline_points[i][0],
2006 outline_points[i][1],
2007 outline_points[i][2]);
2010 outline_points[i][0] = xe;
2011 outline_points[i][1] = yb;
2012 outline_points[i][2] = z;
2013 glVertex3f (outline_points[i][0],
2014 outline_points[i][1],
2015 outline_points[i][2]);
2020 /* bottom left corner */
2022 glBegin (GL_TRIANGLE_FAN);
2023 glVertex3f (xb, yc, 0);
2024 for (th = M_PI/2; th > -tick; th -= tick)
2026 GLfloat x = xb - corner_radius * cos(th);
2027 GLfloat y = yc - corner_radius * sin(th);
2028 glVertex3f (x, y, z);
2029 outline_points[i][0] = x;
2030 outline_points[i][1] = y;
2031 outline_points[i][2] = z;
2036 glFrontFace(GL_CCW);
2039 outline_points[i][0] = xa;
2040 outline_points[i][1] = yd;
2041 outline_points[i][2] = z;
2047 glVertex3f (xa, yd, z);
2048 glVertex3f (xb, yd, z);
2049 glVertex3f (xb, yc, z);
2050 glVertex3f (xa, yc, z);
2053 glVertex3f (xb, ye, z);
2054 glVertex3f (xc, ye, z);
2055 glVertex3f (xc, yb, z);
2056 glVertex3f (xb, yb, z);
2059 glVertex3f (xc, yd, z);
2060 glVertex3f (xd, yd, z);
2061 glVertex3f (xd, yc, z);
2062 glVertex3f (xc, yc, z);
2069 glBegin (GL_LINE_LOOP);
2071 glVertex3fv (outline_points[--i]);
2074 glDisable (GL_POLYGON_OFFSET_FILL);
2079 draw_label (ModeInfo *mi, walker *f, GLfloat y_off, GLfloat scale,
2082 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2083 int wire = MI_IS_WIREFRAME(mi);
2086 if (scale == 0) return;
2089 glDisable (GL_LIGHTING); /* don't light fonts */
2093 /* First, we translate the origin to the center of the robot.
2095 Then we retrieve the prevailing modelview matrix, which
2096 includes any rotation, wandering, and user-trackball-rolling
2099 We set the top 3x3 cells of that matrix to be the identity
2100 matrix. This removes all rotation from the matrix, while
2101 leaving the translation alone. This has the effect of
2102 leaving the prevailing coordinate system perpendicular to
2103 the camera view: were we to draw a square face, it would
2104 be in the plane of the screen.
2106 Now we translate by `size' toward the viewer -- so that the
2107 origin is *just in front* of the ball.
2109 Then we draw the label text, allowing the depth buffer to
2110 do its work: that way, labels on atoms will be occluded
2111 properly when other atoms move in front of them.
2113 This technique (of neutralizing rotation relative to the
2114 observer, after both rotations and translations have been
2115 applied) is known as "billboarding".
2119 glTranslatef(f->y, 0, f->x); /* get matrix */
2121 glTranslatef (0, y_off, 0);
2123 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]); /* load rot. identity */
2124 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
2125 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
2126 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
2127 glLoadIdentity(); /* reset modelview */
2128 glMultMatrixf (&m[0][0]); /* replace with ours */
2130 glTranslatef (0, 0, 0.1); /* move toward camera */
2132 glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
2136 int cw, ch, w, h, ascent, descent;
2138 GLfloat max = 24; /* max point size to avoid pixellated text */
2140 /* Let the font be much larger on iPhone */
2141 if (mi->xgwa.height <= 640 || mi->xgwa.width <= 640)
2144 texture_string_metrics (bp->font_data, "X", &e, &ascent, &descent);
2146 ch = ascent + descent;
2148 if (ch > max) s *= max/ch;
2152 texture_string_metrics (bp->font_data, label, &e, 0, 0);
2154 h = e.ascent + e.descent;
2157 glTranslatef (-w/2, h*2/3 + (cw * 7), 0);
2160 glTranslatef (0, -h + (ch * 1.2), -0.1);
2161 draw_bubble_box (mi, w, h,
2162 ch * 2, /* corner radius */
2163 ch * 2.5, /* arrow height */
2164 w / 2 - cw * 8, /* arrow x */
2165 bp->text_bd, bp->text_bg);
2168 glColor4fv (bp->text_color);
2169 glTranslatef (0, ch/2, 0);
2170 print_texture_string (bp->font_data, label);
2175 /* More efficient to always call glEnable() with correct values
2176 than to call glPushAttrib()/glPopAttrib(), since reading
2177 attributes from GL does a round-trip and stalls the pipeline.
2180 glEnable (GL_LIGHTING);
2185 fill_words (ModeInfo *mi)
2187 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2188 char *p = bp->words + strlen(bp->words);
2191 int max = bp->max_lines;
2193 /* Fewer lines on iPhone */
2194 if ((mi->xgwa.height <= 640 || mi->xgwa.width <= 640) &&
2198 for (c = bp->words; c < p; c++)
2202 while (p < bp->words + sizeof(bp->words) - 1 &&
2205 int c = textclient_getc (bp->tc);
2220 bubble (ModeInfo *mi)
2222 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2224 GLfloat fade = 0.015;
2225 int chance = (talk_chance <= 0.0 ? 0 :
2226 talk_chance >= 0.99 ? 1 :
2227 (1-talk_chance) * 1000);
2229 char *s0 = strdup (bp->words);
2233 while (*s == '\n') s++;
2235 while (L > 0 && (s[L-1] == '\n' || s[L-1] == ' ' || s[L-1] == '\t'))
2237 if (! *s) goto DONE;
2240 if (debug_p) goto DONE;
2243 if (chance <= 0) goto DONE;
2245 if (bp->bubble_tick > 0)
2248 if (! bp->bubble_tick)
2252 if (! bp->bubble_tick)
2254 if (!(random() % chance))
2255 bp->bubble_tick = duration;
2260 scale = (bp->bubble_tick < duration * fade
2261 ? bp->bubble_tick / (duration * fade)
2262 : (bp->bubble_tick > duration * (1 - fade)
2263 ? 1 - ((bp->bubble_tick - duration * (1 - fade))
2264 / (duration * fade))
2267 draw_label (mi, &bp->walkers[0], 1.5, scale, s);
2272 #endif /* WORDBUBBLES */
2282 cmp_depth_sorter (const void *aa, const void *bb)
2284 const depth_sorter *a = (depth_sorter *) aa;
2285 const depth_sorter *b = (depth_sorter *) bb;
2286 return (a->d == b->d ? 0 : a->d < b->d ? -1 : 1);
2291 draw_robot (ModeInfo *mi)
2293 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2294 Display *dpy = MI_DISPLAY(mi);
2295 Window window = MI_WINDOW(mi);
2297 depth_sorter *sorted;
2300 if (!bp->glx_context)
2303 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
2305 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2310 glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
2313 gltrackball_rotate (bp->user_trackball);
2315 glTranslatef (0, -20, 0); /* Move the horizon down the screen */
2317 robot_size = size * 7;
2318 glScalef (robot_size, robot_size, robot_size);
2319 look_at_center (mi);
2322 glScalef (1/robot_size, 1/robot_size, 1/robot_size);
2323 glCallList (bp->dlists[GROUND]);
2334 glTranslatef (0, -1.2, 0);
2336 glRotatef (-43.5, 0, 0, 1);
2337 glRotatef (-90, 0, 1, 0);
2339 /* glTranslatef (bp->debug_z, bp->debug_y, 0); */
2342 if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
2343 if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
2346 glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
2347 glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
2348 glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
2351 glTranslatef (-0.5, 0, -0.5);
2353 glColor3f (1, 0, 0);
2354 glBegin (GL_LINE_LOOP);
2355 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2356 glVertex3f (0, 1, 1); glVertex3f (0, 1, 0);
2359 glBegin (GL_LINE_LOOP);
2360 glVertex3f (1, 0, 0); glVertex3f (1, 0, 1);
2361 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2365 glColor3f (0.5, 0.5, 0.5);
2366 glFrontFace (GL_CCW);
2368 /* glVertex3f (0, 1, 0); glVertex3f (0, 1, 1); */
2369 /* glVertex3f (1, 1, 1); glVertex3f (1, 1, 0); */
2370 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2371 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2374 glFrontFace (GL_CW);
2376 glVertex3f (0, 1, 0); glVertex3f (0, 1, 1);
2377 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2378 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2379 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2383 glColor3f (1, 0, 0);
2385 glVertex3f (0, 0, 0); glVertex3f (1, 0, 0);
2386 glVertex3f (0, 0, 1); glVertex3f (1, 0, 1);
2387 glVertex3f (0, 1, 0); glVertex3f (1, 1, 0);
2388 glVertex3f (0, 1, 1); glVertex3f (1, 1, 1);
2390 glVertex3f (0, 0, 0); glVertex3f (1, 0, 1);
2391 glVertex3f (0, 0, 1); glVertex3f (1, 0, 0);
2392 glVertex3f (0, 1, 0); glVertex3f (1, 1, 1);
2393 glVertex3f (0, 1, 1); glVertex3f (1, 1, 0);
2396 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
2397 if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
2404 /* For transparency to work right, we have to draw the robots from
2405 back to front, from the perspective of the observer. So project
2406 the origin of the robot to screen coordinates, and sort that by Z.
2408 sorted = (depth_sorter *) calloc (bp->nwalkers, sizeof (*sorted));
2410 GLdouble m[16], p[16];
2412 glGetDoublev (GL_MODELVIEW_MATRIX, m);
2413 glGetDoublev (GL_PROJECTION_MATRIX, p);
2414 glGetIntegerv (GL_VIEWPORT, v);
2416 for (i = 0; i < bp->nwalkers; i++)
2419 walker *f = &bp->walkers[i];
2420 gluProject (f->y, f->z, f->x, m, p, v, &x, &y, &z);
2424 qsort (sorted, i, sizeof(*sorted), cmp_depth_sorter);
2428 mi->polygon_count = 0;
2429 for (i = 0; i < bp->nwalkers; i++)
2431 int ii = sorted[i].i;
2432 walker *f = &bp->walkers[ii];
2433 int ticks = 22 * speed * f->speed;
2438 if (ticks < 1) ticks = 1;
2439 if (ticks > max) ticks = max;
2443 sprintf (tag, "%.4f, %.4f, %.4f",
2444 bp->debug_x, bp->debug_y, bp->debug_z);
2448 /* sprintf (tag + strlen(tag), " %d\n", ii); */
2449 sprintf (tag + strlen(tag), " %d\n", bp->nwalkers - i - 1);
2450 /* sprintf (tag + strlen(tag), "%.03f\n", sorted[i].d); */
2456 mi->polygon_count += draw_walker (mi, f, tag);
2458 for (ii = 0; ii < ticks; ii++)
2459 tick_walker (mi, f);
2465 if (mi->fps_p) do_fps (mi);
2468 glXSwapBuffers(dpy, window);
2472 free_robot (ModeInfo *mi)
2475 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2477 textclient_close (bp->tc);
2481 XSCREENSAVER_MODULE_2 ("WindupRobot", winduprobot, robot)