1 /* winduprobot, Copyright (c) 2014-2017 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 refresh_robot 0
72 # define release_robot 0
74 #define countof(x) (sizeof((x))/sizeof((*x)))
76 #define DEF_SPEED "1.0"
77 #define DEF_ROBOT_SIZE "1.0"
78 #define DEF_TEXTURE "True"
79 #define DEF_FADE "True"
80 #define DEF_OPACITY "1.0"
81 #define DEF_TALK "0.2"
83 #include "xlockmore.h"
84 #include "gltrackball.h"
85 #include "xpm-ximage.h"
90 # include "textclient.h"
96 #ifndef HAVE_JWZGLES /* No SPHERE_MAP on iPhone */
101 # include "../images/chromesphere.xpm"
104 #ifdef USE_GL /* whole file */
108 extern const struct gllist
109 *robot_arm_half, *robot_body_half_outside, *robot_body_half_inside,
110 *robot_crank_full, *robot_gearbox_half, *robot_hand_half,
111 *robot_leg_half, *robot_rotator_half, *robot_wireframe;
113 static struct gllist *robot_dome = 0, *robot_gear = 0, *ground = 0;
115 static const struct gllist * const *all_objs[] = {
116 &robot_arm_half, &robot_body_half_outside, &robot_body_half_inside,
117 &robot_crank_full, &robot_gearbox_half, &robot_hand_half,
118 &robot_leg_half, &robot_rotator_half, &robot_wireframe,
119 (const struct gllist * const *) &robot_dome,
120 (const struct gllist * const *) &robot_gear,
121 (const struct gllist * const *) &ground
125 #define ROBOT_BODY_1 1
126 #define ROBOT_BODY_2 2
127 #define ROBOT_CRANK 3
128 #define ROBOT_GEARBOX 4
131 #define ROBOT_ROTATOR 7
132 #define ROBOT_WIREFRAME 8
134 #define ROBOT_GEAR 10
138 GLfloat x, y, z; /* position */
139 GLfloat facing; /* direction of front of robot, degrees */
140 GLfloat pitch; /* front/back tilt angle, degrees */
141 GLfloat roll; /* left/right tilt angle, degrees */
142 GLfloat speed; /* some robots are faster */
143 GLfloat crank_rot; /* gear state, degrees */
144 GLfloat hand_rot[2]; /* rotation of the hands, degrees */
145 GLfloat hand_pos[2]; /* openness of the hands, ratio */
146 GLfloat balance; /* how off-true does it walk? degrees */
147 GLfloat body_transparency; /* ratio */
148 int fading_p; /* -1, 0, 1 */
153 GLXContext *glx_context;
154 trackball_state *user_trackball;
158 GLfloat component_colors[countof(all_objs)][4];
163 GLfloat looking_x, looking_y, looking_z; /* Where camera is aimed */
164 GLfloat olooking_x, olooking_y, olooking_z; /* Where camera was aimed */
165 Bool camera_tracking_p; /* Whether camera in motion */
166 GLfloat tracking_ratio;
169 GLuint chrome_texture;
173 texture_font_data *font_data;
177 int lines, max_lines;
179 GLfloat text_color[4], text_bg[4], text_bd[4];
181 # endif /* WORDBUBBLES */
185 GLfloat debug_x, debug_y, debug_z;
188 } robot_configuration;
190 static robot_configuration *bps = NULL;
192 static GLfloat speed, size, opacity;
193 static int do_texture, do_fade;
195 static GLfloat talk_chance;
201 static XrmOptionDescRec opts[] = {
202 { "-speed", ".speed", XrmoptionSepArg, 0 },
203 { "-size", ".robotSize", XrmoptionSepArg, 0 },
204 { "-opacity", ".opacity", XrmoptionSepArg, 0 },
205 { "-talk", ".talk", XrmoptionSepArg, 0 },
206 {"-texture", ".texture", XrmoptionNoArg, "True" },
207 {"+texture", ".texture", XrmoptionNoArg, "False" },
208 {"-fade", ".fade", XrmoptionNoArg, "True" },
209 {"+fade", ".fade", XrmoptionNoArg, "False" },
211 {"-debug", ".debug", XrmoptionNoArg, "True" },
212 {"+debug", ".debug", XrmoptionNoArg, "False" },
216 static argtype vars[] = {
217 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
218 {&size, "robotSize", "RobotSize", DEF_ROBOT_SIZE, t_Float},
219 {&opacity, "opacity", "Opacity", DEF_OPACITY, t_Float},
220 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
221 {&do_fade, "fade", "Fade", DEF_FADE, t_Bool},
223 {&talk_chance, "talk", "Talk", DEF_TALK, t_Float},
226 {&debug_p, "debug", "Debug", "False", t_Bool},
230 ENTRYPOINT ModeSpecOpt robot_opts = {
231 countof(opts), opts, countof(vars), vars, NULL};
234 /* Window management, etc
237 reshape_robot (ModeInfo *mi, int width, int height)
239 GLfloat h = (GLfloat) height / (GLfloat) width;
241 glViewport (0, 0, width, height);
243 glMatrixMode(GL_PROJECTION);
245 gluPerspective (40.0, 1/h, 1.0, 250);
247 glMatrixMode(GL_MODELVIEW);
253 glClear(GL_COLOR_BUFFER_BIT);
257 robot_configuration *bp = &bps[MI_SCREEN(mi)];
258 int w = (width < 800 ? 25 : 40);
261 textclient_reshape (bp->tc, w, h, w, h,
262 /* Passing bp->max_lines isn't actually necessary */
271 robot_handle_event (ModeInfo *mi, XEvent *event)
273 robot_configuration *bp = &bps[MI_SCREEN(mi)];
275 if (gltrackball_event_handler (event, bp->user_trackball,
276 MI_WIDTH (mi), MI_HEIGHT (mi),
280 else if (event->xany.type == KeyPress && debug_p)
284 double n[3] = { 1.0, 0.1, 0.1 };
285 int s = (event->xkey.state & ShiftMask ? 10 : 1);
287 XLookupString (&event->xkey, &c, 1, &keysym, 0);
289 if (keysym == XK_Right) bp->debug_x += n[0] * s;
290 else if (keysym == XK_Left) bp->debug_x -= n[0] * s;
291 else if (keysym == XK_Up) bp->debug_y += n[1] * s;
292 else if (keysym == XK_Down) bp->debug_y -= n[1] * s;
293 else if (c == '=' || c == '+') bp->debug_z += n[2] * s;
294 else if (c == '-' || c == '_') bp->debug_z -= n[2] * s;
295 else if (c == ' ') bp->debug_x = bp->debug_y = bp->debug_z = 0;
296 else if (c == '\n' || c == '\r')
297 fprintf (stderr, "%.4f %.4f %.4f\n",
298 bp->debug_x, bp->debug_y, bp->debug_z);
311 load_textures (ModeInfo *mi)
313 robot_configuration *bp = &bps[MI_SCREEN(mi)];
316 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
320 glGenTextures (1, &bp->chrome_texture);
321 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
322 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
323 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
324 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
325 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
326 xi->width, xi->height, 0,
329 GL_UNSIGNED_INT_8_8_8_8_REV,
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 *);
347 static void free_robot (ModeInfo *mi);
350 parse_color (ModeInfo *mi, char *key, GLfloat color[4])
353 char *string = get_string_resource (mi->dpy, key, "RobotColor");
354 if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
356 fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
361 color[0] = xcolor.red / 65536.0;
362 color[1] = xcolor.green / 65536.0;
363 color[2] = xcolor.blue / 65536.0;
369 init_robot (ModeInfo *mi)
371 robot_configuration *bp;
372 int wire = MI_IS_WIREFRAME(mi);
374 MI_INIT (mi, bps, free_robot);
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};
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 */
594 /* Let's tilt the floor a little. */
595 gltrackball_reset (bp->user_trackball,
602 draw_component (ModeInfo *mi, int i)
604 robot_configuration *bp = &bps[MI_SCREEN(mi)];
605 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
606 bp->component_colors[i]);
607 glCallList (bp->dlists[i]);
608 return (*all_objs[i])->points / 3;
612 draw_transparent_component (ModeInfo *mi, int i, GLfloat alpha)
614 robot_configuration *bp = &bps[MI_SCREEN(mi)];
615 int wire = MI_IS_WIREFRAME(mi);
618 if (alpha < 0) return 0;
619 if (alpha > 1) alpha = 1;
620 bp->component_colors[i][3] = alpha;
622 if (wire || alpha >= 1)
623 return draw_component (mi, i);
625 /* Draw into the depth buffer but not the frame buffer */
626 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
627 count += draw_component (mi, i);
629 /* Now draw into the frame buffer only where there's already depth */
630 glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
631 glDepthFunc (GL_EQUAL);
633 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
635 count += draw_component (mi, i);
636 glDepthFunc (GL_LESS);
637 glDisable (GL_BLEND);
644 draw_arm_half (ModeInfo *mi, walker *f)
648 count += draw_transparent_component (mi, ROBOT_ARM, f->body_transparency);
654 draw_hand_half (ModeInfo *mi, walker *f)
658 count += draw_transparent_component (mi, ROBOT_HAND, f->body_transparency);
664 /* rotation of arm: 0-360.
665 openness of fingers: 0.0 - 1.0.
668 draw_arm (ModeInfo *mi, walker *f, GLfloat left_p, GLfloat rot, GLfloat open)
671 GLfloat arm_x = 4766; /* distance from origin to arm axis */
672 GLfloat arm_y = 12212;
674 open *= 5.5; /* scale of finger range */
677 if (debug_p) rot = 0;
683 glTranslatef (0, 0, arm_x * 2);
685 glTranslatef (0, arm_y, -arm_x); /* move to origin */
686 glRotatef (rot, 1, 0, 0);
687 glTranslatef (0, -arm_y, arm_x); /* move back */
690 count += draw_arm_half (mi, f);
693 glTranslatef (0, -arm_y * 2, 0);
695 count += draw_arm_half (mi, f);
698 glTranslatef (0, 0, -arm_x * 2);
701 count += draw_arm_half (mi, f);
704 glTranslatef (0, -arm_y * 2, 0);
706 count += draw_arm_half (mi, f);
709 glTranslatef (0, 0, open);
711 count += draw_hand_half (mi, f);
713 glTranslatef (0, 0, -open);
715 glTranslatef (0, 0, arm_x * 2);
717 glTranslatef (0, 0, open);
718 count += draw_hand_half (mi, f);
726 draw_body_half (ModeInfo *mi, walker *f, Bool inside_p)
729 int which = (inside_p ? ROBOT_BODY_2 : ROBOT_BODY_1);
731 count += draw_transparent_component (mi, which, f->body_transparency);
738 draw_body (ModeInfo *mi, walker *f, Bool inside_p)
744 count += draw_body_half (mi, f, inside_p);
748 count += draw_body_half (mi, f, inside_p);
757 draw_gearbox_half (ModeInfo *mi)
761 count += draw_component (mi, ROBOT_GEARBOX);
768 draw_gearbox (ModeInfo *mi)
774 count += draw_gearbox_half (mi);
778 count += draw_gearbox_half (mi);
786 unit_gear (ModeInfo *mi, GLfloat color[4])
788 int wire = MI_IS_WIREFRAME(mi);
796 g->thickness2 = g->thickness * 0.5;
797 g->thickness3 = g->thickness;
798 g->inner_r = g->r * 0.7;
799 g->inner_r2 = g->r * 0.4;
800 g->inner_r3 = g->r * 0.1;
801 g->size = INVOLUTE_LARGE;
803 g->color[0] = g->color2[0] = color[0];
804 g->color[1] = g->color2[1] = color[1];
805 g->color[2] = g->color2[2] = color[2];
806 g->color[3] = g->color2[3] = color[3];
808 return draw_involute_gear (g, wire);
813 draw_gear (ModeInfo *mi)
818 count += draw_component (mi, ROBOT_GEAR);
824 draw_crank (ModeInfo *mi, walker *f, GLfloat rot)
827 GLfloat origin = 12210.0;
833 glTranslatef (0, origin, 0); /* position at origin */
834 glRotatef (rot, 0, 0, 1);
837 glRotatef (90, 1, 0, 0);
838 count += draw_gear (mi);
841 glTranslatef (0, -origin, 0); /* move back */
844 count += draw_component (mi, ROBOT_CRANK);
853 draw_rotator_half (ModeInfo *mi)
857 count += draw_component (mi, ROBOT_ROTATOR);
864 draw_rotator (ModeInfo *mi, walker *f, GLfloat rot)
867 GLfloat origin = 10093.0;
871 glTranslatef (0, origin, 0); /* position at origin */
872 glRotatef (rot, 0, 0, 1);
875 glRotatef (90, 1, 0, 0);
876 count += draw_gear (mi);
882 glDisable(GL_LIGHTING);
884 glVertex3f(0, 0, -3000);
885 glVertex3f(0, 0, 3000);
887 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
891 glTranslatef (0, -origin, 0); /* move back */
894 count += draw_rotator_half (mi);
898 glRotatef (180, 0, 0, 1);
899 glTranslatef (0, -origin * 2, 0); /* move back */
900 count += draw_rotator_half (mi);
908 draw_leg_half (ModeInfo *mi)
912 count += draw_component (mi, ROBOT_LEG);
919 draw_wireframe (ModeInfo *mi, walker *f)
921 int wire = MI_IS_WIREFRAME(mi);
923 GLfloat alpha = 0.6 - f->body_transparency;
924 if (alpha < 0) return 0;
926 if (!wire) glDisable (GL_LIGHTING);
928 count += draw_transparent_component (mi, ROBOT_WIREFRAME, alpha);
930 if (!wire) glEnable (GL_LIGHTING);
936 draw_leg (ModeInfo *mi, GLfloat rot, Bool left_p)
940 GLfloat leg_distance = 9401; /* distance from ground to leg axis */
941 GLfloat rot_distance = 10110; /* distance from ground to rotator axis */
942 GLfloat pin_distance = 14541; /* distance from ground to stop pin */
943 GLfloat orbit_r = rot_distance - leg_distance; /* radius of rotation */
945 /* Actually it's the bottom of the pin minus its diameter, or something. */
951 glRotatef (180, 0, 1, 0);
953 if (!left_p) rot = -(rot + 180);
957 x = orbit_r * cos (-rot * M_PI / 180);
958 y = orbit_r * sin (-rot * M_PI / 180);
961 /* Rotate the leg by angle B of the right A
962 triangle ABC, where: /:
964 A = position of stop pin / :
965 D = position of rotator wheel's axis , - ~ ~ ~ - ,
966 C = D + y , ' / : ' ,
967 B = D + xy (leg's axis) , / : ,
970 H = dist(A,B) , / D ,
971 O = dist(A,C) , / : ,
972 sin(th) = O/H , / : ,
973 th = asin(O/H) B ~ ~ ~ ~ ~ C .
977 GLfloat Ay = pin_distance - leg_distance;
978 GLfloat Cx = 0, Cy = y;
980 GLfloat dBC = Cx - Bx;
981 GLfloat dAC = Cy - Ay;
982 GLfloat dAB = sqrt (dBC*dBC + dAC*dAC);
983 GLfloat th = asin (dAC / dAB);
984 rot = th / (M_PI / 180.0);
986 if (dBC > 0) rot = 360-rot;
989 glTranslatef (0, orbit_r, 0); /* position on rotator axis */
990 glTranslatef (x, y, 0);
992 glTranslatef (0, leg_distance, 0); /* position on leg axis */
993 glRotatef(rot, 0, 0, 1);
994 glTranslatef (0, -leg_distance, 0); /* move back */
997 count += draw_leg_half (mi);
1001 count += draw_leg_half (mi);
1009 draw_dome (ModeInfo *mi, walker *f)
1011 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1012 int wire = MI_IS_WIREFRAME(mi);
1013 int which = ROBOT_DOME;
1016 GLfloat trans = f->body_transparency;
1018 GLfloat dome_y = 15290;
1020 if (trans < 0) trans = 0;
1021 if (trans > max) trans = max;
1023 if (!wire) glEnable (GL_BLEND);
1026 glTranslatef (0, dome_y, 0);
1027 glScalef (100, 100, 100);
1028 glRotatef (90, 1, 0, 0);
1029 glTranslatef (0.35, 0, 0);
1031 glFrontFace(GL_CCW);
1032 bp->component_colors[which][3] = trans;
1033 count += draw_component (mi, which);
1036 if (!wire) glDisable (GL_BLEND);
1042 /* Is this robot overlapping any other?
1045 collision_p (ModeInfo *mi, walker *w, GLfloat extra_space)
1047 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1049 if (MI_COUNT(mi) <= 1) return False;
1051 for (i = 0; i < MI_COUNT(mi); i++)
1053 walker *w2 = &bp->walkers[i];
1054 GLfloat min = 0.75 + extra_space;
1056 if (w == w2) continue;
1059 d = (dx*dx + dy*dy);
1060 if (d <= min*min) return True;
1066 /* I couldn't figure out a straightforward trig solution to the
1067 forward/backward tilting that happens as weight passes from one
1068 foot to another, so I just built a tool to eyeball it manually.
1069 { vertical_translation, rotation, forward_translation }
1071 static const struct { GLfloat up, rot, fwd; } wobble_profile[360] = {
1072 { 0.0000, 0.00, 0.0000 }, /* 0 */
1073 { 0.0000, -0.01, 0.0025 },
1074 { 0.0000, -0.25, 0.0040 },
1075 { 0.0000, -0.41, 0.0060 },
1076 { 0.0000, -0.62, 0.0080 },
1077 { 0.0000, -0.80, 0.0095 },
1078 { 0.0000, -0.90, 0.0120 },
1079 { 0.0000, -1.10, 0.0135 },
1080 { 0.0000, -1.25, 0.0150 },
1081 { 0.0000, -1.40, 0.0175 },
1082 { 0.0000, -1.50, 0.0195 }, /* 10 */
1083 { 0.0000, -1.70, 0.0215 },
1084 { 0.0000, -1.80, 0.0230 },
1085 { 0.0000, -2.00, 0.0250 },
1086 { 0.0000, -2.10, 0.0270 },
1087 { -0.0005, -2.30, 0.0290 },
1088 { -0.0005, -2.50, 0.0305 },
1089 { -0.0005, -2.60, 0.0330 },
1090 { -0.0005, -2.70, 0.0330 },
1091 { -0.0005, -2.90, 0.0350 },
1092 { -0.0005, -3.00, 0.0365 }, /* 20 */
1093 { -0.0010, -3.20, 0.0380 },
1094 { -0.0005, -3.30, 0.0400 },
1095 { -0.0010, -3.50, 0.0420 },
1096 { -0.0010, -3.70, 0.0440 },
1097 { -0.0010, -3.80, 0.0460 },
1098 { -0.0010, -3.90, 0.0470 },
1099 { -0.0015, -4.10, 0.0500 },
1100 { -0.0015, -4.20, 0.0515 },
1101 { -0.0015, -4.40, 0.0535 },
1102 { -0.0015, -4.50, 0.0550 }, /* 30 */
1103 { -0.0015, -4.60, 0.0565 },
1104 { -0.0020, -4.80, 0.0585 },
1105 { -0.0020, -5.00, 0.0600 },
1106 { -0.0020, -5.10, 0.0620 },
1107 { -0.0025, -5.20, 0.0635 },
1108 { -0.0025, -5.40, 0.0655 },
1109 { -0.0025, -5.50, 0.0675 },
1110 { -0.0025, -5.60, 0.0690 },
1111 { -0.0030, -5.80, 0.0710 },
1112 { -0.0030, -5.90, 0.0720 }, /* 40 */
1113 { -0.0030, -6.00, 0.0740 },
1114 { -0.0035, -6.10, 0.0760 },
1115 { -0.0035, -6.30, 0.0790 },
1116 { -0.0040, -6.40, 0.0805 },
1117 { -0.0040, -6.50, 0.0820 },
1118 { -0.0040, -6.60, 0.0835 },
1119 { -0.0045, -6.80, 0.0855 },
1120 { -0.0045, -6.90, 0.0870 },
1121 { -0.0050, -7.00, 0.0885 },
1122 { -0.0050, -7.20, 0.0900 }, /* 50 */
1123 { -0.0050, -7.20, 0.0915 },
1124 { -0.0055, -7.40, 0.0930 },
1125 { -0.0055, -7.50, 0.0945 },
1126 { -0.0060, -7.60, 0.0960 },
1127 { -0.0060, -7.70, 0.0970 },
1128 { -0.0065, -7.80, 0.0985 },
1129 { -0.0060, -7.70, 0.0995 },
1130 { -0.0060, -7.60, 0.1010 },
1131 { -0.0060, -7.50, 0.1020 },
1132 { -0.0055, -7.30, 0.1030 }, /* 60 */
1133 { -0.0050, -7.10, 0.1040 },
1134 { -0.0050, -6.90, 0.1050 },
1135 { -0.0045, -6.80, 0.1065 },
1136 { -0.0045, -6.50, 0.1075 },
1137 { -0.0040, -6.40, 0.1085 },
1138 { -0.0040, -6.20, 0.1095 },
1139 { -0.0040, -6.00, 0.1105 },
1140 { -0.0035, -5.80, 0.1115 },
1141 { -0.0030, -5.50, 0.1125 },
1142 { -0.0030, -5.40, 0.1135 }, /* 70 */
1143 { -0.0030, -5.10, 0.1145 },
1144 { -0.0030, -4.90, 0.1150 },
1145 { -0.0025, -4.70, 0.1160 },
1146 { -0.0025, -4.40, 0.1165 },
1147 { -0.0025, -4.20, 0.1175 },
1148 { -0.0020, -3.90, 0.1180 },
1149 { -0.0020, -3.70, 0.1185 },
1150 { -0.0020, -3.40, 0.1190 },
1151 { -0.0020, -3.10, 0.1195 },
1152 { -0.0020, -2.90, 0.1200 }, /* 80 */
1153 { -0.0015, -2.60, 0.1200 },
1154 { -0.0015, -2.30, 0.1205 },
1155 { -0.0015, -2.00, 0.1210 },
1156 { -0.0015, -1.80, 0.1215 },
1157 { -0.0015, -1.50, 0.1215 },
1158 { -0.0015, -1.20, 0.1215 },
1159 { -0.0015, -0.90, 0.1215 },
1160 { -0.0015, -0.60, 0.1215 },
1161 { -0.0015, -0.30, 0.1215 },
1162 { -0.0010, 0.00, 0.1215 }, /* 90 */
1163 { -0.0010, 0.30, 0.1215 },
1164 { -0.0010, 0.60, 0.1215 },
1165 { -0.0010, 0.90, 0.1215 },
1166 { -0.0010, 1.20, 0.1215 },
1167 { -0.0015, 1.40, 0.1215 },
1168 { -0.0015, 1.70, 0.1215 },
1169 { -0.0015, 2.00, 0.1215 },
1170 { -0.0015, 2.30, 0.1215 },
1171 { -0.0015, 2.60, 0.1215 },
1172 { -0.0015, 2.80, 0.1220 }, /* 100 */
1173 { -0.0020, 3.10, 0.1225 },
1174 { -0.0020, 3.30, 0.1230 },
1175 { -0.0020, 3.60, 0.1235 },
1176 { -0.0020, 3.90, 0.1240 },
1177 { -0.0025, 4.10, 0.1245 },
1178 { -0.0025, 4.40, 0.1250 },
1179 { -0.0025, 4.60, 0.1260 },
1180 { -0.0025, 4.90, 0.1265 },
1181 { -0.0030, 5.10, 0.1275 },
1182 { -0.0030, 5.30, 0.1285 }, /* 110 */
1183 { -0.0035, 5.60, 0.1290 },
1184 { -0.0035, 5.80, 0.1300 },
1185 { -0.0035, 6.00, 0.1310 },
1186 { -0.0040, 6.20, 0.1325 },
1187 { -0.0040, 6.40, 0.1335 },
1188 { -0.0045, 6.60, 0.1345 },
1189 { -0.0045, 6.70, 0.1355 },
1190 { -0.0050, 6.90, 0.1365 },
1191 { -0.0050, 7.10, 0.1375 },
1192 { -0.0055, 7.30, 0.1390 }, /* 120 */
1193 { -0.0055, 7.40, 0.1400 },
1194 { -0.0060, 7.50, 0.1415 },
1195 { -0.0065, 8.00, 0.1425 },
1196 { -0.0065, 7.80, 0.1440 },
1197 { -0.0060, 7.80, 0.1455 },
1198 { -0.0060, 7.60, 0.1470 },
1199 { -0.0055, 7.50, 0.1485 },
1200 { -0.0055, 7.40, 0.1500 },
1201 { -0.0050, 7.30, 0.1515 },
1202 { -0.0050, 7.20, 0.1530 }, /* 130 */
1203 { -0.0050, 7.00, 0.1545 },
1204 { -0.0045, 6.90, 0.1560 },
1205 { -0.0045, 6.80, 0.1575 },
1206 { -0.0040, 6.70, 0.1590 },
1207 { -0.0040, 6.50, 0.1605 },
1208 { -0.0040, 6.40, 0.1625 },
1209 { -0.0035, 6.30, 0.1640 },
1210 { -0.0035, 6.10, 0.1655 },
1211 { -0.0030, 6.10, 0.1670 },
1212 { -0.0030, 5.90, 0.1690 }, /* 140 */
1213 { -0.0030, 5.70, 0.1705 },
1214 { -0.0025, 5.70, 0.1720 },
1215 { -0.0025, 5.50, 0.1740 },
1216 { -0.0025, 5.40, 0.1755 },
1217 { -0.0025, 5.20, 0.1775 },
1218 { -0.0020, 5.10, 0.1790 },
1219 { -0.0020, 5.00, 0.1810 },
1220 { -0.0020, 4.80, 0.1825 },
1221 { -0.0015, 4.70, 0.1840 },
1222 { -0.0015, 4.60, 0.1860 }, /* 150 */
1223 { -0.0015, 4.40, 0.1880 },
1224 { -0.0015, 4.20, 0.1900 },
1225 { -0.0015, 4.10, 0.1915 },
1226 { -0.0010, 4.00, 0.1935 },
1227 { -0.0010, 3.80, 0.1955 },
1228 { -0.0010, 3.70, 0.1970 },
1229 { -0.0010, 3.50, 0.1990 },
1230 { -0.0005, 3.40, 0.2010 },
1231 { -0.0010, 3.20, 0.2025 },
1232 { -0.0005, 3.10, 0.2045 }, /* 160 */
1233 { -0.0005, 2.90, 0.2065 },
1234 { -0.0005, 2.80, 0.2085 },
1235 { -0.0005, 2.60, 0.2105 },
1236 { -0.0005, 2.50, 0.2120 },
1237 { -0.0005, 2.30, 0.2140 },
1238 { -0.0005, 2.20, 0.2160 },
1239 { -0.0005, 2.00, 0.2180 },
1240 { 0.0000, 1.90, 0.2200 },
1241 { 0.0000, 1.70, 0.2220 },
1242 { 0.0000, 1.60, 0.2235 }, /* 170 */
1243 { 0.0000, 1.40, 0.2255 },
1244 { 0.0000, 1.30, 0.2275 },
1245 { 0.0000, 1.10, 0.2295 },
1246 { 0.0000, 0.90, 0.2315 },
1247 { 0.0000, 0.80, 0.2335 },
1248 { 0.0000, 0.60, 0.2355 },
1249 { 0.0000, 0.50, 0.2375 },
1250 { 0.0000, 0.30, 0.2395 },
1251 { 0.0000, 0.10, 0.2415 },
1252 { 0.0000, 0.00, 0.2430 }, /* 180 */
1253 { 0.0000, -0.10, 0.2450 },
1254 { 0.0000, -0.30, 0.2470 },
1255 { 0.0000, -0.40, 0.2490 },
1256 { 0.0000, -0.60, 0.2510 },
1257 { 0.0000, -0.80, 0.2530 },
1258 { 0.0000, -0.90, 0.2550 },
1259 { 0.0000, -1.10, 0.2570 },
1260 { 0.0000, -1.20, 0.2590 },
1261 { 0.0000, -1.40, 0.2610 },
1262 { 0.0000, -1.50, 0.2625 }, /* 190 */
1263 { 0.0000, -1.70, 0.2645 },
1264 { 0.0000, -1.80, 0.2665 },
1265 { -0.0005, -2.00, 0.2685 },
1266 { -0.0005, -2.10, 0.2705 },
1267 { -0.0005, -2.30, 0.2725 },
1268 { -0.0005, -2.40, 0.2740 },
1269 { -0.0005, -2.60, 0.2760 },
1270 { -0.0005, -2.80, 0.2780 },
1271 { -0.0005, -2.90, 0.2800 },
1272 { -0.0005, -3.00, 0.2820 }, /* 200 */
1273 { -0.0010, -3.20, 0.2835 },
1274 { -0.0005, -3.30, 0.2855 },
1275 { -0.0010, -3.50, 0.2875 },
1276 { -0.0010, -3.70, 0.2895 },
1277 { -0.0010, -3.80, 0.2910 },
1278 { -0.0010, -3.90, 0.2930 },
1279 { -0.0010, -4.00, 0.2950 },
1280 { -0.0015, -4.20, 0.2965 },
1281 { -0.0015, -4.40, 0.2985 },
1282 { -0.0015, -4.50, 0.3000 }, /* 210 */
1283 { -0.0015, -4.60, 0.3020 },
1284 { -0.0020, -4.80, 0.3040 },
1285 { -0.0020, -5.00, 0.3055 },
1286 { -0.0020, -5.00, 0.3075 },
1287 { -0.0025, -5.20, 0.3090 },
1288 { -0.0025, -5.30, 0.3110 },
1289 { -0.0025, -5.50, 0.3125 },
1290 { -0.0025, -5.60, 0.3140 },
1291 { -0.0030, -5.70, 0.3160 },
1292 { -0.0030, -5.90, 0.3175 }, /* 220 */
1293 { -0.0030, -6.00, 0.3190 },
1294 { -0.0035, -6.10, 0.3210 },
1295 { -0.0035, -6.30, 0.3225 },
1296 { -0.0040, -6.40, 0.3240 },
1297 { -0.0040, -6.50, 0.3255 },
1298 { -0.0040, -6.60, 0.3270 },
1299 { -0.0045, -6.80, 0.3290 },
1300 { -0.0045, -6.90, 0.3305 },
1301 { -0.0050, -7.00, 0.3320 },
1302 { -0.0050, -7.20, 0.3335 }, /* 230 */
1303 { -0.0050, -7.20, 0.3350 },
1304 { -0.0055, -7.40, 0.3365 },
1305 { -0.0055, -7.50, 0.3380 },
1306 { -0.0060, -7.60, 0.3390 },
1307 { -0.0060, -7.70, 0.3405 },
1308 { -0.0065, -7.80, 0.3420 },
1309 { -0.0060, -7.60, 0.3425 },
1310 { -0.0060, -7.50, 0.3440 },
1311 { -0.0055, -7.40, 0.3455 },
1312 { -0.0055, -7.20, 0.3470 }, /* 240 */
1313 { -0.0050, -7.10, 0.3480 },
1314 { -0.0050, -6.90, 0.3490 },
1315 { -0.0045, -6.80, 0.3500 },
1316 { -0.0045, -6.50, 0.3510 },
1317 { -0.0040, -6.40, 0.3520 },
1318 { -0.0040, -6.10, 0.3535 },
1319 { -0.0035, -6.00, 0.3545 },
1320 { -0.0035, -5.80, 0.3550 },
1321 { -0.0030, -5.50, 0.3560 },
1322 { -0.0030, -5.30, 0.3570 }, /* 250 */
1323 { -0.0030, -5.10, 0.3580 },
1324 { -0.0030, -4.90, 0.3585 },
1325 { -0.0025, -4.70, 0.3595 },
1326 { -0.0025, -4.40, 0.3600 },
1327 { -0.0020, -4.10, 0.3610 },
1328 { -0.0020, -3.90, 0.3615 },
1329 { -0.0020, -3.70, 0.3620 },
1330 { -0.0020, -3.30, 0.3625 },
1331 { -0.0020, -3.10, 0.3630 },
1332 { -0.0015, -2.80, 0.3635 }, /* 260 */
1333 { -0.0015, -2.60, 0.3640 },
1334 { -0.0015, -2.40, 0.3645 },
1335 { -0.0015, -2.00, 0.3645 },
1336 { -0.0015, -1.80, 0.3650 },
1337 { -0.0015, -1.40, 0.3650 },
1338 { -0.0015, -1.20, 0.3655 },
1339 { -0.0010, -0.90, 0.3655 },
1340 { -0.0010, -0.60, 0.3655 },
1341 { -0.0010, -0.30, 0.3655 },
1342 { -0.0010, 0.00, 0.3655 }, /* 270 */
1343 { -0.0010, 0.30, 0.3655 },
1344 { -0.0010, 0.60, 0.3655 },
1345 { -0.0010, 0.90, 0.3655 },
1346 { -0.0015, 1.10, 0.3655 },
1347 { -0.0015, 1.40, 0.3655 },
1348 { -0.0015, 1.70, 0.3655 },
1349 { -0.0015, 1.90, 0.3660 },
1350 { -0.0015, 2.20, 0.3660 },
1351 { -0.0015, 2.50, 0.3665 },
1352 { -0.0015, 2.80, 0.3670 }, /* 280 */
1353 { -0.0015, 3.10, 0.3675 },
1354 { -0.0020, 3.40, 0.3680 },
1355 { -0.0020, 3.70, 0.3685 },
1356 { -0.0020, 3.90, 0.3690 },
1357 { -0.0025, 4.10, 0.3695 },
1358 { -0.0025, 4.40, 0.3700 },
1359 { -0.0025, 4.60, 0.3710 },
1360 { -0.0025, 4.80, 0.3715 },
1361 { -0.0025, 5.00, 0.3730 },
1362 { -0.0030, 5.40, 0.3735 }, /* 290 */
1363 { -0.0035, 5.60, 0.3745 },
1364 { -0.0035, 5.80, 0.3755 },
1365 { -0.0035, 6.00, 0.3765 },
1366 { -0.0040, 6.20, 0.3775 },
1367 { -0.0045, 6.50, 0.3785 },
1368 { -0.0045, 6.60, 0.3795 },
1369 { -0.0045, 6.80, 0.3805 },
1370 { -0.0050, 7.00, 0.3815 },
1371 { -0.0050, 7.10, 0.3825 },
1372 { -0.0055, 7.20, 0.3840 }, /* 300 */
1373 { -0.0055, 7.40, 0.3850 },
1374 { -0.0060, 7.50, 0.3865 },
1375 { -0.0060, 7.70, 0.3875 },
1376 { -0.0065, 7.80, 0.3890 },
1377 { -0.0060, 7.80, 0.3900 },
1378 { -0.0060, 7.60, 0.3915 },
1379 { -0.0055, 7.60, 0.3930 },
1380 { -0.0055, 7.40, 0.3945 },
1381 { -0.0050, 7.30, 0.3960 },
1382 { -0.0050, 7.20, 0.3975 }, /* 310 */
1383 { -0.0050, 7.00, 0.3990 },
1384 { -0.0045, 6.90, 0.4005 },
1385 { -0.0045, 6.80, 0.4020 },
1386 { -0.0040, 6.70, 0.4035 },
1387 { -0.0040, 6.60, 0.4050 },
1388 { -0.0040, 6.40, 0.4065 },
1389 { -0.0035, 6.30, 0.4085 },
1390 { -0.0035, 6.20, 0.4100 },
1391 { -0.0030, 6.10, 0.4115 },
1392 { -0.0030, 5.90, 0.4130 }, /* 320 */
1393 { -0.0030, 5.80, 0.4150 },
1394 { -0.0025, 5.70, 0.4165 },
1395 { -0.0025, 5.50, 0.4180 },
1396 { -0.0025, 5.40, 0.4200 },
1397 { -0.0025, 5.20, 0.4215 },
1398 { -0.0020, 5.10, 0.4235 },
1399 { -0.0020, 5.00, 0.4250 },
1400 { -0.0020, 4.80, 0.4270 },
1401 { -0.0015, 4.70, 0.4285 },
1402 { -0.0015, 4.60, 0.4305 }, /* 330 */
1403 { -0.0015, 4.40, 0.4325 },
1404 { -0.0015, 4.20, 0.4340 },
1405 { -0.0015, 4.10, 0.4360 },
1406 { -0.0010, 4.00, 0.4375 },
1407 { -0.0010, 3.80, 0.4395 },
1408 { -0.0010, 3.70, 0.4415 },
1409 { -0.0010, 3.50, 0.4435 },
1410 { -0.0005, 3.40, 0.4450 },
1411 { -0.0010, 3.20, 0.4470 },
1412 { -0.0005, 3.10, 0.4490 }, /* 340 */
1413 { -0.0005, 2.90, 0.4510 },
1414 { -0.0005, 2.80, 0.4525 },
1415 { -0.0005, 2.60, 0.4545 },
1416 { -0.0005, 2.40, 0.4565 },
1417 { -0.0005, 2.30, 0.4585 },
1418 { -0.0005, 2.20, 0.4605 },
1419 { -0.0005, 2.00, 0.4620 },
1420 { 0.0000, 1.90, 0.4640 },
1421 { 0.0000, 1.70, 0.4660 },
1422 { 0.0000, 1.60, 0.4680 }, /* 350 */
1423 { 0.0000, 1.40, 0.4700 },
1424 { 0.0000, 1.20, 0.4720 },
1425 { 0.0000, 1.10, 0.4740 },
1426 { 0.0000, 0.90, 0.4760 },
1427 { 0.0000, 0.80, 0.4780 },
1428 { 0.0000, 0.60, 0.4795 },
1429 { 0.0000, 0.50, 0.4815 },
1430 { 0.0000, 0.30, 0.4835 },
1431 { 0.0000, 0.20, 0.4855 }, /* 359 */
1435 /* Turn the crank by 1 degree, which moves the legs and displaces the robot.
1438 tick_walker (ModeInfo *mi, walker *f)
1440 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1444 if (bp->button_down_p) return;
1447 deg = ((int) (f->crank_rot + 0.5)) % 360;
1452 f->crank_rot = bp->debug_x;
1453 f->pitch = wobble_profile[deg].rot;
1454 f->z = wobble_profile[deg].up;
1461 fwd = wobble_profile[deg].fwd;
1462 f->pitch = wobble_profile[deg].rot;
1463 f->z = wobble_profile[deg].up;
1467 fwd = (wobble_profile[deg].fwd - wobble_profile[deg-1].fwd);
1468 f->pitch += (wobble_profile[deg].rot - wobble_profile[deg-1].rot);
1469 f->z += (wobble_profile[deg].up - wobble_profile[deg-1].up);
1472 /* Lean slightly toward the foot that is raised off the ground. */
1473 f->roll = -2.5 * sin ((deg - 90) * M_PI / 180);
1475 if (!(random() % 10))
1477 GLfloat b = f->balance / 10.0;
1478 int s = (f->balance > 0 ? 1 : -1);
1480 f->facing += s * frand (b);
1484 if (debug_p) fwd = 0;
1490 th = f->facing * M_PI / 180.0;
1491 f->x += fwd * cos (th);
1492 f->y += fwd * sin (th);
1494 /* If moving this robot would collide with another, undo the move,
1495 recoil, and randomly turn.
1497 if (collision_p (mi, f, 0))
1500 f->x = ox + fwd * cos (th);
1501 f->y = oy + fwd * sin (th);
1502 f->facing += frand(10) - 5;
1503 if (! random() % 30)
1504 f->facing += frand(90) - 45;
1510 opacity > 0.5) /* Don't bother fading if it's already transparent. */
1512 GLfloat tick = 0.002;
1515 /* If we're not fading, maybe start fading out. */
1516 if (f->fading_p == 0 && ! (random() % 40000))
1520 if (debug_p) f->fading_p = 0;
1523 if (f->fading_p < 0)
1525 f->body_transparency -= tick;
1526 if (f->body_transparency <= -linger)
1528 f->body_transparency = -linger;
1532 else if (f->fading_p > 0)
1534 f->body_transparency += tick;
1535 if (f->body_transparency >= opacity)
1537 f->body_transparency = opacity;
1546 init_walker (ModeInfo *mi, walker *f)
1548 int i, start_tick = random() % 360;
1551 f->pitch = wobble_profile[0].rot;
1552 f->z = wobble_profile[0].up;
1554 f->body_transparency = opacity;
1556 f->hand_rot[0] = frand(180);
1557 f->hand_pos[0] = 0.6 + frand(0.4);
1558 f->hand_rot[1] = 180 - f->hand_rot[0];
1559 f->hand_pos[1] = f->hand_pos[0];
1561 if (! (random() % 30)) f->hand_rot[1] += frand(10);
1562 if (! (random() % 30)) f->hand_pos[1] = 0.6 + frand(0.4);
1564 f->facing = frand(360);
1565 f->balance = frand(10) - 5;
1567 if (MI_COUNT(mi) == 1)
1570 f->speed = 0.6 + frand(0.8);
1581 for (i = 0; i < start_tick; i++)
1582 tick_walker (mi, f);
1584 /* Place them randomly, but non-overlapping. */
1585 for (i = 0; i < 1000; i++)
1588 if (MI_COUNT(mi) > 10) range += MI_COUNT(mi) / 10.0;
1589 f->x = frand(range) - range/2;
1590 f->y = frand(range) - range/2;
1591 if (! collision_p (mi, f, 1.5))
1596 if (debug_p) f->x = f->y = 0;
1602 /* Draw a robot standing in the right place, 1 unit tall.
1605 draw_walker (ModeInfo *mi, walker *f, const char *tag)
1607 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1608 int wire = MI_IS_WIREFRAME(mi);
1612 glTranslatef (f->y, f->z, f->x);
1619 glRotatef (90, 0, 1, 0);
1620 glRotatef (f->facing, 0, 1, 0);
1621 glRotatef (f->pitch, 0, 0, 1);
1622 glRotatef (f->roll, 1, 0, 0);
1625 GLfloat n = 0.00484; /* make it 1 unit tall */
1629 count += draw_gearbox (mi);
1630 count += draw_crank (mi, f, f->crank_rot);
1631 count += draw_rotator (mi, f, f->crank_rot);
1632 count += draw_leg (mi, f->crank_rot, False);
1633 count += draw_leg (mi, f->crank_rot, True);
1634 count += draw_wireframe (mi, f);
1636 /* Draw these last, and outer shell first, to make transparency work.
1637 The order in which things hit the depth buffer matters.
1639 if (f->body_transparency >= 0.001)
1641 count += draw_arm (mi, f, True, f->hand_rot[0], f->hand_pos[0]);
1642 count += draw_arm (mi, f, False, f->hand_rot[1], f->hand_pos[1]);
1643 count += draw_body (mi, f, False);
1644 count += draw_body (mi, f, True);
1645 count += draw_dome (mi, f);
1648 if (tag) /* For debugging depth sorting: label each robot */
1651 if (! wire) glDisable (GL_DEPTH_TEST);
1652 glColor3f (1, 1, 1);
1655 /* Billboard rotation */
1656 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]);
1657 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
1658 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
1659 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
1661 glMultMatrixf (&m[0][0]);
1662 glScalef (0.04, 0.04, 0.04);
1664 print_texture_string (bp->font_data, tag);
1666 if (! wire) glEnable (GL_DEPTH_TEST);
1675 draw_ground (ModeInfo *mi, GLfloat color[4])
1677 int wire = MI_IS_WIREFRAME(mi);
1680 /* When using fog, iOS apparently doesn't like lines or quads that are
1681 really long, and extend very far outside of the scene. Maybe? If the
1682 length of the line (cells * cell_size) is greater than 25 or so, lines
1683 that are oriented steeply away from the viewer tend to disappear
1684 (whether implemented as GL_LINES or as GL_QUADS).
1686 So we do a bunch of smaller grids instead of one big one.
1689 GLfloat cell_size = 0.8;
1694 if (debug_p) return 0;
1699 glRotatef (frand(90), 0, 0, 1);
1703 GLfloat fog_color[4] = { 0, 0, 0, 1 };
1706 glEnable (GL_LINE_SMOOTH);
1707 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
1708 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1709 glEnable (GL_BLEND);
1711 glFogi (GL_FOG_MODE, GL_EXP2);
1712 glFogfv (GL_FOG_COLOR, fog_color);
1713 glFogf (GL_FOG_DENSITY, 0.015);
1714 glFogf (GL_FOG_START, -cells/2 * cell_size * grids);
1719 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
1721 glTranslatef (-cells * grids * cell_size / 2,
1722 -cells * grids * cell_size / 2, 0);
1724 for (j = 0; j < grids; j++)
1727 for (k = 0; k < grids; k++)
1730 for (i = -cells/2; i < cells/2; i++)
1732 GLfloat a = i * cell_size;
1733 GLfloat b = cells/2 * cell_size;
1734 glVertex3f (a, -b, 0); glVertex3f (a, b, 0); points++;
1735 glVertex3f (-b, a, 0); glVertex3f (b, a, 0); points++;
1738 glTranslatef (cells * cell_size, 0, 0);
1741 glTranslatef (0, cells * cell_size, 0);
1746 glDisable (GL_LINE_SMOOTH);
1747 glDisable (GL_BLEND);
1757 /* If the target robot (robot #0) has moved too far from the point at which
1758 the camera is aimed, then initiate an animation to move the observer to
1761 Because of the jerky forward motion of the robots, just always focusing
1762 on the center of the robot looks terrible, so instead we let them walk
1763 a little out of the center of the frame, and then catch up.
1766 look_at_center (ModeInfo *mi)
1768 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1769 GLfloat target_x = bp->walkers[0].x;
1770 GLfloat target_y = bp->walkers[0].y;
1771 GLfloat target_z = 0.8; /* Look a little bit above his head */
1772 GLfloat max_dist = 2.5 / size;
1775 if (debug_p) return;
1778 if (max_dist < 1) max_dist = 1;
1779 if (max_dist > 10) max_dist = 10;
1781 if (bp->camera_tracking_p)
1783 GLfloat r = (1 - cos (bp->tracking_ratio * M_PI)) / 2;
1784 bp->looking_x = bp->olooking_x + r * (target_x - bp->olooking_x);
1785 bp->looking_y = bp->olooking_y + r * (target_y - bp->olooking_y);
1786 bp->looking_z = bp->olooking_z + r * (target_z - bp->olooking_z);
1788 bp->tracking_ratio += 0.02;
1789 if (bp->tracking_ratio >= 1)
1791 bp->camera_tracking_p = False;
1792 bp->olooking_x = bp->looking_x;
1793 bp->olooking_y = bp->looking_y;
1794 bp->olooking_z = bp->looking_z;
1798 if (! bp->camera_tracking_p)
1801 sqrt ((target_x - bp->looking_x) * (target_x - bp->looking_x) +
1802 (target_y - bp->looking_y) * (target_y - bp->looking_y) +
1803 (target_z - bp->looking_z) * (target_z - bp->looking_z));
1805 if (dist > max_dist)
1807 bp->camera_tracking_p = True;
1808 bp->tracking_ratio = 0;
1809 bp->olooking_x = bp->looking_x;
1810 bp->olooking_y = bp->looking_y;
1811 bp->olooking_z = bp->looking_z;
1815 glTranslatef (-bp->looking_y, -bp->looking_z, -bp->looking_x);
1822 glTranslatef (target_y, target_z, target_x);
1824 glVertex3f(0, -target_z, 0);
1825 glVertex3f(0, 1, 0);
1826 glVertex3f(-0.1, 0, -0.1);
1827 glVertex3f( 0.1, 0, 0.1);
1828 glVertex3f(-0.1, 0, 0.1);
1829 glVertex3f( 0.1, 0, -0.1);
1835 glTranslatef (bp->looking_y, bp->looking_z, bp->looking_x);
1836 glRotatef (30, 0, 1, 0);
1838 glVertex3f(0, -bp->looking_z, 0);
1839 glVertex3f(0, 1, 0);
1840 glVertex3f(-0.1, 0, -0.1);
1841 glVertex3f( 0.1, 0, 0.1);
1842 glVertex3f(-0.1, 0, 0.1);
1843 glVertex3f( 0.1, 0, -0.1);
1849 glTranslatef (bp->olooking_y, bp->olooking_z, bp->olooking_x);
1850 glRotatef (60, 0, 1, 0);
1852 glVertex3f(0, -bp->olooking_z, 0);
1853 glVertex3f(0, 1, 0);
1854 glVertex3f(-0.1, 0, -0.1);
1855 glVertex3f( 0.1, 0, 0.1);
1856 glVertex3f(-0.1, 0, 0.1);
1857 glVertex3f( 0.1, 0, -0.1);
1860 glTranslatef (0, -bp->olooking_z, 0);
1861 glBegin (GL_LINE_LOOP);
1862 for (th = 0; th < M_PI * 2; th += 0.1)
1863 glVertex3f (bp->olooking_y + max_dist * cos(th), 0,
1864 bp->olooking_x + max_dist * sin(th));
1874 /* Draw a cartoony word bubble.
1875 W and H are the inside size, for text.
1876 Origin is at bottom left.
1877 The bubble frame and arrow are outside that.
1880 draw_bubble_box (ModeInfo *mi,
1881 GLfloat width, GLfloat height,
1882 GLfloat corner_radius,
1883 GLfloat arrow_h, GLfloat arrow_x,
1884 GLfloat fg[4], GLfloat bg[4])
1887 # define CORNER_POINTS 16
1888 GLfloat outline_points[ (CORNER_POINTS + 2) * 4 + 8 ][3];
1891 GLfloat tick = M_PI / 2 / CORNER_POINTS;
1893 GLfloat arrow_w = arrow_h / 2;
1894 GLfloat arrow_x2 = MAX(0, MIN(width - arrow_w, arrow_x));
1896 GLfloat w2 = MAX(arrow_w, width - corner_radius * 1.10);
1897 GLfloat h2 = MAX(0, height - corner_radius * 1.28);
1898 GLfloat x2 = (width - w2) / 2;
1899 GLfloat y2 = (height - h2) / 2;
1901 GLfloat xa = x2 -corner_radius; /* E _------------_ */
1902 GLfloat xb = x2; /* D /__| |__\ */
1903 GLfloat xc = xb + w2; /* | | | | */
1904 GLfloat xd = xc + corner_radius; /* C |__| EF |__| */
1905 GLfloat xe = xb + arrow_x2; /* B \_|_________|_/ */
1906 GLfloat xf = xe + arrow_w; /* A \| */
1908 GLfloat ya = y2 - (corner_radius + arrow_h);
1909 GLfloat yb = y2 - corner_radius;
1911 GLfloat yd = yc + h2;
1912 GLfloat ye = yd + corner_radius;
1916 /* Let the lines take precedence over the fills. */
1917 glEnable (GL_POLYGON_OFFSET_FILL);
1918 glPolygonOffset (1.0, 1.0);
1923 /* top left corner */
1925 glBegin (GL_TRIANGLE_FAN);
1926 glVertex3f (xb, yd, 0);
1927 for (th = 0; th < M_PI/2 + tick; th += tick)
1929 GLfloat x = xb - corner_radius * cos(th);
1930 GLfloat y = yd + corner_radius * sin(th);
1931 glVertex3f (x, y, z);
1932 outline_points[i][0] = x;
1933 outline_points[i][1] = y;
1934 outline_points[i][2] = z;
1940 outline_points[i][0] = xc;
1941 outline_points[i][1] = ye;
1942 outline_points[i][2] = z;
1945 /* top right corner */
1947 glBegin (GL_TRIANGLE_FAN);
1948 glVertex3f (xc, yd, 0);
1949 for (th = M_PI/2; th > -tick; th -= tick)
1951 GLfloat x = xc + corner_radius * cos(th);
1952 GLfloat y = yd + corner_radius * sin(th);
1953 glVertex3f (x, y, z);
1954 outline_points[i][0] = x;
1955 outline_points[i][1] = y;
1956 outline_points[i][2] = z;
1962 outline_points[i][0] = xd;
1963 outline_points[i][1] = yc;
1964 outline_points[i][2] = z;
1967 /* bottom right corner */
1969 glBegin (GL_TRIANGLE_FAN);
1970 glVertex3f (xc, yc, 0);
1971 for (th = 0; th < M_PI/2 + tick; th += tick)
1973 GLfloat x = xc + corner_radius * cos(th);
1974 GLfloat y = yc - corner_radius * sin(th);
1975 glVertex3f (x, y, z);
1976 outline_points[i][0] = x;
1977 outline_points[i][1] = y;
1978 outline_points[i][2] = z;
1983 /* bottom right edge */
1984 outline_points[i][0] = xf;
1985 outline_points[i][1] = yb;
1986 outline_points[i][2] = z;
1989 /* arrow triangle */
1991 glBegin (GL_TRIANGLES);
1993 /* bottom arrow point */
1994 outline_points[i][0] = xf;
1995 outline_points[i][1] = yb;
1996 outline_points[i][2] = z;
1997 glVertex3f (outline_points[i][0],
1998 outline_points[i][1],
1999 outline_points[i][2]);
2002 /* bottom right edge */
2003 outline_points[i][0] = xf;
2004 outline_points[i][1] = ya;
2005 outline_points[i][2] = z;
2006 glVertex3f (outline_points[i][0],
2007 outline_points[i][1],
2008 outline_points[i][2]);
2011 outline_points[i][0] = xe;
2012 outline_points[i][1] = yb;
2013 outline_points[i][2] = z;
2014 glVertex3f (outline_points[i][0],
2015 outline_points[i][1],
2016 outline_points[i][2]);
2021 /* bottom left corner */
2023 glBegin (GL_TRIANGLE_FAN);
2024 glVertex3f (xb, yc, 0);
2025 for (th = M_PI/2; th > -tick; th -= tick)
2027 GLfloat x = xb - corner_radius * cos(th);
2028 GLfloat y = yc - corner_radius * sin(th);
2029 glVertex3f (x, y, z);
2030 outline_points[i][0] = x;
2031 outline_points[i][1] = y;
2032 outline_points[i][2] = z;
2037 glFrontFace(GL_CCW);
2040 outline_points[i][0] = xa;
2041 outline_points[i][1] = yd;
2042 outline_points[i][2] = z;
2048 glVertex3f (xa, yd, z);
2049 glVertex3f (xb, yd, z);
2050 glVertex3f (xb, yc, z);
2051 glVertex3f (xa, yc, z);
2054 glVertex3f (xb, ye, z);
2055 glVertex3f (xc, ye, z);
2056 glVertex3f (xc, yb, z);
2057 glVertex3f (xb, yb, z);
2060 glVertex3f (xc, yd, z);
2061 glVertex3f (xd, yd, z);
2062 glVertex3f (xd, yc, z);
2063 glVertex3f (xc, yc, z);
2070 glBegin (GL_LINE_LOOP);
2072 glVertex3fv (outline_points[--i]);
2075 glDisable (GL_POLYGON_OFFSET_FILL);
2080 draw_label (ModeInfo *mi, walker *f, GLfloat y_off, GLfloat scale,
2083 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2084 int wire = MI_IS_WIREFRAME(mi);
2087 if (scale == 0) return;
2090 glDisable (GL_LIGHTING); /* don't light fonts */
2094 /* First, we translate the origin to the center of the robot.
2096 Then we retrieve the prevailing modelview matrix, which
2097 includes any rotation, wandering, and user-trackball-rolling
2100 We set the top 3x3 cells of that matrix to be the identity
2101 matrix. This removes all rotation from the matrix, while
2102 leaving the translation alone. This has the effect of
2103 leaving the prevailing coordinate system perpendicular to
2104 the camera view: were we to draw a square face, it would
2105 be in the plane of the screen.
2107 Now we translate by `size' toward the viewer -- so that the
2108 origin is *just in front* of the ball.
2110 Then we draw the label text, allowing the depth buffer to
2111 do its work: that way, labels on atoms will be occluded
2112 properly when other atoms move in front of them.
2114 This technique (of neutralizing rotation relative to the
2115 observer, after both rotations and translations have been
2116 applied) is known as "billboarding".
2120 glTranslatef(f->y, 0, f->x); /* get matrix */
2122 glTranslatef (0, y_off, 0);
2124 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]); /* load rot. identity */
2125 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
2126 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
2127 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
2128 glLoadIdentity(); /* reset modelview */
2129 glMultMatrixf (&m[0][0]); /* replace with ours */
2131 glTranslatef (0, 0, 0.1); /* move toward camera */
2133 glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
2137 int cw, ch, w, h, ascent, descent;
2139 GLfloat max = 24; /* max point size to avoid pixellated text */
2141 /* Let the font be much larger on iPhone */
2142 if (mi->xgwa.height <= 640 || mi->xgwa.width <= 640)
2145 texture_string_metrics (bp->font_data, "X", &e, &ascent, &descent);
2147 ch = ascent + descent;
2149 if (ch > max) s *= max/ch;
2153 texture_string_metrics (bp->font_data, label, &e, 0, 0);
2155 h = e.ascent + e.descent;
2158 glTranslatef (-w/2, h*2/3 + (cw * 7), 0);
2161 glTranslatef (0, -h + (ch * 1.2), -0.1);
2162 draw_bubble_box (mi, w, h,
2163 ch * 2, /* corner radius */
2164 ch * 2.5, /* arrow height */
2165 w / 2 - cw * 8, /* arrow x */
2166 bp->text_bd, bp->text_bg);
2169 glColor4fv (bp->text_color);
2170 glTranslatef (0, ch/2, 0);
2171 print_texture_string (bp->font_data, label);
2176 /* More efficient to always call glEnable() with correct values
2177 than to call glPushAttrib()/glPopAttrib(), since reading
2178 attributes from GL does a round-trip and stalls the pipeline.
2181 glEnable (GL_LIGHTING);
2186 fill_words (ModeInfo *mi)
2188 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2189 char *p = bp->words + strlen(bp->words);
2192 int max = bp->max_lines;
2194 /* Fewer lines on iPhone */
2195 if ((mi->xgwa.height <= 640 || mi->xgwa.width <= 640) &&
2199 for (c = bp->words; c < p; c++)
2203 while (p < bp->words + sizeof(bp->words) - 1 &&
2206 int c = textclient_getc (bp->tc);
2221 bubble (ModeInfo *mi)
2223 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2225 GLfloat fade = 0.015;
2226 int chance = (talk_chance <= 0.0 ? 0 :
2227 talk_chance >= 0.99 ? 1 :
2228 (1-talk_chance) * 1000);
2230 char *s0 = strdup (bp->words);
2234 while (*s == '\n') s++;
2236 while (L > 0 && (s[L-1] == '\n' || s[L-1] == ' ' || s[L-1] == '\t'))
2238 if (! *s) goto DONE;
2241 if (debug_p) goto DONE;
2244 if (chance <= 0) goto DONE;
2246 if (bp->bubble_tick > 0)
2249 if (! bp->bubble_tick)
2253 if (! bp->bubble_tick)
2255 if (!(random() % chance))
2256 bp->bubble_tick = duration;
2261 scale = (bp->bubble_tick < duration * fade
2262 ? bp->bubble_tick / (duration * fade)
2263 : (bp->bubble_tick > duration * (1 - fade)
2264 ? 1 - ((bp->bubble_tick - duration * (1 - fade))
2265 / (duration * fade))
2268 draw_label (mi, &bp->walkers[0], 1.5, scale, s);
2273 #endif /* WORDBUBBLES */
2283 cmp_depth_sorter (const void *aa, const void *bb)
2285 const depth_sorter *a = (depth_sorter *) aa;
2286 const depth_sorter *b = (depth_sorter *) bb;
2287 return (a->d == b->d ? 0 : a->d < b->d ? -1 : 1);
2292 draw_robot (ModeInfo *mi)
2294 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2295 Display *dpy = MI_DISPLAY(mi);
2296 Window window = MI_WINDOW(mi);
2298 depth_sorter *sorted;
2301 if (!bp->glx_context)
2304 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
2306 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2311 glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
2314 gltrackball_rotate (bp->user_trackball);
2316 glTranslatef (0, -20, 0); /* Move the horizon down the screen */
2318 robot_size = size * 7;
2319 glScalef (robot_size, robot_size, robot_size);
2320 look_at_center (mi);
2323 glScalef (1/robot_size, 1/robot_size, 1/robot_size);
2324 glCallList (bp->dlists[GROUND]);
2335 glTranslatef (0, -1.2, 0);
2337 glRotatef (-43.5, 0, 0, 1);
2338 glRotatef (-90, 0, 1, 0);
2340 /* glTranslatef (bp->debug_z, bp->debug_y, 0); */
2343 if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
2344 if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
2347 glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
2348 glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
2349 glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
2352 glTranslatef (-0.5, 0, -0.5);
2354 glColor3f (1, 0, 0);
2355 glBegin (GL_LINE_LOOP);
2356 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2357 glVertex3f (0, 1, 1); glVertex3f (0, 1, 0);
2360 glBegin (GL_LINE_LOOP);
2361 glVertex3f (1, 0, 0); glVertex3f (1, 0, 1);
2362 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2366 glColor3f (0.5, 0.5, 0.5);
2367 glFrontFace (GL_CCW);
2369 /* glVertex3f (0, 1, 0); glVertex3f (0, 1, 1); */
2370 /* glVertex3f (1, 1, 1); glVertex3f (1, 1, 0); */
2371 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2372 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2375 glFrontFace (GL_CW);
2377 glVertex3f (0, 1, 0); glVertex3f (0, 1, 1);
2378 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2379 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2380 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2384 glColor3f (1, 0, 0);
2386 glVertex3f (0, 0, 0); glVertex3f (1, 0, 0);
2387 glVertex3f (0, 0, 1); glVertex3f (1, 0, 1);
2388 glVertex3f (0, 1, 0); glVertex3f (1, 1, 0);
2389 glVertex3f (0, 1, 1); glVertex3f (1, 1, 1);
2391 glVertex3f (0, 0, 0); glVertex3f (1, 0, 1);
2392 glVertex3f (0, 0, 1); glVertex3f (1, 0, 0);
2393 glVertex3f (0, 1, 0); glVertex3f (1, 1, 1);
2394 glVertex3f (0, 1, 1); glVertex3f (1, 1, 0);
2397 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
2398 if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
2405 /* For transparency to work right, we have to draw the robots from
2406 back to front, from the perspective of the observer. So project
2407 the origin of the robot to screen coordinates, and sort that by Z.
2409 sorted = (depth_sorter *) calloc (bp->nwalkers, sizeof (*sorted));
2411 GLdouble m[16], p[16];
2413 glGetDoublev (GL_MODELVIEW_MATRIX, m);
2414 glGetDoublev (GL_PROJECTION_MATRIX, p);
2415 glGetIntegerv (GL_VIEWPORT, v);
2417 for (i = 0; i < bp->nwalkers; i++)
2420 walker *f = &bp->walkers[i];
2421 gluProject (f->y, f->z, f->x, m, p, v, &x, &y, &z);
2425 qsort (sorted, i, sizeof(*sorted), cmp_depth_sorter);
2429 mi->polygon_count = 0;
2430 for (i = 0; i < bp->nwalkers; i++)
2432 int ii = sorted[i].i;
2433 walker *f = &bp->walkers[ii];
2434 int ticks = 22 * speed * f->speed;
2439 if (ticks < 1) ticks = 1;
2440 if (ticks > max) ticks = max;
2444 sprintf (tag, "%.4f, %.4f, %.4f",
2445 bp->debug_x, bp->debug_y, bp->debug_z);
2449 /* sprintf (tag + strlen(tag), " %d\n", ii); */
2450 sprintf (tag + strlen(tag), " %d\n", bp->nwalkers - i - 1);
2451 /* sprintf (tag + strlen(tag), "%.03f\n", sorted[i].d); */
2457 mi->polygon_count += draw_walker (mi, f, tag);
2459 for (ii = 0; ii < ticks; ii++)
2460 tick_walker (mi, f);
2466 if (mi->fps_p) do_fps (mi);
2469 glXSwapBuffers(dpy, window);
2473 free_robot (ModeInfo *mi)
2476 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2478 textclient_close (bp->tc);
2482 XSCREENSAVER_MODULE_2 ("WindupRobot", winduprobot, robot)